aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
commit8879b125d26e8db1a5633de5a9c692eb2d1c4f83 (patch)
treec7259d85a991b83dfa85ab2e339360669fc1f58e /framework/src/suricata/src
parent13d05bc8458758ee39cb829098241e89616717ee (diff)
suricata checkin based on commit id a4bce14770beee46a537eda3c3f6e8e8565d5d0a
Change-Id: I9a214fa0ee95e58fc640e50bd604dac7f42db48f
Diffstat (limited to 'framework/src/suricata/src')
-rw-r--r--framework/src/suricata/src/Makefile.am495
-rw-r--r--framework/src/suricata/src/action-globals.h36
-rw-r--r--framework/src/suricata/src/alert-debuglog.c526
-rw-r--r--framework/src/suricata/src/alert-debuglog.h30
-rw-r--r--framework/src/suricata/src/alert-fastlog.c386
-rw-r--r--framework/src/suricata/src/alert-fastlog.h33
-rw-r--r--framework/src/suricata/src/alert-prelude.c885
-rw-r--r--framework/src/suricata/src/alert-prelude.h31
-rw-r--r--framework/src/suricata/src/alert-syslog.c427
-rw-r--r--framework/src/suricata/src/alert-syslog.h33
-rw-r--r--framework/src/suricata/src/alert-unified2-alert.c1960
-rw-r--r--framework/src/suricata/src/alert-unified2-alert.h50
-rw-r--r--framework/src/suricata/src/app-layer-dcerpc-common.h246
-rw-r--r--framework/src/suricata/src/app-layer-dcerpc-udp.c1115
-rw-r--r--framework/src/suricata/src/app-layer-dcerpc-udp.h30
-rw-r--r--framework/src/suricata/src/app-layer-dcerpc.c6411
-rw-r--r--framework/src/suricata/src/app-layer-dcerpc.h44
-rw-r--r--framework/src/suricata/src/app-layer-detect-proto.c3776
-rw-r--r--framework/src/suricata/src/app-layer-detect-proto.h197
-rw-r--r--framework/src/suricata/src/app-layer-dns-common.c1141
-rw-r--r--framework/src/suricata/src/app-layer-dns-common.h259
-rw-r--r--framework/src/suricata/src/app-layer-dns-tcp.c682
-rw-r--r--framework/src/suricata/src/app-layer-dns-tcp.h38
-rw-r--r--framework/src/suricata/src/app-layer-dns-udp.c635
-rw-r--r--framework/src/suricata/src/app-layer-dns-udp.h37
-rw-r--r--framework/src/suricata/src/app-layer-events.c137
-rw-r--r--framework/src/suricata/src/app-layer-events.h83
-rw-r--r--framework/src/suricata/src/app-layer-ftp.c681
-rw-r--r--framework/src/suricata/src/app-layer-ftp.h133
-rw-r--r--framework/src/suricata/src/app-layer-htp-body.c264
-rw-r--r--framework/src/suricata/src/app-layer-htp-body.h36
-rw-r--r--framework/src/suricata/src/app-layer-htp-file.c1635
-rw-r--r--framework/src/suricata/src/app-layer-htp-file.h34
-rw-r--r--framework/src/suricata/src/app-layer-htp-libhtp.c219
-rw-r--r--framework/src/suricata/src/app-layer-htp-libhtp.h51
-rw-r--r--framework/src/suricata/src/app-layer-htp-mem.c150
-rw-r--r--framework/src/suricata/src/app-layer-htp-mem.h26
-rw-r--r--framework/src/suricata/src/app-layer-htp-xff.c364
-rw-r--r--framework/src/suricata/src/app-layer-htp-xff.h54
-rw-r--r--framework/src/suricata/src/app-layer-htp.c6231
-rw-r--r--framework/src/suricata/src/app-layer-htp.h294
-rw-r--r--framework/src/suricata/src/app-layer-modbus.c2671
-rw-r--r--framework/src/suricata/src/app-layer-modbus.h129
-rw-r--r--framework/src/suricata/src/app-layer-nbss.h66
-rw-r--r--framework/src/suricata/src/app-layer-parser.c1385
-rw-r--r--framework/src/suricata/src/app-layer-parser.h235
-rw-r--r--framework/src/suricata/src/app-layer-protos.c88
-rw-r--r--framework/src/suricata/src/app-layer-protos.h68
-rw-r--r--framework/src/suricata/src/app-layer-smb.c2717
-rw-r--r--framework/src/suricata/src/app-layer-smb.h166
-rw-r--r--framework/src/suricata/src/app-layer-smb2.c690
-rw-r--r--framework/src/suricata/src/app-layer-smb2.h83
-rw-r--r--framework/src/suricata/src/app-layer-smtp.c4875
-rw-r--r--framework/src/suricata/src/app-layer-smtp.h149
-rw-r--r--framework/src/suricata/src/app-layer-ssh.c2607
-rw-r--r--framework/src/suricata/src/app-layer-ssh.h77
-rw-r--r--framework/src/suricata/src/app-layer-ssl.c4220
-rw-r--r--framework/src/suricata/src/app-layer-ssl.h172
-rw-r--r--framework/src/suricata/src/app-layer-tls-handshake.c204
-rw-r--r--framework/src/suricata/src/app-layer-tls-handshake.h40
-rw-r--r--framework/src/suricata/src/app-layer.c3490
-rw-r--r--framework/src/suricata/src/app-layer.h141
-rw-r--r--framework/src/suricata/src/conf-yaml-loader.c949
-rw-r--r--framework/src/suricata/src/conf-yaml-loader.h33
-rw-r--r--framework/src/suricata/src/conf.c1532
-rw-r--r--framework/src/suricata/src/conf.h93
-rw-r--r--framework/src/suricata/src/counters.c1500
-rw-r--r--framework/src/suricata/src/counters.h150
-rw-r--r--framework/src/suricata/src/data-queue.c93
-rw-r--r--framework/src/suricata/src/data-queue.h64
-rw-r--r--framework/src/suricata/src/debug.h31
-rw-r--r--framework/src/suricata/src/decode-erspan.c81
-rw-r--r--framework/src/suricata/src/decode-erspan.h37
-rw-r--r--framework/src/suricata/src/decode-ethernet.c152
-rw-r--r--framework/src/suricata/src/decode-ethernet.h52
-rw-r--r--framework/src/suricata/src/decode-events.c27
-rw-r--r--framework/src/suricata/src/decode-events.h252
-rw-r--r--framework/src/suricata/src/decode-gre.c400
-rw-r--r--framework/src/suricata/src/decode-gre.h83
-rw-r--r--framework/src/suricata/src/decode-icmpv4.c784
-rw-r--r--framework/src/suricata/src/decode-icmpv4.h345
-rw-r--r--framework/src/suricata/src/decode-icmpv6.c1642
-rw-r--r--framework/src/suricata/src/decode-icmpv6.h259
-rw-r--r--framework/src/suricata/src/decode-ipv4.c1913
-rw-r--r--framework/src/suricata/src/decode-ipv4.h254
-rw-r--r--framework/src/suricata/src/decode-ipv6.c1001
-rw-r--r--framework/src/suricata/src/decode-ipv6.h334
-rw-r--r--framework/src/suricata/src/decode-mpls.c325
-rw-r--r--framework/src/suricata/src/decode-mpls.h34
-rw-r--r--framework/src/suricata/src/decode-null.c89
-rw-r--r--framework/src/suricata/src/decode-null.h27
-rw-r--r--framework/src/suricata/src/decode-ppp.c312
-rw-r--r--framework/src/suricata/src/decode-ppp.h76
-rw-r--r--framework/src/suricata/src/decode-pppoe.c460
-rw-r--r--framework/src/suricata/src/decode-pppoe.h82
-rw-r--r--framework/src/suricata/src/decode-raw.c232
-rw-r--r--framework/src/suricata/src/decode-raw.h28
-rw-r--r--framework/src/suricata/src/decode-sctp.c83
-rw-r--r--framework/src/suricata/src/decode-sctp.h51
-rw-r--r--framework/src/suricata/src/decode-sll.c76
-rw-r--r--framework/src/suricata/src/decode-sll.h38
-rw-r--r--framework/src/suricata/src/decode-tcp.c521
-rw-r--r--framework/src/suricata/src/decode-tcp.h297
-rw-r--r--framework/src/suricata/src/decode-template.c97
-rw-r--r--framework/src/suricata/src/decode-template.h37
-rw-r--r--framework/src/suricata/src/decode-teredo.c112
-rw-r--r--framework/src/suricata/src/decode-teredo.h19
-rw-r--r--framework/src/suricata/src/decode-udp.c218
-rw-r--r--framework/src/suricata/src/decode-udp.h193
-rw-r--r--framework/src/suricata/src/decode-vlan.c279
-rw-r--r--framework/src/suricata/src/decode-vlan.h55
-rw-r--r--framework/src/suricata/src/decode.c572
-rw-r--r--framework/src/suricata/src/decode.h1048
-rw-r--r--framework/src/suricata/src/defrag-config.c162
-rw-r--r--framework/src/suricata/src/defrag-config.h32
-rw-r--r--framework/src/suricata/src/defrag-hash.c727
-rw-r--r--framework/src/suricata/src/defrag-hash.h103
-rw-r--r--framework/src/suricata/src/defrag-queue.c144
-rw-r--r--framework/src/suricata/src/defrag-queue.h84
-rw-r--r--framework/src/suricata/src/defrag-timeout.c153
-rw-r--r--framework/src/suricata/src/defrag-timeout.h33
-rw-r--r--framework/src/suricata/src/defrag.c2381
-rw-r--r--framework/src/suricata/src/defrag.h139
-rw-r--r--framework/src/suricata/src/detect-ack.c302
-rw-r--r--framework/src/suricata/src/detect-ack.h40
-rw-r--r--framework/src/suricata/src/detect-app-layer-event.c836
-rw-r--r--framework/src/suricata/src/detect-app-layer-event.h37
-rw-r--r--framework/src/suricata/src/detect-app-layer-protocol.c407
-rw-r--r--framework/src/suricata/src/detect-app-layer-protocol.h34
-rw-r--r--framework/src/suricata/src/detect-asn1.c1366
-rw-r--r--framework/src/suricata/src/detect-asn1.h47
-rw-r--r--framework/src/suricata/src/detect-byte-extract.c4897
-rw-r--r--framework/src/suricata/src/detect-byte-extract.h71
-rw-r--r--framework/src/suricata/src/detect-bytejump.c1429
-rw-r--r--framework/src/suricata/src/detect-bytejump.h121
-rw-r--r--framework/src/suricata/src/detect-bytetest.c1580
-rw-r--r--framework/src/suricata/src/detect-bytetest.h129
-rw-r--r--framework/src/suricata/src/detect-classtype.c342
-rw-r--r--framework/src/suricata/src/detect-classtype.h31
-rw-r--r--framework/src/suricata/src/detect-content.c2824
-rw-r--r--framework/src/suricata/src/detect-content.h102
-rw-r--r--framework/src/suricata/src/detect-csum.c1647
-rw-r--r--framework/src/suricata/src/detect-csum.h39
-rw-r--r--framework/src/suricata/src/detect-dce-iface.c1837
-rw-r--r--framework/src/suricata/src/detect-dce-iface.h45
-rw-r--r--framework/src/suricata/src/detect-dce-opnum.c2950
-rw-r--r--framework/src/suricata/src/detect-dce-opnum.h43
-rw-r--r--framework/src/suricata/src/detect-dce-stub-data.c1848
-rw-r--r--framework/src/suricata/src/detect-dce-stub-data.h30
-rw-r--r--framework/src/suricata/src/detect-depth.c156
-rw-r--r--framework/src/suricata/src/detect-depth.h31
-rw-r--r--framework/src/suricata/src/detect-detection-filter.c665
-rw-r--r--framework/src/suricata/src/detect-detection-filter.h44
-rw-r--r--framework/src/suricata/src/detect-distance.c270
-rw-r--r--framework/src/suricata/src/detect-distance.h31
-rw-r--r--framework/src/suricata/src/detect-dns-query.c1180
-rw-r--r--framework/src/suricata/src/detect-dns-query.h33
-rw-r--r--framework/src/suricata/src/detect-dsize.c838
-rw-r--r--framework/src/suricata/src/detect-dsize.h42
-rw-r--r--framework/src/suricata/src/detect-engine-address-ipv4.c1614
-rw-r--r--framework/src/suricata/src/detect-engine-address-ipv4.h39
-rw-r--r--framework/src/suricata/src/detect-engine-address-ipv6.c2279
-rw-r--r--framework/src/suricata/src/detect-engine-address-ipv6.h43
-rw-r--r--framework/src/suricata/src/detect-engine-address.c5419
-rw-r--r--framework/src/suricata/src/detect-engine-address.h63
-rw-r--r--framework/src/suricata/src/detect-engine-alert.c337
-rw-r--r--framework/src/suricata/src/detect-engine-alert.h38
-rw-r--r--framework/src/suricata/src/detect-engine-analyzer.c926
-rw-r--r--framework/src/suricata/src/detect-engine-analyzer.h42
-rw-r--r--framework/src/suricata/src/detect-engine-apt-event.c79
-rw-r--r--framework/src/suricata/src/detect-engine-apt-event.h34
-rw-r--r--framework/src/suricata/src/detect-engine-content-inspection.c576
-rw-r--r--framework/src/suricata/src/detect-engine-content-inspection.h62
-rw-r--r--framework/src/suricata/src/detect-engine-dcepayload.c10192
-rw-r--r--framework/src/suricata/src/detect-engine-dcepayload.h32
-rw-r--r--framework/src/suricata/src/detect-engine-dns.c163
-rw-r--r--framework/src/suricata/src/detect-engine-dns.h40
-rw-r--r--framework/src/suricata/src/detect-engine-event.c412
-rw-r--r--framework/src/suricata/src/detect-engine-event.h256
-rw-r--r--framework/src/suricata/src/detect-engine-file.c301
-rw-r--r--framework/src/suricata/src/detect-engine-file.h37
-rw-r--r--framework/src/suricata/src/detect-engine-filedata-smtp.c563
-rw-r--r--framework/src/suricata/src/detect-engine-filedata-smtp.h43
-rw-r--r--framework/src/suricata/src/detect-engine-hcbd.c1116
-rw-r--r--framework/src/suricata/src/detect-engine-hcbd.h45
-rw-r--r--framework/src/suricata/src/detect-engine-hcd.c1878
-rw-r--r--framework/src/suricata/src/detect-engine-hcd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hhd.c3905
-rw-r--r--framework/src/suricata/src/detect-engine-hhd.h41
-rw-r--r--framework/src/suricata/src/detect-engine-hhhd.c2616
-rw-r--r--framework/src/suricata/src/detect-engine-hhhd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hmd.c1827
-rw-r--r--framework/src/suricata/src/detect-engine-hmd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hrhd.c3545
-rw-r--r--framework/src/suricata/src/detect-engine-hrhd.h40
-rw-r--r--framework/src/suricata/src/detect-engine-hrhhd.c2643
-rw-r--r--framework/src/suricata/src/detect-engine-hrhhd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hrl.c4221
-rw-r--r--framework/src/suricata/src/detect-engine-hrl.h34
-rw-r--r--framework/src/suricata/src/detect-engine-hrud.c3726
-rw-r--r--framework/src/suricata/src/detect-engine-hrud.h40
-rw-r--r--framework/src/suricata/src/detect-engine-hsbd.c4515
-rw-r--r--framework/src/suricata/src/detect-engine-hsbd.h45
-rw-r--r--framework/src/suricata/src/detect-engine-hscd.c2097
-rw-r--r--framework/src/suricata/src/detect-engine-hscd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hsmd.c2097
-rw-r--r--framework/src/suricata/src/detect-engine-hsmd.h39
-rw-r--r--framework/src/suricata/src/detect-engine-hua.c1855
-rw-r--r--framework/src/suricata/src/detect-engine-hua.h39
-rw-r--r--framework/src/suricata/src/detect-engine-iponly.c2321
-rw-r--r--framework/src/suricata/src/detect-engine-iponly.h53
-rw-r--r--framework/src/suricata/src/detect-engine-loader.c300
-rw-r--r--framework/src/suricata/src/detect-engine-loader.h57
-rw-r--r--framework/src/suricata/src/detect-engine-modbus.c1345
-rw-r--r--framework/src/suricata/src/detect-engine-modbus.h41
-rw-r--r--framework/src/suricata/src/detect-engine-mpm.c3030
-rw-r--r--framework/src/suricata/src/detect-engine-mpm.h99
-rw-r--r--framework/src/suricata/src/detect-engine-payload.c1113
-rw-r--r--framework/src/suricata/src/detect-engine-payload.h36
-rw-r--r--framework/src/suricata/src/detect-engine-port.c2850
-rw-r--r--framework/src/suricata/src/detect-engine-port.h68
-rw-r--r--framework/src/suricata/src/detect-engine-proto.c627
-rw-r--r--framework/src/suricata/src/detect-engine-proto.h48
-rw-r--r--framework/src/suricata/src/detect-engine-siggroup.c2390
-rw-r--r--framework/src/suricata/src/detect-engine-siggroup.h94
-rw-r--r--framework/src/suricata/src/detect-engine-sigorder.c2201
-rw-r--r--framework/src/suricata/src/detect-engine-sigorder.h81
-rw-r--r--framework/src/suricata/src/detect-engine-state.c2346
-rw-r--r--framework/src/suricata/src/detect-engine-state.h243
-rw-r--r--framework/src/suricata/src/detect-engine-tag.c1519
-rw-r--r--framework/src/suricata/src/detect-engine-tag.h63
-rw-r--r--framework/src/suricata/src/detect-engine-threshold.c689
-rw-r--r--framework/src/suricata/src/detect-engine-threshold.h46
-rw-r--r--framework/src/suricata/src/detect-engine-uri.c4222
-rw-r--r--framework/src/suricata/src/detect-engine-uri.h36
-rw-r--r--framework/src/suricata/src/detect-engine.c3225
-rw-r--r--framework/src/suricata/src/detect-engine.h121
-rw-r--r--framework/src/suricata/src/detect-fast-pattern.c20156
-rw-r--r--framework/src/suricata/src/detect-fast-pattern.h67
-rw-r--r--framework/src/suricata/src/detect-file-data.c280
-rw-r--r--framework/src/suricata/src/detect-file-data.h30
-rw-r--r--framework/src/suricata/src/detect-fileext.c315
-rw-r--r--framework/src/suricata/src/detect-fileext.h38
-rw-r--r--framework/src/suricata/src/detect-filemagic.c498
-rw-r--r--framework/src/suricata/src/detect-filemagic.h46
-rw-r--r--framework/src/suricata/src/detect-filemd5.c428
-rw-r--r--framework/src/suricata/src/detect-filemd5.h37
-rw-r--r--framework/src/suricata/src/detect-filename.c321
-rw-r--r--framework/src/suricata/src/detect-filename.h39
-rw-r--r--framework/src/suricata/src/detect-filesize.c532
-rw-r--r--framework/src/suricata/src/detect-filesize.h42
-rw-r--r--framework/src/suricata/src/detect-filestore.c443
-rw-r--r--framework/src/suricata/src/detect-filestore.h45
-rw-r--r--framework/src/suricata/src/detect-flags.c1341
-rw-r--r--framework/src/suricata/src/detect-flags.h59
-rw-r--r--framework/src/suricata/src/detect-flow.c1127
-rw-r--r--framework/src/suricata/src/detect-flow.h43
-rw-r--r--framework/src/suricata/src/detect-flowbits.c1156
-rw-r--r--framework/src/suricata/src/detect-flowbits.h45
-rw-r--r--framework/src/suricata/src/detect-flowint.c2178
-rw-r--r--framework/src/suricata/src/detect-flowint.h86
-rw-r--r--framework/src/suricata/src/detect-flowvar.c357
-rw-r--r--framework/src/suricata/src/detect-flowvar.h56
-rw-r--r--framework/src/suricata/src/detect-fragbits.c581
-rw-r--r--framework/src/suricata/src/detect-fragbits.h58
-rw-r--r--framework/src/suricata/src/detect-fragoffset.c396
-rw-r--r--framework/src/suricata/src/detect-fragoffset.h38
-rw-r--r--framework/src/suricata/src/detect-ftpbounce.c560
-rw-r--r--framework/src/suricata/src/detect-ftpbounce.h31
-rw-r--r--framework/src/suricata/src/detect-geoip.c618
-rw-r--r--framework/src/suricata/src/detect-geoip.h46
-rw-r--r--framework/src/suricata/src/detect-gid.c202
-rw-r--r--framework/src/suricata/src/detect-gid.h45
-rw-r--r--framework/src/suricata/src/detect-hostbits.c1473
-rw-r--r--framework/src/suricata/src/detect-hostbits.h34
-rw-r--r--framework/src/suricata/src/detect-http-client-body.c2407
-rw-r--r--framework/src/suricata/src/detect-http-client-body.h29
-rw-r--r--framework/src/suricata/src/detect-http-cookie.c1277
-rw-r--r--framework/src/suricata/src/detect-http-cookie.h33
-rw-r--r--framework/src/suricata/src/detect-http-header.c1822
-rw-r--r--framework/src/suricata/src/detect-http-header.h30
-rw-r--r--framework/src/suricata/src/detect-http-hh.c2106
-rw-r--r--framework/src/suricata/src/detect-http-hh.h29
-rw-r--r--framework/src/suricata/src/detect-http-hrh.c2233
-rw-r--r--framework/src/suricata/src/detect-http-hrh.h29
-rw-r--r--framework/src/suricata/src/detect-http-method.c833
-rw-r--r--framework/src/suricata/src/detect-http-method.h33
-rw-r--r--framework/src/suricata/src/detect-http-raw-header.c1557
-rw-r--r--framework/src/suricata/src/detect-http-raw-header.h29
-rw-r--r--framework/src/suricata/src/detect-http-raw-uri.c566
-rw-r--r--framework/src/suricata/src/detect-http-raw-uri.h30
-rw-r--r--framework/src/suricata/src/detect-http-server-body.c3873
-rw-r--r--framework/src/suricata/src/detect-http-server-body.h29
-rw-r--r--framework/src/suricata/src/detect-http-stat-code.c688
-rw-r--r--framework/src/suricata/src/detect-http-stat-code.h34
-rw-r--r--framework/src/suricata/src/detect-http-stat-msg.c564
-rw-r--r--framework/src/suricata/src/detect-http-stat-msg.h33
-rw-r--r--framework/src/suricata/src/detect-http-ua.c2110
-rw-r--r--framework/src/suricata/src/detect-http-ua.h29
-rw-r--r--framework/src/suricata/src/detect-http-uri.c556
-rw-r--r--framework/src/suricata/src/detect-http-uri.h34
-rw-r--r--framework/src/suricata/src/detect-icmp-id.c500
-rw-r--r--framework/src/suricata/src/detect-icmp-id.h34
-rw-r--r--framework/src/suricata/src/detect-icmp-seq.c390
-rw-r--r--framework/src/suricata/src/detect-icmp-seq.h35
-rw-r--r--framework/src/suricata/src/detect-icode.c528
-rw-r--r--framework/src/suricata/src/detect-icode.h44
-rw-r--r--framework/src/suricata/src/detect-id.c384
-rw-r--r--framework/src/suricata/src/detect-id.h39
-rw-r--r--framework/src/suricata/src/detect-ipopts.c386
-rw-r--r--framework/src/suricata/src/detect-ipopts.h80
-rw-r--r--framework/src/suricata/src/detect-ipproto.c9609
-rw-r--r--framework/src/suricata/src/detect-ipproto.h48
-rw-r--r--framework/src/suricata/src/detect-iprep.c1030
-rw-r--r--framework/src/suricata/src/detect-iprep.h46
-rw-r--r--framework/src/suricata/src/detect-isdataat.c1249
-rw-r--r--framework/src/suricata/src/detect-isdataat.h44
-rw-r--r--framework/src/suricata/src/detect-itype.c529
-rw-r--r--framework/src/suricata/src/detect-itype.h42
-rw-r--r--framework/src/suricata/src/detect-l3proto.c391
-rw-r--r--framework/src/suricata/src/detect-l3proto.h33
-rw-r--r--framework/src/suricata/src/detect-lua-extensions.c625
-rw-r--r--framework/src/suricata/src/detect-lua-extensions.h35
-rw-r--r--framework/src/suricata/src/detect-lua.c2072
-rw-r--r--framework/src/suricata/src/detect-lua.h72
-rw-r--r--framework/src/suricata/src/detect-mark.c353
-rw-r--r--framework/src/suricata/src/detect-mark.h61
-rw-r--r--framework/src/suricata/src/detect-metadata.c49
-rw-r--r--framework/src/suricata/src/detect-metadata.h31
-rw-r--r--framework/src/suricata/src/detect-modbus.c897
-rw-r--r--framework/src/suricata/src/detect-modbus.h64
-rw-r--r--framework/src/suricata/src/detect-msg.c211
-rw-r--r--framework/src/suricata/src/detect-msg.h31
-rw-r--r--framework/src/suricata/src/detect-noalert.c53
-rw-r--r--framework/src/suricata/src/detect-noalert.h31
-rw-r--r--framework/src/suricata/src/detect-nocase.c127
-rw-r--r--framework/src/suricata/src/detect-nocase.h31
-rw-r--r--framework/src/suricata/src/detect-offset.c158
-rw-r--r--framework/src/suricata/src/detect-offset.h31
-rw-r--r--framework/src/suricata/src/detect-parse.c3476
-rw-r--r--framework/src/suricata/src/detect-parse.h68
-rw-r--r--framework/src/suricata/src/detect-pcre.c4126
-rw-r--r--framework/src/suricata/src/detect-pcre.h54
-rw-r--r--framework/src/suricata/src/detect-pkt-data.c152
-rw-r--r--framework/src/suricata/src/detect-pkt-data.h30
-rw-r--r--framework/src/suricata/src/detect-pktvar.c259
-rw-r--r--framework/src/suricata/src/detect-pktvar.h38
-rw-r--r--framework/src/suricata/src/detect-priority.c221
-rw-r--r--framework/src/suricata/src/detect-priority.h34
-rw-r--r--framework/src/suricata/src/detect-rawbytes.c93
-rw-r--r--framework/src/suricata/src/detect-rawbytes.h31
-rw-r--r--framework/src/suricata/src/detect-reference.c376
-rw-r--r--framework/src/suricata/src/detect-reference.h58
-rw-r--r--framework/src/suricata/src/detect-replace.c845
-rw-r--r--framework/src/suricata/src/detect-replace.h51
-rw-r--r--framework/src/suricata/src/detect-rev.c83
-rw-r--r--framework/src/suricata/src/detect-rev.h31
-rw-r--r--framework/src/suricata/src/detect-rpc.c609
-rw-r--r--framework/src/suricata/src/detect-rpc.h58
-rw-r--r--framework/src/suricata/src/detect-sameip.c222
-rw-r--r--framework/src/suricata/src/detect-sameip.h30
-rw-r--r--framework/src/suricata/src/detect-seq.c243
-rw-r--r--framework/src/suricata/src/detect-seq.h40
-rw-r--r--framework/src/suricata/src/detect-sid.c165
-rw-r--r--framework/src/suricata/src/detect-sid.h31
-rw-r--r--framework/src/suricata/src/detect-ssh-proto-version.c700
-rw-r--r--framework/src/suricata/src/detect-ssh-proto-version.h40
-rw-r--r--framework/src/suricata/src/detect-ssh-software-version.c673
-rw-r--r--framework/src/suricata/src/detect-ssh-software-version.h37
-rw-r--r--framework/src/suricata/src/detect-ssl-state.c913
-rw-r--r--framework/src/suricata/src/detect-ssl-state.h42
-rw-r--r--framework/src/suricata/src/detect-ssl-version.c804
-rw-r--r--framework/src/suricata/src/detect-ssl-version.h53
-rw-r--r--framework/src/suricata/src/detect-stream_size.c532
-rw-r--r--framework/src/suricata/src/detect-stream_size.h48
-rw-r--r--framework/src/suricata/src/detect-tag.c488
-rw-r--r--framework/src/suricata/src/detect-tag.h105
-rw-r--r--framework/src/suricata/src/detect-template.c303
-rw-r--r--framework/src/suricata/src/detect-template.h41
-rw-r--r--framework/src/suricata/src/detect-threshold.c1525
-rw-r--r--framework/src/suricata/src/detect-threshold.h95
-rw-r--r--framework/src/suricata/src/detect-tls-version.c722
-rw-r--r--framework/src/suricata/src/detect-tls-version.h35
-rw-r--r--framework/src/suricata/src/detect-tls.c853
-rw-r--r--framework/src/suricata/src/detect-tls.h48
-rw-r--r--framework/src/suricata/src/detect-tos.c430
-rw-r--r--framework/src/suricata/src/detect-tos.h34
-rw-r--r--framework/src/suricata/src/detect-ttl.c638
-rw-r--r--framework/src/suricata/src/detect-ttl.h42
-rw-r--r--framework/src/suricata/src/detect-uricontent.c1983
-rw-r--r--framework/src/suricata/src/detect-uricontent.h42
-rw-r--r--framework/src/suricata/src/detect-urilen.c700
-rw-r--r--framework/src/suricata/src/detect-urilen.h44
-rw-r--r--framework/src/suricata/src/detect-window.c367
-rw-r--r--framework/src/suricata/src/detect-window.h33
-rw-r--r--framework/src/suricata/src/detect-within.c292
-rw-r--r--framework/src/suricata/src/detect-within.h31
-rw-r--r--framework/src/suricata/src/detect-xbits.c545
-rw-r--r--framework/src/suricata/src/detect-xbits.h52
-rw-r--r--framework/src/suricata/src/detect.c12456
-rw-r--r--framework/src/suricata/src/detect.h1284
-rw-r--r--framework/src/suricata/src/flow-bit.c483
-rw-r--r--framework/src/suricata/src/flow-bit.h50
-rw-r--r--framework/src/suricata/src/flow-hash.c851
-rw-r--r--framework/src/suricata/src/flow-hash.h89
-rw-r--r--framework/src/suricata/src/flow-manager.c1285
-rw-r--r--framework/src/suricata/src/flow-manager.h48
-rw-r--r--framework/src/suricata/src/flow-private.h100
-rw-r--r--framework/src/suricata/src/flow-queue.c168
-rw-r--r--framework/src/suricata/src/flow-queue.h85
-rw-r--r--framework/src/suricata/src/flow-storage.c296
-rw-r--r--framework/src/suricata/src/flow-storage.h45
-rw-r--r--framework/src/suricata/src/flow-timeout.c572
-rw-r--r--framework/src/suricata/src/flow-timeout.h32
-rw-r--r--framework/src/suricata/src/flow-util.c179
-rw-r--r--framework/src/suricata/src/flow-util.h153
-rw-r--r--framework/src/suricata/src/flow-var.c168
-rw-r--r--framework/src/suricata/src/flow-var.h73
-rw-r--r--framework/src/suricata/src/flow.c1147
-rw-r--r--framework/src/suricata/src/flow.h584
-rw-r--r--framework/src/suricata/src/host-bit.c503
-rw-r--r--framework/src/suricata/src/host-bit.h41
-rw-r--r--framework/src/suricata/src/host-queue.c144
-rw-r--r--framework/src/suricata/src/host-queue.h84
-rw-r--r--framework/src/suricata/src/host-storage.c290
-rw-r--r--framework/src/suricata/src/host-storage.h45
-rw-r--r--framework/src/suricata/src/host-timeout.c180
-rw-r--r--framework/src/suricata/src/host-timeout.h33
-rw-r--r--framework/src/suricata/src/host.c692
-rw-r--r--framework/src/suricata/src/host.h157
-rw-r--r--framework/src/suricata/src/ippair-bit.c506
-rw-r--r--framework/src/suricata/src/ippair-bit.h42
-rw-r--r--framework/src/suricata/src/ippair-queue.c143
-rw-r--r--framework/src/suricata/src/ippair-queue.h83
-rw-r--r--framework/src/suricata/src/ippair-storage.c299
-rw-r--r--framework/src/suricata/src/ippair-storage.h45
-rw-r--r--framework/src/suricata/src/ippair-timeout.c159
-rw-r--r--framework/src/suricata/src/ippair-timeout.h32
-rw-r--r--framework/src/suricata/src/ippair.c687
-rw-r--r--framework/src/suricata/src/ippair.h154
-rw-r--r--framework/src/suricata/src/log-dnslog.c362
-rw-r--r--framework/src/suricata/src/log-dnslog.h29
-rw-r--r--framework/src/suricata/src/log-droplog.c507
-rw-r--r--framework/src/suricata/src/log-droplog.h31
-rw-r--r--framework/src/suricata/src/log-file.c465
-rw-r--r--framework/src/suricata/src/log-file.h29
-rw-r--r--framework/src/suricata/src/log-filestore.c499
-rw-r--r--framework/src/suricata/src/log-filestore.h29
-rw-r--r--framework/src/suricata/src/log-httplog.c738
-rw-r--r--framework/src/suricata/src/log-httplog.h33
-rw-r--r--framework/src/suricata/src/log-pcap.c1197
-rw-r--r--framework/src/suricata/src/log-pcap.h34
-rw-r--r--framework/src/suricata/src/log-stats.c288
-rw-r--r--framework/src/suricata/src/log-stats.h29
-rw-r--r--framework/src/suricata/src/log-tcp-data.c345
-rw-r--r--framework/src/suricata/src/log-tcp-data.h30
-rw-r--r--framework/src/suricata/src/log-tlslog.c393
-rw-r--r--framework/src/suricata/src/log-tlslog.h35
-rw-r--r--framework/src/suricata/src/log-tlsstore.c439
-rw-r--r--framework/src/suricata/src/log-tlsstore.h29
-rw-r--r--framework/src/suricata/src/output-file.c298
-rw-r--r--framework/src/suricata/src/output-file.h46
-rw-r--r--framework/src/suricata/src/output-filedata.c485
-rw-r--r--framework/src/suricata/src/output-filedata.h50
-rw-r--r--framework/src/suricata/src/output-flow.c235
-rw-r--r--framework/src/suricata/src/output-flow.h50
-rw-r--r--framework/src/suricata/src/output-json-alert.c690
-rw-r--r--framework/src/suricata/src/output-json-alert.h36
-rw-r--r--framework/src/suricata/src/output-json-dns.c448
-rw-r--r--framework/src/suricata/src/output-json-dns.h29
-rw-r--r--framework/src/suricata/src/output-json-drop.c427
-rw-r--r--framework/src/suricata/src/output-json-drop.h30
-rw-r--r--framework/src/suricata/src/output-json-email-common.c266
-rw-r--r--framework/src/suricata/src/output-json-email-common.h40
-rw-r--r--framework/src/suricata/src/output-json-file.c396
-rw-r--r--framework/src/suricata/src/output-json-file.h29
-rw-r--r--framework/src/suricata/src/output-json-flow.c485
-rw-r--r--framework/src/suricata/src/output-json-flow.h29
-rw-r--r--framework/src/suricata/src/output-json-http.c597
-rw-r--r--framework/src/suricata/src/output-json-http.h35
-rw-r--r--framework/src/suricata/src/output-json-netflow.c467
-rw-r--r--framework/src/suricata/src/output-json-netflow.h29
-rw-r--r--framework/src/suricata/src/output-json-smtp.c224
-rw-r--r--framework/src/suricata/src/output-json-smtp.h29
-rw-r--r--framework/src/suricata/src/output-json-ssh.c351
-rw-r--r--framework/src/suricata/src/output-json-ssh.h35
-rw-r--r--framework/src/suricata/src/output-json-stats.c387
-rw-r--r--framework/src/suricata/src/output-json-stats.h29
-rw-r--r--framework/src/suricata/src/output-json-tls.c405
-rw-r--r--framework/src/suricata/src/output-json-tls.h36
-rw-r--r--framework/src/suricata/src/output-json.c584
-rw-r--r--framework/src/suricata/src/output-json.h61
-rw-r--r--framework/src/suricata/src/output-lua.c893
-rw-r--r--framework/src/suricata/src/output-lua.h29
-rw-r--r--framework/src/suricata/src/output-packet.c241
-rw-r--r--framework/src/suricata/src/output-packet.h46
-rw-r--r--framework/src/suricata/src/output-stats.c241
-rw-r--r--framework/src/suricata/src/output-stats.h57
-rw-r--r--framework/src/suricata/src/output-streaming.c469
-rw-r--r--framework/src/suricata/src/output-streaming.h55
-rw-r--r--framework/src/suricata/src/output-tx.c308
-rw-r--r--framework/src/suricata/src/output-tx.h45
-rw-r--r--framework/src/suricata/src/output.c701
-rw-r--r--framework/src/suricata/src/output.h125
-rw-r--r--framework/src/suricata/src/packet-queue.c198
-rw-r--r--framework/src/suricata/src/packet-queue.h34
-rw-r--r--framework/src/suricata/src/pkt-var.c124
-rw-r--r--framework/src/suricata/src/pkt-var.h33
-rw-r--r--framework/src/suricata/src/ptxdump.py71
-rw-r--r--framework/src/suricata/src/queue.h543
-rw-r--r--framework/src/suricata/src/reputation.c2354
-rw-r--r--framework/src/suricata/src/reputation.h123
-rw-r--r--framework/src/suricata/src/respond-reject-libnet11.c541
-rw-r--r--framework/src/suricata/src/respond-reject-libnet11.h33
-rw-r--r--framework/src/suricata/src/respond-reject.c180
-rw-r--r--framework/src/suricata/src/respond-reject.h35
-rw-r--r--framework/src/suricata/src/runmode-af-packet.c590
-rw-r--r--framework/src/suricata/src/runmode-af-packet.h33
-rw-r--r--framework/src/suricata/src/runmode-erf-dag.c150
-rw-r--r--framework/src/suricata/src/runmode-erf-dag.h32
-rw-r--r--framework/src/suricata/src/runmode-erf-file.c279
-rw-r--r--framework/src/suricata/src/runmode-erf-file.h31
-rw-r--r--framework/src/suricata/src/runmode-ipfw.c104
-rw-r--r--framework/src/suricata/src/runmode-ipfw.h31
-rw-r--r--framework/src/suricata/src/runmode-napatech.c235
-rw-r--r--framework/src/suricata/src/runmode-napatech.h37
-rw-r--r--framework/src/suricata/src/runmode-netmap.c459
-rw-r--r--framework/src/suricata/src/runmode-netmap.h33
-rw-r--r--framework/src/suricata/src/runmode-nflog.c254
-rw-r--r--framework/src/suricata/src/runmode-nflog.h32
-rw-r--r--framework/src/suricata/src/runmode-nfq.c100
-rw-r--r--framework/src/suricata/src/runmode-nfq.h31
-rw-r--r--framework/src/suricata/src/runmode-pcap-file.c281
-rw-r--r--framework/src/suricata/src/runmode-pcap-file.h31
-rw-r--r--framework/src/suricata/src/runmode-pcap.c328
-rw-r--r--framework/src/suricata/src/runmode-pcap.h31
-rw-r--r--framework/src/suricata/src/runmode-pfring.c521
-rw-r--r--framework/src/suricata/src/runmode-pfring.h34
-rw-r--r--framework/src/suricata/src/runmode-tile.c287
-rw-r--r--framework/src/suricata/src/runmode-tile.h41
-rw-r--r--framework/src/suricata/src/runmode-unittests.c311
-rw-r--r--framework/src/suricata/src/runmode-unittests.h29
-rw-r--r--framework/src/suricata/src/runmode-unix-socket.c838
-rw-r--r--framework/src/suricata/src/runmode-unix-socket.h42
-rw-r--r--framework/src/suricata/src/runmodes.c927
-rw-r--r--framework/src/suricata/src/runmodes.h100
-rw-r--r--framework/src/suricata/src/source-af-packet.c1919
-rw-r--r--framework/src/suricata/src/source-af-packet.h137
-rw-r--r--framework/src/suricata/src/source-erf-dag.c670
-rw-r--r--framework/src/suricata/src/source-erf-dag.h32
-rw-r--r--framework/src/suricata/src/source-erf-file.c308
-rw-r--r--framework/src/suricata/src/source-erf-file.h30
-rw-r--r--framework/src/suricata/src/source-ipfw.c796
-rw-r--r--framework/src/suricata/src/source-ipfw.h70
-rw-r--r--framework/src/suricata/src/source-mpipe.c1095
-rw-r--r--framework/src/suricata/src/source-mpipe.h93
-rw-r--r--framework/src/suricata/src/source-napatech.c402
-rw-r--r--framework/src/suricata/src/source-napatech.h44
-rw-r--r--framework/src/suricata/src/source-netmap.c1098
-rw-r--r--framework/src/suricata/src/source-netmap.h73
-rw-r--r--framework/src/suricata/src/source-nflog.c550
-rw-r--r--framework/src/suricata/src/source-nflog.h66
-rw-r--r--framework/src/suricata/src/source-nfq-prototypes.h32
-rw-r--r--framework/src/suricata/src/source-nfq.c1277
-rw-r--r--framework/src/suricata/src/source-nfq.h110
-rw-r--r--framework/src/suricata/src/source-pcap-file.c475
-rw-r--r--framework/src/suricata/src/source-pcap-file.h35
-rw-r--r--framework/src/suricata/src/source-pcap.c826
-rw-r--r--framework/src/suricata/src/source-pcap.h70
-rw-r--r--framework/src/suricata/src/source-pfring.c660
-rw-r--r--framework/src/suricata/src/source-pfring.h64
-rw-r--r--framework/src/suricata/src/stream-tcp-inline.c657
-rw-r--r--framework/src/suricata/src/stream-tcp-inline.h36
-rw-r--r--framework/src/suricata/src/stream-tcp-private.h246
-rw-r--r--framework/src/suricata/src/stream-tcp-reassemble.c8717
-rw-r--r--framework/src/suricata/src/stream-tcp-reassemble.h110
-rw-r--r--framework/src/suricata/src/stream-tcp-sack.c960
-rw-r--r--framework/src/suricata/src/stream-tcp-sack.h63
-rw-r--r--framework/src/suricata/src/stream-tcp-util.c264
-rw-r--r--framework/src/suricata/src/stream-tcp-util.h47
-rw-r--r--framework/src/suricata/src/stream-tcp.c10772
-rw-r--r--framework/src/suricata/src/stream-tcp.h232
-rw-r--r--framework/src/suricata/src/stream.c290
-rw-r--r--framework/src/suricata/src/stream.h79
-rw-r--r--framework/src/suricata/src/suricata-common.h343
-rw-r--r--framework/src/suricata/src/suricata.c2543
-rw-r--r--framework/src/suricata/src/suricata.h197
-rw-r--r--framework/src/suricata/src/threads-arch-tile.h114
-rw-r--r--framework/src/suricata/src/threads-debug.h389
-rw-r--r--framework/src/suricata/src/threads-profile.h218
-rw-r--r--framework/src/suricata/src/threads.c151
-rw-r--r--framework/src/suricata/src/threads.h300
-rw-r--r--framework/src/suricata/src/threadvars.h127
-rw-r--r--framework/src/suricata/src/tm-modules.c282
-rw-r--r--framework/src/suricata/src/tm-modules.h97
-rw-r--r--framework/src/suricata/src/tm-queuehandlers.c67
-rw-r--r--framework/src/suricata/src/tm-queuehandlers.h56
-rw-r--r--framework/src/suricata/src/tm-queues.c126
-rw-r--r--framework/src/suricata/src/tm-queues.h44
-rw-r--r--framework/src/suricata/src/tm-threads-common.h132
-rw-r--r--framework/src/suricata/src/tm-threads.c2179
-rw-r--r--framework/src/suricata/src/tm-threads.h203
-rw-r--r--framework/src/suricata/src/tmqh-flow.c510
-rw-r--r--framework/src/suricata/src/tmqh-flow.h48
-rw-r--r--framework/src/suricata/src/tmqh-nfq.c55
-rw-r--r--framework/src/suricata/src/tmqh-nfq.h29
-rw-r--r--framework/src/suricata/src/tmqh-packetpool.c565
-rw-r--r--framework/src/suricata/src/tmqh-packetpool.h82
-rw-r--r--framework/src/suricata/src/tmqh-ringbuffer.c151
-rw-r--r--framework/src/suricata/src/tmqh-ringbuffer.h30
-rw-r--r--framework/src/suricata/src/tmqh-simple.c155
-rw-r--r--framework/src/suricata/src/tmqh-simple.h34
-rw-r--r--framework/src/suricata/src/unix-manager.c1030
-rw-r--r--framework/src/suricata/src/unix-manager.h51
-rw-r--r--framework/src/suricata/src/util-action.c1627
-rw-r--r--framework/src/suricata/src/util-action.h31
-rw-r--r--framework/src/suricata/src/util-affinity.c323
-rw-r--r--framework/src/suricata/src/util-affinity.h94
-rw-r--r--framework/src/suricata/src/util-atomic.c73
-rw-r--r--framework/src/suricata/src/util-atomic.h476
-rw-r--r--framework/src/suricata/src/util-base64.c146
-rw-r--r--framework/src/suricata/src/util-base64.h54
-rw-r--r--framework/src/suricata/src/util-binsearch.c112
-rw-r--r--framework/src/suricata/src/util-binsearch.h32
-rw-r--r--framework/src/suricata/src/util-bloomfilter-counting.c410
-rw-r--r--framework/src/suricata/src/util-bloomfilter-counting.h47
-rw-r--r--framework/src/suricata/src/util-bloomfilter.c290
-rw-r--r--framework/src/suricata/src/util-bloomfilter.h67
-rw-r--r--framework/src/suricata/src/util-buffer.c87
-rw-r--r--framework/src/suricata/src/util-buffer.h177
-rw-r--r--framework/src/suricata/src/util-byte.c629
-rw-r--r--framework/src/suricata/src/util-byte.h292
-rw-r--r--framework/src/suricata/src/util-checksum.c90
-rw-r--r--framework/src/suricata/src/util-checksum.h36
-rw-r--r--framework/src/suricata/src/util-cidr.c48
-rw-r--r--framework/src/suricata/src/util-cidr.h31
-rw-r--r--framework/src/suricata/src/util-classification-config.c839
-rw-r--r--framework/src/suricata/src/util-classification-config.h62
-rw-r--r--framework/src/suricata/src/util-clock.h40
-rw-r--r--framework/src/suricata/src/util-conf.c65
-rw-r--r--framework/src/suricata/src/util-conf.h32
-rw-r--r--framework/src/suricata/src/util-coredump-config.c206
-rw-r--r--framework/src/suricata/src/util-coredump-config.h31
-rw-r--r--framework/src/suricata/src/util-cpu.c231
-rw-r--r--framework/src/suricata/src/util-cpu.h39
-rw-r--r--framework/src/suricata/src/util-crypt.c306
-rw-r--r--framework/src/suricata/src/util-crypt.h84
-rw-r--r--framework/src/suricata/src/util-cuda-buffer.c1358
-rw-r--r--framework/src/suricata/src/util-cuda-buffer.h111
-rw-r--r--framework/src/suricata/src/util-cuda-handlers.c363
-rw-r--r--framework/src/suricata/src/util-cuda-handlers.h50
-rw-r--r--framework/src/suricata/src/util-cuda-vars.c74
-rw-r--r--framework/src/suricata/src/util-cuda-vars.h65
-rw-r--r--framework/src/suricata/src/util-cuda.c5455
-rw-r--r--framework/src/suricata/src/util-cuda.h323
-rw-r--r--framework/src/suricata/src/util-daemon.c195
-rw-r--r--framework/src/suricata/src/util-daemon.h38
-rw-r--r--framework/src/suricata/src/util-debug-filters.c1009
-rw-r--r--framework/src/suricata/src/util-debug-filters.h136
-rw-r--r--framework/src/suricata/src/util-debug.c1653
-rw-r--r--framework/src/suricata/src/util-debug.h551
-rw-r--r--framework/src/suricata/src/util-decode-asn1.c904
-rw-r--r--framework/src/suricata/src/util-decode-asn1.h220
-rw-r--r--framework/src/suricata/src/util-decode-der-get.c278
-rw-r--r--framework/src/suricata/src/util-decode-der-get.h43
-rw-r--r--framework/src/suricata/src/util-decode-der.c787
-rw-r--r--framework/src/suricata/src/util-decode-der.h96
-rw-r--r--framework/src/suricata/src/util-decode-mime.c2887
-rw-r--r--framework/src/suricata/src/util-decode-mime.h240
-rw-r--r--framework/src/suricata/src/util-device.c273
-rw-r--r--framework/src/suricata/src/util-device.h49
-rw-r--r--framework/src/suricata/src/util-enum.c84
-rw-r--r--framework/src/suricata/src/util-enum.h36
-rw-r--r--framework/src/suricata/src/util-error.c315
-rw-r--r--framework/src/suricata/src/util-error.h306
-rw-r--r--framework/src/suricata/src/util-file.c932
-rw-r--r--framework/src/suricata/src/util-file.h192
-rw-r--r--framework/src/suricata/src/util-fix_checksum.c58
-rw-r--r--framework/src/suricata/src/util-fix_checksum.h37
-rw-r--r--framework/src/suricata/src/util-fmemopen.c198
-rw-r--r--framework/src/suricata/src/util-fmemopen.h55
-rw-r--r--framework/src/suricata/src/util-hash-lookup3.c999
-rw-r--r--framework/src/suricata/src/util-hash-lookup3.h63
-rw-r--r--framework/src/suricata/src/util-hash.c432
-rw-r--r--framework/src/suricata/src/util-hash.h61
-rw-r--r--framework/src/suricata/src/util-hashlist.c518
-rw-r--r--framework/src/suricata/src/util-hashlist.h65
-rw-r--r--framework/src/suricata/src/util-host-info.c106
-rw-r--r--framework/src/suricata/src/util-host-info.h29
-rw-r--r--framework/src/suricata/src/util-host-os-info.c1656
-rw-r--r--framework/src/suricata/src/util-host-os-info.h38
-rw-r--r--framework/src/suricata/src/util-ioctl.c248
-rw-r--r--framework/src/suricata/src/util-ioctl.h27
-rw-r--r--framework/src/suricata/src/util-ip.c112
-rw-r--r--framework/src/suricata/src/util-ip.h32
-rw-r--r--framework/src/suricata/src/util-logopenfile-tile.c370
-rw-r--r--framework/src/suricata/src/util-logopenfile-tile.h32
-rw-r--r--framework/src/suricata/src/util-logopenfile.c383
-rw-r--r--framework/src/suricata/src/util-logopenfile.h99
-rw-r--r--framework/src/suricata/src/util-lua-common.c772
-rw-r--r--framework/src/suricata/src/util-lua-common.h42
-rw-r--r--framework/src/suricata/src/util-lua-dns.c321
-rw-r--r--framework/src/suricata/src/util-lua-dns.h33
-rw-r--r--framework/src/suricata/src/util-lua-http.c348
-rw-r--r--framework/src/suricata/src/util-lua-http.h33
-rw-r--r--framework/src/suricata/src/util-lua-tls.c145
-rw-r--r--framework/src/suricata/src/util-lua-tls.h33
-rw-r--r--framework/src/suricata/src/util-lua.c276
-rw-r--r--framework/src/suricata/src/util-lua.h95
-rw-r--r--framework/src/suricata/src/util-magic.c676
-rw-r--r--framework/src/suricata/src/util-magic.h35
-rw-r--r--framework/src/suricata/src/util-mem.h313
-rw-r--r--framework/src/suricata/src/util-memcmp.c407
-rw-r--r--framework/src/suricata/src/util-memcmp.h502
-rw-r--r--framework/src/suricata/src/util-memcpy.h47
-rw-r--r--framework/src/suricata/src/util-memrchr.c67
-rw-r--r--framework/src/suricata/src/util-memrchr.h30
-rw-r--r--framework/src/suricata/src/util-misc.c1141
-rw-r--r--framework/src/suricata/src/util-misc.h55
-rw-r--r--framework/src/suricata/src/util-mpm-ac-bs.c2802
-rw-r--r--framework/src/suricata/src/util-mpm-ac-bs.h101
-rw-r--r--framework/src/suricata/src/util-mpm-ac-cuda-kernel.cu96
-rw-r--r--framework/src/suricata/src/util-mpm-ac-gfbs.c2722
-rw-r--r--framework/src/suricata/src/util-mpm-ac-gfbs.h110
-rw-r--r--framework/src/suricata/src/util-mpm-ac-tile-small.c100
-rw-r--r--framework/src/suricata/src/util-mpm-ac-tile.c2855
-rw-r--r--framework/src/suricata/src/util-mpm-ac-tile.h174
-rw-r--r--framework/src/suricata/src/util-mpm-ac.c3341
-rw-r--r--framework/src/suricata/src/util-mpm-ac.h221
-rw-r--r--framework/src/suricata/src/util-mpm-b2g.c2832
-rw-r--r--framework/src/suricata/src/util-mpm-b2g.h127
-rw-r--r--framework/src/suricata/src/util-mpm-b3g.c1807
-rw-r--r--framework/src/suricata/src/util-mpm-b3g.h130
-rw-r--r--framework/src/suricata/src/util-mpm-wumanber.c3219
-rw-r--r--framework/src/suricata/src/util-mpm-wumanber.h97
-rw-r--r--framework/src/suricata/src/util-mpm.c785
-rw-r--r--framework/src/suricata/src/util-mpm.h324
-rw-r--r--framework/src/suricata/src/util-optimize.h52
-rw-r--r--framework/src/suricata/src/util-path.c66
-rw-r--r--framework/src/suricata/src/util-path.h31
-rw-r--r--framework/src/suricata/src/util-pidfile.c127
-rw-r--r--framework/src/suricata/src/util-pidfile.h33
-rw-r--r--framework/src/suricata/src/util-pool-thread.c458
-rw-r--r--framework/src/suricata/src/util-pool-thread.h100
-rw-r--r--framework/src/suricata/src/util-pool.c737
-rw-r--r--framework/src/suricata/src/util-pool.h87
-rw-r--r--framework/src/suricata/src/util-print.c272
-rw-r--r--framework/src/suricata/src/util-print.h58
-rw-r--r--framework/src/suricata/src/util-privs.c246
-rw-r--r--framework/src/suricata/src/util-privs.h98
-rw-r--r--framework/src/suricata/src/util-profiling-keywords.c390
-rw-r--r--framework/src/suricata/src/util-profiling-locks.c241
-rw-r--r--framework/src/suricata/src/util-profiling-locks.h45
-rw-r--r--framework/src/suricata/src/util-profiling-rules.c593
-rw-r--r--framework/src/suricata/src/util-profiling.c1160
-rw-r--r--framework/src/suricata/src/util-profiling.h282
-rw-r--r--framework/src/suricata/src/util-proto-name.c116
-rw-r--r--framework/src/suricata/src/util-proto-name.h42
-rw-r--r--framework/src/suricata/src/util-radix-tree.c4228
-rw-r--r--framework/src/suricata/src/util-radix-tree.h135
-rw-r--r--framework/src/suricata/src/util-random.c48
-rw-r--r--framework/src/suricata/src/util-random.h30
-rw-r--r--framework/src/suricata/src/util-reference-config.c808
-rw-r--r--framework/src/suricata/src/util-reference-config.h53
-rw-r--r--framework/src/suricata/src/util-ringbuffer.c1088
-rw-r--r--framework/src/suricata/src/util-ringbuffer.h136
-rw-r--r--framework/src/suricata/src/util-rohash.c249
-rw-r--r--framework/src/suricata/src/util-rohash.h48
-rw-r--r--framework/src/suricata/src/util-rule-vars.c532
-rw-r--r--framework/src/suricata/src/util-rule-vars.h36
-rw-r--r--framework/src/suricata/src/util-runmodes.c751
-rw-r--r--framework/src/suricata/src/util-runmodes.h69
-rw-r--r--framework/src/suricata/src/util-running-modes.c62
-rw-r--r--framework/src/suricata/src/util-running-modes.h33
-rw-r--r--framework/src/suricata/src/util-signal.c67
-rw-r--r--framework/src/suricata/src/util-signal.h31
-rw-r--r--framework/src/suricata/src/util-spm-bm.c382
-rw-r--r--framework/src/suricata/src/util-spm-bm.h49
-rw-r--r--framework/src/suricata/src/util-spm-bs.c139
-rw-r--r--framework/src/suricata/src/util-spm-bs.h36
-rw-r--r--framework/src/suricata/src/util-spm-bs2bm.c176
-rw-r--r--framework/src/suricata/src/util-spm-bs2bm.h38
-rw-r--r--framework/src/suricata/src/util-spm.c2368
-rw-r--r--framework/src/suricata/src/util-spm.h61
-rw-r--r--framework/src/suricata/src/util-storage.c545
-rw-r--r--framework/src/suricata/src/util-storage.h75
-rw-r--r--framework/src/suricata/src/util-strlcatu.c79
-rw-r--r--framework/src/suricata/src/util-strlcpyu.c75
-rw-r--r--framework/src/suricata/src/util-syslog.c78
-rw-r--r--framework/src/suricata/src/util-syslog.h31
-rw-r--r--framework/src/suricata/src/util-threshold-config.c2909
-rw-r--r--framework/src/suricata/src/util-threshold-config.h33
-rw-r--r--framework/src/suricata/src/util-time.c304
-rw-r--r--framework/src/suricata/src/util-time.h54
-rw-r--r--framework/src/suricata/src/util-unittest-helper.c1081
-rw-r--r--framework/src/suricata/src/util-unittest-helper.h63
-rw-r--r--framework/src/suricata/src/util-unittest.c326
-rw-r--r--framework/src/suricata/src/util-unittest.h54
-rw-r--r--framework/src/suricata/src/util-validate.h108
-rw-r--r--framework/src/suricata/src/util-var-name.c204
-rw-r--r--framework/src/suricata/src/util-var-name.h34
-rw-r--r--framework/src/suricata/src/util-var.c130
-rw-r--r--framework/src/suricata/src/util-var.h65
-rw-r--r--framework/src/suricata/src/util-vector.h43
-rw-r--r--framework/src/suricata/src/win32-misc.c108
-rw-r--r--framework/src/suricata/src/win32-misc.h44
-rw-r--r--framework/src/suricata/src/win32-service.c394
-rw-r--r--framework/src/suricata/src/win32-service.h35
-rw-r--r--framework/src/suricata/src/win32-syslog.h80
809 files changed, 468783 insertions, 0 deletions
diff --git a/framework/src/suricata/src/Makefile.am b/framework/src/suricata/src/Makefile.am
new file mode 100644
index 00000000..de99d312
--- /dev/null
+++ b/framework/src/suricata/src/Makefile.am
@@ -0,0 +1,495 @@
+noinst_HEADERS = action-globals.h \
+ app-layer-nbss.h app-layer-dcerpc-common.h \
+ debug.h \
+ flow-private.h queue.h source-nfq-prototypes.h \
+ suricata-common.h threadvars.h util-binsearch.h \
+ util-validate.h
+bin_PROGRAMS = suricata
+
+suricata_SOURCES = \
+alert-debuglog.c alert-debuglog.h \
+alert-fastlog.c alert-fastlog.h \
+alert-prelude.c alert-prelude.h \
+alert-syslog.c alert-syslog.h \
+alert-unified2-alert.c alert-unified2-alert.h \
+app-layer.c app-layer.h \
+app-layer-dcerpc.c app-layer-dcerpc.h \
+app-layer-dcerpc-udp.c app-layer-dcerpc-udp.h \
+app-layer-detect-proto.c app-layer-detect-proto.h \
+app-layer-dns-common.c app-layer-dns-common.h \
+app-layer-dns-tcp.c app-layer-dns-tcp.h \
+app-layer-dns-udp.c app-layer-dns-udp.h \
+app-layer-events.c app-layer-events.h \
+app-layer-ftp.c app-layer-ftp.h \
+app-layer-htp-body.c app-layer-htp-body.h \
+app-layer-htp.c app-layer-htp.h \
+app-layer-htp-file.c app-layer-htp-file.h \
+app-layer-htp-libhtp.c app-layer-htp-libhtp.h \
+app-layer-htp-mem.c app-layer-htp-mem.h \
+app-layer-htp-xff.c app-layer-htp-xff.h \
+app-layer-modbus.c app-layer-modbus.h \
+app-layer-parser.c app-layer-parser.h \
+app-layer-protos.c app-layer-protos.h \
+app-layer-smb2.c app-layer-smb2.h \
+app-layer-smb.c app-layer-smb.h \
+app-layer-smtp.c app-layer-smtp.h \
+app-layer-ssh.c app-layer-ssh.h \
+app-layer-ssl.c app-layer-ssl.h \
+app-layer-tls-handshake.c app-layer-tls-handshake.h \
+conf.c conf.h \
+conf-yaml-loader.c conf-yaml-loader.h \
+counters.c counters.h \
+data-queue.c data-queue.h \
+decode.c decode.h \
+decode-erspan.c decode-erspan.h \
+decode-ethernet.c decode-ethernet.h \
+decode-events.c decode-events.h \
+decode-gre.c decode-gre.h \
+decode-icmpv4.c decode-icmpv4.h \
+decode-icmpv6.c decode-icmpv6.h \
+decode-ipv4.c decode-ipv4.h \
+decode-ipv6.c decode-ipv6.h \
+decode-null.c decode-null.h \
+decode-ppp.c decode-ppp.h \
+decode-pppoe.c decode-pppoe.h \
+decode-raw.c decode-raw.h \
+decode-sctp.c decode-sctp.h \
+decode-sll.c decode-sll.h \
+decode-tcp.c decode-tcp.h \
+decode-teredo.c decode-teredo.h \
+decode-udp.c decode-udp.h \
+decode-vlan.c decode-vlan.h \
+decode-mpls.c decode-mpls.h \
+decode-template.c decode-template.h \
+defrag-config.c defrag-config.h \
+defrag.c defrag.h \
+defrag-hash.c defrag-hash.h \
+defrag-queue.c defrag-queue.h \
+defrag-timeout.c defrag-timeout.h \
+detect-ack.c detect-ack.h \
+detect-app-layer-event.c detect-app-layer-event.h \
+detect-app-layer-protocol.c detect-app-layer-protocol.h \
+detect-asn1.c detect-asn1.h \
+detect-byte-extract.c detect-byte-extract.h \
+detect-bytejump.c detect-bytejump.h \
+detect-bytetest.c detect-bytetest.h \
+detect.c detect.h \
+detect-classtype.c detect-classtype.h \
+detect-content.c detect-content.h \
+detect-csum.c detect-csum.h \
+detect-dce-iface.c detect-dce-iface.h \
+detect-dce-opnum.c detect-dce-opnum.h \
+detect-dce-stub-data.c detect-dce-stub-data.h \
+detect-depth.c detect-depth.h \
+detect-detection-filter.c detect-detection-filter.h \
+detect-distance.c detect-distance.h \
+detect-dns-query.c detect-dns-query.h \
+detect-dsize.c detect-dsize.h \
+detect-engine-address.c detect-engine-address.h \
+detect-engine-address-ipv4.c detect-engine-address-ipv4.h \
+detect-engine-address-ipv6.c detect-engine-address-ipv6.h \
+detect-engine-alert.c detect-engine-alert.h \
+detect-engine-analyzer.c detect-engine-analyzer.h \
+detect-engine-apt-event.c detect-engine-apt-event.h \
+detect-engine.c detect-engine.h \
+detect-engine-content-inspection.c detect-engine-content-inspection.h \
+detect-engine-dcepayload.c detect-engine-dcepayload.h \
+detect-engine-dns.c detect-engine-dns.h \
+detect-engine-modbus.c detect-engine-modbus.h \
+detect-engine-event.c detect-engine-event.h \
+detect-engine-file.c detect-engine-file.h \
+detect-engine-filedata-smtp.c detect-engine-filedata-smtp.h \
+detect-engine-hcbd.c detect-engine-hcbd.h \
+detect-engine-hcd.c detect-engine-hcd.h \
+detect-engine-hhd.c detect-engine-hhd.h \
+detect-engine-hhhd.c detect-engine-hhhd.h \
+detect-engine-hmd.c detect-engine-hmd.h \
+detect-engine-hrhd.c detect-engine-hrhd.h \
+detect-engine-hrhhd.c detect-engine-hrhhd.h \
+detect-engine-hrud.c detect-engine-hrud.h \
+detect-engine-hrl.c detect-engine-hrl.h \
+detect-engine-hsbd.c detect-engine-hsbd.h \
+detect-engine-hscd.c detect-engine-hscd.h \
+detect-engine-hsmd.c detect-engine-hsmd.h \
+detect-engine-hua.c detect-engine-hua.h \
+detect-engine-iponly.c detect-engine-iponly.h \
+detect-engine-loader.c detect-engine-loader.h \
+detect-engine-mpm.c detect-engine-mpm.h \
+detect-engine-payload.c detect-engine-payload.h \
+detect-engine-port.c detect-engine-port.h \
+detect-engine-proto.c detect-engine-proto.h \
+detect-engine-siggroup.c detect-engine-siggroup.h \
+detect-engine-sigorder.c detect-engine-sigorder.h \
+detect-engine-state.c detect-engine-state.h \
+detect-engine-tag.c detect-engine-tag.h \
+detect-engine-threshold.c detect-engine-threshold.h \
+detect-engine-uri.c detect-engine-uri.h \
+detect-fast-pattern.c detect-fast-pattern.h \
+detect-file-data.c detect-file-data.h \
+detect-fileext.c detect-fileext.h \
+detect-filemagic.c detect-filemagic.h \
+detect-filemd5.c detect-filemd5.h \
+detect-filename.c detect-filename.h \
+detect-filesize.c detect-filesize.h \
+detect-filestore.c detect-filestore.h \
+detect-flags.c detect-flags.h \
+detect-flowbits.c detect-flowbits.h \
+detect-flow.c detect-flow.h \
+detect-flowint.c detect-flowint.h \
+detect-flowvar.c detect-flowvar.h \
+detect-fragbits.c detect-fragbits.h \
+detect-fragoffset.c detect-fragoffset.h \
+detect-ftpbounce.c detect-ftpbounce.h \
+detect-geoip.c detect-geoip.h \
+detect-gid.c detect-gid.h \
+detect-hostbits.c detect-hostbits.h \
+detect-http-client-body.c detect-http-client-body.h \
+detect-http-cookie.c detect-http-cookie.h \
+detect-http-header.c detect-http-header.h \
+detect-http-hh.c detect-http-hh.h \
+detect-http-hrh.c detect-http-hrh.h \
+detect-http-method.c detect-http-method.h \
+detect-http-raw-header.c detect-http-raw-header.h \
+detect-http-raw-uri.c detect-http-raw-uri.h \
+detect-http-server-body.c detect-http-server-body.h \
+detect-http-stat-code.c detect-http-stat-code.h \
+detect-http-stat-msg.c detect-http-stat-msg.h \
+detect-http-ua.c detect-http-ua.h \
+detect-http-uri.c detect-http-uri.h \
+detect-icmp-id.c detect-icmp-id.h \
+detect-icmp-seq.c detect-icmp-seq.h \
+detect-icode.c detect-icode.h \
+detect-id.c detect-id.h \
+detect-ipopts.c detect-ipopts.h \
+detect-ipproto.c detect-ipproto.h \
+detect-iprep.c detect-iprep.h \
+detect-isdataat.c detect-isdataat.h \
+detect-itype.c detect-itype.h \
+detect-l3proto.c detect-l3proto.h \
+detect-lua.c detect-lua.h \
+detect-lua-extensions.c detect-lua-extensions.h \
+detect-mark.c detect-mark.h \
+detect-metadata.c detect-metadata.h \
+detect-msg.c detect-msg.h \
+detect-noalert.c detect-noalert.h \
+detect-nocase.c detect-nocase.h \
+detect-offset.c detect-offset.h \
+detect-parse.c detect-parse.h \
+detect-pcre.c detect-pcre.h \
+detect-pkt-data.c detect-pkt-data.h \
+detect-pktvar.c detect-pktvar.h \
+detect-priority.c detect-priority.h \
+detect-rawbytes.c detect-rawbytes.h \
+detect-reference.c detect-reference.h \
+detect-replace.c detect-replace.h \
+detect-rev.c detect-rev.h \
+detect-rpc.c detect-rpc.h \
+detect-sameip.c detect-sameip.h \
+detect-seq.c detect-seq.h \
+detect-sid.c detect-sid.h \
+detect-ssh-proto-version.c detect-ssh-proto-version.h \
+detect-ssh-software-version.c detect-ssh-software-version.h \
+detect-ssl-state.c detect-ssl-state.h \
+detect-ssl-version.c detect-ssl-version.h \
+detect-stream_size.c detect-stream_size.h \
+detect-tag.c detect-tag.h \
+detect-template.c detect-template.h \
+detect-threshold.c detect-threshold.h \
+detect-tls.c detect-tls.h \
+detect-tls-version.c detect-tls-version.h \
+detect-tos.c detect-tos.h \
+detect-ttl.c detect-ttl.h \
+detect-uricontent.c detect-uricontent.h \
+detect-urilen.c detect-urilen.h \
+detect-window.c detect-window.h \
+detect-within.c detect-within.h \
+detect-modbus.c detect-modbus.h \
+detect-xbits.c detect-xbits.h \
+flow-bit.c flow-bit.h \
+flow.c flow.h \
+flow-hash.c flow-hash.h \
+flow-manager.c flow-manager.h \
+flow-queue.c flow-queue.h \
+flow-storage.c flow-storage.h \
+flow-timeout.c flow-timeout.h \
+flow-util.c flow-util.h \
+flow-var.c flow-var.h \
+host.c host.h \
+host-bit.c host-bit.h \
+host-queue.c host-queue.h \
+host-storage.c host-storage.h \
+host-timeout.c host-timeout.h \
+ippair.c ippair.h \
+ippair-bit.c ippair-bit.h \
+ippair-queue.c ippair-queue.h \
+ippair-storage.c ippair-storage.h \
+ippair-timeout.c ippair-timeout.h \
+log-dnslog.c log-dnslog.h \
+log-droplog.c log-droplog.h \
+log-file.c log-file.h \
+log-filestore.c log-filestore.h \
+log-httplog.c log-httplog.h \
+log-pcap.c log-pcap.h \
+log-stats.c log-stats.h \
+log-tcp-data.c log-tcp-data.h \
+log-tlslog.c log-tlslog.h \
+log-tlsstore.c log-tlsstore.h \
+output.c output.h \
+output-file.c output-file.h \
+output-filedata.c output-filedata.h \
+output-flow.c output-flow.h \
+output-json-alert.c output-json-alert.h \
+output-json-dns.c output-json-dns.h \
+output-json-drop.c output-json-drop.h \
+output-json-email-common.c output-json-email-common.h \
+output-json-file.c output-json-file.h \
+output-json-flow.c output-json-flow.h \
+output-json-netflow.c output-json-netflow.h \
+output-json-http.c output-json-http.h \
+output-json-smtp.c output-json-smtp.h \
+output-json-ssh.c output-json-ssh.h \
+output-json-stats.c output-json-stats.h \
+output-json-tls.c output-json-tls.h \
+output-lua.c output-lua.h \
+output-packet.c output-packet.h \
+output-stats.c output-stats.h \
+output-streaming.c output-streaming.h \
+output-tx.c output-tx.h \
+output-json.c output-json.h \
+packet-queue.c packet-queue.h \
+pkt-var.c pkt-var.h \
+reputation.c reputation.h \
+respond-reject.c respond-reject.h \
+respond-reject-libnet11.h respond-reject-libnet11.c \
+runmode-af-packet.c runmode-af-packet.h \
+runmode-erf-dag.c runmode-erf-dag.h \
+runmode-erf-file.c runmode-erf-file.h \
+runmode-ipfw.c runmode-ipfw.h \
+runmode-napatech.c runmode-napatech.h \
+runmode-netmap.c runmode-netmap.h \
+runmode-nfq.c runmode-nfq.h \
+runmode-nflog.c runmode-nflog.h \
+runmode-pcap.c runmode-pcap.h \
+runmode-pcap-file.c runmode-pcap-file.h \
+runmode-pfring.c runmode-pfring.h \
+runmode-unittests.c runmode-unittests.h \
+runmode-unix-socket.c runmode-unix-socket.h \
+runmode-tile.c runmode-tile.h \
+runmodes.c runmodes.h \
+source-af-packet.c source-af-packet.h \
+source-erf-dag.c source-erf-dag.h \
+source-erf-file.c source-erf-file.h \
+source-ipfw.c source-ipfw.h \
+source-mpipe.c source-mpipe.h \
+source-napatech.c source-napatech.h \
+source-netmap.c source-netmap.h \
+source-nfq.c source-nfq.h \
+source-nflog.c source-nflog.h \
+source-pcap.c source-pcap.h \
+source-pcap-file.c source-pcap-file.h \
+source-pfring.c source-pfring.h \
+stream.c stream.h \
+stream-tcp.c stream-tcp.h stream-tcp-private.h \
+stream-tcp-inline.c stream-tcp-inline.h \
+stream-tcp-reassemble.c stream-tcp-reassemble.h \
+stream-tcp-sack.c stream-tcp-sack.h \
+stream-tcp-util.c stream-tcp-util.h \
+suricata.c suricata.h \
+threads.c threads.h threads-arch-tile.h \
+threads-debug.h threads-profile.h \
+tm-modules.c tm-modules.h \
+tmqh-flow.c tmqh-flow.h \
+tmqh-nfq.c tmqh-nfq.h \
+tmqh-packetpool.c tmqh-packetpool.h \
+tmqh-ringbuffer.c tmqh-ringbuffer.h \
+tmqh-simple.c tmqh-simple.h \
+tm-queuehandlers.c tm-queuehandlers.h \
+tm-queues.c tm-queues.h \
+tm-threads.c tm-threads.h tm-threads-common.h \
+unix-manager.c unix-manager.h \
+util-action.c util-action.h \
+util-atomic.c util-atomic.h \
+util-base64.c util-base64.h \
+util-bloomfilter-counting.c util-bloomfilter-counting.h \
+util-bloomfilter.c util-bloomfilter.h \
+util-buffer.c util-buffer.h \
+util-byte.c util-byte.h \
+util-checksum.c util-checksum.h \
+util-cidr.c util-cidr.h \
+util-classification-config.c util-classification-config.h \
+util-conf.c util-conf.h \
+util-coredump-config.c util-coredump-config.h \
+util-cpu.c util-cpu.h \
+util-crypt.c util-crypt.h \
+util-cuda.c util-cuda.h \
+util-cuda-buffer.c util-cuda-buffer.h \
+util-cuda-handlers.c util-cuda-handlers.h \
+util-cuda-vars.c util-cuda-vars.h \
+util-daemon.c util-daemon.h \
+util-debug.c util-debug.h \
+util-debug-filters.c util-debug-filters.h \
+util-decode-asn1.c util-decode-asn1.h \
+util-decode-der.c util-decode-der.h \
+util-decode-der-get.c util-decode-der-get.h \
+util-decode-mime.c util-decode-mime.h \
+util-device.c util-device.h \
+util-enum.c util-enum.h \
+util-error.c util-error.h \
+util-file.c util-file.h \
+util-fix_checksum.c util-fix_checksum.h \
+util-fmemopen.c util-fmemopen.h \
+util-hash.c util-hash.h \
+util-hashlist.c util-hashlist.h \
+util-hash-lookup3.c util-hash-lookup3.h \
+util-host-os-info.c util-host-os-info.h \
+util-host-info.c util-host-info.h \
+util-ioctl.h util-ioctl.c \
+util-ip.h util-ip.c \
+util-logopenfile.h util-logopenfile.c \
+util-logopenfile-tile.h util-logopenfile-tile.c \
+util-lua.c util-lua.h \
+util-lua-common.c util-lua-common.h \
+util-lua-dns.c util-lua-dns.h \
+util-lua-http.c util-lua-http.h \
+util-lua-tls.c util-lua-tls.h \
+util-magic.c util-magic.h \
+util-memcmp.c util-memcmp.h \
+util-memcpy.h \
+util-mem.h \
+util-memrchr.c util-memrchr.h \
+util-misc.c util-misc.h \
+util-mpm-ac-bs.c util-mpm-ac-bs.h \
+util-mpm-ac.c util-mpm-ac.h \
+util-mpm-ac-gfbs.c util-mpm-ac-gfbs.h \
+util-mpm-ac-tile.c util-mpm-ac-tile.h \
+util-mpm-ac-tile-small.c \
+util-mpm-b2g.c util-mpm-b2g.h \
+util-mpm-b3g.c util-mpm-b3g.h \
+util-mpm.c util-mpm.h \
+util-mpm-wumanber.c util-mpm-wumanber.h \
+util-optimize.h \
+util-path.c util-path.h \
+util-pidfile.c util-pidfile.h \
+util-pool.c util-pool.h \
+util-pool-thread.c util-pool-thread.h \
+util-print.c util-print.h \
+util-privs.c util-privs.h \
+util-profiling.c util-profiling.h \
+util-profiling-locks.c util-profiling-locks.h \
+util-profiling-rules.c \
+util-profiling-keywords.c \
+util-proto-name.c util-proto-name.h \
+util-radix-tree.c util-radix-tree.h \
+util-random.c util-random.h \
+util-reference-config.c util-reference-config.h \
+util-ringbuffer.c util-ringbuffer.h \
+util-rohash.c util-rohash.h \
+util-rule-vars.c util-rule-vars.h \
+util-runmodes.c util-runmodes.h \
+util-running-modes.c util-running-modes.h \
+util-signal.c util-signal.h \
+util-spm-bm.c util-spm-bm.h \
+util-spm-bs2bm.c util-spm-bs2bm.h \
+util-spm-bs.c util-spm-bs.h \
+util-spm.c util-spm.h util-clock.h \
+util-storage.c util-storage.h \
+util-strlcatu.c \
+util-strlcpyu.c \
+util-syslog.c util-syslog.h \
+util-threshold-config.c util-threshold-config.h \
+util-time.c util-time.h \
+util-unittest.c util-unittest.h \
+util-unittest-helper.c util-unittest-helper.h \
+util-validate.h util-affinity.h util-affinity.c \
+util-var.c util-var.h \
+util-var-name.c util-var-name.h \
+util-vector.h \
+win32-misc.c win32-misc.h \
+win32-service.c win32-service.h \
+win32-syslog.h
+
+EXTRA_DIST = util-mpm-ac-cuda-kernel.cu ptxdump.py
+
+# set the include path found by configure
+AM_CPPFLAGS = $(all_includes)
+
+# the library search path.
+suricata_LDFLAGS = $(all_libraries)
+suricata_LDADD = $(HTP_LDADD)
+
+# Rules to build CUDA ptx modules
+if BUILD_CUDA
+BUILT_SOURCES = cuda-ptxdump.h
+
+suricata_CUDA_KERNELS = \
+util-mpm-ac-cuda-kernel.cu
+
+NVCCFLAGS=-O2
+
+SUFFIXES = \
+.ptx_sm_10 \
+.ptx_sm_11 \
+.ptx_sm_12 \
+.ptx_sm_13 \
+.ptx_sm_20 \
+.ptx_sm_21 \
+.ptx_sm_30 \
+.ptx_sm_35
+
+PTXS = $(suricata_CUDA_KERNELS:.cu=.ptx_sm_10)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_11)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_12)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_13)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_20)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_21)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_30)
+PTXS += $(suricata_CUDA_KERNELS:.cu=.ptx_sm_35)
+
+.cu.ptx_sm_10:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_10 -ptx $<
+
+.cu.ptx_sm_11:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_11 -ptx $<
+
+.cu.ptx_sm_12:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_12 -ptx $<
+
+.cu.ptx_sm_13:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_13 -ptx $<
+
+.cu.ptx_sm_20:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_20 -ptx $<
+
+.cu.ptx_sm_21:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_21 -ptx $<
+
+.cu.ptx_sm_30:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_30 -ptx $<
+
+.cu.ptx_sm_35:
+ $(NVCC) $(NVCCFLAGS) -o $@ -arch=sm_35 -ptx $<
+
+cuda-ptxdump.h: $(PTXS)
+ $(PYTHON) ptxdump.py cuda-ptxdump $(PTXS)
+
+CLEANFILES = $(PTXS) cuda-ptxdump.h
+endif
+
+# default CFLAGS
+AM_CFLAGS = ${OPTIMIZATION_CFLAGS} ${GCC_CFLAGS} ${CLANG_CFLAGS} ${SECCFLAGS} ${PCAP_CFLAGS} -Wall -Wno-unused-parameter -std=gnu99 -DLOCAL_STATE_DIR=\"$(localstatedir)\"
+# different flags for different cases
+if DEBUG
+AM_CFLAGS += -ggdb -O0
+endif
+
+AM_LDFLAGS = ${SECLDFLAGS}
+
+if BUILD_UNITTESTS
+check-am:
+ -mkdir $(top_builddir)/qa/log/
+ $(top_builddir)/src/suricata -u -l $(top_builddir)/qa/log/
+ -rm -rf $(top_builddir)/qa/log
+endif
+
+distclean-local:
+ -rm -rf $(top_builddir)/src/build-info.h
diff --git a/framework/src/suricata/src/action-globals.h b/framework/src/suricata/src/action-globals.h
new file mode 100644
index 00000000..aa46bd29
--- /dev/null
+++ b/framework/src/suricata/src/action-globals.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __ACTION_GLOBALS_H__
+#define __ACTION_GLOBALS_H__
+
+/* Changing them as flags, so later we can have alerts
+ * and drop simultaneously */
+#define ACTION_ALERT 0x01
+#define ACTION_DROP 0x02
+#define ACTION_REJECT 0x04
+#define ACTION_REJECT_DST 0x08
+#define ACTION_REJECT_BOTH 0x10
+#define ACTION_PASS 0x20
+
+#endif /* __ACTION_GLOBALS_H__ */
diff --git a/framework/src/suricata/src/alert-debuglog.c b/framework/src/suricata/src/alert-debuglog.c
new file mode 100644
index 00000000..5df1f4c1
--- /dev/null
+++ b/framework/src/suricata/src/alert-debuglog.c
@@ -0,0 +1,526 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+#include "stream.h"
+#include "app-layer-protos.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+
+#include "pkt-var.h"
+
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "util-buffer.h"
+
+#include "output.h"
+#include "alert-debuglog.h"
+#include "util-privs.h"
+#include "flow-var.h"
+#include "flow-bit.h"
+#include "util-var-name.h"
+#include "util-optimize.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#include "stream-tcp-reassemble.h"
+
+#define DEFAULT_LOG_FILENAME "alert-debug.log"
+
+#define MODULE_NAME "AlertDebugLog"
+
+typedef struct AlertDebugLogThread_ {
+ LogFileCtx *file_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ MemBuffer *buffer;
+} AlertDebugLogThread;
+
+/**
+ * \brief Function to log the FlowVars in to alert-debug.log
+ *
+ * \param aft Pointer to AltertDebugLog Thread
+ * \param p Pointer to the packet
+ *
+ */
+static void AlertDebugLogFlowVars(AlertDebugLogThread *aft, const Packet *p)
+{
+ const GenericVar *gv = p->flow->flowvar;
+ uint16_t i;
+ while (gv != NULL) {
+ if (gv->type == DETECT_FLOWVAR || gv->type == DETECT_FLOWINT) {
+ FlowVar *fv = (FlowVar *) gv;
+
+ if (fv->datatype == FLOWVAR_TYPE_STR) {
+ MemBufferWriteString(aft->buffer, "FLOWVAR idx(%"PRIu32"): ",
+ fv->idx);
+ for (i = 0; i < fv->data.fv_str.value_len; i++) {
+ if (isprint(fv->data.fv_str.value[i])) {
+ MemBufferWriteString(aft->buffer, "%c",
+ fv->data.fv_str.value[i]);
+ } else {
+ MemBufferWriteString(aft->buffer, "\\%02X",
+ fv->data.fv_str.value[i]);
+ }
+ }
+ } else if (fv->datatype == FLOWVAR_TYPE_INT) {
+ MemBufferWriteString(aft->buffer, "FLOWVAR idx(%"PRIu32"): "
+ " %" PRIu32 "\"", fv->idx, fv->data.fv_int.value);
+ }
+ }
+ gv = gv->next;
+ }
+}
+
+/**
+ * \brief Function to log the FlowBits in to alert-debug.log
+ *
+ * \param aft Pointer to AltertDebugLog Thread
+ * \param p Pointer to the packet
+ *
+ * \todo const Packet ptr, requires us to change the
+ * debuglog_flowbits_names logic.
+ */
+static void AlertDebugLogFlowBits(AlertDebugLogThread *aft, Packet *p)
+{
+ int i;
+ for (i = 0; i < p->debuglog_flowbits_names_len; i++) {
+ if (p->debuglog_flowbits_names[i] != NULL) {
+ MemBufferWriteString(aft->buffer, "FLOWBIT: %s\n",
+ p->debuglog_flowbits_names[i]);
+ }
+ }
+
+ SCFree(p->debuglog_flowbits_names);
+ p->debuglog_flowbits_names = NULL;
+ p->debuglog_flowbits_names_len = 0;
+
+ return;
+}
+
+/**
+ * \brief Function to log the PktVars in to alert-debug.log
+ *
+ * \param aft Pointer to AltertDebugLog Thread
+ * \param p Pointer to the packet
+ *
+ */
+static void AlertDebugLogPktVars(AlertDebugLogThread *aft, const Packet *p)
+{
+ const PktVar *pv = p->pktvar;
+
+ while(pv != NULL) {
+ MemBufferWriteString(aft->buffer, "PKTVAR: %s\n", pv->name);
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ pv->value, pv->value_len);
+ pv = pv->next;
+ }
+}
+
+/** \todo doc
+ * assume we have aft lock */
+static int AlertDebugPrintStreamSegmentCallback(const Packet *p, void *data, uint8_t *buf, uint32_t buflen)
+{
+ AlertDebugLogThread *aft = (AlertDebugLogThread *)data;
+
+ MemBufferWriteString(aft->buffer, "STREAM DATA LEN: %"PRIu32"\n", buflen);
+ MemBufferWriteString(aft->buffer, "STREAM DATA:\n");
+
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ buf, buflen);
+
+ return 1;
+}
+
+static TmEcode AlertDebugLogger(ThreadVars *tv, const Packet *p, void *thread_data)
+{
+ AlertDebugLogThread *aft = (AlertDebugLogThread *)thread_data;
+ int i;
+ char timebuf[64];
+ const char *pkt_src_str = NULL;
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ MemBufferReset(aft->buffer);
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ MemBufferWriteString(aft->buffer, "+================\n"
+ "TIME: %s\n", timebuf);
+ if (p->pcap_cnt > 0) {
+ MemBufferWriteString(aft->buffer, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt);
+ }
+ pkt_src_str = PktSrcToString(p->pkt_src);
+ MemBufferWriteString(aft->buffer, "PKT SRC: %s\n", pkt_src_str);
+
+ char srcip[46], dstip[46];
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ }
+
+ MemBufferWriteString(aft->buffer, "SRC IP: %s\n"
+ "DST IP: %s\n"
+ "PROTO: %" PRIu32 "\n",
+ srcip, dstip, p->proto);
+ if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
+ MemBufferWriteString(aft->buffer, "SRC PORT: %" PRIu32 "\n"
+ "DST PORT: %" PRIu32 "\n",
+ p->sp, p->dp);
+ if (PKT_IS_TCP(p)) {
+ MemBufferWriteString(aft->buffer, "TCP SEQ: %"PRIu32"\n"
+ "TCP ACK: %"PRIu32"\n",
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ }
+ }
+
+ /* flow stuff */
+ MemBufferWriteString(aft->buffer, "FLOW: to_server: %s, "
+ "to_client: %s\n",
+ p->flowflags & FLOW_PKT_TOSERVER ? "TRUE" : "FALSE",
+ p->flowflags & FLOW_PKT_TOCLIENT ? "TRUE" : "FALSE");
+
+ if (p->flow != NULL) {
+ int applayer = 0;
+ FLOWLOCK_RDLOCK(p->flow);
+ applayer = StreamTcpAppLayerIsDisabled(p->flow);
+ CreateTimeString(&p->flow->startts, timebuf, sizeof(timebuf));
+ MemBufferWriteString(aft->buffer, "FLOW Start TS: %s\n", timebuf);
+ MemBufferWriteString(aft->buffer, "FLOW PKTS TODST: %"PRIu32"\n"
+ "FLOW PKTS TOSRC: %"PRIu32"\n"
+ "FLOW Total Bytes: %"PRIu64"\n",
+ p->flow->todstpktcnt, p->flow->tosrcpktcnt,
+ p->flow->todstbytecnt + p->flow->tosrcbytecnt);
+ MemBufferWriteString(aft->buffer,
+ "FLOW IPONLY SET: TOSERVER: %s, TOCLIENT: %s\n"
+ "FLOW ACTION: DROP: %s\n"
+ "FLOW NOINSPECTION: PACKET: %s, PAYLOAD: %s, APP_LAYER: %s\n"
+ "FLOW APP_LAYER: DETECTED: %s, PROTO %"PRIu16"\n",
+ p->flow->flags & FLOW_TOSERVER_IPONLY_SET ? "TRUE" : "FALSE",
+ p->flow->flags & FLOW_TOCLIENT_IPONLY_SET ? "TRUE" : "FALSE",
+ p->flow->flags & FLOW_ACTION_DROP ? "TRUE" : "FALSE",
+ p->flow->flags & FLOW_NOPACKET_INSPECTION ? "TRUE" : "FALSE",
+ p->flow->flags & FLOW_NOPAYLOAD_INSPECTION ? "TRUE" : "FALSE",
+ applayer ? "TRUE" : "FALSE",
+ (p->flow->alproto != ALPROTO_UNKNOWN) ? "TRUE" : "FALSE", p->flow->alproto);
+ AlertDebugLogFlowVars(aft, p);
+ AlertDebugLogFlowBits(aft, (Packet *)p); /* < no const */
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+
+ AlertDebugLogPktVars(aft, p);
+
+/* any stuff */
+/* Sig details? */
+
+ MemBufferWriteString(aft->buffer,
+ "PACKET LEN: %" PRIu32 "\n"
+ "PACKET:\n",
+ GET_PKT_LEN(p));
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ GET_PKT_DATA(p), GET_PKT_LEN(p));
+
+ MemBufferWriteString(aft->buffer, "ALERT CNT: %" PRIu32 "\n",
+ p->alerts.cnt);
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ MemBufferWriteString(aft->buffer,
+ "ALERT MSG [%02d]: %s\n"
+ "ALERT GID [%02d]: %" PRIu32 "\n"
+ "ALERT SID [%02d]: %" PRIu32 "\n"
+ "ALERT REV [%02d]: %" PRIu32 "\n"
+ "ALERT CLASS [%02d]: %s\n"
+ "ALERT PRIO [%02d]: %" PRIu32 "\n"
+ "ALERT FOUND IN [%02d]: %s\n",
+ i, pa->s->msg,
+ i, pa->s->gid,
+ i, pa->s->id,
+ i, pa->s->rev,
+ i, pa->s->class_msg ? pa->s->class_msg : "<none>",
+ i, pa->s->prio,
+ i,
+ pa->flags & PACKET_ALERT_FLAG_STREAM_MATCH ? "STREAM" :
+ (pa->flags & PACKET_ALERT_FLAG_STATE_MATCH ? "STATE" : "PACKET"));
+ if (pa->flags & PACKET_ALERT_FLAG_TX) {
+ MemBufferWriteString(aft->buffer,
+ "ALERT IN TX [%02d]: %"PRIu64"\n", i, pa->tx_id);
+ } else {
+ MemBufferWriteString(aft->buffer,
+ "ALERT IN TX [%02d]: N/A\n", i);
+ }
+ if (p->payload_len > 0) {
+ MemBufferWriteString(aft->buffer,
+ "PAYLOAD LEN: %" PRIu32 "\n"
+ "PAYLOAD:\n",
+ p->payload_len);
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ p->payload, p->payload_len);
+ }
+ if ((pa->flags & PACKET_ALERT_FLAG_STATE_MATCH) ||
+ (pa->flags & PACKET_ALERT_FLAG_STREAM_MATCH)) {
+ /* This is an app layer or stream alert */
+ int ret;
+ uint8_t flag;
+ if (!(PKT_IS_TCP(p)) || p->flow == NULL ||
+ p->flow->protoctx == NULL) {
+ return TM_ECODE_OK;
+ }
+ /* IDS mode reverse the data */
+ /** \todo improve the order selection policy */
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag = FLOW_PKT_TOCLIENT;
+ } else {
+ flag = FLOW_PKT_TOSERVER;
+ }
+ ret = StreamSegmentForEach((const Packet *)p, flag,
+ AlertDebugPrintStreamSegmentCallback,
+ (void *)aft);
+ if (ret < 0) {
+ return TM_ECODE_FAILED;
+ }
+ }
+ }
+
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+ (void)MemBufferPrintToFPAsString(aft->buffer, aft->file_ctx->fp);
+ fflush(aft->file_ctx->fp);
+ aft->file_ctx->alerts += p->alerts.cnt;
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode AlertDebugLogDecoderEvent(ThreadVars *tv, const Packet *p, void *thread_data)
+{
+ AlertDebugLogThread *aft = (AlertDebugLogThread *)thread_data;
+ int i;
+ char timebuf[64];
+ const char *pkt_src_str = NULL;
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ MemBufferReset(aft->buffer);
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ MemBufferWriteString(aft->buffer,
+ "+================\n"
+ "TIME: %s\n", timebuf);
+ if (p->pcap_cnt > 0) {
+ MemBufferWriteString(aft->buffer,
+ "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt);
+ }
+ pkt_src_str = PktSrcToString(p->pkt_src);
+ MemBufferWriteString(aft->buffer, "PKT SRC: %s\n", pkt_src_str);
+ MemBufferWriteString(aft->buffer,
+ "ALERT CNT: %" PRIu32 "\n", p->alerts.cnt);
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ MemBufferWriteString(aft->buffer,
+ "ALERT MSG [%02d]: %s\n"
+ "ALERT GID [%02d]: %" PRIu32 "\n"
+ "ALERT SID [%02d]: %" PRIu32 "\n"
+ "ALERT REV [%02d]: %" PRIu32 "\n"
+ "ALERT CLASS [%02d]: %s\n"
+ "ALERT PRIO [%02d]: %" PRIu32 "\n",
+ i, pa->s->msg,
+ i, pa->s->gid,
+ i, pa->s->id,
+ i, pa->s->rev,
+ i, pa->s->class_msg,
+ i, pa->s->prio);
+ }
+
+ MemBufferWriteString(aft->buffer,
+ "PACKET LEN: %" PRIu32 "\n"
+ "PACKET:\n",
+ GET_PKT_LEN(p));
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ GET_PKT_DATA(p), GET_PKT_LEN(p));
+
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+ aft->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), aft->file_ctx);
+ aft->file_ctx->alerts += p->alerts.cnt;
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode AlertDebugLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ AlertDebugLogThread *aft = SCMalloc(sizeof(AlertDebugLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(AlertDebugLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for DebugLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+ /** Use the Ouptut Context (file pointer and mutex) */
+ aft->file_ctx = ((OutputCtx *)initdata)->data;
+
+ /* 1 mb seems sufficient enough */
+ aft->buffer = MemBufferCreateNew(1 * 1024 * 1024);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode AlertDebugLogThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertDebugLogThread *aft = (AlertDebugLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(AlertDebugLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void AlertDebugLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ AlertDebugLogThread *aft = (AlertDebugLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) Alerts %" PRIu64 "", tv->name, aft->file_ctx->alerts);
+}
+
+static void AlertDebugLogDeInitCtx(OutputCtx *output_ctx)
+{
+ if (output_ctx != NULL) {
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ if (logfile_ctx != NULL) {
+ LogFileFreeCtx(logfile_ctx);
+ }
+ SCFree(output_ctx);
+ }
+}
+
+/**
+ * \brief Create a new LogFileCtx for alert debug logging.
+ *
+ * \param ConfNode containing configuration for this logger.
+ *
+ * \return output_ctx if succesful, NULL otherwise
+ */
+static OutputCtx *AlertDebugLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = NULL;
+
+ file_ctx = LogFileNewCtx();
+ if (file_ctx == NULL) {
+ SCLogDebug("couldn't create new file_ctx");
+ goto error;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ goto error;
+ }
+
+ OutputCtx *output_ctx = SCMalloc(sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ goto error;
+
+ memset(output_ctx, 0x00, sizeof(OutputCtx));
+ output_ctx->data = file_ctx;
+ output_ctx->DeInit = AlertDebugLogDeInitCtx;
+
+ SCLogDebug("Alert debug log output initialized");
+ return output_ctx;
+
+error:
+ if (file_ctx != NULL) {
+ LogFileFreeCtx(file_ctx);
+ }
+
+ return NULL;
+}
+
+static int AlertDebugLogCondition(ThreadVars *tv, const Packet *p)
+{
+ return (p->alerts.cnt ? TRUE : FALSE);
+}
+
+static int AlertDebugLogLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ if (PKT_IS_IPV4(p) || PKT_IS_IPV6(p)) {
+ return AlertDebugLogger(tv, p, thread_data);
+ } else if (p->events.cnt > 0) {
+ return AlertDebugLogDecoderEvent(tv, p, thread_data);
+ }
+ return TM_ECODE_OK;
+}
+
+void TmModuleAlertDebugLogRegister (void)
+{
+ tmm_modules[TMM_ALERTDEBUGLOG].name = MODULE_NAME;
+ tmm_modules[TMM_ALERTDEBUGLOG].ThreadInit = AlertDebugLogThreadInit;
+ tmm_modules[TMM_ALERTDEBUGLOG].Func = NULL;
+ tmm_modules[TMM_ALERTDEBUGLOG].ThreadExitPrintStats = AlertDebugLogExitPrintStats;
+ tmm_modules[TMM_ALERTDEBUGLOG].ThreadDeinit = AlertDebugLogThreadDeinit;
+ tmm_modules[TMM_ALERTDEBUGLOG].RegisterTests = NULL;
+ tmm_modules[TMM_ALERTDEBUGLOG].cap_flags = 0;
+ tmm_modules[TMM_ALERTDEBUGLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "alert-debug",
+ AlertDebugLogInitCtx, AlertDebugLogLogger, AlertDebugLogCondition);
+}
diff --git a/framework/src/suricata/src/alert-debuglog.h b/framework/src/suricata/src/alert-debuglog.h
new file mode 100644
index 00000000..8eb38d97
--- /dev/null
+++ b/framework/src/suricata/src/alert-debuglog.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __ALERT_DEBUGLOG_H__
+#define __ALERT_DEBUGLOG_H__
+
+void TmModuleAlertDebugLogRegister (void);
+
+#endif /* __ALERT_DEBUGLOG_H__ */
+
diff --git a/framework/src/suricata/src/alert-fastlog.c b/framework/src/suricata/src/alert-fastlog.c
new file mode 100644
index 00000000..41630f8a
--- /dev/null
+++ b/framework/src/suricata/src/alert-fastlog.c
@@ -0,0 +1,386 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Logs alerts in a line based text format compatible to Snort's
+ * alert_fast format.
+ *
+ * \todo Support classifications
+ * \todo Support more than just IPv4/IPv6 TCP/UDP.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+#include "util-classification-config.h"
+
+#include "output.h"
+#include "alert-fastlog.h"
+
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-optimize.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "fast.log"
+
+#define MODULE_NAME "AlertFastLog"
+
+/* The largest that size allowed for one alert string. */
+#define MAX_FASTLOG_ALERT_SIZE 2048
+/* The largest alert buffer that will be written at one time, possibly
+ * holding multiple alerts. */
+#define MAX_FASTLOG_BUFFER_SIZE (2 * MAX_FASTLOG_ALERT_SIZE)
+
+TmEcode AlertFastLogThreadInit(ThreadVars *, void *, void **);
+TmEcode AlertFastLogThreadDeinit(ThreadVars *, void *);
+void AlertFastLogExitPrintStats(ThreadVars *, void *);
+void AlertFastLogRegisterTests(void);
+static void AlertFastLogDeInitCtx(OutputCtx *);
+
+int AlertFastLogCondition(ThreadVars *tv, const Packet *p);
+int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p);
+
+void TmModuleAlertFastLogRegister (void)
+{
+ tmm_modules[TMM_ALERTFASTLOG].name = MODULE_NAME;
+ tmm_modules[TMM_ALERTFASTLOG].ThreadInit = AlertFastLogThreadInit;
+ tmm_modules[TMM_ALERTFASTLOG].ThreadExitPrintStats = AlertFastLogExitPrintStats;
+ tmm_modules[TMM_ALERTFASTLOG].ThreadDeinit = AlertFastLogThreadDeinit;
+ tmm_modules[TMM_ALERTFASTLOG].RegisterTests = AlertFastLogRegisterTests;
+ tmm_modules[TMM_ALERTFASTLOG].cap_flags = 0;
+ tmm_modules[TMM_ALERTFASTLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "fast",
+ AlertFastLogInitCtx, AlertFastLogger, AlertFastLogCondition);
+}
+
+typedef struct AlertFastLogThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx* file_ctx;
+} AlertFastLogThread;
+
+int AlertFastLogCondition(ThreadVars *tv, const Packet *p)
+{
+ return (p->alerts.cnt ? TRUE : FALSE);
+}
+
+static inline void AlertFastLogOutputAlert(AlertFastLogThread *aft, char *buffer,
+ int alert_size)
+{
+ SCMutex *file_lock = &aft->file_ctx->fp_mutex;
+ /* Output the alert string and count alerts. Only need to lock here. */
+ SCMutexLock(file_lock);
+ aft->file_ctx->alerts++;
+ aft->file_ctx->Write(buffer, alert_size, aft->file_ctx);
+ SCMutexUnlock(file_lock);
+}
+
+int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
+{
+ AlertFastLogThread *aft = (AlertFastLogThread *)data;
+ int i;
+ char timebuf[64];
+ int decoder_event = 0;
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ char srcip[46], dstip[46];
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ } else {
+ decoder_event = 1;
+ }
+
+ /* Buffer to store the generated alert strings. The buffer is
+ * filled with alert strings until it doesn't have room to store
+ * another full alert, only then is the buffer written. This is
+ * more efficient for multiple alerts and only slightly slower for
+ * single alerts.
+ */
+ char alert_buffer[MAX_FASTLOG_BUFFER_SIZE];
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ char *action = "";
+ if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "[Drop] ";
+ } else if (pa->action & ACTION_DROP) {
+ action = "[wDrop] ";
+ }
+
+ char proto[16] = "";
+ if (likely(decoder_event == 0)) {
+ if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
+ }
+ }
+
+ /* Create the alert string without locking. */
+ int size = 0;
+ if (likely(decoder_event == 0)) {
+ PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
+ "%s %s[**] [%" PRIu32 ":%" PRIu32 ":%"
+ PRIu32 "] %s [**] [Classification: %s] [Priority: %"PRIu32"]"
+ " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "\n", timebuf, action,
+ pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
+ proto, srcip, p->sp, dstip, p->dp);
+ } else {
+ PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
+ "%s %s[**] [%" PRIu32 ":%" PRIu32
+ ":%" PRIu32 "] %s [**] [Classification: %s] [Priority: "
+ "%" PRIu32 "] [**] [Raw pkt: ", timebuf, action, pa->s->gid,
+ pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio);
+ PrintBufferRawLineHex(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
+ GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
+ if (p->pcap_cnt != 0) {
+ PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
+ "] [pcap file packet: %"PRIu64"]\n", p->pcap_cnt);
+ } else {
+ PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, "]\n");
+ }
+ }
+
+ /* Write the alert to output file */
+ AlertFastLogOutputAlert(aft, alert_buffer, size);
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertFastLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ AlertFastLogThread *aft = SCMalloc(sizeof(AlertFastLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(AlertFastLogThread));
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for AlertFastLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+ /** Use the Ouptut Context (file pointer and mutex) */
+ aft->file_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertFastLogThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertFastLogThread *aft = (AlertFastLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(aft, 0, sizeof(AlertFastLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void AlertFastLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ AlertFastLogThread *aft = (AlertFastLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("Fast log output wrote %" PRIu64 " alerts", aft->file_ctx->alerts);
+}
+
+/**
+ * \brief Create a new LogFileCtx for "fast" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+OutputCtx *AlertFastLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("AlertFastLogInitCtx2: Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = AlertFastLogDeInitCtx;
+
+ return output_ctx;
+}
+
+static void AlertFastLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(output_ctx);
+}
+
+/*------------------------------Unittests-------------------------------------*/
+
+#ifdef UNITTESTS
+
+static int AlertFastLogTest01()
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n";
+
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"FastLog test\"; content:\"GET\"; "
+ "Classtype:unknown; sid:1;)");
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1) {
+ result = (strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int AlertFastLogTest02()
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"FastLog test\"; content:\"GET\"; "
+ "Classtype:unknown; sid:1;)");
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1) {
+ result = (strcmp(p->alerts.alerts[0].s->class_msg,
+ "Unknown are we") == 0);
+ if (result == 0)
+ printf("p->alerts.alerts[0].class_msg %s: ", p->alerts.alerts[0].s->class_msg);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for AlertFastLog API.
+ */
+void AlertFastLogRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("AlertFastLogTest01", AlertFastLogTest01, 1);
+ UtRegisterTest("AlertFastLogTest02", AlertFastLogTest02, 1);
+
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/alert-fastlog.h b/framework/src/suricata/src/alert-fastlog.h
new file mode 100644
index 00000000..ac3c45a7
--- /dev/null
+++ b/framework/src/suricata/src/alert-fastlog.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __ALERT_FASTLOG_H__
+#define __ALERT_FASTLOG_H__
+
+void TmModuleAlertFastLogRegister(void);
+void TmModuleAlertFastLogIPv4Register(void);
+void TmModuleAlertFastLogIPv6Register(void);
+OutputCtx *AlertFastLogInitCtx(ConfNode *);
+
+#endif /* __ALERT_FASTLOG_H__ */
+
diff --git a/framework/src/suricata/src/alert-prelude.c b/framework/src/suricata/src/alert-prelude.c
new file mode 100644
index 00000000..a053e38b
--- /dev/null
+++ b/framework/src/suricata/src/alert-prelude.c
@@ -0,0 +1,885 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <chifflier@edenwall.com>
+ * \author Yoann Vandoorselaere <yoann.v@prelude-ids.com>
+ *
+ * Logs alerts to the Prelude system, using IDMEF (RFC 4765) messages.
+ *
+ * Each message contains the alert description and reference (using
+ * the SID/GID), and a normalized description (assessment, impact,
+ * sources etc.)
+ *
+ * libprelude handles the connection with the manager (collecting component),
+ * spooling and sending the event asynchronously. It also offers transport
+ * security (using TLS and trusted certificates) and reliability (events
+ * are retransmitted if not sent successfully).
+ *
+ * This modules requires a Prelude profile to work (see man prelude-admin
+ * and the Prelude Handbook for help).
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-time.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-print.h"
+
+#include "output.h"
+#include "util-privs.h"
+#include "util-optimize.h"
+
+#include "stream.h"
+
+#ifndef PRELUDE
+
+/* Handle the case where no PRELUDE support is compiled in. */
+
+static TmEcode AlertPreludeThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogDebug("Can't init Prelude output thread - Prelude support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+static TmEcode AlertPreludeThreadDeinit(ThreadVars *t, void *data)
+{
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleAlertPreludeRegister (void)
+{
+ tmm_modules[TMM_ALERTPRELUDE].name = "AlertPrelude";
+ tmm_modules[TMM_ALERTPRELUDE].ThreadInit = AlertPreludeThreadInit;
+ tmm_modules[TMM_ALERTPRELUDE].ThreadDeinit = AlertPreludeThreadDeinit;
+}
+
+#else /* implied we do have PRELUDE support */
+
+#include <libprelude/prelude.h>
+
+#define ANALYZER_CLASS "NIDS"
+#define ANALYZER_MODEL "Suricata"
+#define ANALYZER_MANUFACTURER "http://www.openinfosecfoundation.org/"
+#define ANALYZER_SID_URL "http://www.snort.org/search/sid/"
+
+#define SNORT_MAX_OWNED_SID 1000000
+#define DEFAULT_ANALYZER_NAME "suricata"
+
+#define DEFAULT_PRELUDE_PROFILE "suricata"
+
+static unsigned int info_priority = 4;
+static unsigned int low_priority = 3;
+static unsigned int mid_priority = 2;
+
+/**
+ * This holds global structures and variables. Since libprelude is thread-safe,
+ * there is no need to store a mutex.
+ */
+typedef struct AlertPreludeCtx_ {
+ /** The client (which has the send function) */
+ prelude_client_t *client;
+ int log_packet_content;
+ int log_packet_header;
+} AlertPreludeCtx;
+
+/**
+ * This holds per-thread specific structures and variables.
+ */
+typedef struct AlertPreludeThread_ {
+ /** Pointer to the global context */
+ AlertPreludeCtx *ctx;
+} AlertPreludeThread;
+
+
+/**
+ * \brief Initialize analyzer description
+ *
+ * \return 0 if ok
+ */
+static int SetupAnalyzer(idmef_analyzer_t *analyzer)
+{
+ int ret;
+ prelude_string_t *string;
+
+ SCEnter();
+
+ ret = idmef_analyzer_new_model(analyzer, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+ prelude_string_set_constant(string, ANALYZER_MODEL);
+
+ ret = idmef_analyzer_new_class(analyzer, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+ prelude_string_set_constant(string, ANALYZER_CLASS);
+
+ ret = idmef_analyzer_new_manufacturer(analyzer, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+ prelude_string_set_constant(string, ANALYZER_MANUFACTURER);
+
+ ret = idmef_analyzer_new_version(analyzer, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+ prelude_string_set_constant(string, VERSION);
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Create event impact description (see section
+ * 4.2.6.1 of RFC 4765).
+ * The impact contains the severity, completion (succeeded or failed)
+ * and basic classification of the attack type.
+ * Here, we don't set the completion since we don't know it (default
+ * is unknown).
+ *
+ * \return 0 if ok
+ */
+static int EventToImpact(const PacketAlert *pa, const Packet *p, idmef_alert_t *alert)
+{
+ int ret;
+ prelude_string_t *str;
+ idmef_impact_t *impact;
+ idmef_assessment_t *assessment;
+ idmef_impact_severity_t severity;
+
+ SCEnter();
+
+ ret = idmef_alert_new_assessment(alert, &assessment);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_assessment_new_impact(assessment, &impact);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ if ( (unsigned int)pa->s->prio < mid_priority )
+ severity = IDMEF_IMPACT_SEVERITY_HIGH;
+
+ else if ( (unsigned int)pa->s->prio < low_priority )
+ severity = IDMEF_IMPACT_SEVERITY_MEDIUM;
+
+ else if ( (unsigned int)pa->s->prio < info_priority )
+ severity = IDMEF_IMPACT_SEVERITY_LOW;
+
+ else
+ severity = IDMEF_IMPACT_SEVERITY_INFO;
+
+ idmef_impact_set_severity(impact, severity);
+
+ if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ idmef_action_t *action;
+
+ ret = idmef_action_new(&action);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ idmef_action_set_category(action, IDMEF_ACTION_CATEGORY_BLOCK_INSTALLED);
+ idmef_assessment_set_action(assessment, action, 0);
+ }
+
+ if (pa->s->class_msg) {
+ ret = idmef_impact_new_description(impact, &str);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ prelude_string_set_ref(str, pa->s->class_msg);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Add Source and Target fields to the IDMEF alert.
+ * These objects contains IP addresses, source and destination
+ * ports (see sections 4.2.4.3 and 4.2.4.4 of RFC 4765).
+ *
+ * \return 0 if ok
+ */
+static int EventToSourceTarget(const Packet *p, idmef_alert_t *alert)
+{
+ int ret;
+ idmef_node_t *node;
+ idmef_source_t *source;
+ idmef_target_t *target;
+ idmef_address_t *address;
+ idmef_service_t *service;
+ prelude_string_t *string;
+ static char saddr[128], daddr[128];
+ uint8_t ip_vers;
+ uint8_t ip_proto;
+
+ SCEnter();
+
+ if ( !p )
+ SCReturnInt(0);
+
+ if ( ! IPH_IS_VALID(p) )
+ SCReturnInt(0);
+
+ if (PKT_IS_IPV4(p)) {
+ ip_vers = 4;
+ ip_proto = IPV4_GET_RAW_IPPROTO(p->ip4h);
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), saddr, sizeof(saddr));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), daddr, sizeof(daddr));
+ } else if (PKT_IS_IPV6(p)) {
+ ip_vers = 6;
+ ip_proto = IPV6_GET_L4PROTO(p);
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), saddr, sizeof(saddr));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), daddr, sizeof(daddr));
+ } else
+ SCReturnInt(0);
+
+ ret = idmef_alert_new_source(alert, &source, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_source_new_service(source, &service);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ if ( p->tcph || p->udph )
+ idmef_service_set_port(service, p->sp);
+
+ idmef_service_set_ip_version(service, ip_vers);
+ idmef_service_set_iana_protocol_number(service, ip_proto);
+
+ ret = idmef_source_new_node(source, &node);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_node_new_address(node, &address, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_address_new_address(address, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ prelude_string_set_ref(string, saddr);
+
+ ret = idmef_alert_new_target(alert, &target, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_target_new_service(target, &service);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ if ( p->tcph || p->udph )
+ idmef_service_set_port(service, p->dp);
+
+ idmef_service_set_ip_version(service, ip_vers);
+ idmef_service_set_iana_protocol_number(service, ip_proto);
+
+ ret = idmef_target_new_node(target, &node);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_node_new_address(node, &address, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_address_new_address(address, &string);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ prelude_string_set_ref(string, daddr);
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Add binary data, to be stored in the Additional Data
+ * field of the IDMEF alert (see section 4.2.4.6 of RFC 4765).
+ *
+ * \return 0 if ok
+ */
+static int AddByteData(idmef_alert_t *alert, const char *meaning, const unsigned char *data, size_t size)
+{
+ int ret;
+ prelude_string_t *str;
+ idmef_additional_data_t *ad;
+
+ SCEnter();
+
+ if ( ! data || ! size )
+ SCReturnInt(0);
+
+ ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(0);
+
+ ret = idmef_additional_data_set_byte_string_ref(ad, data, size);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("%s: error setting byte string data: %s.",
+ prelude_strsource(ret), prelude_strerror(ret));
+ SCReturnInt(-1);
+ }
+
+ ret = idmef_additional_data_new_meaning(ad, &str);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("%s: error creating additional-data meaning: %s.",
+ prelude_strsource(ret), prelude_strerror(ret));
+ SCReturnInt(-1);
+ }
+
+ ret = prelude_string_set_ref(str, meaning);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("%s: error setting byte string data meaning: %s.",
+ prelude_strsource(ret), prelude_strerror(ret));
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Add integer data, to be stored in the Additional Data
+ * field of the IDMEF alert (see section 4.2.4.6 of RFC 4765).
+ *
+ * \return 0 if ok
+ */
+static int AddIntData(idmef_alert_t *alert, const char *meaning, uint32_t data)
+{
+ int ret;
+ prelude_string_t *str;
+ idmef_additional_data_t *ad;
+
+ SCEnter();
+
+ ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ idmef_additional_data_set_integer(ad, data);
+
+ ret = idmef_additional_data_new_meaning(ad, &str);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("%s: error creating additional-data meaning: %s.",
+ prelude_strsource(ret), prelude_strerror(ret));
+ SCReturnInt(-1);
+ }
+
+ ret = prelude_string_set_ref(str, meaning);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("%s: error setting integer data meaning: %s.",
+ prelude_strsource(ret), prelude_strerror(ret));
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Add IPv4 header data, to be stored in the Additional Data
+ * field of the IDMEF alert (see section 4.2.4.6 of RFC 4765).
+ *
+ * \return 0 if ok
+ */
+static int PacketToDataV4(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert)
+{
+ SCEnter();
+
+ AddIntData(alert, "ip_ver", IPV4_GET_RAW_VER(p->ip4h));
+ AddIntData(alert, "ip_hlen", IPV4_GET_RAW_HLEN(p->ip4h));
+ AddIntData(alert, "ip_tos", IPV4_GET_RAW_IPTOS(p->ip4h));
+ AddIntData(alert, "ip_len", ntohs(IPV4_GET_RAW_IPLEN(p->ip4h)));
+
+ AddIntData(alert, "ip_id", ntohs(IPV4_GET_RAW_IPID(p->ip4h)));
+
+ AddIntData(alert, "ip_off", ntohs(IPV4_GET_RAW_IPOFFSET(p->ip4h)));
+
+ AddIntData(alert, "ip_ttl", IPV4_GET_RAW_IPTTL(p->ip4h));
+ AddIntData(alert, "ip_proto", IPV4_GET_RAW_IPPROTO(p->ip4h));
+
+ AddIntData(alert, "ip_sum", ntohs(p->ip4h->ip_csum));
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Add IPv6 header data, to be stored in the Additional Data
+ * field of the IDMEF alert (see section 4.2.4.6 of RFC 4765).
+ *
+ * \return 0 if ok
+ */
+static int PacketToDataV6(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert)
+{
+ return 0;
+}
+
+
+/**
+ * \brief Convert IP packet to an IDMEF alert (RFC 4765).
+ * This function stores the alert SID (description and reference),
+ * the payload of the packet, and pre-processed data.
+ *
+ * \return 0 if ok
+ */
+static int PacketToData(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert, AlertPreludeCtx *ctx)
+{
+ SCEnter();
+
+ if (unlikely(p == NULL))
+ SCReturnInt(0);
+
+ AddIntData(alert, "snort_rule_sid", pa->s->id);
+ AddIntData(alert, "snort_rule_rev", pa->s->rev);
+
+ if (ctx->log_packet_header) {
+ if ( PKT_IS_IPV4(p) )
+ PacketToDataV4(p, pa, alert);
+
+ else if ( PKT_IS_IPV6(p) )
+ PacketToDataV6(p, pa, alert);
+
+ if ( PKT_IS_TCP(p) ) {
+ AddIntData(alert, "tcp_seq", ntohl(p->tcph->th_seq));
+ AddIntData(alert, "tcp_ack", ntohl(p->tcph->th_ack));
+
+ AddIntData(alert, "tcp_off", TCP_GET_RAW_OFFSET(p->tcph));
+ AddIntData(alert, "tcp_res", TCP_GET_RAW_X2(p->tcph));
+ AddIntData(alert, "tcp_flags", p->tcph->th_flags);
+
+ AddIntData(alert, "tcp_win", ntohs(p->tcph->th_win));
+ AddIntData(alert, "tcp_sum", ntohs(p->tcph->th_sum));
+ AddIntData(alert, "tcp_urp", ntohs(p->tcph->th_urp));
+
+ }
+
+ else if ( PKT_IS_UDP(p) ) {
+ AddIntData(alert, "udp_len", ntohs(p->udph->uh_len));
+ AddIntData(alert, "udp_sum", ntohs(p->udph->uh_sum));
+ }
+
+ else if ( PKT_IS_ICMPV4(p) ) {
+ AddIntData(alert, "icmp_type", p->icmpv4h->type);
+ AddIntData(alert, "icmp_code", p->icmpv4h->code);
+ AddIntData(alert, "icmp_sum", ntohs(p->icmpv4h->checksum));
+
+ }
+ }
+
+ if (ctx->log_packet_content)
+ AddByteData(alert, "payload", p->payload, p->payload_len);
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Store reference on rule (SID and GID) in the IDMEF alert,
+ * and embed an URL pointing to the rule description.
+ *
+ * \return 0 if ok
+ */
+static int AddSnortReference(idmef_classification_t *class, int gen_id, int sig_id)
+{
+ int ret;
+ prelude_string_t *str;
+ idmef_reference_t *ref;
+
+ SCEnter();
+
+ if ( sig_id >= SNORT_MAX_OWNED_SID )
+ SCReturnInt(0);
+
+ ret = idmef_classification_new_reference(class, &ref, IDMEF_LIST_APPEND);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_reference_new_name(ref, &str);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ idmef_reference_set_origin(ref, IDMEF_REFERENCE_ORIGIN_VENDOR_SPECIFIC);
+
+ if ( gen_id == 0 )
+ ret = prelude_string_sprintf(str, "%u", sig_id);
+ else
+ ret = prelude_string_sprintf(str, "%u:%u", gen_id, sig_id);
+
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_reference_new_meaning(ref, &str);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = prelude_string_sprintf(str, "Snort Signature ID");
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = idmef_reference_new_url(ref, &str);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ if ( gen_id == 0 )
+ ret = prelude_string_sprintf(str, ANALYZER_SID_URL "%u", sig_id);
+ else
+ ret = prelude_string_sprintf(str, ANALYZER_SID_URL "%u-%u", gen_id, sig_id);
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Create event classification description (see section
+ * 4.2.4.2 of RFC 4765).
+ * The classification is the "name" of the alert, identification of the
+ * rule signature, and additional information on the rule.
+ *
+ * \return 0 if ok
+ */
+static int EventToReference(const PacketAlert *pa, const Packet *p, idmef_classification_t *class)
+{
+ int ret;
+ prelude_string_t *str;
+
+ SCEnter();
+
+ ret = idmef_classification_new_ident(class, &str);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ if ( pa->s->gid == 0 )
+ ret = prelude_string_sprintf(str, "%u", pa->s->id);
+ else
+ ret = prelude_string_sprintf(str, "%u:%u", pa->s->gid, pa->s->id);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ ret = AddSnortReference(class, pa->s->gid, pa->s->id);
+ if (unlikely(ret < 0))
+ SCReturnInt(ret);
+
+ SCReturnInt(0);
+}
+
+static int PreludePrintStreamSegmentCallback(const Packet *p, void *data, uint8_t *buf, uint32_t buflen)
+{
+ int ret;
+
+ ret = AddByteData((idmef_alert_t *)data, "stream-segment", buf, buflen);
+ if (ret == 0)
+ return 1;
+ else
+ return -1;
+}
+
+/**
+ * \brief Initialize thread-specific data. Each thread structure contains
+ * a pointer to the \a AlertPreludeCtx context.
+ *
+ * \return TM_ECODE_OK if ok, else TM_ECODE_FAILED
+ */
+static TmEcode AlertPreludeThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ AlertPreludeThread *aun;
+
+ SCEnter();
+
+ if(unlikely(initdata == NULL))
+ {
+ SCLogDebug("Error getting context for Prelude. \"initdata\" argument NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ aun = SCMalloc(sizeof(AlertPreludeThread));
+ if (unlikely(aun == NULL))
+ SCReturnInt(TM_ECODE_FAILED);
+ memset(aun, 0, sizeof(AlertPreludeThread));
+
+ /** Use the Ouput Context */
+ aun->ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aun;
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Free thread-specific data.
+ *
+ * \return TM_ECODE_OK if ok, else TM_ECODE_FAILED
+ */
+static TmEcode AlertPreludeThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertPreludeThread *aun = (AlertPreludeThread *)data;
+
+ SCEnter();
+
+ if (unlikely(aun == NULL)) {
+ SCLogDebug("AlertPreludeThreadDeinit done (error)");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* clear memory */
+ memset(aun, 0, sizeof(AlertPreludeThread));
+ SCFree(aun);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void AlertPreludeDeinitCtx(OutputCtx *output_ctx)
+{
+ AlertPreludeCtx *ctx = (AlertPreludeCtx *)output_ctx->data;
+
+ prelude_client_destroy(ctx->client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCFree(output_ctx);
+}
+
+/** \brief Initialize the Prelude logging module: initialize
+ * library, create the client and try to establish the connection
+ * to the Prelude Manager.
+ * Client flags are set to force asynchronous (non-blocking) mode for
+ * both alerts and heartbeats.
+ * This function requires an existing Prelude profile to work.
+ *
+ * \return A newly allocated AlertPreludeCtx structure, or NULL
+ */
+static OutputCtx *AlertPreludeInitCtx(ConfNode *conf)
+{
+ int ret;
+ prelude_client_t *client;
+ AlertPreludeCtx *ctx;
+ const char *prelude_profile_name;
+ const char *log_packet_content;
+ const char *log_packet_header;
+ OutputCtx *output_ctx;
+
+ SCEnter();
+
+ ret = prelude_init(0, NULL);
+ if (unlikely(ret < 0)) {
+ prelude_perror(ret, "unable to initialize the prelude library");
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ prelude_profile_name = ConfNodeLookupChildValue(conf, "profile");
+ if (prelude_profile_name == NULL)
+ prelude_profile_name = DEFAULT_PRELUDE_PROFILE;
+
+ log_packet_content = ConfNodeLookupChildValue(conf, "log-packet-content");
+ log_packet_header = ConfNodeLookupChildValue(conf, "log-packet-header");
+
+ ret = prelude_client_new(&client, prelude_profile_name);
+ if ( unlikely(ret < 0 || client == NULL )) {
+ prelude_perror(ret, "Unable to create a prelude client object");
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ ret = prelude_client_set_flags(client, prelude_client_get_flags(client) | PRELUDE_CLIENT_FLAGS_ASYNC_TIMER|PRELUDE_CLIENT_FLAGS_ASYNC_SEND);
+ if (unlikely(ret < 0)) {
+ SCLogDebug("Unable to set asynchronous send and timer.");
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ SetupAnalyzer(prelude_client_get_analyzer(client));
+
+ ret = prelude_client_start(client);
+ if (unlikely(ret < 0)) {
+ prelude_perror(ret, "Unable to start prelude client");
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ ctx = SCMalloc(sizeof(AlertPreludeCtx));
+ if (unlikely(ctx == NULL)) {
+ prelude_perror(ret, "Unable to allocate memory");
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ ctx->client = client;
+ ctx->log_packet_content = 0;
+ ctx->log_packet_header = 1;
+ if (log_packet_content && ConfValIsTrue(log_packet_content))
+ ctx->log_packet_content = 1;
+ if (log_packet_header && ConfValIsFalse(log_packet_header))
+ ctx->log_packet_header = 0;
+
+ output_ctx = SCMalloc(sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(ctx);
+ prelude_perror(ret, "Unable to allocate memory");
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+ SCReturnPtr(NULL, "AlertPreludeCtx");
+ }
+
+ output_ctx->data = ctx;
+ output_ctx->DeInit = AlertPreludeDeinitCtx;
+
+ SCReturnPtr((void*)output_ctx, "OutputCtx");
+}
+
+static int AlertPreludeCondition(ThreadVars *tv, const Packet *p)
+{
+ if (p->alerts.cnt == 0)
+ return FALSE;
+ if (!IPH_IS_VALID(p))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * \brief Handle Suricata alert: convert it to and IDMEF alert (see RFC 4765)
+ * and send it asynchronously (so, this function does not block and returns
+ * immediately).
+ * If the destination Prelude Manager is not available, the alert is spooled
+ * (and the function also returns immediately).
+ * An IDMEF object is created, and all available information is added: IP packet
+ * header and data, rule signature ID, additional data like URL pointing to
+ * rule description, CVE, etc.
+ * The IDMEF alert has a reference to all created objects, so freeing it will
+ * automatically free all allocated memory.
+ *
+ * \note This function is thread safe.
+ *
+ * \return TM_ECODE_OK if ok, else TM_ECODE_FAILED
+ */
+static int AlertPreludeLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ AlertPreludeThread *apn = (AlertPreludeThread *)thread_data;
+ int ret;
+ idmef_time_t *time;
+ idmef_alert_t *alert;
+ prelude_string_t *str;
+ idmef_message_t *idmef = NULL;
+ idmef_classification_t *class;
+ const PacketAlert *pa;
+
+ SCEnter();
+
+ if (unlikely(apn == NULL || apn->ctx == NULL)) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (p->alerts.cnt == 0)
+ SCReturnInt(TM_ECODE_OK);
+
+ if ( !IPH_IS_VALID(p) )
+ SCReturnInt(TM_ECODE_OK);
+
+ /* XXX which one to add to this alert? Lets see how Snort solves this.
+ * For now just take last alert. */
+ pa = &p->alerts.alerts[p->alerts.cnt-1];
+ if (unlikely(pa->s == NULL))
+ goto err;
+
+ ret = idmef_message_new(&idmef);
+ if (unlikely(ret < 0))
+ SCReturnInt(TM_ECODE_FAILED);
+
+ ret = idmef_message_new_alert(idmef, &alert);
+ if (unlikely(ret < 0))
+ goto err;
+
+ ret = idmef_alert_new_classification(alert, &class);
+ if (unlikely(ret < 0))
+ goto err;
+
+ if (pa->s->msg) {
+ ret = idmef_classification_new_text(class, &str);
+ if (unlikely(ret < 0))
+ goto err;
+
+ prelude_string_set_ref(str, pa->s->msg);
+ }
+
+ ret = EventToImpact(pa, p, alert);
+ if (unlikely(ret < 0))
+ goto err;
+
+ ret = EventToReference(pa, p, class);
+ if (unlikely(ret < 0))
+ goto err;
+
+ ret = EventToSourceTarget(p, alert);
+ if (unlikely(ret < 0))
+ goto err;
+
+ ret = PacketToData(p, pa, alert, apn->ctx);
+ if (unlikely(ret < 0))
+ goto err;
+
+ if (PKT_IS_TCP(p) && (pa->flags & PACKET_ALERT_FLAG_STATE_MATCH)) {
+ uint8_t flag;
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag = FLOW_PKT_TOCLIENT;
+ } else {
+ flag = FLOW_PKT_TOSERVER;
+ }
+ ret = StreamSegmentForEach(p, flag,
+ PreludePrintStreamSegmentCallback,
+ (void *)alert);
+ }
+ if (unlikely(ret < 0))
+ goto err;
+
+ ret = idmef_alert_new_detect_time(alert, &time);
+ if (unlikely(ret < 0))
+ goto err;
+ idmef_time_set_from_timeval(time, &p->ts);
+
+ ret = idmef_time_new_from_gettimeofday(&time);
+ if (unlikely(ret < 0))
+ goto err;
+ idmef_alert_set_create_time(alert, time);
+
+ idmef_alert_set_analyzer(alert, idmef_analyzer_ref(prelude_client_get_analyzer(apn->ctx->client)), IDMEF_LIST_PREPEND);
+
+ /* finally, send event */
+ prelude_client_send_idmef(apn->ctx->client, idmef);
+ idmef_message_destroy(idmef);
+
+ SCReturnInt(TM_ECODE_OK);
+
+err:
+ if (idmef != NULL)
+ idmef_message_destroy(idmef);
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+void TmModuleAlertPreludeRegister (void)
+{
+ tmm_modules[TMM_ALERTPRELUDE].name = "AlertPrelude";
+ tmm_modules[TMM_ALERTPRELUDE].ThreadInit = AlertPreludeThreadInit;
+ tmm_modules[TMM_ALERTPRELUDE].Func = NULL;
+ tmm_modules[TMM_ALERTPRELUDE].ThreadDeinit = AlertPreludeThreadDeinit;
+ tmm_modules[TMM_ALERTPRELUDE].cap_flags = 0;
+ tmm_modules[TMM_ALERTPRELUDE].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule("AlertPrelude", "alert-prelude", AlertPreludeInitCtx,
+ AlertPreludeLogger, AlertPreludeCondition);
+}
+#endif /* PRELUDE */
+
diff --git a/framework/src/suricata/src/alert-prelude.h b/framework/src/suricata/src/alert-prelude.h
new file mode 100644
index 00000000..7a30e847
--- /dev/null
+++ b/framework/src/suricata/src/alert-prelude.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \file
+ *
+ * \author Pierre Chifflier <chifflier@edenwall.com>
+ * \author Yoann Vandoorselaere <yoann.v@prelude-ids.com>
+ */
+
+#ifndef __ALERT_PRELUDE_H__
+#define __ALERT_PRELUDE_H__
+
+void TmModuleAlertPreludeRegister (void);
+
+#endif /* __ALERT_PRELUDE_H__ */
diff --git a/framework/src/suricata/src/alert-syslog.c b/framework/src/suricata/src/alert-syslog.c
new file mode 100644
index 00000000..13151dd9
--- /dev/null
+++ b/framework/src/suricata/src/alert-syslog.c
@@ -0,0 +1,427 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Logs alerts in a line based text format in to syslog.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+
+#include "output.h"
+#include "alert-syslog.h"
+
+#include "util-classification-config.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-syslog.h"
+#include "util-optimize.h"
+#include "util-logopenfile.h"
+
+#ifndef OS_WIN32
+
+#define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
+#define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
+#define DEFAULT_ALERT_SYSLOG_LEVEL LOG_ERR
+#define MODULE_NAME "AlertSyslog"
+
+static int alert_syslog_level = DEFAULT_ALERT_SYSLOG_LEVEL;
+
+typedef struct AlertSyslogThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx* file_ctx;
+} AlertSyslogThread;
+
+/**
+ * \brief Function to clear the memory of the output context and closes the
+ * syslog interface
+ *
+ * \param output_ctx pointer to the output context to be cleared
+ */
+static void AlertSyslogDeInitCtx(OutputCtx *output_ctx)
+{
+ if (output_ctx != NULL) {
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ if (logfile_ctx != NULL) {
+ LogFileFreeCtx(logfile_ctx);
+ }
+ SCFree(output_ctx);
+ }
+ closelog();
+}
+
+/**
+ * \brief Create a new LogFileCtx for "syslog" output style.
+ *
+ * \param conf The configuration node for this output.
+ * \return A OutputCtx pointer on success, NULL on failure.
+ */
+OutputCtx *AlertSyslogInitCtx(ConfNode *conf)
+{
+ const char *facility_s = ConfNodeLookupChildValue(conf, "facility");
+ if (facility_s == NULL) {
+ facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR;
+ }
+
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("AlertSyslogInitCtx: Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
+ if (facility == -1) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\","
+ " now using \"%s\" as syslog facility", facility_s,
+ DEFAULT_ALERT_SYSLOG_FACILITY_STR);
+ facility = DEFAULT_ALERT_SYSLOG_FACILITY;
+ }
+
+ const char *level_s = ConfNodeLookupChildValue(conf, "level");
+ if (level_s != NULL) {
+ int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap());
+ if (level != -1) {
+ alert_syslog_level = level;
+ }
+ }
+
+ const char *ident = ConfNodeLookupChildValue(conf, "identity");
+ /* if null we just pass that to openlog, which will then
+ * figure it out by itself. */
+
+ openlog(ident, LOG_PID|LOG_NDELAY, facility);
+
+ OutputCtx *output_ctx = SCMalloc(sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCLogDebug("AlertSyslogInitCtx: Could not create new OutputCtx");
+ return NULL;
+ }
+ memset(output_ctx, 0x00, sizeof(OutputCtx));
+
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = AlertSyslogDeInitCtx;
+
+ SCLogInfo("Syslog output initialized");
+
+ return output_ctx;
+}
+
+/**
+ * \brief Function to initialize the AlertSystlogThread and sets the output
+ * context pointer
+ *
+ * \param tv Pointer to the threadvars
+ * \param initdata Pointer to the output context
+ * \param data pointer to pointer to point to the AlertSyslogThread
+ */
+static TmEcode AlertSyslogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ if(initdata == NULL) {
+ SCLogDebug("Error getting context for AlertSyslog. \"initdata\" "
+ "argument NULL");
+ return TM_ECODE_FAILED;
+ }
+
+ AlertSyslogThread *ast = SCMalloc(sizeof(AlertSyslogThread));
+ if (unlikely(ast == NULL))
+ return TM_ECODE_FAILED;
+
+ memset(ast, 0, sizeof(AlertSyslogThread));
+
+ /** Use the Ouptut Context (file pointer and mutex) */
+ ast->file_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)ast;
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Function to deinitialize the AlertSystlogThread
+ *
+ * \param tv Pointer to the threadvars
+ * \param data pointer to the AlertSyslogThread to be cleared
+ */
+static TmEcode AlertSyslogThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertSyslogThread *ast = (AlertSyslogThread *)data;
+ if (ast == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(ast, 0, sizeof(AlertSyslogThread));
+
+ SCFree(ast);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Function which is called to print the IPv4 alerts to the syslog
+ *
+ * \param tv Pointer to the threadvars
+ * \param p Pointer to the packet
+ * \param data pointer to the AlertSyslogThread
+ *
+ * \return On succes return TM_ECODE_OK
+ */
+static TmEcode AlertSyslogIPv4(ThreadVars *tv, const Packet *p, void *data)
+{
+ AlertSyslogThread *ast = (AlertSyslogThread *)data;
+ int i;
+ char *action = "";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ SCMutexLock(&ast->file_ctx->fp_mutex);
+
+ ast->file_ctx->alerts += p->alerts.cnt;
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ char srcip[16], dstip[16];
+
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+
+ if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "[Drop] ";
+ } else if (pa->action & ACTION_DROP) {
+ action = "[wDrop] ";
+ }
+
+ if (SCProtoNameValid(IPV4_GET_IPPROTO(p)) == TRUE) {
+ syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
+ PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]"
+ " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", action, pa->s->gid,
+ pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
+ known_proto[IPV4_GET_IPPROTO(p)], srcip, p->sp, dstip, p->dp);
+ } else {
+ syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
+ PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]"
+ " {PROTO:%03" PRIu32 "} %s:%" PRIu32 " -> %s:%" PRIu32 "",
+ action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
+ pa->s->prio, IPV4_GET_IPPROTO(p), srcip, p->sp, dstip, p->dp);
+ }
+ }
+ SCMutexUnlock(&ast->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Function which is called to print the IPv6 alerts to the syslog
+ *
+ * \param tv Pointer to the threadvars
+ * \param p Pointer to the packet
+ * \param data pointer to the AlertSyslogThread
+ *
+ * \return On succes return TM_ECODE_OK
+ */
+static TmEcode AlertSyslogIPv6(ThreadVars *tv, const Packet *p, void *data)
+{
+ AlertSyslogThread *ast = (AlertSyslogThread *)data;
+ int i;
+ char *action = "";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ SCMutexLock(&ast->file_ctx->fp_mutex);
+
+ ast->file_ctx->alerts += p->alerts.cnt;
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ char srcip[46], dstip[46];
+
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+
+ if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "[Drop] ";
+ } else if (pa->action & ACTION_DROP) {
+ action = "[wDrop] ";
+ }
+
+ if (SCProtoNameValid(IPV6_GET_L4PROTO(p)) == TRUE) {
+ syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
+ "" PRIu32 "] %s [Classification: %s] [Priority: %"
+ "" PRIu32 "] {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "",
+ action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
+ pa->s->prio, known_proto[IPV6_GET_L4PROTO(p)], srcip, p->sp,
+ dstip, p->dp);
+
+ } else {
+ syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
+ "" PRIu32 "] %s [Classification: %s] [Priority: %"
+ "" PRIu32 "] {PROTO:%03" PRIu32 "} %s:%" PRIu32 " -> %s:%" PRIu32 "",
+ action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
+ pa->s->prio, IPV6_GET_L4PROTO(p), srcip, p->sp, dstip, p->dp);
+ }
+
+ }
+ SCMutexUnlock(&ast->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Function which is called to print the decode alerts to the syslog
+ *
+ * \param tv Pointer to the threadvars
+ * \param p Pointer to the packet
+ * \param data pointer to the AlertSyslogThread
+ * \param pq pointer the to packet queue
+ * \param postpq pointer to the post processed packet queue
+ *
+ * \return On succes return TM_ECODE_OK
+ */
+static TmEcode AlertSyslogDecoderEvent(ThreadVars *tv, const Packet *p, void *data)
+{
+ AlertSyslogThread *ast = (AlertSyslogThread *)data;
+ int i;
+ char *action = "";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ SCMutexLock(&ast->file_ctx->fp_mutex);
+
+ ast->file_ctx->alerts += p->alerts.cnt;
+ char temp_buf_hdr[512];
+ char temp_buf_pkt[65] = "";
+ char temp_buf_tail[32];
+ char alert[2048] = "";
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "[Drop] ";
+ } else if (pa->action & ACTION_DROP) {
+ action = "[wDrop] ";
+ }
+
+ snprintf(temp_buf_hdr, sizeof(temp_buf_hdr), "%s[%" PRIu32 ":%" PRIu32
+ ":%" PRIu32 "] %s [Classification: %s] [Priority: %" PRIu32
+ "] [**] [Raw pkt: ", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg,
+ pa->s->class_msg, pa->s->prio);
+ strlcpy(alert, temp_buf_hdr, sizeof(alert));
+
+ PrintRawLineHexBuf(temp_buf_pkt, sizeof(temp_buf_pkt), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
+ strlcat(alert, temp_buf_pkt, sizeof(alert));
+
+ if (p->pcap_cnt != 0) {
+ snprintf(temp_buf_tail, sizeof(temp_buf_tail), "] [pcap file packet: %"PRIu64"]",
+ p->pcap_cnt);
+ } else {
+ temp_buf_tail[0] = ']';
+ temp_buf_tail[1] = '\0';
+ }
+ strlcat(alert, temp_buf_tail, sizeof(alert));
+
+ syslog(alert_syslog_level, "%s", alert);
+ }
+ SCMutexUnlock(&ast->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Function to print the total alert while closing the engine
+ *
+ * \param tv Pointer to the output threadvars
+ * \param data Pointer to the AlertSyslogThread data
+ */
+static void AlertSyslogExitPrintStats(ThreadVars *tv, void *data)
+{
+ AlertSyslogThread *ast = (AlertSyslogThread *)data;
+ if (ast == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) Alerts %" PRIu64 "", tv->name, ast->file_ctx->alerts);
+}
+
+static int AlertSyslogCondition(ThreadVars *tv, const Packet *p)
+{
+ return (p->alerts.cnt > 0 ? TRUE : FALSE);
+}
+
+static int AlertSyslogLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ if (PKT_IS_IPV4(p)) {
+ return AlertSyslogIPv4(tv, p, thread_data);
+ } else if (PKT_IS_IPV6(p)) {
+ return AlertSyslogIPv6(tv, p, thread_data);
+ } else if (p->events.cnt > 0) {
+ return AlertSyslogDecoderEvent(tv, p, thread_data);
+ }
+
+ return TM_ECODE_OK;
+}
+
+#endif /* !OS_WIN32 */
+
+/** \brief Function to register the AlertSyslog module */
+void TmModuleAlertSyslogRegister (void)
+{
+#ifndef OS_WIN32
+ tmm_modules[TMM_ALERTSYSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_ALERTSYSLOG].ThreadInit = AlertSyslogThreadInit;
+ tmm_modules[TMM_ALERTSYSLOG].Func = NULL;
+ tmm_modules[TMM_ALERTSYSLOG].ThreadExitPrintStats = AlertSyslogExitPrintStats;
+ tmm_modules[TMM_ALERTSYSLOG].ThreadDeinit = AlertSyslogThreadDeinit;
+ tmm_modules[TMM_ALERTSYSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_ALERTSYSLOG].cap_flags = 0;
+ tmm_modules[TMM_ALERTSYSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "syslog",
+ AlertSyslogInitCtx, AlertSyslogLogger, AlertSyslogCondition);
+
+#endif /* !OS_WIN32 */
+}
diff --git a/framework/src/suricata/src/alert-syslog.h b/framework/src/suricata/src/alert-syslog.h
new file mode 100644
index 00000000..976a1122
--- /dev/null
+++ b/framework/src/suricata/src/alert-syslog.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * alert syslog modeule header file
+ *
+ */
+
+#ifndef __ALERT_SYSLOG_H__
+#define __ALERT_SYSLOG_H__
+
+void TmModuleAlertSyslogRegister(void);
+
+#endif /* __ALERT_SYSLOG_H__ */
+
diff --git a/framework/src/suricata/src/alert-unified2-alert.c b/framework/src/suricata/src/alert-unified2-alert.c
new file mode 100644
index 00000000..ede624c4
--- /dev/null
+++ b/framework/src/suricata/src/alert-unified2-alert.c
@@ -0,0 +1,1960 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ *
+ * Logs alerts in a format compatible to Snort's unified2 format, so it should
+ * be readable by Barnyard2.
+ */
+
+#include "suricata-common.h"
+#include "runmodes.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+#include "pkt-var.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "alert-unified2-alert.h"
+#include "decode-ipv4.h"
+
+#include "flow.h"
+
+#include "host.h"
+#include "util-profiling.h"
+#include "decode.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-byte.h"
+#include "util-misc.h"
+#include "util-logopenfile.h"
+
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-htp-xff.h"
+
+#include "output.h"
+#include "alert-unified2-alert.h"
+#include "util-privs.h"
+
+#include "stream.h"
+#include "stream-tcp-inline.h"
+
+#include "util-optimize.h"
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+#define DEFAULT_LOG_FILENAME "unified2.alert"
+
+/**< Default log file limit in MB. */
+#define DEFAULT_LIMIT 32 * 1024 * 1024
+
+/**< Minimum log file limit in MB. */
+#define MIN_LIMIT 1 * 1024 * 1024
+
+/* Default Sensor ID value */
+static uint32_t sensor_id = 0;
+
+/**
+ * Unified2 Extra Data Header
+ *
+ */
+typedef struct Unified2ExtraDataHdr_ {
+ uint32_t event_type;
+ uint32_t event_length;
+} __attribute__((__packed__)) Unified2ExtraDataHdr;
+
+/**
+ * Unified2 Extra Data (currently used only for XFF)
+ *
+ */
+typedef struct Unified2ExtraData_ {
+ uint32_t sensor_id;
+ uint32_t event_id;
+ uint32_t event_second;
+ uint32_t type; /* EventInfo */
+ uint32_t data_type; /*EventDataType */
+ uint32_t blob_length; /* Length of the data + sizeof(blob_length) + sizeof(data_type)*/
+} Unified2ExtraData;
+
+/**
+ * Unified2 file header struct
+ *
+ * Used for storing file header options.
+ */
+typedef struct Unified2AlertFileHeader_ {
+ uint32_t type; /**< unified2 type header */
+ uint32_t length; /**< unified2 struct size length */
+} Unified2AlertFileHeader;
+
+/**
+ * Unified2 Ipv4 struct
+ *
+ * Used for storing ipv4 type values.
+ */
+typedef struct AlertIPv4Unified2_ {
+ uint32_t sensor_id; /**< sendor id */
+ uint32_t event_id; /**< event id */
+ uint32_t event_second; /**< event second */
+ uint32_t event_microsecond; /**< event microsecond */
+ uint32_t signature_id; /**< signature id */
+ uint32_t generator_id; /**< generator id */
+ uint32_t signature_revision; /**< signature revision */
+ uint32_t classification_id; /**< classification id */
+ uint32_t priority_id; /**< priority id */
+ uint32_t src_ip; /**< source ip */
+ uint32_t dst_ip; /**< destination ip */
+ uint16_t sp; /**< source port */
+ uint16_t dp; /**< destination port */
+ uint8_t protocol; /**< protocol */
+ uint8_t packet_action; /**< packet action */
+} AlertIPv4Unified2;
+
+/**
+ * Unified2 Ipv6 type struct
+ *
+ * Used for storing ipv6 type values.
+ */
+typedef struct AlertIPv6Unified2_ {
+ uint32_t sensor_id; /**< sendor id */
+ uint32_t event_id; /**< event id */
+ uint32_t event_second; /**< event second */
+ uint32_t event_microsecond; /**< event microsecond */
+ uint32_t signature_id; /**< signature id */
+ uint32_t generator_id; /**< generator id */
+ uint32_t signature_revision; /**< signature revision */
+ uint32_t classification_id; /**< classification id */
+ uint32_t priority_id; /**< priority id */
+ struct in6_addr src_ip; /**< source ip */
+ struct in6_addr dst_ip; /**< destination ip */
+ uint16_t sp; /**< source port */
+ uint16_t dp; /**< destination port */
+ uint8_t protocol; /**< protocol */
+ uint8_t packet_action; /**< packet action */
+} AlertIPv6Unified2;
+
+/**
+ * Unified2 packet type struct
+ *
+ * Used for storing packet type values.
+ */
+typedef struct AlertUnified2Packet_ {
+ uint32_t sensor_id; /**< sensor id */
+ uint32_t event_id; /**< event id */
+ uint32_t event_second; /**< event second */
+ uint32_t packet_second; /**< packet second */
+ uint32_t packet_microsecond; /**< packet microsecond */
+ uint32_t linktype; /**< link type */
+ uint32_t packet_length; /**< packet length */
+ uint8_t packet_data[4]; /**< packet data */
+} Unified2Packet;
+
+/** Extracted XFF IP is v4 */
+#define UNIFIED2_ALERT_XFF_IPV4 8
+/** Extracted XFF IP is v4 */
+#define UNIFIED2_ALERT_XFF_IPV6 16
+
+typedef struct Unified2AlertFileCtx_ {
+ LogFileCtx *file_ctx;
+ HttpXFFCfg *xff_cfg;
+} Unified2AlertFileCtx;
+
+/**
+ * Unified2 thread vars
+ *
+ * Used for storing file options.
+ */
+typedef struct Unified2AlertThread_ {
+ Unified2AlertFileCtx *unified2alert_ctx; /**< LogFileCtx pointer */
+ uint8_t *data; /**< Per function and thread data */
+ /** Pointer to the Unified2AlertFileHeader contained in
+ * the pointer data. */
+ Unified2AlertFileHeader *hdr;
+ /** Pointer to the Unified2Packet contained in
+ * the pointer data. */
+ Unified2Packet *phdr;
+ /** Pointer to the IPv4 or IPv6 header contained in
+ * the pointer data. */
+ void *iphdr;
+ int datalen; /**< Length of per function and thread data */
+ int offset; /**< Offset used to now where to fill data */
+ int length; /**< Length of data for current alert */
+ uint8_t xff_flags; /**< XFF flags for the current alert */
+ uint32_t xff_ip[4]; /**< The XFF reported IP address for the current alert */
+ uint32_t event_id;
+} Unified2AlertThread;
+
+#define UNIFIED2_PACKET_SIZE (sizeof(Unified2Packet) - 4)
+
+SC_ATOMIC_DECLARE(unsigned int, unified2_event_id); /**< Atomic counter, to link relative event */
+
+/** prototypes */
+//TmEcode Unified2Alert (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode Unified2AlertThreadInit(ThreadVars *, void *, void **);
+TmEcode Unified2AlertThreadDeinit(ThreadVars *, void *);
+static int Unified2IPv4TypeAlert(ThreadVars *, const Packet *, void *);
+static int Unified2IPv6TypeAlert(ThreadVars *, const Packet *, void *);
+static int Unified2PacketTypeAlert(Unified2AlertThread *, const Packet *, uint32_t, int);
+void Unified2RegisterTests(void);
+int Unified2AlertOpenFileCtx(LogFileCtx *, const char *);
+static void Unified2AlertDeInitCtx(OutputCtx *);
+
+int Unified2Condition(ThreadVars *tv, const Packet *p);
+int Unified2Logger(ThreadVars *tv, void *data, const Packet *p);
+
+#define MODULE_NAME "Unified2Alert"
+
+void TmModuleUnified2AlertRegister(void)
+{
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].name = MODULE_NAME;
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].ThreadInit = Unified2AlertThreadInit;
+// tmm_modules[TMM_ALERTUNIFIED2ALERT].Func = Unified2Alert;
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].ThreadDeinit = Unified2AlertThreadDeinit;
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].RegisterTests = Unified2RegisterTests;
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].cap_flags = 0;
+ tmm_modules[TMM_ALERTUNIFIED2ALERT].flags = TM_FLAG_LOGAPI_TM;
+
+ //OutputRegisterModule(MODULE_NAME, "unified2-alert", Unified2AlertInitCtx);
+ OutputRegisterPacketModule(MODULE_NAME, "unified2-alert",
+ Unified2AlertInitCtx, Unified2Logger, Unified2Condition);
+}
+
+/**
+ * \brief Function to close unified2 file
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param aun Unified2 thread variable.
+ */
+
+int Unified2AlertCloseFile(ThreadVars *t, Unified2AlertThread *aun)
+{
+ if (aun->unified2alert_ctx->file_ctx->fp != NULL) {
+ fclose(aun->unified2alert_ctx->file_ctx->fp);
+ }
+ aun->unified2alert_ctx->file_ctx->size_current = 0;
+
+ return 0;
+}
+
+/**
+ * \brief Function to rotate unified2 file
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param aun Unified2 thread variable.
+ * \retval 0 on succces
+ * \retval -1 on failure
+ */
+
+int Unified2AlertRotateFile(ThreadVars *t, Unified2AlertThread *aun)
+{
+ if (Unified2AlertCloseFile(t,aun) < 0) {
+ SCLogError(SC_ERR_UNIFIED2_ALERT_GENERIC,
+ "Error: Unified2AlertCloseFile failed");
+ return -1;
+ }
+ if (Unified2AlertOpenFileCtx(aun->unified2alert_ctx->file_ctx,aun->unified2alert_ctx->
+ file_ctx->prefix) < 0) {
+ SCLogError(SC_ERR_UNIFIED2_ALERT_GENERIC,
+ "Error: Unified2AlertOpenFileCtx, open new log file failed");
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * \brief Wrapper for fwrite
+ *
+ * This function is basically a wrapper for fwrite which take
+ * in charge a size counter.
+ *
+ * \return 1 in case of success
+ */
+static int Unified2Write(Unified2AlertThread *aun)
+{
+ int ret;
+
+ ret = fwrite(aun->data, aun->length, 1, aun->unified2alert_ctx->file_ctx->fp);
+ if (ret != 1) {
+ SCLogError(SC_ERR_FWRITE, "Error: fwrite failed: %s", strerror(errno));
+ return -1;
+ }
+
+ aun->unified2alert_ctx->file_ctx->size_current += aun->length;
+ return 1;
+}
+
+int Unified2Condition(ThreadVars *tv, const Packet *p) {
+ if (likely(p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG)))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * \brief Unified2 main entry function
+ *
+ * \retval TM_ECODE_OK all is good
+ * \retval TM_ECODE_FAILED serious error
+ */
+int Unified2Logger(ThreadVars *t, void *data, const Packet *p)
+{
+ int ret = 0;
+ Unified2AlertThread *aun = (Unified2AlertThread *)data;
+ aun->xff_flags = XFF_DISABLED;
+
+ HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
+
+ /* overwrite mode can only work per u2 block, not per individual
+ * alert. So we'll look for an XFF record once */
+ if ((xff_cfg->flags & XFF_OVERWRITE) && p->flow != NULL) {
+ char buffer[XFF_MAXLEN];
+ int have_xff_ip = 0;
+
+ FLOWLOCK_RDLOCK(p->flow);
+ if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
+ have_xff_ip = HttpXFFGetIP(p, xff_cfg, buffer, XFF_MAXLEN);
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ if (have_xff_ip) {
+ /** Be sure that we have a nice zeroed buffer */
+ memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+
+ /** We can only have override mode if packet IP version matches
+ * the XFF IP version, otherwise fall-back to extra data */
+ if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+ if (PKT_IS_IPV4(p)) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_OVERWRITE);
+ } else {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+ }
+ } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+ if (PKT_IS_IPV6(p)) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_OVERWRITE);
+ } else {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
+ }
+ }
+ }
+ }
+
+ if (PKT_IS_IPV4(p)) {
+ ret = Unified2IPv4TypeAlert (t, p, data);
+ } else if(PKT_IS_IPV6(p)) {
+ ret = Unified2IPv6TypeAlert (t, p, data);
+ } else {
+ /* we're only supporting IPv4 and IPv6 */
+ return TM_ECODE_OK;
+ }
+
+ if (ret != 0) {
+ return TM_ECODE_FAILED;
+ }
+
+ return TM_ECODE_OK;
+}
+
+typedef struct _FakeIPv4Hdr {
+ IPV4Hdr ip4h;
+ TCPHdr tcph;
+} __attribute__((__packed__)) FakeIPv4Hdr;
+
+static int Unified2ForgeFakeIPv4Header(FakeIPv4Hdr *fakehdr, const Packet *p, int pkt_len, char invert)
+{
+ fakehdr->ip4h.ip_verhl = p->ip4h->ip_verhl;
+ fakehdr->ip4h.ip_proto = p->ip4h->ip_proto;
+ if (! invert) {
+ fakehdr->ip4h.s_ip_src.s_addr = p->ip4h->s_ip_src.s_addr;
+ fakehdr->ip4h.s_ip_dst.s_addr = p->ip4h->s_ip_dst.s_addr;
+ } else {
+ fakehdr->ip4h.s_ip_dst.s_addr = p->ip4h->s_ip_src.s_addr;
+ fakehdr->ip4h.s_ip_src.s_addr = p->ip4h->s_ip_dst.s_addr;
+ }
+ fakehdr->ip4h.ip_len = htons((uint16_t)pkt_len);
+
+ if (! invert) {
+ fakehdr->tcph.th_sport = p->tcph->th_sport;
+ fakehdr->tcph.th_dport = p->tcph->th_dport;
+ } else {
+ fakehdr->tcph.th_dport = p->tcph->th_sport;
+ fakehdr->tcph.th_sport = p->tcph->th_dport;
+ }
+ fakehdr->tcph.th_offx2 = 0x50; /* just the TCP header, no options */
+
+ return 1;
+}
+
+typedef struct _FakeIPv6Hdr {
+ IPV6Hdr ip6h;
+ TCPHdr tcph;
+} __attribute__((__packed__)) FakeIPv6Hdr;
+
+/**
+ * \param payload_len length of the payload
+ */
+static int Unified2ForgeFakeIPv6Header(FakeIPv6Hdr *fakehdr, const Packet *p, int payload_len, char invert)
+{
+ fakehdr->ip6h.s_ip6_vfc = p->ip6h->s_ip6_vfc;
+ fakehdr->ip6h.s_ip6_nxt = IPPROTO_TCP;
+ fakehdr->ip6h.s_ip6_plen = htons(sizeof(TCPHdr) + payload_len);
+ if (!invert) {
+ memcpy(fakehdr->ip6h.s_ip6_addrs, p->ip6h->s_ip6_addrs, 32);
+ } else {
+ memcpy(fakehdr->ip6h.s_ip6_src, p->ip6h->s_ip6_dst, 16);
+ memcpy(fakehdr->ip6h.s_ip6_dst, p->ip6h->s_ip6_src, 16);
+ }
+ if (! invert) {
+ fakehdr->tcph.th_sport = p->tcph->th_sport;
+ fakehdr->tcph.th_dport = p->tcph->th_dport;
+ } else {
+ fakehdr->tcph.th_dport = p->tcph->th_sport;
+ fakehdr->tcph.th_sport = p->tcph->th_dport;
+ }
+ fakehdr->tcph.th_offx2 = 0x50; /* just the TCP header, no options */
+
+ return 1;
+}
+
+/**
+ * \brief Write a faked Packet in unified2 file for each stream segment.
+ */
+static int Unified2PrintStreamSegmentCallback(const Packet *p, void *data, uint8_t *buf, uint32_t buflen)
+{
+ int ret = 1;
+ Unified2AlertThread *aun = (Unified2AlertThread *)data;
+ Unified2AlertFileHeader *hdr = (Unified2AlertFileHeader*)(aun->data);
+ Unified2Packet *phdr = (Unified2Packet *)(hdr + 1);
+ /** Prepare the pointers to extra data structures should they be required.
+ * If they are required we will shift the *hdr and the *phdr */
+ Unified2AlertFileHeader *eu2hdr = (Unified2AlertFileHeader*)(aun->data);
+ Unified2ExtraDataHdr *ehdr = (Unified2ExtraDataHdr *)(eu2hdr + 1);
+ Unified2ExtraData *dhdr = (Unified2ExtraData *) (ehdr + 1);
+ uint32_t *edxff = (uint32_t *) (dhdr + 1);
+
+ aun->length = 0;
+ aun->offset = 0;
+
+ // If XFF is in extra data mode...
+ if (aun->xff_flags & XFF_EXTRADATA) {
+ memset(dhdr, 0, sizeof(Unified2ExtraData));
+
+ if (aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4) {
+ eu2hdr->type = htonl (UNIFIED2_IDS_EVENT_EXTRADATA_TYPE);
+ eu2hdr->length = htonl(sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + sizeof(uint32_t));
+ ehdr->event_type = htonl(UNIFIED2_EXTRADATA_TYPE_EXTRA_DATA);
+ ehdr->event_length = htonl(sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + sizeof(uint32_t));
+ dhdr->sensor_id = 0;
+ dhdr->event_id = aun->event_id;
+ dhdr->event_second = htonl(p->ts.tv_sec);
+ dhdr->data_type = htonl(UNIFIED2_EXTRADATA_TYPE_BLOB);
+ dhdr->type = htonl(UNIFIED2_EXTRADATA_CLIENT_IPV4_TYPE);
+ dhdr->blob_length = htonl(3 * sizeof(uint32_t));
+ aun->length += sizeof(Unified2AlertFileHeader) + sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + sizeof(uint32_t);
+ aun->offset += sizeof(Unified2AlertFileHeader) + sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + sizeof(uint32_t);
+ *edxff=aun->xff_ip[0];
+ /** Shift the *hdr and *phdr pointers */
+ hdr = (Unified2AlertFileHeader*)(edxff + 1);
+ phdr = (Unified2Packet *)(hdr + 1);
+ }
+ else if (aun->xff_flags & UNIFIED2_ALERT_XFF_IPV6) {
+ eu2hdr->type = htonl(UNIFIED2_IDS_EVENT_EXTRADATA_TYPE);
+ eu2hdr->length = htonl(sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + 4 * sizeof(uint32_t));
+ ehdr->event_type = htonl(UNIFIED2_EXTRADATA_TYPE_EXTRA_DATA);
+ ehdr->event_length = htonl(sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + 4 * sizeof(uint32_t));
+ dhdr->sensor_id = 0;
+ dhdr->event_id = aun->event_id;
+ dhdr->event_second = htonl(p->ts.tv_sec);
+ dhdr->data_type = htonl(UNIFIED2_EXTRADATA_TYPE_BLOB);
+ dhdr->type = htonl(UNIFIED2_EXTRADATA_CLIENT_IPV6_TYPE);
+ dhdr->blob_length = htonl(6 * sizeof(uint32_t));
+ aun->length += sizeof(Unified2AlertFileHeader) + sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + 4 * sizeof(uint32_t);
+ aun->offset += sizeof(Unified2AlertFileHeader) + sizeof (Unified2ExtraDataHdr)
+ + sizeof (Unified2ExtraData) + 4 * sizeof(uint32_t);
+ memcpy(edxff, aun->xff_ip, 4 * sizeof(uint32_t));
+ /** Shift the *hdr and *phdr pointers */
+ hdr = (Unified2AlertFileHeader*)(edxff + 4);
+ phdr = (Unified2Packet *)(hdr + 1);
+ }
+ }
+
+ int ethh_offset = 0;
+ EthernetHdr ethhdr = { {0,0,0,0,0,0}, {0,0,0,0,0,0}, htons(ETHERNET_TYPE_IPV6) };
+ uint32_t hdr_length = 0;
+ int datalink = p->datalink;
+
+ memset(hdr, 0, sizeof(Unified2AlertFileHeader));
+ memset(phdr, 0, sizeof(Unified2Packet));
+
+ hdr->type = htonl(UNIFIED2_PACKET_TYPE);
+ aun->hdr = hdr;
+
+ phdr->sensor_id = htonl(sensor_id);
+ phdr->linktype = htonl(datalink);
+ phdr->event_id = aun->event_id;
+ phdr->event_second = phdr->packet_second = htonl(p->ts.tv_sec);
+ phdr->packet_microsecond = htonl(p->ts.tv_usec);
+ aun->phdr = phdr;
+
+ if (p->datalink != DLT_EN10MB) {
+ /* We have raw data here */
+ phdr->linktype = htonl(DLT_RAW);
+ datalink = DLT_RAW;
+ }
+
+ aun->length += sizeof(Unified2AlertFileHeader) + UNIFIED2_PACKET_SIZE;
+ aun->offset += sizeof(Unified2AlertFileHeader) + UNIFIED2_PACKET_SIZE;
+
+ /* Include Packet header */
+ if (PKT_IS_IPV4(p)) {
+ FakeIPv4Hdr fakehdr;
+ hdr_length = sizeof(FakeIPv4Hdr);
+
+ if (p->datalink == DLT_EN10MB) {
+ /* Fake this */
+ ethh_offset = 14;
+ datalink = DLT_EN10MB;
+ phdr->linktype = htonl(datalink);
+ aun->length += ethh_offset;
+
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data");
+ goto error;
+ }
+ ethhdr.eth_type = htons(ETHERNET_TYPE_IP);
+
+ memcpy(aun->data + aun->offset, &ethhdr, 14);
+ aun->offset += ethh_offset;
+ }
+
+ memset(&fakehdr, 0, hdr_length);
+ aun->length += hdr_length;
+ Unified2ForgeFakeIPv4Header(&fakehdr, p, hdr_length + buflen, 0);
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data");
+ goto error;
+ }
+ /** If XFF is in overwrite mode... */
+ if (aun->xff_flags & XFF_OVERWRITE) {
+ BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV6);
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ fakehdr.ip4h.s_ip_dst.s_addr = aun->xff_ip[0];
+ } else {
+ fakehdr.ip4h.s_ip_src.s_addr = aun->xff_ip[0];
+ }
+ }
+
+ memcpy(aun->data + aun->offset, &fakehdr, hdr_length);
+ aun->iphdr = (void *)(aun->data + aun->offset);
+ aun->offset += hdr_length;
+
+ } else if (PKT_IS_IPV6(p)) {
+ FakeIPv6Hdr fakehdr;
+ hdr_length = sizeof(FakeIPv6Hdr);
+
+ if (p->datalink == DLT_EN10MB) {
+ /* Fake this */
+ ethh_offset = 14;
+ datalink = DLT_EN10MB;
+ phdr->linktype = htonl(datalink);
+ aun->length += ethh_offset;
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data");
+ goto error;
+ }
+ ethhdr.eth_type = htons(ETHERNET_TYPE_IPV6);
+
+ memcpy(aun->data + aun->offset, &ethhdr, 14);
+ aun->offset += ethh_offset;
+ }
+
+ memset(&fakehdr, 0, hdr_length);
+ Unified2ForgeFakeIPv6Header(&fakehdr, p, buflen, 1);
+
+ aun->length += hdr_length;
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data");
+ goto error;
+ }
+ /** If XFF is in overwrite mode... */
+ if (aun->xff_flags & XFF_OVERWRITE) {
+ BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4);
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ memcpy(fakehdr.ip6h.s_ip6_dst, aun->xff_ip, 4 * sizeof(uint32_t));
+ } else {
+ memcpy(fakehdr.ip6h.s_ip6_src, aun->xff_ip, 4 * sizeof(uint32_t));
+ }
+ }
+
+ memcpy(aun->data + aun->offset, &fakehdr, hdr_length);
+ aun->iphdr = (void *)(aun->data + aun->offset);
+ aun->offset += hdr_length;
+ } else {
+ goto error;
+ }
+
+ /* update unified2 headers for length */
+ aun->hdr->length = htonl(UNIFIED2_PACKET_SIZE + ethh_offset +
+ hdr_length + buflen);
+ aun->phdr->packet_length = htonl(ethh_offset + hdr_length + buflen);
+
+ /* copy stream segment payload in */
+ aun->length += buflen;
+
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread"
+ " data: %d vs %d", aun->length, aun->datalen);
+ goto error;
+ }
+
+ memcpy(aun->data + aun->offset, buf, buflen);
+ aun->offset += buflen;
+
+ /* rebuild checksum */
+ if (PKT_IS_IPV6(p)) {
+ FakeIPv6Hdr *fakehdr = (FakeIPv6Hdr *)aun->iphdr;
+
+ fakehdr->tcph.th_sum = TCPV6CalculateChecksum(fakehdr->ip6h.s_ip6_addrs,
+ (uint16_t *)&fakehdr->tcph, buflen + sizeof(TCPHdr));
+ } else {
+ FakeIPv4Hdr *fakehdr = (FakeIPv4Hdr *)aun->iphdr;
+
+ fakehdr->tcph.th_sum = TCPCalculateChecksum(fakehdr->ip4h.s_ip_addrs,
+ (uint16_t *)&fakehdr->tcph, buflen + sizeof(TCPHdr));
+ fakehdr->ip4h.ip_csum = IPV4CalculateChecksum((uint16_t *)&fakehdr->ip4h,
+ IPV4_GET_RAW_HLEN(&fakehdr->ip4h));
+ }
+
+ /* write out */
+ ret = Unified2Write(aun);
+ if (ret != 1) {
+ goto error;
+ }
+ return 1;
+
+error:
+ aun->length = 0;
+ aun->offset = 0;
+ return -1;
+}
+
+
+/**
+ * \brief Function to fill unified2 packet format into the file. If the alert
+ * was generated based on a stream chunk we call the stream function
+ * to generate the record.
+ *
+ * Barnyard2 doesn't like DLT_RAW + IPv6, so if we don't have an ethernet
+ * header, we create a fake one.
+ *
+ * No need to lock here, since it's already locked.
+ *
+ * \param aun thread local data
+ * \param p Packet
+ * \param stream pointer to stream chunk
+ * \param event_id unique event id
+ * \param stream state/stream match, try logging stream segments
+ *
+ * \retval 0 on succces
+ * \retval -1 on failure
+ */
+static int Unified2PacketTypeAlert(Unified2AlertThread *aun, const Packet *p, uint32_t event_id, int stream)
+{
+ int ret = 0;
+
+ /* try stream logging first */
+ if (stream) {
+ SCLogDebug("logging the state");
+ uint8_t flag;
+
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag = FLOW_PKT_TOCLIENT;
+ } else {
+ flag = FLOW_PKT_TOSERVER;
+ }
+
+ /* make event id available to callback */
+ aun->event_id = event_id;
+
+ /* run callback for all segments in the stream */
+ ret = StreamSegmentForEach(p, flag, Unified2PrintStreamSegmentCallback, (void *)aun);
+ }
+
+ /* or no segment could been logged or no segment have been logged */
+ if (ret == 0) {
+ SCLogDebug("no stream, no state: falling back to payload logging");
+
+ Unified2AlertFileHeader *hdr = (Unified2AlertFileHeader*)(aun->data);
+ Unified2Packet *phdr = (Unified2Packet *)(hdr + 1);
+ int len = (sizeof(Unified2AlertFileHeader) + UNIFIED2_PACKET_SIZE);
+ int datalink = p->datalink;
+#ifdef HAVE_OLD_BARNYARD2
+ int ethh_offset = 0;
+ EthernetHdr ethhdr = { {0,0,0,0,0,0}, {0,0,0,0,0,0}, htons(ETHERNET_TYPE_IPV6) };
+#endif
+ memset(hdr, 0, sizeof(Unified2AlertFileHeader));
+ memset(phdr, 0, sizeof(Unified2Packet));
+
+ hdr->type = htonl(UNIFIED2_PACKET_TYPE);
+ aun->hdr = hdr;
+
+ phdr->sensor_id = htonl(sensor_id);
+ phdr->linktype = htonl(datalink);
+ phdr->event_id = event_id;
+ phdr->event_second = phdr->packet_second = htonl(p->ts.tv_sec);
+ phdr->packet_microsecond = htonl(p->ts.tv_usec);
+ aun->phdr = phdr;
+
+ /* we need to reset offset and length which could
+ * have been modified by the segment logging */
+ aun->offset = len;
+ len += GET_PKT_LEN(p);
+ aun->length = len;
+
+ /* Unified 2 packet header is the one of the packet. */
+ phdr->linktype = htonl(p->datalink);
+#ifdef HAVE_OLD_BARNYARD2
+ /* Fake datalink to avoid bug with old barnyard2 */
+ if (PKT_IS_IPV6(p) && (!p->ethh)) {
+ /* Fake this */
+ ethh_offset = 14;
+ datalink = DLT_EN10MB;
+ phdr->linktype = htonl(datalink);
+ aun->length += ethh_offset;
+ if (aun->length > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data: %d vs %d",
+ len, aun->datalen - aun->offset);
+ return -1;
+ }
+ ethhdr.eth_type = htons(ETHERNET_TYPE_IPV6);
+
+ memcpy(aun->data + aun->offset, &ethhdr, 14);
+ aun->offset += ethh_offset;
+ }
+#endif
+
+ if (len > aun->datalen) {
+ SCLogError(SC_ERR_INVALID_VALUE, "len is too big for thread data: %d vs %d",
+ len, aun->datalen - aun->offset);
+ return -1;
+ }
+ hdr->length = htonl(UNIFIED2_PACKET_SIZE + GET_PKT_LEN(p));
+ phdr->packet_length = htonl(GET_PKT_LEN(p));
+ memcpy(aun->data + aun->offset, GET_PKT_DATA(p), GET_PKT_LEN(p));
+
+ ret = Unified2Write(aun);
+ }
+
+ if (ret < 1) {
+ return -1;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Function to fill unified2 ipv6 ids type format into the file.
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param p Packet struct used to decide for ipv4 or ipv6
+ * \param data Unified2 thread data.
+ *
+ * \retval 0 on succces
+ * \retval -1 on failure
+ */
+static int Unified2IPv6TypeAlert(ThreadVars *t, const Packet *p, void *data)
+{
+ Unified2AlertThread *aun = (Unified2AlertThread *)data;
+ Unified2AlertFileHeader hdr;
+ AlertIPv6Unified2 *phdr;
+ AlertIPv6Unified2 gphdr;
+ const PacketAlert *pa;
+ int offset, length;
+ int ret;
+ unsigned int event_id;
+
+ if (likely(p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG)))
+ return 0;
+
+ phdr = (AlertIPv6Unified2 *)(aun->data +
+ sizeof(Unified2AlertFileHeader));
+
+ length = (sizeof(Unified2AlertFileHeader) + sizeof(AlertIPv6Unified2));
+ offset = length;
+
+ memset(aun->data, 0, aun->datalen);
+
+ hdr.type = htonl(UNIFIED2_IDS_EVENT_IPV6_TYPE);
+ hdr.length = htonl(sizeof(AlertIPv6Unified2));
+
+ /* fill the gphdr structure with the data of the packet */
+ memset(&gphdr, 0, sizeof(gphdr));
+ /* FIXME this need to be copied for each alert */
+ gphdr.sensor_id = htonl(sensor_id);
+ gphdr.event_second = htonl(p->ts.tv_sec);
+ gphdr.event_microsecond = htonl(p->ts.tv_usec);
+ gphdr.src_ip = *(struct in6_addr*)GET_IPV6_SRC_ADDR(p);
+ gphdr.dst_ip = *(struct in6_addr*)GET_IPV6_DST_ADDR(p);
+ /** If XFF is in overwrite mode... */
+ if (aun->xff_flags & XFF_OVERWRITE) {
+ BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4);
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ gphdr.dst_ip = *(struct in6_addr*)aun->xff_ip;
+ } else {
+ gphdr.src_ip = *(struct in6_addr*)aun->xff_ip;
+ }
+ }
+ gphdr.protocol = p->proto;
+
+ if(PACKET_TEST_ACTION(p, ACTION_DROP))
+ gphdr.packet_action = UNIFIED2_BLOCKED_FLAG;
+ else
+ gphdr.packet_action = 0;
+
+ switch(gphdr.protocol) {
+ case IPPROTO_ICMPV6:
+ if(p->icmpv6h) {
+ gphdr.sp = htons(p->icmpv6h->type);
+ gphdr.dp = htons(p->icmpv6h->code);
+ } else {
+ gphdr.sp = 0;
+ gphdr.dp = 0;
+ }
+ break;
+ case IPPROTO_ICMP:
+ if(p->icmpv4h) {
+ gphdr.sp = htons(p->icmpv4h->type);
+ gphdr.dp = htons(p->icmpv4h->code);
+ } else {
+ gphdr.sp = 0;
+ gphdr.dp = 0;
+ }
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ gphdr.sp = htons(p->sp);
+ gphdr.dp = htons(p->dp);
+ break;
+ default:
+ gphdr.sp = 0;
+ gphdr.dp = 0;
+ break;
+ }
+
+ uint16_t i = 0;
+ for (; i < p->alerts.cnt + 1; i++) {
+ if (i < p->alerts.cnt)
+ pa = &p->alerts.alerts[i];
+ else {
+ if (!(p->flags & PKT_HAS_TAG))
+ break;
+ pa = PacketAlertGetTag();
+ }
+
+ if (unlikely(pa->s == NULL))
+ continue;
+
+ HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
+
+ if ((xff_cfg->flags & XFF_EXTRADATA) && p->flow != NULL) {
+ char buffer[XFF_MAXLEN];
+ int have_xff_ip = 0;
+
+ FLOWLOCK_RDLOCK(p->flow);
+ if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
+ if (pa->flags & PACKET_ALERT_FLAG_TX) {
+ have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg, buffer, XFF_MAXLEN);
+ } else {
+ have_xff_ip = HttpXFFGetIP(p, xff_cfg, buffer, XFF_MAXLEN);
+ }
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ if (have_xff_ip) {
+ memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+
+ if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+ } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
+ }
+ }
+ }
+
+ /* reset length and offset */
+ aun->offset = offset;
+ aun->length = length;
+ memset(aun->data + aun->offset, 0, aun->datalen - aun->offset);
+
+ /* copy the part common to all alerts */
+ memcpy(aun->data, &hdr, sizeof(hdr));
+ memcpy(phdr, &gphdr, sizeof(gphdr));
+
+ /* fill the header structure with the data of the alert */
+ event_id = htonl(SC_ATOMIC_ADD(unified2_event_id, 1));
+ phdr->event_id = event_id;
+ phdr->generator_id = htonl(pa->s->gid);
+ phdr->signature_id = htonl(pa->s->id);
+ phdr->signature_revision = htonl(pa->s->rev);
+ phdr->classification_id = htonl(pa->s->class);
+ phdr->priority_id = htonl(pa->s->prio);
+
+ SCMutexLock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ if ((aun->unified2alert_ctx->file_ctx->size_current + length) >
+ aun->unified2alert_ctx->file_ctx->size_limit) {
+ if (Unified2AlertRotateFile(t,aun) < 0) {
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+ }
+
+ if (Unified2Write(aun) != 1) {
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+
+ memset(aun->data, 0, aun->length);
+ aun->length = 0;
+ aun->offset = 0;
+
+ /* stream flag based on state match, but only for TCP */
+ int stream = (gphdr.protocol == IPPROTO_TCP) ?
+ (pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_STREAM_MATCH) ? 1 : 0) : 0;
+ ret = Unified2PacketTypeAlert(aun, p, phdr->event_id, stream);
+ if (ret != 1) {
+ SCLogError(SC_ERR_FWRITE, "Error: fwrite failed: %s", strerror(errno));
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+ fflush(aun->unified2alert_ctx->file_ctx->fp);
+ aun->unified2alert_ctx->file_ctx->alerts++;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to fill unified2 ipv4 ids type format into the file.
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param p Packet struct used to decide for ipv4 or ipv6
+ * \param data Unified2 thread data.
+ * \retval 0 on succces
+ * \retval -1 on failure
+ */
+
+static int Unified2IPv4TypeAlert (ThreadVars *tv, const Packet *p, void *data)
+{
+ Unified2AlertThread *aun = (Unified2AlertThread *)data;
+ Unified2AlertFileHeader hdr;
+ AlertIPv4Unified2 *phdr;
+ AlertIPv4Unified2 gphdr;
+ const PacketAlert *pa;
+ int offset, length;
+ int ret;
+ unsigned int event_id;
+
+ if (likely(p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG)))
+ return 0;
+
+ phdr = (AlertIPv4Unified2 *)(aun->data +
+ sizeof(Unified2AlertFileHeader));
+
+ length = (sizeof(Unified2AlertFileHeader) + sizeof(AlertIPv4Unified2));
+ offset = length;
+
+ memset(aun->data, 0, aun->datalen);
+
+ hdr.type = htonl(UNIFIED2_IDS_EVENT_TYPE);
+ hdr.length = htonl(sizeof(AlertIPv4Unified2));
+
+ /* fill the gphdr structure with the data of the packet */
+ memset(&gphdr, 0, sizeof(gphdr));
+ gphdr.sensor_id = htonl(sensor_id);
+ gphdr.event_id = 0;
+ gphdr.event_second = htonl(p->ts.tv_sec);
+ gphdr.event_microsecond = htonl(p->ts.tv_usec);
+ gphdr.src_ip = p->ip4h->s_ip_src.s_addr;
+ gphdr.dst_ip = p->ip4h->s_ip_dst.s_addr;
+ /** If XFF is in overwrite mode... */
+ if (aun->xff_flags & XFF_OVERWRITE) {
+ BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV6);
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ gphdr.dst_ip = aun->xff_ip[0];
+ } else {
+ gphdr.src_ip = aun->xff_ip[0];
+ }
+ }
+ gphdr.protocol = IPV4_GET_RAW_IPPROTO(p->ip4h);
+
+ if(PACKET_TEST_ACTION(p, ACTION_DROP))
+ gphdr.packet_action = UNIFIED2_BLOCKED_FLAG;
+ else
+ gphdr.packet_action = 0;
+
+ /* TODO inverse order if needed, this should be done on a
+ * alert basis */
+ switch(gphdr.protocol) {
+ case IPPROTO_ICMP:
+ if(p->icmpv4h) {
+ gphdr.sp = htons(p->icmpv4h->type);
+ gphdr.dp = htons(p->icmpv4h->code);
+ }
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ gphdr.sp = htons(p->sp);
+ gphdr.dp = htons(p->dp);
+ break;
+ default:
+ gphdr.sp = 0;
+ gphdr.dp = 0;
+ break;
+ }
+
+ uint16_t i = 0;
+ for (; i < p->alerts.cnt + 1; i++) {
+ if (i < p->alerts.cnt)
+ pa = &p->alerts.alerts[i];
+ else {
+ if (!(p->flags & PKT_HAS_TAG))
+ break;
+ pa = PacketAlertGetTag();
+ }
+
+ if (unlikely(pa->s == NULL))
+ continue;
+
+ HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
+
+ if ((xff_cfg->flags & XFF_EXTRADATA) && p->flow != NULL) {
+ char buffer[XFF_MAXLEN];
+ int have_xff_ip = 0;
+
+ FLOWLOCK_RDLOCK(p->flow);
+ if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
+ if (pa->flags & PACKET_ALERT_FLAG_TX) {
+ have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg, buffer, XFF_MAXLEN);
+ } else {
+ have_xff_ip = HttpXFFGetIP(p, xff_cfg, buffer, XFF_MAXLEN);
+ }
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ if (have_xff_ip) {
+ memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+
+ if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+ } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+ aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
+ }
+ }
+ }
+
+ /* reset length and offset */
+ aun->offset = offset;
+ aun->length = length;
+ memset(aun->data + aun->offset, 0, aun->datalen - aun->offset);
+
+ /* copy the part common to all alerts */
+ memcpy(aun->data, &hdr, sizeof(hdr));
+ memcpy(phdr, &gphdr, sizeof(gphdr));
+
+ /* fill the hdr structure with the alert data */
+ event_id = htonl(SC_ATOMIC_ADD(unified2_event_id, 1));
+ phdr->event_id = event_id;
+ phdr->generator_id = htonl(pa->s->gid);
+ phdr->signature_id = htonl(pa->s->id);
+ phdr->signature_revision = htonl(pa->s->rev);
+ phdr->classification_id = htonl(pa->s->class);
+ phdr->priority_id = htonl(pa->s->prio);
+
+ /* check and enforce the filesize limit */
+ SCMutexLock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+
+ if ((aun->unified2alert_ctx->file_ctx->size_current + length) >
+ aun->unified2alert_ctx->file_ctx->size_limit) {
+ if (Unified2AlertRotateFile(tv,aun) < 0) {
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+ }
+
+ if (Unified2Write(aun) != 1) {
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+
+ memset(aun->data, 0, aun->length);
+ aun->length = 0;
+ aun->offset = 0;
+
+ /* Write the alert (it doesn't lock inside, since we
+ * already locked here for rotation check)
+ */
+ int stream = (gphdr.protocol == IPPROTO_TCP) ?
+ (pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_STREAM_MATCH) ? 1 : 0) : 0;
+ ret = Unified2PacketTypeAlert(aun, p, event_id, stream);
+ if (ret != 1) {
+ aun->unified2alert_ctx->file_ctx->alerts += i;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ return -1;
+ }
+
+ fflush(aun->unified2alert_ctx->file_ctx->fp);
+ aun->unified2alert_ctx->file_ctx->alerts++;
+ SCMutexUnlock(&aun->unified2alert_ctx->file_ctx->fp_mutex);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Thread init function.
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param initdata Unified2 thread initial data.
+ * \param data Unified2 thread data.
+ * \retval TM_ECODE_OK on succces
+ * \retval TM_ECODE_FAILED on failure
+ */
+
+TmEcode Unified2AlertThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ Unified2AlertThread *aun = SCMalloc(sizeof(Unified2AlertThread));
+ if (unlikely(aun == NULL))
+ return TM_ECODE_FAILED;
+ memset(aun, 0, sizeof(Unified2AlertThread));
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for Unified2Alert. \"initdata\" argument NULL");
+ SCFree(aun);
+ return TM_ECODE_FAILED;
+ }
+ /** Use the Ouptut Context (file pointer and mutex) */
+ aun->unified2alert_ctx = ((OutputCtx *)initdata)->data;
+
+ aun->data = SCMalloc(sizeof(Unified2AlertFileHeader) + sizeof(Unified2Packet) +
+ IPV4_MAXPACKET_LEN + sizeof(Unified2ExtraDataHdr) + sizeof (Unified2ExtraData));
+ if (aun->data == NULL) {
+ SCFree(aun);
+ return TM_ECODE_FAILED;
+ }
+ aun->datalen = sizeof(Unified2AlertFileHeader) + sizeof(Unified2Packet) +
+ IPV4_MAXPACKET_LEN + sizeof(Unified2ExtraDataHdr) + sizeof(Unified2ExtraData);
+
+ *data = (void *)aun;
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Thread deinit function.
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param data Unified2 thread data.
+ * \retval TM_ECODE_OK on succces
+ * \retval TM_ECODE_FAILED on failure
+ */
+
+TmEcode Unified2AlertThreadDeinit(ThreadVars *t, void *data)
+{
+ Unified2AlertThread *aun = (Unified2AlertThread *)data;
+ if (aun == NULL) {
+ goto error;
+ }
+
+ if (!(aun->unified2alert_ctx->file_ctx->flags & LOGFILE_ALERTS_PRINTED)) {
+ SCLogInfo("Alert unified2 module wrote %"PRIu64" alerts",
+ aun->unified2alert_ctx->file_ctx->alerts);
+
+ /* Do not print it for each thread */
+ aun->unified2alert_ctx->file_ctx->flags |= LOGFILE_ALERTS_PRINTED;
+
+ }
+
+ if (aun->data != NULL) {
+ SCFree(aun->data);
+ aun->data = NULL;
+ }
+ aun->datalen = 0;
+ /* clear memory */
+ memset(aun, 0, sizeof(Unified2AlertThread));
+ SCFree(aun);
+ return TM_ECODE_OK;
+
+error:
+ return TM_ECODE_FAILED;
+}
+
+/** \brief Create a new LogFileCtx from the provided ConfNode.
+ * \param conf The configuration node for this output.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+OutputCtx *Unified2AlertInitCtx(ConfNode *conf)
+{
+ int ret = 0;
+ LogFileCtx* file_ctx = NULL;
+ OutputCtx* output_ctx = NULL;
+ HttpXFFCfg *xff_cfg = NULL;
+
+ file_ctx = LogFileNewCtx();
+ if (file_ctx == NULL) {
+ SCLogError(SC_ERR_UNIFIED2_ALERT_GENERIC, "Couldn't create new file_ctx");
+ goto error;
+ }
+
+ const char *filename = NULL;
+ if (conf != NULL) { /* To faciliate unit tests. */
+ filename = ConfNodeLookupChildValue(conf, "filename");
+ }
+ if (filename == NULL)
+ filename = DEFAULT_LOG_FILENAME;
+ file_ctx->prefix = SCStrdup(filename);
+ if (unlikely(file_ctx->prefix == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate file prefix");
+ exit(EXIT_FAILURE);
+ }
+
+ const char *s_limit = NULL;
+ file_ctx->size_limit = DEFAULT_LIMIT;
+ if (conf != NULL) {
+ s_limit = ConfNodeLookupChildValue(conf, "limit");
+ if (s_limit != NULL) {
+ if (ParseSizeStringU64(s_limit, &file_ctx->size_limit) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize unified2 output, invalid limit: %s",
+ s_limit);
+ exit(EXIT_FAILURE);
+ }
+ if (file_ctx->size_limit < 4096) {
+ SCLogInfo("unified2-alert \"limit\" value of %"PRIu64" assumed to be pre-1.2 "
+ "style: setting limit to %"PRIu64"mb", file_ctx->size_limit, file_ctx->size_limit);
+ uint64_t size = file_ctx->size_limit * 1024 * 1024;
+ file_ctx->size_limit = size;
+ } else if (file_ctx->size_limit < MIN_LIMIT) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize unified2 output, limit less than "
+ "allowed minimum: %d.", MIN_LIMIT);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (conf != NULL) {
+ const char *sensor_id_s = NULL;
+ sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
+ if (sensor_id_s != NULL) {
+ if (ByteExtractStringUint32(&sensor_id, 10, 0, sensor_id_s) == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to initialize unified2 output, invalid sensor-id: %s", sensor_id_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ ret = Unified2AlertOpenFileCtx(file_ctx, filename);
+ if (ret < 0)
+ goto error;
+
+ output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ goto error;
+
+ xff_cfg = SCMalloc(sizeof(HttpXFFCfg));
+ if (unlikely(xff_cfg == NULL)) {
+ goto error;
+ }
+ memset(xff_cfg, 0x00, sizeof(HttpXFFCfg));
+
+ if (conf != NULL) {
+ HttpXFFGetCfg(conf, xff_cfg);
+ }
+
+ Unified2AlertFileCtx *unified2alert_ctx = SCMalloc(sizeof(Unified2AlertFileCtx));
+ if (unlikely(unified2alert_ctx == NULL)) {
+ goto error;
+ }
+ memset(unified2alert_ctx, 0x00, sizeof(Unified2AlertFileCtx));
+
+ unified2alert_ctx->file_ctx = file_ctx;
+ unified2alert_ctx->xff_cfg = xff_cfg;
+ output_ctx->data = unified2alert_ctx;
+ output_ctx->DeInit = Unified2AlertDeInitCtx;
+
+ SCLogInfo("Unified2-alert initialized: filename %s, limit %"PRIu64" MB",
+ filename, file_ctx->size_limit / (1024*1024));
+
+ SC_ATOMIC_INIT(unified2_event_id);
+
+ return output_ctx;
+
+error:
+ if (xff_cfg != NULL) {
+ SCFree(xff_cfg);
+ }
+ if (output_ctx != NULL) {
+ SCFree(output_ctx);
+ }
+
+ return NULL;
+}
+
+static void Unified2AlertDeInitCtx(OutputCtx *output_ctx)
+{
+ if (output_ctx != NULL) {
+ Unified2AlertFileCtx *unified2alert_ctx = (Unified2AlertFileCtx *) output_ctx->data;
+ if (unified2alert_ctx != NULL) {
+ LogFileCtx *logfile_ctx = unified2alert_ctx->file_ctx;
+ if (logfile_ctx != NULL) {
+ LogFileFreeCtx(logfile_ctx);
+ }
+ HttpXFFCfg *xff_cfg = unified2alert_ctx->xff_cfg;
+ if (xff_cfg != NULL) {
+ SCFree(xff_cfg);
+ }
+ SCFree(unified2alert_ctx);
+ }
+ SCFree(output_ctx);
+ }
+}
+
+/** \brief Read the config set the file pointer, open the file
+ * \param file_ctx pointer to a created LogFileCtx using LogFileNewCtx()
+ * \param prefix Prefix of the log file.
+ * \return -1 if failure, 0 if succesful
+ * */
+int Unified2AlertOpenFileCtx(LogFileCtx *file_ctx, const char *prefix)
+{
+ int ret = 0;
+ char *filename = NULL;
+ if (file_ctx->filename != NULL)
+ filename = file_ctx->filename;
+ else {
+ filename = SCMalloc(PATH_MAX); /* XXX some sane default? */
+ if (unlikely(filename == NULL))
+ return -1;
+ file_ctx->filename = filename;
+
+ memset(filename, 0x00, PATH_MAX);
+ }
+
+ /** get the time so we can have a filename with seconds since epoch */
+ struct timeval ts;
+ memset(&ts, 0x00, sizeof(struct timeval));
+
+ extern int run_mode;
+ if (run_mode == RUNMODE_UNITTEST)
+ TimeGet(&ts);
+ else
+ gettimeofday(&ts, NULL);
+
+ /* create the filename to use */
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32, log_dir, prefix, (uint32_t)ts.tv_sec);
+
+ file_ctx->fp = fopen(filename, "ab");
+ if (file_ctx->fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", filename,
+ strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the ethernet+ipv4+tcp unified2 test
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int Unified2Test01(void)
+{
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ void *data = NULL;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ Signature s;
+
+ uint8_t raw_ipv4_tcp[] = {
+ 0x00, 0x14, 0xbf, 0xe8, 0xcb, 0x26, 0xaa, 0x00,
+ 0x04, 0x00, 0x0a, 0x04, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0x8c, 0x55, 0x40, 0x00, 0x40, 0x06,
+ 0x69, 0x86, 0xc0, 0xa8, 0x0a, 0x68, 0x4a, 0x7d,
+ 0x2f, 0x53, 0xc2, 0x40, 0x00, 0x50, 0x1f, 0x00,
+ 0xa4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0x16, 0xd0, 0x3d, 0x4e, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x00, 0x1c,
+ 0x28, 0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x03, 0x06};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int ret;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&s, 0, sizeof(Signature));
+
+ p->alerts.cnt++;
+ p->alerts.alerts[p->alerts.cnt-1].s = &s;
+ p->alerts.alerts[p->alerts.cnt-1].s->id = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->gid = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->rev = 1;
+ SET_PKT_LEN(p, sizeof(raw_ipv4_tcp));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_ipv4_tcp, sizeof(raw_ipv4_tcp), &pq);
+
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL) {
+ goto end;
+ }
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if(lf == NULL) {
+ goto end;
+ }
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2Logger(&tv, data, p);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == -1) {
+ goto end;
+ }
+
+ Unified2AlertDeInitCtx(oc);
+
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 1;
+
+end:
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 0;
+}
+
+/**
+ * \test Test the ethernet+ipv6+tcp unified2 test
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int Unified2Test02(void)
+{
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ void *data = NULL;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ Signature s;
+
+ uint8_t raw_ipv6_tcp[] = {
+ 0x00, 0x11, 0x25, 0x82, 0x95, 0xb5, 0x00, 0xd0,
+ 0x09, 0xe3, 0xe8, 0xde, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x28, 0x06, 0x40, 0x20, 0x01,
+ 0x06, 0xf8, 0x10, 0x2d, 0x00, 0x00, 0x02, 0xd0,
+ 0x09, 0xff, 0xfe, 0xe3, 0xe8, 0xde, 0x20, 0x01,
+ 0x06, 0xf8, 0x09, 0x00, 0x07, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe7, 0x41,
+ 0x00, 0x50, 0xab, 0xdc, 0xd6, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0x02, 0x16, 0x80, 0x41, 0xa2,
+ 0x00, 0x00, 0x02, 0x04, 0x05, 0xa0, 0x04, 0x02,
+ 0x08, 0x0a, 0x00, 0x0a, 0x22, 0xa8, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x03, 0x03, 0x05 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int ret;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&s, 0, sizeof(Signature));
+
+ p->alerts.cnt++;
+ p->alerts.alerts[p->alerts.cnt-1].s = &s;
+ p->alerts.alerts[p->alerts.cnt-1].s->id = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->gid = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->rev = 1;
+ SET_PKT_LEN(p, sizeof(raw_ipv6_tcp));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_ipv6_tcp, sizeof(raw_ipv6_tcp), &pq);
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL) {
+ goto end;
+ }
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if(lf == NULL) {
+ goto end;
+ }
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if(ret == -1) {
+ goto end;
+ }
+ ret = Unified2Logger(&tv, data, p);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == -1) {
+ goto end;
+ }
+
+ Unified2AlertDeInitCtx(oc);
+
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 1;
+
+end:
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 0;
+}
+
+
+/**
+ * \test Test the GRE unified2 test
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int Unified2Test03(void)
+{
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ void *data = NULL;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ Signature s;
+
+ uint8_t raw_gre[] = {
+ 0x00, 0x0e, 0x50, 0x06, 0x42, 0x96, 0xaa, 0x00,
+ 0x04, 0x00, 0x0a, 0x04, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x74, 0x35, 0xa2, 0x40, 0x00, 0x40, 0x2f,
+ 0xef, 0xcb, 0x0a, 0x00, 0x00, 0x64, 0x0a, 0x00,
+ 0x00, 0x8a, 0x30, 0x01, 0x88, 0x0b, 0x00, 0x54,
+ 0x00, 0x00, 0x00, 0x18, 0x29, 0x5f, 0xff, 0x03,
+ 0x00, 0x21, 0x45, 0x00, 0x00, 0x50, 0xf4, 0x05,
+ 0x40, 0x00, 0x3f, 0x06, 0x20, 0xb8, 0x50, 0x7e,
+ 0x2b, 0x2d, 0xd4, 0xcc, 0xd6, 0x72, 0x0a, 0x92,
+ 0x1a, 0x0b, 0xc9, 0xaf, 0x24, 0x02, 0x8c, 0xdd,
+ 0x45, 0xf6, 0x80, 0x18, 0x21, 0xfc, 0x10, 0x7c,
+ 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x08, 0x19,
+ 0x1a, 0xda, 0x84, 0xd6, 0xda, 0x3e, 0x50, 0x49,
+ 0x4e, 0x47, 0x20, 0x73, 0x74, 0x65, 0x72, 0x6c,
+ 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x72, 0x65, 0x65,
+ 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74,
+ 0x0d, 0x0a};
+ Packet *p = PacketGetFromAlloc();
+ Packet *pkt;
+ if (unlikely(p == NULL))
+ return 0;
+ int ret;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&s, 0, sizeof(Signature));
+
+ p->alerts.cnt++;
+ p->alerts.alerts[p->alerts.cnt-1].s = &s;
+ p->alerts.alerts[p->alerts.cnt-1].s->id = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->gid = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->rev = 1;
+ SET_PKT_LEN(p, sizeof(raw_gre));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_gre, sizeof(raw_gre), &pq);
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL) {
+ goto end;
+ }
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if(lf == NULL) {
+ goto end;
+ }
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if(ret == -1) {
+ goto end;
+ }
+ ret = Unified2Logger(&tv, data, p);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == -1) {
+ goto end;
+ }
+
+ Unified2AlertDeInitCtx(oc);
+
+ pkt = PacketDequeue(&pq);
+ while (pkt != NULL) {
+ PACKET_RECYCLE(pkt);
+ SCFree(pkt);
+ pkt = PacketDequeue(&pq);
+ }
+
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 1;
+
+end:
+ pkt = PacketDequeue(&pq);
+ while (pkt != NULL) {
+ PACKET_RECYCLE(pkt);
+ SCFree(pkt);
+ pkt = PacketDequeue(&pq);
+ }
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 0;
+}
+
+/**
+ * \test Test the PPP unified2 test
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int Unified2Test04(void)
+{
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ void *data = NULL;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ Signature s;
+
+ uint8_t raw_ppp[] = {
+ 0xff, 0x03, 0x00, 0x21, 0x45, 0xc0, 0x00, 0x2c,
+ 0x4d, 0xed, 0x00, 0x00, 0xff, 0x06, 0xd5, 0x17,
+ 0xbf, 0x01, 0x0d, 0x01, 0xbf, 0x01, 0x0d, 0x03,
+ 0xea, 0x37, 0x00, 0x17, 0x6d, 0x0b, 0xba, 0xc3,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x10, 0x20,
+ 0xdd, 0xe1, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int ret;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&s, 0, sizeof(Signature));
+
+ p->alerts.cnt++;
+ p->alerts.alerts[p->alerts.cnt-1].s = &s;
+ p->alerts.alerts[p->alerts.cnt-1].s->id = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->gid = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->rev = 1;
+ SET_PKT_LEN(p, sizeof(raw_ppp));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodePPP(&tv, &dtv, p, raw_ppp, sizeof(raw_ppp), &pq);
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL) {
+ goto end;
+ }
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if(lf == NULL) {
+ goto end;
+ }
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if(ret == -1) {
+ goto end;
+ }
+ ret = Unified2Logger(&tv, data, p);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == -1) {
+ goto end;
+ }
+
+ Unified2AlertDeInitCtx(oc);
+
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 1;
+
+end:
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 0;
+}
+
+/**
+ * \test Test the ethernet+ipv4+tcp droped unified2 test
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int Unified2Test05(void)
+{
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ void *data = NULL;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ Signature s;
+
+ uint8_t raw_ipv4_tcp[] = {
+ 0x00, 0x14, 0xbf, 0xe8, 0xcb, 0x26, 0xaa, 0x00,
+ 0x04, 0x00, 0x0a, 0x04, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0x8c, 0x55, 0x40, 0x00, 0x40, 0x06,
+ 0x69, 0x86, 0xc0, 0xa8, 0x0a, 0x68, 0x4a, 0x7d,
+ 0x2f, 0x53, 0xc2, 0x40, 0x00, 0x50, 0x1f, 0x00,
+ 0xa4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0x16, 0xd0, 0x3d, 0x4e, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x00, 0x1c,
+ 0x28, 0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x03, 0x06};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int ret;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&s, 0, sizeof(Signature));
+
+ p->alerts.cnt++;
+ p->alerts.alerts[p->alerts.cnt-1].s = &s;
+ p->alerts.alerts[p->alerts.cnt-1].s->id = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->gid = 1;
+ p->alerts.alerts[p->alerts.cnt-1].s->rev = 1;
+ SET_PKT_LEN(p, sizeof(raw_ipv4_tcp));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_ipv4_tcp, sizeof(raw_ipv4_tcp), &pq);
+
+ p->action = ACTION_DROP;
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL) {
+ goto end;
+ }
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if(lf == NULL) {
+ goto end;
+ }
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if(ret == -1) {
+ goto end;
+ }
+ ret = Unified2Logger(&tv, data, p);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == TM_ECODE_FAILED) {
+ goto end;
+ }
+
+ Unified2AlertDeInitCtx(oc);
+
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 1;
+
+end:
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+ return 0;
+}
+
+/**
+ * \test Test the Rotate process
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int Unified2TestRotate01(void)
+{
+ int ret = 0;
+ int r = 0;
+ ThreadVars tv;
+ OutputCtx *oc;
+ LogFileCtx *lf;
+ Unified2AlertFileCtx *uaf = NULL;
+ void *data = NULL;
+ char *filename = NULL;
+
+ oc = Unified2AlertInitCtx(NULL);
+ if (oc == NULL)
+ return 0;
+ uaf = oc->data;
+ if (uaf == NULL)
+ return 0;
+ lf = uaf->file_ctx;
+ if (lf == NULL)
+ return 0;
+ filename = SCStrdup(lf->filename);
+ if (unlikely(filename == NULL))
+ return 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ ret = Unified2AlertThreadInit(&tv, oc, &data);
+ if (ret == TM_ECODE_FAILED) {
+ LogFileFreeCtx(lf);
+ if (filename != NULL)
+ SCFree(filename);
+ return 0;
+ }
+
+ TimeSetIncrementTime(1);
+
+ ret = Unified2AlertRotateFile(&tv, data);
+ if (ret == -1)
+ goto error;
+
+ if (strcmp(filename, lf->filename) == 0) {
+ SCLogError(SC_ERR_UNIFIED2_ALERT_GENERIC,
+ "filename \"%s\" == \"%s\": ", filename, lf->filename);
+ goto error;
+ }
+
+ r = 1;
+
+error:
+ ret = Unified2AlertThreadDeinit(&tv, data);
+ if(ret == TM_ECODE_FAILED) {
+ printf("Unified2AlertThreadDeinit error");
+ }
+ if (oc != NULL)
+ Unified2AlertDeInitCtx(oc);
+ if (filename != NULL)
+ SCFree(filename);
+ return r;
+}
+#endif
+
+/**
+ * \brief this function registers unit tests for Unified2
+ */
+void Unified2RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("Unified2Test01 -- Ipv4 test", Unified2Test01, 1);
+ UtRegisterTest("Unified2Test02 -- Ipv6 test", Unified2Test02, 1);
+ UtRegisterTest("Unified2Test03 -- GRE test", Unified2Test03, 1);
+ UtRegisterTest("Unified2Test04 -- PPP test", Unified2Test04, 1);
+ UtRegisterTest("Unified2Test05 -- Inline test", Unified2Test05, 1);
+ UtRegisterTest("Unified2TestRotate01 -- Rotate File", Unified2TestRotate01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/alert-unified2-alert.h b/framework/src/suricata/src/alert-unified2-alert.h
new file mode 100644
index 00000000..d4d3b2ec
--- /dev/null
+++ b/framework/src/suricata/src/alert-unified2-alert.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __ALERT_UNIFIED2_ALERT_H__
+#define __ALERT_UNIFIED2_ALERT_H__
+
+/** Unified2 Option packet action */
+#define UNIFIED2_PACKET_FLAG 1
+#define UNIFIED2_BLOCKED_FLAG 0x20
+
+/** Unified2 Header Types */
+#define UNIFIED2_EVENT_TYPE 1
+#define UNIFIED2_PACKET_TYPE 2
+#define UNIFIED2_IDS_EVENT_TYPE 7
+#define UNIFIED2_EVENT_EXTENDED_TYPE 66
+#define UNIFIED2_PERFORMANCE_TYPE 67
+#define UNIFIED2_PORTSCAN_TYPE 68
+#define UNIFIED2_IDS_EVENT_IPV6_TYPE 72
+#define UNIFIED2_IDS_EVENT_MPLS_TYPE 99
+#define UNIFIED2_IDS_EVENT_IPV6_MPLS_TYPE 100
+#define UNIFIED2_IDS_EVENT_EXTRADATA_TYPE 110
+#define UNIFIED2_EXTRADATA_CLIENT_IPV4_TYPE 1
+#define UNIFIED2_EXTRADATA_CLIENT_IPV6_TYPE 1
+#define UNIFIED2_EXTRADATA_TYPE_BLOB 1
+#define UNIFIED2_EXTRADATA_TYPE_EXTRA_DATA 4
+
+void TmModuleUnified2AlertRegister(void);
+OutputCtx *Unified2AlertInitCtx(ConfNode *);
+
+#endif /* __ALERT_UNIFIED2_ALERT_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-dcerpc-common.h b/framework/src/suricata/src/app-layer-dcerpc-common.h
new file mode 100644
index 00000000..cdda5630
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dcerpc-common.h
@@ -0,0 +1,246 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+
+#ifndef __APP_LAYER_DCERPC_COMMON_H__
+#define __APP_LAYER_DCERPC_COMMON_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+void RegisterDCERPCParsers(void);
+void DCERPCParserTests(void);
+void DCERPCParserRegisterTests(void);
+
+// http://www.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06
+#define REQUEST 0
+#define PING 1
+#define RESPONSE 2
+#define FAULT 3
+#define WORKING 4
+#define NOCALL 5
+#define REJECT 6
+#define ACK 7
+#define CL_CANCEL 8
+#define FACK 9
+#define CANCEL_ACK 10
+#define BIND 11
+#define BIND_ACK 12
+#define BIND_NAK 13
+#define ALTER_CONTEXT 14
+#define ALTER_CONTEXT_RESP 15
+#define SHUTDOWN 17
+#define CO_CANCEL 18
+#define ORPHANED 19
+#if 0
+typedef struct {
+ uint8_t rpc_vers; /* 4 RPC protocol major version (4 LSB only)*/
+ uint8_t ptype; /* Packet type (5 LSB only) */
+ uint8_t flags1; /* Packet flags */
+ uint8_t flags2; /* Packet flags */
+ uint8_t drep[3]; /* Data representation format label */
+ uint8_t serial_hi; /* High byte of serial number */
+ uuid_t object; /* Object identifier */
+ uuid_t if_id; /* Interface identifier */
+ uuid_t act_id; /* Activity identifier */
+ unsigned long server_boot;/* Server boot time */
+ unsigned long if_vers; /* Interface version */
+ unsigned long seqnum; /* Sequence number */
+ unsigned short opnum; /* Operation number */
+ unsigned short ihint; /* Interface hint */
+ unsigned short ahint; /* Activity hint */
+ unsigned short len; /* Length of packet body */
+ unsigned short fragnum; /* Fragment number */
+ unsigned small auth_proto; /* Authentication protocol identifier*/
+ unsigned small serial_lo; /* Low byte of serial number */
+} dc_rpc_cl_pkt_hdr_t;
+#endif
+
+#define RESERVED_01 0x01
+#define LASTFRAG 0x02
+#define FRAG 0x04
+#define NOFACK 0x08
+#define MAYBE 0x10
+#define IDEMPOTENT 0x20
+#define BROADCAST 0x40
+#define RESERVED_80 0x80
+
+#define CANCEL_PENDING 0x02
+#define RESERVED_04 0x04
+#define RESERVED_10 0x10
+#define RESERVED_20 0x20
+#define RESERVED_40 0x40
+#define RESERVED_80 0x80
+
+typedef struct DCERPCHdr_ {
+ uint8_t rpc_vers; /**< 00:01 RPC version should be 5 */
+ uint8_t rpc_vers_minor; /**< 01:01 minor version */
+ uint8_t type; /**< 02:01 packet type */
+ uint8_t pfc_flags; /**< 03:01 flags (see PFC_... ) */
+ uint8_t packed_drep[4]; /**< 04:04 NDR data representation format label */
+ uint16_t frag_length; /**< 08:02 total length of fragment */
+ uint16_t auth_length; /**< 10:02 length of auth_value */
+ uint32_t call_id; /**< 12:04 call identifier */
+} DCERPCHdr;
+
+#define DCERPC_HDR_LEN 16
+
+typedef struct DCERPCHdrUdp_ {
+ uint8_t rpc_vers; /**< 4 RPC protocol major version (4 LSB only)*/
+ uint8_t type; /**< Packet type (5 LSB only) */
+ uint8_t flags1; /**< Packet flags */
+ uint8_t flags2; /**< Packet flags */
+ uint8_t drep[3]; /**< Data representation format label */
+ uint8_t serial_hi; /**< High byte of serial number */
+ uint8_t objectuuid[16];
+ uint8_t interfaceuuid[16];
+ uint8_t activityuuid[16];
+ uint32_t server_boot; /**< Server boot time */
+ uint32_t if_vers; /**< Interface version */
+ uint32_t seqnum; /**< Sequence number */
+ uint16_t opnum; /**< Operation number */
+ uint16_t ihint; /**< Interface hint */
+ uint16_t ahint; /**< Activity hint */
+ uint16_t fraglen; /**< Length of packet body */
+ uint16_t fragnum; /**< Fragment number */
+ uint8_t auth_proto; /**< Authentication protocol identifier*/
+ uint8_t serial_lo; /**< Low byte of serial number */
+} DCERPCHdrUdp;
+
+#define DCERPC_UDP_HDR_LEN 80
+
+#define DCERPC_UUID_ENTRY_FLAG_FF 0x0001 /**< FIRST flag set on the packet
+ that contained this uuid entry */
+
+typedef struct DCERPCUuidEntry_ {
+ uint16_t ctxid;
+ uint16_t internal_id;
+ uint16_t result;
+ uint8_t uuid[16];
+ uint16_t version;
+ uint16_t versionminor;
+ uint16_t flags; /**< DCERPC_UUID_ENTRY_FLAG_* flags */
+ TAILQ_ENTRY(DCERPCUuidEntry_) next;
+} DCERPCUuidEntry;
+
+typedef struct DCERPCBindBindAck_ {
+ uint8_t numctxitems;
+ uint8_t numctxitemsleft;
+ uint8_t ctxbytesprocessed;
+ uint16_t ctxid;
+ uint8_t uuid[16];
+ uint16_t version;
+ uint16_t versionminor;
+ DCERPCUuidEntry *uuid_entry;
+ TAILQ_HEAD(, DCERPCUuidEntry_) uuid_list;
+ /* the interface uuids that the server has accepted */
+ TAILQ_HEAD(, DCERPCUuidEntry_) accepted_uuid_list;
+ uint16_t uuid_internal_id;
+ uint16_t secondaryaddrlen;
+ uint16_t secondaryaddrlenleft;
+ uint16_t result;
+} DCERPCBindBindAck;
+
+typedef struct DCERPCRequest_ {
+ uint16_t ctxid;
+ uint16_t opnum;
+ /* holds the stub data for the request */
+ uint8_t *stub_data_buffer;
+ /* length of the above buffer */
+ uint32_t stub_data_buffer_len;
+ /* used by the dce preproc to indicate fresh entry in the stub data buffer */
+ uint8_t stub_data_fresh;
+ uint8_t first_request_seen;
+} DCERPCRequest;
+
+typedef struct DCERPCResponse_ {
+ /* holds the stub data for the response */
+ uint8_t *stub_data_buffer;
+ /* length of the above buffer */
+ uint32_t stub_data_buffer_len;
+ /* used by the dce preproc to indicate fresh entry in the stub data buffer */
+ uint8_t stub_data_fresh;
+} DCERPCResponse;
+
+typedef struct DCERPC_ {
+ DCERPCHdr dcerpchdr;
+ DCERPCBindBindAck dcerpcbindbindack;
+ DCERPCRequest dcerpcrequest;
+ DCERPCResponse dcerpcresponse;
+ uint16_t bytesprocessed;
+ uint8_t pad;
+ uint16_t padleft;
+ uint16_t transaction_id;
+ /* indicates if the dcerpc pdu state is in the middle of processing
+ * a fragmented pdu */
+ uint8_t pdu_fragged;
+} DCERPC;
+
+typedef struct DCERPCUDP_ {
+ DCERPCHdrUdp dcerpchdrudp;
+ DCERPCBindBindAck dcerpcbindbindack;
+ DCERPCRequest dcerpcrequest;
+ DCERPCResponse dcerpcresponse;
+ uint16_t bytesprocessed;
+ uint16_t fraglenleft;
+ uint8_t *frag_data;
+ DCERPCUuidEntry *uuid_entry;
+ TAILQ_HEAD(, uuid_entry) uuid_list;
+} DCERPCUDP;
+
+/** First fragment */
+#define PFC_FIRST_FRAG 0x01
+/** Last fragment */
+#define PFC_LAST_FRAG 0x02
+/** Cancel was pending at sender */
+#define PFC_PENDING_CANCEL 0x04
+#define PFC_RESERVED_1 0x08
+/** supports concurrent multiplexing of a single connection. */
+#define PFC_CONC_MPX 0x10
+/** only meaningful on `fault' packet; if true, guaranteed
+ * call did not execute. */
+#define PFC_DID_NOT_EXECUTE 0x20
+/** `maybe' call semantics requested */
+#define PFC_MAYBE 0x40
+/** if true, a non-nil object UUID was specified in the handle, and
+ * is present in the optional object field. If false, the object field
+ * is omitted. */
+#define PFC_OBJECT_UUID 0x80
+
+#define REASON_NOT_SPECIFIED 0
+#define TEMPORARY_CONGESTION 1
+#define LOCAL_LIMIT_EXCEEDED 2
+#define CALLED_PADDR_UNKNOWN 3 /* not used */
+#define PROTOCOL_VERSION_NOT_SUPPORTED 4
+#define DEFAULT_CONTEXT_NOT_SUPPORTED 5 /* not used */
+#define USER_DATA_NOT_READABLE 6 /* not used */
+#define NO_PSAP_AVAILABLE 7 /* not used */
+
+int32_t DCERPCParser(DCERPC *, uint8_t *, uint32_t);
+void hexdump(const void *buf, size_t len);
+void printUUID(char *type, DCERPCUuidEntry *uuid);
+
+#endif /* __APP_LAYER_DCERPC_COMMON_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-dcerpc-udp.c b/framework/src/suricata/src/app-layer-dcerpc-udp.c
new file mode 100644
index 00000000..58d714d0
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dcerpc-udp.c
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (c) 2009, 2010 Open Information Security Foundation
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ *
+ * \todo Updated by AS: Inspect the possibilities of sending junk start at the
+ * start of udp session to avoid alproto detection.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "debug.h"
+#include "decode.h"
+
+#include "flow-util.h"
+
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-debug.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+
+#include "app-layer-dcerpc-udp.h"
+
+enum {
+ DCERPC_FIELD_NONE = 0,
+ DCERPC_PARSE_DCERPC_HEADER,
+ DCERPC_PARSE_DCERPC_BIND,
+ DCERPC_PARSE_DCERPC_BIND_ACK,
+ DCERPC_PARSE_DCERPC_REQUEST,
+ /* must be last */
+ DCERPC_FIELD_MAX,
+};
+
+/** \internal
+ * \retval stub_len or 0 in case of error */
+static uint32_t FragmentDataParser(Flow *f, void *dcerpcudp_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ DCERPCUDPState *sstate = (DCERPCUDPState *) dcerpcudp_state;
+ uint8_t **stub_data_buffer = NULL;
+ uint32_t *stub_data_buffer_len = NULL;
+ uint8_t *stub_data_fresh = NULL;
+ uint16_t stub_len = 0;
+ void *ptmp;
+
+ /* request PDU. Retrieve the request stub buffer */
+ if (sstate->dcerpc.dcerpchdrudp.type == REQUEST) {
+ stub_data_buffer = &sstate->dcerpc.dcerpcrequest.stub_data_buffer;
+ stub_data_buffer_len = &sstate->dcerpc.dcerpcrequest.stub_data_buffer_len;
+ stub_data_fresh = &sstate->dcerpc.dcerpcrequest.stub_data_fresh;
+
+ /* response PDU. Retrieve the response stub buffer */
+ } else {
+ stub_data_buffer = &sstate->dcerpc.dcerpcresponse.stub_data_buffer;
+ stub_data_buffer_len = &sstate->dcerpc.dcerpcresponse.stub_data_buffer_len;
+ stub_data_fresh = &sstate->dcerpc.dcerpcresponse.stub_data_fresh;
+ }
+
+ stub_len = (sstate->dcerpc.fraglenleft < input_len) ? sstate->dcerpc.fraglenleft : input_len;
+
+ if (stub_len == 0) {
+ SCReturnUInt(0);
+ }
+ /* if the frag is the the first frag irrespective of it being a part of
+ * a multi frag PDU or not, it indicates the previous PDU's stub would
+ * have been buffered and processed and we can use the buffer to hold
+ * frags from a fresh request/response */
+ if (sstate->dcerpc.dcerpchdrudp.flags1 & PFC_FIRST_FRAG) {
+ *stub_data_buffer_len = 0;
+ }
+
+ ptmp = SCRealloc(*stub_data_buffer, *stub_data_buffer_len + stub_len);
+ if (ptmp == NULL) {
+ SCFree(*stub_data_buffer);
+ *stub_data_buffer = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ SCReturnUInt(0);
+ }
+
+ *stub_data_buffer = ptmp;
+ memcpy(*stub_data_buffer + *stub_data_buffer_len, input, stub_len);
+
+ *stub_data_fresh = 1;
+ /* length of the buffered stub */
+ *stub_data_buffer_len += stub_len;
+
+ sstate->dcerpc.fraglenleft -= stub_len;
+ sstate->dcerpc.bytesprocessed += stub_len;
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ int i = 0;
+ for (i = 0; i < stub_len; i++) {
+ SCLogDebug("0x%02x ", input[i]);
+ }
+ }
+#endif
+
+ SCReturnUInt((uint32_t)stub_len);
+}
+
+/**
+ * \brief DCERPCParseHeader parses the 16 byte DCERPC header
+ * A fast path for normal decoding is used when there is enough bytes
+ * present to parse the entire header. A slow path is used to parse
+ * fragmented packets.
+ */
+static int DCERPCUDPParseHeader(Flow *f, void *dcerpcudp_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+ DCERPCUDPState *sstate = (DCERPCUDPState *) dcerpcudp_state;
+ if (input_len) {
+ switch (sstate->bytesprocessed) {
+ case 0:
+ // fallthrough
+ /* above statement to prevent coverity FPs from the switch
+ * fall through */
+ if (input_len >= DCERPC_UDP_HDR_LEN) {
+ sstate->dcerpc.dcerpchdrudp.rpc_vers = *p;
+ if (sstate->dcerpc.dcerpchdrudp.rpc_vers != 4) {
+ SCLogDebug("DCERPC UDP Header did not validate");
+ SCReturnInt(-1);
+ }
+ sstate->dcerpc.dcerpchdrudp.type = *(p + 1);
+ sstate->dcerpc.dcerpchdrudp.flags1 = *(p + 2);
+ sstate->dcerpc.dcerpchdrudp.flags2 = *(p + 3);
+ sstate->dcerpc.dcerpchdrudp.drep[0] = *(p + 4);
+ sstate->dcerpc.dcerpchdrudp.drep[1] = *(p + 5);
+ sstate->dcerpc.dcerpchdrudp.drep[2] = *(p + 6);
+ sstate->dcerpc.dcerpchdrudp.serial_hi = *(p + 7);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[3] = *(p + 8);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[2] = *(p + 9);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[1] = *(p + 10);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[0] = *(p + 11);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[5] = *(p + 12);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[4] = *(p + 13);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[7] = *(p + 14);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[6] = *(p + 15);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[8] = *(p + 16);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[9] = *(p + 17);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[10] = *(p + 18);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[11] = *(p + 19);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[12] = *(p + 20);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[13] = *(p + 21);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[14] = *(p + 22);
+ sstate->dcerpc.dcerpchdrudp.objectuuid[15] = *(p + 23);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[3] = *(p + 24);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[2] = *(p + 25);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[1] = *(p + 26);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[0] = *(p + 27);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[5] = *(p + 28);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[4] = *(p + 29);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[7] = *(p + 30);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[6] = *(p + 31);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[8] = *(p + 32);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[9] = *(p + 33);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[10] = *(p + 34);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[11] = *(p + 35);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[12] = *(p + 36);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[13] = *(p + 37);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[14] = *(p + 38);
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[15] = *(p + 39);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[3] = *(p + 40);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[2] = *(p + 41);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[1] = *(p + 42);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[0] = *(p + 43);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[5] = *(p + 44);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[4] = *(p + 45);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[7] = *(p + 46);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[6] = *(p + 47);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[8] = *(p + 48);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[9] = *(p + 49);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[10] = *(p + 50);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[11] = *(p + 51);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[12] = *(p + 52);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[13] = *(p + 53);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[14] = *(p + 54);
+ sstate->dcerpc.dcerpchdrudp.activityuuid[15] = *(p + 55);
+ if (sstate->dcerpc.dcerpchdrudp.drep[0] == 0x10) {
+ sstate->dcerpc.dcerpchdrudp.server_boot = *(p + 56);
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 57) << 8;
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 58) << 16;
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 59) << 24;
+ sstate->dcerpc.dcerpchdrudp.if_vers = *(p + 60);
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 61) << 8;
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 62) << 16;
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 63) << 24;
+ sstate->dcerpc.dcerpchdrudp.seqnum = *(p + 64);
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 65) << 8;
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 66) << 16;
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 67) << 24;
+ sstate->dcerpc.dcerpchdrudp.opnum = *(p + 68);
+ sstate->dcerpc.dcerpchdrudp.opnum |= *(p + 69) << 8;
+ sstate->dcerpc.dcerpchdrudp.ihint = *(p + 70);
+ sstate->dcerpc.dcerpchdrudp.ihint |= *(p + 71) << 8;
+ sstate->dcerpc.dcerpchdrudp.ahint = *(p + 72);
+ sstate->dcerpc.dcerpchdrudp.ahint |= *(p + 73) << 8;
+ sstate->dcerpc.dcerpchdrudp.fraglen = *(p + 74);
+ sstate->dcerpc.dcerpchdrudp.fraglen |= *(p + 75) << 8;
+ sstate->dcerpc.dcerpchdrudp.fragnum = *(p + 76);
+ sstate->dcerpc.dcerpchdrudp.fragnum |= *(p + 77) << 8;
+ } else {
+ sstate->dcerpc.dcerpchdrudp.server_boot = *(p + 56) << 24;
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 57) << 16;
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 58) << 8;
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p + 59);
+ sstate->dcerpc.dcerpchdrudp.if_vers = *(p + 60) << 24;
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 61) << 16;
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 62) << 8;
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p + 63);
+ sstate->dcerpc.dcerpchdrudp.seqnum = *(p + 64) << 24;
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 65) << 16;
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 66) << 8;
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p + 67);
+ sstate->dcerpc.dcerpchdrudp.opnum = *(p + 68) << 24;
+ sstate->dcerpc.dcerpchdrudp.opnum |= *(p + 69) << 16;
+ sstate->dcerpc.dcerpchdrudp.ihint = *(p + 70) << 8;
+ sstate->dcerpc.dcerpchdrudp.ihint |= *(p + 71);
+ sstate->dcerpc.dcerpchdrudp.ahint = *(p + 72) << 8;
+ sstate->dcerpc.dcerpchdrudp.ahint |= *(p + 73);
+ sstate->dcerpc.dcerpchdrudp.fraglen = *(p + 74) << 8;
+ sstate->dcerpc.dcerpchdrudp.fraglen |= *(p + 75);
+ sstate->dcerpc.dcerpchdrudp.fragnum = *(p + 76) << 8;
+ sstate->dcerpc.dcerpchdrudp.fragnum |= *(p + 77);
+ }
+ sstate->fraglenleft = sstate->dcerpc.dcerpchdrudp.fraglen;
+ sstate->dcerpc.dcerpchdrudp.auth_proto = *(p + 78);
+ sstate->dcerpc.dcerpchdrudp.serial_lo = *(p + 79);
+ sstate->bytesprocessed = DCERPC_UDP_HDR_LEN;
+ sstate->uuid_entry = (DCERPCUuidEntry *) SCCalloc(1,
+ sizeof(DCERPCUuidEntry));
+ if (sstate->uuid_entry == NULL) {
+ SCReturnUInt(-1);
+ } else {
+ memcpy(sstate->uuid_entry->uuid,
+ sstate->dcerpc.dcerpchdrudp.activityuuid,
+ sizeof(sstate->dcerpc.dcerpchdrudp.activityuuid));
+ TAILQ_INSERT_HEAD(&sstate->uuid_list, sstate->uuid_entry,
+ next);
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("DCERPC UDP", sstate->uuid_entry);
+
+ }
+#endif
+ }
+ SCReturnUInt(80);
+ break;
+ } else {
+ sstate->dcerpc.dcerpchdrudp.rpc_vers = *(p++);
+ if (sstate->dcerpc.dcerpchdrudp.rpc_vers != 4) {
+ SCLogDebug("DCERPC UDP Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* We fall through to the next case if we still have input.
+ * Same applies for other cases as well */
+ }
+ /* fall through */
+ case 1:
+ sstate->dcerpc.dcerpchdrudp.type = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ sstate->dcerpc.dcerpchdrudp.flags1 = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ sstate->dcerpc.dcerpchdrudp.flags2 = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ sstate->dcerpc.dcerpchdrudp.drep[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ sstate->dcerpc.dcerpchdrudp.drep[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ sstate->dcerpc.dcerpchdrudp.drep[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ sstate->dcerpc.dcerpchdrudp.serial_hi = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[3] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[5] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[4] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[7] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[6] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[8] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[9] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[10] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[11] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[12] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[13] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[14] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ sstate->dcerpc.dcerpchdrudp.objectuuid[15] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[3] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 28:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[5] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 29:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[4] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 30:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[7] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 31:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[6] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 32:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[8] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 33:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[9] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 34:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[10] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 35:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[11] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 36:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[12] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 37:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[13] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 38:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[14] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 39:
+ sstate->dcerpc.dcerpchdrudp.interfaceuuid[15] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 40:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[3] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 41:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 42:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 43:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 44:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[5] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 45:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[4] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 46:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[7] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 47:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[6] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 48:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[8] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 49:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[9] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 50:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[10] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 51:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[11] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 52:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[12] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 53:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[13] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 54:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[14] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 55:
+ sstate->dcerpc.dcerpchdrudp.activityuuid[15] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 56:
+ sstate->dcerpc.dcerpchdrudp.server_boot = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 57:
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 58:
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 59:
+ sstate->dcerpc.dcerpchdrudp.server_boot |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 60:
+ sstate->dcerpc.dcerpchdrudp.if_vers = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 61:
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 62:
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 63:
+ sstate->dcerpc.dcerpchdrudp.if_vers |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 64:
+ sstate->dcerpc.dcerpchdrudp.seqnum = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 65:
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 66:
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 67:
+ sstate->dcerpc.dcerpchdrudp.seqnum |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 68:
+ sstate->dcerpc.dcerpchdrudp.opnum = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 69:
+ sstate->dcerpc.dcerpchdrudp.opnum |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 70:
+ sstate->dcerpc.dcerpchdrudp.ihint = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 71:
+ sstate->dcerpc.dcerpchdrudp.ihint |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 72:
+ sstate->dcerpc.dcerpchdrudp.ahint = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 73:
+ sstate->dcerpc.dcerpchdrudp.ahint |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 74:
+ sstate->dcerpc.dcerpchdrudp.fraglen = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 75:
+ sstate->dcerpc.dcerpchdrudp.fraglen |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 76:
+ sstate->dcerpc.dcerpchdrudp.fragnum = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 77:
+ sstate->dcerpc.dcerpchdrudp.fragnum |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 78:
+ sstate->dcerpc.dcerpchdrudp.auth_proto = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 79:
+ sstate->dcerpc.dcerpchdrudp.serial_lo = *(p++);
+ if (sstate->dcerpc.dcerpchdrudp.drep[0] != 0x10) {
+ sstate->dcerpc.dcerpchdrudp.server_boot = SCByteSwap32(sstate->dcerpc.dcerpchdrudp.server_boot);
+ sstate->dcerpc.dcerpchdrudp.if_vers= SCByteSwap32(sstate->dcerpc.dcerpchdrudp.if_vers);
+ sstate->dcerpc.dcerpchdrudp.seqnum= SCByteSwap32(sstate->dcerpc.dcerpchdrudp.seqnum);
+ sstate->dcerpc.dcerpchdrudp.opnum = SCByteSwap16(sstate->dcerpc.dcerpchdrudp.opnum);
+ sstate->dcerpc.dcerpchdrudp.ihint= SCByteSwap16(sstate->dcerpc.dcerpchdrudp.ihint);
+ sstate->dcerpc.dcerpchdrudp.ahint = SCByteSwap16(sstate->dcerpc.dcerpchdrudp.ahint);
+ sstate->dcerpc.dcerpchdrudp.fraglen = SCByteSwap16(sstate->dcerpc.dcerpchdrudp.fraglen);
+ sstate->dcerpc.dcerpchdrudp.fragnum = SCByteSwap16(sstate->dcerpc.dcerpchdrudp.fragnum);
+ }
+ sstate->fraglenleft = sstate->dcerpc.dcerpchdrudp.fraglen;
+ sstate->uuid_entry = (DCERPCUuidEntry *) SCCalloc(1,
+ sizeof(DCERPCUuidEntry));
+ if (sstate->uuid_entry == NULL) {
+ SCReturnUInt(-1);
+ } else {
+ memcpy(sstate->uuid_entry->uuid,
+ sstate->dcerpc.dcerpchdrudp.activityuuid,
+ sizeof(sstate->dcerpc.dcerpchdrudp.activityuuid));
+ TAILQ_INSERT_HEAD(&sstate->uuid_list, sstate->uuid_entry,
+ next);
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("DCERPC UDP", sstate->uuid_entry);
+ }
+#endif
+ }
+ --input_len;
+ break;
+ }
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnInt((p - input));
+}
+
+static int DCERPCUDPParse(Flow *f, void *dcerpc_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ uint32_t retval = 0;
+ uint32_t parsed = 0;
+ int hdrretval = 0;
+ SCEnter();
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ DCERPCUDPState *sstate = (DCERPCUDPState *) dcerpc_state;
+ while (sstate->bytesprocessed < DCERPC_UDP_HDR_LEN && input_len) {
+ hdrretval = DCERPCUDPParseHeader(f, dcerpc_state, pstate, input,
+ input_len);
+ if (hdrretval == -1 || hdrretval > (int32_t)input_len) {
+ sstate->bytesprocessed = 0;
+ SCReturnInt(hdrretval);
+ } else {
+ parsed += hdrretval;
+ input_len -= hdrretval;
+ }
+ }
+
+#if 0
+ printf("Done with DCERPCUDPParseHeader bytesprocessed %u/%u left %u\n",
+ sstate->bytesprocessed, sstate->dcerpc.dcerpchdrudp.fraglen, input_len);
+ printf("\nDCERPC Version:\t%u\n", sstate->dcerpc.dcerpchdrudp.rpc_vers);
+ printf("DCERPC Type:\t%u\n", sstate->dcerpc.dcerpchdrudp.ptype);
+ printf("DCERPC Flags1:\t0x%02x\n", sstate->dcerpc.dcerpchdrudp.flags1);
+ printf("DCERPC Flags2:\t0x%02x\n", sstate->dcerpc.dcerpchdrudp.flags2);
+ printf("DCERPC Packed Drep:\t%02x %02x %02x\n",
+ sstate->dcerpc.dcerpchdrudp.drep[0], sstate->dcerpc.dcerpchdrudp.drep[1],
+ sstate->dcerpc.dcerpchdrudp.drep[2]);
+ printf("DCERPC Frag Length:\t0x%04x %u\n", sstate->dcerpc.dcerpchdrudp.fraglen,
+ sstate->dcerpc.dcerpchdrudp.fraglen);
+ printf("DCERPC Frag Number:\t0x%04x\n", sstate->dcerpc.dcerpchdrudp.fragnum);
+ printf("DCERPC OpNum:\t0x%04x\n", sstate->dcerpc.dcerpchdrudp.opnum);
+#endif
+
+ while (sstate->bytesprocessed >= DCERPC_UDP_HDR_LEN
+ && sstate->bytesprocessed < sstate->dcerpc.dcerpchdrudp.fraglen
+ && input_len) {
+ retval = FragmentDataParser(f, dcerpc_state, pstate, input + parsed,
+ input_len);
+ if (retval || retval > input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing DCERPC UDP Fragment Data");
+ parsed -= input_len;
+ input_len = 0;
+ sstate->bytesprocessed = 0;
+ }
+ }
+
+ if (sstate->bytesprocessed == sstate->dcerpc.dcerpchdrudp.fraglen) {
+ sstate->bytesprocessed = 0;
+ }
+ if (pstate == NULL)
+ SCReturnInt(-1);
+
+ SCReturnInt(1);
+}
+
+static void *DCERPCUDPStateAlloc(void)
+{
+ void *s = SCMalloc(sizeof(DCERPCUDPState));
+ if (unlikely(s == NULL))
+ return NULL;
+
+ memset(s, 0, sizeof(DCERPCUDPState));
+ return s;
+}
+
+static void DCERPCUDPStateFree(void *s)
+{
+ DCERPCUDPState *sstate = (DCERPCUDPState *) s;
+
+ DCERPCUuidEntry *item;
+
+ while ((item = TAILQ_FIRST(&sstate->uuid_list))) {
+ //printUUID("Free", item);
+ TAILQ_REMOVE(&sstate->uuid_list, item, next);
+ SCFree(item);
+ }
+ if (sstate->dcerpc.dcerpcrequest.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcrequest.stub_data_buffer);
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer_len = 0;
+ }
+ if (sstate->dcerpc.dcerpcresponse.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcresponse.stub_data_buffer);
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer_len = 0;
+ }
+ SCFree(s);
+}
+
+static int DCERPCUDPRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_DCERPC,
+ "|04 00|", 2, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+void RegisterDCERPCUDPParsers(void)
+{
+ char *proto_name = "dcerpc";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_DCERPC, proto_name);
+ if (DCERPCUDPRegisterPatternsForProtocolDetection() < 0)
+ return;
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ "dcerpc");
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("udp", "dcerpc")) {
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DCERPC, STREAM_TOSERVER,
+ DCERPCUDPParse);
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ DCERPCUDPParse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_DCERPC, DCERPCUDPStateAlloc,
+ DCERPCUDPStateFree);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_UDP, ALPROTO_DCERPC, STREAM_TOSERVER);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", "dcerpc");
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_DCERPC, DCERPCUDPParserRegisterTests);
+#endif
+
+ return;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+/** \test DCERPC UDP Header Parsing and UUID handling
+ */
+
+int DCERPCUDPParserTest01(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t dcerpcrequest[] = {
+ 0x04, 0x00, 0x2c, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x3f, 0x98, 0xf0, 0x5c, 0xd9, 0x63, 0xcc, 0x46,
+ 0xc2, 0x74, 0x51, 0x6c, 0x8a, 0x53, 0x7d, 0x6f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xff,
+ 0xff, 0xff, 0x70, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x32, 0x24, 0x58, 0xfd,
+ 0xcc, 0x45, 0x64, 0x49, 0xb0, 0x70, 0xdd, 0xae,
+ 0x74, 0x2c, 0x96, 0xd2, 0x60, 0x5e, 0x0d, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x5e, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x7c, 0x5e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x80, 0x96, 0xf1, 0xf1,
+ 0x2a, 0x4d, 0xce, 0x11, 0xa6, 0x6a, 0x00, 0x20,
+ 0xaf, 0x6e, 0x72, 0xf4, 0x0c, 0x00, 0x00, 0x00,
+ 0x4d, 0x41, 0x52, 0x42, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0xf4, 0x0b, 0x00,
+ 0x10, 0x09, 0x00, 0x00, 0x10, 0x09, 0x00, 0x00,
+ 0x4d, 0x45, 0x4f, 0x57, 0x04, 0x00, 0x00, 0x00,
+ 0xa2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x38, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x08, 0x00, 0x00,
+ 0xd8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xc8, 0x00, 0x00, 0x00, 0x4d, 0x45, 0x4f, 0x57,
+ 0xd8, 0x08, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc4, 0x28, 0xcd, 0x00,
+ 0x64, 0x29, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0xb9, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xab, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xa5, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xa6, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xa4, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xad, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0xaa, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0x07, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x90, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x28, 0x06, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x50, 0x00, 0x00, 0x00, 0x4f, 0xb6, 0x88, 0x20,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x48, 0x00, 0x00, 0x00, 0x07, 0x00, 0x66, 0x00,
+ 0x06, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x19, 0x0c, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x98, 0x93,
+ 0x98, 0x4f, 0xd2, 0x11, 0xa9, 0x3d, 0xbe, 0x57,
+ 0xb2, 0x00, 0x00, 0x00, 0x32, 0x00, 0x31, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x80, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x43, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x4d, 0x45, 0x4f, 0x57, 0x04, 0x00, 0x00, 0x00,
+ 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x3b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x81, 0xc5, 0x17, 0x03,
+ 0x80, 0x0e, 0xe9, 0x4a, 0x99, 0x99, 0xf1, 0x8a,
+ 0x50, 0x6f, 0x7a, 0x85, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xd8, 0xda, 0x0d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x2f, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x46, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x2e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x68, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xff, 0xff,
+ 0x68, 0x8b, 0x0b, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x02, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00,
+ 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
+ 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
+ 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
+ 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
+ 0x31, 0x00, 0x31, 0x00, 0x9d, 0x13, 0x00, 0x01,
+ 0xcc, 0xe0, 0xfd, 0x7f, 0xcc, 0xe0, 0xfd, 0x7f,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
+ uint32_t requestlen = sizeof(dcerpcrequest);
+
+ TcpSession ssn;
+ DCERPCUuidEntry *uuid_entry;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START, dcerpcrequest, requestlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCUDPState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdrudp.rpc_vers != 4) {
+ printf("expected dcerpc version 0x04, got 0x%02x : ",
+ dcerpc_state->dcerpc.dcerpchdrudp.rpc_vers);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdrudp.fraglen != 1392) {
+ printf("expected dcerpc fraglen 0x%02x , got 0x%02x : ", 1392, dcerpc_state->dcerpc.dcerpchdrudp.fraglen);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdrudp.opnum != 4) {
+ printf("expected dcerpc opnum 0x%02x , got 0x%02x : ", 4, dcerpc_state->dcerpc.dcerpchdrudp.opnum);
+ result = 0;
+ goto end;
+ }
+
+ TAILQ_FOREACH(uuid_entry, &dcerpc_state->uuid_list, next) {
+ printUUID("REQUEST", uuid_entry);
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+void DCERPCUDPParserRegisterTests(void)
+{
+ UtRegisterTest("DCERPCUDPParserTest01", DCERPCUDPParserTest01, 1);
+}
+#endif
diff --git a/framework/src/suricata/src/app-layer-dcerpc-udp.h b/framework/src/suricata/src/app-layer-dcerpc-udp.h
new file mode 100644
index 00000000..c9054d6d
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dcerpc-udp.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009,2010 Open Information Security Foundation
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+
+#ifndef __APP_LAYER_DCERPC_UDP_H__
+#define __APP_LAYER_DCERPC_UDP_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-dcerpc-common.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+typedef struct DCERPCUDPState_ {
+ DCERPCUDP dcerpc;
+ uint16_t bytesprocessed;
+ uint16_t fraglenleft;
+ uint8_t *frag_data;
+ DCERPCUuidEntry *uuid_entry;
+ TAILQ_HEAD(, DCERPCUuidEntry_) uuid_list;
+} DCERPCUDPState;
+
+void RegisterDCERPCUDPParsers(void);
+void DCERPCUDPParserTests(void);
+void DCERPCUDPParserRegisterTests(void);
+
+#endif /* __APP_LAYER_DCERPC_UDP_H__ */
diff --git a/framework/src/suricata/src/app-layer-dcerpc.c b/framework/src/suricata/src/app-layer-dcerpc.c
new file mode 100644
index 00000000..44705ea9
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dcerpc.c
@@ -0,0 +1,6411 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \file DCE/RPC parser and decoder
+ *
+ * \todo Remove all the unnecessary per byte incremental loops with a full one
+ * time jump, i.e.
+ *
+ * input[0], input_len
+ * for (i = 0; i < x; i++)
+ * input++;
+ *
+ * with
+ *
+ * input += x;
+ *
+ * You'll be surprised at how many such cases we have here. We also need
+ * to do the same for htp parser. Should speed up the engine drastically.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-debug.h"
+
+#include "flow-util.h"
+
+#include "detect-engine-state.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+
+#include "app-layer-dcerpc.h"
+
+enum {
+ DCERPC_FIELD_NONE = 0,
+ DCERPC_PARSE_DCERPC_HEADER,
+ DCERPC_PARSE_DCERPC_BIND,
+ DCERPC_PARSE_DCERPC_BIND_ACK,
+ DCERPC_PARSE_DCERPC_REQUEST,
+ /* must be last */
+ DCERPC_FIELD_MAX,
+};
+
+/* \brief hexdump function from libdnet, used for debugging only */
+void hexdump(/*Flow *f,*/ const void *buf, size_t len)
+{
+ /* dumps len bytes of *buf to stdout. Looks like:
+ * [0000] 75 6E 6B 6E 6F 77 6E 20
+ * 30 FF 00 00 00 00 39 00 unknown 0.....9.
+ * (in a single line of course)
+ */
+
+ const unsigned char *p = buf;
+ unsigned char c;
+ size_t n;
+ char bytestr[4] = {0};
+ char addrstr[10] = {0};
+ char hexstr[ 16*3 + 5] = {0};
+ char charstr[16*1 + 5] = {0};
+ for (n=1; n<=len; n++) {
+ if (n%16 == 1) {
+ /* store address for this line */
+#if __WORDSIZE == 64
+ snprintf(addrstr, sizeof(addrstr), "%.4"PRIx64,
+ ((uint64_t)p-(uint64_t)buf) );
+#else
+ snprintf(addrstr, sizeof(addrstr), "%.4"PRIx32,
+ ((uint32_t)p-(uint32_t)buf) );
+#endif
+ }
+
+ c = *p;
+ if (isalnum(c) == 0) {
+ c = '.';
+ }
+
+ /* store hex str (for left side) */
+ snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
+ strlcat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
+
+ /* store char str (for right side) */
+ snprintf(bytestr, sizeof(bytestr), "%c", c);
+ strlcat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
+
+ if(n%16 == 0) {
+ /* line completed */
+ printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
+ hexstr[0] = 0;
+ charstr[0] = 0;
+ } else if(n%8 == 0) {
+ /* half line: add whitespaces */
+ strlcat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1);
+ strlcat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
+ }
+ p++; /* next byte */
+ }
+
+ if (strlen(hexstr) > 0) {
+ /* print rest of buffer if not empty */
+ printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
+ }
+}
+
+/**
+ * \brief printUUID function used to print UUID, Major and Minor Version Number
+ * and if it was Accepted or Rejected in the BIND_ACK.
+ */
+void printUUID(char *type, DCERPCUuidEntry *uuid)
+{
+ uint8_t i = 0;
+ if (uuid == NULL) {
+ return;
+ }
+ printf("%s UUID [%2u] %s ", type, uuid->ctxid,
+ (uuid->result == 0) ? "Accepted" : "Rejected");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", uuid->uuid[i]);
+ }
+ printf(" Major Version 0x%04x Minor Version 0x%04x\n", uuid->version,
+ uuid->versionminor);
+}
+
+/**
+ * \brief DCERPCParseSecondaryAddr reads secondaryaddrlen bytes from the BIND_ACK
+ * DCERPC call.
+ */
+static uint32_t DCERPCParseSecondaryAddr(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+ while (dcerpc->dcerpcbindbindack.secondaryaddrlenleft-- && input_len--) {
+ SCLogDebug("0x%02x ", *p);
+ p++;
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t PaddingParser(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+ while (dcerpc->padleft-- && input_len--) {
+ SCLogDebug("0x%02x ", *p);
+ p++;
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t DCERPCGetCTXItems(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+ if (input_len) {
+ switch (dcerpc->dcerpcbindbindack.ctxbytesprocessed) {
+ case 0:
+ if (input_len >= 4) {
+ dcerpc->dcerpcbindbindack.numctxitems = *p;
+ dcerpc->dcerpcbindbindack.numctxitemsleft = dcerpc->dcerpcbindbindack.numctxitems;
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += 4;
+ dcerpc->bytesprocessed += 4;
+ SCReturnUInt(4U);
+ } else {
+ dcerpc->dcerpcbindbindack.numctxitems = *(p++);
+ dcerpc->dcerpcbindbindack.numctxitemsleft = dcerpc->dcerpcbindbindack.numctxitems;
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ p++;
+ input_len--;
+ break;
+ }
+ }
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (p - input);
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief DCERPCParseBINDCTXItem is called for each CTXItem found the DCERPC BIND call.
+ * each UUID is added to a TAILQ.
+ */
+
+static uint32_t DCERPCParseBINDCTXItem(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+
+ if (input_len) {
+ switch (dcerpc->dcerpcbindbindack.ctxbytesprocessed) {
+ case 0:
+ if (input_len >= 44) {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.ctxid = *(p);
+ dcerpc->dcerpcbindbindack.ctxid |= *(p + 1) << 8;
+ dcerpc->dcerpcbindbindack.uuid[3] = *(p + 4);
+ dcerpc->dcerpcbindbindack.uuid[2] = *(p + 5);
+ dcerpc->dcerpcbindbindack.uuid[1] = *(p + 6);
+ dcerpc->dcerpcbindbindack.uuid[0] = *(p + 7);
+ dcerpc->dcerpcbindbindack.uuid[5] = *(p + 8);
+ dcerpc->dcerpcbindbindack.uuid[4] = *(p + 9);
+ dcerpc->dcerpcbindbindack.uuid[7] = *(p + 10);
+ dcerpc->dcerpcbindbindack.uuid[6] = *(p + 11);
+ dcerpc->dcerpcbindbindack.uuid[8] = *(p + 12);
+ dcerpc->dcerpcbindbindack.uuid[9] = *(p + 13);
+ dcerpc->dcerpcbindbindack.uuid[10] = *(p + 14);
+ dcerpc->dcerpcbindbindack.uuid[11] = *(p + 15);
+ dcerpc->dcerpcbindbindack.uuid[12] = *(p + 16);
+ dcerpc->dcerpcbindbindack.uuid[13] = *(p + 17);
+ dcerpc->dcerpcbindbindack.uuid[14] = *(p + 18);
+ dcerpc->dcerpcbindbindack.uuid[15] = *(p + 19);
+ dcerpc->dcerpcbindbindack.version = *(p + 20);
+ dcerpc->dcerpcbindbindack.version |= *(p + 21) << 8;
+ dcerpc->dcerpcbindbindack.versionminor = *(p + 22);
+ dcerpc->dcerpcbindbindack.versionminor |= *(p + 23) << 8;
+ } else { /* Big Endian */
+ dcerpc->dcerpcbindbindack.ctxid = *(p) << 8;
+ dcerpc->dcerpcbindbindack.ctxid |= *(p + 1);
+ dcerpc->dcerpcbindbindack.uuid[0] = *(p + 4);
+ dcerpc->dcerpcbindbindack.uuid[1] = *(p + 5);
+ dcerpc->dcerpcbindbindack.uuid[2] = *(p + 6);
+ dcerpc->dcerpcbindbindack.uuid[3] = *(p + 7);
+ dcerpc->dcerpcbindbindack.uuid[4] = *(p + 8);
+ dcerpc->dcerpcbindbindack.uuid[5] = *(p + 9);
+ dcerpc->dcerpcbindbindack.uuid[6] = *(p + 10);
+ dcerpc->dcerpcbindbindack.uuid[7] = *(p + 11);
+ dcerpc->dcerpcbindbindack.uuid[8] = *(p + 12);
+ dcerpc->dcerpcbindbindack.uuid[9] = *(p + 13);
+ dcerpc->dcerpcbindbindack.uuid[10] = *(p + 14);
+ dcerpc->dcerpcbindbindack.uuid[11] = *(p + 15);
+ dcerpc->dcerpcbindbindack.uuid[12] = *(p + 16);
+ dcerpc->dcerpcbindbindack.uuid[13] = *(p + 17);
+ dcerpc->dcerpcbindbindack.uuid[14] = *(p + 18);
+ dcerpc->dcerpcbindbindack.uuid[15] = *(p + 19);
+ dcerpc->dcerpcbindbindack.version = *(p + 20) << 8;
+ dcerpc->dcerpcbindbindack.version |= *(p + 21);
+ dcerpc->dcerpcbindbindack.versionminor = *(p + 22) << 8;
+ dcerpc->dcerpcbindbindack.versionminor |= *(p + 23);
+ }
+ //if (dcerpc->dcerpcbindbindack.ctxid == dcerpc->dcerpcbindbindack.numctxitems
+ // - dcerpc->dcerpcbindbindack.numctxitemsleft) {
+
+ dcerpc->dcerpcbindbindack.uuid_entry = (DCERPCUuidEntry *)SCCalloc(1, sizeof(DCERPCUuidEntry));
+ if (dcerpc->dcerpcbindbindack.uuid_entry == NULL) {
+ SCLogDebug("UUID Entry is NULL");
+ SCReturnUInt(0);
+ }
+
+ dcerpc->dcerpcbindbindack.uuid_entry->internal_id = dcerpc->dcerpcbindbindack.uuid_internal_id++;
+
+ memcpy(dcerpc->dcerpcbindbindack.uuid_entry->uuid,
+ dcerpc->dcerpcbindbindack.uuid,
+ sizeof(dcerpc->dcerpcbindbindack.uuid));
+
+ dcerpc->dcerpcbindbindack.uuid_entry->ctxid = dcerpc->dcerpcbindbindack.ctxid;
+ dcerpc->dcerpcbindbindack.uuid_entry->version = dcerpc->dcerpcbindbindack.version;
+ dcerpc->dcerpcbindbindack.uuid_entry->versionminor = dcerpc->dcerpcbindbindack.versionminor;
+
+ /* store the first frag flag in the uuid as pfc_flags will
+ * be overwritten by new packets. */
+ if (dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG) {
+ dcerpc->dcerpcbindbindack.uuid_entry->flags |= DCERPC_UUID_ENTRY_FLAG_FF;
+ }
+
+ TAILQ_INSERT_HEAD(&dcerpc->dcerpcbindbindack.uuid_list,
+ dcerpc->dcerpcbindbindack.uuid_entry,
+ next);
+
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("BIND", dcerpc->dcerpcbindbindack.uuid_entry);
+ }
+#endif
+ dcerpc->dcerpcbindbindack.numctxitemsleft--;
+ dcerpc->bytesprocessed += (44);
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (44);
+ SCReturnUInt(44U);
+
+ //} else {
+ // SCLogDebug("ctxitem %u, expected %u\n", dcerpc->dcerpcbindbindack.ctxid,
+ // dcerpc->dcerpcbindbindack.numctxitems - dcerpc->dcerpcbindbindack.numctxitemsleft);
+ // SCReturnUInt(0);
+ //}
+ } else {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.ctxid = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.ctxid = *(p++) << 8;
+ }
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.ctxid |= *(p++) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.ctxid |= *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ /* num transact items */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ /* reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[3] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[0] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[2] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[1] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[1] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[2] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[0] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[3] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[5] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[4] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[4] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[5] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[7] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[6] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.uuid[6] = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.uuid[7] = *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ /* The following bytes are in the same order for both big and little endian */
+ dcerpc->dcerpcbindbindack.uuid[8] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ dcerpc->dcerpcbindbindack.uuid[9] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ dcerpc->dcerpcbindbindack.uuid[10] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ dcerpc->dcerpcbindbindack.uuid[11] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ dcerpc->dcerpcbindbindack.uuid[12] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ dcerpc->dcerpcbindbindack.uuid[13] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ dcerpc->dcerpcbindbindack.uuid[14] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ dcerpc->dcerpcbindbindack.uuid[15] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.version = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.version = *(p++) << 8;
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.version |= *(p++) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.version |= *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.versionminor = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.versionminor = *(p++) << 8;
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.versionminor |= *(p++) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.versionminor |= *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 28:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 29:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 30:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 31:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 32:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 33:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 34:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 35:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 36:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 37:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 38:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 39:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 40:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 41:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 42:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 43:
+ p++;
+ --input_len;
+ //if (dcerpc->dcerpcbindbindack.ctxid == dcerpc->dcerpcbindbindack.numctxitems - dcerpc->dcerpcbindbindack.numctxitemsleft) {
+ dcerpc->dcerpcbindbindack.uuid_entry = (DCERPCUuidEntry *)
+ SCCalloc(1, sizeof(DCERPCUuidEntry));
+ if (dcerpc->dcerpcbindbindack.uuid_entry == NULL) {
+ SCLogDebug("UUID Entry is NULL\n");
+ SCReturnUInt(0);
+ }
+
+ dcerpc->dcerpcbindbindack.uuid_entry->internal_id =
+ dcerpc->dcerpcbindbindack.uuid_internal_id++;
+ memcpy(dcerpc->dcerpcbindbindack.uuid_entry->uuid,
+ dcerpc->dcerpcbindbindack.uuid,
+ sizeof(dcerpc->dcerpcbindbindack.uuid));
+ dcerpc->dcerpcbindbindack.uuid_entry->ctxid = dcerpc->dcerpcbindbindack.ctxid;
+ dcerpc->dcerpcbindbindack.uuid_entry->version = dcerpc->dcerpcbindbindack.version;
+ dcerpc->dcerpcbindbindack.uuid_entry->versionminor = dcerpc->dcerpcbindbindack.versionminor;
+
+ /* store the first frag flag in the uuid as pfc_flags will
+ * be overwritten by new packets. */
+ if (dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG) {
+ dcerpc->dcerpcbindbindack.uuid_entry->flags |= DCERPC_UUID_ENTRY_FLAG_FF;
+ }
+
+ TAILQ_INSERT_HEAD(&dcerpc->dcerpcbindbindack.uuid_list,
+ dcerpc->dcerpcbindbindack.uuid_entry,
+ next);
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("BINDACK", dcerpc->dcerpcbindbindack.uuid_entry);
+ }
+#endif
+ dcerpc->dcerpcbindbindack.numctxitemsleft--;
+ dcerpc->bytesprocessed += (p - input);
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+
+ //} else {
+ // SCLogDebug("ctxitem %u, expected %u\n", dcerpc->dcerpcbindbindack.ctxid,
+ // dcerpc->dcerpcbindbindack.numctxitems - dcerpc->dcerpcbindbindack.numctxitemsleft);
+ // SCReturnUInt(0);
+ //}
+ break;
+ }
+ }
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (p - input);
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief DCERPCParseBINDACKCTXItem is called for each CTXItem found in
+ * the BIND_ACK call. The result (Accepted or Rejected) is added to the
+ * correct UUID from the BIND call.
+ */
+static uint32_t DCERPCParseBINDACKCTXItem(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+ DCERPCUuidEntry *uuid_entry;
+
+ if (input_len) {
+ switch (dcerpc->dcerpcbindbindack.ctxbytesprocessed) {
+ case 0:
+ if (input_len >= 24) {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.result = *p;
+ dcerpc->dcerpcbindbindack.result |= *(p + 1) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.result = *p << 8;
+ dcerpc->dcerpcbindbindack.result |= *(p + 1);
+ }
+ TAILQ_FOREACH(uuid_entry, &dcerpc->dcerpcbindbindack.uuid_list, next) {
+ if (uuid_entry->internal_id == dcerpc->dcerpcbindbindack.uuid_internal_id) {
+ uuid_entry->result = dcerpc->dcerpcbindbindack.result;
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("BIND_ACK", uuid_entry);
+ }
+#endif
+ if (uuid_entry->result != 0)
+ break;
+
+ dcerpc->dcerpcbindbindack.uuid_entry = (DCERPCUuidEntry *)
+ SCCalloc(1, sizeof(DCERPCUuidEntry));
+ if (dcerpc->dcerpcbindbindack.uuid_entry != NULL) {
+ memcpy(dcerpc->dcerpcbindbindack.uuid_entry,
+ uuid_entry,
+ sizeof(DCERPCUuidEntry));
+ TAILQ_INSERT_HEAD(&dcerpc->dcerpcbindbindack.accepted_uuid_list,
+ dcerpc->dcerpcbindbindack.uuid_entry,
+ next);
+ }
+ break;
+ }
+ }
+ dcerpc->dcerpcbindbindack.uuid_internal_id++;
+ dcerpc->dcerpcbindbindack.numctxitemsleft--;
+ dcerpc->bytesprocessed += (24);
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (24);
+ SCReturnUInt(24U);
+ } else {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.result = *(p++);
+ } else {
+ dcerpc->dcerpcbindbindack.result = *(p++) << 8;
+ }
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.result |= *(p++) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.result |= *(p++);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ /* num transact items */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ /* reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ TAILQ_FOREACH(uuid_entry, &dcerpc->dcerpcbindbindack.uuid_list, next) {
+ if (uuid_entry->internal_id == dcerpc->dcerpcbindbindack.uuid_internal_id) {
+ uuid_entry->result = dcerpc->dcerpcbindbindack.result;
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ printUUID("BIND_ACK", uuid_entry);
+ }
+#endif
+ if (uuid_entry->result != 0)
+ break;
+
+ dcerpc->dcerpcbindbindack.uuid_entry = (DCERPCUuidEntry *)
+ SCCalloc(1, sizeof(DCERPCUuidEntry));
+ if (dcerpc->dcerpcbindbindack.uuid_entry != NULL) {
+ memcpy(dcerpc->dcerpcbindbindack.uuid_entry,
+ uuid_entry,
+ sizeof(DCERPCUuidEntry));
+ TAILQ_INSERT_HEAD(&dcerpc->dcerpcbindbindack.accepted_uuid_list,
+ dcerpc->dcerpcbindbindack.uuid_entry,
+ next);
+ }
+ break;
+ }
+ }
+ dcerpc->dcerpcbindbindack.uuid_internal_id++;
+ dcerpc->dcerpcbindbindack.numctxitemsleft--;
+ p++;
+ --input_len;
+ break;
+
+ }
+ }
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed += (p - input);
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t DCERPCParseBIND(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ DCERPCUuidEntry *item;
+ uint8_t *p = input;
+ if (input_len) {
+ switch (dcerpc->bytesprocessed) {
+ case 16:
+ dcerpc->dcerpcbindbindack.numctxitems = 0;
+ if (input_len >= 12) {
+ while ((item = TAILQ_FIRST(&dcerpc->dcerpcbindbindack.uuid_list))) {
+ TAILQ_REMOVE(&dcerpc->dcerpcbindbindack.uuid_list, item, next);
+ SCFree(item);
+ }
+ if (dcerpc->dcerpchdr.type == BIND) {
+ while ((item = TAILQ_FIRST(&dcerpc->dcerpcbindbindack.accepted_uuid_list))) {
+ TAILQ_REMOVE(&dcerpc->dcerpcbindbindack.accepted_uuid_list, item, next);
+ SCFree(item);
+ }
+ TAILQ_INIT(&dcerpc->dcerpcbindbindack.accepted_uuid_list);
+ }
+ dcerpc->dcerpcbindbindack.uuid_internal_id = 0;
+ dcerpc->dcerpcbindbindack.numctxitems = *(p + 8);
+ dcerpc->dcerpcbindbindack.numctxitemsleft = dcerpc->dcerpcbindbindack.numctxitems;
+ TAILQ_INIT(&dcerpc->dcerpcbindbindack.uuid_list);
+ dcerpc->bytesprocessed += 12;
+ SCReturnUInt(12U);
+ } else {
+ /* max_xmit_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 17:
+ /* max_xmit_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ /* max_recv_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ /* max_recv_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ while ((item = TAILQ_FIRST(&dcerpc->dcerpcbindbindack.uuid_list))) {
+ TAILQ_REMOVE(&dcerpc->dcerpcbindbindack.uuid_list, item, next);
+ SCFree(item);
+ }
+ if (dcerpc->dcerpchdr.type == BIND) {
+ while ((item = TAILQ_FIRST(&dcerpc->dcerpcbindbindack.accepted_uuid_list))) {
+ TAILQ_REMOVE(&dcerpc->dcerpcbindbindack.accepted_uuid_list, item, next);
+ SCFree(item);
+ }
+ TAILQ_INIT(&dcerpc->dcerpcbindbindack.accepted_uuid_list);
+ }
+ dcerpc->dcerpcbindbindack.uuid_internal_id = 0;
+ dcerpc->dcerpcbindbindack.numctxitems = *(p++);
+ dcerpc->dcerpcbindbindack.numctxitemsleft = dcerpc->dcerpcbindbindack.numctxitems;
+ TAILQ_INIT(&dcerpc->dcerpcbindbindack.uuid_list);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ /* pad byte 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ /* pad byte 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ /* pad byte 3 */
+ p++;
+ --input_len;
+ break;
+ /* fall through */
+ default:
+ dcerpc->bytesprocessed++;
+ SCReturnUInt(1);
+ break;
+ }
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t DCERPCParseBINDACK(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+
+ switch (dcerpc->bytesprocessed) {
+ case 16:
+ dcerpc->dcerpcbindbindack.uuid_internal_id = 0;
+ dcerpc->dcerpcbindbindack.numctxitems = 0;
+ if (input_len >= 10) {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.secondaryaddrlen = *(p + 8);
+ dcerpc->dcerpcbindbindack.secondaryaddrlen |= *(p + 9) << 8;
+ } else {
+ dcerpc->dcerpcbindbindack.secondaryaddrlen = *(p + 8) << 8;
+ dcerpc->dcerpcbindbindack.secondaryaddrlen |= *(p + 9);
+ }
+ dcerpc->dcerpcbindbindack.secondaryaddrlenleft = dcerpc->dcerpcbindbindack.secondaryaddrlen;
+ dcerpc->bytesprocessed += 10;
+ SCReturnUInt(10U);
+ } else {
+ /* max_xmit_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 17:
+ /* max_xmit_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ /* max_recv_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ /* max_recv_frag */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ /* assoc_group_id */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ dcerpc->dcerpcbindbindack.secondaryaddrlen = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ dcerpc->dcerpcbindbindack.secondaryaddrlen |= *(p++);
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcbindbindack.secondaryaddrlen = SCByteSwap16(dcerpc->dcerpcbindbindack.secondaryaddrlen);
+ }
+ dcerpc->dcerpcbindbindack.secondaryaddrlenleft = dcerpc->dcerpcbindbindack.secondaryaddrlen;
+ SCLogDebug("secondaryaddrlen %u 0x%04x\n", dcerpc->dcerpcbindbindack.secondaryaddrlen,
+ dcerpc->dcerpcbindbindack.secondaryaddrlen);
+ --input_len;
+ break;
+ default:
+ dcerpc->bytesprocessed++;
+ SCReturnUInt(1);
+ break;
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t DCERPCParseREQUEST(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+
+ switch (dcerpc->bytesprocessed) {
+ case 16:
+ dcerpc->dcerpcbindbindack.numctxitems = 0;
+ if (input_len >= 8) {
+ if (dcerpc->dcerpchdr.type == REQUEST) {
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpcrequest.ctxid = *(p + 4);
+ dcerpc->dcerpcrequest.ctxid |= *(p + 5) << 8;
+ dcerpc->dcerpcrequest.opnum = *(p + 6);
+ dcerpc->dcerpcrequest.opnum |= *(p + 7) << 8;
+ } else {
+ dcerpc->dcerpcrequest.ctxid = *(p + 4) << 8;
+ dcerpc->dcerpcrequest.ctxid |= *(p + 5);
+ dcerpc->dcerpcrequest.opnum = *(p + 6) << 8;
+ dcerpc->dcerpcrequest.opnum |= *(p + 7);
+ }
+ dcerpc->dcerpcrequest.first_request_seen = 1;
+ }
+ dcerpc->bytesprocessed += 8;
+ SCReturnUInt(8U);
+ } else {
+ /* alloc hint 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 17:
+ /* alloc hint 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ /* alloc hint 3 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ /* alloc hint 4 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ /* context id 1 */
+ dcerpc->dcerpcrequest.ctxid = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ /* context id 2 */
+ dcerpc->dcerpcrequest.ctxid |= *(p++) << 8;
+ if (!(dcerpc->dcerpchdr.packed_drep[0] & 0x10)) {
+ dcerpc->dcerpcrequest.ctxid = SCByteSwap16(dcerpc->dcerpcrequest.ctxid);
+ }
+ dcerpc->dcerpcrequest.first_request_seen = 1;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ if (dcerpc->dcerpchdr.type == REQUEST) {
+ dcerpc->dcerpcrequest.opnum = *(p++);
+ } else {
+ p++;
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ if (dcerpc->dcerpchdr.type == REQUEST) {
+ dcerpc->dcerpcrequest.opnum |= *(p++) << 8;
+ if (!(dcerpc->dcerpchdr.packed_drep[0] & 0x10)) {
+ dcerpc->dcerpcrequest.opnum = SCByteSwap16(dcerpc->dcerpcrequest.opnum);
+ }
+ } else {
+ p++;
+ }
+ --input_len;
+ break;
+ default:
+ dcerpc->bytesprocessed++;
+ SCReturnUInt(1);
+ break;
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/** \internal
+ * \retval stub_len or 0 in case of error */
+static uint32_t StubDataParser(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t **stub_data_buffer = NULL;
+ uint32_t *stub_data_buffer_len = NULL;
+ uint8_t *stub_data_fresh = NULL;
+ uint16_t stub_len = 0;
+ void *ptmp;
+
+ /* request PDU. Retrieve the request stub buffer */
+ if (dcerpc->dcerpchdr.type == REQUEST) {
+ stub_data_buffer = &dcerpc->dcerpcrequest.stub_data_buffer;
+ stub_data_buffer_len = &dcerpc->dcerpcrequest.stub_data_buffer_len;
+ stub_data_fresh = &dcerpc->dcerpcrequest.stub_data_fresh;
+
+ /* response PDU. Retrieve the response stub buffer */
+ } else {
+ stub_data_buffer = &dcerpc->dcerpcresponse.stub_data_buffer;
+ stub_data_buffer_len = &dcerpc->dcerpcresponse.stub_data_buffer_len;
+ stub_data_fresh = &dcerpc->dcerpcresponse.stub_data_fresh;
+ }
+
+ stub_len = (dcerpc->padleft < input_len) ? dcerpc->padleft : input_len;
+ if (stub_len == 0) {
+ SCLogError(SC_ERR_DCERPC, "stub_len is NULL. We shouldn't be seeing "
+ "this. In case you are, there is something gravely wrong "
+ "with the dcerpc parser");
+ SCReturnInt(0);
+ }
+
+ /* To see what is in this stub fragment */
+ //hexdump(input, stub_len);
+ /* if the frag is the the first frag irrespective of it being a part of
+ * a multi frag PDU or not, it indicates the previous PDU's stub would
+ * have been buffered and processed and we can use the buffer to hold
+ * frags from a fresh request/response. Also if the state is in the
+ * process of processing a fragmented pdu, we should append to the
+ * existing stub and not reset the stub buffer */
+ if ((dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG) &&
+ !dcerpc->pdu_fragged) {
+ *stub_data_buffer_len = 0;
+ /* just a hack to get thing working. We shouldn't be setting
+ * this var here. The ideal thing would have been to use
+ * an extra state var, to indicate that the stub parser has made a
+ * fresh entry after reseting the buffer, but maintaing an extra var
+ * would be a nuisance, while we can achieve the same thing with
+ * little or no effort, with a simple set here, although semantically
+ * it is a wrong thing to set it here, since we still can't conclude
+ * if a pdu is fragmented or not at this point, if we are parsing a PDU
+ * that has some stub data in the first segment, but it still doesn't
+ * contain the entire PDU */
+ dcerpc->pdu_fragged = 1;
+ }
+
+ ptmp = SCRealloc(*stub_data_buffer, *stub_data_buffer_len + stub_len);
+ if (ptmp == NULL) {
+ SCFree(*stub_data_buffer);
+ *stub_data_buffer = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ SCReturnUInt(0);
+ }
+ *stub_data_buffer = ptmp;
+
+ memcpy(*stub_data_buffer + *stub_data_buffer_len, input, stub_len);
+
+ *stub_data_fresh = 1;
+ /* length of the buffered stub */
+ *stub_data_buffer_len += stub_len;
+ /* To see the total reassembled stubdata */
+ //hexdump(*stub_data_buffer, *stub_data_buffer_len);
+
+ dcerpc->padleft -= stub_len;
+ dcerpc->bytesprocessed += stub_len;
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ int i = 0;
+ for (i = 0; i < stub_len; i++) {
+ SCLogDebug("0x%02x ", input[i]);
+ }
+ }
+#endif
+
+ SCReturnUInt((uint32_t)stub_len);
+}
+
+/**
+ * \brief DCERPCParseHeader parses the 16 byte DCERPC header
+ * A fast path for normal decoding is used when there is enough bytes
+ * present to parse the entire header. A slow path is used to parse
+ * fragmented packets.
+ * \retval -1 if DCERPC Header does not validate
+ * \retval Number of bytes processed
+ */
+static int DCERPCParseHeader(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ uint8_t *p = input;
+
+ if (input_len) {
+ SCLogDebug("dcerpc->bytesprocessed %u", dcerpc->bytesprocessed);
+ switch (dcerpc->bytesprocessed) {
+ case 0:
+ if (input_len >= DCERPC_HDR_LEN) {
+ dcerpc->dcerpchdr.rpc_vers = *p;
+ dcerpc->dcerpchdr.rpc_vers_minor = *(p + 1);
+ if ((dcerpc->dcerpchdr.rpc_vers != 5) ||
+ ((dcerpc->dcerpchdr.rpc_vers_minor != 0) &&
+ (dcerpc->dcerpchdr.rpc_vers_minor != 1))) {
+ SCLogDebug("DCERPC Header did not validate");
+ SCReturnInt(-1);
+ }
+ dcerpc->dcerpchdr.type = *(p + 2);
+ SCLogDebug("dcerpc->dcerpchdr.type %02x",
+ dcerpc->dcerpchdr.type);
+ dcerpc->dcerpchdr.pfc_flags = *(p + 3);
+ dcerpc->dcerpchdr.packed_drep[0] = *(p + 4);
+ dcerpc->dcerpchdr.packed_drep[1] = *(p + 5);
+ dcerpc->dcerpchdr.packed_drep[2] = *(p + 6);
+ dcerpc->dcerpchdr.packed_drep[3] = *(p + 7);
+ if (dcerpc->dcerpchdr.packed_drep[0] & 0x10) {
+ dcerpc->dcerpchdr.frag_length = *(p + 8);
+ dcerpc->dcerpchdr.frag_length |= *(p + 9) << 8;
+ dcerpc->dcerpchdr.auth_length = *(p + 10);
+ dcerpc->dcerpchdr.auth_length |= *(p + 11) << 8;
+ dcerpc->dcerpchdr.call_id = *(p + 12) << 24;
+ dcerpc->dcerpchdr.call_id |= *(p + 13) << 16;
+ dcerpc->dcerpchdr.call_id |= *(p + 14) << 8;
+ dcerpc->dcerpchdr.call_id |= *(p + 15);
+ } else {
+ dcerpc->dcerpchdr.frag_length = *(p + 8) << 8;
+ dcerpc->dcerpchdr.frag_length |= *(p + 9);
+ dcerpc->dcerpchdr.auth_length = *(p + 10) << 8;
+ dcerpc->dcerpchdr.auth_length |= *(p + 11);
+ dcerpc->dcerpchdr.call_id = *(p + 12);
+ dcerpc->dcerpchdr.call_id |= *(p + 13) << 8;
+ dcerpc->dcerpchdr.call_id |= *(p + 14) << 16;
+ dcerpc->dcerpchdr.call_id |= *(p + 15) << 24;
+ }
+ dcerpc->bytesprocessed = DCERPC_HDR_LEN;
+ SCReturnInt(16);
+ break;
+ } else {
+ dcerpc->dcerpchdr.rpc_vers = *(p++);
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ dcerpc->dcerpchdr.rpc_vers_minor = *(p++);
+ if ((dcerpc->dcerpchdr.rpc_vers != 5) ||
+ ((dcerpc->dcerpchdr.rpc_vers_minor != 0) &&
+ (dcerpc->dcerpchdr.rpc_vers_minor != 1))) {
+ SCLogDebug("DCERPC Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ dcerpc->dcerpchdr.type = *(p++);
+ SCLogDebug("dcerpc->dcerpchdr.type %02x",
+ dcerpc->dcerpchdr.type);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ dcerpc->dcerpchdr.pfc_flags = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ dcerpc->dcerpchdr.packed_drep[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ dcerpc->dcerpchdr.packed_drep[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ dcerpc->dcerpchdr.packed_drep[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ dcerpc->dcerpchdr.packed_drep[3] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ dcerpc->dcerpchdr.frag_length = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ dcerpc->dcerpchdr.frag_length |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ dcerpc->dcerpchdr.auth_length = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ dcerpc->dcerpchdr.auth_length |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ dcerpc->dcerpchdr.call_id = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ dcerpc->dcerpchdr.call_id |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ dcerpc->dcerpchdr.call_id |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ dcerpc->dcerpchdr.call_id |= *(p++) << 24;
+ if (!(dcerpc->dcerpchdr.packed_drep[0] & 0x10)) {
+ dcerpc->dcerpchdr.frag_length = SCByteSwap16(dcerpc->dcerpchdr.frag_length);
+ dcerpc->dcerpchdr.auth_length = SCByteSwap16(dcerpc->dcerpchdr.auth_length);
+ dcerpc->dcerpchdr.call_id = SCByteSwap32(dcerpc->dcerpchdr.call_id);
+ }
+ --input_len;
+ break;
+ default:
+ dcerpc->bytesprocessed++;
+ SCReturnInt(1);
+ }
+ }
+ dcerpc->bytesprocessed += (p - input);
+ SCReturnInt((p - input));
+}
+
+static inline void DCERPCResetParsingState(DCERPC *dcerpc)
+{
+ dcerpc->bytesprocessed = 0;
+ dcerpc->pdu_fragged = 0;
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed = 0;
+
+ return;
+}
+
+static inline void DCERPCResetStub(DCERPC *dcerpc)
+{
+ if (dcerpc->dcerpchdr.type == REQUEST)
+ dcerpc->dcerpcrequest.stub_data_buffer_len = 0;
+ else if (dcerpc->dcerpchdr.type == RESPONSE)
+ dcerpc->dcerpcresponse.stub_data_buffer_len = 0;
+
+ return;
+}
+
+static inline int DCERPCThrowOutExtraData(DCERPC *dcerpc, uint8_t *input,
+ uint16_t input_len)
+{
+ int parsed = 0;
+ /* the function always assumes that
+ * dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length */
+ if (input_len > (dcerpc->dcerpchdr.frag_length - dcerpc->bytesprocessed)) {
+ parsed = dcerpc->dcerpchdr.frag_length - dcerpc->bytesprocessed;
+ } else {
+ parsed = input_len;
+ }
+ dcerpc->bytesprocessed += parsed;
+
+ return parsed;
+}
+
+/**
+ * \todo - Currently the parser is very generic. Enable target based
+ * reassembly.
+ * - Disable reiniting tailq for mid and last bind/alter_context pdus.
+ * - Use a PM to search for subsequent 05 00 when we see an inconsistent
+ * pdu. This should be done for each platform based on how it handles
+ * a condition where it has receives a segment with 2 pdus, while the
+ * first pdu in the segment is corrupt.
+ */
+int32_t DCERPCParser(DCERPC *dcerpc, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ uint32_t retval = 0;
+ uint32_t parsed = 0;
+ int hdrretval = 0;
+
+ dcerpc->dcerpcrequest.stub_data_fresh = 0;
+ dcerpc->dcerpcresponse.stub_data_fresh = 0;
+
+ /* temporary use. we will get rid of this later, once we have ironed out
+ * all the endless loops cases */
+ int counter = 0;
+
+ while (input_len) {
+ /* in case we have any corner cases remainging, we have this */
+ if (counter++ == 30) {
+ SCLogDebug("Somehow seem to be stuck inside the dce "
+ "parser for quite sometime. Let's get out of here.");
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN && input_len) {
+ hdrretval = DCERPCParseHeader(dcerpc, input + parsed, input_len);
+ if (hdrretval == -1 || hdrretval > (int32_t)input_len) {
+ SCLogDebug("Error parsing dce header. Discarding "
+ "PDU and reseting parsing state to parse next PDU");
+ /* error parsing pdu header. Let's clear the dce state */
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ } else {
+ parsed += hdrretval;
+ input_len -= hdrretval;
+ }
+ }
+ SCLogDebug("Done with DCERPCParseHeader bytesprocessed %u/%u left %u",
+ dcerpc->bytesprocessed, dcerpc->dcerpchdr.frag_length, input_len);
+#if 0
+ printf("Done with DCERPCParseHeader bytesprocessed %u/%u input_len left %u\n",
+ dcerpc->bytesprocessed, dcerpc->dcerpchdr.frag_length, input_len);
+ printf("\nDCERPC Version:\t%u\n", dcerpc->dcerpchdr.rpc_vers);
+ printf("DCERPC Version Minor:\t%u\n", dcerpc->dcerpchdr.rpc_vers_minor);
+ printf("DCERPC Type:\t%u\n", dcerpc->dcerpchdr.type);
+ printf("DCERPC Flags:\t0x%02x\n", dcerpc->dcerpchdr.pfc_flags);
+ printf("DCERPC Packed Drep:\t%02x %02x %02x %02x\n",
+ dcerpc->dcerpchdr.packed_drep[0], dcerpc->dcerpchdr.packed_drep[1],
+ dcerpc->dcerpchdr.packed_drep[2], dcerpc->dcerpchdr.packed_drep[3]);
+ printf("DCERPC Frag Length:\t0x%04x %u\n",
+ dcerpc->dcerpchdr.frag_length, dcerpc->dcerpchdr.frag_length);
+ printf("DCERPC Auth Length:\t0x%04x\n", dcerpc->dcerpchdr.auth_length);
+ printf("DCERPC Call Id:\t0x%08x\n", dcerpc->dcerpchdr.call_id);
+#endif
+
+ /* check if we have parsed the entire input passed in the header parser.
+ * If we have, time to leave */
+ if (input_len == 0) {
+ if (dcerpc->bytesprocessed < 10) {
+ /* if the parser is known to be fragmented at this stage itself,
+ * we reset the stub buffer here itself */
+ if (!dcerpc->pdu_fragged && (dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG)) {
+ DCERPCResetStub(dcerpc);
+ }
+ dcerpc->pdu_fragged = 1;
+ } else {
+ if (dcerpc->bytesprocessed >= dcerpc->dcerpchdr.frag_length) {
+ SCLogDebug("Weird DCE PDU");
+ DCERPCResetParsingState(dcerpc);
+ } else {
+ /* if the parser is known to be fragmented at this stage itself,
+ * we reset the stub buffer here itself */
+ if (!dcerpc->pdu_fragged && (dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG)) {
+ DCERPCResetStub(dcerpc);
+ }
+ dcerpc->pdu_fragged = 1;
+ }
+ }
+ SCReturnInt(parsed);
+ }
+ switch (dcerpc->dcerpchdr.type) {
+ case BIND:
+ case ALTER_CONTEXT:
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN + 12
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length
+ && input_len) {
+ retval = DCERPCParseBIND(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error Parsing DCERPC %s PDU",
+ (dcerpc->dcerpchdr.type == BIND) ?
+ "BIND" : "ALTER_CONTEXT");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+ SCLogDebug("Done with DCERPCParseBIND bytesprocessed %u/%u numctxitems %u",
+ dcerpc->bytesprocessed, dcerpc->dcerpchdr.frag_length,
+ dcerpc->dcerpcbindbindack.numctxitems);
+ while (dcerpc->dcerpcbindbindack.numctxitemsleft && dcerpc->bytesprocessed
+ < dcerpc->dcerpchdr.frag_length && input_len) {
+ retval = DCERPCParseBINDCTXItem(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ if (dcerpc->dcerpcbindbindack.ctxbytesprocessed == 44) {
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed = 0;
+ }
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("BIND processed %u/%u ctxitems %u/%u input_len left %u\n",
+ dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length,
+ dcerpc->dcerpcbindbindack.numctxitemsleft,
+ dcerpc->dcerpcbindbindack.numctxitems, input_len);
+ } else if (input_len) {
+ //parsed -= input_len;
+ SCLogDebug("Error Parsing CTX Item %u\n", parsed);
+ parsed = 0;
+ input_len = 0;
+ dcerpc->dcerpcbindbindack.numctxitemsleft = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else if (dcerpc->bytesprocessed > dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ } else {
+ /* temporary fix */
+ if (input_len) {
+ retval = DCERPCThrowOutExtraData(dcerpc, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ input_len -= retval;
+ parsed += retval;
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ } else {
+ SCLogDebug("Error Parsing DCERPC");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ }
+ break;
+
+ case BIND_ACK:
+ case ALTER_CONTEXT_RESP:
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN + 9
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length
+ && input_len) {
+ retval = DCERPCParseBINDACK(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("DCERPCParseBINDACK processed %u/%u input_len left %u",
+ dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length, input_len);
+ } else if (input_len) {
+ SCLogDebug("Error parsing %s\n",
+ (dcerpc->dcerpchdr.type == BIND_ACK) ?
+ "BIND_ACK" : "ALTER_CONTEXT_RESP");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN + 10
+ + dcerpc->dcerpcbindbindack.secondaryaddrlen
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length && input_len) {
+ retval = DCERPCParseSecondaryAddr(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("DCERPCParseSecondaryAddr %u/%u left %u secondaryaddr len(%u)",
+ dcerpc->bytesprocessed, dcerpc->dcerpchdr.frag_length, input_len,
+ dcerpc->dcerpcbindbindack.secondaryaddrlen);
+ } else if (input_len) {
+ SCLogDebug("Error parsing Secondary Address");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ if (dcerpc->bytesprocessed == DCERPC_HDR_LEN + 10
+ + dcerpc->dcerpcbindbindack.secondaryaddrlen) {
+ if (dcerpc->bytesprocessed % 4) {
+ dcerpc->pad = (4 - dcerpc->bytesprocessed % 4);
+ dcerpc->padleft = dcerpc->pad;
+ }
+ }
+
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN + 10
+ + dcerpc->dcerpcbindbindack.secondaryaddrlen + dcerpc->pad
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length && input_len) {
+ retval = PaddingParser(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("PaddingParser %u/%u left %u pad(%u)",
+ dcerpc->bytesprocessed, dcerpc->dcerpchdr.frag_length, input_len,
+ dcerpc->pad);
+ } else if (input_len) {
+ SCLogDebug("Error parsing DCERPC Padding");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ while (dcerpc->bytesprocessed >= DCERPC_HDR_LEN + 10 + dcerpc->pad
+ + dcerpc->dcerpcbindbindack.secondaryaddrlen && dcerpc->bytesprocessed
+ < DCERPC_HDR_LEN + 14 + dcerpc->pad + dcerpc->dcerpcbindbindack.secondaryaddrlen
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length && input_len) {
+ retval = DCERPCGetCTXItems(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("DCERPCGetCTXItems %u/%u (%u)", dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length, dcerpc->dcerpcbindbindack.numctxitems);
+ } else if (input_len) {
+ SCLogDebug("Error parsing CTX Items");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ if (dcerpc->bytesprocessed == DCERPC_HDR_LEN + 14 + dcerpc->pad
+ + dcerpc->dcerpcbindbindack.secondaryaddrlen) {
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed = 0;
+ }
+
+ while (dcerpc->dcerpcbindbindack.numctxitemsleft && dcerpc->bytesprocessed
+ < dcerpc->dcerpchdr.frag_length && input_len) {
+ retval = DCERPCParseBINDACKCTXItem(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ if (dcerpc->dcerpcbindbindack.ctxbytesprocessed == 24) {
+ dcerpc->dcerpcbindbindack.ctxbytesprocessed = 0;
+ }
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing CTX Items");
+ parsed = 0;
+ input_len = 0;
+ dcerpc->dcerpcbindbindack.numctxitemsleft = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+ SCLogDebug("BINDACK processed %u/%u input_len left %u",
+ dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length, input_len);
+
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ /* response and request done */
+ if (dcerpc->dcerpchdr.type == BIND_ACK) {
+ /* update transaction id */
+ dcerpc->transaction_id++;
+ SCLogDebug("transaction_id updated to %"PRIu16,
+ dcerpc->transaction_id);
+ }
+ DCERPCResetParsingState(dcerpc);
+ } else if (dcerpc->bytesprocessed > dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ } else {
+ /* temporary fix */
+ if (input_len) {
+ retval = DCERPCThrowOutExtraData(dcerpc, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ input_len -= retval;
+ parsed += retval;
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ } else {
+ SCLogDebug("Error Parsing DCERPC");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ }
+ break;
+
+ case REQUEST:
+ case RESPONSE:
+ while (dcerpc->bytesprocessed < DCERPC_HDR_LEN + 8
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length
+ && input_len) {
+ retval = DCERPCParseREQUEST(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ dcerpc->padleft = dcerpc->dcerpchdr.frag_length - dcerpc->bytesprocessed;
+ } else if (input_len) {
+ SCLogDebug("Error parsing DCERPC %s",
+ (dcerpc->dcerpchdr.type == REQUEST) ? "REQUEST" : "RESPONSE");
+ parsed = 0;
+ dcerpc->padleft = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ while (dcerpc->bytesprocessed >= DCERPC_HDR_LEN + 8
+ && dcerpc->bytesprocessed < dcerpc->dcerpchdr.frag_length
+ && dcerpc->padleft && input_len) {
+ retval = StubDataParser(dcerpc, input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing DCERPC Stub Data");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ }
+
+ if (dcerpc->dcerpchdr.type == REQUEST) {
+ SCLogDebug("REQUEST processed %u frag length %u opnum %u input_len %u", dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length, dcerpc->dcerpcrequest.opnum, input_len);
+ } else {
+ SCLogDebug("RESPONSE processed %u frag length %u opnum %u input_len %u", dcerpc->bytesprocessed,
+ dcerpc->dcerpchdr.frag_length, dcerpc->dcerpcrequest.opnum, input_len);
+ }
+
+ /* don't see how we could break the parser for request pdus, by
+ * pusing bytesprocessed beyond frag_length. Let's have the
+ * check anyways */
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else if (dcerpc->bytesprocessed > dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ } else {
+ if (!dcerpc->pdu_fragged &&
+ (dcerpc->dcerpchdr.pfc_flags & PFC_FIRST_FRAG)) {
+ DCERPCResetStub(dcerpc);
+ }
+ /* temporary fix */
+ if (input_len) {
+ retval = DCERPCThrowOutExtraData(dcerpc, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ input_len -= retval;
+ parsed += retval;
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ } else {
+ SCLogDebug("Error Parsing DCERPC");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ }
+
+ /* response and request done */
+ if (dcerpc->dcerpchdr.type == RESPONSE) {
+ /* update transaction id */
+ dcerpc->transaction_id++;
+ SCLogDebug("transaction_id updated to %"PRIu16,
+ dcerpc->transaction_id);
+ }
+ break;
+
+ default:
+ SCLogDebug("DCERPC Type 0x%02x not implemented yet", dcerpc->dcerpchdr.type);
+ retval = DCERPCThrowOutExtraData(dcerpc, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ input_len -= retval;
+ parsed += retval;
+ if (dcerpc->bytesprocessed == dcerpc->dcerpchdr.frag_length) {
+ DCERPCResetParsingState(dcerpc);
+ } else {
+ dcerpc->pdu_fragged = 1;
+ }
+ } else {
+ SCLogDebug("Error Parsing DCERPC");
+ parsed = 0;
+ input_len = 0;
+ DCERPCResetParsingState(dcerpc);
+ SCReturnInt(0);
+ }
+ break;
+ }
+ }
+
+ SCReturnInt(parsed);
+}
+
+static int DCERPCParse(Flow *f, void *dcerpc_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data, int dir)
+{
+ SCEnter();
+
+ int32_t retval = 0;
+ DCERPCState *sstate = (DCERPCState *) dcerpc_state;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ if (sstate->dcerpc.bytesprocessed != 0 && sstate->data_needed_for_dir != dir) {
+ SCReturnInt(-1);
+ }
+
+ retval = DCERPCParser(&sstate->dcerpc, input, input_len);
+ if (retval == -1) {
+ SCReturnInt(0);
+ }
+
+ sstate->data_needed_for_dir = dir;
+
+ if (pstate == NULL)
+ SCReturnInt(-1);
+
+ SCReturnInt(1);
+}
+
+static int DCERPCParseRequest(Flow *f, void *dcerpc_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return DCERPCParse(f, dcerpc_state, pstate, input, input_len,
+ local_data, 0);
+}
+
+static int DCERPCParseResponse(Flow *f, void *dcerpc_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return DCERPCParse(f, dcerpc_state, pstate, input, input_len,
+ local_data, 1);
+}
+
+static void *DCERPCStateAlloc(void)
+{
+ SCEnter();
+
+ DCERPCState *s = SCMalloc(sizeof(DCERPCState));
+ if (unlikely(s == NULL)) {
+ SCReturnPtr(NULL, "void");
+ }
+ memset(s, 0, sizeof(DCERPCState));
+
+ s->dcerpc.transaction_id = 1;
+
+ SCReturnPtr((void *)s, "void");
+}
+
+static void DCERPCStateFree(void *s)
+{
+ DCERPCState *sstate = (DCERPCState *) s;
+
+ DCERPCUuidEntry *item;
+
+ while ((item = TAILQ_FIRST(&sstate->dcerpc.dcerpcbindbindack.uuid_list))) {
+ //printUUID("Free", item);
+ TAILQ_REMOVE(&sstate->dcerpc.dcerpcbindbindack.uuid_list, item, next);
+ SCFree(item);
+ }
+
+ while ((item = TAILQ_FIRST(&sstate->dcerpc.dcerpcbindbindack.accepted_uuid_list))) {
+ //printUUID("Free", item);
+ TAILQ_REMOVE(&sstate->dcerpc.dcerpcbindbindack.accepted_uuid_list, item, next);
+ SCFree(item);
+ }
+
+ if (sstate->dcerpc.dcerpcrequest.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcrequest.stub_data_buffer);
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer_len = 0;
+ }
+ if (sstate->dcerpc.dcerpcresponse.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcresponse.stub_data_buffer);
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer_len = 0;
+ }
+
+ SCFree(s);
+}
+
+static int DCERPCRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC,
+ "|05 00|", 2, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC,
+ "|05 00|", 2, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+void RegisterDCERPCParsers(void)
+{
+ char *proto_name = "dcerpc";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_DCERPC, proto_name);
+ if (DCERPCRegisterPatternsForProtocolDetection() < 0)
+ return;
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DCERPC, STREAM_TOSERVER,
+ DCERPCParseRequest);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ DCERPCParseResponse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DCERPC, DCERPCStateAlloc,
+ DCERPCStateFree);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_DCERPC, STREAM_TOSERVER);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DCERPC, DCERPCParserRegisterTests);
+#endif
+
+ return;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+/** \test DCERPC Header Parsing and BIND / BIND_ACK multiple UUID handling
+*/
+
+/* set this to 1 to see problem */
+
+int DCERPCParserTest01(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t dcerpcbind[] = {
+ 0x05, 0x00,
+ 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x3c, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x16,
+ 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0xd0,
+ 0x28, 0xda, 0x76, 0x91, 0xf6, 0x6e, 0xcb, 0x0f,
+ 0xbf, 0x85, 0xcd, 0x9b, 0xf6, 0x39, 0x01, 0x00,
+ 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x2c, 0x75, 0xce, 0x7e, 0x82, 0x3b,
+ 0x06, 0xac, 0x1b, 0xf0, 0xf5, 0xb7, 0xa7, 0xf7,
+ 0x28, 0xaf, 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0xe3, 0xb2,
+ 0x10, 0xd1, 0xd0, 0x0c, 0xcc, 0x3d, 0x2f, 0x80,
+ 0x20, 0x7c, 0xef, 0xe7, 0x09, 0xe0, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0xde, 0x85, 0x70, 0xc4, 0x02, 0x7c,
+ 0x60, 0x23, 0x67, 0x0c, 0x22, 0xbf, 0x18, 0x36,
+ 0x79, 0x17, 0x01, 0x00, 0x02, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x41, 0x65,
+ 0x29, 0x51, 0xaa, 0xe7, 0x7b, 0xa8, 0xf2, 0x37,
+ 0x0b, 0xd0, 0x3f, 0xb3, 0x36, 0xed, 0x05, 0x00,
+ 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x01, 0x00, 0x14, 0x96, 0x80, 0x01, 0x2e, 0x78,
+ 0xfb, 0x5d, 0xb4, 0x3c, 0x14, 0xb3, 0x3d, 0xaa,
+ 0x02, 0xfb, 0x06, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x3b, 0x04,
+ 0x68, 0x3e, 0x63, 0xfe, 0x9f, 0xd8, 0x64, 0x55,
+ 0xcd, 0xe7, 0x39, 0xaf, 0x98, 0x9f, 0x03, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x01, 0x00, 0x16, 0x7a, 0x4f, 0x1b, 0xdb, 0x25,
+ 0x92, 0x55, 0xdd, 0xae, 0x9e, 0x5b, 0x3e, 0x93,
+ 0x66, 0x93, 0x04, 0x00, 0x01, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xe8, 0xa4,
+ 0x8a, 0xcf, 0x95, 0x6c, 0xc7, 0x8f, 0x14, 0xcc,
+ 0x56, 0xfc, 0x7b, 0x5f, 0x4f, 0xe8, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x01, 0x00, 0xd8, 0xda, 0xfb, 0xbc, 0xa2, 0x55,
+ 0x6f, 0x5d, 0xc0, 0x2d, 0x88, 0x6f, 0x00, 0x17,
+ 0x52, 0x8d, 0x06, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x3f, 0x17,
+ 0x55, 0x0c, 0xf4, 0x23, 0x3c, 0xca, 0xe6, 0xa0,
+ 0xaa, 0xcc, 0xb5, 0xe3, 0xf9, 0xce, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00,
+ 0x01, 0x00, 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1,
+ 0xd0, 0x11, 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9,
+ 0x2e, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0xc9, 0x9f,
+ 0x3e, 0x6e, 0x82, 0x0a, 0x2b, 0x28, 0x37, 0x78,
+ 0xe1, 0x13, 0x70, 0x05, 0x38, 0x4d, 0x01, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x01, 0x00, 0x11, 0xaa, 0x4b, 0x15, 0xdf, 0xa6,
+ 0x86, 0x3f, 0xfb, 0xe0, 0x09, 0xb7, 0xf8, 0x56,
+ 0xd2, 0x3f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00, 0xee, 0x99,
+ 0xc4, 0x25, 0x11, 0xe4, 0x95, 0x62, 0x29, 0xfa,
+ 0xfd, 0x26, 0x57, 0x02, 0xf1, 0xce, 0x03, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0xba, 0x81, 0x9e, 0x1a, 0xdf, 0x2b,
+ 0xba, 0xe4, 0xd3, 0x17, 0x41, 0x60, 0x6d, 0x2d,
+ 0x9e, 0x28, 0x03, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0xa0, 0x24,
+ 0x03, 0x9a, 0xa9, 0x99, 0xfb, 0xbe, 0x49, 0x11,
+ 0xad, 0x77, 0x30, 0xaa, 0xbc, 0xb6, 0x02, 0x00,
+ 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00,
+ 0x01, 0x00, 0x32, 0x04, 0x7e, 0xae, 0xec, 0x28,
+ 0xd1, 0x55, 0x83, 0x4e, 0xc3, 0x47, 0x5d, 0x1d,
+ 0xc6, 0x65, 0x02, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x12, 0x00, 0x01, 0x00, 0xc6, 0xa4,
+ 0x81, 0x48, 0x66, 0x2a, 0x74, 0x7d, 0x56, 0x6e,
+ 0xc5, 0x1d, 0x19, 0xf2, 0xb5, 0xb6, 0x03, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00,
+ 0x01, 0x00, 0xcb, 0xae, 0xb3, 0xc0, 0x0c, 0xf4,
+ 0xa4, 0x5e, 0x91, 0x72, 0xdd, 0x53, 0x24, 0x70,
+ 0x89, 0x02, 0x05, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0xb8, 0xd0,
+ 0xa0, 0x1a, 0x5e, 0x7a, 0x2d, 0xfe, 0x35, 0xc6,
+ 0x7d, 0x08, 0x0d, 0x33, 0x73, 0x18, 0x02, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00,
+ 0x01, 0x00, 0x21, 0xd3, 0xaa, 0x09, 0x03, 0xa7,
+ 0x0b, 0xc2, 0x06, 0x45, 0xd9, 0x6c, 0x75, 0xc2,
+ 0x15, 0xa8, 0x01, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x16, 0x00, 0x01, 0x00, 0xe1, 0xbd,
+ 0x59, 0xfc, 0xbc, 0xa9, 0x95, 0xc2, 0x68, 0x79,
+ 0xf3, 0x75, 0xe0, 0xae, 0x6c, 0xe5, 0x04, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00,
+ 0x01, 0x00, 0x06, 0x52, 0xb4, 0x71, 0x70, 0x15,
+ 0x4e, 0xf5, 0x7f, 0x08, 0x86, 0x14, 0xe6, 0x17,
+ 0xd5, 0x97, 0x04, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00};
+
+ uint8_t dcerpcbindack[] = {
+ 0x05, 0x00, 0x0c, 0x03,
+ 0x10, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb8, 0x10, 0xb8, 0x10,
+ 0xce, 0x47, 0x00, 0x00, 0x0c, 0x00, 0x5c, 0x50,
+ 0x49, 0x50, 0x45, 0x5c, 0x6c, 0x73, 0x61, 0x73,
+ 0x73, 0x00, 0xf6, 0x6e, 0x18, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ uint8_t dcerpcrequest[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x0b,
+ 0x00, 0x09, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x4d,
+ 0x00, 0x73, 0x00, 0x53, 0x00, 0x59, 0x00, 0x2a,
+ 0x00, 0x4a, 0x00, 0x7a, 0x00, 0x3e, 0x00, 0x58,
+ 0x00, 0x21, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x41,
+ 0x00, 0x4b, 0x00, 0x4b, 0x00, 0x3c, 0x00, 0x48,
+ 0x00, 0x24, 0x00, 0x38, 0x00, 0x54, 0x00, 0x60,
+ 0x00, 0x2d, 0x00, 0x29, 0x00, 0x64, 0x00, 0x5b,
+ 0x00, 0x77, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x24,
+ 0x00, 0x23, 0x00, 0x66, 0x00, 0x43, 0x00, 0x68,
+ 0x00, 0x22, 0x00, 0x55, 0x00, 0x29, 0x00, 0x2c,
+ 0x00, 0x4f, 0x00, 0x5a, 0x00, 0x50, 0x00, 0x61,
+ 0x00, 0x2a, 0x00, 0x6f, 0x00, 0x2f, 0x00, 0x4d,
+ 0x00, 0x68, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x67,
+ 0x00, 0x68, 0x00, 0x68, 0x00, 0x49, 0x00, 0x45,
+ 0x00, 0x4c, 0x00, 0x72, 0x00, 0x53, 0x00, 0x4c,
+ 0x00, 0x25, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x2e,
+ 0x00, 0x4f, 0x00, 0x64, 0x00, 0x61, 0x00, 0x73,
+ 0x00, 0x24, 0x00, 0x46, 0x00, 0x35, 0x00, 0x2e,
+ 0x00, 0x45, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x38, 0x00, 0x47, 0x00, 0x71,
+ 0x00, 0x5a, 0x00, 0x37, 0x00, 0x7a, 0x00, 0x35,
+ 0x00, 0x6b, 0x00, 0x3c, 0x00, 0x26, 0x00, 0x37,
+ 0x00, 0x69, 0x00, 0x75, 0x00, 0x36, 0x00, 0x37,
+ 0x00, 0x47, 0x00, 0x21, 0x00, 0x2d, 0x00, 0x69,
+ 0x00, 0x37, 0x00, 0x78, 0x00, 0x5f, 0x00, 0x72,
+ 0x00, 0x4b, 0x00, 0x5c, 0x00, 0x74, 0x00, 0x3e,
+ 0x00, 0x52, 0x00, 0x7a, 0x00, 0x49, 0x00, 0x31,
+ 0x00, 0x5a, 0x00, 0x7b, 0x00, 0x29, 0x00, 0x3b,
+ 0x00, 0x78, 0x00, 0x3b, 0x00, 0x55, 0x00, 0x3e,
+ 0x00, 0x35, 0x00, 0x2b, 0x00, 0x4e, 0x00, 0x4f,
+ 0x00, 0x59, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x59,
+ 0x00, 0x6b, 0x00, 0x42, 0x00, 0x4c, 0x00, 0x3e,
+ 0x00, 0x6a, 0x00, 0x49, 0x00, 0x2c, 0x00, 0x79,
+ 0x00, 0x6e, 0x00, 0x35, 0x00, 0x4f, 0x00, 0x49,
+ 0x00, 0x55, 0x00, 0x35, 0x00, 0x61, 0x00, 0x72,
+ 0x00, 0x77, 0x00, 0x38, 0x00, 0x32, 0x00, 0x24,
+ 0x00, 0x46, 0x00, 0x32, 0x00, 0x32, 0x00, 0x27,
+ 0x00, 0x64, 0x00, 0x5a, 0x00, 0x77, 0x00, 0x2e,
+ 0x00, 0x37, 0x00, 0x77, 0x00, 0x2e, 0x00, 0x28,
+ 0x00, 0x63, 0x00, 0x4f, 0x00, 0x67, 0x00, 0x64,
+ 0x00, 0x39, 0x00, 0x37, 0x00, 0x31, 0x00, 0x30,
+ 0x00, 0x28, 0x00, 0x2e, 0x00, 0x6f, 0x00, 0x3e,
+ 0x00, 0x59, 0x00, 0x28, 0x00, 0x67, 0x00, 0x52,
+ 0x00, 0x35, 0x00, 0x5a, 0x00, 0x7c, 0x00, 0x56,
+ 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x30,
+ 0x00, 0x59, 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x38,
+ 0x00, 0x54, 0x00, 0x5c, 0x00, 0x5b, 0x00, 0x42,
+ 0x00, 0x62, 0x00, 0x70, 0x00, 0x34, 0x00, 0x5c,
+ 0x00, 0x57, 0x00, 0x7a, 0x00, 0x4b, 0x00, 0x2f,
+ 0x00, 0x6b, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x52, 0x00, 0x36, 0x00, 0x27,
+ 0x00, 0x30, 0x00, 0x6d, 0x00, 0x4a, 0x00, 0x30,
+ 0x00, 0x78, 0x00, 0x46, 0x00, 0x65, 0x00, 0x4e,
+ 0x00, 0x29, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x72,
+ 0x00, 0x71, 0x00, 0x75, 0x00, 0x4c, 0x00, 0x2b,
+ 0x00, 0x5c, 0x00, 0x46, 0x00, 0x52, 0x00, 0x7b,
+ 0x00, 0x5c, 0x00, 0x69, 0x00, 0x66, 0x00, 0x56,
+ 0x00, 0x31, 0x00, 0x2d, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x68, 0x00, 0x28, 0x00, 0x7d, 0x00, 0x58,
+ 0x00, 0x2a, 0x00, 0x7b, 0x00, 0x28, 0x00, 0x5b,
+ 0x00, 0x54, 0x00, 0x3a, 0x00, 0x26, 0x00, 0x52,
+ 0x00, 0x44, 0x00, 0x60, 0x00, 0x50, 0x00, 0x65,
+ 0x00, 0x48, 0x00, 0x7d, 0x00, 0x2a, 0x00, 0x74,
+ 0x00, 0x49, 0x00, 0x7b, 0x00, 0x21, 0x00, 0x61,
+ 0x00, 0x52, 0x00, 0x43, 0x00, 0x5f, 0x00, 0x5a,
+ 0x00, 0x74, 0x00, 0x5c, 0x00, 0x62, 0x00, 0x68,
+ 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x2b, 0x00, 0x6f,
+ 0x00, 0x7c, 0x00, 0x42, 0x00, 0x67, 0x00, 0x32,
+ 0x00, 0x58, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2f,
+ 0x00, 0x2d, 0x00, 0x60, 0x00, 0x62, 0x00, 0x51,
+ 0x00, 0x2a, 0x00, 0x30, 0x00, 0x31, 0x00, 0x48,
+ 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x25,
+ 0x00, 0x58, 0x00, 0x4a, 0x00, 0x76, 0x00, 0x32,
+ 0x00, 0x62, 0x00, 0x27, 0x00, 0x42, 0x00, 0x40,
+ 0x00, 0x53, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x50,
+ 0x00, 0x3d, 0x00, 0x40, 0x00, 0x76, 0x00, 0x38,
+ 0x00, 0x58, 0x00, 0x39, 0x00, 0x63, 0x00, 0x3c,
+ 0x00, 0x5b, 0x00, 0x23, 0x00, 0x53, 0x00, 0x7a,
+ 0x00, 0x54, 0x00, 0x74, 0x00, 0x61, 0x00, 0x76,
+ 0x00, 0x4a, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x75,
+ 0x00, 0x66, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x33,
+ 0x00, 0x71, 0x00, 0x76, 0x00, 0x48, 0x00, 0x71,
+ 0x00, 0x41, 0x00, 0x6f, 0x00, 0x2a, 0x00, 0x67,
+ 0x00, 0x70, 0x00, 0x21, 0x00, 0x70, 0x00, 0x4b,
+ 0x00, 0x52, 0x00, 0x58, 0x00, 0x68, 0x00, 0x23,
+ 0x00, 0x39, 0x00, 0x46, 0x00, 0x4d, 0x00, 0x51,
+ 0x00, 0x57, 0x00, 0x3a, 0x00, 0x79, 0x00, 0x7b,
+ 0x00, 0x6c, 0x00, 0x55, 0x00, 0x33, 0x00, 0x65,
+ 0x00, 0x49, 0x00, 0x72, 0x00, 0x30, 0x00, 0x4f,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x31, 0x00, 0x4a,
+ 0x00, 0x60, 0x00, 0x79, 0x00, 0x70, 0x00, 0x4f,
+ 0x00, 0x58, 0x00, 0x75, 0x00, 0x44, 0x00, 0x59,
+ 0x00, 0x58, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x46,
+ 0x00, 0x74, 0x00, 0x51, 0x00, 0x57, 0x00, 0x6e,
+ 0x00, 0x2d, 0x00, 0x47, 0x00, 0x23, 0x00, 0x45,
+ 0x00, 0x60, 0x00, 0x4c, 0x00, 0x72, 0x00, 0x4e,
+ 0x00, 0x74, 0x00, 0x40, 0x00, 0x76, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x56, 0x00, 0x44, 0x00, 0x29,
+ 0x00, 0x62, 0x00, 0x58, 0x00, 0x31, 0x00, 0x78,
+ 0x00, 0x32, 0x00, 0x52, 0x00, 0x4a, 0x00, 0x6b,
+ 0x00, 0x55, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f,
+ 0x00, 0x4a, 0x00, 0x54, 0x00, 0x7d, 0x00, 0x68,
+ 0x00, 0x3f, 0x00, 0x28, 0x00, 0x21, 0x00, 0x53,
+ 0x00, 0x48, 0x00, 0x5a, 0x00, 0x34, 0x00, 0x36,
+ 0x00, 0x35, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x75,
+ 0x00, 0x69, 0x00, 0x23, 0x00, 0x75, 0x00, 0x55,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x2f, 0x00, 0x73,
+ 0x00, 0x62, 0x00, 0x6f, 0x00, 0x37, 0x00, 0x4e,
+ 0x00, 0x25, 0x00, 0x25, 0x00, 0x21, 0x00, 0x3d,
+ 0x00, 0x3c, 0x00, 0x71, 0x00, 0x3e, 0x00, 0x3f,
+ 0x00, 0x30, 0x00, 0x36, 0x00, 0x62, 0x00, 0x63,
+ 0x00, 0x53, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x61,
+ 0x00, 0x4c, 0x00, 0x28, 0x00, 0x2b, 0x00, 0x4c,
+ 0x00, 0x4e, 0x00, 0x66, 0x00, 0x5f, 0x00, 0x4b,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x45, 0x00, 0x37,
+ 0x00, 0x28, 0x00, 0x56, 0x00, 0x36, 0x00, 0x6a,
+ 0x00, 0x3e, 0x00, 0x64, 0x00, 0x34, 0x00, 0x6a,
+ 0x00, 0x7d, 0x00, 0x4a, 0x00, 0x66, 0x00, 0x7a,
+ 0x00, 0x3e, 0x00, 0x75, 0x00, 0x38, 0x00, 0x7b,
+ 0x00, 0x42, 0x00, 0x76, 0x00, 0x29, 0x00, 0x4c,
+ 0x00, 0x65, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x4b,
+ 0x00, 0x2b, 0x00, 0x51, 0x00, 0x47, 0x00, 0x22,
+ 0x00, 0x48, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x44,
+ 0x00, 0x5d, 0x00, 0x59, 0x00, 0x63, 0x00, 0x5c,
+ 0x00, 0x24, 0x00, 0x35, 0x00, 0x34, 0x00, 0x70,
+ 0x00, 0x69, 0x00};
+ uint32_t requestlen = sizeof(dcerpcrequest);
+
+ uint32_t bindlen = sizeof(dcerpcbind);
+ uint32_t bindacklen = sizeof(dcerpcbindack);
+ TcpSession ssn;
+ DCERPCUuidEntry *uuid_entry;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START, dcerpcbind, bindlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.rpc_vers != 5) {
+ printf("expected dcerpc version 0x05, got 0x%02x : ",
+ dcerpc_state->dcerpc.dcerpchdr.rpc_vers);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.type != BIND) {
+ printf("expected dcerpc type 0x%02x , got 0x%02x : ", BIND, dcerpc_state->dcerpc.dcerpchdr.type);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.frag_length != 1084) {
+ printf("expected dcerpc frag_length 0x%02x , got 0x%02x : ", 1084, dcerpc_state->dcerpc.dcerpchdr.frag_length);
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpcbindack, bindacklen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (dcerpc_state->dcerpc.dcerpchdr.type != BIND_ACK) {
+ printf("expected dcerpc type 0x%02x , got 0x%02x : ", BIND_ACK, dcerpc_state->dcerpc.dcerpchdr.type);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.frag_length != 620) {
+ printf("expected dcerpc frag_length 0x%02x , got 0x%02x : ", 620, dcerpc_state->dcerpc.dcerpchdr.frag_length);
+ result = 0;
+ goto end;
+ }
+ TAILQ_FOREACH(uuid_entry, &dcerpc_state->dcerpc.dcerpcbindbindack.uuid_list, next) {
+ printUUID("BIND_ACK", uuid_entry);
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_EOF, dcerpcrequest, requestlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (dcerpc_state->dcerpc.dcerpchdr.type != REQUEST) {
+ printf("expected dcerpc type 0x%02x , got 0x%02x : ", REQUEST, dcerpc_state->dcerpc.dcerpchdr.type);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test DCERPC Request decoding and opnum parsing.
+*/
+int DCERPCParserTest02(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t dcerpcrequest[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x0b,
+ 0x00, 0x09, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x4d,
+ 0x00, 0x73, 0x00, 0x53, 0x00, 0x59, 0x00, 0x2a,
+ 0x00, 0x4a, 0x00, 0x7a, 0x00, 0x3e, 0x00, 0x58,
+ 0x00, 0x21, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x41,
+ 0x00, 0x4b, 0x00, 0x4b, 0x00, 0x3c, 0x00, 0x48,
+ 0x00, 0x24, 0x00, 0x38, 0x00, 0x54, 0x00, 0x60,
+ 0x00, 0x2d, 0x00, 0x29, 0x00, 0x64, 0x00, 0x5b,
+ 0x00, 0x77, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x24,
+ 0x00, 0x23, 0x00, 0x66, 0x00, 0x43, 0x00, 0x68,
+ 0x00, 0x22, 0x00, 0x55, 0x00, 0x29, 0x00, 0x2c,
+ 0x00, 0x4f, 0x00, 0x5a, 0x00, 0x50, 0x00, 0x61,
+ 0x00, 0x2a, 0x00, 0x6f, 0x00, 0x2f, 0x00, 0x4d,
+ 0x00, 0x68, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x67,
+ 0x00, 0x68, 0x00, 0x68, 0x00, 0x49, 0x00, 0x45,
+ 0x00, 0x4c, 0x00, 0x72, 0x00, 0x53, 0x00, 0x4c,
+ 0x00, 0x25, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x2e,
+ 0x00, 0x4f, 0x00, 0x64, 0x00, 0x61, 0x00, 0x73,
+ 0x00, 0x24, 0x00, 0x46, 0x00, 0x35, 0x00, 0x2e,
+ 0x00, 0x45, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x38, 0x00, 0x47, 0x00, 0x71,
+ 0x00, 0x5a, 0x00, 0x37, 0x00, 0x7a, 0x00, 0x35,
+ 0x00, 0x6b, 0x00, 0x3c, 0x00, 0x26, 0x00, 0x37,
+ 0x00, 0x69, 0x00, 0x75, 0x00, 0x36, 0x00, 0x37,
+ 0x00, 0x47, 0x00, 0x21, 0x00, 0x2d, 0x00, 0x69,
+ 0x00, 0x37, 0x00, 0x78, 0x00, 0x5f, 0x00, 0x72,
+ 0x00, 0x4b, 0x00, 0x5c, 0x00, 0x74, 0x00, 0x3e,
+ 0x00, 0x52, 0x00, 0x7a, 0x00, 0x49, 0x00, 0x31,
+ 0x00, 0x5a, 0x00, 0x7b, 0x00, 0x29, 0x00, 0x3b,
+ 0x00, 0x78, 0x00, 0x3b, 0x00, 0x55, 0x00, 0x3e,
+ 0x00, 0x35, 0x00, 0x2b, 0x00, 0x4e, 0x00, 0x4f,
+ 0x00, 0x59, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x59,
+ 0x00, 0x6b, 0x00, 0x42, 0x00, 0x4c, 0x00, 0x3e,
+ 0x00, 0x6a, 0x00, 0x49, 0x00, 0x2c, 0x00, 0x79,
+ 0x00, 0x6e, 0x00, 0x35, 0x00, 0x4f, 0x00, 0x49,
+ 0x00, 0x55, 0x00, 0x35, 0x00, 0x61, 0x00, 0x72,
+ 0x00, 0x77, 0x00, 0x38, 0x00, 0x32, 0x00, 0x24,
+ 0x00, 0x46, 0x00, 0x32, 0x00, 0x32, 0x00, 0x27,
+ 0x00, 0x64, 0x00, 0x5a, 0x00, 0x77, 0x00, 0x2e,
+ 0x00, 0x37, 0x00, 0x77, 0x00, 0x2e, 0x00, 0x28,
+ 0x00, 0x63, 0x00, 0x4f, 0x00, 0x67, 0x00, 0x64,
+ 0x00, 0x39, 0x00, 0x37, 0x00, 0x31, 0x00, 0x30,
+ 0x00, 0x28, 0x00, 0x2e, 0x00, 0x6f, 0x00, 0x3e,
+ 0x00, 0x59, 0x00, 0x28, 0x00, 0x67, 0x00, 0x52,
+ 0x00, 0x35, 0x00, 0x5a, 0x00, 0x7c, 0x00, 0x56,
+ 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x30,
+ 0x00, 0x59, 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x38,
+ 0x00, 0x54, 0x00, 0x5c, 0x00, 0x5b, 0x00, 0x42,
+ 0x00, 0x62, 0x00, 0x70, 0x00, 0x34, 0x00, 0x5c,
+ 0x00, 0x57, 0x00, 0x7a, 0x00, 0x4b, 0x00, 0x2f,
+ 0x00, 0x6b, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x52, 0x00, 0x36, 0x00, 0x27,
+ 0x00, 0x30, 0x00, 0x6d, 0x00, 0x4a, 0x00, 0x30,
+ 0x00, 0x78, 0x00, 0x46, 0x00, 0x65, 0x00, 0x4e,
+ 0x00, 0x29, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x72,
+ 0x00, 0x71, 0x00, 0x75, 0x00, 0x4c, 0x00, 0x2b,
+ 0x00, 0x5c, 0x00, 0x46, 0x00, 0x52, 0x00, 0x7b,
+ 0x00, 0x5c, 0x00, 0x69, 0x00, 0x66, 0x00, 0x56,
+ 0x00, 0x31, 0x00, 0x2d, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x68, 0x00, 0x28, 0x00, 0x7d, 0x00, 0x58,
+ 0x00, 0x2a, 0x00, 0x7b, 0x00, 0x28, 0x00, 0x5b,
+ 0x00, 0x54, 0x00, 0x3a, 0x00, 0x26, 0x00, 0x52,
+ 0x00, 0x44, 0x00, 0x60, 0x00, 0x50, 0x00, 0x65,
+ 0x00, 0x48, 0x00, 0x7d, 0x00, 0x2a, 0x00, 0x74,
+ 0x00, 0x49, 0x00, 0x7b, 0x00, 0x21, 0x00, 0x61,
+ 0x00, 0x52, 0x00, 0x43, 0x00, 0x5f, 0x00, 0x5a,
+ 0x00, 0x74, 0x00, 0x5c, 0x00, 0x62, 0x00, 0x68,
+ 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x2b, 0x00, 0x6f,
+ 0x00, 0x7c, 0x00, 0x42, 0x00, 0x67, 0x00, 0x32,
+ 0x00, 0x58, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2f,
+ 0x00, 0x2d, 0x00, 0x60, 0x00, 0x62, 0x00, 0x51,
+ 0x00, 0x2a, 0x00, 0x30, 0x00, 0x31, 0x00, 0x48,
+ 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x25,
+ 0x00, 0x58, 0x00, 0x4a, 0x00, 0x76, 0x00, 0x32,
+ 0x00, 0x62, 0x00, 0x27, 0x00, 0x42, 0x00, 0x40,
+ 0x00, 0x53, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x50,
+ 0x00, 0x3d, 0x00, 0x40, 0x00, 0x76, 0x00, 0x38,
+ 0x00, 0x58, 0x00, 0x39, 0x00, 0x63, 0x00, 0x3c,
+ 0x00, 0x5b, 0x00, 0x23, 0x00, 0x53, 0x00, 0x7a,
+ 0x00, 0x54, 0x00, 0x74, 0x00, 0x61, 0x00, 0x76,
+ 0x00, 0x4a, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x75,
+ 0x00, 0x66, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x33,
+ 0x00, 0x71, 0x00, 0x76, 0x00, 0x48, 0x00, 0x71,
+ 0x00, 0x41, 0x00, 0x6f, 0x00, 0x2a, 0x00, 0x67,
+ 0x00, 0x70, 0x00, 0x21, 0x00, 0x70, 0x00, 0x4b,
+ 0x00, 0x52, 0x00, 0x58, 0x00, 0x68, 0x00, 0x23,
+ 0x00, 0x39, 0x00, 0x46, 0x00, 0x4d, 0x00, 0x51,
+ 0x00, 0x57, 0x00, 0x3a, 0x00, 0x79, 0x00, 0x7b,
+ 0x00, 0x6c, 0x00, 0x55, 0x00, 0x33, 0x00, 0x65,
+ 0x00, 0x49, 0x00, 0x72, 0x00, 0x30, 0x00, 0x4f,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x31, 0x00, 0x4a,
+ 0x00, 0x60, 0x00, 0x79, 0x00, 0x70, 0x00, 0x4f,
+ 0x00, 0x58, 0x00, 0x75, 0x00, 0x44, 0x00, 0x59,
+ 0x00, 0x58, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x46,
+ 0x00, 0x74, 0x00, 0x51, 0x00, 0x57, 0x00, 0x6e,
+ 0x00, 0x2d, 0x00, 0x47, 0x00, 0x23, 0x00, 0x45,
+ 0x00, 0x60, 0x00, 0x4c, 0x00, 0x72, 0x00, 0x4e,
+ 0x00, 0x74, 0x00, 0x40, 0x00, 0x76, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x56, 0x00, 0x44, 0x00, 0x29,
+ 0x00, 0x62, 0x00, 0x58, 0x00, 0x31, 0x00, 0x78,
+ 0x00, 0x32, 0x00, 0x52, 0x00, 0x4a, 0x00, 0x6b,
+ 0x00, 0x55, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f,
+ 0x00, 0x4a, 0x00, 0x54, 0x00, 0x7d, 0x00, 0x68,
+ 0x00, 0x3f, 0x00, 0x28, 0x00, 0x21, 0x00, 0x53,
+ 0x00, 0x48, 0x00, 0x5a, 0x00, 0x34, 0x00, 0x36,
+ 0x00, 0x35, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x75,
+ 0x00, 0x69, 0x00, 0x23, 0x00, 0x75, 0x00, 0x55,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x2f, 0x00, 0x73,
+ 0x00, 0x62, 0x00, 0x6f, 0x00, 0x37, 0x00, 0x4e,
+ 0x00, 0x25, 0x00, 0x25, 0x00, 0x21, 0x00, 0x3d,
+ 0x00, 0x3c, 0x00, 0x71, 0x00, 0x3e, 0x00, 0x3f,
+ 0x00, 0x30, 0x00, 0x36, 0x00, 0x62, 0x00, 0x63,
+ 0x00, 0x53, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x61,
+ 0x00, 0x4c, 0x00, 0x28, 0x00, 0x2b, 0x00, 0x4c,
+ 0x00, 0x4e, 0x00, 0x66, 0x00, 0x5f, 0x00, 0x4b,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x45, 0x00, 0x37,
+ 0x00, 0x28, 0x00, 0x56, 0x00, 0x36, 0x00, 0x6a,
+ 0x00, 0x3e, 0x00, 0x64, 0x00, 0x34, 0x00, 0x6a,
+ 0x00, 0x7d, 0x00, 0x4a, 0x00, 0x66, 0x00, 0x7a,
+ 0x00, 0x3e, 0x00, 0x75, 0x00, 0x38, 0x00, 0x7b,
+ 0x00, 0x42, 0x00, 0x76, 0x00, 0x29, 0x00, 0x4c,
+ 0x00, 0x65, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x4b,
+ 0x00, 0x2b, 0x00, 0x51, 0x00, 0x47, 0x00, 0x22,
+ 0x00, 0x48, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x44,
+ 0x00, 0x5d, 0x00, 0x59, 0x00, 0x63, 0x00, 0x5c,
+ 0x00, 0x24, 0x00, 0x35, 0x00, 0x34, 0x00, 0x70,
+ 0x00, 0x69, 0x00};
+ uint32_t requestlen = sizeof(dcerpcrequest);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START, dcerpcrequest, requestlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.rpc_vers != 5) {
+ printf("expected dcerpc version 0x05, got 0x%02x : ",
+ dcerpc_state->dcerpc.dcerpchdr.rpc_vers);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.type != REQUEST) {
+ printf("expected dcerpc type 0x%02x , got 0x%02x : ", REQUEST, dcerpc_state->dcerpc.dcerpchdr.type);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.frag_length != 1024) {
+ printf("expected dcerpc frag_length 0x%02x , got 0x%02x : ", 1024, dcerpc_state->dcerpc.dcerpchdr.frag_length);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 9) {
+ printf("expected dcerpc opnum 0x%02x , got 0x%02x : ", 9, dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Test endianness handling
+*/
+int DCERPCParserTest03(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t dcerpcrequest[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x09, 0x45, 0x00, 0x2c, 0x00, 0x4d,
+ 0x00, 0x73, 0x00, 0x53, 0x00, 0x59, 0x00, 0x2a,
+ 0x00, 0x4a, 0x00, 0x7a, 0x00, 0x3e, 0x00, 0x58,
+ 0x00, 0x21, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x41,
+ 0x00, 0x4b, 0x00, 0x4b, 0x00, 0x3c, 0x00, 0x48,
+ 0x00, 0x24, 0x00, 0x38, 0x00, 0x54, 0x00, 0x60,
+ 0x00, 0x2d, 0x00, 0x29, 0x00, 0x64, 0x00, 0x5b,
+ 0x00, 0x77, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x24,
+ 0x00, 0x23, 0x00, 0x66, 0x00, 0x43, 0x00, 0x68,
+ 0x00, 0x22, 0x00, 0x55, 0x00, 0x29, 0x00, 0x2c,
+ 0x00, 0x4f, 0x00, 0x5a, 0x00, 0x50, 0x00, 0x61,
+ 0x00, 0x2a, 0x00, 0x6f, 0x00, 0x2f, 0x00, 0x4d,
+ 0x00, 0x68, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x67,
+ 0x00, 0x68, 0x00, 0x68, 0x00, 0x49, 0x00, 0x45,
+ 0x00, 0x4c, 0x00, 0x72, 0x00, 0x53, 0x00, 0x4c,
+ 0x00, 0x25, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x2e,
+ 0x00, 0x4f, 0x00, 0x64, 0x00, 0x61, 0x00, 0x73,
+ 0x00, 0x24, 0x00, 0x46, 0x00, 0x35, 0x00, 0x2e,
+ 0x00, 0x45, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x38, 0x00, 0x47, 0x00, 0x71,
+ 0x00, 0x5a, 0x00, 0x37, 0x00, 0x7a, 0x00, 0x35,
+ 0x00, 0x6b, 0x00, 0x3c, 0x00, 0x26, 0x00, 0x37,
+ 0x00, 0x69, 0x00, 0x75, 0x00, 0x36, 0x00, 0x37,
+ 0x00, 0x47, 0x00, 0x21, 0x00, 0x2d, 0x00, 0x69,
+ 0x00, 0x37, 0x00, 0x78, 0x00, 0x5f, 0x00, 0x72,
+ 0x00, 0x4b, 0x00, 0x5c, 0x00, 0x74, 0x00, 0x3e,
+ 0x00, 0x52, 0x00, 0x7a, 0x00, 0x49, 0x00, 0x31,
+ 0x00, 0x5a, 0x00, 0x7b, 0x00, 0x29, 0x00, 0x3b,
+ 0x00, 0x78, 0x00, 0x3b, 0x00, 0x55, 0x00, 0x3e,
+ 0x00, 0x35, 0x00, 0x2b, 0x00, 0x4e, 0x00, 0x4f,
+ 0x00, 0x59, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x59,
+ 0x00, 0x6b, 0x00, 0x42, 0x00, 0x4c, 0x00, 0x3e,
+ 0x00, 0x6a, 0x00, 0x49, 0x00, 0x2c, 0x00, 0x79,
+ 0x00, 0x6e, 0x00, 0x35, 0x00, 0x4f, 0x00, 0x49,
+ 0x00, 0x55, 0x00, 0x35, 0x00, 0x61, 0x00, 0x72,
+ 0x00, 0x77, 0x00, 0x38, 0x00, 0x32, 0x00, 0x24,
+ 0x00, 0x46, 0x00, 0x32, 0x00, 0x32, 0x00, 0x27,
+ 0x00, 0x64, 0x00, 0x5a, 0x00, 0x77, 0x00, 0x2e,
+ 0x00, 0x37, 0x00, 0x77, 0x00, 0x2e, 0x00, 0x28,
+ 0x00, 0x63, 0x00, 0x4f, 0x00, 0x67, 0x00, 0x64,
+ 0x00, 0x39, 0x00, 0x37, 0x00, 0x31, 0x00, 0x30,
+ 0x00, 0x28, 0x00, 0x2e, 0x00, 0x6f, 0x00, 0x3e,
+ 0x00, 0x59, 0x00, 0x28, 0x00, 0x67, 0x00, 0x52,
+ 0x00, 0x35, 0x00, 0x5a, 0x00, 0x7c, 0x00, 0x56,
+ 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x30,
+ 0x00, 0x59, 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x38,
+ 0x00, 0x54, 0x00, 0x5c, 0x00, 0x5b, 0x00, 0x42,
+ 0x00, 0x62, 0x00, 0x70, 0x00, 0x34, 0x00, 0x5c,
+ 0x00, 0x57, 0x00, 0x7a, 0x00, 0x4b, 0x00, 0x2f,
+ 0x00, 0x6b, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x41,
+ 0x00, 0x33, 0x00, 0x52, 0x00, 0x36, 0x00, 0x27,
+ 0x00, 0x30, 0x00, 0x6d, 0x00, 0x4a, 0x00, 0x30,
+ 0x00, 0x78, 0x00, 0x46, 0x00, 0x65, 0x00, 0x4e,
+ 0x00, 0x29, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x72,
+ 0x00, 0x71, 0x00, 0x75, 0x00, 0x4c, 0x00, 0x2b,
+ 0x00, 0x5c, 0x00, 0x46, 0x00, 0x52, 0x00, 0x7b,
+ 0x00, 0x5c, 0x00, 0x69, 0x00, 0x66, 0x00, 0x56,
+ 0x00, 0x31, 0x00, 0x2d, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x68, 0x00, 0x28, 0x00, 0x7d, 0x00, 0x58,
+ 0x00, 0x2a, 0x00, 0x7b, 0x00, 0x28, 0x00, 0x5b,
+ 0x00, 0x54, 0x00, 0x3a, 0x00, 0x26, 0x00, 0x52,
+ 0x00, 0x44, 0x00, 0x60, 0x00, 0x50, 0x00, 0x65,
+ 0x00, 0x48, 0x00, 0x7d, 0x00, 0x2a, 0x00, 0x74,
+ 0x00, 0x49, 0x00, 0x7b, 0x00, 0x21, 0x00, 0x61,
+ 0x00, 0x52, 0x00, 0x43, 0x00, 0x5f, 0x00, 0x5a,
+ 0x00, 0x74, 0x00, 0x5c, 0x00, 0x62, 0x00, 0x68,
+ 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x2b, 0x00, 0x6f,
+ 0x00, 0x7c, 0x00, 0x42, 0x00, 0x67, 0x00, 0x32,
+ 0x00, 0x58, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2f,
+ 0x00, 0x2d, 0x00, 0x60, 0x00, 0x62, 0x00, 0x51,
+ 0x00, 0x2a, 0x00, 0x30, 0x00, 0x31, 0x00, 0x48,
+ 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x25,
+ 0x00, 0x58, 0x00, 0x4a, 0x00, 0x76, 0x00, 0x32,
+ 0x00, 0x62, 0x00, 0x27, 0x00, 0x42, 0x00, 0x40,
+ 0x00, 0x53, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x50,
+ 0x00, 0x3d, 0x00, 0x40, 0x00, 0x76, 0x00, 0x38,
+ 0x00, 0x58, 0x00, 0x39, 0x00, 0x63, 0x00, 0x3c,
+ 0x00, 0x5b, 0x00, 0x23, 0x00, 0x53, 0x00, 0x7a,
+ 0x00, 0x54, 0x00, 0x74, 0x00, 0x61, 0x00, 0x76,
+ 0x00, 0x4a, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x75,
+ 0x00, 0x66, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x33,
+ 0x00, 0x71, 0x00, 0x76, 0x00, 0x48, 0x00, 0x71,
+ 0x00, 0x41, 0x00, 0x6f, 0x00, 0x2a, 0x00, 0x67,
+ 0x00, 0x70, 0x00, 0x21, 0x00, 0x70, 0x00, 0x4b,
+ 0x00, 0x52, 0x00, 0x58, 0x00, 0x68, 0x00, 0x23,
+ 0x00, 0x39, 0x00, 0x46, 0x00, 0x4d, 0x00, 0x51,
+ 0x00, 0x57, 0x00, 0x3a, 0x00, 0x79, 0x00, 0x7b,
+ 0x00, 0x6c, 0x00, 0x55, 0x00, 0x33, 0x00, 0x65,
+ 0x00, 0x49, 0x00, 0x72, 0x00, 0x30, 0x00, 0x4f,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x31, 0x00, 0x4a,
+ 0x00, 0x60, 0x00, 0x79, 0x00, 0x70, 0x00, 0x4f,
+ 0x00, 0x58, 0x00, 0x75, 0x00, 0x44, 0x00, 0x59,
+ 0x00, 0x58, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x46,
+ 0x00, 0x74, 0x00, 0x51, 0x00, 0x57, 0x00, 0x6e,
+ 0x00, 0x2d, 0x00, 0x47, 0x00, 0x23, 0x00, 0x45,
+ 0x00, 0x60, 0x00, 0x4c, 0x00, 0x72, 0x00, 0x4e,
+ 0x00, 0x74, 0x00, 0x40, 0x00, 0x76, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x56, 0x00, 0x44, 0x00, 0x29,
+ 0x00, 0x62, 0x00, 0x58, 0x00, 0x31, 0x00, 0x78,
+ 0x00, 0x32, 0x00, 0x52, 0x00, 0x4a, 0x00, 0x6b,
+ 0x00, 0x55, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6f,
+ 0x00, 0x4a, 0x00, 0x54, 0x00, 0x7d, 0x00, 0x68,
+ 0x00, 0x3f, 0x00, 0x28, 0x00, 0x21, 0x00, 0x53,
+ 0x00, 0x48, 0x00, 0x5a, 0x00, 0x34, 0x00, 0x36,
+ 0x00, 0x35, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x75,
+ 0x00, 0x69, 0x00, 0x23, 0x00, 0x75, 0x00, 0x55,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x2f, 0x00, 0x73,
+ 0x00, 0x62, 0x00, 0x6f, 0x00, 0x37, 0x00, 0x4e,
+ 0x00, 0x25, 0x00, 0x25, 0x00, 0x21, 0x00, 0x3d,
+ 0x00, 0x3c, 0x00, 0x71, 0x00, 0x3e, 0x00, 0x3f,
+ 0x00, 0x30, 0x00, 0x36, 0x00, 0x62, 0x00, 0x63,
+ 0x00, 0x53, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x61,
+ 0x00, 0x4c, 0x00, 0x28, 0x00, 0x2b, 0x00, 0x4c,
+ 0x00, 0x4e, 0x00, 0x66, 0x00, 0x5f, 0x00, 0x4b,
+ 0x00, 0x43, 0x00, 0x75, 0x00, 0x45, 0x00, 0x37,
+ 0x00, 0x28, 0x00, 0x56, 0x00, 0x36, 0x00, 0x6a,
+ 0x00, 0x3e, 0x00, 0x64, 0x00, 0x34, 0x00, 0x6a,
+ 0x00, 0x7d, 0x00, 0x4a, 0x00, 0x66, 0x00, 0x7a,
+ 0x00, 0x3e, 0x00, 0x75, 0x00, 0x38, 0x00, 0x7b,
+ 0x00, 0x42, 0x00, 0x76, 0x00, 0x29, 0x00, 0x4c,
+ 0x00, 0x65, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x4b,
+ 0x00, 0x2b, 0x00, 0x51, 0x00, 0x47, 0x00, 0x22,
+ 0x00, 0x48, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x44,
+ 0x00, 0x5d, 0x00, 0x59, 0x00, 0x63, 0x00, 0x5c,
+ 0x00, 0x24, 0x00, 0x35, 0x00, 0x34, 0x00, 0x70,
+ 0x00, 0x69, 0x00};
+ uint32_t requestlen = sizeof(dcerpcrequest);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START, dcerpcrequest, requestlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] != 0x01) {
+ printf("expected dcerpc data representation 0x01, got 0x%02x : ",
+ dcerpc_state->dcerpc.dcerpchdr.packed_drep[0]);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpchdr.frag_length != 1024) {
+ printf("expected dcerpc frag_length 0x%02x , got 0x%02x : ", 1024, dcerpc_state->dcerpc.dcerpchdr.frag_length);
+ result = 0;
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 9) {
+ printf("expected dcerpc opnum 0x%02x , got 0x%02x : ", 9, dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \todo Needs to be rewritten
+ */
+int DCERPCParserTest04(void)
+{
+ /* AWS - Disabled this test since clamav FPs on the payloads used.
+ * We will have to rewrite this test with new payloads. Will be done
+ * as a part of dcerpc update/fixes */
+#if 0
+ int result = 1;
+ Flow f;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t request3[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x26, 0x65, 0x3c, 0x6e, 0x6d, 0x64, 0x24, 0x39,
+ 0x56, 0x43, 0x3e, 0x61, 0x5c, 0x54, 0x42, 0x23,
+ 0x75, 0x6b, 0x71, 0x27, 0x66, 0x2e, 0x6e, 0x3d,
+ 0x58, 0x23, 0x54, 0x77, 0x3b, 0x52, 0x6b, 0x50,
+ 0x3b, 0x74, 0x2c, 0x54, 0x25, 0x5c, 0x51, 0x7c,
+ 0x29, 0x7c, 0x5f, 0x4a, 0x35, 0x5c, 0x3d, 0x3f,
+ 0x33, 0x55, 0x3b, 0x5a, 0x57, 0x31, 0x59, 0x4f,
+ 0x6d, 0x6d, 0x7b, 0x3e, 0x38, 0x4d, 0x68, 0x75,
+ 0x64, 0x21, 0x50, 0x63, 0x47, 0x42, 0x56, 0x39,
+ 0x6c, 0x6f, 0x61, 0x53, 0x32, 0x56, 0x43, 0x52,
+ 0x43, 0x67, 0x26, 0x45, 0x28, 0x6b, 0x77, 0x28,
+ 0x7c, 0x64, 0x61, 0x24, 0x38, 0x6b, 0x59, 0x2a,
+ 0x4f, 0x6e, 0x5b, 0x57, 0x24, 0x54, 0x33, 0x37,
+ 0x47, 0x58, 0x4b, 0x58, 0x3d, 0x21, 0x38, 0x7c,
+ 0x2c, 0x24, 0x5f, 0x67, 0x3a, 0x41, 0x3e, 0x2a,
+ 0x72, 0x66, 0x2d, 0x6b, 0x66, 0x7b, 0x2b, 0x75,
+ 0x78, 0x2f, 0x4d, 0x4c, 0x51, 0x70, 0x5d, 0x55,
+ 0x54, 0x3c, 0x63, 0x46, 0x6b, 0x64, 0x4d, 0x25,
+ 0x45, 0x21, 0x34, 0x65, 0x48, 0x32, 0x58, 0x4c,
+ 0x70, 0x4c, 0x4c, 0x75, 0x5c, 0x77, 0x68, 0x78,
+ 0x34, 0x5c, 0x2d, 0x39, 0x58, 0x3b, 0x40, 0x71,
+ 0x77, 0x47, 0x32, 0x2e, 0x3c, 0x61, 0x6f, 0x6d,
+ 0x5f, 0x43, 0x74, 0x36, 0x4f, 0x21, 0x44, 0x66,
+ 0x36, 0x62, 0x30, 0x29, 0x5a, 0x34, 0x66, 0x4e,
+ 0x51, 0x23, 0x4e, 0x38, 0x51, 0x78, 0x74, 0x58,
+ 0x2e, 0x6d, 0x51, 0x49, 0x55, 0x73, 0x2a, 0x71,
+ 0x3c, 0x74, 0x38, 0x6f, 0x5d, 0x4b, 0x74, 0x68,
+ 0x65, 0x4a, 0x58, 0x41, 0x55, 0x29, 0x42, 0x69,
+ 0x55, 0x3b, 0x2b, 0x47, 0x64, 0x3b, 0x77, 0x72,
+ 0x74, 0x38, 0x53, 0x5c, 0x69, 0x49, 0x49, 0x5b,
+ 0x31, 0x41, 0x6a, 0x4e, 0x2c, 0x6a, 0x63, 0x3f,
+ 0x58, 0x4e, 0x25, 0x3e, 0x57, 0x41, 0x61, 0x26,
+ 0x5e, 0x24, 0x69, 0x7a, 0x38, 0x60, 0x73, 0x70,
+ 0x7d, 0x63, 0x34, 0x78, 0x4d, 0x50, 0x35, 0x69,
+ 0x49, 0x22, 0x45, 0x44, 0x3f, 0x6e, 0x75, 0x64,
+ 0x57, 0x3a, 0x61, 0x60, 0x34, 0x21, 0x61, 0x21,
+ 0x2a, 0x78, 0x7b, 0x52, 0x43, 0x50, 0x5b, 0x76,
+ 0x5f, 0x4b, 0x6a, 0x5d, 0x23, 0x5b, 0x57, 0x40,
+ 0x53, 0x51, 0x33, 0x21, 0x35, 0x7d, 0x31, 0x46,
+ 0x65, 0x52, 0x28, 0x25, 0x30, 0x5a, 0x37, 0x7c,
+ 0x2c, 0x3d, 0x2a, 0x48, 0x24, 0x5a, 0x2f, 0x47,
+ 0x64, 0x73, 0x64, 0x3d, 0x7a, 0x5b, 0x34, 0x5e,
+ 0x42, 0x22, 0x32, 0x47, 0x6e, 0x58, 0x3b, 0x3e,
+ 0x25, 0x2f, 0x58, 0x78, 0x42, 0x66, 0x71, 0x56,
+ 0x2a, 0x66, 0x66, 0x5b, 0x55, 0x35, 0x7a, 0x41,
+ 0x7c, 0x7c, 0x6a, 0x2d, 0x59, 0x25, 0x22, 0x34,
+ 0x5a, 0x61, 0x37, 0x48, 0x39, 0x31, 0x4a, 0x55,
+ 0x6a, 0x68, 0x40, 0x2f, 0x45, 0x69, 0x46, 0x25,
+ 0x51, 0x7d, 0x4f, 0x71, 0x21, 0x33, 0x55, 0x50,
+ 0x56, 0x5f, 0x75, 0x27, 0x64, 0x36, 0x7a, 0x39,
+ 0x40, 0x6a, 0x77, 0x38, 0x5d, 0x39, 0x30, 0x5e,
+ 0x74, 0x54, 0x24, 0x3f, 0x3d, 0x79, 0x3b, 0x27,
+ 0x7d, 0x68, 0x7d, 0x40, 0x71, 0x7a, 0x65, 0x54,
+ 0x50, 0x66, 0x33, 0x3c, 0x42, 0x69, 0x6e, 0x3c,
+ 0x63, 0x63, 0x69, 0x7a, 0x5e, 0x7b, 0x76, 0x26,
+ 0x71, 0x6f, 0x4a, 0x6d, 0x70, 0x73, 0x66, 0x3b,
+ 0x26, 0x70, 0x43, 0x5b, 0x52, 0x4c, 0x6d, 0x51,
+ 0x2a, 0x66, 0x6c, 0x3e, 0x68, 0x6a, 0x31, 0x41,
+ 0x79, 0x72, 0x37, 0x47, 0x7d, 0x2b, 0x3c, 0x40,
+ 0x6b, 0x75, 0x56, 0x70, 0x7b, 0x2d, 0x5f, 0x33,
+ 0x30, 0x30, 0x21, 0x35, 0x7a, 0x7a, 0x67, 0x48,
+ 0x5e, 0x3b, 0x73, 0x50, 0x54, 0x47, 0x23, 0x2b,
+ 0x4c, 0x4e, 0x2f, 0x24, 0x44, 0x34, 0x23, 0x5d,
+ 0x76, 0x51, 0x5a, 0x73, 0x72, 0x3e, 0x47, 0x77,
+ 0x40, 0x28, 0x65, 0x2e, 0x2a, 0x75, 0x3c, 0x2a,
+ 0x27, 0x4a, 0x3f, 0x3c, 0x66, 0x2d, 0x21, 0x79,
+ 0x2d, 0x2b, 0x78, 0x7c, 0x5a, 0x73, 0x46, 0x6b,
+ 0x39, 0x65, 0x5e, 0x3d, 0x38, 0x40, 0x32, 0x3e,
+ 0x21, 0x62, 0x34, 0x41, 0x58, 0x53, 0x67, 0x34,
+ 0x58, 0x56, 0x61, 0x5b, 0x3e, 0x4e, 0x2c, 0x5b,
+ 0x73, 0x35, 0x34, 0x35, 0x21, 0x3a, 0x61, 0x5f,
+ 0x6e, 0x45, 0x78, 0x44, 0x28, 0x23, 0x48, 0x65,
+ 0x53, 0x47, 0x6e, 0x2c, 0x38, 0x5e, 0x2c, 0x57,
+ 0x58, 0x30, 0x7a, 0x3b, 0x4b, 0x4a, 0x74, 0x7d,
+ 0x3e, 0x4d, 0x30, 0x24, 0x76, 0x66, 0x6d, 0x2e,
+ 0x74, 0x75, 0x28, 0x48, 0x5c, 0x23, 0x6c, 0x46,
+ 0x27, 0x46, 0x6e, 0x34, 0x63, 0x21, 0x58, 0x54,
+ 0x50, 0x2f, 0x40, 0x47, 0x40, 0x32, 0x36, 0x48,
+ 0x5f, 0x7d, 0x4a, 0x41, 0x6e, 0x60, 0x2c, 0x4a,
+ 0x6a, 0x67, 0x6c, 0x41, 0x27, 0x23, 0x30, 0x48,
+ 0x6a, 0x49, 0x73, 0x26, 0x77, 0x75, 0x4d, 0x65,
+ 0x5b, 0x34, 0x79, 0x67, 0x61, 0x5b, 0x5c, 0x2b,
+ 0x71, 0x3f, 0x62, 0x51, 0x3a, 0x53, 0x42, 0x26,
+ 0x6f, 0x36, 0x57, 0x3f, 0x2b, 0x34, 0x24, 0x30,
+ 0x60, 0x55, 0x70, 0x65, 0x70, 0x57, 0x5d, 0x68,
+ 0x36, 0x52, 0x5d, 0x3f, 0x6a, 0x3a, 0x33, 0x31,
+ 0x6c, 0x4e, 0x57, 0x79, 0x49, 0x79, 0x69, 0x71,
+ 0x6f, 0x70, 0x6a, 0x76, 0x4b, 0x2f, 0x33, 0x51,
+ 0x68, 0x30, 0x2e, 0x77, 0x78, 0x55, 0x2f, 0x53,
+ 0x52, 0x5e, 0x57, 0x60, 0x3b, 0x6f, 0x69, 0x61,
+ 0x6c, 0x60, 0x5a, 0x34, 0x5a, 0x35, 0x4b, 0x28,
+ 0x54, 0x32, 0x6a, 0x35, 0x36, 0x6d, 0x68, 0x47,
+ 0x5c, 0x74, 0x2e, 0x5f, 0x6c, 0x6d, 0x55, 0x42,
+ 0x77, 0x64, 0x7d, 0x53, 0x4d, 0x39, 0x2c, 0x41,
+ 0x42, 0x23, 0x3a, 0x73, 0x40, 0x60, 0x5d, 0x38,
+ 0x6d, 0x36, 0x56, 0x57, 0x2a, 0x28, 0x3d, 0x3b,
+ 0x5c, 0x75, 0x35, 0x2d, 0x69, 0x2d, 0x44, 0x51,
+ 0x27, 0x63, 0x66, 0x33, 0x46, 0x42, 0x2e, 0x36,
+ 0x6b, 0x7b, 0x2c, 0x23, 0x3b, 0x5a, 0x50, 0x2a,
+ 0x65, 0x28, 0x3b, 0x3c, 0x51, 0x3f, 0x4d, 0x63,
+ 0x38, 0x25, 0x74, 0x2e, 0x51, 0x22, 0x31, 0x74,
+ 0x35, 0x33, 0x23, 0x2d, 0x3f, 0x77, 0x26, 0x2c,
+ 0x55, 0x6d, 0x27, 0x39, 0x79, 0x76, 0x63, 0x4b,
+ 0x43, 0x4a, 0x3a, 0x6b, 0x59, 0x55, 0x65, 0x26,
+ 0x2f, 0x3f, 0x56, 0x67, 0x5a, 0x77, 0x71, 0x22,
+ 0x51, 0x2b, 0x6d, 0x4c, 0x2c, 0x57, 0x66, 0x76,
+ 0x37, 0x70, 0x5f, 0x52, 0x29, 0x44, 0x52, 0x22,
+ 0x57, 0x37, 0x27, 0x79, 0x29, 0x5c, 0x57, 0x3b,
+ 0x54, 0x3c, 0x3f, 0x53, 0x35, 0x27, 0x5e, 0x7c,
+ 0x49, 0x77, 0x57, 0x5a, 0x22, 0x76, 0x7c, 0x5b,
+ 0x2f, 0x53, 0x5e, 0x55, 0x6d, 0x64, 0x67, 0x34,
+ 0x41, 0x23, 0x76, 0x67, 0x23, 0x78, 0x6a, 0x63,
+ 0x27, 0x68, 0x43, 0x7d, 0x58, 0x49, 0x2d, 0x79,
+ 0x2e, 0x75, 0x60, 0x6b, 0x34, 0x48, 0x6f, 0x4a,
+ 0x6c, 0x48, 0x40, 0x68, 0x5f, 0x35, 0x25, 0x6c,
+ 0x38, 0x5c, 0x30, 0x32, 0x4c, 0x36, 0x31, 0x29,
+ 0x74, 0x4a, 0x55, 0x56, 0x6d, 0x4e, 0x23, 0x54,
+ 0x2e, 0x69, 0x78, 0x61, 0x76, 0x66, 0x22, 0x44,
+ 0x73, 0x25, 0x44, 0x29, 0x2a, 0x28, 0x3b, 0x67,
+ 0x48, 0x58, 0x37, 0x4a, 0x76, 0x76, 0x51, 0x4a,
+ 0x61, 0x70, 0x51, 0x74, 0x40, 0x23, 0x29, 0x63,
+ 0x69, 0x4a, 0x29, 0x23, 0x34, 0x6a, 0x3b, 0x25,
+ 0x28, 0x54, 0x45, 0x33, 0x28, 0x44, 0x30, 0x61,
+ 0x5b, 0x60, 0x51, 0x3f, 0x68, 0x50, 0x70, 0x3d,
+ 0x58, 0x2e, 0x6e, 0x59, 0x5a, 0x62, 0x66, 0x4d,
+ 0x7a, 0x2e, 0x37, 0x37, 0x3d, 0x7b, 0x74, 0x79,
+ 0x48, 0x45, 0x77, 0x56, 0x33, 0x76, 0x71, 0x60,
+ 0x74, 0x3f, 0x61, 0x22, 0x52, 0x51, 0x71, 0x69
+ };
+ uint32_t request3_len = sizeof(request3);
+
+ uint8_t request4[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x75, 0x3e, 0x76, 0x3e, 0x66, 0x6b, 0x6b, 0x3e,
+ 0x6d, 0x59, 0x38, 0x2b, 0x63, 0x4d, 0x2c, 0x73,
+ 0x54, 0x57, 0x34, 0x25, 0x5b, 0x42, 0x7d, 0x5d,
+ 0x37, 0x34, 0x2c, 0x79, 0x24, 0x4b, 0x74, 0x73,
+ 0x25, 0x36, 0x73, 0x3a, 0x2c, 0x55, 0x69, 0x3c,
+ 0x58, 0x67, 0x33, 0x53, 0x67, 0x5c, 0x61, 0x7b,
+ 0x44, 0x2e, 0x42, 0x2d, 0x6b, 0x50, 0x55, 0x24,
+ 0x70, 0x58, 0x60, 0x38, 0x42, 0x45, 0x70, 0x6d,
+ 0x2f, 0x27, 0x27, 0x2c, 0x21, 0x6d, 0x57, 0x6e,
+ 0x43, 0x3c, 0x5b, 0x27, 0x7a, 0x34, 0x49, 0x5a,
+ 0x69, 0x30, 0x3f, 0x6f, 0x77, 0x70, 0x39, 0x2d,
+ 0x51, 0x74, 0x4b, 0x25, 0x70, 0x51, 0x64, 0x4d,
+ 0x75, 0x52, 0x5e, 0x3e, 0x37, 0x30, 0x5d, 0x3b,
+ 0x2c, 0x72, 0x25, 0x6c, 0x6f, 0x79, 0x69, 0x3c,
+ 0x5b, 0x73, 0x3d, 0x41, 0x28, 0x28, 0x64, 0x60,
+ 0x4b, 0x7a, 0x2c, 0x4a, 0x6b, 0x3d, 0x2e, 0x6c,
+ 0x7a, 0x54, 0x70, 0x61, 0x6f, 0x4b, 0x40, 0x28,
+ 0x59, 0x31, 0x25, 0x21, 0x57, 0x79, 0x4b, 0x31,
+ 0x6f, 0x4e, 0x71, 0x2b, 0x3c, 0x24, 0x30, 0x28,
+ 0x3c, 0x61, 0x28, 0x4b, 0x35, 0x61, 0x4d, 0x55,
+ 0x5e, 0x66, 0x34, 0x5f, 0x61, 0x70, 0x7b, 0x67,
+ 0x51, 0x55, 0x68, 0x78, 0x26, 0x3a, 0x27, 0x4e,
+ 0x71, 0x79, 0x4f, 0x67, 0x2c, 0x5a, 0x79, 0x75,
+ 0x59, 0x3a, 0x33, 0x4a, 0x36, 0x71, 0x72, 0x6d,
+ 0x49, 0x3e, 0x53, 0x59, 0x2b, 0x2b, 0x27, 0x4e,
+ 0x50, 0x5d, 0x21, 0x55, 0x64, 0x4b, 0x72, 0x73,
+ 0x25, 0x55, 0x26, 0x4f, 0x3a, 0x21, 0x54, 0x29,
+ 0x4f, 0x64, 0x51, 0x59, 0x60, 0x7b, 0x7c, 0x6f,
+ 0x3e, 0x65, 0x74, 0x6a, 0x5b, 0x52, 0x2c, 0x56,
+ 0x4e, 0x45, 0x53, 0x4b, 0x7c, 0x38, 0x49, 0x4b,
+ 0x4e, 0x4f, 0x4a, 0x47, 0x5e, 0x7c, 0x46, 0x3b,
+ 0x67, 0x2e, 0x43, 0x79, 0x35, 0x55, 0x59, 0x6d,
+ 0x38, 0x70, 0x2f, 0x59, 0x4f, 0x27, 0x63, 0x40,
+ 0x66, 0x2d, 0x39, 0x4f, 0x3d, 0x2e, 0x4c, 0x67,
+ 0x71, 0x7d, 0x34, 0x22, 0x52, 0x4e, 0x36, 0x7b,
+ 0x2c, 0x39, 0x4d, 0x42, 0x60, 0x75, 0x74, 0x72,
+ 0x4f, 0x72, 0x68, 0x3a, 0x51, 0x31, 0x2d, 0x21,
+ 0x4a, 0x35, 0x47, 0x6d, 0x69, 0x3c, 0x50, 0x4c,
+ 0x59, 0x66, 0x4c, 0x71, 0x24, 0x3a, 0x36, 0x67,
+ 0x24, 0x5a, 0x59, 0x28, 0x7c, 0x21, 0x5e, 0x77,
+ 0x68, 0x5e, 0x7b, 0x6e, 0x56, 0x62, 0x36, 0x29,
+ 0x6f, 0x4f, 0x5d, 0x57, 0x56, 0x2b, 0x75, 0x2a,
+ 0x2c, 0x69, 0x63, 0x51, 0x74, 0x6e, 0x5e, 0x46,
+ 0x50, 0x28, 0x2c, 0x3b, 0x32, 0x53, 0x28, 0x78,
+ 0x59, 0x72, 0x39, 0x5e, 0x44, 0x5c, 0x77, 0x60,
+ 0x72, 0x44, 0x3b, 0x75, 0x68, 0x39, 0x55, 0x3e,
+ 0x44, 0x50, 0x76, 0x3c, 0x48, 0x46, 0x43, 0x22,
+ 0x56, 0x27, 0x21, 0x31, 0x33, 0x4a, 0x5a, 0x74,
+ 0x41, 0x58, 0x3f, 0x39, 0x29, 0x71, 0x73, 0x30,
+ 0x57, 0x70, 0x33, 0x62, 0x7b, 0x4a, 0x75, 0x3e,
+ 0x4d, 0x4c, 0x4e, 0x55, 0x63, 0x38, 0x66, 0x7d,
+ 0x68, 0x7d, 0x6f, 0x23, 0x55, 0x50, 0x3d, 0x34,
+ 0x46, 0x5e, 0x2f, 0x55, 0x27, 0x62, 0x68, 0x7c,
+ 0x6c, 0x21, 0x2b, 0x63, 0x4b, 0x47, 0x6b, 0x6a,
+ 0x5b, 0x7b, 0x5c, 0x71, 0x37, 0x7c, 0x52, 0x2b,
+ 0x2f, 0x4a, 0x47, 0x70, 0x78, 0x50, 0x2f, 0x75,
+ 0x28, 0x4c, 0x60, 0x4c, 0x4c, 0x54, 0x6b, 0x68,
+ 0x63, 0x4f, 0x47, 0x39, 0x2a, 0x70, 0x51, 0x7d,
+ 0x28, 0x59, 0x52, 0x46, 0x4b, 0x38, 0x27, 0x49,
+ 0x50, 0x5d, 0x25, 0x22, 0x5f, 0x48, 0x2c, 0x2f,
+ 0x67, 0x59, 0x5d, 0x7d, 0x21, 0x3d, 0x72, 0x4f,
+ 0x5c, 0x5b, 0x41, 0x47, 0x5f, 0x56, 0x69, 0x42,
+ 0x55, 0x60, 0x68, 0x4b, 0x77, 0x44, 0x4c, 0x3b,
+ 0x7d, 0x5a, 0x58, 0x43, 0x7a, 0x33, 0x22, 0x58,
+ 0x58, 0x6f, 0x74, 0x53, 0x57, 0x6d, 0x6e, 0x29,
+ 0x6b, 0x33, 0x71, 0x68, 0x29, 0x48, 0x67, 0x35,
+ 0x52, 0x41, 0x6b, 0x36, 0x4f, 0x46, 0x31, 0x24,
+ 0x73, 0x56, 0x40, 0x48, 0x37, 0x51, 0x24, 0x2a,
+ 0x59, 0x21, 0x74, 0x76, 0x25, 0x2e, 0x4a, 0x74,
+ 0x32, 0x29, 0x5f, 0x57, 0x7c, 0x58, 0x30, 0x2c,
+ 0x7b, 0x70, 0x5b, 0x51, 0x73, 0x27, 0x4a, 0x28,
+ 0x77, 0x2a, 0x43, 0x28, 0x2e, 0x32, 0x3d, 0x38,
+ 0x36, 0x2e, 0x6b, 0x40, 0x6c, 0x76, 0x54, 0x66,
+ 0x4a, 0x5c, 0x25, 0x62, 0x2e, 0x61, 0x48, 0x30,
+ 0x28, 0x41, 0x40, 0x69, 0x3c, 0x39, 0x36, 0x4b,
+ 0x64, 0x50, 0x76, 0x3d, 0x52, 0x50, 0x77, 0x33,
+ 0x3b, 0x65, 0x59, 0x31, 0x5c, 0x48, 0x6a, 0x74,
+ 0x78, 0x5b, 0x74, 0x60, 0x47, 0x27, 0x60, 0x22,
+ 0x4a, 0x72, 0x25, 0x34, 0x5d, 0x3a, 0x21, 0x66,
+ 0x61, 0x7b, 0x34, 0x41, 0x3b, 0x3a, 0x27, 0x44,
+ 0x48, 0x7c, 0x7a, 0x74, 0x3a, 0x68, 0x59, 0x48,
+ 0x61, 0x32, 0x49, 0x61, 0x40, 0x22, 0x33, 0x75,
+ 0x29, 0x76, 0x5b, 0x24, 0x5b, 0x5c, 0x76, 0x5c,
+ 0x28, 0x75, 0x36, 0x26, 0x2c, 0x65, 0x5e, 0x51,
+ 0x7b, 0x3a, 0x7d, 0x4f, 0x35, 0x73, 0x6b, 0x5b,
+ 0x5c, 0x37, 0x35, 0x6b, 0x41, 0x35, 0x40, 0x3a,
+ 0x22, 0x28, 0x6c, 0x71, 0x46, 0x68, 0x7b, 0x66,
+ 0x56, 0x24, 0x7c, 0x54, 0x28, 0x30, 0x22, 0x4e,
+ 0x3c, 0x65, 0x69, 0x36, 0x44, 0x53, 0x3d, 0x6c,
+ 0x5f, 0x73, 0x6c, 0x6f, 0x5e, 0x27, 0x23, 0x4e,
+ 0x60, 0x45, 0x2f, 0x3d, 0x37, 0x28, 0x51, 0x29,
+ 0x77, 0x6a, 0x6b, 0x2a, 0x2a, 0x51, 0x26, 0x4c,
+ 0x4e, 0x71, 0x77, 0x73, 0x71, 0x2d, 0x5a, 0x2c,
+ 0x23, 0x3d, 0x5f, 0x62, 0x63, 0x2e, 0x72, 0x2a,
+ 0x75, 0x66, 0x43, 0x56, 0x5f, 0x21, 0x64, 0x66,
+ 0x35, 0x3b, 0x7a, 0x45, 0x3f, 0x4f, 0x57, 0x22,
+ 0x5a, 0x45, 0x65, 0x37, 0x58, 0x5b, 0x43, 0x66,
+ 0x4f, 0x5d, 0x6e, 0x41, 0x41, 0x62, 0x5e, 0x39,
+ 0x65, 0x6f, 0x43, 0x4b, 0x5e, 0x51, 0x42, 0x3f,
+ 0x2d, 0x68, 0x4b, 0x6e, 0x46, 0x6f, 0x21, 0x44,
+ 0x3c, 0x22, 0x46, 0x31, 0x31, 0x2e, 0x56, 0x2e,
+ 0x77, 0x48, 0x68, 0x23, 0x4a, 0x36, 0x52, 0x5d,
+ 0x61, 0x47, 0x71, 0x2e, 0x3a, 0x4a, 0x5b, 0x56,
+ 0x6b, 0x52, 0x2a, 0x4c, 0x4f, 0x24, 0x34, 0x60,
+ 0x70, 0x58, 0x7a, 0x76, 0x4b, 0x68, 0x24, 0x5f,
+ 0x51, 0x6d, 0x75, 0x45, 0x48, 0x21, 0x53, 0x4d,
+ 0x27, 0x75, 0x5f, 0x50, 0x3e, 0x40, 0x3f, 0x5e,
+ 0x64, 0x41, 0x5f, 0x68, 0x48, 0x30, 0x71, 0x4b,
+ 0x66, 0x2c, 0x2f, 0x76, 0x4b, 0x23, 0x46, 0x34,
+ 0x50, 0x58, 0x52, 0x69, 0x2b, 0x6e, 0x7a, 0x33,
+ 0x53, 0x43, 0x43, 0x35, 0x54, 0x30, 0x73, 0x63,
+ 0x3b, 0x43, 0x52, 0x29, 0x45, 0x37, 0x71, 0x79,
+ 0x5a, 0x26, 0x24, 0x72, 0x73, 0x4e, 0x44, 0x38,
+ 0x5b, 0x71, 0x36, 0x3a, 0x4f, 0x5b, 0x71, 0x28,
+ 0x71, 0x79, 0x72, 0x40, 0x6e, 0x51, 0x72, 0x29,
+ 0x3d, 0x4f, 0x33, 0x22, 0x73, 0x5a, 0x30, 0x71,
+ 0x58, 0x54, 0x59, 0x48, 0x29, 0x2b, 0x5c, 0x73,
+ 0x6f, 0x4e, 0x60, 0x2a, 0x72, 0x39, 0x50, 0x59,
+ 0x6f, 0x48, 0x3e, 0x62, 0x6c, 0x62, 0x49, 0x6c,
+ 0x2c, 0x3f, 0x43, 0x3f, 0x32, 0x7c, 0x6f, 0x6c,
+ 0x39, 0x26, 0x26, 0x7b, 0x5d, 0x65, 0x6f, 0x41,
+ 0x7c, 0x42, 0x2b, 0x65, 0x6f, 0x3e, 0x7b, 0x69,
+ 0x46, 0x4d, 0x68, 0x68, 0x5a, 0x33, 0x25, 0x5d,
+ 0x6f, 0x48, 0x7c, 0x77, 0x7d, 0x3f, 0x4e, 0x30,
+ 0x69, 0x65, 0x28, 0x2e, 0x34, 0x34, 0x41, 0x43,
+ 0x5e, 0x30, 0x23, 0x3b, 0x60, 0x79, 0x5b, 0x26,
+ 0x7c, 0x77, 0x3e, 0x43, 0x24, 0x31, 0x3a, 0x56,
+ 0x24, 0x3c, 0x60, 0x3f, 0x60, 0x55, 0x6a, 0x68
+ };
+ uint32_t request4_len = sizeof(request4);
+
+ uint8_t request5[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x69, 0x3e, 0x72, 0x44, 0x31, 0x6b, 0x28, 0x2f,
+ 0x79, 0x37, 0x58, 0x5d, 0x5f, 0x68, 0x71, 0x47,
+ 0x7a, 0x68, 0x7c, 0x6c, 0x65, 0x3c, 0x74, 0x67,
+ 0x59, 0x5c, 0x3d, 0x28, 0x65, 0x28, 0x58, 0x74,
+ 0x44, 0x62, 0x2e, 0x36, 0x54, 0x2f, 0x24, 0x34,
+ 0x4b, 0x6d, 0x3a, 0x7b, 0x60, 0x71, 0x5a, 0x77,
+ 0x4a, 0x27, 0x25, 0x70, 0x75, 0x56, 0x78, 0x73,
+ 0x2e, 0x38, 0x6c, 0x70, 0x66, 0x7b, 0x7b, 0x2d,
+ 0x78, 0x27, 0x65, 0x63, 0x58, 0x4f, 0x7d, 0x5c,
+ 0x31, 0x3e, 0x36, 0x6e, 0x65, 0x61, 0x2e, 0x4e,
+ 0x26, 0x68, 0x2b, 0x33, 0x7d, 0x54, 0x2c, 0x28,
+ 0x47, 0x3a, 0x31, 0x47, 0x56, 0x32, 0x74, 0x51,
+ 0x79, 0x65, 0x42, 0x45, 0x60, 0x55, 0x6f, 0x48,
+ 0x61, 0x23, 0x72, 0x62, 0x74, 0x3a, 0x5a, 0x26,
+ 0x2d, 0x41, 0x58, 0x62, 0x75, 0x4b, 0x37, 0x2e,
+ 0x3f, 0x2a, 0x6e, 0x2e, 0x2c, 0x43, 0x6f, 0x53,
+ 0x5f, 0x48, 0x7a, 0x53, 0x7b, 0x54, 0x28, 0x30,
+ 0x2b, 0x7a, 0x34, 0x33, 0x28, 0x2b, 0x23, 0x23,
+ 0x72, 0x38, 0x25, 0x30, 0x35, 0x66, 0x76, 0x46,
+ 0x2a, 0x57, 0x7a, 0x60, 0x38, 0x5a, 0x26, 0x4f,
+ 0x78, 0x43, 0x2c, 0x7d, 0x3d, 0x76, 0x7d, 0x66,
+ 0x48, 0x7d, 0x3e, 0x59, 0x31, 0x58, 0x6b, 0x30,
+ 0x76, 0x45, 0x6e, 0x70, 0x72, 0x5f, 0x3c, 0x70,
+ 0x6d, 0x77, 0x42, 0x75, 0x42, 0x73, 0x68, 0x5e,
+ 0x5f, 0x72, 0x2b, 0x2a, 0x70, 0x38, 0x7a, 0x4c,
+ 0x58, 0x2e, 0x5e, 0x2d, 0x2d, 0x78, 0x67, 0x5a,
+ 0x77, 0x34, 0x5a, 0x50, 0x76, 0x2d, 0x2b, 0x77,
+ 0x37, 0x6e, 0x38, 0x2d, 0x7b, 0x44, 0x78, 0x67,
+ 0x52, 0x57, 0x79, 0x43, 0x7d, 0x6d, 0x4d, 0x32,
+ 0x23, 0x37, 0x51, 0x4b, 0x41, 0x60, 0x6e, 0x53,
+ 0x4e, 0x78, 0x37, 0x37, 0x60, 0x56, 0x64, 0x52,
+ 0x25, 0x46, 0x53, 0x5f, 0x2b, 0x56, 0x2b, 0x3b,
+ 0x40, 0x37, 0x33, 0x37, 0x23, 0x43, 0x36, 0x6b,
+ 0x6b, 0x5d, 0x35, 0x28, 0x7d, 0x6a, 0x2c, 0x68,
+ 0x28, 0x4b, 0x4a, 0x6c, 0x27, 0x35, 0x51, 0x66,
+ 0x30, 0x39, 0x28, 0x4d, 0x61, 0x2f, 0x64, 0x36,
+ 0x59, 0x39, 0x68, 0x4b, 0x24, 0x51, 0x7b, 0x6e,
+ 0x38, 0x49, 0x55, 0x72, 0x5f, 0x33, 0x5c, 0x26,
+ 0x45, 0x2f, 0x71, 0x66, 0x33, 0x3d, 0x36, 0x68,
+ 0x65, 0x48, 0x42, 0x40, 0x58, 0x61, 0x4f, 0x50,
+ 0x70, 0x5e, 0x3c, 0x5d, 0x56, 0x43, 0x4c, 0x41,
+ 0x45, 0x54, 0x76, 0x4b, 0x21, 0x25, 0x45, 0x4c,
+ 0x5e, 0x58, 0x23, 0x7d, 0x34, 0x61, 0x5c, 0x53,
+ 0x2a, 0x47, 0x37, 0x22, 0x6d, 0x31, 0x42, 0x6e,
+ 0x22, 0x72, 0x62, 0x55, 0x59, 0x66, 0x28, 0x73,
+ 0x55, 0x50, 0x5c, 0x6f, 0x52, 0x40, 0x3e, 0x3b,
+ 0x44, 0x2a, 0x51, 0x3d, 0x4d, 0x47, 0x3a, 0x57,
+ 0x3e, 0x29, 0x29, 0x7d, 0x40, 0x36, 0x41, 0x3f,
+ 0x58, 0x77, 0x3b, 0x41, 0x2d, 0x64, 0x5a, 0x72,
+ 0x7c, 0x7d, 0x30, 0x68, 0x54, 0x34, 0x40, 0x21,
+ 0x7d, 0x2b, 0x2d, 0x2b, 0x6d, 0x5f, 0x49, 0x57,
+ 0x68, 0x65, 0x79, 0x2c, 0x21, 0x41, 0x31, 0x55,
+ 0x27, 0x4d, 0x78, 0x55, 0x2f, 0x61, 0x62, 0x78,
+ 0x58, 0x25, 0x3a, 0x4b, 0x3e, 0x67, 0x44, 0x7c,
+ 0x7d, 0x52, 0x3d, 0x3e, 0x3b, 0x62, 0x2d, 0x28,
+ 0x48, 0x70, 0x2c, 0x79, 0x31, 0x5a, 0x5e, 0x3f,
+ 0x6a, 0x30, 0x78, 0x41, 0x44, 0x60, 0x4e, 0x63,
+ 0x63, 0x2e, 0x31, 0x79, 0x2b, 0x47, 0x57, 0x26,
+ 0x22, 0x6a, 0x46, 0x43, 0x70, 0x30, 0x51, 0x7d,
+ 0x21, 0x3c, 0x68, 0x74, 0x40, 0x5a, 0x6e, 0x71,
+ 0x3f, 0x76, 0x73, 0x2e, 0x29, 0x3f, 0x6a, 0x55,
+ 0x21, 0x72, 0x65, 0x75, 0x5e, 0x6b, 0x39, 0x6e,
+ 0x3e, 0x76, 0x42, 0x41, 0x65, 0x3f, 0x2b, 0x37,
+ 0x70, 0x7a, 0x7a, 0x29, 0x50, 0x66, 0x21, 0x67,
+ 0x3f, 0x54, 0x32, 0x5f, 0x73, 0x27, 0x59, 0x6f,
+ 0x39, 0x4b, 0x4e, 0x23, 0x54, 0x3b, 0x39, 0x21,
+ 0x38, 0x41, 0x33, 0x44, 0x57, 0x6b, 0x51, 0x30,
+ 0x6a, 0x76, 0x62, 0x2c, 0x5c, 0x5e, 0x49, 0x3e,
+ 0x59, 0x38, 0x5e, 0x4a, 0x59, 0x77, 0x34, 0x25,
+ 0x4f, 0x76, 0x6a, 0x68, 0x6f, 0x73, 0x7c, 0x3d,
+ 0x2d, 0x64, 0x6c, 0x7a, 0x3d, 0x2c, 0x26, 0x28,
+ 0x58, 0x2b, 0x4b, 0x45, 0x68, 0x38, 0x74, 0x63,
+ 0x7b, 0x4a, 0x63, 0x52, 0x26, 0x54, 0x3c, 0x46,
+ 0x77, 0x2d, 0x6b, 0x78, 0x63, 0x7b, 0x6a, 0x50,
+ 0x26, 0x42, 0x62, 0x63, 0x65, 0x6b, 0x63, 0x54,
+ 0x4d, 0x47, 0x59, 0x48, 0x2e, 0x60, 0x7c, 0x4d,
+ 0x33, 0x4d, 0x61, 0x72, 0x76, 0x72, 0x21, 0x4d,
+ 0x2b, 0x43, 0x58, 0x47, 0x4a, 0x36, 0x2d, 0x7b,
+ 0x32, 0x72, 0x21, 0x78, 0x22, 0x38, 0x2c, 0x7a,
+ 0x34, 0x44, 0x45, 0x66, 0x31, 0x7b, 0x37, 0x68,
+ 0x62, 0x65, 0x62, 0x6d, 0x4e, 0x7c, 0x75, 0x38,
+ 0x2a, 0x73, 0x27, 0x64, 0x33, 0x4f, 0x21, 0x41,
+ 0x7c, 0x41, 0x3f, 0x60, 0x68, 0x34, 0x72, 0x5b,
+ 0x38, 0x33, 0x6f, 0x65, 0x3e, 0x5a, 0x7d, 0x25,
+ 0x49, 0x50, 0x60, 0x36, 0x59, 0x5e, 0x6b, 0x25,
+ 0x66, 0x7a, 0x7d, 0x71, 0x40, 0x6c, 0x2c, 0x6e,
+ 0x6a, 0x5a, 0x24, 0x5a, 0x76, 0x21, 0x67, 0x39,
+ 0x4b, 0x4a, 0x31, 0x24, 0x66, 0x66, 0x2e, 0x58,
+ 0x43, 0x46, 0x75, 0x6c, 0x47, 0x28, 0x4f, 0x21,
+ 0x75, 0x77, 0x6f, 0x71, 0x48, 0x3f, 0x4d, 0x4c,
+ 0x51, 0x37, 0x3b, 0x41, 0x4d, 0x41, 0x48, 0x28,
+ 0x71, 0x24, 0x2f, 0x7a, 0x22, 0x49, 0x4a, 0x39,
+ 0x44, 0x43, 0x68, 0x21, 0x3a, 0x34, 0x4e, 0x52,
+ 0x7a, 0x60, 0x71, 0x61, 0x6d, 0x51, 0x58, 0x2a,
+ 0x59, 0x4c, 0x4a, 0x59, 0x6b, 0x77, 0x78, 0x2e,
+ 0x27, 0x78, 0x76, 0x48, 0x4f, 0x46, 0x79, 0x2c,
+ 0x54, 0x42, 0x7b, 0x2c, 0x52, 0x41, 0x54, 0x2b,
+ 0x2c, 0x33, 0x6b, 0x70, 0x77, 0x2e, 0x2e, 0x41,
+ 0x25, 0x7a, 0x48, 0x6e, 0x71, 0x55, 0x6a, 0x43,
+ 0x5a, 0x2c, 0x6c, 0x76, 0x6d, 0x71, 0x72, 0x4d,
+ 0x76, 0x5b, 0x7b, 0x22, 0x4b, 0x45, 0x31, 0x30,
+ 0x26, 0x53, 0x75, 0x3f, 0x26, 0x59, 0x36, 0x2f,
+ 0x68, 0x4f, 0x34, 0x5e, 0x2b, 0x30, 0x63, 0x68,
+ 0x7b, 0x32, 0x5e, 0x77, 0x7d, 0x7b, 0x53, 0x5f,
+ 0x63, 0x53, 0x77, 0x7a, 0x7d, 0x35, 0x28, 0x3e,
+ 0x41, 0x6f, 0x5b, 0x31, 0x78, 0x7b, 0x2b, 0x51,
+ 0x23, 0x43, 0x46, 0x6a, 0x32, 0x32, 0x25, 0x45,
+ 0x57, 0x43, 0x22, 0x50, 0x60, 0x32, 0x70, 0x2e,
+ 0x79, 0x2e, 0x6b, 0x33, 0x67, 0x6c, 0x43, 0x5b,
+ 0x3b, 0x68, 0x53, 0x53, 0x6a, 0x48, 0x59, 0x5f,
+ 0x30, 0x72, 0x7d, 0x6b, 0x37, 0x24, 0x75, 0x52,
+ 0x50, 0x2b, 0x75, 0x35, 0x24, 0x3b, 0x6e, 0x53,
+ 0x56, 0x34, 0x23, 0x54, 0x65, 0x4f, 0x78, 0x3e,
+ 0x46, 0x7d, 0x25, 0x3f, 0x2f, 0x49, 0x6b, 0x49,
+ 0x47, 0x45, 0x24, 0x38, 0x3b, 0x68, 0x6c, 0x4f,
+ 0x29, 0x21, 0x50, 0x32, 0x67, 0x47, 0x5a, 0x72,
+ 0x76, 0x21, 0x39, 0x67, 0x3c, 0x72, 0x47, 0x43,
+ 0x4a, 0x2e, 0x31, 0x32, 0x34, 0x3c, 0x53, 0x2d,
+ 0x22, 0x5b, 0x5b, 0x6a, 0x77, 0x75, 0x31, 0x68,
+ 0x30, 0x45, 0x43, 0x5f, 0x60, 0x5d, 0x56, 0x67,
+ 0x66, 0x55, 0x6a, 0x72, 0x77, 0x7b, 0x44, 0x61,
+ 0x22, 0x64, 0x36, 0x39, 0x6e, 0x44, 0x37, 0x54,
+ 0x45, 0x46, 0x6f, 0x58, 0x35, 0x51, 0x3c, 0x62,
+ 0x49, 0x3a, 0x50, 0x58, 0x56, 0x5d, 0x77, 0x6f,
+ 0x56, 0x64, 0x7b, 0x49, 0x39, 0x21, 0x31, 0x2d,
+ 0x5f, 0x56, 0x56, 0x33, 0x31, 0x69, 0x4a, 0x52,
+ 0x62, 0x5b, 0x6e, 0x65, 0x7c, 0x3d, 0x31, 0x55,
+ 0x3d, 0x75, 0x25, 0x61, 0x50, 0x71, 0x45, 0x29
+ };
+ uint32_t request5_len = sizeof(request5);
+
+ uint8_t request6[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x5b, 0x56, 0x3d, 0x5a, 0x6b, 0x43, 0x73, 0x26,
+ 0x65, 0x3b, 0x38, 0x79, 0x26, 0x5e, 0x60, 0x59,
+ 0x40, 0x71, 0x7c, 0x72, 0x28, 0x29, 0x69, 0x32,
+ 0x72, 0x5a, 0x6c, 0x55, 0x43, 0x65, 0x3f, 0x4a,
+ 0x21, 0x66, 0x59, 0x30, 0x76, 0x39, 0x21, 0x69,
+ 0x4b, 0x25, 0x5d, 0x6e, 0x5f, 0x24, 0x2b, 0x38,
+ 0x70, 0x78, 0x35, 0x7d, 0x39, 0x36, 0x31, 0x72,
+ 0x44, 0x49, 0x45, 0x3d, 0x25, 0x50, 0x24, 0x3b,
+ 0x52, 0x27, 0x66, 0x46, 0x5d, 0x4f, 0x34, 0x50,
+ 0x26, 0x5a, 0x25, 0x3e, 0x3f, 0x34, 0x4b, 0x35,
+ 0x77, 0x3a, 0x3f, 0x3e, 0x23, 0x4e, 0x30, 0x23,
+ 0x70, 0x72, 0x33, 0x34, 0x60, 0x2a, 0x4a, 0x32,
+ 0x6e, 0x29, 0x54, 0x73, 0x5f, 0x26, 0x71, 0x3a,
+ 0x78, 0x5d, 0x3f, 0x31, 0x48, 0x59, 0x61, 0x44,
+ 0x5c, 0x38, 0x4f, 0x41, 0x73, 0x67, 0x62, 0x73,
+ 0x33, 0x52, 0x77, 0x73, 0x57, 0x49, 0x7a, 0x59,
+ 0x26, 0x21, 0x34, 0x38, 0x2b, 0x5f, 0x5f, 0x37,
+ 0x74, 0x28, 0x46, 0x3d, 0x43, 0x42, 0x26, 0x66,
+ 0x63, 0x37, 0x6d, 0x2a, 0x65, 0x3f, 0x71, 0x2d,
+ 0x4c, 0x72, 0x29, 0x4b, 0x3a, 0x77, 0x64, 0x6a,
+ 0x6b, 0x42, 0x70, 0x5c, 0x51, 0x38, 0x71, 0x25,
+ 0x4c, 0x7c, 0x6f, 0x74, 0x71, 0x39, 0x71, 0x25,
+ 0x3f, 0x62, 0x23, 0x45, 0x5f, 0x77, 0x59, 0x56,
+ 0x56, 0x67, 0x78, 0x3a, 0x2e, 0x4e, 0x27, 0x59,
+ 0x65, 0x2f, 0x64, 0x3c, 0x62, 0x40, 0x69, 0x52,
+ 0x36, 0x49, 0x3e, 0x3b, 0x2c, 0x47, 0x4f, 0x3e,
+ 0x61, 0x78, 0x2d, 0x45, 0x71, 0x3f, 0x7b, 0x55,
+ 0x34, 0x36, 0x47, 0x5e, 0x36, 0x51, 0x3d, 0x5a,
+ 0x4b, 0x75, 0x44, 0x72, 0x61, 0x44, 0x71, 0x4e,
+ 0x42, 0x6a, 0x2c, 0x34, 0x40, 0x3b, 0x40, 0x31,
+ 0x31, 0x75, 0x4b, 0x32, 0x71, 0x69, 0x3a, 0x5d,
+ 0x31, 0x25, 0x53, 0x2a, 0x61, 0x54, 0x68, 0x2a,
+ 0x76, 0x71, 0x57, 0x67, 0x56, 0x23, 0x7d, 0x70,
+ 0x7d, 0x28, 0x57, 0x5f, 0x2f, 0x4c, 0x71, 0x2e,
+ 0x40, 0x63, 0x49, 0x5b, 0x7c, 0x7b, 0x56, 0x76,
+ 0x77, 0x46, 0x69, 0x56, 0x3d, 0x75, 0x31, 0x3b,
+ 0x35, 0x40, 0x37, 0x2c, 0x51, 0x37, 0x49, 0x6a,
+ 0x79, 0x68, 0x53, 0x31, 0x4c, 0x6f, 0x57, 0x4c,
+ 0x48, 0x31, 0x6a, 0x30, 0x2b, 0x69, 0x30, 0x56,
+ 0x58, 0x4b, 0x76, 0x3b, 0x60, 0x6d, 0x35, 0x4d,
+ 0x74, 0x2f, 0x74, 0x2c, 0x54, 0x4f, 0x6e, 0x3f,
+ 0x38, 0x56, 0x5c, 0x67, 0x2b, 0x4a, 0x35, 0x30,
+ 0x67, 0x7d, 0x58, 0x24, 0x59, 0x54, 0x48, 0x2e,
+ 0x28, 0x7d, 0x6e, 0x51, 0x55, 0x68, 0x56, 0x54,
+ 0x59, 0x31, 0x4a, 0x65, 0x5a, 0x5e, 0x27, 0x76,
+ 0x76, 0x65, 0x6d, 0x2f, 0x75, 0x63, 0x67, 0x52,
+ 0x5e, 0x29, 0x58, 0x3d, 0x5c, 0x3f, 0x54, 0x7c,
+ 0x67, 0x21, 0x6e, 0x75, 0x67, 0x35, 0x77, 0x31,
+ 0x3d, 0x26, 0x3f, 0x60, 0x45, 0x2d, 0x2b, 0x45,
+ 0x5d, 0x3f, 0x55, 0x73, 0x59, 0x4c, 0x5e, 0x6c,
+ 0x30, 0x4a, 0x4e, 0x47, 0x55, 0x42, 0x6a, 0x4b,
+ 0x32, 0x3c, 0x75, 0x6e, 0x36, 0x51, 0x5f, 0x4c,
+ 0x68, 0x72, 0x72, 0x27, 0x3b, 0x51, 0x59, 0x7b,
+ 0x68, 0x7b, 0x3b, 0x54, 0x35, 0x37, 0x7c, 0x44,
+ 0x43, 0x36, 0x4c, 0x4f, 0x67, 0x62, 0x4e, 0x39,
+ 0x4b, 0x7a, 0x49, 0x36, 0x68, 0x38, 0x4c, 0x4a,
+ 0x64, 0x33, 0x35, 0x2f, 0x3e, 0x5c, 0x58, 0x61,
+ 0x23, 0x5b, 0x50, 0x6e, 0x34, 0x44, 0x60, 0x28,
+ 0x54, 0x41, 0x5c, 0x31, 0x53, 0x2d, 0x58, 0x58,
+ 0x54, 0x28, 0x77, 0x51, 0x6f, 0x64, 0x4c, 0x68,
+ 0x34, 0x79, 0x45, 0x66, 0x2c, 0x26, 0x77, 0x64,
+ 0x5f, 0x6c, 0x3b, 0x71, 0x28, 0x4d, 0x68, 0x2a,
+ 0x6b, 0x37, 0x6a, 0x34, 0x51, 0x27, 0x2a, 0x46,
+ 0x3a, 0x2e, 0x35, 0x21, 0x21, 0x79, 0x51, 0x44,
+ 0x58, 0x5d, 0x6f, 0x65, 0x6b, 0x76, 0x68, 0x3a,
+ 0x43, 0x70, 0x36, 0x41, 0x6b, 0x56, 0x64, 0x75,
+ 0x5b, 0x37, 0x24, 0x56, 0x7c, 0x6e, 0x6c, 0x41,
+ 0x3a, 0x60, 0x56, 0x38, 0x55, 0x63, 0x77, 0x4d,
+ 0x6e, 0x50, 0x3c, 0x3d, 0x7a, 0x44, 0x71, 0x42,
+ 0x4b, 0x55, 0x75, 0x72, 0x61, 0x60, 0x65, 0x6f,
+ 0x7a, 0x26, 0x64, 0x46, 0x67, 0x74, 0x29, 0x2a,
+ 0x5b, 0x62, 0x41, 0x28, 0x62, 0x30, 0x34, 0x33,
+ 0x40, 0x79, 0x7a, 0x38, 0x56, 0x38, 0x73, 0x22,
+ 0x7a, 0x7d, 0x73, 0x2a, 0x2a, 0x28, 0x2b, 0x63,
+ 0x27, 0x6f, 0x3d, 0x3e, 0x2c, 0x56, 0x23, 0x32,
+ 0x4b, 0x3b, 0x58, 0x4d, 0x72, 0x4c, 0x49, 0x6f,
+ 0x30, 0x76, 0x23, 0x21, 0x21, 0x3c, 0x49, 0x56,
+ 0x7a, 0x56, 0x79, 0x2f, 0x50, 0x7a, 0x5b, 0x21,
+ 0x21, 0x4a, 0x48, 0x61, 0x33, 0x52, 0x49, 0x2e,
+ 0x30, 0x7d, 0x2c, 0x2d, 0x67, 0x23, 0x55, 0x62,
+ 0x66, 0x52, 0x5a, 0x61, 0x75, 0x63, 0x3c, 0x39,
+ 0x69, 0x41, 0x31, 0x6b, 0x4e, 0x6f, 0x25, 0x34,
+ 0x74, 0x30, 0x21, 0x3a, 0x40, 0x72, 0x44, 0x40,
+ 0x60, 0x4c, 0x53, 0x74, 0x42, 0x64, 0x44, 0x49,
+ 0x76, 0x67, 0x21, 0x79, 0x36, 0x3c, 0x37, 0x70,
+ 0x4f, 0x58, 0x29, 0x71, 0x2a, 0x3a, 0x4d, 0x5d,
+ 0x67, 0x68, 0x52, 0x63, 0x23, 0x24, 0x4b, 0x21,
+ 0x3f, 0x68, 0x69, 0x6c, 0x66, 0x66, 0x42, 0x28,
+ 0x59, 0x35, 0x34, 0x6f, 0x2d, 0x6a, 0x25, 0x66,
+ 0x34, 0x54, 0x5d, 0x50, 0x26, 0x41, 0x22, 0x4f,
+ 0x34, 0x79, 0x3c, 0x50, 0x68, 0x2d, 0x5f, 0x7b,
+ 0x63, 0x7d, 0x58, 0x2e, 0x73, 0x46, 0x2f, 0x54,
+ 0x61, 0x27, 0x74, 0x45, 0x23, 0x72, 0x31, 0x7d,
+ 0x63, 0x4b, 0x43, 0x5e, 0x44, 0x54, 0x2c, 0x38,
+ 0x58, 0x24, 0x75, 0x6c, 0x50, 0x3c, 0x23, 0x5f,
+ 0x35, 0x57, 0x4f, 0x7b, 0x2f, 0x57, 0x29, 0x73,
+ 0x58, 0x2a, 0x66, 0x3e, 0x49, 0x42, 0x5a, 0x6b,
+ 0x75, 0x6a, 0x38, 0x3f, 0x73, 0x44, 0x42, 0x46,
+ 0x2d, 0x39, 0x66, 0x5b, 0x28, 0x3e, 0x63, 0x62,
+ 0x53, 0x75, 0x65, 0x64, 0x79, 0x32, 0x35, 0x71,
+ 0x22, 0x6a, 0x7b, 0x41, 0x2b, 0x26, 0x43, 0x79,
+ 0x58, 0x6f, 0x71, 0x25, 0x24, 0x34, 0x72, 0x5b,
+ 0x4a, 0x2c, 0x5c, 0x77, 0x23, 0x42, 0x27, 0x6a,
+ 0x67, 0x51, 0x5f, 0x3c, 0x75, 0x2c, 0x3f, 0x43,
+ 0x45, 0x5b, 0x48, 0x65, 0x6f, 0x6c, 0x27, 0x65,
+ 0x21, 0x3e, 0x33, 0x37, 0x5f, 0x2b, 0x2e, 0x24,
+ 0x22, 0x47, 0x4e, 0x33, 0x5b, 0x7b, 0x21, 0x3c,
+ 0x53, 0x69, 0x2e, 0x31, 0x3d, 0x48, 0x57, 0x3a,
+ 0x56, 0x48, 0x6b, 0x47, 0x5d, 0x33, 0x41, 0x6c,
+ 0x66, 0x4c, 0x61, 0x67, 0x32, 0x69, 0x53, 0x2c,
+ 0x2f, 0x3e, 0x36, 0x68, 0x37, 0x28, 0x40, 0x21,
+ 0x76, 0x27, 0x44, 0x26, 0x24, 0x6a, 0x30, 0x75,
+ 0x2a, 0x73, 0x48, 0x36, 0x52, 0x4a, 0x3b, 0x51,
+ 0x4e, 0x2f, 0x23, 0x36, 0x4b, 0x49, 0x33, 0x5a,
+ 0x70, 0x2c, 0x54, 0x5b, 0x67, 0x48, 0x53, 0x5d,
+ 0x21, 0x3e, 0x6b, 0x52, 0x6a, 0x3c, 0x48, 0x29,
+ 0x68, 0x27, 0x32, 0x75, 0x61, 0x7c, 0x51, 0x2e,
+ 0x7b, 0x49, 0x2f, 0x5b, 0x3d, 0x74, 0x5a, 0x28,
+ 0x26, 0x29, 0x2c, 0x30, 0x54, 0x74, 0x45, 0x55,
+ 0x4a, 0x3d, 0x39, 0x35, 0x66, 0x56, 0x28, 0x6d,
+ 0x6e, 0x38, 0x7b, 0x2b, 0x40, 0x31, 0x56, 0x61,
+ 0x74, 0x2b, 0x79, 0x5f, 0x63, 0x51, 0x53, 0x52,
+ 0x7d, 0x73, 0x4e, 0x2e, 0x45, 0x3b, 0x22, 0x28,
+ 0x6c, 0x2b, 0x47, 0x21, 0x50, 0x2a, 0x7c, 0x45,
+ 0x48, 0x57, 0x3e, 0x2f, 0x6d, 0x66, 0x6c, 0x51,
+ 0x23, 0x6c, 0x37, 0x4d, 0x4b, 0x4b, 0x66, 0x55,
+ 0x69, 0x2e, 0x4a, 0x69, 0x71, 0x7c, 0x71, 0x30,
+ 0x5c, 0x43, 0x46, 0x63, 0x5a, 0x23, 0x75, 0x40
+ };
+ uint32_t request6_len = sizeof(request6);
+
+ uint8_t request7[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x5d, 0x32, 0x55, 0x71, 0x51, 0x45, 0x4e, 0x54,
+ 0x34, 0x21, 0x46, 0x77, 0x5e, 0x5b, 0x75, 0x62,
+ 0x2b, 0x5c, 0x34, 0x26, 0x72, 0x2b, 0x2c, 0x64,
+ 0x4b, 0x65, 0x56, 0x72, 0x31, 0x7d, 0x6a, 0x5f,
+ 0x70, 0x26, 0x32, 0x29, 0x7d, 0x21, 0x5b, 0x3e,
+ 0x5e, 0x53, 0x3d, 0x48, 0x5e, 0x2a, 0x4c, 0x37,
+ 0x3d, 0x59, 0x79, 0x21, 0x4f, 0x56, 0x79, 0x2a,
+ 0x4e, 0x28, 0x61, 0x7d, 0x2c, 0x58, 0x2f, 0x78,
+ 0x5c, 0x3f, 0x5c, 0x42, 0x6d, 0x2f, 0x71, 0x54,
+ 0x25, 0x31, 0x73, 0x38, 0x6c, 0x31, 0x5a, 0x2e,
+ 0x42, 0x5b, 0x2d, 0x41, 0x24, 0x4c, 0x37, 0x40,
+ 0x39, 0x7d, 0x2a, 0x67, 0x60, 0x6a, 0x7a, 0x62,
+ 0x24, 0x4e, 0x3f, 0x2e, 0x69, 0x35, 0x28, 0x65,
+ 0x77, 0x53, 0x23, 0x44, 0x59, 0x71, 0x31, 0x5c,
+ 0x40, 0x5d, 0x3a, 0x27, 0x46, 0x55, 0x30, 0x56,
+ 0x21, 0x74, 0x3e, 0x73, 0x41, 0x22, 0x52, 0x68,
+ 0x40, 0x6c, 0x37, 0x3e, 0x62, 0x5a, 0x2e, 0x21,
+ 0x23, 0x33, 0x27, 0x73, 0x68, 0x26, 0x60, 0x67,
+ 0x70, 0x58, 0x50, 0x42, 0x58, 0x27, 0x3a, 0x35,
+ 0x6f, 0x51, 0x62, 0x78, 0x25, 0x2c, 0x7b, 0x66,
+ 0x34, 0x6a, 0x5a, 0x39, 0x60, 0x70, 0x41, 0x2d,
+ 0x65, 0x26, 0x5a, 0x67, 0x58, 0x2d, 0x3e, 0x56,
+ 0x6d, 0x30, 0x4b, 0x4d, 0x5d, 0x45, 0x41, 0x3d,
+ 0x6e, 0x27, 0x4e, 0x5a, 0x7d, 0x2e, 0x62, 0x4d,
+ 0x42, 0x70, 0x31, 0x24, 0x73, 0x5c, 0x78, 0x77,
+ 0x50, 0x73, 0x27, 0x48, 0x3d, 0x35, 0x2c, 0x4b,
+ 0x40, 0x2d, 0x25, 0x77, 0x5d, 0x3d, 0x6b, 0x50,
+ 0x6f, 0x57, 0x73, 0x2f, 0x4f, 0x6e, 0x4c, 0x6e,
+ 0x56, 0x7b, 0x55, 0x3c, 0x6d, 0x60, 0x47, 0x53,
+ 0x56, 0x39, 0x3b, 0x51, 0x61, 0x71, 0x75, 0x73,
+ 0x6b, 0x70, 0x58, 0x5f, 0x2c, 0x27, 0x74, 0x49,
+ 0x2c, 0x2b, 0x53, 0x2d, 0x5b, 0x79, 0x43, 0x34,
+ 0x39, 0x5a, 0x38, 0x3e, 0x2d, 0x66, 0x70, 0x3d,
+ 0x49, 0x51, 0x29, 0x4d, 0x5d, 0x4c, 0x57, 0x4a,
+ 0x2f, 0x41, 0x69, 0x56, 0x57, 0x77, 0x49, 0x58,
+ 0x75, 0x28, 0x29, 0x4a, 0x6d, 0x54, 0x4f, 0x4f,
+ 0x3f, 0x58, 0x5f, 0x58, 0x6f, 0x39, 0x22, 0x4d,
+ 0x5d, 0x31, 0x75, 0x43, 0x2f, 0x7d, 0x31, 0x3d,
+ 0x4c, 0x4d, 0x76, 0x74, 0x4d, 0x57, 0x3b, 0x56,
+ 0x57, 0x48, 0x2b, 0x5d, 0x32, 0x67, 0x51, 0x6e,
+ 0x60, 0x39, 0x6f, 0x64, 0x38, 0x37, 0x52, 0x4b,
+ 0x52, 0x42, 0x32, 0x4f, 0x24, 0x53, 0x31, 0x6e,
+ 0x4a, 0x68, 0x2f, 0x28, 0x2e, 0x27, 0x49, 0x75,
+ 0x77, 0x75, 0x26, 0x47, 0x7c, 0x5d, 0x72, 0x5a,
+ 0x77, 0x50, 0x2e, 0x6c, 0x27, 0x68, 0x6b, 0x7b,
+ 0x27, 0x63, 0x21, 0x3d, 0x30, 0x2d, 0x5c, 0x67,
+ 0x4d, 0x41, 0x79, 0x47, 0x42, 0x50, 0x6d, 0x32,
+ 0x74, 0x39, 0x62, 0x4d, 0x5f, 0x65, 0x78, 0x4f,
+ 0x67, 0x3a, 0x60, 0x26, 0x45, 0x61, 0x7c, 0x61,
+ 0x63, 0x40, 0x46, 0x79, 0x52, 0x47, 0x57, 0x49,
+ 0x53, 0x4c, 0x48, 0x36, 0x67, 0x47, 0x5c, 0x71,
+ 0x50, 0x4d, 0x4f, 0x58, 0x26, 0x40, 0x6d, 0x54,
+ 0x55, 0x67, 0x66, 0x23, 0x70, 0x23, 0x68, 0x70,
+ 0x4d, 0x2c, 0x7a, 0x3d, 0x60, 0x51, 0x35, 0x64,
+ 0x56, 0x2f, 0x26, 0x6d, 0x72, 0x6a, 0x59, 0x34,
+ 0x3a, 0x73, 0x4b, 0x27, 0x33, 0x61, 0x26, 0x45,
+ 0x61, 0x28, 0x74, 0x22, 0x54, 0x50, 0x2e, 0x39,
+ 0x6a, 0x2c, 0x27, 0x59, 0x26, 0x73, 0x44, 0x71,
+ 0x67, 0x4c, 0x37, 0x74, 0x2c, 0x63, 0x52, 0x2a,
+ 0x60, 0x4f, 0x7b, 0x32, 0x39, 0x21, 0x79, 0x54,
+ 0x79, 0x6d, 0x28, 0x27, 0x3a, 0x6a, 0x7d, 0x40,
+ 0x6a, 0x4f, 0x4b, 0x46, 0x61, 0x36, 0x6a, 0x22,
+ 0x3f, 0x77, 0x2d, 0x6a, 0x3b, 0x73, 0x71, 0x72,
+ 0x3c, 0x21, 0x2e, 0x3f, 0x33, 0x25, 0x76, 0x64,
+ 0x64, 0x70, 0x43, 0x32, 0x44, 0x73, 0x61, 0x51,
+ 0x3c, 0x3b, 0x45, 0x3a, 0x68, 0x46, 0x5b, 0x6e,
+ 0x36, 0x47, 0x4d, 0x38, 0x26, 0x4f, 0x5c, 0x7d,
+ 0x73, 0x29, 0x24, 0x78, 0x44, 0x75, 0x40, 0x42,
+ 0x41, 0x2a, 0x73, 0x2b, 0x24, 0x38, 0x51, 0x67,
+ 0x36, 0x67, 0x2f, 0x70, 0x58, 0x54, 0x6e, 0x5d,
+ 0x3b, 0x41, 0x59, 0x76, 0x7d, 0x2d, 0x40, 0x70,
+ 0x29, 0x4a, 0x4a, 0x31, 0x79, 0x2c, 0x4e, 0x22,
+ 0x31, 0x59, 0x31, 0x3c, 0x2f, 0x21, 0x29, 0x3f,
+ 0x65, 0x6c, 0x38, 0x55, 0x4f, 0x27, 0x66, 0x66,
+ 0x34, 0x45, 0x49, 0x41, 0x56, 0x24, 0x2e, 0x40,
+ 0x36, 0x23, 0x5a, 0x46, 0x40, 0x23, 0x7b, 0x2d,
+ 0x69, 0x54, 0x6c, 0x51, 0x58, 0x73, 0x56, 0x60,
+ 0x5f, 0x60, 0x63, 0x5f, 0x77, 0x6a, 0x4c, 0x2c,
+ 0x35, 0x39, 0x60, 0x73, 0x63, 0x3e, 0x2d, 0x55,
+ 0x5a, 0x26, 0x4b, 0x43, 0x3b, 0x56, 0x33, 0x58,
+ 0x74, 0x51, 0x4f, 0x5c, 0x2a, 0x44, 0x78, 0x66,
+ 0x78, 0x71, 0x40, 0x29, 0x5e, 0x26, 0x57, 0x51,
+ 0x49, 0x30, 0x29, 0x73, 0x38, 0x56, 0x6c, 0x41,
+ 0x78, 0x3d, 0x61, 0x3d, 0x2c, 0x33, 0x46, 0x57,
+ 0x54, 0x63, 0x3e, 0x79, 0x55, 0x4a, 0x7d, 0x2e,
+ 0x2a, 0x3c, 0x77, 0x47, 0x35, 0x29, 0x5a, 0x6d,
+ 0x69, 0x48, 0x6b, 0x73, 0x7d, 0x4f, 0x5f, 0x6f,
+ 0x3a, 0x7a, 0x4e, 0x54, 0x59, 0x38, 0x62, 0x44,
+ 0x72, 0x51, 0x57, 0x6a, 0x74, 0x54, 0x4f, 0x77,
+ 0x6b, 0x66, 0x4a, 0x6b, 0x39, 0x29, 0x69, 0x60,
+ 0x71, 0x52, 0x6a, 0x32, 0x66, 0x6c, 0x25, 0x76,
+ 0x27, 0x7a, 0x2c, 0x38, 0x72, 0x4e, 0x5f, 0x40,
+ 0x26, 0x74, 0x6a, 0x5e, 0x42, 0x38, 0x78, 0x34,
+ 0x4f, 0x4f, 0x35, 0x27, 0x39, 0x62, 0x52, 0x61,
+ 0x37, 0x54, 0x47, 0x38, 0x70, 0x31, 0x7a, 0x66,
+ 0x69, 0x72, 0x24, 0x52, 0x2a, 0x2a, 0x78, 0x72,
+ 0x2b, 0x2e, 0x2a, 0x57, 0x4a, 0x21, 0x52, 0x3c,
+ 0x2a, 0x2f, 0x24, 0x58, 0x34, 0x3c, 0x42, 0x5c,
+ 0x5b, 0x78, 0x27, 0x55, 0x63, 0x58, 0x3e, 0x26,
+ 0x50, 0x2c, 0x72, 0x60, 0x36, 0x6c, 0x46, 0x58,
+ 0x63, 0x59, 0x23, 0x2a, 0x2d, 0x63, 0x6a, 0x68,
+ 0x69, 0x74, 0x3f, 0x49, 0x4f, 0x48, 0x4a, 0x3b,
+ 0x59, 0x56, 0x77, 0x43, 0x6d, 0x57, 0x28, 0x5f,
+ 0x39, 0x73, 0x28, 0x74, 0x3c, 0x4f, 0x43, 0x48,
+ 0x6a, 0x57, 0x5d, 0x41, 0x73, 0x3f, 0x41, 0x7c,
+ 0x65, 0x5e, 0x2d, 0x38, 0x72, 0x3a, 0x53, 0x3e,
+ 0x33, 0x47, 0x69, 0x6a, 0x6e, 0x78, 0x67, 0x5d,
+ 0x35, 0x3b, 0x3f, 0x23, 0x7c, 0x71, 0x3d, 0x7c,
+ 0x3a, 0x3c, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x6a, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x6a, 0x40, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x6a, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x6a, 0x40, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x80, 0x23, 0x00, 0xdf, 0xaf, 0xff, 0x33,
+ 0x9b, 0x78, 0x70, 0x43, 0xc5, 0x0a, 0x4d, 0x98,
+ 0x96, 0x02, 0x64, 0x92, 0xc1, 0xee, 0x70, 0x32
+ };
+ uint32_t request7_len = sizeof(request7);
+
+ uint8_t request8[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x65, 0xc1, 0xef, 0x7b, 0xd6, 0xaa, 0xd6, 0x09,
+ 0x21, 0xf6, 0xe7, 0xd1, 0x4c, 0xdf, 0x6a, 0x2d,
+ 0x0a, 0xfb, 0x43, 0xea, 0xda, 0x07, 0x24, 0x84,
+ 0x88, 0x52, 0x9e, 0xa8, 0xa1, 0x7f, 0x4b, 0x60,
+ 0xec, 0x94, 0x57, 0x33, 0x06, 0x93, 0x92, 0x25,
+ 0xd6, 0xac, 0xdc, 0x89, 0x68, 0x5e, 0xbb, 0x32,
+ 0x2b, 0x17, 0x68, 0xf2, 0x06, 0xb7, 0x86, 0xac,
+ 0x81, 0xfe, 0x52, 0x27, 0xf5, 0x80, 0x11, 0x0d,
+ 0x4e, 0x2e, 0x1b, 0xa3, 0x44, 0x8a, 0x58, 0xed,
+ 0xf3, 0x9c, 0xe9, 0x31, 0x01, 0x72, 0xa6, 0xab,
+ 0xfa, 0xa8, 0x05, 0x00, 0x37, 0x60, 0x6b, 0x81,
+ 0xef, 0xf4, 0x96, 0x9a, 0xf7, 0x67, 0x95, 0x27,
+ 0x7a, 0x25, 0xef, 0x6f, 0x0e, 0xff, 0x2d, 0x15,
+ 0x7f, 0x23, 0x1c, 0xa7, 0x56, 0x94, 0x4a, 0x18,
+ 0x98, 0xc6, 0xd8, 0xd2, 0x29, 0x5b, 0x57, 0xb8,
+ 0x5d, 0x3a, 0x93, 0x58, 0x45, 0x77, 0x36, 0xe3,
+ 0xd1, 0x36, 0x87, 0xff, 0xe3, 0x94, 0x0f, 0x00,
+ 0xe6, 0x7c, 0x1a, 0x92, 0xc1, 0x5f, 0x40, 0xc3,
+ 0xa3, 0x25, 0xce, 0xd4, 0xaf, 0x39, 0xeb, 0x17,
+ 0xcf, 0x22, 0x43, 0xd9, 0x0c, 0xce, 0x37, 0x86,
+ 0x46, 0x54, 0xd6, 0xce, 0x00, 0x30, 0x36, 0xae,
+ 0xf9, 0xb5, 0x2b, 0x11, 0xa0, 0xfe, 0xa3, 0x4b,
+ 0x2e, 0x05, 0xbe, 0x54, 0xa9, 0xd8, 0xa5, 0x76,
+ 0x83, 0x5b, 0x63, 0x01, 0x1c, 0xd4, 0x56, 0x72,
+ 0xcd, 0xdc, 0x4a, 0x1d, 0x77, 0xda, 0x8a, 0x9e,
+ 0xba, 0xcb, 0x6c, 0xe8, 0x19, 0x5d, 0x68, 0xef,
+ 0x8e, 0xbc, 0x6a, 0x05, 0x53, 0x0b, 0xc7, 0xc5,
+ 0x96, 0x84, 0x04, 0xd9, 0xda, 0x4c, 0x42, 0x31,
+ 0xd9, 0xbd, 0x99, 0x06, 0xf7, 0xa3, 0x0a, 0x19,
+ 0x49, 0x07, 0x77, 0xf0, 0xdb, 0x7c, 0x43, 0xfa,
+ 0xb2, 0xad, 0xb0, 0xfa, 0x87, 0x52, 0xba, 0xc9,
+ 0x94, 0x61, 0xdc, 0xcf, 0x16, 0xac, 0x0f, 0x4a,
+ 0xa3, 0x6b, 0x5b, 0x6e, 0x27, 0x86, 0x1f, 0xfe,
+ 0x4d, 0x28, 0x3a, 0xa5, 0x10, 0x54, 0x6d, 0xed,
+ 0x53, 0xf9, 0x73, 0xc6, 0x6e, 0xa8, 0xc0, 0x97,
+ 0xcf, 0x56, 0x3b, 0x61, 0xdf, 0xab, 0x83, 0x18,
+ 0xe8, 0x09, 0xee, 0x6a, 0xb7, 0xf5, 0xc9, 0x62,
+ 0x55, 0x2d, 0xc7, 0x0c, 0x0d, 0xa0, 0x22, 0xd8,
+ 0xd4, 0xd6, 0xb2, 0x12, 0x21, 0xd7, 0x73, 0x3e,
+ 0x41, 0xb0, 0x5c, 0xd4, 0xcf, 0x98, 0xf3, 0x70,
+ 0xe6, 0x08, 0xe6, 0x2a, 0x4f, 0x24, 0x85, 0xe8,
+ 0x74, 0xa8, 0x41, 0x5f, 0x0e, 0xfd, 0xf1, 0xf3,
+ 0xbe, 0x9b, 0x14, 0xfd, 0xc0, 0x73, 0x11, 0xff,
+ 0xa5, 0x5b, 0x06, 0x34, 0xc3, 0x6c, 0x28, 0x42,
+ 0x07, 0xfe, 0x8a, 0xa5, 0xbe, 0x72, 0x7a, 0xf7,
+ 0xfa, 0x25, 0xec, 0x35, 0x5e, 0x98, 0x71, 0x50,
+ 0x60, 0x35, 0x76, 0x53, 0x40, 0x1a, 0x34, 0xa5,
+ 0x99, 0x09, 0xa2, 0xc6, 0xca, 0xa5, 0xce, 0x08,
+ 0x50, 0x45, 0xab, 0x8d, 0xfb, 0xe3, 0xb8, 0xe4,
+ 0x8a, 0x61, 0x48, 0x14, 0x6e, 0xf7, 0x58, 0x71,
+ 0xe5, 0x2e, 0xbc, 0x12, 0xd1, 0x25, 0xe9, 0x65,
+ 0x7a, 0xa1, 0x27, 0xbe, 0x3b, 0x8b, 0xe8, 0xe7,
+ 0xbc, 0xe1, 0x05, 0xe7, 0x92, 0xeb, 0xb9, 0xdf,
+ 0x5d, 0x53, 0x74, 0xc0, 0x63, 0x97, 0x80, 0xb8,
+ 0x3c, 0xae, 0xf3, 0xf2, 0x09, 0x12, 0x81, 0x6c,
+ 0x69, 0x10, 0x6f, 0xf6, 0xbe, 0x03, 0x7b, 0x88,
+ 0xcf, 0x26, 0x6b, 0x51, 0x06, 0x23, 0x68, 0x03,
+ 0xa1, 0xb7, 0xd3, 0x0c, 0xca, 0xbf, 0x29, 0x01,
+ 0xa9, 0x61, 0x34, 0x75, 0x98, 0x1e, 0x05, 0x59,
+ 0xb3, 0x46, 0x44, 0xff, 0x2b, 0x98, 0x04, 0x88,
+ 0x89, 0xfd, 0x7f, 0xd5, 0x19, 0x8a, 0xa6, 0xf3,
+ 0xd9, 0x44, 0xd5, 0xf9, 0x3a, 0x3c, 0xec, 0xd9,
+ 0x9b, 0x8c, 0x93, 0x93, 0x2b, 0x44, 0x86, 0x8b,
+ 0x80, 0x83, 0x23, 0x00, 0xdf, 0xaf, 0xff, 0x33,
+ 0x9b, 0x78, 0x70, 0x43, 0xf1, 0x55, 0x87, 0xb1,
+ 0xa1, 0xb3, 0x8e, 0x79, 0x02, 0x70, 0x82, 0x6c,
+ 0x0b, 0xc1, 0xef, 0x96, 0xf1, 0xef, 0xdd, 0xa2,
+ 0x69, 0x86, 0xc7, 0x85, 0x09, 0x7e, 0xf0, 0x2f,
+ 0x8e, 0xa0, 0x5f, 0xea, 0x39, 0x2e, 0x24, 0xf0,
+ 0x82, 0x30, 0x26, 0xa8, 0xa1, 0x4f, 0xc6, 0x5c,
+ 0xec, 0x94, 0x87, 0x52, 0x9b, 0x93, 0x92, 0xf3,
+ 0xa3, 0x1b, 0xc7, 0x8f, 0x9e, 0xb3, 0xbb, 0x32,
+ 0x2b, 0x17, 0x54, 0xf2, 0x06, 0x0c, 0x86, 0x92,
+ 0x0f, 0xb8, 0xe0, 0x27, 0x50, 0xaa, 0xeb, 0xf5,
+ 0x4e, 0x2b, 0x1b, 0xb2, 0x44, 0xe6, 0x58, 0x02,
+ 0xd7, 0x65, 0xdc, 0x31, 0x01, 0xec, 0xa6, 0xab,
+ 0xfa, 0xa8, 0x05, 0x00, 0x37, 0x60, 0x4f, 0xa1,
+ 0x3c, 0x4f, 0x7a, 0x9a, 0x10, 0x67, 0x95, 0xc2,
+ 0x5b, 0x25, 0xef, 0x76, 0x0e, 0xff, 0x2d, 0x15,
+ 0x7f, 0x23, 0x1c, 0x77, 0x56, 0x94, 0x4a, 0x18,
+ 0x98, 0xc6, 0xd8, 0xd2, 0x29, 0x44, 0x57, 0xb8,
+ 0x40, 0x3a, 0x93, 0x58, 0x45, 0x77, 0x36, 0x36,
+ 0x07, 0x35, 0x2a, 0xff, 0x00, 0x94, 0x5c, 0x80,
+ 0xe6, 0x7c, 0x1a, 0x92, 0xc1, 0x5f, 0x40, 0xc3,
+ 0xbc, 0xf8, 0xce, 0x05, 0x77, 0x39, 0x40, 0x17,
+ 0xcf, 0x63, 0x43, 0x77, 0x27, 0xce, 0x37, 0x86,
+ 0x46, 0x54, 0xd6, 0xce, 0x00, 0x30, 0x36, 0xae,
+ 0x9f, 0x24, 0x2b, 0x5a, 0xa0, 0xfe, 0xa3, 0x4b,
+ 0x2e, 0x7e, 0xf7, 0x54, 0xa9, 0xd8, 0xa5, 0x76,
+ 0x83, 0x7b, 0x63, 0x01, 0x1c, 0xd4, 0x56, 0x17,
+ 0x02, 0xdc, 0x4a, 0x89, 0x77, 0xda, 0x8f, 0x9e,
+ 0xba, 0xcb, 0x37, 0xe8, 0x19, 0x5d, 0x68, 0x38,
+ 0x8e, 0xbc, 0x6a, 0x05, 0x53, 0x0b, 0xc7, 0xc5,
+ 0x96, 0x84, 0x5a, 0xd9, 0x6d, 0x4c, 0x42, 0x31,
+ 0xd9, 0xf2, 0x99, 0x06, 0xf7, 0x0c, 0x99, 0xbe,
+ 0x49, 0x07, 0x77, 0xf0, 0x8b, 0x7c, 0x43, 0xfa,
+ 0xb2, 0xad, 0xb0, 0xfa, 0x87, 0x52, 0xba, 0xc9,
+ 0x94, 0x61, 0xdc, 0xcf, 0x16, 0xac, 0x0f, 0x4a,
+ 0xa3, 0x6b, 0x5b, 0x6e, 0x27, 0x86, 0x1f, 0xfe,
+ 0x4d, 0x28, 0x3a, 0xa5, 0x10, 0x98, 0x6d, 0xed,
+ 0x53, 0xf9, 0x73, 0xc6, 0xa5, 0xa8, 0xf7, 0x66,
+ 0xcf, 0x56, 0x3b, 0x61, 0xdf, 0xab, 0x83, 0x18,
+ 0xe8, 0x09, 0xee, 0x6a, 0xb7, 0xf5, 0xc9, 0x62,
+ 0x55, 0x2d, 0xc7, 0x0c, 0x0d, 0xa0, 0x22, 0xd8,
+ 0xd4, 0xd6, 0xb2, 0x12, 0x21, 0xd7, 0x73, 0x3e,
+ 0x41, 0xb0, 0x5c, 0xd4, 0xcf, 0x98, 0xf3, 0x70,
+ 0xe6, 0x08, 0xe6, 0x2a, 0x4f, 0x92, 0x85, 0xe8,
+ 0x74, 0xa8, 0x41, 0x5f, 0x0e, 0xfd, 0xf1, 0xf3,
+ 0xbe, 0x9b, 0x14, 0xfd, 0xc0, 0x73, 0x11, 0xff,
+ 0xa5, 0x5b, 0x06, 0x34, 0xc3, 0x5d, 0x28, 0x42,
+ 0x34, 0xfe, 0x8a, 0xa5, 0xbe, 0x72, 0x7a, 0xf7,
+ 0xfa, 0x25, 0x2b, 0x35, 0x5e, 0x98, 0x71, 0x50,
+ 0x2c, 0x35, 0x76, 0x53, 0x4e, 0x1a, 0x34, 0xa5,
+ 0x99, 0x09, 0xa2, 0xc6, 0xca, 0xa5, 0xce, 0x08,
+ 0x50, 0x45, 0xab, 0x8d, 0xfb, 0xe3, 0xb8, 0xe4,
+ 0x8a, 0x61, 0x48, 0x14, 0x6e, 0xf7, 0x58, 0x71,
+ 0xe5, 0x2e, 0xbc, 0x12, 0xd1, 0x25, 0xe9, 0x65,
+ 0x7a, 0xa1, 0x27, 0xbe, 0x3b, 0x8b, 0xe8, 0xe7,
+ 0xbc, 0x77, 0x05, 0xe7, 0x92, 0xeb, 0xb9, 0xdf,
+ 0x5d, 0x53, 0x74, 0xc0, 0x63, 0x97, 0x80, 0xb8,
+ 0x3c, 0xae, 0xf3, 0xf2, 0x09, 0x12, 0x81, 0x6c,
+ 0x69, 0x10, 0x6f, 0xf6, 0xbe, 0x03, 0x7b, 0x88,
+ 0xcf, 0x26, 0x6b, 0x51, 0x06, 0x23, 0x68, 0x03,
+ 0xa1, 0xb7, 0xd3, 0x0c, 0xca, 0xbf, 0x29, 0x01,
+ 0xa9, 0x61, 0x34, 0x75, 0x98, 0x1e, 0x6f, 0x59,
+ 0xb3, 0x46, 0x44, 0xff, 0x2b, 0x98, 0x04, 0x88,
+ 0x89, 0xfd, 0x1c, 0xd5, 0x19, 0x8a, 0xa6, 0xf3,
+ 0xd9, 0x44, 0xd5, 0xf9, 0x79, 0x26, 0x46, 0xf7
+ };
+ uint32_t request8_len = sizeof(request8);
+
+ uint8_t request9[] = {
+ 0x05, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xbf, 0xa1, 0x12, 0x73, 0x23, 0x44, 0x86, 0x8b,
+ 0x50, 0x6a, 0x40, 0x00
+ };
+ uint32_t request9_len = sizeof(request9);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* bind */
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START,
+ bind, bind_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 0) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+
+ /* bind_ack */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 0) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+
+ /* request1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 1024 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 2048 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 3072 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request4 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request4, request4_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 4096 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request5 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request5, request5_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 5120) &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL);
+ if (result == 0)
+ goto end;
+
+ /* request6 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request6, request6_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 6144 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request7 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request7, request7_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 7168 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request8 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request8, request8_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 8192 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request9 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request9, request9_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 8204 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh = 0;
+
+ /* request1 again */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ result &= ( (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 1024 &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh == 1) &&
+ (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) );
+ if (result == 0)
+ goto end;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+#endif
+ return 1;
+}
+
+/**
+ * \test General test.
+ */
+int DCERPCParserTest05(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+ uint8_t bind1[] = {
+ 0x05, 0x00, 0x0b, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xb8, 0x4a, 0x9f, 0x4d, 0x1c, 0x7d, 0xcf, 0x11,
+ 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind1_len = sizeof(bind1);
+
+ uint8_t bind2[] = {
+ 0x05, 0x00, 0x0b, 0x02, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0xb8, 0x4a, 0x9f, 0x4d, 0x1c, 0x7d, 0xcf, 0x11,
+ 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind2_len = sizeof(bind2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ bind1, bind1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ DCERPCUuidEntry *item = NULL;
+ int m = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.uuid_list, next) {
+ printf("%d ", m);
+ printUUID("BIND",item);
+ m++;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind2, bind2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ item = NULL;
+ m = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.uuid_list, next) {
+ printf("%d ", m);
+ printUUID("BIND",item);
+ m++;
+ }
+
+ /* we will need this test later for fragged bind pdus. keep it */
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented bind PDU(one PDU which is frag'ed)
+ */
+int DCERPCParserTest06(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+ uint8_t bind1[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xdc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xc7, 0x70, 0x0d, 0x3e, 0x71, 0x37, 0x39, 0x0d,
+ 0x3a, 0x4f, 0xd3, 0xdc, 0xca, 0x49, 0xe8, 0xa3,
+ 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x84, 0xb6, 0x55, 0x75,
+ 0xdb, 0x9e, 0xba, 0x54, 0x56, 0xd3, 0x45, 0x10,
+ 0xb7, 0x7a, 0x2a, 0xe2, 0x04, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x6e, 0x39, 0x21, 0x24, 0x70, 0x6f, 0x41, 0x57,
+ 0x54, 0x70, 0xb8, 0xc3, 0x5e, 0x89, 0x3b, 0x43,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0x39, 0x6a, 0x86, 0x5d,
+ 0x24, 0x0f, 0xd2, 0xf7, 0xb6, 0xce, 0x95, 0x9c,
+ 0x54, 0x1d, 0x3a, 0xdb, 0x02, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
+ 0x12, 0xa5, 0xdd, 0xc5, 0x55, 0xce, 0xc3, 0x46,
+ 0xbd, 0xa0, 0x94, 0x39, 0x3c, 0x0d, 0x9b, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x01, 0x00, 0x87, 0x1c, 0x8b, 0x6e,
+ 0x11, 0xa8, 0x67, 0x98, 0xd4, 0x5d, 0xf6, 0x8a,
+ 0x2f, 0x33, 0x24, 0x7b, 0x05, 0x00, 0x03, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0x9b, 0x82, 0x13, 0xd1, 0x28, 0xe0, 0x63, 0xf3,
+ 0x62, 0xee, 0x76, 0x73, 0xf9, 0xac, 0x3d, 0x2e,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x00, 0xa9, 0xd4, 0x73, 0xf2,
+ 0xed, 0xad, 0xe8, 0x82, 0xf8, 0xcf, 0x9d, 0x9f,
+ 0x66, 0xe6, 0x43, 0x37, 0x02, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x06, 0x2b, 0x85, 0x38, 0x4f, 0x73, 0x96, 0xb1,
+ 0x73, 0xe1, 0x59, 0xbe, 0x9d, 0xe2, 0x6c, 0x07,
+ 0x05, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60};
+ uint32_t bind1_len = sizeof(bind1);
+
+ uint8_t bind2[] = {
+ 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
+ 0xbf, 0xfa, 0xbb, 0xa4, 0x9e, 0x5c, 0x80, 0x61,
+ 0xb5, 0x8b, 0x79, 0x69, 0xa6, 0x32, 0x88, 0x77,
+ 0x01, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x01, 0x00, 0x39, 0xa8, 0x2c, 0x39,
+ 0x73, 0x50, 0x06, 0x8d, 0xf2, 0x37, 0x1e, 0x1e,
+ 0xa8, 0x8f, 0x46, 0x98, 0x02, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x01, 0x00,
+ 0x91, 0x13, 0xd0, 0xa7, 0xef, 0xc4, 0xa7, 0x96,
+ 0x0c, 0x4a, 0x0d, 0x29, 0x80, 0xd3, 0xfe, 0xbf,
+ 0x00, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x01, 0x00, 0xcc, 0x2b, 0x55, 0x1d,
+ 0xd4, 0xa4, 0x0d, 0xfb, 0xcb, 0x6f, 0x86, 0x36,
+ 0xa6, 0x57, 0xc3, 0x21, 0x02, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
+ 0x43, 0x7b, 0x07, 0xee, 0x85, 0xa8, 0xb9, 0x3a,
+ 0x0f, 0xf9, 0x83, 0x70, 0xe6, 0x0b, 0x4f, 0x33,
+ 0x02, 0x00, 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x01, 0x00, 0x9c, 0x6a, 0x15, 0x8c,
+ 0xd6, 0x9c, 0xa6, 0xc3, 0xb2, 0x9e, 0x62, 0x9f,
+ 0x3d, 0x8e, 0x47, 0x73, 0x02, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x00,
+ 0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01,
+ 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind2_len = sizeof(bind2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START,
+ bind1, bind1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 420);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.ctxbytesprocessed == 40);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.numctxitems == 16);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.numctxitemsleft == 8);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind2, bind2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.ctxbytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.numctxitems == 16);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.numctxitemsleft == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented bind PDU(one PDU which is frag'ed).
+ */
+int DCERPCParserTest07(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x0D, 0x0E
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t request3[] = {
+ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14
+ };
+ uint32_t request3_len = sizeof(request3);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 36);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 12);
+ result &= (dcerpc_state->dcerpc.pdu_fragged = 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 38);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 14);
+ result &= (dcerpc_state->dcerpc.pdu_fragged = 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 20);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented bind PDU(one PDU which is frag'ed).
+ */
+int DCERPCParserTest08(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+ uint8_t request[] = {
+ 0x05, 0x02, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C,
+ };
+ uint32_t request_len = sizeof(request);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ request, request_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer == NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 0);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented bind PDU(one PDU which is frag'ed).
+ */
+int DCERPCParserTest09(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+ uint8_t request[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C,
+ };
+ uint32_t request_len = sizeof(request);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ request, request_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 36);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 12);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 1);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented PDU.
+ */
+int DCERPCParserTest10(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t fault[] = {
+ 0x05, 0x00, 0x03, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t fault_len = sizeof(fault);
+
+ uint8_t request1[] = {
+ 0x05, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER|STREAM_START,
+ fault, fault_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 2);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer == NULL);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 12);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented PDU.
+ */
+int DCERPCParserTest11(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t request3[] = {
+ 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x26, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0xFF, 0xFF
+ };
+ uint32_t request3_len = sizeof(request3);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 12);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 2);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 14);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented PDU.
+ */
+int DCERPCParserTest12(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind_ack1[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ };
+ uint32_t bind_ack1_len = sizeof(bind_ack1);
+
+ uint8_t bind_ack2[] = {
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack2_len = sizeof(bind_ack2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack1, bind_ack1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 24);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack2, bind_ack2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Check if the parser accepts bind pdus that have context ids starting
+ * from a non-zero value.
+ */
+int DCERPCParserTest13(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind, bind_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcbindbindack.numctxitems == 1);
+ if (result == 0)
+ goto end;
+
+ result = 0;
+ uint8_t ctx_uuid_from_pcap[16] = {
+ 0x00, 0x00, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
+ DCERPCUuidEntry *item = NULL;
+ int internal_id = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (ctx_uuid_from_pcap[i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ result = 1;
+ result &= (item->internal_id == internal_id++);
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Check for another endless loop with bind pdus.
+ */
+int DCERPCParserTest14(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x4A, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0xFF /* ka boom - endless loop */
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind, bind_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Check for another endless loop for bind_ack pdus.
+ */
+int DCERPCParserTest15(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0xfd, 0x04, 0x01, 0x00,
+ 0x04, 0x00, 0x31, 0x33, 0x35, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x01, 0x02, 0x03, 0x04, 0xFF
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Check for correct internal ids for bind_acks.
+ */
+int DCERPCParserTest16(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind1[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x50, 0x08, 0x43, 0x95, 0x43, 0x5a, 0x8b, 0xb2,
+ 0xf4, 0xc5, 0xb9, 0xee, 0x67, 0x55, 0x7c, 0x19,
+ 0x00, 0x00, 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0xda, 0xc2, 0xbc, 0x9b,
+ 0x35, 0x2e, 0xd4, 0xc9, 0x1f, 0x85, 0x01, 0xe6,
+ 0x4e, 0x5a, 0x5e, 0xd4, 0x04, 0x00, 0x03, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0xb2, 0x97, 0xcc, 0x14, 0x6f, 0x70, 0x0d, 0xa5,
+ 0x33, 0xd7, 0xf4, 0xe3, 0x8e, 0xb2, 0x2a, 0x1e,
+ 0x05, 0x00, 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0x96, 0x4e, 0xa6, 0xf6,
+ 0xb2, 0x4b, 0xae, 0xb3, 0x21, 0xf4, 0x97, 0x7c,
+ 0xcd, 0xa7, 0x08, 0xb0, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
+ 0xbc, 0xc0, 0xf7, 0x71, 0x3f, 0x71, 0x54, 0x44,
+ 0x22, 0xa8, 0x55, 0x0f, 0x98, 0x83, 0x1f, 0xfe,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x01, 0x00, 0xbe, 0x52, 0xf2, 0x58,
+ 0x4a, 0xc3, 0xb5, 0xd0, 0xba, 0xac, 0xda, 0xf0,
+ 0x12, 0x99, 0x38, 0x6e, 0x04, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0xdb, 0xfa, 0x73, 0x01, 0xb3, 0x81, 0x01, 0xd4,
+ 0x7f, 0xa0, 0x36, 0xb1, 0x97, 0xae, 0x29, 0x7f,
+ 0x01, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x00, 0x89, 0xbe, 0x41, 0x1d,
+ 0x38, 0x75, 0xf5, 0xb5, 0xad, 0x27, 0x73, 0xf1,
+ 0xb0, 0x7a, 0x28, 0x82, 0x05, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0xf6, 0x87, 0x09, 0x93, 0xb8, 0xa8, 0x20, 0xc4,
+ 0xb8, 0x63, 0xe6, 0x95, 0xed, 0x59, 0xee, 0x3f,
+ 0x05, 0x00, 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x01, 0x00, 0x92, 0x77, 0x92, 0x68,
+ 0x3e, 0xa4, 0xbc, 0x3f, 0x44, 0x33, 0x0e, 0xb8,
+ 0x33, 0x0a, 0x2f, 0xdf, 0x01, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00,
+ 0xa1, 0x03, 0xd2, 0xa9, 0xd2, 0x16, 0xc9, 0x89,
+ 0x67, 0x18, 0x3e, 0xb1, 0xee, 0x6b, 0xf9, 0x18,
+ 0x02, 0x00, 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x01, 0x00, 0x2f, 0x09, 0x5e, 0x74,
+ 0xec, 0xa0, 0xbb, 0xc1, 0x60, 0x18, 0xf1, 0x93,
+ 0x04, 0x17, 0x11, 0xf9, 0x01, 0x00, 0x03, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
+ 0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01,
+ 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind1_len = sizeof(bind1);
+
+ uint8_t bind_ack1[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0xc1, 0x2b, 0x00, 0x00,
+ 0x0e, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack1_len = sizeof(bind_ack1);
+
+ uint8_t bind2[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xdc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xc7, 0x70, 0x0d, 0x3e, 0x71, 0x37, 0x39, 0x0d,
+ 0x3a, 0x4f, 0xd3, 0xdc, 0xca, 0x49, 0xe8, 0xa3,
+ 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x84, 0xb6, 0x55, 0x75,
+ 0xdb, 0x9e, 0xba, 0x54, 0x56, 0xd3, 0x45, 0x10,
+ 0xb7, 0x7a, 0x2a, 0xe2, 0x04, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x6e, 0x39, 0x21, 0x24, 0x70, 0x6f, 0x41, 0x57,
+ 0x54, 0x70, 0xb8, 0xc3, 0x5e, 0x89, 0x3b, 0x43,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0x39, 0x6a, 0x86, 0x5d,
+ 0x24, 0x0f, 0xd2, 0xf7, 0xb6, 0xce, 0x95, 0x9c,
+ 0x54, 0x1d, 0x3a, 0xdb, 0x02, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
+ 0x12, 0xa5, 0xdd, 0xc5, 0x55, 0xce, 0xc3, 0x46,
+ 0xbd, 0xa0, 0x94, 0x39, 0x3c, 0x0d, 0x9b, 0x5b,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x01, 0x00, 0x87, 0x1c, 0x8b, 0x6e,
+ 0x11, 0xa8, 0x67, 0x98, 0xd4, 0x5d, 0xf6, 0x8a,
+ 0x2f, 0x33, 0x24, 0x7b, 0x05, 0x00, 0x03, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0x9b, 0x82, 0x13, 0xd1, 0x28, 0xe0, 0x63, 0xf3,
+ 0x62, 0xee, 0x76, 0x73, 0xf9, 0xac, 0x3d, 0x2e,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x00, 0xa9, 0xd4, 0x73, 0xf2,
+ 0xed, 0xad, 0xe8, 0x82, 0xf8, 0xcf, 0x9d, 0x9f,
+ 0x66, 0xe6, 0x43, 0x37, 0x02, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x06, 0x2b, 0x85, 0x38, 0x4f, 0x73, 0x96, 0xb1,
+ 0x73, 0xe1, 0x59, 0xbe, 0x9d, 0xe2, 0x6c, 0x07,
+ 0x05, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x01, 0x00, 0xbf, 0xfa, 0xbb, 0xa4,
+ 0x9e, 0x5c, 0x80, 0x61, 0xb5, 0x8b, 0x79, 0x69,
+ 0xa6, 0x32, 0x88, 0x77, 0x01, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00,
+ 0x39, 0xa8, 0x2c, 0x39, 0x73, 0x50, 0x06, 0x8d,
+ 0xf2, 0x37, 0x1e, 0x1e, 0xa8, 0x8f, 0x46, 0x98,
+ 0x02, 0x00, 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x01, 0x00, 0x91, 0x13, 0xd0, 0xa7,
+ 0xef, 0xc4, 0xa7, 0x96, 0x0c, 0x4a, 0x0d, 0x29,
+ 0x80, 0xd3, 0xfe, 0xbf, 0x00, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
+ 0xcc, 0x2b, 0x55, 0x1d, 0xd4, 0xa4, 0x0d, 0xfb,
+ 0xcb, 0x6f, 0x86, 0x36, 0xa6, 0x57, 0xc3, 0x21,
+ 0x02, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x01, 0x00, 0x43, 0x7b, 0x07, 0xee,
+ 0x85, 0xa8, 0xb9, 0x3a, 0x0f, 0xf9, 0x83, 0x70,
+ 0xe6, 0x0b, 0x4f, 0x33, 0x02, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00,
+ 0x9c, 0x6a, 0x15, 0x8c, 0xd6, 0x9c, 0xa6, 0xc3,
+ 0xb2, 0x9e, 0x62, 0x9f, 0x3d, 0x8e, 0x47, 0x73,
+ 0x02, 0x00, 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x01, 0x00, 0xc8, 0x4f, 0x32, 0x4b,
+ 0x70, 0x16, 0xd3, 0x01, 0x12, 0x78, 0x5a, 0x47,
+ 0xbf, 0x6e, 0xe1, 0x88, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind2_len = sizeof(bind2);
+
+ uint8_t bind_ack2[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xac, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0xc2, 0x2b, 0x00, 0x00,
+ 0x0e, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack2_len = sizeof(bind_ack2);
+
+ uint8_t bind3[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x2c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xa4, 0x7f, 0x8e, 0xc6, 0xef, 0x56, 0x9b, 0x63,
+ 0x92, 0xfa, 0x08, 0xb3, 0x35, 0xe2, 0xa5, 0x81,
+ 0x00, 0x00, 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x9f, 0xfc, 0x78, 0xd2,
+ 0x5f, 0x16, 0x0b, 0xbc, 0xc6, 0xdb, 0x5d, 0xef,
+ 0xde, 0x54, 0xa2, 0x6f, 0x04, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x78, 0xb8, 0x96, 0xc7, 0x2f, 0xda, 0x11, 0x6b,
+ 0xd1, 0x28, 0x68, 0xe1, 0xd6, 0x71, 0xac, 0x9d,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0xcf, 0xf4, 0xd7, 0x37,
+ 0x03, 0xda, 0xcc, 0xe3, 0x3e, 0x34, 0x7f, 0x67,
+ 0x99, 0x91, 0x41, 0x3d, 0x01, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
+ 0x48, 0xeb, 0x32, 0xf0, 0x27, 0xd5, 0x9d, 0xd0,
+ 0x1e, 0xc6, 0x48, 0x46, 0x97, 0xe9, 0xdb, 0x09,
+ 0x05, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x01, 0x00, 0x82, 0xec, 0x0d, 0x08,
+ 0xf2, 0x8f, 0x22, 0x57, 0x42, 0x9b, 0xce, 0xa8,
+ 0x74, 0x16, 0xc6, 0xec, 0x00, 0x00, 0x01, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0x2e, 0x00, 0x70, 0x44, 0xee, 0xc9, 0x30, 0x6b,
+ 0xf4, 0x34, 0x1e, 0x3d, 0x35, 0x0f, 0xf7, 0xf7,
+ 0x00, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x00, 0x59, 0x04, 0x39, 0x3f,
+ 0x59, 0x87, 0x14, 0x0e, 0x76, 0x8d, 0x17, 0xc2,
+ 0x47, 0xfa, 0x67, 0x7f, 0x04, 0x00, 0x02, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x30, 0xd6, 0xed, 0x2e, 0x57, 0xfa, 0xf4, 0x72,
+ 0x6c, 0x10, 0x0d, 0xe5, 0x51, 0x7f, 0xd0, 0x39,
+ 0x02, 0x00, 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x01, 0x00, 0xea, 0x8b, 0x84, 0x4d,
+ 0x44, 0x43, 0xc1, 0x94, 0x75, 0xe2, 0x81, 0x48,
+ 0xd8, 0x77, 0xd9, 0xce, 0x05, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00,
+ 0x89, 0x4f, 0xe7, 0x95, 0xa3, 0xc1, 0x62, 0x36,
+ 0x26, 0x9e, 0x67, 0xdb, 0x2c, 0x52, 0x89, 0xd3,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x01, 0x00, 0x78, 0x56, 0x34, 0x12,
+ 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23,
+ 0x45, 0x67, 0x89, 0xab, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind3_len = sizeof(bind3);
+
+ uint8_t bind_ack3[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x1a, 0x33, 0x00, 0x00,
+ 0x0e, 0x00, 0x5c, 0x70, 0x69, 0x70, 0x65, 0x5c,
+ 0x73, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x73, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack3_len = sizeof(bind_ack3);
+
+ TcpSession ssn;
+ DCERPCUuidEntry *item = NULL;
+ int count = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ uint8_t accepted_uuids[3][16] = {
+ {0x4b, 0x32, 0x4f, 0xc8, 0x16, 0x70, 0x01, 0xd3,
+ 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88},
+ {0x4b, 0x32, 0x4f, 0xc8, 0x16, 0x70, 0x01, 0xd3,
+ 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88},
+ {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0xab, 0xcd,
+ 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab}
+ };
+
+ uint16_t accepted_ctxids[3] = {12, 15, 11};
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind1, bind1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack1, bind_ack1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (accepted_uuids[0][i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ if (accepted_ctxids[0] != item->ctxid) {
+ result = 0;
+ goto end;
+ }
+ count++;
+ }
+ if (count != 1) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind2, bind2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ count++;
+ }
+ if (count != 0) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack2, bind_ack2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (accepted_uuids[1][i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ if (accepted_ctxids[1] != item->ctxid) {
+ result = 0;
+ goto end;
+ }
+ count++;
+ }
+ if (count != 1) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind3, bind3_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ count++;
+ }
+ if (count != 0) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack3, bind_ack3_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (accepted_uuids[2][i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ if (accepted_ctxids[2] != item->ctxid) {
+ result = 0;
+ goto end;
+ }
+ count++;
+ }
+ if (count != 1) {
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+
+/**
+ * \test Check for correct internal ids for bind_acks + alter_contexts
+ */
+int DCERPCParserTest17(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x40, 0xfd, 0x2c, 0x34, 0x6c, 0x3c, 0xce, 0x11,
+ 0xa8, 0x93, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x7d, 0xd8, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x70, 0x69, 0x70, 0x65, 0x5c,
+ 0x6c, 0x6c, 0x73, 0x72, 0x70, 0x63, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t alter_context[] = {
+ 0x05, 0x00, 0x0e, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0xd0, 0x4c, 0x67, 0x57, 0x00, 0x52, 0xce, 0x11,
+ 0xa8, 0x97, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t alter_context_len = sizeof(alter_context);
+
+ uint8_t alter_context_resp[] = {
+ 0x05, 0x00, 0x0f, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x7d, 0xd8, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t alter_context_resp_len = sizeof(alter_context_resp);
+
+
+ TcpSession ssn;
+ DCERPCUuidEntry *item = NULL;
+ int count = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ uint8_t accepted_uuids[2][16] = {
+ {0x57, 0x67, 0x4c, 0xd0, 0x52, 0x00, 0x11, 0xce,
+ 0xa8, 0x97, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d},
+ {0x34, 0x2c, 0xfd, 0x40, 0x3c, 0x6c, 0x11, 0xce,
+ 0xa8, 0x93, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d},
+ };
+
+ uint16_t accepted_ctxids[2] = {1, 0};
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ bind, bind_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (accepted_uuids[1][i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ if (accepted_ctxids[1] != item->ctxid) {
+ result = 0;
+ goto end;
+ }
+ count++;
+ }
+ if (count != 1) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ alter_context, alter_context_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ count++;
+ }
+ if (count != 1) {
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ alter_context_resp, alter_context_resp_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ count = 0;
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ int i = 0;
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (accepted_uuids[count][i] != item->uuid[i]) {
+ result = 0;
+ goto end;
+ }
+ }
+ if (accepted_ctxids[count] != item->ctxid) {
+ result = 0;
+ goto end;
+ }
+ count++;
+ }
+ if (count != 2) {
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test DCERPC fragmented PDU.
+ */
+int DCERPCParserTest18(void)
+{
+ int result = 1;
+ Flow f;
+ int r = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00,
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0xFF, 0xFF
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ result = 0;
+ goto end;
+ }
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 18);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer == NULL);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 1);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result &= (dcerpc_state->dcerpc.bytesprocessed == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len == 14);
+ result &= (dcerpc_state->dcerpc.pdu_fragged == 0);
+ result &= (dcerpc_state->dcerpc.dcerpcrequest.opnum == 2);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+int DCERPCParserTest19(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t dcerpcbind[] = {
+ 0x05, 0x00,
+ 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x3c, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x16,
+ 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0xd0,
+ 0x28, 0xda, 0x76, 0x91, 0xf6, 0x6e, 0xcb, 0x0f,
+ 0xbf, 0x85, 0xcd, 0x9b, 0xf6, 0x39, 0x01, 0x00,
+ 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x2c, 0x75, 0xce, 0x7e, 0x82, 0x3b,
+ 0x06, 0xac, 0x1b, 0xf0, 0xf5, 0xb7, 0xa7, 0xf7,
+ 0x28, 0xaf, 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0xe3, 0xb2,
+ 0x10, 0xd1, 0xd0, 0x0c, 0xcc, 0x3d, 0x2f, 0x80,
+ 0x20, 0x7c, 0xef, 0xe7, 0x09, 0xe0, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0xde, 0x85, 0x70, 0xc4, 0x02, 0x7c,
+ 0x60, 0x23, 0x67, 0x0c, 0x22, 0xbf, 0x18, 0x36,
+ 0x79, 0x17, 0x01, 0x00, 0x02, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x41, 0x65,
+ 0x29, 0x51, 0xaa, 0xe7, 0x7b, 0xa8, 0xf2, 0x37,
+ 0x0b, 0xd0, 0x3f, 0xb3, 0x36, 0xed, 0x05, 0x00,
+ 0x01, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x01, 0x00, 0x14, 0x96, 0x80, 0x01, 0x2e, 0x78,
+ 0xfb, 0x5d, 0xb4, 0x3c, 0x14, 0xb3, 0x3d, 0xaa,
+ 0x02, 0xfb, 0x06, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x3b, 0x04,
+ 0x68, 0x3e, 0x63, 0xfe, 0x9f, 0xd8, 0x64, 0x55,
+ 0xcd, 0xe7, 0x39, 0xaf, 0x98, 0x9f, 0x03, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x01, 0x00, 0x16, 0x7a, 0x4f, 0x1b, 0xdb, 0x25,
+ 0x92, 0x55, 0xdd, 0xae, 0x9e, 0x5b, 0x3e, 0x93,
+ 0x66, 0x93, 0x04, 0x00, 0x01, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xe8, 0xa4,
+ 0x8a, 0xcf, 0x95, 0x6c, 0xc7, 0x8f, 0x14, 0xcc,
+ 0x56, 0xfc, 0x7b, 0x5f, 0x4f, 0xe8, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x01, 0x00, 0xd8, 0xda, 0xfb, 0xbc, 0xa2, 0x55,
+ 0x6f, 0x5d, 0xc0, 0x2d, 0x88, 0x6f, 0x00, 0x17,
+ 0x52, 0x8d, 0x06, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x3f, 0x17,
+ 0x55, 0x0c, 0xf4, 0x23, 0x3c, 0xca, 0xe6, 0xa0,
+ 0xaa, 0xcc, 0xb5, 0xe3, 0xf9, 0xce, 0x04, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00,
+ 0x01, 0x00, 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1,
+ 0xd0, 0x11, 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9,
+ 0x2e, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0xc9, 0x9f,
+ 0x3e, 0x6e, 0x82, 0x0a, 0x2b, 0x28, 0x37, 0x78,
+ 0xe1, 0x13, 0x70, 0x05, 0x38, 0x4d, 0x01, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x01, 0x00, 0x11, 0xaa, 0x4b, 0x15, 0xdf, 0xa6,
+ 0x86, 0x3f, 0xfb, 0xe0, 0x09, 0xb7, 0xf8, 0x56,
+ 0xd2, 0x3f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00, 0xee, 0x99,
+ 0xc4, 0x25, 0x11, 0xe4, 0x95, 0x62, 0x29, 0xfa,
+ 0xfd, 0x26, 0x57, 0x02, 0xf1, 0xce, 0x03, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0xba, 0x81, 0x9e, 0x1a, 0xdf, 0x2b,
+ 0xba, 0xe4, 0xd3, 0x17, 0x41, 0x60, 0x6d, 0x2d,
+ 0x9e, 0x28, 0x03, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0xa0, 0x24,
+ 0x03, 0x9a, 0xa9, 0x99, 0xfb, 0xbe, 0x49, 0x11,
+ 0xad, 0x77, 0x30, 0xaa, 0xbc, 0xb6, 0x02, 0x00,
+ 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00,
+ 0x01, 0x00, 0x32, 0x04, 0x7e, 0xae, 0xec, 0x28,
+ 0xd1, 0x55, 0x83, 0x4e, 0xc3, 0x47, 0x5d, 0x1d,
+ 0xc6, 0x65, 0x02, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x12, 0x00, 0x01, 0x00, 0xc6, 0xa4,
+ 0x81, 0x48, 0x66, 0x2a, 0x74, 0x7d, 0x56, 0x6e,
+ 0xc5, 0x1d, 0x19, 0xf2, 0xb5, 0xb6, 0x03, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00,
+ 0x01, 0x00, 0xcb, 0xae, 0xb3, 0xc0, 0x0c, 0xf4,
+ 0xa4, 0x5e, 0x91, 0x72, 0xdd, 0x53, 0x24, 0x70,
+ 0x89, 0x02, 0x05, 0x00, 0x03, 0x00, 0x04, 0x5d,
+ 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8,
+ 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
+ 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0xb8, 0xd0,
+ 0xa0, 0x1a, 0x5e, 0x7a, 0x2d, 0xfe, 0x35, 0xc6,
+ 0x7d, 0x08, 0x0d, 0x33, 0x73, 0x18, 0x02, 0x00,
+ 0x02, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ };
+
+ uint8_t dcerpcbindack[] = {
+ 0x05, 0x00, 0x0c, 0x03,
+ 0x10, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb8, 0x10, 0xb8, 0x10,
+ 0xce, 0x47, 0x00, 0x00, 0x0c, 0x00, 0x5c, 0x50,
+ 0x49, 0x50, 0x45, 0x5c, 0x6c, 0x73, 0x61, 0x73,
+ 0x73, 0x00, 0xf6, 0x6e, 0x18, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ uint32_t bindlen = sizeof(dcerpcbind);
+ uint32_t bindacklen = sizeof(dcerpcbindack);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START, dcerpcbind, bindlen);
+ if (r != 0) {
+ printf("dcerpc header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ DCERPCState *dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.bytesprocessed == 0) {
+ printf("request - dce parser bytesprocessed should not be 0.\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpcbindack, bindacklen);
+ if (r == 0) {
+ printf("dce parser didn't return fail\n");
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DCERPCParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DCERPCParserTest01", DCERPCParserTest01, 1);
+ UtRegisterTest("DCERPCParserTest02", DCERPCParserTest02, 1);
+ UtRegisterTest("DCERPCParserTest03", DCERPCParserTest03, 1);
+ UtRegisterTest("DCERPCParserTest04", DCERPCParserTest04, 1);
+ UtRegisterTest("DCERPCParserTest05", DCERPCParserTest05, 1);
+ UtRegisterTest("DCERPCParserTest06", DCERPCParserTest06, 1);
+ UtRegisterTest("DCERPCParserTest07", DCERPCParserTest07, 1);
+ UtRegisterTest("DCERPCParserTest08", DCERPCParserTest08, 1);
+ UtRegisterTest("DCERPCParserTest09", DCERPCParserTest09, 1);
+ UtRegisterTest("DCERPCParserTest10", DCERPCParserTest10, 1);
+ UtRegisterTest("DCERPCParserTest11", DCERPCParserTest11, 1);
+ UtRegisterTest("DCERPCParserTest12", DCERPCParserTest12, 1);
+ UtRegisterTest("DCERPCParserTest13", DCERPCParserTest13, 1);
+ UtRegisterTest("DCERPCParserTest14", DCERPCParserTest14, 1);
+ UtRegisterTest("DCERPCParserTest15", DCERPCParserTest15, 1);
+ UtRegisterTest("DCERPCParserTest16", DCERPCParserTest16, 1);
+ UtRegisterTest("DCERPCParserTest17", DCERPCParserTest17, 1);
+ UtRegisterTest("DCERPCParserTest18", DCERPCParserTest18, 1);
+ UtRegisterTest("DCERPCParserTest19", DCERPCParserTest19, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/app-layer-dcerpc.h b/framework/src/suricata/src/app-layer-dcerpc.h
new file mode 100644
index 00000000..4781f0d1
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dcerpc.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+
+#ifndef __APP_LAYER_DCERPC_H__
+#define __APP_LAYER_DCERPC_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-dcerpc-common.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+typedef struct DCERPCState_ {
+ DCERPC dcerpc;
+ uint8_t data_needed_for_dir;
+} DCERPCState;
+
+void RegisterDCERPCParsers(void);
+void DCERPCParserTests(void);
+void DCERPCParserRegisterTests(void);
+
+#endif /* __APP_LAYER_DCERPC_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-detect-proto.c b/framework/src/suricata/src/app-layer-detect-proto.c
new file mode 100644
index 00000000..ed029e5e
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-detect-proto.c
@@ -0,0 +1,3776 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "detect.h"
+#include "detect-engine-port.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-content.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "flow.h"
+#include "flow-util.h"
+#include "flow-private.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-detect-proto.h"
+
+#include "conf.h"
+#include "util-memcmp.h"
+#include "util-spm.h"
+#include "util-cuda.h"
+#include "util-debug.h"
+
+#include "runmodes.h"
+
+typedef struct AppLayerProtoDetectProbingParserElement_ {
+ AppProto alproto;
+ /* \todo don't really need it. See if you can get rid of it */
+ uint16_t port;
+ /* \todo calculate at runtime and get rid of this var */
+ uint32_t alproto_mask;
+ /* \todo check if we can reduce the bottom 2 vars to uint16_t */
+ /* the min length of data that has to be supplied to invoke the parser */
+ uint32_t min_depth;
+ /* the max length of data after which this parser won't be invoked */
+ uint32_t max_depth;
+ /* the probing parser function */
+ ProbingParserFPtr ProbingParser;
+
+ struct AppLayerProtoDetectProbingParserElement_ *next;
+} AppLayerProtoDetectProbingParserElement;
+
+typedef struct AppLayerProtoDetectProbingParserPort_ {
+ /* the port no for which probing parser(s) are invoked */
+ uint16_t port;
+
+ uint32_t alproto_mask;
+
+ /* the max depth for all the probing parsers registered for this port */
+ uint16_t dp_max_depth;
+ uint16_t sp_max_depth;
+
+ AppLayerProtoDetectProbingParserElement *dp;
+ AppLayerProtoDetectProbingParserElement *sp;
+
+ struct AppLayerProtoDetectProbingParserPort_ *next;
+} AppLayerProtoDetectProbingParserPort;
+
+typedef struct AppLayerProtoDetectProbingParser_ {
+ uint8_t ipproto;
+ AppLayerProtoDetectProbingParserPort *port;
+
+ struct AppLayerProtoDetectProbingParser_ *next;
+} AppLayerProtoDetectProbingParser;
+
+typedef struct AppLayerProtoDetectPMSignature_ {
+ AppProto alproto;
+ /* \todo Change this into a non-pointer */
+ DetectContentData *cd;
+ struct AppLayerProtoDetectPMSignature_ *next;
+} AppLayerProtoDetectPMSignature;
+
+typedef struct AppLayerProtoDetectPMCtx_ {
+ uint16_t max_len;
+ uint16_t min_len;
+ MpmCtx mpm_ctx;
+
+ /** Mapping between pattern id and signature. As each signature has a
+ * unique pattern with a unique id, we can lookup the signature by
+ * the pattern id. */
+ AppLayerProtoDetectPMSignature **map;
+ AppLayerProtoDetectPMSignature *head;
+
+ /* \todo we don't need this except at setup time. Get rid of it. */
+ PatIntId max_pat_id;
+} AppLayerProtoDetectPMCtx;
+
+typedef struct AppLayerProtoDetectCtxIpproto_ {
+ /* 0 - toserver, 1 - toclient */
+ AppLayerProtoDetectPMCtx ctx_pm[2];
+} AppLayerProtoDetectCtxIpproto;
+
+/**
+ * \brief The app layer protocol detection context.
+ */
+typedef struct AppLayerProtoDetectCtx_ {
+ /* Context per ip_proto.
+ * \todo Modify ctx_ipp to hold for only tcp and udp. The rest can be
+ * implemented if needed. Waste of space otherwise. */
+ AppLayerProtoDetectCtxIpproto ctx_ipp[FLOW_PROTO_DEFAULT];
+
+ AppLayerProtoDetectProbingParser *ctx_pp;
+
+ /* Indicates the protocols that have registered themselves
+ * for protocol detection. This table is independent of the
+ * ipproto. */
+ char *alproto_names[ALPROTO_MAX];
+} AppLayerProtoDetectCtx;
+
+/**
+ * \brief The app layer protocol detection thread context.
+ */
+struct AppLayerProtoDetectThreadCtx_ {
+ PatternMatcherQueue pmq;
+ /* The value 2 is for direction(0 - toserver, 1 - toclient). */
+ MpmThreadCtx mpm_tctx[FLOW_PROTO_DEFAULT][2];
+};
+
+/* The global app layer proto detection context. */
+static AppLayerProtoDetectCtx alpd_ctx;
+
+/***** Static Internal Calls: Protocol Retrieval *****/
+
+/** \internal
+ * \brief Handle SPM search for Signature */
+static AppProto AppLayerProtoDetectPMMatchSignature(const AppLayerProtoDetectPMSignature *s,
+ uint8_t *buf, uint16_t buflen,
+ uint8_t ipproto)
+{
+ SCEnter();
+ AppProto proto = ALPROTO_UNKNOWN;
+ uint8_t *found = NULL;
+
+ if (s->cd->offset > buflen) {
+ SCLogDebug("s->co->offset (%"PRIu16") > buflen (%"PRIu16")",
+ s->cd->offset, buflen);
+ goto end;
+ }
+
+ if (s->cd->depth > buflen) {
+ SCLogDebug("s->co->depth (%"PRIu16") > buflen (%"PRIu16")",
+ s->cd->depth, buflen);
+ goto end;
+ }
+
+ uint8_t *sbuf = buf + s->cd->offset;
+ uint16_t sbuflen = s->cd->depth - s->cd->offset;
+ SCLogDebug("s->co->offset (%"PRIu16") s->cd->depth (%"PRIu16")",
+ s->cd->offset, s->cd->depth);
+
+ if (s->cd->flags & DETECT_CONTENT_NOCASE)
+ found = BoyerMooreNocase(s->cd->content, s->cd->content_len, sbuf, sbuflen, s->cd->bm_ctx);
+ else
+ found = BoyerMoore(s->cd->content, s->cd->content_len, sbuf, sbuflen, s->cd->bm_ctx);
+ if (found != NULL)
+ proto = s->alproto;
+
+ end:
+ SCReturnUInt(proto);
+}
+
+/** \internal
+ * \brief Run Pattern Sigs against buffer
+ * \param pm_results[out] AppProto array of size ALPROTO_MAX */
+static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx,
+ Flow *f,
+ uint8_t *buf, uint16_t buflen,
+ uint8_t direction,
+ uint8_t ipproto,
+ AppProto *pm_results)
+{
+ SCEnter();
+
+ pm_results[0] = ALPROTO_UNKNOWN;
+
+ AppLayerProtoDetectPMCtx *pm_ctx;
+ MpmThreadCtx *mpm_tctx;
+ uint16_t pm_matches = 0;
+ uint8_t cnt;
+ uint16_t searchlen;
+
+ if (f->protomap >= FLOW_PROTO_DEFAULT)
+ return ALPROTO_UNKNOWN;
+
+ if (direction & STREAM_TOSERVER) {
+ pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0];
+ mpm_tctx = &tctx->mpm_tctx[f->protomap][0];
+ } else {
+ pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1];
+ mpm_tctx = &tctx->mpm_tctx[f->protomap][1];
+ }
+ if (pm_ctx->mpm_ctx.pattern_cnt == 0)
+ goto end;
+
+ searchlen = buflen;
+ if (searchlen > pm_ctx->max_len)
+ searchlen = pm_ctx->max_len;
+
+ uint32_t search_cnt = 0;
+
+ /* do the mpm search */
+ search_cnt = mpm_table[pm_ctx->mpm_ctx.mpm_type].Search(&pm_ctx->mpm_ctx,
+ mpm_tctx,
+ &tctx->pmq,
+ buf, searchlen);
+ if (search_cnt == 0)
+ goto end;
+
+ /* alproto bit field */
+ uint8_t pm_results_bf[(ALPROTO_MAX / 8) + 1];
+ memset(pm_results_bf, 0, sizeof(pm_results_bf));
+
+ /* loop through unique pattern id's. Can't use search_cnt here,
+ * as that contains all matches, tctx->pmq.pattern_id_array_cnt
+ * contains only *unique* matches. */
+ for (cnt = 0; cnt < tctx->pmq.pattern_id_array_cnt; cnt++) {
+ const AppLayerProtoDetectPMSignature *s = pm_ctx->map[tctx->pmq.pattern_id_array[cnt]];
+ while (s != NULL) {
+ AppProto proto = AppLayerProtoDetectPMMatchSignature(s,
+ buf, searchlen, ipproto);
+
+ /* store each unique proto once */
+ if (proto != ALPROTO_UNKNOWN &&
+ !(pm_results_bf[proto / 8] & (1 << (proto % 8))) )
+ {
+ pm_results[pm_matches++] = proto;
+ pm_results_bf[proto / 8] |= 1 << (proto % 8);
+ }
+ s = s->next;
+ }
+ }
+
+ end:
+ PmqReset(&tctx->pmq);
+ if (buflen >= pm_ctx->max_len)
+ FLOW_SET_PM_DONE(f, direction);
+ SCReturnUInt(pm_matches);
+}
+
+static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectGetProbingParsers(AppLayerProtoDetectProbingParser *pp,
+ uint8_t ipproto,
+ uint16_t port)
+{
+ AppLayerProtoDetectProbingParserPort *pp_port = NULL;
+
+ while (pp != NULL) {
+ if (pp->ipproto == ipproto)
+ break;
+
+ pp = pp->next;
+ }
+
+ if (pp == NULL)
+ goto end;
+
+ pp_port = pp->port;
+ while (pp_port != NULL) {
+ if (pp_port->port == port || pp_port->port == 0) {
+ break;
+ }
+ pp_port = pp_port->next;
+ }
+
+ end:
+ SCReturnPtr(pp_port, "AppLayerProtoDetectProbingParserPort *");
+}
+
+/**
+ * \brief Call the probing parser if it exists for this flow.
+ *
+ * First we check the flow's dp as it's most likely to match. If that didn't
+ * lead to a PP, we try the sp.
+ *
+ */
+static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
+ uint8_t *buf, uint32_t buflen,
+ uint8_t ipproto, uint8_t direction)
+{
+ const AppLayerProtoDetectProbingParserPort *pp_port_dp = NULL;
+ const AppLayerProtoDetectProbingParserPort *pp_port_sp = NULL;
+ const AppLayerProtoDetectProbingParserElement *pe = NULL;
+ const AppLayerProtoDetectProbingParserElement *pe1 = NULL;
+ const AppLayerProtoDetectProbingParserElement *pe2 = NULL;
+ AppProto alproto = ALPROTO_UNKNOWN;
+ uint32_t *alproto_masks;
+ uint32_t mask = 0;
+
+ if (direction & STREAM_TOSERVER) {
+ /* first try the destination port */
+ pp_port_dp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, f->dp);
+ alproto_masks = &f->probing_parser_toserver_alproto_masks;
+ if (pp_port_dp != NULL) {
+ SCLogDebug("toserver - Probing parser found for destination port %"PRIu16, f->dp);
+
+ /* found based on destination port, so use dp registration */
+ pe1 = pp_port_dp->dp;
+ } else {
+ SCLogDebug("toserver - No probing parser registered for dest port %"PRIu16,
+ f->dp);
+ }
+
+ pp_port_sp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, f->sp);
+ if (pp_port_sp != NULL) {
+ SCLogDebug("toserver - Probing parser found for source port %"PRIu16, f->sp);
+
+ /* found based on source port, so use sp registration */
+ pe2 = pp_port_sp->sp;
+ } else {
+ SCLogDebug("toserver - No probing parser registered for source port %"PRIu16,
+ f->sp);
+ }
+ } else {
+ /* first try the destination port */
+ pp_port_dp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, f->dp);
+ alproto_masks = &f->probing_parser_toclient_alproto_masks;
+ if (pp_port_dp != NULL) {
+ SCLogDebug("toclient - Probing parser found for destination port %"PRIu16, f->dp);
+
+ /* found based on destination port, so use dp registration */
+ pe1 = pp_port_dp->dp;
+ } else {
+ SCLogDebug("toclient - No probing parser registered for dest port %"PRIu16,
+ f->dp);
+ }
+
+ pp_port_sp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, f->sp);
+ if (pp_port_sp != NULL) {
+ SCLogDebug("toclient - Probing parser found for source port %"PRIu16, f->sp);
+
+ pe2 = pp_port_sp->sp;
+ } else {
+ SCLogDebug("toclient - No probing parser registered for source port %"PRIu16,
+ f->sp);
+ }
+ }
+
+ if (pe1 == NULL && pe2 == NULL) {
+ SCLogDebug("%s - No probing parsers found for either port",
+ (direction & STREAM_TOSERVER) ? "toserver":"toclient");
+ FLOW_SET_PP_DONE(f, direction);
+ goto end;
+ }
+
+ /* run the parser(s) */
+ pe = pe1;
+ while (pe != NULL) {
+ if ((buflen < pe->min_depth) ||
+ (alproto_masks[0] & pe->alproto_mask)) {
+ pe = pe->next;
+ continue;
+ }
+
+ alproto = pe->ProbingParser(buf, buflen, NULL);
+ if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED)
+ goto end;
+ if (alproto == ALPROTO_FAILED ||
+ (pe->max_depth != 0 && buflen > pe->max_depth)) {
+ alproto_masks[0] |= pe->alproto_mask;
+ }
+ pe = pe->next;
+ }
+ pe = pe2;
+ while (pe != NULL) {
+ if ((buflen < pe->min_depth) ||
+ (alproto_masks[0] & pe->alproto_mask)) {
+ pe = pe->next;
+ continue;
+ }
+
+ alproto = pe->ProbingParser(buf, buflen, NULL);
+ if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED)
+ goto end;
+ if (alproto == ALPROTO_FAILED ||
+ (pe->max_depth != 0 && buflen > pe->max_depth)) {
+ alproto_masks[0] |= pe->alproto_mask;
+ }
+ pe = pe->next;
+ }
+
+ /* get the mask we need for this direction */
+ if (pp_port_dp && pp_port_sp)
+ mask = pp_port_dp->alproto_mask|pp_port_sp->alproto_mask;
+ else if (pp_port_dp)
+ mask = pp_port_dp->alproto_mask;
+ else if (pp_port_sp)
+ mask = pp_port_sp->alproto_mask;
+ else
+ mask = 0;
+
+ if (alproto_masks[0] == mask) {
+ FLOW_SET_PP_DONE(f, direction);
+ SCLogDebug("%s, mask is now %08x, needed %08x, so done",
+ (direction & STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0], mask);
+ } else {
+ SCLogDebug("%s, mask is now %08x, need %08x",
+ (direction & STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0], mask);
+ }
+
+ end:
+ SCLogDebug("%s, mask is now %08x",
+ (direction & STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0]);
+ SCReturnUInt(alproto);
+}
+
+/***** Static Internal Calls: PP registration *****/
+
+static void AppLayerProtoDetectPPGetIpprotos(AppProto alproto,
+ uint8_t *ipprotos)
+{
+ SCEnter();
+
+ const AppLayerProtoDetectProbingParser *pp;
+ const AppLayerProtoDetectProbingParserPort *pp_port;
+ const AppLayerProtoDetectProbingParserElement *pp_pe;
+
+ for (pp = alpd_ctx.ctx_pp; pp != NULL; pp = pp->next) {
+ for (pp_port = pp->port; pp_port != NULL; pp_port = pp_port->next) {
+ for (pp_pe = pp_port->dp; pp_pe != NULL; pp_pe = pp_pe->next) {
+ if (alproto == pp_pe->alproto)
+ ipprotos[pp->ipproto / 8] |= 1 << (pp->ipproto % 8);
+ }
+ for (pp_pe = pp_port->sp; pp_pe != NULL; pp_pe = pp_pe->next) {
+ if (alproto == pp_pe->alproto)
+ ipprotos[pp->ipproto / 8] |= 1 << (pp->ipproto % 8);
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+static uint32_t AppLayerProtoDetectProbingParserGetMask(AppProto alproto)
+{
+ SCEnter();
+
+ if (!(alproto > ALPROTO_UNKNOWN && alproto < ALPROTO_FAILED)) {
+ SCLogError(SC_ERR_ALPARSER, "Unknown protocol detected - %"PRIu16,
+ alproto);
+ exit(EXIT_FAILURE);
+ }
+
+ SCReturnUInt(1 << alproto);
+}
+
+static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParserElementAlloc(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserElement *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParserElement));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(AppLayerProtoDetectProbingParserElement));
+
+ SCReturnPtr(p, "AppLayerProtoDetectProbingParserElement");
+}
+
+
+static void AppLayerProtoDetectProbingParserElementFree(AppLayerProtoDetectProbingParserElement *p)
+{
+ SCEnter();
+ SCFree(p);
+ SCReturn;
+}
+
+static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectProbingParserPortAlloc(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserPort *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParserPort));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(AppLayerProtoDetectProbingParserPort));
+
+ SCReturnPtr(p, "AppLayerProtoDetectProbingParserPort");
+}
+
+static void AppLayerProtoDetectProbingParserPortFree(AppLayerProtoDetectProbingParserPort *p)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserElement *e;
+
+ e = p->dp;
+ while (e != NULL) {
+ AppLayerProtoDetectProbingParserElement *e_next = e->next;
+ AppLayerProtoDetectProbingParserElementFree(e);
+ e = e_next;
+ }
+
+ e = p->sp;
+ while (e != NULL) {
+ AppLayerProtoDetectProbingParserElement *e_next = e->next;
+ AppLayerProtoDetectProbingParserElementFree(e);
+ e = e_next;
+ }
+
+ SCFree(p);
+
+ SCReturn;
+}
+
+static AppLayerProtoDetectProbingParser *AppLayerProtoDetectProbingParserAlloc(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParser *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParser));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(AppLayerProtoDetectProbingParser));
+
+ SCReturnPtr(p, "AppLayerProtoDetectProbingParser");
+}
+
+static void AppLayerProtoDetectProbingParserFree(AppLayerProtoDetectProbingParser *p)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserPort *pt = p->port;
+ while (pt != NULL) {
+ AppLayerProtoDetectProbingParserPort *pt_next = pt->next;
+ AppLayerProtoDetectProbingParserPortFree(pt);
+ pt = pt_next;
+ }
+
+ SCFree(p);
+
+ SCReturn;
+}
+
+static AppLayerProtoDetectProbingParserElement *
+AppLayerProtoDetectProbingParserElementCreate(AppProto alproto,
+ uint16_t port,
+ uint16_t min_depth,
+ uint16_t max_depth,
+ uint16_t (*AppLayerProtoDetectProbingParser)
+ (uint8_t *input, uint32_t input_len, uint32_t *offset))
+{
+ AppLayerProtoDetectProbingParserElement *pe = AppLayerProtoDetectProbingParserElementAlloc();
+
+ pe->alproto = alproto;
+ pe->port = port;
+ pe->alproto_mask = AppLayerProtoDetectProbingParserGetMask(alproto);
+ pe->min_depth = min_depth;
+ pe->max_depth = max_depth;
+ pe->ProbingParser = AppLayerProtoDetectProbingParser;
+ pe->next = NULL;
+
+ if (max_depth != 0 && min_depth >= max_depth) {
+ SCLogError(SC_ERR_ALPARSER, "Invalid arguments sent to "
+ "register the probing parser. min_depth >= max_depth");
+ goto error;
+ }
+ if (alproto <= ALPROTO_UNKNOWN || alproto >= ALPROTO_MAX) {
+ SCLogError(SC_ERR_ALPARSER, "Invalid arguments sent to register "
+ "the probing parser. Invalid alproto - %d", alproto);
+ goto error;
+ }
+ if (AppLayerProtoDetectProbingParser == NULL) {
+ SCLogError(SC_ERR_ALPARSER, "Invalid arguments sent to "
+ "register the probing parser. Probing parser func NULL");
+ goto error;
+ }
+
+ SCReturnPtr(pe, "AppLayerProtoDetectProbingParserElement");
+ error:
+ AppLayerProtoDetectProbingParserElementFree(pe);
+ SCReturnPtr(NULL, "AppLayerProtoDetectProbingParserElement");
+}
+
+static AppLayerProtoDetectProbingParserElement *
+AppLayerProtoDetectProbingParserElementDuplicate(AppLayerProtoDetectProbingParserElement *pe)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserElement *new_pe = AppLayerProtoDetectProbingParserElementAlloc();
+
+ new_pe->alproto = pe->alproto;
+ new_pe->port = pe->port;
+ new_pe->alproto_mask = pe->alproto_mask;
+ new_pe->min_depth = pe->min_depth;
+ new_pe->max_depth = pe->max_depth;
+ new_pe->ProbingParser = pe->ProbingParser;
+ new_pe->next = NULL;
+
+ SCReturnPtr(new_pe, "AppLayerProtoDetectProbingParserElement");
+}
+
+void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParserPort *pp_port = NULL;
+ AppLayerProtoDetectProbingParserElement *pp_pe = NULL;
+
+ printf("\nProtocol Detection Configuration\n");
+
+ for ( ; pp != NULL; pp = pp->next) {
+ /* print ip protocol */
+ if (pp->ipproto == IPPROTO_TCP)
+ printf("IPProto: TCP\n");
+ else if (pp->ipproto == IPPROTO_UDP)
+ printf("IPProto: UDP\n");
+ else
+ printf("IPProto: %"PRIu8"\n", pp->ipproto);
+
+ pp_port = pp->port;
+ for ( ; pp_port != NULL; pp_port = pp_port->next) {
+ if (pp_port->dp != NULL) {
+ printf(" Port: %"PRIu16 "\n", pp_port->port);
+
+ printf(" Destination port: (max-depth: %"PRIu16 ", "
+ "mask - %"PRIu32")\n",
+ pp_port->dp_max_depth,
+ pp_port->alproto_mask);
+ pp_pe = pp_port->dp;
+ for ( ; pp_pe != NULL; pp_pe = pp_pe->next) {
+
+ if (pp_pe->alproto == ALPROTO_HTTP)
+ printf(" alproto: ALPROTO_HTTP\n");
+ else if (pp_pe->alproto == ALPROTO_FTP)
+ printf(" alproto: ALPROTO_FTP\n");
+ else if (pp_pe->alproto == ALPROTO_SMTP)
+ printf(" alproto: ALPROTO_SMTP\n");
+ else if (pp_pe->alproto == ALPROTO_TLS)
+ printf(" alproto: ALPROTO_TLS\n");
+ else if (pp_pe->alproto == ALPROTO_SSH)
+ printf(" alproto: ALPROTO_SSH\n");
+ else if (pp_pe->alproto == ALPROTO_IMAP)
+ printf(" alproto: ALPROTO_IMAP\n");
+ else if (pp_pe->alproto == ALPROTO_MSN)
+ printf(" alproto: ALPROTO_MSN\n");
+ else if (pp_pe->alproto == ALPROTO_JABBER)
+ printf(" alproto: ALPROTO_JABBER\n");
+ else if (pp_pe->alproto == ALPROTO_SMB)
+ printf(" alproto: ALPROTO_SMB\n");
+ else if (pp_pe->alproto == ALPROTO_SMB2)
+ printf(" alproto: ALPROTO_SMB2\n");
+ else if (pp_pe->alproto == ALPROTO_DCERPC)
+ printf(" alproto: ALPROTO_DCERPC\n");
+ else if (pp_pe->alproto == ALPROTO_IRC)
+ printf(" alproto: ALPROTO_IRC\n");
+ else if (pp_pe->alproto == ALPROTO_DNS)
+ printf(" alproto: ALPROTO_DNS\n");
+ else if (pp_pe->alproto == ALPROTO_MODBUS)
+ printf(" alproto: ALPROTO_MODBUS\n");
+ else
+ printf("impossible\n");
+
+ printf(" port: %"PRIu16 "\n", pp_pe->port);
+ printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask);
+ printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth);
+ printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth);
+
+ printf("\n");
+ }
+ }
+
+ if (pp_port->sp == NULL) {
+ continue;
+ }
+
+ printf(" Source port: (max-depth: %"PRIu16 ", "
+ "mask - %"PRIu32")\n",
+ pp_port->sp_max_depth,
+ pp_port->alproto_mask);
+ pp_pe = pp_port->sp;
+ for ( ; pp_pe != NULL; pp_pe = pp_pe->next) {
+
+ if (pp_pe->alproto == ALPROTO_HTTP)
+ printf(" alproto: ALPROTO_HTTP\n");
+ else if (pp_pe->alproto == ALPROTO_FTP)
+ printf(" alproto: ALPROTO_FTP\n");
+ else if (pp_pe->alproto == ALPROTO_SMTP)
+ printf(" alproto: ALPROTO_SMTP\n");
+ else if (pp_pe->alproto == ALPROTO_TLS)
+ printf(" alproto: ALPROTO_TLS\n");
+ else if (pp_pe->alproto == ALPROTO_SSH)
+ printf(" alproto: ALPROTO_SSH\n");
+ else if (pp_pe->alproto == ALPROTO_IMAP)
+ printf(" alproto: ALPROTO_IMAP\n");
+ else if (pp_pe->alproto == ALPROTO_MSN)
+ printf(" alproto: ALPROTO_MSN\n");
+ else if (pp_pe->alproto == ALPROTO_JABBER)
+ printf(" alproto: ALPROTO_JABBER\n");
+ else if (pp_pe->alproto == ALPROTO_SMB)
+ printf(" alproto: ALPROTO_SMB\n");
+ else if (pp_pe->alproto == ALPROTO_SMB2)
+ printf(" alproto: ALPROTO_SMB2\n");
+ else if (pp_pe->alproto == ALPROTO_DCERPC)
+ printf(" alproto: ALPROTO_DCERPC\n");
+ else if (pp_pe->alproto == ALPROTO_IRC)
+ printf(" alproto: ALPROTO_IRC\n");
+ else if (pp_pe->alproto == ALPROTO_DNS)
+ printf(" alproto: ALPROTO_DNS\n");
+ else if (pp_pe->alproto == ALPROTO_MODBUS)
+ printf(" alproto: ALPROTO_MODBUS\n");
+ else
+ printf("impossible\n");
+
+ printf(" port: %"PRIu16 "\n", pp_pe->port);
+ printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask);
+ printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth);
+ printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth);
+
+ printf("\n");
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+static void AppLayerProtoDetectProbingParserElementAppend(AppLayerProtoDetectProbingParserElement **head_pe,
+ AppLayerProtoDetectProbingParserElement *new_pe)
+{
+ SCEnter();
+
+ if (*head_pe == NULL) {
+ *head_pe = new_pe;
+ goto end;
+ }
+
+ if ((*head_pe)->port == 0) {
+ if (new_pe->port != 0) {
+ new_pe->next = *head_pe;
+ *head_pe = new_pe;
+ } else {
+ AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe;
+ while (temp_pe->next != NULL)
+ temp_pe = temp_pe->next;
+ temp_pe->next = new_pe;
+ }
+ } else {
+ AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe;
+ if (new_pe->port == 0) {
+ while (temp_pe->next != NULL)
+ temp_pe = temp_pe->next;
+ temp_pe->next = new_pe;
+ } else {
+ while (temp_pe->next != NULL && temp_pe->next->port != 0)
+ temp_pe = temp_pe->next;
+ new_pe->next = temp_pe->next;
+ temp_pe->next = new_pe;
+
+ }
+ }
+
+ end:
+ SCReturn;
+}
+
+static void AppLayerProtoDetectProbingParserAppend(AppLayerProtoDetectProbingParser **head_pp,
+ AppLayerProtoDetectProbingParser *new_pp)
+{
+ SCEnter();
+
+ if (*head_pp == NULL) {
+ *head_pp = new_pp;
+ goto end;
+ }
+
+ AppLayerProtoDetectProbingParser *temp_pp = *head_pp;
+ while (temp_pp->next != NULL)
+ temp_pp = temp_pp->next;
+ temp_pp->next = new_pp;
+
+ end:
+ SCReturn;
+}
+
+static void AppLayerProtoDetectProbingParserPortAppend(AppLayerProtoDetectProbingParserPort **head_port,
+ AppLayerProtoDetectProbingParserPort *new_port)
+{
+ SCEnter();
+
+ if (*head_port == NULL) {
+ *head_port = new_port;
+ goto end;
+ }
+
+ if ((*head_port)->port == 0) {
+ new_port->next = *head_port;
+ *head_port = new_port;
+ } else {
+ AppLayerProtoDetectProbingParserPort *temp_port = *head_port;
+ while (temp_port->next != NULL && temp_port->next->port != 0) {
+ temp_port = temp_port->next;
+ }
+ new_port->next = temp_port->next;
+ temp_port->next = new_port;
+ }
+
+ end:
+ SCReturn;
+}
+
+static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbingParser **pp,
+ uint8_t ipproto,
+ uint16_t port,
+ AppProto alproto,
+ uint16_t min_depth, uint16_t max_depth,
+ uint8_t direction,
+ ProbingParserFPtr ProbingParser)
+{
+ SCEnter();
+
+ /* get the top level ipproto pp */
+ AppLayerProtoDetectProbingParser *curr_pp = *pp;
+ while (curr_pp != NULL) {
+ if (curr_pp->ipproto == ipproto)
+ break;
+ curr_pp = curr_pp->next;
+ }
+ if (curr_pp == NULL) {
+ AppLayerProtoDetectProbingParser *new_pp = AppLayerProtoDetectProbingParserAlloc();
+ new_pp->ipproto = ipproto;
+ AppLayerProtoDetectProbingParserAppend(pp, new_pp);
+ curr_pp = new_pp;
+ }
+
+ /* get the top level port pp */
+ AppLayerProtoDetectProbingParserPort *curr_port = curr_pp->port;
+ while (curr_port != NULL) {
+ if (curr_port->port == port)
+ break;
+ curr_port = curr_port->next;
+ }
+ if (curr_port == NULL) {
+ AppLayerProtoDetectProbingParserPort *new_port = AppLayerProtoDetectProbingParserPortAlloc();
+ new_port->port = port;
+ AppLayerProtoDetectProbingParserPortAppend(&curr_pp->port, new_port);
+ curr_port = new_port;
+ if (direction & STREAM_TOSERVER) {
+ curr_port->dp_max_depth = max_depth;
+ } else {
+ curr_port->sp_max_depth = max_depth;
+ }
+
+ AppLayerProtoDetectProbingParserPort *zero_port;
+
+ zero_port = curr_pp->port;
+ while (zero_port != NULL && zero_port->port != 0) {
+ zero_port = zero_port->next;
+ }
+ if (zero_port != NULL) {
+ AppLayerProtoDetectProbingParserElement *zero_pe;
+
+ zero_pe = zero_port->dp;
+ for ( ; zero_pe != NULL; zero_pe = zero_pe->next) {
+ if (curr_port->dp == NULL)
+ curr_port->dp_max_depth = zero_pe->max_depth;
+ if (zero_pe->max_depth == 0)
+ curr_port->dp_max_depth = zero_pe->max_depth;
+ if (curr_port->dp_max_depth != 0 &&
+ curr_port->dp_max_depth < zero_pe->max_depth) {
+ curr_port->dp_max_depth = zero_pe->max_depth;
+ }
+
+ AppLayerProtoDetectProbingParserElement *dup_pe =
+ AppLayerProtoDetectProbingParserElementDuplicate(zero_pe);
+ AppLayerProtoDetectProbingParserElementAppend(&curr_port->dp, dup_pe);
+ curr_port->alproto_mask |= dup_pe->alproto_mask;
+ }
+
+ zero_pe = zero_port->sp;
+ for ( ; zero_pe != NULL; zero_pe = zero_pe->next) {
+ if (curr_port->sp == NULL)
+ curr_port->sp_max_depth = zero_pe->max_depth;
+ if (zero_pe->max_depth == 0)
+ curr_port->sp_max_depth = zero_pe->max_depth;
+ if (curr_port->sp_max_depth != 0 &&
+ curr_port->sp_max_depth < zero_pe->max_depth) {
+ curr_port->sp_max_depth = zero_pe->max_depth;
+ }
+
+ AppLayerProtoDetectProbingParserElement *dup_pe =
+ AppLayerProtoDetectProbingParserElementDuplicate(zero_pe);
+ AppLayerProtoDetectProbingParserElementAppend(&curr_port->sp, dup_pe);
+ curr_port->alproto_mask |= dup_pe->alproto_mask;
+ }
+ } /* if (zero_port != NULL) */
+ } /* if (curr_port == NULL) */
+
+ /* insert the pe_pp */
+ AppLayerProtoDetectProbingParserElement *curr_pe;
+ if (direction & STREAM_TOSERVER)
+ curr_pe = curr_port->dp;
+ else
+ curr_pe = curr_port->sp;
+ while (curr_pe != NULL) {
+ if (curr_pe->alproto == alproto) {
+ SCLogError(SC_ERR_ALPARSER, "Duplicate pp registered - "
+ "ipproto - %"PRIu8" Port - %"PRIu16" "
+ "App Protocol - NULL, App Protocol(ID) - "
+ "%"PRIu16" min_depth - %"PRIu16" "
+ "max_dept - %"PRIu16".",
+ ipproto, port, alproto,
+ min_depth, max_depth);
+ goto error;
+ }
+ curr_pe = curr_pe->next;
+ }
+ /* Get a new parser element */
+ AppLayerProtoDetectProbingParserElement *new_pe =
+ AppLayerProtoDetectProbingParserElementCreate(alproto,
+ curr_port->port,
+ min_depth, max_depth,
+ ProbingParser);
+ if (new_pe == NULL)
+ goto error;
+ curr_pe = new_pe;
+ AppLayerProtoDetectProbingParserElement **head_pe;
+ if (direction & STREAM_TOSERVER) {
+ if (curr_port->dp == NULL)
+ curr_port->dp_max_depth = new_pe->max_depth;
+ if (new_pe->max_depth == 0)
+ curr_port->dp_max_depth = new_pe->max_depth;
+ if (curr_port->dp_max_depth != 0 &&
+ curr_port->dp_max_depth < new_pe->max_depth) {
+ curr_port->dp_max_depth = new_pe->max_depth;
+ }
+ curr_port->alproto_mask |= new_pe->alproto_mask;
+ head_pe = &curr_port->dp;
+ } else {
+ if (curr_port->sp == NULL)
+ curr_port->sp_max_depth = new_pe->max_depth;
+ if (new_pe->max_depth == 0)
+ curr_port->sp_max_depth = new_pe->max_depth;
+ if (curr_port->sp_max_depth != 0 &&
+ curr_port->sp_max_depth < new_pe->max_depth) {
+ curr_port->sp_max_depth = new_pe->max_depth;
+ }
+ curr_port->alproto_mask |= new_pe->alproto_mask;
+ head_pe = &curr_port->sp;
+ }
+ AppLayerProtoDetectProbingParserElementAppend(head_pe, new_pe);
+
+ if (curr_port->port == 0) {
+ AppLayerProtoDetectProbingParserPort *temp_port = curr_pp->port;
+ while (temp_port != NULL && temp_port->port != 0) {
+ if (direction & STREAM_TOSERVER) {
+ if (temp_port->dp == NULL)
+ temp_port->dp_max_depth = curr_pe->max_depth;
+ if (curr_pe->max_depth == 0)
+ temp_port->dp_max_depth = curr_pe->max_depth;
+ if (temp_port->dp_max_depth != 0 &&
+ temp_port->dp_max_depth < curr_pe->max_depth) {
+ temp_port->dp_max_depth = curr_pe->max_depth;
+ }
+ AppLayerProtoDetectProbingParserElementAppend(&temp_port->dp,
+ AppLayerProtoDetectProbingParserElementDuplicate(curr_pe));
+ temp_port->alproto_mask |= curr_pe->alproto_mask;
+ } else {
+ if (temp_port->sp == NULL)
+ temp_port->sp_max_depth = curr_pe->max_depth;
+ if (curr_pe->max_depth == 0)
+ temp_port->sp_max_depth = curr_pe->max_depth;
+ if (temp_port->sp_max_depth != 0 &&
+ temp_port->sp_max_depth < curr_pe->max_depth) {
+ temp_port->sp_max_depth = curr_pe->max_depth;
+ }
+ AppLayerProtoDetectProbingParserElementAppend(&temp_port->sp,
+ AppLayerProtoDetectProbingParserElementDuplicate(curr_pe));
+ temp_port->alproto_mask |= curr_pe->alproto_mask;
+ }
+ temp_port = temp_port->next;
+ } /* while */
+ } /* if */
+
+ error:
+ SCReturn;
+}
+
+/***** Static Internal Calls: PM registration *****/
+
+static void AppLayerProtoDetectPMGetIpprotos(AppProto alproto,
+ uint8_t *ipprotos)
+{
+ SCEnter();
+
+ const AppLayerProtoDetectPMSignature *s = NULL;
+ int pat_id, max_pat_id;
+
+ int i, j;
+ uint8_t ipproto;
+
+ for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
+ ipproto = FlowGetReverseProtoMapping(i);
+ for (j = 0; j < 2; j++) {
+ AppLayerProtoDetectPMCtx *pm_ctx = &alpd_ctx.ctx_ipp[i].ctx_pm[j];
+ max_pat_id = pm_ctx->max_pat_id;
+
+ for (pat_id = 0; pat_id < max_pat_id; pat_id++) {
+ s = pm_ctx->map[pat_id];
+ while (s != NULL) {
+ if (s->alproto == alproto)
+ ipprotos[ipproto / 8] |= 1 << (ipproto % 8);
+ s = s->next;
+ }
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+static int AppLayerProtoDetectPMSetContentIDs(AppLayerProtoDetectPMCtx *ctx)
+{
+ SCEnter();
+
+ typedef struct TempContainer_ {
+ PatIntId id;
+ uint16_t content_len;
+ uint8_t *content;
+ } TempContainer;
+
+ AppLayerProtoDetectPMSignature *s = NULL;
+ uint32_t struct_total_size = 0;
+ uint32_t content_total_size = 0;
+ /* array hash buffer */
+ uint8_t *ahb = NULL;
+ uint8_t *content = NULL;
+ uint8_t content_len = 0;
+ PatIntId max_id = 0;
+ TempContainer *struct_offset = NULL;
+ uint8_t *content_offset = NULL;
+ TempContainer *dup = NULL;
+ int ret = 0;
+
+ if (ctx->head == NULL)
+ goto end;
+
+ for (s = ctx->head; s != NULL; s = s->next) {
+ struct_total_size += sizeof(TempContainer);
+ content_total_size += s->cd->content_len;
+ }
+
+ ahb = SCMalloc(sizeof(uint8_t) * (struct_total_size + content_total_size));
+ if (unlikely(ahb == NULL))
+ goto error;
+
+ struct_offset = (TempContainer *)ahb;
+ content_offset = ahb + struct_total_size;
+ for (s = ctx->head; s != NULL; s = s->next) {
+ dup = (TempContainer *)ahb;
+ content = s->cd->content;
+ content_len = s->cd->content_len;
+
+ for (; dup != struct_offset; dup++) {
+ if (dup->content_len != content_len ||
+ SCMemcmp(dup->content, content, dup->content_len) != 0)
+ {
+ continue;
+ }
+ break;
+ }
+
+ if (dup != struct_offset) {
+ s->cd->id = dup->id;
+ continue;
+ }
+
+ struct_offset->content_len = content_len;
+ struct_offset->content = content_offset;
+ content_offset += content_len;
+ memcpy(struct_offset->content, content, content_len);
+ struct_offset->id = max_id++;
+ s->cd->id = struct_offset->id;
+
+ struct_offset++;
+ }
+
+ ctx->max_pat_id = max_id;
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ if (ahb != NULL)
+ SCFree(ahb);
+ SCReturnInt(ret);
+}
+
+static int AppLayerProtoDetectPMMapSignatures(AppLayerProtoDetectPMCtx *ctx)
+{
+ SCEnter();
+
+ int ret = 0;
+ PatIntId max_pat_id = 0, tmp_pat_id;
+ AppLayerProtoDetectPMSignature *s, *next_s;
+ int mpm_ret;
+
+ max_pat_id = ctx->max_pat_id;
+
+ ctx->map = SCMalloc((max_pat_id) * sizeof(AppLayerProtoDetectPMSignature *));
+ if (ctx->map == NULL)
+ goto error;
+ memset(ctx->map, 0, (max_pat_id) * sizeof(AppLayerProtoDetectPMSignature *));
+
+ /* add an array indexed by pattern id to look up the sig */
+ for (s = ctx->head; s != NULL;) {
+ next_s = s->next;
+ s->next = ctx->map[s->cd->id];
+ ctx->map[s->cd->id] = s;
+ s = next_s;
+ }
+ ctx->head = NULL;
+
+
+ for (tmp_pat_id = 0; tmp_pat_id < max_pat_id; tmp_pat_id++) {
+ s = NULL;
+ for (s = ctx->map[tmp_pat_id]; s != NULL; s = s->next) {
+ if (s->cd->flags & DETECT_CONTENT_NOCASE) {
+ break;
+ }
+ }
+ /* if s != NULL now, it's CI. If NULL, CS */
+
+ if (s != NULL) {
+ mpm_ret = MpmAddPatternCI(&ctx->mpm_ctx,
+ s->cd->content, s->cd->content_len,
+ 0, 0, tmp_pat_id, 0, 0);
+ if (mpm_ret < 0)
+ goto error;
+ } else {
+ s = ctx->map[tmp_pat_id];
+ if (s == NULL)
+ goto error;
+
+ mpm_ret = MpmAddPatternCS(&ctx->mpm_ctx,
+ s->cd->content, s->cd->content_len,
+ 0, 0, tmp_pat_id, 0, 0);
+ if (mpm_ret < 0)
+ goto error;
+ }
+ }
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ SCReturnInt(ret);
+}
+
+static int AppLayerProtoDetectPMPrepareMpm(AppLayerProtoDetectPMCtx *ctx)
+{
+ SCEnter();
+
+ int ret = 0;
+ MpmCtx *mpm_ctx = &ctx->mpm_ctx;
+
+ if (mpm_table[mpm_ctx->mpm_type].Prepare(mpm_ctx) < 0)
+ goto error;
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ SCReturnInt(ret);
+}
+
+static void AppLayerProtoDetectPMFreeSignature(AppLayerProtoDetectPMSignature *sig)
+{
+ SCEnter();
+ if (sig == NULL)
+ SCReturn;
+ if (sig->cd)
+ DetectContentFree(sig->cd);
+ SCFree(sig);
+ SCReturn;
+}
+
+static int AppLayerProtoDetectPMAddSignature(AppLayerProtoDetectPMCtx *ctx, DetectContentData *cd,
+ AppProto alproto)
+{
+ SCEnter();
+
+ int ret = 0;
+ AppLayerProtoDetectPMSignature *s = SCMalloc(sizeof(*s));
+ if (unlikely(s == NULL))
+ goto error;
+ memset(s, 0, sizeof(*s));
+
+ s->alproto = alproto;
+ s->cd = cd;
+
+ /* prepend to the list */
+ s->next = ctx->head;
+ ctx->head = s;
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ SCReturnInt(ret);
+}
+
+static int AppLayerProtoDetectPMRegisterPattern(uint8_t ipproto, AppProto alproto,
+ char *pattern,
+ uint16_t depth, uint16_t offset,
+ uint8_t direction,
+ uint8_t is_cs)
+{
+ SCEnter();
+
+ AppLayerProtoDetectCtxIpproto *ctx_ipp = &alpd_ctx.ctx_ipp[FlowGetProtoMapping(ipproto)];
+ AppLayerProtoDetectPMCtx *ctx_pm = NULL;
+ DetectContentData *cd;
+ int ret = 0;
+
+ cd = DetectContentParseEncloseQuotes(pattern);
+ if (cd == NULL)
+ goto error;
+ cd->depth = depth;
+ cd->offset = offset;
+ if (!is_cs) {
+ BoyerMooreCtxToNocase(cd->bm_ctx, cd->content, cd->content_len);
+ cd->flags |= DETECT_CONTENT_NOCASE;
+ }
+ if (depth < cd->content_len)
+ goto error;
+
+ if (direction & STREAM_TOSERVER)
+ ctx_pm = (AppLayerProtoDetectPMCtx *)&ctx_ipp->ctx_pm[0];
+ else
+ ctx_pm = (AppLayerProtoDetectPMCtx *)&ctx_ipp->ctx_pm[1];
+
+ if (depth > ctx_pm->max_len)
+ ctx_pm->max_len = depth;
+ if (depth < ctx_pm->min_len)
+ ctx_pm->min_len = depth;
+
+ /* Finally turn it into a signature and add to the ctx. */
+ AppLayerProtoDetectPMAddSignature(ctx_pm, cd, alproto);
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ SCReturnInt(ret);
+}
+
+/***** Protocol Retrieval *****/
+
+AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
+ Flow *f,
+ uint8_t *buf, uint32_t buflen,
+ uint8_t ipproto, uint8_t direction)
+{
+ SCEnter();
+
+ AppProto alproto = ALPROTO_UNKNOWN;
+ AppProto pm_results[ALPROTO_MAX];
+ uint16_t pm_matches;
+
+ if (!FLOW_IS_PM_DONE(f, direction)) {
+ pm_matches = AppLayerProtoDetectPMGetProto(tctx, f,
+ buf, buflen,
+ direction,
+ ipproto,
+ pm_results);
+ if (pm_matches > 0) {
+ alproto = pm_results[0];
+ goto end;
+ }
+ }
+
+ if (!FLOW_IS_PP_DONE(f, direction))
+ alproto = AppLayerProtoDetectPPGetProto(f, buf, buflen, ipproto, direction);
+
+ end:
+ SCReturnCT(alproto, "AppProto");
+}
+
+static void AppLayerProtoDetectFreeProbingParsers(AppLayerProtoDetectProbingParser *pp)
+{
+ SCEnter();
+
+ AppLayerProtoDetectProbingParser *tmp_pp = NULL;
+
+ if (pp == NULL)
+ goto end;
+
+ while (pp != NULL) {
+ tmp_pp = pp->next;
+ AppLayerProtoDetectProbingParserFree(pp);
+ pp = tmp_pp;
+ }
+
+ end:
+ SCReturn;
+}
+
+/***** State Preparation *****/
+
+int AppLayerProtoDetectPrepareState(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectPMCtx *ctx_pm;
+ int i, j;
+ int ret = 0;
+
+ for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
+ for (j = 0; j < 2; j++) {
+ ctx_pm = &alpd_ctx.ctx_ipp[i].ctx_pm[j];
+
+ if (AppLayerProtoDetectPMSetContentIDs(ctx_pm) < 0)
+ goto error;
+
+ if (ctx_pm->max_pat_id == 0)
+ continue;
+
+ if (AppLayerProtoDetectPMMapSignatures(ctx_pm) < 0)
+ goto error;
+ if (AppLayerProtoDetectPMPrepareMpm(ctx_pm) < 0)
+ goto error;
+ }
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ AppLayerProtoDetectPrintProbingParsers(alpd_ctx.ctx_pp);
+ }
+#endif
+
+ goto end;
+ error:
+ ret = -1;
+ end:
+ SCReturnInt(ret);
+}
+
+/***** PP registration *****/
+
+/** \brief register parser at a port
+ *
+ * \param direction STREAM_TOSERVER or STREAM_TOCLIENT for dp or sp
+ */
+void AppLayerProtoDetectPPRegister(uint8_t ipproto,
+ char *portstr,
+ AppProto alproto,
+ uint16_t min_depth, uint16_t max_depth,
+ uint8_t direction,
+ ProbingParserFPtr ProbingParser)
+{
+ SCEnter();
+
+ DetectPort *head = NULL;
+ DetectPortParse(NULL,&head, portstr);
+ DetectPort *temp_dp = head;
+ while (temp_dp != NULL) {
+ uint32_t port = temp_dp->port;
+ if (port == 0 && temp_dp->port2 != 0)
+ port++;
+ for ( ; port <= temp_dp->port2; port++) {
+ AppLayerProtoDetectInsertNewProbingParser(&alpd_ctx.ctx_pp,
+ ipproto,
+ port,
+ alproto,
+ min_depth, max_depth,
+ direction,
+ ProbingParser);
+ }
+ temp_dp = temp_dp->next;
+ }
+ DetectPortCleanupList(head);
+
+ SCReturn;
+}
+
+int AppLayerProtoDetectPPParseConfPorts(const char *ipproto_name,
+ uint8_t ipproto,
+ const char *alproto_name,
+ AppProto alproto,
+ uint16_t min_depth, uint16_t max_depth,
+ ProbingParserFPtr ProbingParser)
+{
+ SCEnter();
+
+ char param[100];
+ int r;
+ ConfNode *node;
+ ConfNode *port_node = NULL;
+ int config = 0;
+
+ r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.",
+ alproto_name, ".detection-ports");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+ node = ConfGetNode(param);
+ if (node == NULL) {
+ SCLogDebug("Entry for %s not found.", param);
+ r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.",
+ alproto_name, ".", ipproto_name, ".detection-ports");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+ node = ConfGetNode(param);
+ if (node == NULL)
+ goto end;
+ }
+
+ /* detect by destination port of the flow (e.g. port 53 for DNS) */
+ port_node = ConfNodeLookupChild(node, "dp");
+ if (port_node == NULL)
+ port_node = ConfNodeLookupChild(node, "toserver");
+
+ if (port_node != NULL && port_node->val != NULL) {
+ AppLayerProtoDetectPPRegister(ipproto,
+ port_node->val,
+ alproto,
+ min_depth, max_depth,
+ STREAM_TOSERVER, /* to indicate dp */
+ ProbingParser);
+ }
+
+ /* detect by source port of flow */
+ port_node = ConfNodeLookupChild(node, "sp");
+ if (port_node == NULL)
+ port_node = ConfNodeLookupChild(node, "toclient");
+
+ if (port_node != NULL && port_node->val != NULL) {
+ AppLayerProtoDetectPPRegister(ipproto,
+ port_node->val,
+ alproto,
+ min_depth, max_depth,
+ STREAM_TOCLIENT, /* to indicate sp */
+ ProbingParser);
+
+ }
+
+ config = 1;
+ end:
+ SCReturnInt(config);
+}
+
+/***** PM registration *****/
+
+int AppLayerProtoDetectPMRegisterPatternCS(uint8_t ipproto, AppProto alproto,
+ char *pattern,
+ uint16_t depth, uint16_t offset,
+ uint8_t direction)
+{
+ SCEnter();
+ int r = 0;
+ r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
+ pattern,
+ depth, offset,
+ direction,
+ 1 /* case-sensitive */);
+ SCReturnInt(r);
+}
+
+int AppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto,
+ char *pattern,
+ uint16_t depth, uint16_t offset,
+ uint8_t direction)
+{
+ SCEnter();
+ int r = 0;
+ r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
+ pattern,
+ depth, offset,
+ direction,
+ 0 /* !case-sensitive */);
+ SCReturnInt(r);
+}
+
+/***** Setup/General Registration *****/
+
+int AppLayerProtoDetectSetup(void)
+{
+ SCEnter();
+
+ int i, j;
+
+ memset(&alpd_ctx, 0, sizeof(alpd_ctx));
+
+ for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
+ for (j = 0; j < 2; j++) {
+ MpmInitCtx(&alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx, MPM_AC);
+ }
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \todo incomplete. Need more work.
+ */
+int AppLayerProtoDetectDeSetup(void)
+{
+ SCEnter();
+
+ int ipproto_map = 0;
+ int dir = 0;
+ PatIntId id = 0;
+ AppLayerProtoDetectPMCtx *pm_ctx = NULL;
+ AppLayerProtoDetectPMSignature *sig = NULL, *next_sig = NULL;
+
+ for (ipproto_map = 0; ipproto_map < FLOW_PROTO_DEFAULT; ipproto_map++) {
+ for (dir = 0; dir < 2; dir++) {
+ pm_ctx = &alpd_ctx.ctx_ipp[ipproto_map].ctx_pm[dir];
+ mpm_table[pm_ctx->mpm_ctx.mpm_type].DestroyCtx(pm_ctx->mpm_ctx.ctx);
+ for (id = 0; id < pm_ctx->max_pat_id; id++) {
+ sig = pm_ctx->map[id];
+ while (sig != NULL) {
+ next_sig = sig->next;
+ AppLayerProtoDetectPMFreeSignature(sig);
+ sig = next_sig;
+ }
+ }
+ }
+ }
+
+ AppLayerProtoDetectFreeProbingParsers(alpd_ctx.ctx_pp);
+
+ SCReturnInt(0);
+}
+
+void AppLayerProtoDetectRegisterProtocol(AppProto alproto, char *alproto_name)
+{
+ SCEnter();
+
+ if (alpd_ctx.alproto_names[alproto] != NULL)
+ goto end;
+
+ alpd_ctx.alproto_names[alproto] = alproto_name;
+
+ goto end;
+ end:
+ SCReturn;
+}
+
+int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto,
+ const char *alproto)
+{
+ SCEnter();
+
+ BUG_ON(ipproto == NULL || alproto == NULL);
+
+ int enabled = 1;
+ char param[100];
+ ConfNode *node;
+ int r;
+
+ if (RunmodeIsUnittests())
+ goto enabled;
+
+ r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.",
+ alproto, ".enabled");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+
+ node = ConfGetNode(param);
+ if (node == NULL) {
+ SCLogDebug("Entry for %s not found.", param);
+ r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.",
+ alproto, ".", ipproto, ".enabled");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+
+ node = ConfGetNode(param);
+ if (node == NULL) {
+ SCLogDebug("Entry for %s not found.", param);
+ goto enabled;
+ }
+ }
+
+ if (node->val) {
+ if (ConfValIsTrue(node->val)) {
+ goto enabled;
+ } else if (ConfValIsFalse(node->val)) {
+ goto disabled;
+ } else if (strcasecmp(node->val, "detection-only") == 0) {
+ goto enabled;
+ }
+ }
+
+ /* Invalid or null value. */
+ SCLogError(SC_ERR_FATAL, "Invalid value found for %s.", param);
+ exit(EXIT_FAILURE);
+
+ disabled:
+ enabled = 0;
+ enabled:
+ SCReturnInt(enabled);
+}
+
+AppLayerProtoDetectThreadCtx *AppLayerProtoDetectGetCtxThread(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectThreadCtx *alpd_tctx = NULL;
+ MpmCtx *mpm_ctx;
+ MpmThreadCtx *mpm_tctx;
+ int i, j;
+ PatIntId max_pat_id = 0;
+
+ for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
+ for (j = 0; j < 2; j++) {
+ if (max_pat_id == 0) {
+ max_pat_id = alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id;
+
+ } else if (alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id &&
+ max_pat_id < alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id)
+ {
+ max_pat_id = alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id;
+ }
+ }
+ }
+
+ alpd_tctx = SCMalloc(sizeof(*alpd_tctx));
+ if (alpd_tctx == NULL)
+ goto error;
+ memset(alpd_tctx, 0, sizeof(*alpd_tctx));
+
+ /* Get the max pat id for all the mpm ctxs. */
+ if (PmqSetup(&alpd_tctx->pmq, max_pat_id) < 0)
+ goto error;
+
+ for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
+ for (j = 0; j < 2; j++) {
+ mpm_ctx = &alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx;
+ mpm_tctx = &alpd_tctx->mpm_tctx[i][j];
+ mpm_table[mpm_ctx->mpm_type].InitThreadCtx(mpm_ctx, mpm_tctx, 0);
+ }
+ }
+
+ goto end;
+ error:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ alpd_tctx = NULL;
+ end:
+ SCReturnPtr(alpd_tctx, "AppLayerProtoDetectThreadCtx");
+}
+
+void AppLayerProtoDetectDestroyCtxThread(AppLayerProtoDetectThreadCtx *alpd_tctx)
+{
+ SCEnter();
+
+ MpmCtx *mpm_ctx;
+ MpmThreadCtx *mpm_tctx;
+ int ipproto_map, dir;
+
+ for (ipproto_map = 0; ipproto_map < FLOW_PROTO_DEFAULT; ipproto_map++) {
+ for (dir = 0; dir < 2; dir++) {
+ mpm_ctx = &alpd_ctx.ctx_ipp[ipproto_map].ctx_pm[dir].mpm_ctx;
+ mpm_tctx = &alpd_tctx->mpm_tctx[ipproto_map][dir];
+ mpm_table[mpm_ctx->mpm_type].DestroyThreadCtx(mpm_ctx, mpm_tctx);
+ }
+ }
+ PmqFree(&alpd_tctx->pmq);
+ SCFree(alpd_tctx);
+
+ SCReturn;
+}
+
+/***** Utility *****/
+
+void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos)
+{
+ SCEnter();
+
+ AppLayerProtoDetectPMGetIpprotos(alproto, ipprotos);
+ AppLayerProtoDetectPPGetIpprotos(alproto, ipprotos);
+
+ SCReturn;
+}
+
+AppProto AppLayerProtoDetectGetProtoByName(char *alproto_name)
+{
+ SCEnter();
+
+ AppProto a;
+ for (a = 0; a < ALPROTO_MAX; a++) {
+ if (alpd_ctx.alproto_names[a] != NULL &&
+ strlen(alpd_ctx.alproto_names[a]) == strlen(alproto_name) &&
+ (SCMemcmp(alpd_ctx.alproto_names[a], alproto_name, strlen(alproto_name)) == 0))
+ {
+ SCReturnCT(a, "AppProto");
+ }
+ }
+
+ SCReturnCT(ALPROTO_UNKNOWN, "AppProto");
+}
+
+char *AppLayerProtoDetectGetProtoName(AppProto alproto)
+{
+ return alpd_ctx.alproto_names[alproto];
+}
+
+void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos)
+{
+ SCEnter();
+
+ memset(alprotos, 0, ALPROTO_MAX * sizeof(AppProto));
+
+ int alproto;
+
+ for (alproto = 0; alproto != ALPROTO_MAX; alproto++) {
+ if (alpd_ctx.alproto_names[alproto] != NULL)
+ alprotos[alproto] = 1;
+ }
+
+ SCReturn;
+}
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+
+static AppLayerProtoDetectCtx alpd_ctx_ut;
+
+void AppLayerProtoDetectUnittestCtxBackup(void)
+{
+ SCEnter();
+ alpd_ctx_ut = alpd_ctx;
+ memset(&alpd_ctx, 0, sizeof(alpd_ctx));
+ SCReturn;
+}
+
+void AppLayerProtoDetectUnittestCtxRestore(void)
+{
+ SCEnter();
+ alpd_ctx = alpd_ctx_ut;
+ memset(&alpd_ctx_ut, 0, sizeof(alpd_ctx_ut));
+ SCReturn;
+}
+
+int AppLayerProtoDetectTest01(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ char *buf;
+ int r = 0;
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+ buf = "GET";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOSERVER);
+
+ AppLayerProtoDetectPrepareState();
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1) {
+ printf("Failure - "
+ "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("Failure - "
+ "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest02(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ char *buf;
+ int r = 0;
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+ buf = "ftp";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest03(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+ buf = "220 ";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest04(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "200 ";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 13, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest05(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n<HTML><BODY>Blahblah</BODY></HTML>";
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+ buf = "220 ";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest06(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "220 Welcome to the OISF FTP server\r\n";
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+ buf = "220 ";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_FTP) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_FTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest07(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "220 Welcome to the OISF HTTP/FTP server\r\n";
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "HTTP";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_HTTP\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 0) {
+ printf("cnt != 0\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest08(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = {
+ 0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02,
+ 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
+ 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52,
+ 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02,
+ 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e,
+ 0x30, 0x00, 0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f,
+ 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57,
+ 0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61, 0x00, 0x02,
+ 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30,
+ 0x32, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41,
+ 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54,
+ 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32,
+ 0x00
+ };
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "|ff|SMB";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, buf, 8, 4, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_SMB\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_SMB) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_SMB\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest09(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = {
+ 0x00, 0x00, 0x00, 0x66, 0xfe, 0x53, 0x4d, 0x42,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x02
+ };
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "|fe|SMB";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, buf, 8, 4, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB2) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_SMB2\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_SMB2) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_SMB2\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+int AppLayerProtoDetectTest10(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xb8, 0x4a, 0x9f, 0x4d, 0x1c, 0x7d, 0xcf, 0x11,
+ 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ char *buf;
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ buf = "|05 00|";
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC, buf, 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_DCERPC) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_DCERPC\n");
+ goto end;
+ }
+
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_DCERPC) {
+ printf("cnt != 1 && pm_results[0] != AlPROTO_DCERPC\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+/**
+ * \test Why we still get http for connect... obviously because
+ * we also match on the reply, duh
+ */
+int AppLayerProtoDetectTest11(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
+ uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ memset(pm_results, 0, sizeof(pm_results));
+
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "CONNECT", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
+ {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOSERVER,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("l7data - cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data_resp, sizeof(l7data_resp),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("l7data_resp - cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+/**
+ * \test AlpProtoSignature test
+ */
+int AppLayerProtoDetectTest12(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ int r = 0;
+
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].head == NULL ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL)
+ {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ AppLayerProtoDetectPrepareState();
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1) {
+ printf("failure 2\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].head != NULL ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL)
+ {
+ printf("failure 3\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP) {
+ printf("failure 4\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->cd->id != 0) {
+ printf("failure 5\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->next != NULL) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+/**
+ * \test What about if we add some sigs only for udp but call for tcp?
+ * It should not detect any proto
+ */
+int AppLayerProtoDetectTest13(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
+ uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+ uint32_t cnt;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
+
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "CONNECT", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
+ {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOSERVER,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 0) {
+ printf("l7data - cnt != 0\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data_resp, sizeof(l7data_resp),
+ STREAM_TOCLIENT,
+ IPPROTO_TCP,
+ pm_results);
+ if (cnt != 0) {
+ printf("l7data_resp - cnt != 0\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+/**
+ * \test What about if we add some sigs only for udp calling it for UDP?
+ * It should detect ALPROTO_HTTP (over udp). This is just a check
+ * to ensure that TCP/UDP differences work correctly.
+ */
+int AppLayerProtoDetectTest14(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
+ uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
+ int r = 0;
+ Flow f;
+ AppProto pm_results[ALPROTO_MAX];
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+ uint32_t cnt;
+
+ memset(&f, 0x00, sizeof(f));
+ f.protomap = FlowGetProtoMapping(IPPROTO_UDP);
+
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "CONNECT", 7, 0, STREAM_TOSERVER);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
+
+ AppLayerProtoDetectPrepareState();
+ /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
+ * it sets internal structures which depends on the above function. */
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7\n");
+ goto end;
+ }
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1) {
+ printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1\n");
+ goto end;
+ }
+
+ if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
+ alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
+ {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data, sizeof(l7data),
+ STREAM_TOSERVER,
+ IPPROTO_UDP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("l7data - cnt != 0\n");
+ goto end;
+ }
+
+ memset(pm_results, 0, sizeof(pm_results));
+ cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+ &f,
+ l7data_resp, sizeof(l7data_resp),
+ STREAM_TOCLIENT,
+ IPPROTO_UDP,
+ pm_results);
+ if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
+ printf("l7data_resp - cnt != 0\n");
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return r;
+}
+
+typedef struct AppLayerProtoDetectPPTestDataElement_ {
+ char *alproto_name;
+ AppProto alproto;
+ uint16_t port;
+ uint32_t alproto_mask;
+ uint32_t min_depth;
+ uint32_t max_depth;
+} AppLayerProtoDetectPPTestDataElement;
+
+typedef struct AppLayerProtoDetectPPTestDataPort_ {
+ uint16_t port;
+ uint32_t alproto_mask;
+ uint16_t dp_max_depth;
+ uint16_t sp_max_depth;
+
+ AppLayerProtoDetectPPTestDataElement *toserver_element;
+ AppLayerProtoDetectPPTestDataElement *toclient_element;
+ int ts_no_of_element;
+ int tc_no_of_element;
+} AppLayerProtoDetectPPTestDataPort;
+
+
+typedef struct AppLayerProtoDetectPPTestDataIPProto_ {
+ uint8_t ipproto;
+
+ AppLayerProtoDetectPPTestDataPort *port;
+ int no_of_port;
+} AppLayerProtoDetectPPTestDataIPProto;
+
+static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp,
+ AppLayerProtoDetectPPTestDataIPProto *ip_proto,
+ int no_of_ip_proto)
+{
+ int result = 0;
+ int i = -1, j = -1 , k = -1;
+#ifdef DEBUG
+ int dir = 0;
+#endif
+ for (i = 0; i < no_of_ip_proto; i++, pp = pp->next) {
+ if (pp->ipproto != ip_proto[i].ipproto)
+ goto end;
+
+ AppLayerProtoDetectProbingParserPort *pp_port = pp->port;
+ for (k = 0; k < ip_proto[i].no_of_port; k++, pp_port = pp_port->next) {
+ if (pp_port->port != ip_proto[i].port[k].port)
+ goto end;
+ if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask)
+ goto end;
+ if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask)
+ goto end;
+ if (pp_port->dp_max_depth != ip_proto[i].port[k].dp_max_depth)
+ goto end;
+ if (pp_port->sp_max_depth != ip_proto[i].port[k].sp_max_depth)
+ goto end;
+
+ AppLayerProtoDetectProbingParserElement *pp_element = pp_port->dp;
+#ifdef DEBUG
+ dir = 0;
+#endif
+ for (j = 0 ; j < ip_proto[i].port[k].ts_no_of_element;
+ j++, pp_element = pp_element->next) {
+
+ if (pp_element->alproto != ip_proto[i].port[k].toserver_element[j].alproto) {
+ goto end;
+ }
+ if (pp_element->port != ip_proto[i].port[k].toserver_element[j].port) {
+ goto end;
+ }
+ if (pp_element->alproto_mask != ip_proto[i].port[k].toserver_element[j].alproto_mask) {
+ goto end;
+ }
+ if (pp_element->min_depth != ip_proto[i].port[k].toserver_element[j].min_depth) {
+ goto end;
+ }
+ if (pp_element->max_depth != ip_proto[i].port[k].toserver_element[j].max_depth) {
+ goto end;
+ }
+ } /* for */
+ if (pp_element != NULL)
+ goto end;
+
+ pp_element = pp_port->sp;
+#ifdef DEBUG
+ dir = 1;
+#endif
+ for (j = 0 ; j < ip_proto[i].port[k].tc_no_of_element; j++, pp_element = pp_element->next) {
+ if (pp_element->alproto != ip_proto[i].port[k].toclient_element[j].alproto) {
+ goto end;
+ }
+ if (pp_element->port != ip_proto[i].port[k].toclient_element[j].port) {
+ goto end;
+ }
+ if (pp_element->alproto_mask != ip_proto[i].port[k].toclient_element[j].alproto_mask) {
+ goto end;
+ }
+ if (pp_element->min_depth != ip_proto[i].port[k].toclient_element[j].min_depth) {
+ goto end;
+ }
+ if (pp_element->max_depth != ip_proto[i].port[k].toclient_element[j].max_depth) {
+ goto end;
+ }
+ } /* for */
+ if (pp_element != NULL)
+ goto end;
+ }
+ if (pp_port != NULL)
+ goto end;
+ }
+ if (pp != NULL)
+ goto end;
+
+ result = 1;
+ end:
+#ifdef DEBUG
+ printf("i = %d, k = %d, j = %d(%s)\n", i, k, j, (dir == 0) ? "ts" : "tc");
+#endif
+ return result;
+}
+
+static uint16_t ProbingParserDummyForTesting(uint8_t *input,
+ uint32_t input_len,
+ uint32_t *offset)
+{
+ return 0;
+}
+
+static int AppLayerProtoDetectTest15(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ int result = 0;
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_HTTP,
+ 5, 8,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_SMB,
+ 5, 6,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_FTP,
+ 7, 10,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "81",
+ ALPROTO_DCERPC,
+ 9, 10,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "81",
+ ALPROTO_FTP,
+ 7, 15,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_SMTP,
+ 12, 0,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_TLS,
+ 12, 18,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "85",
+ ALPROTO_DCERPC,
+ 9, 10,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "85",
+ ALPROTO_FTP,
+ 7, 15,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+ result = 1;
+
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+ "85",
+ ALPROTO_IMAP,
+ 12, 23,
+ STREAM_TOSERVER,
+ ProbingParserDummyForTesting);
+
+ /* toclient */
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_JABBER,
+ 12, 23,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_IRC,
+ 12, 14,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "85",
+ ALPROTO_DCERPC,
+ 9, 10,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "81",
+ ALPROTO_FTP,
+ 7, 15,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_TLS,
+ 12, 18,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_HTTP,
+ 5, 8,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "81",
+ ALPROTO_DCERPC,
+ 9, 10,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "90",
+ ALPROTO_FTP,
+ 7, 15,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_SMB,
+ 5, 6,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+ "85",
+ ALPROTO_IMAP,
+ 12, 23,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "0",
+ ALPROTO_SMTP,
+ 12, 17,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "80",
+ ALPROTO_FTP,
+ 7, 10,
+ STREAM_TOCLIENT,
+ ProbingParserDummyForTesting);
+
+ AppLayerProtoDetectPPTestDataElement element_ts_80[] = {
+ { "http", ALPROTO_HTTP, 80, 1 << ALPROTO_HTTP, 5, 8 },
+ { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 },
+ { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_80[] = {
+ { "http", ALPROTO_HTTP, 80, 1 << ALPROTO_HTTP, 5, 8 },
+ { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 },
+ { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 }
+ };
+
+ AppLayerProtoDetectPPTestDataElement element_ts_81[] = {
+ { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 },
+ { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_81[] = {
+ { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 },
+ { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 }
+ };
+
+ AppLayerProtoDetectPPTestDataElement element_ts_85[] = {
+ { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 },
+ { "ftp", ALPROTO_FTP, 85, 1 << ALPROTO_FTP, 7, 15 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_85[] = {
+ { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 }
+ };
+
+ AppLayerProtoDetectPPTestDataElement element_ts_90[] = {
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_90[] = {
+ { "ftp", ALPROTO_FTP, 90, 1 << ALPROTO_FTP, 7, 15 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 }
+ };
+
+ AppLayerProtoDetectPPTestDataElement element_ts_0[] = {
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 },
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_0[] = {
+ { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 },
+ { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 },
+ { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 },
+ { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 }
+ };
+
+
+ AppLayerProtoDetectPPTestDataElement element_ts_85_udp[] = {
+ { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 },
+ };
+ AppLayerProtoDetectPPTestDataElement element_tc_85_udp[] = {
+ { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 },
+ };
+
+ AppLayerProtoDetectPPTestDataPort ports_tcp[] = {
+ { 80,
+ ((1 << ALPROTO_HTTP) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) |
+ (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)),
+ ((1 << ALPROTO_HTTP) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) |
+ (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)),
+ 23,
+ element_ts_80, element_tc_80,
+ sizeof(element_ts_80) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_80) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ },
+ { 81,
+ ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) |
+ (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)),
+ ((1 << ALPROTO_FTP) | (1 << ALPROTO_DCERPC) |
+ (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)),
+ 23,
+ element_ts_81, element_tc_81,
+ sizeof(element_ts_81) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_81) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ },
+ { 85,
+ ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) |
+ (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)),
+ ((1 << ALPROTO_DCERPC) |
+ (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)),
+ 23,
+ element_ts_85, element_tc_85,
+ sizeof(element_ts_85) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_85) / sizeof(AppLayerProtoDetectPPTestDataElement)
+ },
+ { 90,
+ ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)),
+ ((1 << ALPROTO_FTP) |
+ (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)),
+ 23,
+ element_ts_90, element_tc_90,
+ sizeof(element_ts_90) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_90) / sizeof(AppLayerProtoDetectPPTestDataElement)
+ },
+ { 0,
+ ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)),
+ ((1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)),
+ 23,
+ element_ts_0, element_tc_0,
+ sizeof(element_ts_0) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_0) / sizeof(AppLayerProtoDetectPPTestDataElement)
+ }
+ };
+
+ AppLayerProtoDetectPPTestDataPort ports_udp[] = {
+ { 85,
+ (1 << ALPROTO_IMAP),
+ (1 << ALPROTO_IMAP),
+ 23,
+ element_ts_85_udp, element_tc_85_udp,
+ sizeof(element_ts_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ sizeof(element_tc_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement),
+ },
+ };
+
+ AppLayerProtoDetectPPTestDataIPProto ip_proto[] = {
+ { IPPROTO_TCP,
+ ports_tcp,
+ sizeof(ports_tcp) / sizeof(AppLayerProtoDetectPPTestDataPort),
+ },
+ { IPPROTO_UDP,
+ ports_udp,
+ sizeof(ports_udp) / sizeof(AppLayerProtoDetectPPTestDataPort),
+ },
+ };
+
+
+ if (AppLayerProtoDetectPPTestData(alpd_ctx.ctx_pp, ip_proto,
+ sizeof(ip_proto) / sizeof(AppLayerProtoDetectPPTestDataIPProto)) == 0) {
+ goto end;
+ }
+ result = 1;
+
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ return result;
+}
+
+
+/** \test test if the engine detect the proto and match with it */
+static int AppLayerProtoDetectTest16(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL) {
+ printf("packet setup failed: ");
+ goto end;
+ }
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL) {
+ printf("flow setup failed: ");
+ goto end;
+ }
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ f->alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(msg:\"Test content option\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should: ");
+ goto end;
+ }
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test test if the engine detect the proto on a non standar port
+ * and match with it */
+static int AppLayerProtoDetectTest17(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f->alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any "
+ "(msg:\"http over non standar port\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test test if the engine detect the proto and doesn't match
+ * because the sig expects another proto (ex ftp)*/
+static int AppLayerProtoDetectTest18(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f->alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert ftp any any -> any any "
+ "(msg:\"Test content option\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not (it's not ftp): ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test test if the engine detect the proto and doesn't match
+ * because the packet has another proto (ex ftp) */
+static int AppLayerProtoDetectTest19(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t http_buf1[] = "MPUT one\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f->alproto = ALPROTO_FTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any "
+ "(msg:\"http over non standar port\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_FTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not (it's ftp): ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test test if the engine detect the proto and match with it
+ * and also against a content option */
+static int AppLayerProtoDetectTest20(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f->alproto = ALPROTO_HTTP;
+ f->proto = IPPROTO_TCP;
+ p->flags |= PKT_STREAM_ADD;
+ p->flags |= PKT_STREAM_EOF;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ StreamTcpInitConfig(TRUE);
+
+ StreamMsg *stream_msg = StreamMsgGetFromPool();
+ if (stream_msg == NULL) {
+ printf("no stream_msg: ");
+ goto end;
+ }
+
+ memcpy(stream_msg->data, http_buf1, http_buf1_len);
+ stream_msg->data_len = http_buf1_len;
+
+ ssn.toserver_smsg_head = stream_msg;
+ ssn.toserver_smsg_tail = stream_msg;
+
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(msg:\"Test content option\"; "
+ "content:\"one\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ UTHFreeFlow(f);
+ return result;
+}
+
+
+void AppLayerProtoDetectUnittestsRegister(void)
+{
+ SCEnter();
+
+ UtRegisterTest("AppLayerProtoDetectTest01", AppLayerProtoDetectTest01, 1);
+ UtRegisterTest("AppLayerProtoDetectTest02", AppLayerProtoDetectTest02, 1);
+ UtRegisterTest("AppLayerProtoDetectTest03", AppLayerProtoDetectTest03, 1);
+ UtRegisterTest("AppLayerProtoDetectTest04", AppLayerProtoDetectTest04, 1);
+ UtRegisterTest("AppLayerProtoDetectTest05", AppLayerProtoDetectTest05, 1);
+ UtRegisterTest("AppLayerProtoDetectTest06", AppLayerProtoDetectTest06, 1);
+ UtRegisterTest("AppLayerProtoDetectTest07", AppLayerProtoDetectTest07, 1);
+ UtRegisterTest("AppLayerProtoDetectTest08", AppLayerProtoDetectTest08, 1);
+ UtRegisterTest("AppLayerProtoDetectTest09", AppLayerProtoDetectTest09, 1);
+ UtRegisterTest("AppLayerProtoDetectTest10", AppLayerProtoDetectTest10, 1);
+ UtRegisterTest("AppLayerProtoDetectTest11", AppLayerProtoDetectTest11, 1);
+ UtRegisterTest("AppLayerProtoDetectTest12", AppLayerProtoDetectTest12, 1);
+ UtRegisterTest("AppLayerProtoDetectTest13", AppLayerProtoDetectTest13, 1);
+ UtRegisterTest("AppLayerProtoDetectTest14", AppLayerProtoDetectTest14, 1);
+ UtRegisterTest("AppLayerProtoDetectTest15", AppLayerProtoDetectTest15, 1);
+ UtRegisterTest("AppLayerProtoDetectTest16", AppLayerProtoDetectTest16, 1);
+ UtRegisterTest("AppLayerProtoDetectTest17", AppLayerProtoDetectTest17, 1);
+ UtRegisterTest("AppLayerProtoDetectTest18", AppLayerProtoDetectTest18, 1);
+ UtRegisterTest("AppLayerProtoDetectTest19", AppLayerProtoDetectTest19, 1);
+ UtRegisterTest("AppLayerProtoDetectTest20", AppLayerProtoDetectTest20, 1);
+
+ SCReturn;
+}
+
+#endif /* UNITTESTS */
diff --git a/framework/src/suricata/src/app-layer-detect-proto.h b/framework/src/suricata/src/app-layer-detect-proto.h
new file mode 100644
index 00000000..81b75fe3
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-detect-proto.h
@@ -0,0 +1,197 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_DETECT_PROTO__H__
+#define __APP_LAYER_DETECT_PROTO__H__
+
+typedef struct AppLayerProtoDetectThreadCtx_ AppLayerProtoDetectThreadCtx;
+
+typedef AppProto (*ProbingParserFPtr)(uint8_t *input, uint32_t input_len,
+ uint32_t *offset);
+
+/***** Protocol Retrieval *****/
+
+/**
+ * \brief Returns the app layer protocol given a buffer.
+ *
+ * \param tctx Pointer to the app layer protocol detection thread context.
+ * \param f Pointer to the flow.
+ * \param buf The buffer to be inspected.
+ * \param buflen The length of the above buffer.
+ * \param ipproto The ip protocol.
+ * \param direction The direction bitfield - STREAM_TOSERVER/STREAM_TOCLIENT.
+ *
+ * \retval The app layer protocol.
+ */
+AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
+ Flow *f,
+ uint8_t *buf, uint32_t buflen,
+ uint8_t ipproto, uint8_t direction);
+
+/***** State Preparation *****/
+
+/**
+ * \brief Prepares the internal state for protocol detection.
+ * This needs to be called once all the patterns and probing parser
+ * ports have been registered.
+ */
+int AppLayerProtoDetectPrepareState(void);
+
+/***** PP registration *****/
+
+void AppLayerProtoDetectPPRegister(uint8_t ipproto,
+ char *portstr,
+ AppProto alproto,
+ uint16_t min_depth, uint16_t max_depth,
+ uint8_t direction,
+ ProbingParserFPtr ProbingParser);
+/**
+ * \retval bool 0 if no config was found, 1 if config was found
+ */
+int AppLayerProtoDetectPPParseConfPorts(const char *ipproto_name,
+ uint8_t ipproto,
+ const char *alproto_name,
+ AppProto alproto,
+ uint16_t min_depth, uint16_t max_depth,
+ ProbingParserFPtr ProbingParser);
+
+/***** PM registration *****/
+
+/**
+ * \brief Registers a case-sensitive pattern for protocol detection.
+ */
+int AppLayerProtoDetectPMRegisterPatternCS(uint8_t ipproto, AppProto alproto,
+ char *pattern,
+ uint16_t depth, uint16_t offset,
+ uint8_t direction);
+/**
+ * \brief Registers a case-insensitive pattern for protocol detection.
+ */
+int AppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto,
+ char *pattern,
+ uint16_t depth, uint16_t offset,
+ uint8_t direction);
+
+/***** Setup/General Registration *****/
+
+/**
+ * \brief The first function to be called. This initializes a global
+ * protocol detection context.
+ *
+ * \retval 0 On succcess;
+ * \retval -1 On failure.
+ */
+int AppLayerProtoDetectSetup(void);
+
+/**
+ * \brief Cleans up the app layer protocol detection phase.
+ */
+int AppLayerProtoDetectDeSetup(void);
+
+/**
+ * \brief Registers a protocol for protocol detection phase.
+ *
+ * This is the first function to be called after calling the
+ * setup function, AppLayerProtoDetectSetup(), before calling any other
+ * app layer functions, AppLayerParser or AppLayerProtoDetect, alike.
+ * With this function you are associating/registering a string
+ * that can be used by users to write rules, i.e.
+ * you register the http protocol for protocol detection using
+ * AppLayerProtoDetectRegisterProtocol(ctx, ALPROTO_HTTP, "http"),
+ * following which you can write rules like -
+ * alert http any any -> any any (sid:1;)
+ * which basically matches on the HTTP protocol.
+ *
+ * \param alproto The protocol.
+ * \param alproto_str The string to associate with the above "alproto".
+ * Please send a static string that won't be destroyed
+ * post making this call, since this function won't
+ * create a copy of the received argument.
+ *
+ * \retval 0 On success;
+ * -1 On failure.
+ */
+void AppLayerProtoDetectRegisterProtocol(AppProto alproto, char *alproto_name);
+
+/**
+ * \brief Given a protocol name, checks if proto detection is enabled in
+ * the conf file.
+ *
+ * \param alproto Name of the app layer protocol.
+ *
+ * \retval 1 If enabled.
+ * \retval 0 If disabled.
+ */
+int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto,
+ const char *alproto);
+
+/**
+ * \brief Inits and returns an app layer protocol detection thread context.
+
+ * \param ctx Pointer to the app layer protocol detection context.
+ *
+ * \retval Pointer to the thread context, on success;
+ * NULL, on failure.
+ */
+AppLayerProtoDetectThreadCtx *AppLayerProtoDetectGetCtxThread(void);
+
+/**
+ * \brief Destroys the app layer protocol detection thread context.
+ *
+ * \param tctx Pointer to the app layer protocol detection thread context.
+ */
+void AppLayerProtoDetectDestroyCtxThread(AppLayerProtoDetectThreadCtx *tctx);
+
+/***** Utility *****/
+
+void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos);
+AppProto AppLayerProtoDetectGetProtoByName(char *alproto_name);
+char *AppLayerProtoDetectGetProtoName(AppProto alproto);
+void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos);
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+
+/**
+ * \brief Backs up the internal context used by the app layer proto detection
+ * module.
+ */
+void AppLayerProtoDetectUnittestCtxBackup(void);
+
+/**
+ * \brief Restores back the internal context used by the app layer proto
+ * detection module, that was previously backed up by calling
+ * AppLayerProtoDetectUnittestCtxBackup().
+ */
+void AppLayerProtoDetectUnittestCtxRestore(void);
+
+/**
+ * \brief Register unittests for app layer proto detection module.
+ */
+void AppLayerProtoDetectUnittestsRegister(void);
+
+#endif /* UNITTESTS */
+
+#endif /* __APP_LAYER_DETECT_PROTO__H__ */
diff --git a/framework/src/suricata/src/app-layer-dns-common.c b/framework/src/suricata/src/app-layer-dns-common.c
new file mode 100644
index 00000000..4a3f9ccd
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-common.c
@@ -0,0 +1,1141 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "stream.h"
+#include "app-layer-parser.h"
+#include "app-layer-dns-common.h"
+#ifdef DEBUG
+#include "util-print.h"
+#endif
+#include "util-memcmp.h"
+#include "util-atomic.h"
+
+typedef struct DNSConfig_ {
+ uint32_t request_flood;
+ uint32_t state_memcap; /**< memcap in bytes per state */
+ uint64_t global_memcap; /**< memcap in bytes globally for parser */
+} DNSConfig;
+static DNSConfig dns_config;
+
+void DNSConfigInit(void)
+{
+ memset(&dns_config, 0x00, sizeof(dns_config));
+}
+
+void DNSConfigSetRequestFlood(uint32_t value)
+{
+ dns_config.request_flood = value;
+}
+
+void DNSConfigSetStateMemcap(uint32_t value)
+{
+ dns_config.state_memcap = value;
+}
+
+SC_ATOMIC_DECLARE(uint64_t, dns_memuse); /**< byte counter of current memuse */
+SC_ATOMIC_DECLARE(uint64_t, dns_memcap_state); /**< counts number of 'rejects' */
+SC_ATOMIC_DECLARE(uint64_t, dns_memcap_global); /**< counts number of 'rejects' */
+
+void DNSConfigSetGlobalMemcap(uint64_t value)
+{
+ dns_config.global_memcap = value;
+
+ SC_ATOMIC_INIT(dns_memuse);
+ SC_ATOMIC_INIT(dns_memcap_state);
+ SC_ATOMIC_INIT(dns_memcap_global);
+}
+
+void DNSIncrMemcap(uint32_t size, DNSState *state)
+{
+ if (state != NULL) {
+ state->memuse += size;
+ }
+ SC_ATOMIC_ADD(dns_memuse, size);
+}
+
+void DNSDecrMemcap(uint32_t size, DNSState *state)
+{
+ if (state != NULL) {
+ BUG_ON(size > state->memuse); /**< TODO remove later */
+ state->memuse -= size;
+ }
+
+ BUG_ON(size > SC_ATOMIC_GET(dns_memuse)); /**< TODO remove later */
+ (void)SC_ATOMIC_SUB(dns_memuse, size);
+}
+
+int DNSCheckMemcap(uint32_t want, DNSState *state)
+{
+ if (state != NULL) {
+ if (state->memuse + want > dns_config.state_memcap) {
+ SC_ATOMIC_ADD(dns_memcap_state, 1);
+ DNSSetEvent(state, DNS_DECODER_EVENT_STATE_MEMCAP_REACHED);
+ return -1;
+ }
+ }
+
+ if (SC_ATOMIC_GET(dns_memuse) + (uint64_t)want > dns_config.global_memcap) {
+ SC_ATOMIC_ADD(dns_memcap_global, 1);
+ return -2;
+ }
+
+ return 0;
+}
+
+uint64_t DNSMemcapGetMemuseCounter(void)
+{
+ uint64_t x = SC_ATOMIC_GET(dns_memuse);
+ return x;
+}
+
+uint64_t DNSMemcapGetMemcapStateCounter(void)
+{
+ uint64_t x = SC_ATOMIC_GET(dns_memcap_state);
+ return x;
+}
+
+uint64_t DNSMemcapGetMemcapGlobalCounter(void)
+{
+ uint64_t x = SC_ATOMIC_GET(dns_memcap_global);
+ return x;
+}
+
+SCEnumCharMap dns_decoder_event_table[ ] = {
+ { "UNSOLLICITED_RESPONSE", DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE, },
+ { "MALFORMED_DATA", DNS_DECODER_EVENT_MALFORMED_DATA, },
+ { "NOT_A_REQUEST", DNS_DECODER_EVENT_NOT_A_REQUEST, },
+ { "NOT_A_RESPONSE", DNS_DECODER_EVENT_NOT_A_RESPONSE, },
+ { "Z_FLAG_SET", DNS_DECODER_EVENT_Z_FLAG_SET, },
+ { "FLOODED", DNS_DECODER_EVENT_FLOODED, },
+ { "STATE_MEMCAP_REACHED", DNS_DECODER_EVENT_STATE_MEMCAP_REACHED, },
+
+ { NULL, -1 },
+};
+
+int DNSStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, dns_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "dns's enum map table.", event_name);
+ /* this should be treated as fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+void DNSAppLayerRegisterGetEventInfo(uint8_t ipproto, AppProto alproto)
+{
+ AppLayerParserRegisterGetEventInfo(ipproto, alproto, DNSStateGetEventInfo);
+
+ return;
+}
+
+AppLayerDecoderEvents *DNSGetEvents(void *state, uint64_t id)
+{
+ DNSState *dns_state = (DNSState *)state;
+ DNSTransaction *tx;
+
+ if (dns_state->curr && dns_state->curr->tx_num == (id + 1)) {
+ return dns_state->curr->decoder_events;
+ }
+
+ TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
+ if (tx->tx_num == (id+1))
+ return tx->decoder_events;
+ }
+ return NULL;
+}
+
+int DNSHasEvents(void *state)
+{
+ DNSState *dns_state = (DNSState *)state;
+ return (dns_state->events > 0);
+}
+
+void *DNSGetTx(void *alstate, uint64_t tx_id)
+{
+ DNSState *dns_state = (DNSState *)alstate;
+ DNSTransaction *tx = NULL;
+
+ /* fast track: try the current tx */
+ if (dns_state->curr && dns_state->curr->tx_num == tx_id + 1)
+ return dns_state->curr;
+
+ /* fast track:
+ * if the prev tx_id is equal to the stored tx ptr, we can
+ * use this shortcut to get to the next. */
+ if (dns_state->iter) {
+ if (tx_id == dns_state->iter->tx_num) {
+ tx = TAILQ_NEXT(dns_state->iter, next);
+ if (tx && tx->tx_num == tx_id + 1) {
+ dns_state->iter = tx;
+ return tx;
+ }
+ }
+ }
+
+ /* no luck with the fast tracks, do the full list walk */
+ TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
+ SCLogDebug("tx->tx_num %u, tx_id %"PRIu64, tx->tx_num, (tx_id+1));
+ if ((tx_id+1) != tx->tx_num)
+ continue;
+
+ SCLogDebug("returning tx %p", tx);
+ dns_state->iter = tx;
+ return tx;
+ }
+
+ return NULL;
+}
+
+uint64_t DNSGetTxCnt(void *alstate)
+{
+ DNSState *dns_state = (DNSState *)alstate;
+ return (uint64_t)dns_state->transaction_max;
+}
+
+int DNSGetAlstateProgress(void *tx, uint8_t direction)
+{
+ DNSTransaction *dns_tx = (DNSTransaction *)tx;
+ if (direction & STREAM_TOCLIENT) {
+ /* response side of the tx is done if we parsed a reply
+ * or if we tagged this tx as 'reply lost'. */
+ return (dns_tx->replied|dns_tx->reply_lost) ? 1 : 0;
+ }
+ else {
+ /* tx is only created if we have a complete request,
+ * or if we lost the request. Either way, if we have
+ * a tx it we consider the request complete. */
+ return 1;
+ }
+}
+
+/** \brief get value for 'complete' status in DNS
+ *
+ * For DNS we use a simple bool. 1 means done.
+ */
+int DNSGetAlstateProgressCompletionStatus(uint8_t direction)
+{
+ return 1;
+}
+
+void DNSSetEvent(DNSState *s, uint8_t e)
+{
+ if (s && s->curr) {
+ SCLogDebug("s->curr->decoder_events %p", s->curr->decoder_events);
+ AppLayerDecoderEventsSetEventRaw(&s->curr->decoder_events, e);
+ SCLogDebug("s->curr->decoder_events %p", s->curr->decoder_events);
+ s->events++;
+ } else {
+ SCLogDebug("couldn't set event %u", e);
+ }
+}
+
+/** \internal
+ * \brief Allocate a DNS TX
+ * \retval tx or NULL */
+static DNSTransaction *DNSTransactionAlloc(DNSState *state, const uint16_t tx_id)
+{
+ if (DNSCheckMemcap(sizeof(DNSTransaction), state) < 0)
+ return NULL;
+
+ DNSTransaction *tx = SCMalloc(sizeof(DNSTransaction));
+ if (unlikely(tx == NULL))
+ return NULL;
+ DNSIncrMemcap(sizeof(DNSTransaction), state);
+
+ memset(tx, 0x00, sizeof(DNSTransaction));
+
+ TAILQ_INIT(&tx->query_list);
+ TAILQ_INIT(&tx->answer_list);
+ TAILQ_INIT(&tx->authority_list);
+
+ tx->tx_id = tx_id;
+ return tx;
+}
+
+/** \internal
+ * \brief Free a DNS TX
+ * \param tx DNS TX to free */
+static void DNSTransactionFree(DNSTransaction *tx, DNSState *state)
+{
+ SCEnter();
+
+ DNSQueryEntry *q = NULL;
+ while ((q = TAILQ_FIRST(&tx->query_list))) {
+ TAILQ_REMOVE(&tx->query_list, q, next);
+ DNSDecrMemcap((sizeof(DNSQueryEntry) + q->len), state);
+ SCFree(q);
+ }
+
+ DNSAnswerEntry *a = NULL;
+ while ((a = TAILQ_FIRST(&tx->answer_list))) {
+ TAILQ_REMOVE(&tx->answer_list, a, next);
+ DNSDecrMemcap((sizeof(DNSAnswerEntry) + a->fqdn_len + a->data_len), state);
+ SCFree(a);
+ }
+ while ((a = TAILQ_FIRST(&tx->authority_list))) {
+ TAILQ_REMOVE(&tx->authority_list, a, next);
+ DNSDecrMemcap((sizeof(DNSAnswerEntry) + a->fqdn_len + a->data_len), state);
+ SCFree(a);
+ }
+
+ AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
+
+ if (tx->de_state != NULL) {
+ DetectEngineStateFree(tx->de_state);
+ BUG_ON(state->tx_with_detect_state_cnt == 0);
+ state->tx_with_detect_state_cnt--;
+ }
+
+ if (state->iter == tx)
+ state->iter = NULL;
+
+ DNSDecrMemcap(sizeof(DNSTransaction), state);
+ SCFree(tx);
+ SCReturn;
+}
+
+/**
+ * \brief dns transaction cleanup callback
+ */
+void DNSStateTransactionFree(void *state, uint64_t tx_id)
+{
+ SCEnter();
+
+ DNSState *dns_state = state;
+ DNSTransaction *tx = NULL;
+
+ SCLogDebug("state %p, id %"PRIu64, dns_state, tx_id);
+
+ TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
+ SCLogDebug("tx %p tx->tx_num %u, tx_id %"PRIu64, tx, tx->tx_num, (tx_id+1));
+ if ((tx_id+1) < tx->tx_num)
+ break;
+ else if ((tx_id+1) > tx->tx_num)
+ continue;
+
+ if (tx == dns_state->curr)
+ dns_state->curr = NULL;
+
+ if (tx->decoder_events != NULL) {
+ if (tx->decoder_events->cnt <= dns_state->events)
+ dns_state->events -= tx->decoder_events->cnt;
+ else
+ dns_state->events = 0;
+ }
+
+ TAILQ_REMOVE(&dns_state->tx_list, tx, next);
+ DNSTransactionFree(tx, state);
+ break;
+ }
+ SCReturn;
+}
+
+/** \internal
+ * \brief Find the DNS Tx in the state
+ * \param tx_id id of the tx
+ * \retval tx or NULL if not found */
+DNSTransaction *DNSTransactionFindByTxId(const DNSState *dns_state, const uint16_t tx_id)
+{
+ if (dns_state->curr == NULL)
+ return NULL;
+
+ /* fast path */
+ if (dns_state->curr->tx_id == tx_id) {
+ return dns_state->curr;
+
+ /* slow path, iterate list */
+ } else {
+ DNSTransaction *tx = NULL;
+ TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
+ if (tx->tx_id == tx_id) {
+ return tx;
+ }
+ }
+ }
+ /* not found */
+ return NULL;
+}
+
+int DNSStateHasTxDetectState(void *alstate)
+{
+ DNSState *state = (DNSState *)alstate;
+ return (state->tx_with_detect_state_cnt > 0);
+}
+
+DetectEngineState *DNSGetTxDetectState(void *vtx)
+{
+ DNSTransaction *tx = (DNSTransaction *)vtx;
+ return tx->de_state;
+}
+
+int DNSSetTxDetectState(void *alstate, void *vtx, DetectEngineState *s)
+{
+ DNSState *state = (DNSState *)alstate;
+ DNSTransaction *tx = (DNSTransaction *)vtx;
+ state->tx_with_detect_state_cnt++;
+ tx->de_state = s;
+ return 0;
+}
+
+void *DNSStateAlloc(void)
+{
+ void *s = SCMalloc(sizeof(DNSState));
+ if (unlikely(s == NULL))
+ return NULL;
+
+ memset(s, 0, sizeof(DNSState));
+
+ DNSState *dns_state = (DNSState *)s;
+
+ DNSIncrMemcap(sizeof(DNSState), dns_state);
+
+ TAILQ_INIT(&dns_state->tx_list);
+ return s;
+}
+
+void DNSStateFree(void *s)
+{
+ SCEnter();
+ if (s) {
+ DNSState *dns_state = (DNSState *) s;
+
+ DNSTransaction *tx = NULL;
+ while ((tx = TAILQ_FIRST(&dns_state->tx_list))) {
+ TAILQ_REMOVE(&dns_state->tx_list, tx, next);
+ DNSTransactionFree(tx, dns_state);
+ }
+
+ if (dns_state->buffer != NULL) {
+ DNSDecrMemcap(0xffff, dns_state); /** TODO update if/once we alloc
+ * in a smarter way */
+ SCFree(dns_state->buffer);
+ }
+
+ BUG_ON(dns_state->tx_with_detect_state_cnt > 0);
+
+ DNSDecrMemcap(sizeof(DNSState), dns_state);
+ BUG_ON(dns_state->memuse > 0);
+ SCFree(s);
+ }
+ SCReturn;
+}
+
+/** \brief Validation checks for DNS request header
+ *
+ * Will set decoder events if anomalies are found.
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int DNSValidateRequestHeader(DNSState *dns_state, const DNSHeader *dns_header)
+{
+ uint16_t flags = ntohs(dns_header->flags);
+
+ if ((flags & 0x8000) != 0) {
+ SCLogDebug("not a request 0x%04x", flags);
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_NOT_A_REQUEST);
+ goto bad_data;
+ }
+
+ if ((flags & 0x0040) != 0) {
+ SCLogDebug("Z flag not 0, 0x%04x", flags);
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_Z_FLAG_SET);
+ goto bad_data;
+ }
+
+ return 0;
+bad_data:
+ return -1;
+}
+
+/** \brief Validation checks for DNS response header
+ *
+ * Will set decoder events if anomalies are found.
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int DNSValidateResponseHeader(DNSState *dns_state, const DNSHeader *dns_header)
+{
+ uint16_t flags = ntohs(dns_header->flags);
+
+ if ((flags & 0x8000) == 0) {
+ SCLogDebug("not a response 0x%04x", flags);
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_NOT_A_RESPONSE);
+ goto bad_data;
+ }
+
+ if ((flags & 0x0040) != 0) {
+ SCLogDebug("Z flag not 0, 0x%04x", flags);
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_Z_FLAG_SET);
+ goto bad_data;
+ }
+
+ return 0;
+bad_data:
+ return -1;
+}
+
+/** \internal
+ * \brief check the query list to see if we already have this exact query
+ * \retval bool true or false
+ */
+static int QueryIsDuplicate(DNSTransaction *tx, const uint8_t *fqdn, const uint16_t fqdn_len,
+ const uint16_t type, const uint16_t class)
+{
+ DNSQueryEntry *q = NULL;
+
+ TAILQ_FOREACH(q, &tx->query_list, next) {
+ uint8_t *qfqdn = (uint8_t *)q + sizeof(DNSQueryEntry);
+
+ if (q->len == fqdn_len && q->type == type &&
+ q->class == class &&
+ SCMemcmp(qfqdn, fqdn, fqdn_len) == 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void DNSStoreQueryInState(DNSState *dns_state, const uint8_t *fqdn, const uint16_t fqdn_len,
+ const uint16_t type, const uint16_t class, const uint16_t tx_id)
+{
+ /* flood protection */
+ if (dns_state->givenup)
+ return;
+
+ /* find the tx and see if this is an exact duplicate */
+ DNSTransaction *tx = DNSTransactionFindByTxId(dns_state, tx_id);
+ if ((tx != NULL) && (QueryIsDuplicate(tx, fqdn, fqdn_len, type, class) == TRUE)) {
+ SCLogDebug("query is duplicate");
+ return;
+ }
+
+ /* see if the last tx is unreplied */
+ if (dns_state->curr != tx && dns_state->curr != NULL &&
+ dns_state->curr->replied == 0)
+ {
+ dns_state->curr->reply_lost = 1;
+ dns_state->unreplied_cnt++;
+
+ /* check flood limit */
+ if (dns_config.request_flood != 0 &&
+ dns_state->unreplied_cnt > dns_config.request_flood) {
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_FLOODED);
+ dns_state->givenup = 1;
+ }
+ }
+
+ if (tx == NULL) {
+ tx = DNSTransactionAlloc(dns_state, tx_id);
+ if (tx == NULL)
+ return;
+ dns_state->transaction_max++;
+ SCLogDebug("dns_state->transaction_max updated to %"PRIu64, dns_state->transaction_max);
+ TAILQ_INSERT_TAIL(&dns_state->tx_list, tx, next);
+ dns_state->curr = tx;
+ tx->tx_num = dns_state->transaction_max;
+ SCLogDebug("new tx %u with internal id %u", tx->tx_id, tx->tx_num);
+ }
+
+ if (DNSCheckMemcap((sizeof(DNSQueryEntry) + fqdn_len), dns_state) < 0)
+ return;
+ DNSQueryEntry *q = SCMalloc(sizeof(DNSQueryEntry) + fqdn_len);
+ if (unlikely(q == NULL))
+ return;
+ DNSIncrMemcap((sizeof(DNSQueryEntry) + fqdn_len), dns_state);
+
+ q->type = type;
+ q->class = class;
+ q->len = fqdn_len;
+ memcpy((uint8_t *)q + sizeof(DNSQueryEntry), fqdn, fqdn_len);
+
+ TAILQ_INSERT_TAIL(&tx->query_list, q, next);
+
+ SCLogDebug("Query for TX %04x stored", tx_id);
+}
+
+void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *fqdn,
+ const uint16_t fqdn_len, const uint16_t type, const uint16_t class, const uint16_t ttl,
+ const uint8_t *data, const uint16_t data_len, const uint16_t tx_id)
+{
+ DNSTransaction *tx = DNSTransactionFindByTxId(dns_state, tx_id);
+ if (tx == NULL) {
+ tx = DNSTransactionAlloc(dns_state, tx_id);
+ if (tx == NULL)
+ return;
+ TAILQ_INSERT_TAIL(&dns_state->tx_list, tx, next);
+ dns_state->curr = tx;
+ tx->tx_num = dns_state->transaction_max;
+ }
+
+ if (DNSCheckMemcap((sizeof(DNSAnswerEntry) + fqdn_len + data_len), dns_state) < 0)
+ return;
+ DNSAnswerEntry *q = SCMalloc(sizeof(DNSAnswerEntry) + fqdn_len + data_len);
+ if (unlikely(q == NULL))
+ return;
+ DNSIncrMemcap((sizeof(DNSAnswerEntry) + fqdn_len + data_len), dns_state);
+
+ q->type = type;
+ q->class = class;
+ q->ttl = ttl;
+ q->fqdn_len = fqdn_len;
+ q->data_len = data_len;
+
+ uint8_t *ptr = (uint8_t *)q + sizeof(DNSAnswerEntry);
+ if (fqdn != NULL && fqdn_len > 0) {
+ memcpy(ptr, fqdn, fqdn_len);
+ ptr += fqdn_len;
+ }
+ if (data != NULL && data_len > 0) {
+ memcpy(ptr, data, data_len);
+ }
+
+ if (rtype == DNS_LIST_ANSWER)
+ TAILQ_INSERT_TAIL(&tx->answer_list, q, next);
+ else if (rtype == DNS_LIST_AUTHORITY)
+ TAILQ_INSERT_TAIL(&tx->authority_list, q, next);
+ else
+ BUG_ON(1);
+
+ SCLogDebug("Answer for TX %04x stored", tx_id);
+
+ /* mark tx is as replied so we can log it */
+ tx->replied = 1;
+
+ /* reset unreplied counter */
+ dns_state->unreplied_cnt = 0;
+}
+
+/** \internal
+ * \brief get domain name from dns packet
+ *
+ * In case of compressed name storage this function follows the ptrs to
+ * create the full domain name.
+ *
+ * The length bytes are converted into dots, e.g. |03|com|00| becomes
+ * .com
+ * The trailing . is not stored.
+ *
+ * \param input input buffer (complete dns record)
+ * \param input_len lenght of input buffer
+ * \param offset offset into @input where dns name starts
+ * \param fqdn buffer to store result
+ * \param fqdn_size size of @fqdn buffer
+ * \retval 0 on error/no buffer
+ * \retval size size of fqdn
+ */
+static uint16_t DNSResponseGetNameByOffset(const uint8_t * const input, const uint32_t input_len,
+ const uint16_t offset, uint8_t *fqdn, const size_t fqdn_size)
+{
+ if (input + input_len < input + offset + 1) {
+ SCLogDebug("input buffer too small for domain of len %u", offset);
+ goto insufficient_data;
+ }
+
+ int steps = 0;
+ uint16_t fqdn_offset = 0;
+ uint8_t length = *(input + offset);
+ const uint8_t *qdata = input + offset;
+ SCLogDebug("qry length %u", length);
+
+ if (length == 0) {
+ memcpy(fqdn, "<root>", 6);
+ SCReturnUInt(6U);
+ }
+
+ while (length != 0) {
+ int cnt = 0;
+ while (length & 0xc0) {
+ uint16_t offset = ((length & 0x3f) << 8) + *(qdata+1);
+ qdata = (const uint8_t *)input + offset;
+
+ if (input + input_len < qdata + 1) {
+ SCLogDebug("input buffer too small");
+ goto insufficient_data;
+ }
+
+ length = *qdata;
+ SCLogDebug("qry length %u", length);
+
+ if (cnt++ == 100) {
+ SCLogDebug("too many pointer iterations, loop?");
+ goto bad_data;
+ }
+ }
+ qdata++;
+
+ if (length == 0) {
+ break;
+ }
+
+ if (input + input_len < qdata + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, qdata, length);
+
+ if ((size_t)(fqdn_offset + length + 1) < fqdn_size) {
+ memcpy(fqdn + fqdn_offset, qdata, length);
+ fqdn_offset += length;
+ fqdn[fqdn_offset++] = '.';
+ }
+ qdata += length;
+
+ if (input + input_len < qdata + 1) {
+ SCLogDebug("input buffer too small for len field");
+ goto insufficient_data;
+ }
+
+ length = *qdata;
+ SCLogDebug("qry length %u", length);
+ steps++;
+ if (steps >= 255)
+ goto bad_data;
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+ //PrintRawDataFp(stdout, fqdn, fqdn_offset);
+ SCReturnUInt(fqdn_offset);
+bad_data:
+insufficient_data:
+ SCReturnUInt(0U);
+}
+
+/** \internal
+ * \brief skip past domain name field
+ *
+ * Skip the domain at position data. We don't care about following compressed names
+ * as we only want to know when the next part of the buffer starts
+ *
+ * \param input input buffer (complete dns record)
+ * \param input_len lenght of input buffer
+ * \param data current position
+ *
+ * \retval NULL on out of bounds data
+ * \retval sdata ptr to position in buffer past the name
+ */
+static const uint8_t *SkipDomain(const uint8_t * const input,
+ const uint32_t input_len, const uint8_t *data)
+{
+ const uint8_t *sdata = data;
+ while (*sdata != 0x00) {
+ if (*sdata & 0xc0) {
+ sdata++;
+ break;
+ } else {
+ sdata += ((*sdata) + 1);
+ }
+ if (input + input_len < sdata) {
+ SCLogDebug("input buffer too small for data of len");
+ goto insufficient_data;
+ }
+ }
+ sdata++;
+ if (input + input_len < sdata) {
+ SCLogDebug("input buffer too small for data of len");
+ goto insufficient_data;
+ }
+ return sdata;
+insufficient_data:
+ return NULL;
+}
+
+const uint8_t *DNSReponseParse(DNSState *dns_state, const DNSHeader * const dns_header,
+ const uint16_t num, const DnsListEnum list, const uint8_t * const input,
+ const uint32_t input_len, const uint8_t *data)
+{
+ if (input + input_len < data + 2) {
+ SCLogDebug("input buffer too small for record 'name' field, record %u, "
+ "total answer_rr %u", num, ntohs(dns_header->answer_rr));
+ goto insufficient_data;
+ }
+
+ uint8_t fqdn[DNS_MAX_SIZE];
+ uint16_t fqdn_len = 0;
+
+ /* see if name is compressed */
+ if (!(data[0] & 0xc0)) {
+ if ((fqdn_len = DNSResponseGetNameByOffset(input, input_len,
+ data - input, fqdn, sizeof(fqdn))) == 0)
+ {
+#if DEBUG
+ PrintRawDataFp(stdout, (uint8_t *)input, input_len);
+ BUG_ON(1);
+#endif
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, fqdn, fqdn_len);
+ const uint8_t *tdata = SkipDomain(input, input_len, data);
+ if (tdata == NULL) {
+ goto insufficient_data;
+ }
+ data = tdata;
+ } else {
+ uint16_t offset = (data[0] & 0x3f) << 8 | data[1];
+
+ if ((fqdn_len = DNSResponseGetNameByOffset(input, input_len,
+ offset, fqdn, sizeof(fqdn))) == 0)
+ {
+#if DEBUG
+ PrintRawDataFp(stdout, (uint8_t *)input, input_len);
+ BUG_ON(1);
+#endif
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, fqdn, fqdn_len);
+ data += 2;
+ }
+
+ if (input + input_len < data + sizeof(DNSAnswerHeader)) {
+ SCLogDebug("input buffer too small for DNSAnswerHeader");
+ goto insufficient_data;
+ }
+
+ const DNSAnswerHeader *head = (DNSAnswerHeader *)data;
+
+ data += sizeof(DNSAnswerHeader);
+
+ SCLogDebug("head->len %u", ntohs(head->len));
+
+ if (input + input_len < data + ntohs(head->len)) {
+ SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
+ goto insufficient_data;
+ }
+
+ SCLogDebug("TTL %u", ntohl(head->ttl));
+
+ switch (ntohs(head->type)) {
+ case DNS_RECORD_TYPE_A:
+ {
+ if (ntohs(head->len) == 4) {
+ //PrintRawDataFp(stdout, data, ntohs(head->len));
+ //char a[16];
+ //PrintInet(AF_INET, (const void *)data, a, sizeof(a));
+ //SCLogInfo("A %s TTL %u", a, ntohl(head->ttl));
+
+ DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ data, 4, ntohs(dns_header->tx_id));
+ } else {
+ SCLogDebug("invalid length for A response data: %u", ntohs(head->len));
+ goto bad_data;
+ }
+
+ data += ntohs(head->len);
+ break;
+ }
+ case DNS_RECORD_TYPE_AAAA:
+ {
+ if (ntohs(head->len) == 16) {
+ //char a[46];
+ //PrintInet(AF_INET6, (const void *)data, a, sizeof(a));
+ //SCLogInfo("AAAA %s TTL %u", a, ntohl(head->ttl));
+
+ DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ data, 16, ntohs(dns_header->tx_id));
+ } else {
+ SCLogDebug("invalid length for AAAA response data: %u", ntohs(head->len));
+ goto bad_data;
+ }
+
+ data += ntohs(head->len);
+ break;
+ }
+ case DNS_RECORD_TYPE_MX:
+ case DNS_RECORD_TYPE_CNAME:
+ case DNS_RECORD_TYPE_PTR:
+ {
+ uint8_t name[DNS_MAX_SIZE];
+ uint16_t name_len = 0;
+ uint8_t skip = 0;
+
+ if (ntohs(head->type) == DNS_RECORD_TYPE_MX) {
+ // Skip the preference header
+ skip = 2;
+ }
+
+ if ((name_len = DNSResponseGetNameByOffset(input, input_len,
+ data - input + skip, name, sizeof(name))) == 0) {
+#if DEBUG
+ PrintRawDataFp(stdout, (uint8_t *)input, input_len);
+ BUG_ON(1);
+#endif
+ goto insufficient_data;
+ }
+
+ DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ name, name_len, ntohs(dns_header->tx_id));
+
+ data += ntohs(head->len);
+ break;
+ }
+ case DNS_RECORD_TYPE_NS:
+ case DNS_RECORD_TYPE_SOA:
+ {
+ uint8_t pname[DNS_MAX_SIZE];
+ uint16_t pname_len = 0;
+
+ if ((pname_len = DNSResponseGetNameByOffset(input, input_len,
+ data - input, pname, sizeof(pname))) == 0)
+ {
+#if DEBUG
+ PrintRawDataFp(stdout, (uint8_t *)input, input_len);
+ BUG_ON(1);
+#endif
+ goto insufficient_data;
+ }
+
+ if (ntohs(head->type) == DNS_RECORD_TYPE_SOA) {
+ const uint8_t *sdata = SkipDomain(input, input_len, data);
+ if (sdata == NULL) {
+ goto insufficient_data;
+ }
+
+ uint8_t pmail[DNS_MAX_SIZE];
+ uint16_t pmail_len = 0;
+ SCLogDebug("getting pmail");
+ if ((pmail_len = DNSResponseGetNameByOffset(input, input_len,
+ sdata - input, pmail, sizeof(pmail))) == 0)
+ {
+#if DEBUG
+ PrintRawDataFp(stdout, (uint8_t *)input, input_len);
+ BUG_ON(1);
+#endif
+ goto insufficient_data;
+ }
+ SCLogDebug("pmail_len %u", pmail_len);
+ //PrintRawDataFp(stdout, (uint8_t *)pmail, pmail_len);
+
+ const uint8_t *tdata = SkipDomain(input, input_len, sdata);
+ if (tdata == NULL) {
+ goto insufficient_data;
+ }
+#if DEBUG
+ struct Trailer {
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t experiation;
+ uint32_t minttl;
+ } *tail = (struct Trailer *)tdata;
+
+ if (input + input_len < tdata + sizeof(struct Trailer)) {
+ SCLogDebug("input buffer too small for data of len");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("serial %u refresh %u retry %u exp %u min ttl %u",
+ ntohl(tail->serial), ntohl(tail->refresh),
+ ntohl(tail->retry), ntohl(tail->experiation),
+ ntohl(tail->minttl));
+#endif
+ }
+
+ DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ pname, pname_len, ntohs(dns_header->tx_id));
+
+ data += ntohs(head->len);
+ break;
+ }
+ case DNS_RECORD_TYPE_TXT:
+ {
+ uint16_t datalen = ntohs(head->len);
+ uint8_t txtlen = *data;
+ const uint8_t *tdata = data + 1;
+
+ do {
+ //PrintRawDataFp(stdout, (uint8_t*)tdata, txtlen);
+
+ if (txtlen > datalen)
+ goto bad_data;
+
+ DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ (uint8_t*)tdata, (uint16_t)txtlen, ntohs(dns_header->tx_id));
+
+ datalen -= txtlen;
+ tdata += txtlen;
+ txtlen = *tdata;
+
+ tdata++;
+ datalen--;
+
+ SCLogDebug("datalen %u, txtlen %u", datalen, txtlen);
+ } while (datalen > 1);
+
+ data += ntohs(head->len);
+ break;
+ }
+ default: /* unsupported record */
+ {
+ DNSStoreAnswerInState(dns_state, list, NULL, 0,
+ ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
+ NULL, 0, ntohs(dns_header->tx_id));
+
+ //PrintRawDataFp(stdout, data, ntohs(head->len));
+ data += ntohs(head->len);
+ break;
+ }
+ }
+ return data;
+bad_data:
+insufficient_data:
+ return NULL;
+}
+
+void DNSCreateTypeString(uint16_t type, char *str, size_t str_size)
+{
+ switch (type) {
+ case DNS_RECORD_TYPE_A:
+ snprintf(str, str_size, "A");
+ break;
+ case DNS_RECORD_TYPE_NS:
+ snprintf(str, str_size, "NS");
+ break;
+ case DNS_RECORD_TYPE_AAAA:
+ snprintf(str, str_size, "AAAA");
+ break;
+ case DNS_RECORD_TYPE_TXT:
+ snprintf(str, str_size, "TXT");
+ break;
+ case DNS_RECORD_TYPE_CNAME:
+ snprintf(str, str_size, "CNAME");
+ break;
+ case DNS_RECORD_TYPE_SOA:
+ snprintf(str, str_size, "SOA");
+ break;
+ case DNS_RECORD_TYPE_MX:
+ snprintf(str, str_size, "MX");
+ break;
+ case DNS_RECORD_TYPE_PTR:
+ snprintf(str, str_size, "PTR");
+ break;
+ case DNS_RECORD_TYPE_ANY:
+ snprintf(str, str_size, "ANY");
+ break;
+ case DNS_RECORD_TYPE_TKEY:
+ snprintf(str, str_size, "TKEY");
+ break;
+ case DNS_RECORD_TYPE_TSIG:
+ snprintf(str, str_size, "TSIG");
+ break;
+ case DNS_RECORD_TYPE_SRV:
+ snprintf(str, str_size, "SRV");
+ break;
+ case DNS_RECORD_TYPE_NAPTR:
+ snprintf(str, str_size, "NAPTR");
+ break;
+ case DNS_RECORD_TYPE_DS:
+ snprintf(str, str_size, "DS");
+ break;
+ case DNS_RECORD_TYPE_RRSIG:
+ snprintf(str, str_size, "RRSIG");
+ break;
+ case DNS_RECORD_TYPE_NSEC:
+ snprintf(str, str_size, "NSEC");
+ break;
+ case DNS_RECORD_TYPE_NSEC3:
+ snprintf(str, str_size, "NSEC3");
+ break;
+ default:
+ snprintf(str, str_size, "%04x/%u", type, type);
+ }
+}
+
+void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size)
+{
+ switch (rcode) {
+ case DNS_RCODE_NOERROR:
+ snprintf(str, str_size, "NOERROR");
+ break;
+ case DNS_RCODE_FORMERR:
+ snprintf(str, str_size, "FORMERR");
+ break;
+ case DNS_RCODE_SERVFAIL:
+ snprintf(str, str_size, "SERVFAIL");
+ break;
+ case DNS_RCODE_NXDOMAIN:
+ snprintf(str, str_size, "NXDOMAIN");
+ break;
+ case DNS_RCODE_NOTIMP:
+ snprintf(str, str_size, "NOTIMP");
+ break;
+ case DNS_RCODE_REFUSED:
+ snprintf(str, str_size, "REFUSED");
+ break;
+ case DNS_RCODE_YXDOMAIN:
+ snprintf(str, str_size, "YXDOMAIN");
+ break;
+ case DNS_RCODE_YXRRSET:
+ snprintf(str, str_size, "YXRRSET");
+ break;
+ case DNS_RCODE_NXRRSET:
+ snprintf(str, str_size, "NXRRSET");
+ break;
+ case DNS_RCODE_NOTAUTH:
+ snprintf(str, str_size, "NOTAUTH");
+ break;
+ case DNS_RCODE_NOTZONE:
+ snprintf(str, str_size, "NOTZONE");
+ break;
+ /* these are the same, need more logic */
+ case DNS_RCODE_BADVERS:
+ //case DNS_RCODE_BADSIG:
+ snprintf(str, str_size, "BADVERS/BADSIG");
+ break;
+ case DNS_RCODE_BADKEY:
+ snprintf(str, str_size, "BADKEY");
+ break;
+ case DNS_RCODE_BADTIME:
+ snprintf(str, str_size, "BADTIME");
+ break;
+ case DNS_RCODE_BADMODE:
+ snprintf(str, str_size, "BADMODE");
+ break;
+ case DNS_RCODE_BADNAME:
+ snprintf(str, str_size, "BADNAME");
+ break;
+ case DNS_RCODE_BADALG:
+ snprintf(str, str_size, "BADALG");
+ break;
+ case DNS_RCODE_BADTRUNC:
+ snprintf(str, str_size, "BADTRUNC");
+ break;
+ default:
+ SCLogDebug("could not map DNS rcode to name, bug!");
+ snprintf(str, str_size, "%04x/%u", rcode, rcode);
+ }
+}
diff --git a/framework/src/suricata/src/app-layer-dns-common.h b/framework/src/suricata/src/app-layer-dns-common.h
new file mode 100644
index 00000000..c1979526
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-common.h
@@ -0,0 +1,259 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __APP_LAYER_DNS_COMMON_H__
+#define __APP_LAYER_DNS_COMMON_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+#define DNS_MAX_SIZE 256
+
+#define DNS_RECORD_TYPE_A 1
+#define DNS_RECORD_TYPE_NS 2
+
+#define DNS_RECORD_TYPE_CNAME 5
+#define DNS_RECORD_TYPE_SOA 6
+
+#define DNS_RECORD_TYPE_PTR 12
+#define DNS_RECORD_TYPE_MX 15
+#define DNS_RECORD_TYPE_TXT 16
+
+#define DNS_RECORD_TYPE_AAAA 28
+
+#define DNS_RECORD_TYPE_SRV 33
+
+#define DNS_RECORD_TYPE_NAPTR 35
+
+#define DNS_RECORD_TYPE_DS 43
+
+#define DNS_RECORD_TYPE_RRSIG 46
+#define DNS_RECORD_TYPE_NSEC 47
+
+#define DNS_RECORD_TYPE_NSEC3 50
+
+#define DNS_RECORD_TYPE_TKEY 249
+#define DNS_RECORD_TYPE_TSIG 250
+
+#define DNS_RECORD_TYPE_ANY 255
+
+#define DNS_RCODE_NOERROR 0
+#define DNS_RCODE_FORMERR 1
+#define DNS_RCODE_SERVFAIL 2
+#define DNS_RCODE_NXDOMAIN 3
+#define DNS_RCODE_NOTIMP 4
+#define DNS_RCODE_REFUSED 5
+#define DNS_RCODE_YXDOMAIN 6
+#define DNS_RCODE_YXRRSET 7
+#define DNS_RCODE_NXRRSET 8
+#define DNS_RCODE_NOTAUTH 9
+#define DNS_RCODE_NOTZONE 10
+// Support for OPT RR from RFC6891 will be needed to
+// parse RCODE values over 15
+#define DNS_RCODE_BADVERS 16
+#define DNS_RCODE_BADSIG 16
+#define DNS_RCODE_BADKEY 17
+#define DNS_RCODE_BADTIME 18
+#define DNS_RCODE_BADMODE 19
+#define DNS_RCODE_BADNAME 20
+#define DNS_RCODE_BADALG 21
+#define DNS_RCODE_BADTRUNC 22
+
+enum {
+ DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE,
+ DNS_DECODER_EVENT_MALFORMED_DATA,
+ DNS_DECODER_EVENT_NOT_A_REQUEST,
+ DNS_DECODER_EVENT_NOT_A_RESPONSE,
+ DNS_DECODER_EVENT_Z_FLAG_SET,
+ DNS_DECODER_EVENT_FLOODED,
+ DNS_DECODER_EVENT_STATE_MEMCAP_REACHED,
+};
+
+/** \brief DNS packet header */
+typedef struct DNSHeader_ {
+ uint16_t tx_id;
+ uint16_t flags;
+ uint16_t questions;
+ uint16_t answer_rr;
+ uint16_t authority_rr;
+ uint16_t additional_rr;
+} __attribute__((__packed__)) DNSHeader;
+
+typedef struct DNSQueryTrailer_ {
+ uint16_t type;
+ uint16_t class;
+} __attribute__((__packed__)) DNSQueryTrailer;
+
+/** \brief DNS answer header
+ * packed as we don't want alignment to mess up sizeof() */
+struct DNSAnswerHeader_ {
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t len;
+} __attribute__((__packed__));
+typedef struct DNSAnswerHeader_ DNSAnswerHeader;
+
+/** \brief List types in the TX.
+ * Used when storing answers from "Answer" or "Authority" */
+typedef enum {
+ DNS_LIST_ANSWER = 0,
+ DNS_LIST_AUTHORITY,
+} DnsListEnum;
+
+/** \brief DNS Query storage. Stored in TX list.
+ *
+ * Layout is:
+ * [list ptr][2 byte type][2 byte class][2 byte len][...data...]
+ */
+typedef struct DNSQueryEntry_ {
+ TAILQ_ENTRY(DNSQueryEntry_) next;
+ uint16_t type;
+ uint16_t class;
+ uint16_t len;
+} DNSQueryEntry;
+
+/** \brief DNS Answer storage. Stored in TX list.
+ *
+ * Layout is:
+ * [list ptr][2 byte type][2 byte class][2 byte ttl] \
+ * [2 byte fqdn len][2 byte data len][...fqdn...][...data...]
+ */
+typedef struct DNSAnswerEntry_ {
+ TAILQ_ENTRY(DNSAnswerEntry_) next;
+
+ uint16_t type;
+ uint16_t class;
+
+ uint32_t ttl;
+
+ uint16_t fqdn_len;
+ uint16_t data_len;
+} DNSAnswerEntry;
+
+/** \brief DNS Transaction, request/reply with same TX id. */
+typedef struct DNSTransaction_ {
+ uint16_t tx_num; /**< internal: id */
+ uint16_t tx_id; /**< transaction id */
+ uint8_t replied; /**< bool indicating request is
+ replied to. */
+ uint8_t reply_lost;
+ uint8_t rcode; /**< response code (e.g. "no error" / "no such name") */
+ uint8_t recursion_desired; /**< server said "recursion desired" */
+
+ TAILQ_HEAD(, DNSQueryEntry_) query_list; /**< list for query/queries */
+ TAILQ_HEAD(, DNSAnswerEntry_) answer_list; /**< list for answers */
+ TAILQ_HEAD(, DNSAnswerEntry_) authority_list; /**< list for authority records */
+
+ AppLayerDecoderEvents *decoder_events; /**< per tx events */
+
+ TAILQ_ENTRY(DNSTransaction_) next;
+ DetectEngineState *de_state;
+} DNSTransaction;
+
+/** \brief Per flow DNS state container */
+typedef struct DNSState_ {
+ TAILQ_HEAD(, DNSTransaction_) tx_list; /**< transaction list */
+ DNSTransaction *curr; /**< ptr to current tx */
+ DNSTransaction *iter;
+ uint64_t transaction_max;
+ uint32_t unreplied_cnt; /**< number of unreplied requests in a row */
+ uint32_t memuse; /**< state memuse, for comparing with
+ state-memcap settings */
+ uint64_t tx_with_detect_state_cnt;
+
+ uint16_t events;
+ uint16_t givenup;
+
+ /* used by TCP only */
+ uint16_t offset;
+ uint16_t record_len;
+ uint8_t *buffer;
+} DNSState;
+
+#define DNS_CONFIG_DEFAULT_REQUEST_FLOOD 500
+#define DNS_CONFIG_DEFAULT_STATE_MEMCAP 512*1024
+#define DNS_CONFIG_DEFAULT_GLOBAL_MEMCAP 16*1024*1024
+
+void DNSConfigInit(void);
+void DNSConfigSetRequestFlood(uint32_t value);
+void DNSConfigSetStateMemcap(uint32_t value);
+void DNSConfigSetGlobalMemcap(uint64_t value);
+
+void DNSIncrMemcap(uint32_t size, DNSState *state);
+void DNSDecrMemcap(uint32_t size, DNSState *state);
+int DNSCheckMemcap(uint32_t want, DNSState *state);
+uint64_t DNSMemcapGetMemuseCounter(void);
+uint64_t DNSMemcapGetMemcapStateCounter(void);
+uint64_t DNSMemcapGetMemcapGlobalCounter(void);
+
+void RegisterDNSParsers(void);
+void DNSParserTests(void);
+void DNSParserRegisterTests(void);
+void DNSAppLayerDecoderEventsRegister(int alproto);
+int DNSStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type);
+void DNSAppLayerRegisterGetEventInfo(uint8_t ipproto, AppProto alproto);
+
+void *DNSGetTx(void *alstate, uint64_t tx_id);
+uint64_t DNSGetTxCnt(void *alstate);
+int DNSGetAlstateProgress(void *tx, uint8_t direction);
+int DNSGetAlstateProgressCompletionStatus(uint8_t direction);
+
+void DNSStateTransactionFree(void *state, uint64_t tx_id);
+DNSTransaction *DNSTransactionFindByTxId(const DNSState *dns_state, const uint16_t tx_id);
+
+int DNSStateHasTxDetectState(void *alstate);
+DetectEngineState *DNSGetTxDetectState(void *vtx);
+int DNSSetTxDetectState(void *alstate, void *vtx, DetectEngineState *s);
+
+void DNSSetEvent(DNSState *s, uint8_t e);
+void *DNSStateAlloc(void);
+void DNSStateFree(void *s);
+AppLayerDecoderEvents *DNSGetEvents(void *state, uint64_t id);
+int DNSHasEvents(void *state);
+
+int DNSValidateRequestHeader(DNSState *, const DNSHeader *dns_header);
+int DNSValidateResponseHeader(DNSState *, const DNSHeader *dns_header);
+
+void DNSStoreQueryInState(DNSState *dns_state, const uint8_t *fqdn, const uint16_t fqdn_len,
+ const uint16_t type, const uint16_t class, const uint16_t tx_id);
+
+void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *fqdn,
+ const uint16_t fqdn_len, const uint16_t type, const uint16_t class, const uint16_t ttl,
+ const uint8_t *data, const uint16_t data_len, const uint16_t tx_id);
+
+const uint8_t *DNSReponseParse(DNSState *dns_state, const DNSHeader * const dns_header,
+ const uint16_t num, const DnsListEnum list, const uint8_t * const input,
+ const uint32_t input_len, const uint8_t *data);
+
+uint16_t DNSUdpResponseGetNameByOffset(const uint8_t * const input, const uint32_t input_len,
+ const uint16_t offset, uint8_t *fqdn, const size_t fqdn_size);
+
+void DNSCreateTypeString(uint16_t type, char *str, size_t str_size);
+void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size);
+
+#endif /* __APP_LAYER_DNS_COMMON_H__ */
diff --git a/framework/src/suricata/src/app-layer-dns-tcp.c b/framework/src/suricata/src/app-layer-dns-tcp.c
new file mode 100644
index 00000000..f1cb597d
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-tcp.c
@@ -0,0 +1,682 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "debug.h"
+#include "decode.h"
+
+#include "flow-util.h"
+
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-debug.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+
+#include "app-layer-dns-tcp.h"
+
+struct DNSTcpHeader_ {
+ uint16_t len;
+ uint16_t tx_id;
+ uint16_t flags;
+ uint16_t questions;
+ uint16_t answer_rr;
+ uint16_t authority_rr;
+ uint16_t additional_rr;
+} __attribute__((__packed__));
+typedef struct DNSTcpHeader_ DNSTcpHeader;
+
+/** \internal
+ * \param input_len at least enough for the DNSTcpHeader
+ */
+static int DNSTCPRequestParseProbe(uint8_t *input, uint32_t input_len)
+{
+#ifdef DEBUG
+ BUG_ON(input_len < sizeof(DNSTcpHeader));
+#endif
+ SCLogDebug("starting %u", input_len);
+
+ DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
+ if (ntohs(dns_tcp_header->len) < sizeof(DNSHeader)) {
+ goto bad_data;
+ }
+ if (ntohs(dns_tcp_header->len) >= input_len) {
+ goto insufficient_data;
+ }
+
+ input += 2;
+ input_len -= 2;
+ DNSHeader *dns_header = (DNSHeader *)input;
+
+ uint16_t q;
+ const uint8_t *data = input + sizeof(DNSHeader);
+
+ for (q = 0; q < ntohs(dns_header->questions); q++) {
+ uint16_t fqdn_offset = 0;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len field");
+ goto insufficient_data;
+ }
+ SCLogDebug("query length %u", *data);
+
+ while (*data != 0) {
+ if (*data > 63) {
+ /** \todo set event?*/
+ goto bad_data;
+ }
+ uint8_t length = *data;
+
+ data++;
+
+ if (length > 0) {
+ if (input + input_len < data + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, data, qry->length);
+
+ if ((fqdn_offset + length + 1) < DNS_MAX_SIZE) {
+ fqdn_offset += length;
+ } else {
+ /** \todo set event? */
+ goto bad_data;
+ }
+ }
+
+ data += length;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for new len");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("qry length %u", *data);
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+
+ data++;
+ if (input + input_len < data + sizeof(DNSQueryTrailer)) {
+ SCLogDebug("input buffer too small for DNSQueryTrailer");
+ goto insufficient_data;
+ }
+#ifdef DEBUG
+ DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
+ SCLogDebug("trailer type %04x class %04x", ntohs(trailer->type), ntohs(trailer->class));
+#endif
+ data += sizeof(DNSQueryTrailer);
+ }
+
+ SCReturnInt(1);
+insufficient_data:
+ SCReturnInt(0);
+bad_data:
+ SCReturnInt(-1);
+}
+
+static int BufferData(DNSState *dns_state, uint8_t *data, uint16_t len)
+{
+ if (dns_state->buffer == NULL) {
+ if (DNSCheckMemcap(0xffff, dns_state) < 0)
+ return -1;
+
+ /** \todo be smarter about this, like use a pool or several pools for
+ * chunks of various sizes */
+ dns_state->buffer = SCMalloc(0xffff);
+ if (dns_state->buffer == NULL) {
+ return -1;
+ }
+ DNSIncrMemcap(0xffff, dns_state);
+ }
+
+ if ((uint32_t)len + (uint32_t)dns_state->offset > (uint32_t)dns_state->record_len) {
+ SCLogDebug("oh my, we have more data than the max record size. What do we do. WHAT DO WE DOOOOO!");
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ len = dns_state->record_len - dns_state->offset;
+ }
+
+ memcpy(dns_state->buffer + dns_state->offset, data, len);
+ dns_state->offset += len;
+ return 0;
+}
+
+static void BufferReset(DNSState *dns_state)
+{
+ dns_state->record_len = 0;
+ dns_state->offset = 0;
+}
+
+static int DNSRequestParseData(Flow *f, DNSState *dns_state, const uint8_t *input, const uint32_t input_len)
+{
+ DNSHeader *dns_header = (DNSHeader *)input;
+
+ if (DNSValidateRequestHeader(dns_state, dns_header) < 0)
+ goto bad_data;
+
+ //SCLogInfo("ID %04x", ntohs(dns_header->tx_id));
+
+ uint16_t q;
+ const uint8_t *data = input + sizeof(DNSHeader);
+
+ //PrintRawDataFp(stdout, (uint8_t*)data, input_len - (data - input));
+
+ for (q = 0; q < ntohs(dns_header->questions); q++) {
+ uint8_t fqdn[DNS_MAX_SIZE];
+ uint16_t fqdn_offset = 0;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for DNSTcpQuery");
+ goto insufficient_data;
+ }
+ SCLogDebug("query length %u", *data);
+
+ while (*data != 0) {
+ if (*data > 63) {
+ /** \todo set event?*/
+ goto insufficient_data;
+ }
+ uint8_t length = *data;
+
+ data++;
+
+ if (length > 0) {
+ if (input + input_len < data + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, data, qry->length);
+
+ if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
+ memcpy(fqdn + fqdn_offset, data, length);
+ fqdn_offset += length;
+ fqdn[fqdn_offset++] = '.';
+ } else {
+ /** \todo set event? */
+ goto insufficient_data;
+ }
+ }
+
+ data += length;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for DNSTcpQuery(2)");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("qry length %u", *data);
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+
+ data++;
+ if (input + input_len < data + sizeof(DNSQueryTrailer)) {
+ SCLogDebug("input buffer too small for DNSQueryTrailer");
+ goto insufficient_data;
+ }
+ DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
+ SCLogDebug("trailer type %04x class %04x", ntohs(trailer->type), ntohs(trailer->class));
+ data += sizeof(DNSQueryTrailer);
+
+ /* store our data */
+ if (dns_state != NULL) {
+ DNSStoreQueryInState(dns_state, fqdn, fqdn_offset,
+ ntohs(trailer->type), ntohs(trailer->class),
+ ntohs(dns_header->tx_id));
+ }
+ }
+
+ SCReturnInt(1);
+bad_data:
+insufficient_data:
+ SCReturnInt(-1);
+
+}
+
+/** \internal
+ * \brief Parse DNS request packet
+ */
+static int DNSTCPRequestParse(Flow *f, void *dstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ DNSState *dns_state = (DNSState *)dstate;
+ SCLogDebug("starting %u", input_len);
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ }
+
+ /** \todo remove this when PP is fixed to enforce ipproto */
+ if (f != NULL && f->proto != IPPROTO_TCP)
+ SCReturnInt(-1);
+
+ /* probably a rst/fin sending an eof */
+ if (input == NULL || input_len == 0) {
+ goto insufficient_data;
+ }
+
+next_record:
+ /* if this is the beginning of a record, we need at least the header */
+ if (dns_state->offset == 0 && input_len < sizeof(DNSTcpHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
+ goto insufficient_data;
+ }
+ SCLogDebug("input_len %u offset %u record %u",
+ input_len, dns_state->offset, dns_state->record_len);
+
+ /* this is the first data of this record */
+ if (dns_state->offset == 0) {
+ DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
+ SCLogDebug("DNS %p", dns_tcp_header);
+
+ if (ntohs(dns_tcp_header->len) < sizeof(DNSHeader)) {
+ /* bogus len, doesn't fit even basic dns header */
+ goto bad_data;
+ } else if (ntohs(dns_tcp_header->len) == (input_len-2)) {
+ /* we have all data, so process w/o buffering */
+ if (DNSRequestParseData(f, dns_state, input+2, input_len-2) < 0)
+ goto bad_data;
+
+ } else if ((input_len-2) > ntohs(dns_tcp_header->len)) {
+ /* we have all data, so process w/o buffering */
+ if (DNSRequestParseData(f, dns_state, input+2, ntohs(dns_tcp_header->len)) < 0)
+ goto bad_data;
+
+ /* treat the rest of the data as a (potential) new record */
+ input += ntohs(dns_tcp_header->len);
+ input_len -= ntohs(dns_tcp_header->len);
+ goto next_record;
+ } else {
+ /* not enough data, store record length and buffer */
+ dns_state->record_len = ntohs(dns_tcp_header->len);
+ BufferData(dns_state, input+2, input_len-2);
+ }
+ } else if (input_len + dns_state->offset < dns_state->record_len) {
+ /* we don't have the full record yet, buffer */
+ BufferData(dns_state, input, input_len);
+ } else if (input_len > (uint32_t)(dns_state->record_len - dns_state->offset)) {
+ /* more data than expected, we may have another record coming up */
+ uint16_t need = (dns_state->record_len - dns_state->offset);
+ BufferData(dns_state, input, need);
+ int r = DNSRequestParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
+ BufferReset(dns_state);
+ if (r < 0)
+ goto bad_data;
+
+ /* treat the rest of the data as a (potential) new record */
+ input += need;
+ input_len -= need;
+ goto next_record;
+ } else {
+ /* implied exactly the amount of data we want
+ * add current to buffer, then inspect buffer */
+ BufferData(dns_state, input, input_len);
+ int r = DNSRequestParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
+ BufferReset(dns_state);
+ if (r < 0)
+ goto bad_data;
+ }
+
+ SCReturnInt(1);
+insufficient_data:
+ SCReturnInt(-1);
+bad_data:
+ SCReturnInt(-1);
+}
+
+static int DNSReponseParseData(Flow *f, DNSState *dns_state, const uint8_t *input, const uint32_t input_len)
+{
+ DNSHeader *dns_header = (DNSHeader *)input;
+
+ if (DNSValidateResponseHeader(dns_state, dns_header) < 0)
+ goto bad_data;
+
+ DNSTransaction *tx = NULL;
+ int found = 0;
+ if ((tx = DNSTransactionFindByTxId(dns_state, ntohs(dns_header->tx_id))) != NULL)
+ found = 1;
+
+ if (!found) {
+ SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
+ }
+
+ uint16_t q;
+ const uint8_t *data = input + sizeof(DNSHeader);
+ for (q = 0; q < ntohs(dns_header->questions); q++) {
+ uint8_t fqdn[DNS_MAX_SIZE];
+ uint16_t fqdn_offset = 0;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len field");
+ goto insufficient_data;
+ }
+ SCLogDebug("qry length %u", *data);
+
+ while (*data != 0) {
+ uint8_t length = *data;
+ data++;
+
+ if (length > 0) {
+ if (input + input_len < data + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, data, length);
+
+ if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
+ memcpy(fqdn + fqdn_offset, data, length);
+ fqdn_offset += length;
+ fqdn[fqdn_offset++] = '.';
+ }
+ }
+
+ data += length;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len field");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("length %u", *data);
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+
+ data++;
+ if (input + input_len < data + sizeof(DNSQueryTrailer)) {
+ SCLogDebug("input buffer too small for DNSQueryTrailer");
+ goto insufficient_data;
+ }
+#if DEBUG
+ DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
+ SCLogDebug("trailer type %04x class %04x", ntohs(trailer->type), ntohs(trailer->class));
+#endif
+ data += sizeof(DNSQueryTrailer);
+ }
+
+ for (q = 0; q < ntohs(dns_header->answer_rr); q++) {
+ data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_ANSWER,
+ input, input_len, data);
+ if (data == NULL) {
+ goto insufficient_data;
+ }
+ }
+
+ //PrintRawDataFp(stdout, (uint8_t *)data, input_len - (data - input));
+ for (q = 0; q < ntohs(dns_header->authority_rr); q++) {
+ data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_AUTHORITY,
+ input, input_len, data);
+ if (data == NULL) {
+ goto insufficient_data;
+ }
+ }
+
+ /* parse rcode, e.g. "noerror" or "nxdomain" */
+ uint8_t rcode = ntohs(dns_header->flags) & 0x0F;
+ if (rcode <= DNS_RCODE_NOTZONE) {
+ SCLogDebug("rcode %u", rcode);
+ if (tx != NULL)
+ tx->rcode = rcode;
+ } else {
+ /* this is not invalid, rcodes can be user defined */
+ SCLogDebug("unexpected DNS rcode %u", rcode);
+ }
+
+ if (ntohs(dns_header->flags) & 0x0080) {
+ SCLogDebug("recursion desired");
+ if (tx != NULL)
+ tx->recursion_desired = 1;
+ }
+
+ if (tx != NULL) {
+ tx->replied = 1;
+ }
+
+ SCReturnInt(1);
+bad_data:
+insufficient_data:
+ SCReturnInt(-1);
+}
+
+/** \internal
+ * \brief DNS TCP record parser, entry function
+ *
+ * Parses a DNS TCP record and fills the DNS state
+ *
+ * As TCP records can be 64k we'll have to buffer the data. Streaming parsing
+ * would have been _very_ tricky due to the way names are compressed in DNS
+ *
+ */
+static int DNSTCPResponseParse(Flow *f, void *dstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ DNSState *dns_state = (DNSState *)dstate;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ }
+
+ /** \todo remove this when PP is fixed to enforce ipproto */
+ if (f != NULL && f->proto != IPPROTO_TCP)
+ SCReturnInt(-1);
+
+ /* probably a rst/fin sending an eof */
+ if (input == NULL || input_len == 0) {
+ goto insufficient_data;
+ }
+
+next_record:
+ /* if this is the beginning of a record, we need at least the header */
+ if (dns_state->offset == 0 && input_len < sizeof(DNSTcpHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
+ goto insufficient_data;
+ }
+ SCLogDebug("input_len %u offset %u record %u",
+ input_len, dns_state->offset, dns_state->record_len);
+
+ /* this is the first data of this record */
+ if (dns_state->offset == 0) {
+ DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
+ SCLogDebug("DNS %p", dns_tcp_header);
+
+ if (ntohs(dns_tcp_header->len) == (input_len-2)) {
+ /* we have all data, so process w/o buffering */
+ if (DNSReponseParseData(f, dns_state, input+2, input_len-2) < 0)
+ goto bad_data;
+
+ } else if ((input_len-2) > ntohs(dns_tcp_header->len)) {
+ /* we have all data, so process w/o buffering */
+ if (DNSReponseParseData(f, dns_state, input+2, ntohs(dns_tcp_header->len)) < 0)
+ goto bad_data;
+
+ /* treat the rest of the data as a (potential) new record */
+ input += ntohs(dns_tcp_header->len);
+ input_len -= ntohs(dns_tcp_header->len);
+ goto next_record;
+ } else {
+ /* not enough data, store record length and buffer */
+ dns_state->record_len = ntohs(dns_tcp_header->len);
+ BufferData(dns_state, input+2, input_len-2);
+ }
+ } else if (input_len + dns_state->offset < dns_state->record_len) {
+ /* we don't have the full record yet, buffer */
+ BufferData(dns_state, input, input_len);
+ } else if (input_len > (uint32_t)(dns_state->record_len - dns_state->offset)) {
+ /* more data than expected, we may have another record coming up */
+ uint16_t need = (dns_state->record_len - dns_state->offset);
+ BufferData(dns_state, input, need);
+ int r = DNSReponseParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
+ BufferReset(dns_state);
+ if (r < 0)
+ goto bad_data;
+
+ /* treat the rest of the data as a (potential) new record */
+ input += need;
+ input_len -= need;
+ goto next_record;
+ } else {
+ /* implied exactly the amount of data we want
+ * add current to buffer, then inspect buffer */
+ BufferData(dns_state, input, input_len);
+ int r = DNSReponseParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
+ BufferReset(dns_state);
+ if (r < 0)
+ goto bad_data;
+ }
+ SCReturnInt(1);
+insufficient_data:
+ SCReturnInt(-1);
+bad_data:
+ SCReturnInt(-1);
+}
+
+static uint16_t DNSTcpProbingParser(uint8_t *input, uint32_t ilen, uint32_t *offset)
+{
+ if (ilen == 0 || ilen < sizeof(DNSTcpHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
+ return ALPROTO_UNKNOWN;
+ }
+
+ DNSTcpHeader *dns_header = (DNSTcpHeader *)input;
+ if (ntohs(dns_header->len) < sizeof(DNSHeader)) {
+ /* length field bogus, won't even fit a minimal DNS header. */
+ return ALPROTO_FAILED;
+ } else if (ntohs(dns_header->len) > ilen) {
+ int r = DNSTCPRequestParseProbe(input, ilen);
+ if (r == -1) {
+ /* probing parser told us "bad data", so it's not
+ * DNS */
+ return ALPROTO_FAILED;
+ } else if (ilen > 512) {
+ SCLogDebug("all the parser told us was not enough data, which is expected. Lets assume it's DNS");
+ return ALPROTO_DNS;
+ }
+
+ SCLogDebug("not yet enough info %u > %u", ntohs(dns_header->len), ilen);
+ return ALPROTO_UNKNOWN;
+ }
+
+ int r = DNSTCPRequestParseProbe(input, ilen);
+ if (r != 1)
+ return ALPROTO_FAILED;
+
+ SCLogDebug("ALPROTO_DNS");
+ return ALPROTO_DNS;
+}
+
+void RegisterDNSTCPParsers(void)
+{
+ char *proto_name = "dns";
+
+ /** DNS */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "53",
+ ALPROTO_DNS,
+ 0, sizeof(DNSTcpHeader),
+ STREAM_TOSERVER,
+ DNSTcpProbingParser);
+ } else {
+ int have_cfg = AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
+ proto_name, ALPROTO_DNS,
+ 0, sizeof(DNSTcpHeader),
+ DNSTcpProbingParser);
+ /* if we have no config, we enable the default port 53 */
+ if (!have_cfg) {
+ SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS TCP config found, "
+ "enabling DNS detection on "
+ "port 53.");
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53",
+ ALPROTO_DNS, 0, sizeof(DNSTcpHeader),
+ STREAM_TOSERVER, DNSTcpProbingParser);
+ }
+ }
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNS, STREAM_TOSERVER,
+ DNSTCPRequestParse);
+ AppLayerParserRegisterParser(IPPROTO_TCP , ALPROTO_DNS, STREAM_TOCLIENT,
+ DNSTCPResponseParse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNS, DNSStateAlloc,
+ DNSStateFree);
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNS,
+ DNSStateTransactionFree);
+
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_DNS, DNSGetEvents);
+ AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_DNS, DNSHasEvents);
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
+ DNSStateHasTxDetectState,
+ DNSGetTxDetectState, DNSSetTxDetectState);
+
+ AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNS, DNSGetTx);
+ AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNS, DNSGetTxCnt);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNS,
+ DNSGetAlstateProgress);
+ AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, ALPROTO_DNS,
+ DNSGetAlstateProgressCompletionStatus);
+ DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+ return;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+void DNSTCPParserRegisterTests(void)
+{
+// UtRegisterTest("DNSTCPParserTest01", DNSTCPParserTest01, 1);
+}
+#endif
diff --git a/framework/src/suricata/src/app-layer-dns-tcp.h b/framework/src/suricata/src/app-layer-dns-tcp.h
new file mode 100644
index 00000000..2f3b4ffc
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-tcp.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+
+#ifndef __APP_LAYER_DNS_TCP_H__
+#define __APP_LAYER_DNS_TCP_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-dns-common.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+void RegisterDNSTCPParsers(void);
+void DNSTCPParserTests(void);
+void DNSTCPParserRegisterTests(void);
+
+#endif /* __APP_LAYER_DNS_TCP_H__ */
diff --git a/framework/src/suricata/src/app-layer-dns-udp.c b/framework/src/suricata/src/app-layer-dns-udp.c
new file mode 100644
index 00000000..e3ee01ff
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-udp.c
@@ -0,0 +1,635 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "conf.h"
+#include "util-misc.h"
+
+#include "debug.h"
+#include "decode.h"
+
+#include "flow-util.h"
+
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-debug.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+
+#include "app-layer-dns-udp.h"
+
+/** \internal
+ * \brief Parse DNS request packet
+ */
+static int DNSUDPRequestParse(Flow *f, void *dstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ DNSState *dns_state = (DNSState *)dstate;
+
+ SCLogDebug("starting %u", input_len);
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ }
+
+ /** \todo remove this when PP is fixed to enforce ipproto */
+ if (f != NULL && f->proto != IPPROTO_UDP)
+ SCReturnInt(-1);
+
+ if (input == NULL || input_len == 0 || input_len < sizeof(DNSHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
+ goto insufficient_data;
+ }
+
+ DNSHeader *dns_header = (DNSHeader *)input;
+ SCLogDebug("DNS %p", dns_header);
+
+ if (DNSValidateRequestHeader(dns_state, dns_header) < 0)
+ goto bad_data;
+
+ uint16_t q;
+ const uint8_t *data = input + sizeof(DNSHeader);
+ for (q = 0; q < ntohs(dns_header->questions); q++) {
+ uint8_t fqdn[DNS_MAX_SIZE];
+ uint16_t fqdn_offset = 0;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len");
+ goto insufficient_data;
+ }
+ SCLogDebug("query length %u", *data);
+
+ while (*data != 0) {
+ if (*data > 63) {
+ /** \todo set event?*/
+ goto insufficient_data;
+ }
+ uint8_t length = *data;
+
+ data++;
+
+ if (length == 0) {
+ break;
+ }
+
+ if (input + input_len < data + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, data, qry->length);
+
+ if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
+ memcpy(fqdn + fqdn_offset, data, length);
+ fqdn_offset += length;
+ fqdn[fqdn_offset++] = '.';
+ } else {
+ /** \todo set event? */
+ goto insufficient_data;
+ }
+
+ data += length;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len(2)");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("qry length %u", *data);
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+
+ data++;
+ if (input + input_len < data + sizeof(DNSQueryTrailer)) {
+ SCLogDebug("input buffer too small for DNSQueryTrailer");
+ goto insufficient_data;
+ }
+ DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
+ SCLogDebug("trailer type %04x class %04x", ntohs(trailer->type), ntohs(trailer->class));
+ data += sizeof(DNSQueryTrailer);
+
+ /* store our data */
+ if (dns_state != NULL) {
+ DNSStoreQueryInState(dns_state, fqdn, fqdn_offset,
+ ntohs(trailer->type), ntohs(trailer->class),
+ ntohs(dns_header->tx_id));
+ }
+ }
+
+ SCReturnInt(1);
+bad_data:
+insufficient_data:
+ SCReturnInt(-1);
+}
+
+/** \internal
+ * \brief DNS UDP record parser, entry function
+ *
+ * Parses a DNS UDP record and fills the DNS state
+ *
+ */
+static int DNSUDPResponseParse(Flow *f, void *dstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ DNSState *dns_state = (DNSState *)dstate;
+
+ SCLogDebug("starting %u", input_len);
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ }
+
+ /** \todo remove this when PP is fixed to enforce ipproto */
+ if (f != NULL && f->proto != IPPROTO_UDP)
+ SCReturnInt(-1);
+
+ if (input == NULL || input_len == 0 || input_len < sizeof(DNSHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
+ goto insufficient_data;
+ }
+
+ DNSHeader *dns_header = (DNSHeader *)input;
+ SCLogDebug("DNS %p %04x %04x", dns_header, ntohs(dns_header->tx_id), dns_header->flags);
+
+ DNSTransaction *tx = NULL;
+ int found = 0;
+ if ((tx = DNSTransactionFindByTxId(dns_state, ntohs(dns_header->tx_id))) != NULL)
+ found = 1;
+
+ if (!found) {
+ SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
+ }
+
+ if (DNSValidateResponseHeader(dns_state, dns_header) < 0)
+ goto bad_data;
+
+ SCLogDebug("queries %04x", ntohs(dns_header->questions));
+
+ uint16_t q;
+ const uint8_t *data = input + sizeof(DNSHeader);
+ for (q = 0; q < ntohs(dns_header->questions); q++) {
+ uint8_t fqdn[DNS_MAX_SIZE];
+ uint16_t fqdn_offset = 0;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len");
+ goto insufficient_data;
+ }
+ SCLogDebug("qry length %u", *data);
+
+ while (*data != 0) {
+ uint8_t length = *data;
+ data++;
+
+ if (length == 0)
+ break;
+
+ if (input + input_len < data + length) {
+ SCLogDebug("input buffer too small for domain of len %u", length);
+ goto insufficient_data;
+ }
+ //PrintRawDataFp(stdout, data, length);
+
+ if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
+ memcpy(fqdn + fqdn_offset, data, length);
+ fqdn_offset += length;
+ fqdn[fqdn_offset++] = '.';
+ }
+
+ data += length;
+
+ if (input + input_len < data + 1) {
+ SCLogDebug("input buffer too small for len");
+ goto insufficient_data;
+ }
+
+ SCLogDebug("length %u", *data);
+ }
+ if (fqdn_offset) {
+ fqdn_offset--;
+ }
+
+ data++;
+ if (input + input_len < data + sizeof(DNSQueryTrailer)) {
+ SCLogDebug("input buffer too small for DNSQueryTrailer");
+ goto insufficient_data;
+ }
+#if DEBUG
+ DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
+ SCLogDebug("trailer type %04x class %04x", ntohs(trailer->type), ntohs(trailer->class));
+#endif
+ data += sizeof(DNSQueryTrailer);
+ }
+
+ SCLogDebug("answer_rr %04x", ntohs(dns_header->answer_rr));
+ for (q = 0; q < ntohs(dns_header->answer_rr); q++) {
+ data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_ANSWER,
+ input, input_len, data);
+ if (data == NULL) {
+ goto insufficient_data;
+ }
+ }
+
+ SCLogDebug("authority_rr %04x", ntohs(dns_header->authority_rr));
+ for (q = 0; q < ntohs(dns_header->authority_rr); q++) {
+ data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_AUTHORITY,
+ input, input_len, data);
+ if (data == NULL) {
+ goto insufficient_data;
+ }
+ }
+
+ /* parse rcode, e.g. "noerror" or "nxdomain" */
+ uint8_t rcode = ntohs(dns_header->flags) & 0x0F;
+ if (rcode <= DNS_RCODE_NOTZONE) {
+ SCLogDebug("rcode %u", rcode);
+ if (tx != NULL)
+ tx->rcode = rcode;
+ } else {
+ /* this is not invalid, rcodes can be user defined */
+ SCLogDebug("unexpected DNS rcode %u", rcode);
+ }
+
+ if (ntohs(dns_header->flags) & 0x0080) {
+ SCLogDebug("recursion desired");
+ if (tx != NULL)
+ tx->recursion_desired = 1;
+ }
+
+ if (tx != NULL) {
+ tx->replied = 1;
+ }
+
+ SCReturnInt(1);
+
+bad_data:
+insufficient_data:
+ DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
+ SCReturnInt(-1);
+}
+
+static uint16_t DNSUdpProbingParser(uint8_t *input, uint32_t ilen, uint32_t *offset)
+{
+ if (ilen == 0 || ilen < sizeof(DNSHeader)) {
+ SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
+ return ALPROTO_UNKNOWN;
+ }
+
+ if (DNSUDPRequestParse(NULL, NULL, NULL, input, ilen, NULL) == -1)
+ return ALPROTO_FAILED;
+
+ return ALPROTO_DNS;
+}
+
+static void DNSUDPConfigure(void)
+{
+ uint32_t request_flood = DNS_CONFIG_DEFAULT_REQUEST_FLOOD;
+ uint32_t state_memcap = DNS_CONFIG_DEFAULT_STATE_MEMCAP;
+ uint64_t global_memcap = DNS_CONFIG_DEFAULT_GLOBAL_MEMCAP;
+
+ ConfNode *p = ConfGetNode("app-layer.protocols.dns.request-flood");
+ if (p != NULL) {
+ uint32_t value;
+ if (ParseSizeStringU32(p->val, &value) < 0) {
+ SCLogError(SC_ERR_DNS_CONFIG, "invalid value for request-flood %s", p->val);
+ } else {
+ request_flood = value;
+ }
+ }
+ SCLogInfo("DNS request flood protection level: %u", request_flood);
+ DNSConfigSetRequestFlood(request_flood);
+
+ p = ConfGetNode("app-layer.protocols.dns.state-memcap");
+ if (p != NULL) {
+ uint32_t value;
+ if (ParseSizeStringU32(p->val, &value) < 0) {
+ SCLogError(SC_ERR_DNS_CONFIG, "invalid value for state-memcap %s", p->val);
+ } else {
+ state_memcap = value;
+ }
+ }
+ SCLogInfo("DNS per flow memcap (state-memcap): %u", state_memcap);
+ DNSConfigSetStateMemcap(state_memcap);
+
+ p = ConfGetNode("app-layer.protocols.dns.global-memcap");
+ if (p != NULL) {
+ uint64_t value;
+ if (ParseSizeStringU64(p->val, &value) < 0) {
+ SCLogError(SC_ERR_DNS_CONFIG, "invalid value for global-memcap %s", p->val);
+ } else {
+ global_memcap = value;
+ }
+ }
+ SCLogInfo("DNS global memcap: %"PRIu64, global_memcap);
+ DNSConfigSetGlobalMemcap(global_memcap);
+}
+
+void RegisterDNSUDPParsers(void)
+{
+ char *proto_name = "dns";
+
+ /** DNS */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+ "53",
+ ALPROTO_DNS,
+ 0, sizeof(DNSHeader),
+ STREAM_TOSERVER,
+ DNSUdpProbingParser);
+ } else {
+ int have_cfg = AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
+ proto_name, ALPROTO_DNS,
+ 0, sizeof(DNSHeader),
+ DNSUdpProbingParser);
+ /* if we have no config, we enable the default port 53 */
+ if (!have_cfg) {
+ SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS UDP config found, "
+ "enabling DNS detection on "
+ "port 53.");
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53",
+ ALPROTO_DNS, 0, sizeof(DNSHeader),
+ STREAM_TOSERVER, DNSUdpProbingParser);
+ }
+ }
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("udp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOSERVER,
+ DNSUDPRequestParse);
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOCLIENT,
+ DNSUDPResponseParse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_DNS, DNSStateAlloc,
+ DNSStateFree);
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_DNS,
+ DNSStateTransactionFree);
+
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_DNS, DNSGetEvents);
+ AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_DNS, DNSHasEvents);
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
+ DNSStateHasTxDetectState,
+ DNSGetTxDetectState, DNSSetTxDetectState);
+
+ AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_DNS,
+ DNSGetTx);
+ AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_DNS,
+ DNSGetTxCnt);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_DNS,
+ DNSGetAlstateProgress);
+ AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_UDP, ALPROTO_DNS,
+ DNSGetAlstateProgressCompletionStatus);
+
+ DNSAppLayerRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_DNS);
+
+ DNSUDPConfigure();
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_DNS, DNSUDPParserRegisterTests);
+#endif
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+#include "util-unittest-helper.h"
+
+static int DNSUDPParserTest01 (void)
+{
+ int result = 0;
+ /* query: abcdefghijk.com
+ * TTL: 86400
+ * serial 20130422 refresh 28800 retry 7200 exp 604800 min ttl 86400
+ * ns, hostmaster */
+ uint8_t buf[] = { 0x00, 0x3c, 0x85, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x0b, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01,
+ 0x51, 0x80, 0x00, 0x25, 0x02, 0x6e, 0x73, 0x00,
+ 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x73,
+ 0x74, 0x65, 0x72, 0xc0, 0x2f, 0x01, 0x33, 0x2a,
+ 0x76, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1c,
+ 0x20, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51,
+ 0x80};
+ size_t buflen = sizeof(buf);
+ Flow *f = NULL;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
+ if (f == NULL)
+ goto end;
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = DNSStateAlloc();
+
+ int r = DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL);
+ if (r != 1)
+ goto end;
+
+ result = 1;
+end:
+ UTHFreeFlow(f);
+ return (result);
+}
+
+static int DNSUDPParserTest02 (void)
+{
+ int result = 0;
+ uint8_t buf[] = {
+ 0x6D,0x08,0x84,0x80,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x01,0x03,0x57,0x57,0x57,
+ 0x04,0x54,0x54,0x54,0x54,0x03,0x56,0x56,0x56,0x03,0x63,0x6F,0x6D,0x02,0x79,0x79,
+ 0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,
+ 0x02,0xC0,0x0C,0xC0,0x31,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,
+ 0x31,0xC0,0x3F,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x3F,0xC0,
+ 0x4D,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x4D,0xC0,0x5B,0x00,
+ 0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x5B,0xC0,0x69,0x00,0x05,0x00,
+ 0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x69,0xC0,0x77,0x00,0x05,0x00,0x01,0x00,
+ 0x00,0x0E,0x10,0x00,0x02,0xC0,0x77,0xC0,0x85,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,
+ 0x10,0x00,0x02,0xC0,0x85,0x00,0x00,0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ };
+ size_t buflen = sizeof(buf);
+ Flow *f = NULL;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
+ if (f == NULL)
+ goto end;
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = DNSStateAlloc();
+
+ int r = DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL);
+ if (r != 1)
+ goto end;
+
+ result = 1;
+end:
+ UTHFreeFlow(f);
+ return (result);
+}
+
+static int DNSUDPParserTest03 (void)
+{
+ int result = 0;
+ uint8_t buf[] = {
+ 0x6F,0xB4,0x84,0x80,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x03,0x03,0x57,0x57,0x77,
+ 0x0B,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x03,0x55,0x55,0x55,
+ 0x02,0x79,0x79,0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,
+ 0x0E,0x10,0x00,0x02,0xC0,0x10,0xC0,0x34,0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,
+ 0x00,0x04,0xC3,0xEA,0x04,0x19,0xC0,0x34,0x00,0x02,0x00,0x01,0x00,0x00,0x0E,0x10,
+ 0x00,0x0A,0x03,0x6E,0x73,0x31,0x03,0x61,0x67,0x62,0xC0,0x20,0xC0,0x46,0x00,0x02,
+ 0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x06,0x03,0x6E,0x73,0x32,0xC0,0x56,0xC0,0x52,
+ 0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x04,0x0A,0xC0,0x68,
+ 0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x05,0x14,0x00,0x00,
+ 0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ };
+ size_t buflen = sizeof(buf);
+ Flow *f = NULL;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
+ if (f == NULL)
+ goto end;
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = DNSStateAlloc();
+
+ int r = DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL);
+ if (r != 1)
+ goto end;
+
+ result = 1;
+end:
+ UTHFreeFlow(f);
+ return (result);
+}
+
+/** \test TXT records in answer */
+static int DNSUDPParserTest04 (void)
+{
+ int result = 0;
+ uint8_t buf[] = {
+ 0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
+ 0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
+ 0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
+ 0x01,
+ /* answer record start */
+ 0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
+ /* txt record starts: */
+ 0x20, /* <txt len 32 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
+ 0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
+ 0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01,
+ 0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
+ 0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
+ };
+ size_t buflen = sizeof(buf);
+ Flow *f = NULL;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
+ if (f == NULL)
+ goto end;
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = DNSStateAlloc();
+
+ int r = DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL);
+ if (r != 1)
+ goto end;
+
+ result = 1;
+end:
+ UTHFreeFlow(f);
+ return (result);
+}
+
+/** \test TXT records in answer, bad txtlen */
+static int DNSUDPParserTest05 (void)
+{
+ int result = 0;
+ uint8_t buf[] = {
+ 0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
+ 0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
+ 0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
+ 0x01,
+ /* answer record start */
+ 0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
+ /* txt record starts: */
+ 0x40, /* <txt len 64 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
+ 0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
+ 0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01,
+ 0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
+ 0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
+ };
+ size_t buflen = sizeof(buf);
+ Flow *f = NULL;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
+ if (f == NULL)
+ goto end;
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = DNSStateAlloc();
+
+ int r = DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL);
+ if (r != -1)
+ goto end;
+
+ result = 1;
+end:
+ UTHFreeFlow(f);
+ return (result);
+}
+
+
+void DNSUDPParserRegisterTests(void)
+{
+ UtRegisterTest("DNSUDPParserTest01", DNSUDPParserTest01, 1);
+ UtRegisterTest("DNSUDPParserTest02", DNSUDPParserTest02, 1);
+ UtRegisterTest("DNSUDPParserTest03", DNSUDPParserTest03, 1);
+ UtRegisterTest("DNSUDPParserTest04", DNSUDPParserTest04, 1);
+ UtRegisterTest("DNSUDPParserTest05", DNSUDPParserTest05, 1);
+}
+#endif
diff --git a/framework/src/suricata/src/app-layer-dns-udp.h b/framework/src/suricata/src/app-layer-dns-udp.h
new file mode 100644
index 00000000..a6ee12a8
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-dns-udp.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __APP_LAYER_DNS_UDP_H__
+#define __APP_LAYER_DNS_UDP_H__
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-dns-common.h"
+#include "flow.h"
+#include "queue.h"
+#include "util-byte.h"
+
+void RegisterDNSUDPParsers(void);
+void DNSUDPParserTests(void);
+void DNSUDPParserRegisterTests(void);
+
+#endif /* __APP_LAYER_DNS_UDP_H__ */
diff --git a/framework/src/suricata/src/app-layer-events.c b/framework/src/suricata/src/app-layer-events.c
new file mode 100644
index 00000000..cd00a4ee
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-events.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "flow.h"
+#include "app-layer-events.h"
+#include "app-layer-parser.h"
+#include "util-enum.h"
+
+/* events raised during protocol detection are stored in the
+ * packets storage, not in the flow. */
+SCEnumCharMap app_layer_event_pkt_table[ ] = {
+ { "APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS",
+ APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS },
+ { "APPLAYER_WRONG_DIRECTION_FIRST_DATA",
+ APPLAYER_WRONG_DIRECTION_FIRST_DATA },
+ { "APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION",
+ APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION },
+ { "APPLAYER_PROTO_DETECTION_SKIPPED",
+ APPLAYER_PROTO_DETECTION_SKIPPED },
+ { NULL,
+ -1 },
+};
+
+int AppLayerGetPktEventInfo(const char *event_name, int *event_id)
+{
+ *event_id = SCMapEnumNameToValue(event_name, app_layer_event_pkt_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "app-layer-event's packet event table.", event_name);
+ /* this should be treated as fatal */
+ return -1;
+ }
+
+ return 0;
+}
+
+#define DECODER_EVENTS_BUFFER_STEPS 8
+
+/**
+ * \brief Set an app layer decoder event.
+ *
+ * \param sevents Pointer to a AppLayerDecoderEvents pointer. If *sevents is NULL
+ * memory will be allocated.
+ * \param event The event to be stored.
+ */
+void AppLayerDecoderEventsSetEventRaw(AppLayerDecoderEvents **sevents, uint8_t event)
+{
+ if (*sevents == NULL) {
+ AppLayerDecoderEvents *new_devents = SCMalloc(sizeof(AppLayerDecoderEvents));
+ if (new_devents == NULL)
+ return;
+
+ memset(new_devents, 0, sizeof(AppLayerDecoderEvents));
+ *sevents = new_devents;
+
+ }
+ if ((*sevents)->cnt == UCHAR_MAX) {
+ /* we're full */
+ return;
+ }
+ if ((*sevents)->cnt == (*sevents)->events_buffer_size) {
+ int steps = DECODER_EVENTS_BUFFER_STEPS;
+ if (UCHAR_MAX - (*sevents)->cnt < steps)
+ steps = UCHAR_MAX - (*sevents)->cnt < steps;
+
+ void *ptr = SCRealloc((*sevents)->events,
+ ((*sevents)->cnt + steps) * sizeof(uint8_t));
+ if (ptr == NULL) {
+ /* couldn't grow buffer, but no reason to free old
+ * so we keep the events that may already be here */
+ return;
+ }
+ (*sevents)->events = ptr;
+ (*sevents)->events_buffer_size += steps;
+ }
+
+ (*sevents)->events[(*sevents)->cnt++] = event;
+}
+
+/**
+ * \brief Set an app layer decoder event.
+ *
+ * \param f Pointer to a flow containing DecoderEvents pointer head. If
+ * the head points to a DecoderEvents instance, a
+ * new instance would be created and the pointer head would
+ * would be updated with this new instance
+ * \param event The event to be stored.
+ */
+void AppLayerDecoderEventsSetEvent(Flow *f, uint8_t event)
+{
+ AppLayerDecoderEvents *events = AppLayerParserGetDecoderEvents(f->alparser);
+ AppLayerDecoderEvents *new = events;
+ AppLayerDecoderEventsSetEventRaw(&events, event);
+ if (events != new)
+ AppLayerParserSetDecoderEvents(f->alparser, events);
+}
+
+void AppLayerDecoderEventsResetEvents(AppLayerDecoderEvents *events)
+{
+ if (events != NULL)
+ events->cnt = 0;
+}
+
+
+void AppLayerDecoderEventsFreeEvents(AppLayerDecoderEvents **events)
+{
+ if (events && *events != NULL) {
+ if ((*events)->events != NULL)
+ SCFree((*events)->events);
+ SCFree(*events);
+ *events = NULL;
+ }
+}
+
diff --git a/framework/src/suricata/src/app-layer-events.h b/framework/src/suricata/src/app-layer-events.h
new file mode 100644
index 00000000..11dfb9e6
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-events.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_EVENTS_H__
+#define __APP_LAYER_EVENTS_H__
+
+/* contains fwd declaration of AppLayerDecoderEvents_ */
+#include "decode.h"
+
+/**
+ * \brief Data structure to store app layer decoder events.
+ */
+struct AppLayerDecoderEvents_ {
+ /* array of events */
+ uint8_t *events;
+ /* number of events in the above buffer */
+ uint8_t cnt;
+ /* current event buffer size */
+ uint8_t events_buffer_size;
+};
+
+/* app layer pkt level events */
+enum {
+ APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS,
+ APPLAYER_WRONG_DIRECTION_FIRST_DATA,
+ APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION,
+ APPLAYER_PROTO_DETECTION_SKIPPED,
+};
+
+/* the event types for app events */
+typedef enum AppLayerEventType_ {
+ APP_LAYER_EVENT_TYPE_GENERAL = 1,
+ APP_LAYER_EVENT_TYPE_TRANSACTION,
+ APP_LAYER_EVENT_TYPE_PACKET,
+} AppLayerEventType;
+
+int AppLayerGetPktEventInfo(const char *event_name, int *event_id);
+
+void AppLayerDecoderEventsSetEventRaw(AppLayerDecoderEvents **sevents, uint8_t event);
+void AppLayerDecoderEventsSetEvent(Flow *f, uint8_t event);
+
+static inline int AppLayerDecoderEventsIsEventSet(AppLayerDecoderEvents *devents,
+ uint8_t event)
+{
+ if (devents == NULL)
+ return 0;
+
+ int i;
+ int cnt = devents->cnt;
+ for (i = 0; i < cnt; i++) {
+ if (devents->events[i] == event)
+ return 1;
+ }
+
+ return 0;
+}
+
+void AppLayerDecoderEventsResetEvents(AppLayerDecoderEvents *events);
+void AppLayerDecoderEventsFreeEvents(AppLayerDecoderEvents **events);
+
+#endif /* __APP_LAYER_EVENTS_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-ftp.c b/framework/src/suricata/src/app-layer-ftp.c
new file mode 100644
index 00000000..b5d4a03d
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ftp.c
@@ -0,0 +1,681 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * App Layer Parser for FTP
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "flow-util.h"
+
+#include "detect-engine-state.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-ftp.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-memcmp.h"
+
+static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
+{
+ void *ptmp;
+ if (line_state->current_line_lf_seen == 1) {
+ /* we have seen the lf for the previous line. Clear the parser
+ * details to parse new line */
+ line_state->current_line_lf_seen = 0;
+ if (line_state->current_line_db == 1) {
+ line_state->current_line_db = 0;
+ SCFree(line_state->db);
+ line_state->db = NULL;
+ line_state->db_len = 0;
+ state->current_line = NULL;
+ state->current_line_len = 0;
+ }
+ }
+
+ uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
+
+ if (lf_idx == NULL) {
+ /* fragmented lines. Decoder event for special cases. Not all
+ * fragmented lines should be treated as a possible evasion
+ * attempt. With multi payload ftp chunks we can have valid
+ * cases of fragmentation. But within the same segment chunk
+ * if we see fragmentation then it's definitely something you
+ * should alert about */
+ if (line_state->current_line_db == 0) {
+ line_state->db = SCMalloc(state->input_len);
+ if (line_state->db == NULL) {
+ return -1;
+ }
+ line_state->current_line_db = 1;
+ memcpy(line_state->db, state->input, state->input_len);
+ line_state->db_len = state->input_len;
+ } else {
+ ptmp = SCRealloc(line_state->db,
+ (line_state->db_len + state->input_len));
+ if (ptmp == NULL) {
+ SCFree(line_state->db);
+ line_state->db = NULL;
+ line_state->db_len = 0;
+ return -1;
+ }
+ line_state->db = ptmp;
+
+ memcpy(line_state->db + line_state->db_len,
+ state->input, state->input_len);
+ line_state->db_len += state->input_len;
+ }
+ state->input += state->input_len;
+ state->input_len = 0;
+
+ return -1;
+
+ } else {
+ line_state->current_line_lf_seen = 1;
+
+ if (line_state->current_line_db == 1) {
+ ptmp = SCRealloc(line_state->db,
+ (line_state->db_len + (lf_idx + 1 - state->input)));
+ if (ptmp == NULL) {
+ SCFree(line_state->db);
+ line_state->db = NULL;
+ line_state->db_len = 0;
+ return -1;
+ }
+ line_state->db = ptmp;
+
+ memcpy(line_state->db + line_state->db_len,
+ state->input, (lf_idx + 1 - state->input));
+ line_state->db_len += (lf_idx + 1 - state->input);
+
+ if (line_state->db_len > 1 &&
+ line_state->db[line_state->db_len - 2] == 0x0D) {
+ line_state->db_len -= 2;
+ state->current_line_delimiter_len = 2;
+ } else {
+ line_state->db_len -= 1;
+ state->current_line_delimiter_len = 1;
+ }
+
+ state->current_line = line_state->db;
+ state->current_line_len = line_state->db_len;
+
+ } else {
+ state->current_line = state->input;
+ state->current_line_len = lf_idx - state->input;
+
+ if (state->input != lf_idx &&
+ *(lf_idx - 1) == 0x0D) {
+ state->current_line_len--;
+ state->current_line_delimiter_len = 2;
+ } else {
+ state->current_line_delimiter_len = 1;
+ }
+ }
+
+ state->input_len -= (lf_idx - state->input) + 1;
+ state->input = (lf_idx + 1);
+
+ return 0;
+ }
+
+}
+
+static int FTPGetLine(FtpState *state)
+{
+ SCEnter();
+
+ /* we have run out of input */
+ if (state->input_len <= 0)
+ return -1;
+
+ /* toserver */
+ if (state->direction == 0)
+ return FTPGetLineForDirection(state, &state->line_state[0]);
+ else
+ return FTPGetLineForDirection(state, &state->line_state[1]);
+}
+
+/**
+ * \brief This function is called to determine and set which command is being
+ * transfered to the ftp server
+ * \param ftp_state the ftp state structure for the parser
+ * \param input input line of the command
+ * \param len of the command
+ *
+ * \retval 1 when the command is parsed, 0 otherwise
+ */
+static int FTPParseRequestCommand(void *ftp_state, uint8_t *input,
+ uint32_t input_len)
+{
+ SCEnter();
+ FtpState *fstate = (FtpState *)ftp_state;
+ fstate->command = FTP_COMMAND_UNKNOWN;
+
+ if (input_len >= 4) {
+ if (SCMemcmpLowercase("port", input, 4) == 0) {
+ fstate->command = FTP_COMMAND_PORT;
+ }
+
+ /* else {
+ * Add the ftp commands you need here
+ * }
+ */
+ }
+ return 1;
+}
+
+/**
+ * \brief This function is called to retrieve a ftp request
+ * \param ftp_state the ftp state structure for the parser
+ * \param input input line of the command
+ * \param input_len length of the request
+ * \param output the resulting output
+ *
+ * \retval 1 when the command is parsed, 0 otherwise
+ */
+static int FTPParseRequest(Flow *f, void *ftp_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ /* PrintRawDataFp(stdout, input,input_len); */
+
+ FtpState *state = (FtpState *)ftp_state;
+ void *ptmp;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ state->input = input;
+ state->input_len = input_len;
+ /* toserver stream */
+ state->direction = 0;
+
+ while (FTPGetLine(state) >= 0) {
+ FTPParseRequestCommand(state,
+ state->current_line, state->current_line_len);
+ if (state->command == FTP_COMMAND_PORT) {
+ if (state->current_line_len > state->port_line_size) {
+ ptmp = SCRealloc(state->port_line, state->current_line_len);
+ if (ptmp == NULL) {
+ SCFree(state->port_line);
+ state->port_line = NULL;
+ state->port_line_size = 0;
+ return 0;
+ }
+ state->port_line = ptmp;
+
+ state->port_line_size = state->current_line_len;
+ }
+ memcpy(state->port_line, state->current_line,
+ state->current_line_len);
+ state->port_line_len = state->current_line_len;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * \brief This function is called to retrieve a ftp response
+ * \param ftp_state the ftp state structure for the parser
+ * \param input input line of the command
+ * \param input_len length of the request
+ * \param output the resulting output
+ *
+ * \retval 1 when the command is parsed, 0 otherwise
+ */
+static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return 1;
+}
+
+#ifdef DEBUG
+static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
+static uint64_t ftp_state_memuse = 0;
+static uint64_t ftp_state_memcnt = 0;
+#endif
+
+static void *FTPStateAlloc(void)
+{
+ void *s = SCMalloc(sizeof(FtpState));
+ if (unlikely(s == NULL))
+ return NULL;
+
+ memset(s, 0, sizeof(FtpState));
+
+#ifdef DEBUG
+ SCMutexLock(&ftp_state_mem_lock);
+ ftp_state_memcnt++;
+ ftp_state_memuse+=sizeof(FtpState);
+ SCMutexUnlock(&ftp_state_mem_lock);
+#endif
+ return s;
+}
+
+static void FTPStateFree(void *s)
+{
+ FtpState *fstate = (FtpState *) s;
+ if (fstate->port_line != NULL)
+ SCFree(fstate->port_line);
+ if (fstate->line_state[0].db)
+ SCFree(fstate->line_state[0].db);
+ if (fstate->line_state[1].db)
+ SCFree(fstate->line_state[1].db);
+ SCFree(s);
+#ifdef DEBUG
+ SCMutexLock(&ftp_state_mem_lock);
+ ftp_state_memcnt--;
+ ftp_state_memuse-=sizeof(FtpState);
+ SCMutexUnlock(&ftp_state_mem_lock);
+#endif
+}
+
+static int FTPRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
+ "USER ", 5, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
+ "PASS ", 5, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
+ "PORT ", 5, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+void RegisterFTPParsers(void)
+{
+ char *proto_name = "ftp";
+
+ /** FTP */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);
+ if (FTPRegisterPatternsForProtocolDetection() < 0 )
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
+ FTPParseRequest);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
+ FTPParseResponse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
+#endif
+}
+
+void FTPAtExitPrintStats(void)
+{
+#ifdef DEBUG
+ SCMutexLock(&ftp_state_mem_lock);
+ SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
+ ftp_state_memcnt, ftp_state_memuse);
+ SCMutexUnlock(&ftp_state_mem_lock);
+#endif
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+/** \test Send a get request in one chunk. */
+int FTPParserTest01(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
+ uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf, ftplen);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Send a splitted get request. */
+int FTPParserTest03(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t ftpbuf1[] = "POR";
+ uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
+ uint8_t ftpbuf2[] = "T 192,168,1";
+ uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
+ uint8_t ftpbuf3[] = "1,1,10,20\r\n";
+ uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf3, ftplen3);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test See how it deals with an incomplete request. */
+int FTPParserTest06(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t ftpbuf1[] = "PORT";
+ uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, ftpbuf1, ftplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
+ SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test See how it deals with an incomplete request in multiple chunks. */
+int FTPParserTest07(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t ftpbuf1[] = "PO";
+ uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
+ uint8_t ftpbuf2[] = "RT\r\n";
+ uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf2, ftplen2);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
+ FTP_COMMAND_PORT, ftp_state->command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Test case where chunks are smaller than the delim length and the
+ * last chunk is supposed to match the delim. */
+int FTPParserTest10(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
+ uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < ftplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, flags, &ftpbuf1[u], 1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ }
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void FTPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FTPParserTest01", FTPParserTest01, 1);
+ UtRegisterTest("FTPParserTest03", FTPParserTest03, 1);
+ UtRegisterTest("FTPParserTest06", FTPParserTest06, 1);
+ UtRegisterTest("FTPParserTest07", FTPParserTest07, 1);
+ UtRegisterTest("FTPParserTest10", FTPParserTest10, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/app-layer-ftp.h b/framework/src/suricata/src/app-layer-ftp.h
new file mode 100644
index 00000000..4a001290
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ftp.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __APP_LAYER_FTP_H__
+#define __APP_LAYER_FTP_H__
+
+typedef enum {
+ FTP_COMMAND_UNKNOWN = 0,
+ FTP_COMMAND_ABOR,
+ FTP_COMMAND_ACCT,
+ FTP_COMMAND_ALLO,
+ FTP_COMMAND_APPE,
+ FTP_COMMAND_CDUP,
+ FTP_COMMAND_CHMOD,
+ FTP_COMMAND_CWD,
+ FTP_COMMAND_DELE,
+ FTP_COMMAND_HELP,
+ FTP_COMMAND_IDLE,
+ FTP_COMMAND_LIST,
+ FTP_COMMAND_MAIL,
+ FTP_COMMAND_MDTM,
+ FTP_COMMAND_MKD,
+ FTP_COMMAND_MLFL,
+ FTP_COMMAND_MODE,
+ FTP_COMMAND_MRCP,
+ FTP_COMMAND_MRSQ,
+ FTP_COMMAND_MSAM,
+ FTP_COMMAND_MSND,
+ FTP_COMMAND_MSOM,
+ FTP_COMMAND_NLST,
+ FTP_COMMAND_NOOP,
+ FTP_COMMAND_PASS,
+ FTP_COMMAND_PASV,
+ FTP_COMMAND_PORT,
+ FTP_COMMAND_PWD,
+ FTP_COMMAND_QUIT,
+ FTP_COMMAND_REIN,
+ FTP_COMMAND_REST,
+ FTP_COMMAND_RETR,
+ FTP_COMMAND_RMD,
+ FTP_COMMAND_RNFR,
+ FTP_COMMAND_RNTO,
+ FTP_COMMAND_SITE,
+ FTP_COMMAND_SIZE,
+ FTP_COMMAND_SMNT,
+ FTP_COMMAND_STAT,
+ FTP_COMMAND_STOR,
+ FTP_COMMAND_STOU,
+ FTP_COMMAND_STRU,
+ FTP_COMMAND_SYST,
+ FTP_COMMAND_TYPE,
+ FTP_COMMAND_UMASK,
+ FTP_COMMAND_USER
+ /** \todo more if missing.. */
+} FtpRequestCommand;
+typedef uint32_t FtpRequestCommandArgOfs;
+
+typedef uint16_t FtpResponseCode;
+
+enum {
+ FTP_FIELD_NONE = 0,
+
+ FTP_FIELD_REQUEST_LINE,
+ FTP_FIELD_REQUEST_COMMAND,
+ FTP_FIELD_REQUEST_ARGS,
+
+ FTP_FIELD_RESPONSE_LINE,
+ FTP_FIELD_REPONSE_CODE,
+
+ /* must be last */
+ FTP_FIELD_MAX,
+};
+
+/** used to hold the line state when we have fragmentation. */
+typedef struct FtpLineState_ {
+ /** used to indicate if the current_line buffer is a malloced buffer. We
+ * use a malloced buffer, if a line is fragmented */
+ uint8_t *db;
+ uint32_t db_len;
+ uint8_t current_line_db;
+ /** we have see LF for the currently parsed line */
+ uint8_t current_line_lf_seen;
+} FtpLineState;
+
+/** FTP State for app layer parser */
+typedef struct FtpState_ {
+ uint8_t *input;
+ int32_t input_len;
+ uint8_t direction;
+
+ /* --parser details-- */
+ /** current line extracted by the parser from the call to FTPGetline() */
+ uint8_t *current_line;
+ /** length of the line in current_line. Doesn't include the delimiter */
+ uint32_t current_line_len;
+ uint8_t current_line_delimiter_len;
+
+ /* 0 for toserver, 1 for toclient */
+ FtpLineState line_state[2];
+
+ FtpRequestCommand command;
+ FtpRequestCommandArgOfs arg_offset;
+ uint32_t port_line_len;
+ uint32_t port_line_size;
+ uint8_t *port_line;
+} FtpState;
+
+void RegisterFTPParsers(void);
+void FTPParserRegisterTests(void);
+void FTPAtExitPrintStats(void);
+
+#endif /* __APP_LAYER_FTP_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-htp-body.c b/framework/src/suricata/src/app-layer-htp-body.c
new file mode 100644
index 00000000..6454fe1c
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-body.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * This file provides a HTTP protocol support for the engine using HTP library.
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-radix-tree.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+
+#include "util-spm.h"
+#include "util-debug.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-file.h"
+#include "util-time.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "flow-util.h"
+
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+
+#include "conf.h"
+
+#include "util-memcmp.h"
+
+/**
+ * \brief Append a chunk of body to the HtpBody struct
+ *
+ * \param body pointer to the HtpBody holding the list
+ * \param data pointer to the data of the chunk
+ * \param len length of the chunk pointed by data
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int HtpBodyAppendChunk(HtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32_t len)
+{
+ SCEnter();
+
+ HtpBodyChunk *bd = NULL;
+
+ if (len == 0 || data == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (body->first == NULL) {
+ /* New chunk */
+ bd = (HtpBodyChunk *)HTPMalloc(sizeof(HtpBodyChunk));
+ if (bd == NULL)
+ goto error;
+
+ bd->len = len;
+ bd->stream_offset = 0;
+ bd->next = NULL;
+ bd->logged = 0;
+
+ bd->data = HTPMalloc(len);
+ if (bd->data == NULL) {
+ goto error;
+ }
+ memcpy(bd->data, data, len);
+
+ body->first = body->last = bd;
+
+ body->content_len_so_far = len;
+ } else {
+ bd = (HtpBodyChunk *)HTPMalloc(sizeof(HtpBodyChunk));
+ if (bd == NULL)
+ goto error;
+
+ bd->len = len;
+ bd->stream_offset = body->content_len_so_far;
+ bd->next = NULL;
+ bd->logged = 0;
+
+ bd->data = HTPMalloc(len);
+ if (bd->data == NULL) {
+ goto error;
+ }
+ memcpy(bd->data, data, len);
+
+ body->last->next = bd;
+ body->last = bd;
+
+ body->content_len_so_far += len;
+ }
+ SCLogDebug("Body %p; data %p, len %"PRIu32, body, bd->data, (uint32_t)bd->len);
+
+ SCReturnInt(0);
+
+error:
+ if (bd != NULL) {
+ if (bd->data != NULL) {
+ HTPFree(bd->data, bd->len);
+ }
+ HTPFree(bd, sizeof(HtpBodyChunk));
+ }
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Print the information and chunks of a Body
+ * \param body pointer to the HtpBody holding the list
+ * \retval none
+ */
+void HtpBodyPrint(HtpBody *body)
+{
+ if (SCLogDebugEnabled()||1) {
+ SCEnter();
+
+ if (body->first == NULL)
+ return;
+
+ HtpBodyChunk *cur = NULL;
+ SCLogDebug("--- Start body chunks at %p ---", body);
+ printf("--- Start body chunks at %p ---\n", body);
+ for (cur = body->first; cur != NULL; cur = cur->next) {
+ SCLogDebug("Body %p; data %p, len %"PRIu32, body, cur->data, (uint32_t)cur->len);
+ printf("Body %p; data %p, len %"PRIu32"\n", body, cur->data, (uint32_t)cur->len);
+ PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len);
+ }
+ SCLogDebug("--- End body chunks at %p ---", body);
+ }
+}
+
+/**
+ * \brief Free the information held in the request body
+ * \param body pointer to the HtpBody holding the list
+ * \retval none
+ */
+void HtpBodyFree(HtpBody *body)
+{
+ SCEnter();
+
+ if (body->first == NULL)
+ return;
+
+ SCLogDebug("Removing chunks of Body %p; data %p, len %"PRIu32, body,
+ body->last->data, (uint32_t)body->last->len);
+
+ HtpBodyChunk *cur = NULL;
+ HtpBodyChunk *prev = NULL;
+
+ prev = body->first;
+ while (prev != NULL) {
+ cur = prev->next;
+ if (prev->data != NULL)
+ HTPFree(prev->data, prev->len);
+ HTPFree(prev, sizeof(HtpBodyChunk));
+ prev = cur;
+ }
+ body->first = body->last = NULL;
+}
+
+/**
+ * \brief Free request body chunks that are already fully parsed.
+ *
+ * \param state htp_state, with reference to our config
+ * \param body the body to prune
+ * \param direction STREAM_TOSERVER (request), STREAM_TOCLIENT (response)
+ *
+ * \retval none
+ */
+void HtpBodyPrune(HtpState *state, HtpBody *body, int direction)
+{
+ SCEnter();
+
+ if (body == NULL || body->first == NULL) {
+ SCReturn;
+ }
+
+ if (body->body_parsed == 0) {
+ SCReturn;
+ }
+
+ /* get the configured inspect sizes. Default to response values */
+ uint32_t min_size = state->cfg->response_inspect_min_size;
+ uint32_t window = state->cfg->response_inspect_window;
+
+ if (direction == STREAM_TOSERVER) {
+ min_size = state->cfg->request_inspect_min_size;
+ window = state->cfg->request_inspect_window;
+ }
+
+ if (body->body_inspected < (min_size > window) ? min_size : window) {
+ SCReturn;
+ }
+
+ SCLogDebug("Pruning chunks of Body %p; data %p, len %"PRIu32, body,
+ body->last->data, (uint32_t)body->last->len);
+
+ HtpBodyChunk *cur = body->first;
+ while (cur != NULL) {
+ HtpBodyChunk *next = cur->next;
+
+ SCLogDebug("cur->stream_offset %"PRIu64" + cur->len %u = %"PRIu64", "
+ "body->body_parsed %"PRIu64, cur->stream_offset, cur->len,
+ cur->stream_offset + cur->len, body->body_parsed);
+
+ uint64_t left_edge = body->body_inspected;
+ if (left_edge <= min_size || left_edge <= window)
+ left_edge = 0;
+ if (left_edge)
+ left_edge -= window;
+
+ if (cur->stream_offset + cur->len > left_edge) {
+ break;
+ }
+
+ body->first = next;
+ if (body->last == cur) {
+ body->last = next;
+ }
+
+ if (cur->data != NULL) {
+ HTPFree(cur->data, cur->len);
+ }
+ HTPFree(cur, sizeof(HtpBodyChunk));
+
+ cur = next;
+ }
+
+ SCReturn;
+}
diff --git a/framework/src/suricata/src/app-layer-htp-body.h b/framework/src/suricata/src/app-layer-htp-body.h
new file mode 100644
index 00000000..6d54f0d5
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-body.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * This file provides a HTTP protocol support for the engine using HTP library.
+ */
+
+#ifndef __APP_LAYER_HTP_BODY_H__
+#define __APP_LAYER_HTP_BODY_H__
+
+int HtpBodyAppendChunk(HtpTxUserData *, HtpBody *, uint8_t *, uint32_t);
+void HtpBodyPrint(HtpBody *);
+void HtpBodyFree(HtpBody *);
+void HtpBodyPrune(HtpState *, HtpBody *, int);
+
+#endif /* __APP_LAYER_HTP_BODY_H__ */
diff --git a/framework/src/suricata/src/app-layer-htp-file.c b/framework/src/suricata/src/app-layer-htp-file.c
new file mode 100644
index 00000000..d8659f33
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-file.c
@@ -0,0 +1,1635 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * This file provides HTTP protocol file handling support for the engine
+ * using HTP library.
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-radix-tree.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+
+#include "util-spm.h"
+#include "util-debug.h"
+#include "app-layer-htp.h"
+#include "util-time.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "flow-util.h"
+
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+
+#include "conf.h"
+
+#include "util-memcmp.h"
+
+/**
+ * \brief Open the file with "filename" and pass the first chunk
+ * of data if any.
+ *
+ * \param s http state
+ * \param filename name of the file
+ * \param filename_len length of the name
+ * \param data data chunk (if any)
+ * \param data_len length of the data portion
+ * \param direction flow direction
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * \retval -2 not handling files on this flow
+ */
+int HTPFileOpen(HtpState *s, uint8_t *filename, uint16_t filename_len,
+ uint8_t *data, uint32_t data_len, uint16_t txid, uint8_t direction)
+{
+ int retval = 0;
+ uint8_t flags = 0;
+ FileContainer *files = NULL;
+ FileContainer *files_opposite = NULL;
+
+ SCLogDebug("data %p data_len %"PRIu32, data, data_len);
+
+ if (s == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (direction & STREAM_TOCLIENT) {
+ if (s->files_tc == NULL) {
+ s->files_tc = FileContainerAlloc();
+ if (s->files_tc == NULL) {
+ retval = -1;
+ goto end;
+ }
+ }
+
+ files = s->files_tc;
+ files_opposite = s->files_ts;
+
+ if ((s->flags & HTP_FLAG_STORE_FILES_TS) ||
+ ((s->flags & HTP_FLAG_STORE_FILES_TX_TS) && txid == s->store_tx_id)) {
+ flags |= FILE_STORE;
+ }
+
+ if (s->f->flags & FLOW_FILE_NO_MAGIC_TC) {
+ SCLogDebug("no magic for this flow in toclient direction, so none for this file");
+ flags |= FILE_NOMAGIC;
+ }
+
+ if (s->f->flags & FLOW_FILE_NO_MD5_TC) {
+ SCLogDebug("no md5 for this flow in toclient direction, so none for this file");
+ flags |= FILE_NOMD5;
+ }
+
+ if (!(flags & FILE_STORE) && (s->f->flags & FLOW_FILE_NO_STORE_TC)) {
+ flags |= FILE_NOSTORE;
+ }
+ } else {
+ if (s->files_ts == NULL) {
+ s->files_ts = FileContainerAlloc();
+ if (s->files_ts == NULL) {
+ retval = -1;
+ goto end;
+ }
+ }
+
+ files = s->files_ts;
+ files_opposite = s->files_tc;
+
+ if ((s->flags & HTP_FLAG_STORE_FILES_TC) ||
+ ((s->flags & HTP_FLAG_STORE_FILES_TX_TC) && txid == s->store_tx_id)) {
+ flags |= FILE_STORE;
+ }
+ if (s->f->flags & FLOW_FILE_NO_MAGIC_TS) {
+ SCLogDebug("no magic for this flow in toserver direction, so none for this file");
+ flags |= FILE_NOMAGIC;
+ }
+
+ if (s->f->flags & FLOW_FILE_NO_MD5_TS) {
+ SCLogDebug("no md5 for this flow in toserver direction, so none for this file");
+ flags |= FILE_NOMD5;
+ }
+
+ if (!(flags & FILE_STORE) && (s->f->flags & FLOW_FILE_NO_STORE_TS)) {
+ flags |= FILE_NOSTORE;
+ }
+ }
+
+ /* if the previous file is in the same txid, we reset the file part of the
+ * stateful detection engine. We cannot do that here directly, because of
+ * locking order. Flow is locked at this point and we can't lock flow
+ * before de_state */
+ if (files != NULL && files->tail != NULL && files->tail->txid == txid) {
+ SCLogDebug("new file in same tx, flagging http state for de_state reset");
+
+ if (direction & STREAM_TOCLIENT) {
+ s->flags |= HTP_FLAG_NEW_FILE_TX_TC;
+ } else {
+ s->flags |= HTP_FLAG_NEW_FILE_TX_TS;
+ }
+ }
+ if (files_opposite != NULL && files_opposite->tail != NULL && files_opposite->tail->txid == txid) {
+ SCLogDebug("new file in same tx, flagging http state for de_state reset");
+
+ if (direction & STREAM_TOCLIENT) {
+ SCLogDebug("flagging TC");
+ s->flags |= HTP_FLAG_NEW_FILE_TX_TC;
+ } else {
+ SCLogDebug("flagging TS");
+ s->flags |= HTP_FLAG_NEW_FILE_TX_TS;
+ }
+ }
+
+ if (FileOpenFile(files, filename, filename_len,
+ data, data_len, flags) == NULL)
+ {
+ retval = -1;
+ }
+
+ FileSetTx(files->tail, txid);
+
+ FilePrune(files);
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \brief Store a chunk of data in the flow
+ *
+ * \param s http state
+ * \param data data chunk (if any)
+ * \param data_len length of the data portion
+ * \param direction flow direction
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * \retval -2 file doesn't need storing
+ */
+int HTPFileStoreChunk(HtpState *s, uint8_t *data, uint32_t data_len,
+ uint8_t direction)
+{
+ SCEnter();
+
+ int retval = 0;
+ int result = 0;
+ FileContainer *files = NULL;
+
+ if (s == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (direction & STREAM_TOCLIENT) {
+ files = s->files_tc;
+ } else {
+ files = s->files_ts;
+ }
+
+ if (files == NULL) {
+ SCLogDebug("no files in state");
+ retval = -1;
+ goto end;
+ }
+
+ result = FileAppendData(files, data, data_len);
+ if (result == -1) {
+ SCLogDebug("appending data failed");
+ retval = -1;
+ } else if (result == -2) {
+ retval = -2;
+ }
+
+ FilePrune(files);
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \brief Close the file in the flow
+ *
+ * \param s http state
+ * \param data data chunk if any
+ * \param data_len length of the data portion
+ * \param flags flags to indicate events
+ * \param direction flow direction
+ *
+ * Currently on the FLOW_FILE_TRUNCATED flag is implemented, indicating
+ * that the file isn't complete but we're stopping storing it.
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * \retval -2 not storing files on this flow/tx
+ */
+int HTPFileClose(HtpState *s, uint8_t *data, uint32_t data_len,
+ uint8_t flags, uint8_t direction)
+{
+ SCEnter();
+
+ int retval = 0;
+ int result = 0;
+ FileContainer *files = NULL;
+
+ if (s == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (direction & STREAM_TOCLIENT) {
+ files = s->files_tc;
+ } else {
+ files = s->files_ts;
+ }
+
+ if (files == NULL) {
+ retval = -1;
+ goto end;
+ }
+
+ result = FileCloseFile(files, data, data_len, flags);
+ if (result == -1) {
+ retval = -1;
+ } else if (result == -2) {
+ retval = -2;
+ }
+
+ FilePrune(files);
+end:
+ SCReturnInt(retval);
+}
+
+#ifdef UNITTESTS
+static int HTPFileParserTest01(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ HtpState *http_state = NULL;
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+static int HTPFileParserTest02(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 337\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"email\"\r\n"
+ "\r\n"
+ "someaddress@somedomain.lan\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+static int HTPFileParserTest03(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 337\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"email\"\r\n"
+ "\r\n"
+ "someaddress@somedomain.lan\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "file";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ uint8_t httpbuf5[] = "content\r\n";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+
+ uint8_t httpbuf6[] = "-----------------------------277531038314945--";
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ goto end;
+ }
+
+ if (http_state->files_ts->head->chunks_head->len != 11) {
+ printf("filedata len not 11 but %u: ", http_state->files_ts->head->chunks_head->len);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+static int HTPFileParserTest04(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 373\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"email\"\r\n"
+ "\r\n"
+ "someaddress@somedomain.lan\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "file0123456789abcdefghijklmnopqrstuvwxyz";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ uint8_t httpbuf5[] = "content\r\n";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+
+ uint8_t httpbuf6[] = "-----------------------------277531038314945--";
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s: ", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+static int HTPFileParserTest05(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 544\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "FILECONTENT\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ goto end;
+ }
+
+ if (http_state->files_ts->head == http_state->files_ts->tail)
+ goto end;
+
+ if (http_state->files_ts->head->next != http_state->files_ts->tail)
+ goto end;
+
+ if (http_state->files_ts->head->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->head->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("filecontent", http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len) != 0) {
+ goto end;
+ }
+
+ if (http_state->files_ts->tail->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->tail->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->tail->chunks_head->data,
+ http_state->files_ts->tail->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("FILECONTENT", http_state->files_ts->tail->chunks_head->data,
+ http_state->files_ts->tail->chunks_head->len) != 0) {
+ goto end;
+ }
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test first multipart part contains file but doesn't end in first chunk */
+static int HTPFileParserTest06(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 544\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------27753103831494";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "5\r\nContent-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "FILECONTENT\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ goto end;
+ }
+
+ if (http_state->files_ts->head == http_state->files_ts->tail)
+ goto end;
+
+ if (http_state->files_ts->head->next != http_state->files_ts->tail)
+ goto end;
+
+ if (http_state->files_ts->head->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->head->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("filecontent", http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len) != 0) {
+ goto end;
+ }
+
+ if (http_state->files_ts->tail->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->tail->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->tail->chunks_head->data,
+ http_state->files_ts->tail->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("FILECONTENT", http_state->files_ts->tail->chunks_head->data,
+ http_state->files_ts->tail->chunks_head->len) != 0) {
+ goto end;
+ }
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test POST, but not multipart */
+static int HTPFileParserTest07(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /filename HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Length: 11\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "FILECONTENT";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ printf("state != FILE_STATE_CLOSED");
+ goto end;
+ }
+
+ if (http_state->files_ts->head->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->head->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("FILECONTENT", http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+static int HTPFileParserTest08(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "filecontent\r\n\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ HtpState *http_state = NULL;
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events == NULL) {
+ printf("no app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ if (decoder_events->cnt != 2) {
+ printf("expected 2 events: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test invalid header: Somereallylongheaderstr: has no value */
+static int HTPFileParserTest09(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 337\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"email\"\r\n"
+ "\r\n"
+ "someaddress@somedomain.lan\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Somereallylongheaderstr:\r\n"
+ "\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events == NULL) {
+ printf("no app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ if (decoder_events->cnt != 1) {
+ printf("expected 1 event: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test empty entries */
+static int HTPFileParserTest10(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 337\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
+ "\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Somereallylongheaderstr: with a good value\r\n"
+ "\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events != NULL) {
+ printf("app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test filedata cut in two pieces */
+static int HTPFileParserTest11(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Length: 1102\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "Content-Disposition: form-data; name=\"PROGRESS_URL\"\r\n"
+ "\r\n"
+ "http://somserver.com/progress.php?UPLOAD_IDENTIFIER=XXXXXXXXX.XXXXXXXXXX.XXXXXXXX.XX.X\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"DESTINATION_DIR\"\r\n"
+ "\r\n"
+ "10\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"js_enabled\"\r\n"
+ "\r\n"
+ "1"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"signature\"\r\n"
+ "\r\n"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"upload_files\"\r\n"
+ "\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"terms\"\r\n"
+ "\r\n"
+ "1"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"file[]\"\r\n"
+ "\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"description[]\"\r\n"
+ "\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
+ "Content-Disposition: form-data; name=\"upload_file[]\"; filename=\"filename.doc\"\r\n"
+ "Content-Type: application/msword\r\n"
+ "\r\n"
+ "FILE";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "CONTENT\r\n"
+ "------WebKitFormBoundaryBRDbP74mBhBxsIdo--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events != NULL) {
+ printf("app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, http_state, 0);
+ if (tx == NULL) {
+ goto end;
+ }
+
+ if (tx->request_method == NULL || memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0)
+ {
+ printf("expected method POST, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL || http_state->files_ts->tail == NULL ||
+ http_state->files_ts->tail->state != FILE_STATE_CLOSED) {
+ printf("state != FILE_STATE_CLOSED: ");
+ goto end;
+ }
+
+ if (http_state->files_ts->head->chunks_head->len != 11) {
+ printf("expected 11 but file is %u bytes instead: ",
+ http_state->files_ts->head->chunks_head->len);
+ PrintRawDataFp(stdout, http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len);
+ goto end;
+ }
+
+ if (memcmp("FILECONTENT", http_state->files_ts->head->chunks_head->data,
+ http_state->files_ts->head->chunks_head->len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void HTPFileParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HTPFileParserTest01", HTPFileParserTest01, 1);
+ UtRegisterTest("HTPFileParserTest02", HTPFileParserTest02, 1);
+ UtRegisterTest("HTPFileParserTest03", HTPFileParserTest03, 1);
+ UtRegisterTest("HTPFileParserTest04", HTPFileParserTest04, 1);
+ UtRegisterTest("HTPFileParserTest05", HTPFileParserTest05, 1);
+ UtRegisterTest("HTPFileParserTest06", HTPFileParserTest06, 1);
+ UtRegisterTest("HTPFileParserTest07", HTPFileParserTest07, 1);
+ UtRegisterTest("HTPFileParserTest08", HTPFileParserTest08, 1);
+ UtRegisterTest("HTPFileParserTest09", HTPFileParserTest09, 1);
+ UtRegisterTest("HTPFileParserTest10", HTPFileParserTest10, 1);
+ UtRegisterTest("HTPFileParserTest11", HTPFileParserTest11, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/app-layer-htp-file.h b/framework/src/suricata/src/app-layer-htp-file.h
new file mode 100644
index 00000000..d70794ea
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-file.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#ifndef __APP_LAYER_HTP_FILE_H__
+#define __APP_LAYER_HTP_FILE_H__
+
+int HTPFileOpen(HtpState *, uint8_t *, uint16_t, uint8_t *, uint32_t, uint16_t, uint8_t);
+int HTPFileStoreChunk(HtpState *, uint8_t *, uint32_t, uint8_t);
+int HTPFileClose(HtpState *, uint8_t *, uint32_t, uint8_t, uint8_t);
+
+void HTPFileParserRegisterTests(void);
+
+#endif /* __APP_LAYER_HTP_FILE_H__ */
diff --git a/framework/src/suricata/src/app-layer-htp-libhtp.c b/framework/src/suricata/src/app-layer-htp-libhtp.c
new file mode 100644
index 00000000..69d86220
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-libhtp.c
@@ -0,0 +1,219 @@
+/*
+ * We are using this file to hold APIs copied from libhtp 0.5.x.
+ */
+
+/***************************************************************************
+ * Copyright (c) 2009-2010 Open Information Security Foundation
+ * Copyright (c) 2010-2013 Qualys, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Qualys, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***************************************************************************/
+
+/**
+ * Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+
+
+/**
+ * \brief A direct flick off libhtp-0.5.x htp_is_lws().
+ */
+static int SC_htp_is_lws(int c)
+{
+ if ((c == ' ') || (c == '\t')) return 1;
+ else return 0;
+}
+
+/**
+ * \brief A direct flick off libhtp-0.5.x htp_parse_positive_integer_whitespace().
+ */
+static int64_t SC_htp_parse_positive_integer_whitespace(unsigned char *data, size_t len, int base)
+{
+ if (len == 0) return -1003;
+
+ size_t last_pos;
+ size_t pos = 0;
+
+ // Ignore LWS before
+ while ((pos < len) && (SC_htp_is_lws(data[pos]))) pos++;
+ if (pos == len) return -1001;
+
+ int64_t r = bstr_util_mem_to_pint(data + pos, len - pos, base, &last_pos);
+ if (r < 0) return r;
+
+ // Move after the last digit
+ pos += last_pos;
+
+ // Ignore LWS after
+ while (pos < len) {
+ if (!SC_htp_is_lws(data[pos])) {
+ return -1002;
+ }
+
+ pos++;
+ }
+
+ return r;
+}
+
+/**
+ * \brief A direct flick off libhtp-0.5.x htp_parse_content_length()
+ */
+int64_t SC_htp_parse_content_length(bstr *b)
+{
+ return SC_htp_parse_positive_integer_whitespace((unsigned char *) bstr_ptr(b), bstr_len(b), 10);
+}
+
+/**
+ * \brief Generates the normalized uri.
+ *
+ * Libhtp doesn't recreate the whole normalized uri and save it.
+ * That duty has now been passed to us. A lot of this code has been
+ * copied from libhtp.
+ *
+ * Keep an eye out on the tx->parsed_uri struct and how the parameters
+ * in it are generated, just in case some modifications are made to
+ * them in the future.
+ *
+ * \param uri_include_all boolean to indicate if scheme, username/password,
+ hostname and port should be part of the buffer
+ */
+bstr *SCHTPGenerateNormalizedUri(htp_tx_t *tx, htp_uri_t *uri, int uri_include_all)
+{
+ if (uri == NULL)
+ return NULL;
+
+ // On the first pass determine the length of the final string
+ size_t len = 0;
+
+ if (uri_include_all) {
+ if (uri->scheme != NULL) {
+ len += bstr_len(uri->scheme);
+ len += 3; // "://"
+ }
+
+ if ((uri->username != NULL) || (uri->password != NULL)) {
+ if (uri->username != NULL) {
+ len += bstr_len(uri->username);
+ }
+
+ len += 1; // ":"
+
+ if (uri->password != NULL) {
+ len += bstr_len(uri->password);
+ }
+
+ len += 1; // "@"
+ }
+
+ if (uri->hostname != NULL) {
+ len += bstr_len(uri->hostname);
+ }
+
+ if (uri->port != NULL) {
+ len += 1; // ":"
+ len += bstr_len(uri->port);
+ }
+ }
+
+ if (uri->path != NULL) {
+ len += bstr_len(uri->path);
+ }
+
+ if (uri->query != NULL) {
+ len += 1; // "?"
+ len += bstr_len(uri->query);
+ }
+
+ if (uri->fragment != NULL) {
+ len += 1; // "#"
+ len += bstr_len(uri->fragment);
+ }
+
+ // On the second pass construct the string
+ /* FIXME in memcap */
+ bstr *r = bstr_alloc(len);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ if (uri_include_all) {
+ if (uri->scheme != NULL) {
+ bstr_add_noex(r, uri->scheme);
+ bstr_add_c_noex(r, "://");
+ }
+
+ if ((uri->username != NULL) || (uri->password != NULL)) {
+ if (uri->username != NULL) {
+ bstr_add_noex(r, uri->username);
+ }
+
+ bstr_add_c(r, ":");
+
+ if (uri->password != NULL) {
+ bstr_add_noex(r, uri->password);
+ }
+
+ bstr_add_c_noex(r, "@");
+ }
+
+ if (uri->hostname != NULL) {
+ bstr_add_noex(r, uri->hostname);
+ }
+
+ if (uri->port != NULL) {
+ bstr_add_c(r, ":");
+ bstr_add_noex(r, uri->port);
+ }
+ }
+
+ if (uri->path != NULL) {
+ bstr_add_noex(r, uri->path);
+ }
+
+ if (uri->query != NULL) {
+ bstr *query = bstr_dup(uri->query);
+ if (query) {
+ uint64_t flags = 0;
+ htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, query, &flags);
+ bstr_add_c_noex(r, "?");
+ bstr_add_noex(r, query);
+ bstr_free(query);
+ }
+ }
+
+ if (uri->fragment != NULL) {
+ bstr_add_c_noex(r, "#");
+ bstr_add_noex(r, uri->fragment);
+ }
+
+ return r;
+}
diff --git a/framework/src/suricata/src/app-layer-htp-libhtp.h b/framework/src/suricata/src/app-layer-htp-libhtp.h
new file mode 100644
index 00000000..4c4eb3cd
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-libhtp.h
@@ -0,0 +1,51 @@
+/*
+ * We are using this file to hold APIs copied from libhtp 0.5.x.
+ */
+
+/***************************************************************************
+ * Copyright (c) 2009-2010 Open Information Security Foundation
+ * Copyright (c) 2010-2013 Qualys, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Qualys, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***************************************************************************/
+
+/**
+ * Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_HTP_LIBHTP__H__
+#define __APP_LAYER_HTP_LIBHTP__H__
+
+#include "suricata.h"
+#include "suricata-common.h"
+
+bstr *SCHTPGenerateNormalizedUri(htp_tx_t *tx, htp_uri_t *uri, int uri_include_all);
+int64_t SC_htp_parse_content_length(bstr *b);
+
+#endif /* __APP_LAYER_HTP_LIBHTP__H__ */
diff --git a/framework/src/suricata/src/app-layer-htp-mem.c b/framework/src/suricata/src/app-layer-htp-mem.c
new file mode 100644
index 00000000..c4f94e82
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-mem.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * This file provides a memory handling for the HTTP protocol support.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "conf.h"
+#include "util-mem.h"
+#include "util-misc.h"
+
+#include "app-layer-htp-mem.h"
+
+uint64_t htp_config_memcap = 0;
+
+SC_ATOMIC_DECLARE(uint64_t, htp_memuse);
+SC_ATOMIC_DECLARE(uint64_t, htp_memcap);
+
+void HTPParseMemcap()
+{
+ char *conf_val;
+
+ /** set config values for memcap, prealloc and hash_size */
+ if ((ConfGet("app-layer.protocols.http.memcap", &conf_val)) == 1)
+ {
+ if (ParseSizeStringU64(conf_val, &htp_config_memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing http.memcap "
+ "from conf file - %s. Killing engine",
+ conf_val);
+ exit(EXIT_FAILURE);
+ }
+ SCLogInfo("HTTP memcap: %"PRIu64, htp_config_memcap);
+ } else {
+ /* default to unlimited */
+ htp_config_memcap = 0;
+ }
+
+ SC_ATOMIC_INIT(htp_memuse);
+ SC_ATOMIC_INIT(htp_memcap);
+}
+
+void HTPIncrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_ADD(htp_memuse, size);
+ return;
+}
+
+void HTPDecrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_SUB(htp_memuse, size);
+ return;
+}
+
+uint64_t HTPMemuseGlobalCounter(void)
+{
+ uint64_t tmpval = SC_ATOMIC_GET(htp_memuse);
+ return tmpval;
+}
+
+uint64_t HTPMemcapGlobalCounter(void)
+{
+ uint64_t tmpval = SC_ATOMIC_GET(htp_memcap);
+ return tmpval;
+}
+
+/**
+ * \brief Check if alloc'ing "size" would mean we're over memcap
+ *
+ * \retval 1 if in bounds
+ * \retval 0 if not in bounds
+ */
+int HTPCheckMemcap(uint64_t size)
+{
+ if (htp_config_memcap == 0 || size + SC_ATOMIC_GET(htp_memuse) <= htp_config_memcap)
+ return 1;
+ (void) SC_ATOMIC_ADD(htp_memcap, 1);
+ return 0;
+}
+
+void *HTPMalloc(size_t size)
+{
+ void *ptr = NULL;
+
+ if (HTPCheckMemcap((uint32_t)size) == 0)
+ return NULL;
+
+ ptr = SCMalloc(size);
+
+ if (unlikely(ptr == NULL))
+ return NULL;
+
+ HTPIncrMemuse((uint64_t)size);
+
+ return ptr;
+}
+
+void *HTPRealloc(void *ptr, size_t orig_size, size_t size)
+{
+ void *rptr = NULL;
+
+ if (HTPCheckMemcap((uint32_t)(size - orig_size)) == 0)
+ return NULL;
+
+ rptr = SCRealloc(ptr, size);
+ if (rptr == NULL)
+ return NULL;
+
+ HTPIncrMemuse((uint64_t)(size - orig_size));
+
+ return rptr;
+}
+
+void HTPFree(void *ptr, size_t size)
+{
+ SCFree(ptr);
+
+ HTPDecrMemuse((uint64_t)size);
+}
+
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/app-layer-htp-mem.h b/framework/src/suricata/src/app-layer-htp-mem.h
new file mode 100644
index 00000000..44b50f5c
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-mem.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "stream-tcp-reassemble.h"
+
+void HTPParseMemcap();
+void *HTPMalloc(size_t size);
+void *HTPRealloc(void *ptr, size_t orig_size, size_t size);
+void HTPFree(void *ptr, size_t size);
+
+uint64_t HTPMemuseGlobalCounter(void);
+uint64_t HTPMemcapGlobalCounter(void);
diff --git a/framework/src/suricata/src/app-layer-htp-xff.c b/framework/src/suricata/src/app-layer-htp-xff.c
new file mode 100644
index 00000000..96c6de48
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-xff.c
@@ -0,0 +1,364 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-xff.h"
+
+#include "util-misc.h"
+#include "util-memrchr.h"
+#include "util-unittest.h"
+
+/** XFF header value minimal length */
+#define XFF_CHAIN_MINLEN 7
+/** XFF header value maximum length */
+#define XFF_CHAIN_MAXLEN 256
+/** Default XFF header name */
+#define XFF_DEFAULT "X-Forwarded-For"
+
+/** \internal
+ * \brief parse XFF string
+ * \param input input string, might be modified
+ * \param output output buffer
+ * \param output_size size of output buffer
+ * \retval bool 1 ok, 0 fail
+ */
+static int ParseXFFString(char *input, char *output, int output_size)
+{
+ size_t len = strlen(input);
+ if (len == 0)
+ return 0;
+
+ if (input[0] == '[') {
+ char *end = strchr(input, ']');
+ if (end == NULL) // malformed, not closed
+ return 0;
+
+ if (end != input+(len - 1)) {
+ SCLogDebug("data after closing bracket");
+ // if we ever want to parse the port, we can do it here
+ }
+
+ /* done, lets wrap up */
+ input++; // skip past [
+ *end = '\0'; // overwrite ], ignore anything after
+
+ } else {
+ /* lets see if the xff string ends in a port */
+ int c = 0;
+ int d = 0;
+ char *p = input;
+ while (*p != '\0') {
+ if (*p == ':')
+ c++;
+ if (*p == '.')
+ d++;
+ p++;
+ }
+ /* 3 dots: ipv4, one ':' port */
+ if (d == 3 && c == 1) {
+ SCLogDebug("XFF w port %s", input);
+ char *x = strchr(input, ':');
+ if (x) {
+ *x = '\0';
+ SCLogDebug("XFF w/o port %s", input);
+ // if we ever want to parse the port, we can do it here
+ }
+ }
+ }
+
+ SCLogDebug("XFF %s", input);
+
+ /** Sanity check on extracted IP for IPv4 and IPv6 */
+ uint32_t ip[4];
+ if (inet_pton(AF_INET, input, ip) == 1 ||
+ inet_pton(AF_INET6, input, ip) == 1)
+ {
+ strlcpy(output, input, output_size);
+ return 1; // OK
+ }
+ return 0;
+}
+
+/**
+ * \brief Function to return XFF IP if any in the selected transaction. The
+ * caller needs to lock the flow.
+ * \retval 1 if the IP has been found and returned in dstbuf
+ * \retval 0 if the IP has not being found or error
+ */
+int HttpXFFGetIPFromTx(const Packet *p, uint64_t tx_id, HttpXFFCfg *xff_cfg,
+ char *dstbuf, int dstbuflen)
+{
+ uint8_t xff_chain[XFF_CHAIN_MAXLEN];
+ HtpState *htp_state = NULL;
+ htp_tx_t *tx = NULL;
+ uint64_t total_txs = 0;
+ uint8_t *p_xff = NULL;
+
+ htp_state = (HtpState *)FlowGetAppState(p->flow);
+
+ if (htp_state == NULL) {
+ SCLogDebug("no http state, XFF IP cannot be retrieved");
+ return 0;
+ }
+
+ total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
+ if (tx_id >= total_txs)
+ return 0;
+
+ tx = AppLayerParserGetTx(p->flow->proto, ALPROTO_HTTP, htp_state, tx_id);
+ if (tx == NULL) {
+ SCLogDebug("tx is NULL, XFF cannot be retrieved");
+ return 0;
+ }
+
+ htp_header_t *h_xff = NULL;
+ if (tx->request_headers != NULL) {
+ h_xff = htp_table_get_c(tx->request_headers, xff_cfg->header);
+ }
+
+ if (h_xff != NULL && bstr_len(h_xff->value) >= XFF_CHAIN_MINLEN &&
+ bstr_len(h_xff->value) < XFF_CHAIN_MAXLEN) {
+
+ memcpy(xff_chain, bstr_ptr(h_xff->value), bstr_len(h_xff->value));
+ xff_chain[bstr_len(h_xff->value)]=0;
+
+ if (xff_cfg->flags & XFF_REVERSE) {
+ /** Get the last IP address from the chain */
+ p_xff = memrchr(xff_chain, ' ', bstr_len(h_xff->value));
+ if (p_xff == NULL) {
+ p_xff = xff_chain;
+ } else {
+ p_xff++;
+ }
+ }
+ else {
+ /** Get the first IP address from the chain */
+ p_xff = memchr(xff_chain, ',', bstr_len(h_xff->value));
+ if (p_xff != NULL) {
+ xff_chain[bstr_len(h_xff->value) - (p_xff - xff_chain)]=0;
+ }
+ p_xff = xff_chain;
+ }
+ return ParseXFFString((char *)p_xff, dstbuf, dstbuflen);
+ }
+ return 0;
+}
+
+/**
+ * \brief Function to return XFF IP if any. The caller needs to lock the flow.
+ * \retval 1 if the IP has been found and returned in dstbuf
+ * \retval 0 if the IP has not being found or error
+ */
+int HttpXFFGetIP(const Packet *p, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
+{
+ HtpState *htp_state = NULL;
+ uint64_t tx_id = 0;
+ uint64_t total_txs = 0;
+
+ htp_state = (HtpState *)FlowGetAppState(p->flow);
+ if (htp_state == NULL) {
+ SCLogDebug("no http state, XFF IP cannot be retrieved");
+ goto end;
+ }
+
+ total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
+ for (; tx_id < total_txs; tx_id++) {
+ if (HttpXFFGetIPFromTx(p, tx_id, xff_cfg, dstbuf, dstbuflen) == 1)
+ return 1;
+ }
+
+end:
+ return 0; // Not found
+}
+
+/**
+ * \brief Function to return XFF configuration from a configuration node.
+ */
+void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result)
+{
+ BUG_ON(conf == NULL || result == NULL);
+
+ ConfNode *xff_node = NULL;
+
+ if (conf != NULL)
+ xff_node = ConfNodeLookupChild(conf, "xff");
+
+ if (xff_node != NULL && ConfNodeChildValueIsTrue(xff_node, "enabled")) {
+ const char *xff_mode = ConfNodeLookupChildValue(xff_node, "mode");
+
+ if (xff_mode != NULL && strcasecmp(xff_mode, "overwrite") == 0) {
+ result->flags |= XFF_OVERWRITE;
+ } else {
+ if (xff_mode == NULL) {
+ SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode hasn't been defined, falling back to extra-data mode");
+ }
+ else if (strcasecmp(xff_mode, "extra-data") != 0) {
+ SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode %s is invalid, falling back to extra-data mode",
+ xff_mode);
+ }
+ result->flags |= XFF_EXTRADATA;
+ }
+
+ const char *xff_deployment = ConfNodeLookupChildValue(xff_node, "deployment");
+
+ if (xff_deployment != NULL && strcasecmp(xff_deployment, "forward") == 0) {
+ result->flags |= XFF_FORWARD;
+ } else {
+ if (xff_deployment == NULL) {
+ SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT, "The XFF deployment hasn't been defined, falling back to reverse proxy deployment");
+ }
+ else if (strcasecmp(xff_deployment, "reverse") != 0) {
+ SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT, "The XFF mode %s is invalid, falling back to reverse proxy deployment",
+ xff_deployment);
+ }
+ result->flags |= XFF_REVERSE;
+ }
+
+ const char *xff_header = ConfNodeLookupChildValue(xff_node, "header");
+
+ if (xff_header != NULL) {
+ result->header = (char *) xff_header;
+ } else {
+ SCLogWarning(SC_WARN_XFF_INVALID_HEADER, "The XFF header hasn't been defined, using the default %s",
+ XFF_DEFAULT);
+ result->header = XFF_DEFAULT;
+ }
+ }
+ else {
+ result->flags = XFF_DISABLED;
+ }
+}
+
+
+#ifdef UNITTESTS
+static int XFFTest01(void) {
+ char input[] = "1.2.3.4:5678";
+ char output[16];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "1.2.3.4") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest02(void) {
+ char input[] = "[12::34]:1234"; // thanks chort!
+ char output[16];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "12::34") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest03(void) {
+ char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest04(void) {
+ char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest05(void) {
+ char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest06(void) {
+ char input[] = "12::34";
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "12::34") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest07(void) {
+ char input[] = "1.2.3.4";
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 1 && strcmp(output, "1.2.3.4") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest08(void) {
+ char input[] = "[1.2.3.4:1234";
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static int XFFTest09(void) {
+ char input[] = "999.999.999.999:1234";
+ char output[46];
+ int r = ParseXFFString(input, output, sizeof(output));
+ if (r == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+
+void HTPXFFParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("XFFTest01", XFFTest01, 1);
+ UtRegisterTest("XFFTest02", XFFTest02, 1);
+ UtRegisterTest("XFFTest03", XFFTest03, 1);
+ UtRegisterTest("XFFTest04", XFFTest04, 1);
+ UtRegisterTest("XFFTest05", XFFTest05, 1);
+ UtRegisterTest("XFFTest06", XFFTest06, 1);
+ UtRegisterTest("XFFTest07", XFFTest07, 1);
+ UtRegisterTest("XFFTest08", XFFTest08, 1);
+ UtRegisterTest("XFFTest09", XFFTest09, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/app-layer-htp-xff.h b/framework/src/suricata/src/app-layer-htp-xff.h
new file mode 100644
index 00000000..1a3b67e1
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp-xff.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ */
+
+#ifndef __APP_LAYER_HTP_XFF_H__
+#define __APP_LAYER_HTP_XFF_H__
+
+/** XFF is disabled */
+#define XFF_DISABLED 1
+/** XFF extra data mode */
+#define XFF_EXTRADATA 2
+/** XFF overwrite mode */
+#define XFF_OVERWRITE 4
+/** XFF is to be used in a reverse proxy deployment */
+#define XFF_REVERSE 8
+/** XFF is to be used in a forward proxy deployment */
+#define XFF_FORWARD 16
+/** Single XFF IP maximum length (default value based on IPv6 address length) */
+#define XFF_MAXLEN 46
+
+typedef struct HttpXFFCfg_ {
+ uint8_t flags; /**< XFF operation mode and deployment */
+ char *header; /**< XFF header name */
+} HttpXFFCfg;
+
+void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result);
+
+int HttpXFFGetIPFromTx(const Packet *p, uint64_t tx_id, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen);
+
+int HttpXFFGetIP(const Packet *p, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen);
+
+void HTPXFFParserRegisterTests(void);
+
+#endif /* __APP_LAYER_HTP_XFF_H__ */
diff --git a/framework/src/suricata/src/app-layer-htp.c b/framework/src/suricata/src/app-layer-htp.c
new file mode 100644
index 00000000..452a607e
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp.c
@@ -0,0 +1,6231 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Brian Rectanus <brectanu@gmail.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * This file provides a HTTP protocol support for the engine using HTP library.
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "conf.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+#include "counters.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-radix-tree.h"
+#include "util-file.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-body.h"
+#include "app-layer-htp-file.h"
+#include "app-layer-htp-libhtp.h"
+#include "app-layer-htp-xff.h"
+
+#include "util-spm.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-misc.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "flow-util.h"
+
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+
+#include "decode-events.h"
+
+#include "util-memcmp.h"
+
+#ifndef HAVE_HTP_SET_PATH_DECODE_U_ENCODING
+void htp_config_set_path_decode_u_encoding(htp_cfg_t *cfg, int decode_u_encoding);
+#endif
+
+//#define PRINT
+
+/** Fast lookup tree (radix) for the various HTP configurations */
+static SCRadixTree *cfgtree;
+/** List of HTP configurations. */
+static HTPCfgRec cfglist;
+
+#ifdef DEBUG
+static SCMutex htp_state_mem_lock = SCMUTEX_INITIALIZER;
+static uint64_t htp_state_memuse = 0;
+static uint64_t htp_state_memcnt = 0;
+#endif
+
+SCEnumCharMap http_decoder_event_table[ ] = {
+ { "UNKNOWN_ERROR",
+ HTTP_DECODER_EVENT_UNKNOWN_ERROR},
+ { "GZIP_DECOMPRESSION_FAILED",
+ HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
+ { "REQUEST_FIELD_MISSING_COLON",
+ HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON},
+ { "RESPONSE_FIELD_MISSING_COLON",
+ HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON},
+ { "INVALID_REQUEST_CHUNK_LEN",
+ HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN},
+ { "INVALID_RESPONSE_CHUNK_LEN",
+ HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN},
+ { "INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST",
+ HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST},
+ { "INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE",
+ HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE},
+ { "INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST",
+ HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST},
+ { "INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE",
+ HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE},
+ { "100_CONTINUE_ALREADY_SEEN",
+ HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN},
+ { "UNABLE_TO_MATCH_RESPONSE_TO_REQUEST",
+ HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST},
+ { "INVALID_SERVER_PORT_IN_REQUEST",
+ HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST},
+ { "INVALID_AUTHORITY_PORT",
+ HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT},
+ { "REQUEST_HEADER_INVALID",
+ HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
+ { "RESPONSE_HEADER_INVALID",
+ HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
+ { "MISSING_HOST_HEADER",
+ HTTP_DECODER_EVENT_MISSING_HOST_HEADER},
+ { "HOST_HEADER_AMBIGUOUS",
+ HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS},
+ { "INVALID_REQUEST_FIELD_FOLDING",
+ HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING},
+ { "INVALID_RESPONSE_FIELD_FOLDING",
+ HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING},
+ { "REQUEST_FIELD_TOO_LONG",
+ HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
+ { "RESPONSE_FIELD_TOO_LONG",
+ HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
+ { "REQUEST_SERVER_PORT_TCP_PORT_MISMATCH",
+ HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH},
+ { "REQUEST_URI_HOST_INVALID",
+ HTTP_DECODER_EVENT_URI_HOST_INVALID},
+ { "REQUEST_HEADER_HOST_INVALID",
+ HTTP_DECODER_EVENT_HEADER_HOST_INVALID},
+ { "URI_DELIM_NON_COMPLIANT",
+ HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT},
+ { "METHOD_DELIM_NON_COMPLIANT",
+ HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT},
+ { "REQUEST_LINE_LEADING_WHITESPACE",
+ HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE},
+
+ /* suricata warnings/errors */
+ { "MULTIPART_GENERIC_ERROR",
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR},
+ { "MULTIPART_NO_FILEDATA",
+ HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA},
+ { "MULTIPART_INVALID_HEADER",
+ HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER},
+
+ { NULL, -1 },
+};
+
+static void *HTPStateGetTx(void *alstate, uint64_t tx_id);
+static int HTPStateGetAlstateProgress(void *tx, uint8_t direction);
+static uint64_t HTPStateGetTxCnt(void *alstate);
+static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction);
+
+#ifdef DEBUG
+/**
+ * \internal
+ *
+ * \brief Lookup the HTP personality string from the numeric personality.
+ *
+ * \todo This needs to be a libhtp function.
+ */
+static const char *HTPLookupPersonalityString(int p)
+{
+#define CASE_HTP_PERSONALITY_STRING(p) \
+ case HTP_SERVER_ ## p: return #p
+
+ switch (p) {
+ CASE_HTP_PERSONALITY_STRING(MINIMAL);
+ CASE_HTP_PERSONALITY_STRING(GENERIC);
+ CASE_HTP_PERSONALITY_STRING(IDS);
+ CASE_HTP_PERSONALITY_STRING(IIS_4_0);
+ CASE_HTP_PERSONALITY_STRING(IIS_5_0);
+ CASE_HTP_PERSONALITY_STRING(IIS_5_1);
+ CASE_HTP_PERSONALITY_STRING(IIS_6_0);
+ CASE_HTP_PERSONALITY_STRING(IIS_7_0);
+ CASE_HTP_PERSONALITY_STRING(IIS_7_5);
+ CASE_HTP_PERSONALITY_STRING(APACHE_2);
+ }
+
+ return NULL;
+}
+#endif /* DEBUG */
+
+/**
+ * \internal
+ *
+ * \brief Lookup the numeric HTP personality from a string.
+ *
+ * \todo This needs to be a libhtp function.
+ */
+static int HTPLookupPersonality(const char *str)
+{
+#define IF_HTP_PERSONALITY_NUM(p) \
+ if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
+
+ IF_HTP_PERSONALITY_NUM(MINIMAL);
+ IF_HTP_PERSONALITY_NUM(GENERIC);
+ IF_HTP_PERSONALITY_NUM(IDS);
+ IF_HTP_PERSONALITY_NUM(IIS_4_0);
+ IF_HTP_PERSONALITY_NUM(IIS_5_0);
+ IF_HTP_PERSONALITY_NUM(IIS_5_1);
+ IF_HTP_PERSONALITY_NUM(IIS_6_0);
+ IF_HTP_PERSONALITY_NUM(IIS_7_0);
+ IF_HTP_PERSONALITY_NUM(IIS_7_5);
+ IF_HTP_PERSONALITY_NUM(APACHE_2);
+ if (strcasecmp("TOMCAT_6_0", str) == 0) {
+ SCLogError(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
+ "longer supported by libhtp.", str);
+ return -1;
+ } else if ((strcasecmp("APACHE", str) == 0) ||
+ (strcasecmp("APACHE_2_2", str) == 0))
+ {
+ SCLogWarning(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
+ "longer supported by libhtp, failing back to "
+ "Apache2 personality.", str);
+ return HTP_SERVER_APACHE_2;
+ }
+
+ return -1;
+}
+
+void HTPSetEvent(HtpState *s, HtpTxUserData *htud, uint8_t e)
+{
+ SCLogDebug("setting event %u", e);
+
+ if (htud) {
+ AppLayerDecoderEventsSetEventRaw(&htud->decoder_events, e);
+ s->events++;
+ return;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(s, s->transaction_cnt);
+ if (tx != NULL) {
+ htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud != NULL) {
+ AppLayerDecoderEventsSetEventRaw(&htud->decoder_events, e);
+ s->events++;
+ return;
+ }
+ }
+ SCLogDebug("couldn't set event %u", e);
+}
+
+static int HTPHasEvents(void *state)
+{
+ HtpState *htp_state = (HtpState *)state;
+ return (htp_state->events > 0);
+}
+
+static AppLayerDecoderEvents *HTPGetEvents(void *state, uint64_t tx_id)
+{
+ SCLogDebug("get HTTP events for TX %"PRIu64, tx_id);
+
+ HtpState *s = (HtpState *)state;
+ htp_tx_t *tx = HTPStateGetTx(s, tx_id);
+ if (tx != NULL) {
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud != NULL) {
+ SCLogDebug("has htud, htud->decoder_events %p", htud->decoder_events);
+ return htud->decoder_events;
+ }
+ }
+ return NULL;
+}
+
+/** \brief Function to allocates the HTTP state memory and also creates the HTTP
+ * connection parser to be used by the HTP library
+ */
+static void *HTPStateAlloc(void)
+{
+ SCEnter();
+
+ HtpState *s = HTPMalloc(sizeof(HtpState));
+ if (unlikely(s == NULL))
+ goto error;
+
+ memset(s, 0x00, sizeof(HtpState));
+
+#ifdef DEBUG
+ SCMutexLock(&htp_state_mem_lock);
+ htp_state_memcnt++;
+ htp_state_memuse += sizeof(HtpState);
+ SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
+ SCMutexUnlock(&htp_state_mem_lock);
+#endif
+
+ SCReturnPtr((void *)s, "void");
+
+error:
+ if (s != NULL) {
+ HTPFree(s, sizeof(HtpState));
+ }
+
+ SCReturnPtr(NULL, "void");
+}
+
+static void HtpTxUserDataFree(HtpState *state, HtpTxUserData *htud)
+{
+ if (likely(htud)) {
+ HtpBodyFree(&htud->request_body);
+ HtpBodyFree(&htud->response_body);
+ bstr_free(htud->request_uri_normalized);
+ if (htud->request_headers_raw)
+ HTPFree(htud->request_headers_raw, htud->request_headers_raw_len);
+ if (htud->response_headers_raw)
+ HTPFree(htud->response_headers_raw, htud->response_headers_raw_len);
+ AppLayerDecoderEventsFreeEvents(&htud->decoder_events);
+ if (htud->boundary)
+ HTPFree(htud->boundary, htud->boundary_len);
+ if (htud->de_state != NULL) {
+ if (likely(state != NULL)) { // should be impossible that it's null
+ BUG_ON(state->tx_with_detect_state_cnt == 0);
+ state->tx_with_detect_state_cnt--;
+ }
+
+ DetectEngineStateFree(htud->de_state);
+ }
+ HTPFree(htud, sizeof(HtpTxUserData));
+ }
+}
+
+/** \brief Function to frees the HTTP state memory and also frees the HTTP
+ * connection parser memory which was used by the HTP library
+ */
+void HTPStateFree(void *state)
+{
+ SCEnter();
+
+ HtpState *s = (HtpState *)state;
+ if (s == NULL) {
+ SCReturn;
+ }
+
+ /* Unset the body inspection */
+ s->flags &=~ HTP_FLAG_NEW_BODY_SET;
+
+ /* free the connection parser memory used by HTP library */
+ if (s->connp != NULL) {
+ SCLogDebug("freeing HTP state");
+
+ uint64_t tx_id;
+ uint64_t total_txs = HTPStateGetTxCnt(state);
+ /* free the list of body chunks */
+ if (s->conn != NULL) {
+ for (tx_id = 0; tx_id < total_txs; tx_id++) {
+ htp_tx_t *tx = HTPStateGetTx(s, tx_id);
+ if (tx != NULL) {
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ HtpTxUserDataFree(s, htud);
+ htp_tx_set_user_data(tx, NULL);
+ }
+ }
+ }
+ htp_connp_destroy_all(s->connp);
+ }
+ BUG_ON(s->tx_with_detect_state_cnt > 0);
+
+ FileContainerFree(s->files_ts);
+ FileContainerFree(s->files_tc);
+ HTPFree(s, sizeof(HtpState));
+
+#ifdef DEBUG
+ SCMutexLock(&htp_state_mem_lock);
+ htp_state_memcnt--;
+ htp_state_memuse -= sizeof(HtpState);
+ SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
+ SCMutexUnlock(&htp_state_mem_lock);
+#endif
+
+ SCReturn;
+}
+
+/**
+ * \brief HTP transaction cleanup callback
+ *
+ * \warning We cannot actually free the transactions here. It seems that
+ * HTP only accepts freeing of transactions in the response callback.
+ */
+static void HTPStateTransactionFree(void *state, uint64_t id)
+{
+ SCEnter();
+
+ HtpState *s = (HtpState *)state;
+
+ SCLogDebug("state %p, id %"PRIu64, s, id);
+
+ htp_tx_t *tx = HTPStateGetTx(s, id);
+ if (tx != NULL) {
+ /* This will remove obsolete body chunks */
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ HtpTxUserDataFree(s, htud);
+ htp_tx_set_user_data(tx, NULL);
+
+ /* hack: even if libhtp considers the tx incomplete, we want to
+ * free it here. htp_tx_destroy however, will refuse to do this.
+ * As htp_tx_destroy_incomplete isn't available in the public API,
+ * we hack around it here. */
+ if (unlikely(!(
+ tx->request_progress == HTP_REQUEST_COMPLETE &&
+ tx->response_progress == HTP_RESPONSE_COMPLETE)))
+ {
+ tx->request_progress = HTP_REQUEST_COMPLETE;
+ tx->response_progress = HTP_RESPONSE_COMPLETE;
+ }
+ htp_tx_destroy(tx);
+ }
+}
+
+/**
+ * \brief Sets a flag that informs the HTP app layer that some module in the
+ * engine needs the http request body data.
+ * \initonly
+ */
+void AppLayerHtpEnableRequestBodyCallback(void)
+{
+ SCEnter();
+
+ SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_BODY);
+ SCReturn;
+}
+
+/**
+ * \brief Sets a flag that informs the HTP app layer that some module in the
+ * engine needs the http request body data.
+ * \initonly
+ */
+void AppLayerHtpEnableResponseBodyCallback(void)
+{
+ SCEnter();
+
+ SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_RESPONSE_BODY);
+ SCReturn;
+}
+
+/**
+ * \brief Sets a flag that informs the HTP app layer that some module in the
+ * engine needs the http request multi part header.
+ *
+ * \initonly
+ */
+void AppLayerHtpNeedMultipartHeader(void)
+{
+ SCEnter();
+ AppLayerHtpEnableRequestBodyCallback();
+
+ SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_MULTIPART);
+ SCReturn;
+}
+
+/**
+ * \brief Sets a flag that informs the HTP app layer that some module in the
+ * engine needs the http request file.
+ *
+ * \initonly
+ */
+void AppLayerHtpNeedFileInspection(void)
+{
+ SCEnter();
+ AppLayerHtpNeedMultipartHeader();
+ AppLayerHtpEnableRequestBodyCallback();
+ AppLayerHtpEnableResponseBodyCallback();
+
+ SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_FILE);
+ SCReturn;
+}
+
+/* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
+struct {
+ char *msg;
+ int de;
+} htp_errors[] = {
+ { "GZip decompressor: inflateInit2 failed", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
+ { "Request field invalid: colon missing", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON},
+ { "Response field invalid: missing colon", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON},
+ { "Request chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN},
+ { "Response chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN},
+/* { "Invalid T-E value in request", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_T_E
+ { "Invalid T-E value in response", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE}, <- nothing to replace it */
+/* { "Invalid C-L field in request", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_C_L */
+ { "Invalid C-L field in response", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE},
+ { "Already seen 100-Continue", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN},
+ { "Unable to match response to request", HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST},
+ { "Invalid server port information in request", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST},
+/* { "Invalid authority port", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT}, htp no longer returns this error */
+ { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
+ { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
+};
+
+struct {
+ char *msg;
+ int de;
+} htp_warnings[] = {
+ { "GZip decompressor:", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
+ { "Request field invalid", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
+ { "Response field invalid", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
+ { "Request header name is not a token", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
+ { "Response header name is not a token", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
+/* { "Host information in request headers required by HTTP/1.1", HTTP_DECODER_EVENT_MISSING_HOST_HEADER}, <- tx flag HTP_HOST_MISSING
+ { "Host information ambiguous", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS}, <- tx flag HTP_HOST_AMBIGUOUS */
+ { "Invalid request field folding", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING},
+ { "Invalid response field folding", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING},
+ /* line is now: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request server port=%d number differs from the actual TCP port=%d", port, connp->conn->server_port);
+ * luckily, "Request server port=" is unique */
+/* { "Request server port number differs from the actual TCP port", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH}, */
+ { "Request server port=", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH},
+ { "Request line: URI contains non-compliant delimiter", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT},
+ { "Request line: non-compliant delimiter between Method and URI", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT},
+ { "Request line: leading whitespace", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE},
+};
+
+#define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
+#define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
+
+/**
+ * \internal
+ *
+ * \brief Get the warning id for the warning msg.
+ *
+ * \param msg warning message
+ *
+ * \retval id the id or 0 in case of not found
+ */
+static int HTPHandleWarningGetId(const char *msg)
+{
+ SCLogDebug("received warning \"%s\"", msg);
+ size_t idx;
+ for (idx = 0; idx < HTP_WARNING_MAX; idx++) {
+ if (strncmp(htp_warnings[idx].msg, msg,
+ strlen(htp_warnings[idx].msg)) == 0)
+ {
+ return htp_warnings[idx].de;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ *
+ * \brief Get the error id for the error msg.
+ *
+ * \param msg error message
+ *
+ * \retval id the id or 0 in case of not found
+ */
+static int HTPHandleErrorGetId(const char *msg)
+{
+ SCLogDebug("received error \"%s\"", msg);
+
+ size_t idx;
+ for (idx = 0; idx < HTP_ERROR_MAX; idx++) {
+ if (strncmp(htp_errors[idx].msg, msg,
+ strlen(htp_errors[idx].msg)) == 0)
+ {
+ return htp_errors[idx].de;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ *
+ * \brief Check state for errors, warnings and add any as events
+ *
+ * \param s state
+ */
+static void HTPHandleError(HtpState *s)
+{
+ if (s == NULL || s->conn == NULL ||
+ s->conn->messages == NULL) {
+ return;
+ }
+
+ size_t size = htp_list_size(s->conn->messages);
+ size_t msg;
+
+ for (msg = s->htp_messages_offset; msg < size; msg++) {
+ htp_log_t *log = htp_list_get(s->conn->messages, msg);
+ if (log == NULL)
+ continue;
+
+ HtpTxUserData *htud = NULL;
+ htp_tx_t *tx = log->tx; // will be NULL in <=0.5.9
+ if (tx != NULL)
+ htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+
+ SCLogDebug("message %s", log->msg);
+
+ int id = HTPHandleErrorGetId(log->msg);
+ if (id == 0) {
+ id = HTPHandleWarningGetId(log->msg);
+ if (id == 0)
+ id = HTTP_DECODER_EVENT_UNKNOWN_ERROR;
+ }
+
+ if (id > 0) {
+ HTPSetEvent(s, htud, id);
+ }
+ }
+ s->htp_messages_offset = (uint16_t)msg;
+ SCLogDebug("s->htp_messages_offset %u", s->htp_messages_offset);
+}
+
+static inline void HTPErrorCheckTxRequestFlags(HtpState *s, htp_tx_t *tx)
+{
+#ifdef DEBUG
+ BUG_ON(s == NULL || tx == NULL);
+#endif
+ if (tx->flags & ( HTP_REQUEST_INVALID_T_E|HTP_REQUEST_INVALID_C_L|
+ HTP_HOST_MISSING|HTP_HOST_AMBIGUOUS|HTP_HOSTU_INVALID|
+ HTP_HOSTH_INVALID))
+ {
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud == NULL)
+ return;
+
+ if (tx->flags & HTP_REQUEST_INVALID_T_E)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST);
+ if (tx->flags & HTP_REQUEST_INVALID_C_L)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST);
+ if (tx->flags & HTP_HOST_MISSING)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_MISSING_HOST_HEADER);
+ if (tx->flags & HTP_HOST_AMBIGUOUS)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS);
+ if (tx->flags & HTP_HOSTU_INVALID)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_URI_HOST_INVALID);
+ if (tx->flags & HTP_HOSTH_INVALID)
+ HTPSetEvent(s, htud,
+ HTTP_DECODER_EVENT_HEADER_HOST_INVALID);
+ }
+}
+
+/**
+ * \brief Function to handle the reassembled data from client and feed it to
+ * the HTP library to process it.
+ *
+ * \param flow Pointer to the flow the data belong to
+ * \param htp_state Pointer the state in which the parsed value to be stored
+ * \param pstate Application layer parser state for this session
+ * \param input Pointer the received HTTP client data
+ * \param input_len Length in bytes of the received data
+ * \param output Pointer to the output (not used in this function)
+ *
+ * \retval On success returns 1 or on failure returns -1.
+ */
+static int HTPHandleRequestData(Flow *f, void *htp_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ int r = -1;
+ int ret = 1;
+
+ //PrintRawDataFp(stdout, input, input_len);
+
+ HtpState *hstate = (HtpState *)htp_state;
+ hstate->f = f;
+
+ /* On the first invocation, create the connection parser structure to
+ * be used by HTP library. This is looked up via IP in the radix
+ * tree. Failing that, the default HTP config is used.
+ */
+ if (NULL == hstate->conn) {
+ HTPCfgRec *htp_cfg_rec = &cfglist;
+ htp_cfg_t *htp = cfglist.cfg; /* Default to the global HTP config */
+ void *user_data = NULL;
+
+ if (FLOW_IS_IPV4(f)) {
+ SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f));
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f), cfgtree, &user_data);
+ }
+ else if (FLOW_IS_IPV6(f)) {
+ SCLogDebug("Looking up HTP config for ipv6");
+ (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f), cfgtree, &user_data);
+ }
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown address family, bug!");
+ goto error;
+ }
+
+ if (user_data != NULL) {
+ htp_cfg_rec = user_data;
+ htp = htp_cfg_rec->cfg;
+ SCLogDebug("LIBHTP using config: %p", htp);
+ } else {
+ SCLogDebug("Using default HTP config: %p", htp);
+ }
+
+ if (NULL == htp) {
+#ifdef DEBUG_VALIDATION
+ BUG_ON(htp == NULL);
+#endif
+ /* should never happen if HTPConfigure is properly invoked */
+ goto error;
+ }
+
+ hstate->connp = htp_connp_create(htp);
+ if (hstate->connp == NULL) {
+ goto error;
+ }
+
+ hstate->conn = htp_connp_get_connection(hstate->connp);
+
+ htp_connp_set_user_data(hstate->connp, (void *)hstate);
+ hstate->cfg = htp_cfg_rec;
+
+ SCLogDebug("New hstate->connp %p", hstate->connp);
+ }
+
+ /* the code block above should make sure connp is never NULL here */
+#ifdef DEBUG_VALIDATION
+ BUG_ON(hstate->connp == NULL);
+#endif
+
+ /* Unset the body inspection (the callback should
+ * reactivate it if necessary) */
+ hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
+
+ /* Open the HTTP connection on receiving the first request */
+ if (!(hstate->flags & HTP_FLAG_STATE_OPEN)) {
+ SCLogDebug("opening htp handle at %p", hstate->connp);
+
+ htp_connp_open(hstate->connp, NULL, f->sp, NULL, f->dp, &f->startts);
+ hstate->flags |= HTP_FLAG_STATE_OPEN;
+ } else {
+ SCLogDebug("using existing htp handle at %p", hstate->connp);
+ }
+
+ htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
+ /* pass the new data to the htp parser */
+ if (input_len > 0) {
+ r = htp_connp_req_data(hstate->connp, &ts, input, input_len);
+
+ switch(r) {
+ case HTP_STREAM_ERROR:
+
+ hstate->flags |= HTP_FLAG_STATE_ERROR;
+ hstate->flags &= ~HTP_FLAG_STATE_DATA;
+ hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
+ ret = -1;
+ break;
+ case HTP_STREAM_DATA:
+ case HTP_STREAM_DATA_OTHER:
+
+ hstate->flags |= HTP_FLAG_STATE_DATA;
+ break;
+ case HTP_STREAM_TUNNEL:
+ break;
+ default:
+ hstate->flags &= ~HTP_FLAG_STATE_DATA;
+ hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
+ }
+ HTPHandleError(hstate);
+ }
+
+ /* if the TCP connection is closed, then close the HTTP connection */
+ if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) &&
+ !(hstate->flags & HTP_FLAG_STATE_CLOSED_TS))
+ {
+ htp_connp_close(hstate->connp, &ts);
+ hstate->flags |= HTP_FLAG_STATE_CLOSED_TS;
+ SCLogDebug("stream eof encountered, closing htp handle for ts");
+ }
+
+ SCLogDebug("hstate->connp %p", hstate->connp);
+ SCReturnInt(ret);
+
+error:
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Function to handle the reassembled data from server and feed it to
+ * the HTP library to process it.
+ *
+ * \param flow Pointer to the flow the data belong to
+ * \param htp_state Pointer the state in which the parsed value to be stored
+ * \param pstate Application layer parser state for this session
+ * \param input Pointer the received HTTP server data
+ * \param input_len Length in bytes of the received data
+ * \param output Pointer to the output (not used in this function)
+ *
+ * \retval On success returns 1 or on failure returns -1
+ */
+static int HTPHandleResponseData(Flow *f, void *htp_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ int r = -1;
+ int ret = 1;
+
+ HtpState *hstate = (HtpState *)htp_state;
+ hstate->f = f;
+ if (hstate->connp == NULL) {
+ SCLogDebug("HTP state has no connp");
+ /* till we have the new libhtp changes that allow response first,
+ * let's take response in first. */
+ //BUG_ON(1);
+ SCReturnInt(-1);
+ }
+
+ /* Unset the body inspection (the callback should
+ * reactivate it if necessary) */
+ hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
+
+ htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
+ if (input_len > 0) {
+ r = htp_connp_res_data(hstate->connp, &ts, input, input_len);
+ switch(r) {
+ case HTP_STREAM_ERROR:
+ hstate->flags = HTP_FLAG_STATE_ERROR;
+ hstate->flags &= ~HTP_FLAG_STATE_DATA;
+ hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
+ ret = -1;
+ break;
+ case HTP_STREAM_DATA:
+ case HTP_STREAM_DATA_OTHER:
+ hstate->flags |= HTP_FLAG_STATE_DATA;
+ break;
+ case HTP_STREAM_TUNNEL:
+ break;
+ default:
+ hstate->flags &= ~HTP_FLAG_STATE_DATA;
+ hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
+ }
+ HTPHandleError(hstate);
+ }
+
+ /* if we the TCP connection is closed, then close the HTTP connection */
+ if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) &&
+ !(hstate->flags & HTP_FLAG_STATE_CLOSED_TC))
+ {
+ htp_connp_close(hstate->connp, &ts);
+ hstate->flags |= HTP_FLAG_STATE_CLOSED_TC;
+ }
+
+ SCLogDebug("hstate->connp %p", hstate->connp);
+ SCReturnInt(ret);
+}
+
+/**
+ * \param name /Lowercase/ version of the variable name
+ */
+static int HTTPParseContentDispositionHeader(uint8_t *name, size_t name_len,
+ uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
+{
+#ifdef PRINT
+ printf("DATA START: \n");
+ PrintRawDataFp(stdout, data, len);
+ printf("DATA END: \n");
+#endif
+ size_t x;
+ int quote = 0;
+
+ for (x = 0; x < len; x++) {
+ if (!(isspace(data[x])))
+ break;
+ }
+
+ if (x >= len)
+ return 0;
+
+ uint8_t *line = data+x;
+ size_t line_len = len-x;
+ size_t offset = 0;
+#ifdef PRINT
+ printf("LINE START: \n");
+ PrintRawDataFp(stdout, line, line_len);
+ printf("LINE END: \n");
+#endif
+ for (x = 0 ; x < line_len; x++) {
+ if (x > 0) {
+ if (line[x - 1] != '\\' && line[x] == '\"') {
+ quote++;
+ }
+
+ if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
+ uint8_t *token = line + offset;
+ size_t token_len = x - offset;
+
+ if ((x + 1) == line_len) {
+ token_len++;
+ }
+
+ offset = x + 1;
+
+ while (offset < line_len && isspace(line[offset])) {
+ x++;
+ offset++;
+ }
+#ifdef PRINT
+ printf("TOKEN START: \n");
+ PrintRawDataFp(stdout, token, token_len);
+ printf("TOKEN END: \n");
+#endif
+ if (token_len > name_len) {
+ if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
+ uint8_t *value = token + name_len;
+ size_t value_len = token_len - name_len;
+
+ if (value[0] == '\"') {
+ value++;
+ value_len--;
+ }
+ if (value[value_len-1] == '\"') {
+ value_len--;
+ }
+#ifdef PRINT
+ printf("VALUE START: \n");
+ PrintRawDataFp(stdout, value, value_len);
+ printf("VALUE END: \n");
+#endif
+ *retptr = value;
+ *retlen = value_len;
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \param name /Lowercase/ version of the variable name
+ */
+static int HTTPParseContentTypeHeader(uint8_t *name, size_t name_len,
+ uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
+{
+ SCEnter();
+#ifdef PRINT
+ printf("DATA START: \n");
+ PrintRawDataFp(stdout, data, len);
+ printf("DATA END: \n");
+#endif
+ size_t x;
+ int quote = 0;
+
+ for (x = 0; x < len; x++) {
+ if (!(isspace(data[x])))
+ break;
+ }
+
+ if (x >= len) {
+ SCReturnInt(0);
+ }
+
+ uint8_t *line = data+x;
+ size_t line_len = len-x;
+ size_t offset = 0;
+#ifdef PRINT
+ printf("LINE START: \n");
+ PrintRawDataFp(stdout, line, line_len);
+ printf("LINE END: \n");
+#endif
+ for (x = 0 ; x < line_len; x++) {
+ if (x > 0) {
+ if (line[x - 1] != '\\' && line[x] == '\"') {
+ quote++;
+ }
+
+ if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
+ uint8_t *token = line + offset;
+ size_t token_len = x - offset;
+
+ if ((x + 1) == line_len) {
+ token_len++;
+ }
+
+ offset = x + 1;
+
+ while (offset < line_len && isspace(line[offset])) {
+ x++;
+ offset++;
+ }
+#ifdef PRINT
+ printf("TOKEN START: \n");
+ PrintRawDataFp(stdout, token, token_len);
+ printf("TOKEN END: \n");
+#endif
+ if (token_len > name_len) {
+ if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
+ uint8_t *value = token + name_len;
+ size_t value_len = token_len - name_len;
+
+ if (value[0] == '\"') {
+ value++;
+ value_len--;
+ }
+ if (value[value_len-1] == '\"') {
+ value_len--;
+ }
+#ifdef PRINT
+ printf("VALUE START: \n");
+ PrintRawDataFp(stdout, value, value_len);
+ printf("VALUE END: \n");
+#endif
+ *retptr = value;
+ *retlen = value_len;
+ SCReturnInt(1);
+ }
+ }
+ }
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief setup multipart parsing: extract boundary and store it
+ *
+ * \param d HTTP transaction
+ * \param htud transaction userdata
+ *
+ * \retval 1 ok, multipart set up
+ * \retval 0 ok, not multipart though
+ * \retval -1 error: problem with the boundary
+ *
+ * If the request contains a multipart message, this function will
+ * set the HTP_BOUNDARY_SET in the transaction.
+ */
+static int HtpRequestBodySetupMultipart(htp_tx_data_t *d, HtpTxUserData *htud)
+{
+ htp_header_t *h = (htp_header_t *)htp_table_get_c(d->tx->request_headers,
+ "Content-Type");
+ if (h != NULL && bstr_len(h->value) > 0) {
+ uint8_t *boundary = NULL;
+ size_t boundary_len = 0;
+
+ int r = HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
+ (uint8_t *) bstr_ptr(h->value), bstr_len(h->value),
+ &boundary, &boundary_len);
+ if (r == 1) {
+#ifdef PRINT
+ printf("BOUNDARY START: \n");
+ PrintRawDataFp(stdout, boundary, boundary_len);
+ printf("BOUNDARY END: \n");
+#endif
+ if (boundary_len < HTP_BOUNDARY_MAX) {
+ htud->boundary = HTPMalloc(boundary_len);
+ if (htud->boundary == NULL) {
+ return -1;
+ }
+ htud->boundary_len = (uint8_t)boundary_len;
+ memcpy(htud->boundary, boundary, boundary_len);
+
+ htud->tsflags |= HTP_BOUNDARY_SET;
+ } else {
+ SCLogDebug("invalid boundary");
+ return -1;
+ }
+ SCReturnInt(1);
+ }
+ //SCReturnInt(1);
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Setup boundary buffers
+ */
+static int HtpRequestBodySetupBoundary(HtpTxUserData *htud,
+ uint8_t **expected_boundary, uint8_t *expected_boundary_len,
+ uint8_t **expected_boundary_end, uint8_t *expected_boundary_end_len)
+{
+ uint8_t *eb = NULL;
+ uint8_t *ebe = NULL;
+
+ uint8_t eb_len = htud->boundary_len + 2;
+ eb = (uint8_t *)HTPMalloc(eb_len);
+ if (eb == NULL) {
+ goto error;
+ }
+ memset(eb, '-', eb_len);
+ memcpy(eb + 2, htud->boundary, htud->boundary_len);
+
+ uint8_t ebe_len = htud->boundary_len + 4;
+ ebe = (uint8_t *)HTPMalloc(ebe_len);
+ if (ebe == NULL) {
+ goto error;
+ }
+ memset(ebe, '-', ebe_len);
+ memcpy(ebe + 2, htud->boundary, htud->boundary_len);
+
+ *expected_boundary = eb;
+ *expected_boundary_len = eb_len;
+ *expected_boundary_end = ebe;
+ *expected_boundary_end_len = ebe_len;
+
+ SCReturnInt(0);
+
+error:
+ if (eb != NULL) {
+ HTPFree(eb, eb_len);
+ }
+ if (ebe != NULL) {
+ HTPFree(ebe, ebe_len);
+ }
+ SCReturnInt(-1);
+}
+
+#define C_D_HDR "content-disposition:"
+#define C_D_HDR_LEN 20
+#define C_T_HDR "content-type:"
+#define C_T_HDR_LEN 13
+
+static void HtpRequestBodyMultipartParseHeader(HtpState *hstate,
+ HtpTxUserData *htud,
+ uint8_t *header, uint32_t header_len,
+ uint8_t **filename, uint16_t *filename_len,
+ uint8_t **filetype, uint16_t *filetype_len)
+{
+ uint8_t *fn = NULL;
+ size_t fn_len = 0;
+ uint8_t *ft = NULL;
+ size_t ft_len = 0;
+
+#ifdef PRINT
+ printf("HEADER START: \n");
+ PrintRawDataFp(stdout, header, header_len);
+ printf("HEADER END: \n");
+#endif
+
+ while (header_len > 0) {
+ uint8_t *next_line = Bs2bmSearch(header, header_len, (uint8_t *)"\r\n", 2);
+ uint8_t *line = header;
+ uint32_t line_len;
+
+ if (next_line == NULL) {
+ line_len = header_len;
+ } else {
+ line_len = next_line - header;
+ }
+ uint8_t *sc = (uint8_t *)memchr(line, ':', line_len);
+ if (sc == NULL) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
+ /* if the : we found is the final char, it means we have
+ * no value */
+ } else if (line_len > 0 && sc == &line[line_len - 1]) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
+ } else {
+#ifdef PRINT
+ printf("LINE START: \n");
+ PrintRawDataFp(stdout, line, line_len);
+ printf("LINE END: \n");
+#endif
+ if (line_len >= C_D_HDR_LEN &&
+ SCMemcmpLowercase(C_D_HDR, line, C_D_HDR_LEN) == 0) {
+ uint8_t *value = line + C_D_HDR_LEN;
+ uint32_t value_len = line_len - C_D_HDR_LEN;
+
+ /* parse content-disposition */
+ (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
+ value, value_len, &fn, &fn_len);
+ } else if (line_len >= C_T_HDR_LEN &&
+ SCMemcmpLowercase(C_T_HDR, line, C_T_HDR_LEN) == 0) {
+ SCLogDebug("content-type line");
+ uint8_t *value = line + C_T_HDR_LEN;
+ uint32_t value_len = line_len - C_T_HDR_LEN;
+
+ (void)HTTPParseContentTypeHeader(NULL, 0,
+ value, value_len, &ft, &ft_len);
+ }
+ }
+
+ if (next_line == NULL) {
+ SCLogDebug("no next_line");
+ break;
+ }
+ header_len -= ((next_line + 2) - header);
+ header = next_line + 2;
+ } /* while (header_len > 0) */
+
+ if (fn_len > USHRT_MAX)
+ fn_len = USHRT_MAX;
+ if (ft_len > USHRT_MAX)
+ ft_len = USHRT_MAX;
+
+ *filename = fn;
+ *filename_len = fn_len;
+ *filetype = ft;
+ *filetype_len = ft_len;
+}
+
+/**
+ * \brief Create a single buffer from the HtpBodyChunks in our list
+ *
+ * \param htud transaction user data
+ * \param chunks_buffers pointer to pass back the buffer to the caller
+ * \param chunks_buffer_len pointer to pass back the buffer length to the caller
+ */
+static void HtpRequestBodyReassemble(HtpTxUserData *htud,
+ uint8_t **chunks_buffer, uint32_t *chunks_buffer_len)
+{
+ uint8_t *buf = NULL;
+ uint8_t *pbuf = NULL;
+ uint32_t buf_len = 0;
+ HtpBodyChunk *cur = htud->request_body.first;
+
+ for ( ; cur != NULL; cur = cur->next) {
+ SCLogDebug("chunk %p", cur);
+
+ /* skip body chunks entirely before what we parsed already */
+ if ((uint64_t )cur->stream_offset + cur->len <= htud->request_body.body_parsed) {
+ SCLogDebug("skipping chunk");
+ continue;
+ }
+
+ SCLogDebug("cur->stream_offset %"PRIu64", cur->len %"PRIu32", body_parsed %"PRIu64,
+ cur->stream_offset, cur->len, htud->request_body.body_parsed);
+
+ if (cur->stream_offset < htud->request_body.body_parsed &&
+ cur->stream_offset + cur->len >= htud->request_body.body_parsed) {
+ SCLogDebug("use part");
+
+ uint32_t toff = htud->request_body.body_parsed - cur->stream_offset;
+ uint32_t tlen = (cur->stream_offset + cur->len) - htud->request_body.body_parsed;
+ uint8_t *pbuf = NULL;
+
+ buf_len += tlen;
+ if ((pbuf = HTPRealloc(buf, buf_len - tlen, buf_len)) == NULL) {
+ HTPFree(buf, buf_len - tlen);
+ buf = NULL;
+ buf_len = 0;
+ break;
+ }
+ buf = pbuf;
+ memcpy(buf + buf_len - tlen, cur->data + toff, tlen);
+
+ } else {
+ SCLogDebug("use entire chunk");
+
+ buf_len += cur->len;
+ if ((pbuf = HTPRealloc(buf, buf_len - cur->len, buf_len)) == NULL) {
+ HTPFree(buf, buf_len - cur->len);
+ buf = NULL;
+ buf_len = 0;
+ break;
+ }
+ buf = pbuf;
+ memcpy(buf + buf_len - cur->len, cur->data, cur->len);
+ }
+ }
+
+ *chunks_buffer = buf;
+ *chunks_buffer_len = buf_len;
+}
+
+int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
+ void *tx, uint8_t *chunks_buffer, uint32_t chunks_buffer_len)
+{
+ int result = 0;
+ uint8_t *expected_boundary = NULL;
+ uint8_t *expected_boundary_end = NULL;
+ uint8_t expected_boundary_len = 0;
+ uint8_t expected_boundary_end_len = 0;
+ int tx_progress = 0;
+
+#ifdef PRINT
+ printf("CHUNK START: \n");
+ PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
+ printf("CHUNK END: \n");
+#endif
+
+ if (HtpRequestBodySetupBoundary(htud, &expected_boundary, &expected_boundary_len,
+ &expected_boundary_end, &expected_boundary_end_len) < 0) {
+ goto end;
+ }
+
+ /* search for the header start, header end and form end */
+ uint8_t *header_start = Bs2bmSearch(chunks_buffer, chunks_buffer_len,
+ expected_boundary, expected_boundary_len);
+ uint8_t *header_end = NULL;
+ if (header_start != NULL) {
+ header_end = Bs2bmSearch(header_start, chunks_buffer_len - (header_start - chunks_buffer),
+ (uint8_t *)"\r\n\r\n", 4);
+ }
+ uint8_t *form_end = Bs2bmSearch(chunks_buffer, chunks_buffer_len,
+ expected_boundary_end, expected_boundary_end_len);
+
+ SCLogDebug("header_start %p, header_end %p, form_end %p", header_start,
+ header_end, form_end);
+
+ /* we currently only handle multipart for ts. When we support it for tc,
+ * we will need to supply right direction */
+ tx_progress = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, STREAM_TOSERVER);
+ /* if we're in the file storage process, deal with that now */
+ if (htud->tsflags & HTP_FILENAME_SET) {
+ if (header_start != NULL || form_end != NULL || (tx_progress > HTP_REQUEST_BODY)) {
+ SCLogDebug("reached the end of the file");
+
+ uint8_t *filedata = chunks_buffer;
+ uint32_t filedata_len = 0;
+ uint8_t flags = 0;
+
+ if (header_start < form_end || (header_start != NULL && form_end == NULL)) {
+ filedata_len = header_start - filedata - 2; /* 0d 0a */
+ } else if (form_end != NULL && form_end < header_start) {
+ filedata_len = form_end - filedata;
+ } else if (form_end != NULL && form_end == header_start) {
+ filedata_len = form_end - filedata - 2; /* 0d 0a */
+ } else if (tx_progress > HTP_RESPONSE_BODY) {
+ filedata_len = chunks_buffer_len;
+ flags = FILE_TRUNCATED;
+ }
+
+ if (filedata_len > chunks_buffer_len) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
+ goto end;
+ }
+#ifdef PRINT
+ printf("FILEDATA (final chunk) START: \n");
+ PrintRawDataFp(stdout, filedata, filedata_len);
+ printf("FILEDATA (final chunk) END: \n");
+#endif
+ if (!(htud->tsflags & HTP_DONTSTORE)) {
+ if (HTPFileClose(hstate, filedata, filedata_len, flags,
+ STREAM_TOSERVER) == -1)
+ {
+ goto end;
+ }
+ }
+
+ htud->tsflags &=~ HTP_FILENAME_SET;
+
+ /* fall through */
+ } else {
+ SCLogDebug("not yet at the end of the file");
+
+ if (chunks_buffer_len > expected_boundary_end_len) {
+ uint8_t *filedata = chunks_buffer;
+ uint32_t filedata_len = chunks_buffer_len - expected_boundary_len;
+#ifdef PRINT
+ printf("FILEDATA (part) START: \n");
+ PrintRawDataFp(stdout, filedata, filedata_len);
+ printf("FILEDATA (part) END: \n");
+#endif
+
+ if (!(htud->tsflags & HTP_DONTSTORE)) {
+ result = HTPFileStoreChunk(hstate, filedata,
+ filedata_len, STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ /* we know for sure we're not storing the file */
+ htud->tsflags |= HTP_DONTSTORE;
+ }
+ }
+
+ htud->request_body.body_parsed += filedata_len;
+ } else {
+ SCLogDebug("chunk too small to already process in part");
+ }
+
+ goto end;
+ }
+ }
+
+ while (header_start != NULL && header_end != NULL &&
+ header_end != form_end &&
+ header_start < (chunks_buffer + chunks_buffer_len) &&
+ header_end < (chunks_buffer + chunks_buffer_len) &&
+ header_start < header_end)
+ {
+ uint8_t *filename = NULL;
+ uint16_t filename_len = 0;
+ uint8_t *filetype = NULL;
+ uint16_t filetype_len = 0;
+
+ uint32_t header_len = header_end - header_start;
+ SCLogDebug("header_len %u", header_len);
+ uint8_t *header = header_start;
+
+ /* skip empty records */
+ if (expected_boundary_len == header_len) {
+ goto next;
+ } else if ((uint32_t)(expected_boundary_len + 2) <= header_len) {
+ header_len -= (expected_boundary_len + 2);
+ header = header_start + (expected_boundary_len + 2); // + for 0d 0a
+ }
+
+ HtpRequestBodyMultipartParseHeader(hstate, htud, header, header_len,
+ &filename, &filename_len, &filetype, &filetype_len);
+
+ if (filename != NULL) {
+ uint8_t *filedata = NULL;
+ uint32_t filedata_len = 0;
+
+ SCLogDebug("we have a filename");
+
+ htud->tsflags |= HTP_FILENAME_SET;
+ htud->tsflags &= ~HTP_DONTSTORE;
+
+ SCLogDebug("header_end %p", header_end);
+ SCLogDebug("form_end %p", form_end);
+
+ /* everything until the final boundary is the file */
+ if (form_end != NULL) {
+ filedata = header_end + 4;
+ if (form_end == filedata) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA);
+ goto end;
+ } else if (form_end < filedata) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
+ goto end;
+ }
+
+ filedata_len = form_end - (header_end + 4 + 2);
+ SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
+
+ /* or is it? */
+ uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
+ expected_boundary, expected_boundary_len);
+ if (header_next != NULL) {
+ filedata_len -= (form_end - header_next);
+ }
+
+ if (filedata_len > chunks_buffer_len) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
+ goto end;
+ }
+ SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
+#ifdef PRINT
+ printf("FILEDATA START: \n");
+ PrintRawDataFp(stdout, filedata, filedata_len);
+ printf("FILEDATA END: \n");
+#endif
+
+ result = HTPFileOpen(hstate, filename, filename_len,
+ filedata, filedata_len, hstate->transaction_cnt,
+ STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tsflags |= HTP_DONTSTORE;
+ } else {
+ if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
+ goto end;
+ }
+ }
+
+ htud->request_body.body_parsed += (header_end - chunks_buffer);
+ htud->tsflags &= ~HTP_FILENAME_SET;
+ } else {
+ SCLogDebug("chunk doesn't contain form end");
+
+ filedata = header_end + 4;
+ filedata_len = chunks_buffer_len - (filedata - chunks_buffer);
+ SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len, chunks_buffer_len);
+
+ if (filedata_len > chunks_buffer_len) {
+ HTPSetEvent(hstate, htud,
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
+ goto end;
+ }
+
+#ifdef PRINT
+ printf("FILEDATA START: \n");
+ PrintRawDataFp(stdout, filedata, filedata_len);
+ printf("FILEDATA END: \n");
+#endif
+ /* form doesn't end in this chunk, but part might. Lets
+ * see if have another coming up */
+ uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
+ expected_boundary, expected_boundary_len);
+ SCLogDebug("header_next %p", header_next);
+ if (header_next == NULL) {
+ /* no, but we'll handle the file data when we see the
+ * form_end */
+
+ SCLogDebug("more file data to come");
+
+ uint32_t offset = (header_end + 4) - chunks_buffer;
+ SCLogDebug("offset %u", offset);
+ htud->request_body.body_parsed += offset;
+
+ result = HTPFileOpen(hstate, filename, filename_len,
+ NULL, 0, hstate->transaction_cnt,
+ STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tsflags |= HTP_DONTSTORE;
+ }
+ } else if (header_next - filedata > 2) {
+ filedata_len = header_next - filedata - 2;
+ SCLogDebug("filedata_len %u", filedata_len);
+
+ result = HTPFileOpen(hstate, filename, filename_len,
+ filedata, filedata_len, hstate->transaction_cnt,
+ STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tsflags |= HTP_DONTSTORE;
+ } else {
+ if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
+ goto end;
+ }
+ }
+
+ htud->tsflags &= ~HTP_FILENAME_SET;
+ htud->request_body.body_parsed += (header_end - chunks_buffer);
+ }
+ }
+ }
+next:
+ SCLogDebug("header_start %p, header_end %p, form_end %p",
+ header_start, header_end, form_end);
+
+ /* Search next boundary entry after the start of body */
+ uint32_t cursizeread = header_end - chunks_buffer;
+ header_start = Bs2bmSearch(header_end + 4,
+ chunks_buffer_len - (cursizeread + 4),
+ expected_boundary, expected_boundary_len);
+ if (header_start != NULL) {
+ header_end = Bs2bmSearch(header_end + 4,
+ chunks_buffer_len - (cursizeread + 4),
+ (uint8_t *) "\r\n\r\n", 4);
+ }
+ }
+end:
+ if (expected_boundary != NULL) {
+ HTPFree(expected_boundary, expected_boundary_len);
+ }
+ if (expected_boundary_end != NULL) {
+ HTPFree(expected_boundary_end, expected_boundary_end_len);
+ }
+
+ SCLogDebug("htud->request_body.body_parsed %"PRIu64, htud->request_body.body_parsed);
+ return 0;
+}
+
+/** \brief setup things for put request
+ * \todo really needed? */
+int HtpRequestBodySetupPUT(htp_tx_data_t *d, HtpTxUserData *htud)
+{
+// if (d->tx->parsed_uri == NULL || d->tx->parsed_uri->path == NULL) {
+// return -1;
+// }
+
+ /* filename is d->tx->parsed_uri->path */
+
+ return 0;
+}
+
+/** \internal
+ * \brief Handle POST, no multipart body data
+ */
+static int HtpRequestBodyHandlePOST(HtpState *hstate, HtpTxUserData *htud,
+ htp_tx_t *tx, uint8_t *data, uint32_t data_len)
+{
+ int result = 0;
+
+ /* see if we need to open the file */
+ if (!(htud->tsflags & HTP_FILENAME_SET))
+ {
+ uint8_t *filename = NULL;
+ size_t filename_len = 0;
+
+ /* get the name */
+ if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
+ filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
+ filename_len = bstr_len(tx->parsed_uri->path);
+ }
+
+ if (filename != NULL) {
+ result = HTPFileOpen(hstate, filename, (uint32_t)filename_len, data, data_len,
+ hstate->transaction_cnt, STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tsflags |= HTP_DONTSTORE;
+ } else {
+ htud->tsflags |= HTP_FILENAME_SET;
+ htud->tsflags &= ~HTP_DONTSTORE;
+ }
+ }
+ }
+ else
+ {
+ /* otherwise, just store the data */
+
+ if (!(htud->tsflags & HTP_DONTSTORE)) {
+ result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ /* we know for sure we're not storing the file */
+ htud->tsflags |= HTP_DONTSTORE;
+ }
+ }
+ }
+
+ return 0;
+end:
+ return -1;
+}
+
+/** \internal
+ * \brief Handle PUT body data
+ */
+static int HtpRequestBodyHandlePUT(HtpState *hstate, HtpTxUserData *htud,
+ htp_tx_t *tx, uint8_t *data, uint32_t data_len)
+{
+ int result = 0;
+
+ /* see if we need to open the file */
+ if (!(htud->tsflags & HTP_FILENAME_SET))
+ {
+ uint8_t *filename = NULL;
+ size_t filename_len = 0;
+
+ /* get the name */
+ if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
+ filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
+ filename_len = bstr_len(tx->parsed_uri->path);
+ }
+
+ if (filename != NULL) {
+ result = HTPFileOpen(hstate, filename, (uint32_t)filename_len, data, data_len,
+ hstate->transaction_cnt, STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tsflags |= HTP_DONTSTORE;
+ } else {
+ htud->tsflags |= HTP_FILENAME_SET;
+ htud->tsflags &= ~HTP_DONTSTORE;
+ }
+ }
+ }
+ else
+ {
+ /* otherwise, just store the data */
+
+ if (!(htud->tsflags & HTP_DONTSTORE)) {
+ result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOSERVER);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ /* we know for sure we're not storing the file */
+ htud->tsflags |= HTP_DONTSTORE;
+ }
+ }
+ }
+
+ return 0;
+end:
+ return -1;
+}
+
+int HtpResponseBodyHandle(HtpState *hstate, HtpTxUserData *htud,
+ htp_tx_t *tx, uint8_t *data, uint32_t data_len)
+{
+ SCEnter();
+
+ int result = 0;
+
+ /* see if we need to open the file */
+ if (!(htud->tcflags & HTP_FILENAME_SET))
+ {
+ SCLogDebug("setting up file name");
+
+ uint8_t *filename = NULL;
+ size_t filename_len = 0;
+
+ /* try Content-Disposition header first */
+ htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers,
+ "Content-Disposition");
+ if (h != NULL && bstr_len(h->value) > 0) {
+ /* parse content-disposition */
+ (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
+ (uint8_t *) bstr_ptr(h->value), bstr_len(h->value), &filename, &filename_len);
+ }
+
+ /* fall back to name from the uri */
+ if (filename == NULL) {
+ /* get the name */
+ if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
+ filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
+ filename_len = bstr_len(tx->parsed_uri->path);
+ }
+ }
+
+ if (filename != NULL) {
+ result = HTPFileOpen(hstate, filename, (uint32_t)filename_len,
+ data, data_len, hstate->transaction_cnt, STREAM_TOCLIENT);
+ SCLogDebug("result %d", result);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ htud->tcflags |= HTP_DONTSTORE;
+ } else {
+ htud->tcflags |= HTP_FILENAME_SET;
+ htud->tcflags &= ~HTP_DONTSTORE;
+ }
+ }
+ }
+ else
+ {
+ /* otherwise, just store the data */
+
+ if (!(htud->tcflags & HTP_DONTSTORE)) {
+ result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOCLIENT);
+ SCLogDebug("result %d", result);
+ if (result == -1) {
+ goto end;
+ } else if (result == -2) {
+ /* we know for sure we're not storing the file */
+ htud->tcflags |= HTP_DONTSTORE;
+ }
+ }
+ }
+
+ htud->response_body.body_parsed += data_len;
+ return 0;
+end:
+ return -1;
+}
+
+/**
+ * \brief Function callback to append chunks for Requests
+ * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
+ * \retval int HTP_OK if all goes well
+ */
+int HTPCallbackRequestBodyData(htp_tx_data_t *d)
+{
+ SCEnter();
+
+ if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_REQUEST_BODY))
+ SCReturnInt(HTP_OK);
+
+ if (d->data == NULL || d->len == 0)
+ SCReturnInt(HTP_OK);
+
+#ifdef PRINT
+ printf("HTPBODY START: \n");
+ PrintRawDataFp(stdout, (uint8_t *)d->data, d->len);
+ printf("HTPBODY END: \n");
+#endif
+
+ HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
+ if (hstate == NULL) {
+ SCReturnInt(HTP_ERROR);
+ }
+
+ SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
+ "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
+
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
+ if (tx_ud == NULL) {
+ tx_ud = HTPMalloc(sizeof(HtpTxUserData));
+ if (unlikely(tx_ud == NULL)) {
+ SCReturnInt(HTP_OK);
+ }
+ memset(tx_ud, 0, sizeof(HtpTxUserData));
+
+ /* Set the user data for handling body chunks on this transaction */
+ htp_tx_set_user_data(d->tx, tx_ud);
+ }
+ if (!tx_ud->response_body_init) {
+ tx_ud->response_body_init = 1;
+ tx_ud->operation = HTP_BODY_REQUEST;
+
+ if (d->tx->request_method_number == HTP_M_POST) {
+ SCLogDebug("POST");
+ int r = HtpRequestBodySetupMultipart(d, tx_ud);
+ if (r == 1) {
+ tx_ud->request_body_type = HTP_BODY_REQUEST_MULTIPART;
+ } else if (r == 0) {
+ tx_ud->request_body_type = HTP_BODY_REQUEST_POST;
+ SCLogDebug("not multipart");
+ }
+ } else if (d->tx->request_method_number == HTP_M_PUT) {
+ if (HtpRequestBodySetupPUT(d, tx_ud) == 0) {
+ tx_ud->request_body_type = HTP_BODY_REQUEST_PUT;
+ }
+ }
+ }
+
+ SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64, tx_ud->request_body.content_len_so_far);
+ SCLogDebug("hstate->cfg->request_body_limit %u", hstate->cfg->request_body_limit);
+
+ /* within limits, add the body chunk to the state. */
+ if (hstate->cfg->request_body_limit == 0 || tx_ud->request_body.content_len_so_far < hstate->cfg->request_body_limit)
+ {
+ uint32_t len = (uint32_t)d->len;
+
+ if (hstate->cfg->request_body_limit > 0 &&
+ (tx_ud->request_body.content_len_so_far + len) > hstate->cfg->request_body_limit)
+ {
+ len = hstate->cfg->request_body_limit - tx_ud->request_body.content_len_so_far;
+ BUG_ON(len > (uint32_t)d->len);
+ }
+ SCLogDebug("len %u", len);
+
+ HtpBodyAppendChunk(tx_ud, &tx_ud->request_body, (uint8_t *)d->data, len);
+
+ uint8_t *chunks_buffer = NULL;
+ uint32_t chunks_buffer_len = 0;
+
+ if (tx_ud->request_body_type == HTP_BODY_REQUEST_MULTIPART) {
+ /* multi-part body handling starts here */
+ if (!(tx_ud->tsflags & HTP_BOUNDARY_SET)) {
+ goto end;
+ }
+
+ HtpRequestBodyReassemble(tx_ud, &chunks_buffer, &chunks_buffer_len);
+ if (chunks_buffer == NULL) {
+ goto end;
+ }
+#ifdef PRINT
+ printf("REASSCHUNK START: \n");
+ PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
+ printf("REASSCHUNK END: \n");
+#endif
+
+ HtpRequestBodyHandleMultipart(hstate, tx_ud, d->tx, chunks_buffer, chunks_buffer_len);
+
+ if (chunks_buffer != NULL) {
+ HTPFree(chunks_buffer, chunks_buffer_len);
+ }
+ } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_POST) {
+ HtpRequestBodyHandlePOST(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
+ } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_PUT) {
+ HtpRequestBodyHandlePUT(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
+ }
+
+ }
+
+end:
+ /* see if we can get rid of htp body chunks */
+ HtpBodyPrune(hstate, &tx_ud->request_body, STREAM_TOSERVER);
+
+ /* set the new chunk flag */
+ hstate->flags |= HTP_FLAG_NEW_BODY_SET;
+
+ SCReturnInt(HTP_OK);
+}
+
+/**
+ * \brief Function callback to append chunks for Responses
+ * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
+ * \retval int HTP_OK if all goes well
+ */
+int HTPCallbackResponseBodyData(htp_tx_data_t *d)
+{
+ SCEnter();
+
+ if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_RESPONSE_BODY))
+ SCReturnInt(HTP_OK);
+
+ if (d->data == NULL || d->len == 0)
+ SCReturnInt(HTP_OK);
+
+ HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
+ if (hstate == NULL) {
+ SCReturnInt(HTP_ERROR);
+ }
+
+ SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
+ "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
+
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
+ if (tx_ud == NULL) {
+ tx_ud = HTPMalloc(sizeof(HtpTxUserData));
+ if (unlikely(tx_ud == NULL)) {
+ SCReturnInt(HTP_OK);
+ }
+ memset(tx_ud, 0, sizeof(HtpTxUserData));
+
+ /* Set the user data for handling body chunks on this transaction */
+ htp_tx_set_user_data(d->tx, tx_ud);
+ }
+ if (!tx_ud->request_body_init) {
+ tx_ud->request_body_init = 1;
+ tx_ud->operation = HTP_BODY_RESPONSE;
+ }
+
+ SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64, tx_ud->response_body.content_len_so_far);
+ SCLogDebug("hstate->cfg->response_body_limit %u", hstate->cfg->response_body_limit);
+
+ /* within limits, add the body chunk to the state. */
+ if (hstate->cfg->response_body_limit == 0 || tx_ud->response_body.content_len_so_far < hstate->cfg->response_body_limit)
+ {
+ uint32_t len = (uint32_t)d->len;
+
+ if (hstate->cfg->response_body_limit > 0 &&
+ (tx_ud->response_body.content_len_so_far + len) > hstate->cfg->response_body_limit)
+ {
+ len = hstate->cfg->response_body_limit - tx_ud->response_body.content_len_so_far;
+ BUG_ON(len > (uint32_t)d->len);
+ }
+ SCLogDebug("len %u", len);
+
+ HtpBodyAppendChunk(tx_ud, &tx_ud->response_body, (uint8_t *)d->data, len);
+
+ HtpResponseBodyHandle(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
+ }
+
+ /* see if we can get rid of htp body chunks */
+ HtpBodyPrune(hstate, &tx_ud->response_body, STREAM_TOCLIENT);
+
+ /* set the new chunk flag */
+ hstate->flags |= HTP_FLAG_NEW_BODY_SET;
+
+ SCReturnInt(HTP_OK);
+}
+
+/**
+ * \brief Print the stats of the HTTP requests
+ */
+void HTPAtExitPrintStats(void)
+{
+#ifdef DEBUG
+ SCEnter();
+ SCMutexLock(&htp_state_mem_lock);
+ SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
+ htp_state_memcnt, htp_state_memuse);
+ SCMutexUnlock(&htp_state_mem_lock);
+ SCReturn;
+#endif
+}
+
+/** \brief Clears the HTTP server configuration memory used by HTP library */
+void HTPFreeConfig(void)
+{
+ SCEnter();
+
+ if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
+ !AppLayerParserConfParserEnabled("tcp", "http"))
+ {
+ SCReturn;
+ }
+
+ HTPCfgRec *nextrec = cfglist.next;
+ SCRadixReleaseRadixTree(cfgtree);
+ cfgtree = NULL;
+ htp_config_destroy(cfglist.cfg);
+ while (nextrec != NULL) {
+ HTPCfgRec *htprec = nextrec;
+ nextrec = nextrec->next;
+
+ htp_config_destroy(htprec->cfg);
+ SCFree(htprec);
+ }
+ SCReturn;
+}
+
+/**
+ * \brief callback for request to store the recent incoming request
+ in to the recent_in_tx for the given htp state
+ * \param connp pointer to the current connection parser which has the htp
+ * state in it as user data
+ */
+static int HTPCallbackRequest(htp_tx_t *tx)
+{
+ SCEnter();
+
+ if (tx == NULL) {
+ SCReturnInt(HTP_ERROR);
+ }
+
+ HtpState *hstate = htp_connp_get_user_data(tx->connp);
+ if (hstate == NULL) {
+ SCReturnInt(HTP_ERROR);
+ }
+
+ SCLogDebug("transaction_cnt %"PRIu64", list_size %"PRIu64,
+ hstate->transaction_cnt, HTPStateGetTxCnt(hstate));
+
+ SCLogDebug("HTTP request completed");
+
+ HTPErrorCheckTxRequestFlags(hstate, tx);
+
+ HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (htud != NULL) {
+ if (htud->tsflags & HTP_FILENAME_SET) {
+ SCLogDebug("closing file that was being stored");
+ (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER);
+ htud->tsflags &= ~HTP_FILENAME_SET;
+ }
+ }
+
+ /* request done, do raw reassembly now to inspect state and stream
+ * at the same time. */
+ AppLayerParserTriggerRawStreamReassembly(hstate->f);
+ SCReturnInt(HTP_OK);
+}
+
+/**
+ * \brief callback for response to remove the recent received requests
+ from the recent_in_tx for the given htp state
+ * \param connp pointer to the current connection parser which has the htp
+ * state in it as user data
+ */
+static int HTPCallbackResponse(htp_tx_t *tx)
+{
+ SCEnter();
+
+ HtpState *hstate = htp_connp_get_user_data(tx->connp);
+ if (hstate == NULL) {
+ SCReturnInt(HTP_ERROR);
+ }
+
+ /* we have one whole transaction now */
+ hstate->transaction_cnt++;
+
+ /* Unset the body inspection (if any) */
+ hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud != NULL) {
+ if (htud->tcflags & HTP_FILENAME_SET) {
+ SCLogDebug("closing file that was being stored");
+ (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOCLIENT);
+ htud->tcflags &= ~HTP_FILENAME_SET;
+ }
+ }
+
+ /* response done, do raw reassembly now to inspect state and stream
+ * at the same time. */
+ AppLayerParserTriggerRawStreamReassembly(hstate->f);
+ SCReturnInt(HTP_OK);
+}
+
+static int HTPCallbackRequestLine(htp_tx_t *tx)
+{
+ HtpTxUserData *tx_ud;
+ bstr *request_uri_normalized;
+ HtpState *hstate = htp_connp_get_user_data(tx->connp);
+ HTPCfgRec *cfg = hstate->cfg;
+
+ request_uri_normalized = SCHTPGenerateNormalizedUri(tx, tx->parsed_uri, cfg->uri_include_all);
+ if (request_uri_normalized == NULL)
+ return HTP_OK;
+
+ tx_ud = htp_tx_get_user_data(tx);
+ if (likely(tx_ud == NULL)) {
+ tx_ud = HTPMalloc(sizeof(*tx_ud));
+ if (unlikely(tx_ud == NULL)) {
+ bstr_free(request_uri_normalized);
+ return HTP_OK;
+ }
+ memset(tx_ud, 0, sizeof(*tx_ud));
+ htp_tx_set_user_data(tx, tx_ud);
+ }
+ if (unlikely(tx_ud->request_uri_normalized != NULL))
+ bstr_free(tx_ud->request_uri_normalized);
+ tx_ud->request_uri_normalized = request_uri_normalized;
+
+ if (tx->flags) {
+ HTPErrorCheckTxRequestFlags(hstate, tx);
+ }
+ return HTP_OK;
+}
+
+static int HTPCallbackDoubleDecodeQuery(htp_tx_t *tx)
+{
+ if (tx->parsed_uri == NULL || tx->parsed_uri->query == NULL)
+ return HTP_OK;
+
+ uint64_t flags = 0;
+ htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, tx->parsed_uri->query, &flags);
+
+ return HTP_OK;
+}
+
+static int HTPCallbackDoubleDecodePath(htp_tx_t *tx)
+{
+ if (tx->parsed_uri == NULL || tx->parsed_uri->path == NULL)
+ return HTP_OK;
+
+ uint64_t flags = 0;
+ htp_urldecode_inplace(tx->cfg, HTP_DECODER_URL_PATH, tx->parsed_uri->path, &flags);
+
+ return HTP_OK;
+}
+
+static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data)
+{
+ void *ptmp;
+ if (tx_data->len == 0)
+ return HTP_OK;
+
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
+ if (tx_ud == NULL) {
+ tx_ud = HTPMalloc(sizeof(*tx_ud));
+ if (unlikely(tx_ud == NULL))
+ return HTP_OK;
+ memset(tx_ud, 0, sizeof(*tx_ud));
+ htp_tx_set_user_data(tx_data->tx, tx_ud);
+ }
+ ptmp = HTPRealloc(tx_ud->request_headers_raw,
+ tx_ud->request_headers_raw_len,
+ tx_ud->request_headers_raw_len + tx_data->len);
+ if (ptmp == NULL) {
+ /* error: we're freeing the entire user data */
+ HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
+ HtpTxUserDataFree(hstate, tx_ud);
+ htp_tx_set_user_data(tx_data->tx, NULL);
+ return HTP_OK;
+ }
+ tx_ud->request_headers_raw = ptmp;
+
+ memcpy(tx_ud->request_headers_raw + tx_ud->request_headers_raw_len,
+ tx_data->data, tx_data->len);
+ tx_ud->request_headers_raw_len += tx_data->len;
+
+ if (tx_data->tx && tx_data->tx->flags) {
+ HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
+ HTPErrorCheckTxRequestFlags(hstate, tx_data->tx);
+ }
+ return HTP_OK;
+}
+
+static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data)
+{
+ void *ptmp;
+ if (tx_data->len == 0)
+ return HTP_OK;
+
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
+ if (tx_ud == NULL) {
+ tx_ud = HTPMalloc(sizeof(*tx_ud));
+ if (unlikely(tx_ud == NULL))
+ return HTP_OK;
+ memset(tx_ud, 0, sizeof(*tx_ud));
+ htp_tx_set_user_data(tx_data->tx, tx_ud);
+ }
+ ptmp = HTPRealloc(tx_ud->response_headers_raw,
+ tx_ud->response_headers_raw_len,
+ tx_ud->response_headers_raw_len + tx_data->len);
+ if (ptmp == NULL) {
+ /* error: we're freeing the entire user data */
+ HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
+ HtpTxUserDataFree(hstate, tx_ud);
+ htp_tx_set_user_data(tx_data->tx, NULL);
+ return HTP_OK;
+ }
+ tx_ud->response_headers_raw = ptmp;
+
+ memcpy(tx_ud->response_headers_raw + tx_ud->response_headers_raw_len,
+ tx_data->data, tx_data->len);
+ tx_ud->response_headers_raw_len += tx_data->len;
+
+ return HTP_OK;
+}
+
+/*
+ * We have a similar set function called HTPConfigSetDefaultsPhase1.
+ */
+static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec)
+{
+ cfg_prec->uri_include_all = FALSE;
+ cfg_prec->request_body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
+ cfg_prec->response_body_limit = HTP_CONFIG_DEFAULT_RESPONSE_BODY_LIMIT;
+ cfg_prec->request_inspect_min_size = HTP_CONFIG_DEFAULT_REQUEST_INSPECT_MIN_SIZE;
+ cfg_prec->request_inspect_window = HTP_CONFIG_DEFAULT_REQUEST_INSPECT_WINDOW;
+ cfg_prec->response_inspect_min_size = HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_MIN_SIZE;
+ cfg_prec->response_inspect_window = HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_WINDOW;
+ cfg_prec->randomize = HTP_CONFIG_DEFAULT_RANDOMIZE;
+ cfg_prec->randomize_range = HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE;
+
+ htp_config_register_request_header_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
+ htp_config_register_request_trailer_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
+ htp_config_register_response_header_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
+ htp_config_register_response_trailer_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
+
+ htp_config_register_request_body_data(cfg_prec->cfg, HTPCallbackRequestBodyData);
+ htp_config_register_response_body_data(cfg_prec->cfg, HTPCallbackResponseBodyData);
+
+ htp_config_register_request_complete(cfg_prec->cfg, HTPCallbackRequest);
+ htp_config_register_response_complete(cfg_prec->cfg, HTPCallbackResponse);
+
+ htp_config_set_parse_request_cookies(cfg_prec->cfg, 0);
+ htp_config_set_parse_request_auth(cfg_prec->cfg, 0);
+
+ /* don't convert + to space by default */
+ htp_config_set_plusspace_decode(cfg_prec->cfg, HTP_DECODER_URLENCODED, 0);
+
+ /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
+ * only the hard limit. So we set both here to the (current) htp defaults.
+ * The reason we do this is that if the user sets the hard limit in the
+ * config, we have to set the soft limit as well. If libhtp starts using
+ * the soft limit in the future, we at least make sure we control what
+ * it's value is. */
+ htp_config_set_field_limits(cfg_prec->cfg,
+ (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT,
+ (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_HARD);
+ return;
+}
+
+/*
+ * We have this splitup so that in case double decoding has been enabled
+ * for query and path, they would be called first on the callback queue,
+ * before the callback set by Phase2() is called. We need this, since
+ * the callback in Phase2() generates the normalized uri which utilizes
+ * the query and path. */
+static void HTPConfigSetDefaultsPhase2(char *name, HTPCfgRec *cfg_prec)
+{
+ /* randomize inspection size if needed */
+ if (cfg_prec->randomize) {
+ int rdrange = cfg_prec->randomize_range;
+
+ cfg_prec->request_inspect_min_size +=
+ (int) (cfg_prec->request_inspect_min_size *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+ cfg_prec->request_inspect_window +=
+ (int) (cfg_prec->request_inspect_window *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+ SCLogInfo("'%s' server has 'request-body-minimal-inspect-size' set to"
+ " %d and 'request-body-inspect-window' set to %d after"
+ " randomization.",
+ name,
+ cfg_prec->request_inspect_min_size,
+ cfg_prec->request_inspect_window);
+
+
+ cfg_prec->response_inspect_min_size +=
+ (int) (cfg_prec->response_inspect_min_size *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+ cfg_prec->response_inspect_window +=
+ (int) (cfg_prec->response_inspect_window *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+
+ SCLogInfo("'%s' server has 'response-body-minimal-inspect-size' set to"
+ " %d and 'response-body-inspect-window' set to %d after"
+ " randomization.",
+ name,
+ cfg_prec->response_inspect_min_size,
+ cfg_prec->response_inspect_window);
+ }
+
+ htp_config_register_request_line(cfg_prec->cfg, HTPCallbackRequestLine);
+
+ return;
+}
+
+static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s,
+ SCRadixTree *tree)
+{
+ if (cfg_prec == NULL || s == NULL || tree == NULL)
+ return;
+
+ ConfNode *p = NULL;
+
+ /* Default Parameters */
+ TAILQ_FOREACH(p, &s->head, next) {
+
+ if (strcasecmp("address", p->name) == 0) {
+ ConfNode *pval;
+ /* Addresses */
+ TAILQ_FOREACH(pval, &p->head, next) {
+ SCLogDebug("LIBHTP server %s: %s=%s", s->name, p->name,
+ pval->val);
+
+ /* IPV6 or IPV4? */
+ if (strchr(pval->val, ':') != NULL) {
+ SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
+ s->name, pval->val, cfg_prec->cfg);
+ if (SCRadixAddKeyIPV6String(pval->val, tree, cfg_prec) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed to "
+ "add ipv6 server %s, ignoring", pval->val);
+ }
+ } else {
+ SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
+ s->name, pval->val, cfg_prec->cfg);
+ if (SCRadixAddKeyIPV4String(pval->val, tree, cfg_prec) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed "
+ "to add ipv4 server %s, ignoring",
+ pval->val);
+ }
+ } /* else - if (strchr(pval->val, ':') != NULL) */
+ } /* TAILQ_FOREACH(pval, &p->head, next) */
+
+ } else if (strcasecmp("personality", p->name) == 0) {
+ /* Personalities */
+ int personality = HTPLookupPersonality(p->val);
+ SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
+ SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
+
+ if (personality >= 0) {
+ SCLogDebug("LIBHTP default: %s=%s (%d)", p->name, p->val,
+ personality);
+ if (htp_config_set_server_personality(cfg_prec->cfg, personality) == HTP_ERROR){
+ SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP Failed adding "
+ "personality \"%s\", ignoring", p->val);
+ } else {
+ SCLogDebug("LIBHTP personality set to %s",
+ HTPLookupPersonalityString(personality));
+ }
+
+ /* The IDS personality by default converts the path (and due to
+ * our query string callback also the query string) to lowercase.
+ * Signatures do not expect this, so override it. */
+ htp_config_set_convert_lowercase(cfg_prec->cfg, HTP_DECODER_URL_PATH, 0);
+ } else {
+ SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Unknown personality "
+ "\"%s\", ignoring", p->val);
+ continue;
+ }
+
+ } else if (strcasecmp("request-body-limit", p->name) == 0 ||
+ strcasecmp("request_body_limit", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->request_body_limit) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-limit "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("response-body-limit", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->response_body_limit) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-limit "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("request-body-minimal-inspect-size", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->request_inspect_min_size) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-minimal-inspect-size "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("request-body-inspect-window", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->request_inspect_window) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-inspect-window "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("double-decode-path", p->name) == 0) {
+ if (ConfValIsTrue(p->val)) {
+ htp_config_register_request_line(cfg_prec->cfg,
+ HTPCallbackDoubleDecodeQuery);
+ }
+
+ } else if (strcasecmp("double-decode-query", p->name) == 0) {
+ if (ConfValIsTrue(p->val)) {
+ htp_config_register_request_line(cfg_prec->cfg,
+ HTPCallbackDoubleDecodePath);
+ }
+
+ } else if (strcasecmp("response-body-minimal-inspect-size", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->response_inspect_min_size) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-minimal-inspect-size "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("response-body-inspect-window", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &cfg_prec->response_inspect_window) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+
+ } else if (strcasecmp("path-convert-backslash-separators", p->name) == 0) {
+ htp_config_set_backslash_convert_slashes(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-bestfit-replacement-char", p->name) == 0) {
+ if (strlen(p->val) == 1) {
+ htp_config_set_bestfit_replacement_byte(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ p->val[0]);
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
+ "for libhtp param path-bestfit-replacement-char");
+ }
+ } else if (strcasecmp("path-convert-lowercase", p->name) == 0) {
+ htp_config_set_convert_lowercase(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-nul-encoded-terminates", p->name) == 0) {
+ htp_config_set_nul_encoded_terminates(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-nul-raw-terminates", p->name) == 0) {
+ htp_config_set_nul_raw_terminates(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-separators-compress", p->name) == 0) {
+ htp_config_set_path_separators_compress(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-separators-decode", p->name) == 0) {
+ htp_config_set_path_separators_decode(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-u-encoding-decode", p->name) == 0) {
+ htp_config_set_u_encoding_decode(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("path-url-encoding-invalid-handling", p->name) == 0) {
+ enum htp_url_encoding_handling_t handling;
+ if (strcasecmp(p->val, "preserve_percent") == 0) {
+ handling = HTP_URL_DECODE_PRESERVE_PERCENT;
+ } else if (strcasecmp(p->val, "remove_percent") == 0) {
+ handling = HTP_URL_DECODE_REMOVE_PERCENT;
+ } else if (strcasecmp(p->val, "decode_invalid") == 0) {
+ handling = HTP_URL_DECODE_PROCESS_INVALID;
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
+ "for libhtp param path-url-encoding-invalid-handling");
+ return;
+ }
+ htp_config_set_url_encoding_invalid_handling(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ handling);
+ } else if (strcasecmp("path-utf8-convert-bestfit", p->name) == 0) {
+ htp_config_set_utf8_convert_bestfit(cfg_prec->cfg,
+ HTP_DECODER_URL_PATH,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("uri-include-all", p->name) == 0) {
+ cfg_prec->uri_include_all = ConfValIsTrue(p->val);
+ SCLogDebug("uri-include-all %s",
+ cfg_prec->uri_include_all ? "enabled" : "disabled");
+ } else if (strcasecmp("query-plusspace-decode", p->name) == 0) {
+ htp_config_set_plusspace_decode(cfg_prec->cfg,
+ HTP_DECODER_URLENCODED,
+ ConfValIsTrue(p->val));
+ } else if (strcasecmp("meta-field-limit", p->name) == 0) {
+ uint32_t limit = 0;
+ if (ParseSizeStringU32(p->val, &limit) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
+ "from conf file - %s. Killing engine", p->val);
+ exit(EXIT_FAILURE);
+ }
+ if (limit == 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
+ "from conf file cannot be 0. Killing engine");
+ exit(EXIT_FAILURE);
+ }
+ /* set default soft-limit with our new hard limit */
+ htp_config_set_field_limits(cfg_prec->cfg,
+ (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT,
+ (size_t)limit);
+ } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) {
+ cfg_prec->randomize = ConfValIsTrue(p->val);
+ } else if (strcasecmp("randomize-inspection-range", p->name) == 0) {
+ uint32_t range = atoi(p->val);
+ if (range > 100) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Invalid value for randomize"
+ " inspection range setting from conf file - %s."
+ " It should be inferior to 100."
+ " Killing engine",
+ p->val);
+ exit(EXIT_FAILURE);
+ }
+ cfg_prec->randomize_range = range;
+ } else if (strcasecmp("http-body-inline", p->name) == 0) {
+ if (ConfValIsTrue(p->val)) {
+ cfg_prec->http_body_inline = 1;
+ } else if (ConfValIsFalse(p->val)) {
+ cfg_prec->http_body_inline = 0;
+ } else {
+ if (strcmp("auto", p->val) != 0) {
+ WarnInvalidConfEntry("http_body_inline", "%s", "auto");
+ }
+ if (EngineModeIsIPS()) {
+ cfg_prec->http_body_inline = 1;
+ } else {
+ cfg_prec->http_body_inline = 0;
+ }
+ }
+ } else {
+ SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Ignoring unknown "
+ "default config: %s", p->name);
+ }
+ } /* TAILQ_FOREACH(p, &default_config->head, next) */
+
+ return;
+}
+
+void HTPConfigure(void)
+{
+ SCEnter();
+
+ cfglist.next = NULL;
+
+ cfgtree = SCRadixCreateRadixTree(NULL, NULL);
+ if (NULL == cfgtree)
+ exit(EXIT_FAILURE);
+
+ /* Default Config */
+ cfglist.cfg = htp_config_create();
+ if (NULL == cfglist.cfg) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP default config");
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("LIBHTP default config: %p", cfglist.cfg);
+ HTPConfigSetDefaultsPhase1(&cfglist);
+ if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL) {
+ HTPConfigParseParameters(&cfglist, ConfGetNode("libhtp.default-config"),
+ cfgtree);
+ } else {
+ HTPConfigParseParameters(&cfglist, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree);
+ }
+ HTPConfigSetDefaultsPhase2("default", &cfglist);
+
+ HTPParseMemcap();
+
+ /* Read server config and create a parser for each IP in radix tree */
+ ConfNode *server_config = ConfGetNode("app-layer.protocols.http.libhtp.server-config");
+ if (server_config == NULL) {
+ server_config = ConfGetNode("libhtp.server-config");
+ if (server_config == NULL) {
+ SCLogDebug("LIBHTP Configuring %p", server_config);
+ SCReturn;
+ }
+ }
+ SCLogDebug("LIBHTP Configuring %p", server_config);
+
+ ConfNode *si;
+ /* Server Nodes */
+ TAILQ_FOREACH(si, &server_config->head, next) {
+ /* Need the named node, not the index */
+ ConfNode *s = TAILQ_FIRST(&si->head);
+ if (NULL == s) {
+ SCLogDebug("LIBHTP s NULL");
+ continue;
+ }
+
+ SCLogDebug("LIBHTP server %s", s->name);
+
+ HTPCfgRec *nextrec = cfglist.next;
+ HTPCfgRec *htprec = SCMalloc(sizeof(HTPCfgRec));
+ if (NULL == htprec)
+ exit(EXIT_FAILURE);
+ memset(htprec, 0x00, sizeof(*htprec));
+
+ cfglist.next = htprec;
+
+ cfglist.next->next = nextrec;
+ cfglist.next->cfg = htp_config_create();
+ if (NULL == cfglist.next->cfg) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP server config");
+ exit(EXIT_FAILURE);
+ }
+
+ HTPConfigSetDefaultsPhase1(htprec);
+ HTPConfigParseParameters(htprec, s, cfgtree);
+ HTPConfigSetDefaultsPhase2(s->name, htprec);
+ }
+
+ SCReturn;
+}
+
+void AppLayerHtpPrintStats(void)
+{
+#ifdef DEBUG
+ SCMutexLock(&htp_state_mem_lock);
+ SCLogInfo("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
+ SCMutexUnlock(&htp_state_mem_lock);
+#endif
+}
+
+/** \internal
+ * \brief get files callback
+ * \param state state ptr
+ * \param direction flow direction
+ * \retval files files ptr
+ */
+static FileContainer *HTPStateGetFiles(void *state, uint8_t direction)
+{
+ if (state == NULL)
+ return NULL;
+
+ HtpState *http_state = (HtpState *)state;
+
+ if (direction & STREAM_TOCLIENT) {
+ SCReturnPtr(http_state->files_tc, "FileContainer");
+ } else {
+ SCReturnPtr(http_state->files_ts, "FileContainer");
+ }
+}
+
+static int HTPStateGetAlstateProgress(void *tx, uint8_t direction)
+{
+ if (direction & STREAM_TOSERVER)
+ return ((htp_tx_t *)tx)->request_progress;
+ else
+ return ((htp_tx_t *)tx)->response_progress;
+}
+
+static uint64_t HTPStateGetTxCnt(void *alstate)
+{
+ HtpState *http_state = (HtpState *)alstate;
+
+ if (http_state != NULL && http_state->conn != NULL)
+ return (uint64_t)htp_list_size(http_state->conn->transactions);
+ else
+ return 0ULL;
+}
+
+static void *HTPStateGetTx(void *alstate, uint64_t tx_id)
+{
+ HtpState *http_state = (HtpState *)alstate;
+
+ if (http_state != NULL && http_state->conn != NULL)
+ return htp_list_get(http_state->conn->transactions, tx_id);
+ else
+ return NULL;
+}
+
+static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction)
+{
+ return (direction & STREAM_TOSERVER) ? HTP_REQUEST_COMPLETE : HTP_RESPONSE_COMPLETE;
+}
+
+int HTPStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, http_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "http's enum map table.", event_name);
+ /* this should be treated as fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+static void HTPStateTruncate(void *state, uint8_t direction)
+{
+ FileContainer *fc = HTPStateGetFiles(state, direction);
+ if (fc != NULL) {
+ FileTruncateAllOpenFiles(fc);
+ }
+}
+
+static int HTPStateHasTxDetectState(void *alstate)
+{
+ HtpState *htp_state = (HtpState *)alstate;
+ return (htp_state->tx_with_detect_state_cnt > 0);
+}
+
+static DetectEngineState *HTPGetTxDetectState(void *vtx)
+{
+ htp_tx_t *tx = (htp_tx_t *)vtx;
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ return tx_ud ? tx_ud->de_state : NULL;
+}
+
+static int HTPSetTxDetectState(void *alstate, void *vtx, DetectEngineState *s)
+{
+ HtpState *htp_state = (HtpState *)alstate;
+ htp_tx_t *tx = (htp_tx_t *)vtx;
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ if (tx_ud == NULL) {
+ tx_ud = HTPMalloc(sizeof(*tx_ud));
+ if (unlikely(tx_ud == NULL))
+ return -ENOMEM;
+ memset(tx_ud, 0, sizeof(*tx_ud));
+ htp_tx_set_user_data(tx, tx_ud);
+ }
+ htp_state->tx_with_detect_state_cnt++;
+ tx_ud->de_state = s;
+ return 0;
+}
+
+static int HTPRegisterPatternsForProtocolDetection(void)
+{
+ char *methods[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
+ "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
+ "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
+ "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
+ "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL};
+ char *spacings[] = { "|20|", "|09|", NULL };
+ char *versions[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL };
+
+ uint methods_pos;
+ uint spacings_pos;
+ uint versions_pos;
+ int register_result;
+ char method_buffer[32] = "";
+
+ /* Loop through all the methods ands spacings and register the patterns */
+ for (methods_pos = 0; methods[methods_pos]; methods_pos++) {
+ for (spacings_pos = 0; spacings[spacings_pos]; spacings_pos++) {
+
+ /* Combine the method name and the spacing */
+ snprintf(method_buffer, sizeof(method_buffer), "%s%s", methods[methods_pos], spacings[spacings_pos]);
+
+ /* Register the new method+spacing pattern
+ * 3 is subtracted from the length since the spacing is hex typed as |xx|
+ * but the pattern matching should only be one char
+ */
+ register_result = AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP,
+ ALPROTO_HTTP, method_buffer, strlen(method_buffer)-3, 0, STREAM_TOSERVER);
+ if (register_result < 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* Loop through all the http verions patterns that are TO_CLIENT */
+ for (versions_pos = 0; versions[versions_pos]; versions_pos++) {
+ register_result = AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP,
+ ALPROTO_HTTP, versions[versions_pos], strlen(versions[versions_pos]),
+ 0, STREAM_TOCLIENT);
+ if (register_result < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Register the HTTP protocol and state handling functions to APP layer
+ * of the engine.
+ */
+void RegisterHTPParsers(void)
+{
+ SCEnter();
+
+ char *proto_name = "http";
+
+ /** HTTP */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP, proto_name);
+ if (HTPRegisterPatternsForProtocolDetection() < 0)
+ return;
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTransactionFree);
+ AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetFiles);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetAlstateProgress);
+ AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxCnt);
+ AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTx);
+ AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, ALPROTO_HTTP,
+ HTPStateGetAlstateProgressCompletionStatus);
+ AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPHasEvents);
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPGetEvents);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetEventInfo);
+
+ AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTruncate);
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_HTTP,
+ HTPStateHasTxDetectState,
+ HTPGetTxDetectState, HTPSetTxDetectState);
+
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP, STREAM_TOSERVER,
+ HTPHandleRequestData);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP, STREAM_TOCLIENT,
+ HTPHandleResponseData);
+ SC_ATOMIC_INIT(htp_config_flags);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_HTTP, STREAM_TOSERVER);
+ HTPConfigure();
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_HTTP, HTPParserRegisterTests);
+#endif
+
+ SCReturn;
+}
+
+#ifdef UNITTESTS
+static HTPCfgRec cfglist_backup;
+
+void HtpConfigCreateBackup(void)
+{
+ cfglist_backup = cfglist;
+
+ return;
+}
+
+void HtpConfigRestoreBackup(void)
+{
+ cfglist = cfglist_backup;
+
+ return;
+}
+
+/** \test Test case where chunks are sent in smaller chunks and check the
+ * response of the parser from HTP library. */
+int HTPParserTest01(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
+ " Data is c0oL!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0")
+ || tx->request_method_number != HTP_M_POST ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_0)
+ {
+ printf("expected header value: Victor/1.0 and got %s: and expected"
+ " method: POST and got %s, expected protocol number HTTP/1.0"
+ " and got: %s \n", bstr_util_strdup_to_c(h->value),
+ bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test case where chunks are sent in smaller chunks and check the
+ * response of the parser from HTP library. */
+static int HTPParserTest01a(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
+ " Data is c0oL!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0")
+ || tx->request_method_number != HTP_M_POST ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_0)
+ {
+ printf("expected header value: Victor/1.0 and got %s: and expected"
+ " method: POST and got %s, expected protocol number HTTP/1.0"
+ " and got: %s \n", bstr_util_strdup_to_c(h->value),
+ bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test See how it deals with an incomplete request. */
+int HTPParserTest02(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(http_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if ((tx->request_method) != NULL || h != NULL)
+ {
+ printf("expected method NULL, got %s \n", bstr_util_strdup_to_c(tx->request_method));
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test case where method is invalid and data is sent in smaller chunks
+ * and check the response of the parser from HTP library. */
+int HTPParserTest03(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (tx->request_method_number != HTP_M_UNKNOWN ||
+ h != NULL || tx->request_protocol_number != HTP_PROTOCOL_1_0)
+ {
+ printf("expected method M_UNKNOWN and got %s: , expected protocol "
+ "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test case where invalid data is sent and check the response of the
+ * parser from HTP library. */
+int HTPParserTest04(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ HtpState *htp_state = NULL;
+ uint8_t httpbuf1[] = "World!\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (tx->request_method_number != HTP_M_UNKNOWN ||
+ h != NULL || tx->request_protocol_number != HTP_PROTOCOL_0_9)
+ {
+ printf("expected method M_UNKNOWN and got %s: , expected protocol "
+ "NULL and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test both sides of a http stream mixed up to see if the HTP parser
+ * properly parsed them and also keeps them separated. */
+int HTPParserTest05(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ HtpState *http_state = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "Post D";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "ata is c0oL!";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "post R";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint8_t httpbuf6[] = "esults are tha bomb!";
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
+ httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf4,
+ httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3,
+ httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf6,
+ httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(http_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (tx->request_method_number != HTP_M_POST ||
+ h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_0)
+ {
+ printf("expected method M_POST and got %s: , expected protocol "
+ "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+ if (tx->response_status_number != 200) {
+ printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
+ "HTTP/1.0 and got %s \n", tx->response_status_number,
+ bstr_util_strdup_to_c(tx->response_message),
+ bstr_util_strdup_to_c(tx->response_protocol));
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test proper chunked encoded response body
+ */
+int HTPParserTest06(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
+ "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
+ "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
+ "GMT\r\n"
+ "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
+ "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
+ "FrontPage/5.0.2.2510\r\n"
+ "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
+ "chunked\r\n"
+ "Content-Type: text/html\r\n\r\n"
+ "580\r\n"
+ "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
+ "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
+ "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
+ "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
+ "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
+ "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
+ "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
+ "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
+ "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
+ "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
+ "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
+ "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
+ "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
+ "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
+ "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
+ "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
+ "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
+ "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
+ "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
+ "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
+ "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
+ "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
+ "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
+ "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
+ "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
+ "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
+ "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
+ "aHA=\r\n0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
+ httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf2,
+ httplen2);
+ if (r != 0) {
+ printf("toclient chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(http_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (tx->request_method_number != HTP_M_GET ||
+ h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ printf("expected method M_GET and got %s: , expected protocol "
+ "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ result = 0;
+ goto end;
+ }
+
+ if (tx->response_status_number != 200 ||
+ h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ printf("expected response 200 OK and got %"PRId32" %s: , expected proto"
+ "col HTTP/1.1 and got %s \n", tx->response_status_number,
+ bstr_util_strdup_to_c(tx->response_message),
+ bstr_util_strdup_to_c(tx->response_protocol));
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test
+ */
+int HTPParserTest07(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ uint8_t ref[] = "/awstats.pl?/migratemigrate = |";
+ size_t reflen = sizeof(ref) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+#include "conf-yaml-loader.h"
+
+/** \test Abort
+ */
+int HTPParserTest08(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint8_t flags = 0;
+ flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk returned %" PRId32 ", expected"
+ " 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
+ PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Abort
+ */
+int HTPParserTest09(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: Apache_2_2\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint8_t flags = 0;
+ flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk returned %" PRId32 ", expected"
+ " 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
+ PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Host:www.google.com <- missing space between name:value (rfc violation)
+ */
+int HTPParserTest10(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (h == NULL) {
+ goto end;
+ }
+
+ char *name = bstr_util_strdup_to_c(h->name);
+ if (name == NULL) {
+ goto end;
+ }
+
+ if (strcmp(name, "Host") != 0) {
+ printf("header name not \"Host\", instead \"%s\": ", name);
+ free(name);
+ goto end;
+ }
+ free(name);
+
+ char *value = bstr_util_strdup_to_c(h->value);
+ if (value == NULL) {
+ goto end;
+ }
+
+ if (strcmp(value, "www.google.com") != 0) {
+ printf("header value not \"www.google.com\", instead \"%s\": ", value);
+ free(value);
+ goto end;
+ }
+ free(value);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test double encoding in path
+ */
+static int HTPParserTest11(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /%2500 HTTP/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (tx != NULL && tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (4 != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be 2, is %"PRIuMAX,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[1] != '%' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[2] != '0' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[3] != '0')
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test double encoding in query
+ */
+static int HTPParserTest12(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (7 != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be 5, is %"PRIuMAX,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[1] != '?' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[2] != 'a' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[3] != '=' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[4] != '%' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[5] != '0' ||
+ bstr_ptr(tx_ud->request_uri_normalized)[6] != '0')
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
+ */
+int HTPParserTest13(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0)
+ flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1))
+ flags = STREAM_TOSERVER|STREAM_EOF;
+ else
+ flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
+ if (h == NULL) {
+ goto end;
+ }
+
+ char *name = bstr_util_strdup_to_c(h->name);
+ if (name == NULL) {
+ goto end;
+ }
+
+ if (strcmp(name, "Host") != 0) {
+ printf("header name not \"Host\", instead \"%s\": ", name);
+ free(name);
+ goto end;
+ }
+ free(name);
+
+ char *value = bstr_util_strdup_to_c(h->value);
+ if (value == NULL) {
+ goto end;
+ }
+
+ if (strcmp(value, "www.google.com\rName: Value") != 0) {
+ printf("header value not \"www.google.com\", instead \"");
+ PrintRawUriFp(stdout, (uint8_t *)value, strlen(value));
+ printf("\": ");
+ free(value);
+ goto end;
+ }
+ free(value);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test basic config */
+int HTPParserConfigTest01(void)
+{
+ int ret = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+\n\
+ server-config:\n\
+\n\
+ - apache-tomcat:\n\
+ address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
+ personality: Tomcat_6_0\n\
+\n\
+ - iis7:\n\
+ address: \n\
+ - 192.168.0.0/24\n\
+ - 192.168.10.0/24\n\
+ personality: IIS_7_0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ ConfNode *outputs;
+ outputs = ConfGetNode("libhtp.default-config.personality");
+ if (outputs == NULL) {
+ goto end;
+ }
+
+ outputs = ConfGetNode("libhtp.server-config");
+ if (outputs == NULL) {
+ goto end;
+ }
+
+ ConfNode *node = TAILQ_FIRST(&outputs->head);
+ if (node == NULL) {
+ goto end;
+ }
+ if (strcmp(node->name, "0") != 0) {
+ goto end;
+ }
+ node = TAILQ_FIRST(&node->head);
+ if (node == NULL) {
+ goto end;
+ }
+ if (strcmp(node->name, "apache-tomcat") != 0) {
+ goto end;
+ }
+
+ int i = 0;
+ ConfNode *n;
+
+ ConfNode *node2 = ConfNodeLookupChild(node, "personality");
+ if (node2 == NULL) {
+ goto end;
+ }
+ if (strcmp(node2->val, "Tomcat_6_0") != 0) {
+ goto end;
+ }
+
+ node = ConfNodeLookupChild(node, "address");
+ if (node == NULL) {
+ goto end;
+ }
+ TAILQ_FOREACH(n, &node->head, next) {
+ if (n == NULL) {
+ goto end;
+ }
+
+ switch(i) {
+ case 0:
+ if (strcmp(n->name, "0") != 0) {
+ goto end;
+ }
+ if (strcmp(n->val, "192.168.1.0/24") != 0) {
+ goto end;
+ }
+ break;
+ case 1:
+ if (strcmp(n->name, "1") != 0) {
+ goto end;
+ }
+ if (strcmp(n->val, "127.0.0.0/8") != 0) {
+ goto end;
+ }
+ break;
+ case 2:
+ if (strcmp(n->name, "2") != 0) {
+ goto end;
+ }
+ if (strcmp(n->val, "::1") != 0) {
+ goto end;
+ }
+ break;
+ default:
+ goto end;
+ }
+ i++;
+ }
+
+ outputs = ConfGetNode("libhtp.server-config");
+ if (outputs == NULL) {
+ goto end;
+ }
+
+ node = TAILQ_FIRST(&outputs->head);
+ node = TAILQ_NEXT(node, next);
+ if (node == NULL) {
+ goto end;
+ }
+ if (strcmp(node->name, "1") != 0) {
+ goto end;
+ }
+ node = TAILQ_FIRST(&node->head);
+ if (node == NULL) {
+ goto end;
+ }
+ if (strcmp(node->name, "iis7") != 0) {
+ goto end;
+ }
+
+ node2 = ConfNodeLookupChild(node, "personality");
+ if (node2 == NULL) {
+ goto end;
+ }
+ if (strcmp(node2->val, "IIS_7_0") != 0) {
+ goto end;
+ }
+
+ node = ConfNodeLookupChild(node, "address");
+ if (node == NULL) {
+ goto end;
+ }
+
+ i = 0;
+ TAILQ_FOREACH(n, &node->head, next) {
+ if (n == NULL) {
+ goto end;
+ }
+
+ switch(i) {
+ case 0:
+ if (strcmp(n->name, "0") != 0) {
+ goto end;
+ }
+ if (strcmp(n->val, "192.168.0.0/24") != 0) {
+ goto end;
+ }
+ break;
+ case 1:
+ if (strcmp(n->name, "1") != 0) {
+ goto end;
+ }
+ if (strcmp(n->val, "192.168.10.0/24") != 0) {
+ goto end;
+ }
+ break;
+ default:
+ goto end;
+ }
+ i++;
+ }
+
+ ret = 1;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return ret;
+}
+
+/** \test Test config builds radix correctly */
+int HTPParserConfigTest02(void)
+{
+ int ret = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+\n\
+ server-config:\n\
+\n\
+ - apache-tomcat:\n\
+ address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
+ personality: Tomcat_6_0\n\
+\n\
+ - iis7:\n\
+ address: \n\
+ - 192.168.0.0/24\n\
+ - 192.168.10.0/24\n\
+ personality: IIS_7_0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ HTPConfigure();
+
+ if (cfglist.cfg == NULL) {
+ printf("No default config created.\n");
+ goto end;
+ }
+
+ if (cfgtree == NULL) {
+ printf("No config tree created.\n");
+ goto end;
+ }
+
+ htp_cfg_t *htp = cfglist.cfg;
+ uint8_t buf[128];
+ const char *addr;
+ void *user_data = NULL;
+
+ addr = "192.168.10.42";
+ if (inet_pton(AF_INET, addr, buf) == 1) {
+ (void)SCRadixFindKeyIPV4BestMatch(buf, cfgtree, &user_data);
+ if (user_data != NULL) {
+ HTPCfgRec *htp_cfg_rec = user_data;
+ htp = htp_cfg_rec->cfg;
+ SCLogDebug("LIBHTP using config: %p", htp);
+ }
+ if (htp == NULL) {
+ printf("Could not get config for: %s\n", addr);
+ goto end;
+ }
+ }
+ else {
+ printf("Failed to parse address: %s\n", addr);
+ goto end;
+ }
+
+ user_data = NULL;
+ addr = "::1";
+ if (inet_pton(AF_INET6, addr, buf) == 1) {
+ (void)SCRadixFindKeyIPV6BestMatch(buf, cfgtree, &user_data);
+ if (user_data != NULL) {
+ HTPCfgRec *htp_cfg_rec = user_data;
+ htp = htp_cfg_rec->cfg;
+ SCLogDebug("LIBHTP using config: %p", htp);
+ }
+ if (htp == NULL) {
+ printf("Could not get config for: %s\n", addr);
+ goto end;
+ }
+ }
+ else {
+ printf("Failed to parse address: %s\n", addr);
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ return ret;
+}
+
+/** \test Test traffic is handled by the correct htp config */
+int HTPParserConfigTest03(void)
+{
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
+ " Data is c0oL!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+\n\
+ server-config:\n\
+\n\
+ - apache-tomcat:\n\
+ address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
+ personality: Tomcat_6_0\n\
+\n\
+ - iis7:\n\
+ address: \n\
+ - 192.168.0.0/24\n\
+ - 192.168.10.0/24\n\
+ personality: IIS_7_0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ HTPConfigure();
+
+ char *addr = "192.168.10.42";
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ htp_cfg_t *htp = cfglist.cfg;
+
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f->dst.addr_data32, cfgtree, &user_data);
+ if (user_data != NULL) {
+ HTPCfgRec *htp_cfg_rec = user_data;
+ htp = htp_cfg_rec->cfg;
+ SCLogDebug("LIBHTP using config: %p", htp);
+ }
+ if (htp == NULL) {
+ printf("Could not get config for: %s\n", addr);
+ goto end;
+ }
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (HTPStateGetTxCnt(htp_state) != 2) {
+ printf("HTPStateGetTxCnt(htp_state) failure\n");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ if (tx->cfg != htp) {
+ printf("wrong HTP config (%p instead of %p - default=%p): ",
+ tx->cfg, htp, cfglist.cfg);
+ goto end;
+ }
+ tx = HTPStateGetTx(htp_state, 1);
+ if (tx == NULL)
+ goto end;
+ if (tx->cfg != htp) {
+ printf("wrong HTP config (%p instead of %p - default=%p): ",
+ tx->cfg, htp, cfglist.cfg);
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/* disabled when we upgraded to libhtp 0.5.x */
+#if 0
+int HTPParserConfigTest04(void)
+{
+ int result = 0;
+
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ path-control-char-handling: status_400\n\
+ path-convert-utf8: yes\n\
+ path-invalid-encoding-handling: remove_percent\n\
+\n\
+ server-config:\n\
+\n\
+ - apache-tomcat:\n\
+ personality: Tomcat_6_0\n\
+ path-invalid-utf8-handling: none\n\
+ path-nul-encoded-handling: status_404\n\
+ path-nul-raw-handling: status_400\n\
+\n\
+ - iis7:\n\
+ personality: IIS_7_0\n\
+ path-replacement-char: o\n\
+ path-unicode-mapping: status_400\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ HTPConfigure();
+
+ HTPCfgRec *cfg_rec = &cfglist;
+ if (cfg_rec->cfg->path_control_char_handling != STATUS_400 ||
+ cfg_rec->cfg->path_convert_utf8 != 1 ||
+ cfg_rec->cfg->path_invalid_encoding_handling != URL_DECODER_REMOVE_PERCENT) {
+ printf("failed 1\n");
+ goto end;
+ }
+
+ cfg_rec = cfg_rec->next;
+ if (cfg_rec->cfg->bestfit_replacement_char != 'o' ||
+ cfg_rec->cfg->path_unicode_mapping != STATUS_400) {
+ printf("failed 2\n");
+ goto end;
+ }
+
+ cfg_rec = cfg_rec->next;
+ if (cfg_rec->cfg->path_invalid_utf8_handling != NONE ||
+ cfg_rec->cfg->path_nul_encoded_handling != STATUS_404 ||
+ cfg_rec->cfg->path_nul_raw_handling != STATUS_400) {
+ printf("failed 3\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ return result;
+}
+#endif
+
+/** \test Test %2f decoding in profile Apache_2_2
+ *
+ * %2f in path is left untouched
+ * %2f in query string is normalized to %2F
+ * %252f in query string is decoded/normalized to %2F
+ */
+static int HTPParserDecodingTest01(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
+ "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
+ "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: Apache_2\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/abc%2fdef";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ uint8_t ref2[] = "/abc/def?ghi/jkl";
+ reflen = sizeof(ref2) - 1;
+
+ tx = HTPStateGetTx(htp_state, 1);
+ if (tx == NULL)
+ goto end;
+ tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref2, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ uint8_t ref3[] = "/abc/def?ghi%2fjkl";
+ reflen = sizeof(ref3) - 1;
+ tx = HTPStateGetTx(htp_state, 2);
+ if (tx == NULL)
+ goto end;
+ tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref3, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test %2f decoding in profile IDS
+ *
+ * %2f in path decoded to /
+ * %2f in query string is decoded to /
+ * %252f in query string is decoded to %2F
+ */
+static int HTPParserDecodingTest02(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
+ "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
+ "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: no\n\
+ double-decode-query: no\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/abc/def";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ uint8_t ref2[] = "/abc/def?ghi/jkl";
+ reflen = sizeof(ref2) - 1;
+
+ tx = HTPStateGetTx(htp_state, 1);
+ if (tx == NULL)
+ goto end;
+ tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref2, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ uint8_t ref3[] = "/abc/def?ghi%2fjkl";
+ reflen = sizeof(ref3) - 1;
+ tx = HTPStateGetTx(htp_state, 2);
+ if (tx == NULL)
+ goto end;
+ tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX" (3): ",
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref3, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test %2f decoding in profile IDS with double-decode-* options
+ *
+ * %252f in path decoded to /
+ * %252f in query string is decoded to /
+ */
+static int HTPParserDecodingTest03(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
+ "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: yes\n\
+ double-decode-query: yes\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/abc/def";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ uint8_t ref2[] = "/abc/def?ghi/jkl";
+ reflen = sizeof(ref2) - 1;
+
+ tx = HTPStateGetTx(htp_state, 1);
+ if (tx == NULL)
+ goto end;
+ tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref2, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test http:// in query profile IDS
+ */
+static int HTPParserDecodingTest04(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: yes\n\
+ double-decode-query: yes\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/abc/def?a=http://www.abc.com/";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test \ char in query profile IDS. Bug 739
+ */
+static int HTPParserDecodingTest05(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: yes\n\
+ double-decode-query: yes\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test + char in query. Bug 1035
+ */
+static int HTPParserDecodingTest06(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: yes\n\
+ double-decode-query: yes\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/put.php?ip=1.2.3.4&port=+6000";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test + char in query. Bug 1035
+ */
+static int HTPParserDecodingTest07(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: yes\n\
+ double-decode-query: yes\n\
+ query-plusspace-decode: yes\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/put.php?ip=1.2.3.4&port= 6000";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test 'proxy' URI normalization. Ticket 1008
+ */
+static int HTPParserDecodingTest08(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "/blah/";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test 'proxy' URI normalization. Ticket 1008
+ */
+static int HTPParserDecodingTest09(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] =
+ "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ uri-include-all: true\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+ char *addr = "4.3.2.1";
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < httplen1; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ uint8_t ref1[] = "http://suricata-ids.org/blah/";
+ size_t reflen = sizeof(ref1) - 1;
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL)
+ goto end;
+ HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
+ printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
+ (uintmax_t)reflen,
+ (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
+ goto end;
+ }
+
+ if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
+ bstr_len(tx_ud->request_uri_normalized)) != 0)
+ {
+ printf("normalized uri \"");
+ PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
+ printf("\" != \"");
+ PrintRawUriFp(stdout, ref1, reflen);
+ printf("\": ");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test BG box crash -- chunks are messed up. Observed for real. */
+static int HTPBodyReassemblyTest01(void)
+{
+ int result = 0;
+ HtpTxUserData htud;
+ memset(&htud, 0x00, sizeof(htud));
+ HtpState hstate;
+ memset(&hstate, 0x00, sizeof(hstate));
+ Flow flow;
+ memset(&flow, 0x00, sizeof(flow));
+ AppLayerParserState *parser = AppLayerParserStateAlloc();
+ htp_tx_t tx;
+ memset(&tx, 0, sizeof(tx));
+
+ hstate.f = &flow;
+ flow.alparser = parser;
+
+ uint8_t chunk1[] = "--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
+ uint8_t chunk2[] = "POST /uri HTTP/1.1\r\nHost: hostname.com\r\nKeep-Alive: 115\r\nAccept-Charset: utf-8\r\nUser-Agent: Mozilla/5.0 (X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nConnection: keep-alive\r\nContent-length: 68102\r\nReferer: http://otherhost.com\r\nAccept-Encoding: gzip\r\nContent-Type: multipart/form-data; boundary=e5a320f21416a02493a0a6f561b1c494\r\nCookie: blah\r\nAccept-Language: us\r\n\r\n--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
+
+ int r = HtpBodyAppendChunk(&htud, &htud.request_body, (uint8_t *)chunk1, sizeof(chunk1)-1);
+ BUG_ON(r != 0);
+ r = HtpBodyAppendChunk(&htud, &htud.request_body, (uint8_t *)chunk2, sizeof(chunk2)-1);
+ BUG_ON(r != 0);
+
+ uint8_t *chunks_buffer = NULL;
+ uint32_t chunks_buffer_len = 0;
+
+ HtpRequestBodyReassemble(&htud, &chunks_buffer, &chunks_buffer_len);
+ if (chunks_buffer == NULL) {
+ goto end;
+ }
+#ifdef PRINT
+ printf("REASSCHUNK START: \n");
+ PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
+ printf("REASSCHUNK END: \n");
+#endif
+
+ HtpRequestBodyHandleMultipart(&hstate, &htud, &tx, chunks_buffer, chunks_buffer_len);
+
+ if (htud.request_body.content_len_so_far != 669) {
+ printf("htud.request_body.content_len_so_far %"PRIu64": ", htud.request_body.content_len_so_far);
+ goto end;
+ }
+
+ if (hstate.files_ts != NULL)
+ goto end;
+
+ result = 1;
+end:
+ return result;
+}
+
+/** \test BG crash */
+static int HTPSegvTest01(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t httpbuf1[] = "POST /uri HTTP/1.1\r\nHost: hostname.com\r\nKeep-Alive: 115\r\nAccept-Charset: utf-8\r\nUser-Agent: Mozilla/5.0 (X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nConnection: keep-alive\r\nContent-length: 68102\r\nReferer: http://otherhost.com\r\nAccept-Encoding: gzip\r\nContent-Type: multipart/form-data; boundary=e5a320f21416a02493a0a6f561b1c494\r\nCookie: blah\r\nAccept-Language: us\r\n\r\n--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: no\n\
+ double-decode-query: no\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetDecoderEvents(f->alparser);
+ if (decoder_events != NULL) {
+ printf("app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+ StreamTcpFreeConfig(TRUE);
+ if (http_state != NULL)
+ HTPStateFree(http_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+/** \test Test really long request, this should result in HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
+int HTPParserTest14(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ char *httpbuf = NULL;
+ size_t len = 18887;
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: no\n\
+ double-decode-query: no\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+";
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ httpbuf = SCMalloc(len);
+ if (unlikely(httpbuf == NULL))
+ goto end;
+ memset(httpbuf, 0x00, len);
+
+ /* create the request with a longer than 18k cookie */
+ strlcpy(httpbuf, "GET /blah/ HTTP/1.1\r\n"
+ "Host: myhost.lan\r\n"
+ "Connection: keep-alive\r\n"
+ "Accept: */*\r\n"
+ "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
+ "Referer: http://blah.lan/\r\n"
+ "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
+ "Cookie: ", len);
+ size_t o = strlen(httpbuf);
+ for ( ; o < len - 4; o++) {
+ httpbuf[o] = 'A';
+ }
+ httpbuf[len - 4] = '\r';
+ httpbuf[len - 3] = '\n';
+ httpbuf[len - 2] = '\r';
+ httpbuf[len - 1] = '\n';
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < len; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (len - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, (uint8_t *)&httpbuf[u], 1);
+ if (u < 18294) { /* first 18294 bytes should result in 0 */
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ } else if (u == 18294UL) { /* byte 18294 should result in error */
+ if (r != -1) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " -1: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+
+ /* break out, htp state is in error state now */
+ SCMutexUnlock(&f->m);
+ break;
+ }
+ SCMutexUnlock(&f->m);
+ }
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL || tx->request_method_number != HTP_M_GET || tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ printf("expected method M_GET and got %s: , expected protocol "
+ "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events == NULL) {
+ printf("no app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ if (decoder_events->events[0] != HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG) {
+ printf("HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG not set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ if (httpbuf != NULL)
+ SCFree(httpbuf);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+ return result;
+}
+
+/** \test Test really long request (same as HTPParserTest14), now with config
+ * update to allow it */
+int HTPParserTest15(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ char *httpbuf = NULL;
+ size_t len = 18887;
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ double-decode-path: no\n\
+ double-decode-query: no\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+ meta-field-limit: 20000\n\
+";
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ httpbuf = SCMalloc(len);
+ if (unlikely(httpbuf == NULL))
+ goto end;
+ memset(httpbuf, 0x00, len);
+
+ /* create the request with a longer than 18k cookie */
+ strlcpy(httpbuf, "GET /blah/ HTTP/1.1\r\n"
+ "Host: myhost.lan\r\n"
+ "Connection: keep-alive\r\n"
+ "Accept: */*\r\n"
+ "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
+ "Referer: http://blah.lan/\r\n"
+ "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
+ "Cookie: ", len);
+ size_t o = strlen(httpbuf);
+ for ( ; o < len - 4; o++) {
+ httpbuf[o] = 'A';
+ }
+ httpbuf[len - 4] = '\r';
+ httpbuf[len - 3] = '\n';
+ httpbuf[len - 2] = '\r';
+ httpbuf[len - 1] = '\n';
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint32_t u;
+ for (u = 0; u < len; u++) {
+ uint8_t flags = 0;
+
+ if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
+ else if (u == (len - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
+ else flags = STREAM_TOSERVER;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, (uint8_t *)&httpbuf[u], 1);
+ if (r != 0) {
+ printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
+ " 0: ", u, r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+ }
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL || tx->request_method_number != HTP_M_GET || tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ printf("expected method M_GET and got %s: , expected protocol "
+ "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
+ bstr_util_strdup_to_c(tx->request_protocol));
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events != NULL) {
+ printf("app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ if (httpbuf != NULL)
+ SCFree(httpbuf);
+ HTPFreeConfig();
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ HtpConfigRestoreBackup();
+ return result;
+}
+
+/** \test Test unusual delims in request line HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
+int HTPParserTest16(void)
+{
+ int result = 0;
+ Flow *f = NULL;
+ TcpSession ssn;
+ HtpState *htp_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ uint8_t httpbuf[] = "GET\f/blah/\fHTTP/1.1\r\n"
+ "Host: myhost.lan\r\n"
+ "Connection: keep-alive\r\n"
+ "Accept: */*\r\n"
+ "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
+ "Referer: http://blah.lan/\r\n"
+ "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
+ "Cookie: blah\r\n\r\n";
+ size_t len = sizeof(httpbuf) - 1;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ uint8_t flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
+
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, flags, (uint8_t *)httpbuf, len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ htp_state = f->alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
+ if (tx == NULL || tx->request_method_number != HTP_M_GET || tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ printf("expected method M_GET and got %s: , expected protocol "
+ "HTTP/1.1 and got %s \n", tx ? bstr_util_strdup_to_c(tx->request_method) : "tx null",
+ tx ? bstr_util_strdup_to_c(tx->request_protocol) : "tx null");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
+ if (decoder_events == NULL) {
+ printf("no app events: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ if (decoder_events->events[0] != HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT) {
+ printf("HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT not set: ");
+ goto end;
+ }
+
+ if (decoder_events->events[1] != HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT) {
+ printf("HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT not set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ UTHFreeFlow(f);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the Unit tests for the HTTP protocol
+ */
+void HTPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HTPParserTest01", HTPParserTest01, 1);
+ UtRegisterTest("HTPParserTest01a", HTPParserTest01a, 1);
+ UtRegisterTest("HTPParserTest02", HTPParserTest02, 1);
+ UtRegisterTest("HTPParserTest03", HTPParserTest03, 1);
+ UtRegisterTest("HTPParserTest04", HTPParserTest04, 1);
+ UtRegisterTest("HTPParserTest05", HTPParserTest05, 1);
+ UtRegisterTest("HTPParserTest06", HTPParserTest06, 1);
+ UtRegisterTest("HTPParserTest07", HTPParserTest07, 1);
+ UtRegisterTest("HTPParserTest08", HTPParserTest08, 1);
+ UtRegisterTest("HTPParserTest09", HTPParserTest09, 1);
+ UtRegisterTest("HTPParserTest10", HTPParserTest10, 1);
+ UtRegisterTest("HTPParserTest11", HTPParserTest11, 1);
+ UtRegisterTest("HTPParserTest12", HTPParserTest12, 1);
+ UtRegisterTest("HTPParserTest13", HTPParserTest13, 1);
+ UtRegisterTest("HTPParserConfigTest01", HTPParserConfigTest01, 1);
+ UtRegisterTest("HTPParserConfigTest02", HTPParserConfigTest02, 1);
+ UtRegisterTest("HTPParserConfigTest03", HTPParserConfigTest03, 1);
+#if 0 /* disabled when we upgraded to libhtp 0.5.x */
+ UtRegisterTest("HTPParserConfigTest04", HTPParserConfigTest04, 1);
+#endif
+
+ UtRegisterTest("HTPParserDecodingTest01", HTPParserDecodingTest01, 1);
+ UtRegisterTest("HTPParserDecodingTest02", HTPParserDecodingTest02, 1);
+ UtRegisterTest("HTPParserDecodingTest03", HTPParserDecodingTest03, 1);
+ UtRegisterTest("HTPParserDecodingTest04", HTPParserDecodingTest04, 1);
+ UtRegisterTest("HTPParserDecodingTest05", HTPParserDecodingTest05, 1);
+ UtRegisterTest("HTPParserDecodingTest06", HTPParserDecodingTest06, 1);
+ UtRegisterTest("HTPParserDecodingTest07", HTPParserDecodingTest07, 1);
+ UtRegisterTest("HTPParserDecodingTest08", HTPParserDecodingTest08, 1);
+ UtRegisterTest("HTPParserDecodingTest09", HTPParserDecodingTest09, 1);
+
+ UtRegisterTest("HTPBodyReassemblyTest01", HTPBodyReassemblyTest01, 1);
+
+ UtRegisterTest("HTPSegvTest01", HTPSegvTest01, 1);
+
+ UtRegisterTest("HTPParserTest14", HTPParserTest14, 1);
+ UtRegisterTest("HTPParserTest15", HTPParserTest15, 1);
+ UtRegisterTest("HTPParserTest16", HTPParserTest16, 1);
+
+ HTPFileParserRegisterTests();
+ HTPXFFParserRegisterTests();
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/app-layer-htp.h b/framework/src/suricata/src/app-layer-htp.h
new file mode 100644
index 00000000..275bc4b7
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-htp.h
@@ -0,0 +1,294 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup httplayer HTTP layer support
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * This file provides a HTTP protocol support for the engine using HTP library.
+ */
+
+#ifndef __APP_LAYER_HTP_H__
+#define __APP_LAYER_HTP_H__
+
+#include "util-radix-tree.h"
+#include "util-file.h"
+#include "app-layer-htp-mem.h"
+#include "detect-engine-state.h"
+
+#include <htp/htp.h>
+
+/* default request body limit */
+#define HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT 4096U
+#define HTP_CONFIG_DEFAULT_RESPONSE_BODY_LIMIT 4096U
+#define HTP_CONFIG_DEFAULT_REQUEST_INSPECT_MIN_SIZE 32768U
+#define HTP_CONFIG_DEFAULT_REQUEST_INSPECT_WINDOW 4096U
+#define HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_MIN_SIZE 32768U
+#define HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_WINDOW 4096U
+#define HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT 9000U
+#define HTP_CONFIG_DEFAULT_FIELD_LIMIT_HARD 18000U
+
+#define HTP_CONFIG_DEFAULT_RANDOMIZE 1
+#define HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE 10
+
+/** a boundary should be smaller in size */
+#define HTP_BOUNDARY_MAX 200U
+
+#define HTP_FLAG_STATE_OPEN 0x0001 /**< Flag to indicate that HTTP
+ connection is open */
+#define HTP_FLAG_STATE_CLOSED_TS 0x0002 /**< Flag to indicate that HTTP
+ connection is closed */
+#define HTP_FLAG_STATE_CLOSED_TC 0x0004 /**< Flag to indicate that HTTP
+ connection is closed */
+#define HTP_FLAG_STATE_DATA 0x0008 /**< Flag to indicate that HTTP
+ connection needs more data */
+#define HTP_FLAG_STATE_ERROR 0x0010 /**< Flag to indicate that an error
+ has been occured on HTTP
+ connection */
+#define HTP_FLAG_NEW_BODY_SET 0x0020 /**< Flag to indicate that HTTP
+ has parsed a new body (for
+ pcre) */
+#define HTP_FLAG_STORE_FILES_TS 0x0040
+#define HTP_FLAG_STORE_FILES_TC 0x0080
+#define HTP_FLAG_STORE_FILES_TX_TS 0x0100
+#define HTP_FLAG_STORE_FILES_TX_TC 0x0200
+/** flag the state that a new file has been set in this tx */
+#define HTP_FLAG_NEW_FILE_TX_TS 0x0400
+/** flag the state that a new file has been set in this tx */
+#define HTP_FLAG_NEW_FILE_TX_TC 0x0800
+
+enum {
+ HTP_BODY_NONE = 0, /**< Flag to indicate the current
+ operation */
+ HTP_BODY_REQUEST, /**< Flag to indicate that the
+ current operation is a request */
+ HTP_BODY_RESPONSE /**< Flag to indicate that the current
+ * operation is a response */
+};
+
+enum {
+ HTP_BODY_REQUEST_NONE = 0,
+ HTP_BODY_REQUEST_MULTIPART, /* POST, MP */
+ HTP_BODY_REQUEST_POST, /* POST, no MP */
+ HTP_BODY_REQUEST_PUT,
+};
+
+enum {
+ /* libhtp errors/warnings */
+ HTTP_DECODER_EVENT_UNKNOWN_ERROR,
+ HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED,
+ HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON,
+ HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON,
+ HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN,
+ HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN,
+ HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST,
+ HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE,
+ HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST,
+ HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE,
+ HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN,
+ HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST,
+ HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST,
+ HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT,
+ HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID,
+ HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID,
+ HTTP_DECODER_EVENT_MISSING_HOST_HEADER,
+ HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS,
+ HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING,
+ HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING,
+ HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG,
+ HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG,
+ HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH,
+ HTTP_DECODER_EVENT_URI_HOST_INVALID,
+ HTTP_DECODER_EVENT_HEADER_HOST_INVALID,
+ HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT,
+ HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT,
+ HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE,
+
+ /* suricata errors/warnings */
+ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR,
+ HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA,
+ HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER,
+};
+
+#define HTP_PCRE_NONE 0x00 /**< No pcre executed yet */
+#define HTP_PCRE_DONE 0x01 /**< Flag to indicate that pcre has
+ done some inspection in the
+ chunks */
+#define HTP_PCRE_HAS_MATCH 0x02 /**< Flag to indicate that the chunks
+ matched on some rule */
+
+/** Need a linked list in order to keep track of these */
+typedef struct HTPCfgRec_ {
+ htp_cfg_t *cfg;
+ struct HTPCfgRec_ *next;
+
+ int uri_include_all; /**< use all info in uri (bool) */
+
+ /** max size of the client body we inspect */
+ uint32_t request_body_limit;
+ uint32_t response_body_limit;
+
+ uint32_t request_inspect_min_size;
+ uint32_t request_inspect_window;
+
+ uint32_t response_inspect_min_size;
+ uint32_t response_inspect_window;
+ int randomize;
+ int randomize_range;
+ int http_body_inline;
+} HTPCfgRec;
+
+/** Struct used to hold chunks of a body on a request */
+struct HtpBodyChunk_ {
+ uint8_t *data; /**< Pointer to the data of the chunk */
+ struct HtpBodyChunk_ *next; /**< Pointer to the next chunk */
+ uint64_t stream_offset;
+ uint32_t len; /**< Length of the chunk */
+ int logged;
+} __attribute__((__packed__));
+typedef struct HtpBodyChunk_ HtpBodyChunk;
+
+/** Struct used to hold all the chunks of a body on a request */
+typedef struct HtpBody_ {
+ HtpBodyChunk *first; /**< Pointer to the first chunk */
+ HtpBodyChunk *last; /**< Pointer to the last chunk */
+
+ /* Holds the length of the htp request body seen so far */
+ uint64_t content_len_so_far;
+ /* parser tracker */
+ uint64_t body_parsed;
+ /* inspection tracker */
+ uint64_t body_inspected;
+} HtpBody;
+
+#define HTP_CONTENTTYPE_SET 0x01 /**< We have the content type */
+#define HTP_BOUNDARY_SET 0x02 /**< We have a boundary string */
+#define HTP_BOUNDARY_OPEN 0x04 /**< We have a boundary string */
+#define HTP_FILENAME_SET 0x08 /**< filename is registered in the flow */
+#define HTP_DONTSTORE 0x10 /**< not storing this file */
+
+#define HTP_TX_HAS_FILE 0x01
+#define HTP_TX_HAS_FILENAME 0x02 /**< filename is known at this time */
+#define HTP_TX_HAS_TYPE 0x04
+#define HTP_TX_HAS_FILECONTENT 0x08 /**< file has content so we can do type detect */
+
+#define HTP_RULE_NEED_FILE HTP_TX_HAS_FILE
+#define HTP_RULE_NEED_FILENAME HTP_TX_HAS_FILENAME
+#define HTP_RULE_NEED_TYPE HTP_TX_HAS_TYPE
+#define HTP_RULE_NEED_FILECONTENT HTP_TX_HAS_FILECONTENT
+
+/** Now the Body Chunks will be stored per transaction, at
+ * the tx user data */
+typedef struct HtpTxUserData_ {
+ /* Body of the request (if any) */
+ uint8_t request_body_init;
+ uint8_t response_body_init;
+ HtpBody request_body;
+ HtpBody response_body;
+
+ bstr *request_uri_normalized;
+
+ uint8_t *request_headers_raw;
+ uint8_t *response_headers_raw;
+ uint32_t request_headers_raw_len;
+ uint32_t response_headers_raw_len;
+
+ AppLayerDecoderEvents *decoder_events; /**< per tx events */
+
+ /** Holds the boundary identificator string if any (used on
+ * multipart/form-data only)
+ */
+ uint8_t *boundary;
+ uint8_t boundary_len;
+
+ uint8_t tsflags;
+ uint8_t tcflags;
+
+ int16_t operation;
+
+ uint8_t request_body_type;
+ uint8_t response_body_type;
+
+ DetectEngineState *de_state;
+} HtpTxUserData;
+
+typedef struct HtpState_ {
+
+ /* Connection parser structure for each connection */
+ htp_connp_t *connp;
+ /* Connection structure for each connection */
+ htp_conn_t *conn;
+ Flow *f; /**< Needed to retrieve the original flow when usin HTPLib callbacks */
+ uint64_t transaction_cnt;
+ uint64_t store_tx_id;
+ FileContainer *files_ts;
+ FileContainer *files_tc;
+ struct HTPCfgRec_ *cfg;
+ uint16_t flags;
+ uint16_t events;
+ uint16_t htp_messages_offset; /**< offset into conn->messages list */
+ uint64_t tx_with_detect_state_cnt;
+} HtpState;
+
+/** part of the engine needs the request body (e.g. http_client_body keyword) */
+#define HTP_REQUIRE_REQUEST_BODY (1 << 0)
+/** part of the engine needs the request body multipart header (e.g. filename
+ * and / or fileext keywords) */
+#define HTP_REQUIRE_REQUEST_MULTIPART (1 << 1)
+/** part of the engine needs the request file (e.g. log-file module) */
+#define HTP_REQUIRE_REQUEST_FILE (1 << 2)
+/** part of the engine needs the request body (e.g. file_data keyword) */
+#define HTP_REQUIRE_RESPONSE_BODY (1 << 3)
+
+SC_ATOMIC_DECLARE(uint32_t, htp_config_flags);
+
+void RegisterHTPParsers(void);
+void HTPParserRegisterTests(void);
+void HTPAtExitPrintStats(void);
+void HTPFreeConfig(void);
+
+htp_tx_t *HTPTransactionMain(const HtpState *);
+
+int HTPCallbackRequestBodyData(htp_tx_data_t *);
+int HtpTransactionGetLoggableId(Flow *);
+void HtpBodyPrint(HtpBody *);
+void HtpBodyFree(HtpBody *);
+/* To free the state from unittests using app-layer-htp */
+void HTPStateFree(void *);
+void AppLayerHtpEnableRequestBodyCallback(void);
+void AppLayerHtpEnableResponseBodyCallback(void);
+void AppLayerHtpNeedFileInspection(void);
+void AppLayerHtpPrintStats(void);
+
+void HTPConfigure(void);
+
+void HtpConfigCreateBackup(void);
+void HtpConfigRestoreBackup(void);
+
+#endif /* __APP_LAYER_HTP_H__ */
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/app-layer-modbus.c b/framework/src/suricata/src/app-layer-modbus.c
new file mode 100644
index 00000000..fa965135
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-modbus.c
@@ -0,0 +1,2671 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ *
+ * App-layer parser for Modbus protocol
+ *
+ */
+
+#include "suricata-common.h"
+
+#include "util-debug.h"
+#include "util-byte.h"
+#include "util-enum.h"
+#include "util-mem.h"
+#include "util-misc.h"
+
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-modbus.h"
+
+#include "app-layer-detect-proto.h"
+
+#include "conf.h"
+#include "decode.h"
+
+SCEnumCharMap modbus_decoder_event_table[ ] = {
+ /* Modbus Application Data Unit messages - ADU Modbus */
+ { "INVALID_PROTOCOL_ID", MODBUS_DECODER_EVENT_INVALID_PROTOCOL_ID },
+ { "UNSOLICITED_RESPONSE", MODBUS_DECODER_EVENT_UNSOLICITED_RESPONSE },
+ { "INVALID_LENGTH", MODBUS_DECODER_EVENT_INVALID_LENGTH },
+ { "INVALID_UNIT_IDENTIFIER", MODBUS_DECODER_EVENT_INVALID_UNIT_IDENTIFIER},
+
+ /* Modbus Protocol Data Unit messages - PDU Modbus */
+ { "INVALID_FUNCTION_CODE", MODBUS_DECODER_EVENT_INVALID_FUNCTION_CODE },
+ { "INVALID_VALUE", MODBUS_DECODER_EVENT_INVALID_VALUE },
+ { "INVALID_EXCEPTION_CODE", MODBUS_DECODER_EVENT_INVALID_EXCEPTION_CODE },
+ { "VALUE_MISMATCH", MODBUS_DECODER_EVENT_VALUE_MISMATCH },
+
+ /* Modbus Decoder event */
+ { "FLOODED", MODBUS_DECODER_EVENT_FLOODED},
+ { NULL, -1 },
+};
+
+/* Modbus Application Data Unit (ADU) length range. */
+#define MODBUS_MIN_ADU_LEN 2
+#define MODBUS_MAX_ADU_LEN 254
+
+/* Modbus Protocol version. */
+#define MODBUS_PROTOCOL_VER 0
+
+/* Modbus Unit Identifier range. */
+#define MODBUS_MIN_INVALID_UNIT_ID 247
+#define MODBUS_MAX_INVALID_UNIT_ID 255
+
+/* Modbus Quantity range. */
+#define MODBUS_MIN_QUANTITY 0
+#define MODBUS_MAX_QUANTITY_IN_BIT_ACCESS 2000
+#define MODBUS_MAX_QUANTITY_IN_WORD_ACCESS 125
+
+/* Modbus Count range. */
+#define MODBUS_MIN_COUNT 1
+#define MODBUS_MAX_COUNT 250
+
+/* Modbus Function Code. */
+#define MODBUS_FUNC_NONE 0x00
+#define MODBUS_FUNC_READCOILS 0x01
+#define MODBUS_FUNC_READDISCINPUTS 0x02
+#define MODBUS_FUNC_READHOLDREGS 0x03
+#define MODBUS_FUNC_READINPUTREGS 0x04
+#define MODBUS_FUNC_WRITESINGLECOIL 0x05
+#define MODBUS_FUNC_WRITESINGLEREG 0x06
+#define MODBUS_FUNC_READEXCSTATUS 0x07
+#define MODBUS_FUNC_DIAGNOSTIC 0x08
+#define MODBUS_FUNC_GETCOMEVTCOUNTER 0x0b
+#define MODBUS_FUNC_GETCOMEVTLOG 0x0c
+#define MODBUS_FUNC_WRITEMULTCOILS 0x0f
+#define MODBUS_FUNC_WRITEMULTREGS 0x10
+#define MODBUS_FUNC_REPORTSERVERID 0x11
+#define MODBUS_FUNC_READFILERECORD 0x14
+#define MODBUS_FUNC_WRITEFILERECORD 0x15
+#define MODBUS_FUNC_MASKWRITEREG 0x16
+#define MODBUS_FUNC_READWRITEMULTREGS 0x17
+#define MODBUS_FUNC_READFIFOQUEUE 0x18
+#define MODBUS_FUNC_ENCAPINTTRANS 0x2b
+#define MODBUS_FUNC_MASK 0x7f
+#define MODBUS_FUNC_ERRORMASK 0x80
+
+/* Modbus Diagnostic functions: Subfunction Code. */
+#define MODBUS_SUBFUNC_QUERY_DATA 0x00
+#define MODBUS_SUBFUNC_RESTART_COM 0x01
+#define MODBUS_SUBFUNC_DIAG_REGS 0x02
+#define MODBUS_SUBFUNC_CHANGE_DELIMITER 0x03
+#define MODBUS_SUBFUNC_LISTEN_MODE 0x04
+#define MODBUS_SUBFUNC_CLEAR_REGS 0x0a
+#define MODBUS_SUBFUNC_BUS_MSG_COUNT 0x0b
+#define MODBUS_SUBFUNC_COM_ERR_COUNT 0x0c
+#define MODBUS_SUBFUNC_EXCEPT_ERR_COUNT 0x0d
+#define MODBUS_SUBFUNC_SERVER_MSG_COUNT 0x0e
+#define MODBUS_SUBFUNC_SERVER_NO_RSP_COUNT 0x0f
+#define MODBUS_SUBFUNC_SERVER_NAK_COUNT 0x10
+#define MODBUS_SUBFUNC_SERVER_BUSY_COUNT 0x11
+#define MODBUS_SUBFUNC_SERVER_CHAR_COUNT 0x12
+#define MODBUS_SUBFUNC_CLEAR_COUNT 0x14
+
+/* Modbus Encapsulated Interface Transport function: MEI type. */
+#define MODBUS_MEI_ENCAPINTTRANS_CAN 0x0d
+#define MODBUS_MEI_ENCAPINTTRANS_READ 0x0e
+
+/* Modbus Exception Codes. */
+#define MODBUS_ERROR_CODE_ILLEGAL_FUNCTION 0x01
+#define MODBUS_ERROR_CODE_ILLEGAL_DATA_ADDRESS 0x02
+#define MODBUS_ERROR_CODE_ILLEGAL_DATA_VALUE 0x03
+#define MODBUS_ERROR_CODE_SERVER_DEVICE_FAILURE 0x04
+#define MODBUS_ERROR_CODE_MEMORY_PARITY_ERROR 0x08
+
+/* Modbus Application Protocol (MBAP) header. */
+struct ModbusHeader_ {
+ uint16_t transactionId;
+ uint16_t protocolId;
+ uint16_t length;
+ uint8_t unitId;
+} __attribute__((__packed__));
+typedef struct ModbusHeader_ ModbusHeader;
+
+/* Modbus Read/Write function and Access Types. */
+#define MODBUS_TYP_WRITE_SINGLE (MODBUS_TYP_WRITE | MODBUS_TYP_SINGLE)
+#define MODBUS_TYP_WRITE_MULTIPLE (MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
+#define MODBUS_TYP_READ_WRITE_MULTIPLE (MODBUS_TYP_READ | MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
+
+/* Macro to convert quantity value (in bit) into count value (in word): count = Ceil(quantity/8) */
+#define CEIL(quantity) (((quantity) + 7)>>3)
+
+/* Modbus Default unreplied Modbus requests are considered a flood */
+#define MODBUS_CONFIG_DEFAULT_REQUEST_FLOOD 500
+
+static uint32_t request_flood = MODBUS_CONFIG_DEFAULT_REQUEST_FLOOD;
+
+int ModbusStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type) {
+ *event_id = SCMapEnumNameToValue(event_name, modbus_decoder_event_table);
+
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "modbus's enum map table.", event_name);
+ /* yes this is fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+void ModbusSetEvent(ModbusState *modbus, uint8_t e) {
+ if (modbus && modbus->curr) {
+ SCLogDebug("modbus->curr->decoder_events %p", modbus->curr->decoder_events);
+ AppLayerDecoderEventsSetEventRaw(&modbus->curr->decoder_events, e);
+ SCLogDebug("modbus->curr->decoder_events %p", modbus->curr->decoder_events);
+ modbus->events++;
+ } else
+ SCLogDebug("couldn't set event %u", e);
+}
+
+AppLayerDecoderEvents *ModbusGetEvents(void *state, uint64_t id) {
+ ModbusState *modbus = (ModbusState *) state;
+ ModbusTransaction *tx;
+
+ if (modbus->curr && modbus->curr->tx_num == (id + 1))
+ return modbus->curr->decoder_events;
+
+ TAILQ_FOREACH(tx, &modbus->tx_list, next) {
+ if (tx->tx_num == (id+1))
+ return tx->decoder_events;
+ }
+
+ return NULL;
+}
+
+int ModbusHasEvents(void *state) {
+ return (((ModbusState *) state)->events > 0);
+}
+
+int ModbusGetAlstateProgress(void *modbus_tx, uint8_t direction) {
+ ModbusTransaction *tx = (ModbusTransaction *) modbus_tx;
+ ModbusState *modbus = tx->modbus;
+
+ if (tx->replied == 1)
+ return 1;
+
+ /* Check flood limit */
+ if ((modbus->givenup == 1) &&
+ ((modbus->transaction_max - tx->tx_num) > request_flood))
+ return 1;
+
+ return 0;
+}
+
+/** \brief Get value for 'complete' status in Modbus
+ */
+int ModbusGetAlstateProgressCompletionStatus(uint8_t direction) {
+ return 1;
+}
+
+void *ModbusGetTx(void *alstate, uint64_t tx_id) {
+ ModbusState *modbus = (ModbusState *) alstate;
+ ModbusTransaction *tx = NULL;
+
+ if (modbus->curr && modbus->curr->tx_num == tx_id + 1)
+ return modbus->curr;
+
+ TAILQ_FOREACH(tx, &modbus->tx_list, next) {
+ SCLogDebug("tx->tx_num %"PRIu64", tx_id %"PRIu64, tx->tx_num, (tx_id+1));
+ if (tx->tx_num != (tx_id+1))
+ continue;
+
+ SCLogDebug("returning tx %p", tx);
+ return tx;
+ }
+
+ return NULL;
+}
+
+uint64_t ModbusGetTxCnt(void *alstate) {
+ return ((uint64_t) ((ModbusState *) alstate)->transaction_max);
+}
+
+/** \internal
+ * \brief Find the Modbus Transaction in the state based on Transaction ID.
+ *
+ * \param modbus Pointer to Modbus state structure
+ * \param transactionId Transaction ID of the transaction
+ *
+ * \retval tx or NULL if not found
+ */
+static ModbusTransaction *ModbusTxFindByTransaction(const ModbusState *modbus,
+ const uint16_t transactionId) {
+ ModbusTransaction *tx = NULL;
+
+ if (modbus->curr == NULL)
+ return NULL;
+
+ /* fast path */
+ if ((modbus->curr->transactionId == transactionId) &&
+ !(modbus->curr->replied)) {
+ return modbus->curr;
+ /* slow path, iterate list */
+ } else {
+ TAILQ_FOREACH(tx, &modbus->tx_list, next) {
+ if ((tx->transactionId == transactionId) &&
+ !(modbus->curr->replied))
+ return tx;
+ }
+ }
+ /* not found */
+ return NULL;
+}
+
+/** \internal
+ * \brief Allocate a Modbus Transaction and
+ * add it into Transaction list of Modbus State
+ *
+ * \param modbus Pointer to Modbus state structure
+ *
+ * \retval Pointer to Transaction or NULL pointer
+ */
+static ModbusTransaction *ModbusTxAlloc(ModbusState *modbus) {
+ ModbusTransaction *tx;
+
+ tx = (ModbusTransaction *) SCCalloc(1, sizeof(ModbusTransaction));
+ if (unlikely(tx == NULL))
+ return NULL;
+
+ modbus->transaction_max++;
+ modbus->unreplied_cnt++;
+
+ /* Check flood limit */
+ if ((request_flood != 0) && (modbus->unreplied_cnt > request_flood)) {
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_FLOODED);
+ modbus->givenup = 1;
+ }
+
+ modbus->curr = tx;
+
+ SCLogDebug("modbus->transaction_max updated to %"PRIu64, modbus->transaction_max);
+
+ TAILQ_INSERT_TAIL(&modbus->tx_list, tx, next);
+
+ tx->modbus = modbus;
+ tx->tx_num = modbus->transaction_max;
+
+ return tx;
+}
+
+/** \internal
+ * \brief Free a Modbus Transaction
+ *
+ * \retval Pointer to Transaction or NULL pointer
+ */
+static void ModbusTxFree(ModbusTransaction *tx) {
+ SCEnter();
+ if (tx->data != NULL)
+ SCFree(tx->data);
+
+ AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
+
+ if (tx->de_state != NULL)
+ DetectEngineStateFree(tx->de_state);
+
+ SCFree(tx);
+ SCReturn;
+}
+
+/**
+ * \brief Modbus transaction cleanup callback
+ */
+void ModbusStateTxFree(void *state, uint64_t tx_id) {
+ SCEnter();
+ ModbusState *modbus = (ModbusState *) state;
+ ModbusTransaction *tx = NULL, *ttx;
+
+ SCLogDebug("state %p, id %"PRIu64, modbus, tx_id);
+
+ TAILQ_FOREACH_SAFE(tx, &modbus->tx_list, next, ttx) {
+ SCLogDebug("tx %p tx->tx_num %"PRIu64", tx_id %"PRIu64, tx, tx->tx_num, (tx_id+1));
+
+ if (tx->tx_num != (tx_id+1))
+ continue;
+
+ if (tx == modbus->curr)
+ modbus->curr = NULL;
+
+ if (tx->decoder_events != NULL) {
+ if (tx->decoder_events->cnt <= modbus->events)
+ modbus->events -= tx->decoder_events->cnt;
+ else
+ modbus->events = 0;
+ }
+
+ modbus->unreplied_cnt--;
+
+ /* Check flood limit */
+ if ((modbus->givenup == 1) &&
+ (request_flood != 0) &&
+ (modbus->unreplied_cnt < request_flood) )
+ modbus->givenup = 0;
+
+ TAILQ_REMOVE(&modbus->tx_list, tx, next);
+ ModbusTxFree(tx);
+ break;
+ }
+ SCReturn;
+}
+
+/** \internal
+ * \brief Extract 8bits data from pointer the received input data
+ *
+ * \param res Pointer to the result
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static int ModbusExtractUint8(ModbusState *modbus,
+ uint8_t *res,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset) {
+ SCEnter();
+ if (input_len < (uint32_t) (*offset + sizeof(uint8_t))) {
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_LENGTH);
+ SCReturnInt(-1);
+ }
+
+ *res = *(input + *offset);
+ *offset += sizeof(uint8_t);
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Extract 16bits data from pointer the received input data
+ *
+ * \param res Pointer to the result
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static int ModbusExtractUint16(ModbusState *modbus,
+ uint16_t *res,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset) {
+ SCEnter();
+ if (input_len < (uint32_t) (*offset + sizeof(uint16_t))) {
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_LENGTH);
+ SCReturnInt(-1);
+ }
+
+ ByteExtractUint16(res, BYTE_BIG_ENDIAN, sizeof(uint16_t), (const uint8_t *) (input + *offset));
+ *offset += sizeof(uint16_t);
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Check length field in Modbus header according to code function
+ *
+ * \param modbus Pointer to Modbus state structure
+ * \param length Length field in Modbus Header
+ * \param len Length according to code functio
+ */
+static int ModbusCheckHeaderLength(ModbusState *modbus,
+ uint16_t length,
+ uint16_t len) {
+ SCEnter();
+ if (length != len) {
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_LENGTH);
+ SCReturnInt(-1);
+ }
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Check Modbus header
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param header Pointer to Modbus header state in which the value to be stored
+ */
+static void ModbusCheckHeader(ModbusState *modbus,
+ ModbusHeader *header)
+{
+ SCEnter();
+ /* MODBUS protocol is identified by the value 0. */
+ if (header->protocolId != MODBUS_PROTOCOL_VER)
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_PROTOCOL_ID);
+
+ /* Check Length field that is a byte count of the following fields */
+ if ((header->length < MODBUS_MIN_ADU_LEN) ||
+ (header->length > MODBUS_MAX_ADU_LEN) )
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_LENGTH);
+
+ /* Check Unit Identifier field that is not in invalid range */
+ if ((header->unitId > MODBUS_MIN_INVALID_UNIT_ID) &&
+ (header->unitId < MODBUS_MAX_INVALID_UNIT_ID) )
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_UNIT_IDENTIFIER);
+
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse Exception Response and verify protocol compliance.
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static void ModbusExceptionResponse(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint8_t exception;
+
+ /* Exception code (1 byte) */
+ if (ModbusExtractUint8(modbus, &exception, input, input_len, offset))
+ SCReturn;
+
+ switch (exception) {
+ case MODBUS_ERROR_CODE_ILLEGAL_FUNCTION:
+ case MODBUS_ERROR_CODE_SERVER_DEVICE_FAILURE:
+ break;
+ case MODBUS_ERROR_CODE_ILLEGAL_DATA_VALUE:
+ if (tx->function == MODBUS_FUNC_DIAGNOSTIC) {
+ break;
+ }
+ /* Fallthrough */
+ case MODBUS_ERROR_CODE_ILLEGAL_DATA_ADDRESS:
+ if ( (tx->type & MODBUS_TYP_ACCESS_FUNCTION_MASK) ||
+ (tx->function == MODBUS_FUNC_READFIFOQUEUE) ||
+ (tx->function == MODBUS_FUNC_ENCAPINTTRANS)) {
+ break;
+ }
+ /* Fallthrough */
+ case MODBUS_ERROR_CODE_MEMORY_PARITY_ERROR:
+ if ( (tx->function == MODBUS_FUNC_READFILERECORD) ||
+ (tx->function == MODBUS_FUNC_WRITEFILERECORD) ) {
+ break;
+ }
+ /* Fallthrough */
+ default:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_EXCEPTION_CODE);
+ break;
+ }
+
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse Read data Request, complete Transaction structure
+ * and verify protocol compliance.
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static void ModbusParseReadRequest(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint16_t quantity;
+ uint8_t type = tx->type;
+
+ /* Starting Address (2 bytes) */
+ if (ModbusExtractUint16(modbus, &(tx->read.address), input, input_len, offset))
+ goto end;
+
+ /* Quantity (2 bytes) */
+ if (ModbusExtractUint16(modbus, &(tx->read.quantity), input, input_len, offset))
+ goto end;
+ quantity = tx->read.quantity;
+
+ /* Check Quantity range */
+ if (type & MODBUS_TYP_BIT_ACCESS_MASK) {
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_BIT_ACCESS))
+ goto error;
+ } else {
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_WORD_ACCESS))
+ goto error;
+ }
+
+ if (~type & MODBUS_TYP_WRITE)
+ /* Except from Read/Write Multiple Registers function (code 23) */
+ /* The length of all Read Data function requests is 6 bytes */
+ /* Modbus Application Protocol Specification V1.1b3 from 6.1 to 6.4 */
+ ModbusCheckHeaderLength(modbus, tx->length, 6);
+
+ goto end;
+
+error:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_VALUE);
+end:
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse Read data Response and verify protocol compliance
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static void ModbusParseReadResponse(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint8_t count;
+
+ /* Count (1 bytes) */
+ if (ModbusExtractUint8(modbus, &count, input, input_len, offset))
+ goto end;
+
+ /* Check Count range and value according to the request */
+ if ((tx->type) & MODBUS_TYP_BIT_ACCESS_MASK) {
+ if ( (count < MODBUS_MIN_COUNT) ||
+ (count > MODBUS_MAX_COUNT) ||
+ (count != CEIL(tx->read.quantity)))
+ goto error;
+ } else {
+ if ( (count == MODBUS_MIN_COUNT) ||
+ (count > MODBUS_MAX_COUNT) ||
+ (count != (2 * (tx->read.quantity))))
+ goto error;
+ }
+
+ /* Except from Read/Write Multiple Registers function (code 23) */
+ /* The length of all Read Data function responses is (3 bytes + count) */
+ /* Modbus Application Protocol Specification V1.1b3 from 6.1 to 6.4 */
+ ModbusCheckHeaderLength(modbus, tx->length, 3 + count);
+ goto end;
+
+error:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_VALUE_MISMATCH);
+end:
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse Write data Request, complete Transaction structure
+ * and verify protocol compliance.
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ *
+ * \retval On success returns 0 or on failure returns -1.
+ */
+static int ModbusParseWriteRequest(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint16_t quantity = 1, word;
+ uint8_t byte, count = 1, type = tx->type;
+
+ int i = 0;
+
+ /* Starting/Output/Register Address (2 bytes) */
+ if (ModbusExtractUint16(modbus, &(tx->write.address), input, input_len, offset))
+ goto end;
+
+ if (type & MODBUS_TYP_SINGLE) {
+ /* The length of Write Single Coil (code 5) and */
+ /* Write Single Register (code 6) requests is 6 bytes */
+ /* Modbus Application Protocol Specification V1.1b3 6.5 and 6.6 */
+ if (ModbusCheckHeaderLength(modbus, tx->length, 6))
+ goto end;
+ } else if (type & MODBUS_TYP_MULTIPLE) {
+ /* Quantity (2 bytes) */
+ if (ModbusExtractUint16(modbus, &quantity, input, input_len, offset))
+ goto end;
+ tx->write.quantity = quantity;
+
+ /* Count (1 bytes) */
+ if (ModbusExtractUint8(modbus, &count, input, input_len, offset))
+ goto end;
+ tx->write.count = count;
+
+ if (type & MODBUS_TYP_BIT_ACCESS_MASK) {
+ /* Check Quantity range and conversion in byte (count) */
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_BIT_ACCESS) ||
+ (quantity != CEIL(count)))
+ goto error;
+
+ /* The length of Write Multiple Coils (code 15) request is (7 + count) */
+ /* Modbus Application Protocol Specification V1.1b3 6.11 */
+ if (ModbusCheckHeaderLength(modbus, tx->length, 7 + count))
+ goto end;
+ } else {
+ /* Check Quantity range and conversion in byte (count) */
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_WORD_ACCESS) ||
+ (count != (2 * quantity)))
+ goto error;
+
+ if (type & MODBUS_TYP_READ) {
+ /* The length of Read/Write Multiple Registers function (code 23) */
+ /* request is (11 bytes + count) */
+ /* Modbus Application Protocol Specification V1.1b3 6.17 */
+ if (ModbusCheckHeaderLength(modbus, tx->length, 11 + count))
+ goto end;
+ } else {
+ /* The length of Write Multiple Coils (code 15) and */
+ /* Write Multiple Registers (code 16) functions requests is (7 bytes + count) */
+ /* Modbus Application Protocol Specification V1.1b3 from 6.11 and 6.12 */
+ if (ModbusCheckHeaderLength(modbus, tx->length, 7 + count))
+ goto end;
+ }
+ }
+ } else {
+ /* Mask Write Register function (And_Mask and Or_Mask) */
+ quantity = 2;
+
+ /* The length of Mask Write Register (code 22) function request is 8 */
+ /* Modbus Application Protocol Specification V1.1b3 6.16 */
+ if (ModbusCheckHeaderLength(modbus, tx->length, 8))
+ goto end;
+ }
+
+ if (type & MODBUS_TYP_COILS) {
+ /* Output value (data block) unit is count */
+ tx->data = (uint16_t *) SCCalloc(1, count * sizeof(uint16_t));
+ if (unlikely(tx->data == NULL))
+ SCReturnInt(-1);
+
+ if (type & MODBUS_TYP_SINGLE) {
+ /* Outputs value (2 bytes) */
+ if (ModbusExtractUint16(modbus, &word, input, input_len, offset))
+ goto end;
+ tx->data[i] = word;
+
+ if ((word != 0x00) && (word != 0xFF00))
+ goto error;
+ } else {
+ for (i = 0; i < count; i++) {
+ /* Outputs value (1 byte) */
+ if (ModbusExtractUint8(modbus, &byte, input, input_len, offset))
+ goto end;
+ tx->data[i] = (uint16_t) byte;
+ }
+ }
+ } else {
+ /* Registers value (data block) unit is quantity */
+ tx->data = (uint16_t *) SCCalloc(1, quantity * sizeof(uint16_t));
+ if (unlikely(tx->data == NULL))
+ SCReturnInt(-1);
+
+ for (i = 0; i < quantity; i++) {
+ /* Outputs/Registers value (2 bytes) */
+ if (ModbusExtractUint16(modbus, &word, input, input_len, offset))
+ goto end;
+ tx->data[i] = word;
+ }
+ }
+ goto end;
+
+error:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_VALUE);
+end:
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Parse Write data Response and verify protocol compliance
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static void ModbusParseWriteResponse(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint16_t address, quantity, word;
+ uint8_t type = tx->type;
+
+ /* Starting Address (2 bytes) */
+ if (ModbusExtractUint16(modbus, &address, input, input_len, offset))
+ goto end;
+
+ if (address != tx->write.address)
+ goto error;
+
+ if (type & MODBUS_TYP_SINGLE) {
+ /* Outputs/Registers value (2 bytes) */
+ if (ModbusExtractUint16(modbus, &word, input, input_len, offset))
+ goto end;
+
+ /* Check with Outputs/Registers from request */
+ if (word != tx->data[0])
+ goto error;
+ } else if (type & MODBUS_TYP_MULTIPLE) {
+ /* Quantity (2 bytes) */
+ if (ModbusExtractUint16(modbus, &quantity, input, input_len, offset))
+ goto end;
+
+ /* Check Quantity range */
+ if (type & MODBUS_TYP_BIT_ACCESS_MASK) {
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_WORD_ACCESS))
+ goto error;
+ } else {
+ if ((quantity == MODBUS_MIN_QUANTITY) ||
+ (quantity > MODBUS_MAX_QUANTITY_IN_BIT_ACCESS))
+ goto error;
+ }
+
+ /* Check Quantity value according to the request */
+ if (quantity != tx->write.quantity)
+ goto error;
+ } else {
+ /* And_Mask value (2 bytes) */
+ if (ModbusExtractUint16(modbus, &word, input, input_len, offset))
+ goto end;
+
+ /* Check And_Mask value according to the request */
+ if (word != tx->data[0])
+ goto error;
+
+ /* And_Or_Mask value (2 bytes) */
+ if (ModbusExtractUint16(modbus, &word, input, input_len, offset))
+
+ /* Check Or_Mask value according to the request */
+ if (word != tx->data[1])
+ goto error;
+
+ /* The length of Mask Write Register (code 22) function response is 8 */
+ /* Modbus Application Protocol Specification V1.1b3 6.16 */
+ ModbusCheckHeaderLength(modbus, tx->length, 8);
+ goto end;
+ }
+
+ /* Except from Mask Write Register (code 22) */
+ /* The length of all Write Data function responses is 6 */
+ /* Modbus Application Protocol Specification V1.1b3 6.5, 6.6, 6.11, 6.12 and 6.17 */
+ ModbusCheckHeaderLength(modbus, tx->length, 6);
+ goto end;
+
+error:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_VALUE_MISMATCH);
+end:
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse Diagnostic Request, complete Transaction
+ * structure (Category) and verify protocol compliance.
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer to Modbus state structure
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ *
+ * \retval Reserved category function returns 1 otherwise returns 0.
+ */
+static int ModbusParseDiagnosticRequest(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len,
+ uint16_t *offset)
+{
+ SCEnter();
+ uint16_t data;
+
+ /* Sub-function (2 bytes) */
+ if (ModbusExtractUint16(modbus, &(tx->subFunction), input, input_len, offset))
+ goto end;
+
+ /* Data (2 bytes) */
+ if (ModbusExtractUint16(modbus, &data, input, input_len, offset))
+ goto end;
+
+ if (tx->subFunction != MODBUS_SUBFUNC_QUERY_DATA) {
+ switch (tx->subFunction) {
+ case MODBUS_SUBFUNC_RESTART_COM:
+ if ((data != 0x00) && (data != 0xFF00))
+ goto error;
+ break;
+
+ case MODBUS_SUBFUNC_CHANGE_DELIMITER:
+ if ((data & 0xFF) != 0x00)
+ goto error;
+ break;
+
+ case MODBUS_SUBFUNC_LISTEN_MODE:
+ /* No answer is expected then mark tx as completed. */
+ tx->replied = 1;
+ /* Fallthrough */
+ case MODBUS_SUBFUNC_DIAG_REGS:
+ case MODBUS_SUBFUNC_CLEAR_REGS:
+ case MODBUS_SUBFUNC_BUS_MSG_COUNT:
+ case MODBUS_SUBFUNC_COM_ERR_COUNT:
+ case MODBUS_SUBFUNC_EXCEPT_ERR_COUNT:
+ case MODBUS_SUBFUNC_SERVER_MSG_COUNT:
+ case MODBUS_SUBFUNC_SERVER_NO_RSP_COUNT:
+ case MODBUS_SUBFUNC_SERVER_NAK_COUNT:
+ case MODBUS_SUBFUNC_SERVER_BUSY_COUNT:
+ case MODBUS_SUBFUNC_SERVER_CHAR_COUNT:
+ case MODBUS_SUBFUNC_CLEAR_COUNT:
+ if (data != 0x00)
+ goto error;
+ break;
+
+ default:
+ /* Set function code category */
+ tx->category = MODBUS_CAT_RESERVED;
+ SCReturnInt(1);
+ }
+
+ /* The length of all Diagnostic Requests is 6 */
+ /* Modbus Application Protocol Specification V1.1b3 6.8 */
+ ModbusCheckHeaderLength(modbus, tx->length, 6);
+ }
+
+ goto end;
+
+error:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_VALUE);
+end:
+ SCReturnInt(0);
+}
+
+/* Modbus Function Code Categories structure. */
+typedef struct ModbusFunctionCodeRange_ {
+ uint8_t function;
+ uint8_t category;
+} ModbusFunctionCodeRange;
+
+/* Modbus Function Code Categories table. */
+static ModbusFunctionCodeRange modbusFunctionCodeRanges[] = {
+ { 0, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 9, MODBUS_CAT_RESERVED },
+ { 15, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 41, MODBUS_CAT_RESERVED },
+ { 43, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 65, MODBUS_CAT_USER_DEFINED },
+ { 73, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 90, MODBUS_CAT_RESERVED },
+ { 92, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 100, MODBUS_CAT_USER_DEFINED },
+ { 111, MODBUS_CAT_PUBLIC_UNASSIGNED},
+ { 125, MODBUS_CAT_RESERVED },
+ { 128, MODBUS_CAT_NONE }
+};
+
+/** \internal
+ * \brief Parse the Modbus Protocol Data Unit (PDU) Request
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param ModbusPdu Pointer the Modbus PDU state in which the value to be stored
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ */
+static void ModbusParseRequestPDU(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len)
+{
+ SCEnter();
+ uint16_t offset = (uint16_t) sizeof(ModbusHeader);
+ uint8_t count;
+
+ int i = 0;
+
+ /* Standard function codes used on MODBUS application layer protocol (1 byte) */
+ if (ModbusExtractUint8(modbus, &(tx->function), input, input_len, &offset))
+ goto end;
+
+ /* Set default function code category */
+ tx->category = MODBUS_CAT_NONE;
+
+ /* Set default function primary table */
+ tx->type = MODBUS_TYP_NONE;
+
+ switch (tx->function) {
+ case MODBUS_FUNC_NONE:
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_FUNCTION_CODE);
+ break;
+
+ case MODBUS_FUNC_READCOILS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_COILS | MODBUS_TYP_READ);
+ break;
+
+ case MODBUS_FUNC_READDISCINPUTS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_DISCRETES | MODBUS_TYP_READ);
+ break;
+
+ case MODBUS_FUNC_READHOLDREGS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_HOLDING | MODBUS_TYP_READ);
+ break;
+
+ case MODBUS_FUNC_READINPUTREGS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_INPUT | MODBUS_TYP_READ);
+ break;
+
+ case MODBUS_FUNC_WRITESINGLECOIL:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_COILS | MODBUS_TYP_WRITE_SINGLE);
+ break;
+
+ case MODBUS_FUNC_WRITESINGLEREG:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_HOLDING | MODBUS_TYP_WRITE_SINGLE);
+ break;
+
+ case MODBUS_FUNC_WRITEMULTCOILS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_COILS | MODBUS_TYP_WRITE_MULTIPLE);
+ break;
+
+ case MODBUS_FUNC_WRITEMULTREGS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_HOLDING | MODBUS_TYP_WRITE_MULTIPLE);
+ break;
+
+ case MODBUS_FUNC_MASKWRITEREG:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_HOLDING | MODBUS_TYP_WRITE);
+ break;
+
+ case MODBUS_FUNC_READWRITEMULTREGS:
+ /* Set function type */
+ tx->type = (MODBUS_TYP_HOLDING | MODBUS_TYP_READ_WRITE_MULTIPLE);
+ break;
+
+ case MODBUS_FUNC_READFILERECORD:
+ case MODBUS_FUNC_WRITEFILERECORD:
+ /* Count/length (1 bytes) */
+ if (ModbusExtractUint8(modbus, &count, input, input_len, &offset))
+ goto end;
+
+ /* Modbus Application Protocol Specification V1.1b3 6.14 and 6.15 */
+ ModbusCheckHeaderLength(modbus, tx->length, 2 + count);
+ break;
+
+ case MODBUS_FUNC_DIAGNOSTIC:
+ if(ModbusParseDiagnosticRequest(tx, modbus, input, input_len, &offset))
+ goto end;
+ break;
+
+ case MODBUS_FUNC_READEXCSTATUS:
+ case MODBUS_FUNC_GETCOMEVTCOUNTER:
+ case MODBUS_FUNC_GETCOMEVTLOG:
+ case MODBUS_FUNC_REPORTSERVERID:
+ /* Modbus Application Protocol Specification V1.1b3 6.7, 6.9, 6.10 and 6.13 */
+ ModbusCheckHeaderLength(modbus, tx->length, 2);
+ break;
+
+ case MODBUS_FUNC_READFIFOQUEUE:
+ /* Modbus Application Protocol Specification V1.1b3 6.18 */
+ ModbusCheckHeaderLength(modbus, tx->length, 4);
+ break;
+
+ case MODBUS_FUNC_ENCAPINTTRANS:
+ /* MEI type (1 byte) */
+ if (ModbusExtractUint8(modbus, &(tx->mei), input, input_len, &offset))
+ goto end;
+
+ if (tx->mei == MODBUS_MEI_ENCAPINTTRANS_READ) {
+ /* Modbus Application Protocol Specification V1.1b3 6.21 */
+ ModbusCheckHeaderLength(modbus, tx->length, 5);
+ } else if (tx->mei != MODBUS_MEI_ENCAPINTTRANS_CAN) {
+ /* Set function code category */
+ tx->category = MODBUS_CAT_RESERVED;
+ goto end;
+ }
+ break;
+
+ default:
+ /* Check if request is error. */
+ if (tx->function & MODBUS_FUNC_ERRORMASK) {
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_INVALID_FUNCTION_CODE);
+ goto end;
+ }
+
+ /* Get and store function code category */
+ for (i = 0; modbusFunctionCodeRanges[i].category != MODBUS_CAT_NONE; i++) {
+ if (tx->function <= modbusFunctionCodeRanges[i].function)
+ break;
+ tx->category = modbusFunctionCodeRanges[i].category;
+ }
+ goto end;
+ }
+
+ /* Set function code category */
+ tx->category = MODBUS_CAT_PUBLIC_ASSIGNED;
+
+ if (tx->type & MODBUS_TYP_READ)
+ ModbusParseReadRequest(tx, modbus, input, input_len, &offset);
+
+ if (tx->type & MODBUS_TYP_WRITE)
+ ModbusParseWriteRequest(tx, modbus, input, input_len, &offset);
+
+end:
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse the Modbus Protocol Data Unit (PDU) Response
+ *
+ * \param tx Pointer to Modbus Transaction structure
+ * \param modbus Pointer the Modbus PDU state in which the value to be stored
+ * \param input Pointer the received input data
+ * \param input_len Length of the received input data
+ * \param offset Offset of the received input data pointer
+ */
+static void ModbusParseResponsePDU(ModbusTransaction *tx,
+ ModbusState *modbus,
+ uint8_t *input,
+ uint32_t input_len)
+{
+ SCEnter();
+ uint16_t offset = (uint16_t) sizeof(ModbusHeader);
+ uint8_t count, error = FALSE, function, mei;
+
+ /* Standard function codes used on MODBUS application layer protocol (1 byte) */
+ if (ModbusExtractUint8(modbus, &function, input, input_len, &offset))
+ goto end;
+
+ /* Check if response is error */
+ if(function & MODBUS_FUNC_ERRORMASK) {
+ function &= MODBUS_FUNC_MASK;
+ error = TRUE;
+ }
+
+ if (tx->category == MODBUS_CAT_PUBLIC_ASSIGNED) {
+ /* Check if response is error. */
+ if (error) {
+ ModbusExceptionResponse(tx, modbus, input, input_len, &offset);
+ } else {
+ switch(function) {
+ case MODBUS_FUNC_READEXCSTATUS:
+ /* Modbus Application Protocol Specification V1.1b3 6.7 */
+ ModbusCheckHeaderLength(modbus, tx->length, 3);
+ goto end;
+
+ case MODBUS_FUNC_GETCOMEVTCOUNTER:
+ /* Modbus Application Protocol Specification V1.1b3 6.9 */
+ ModbusCheckHeaderLength(modbus, tx->length, 6);
+ goto end;
+
+ case MODBUS_FUNC_READFILERECORD:
+ case MODBUS_FUNC_WRITEFILERECORD:
+ /* Count/length (1 bytes) */
+ if (ModbusExtractUint8(modbus, &count, input, input_len, &offset))
+ goto end;
+
+ /* Modbus Application Protocol Specification V1.1b3 6.14 and 6.15 */
+ ModbusCheckHeaderLength(modbus, tx->length, 2 + count);
+ goto end;
+
+ case MODBUS_FUNC_ENCAPINTTRANS:
+ /* MEI type (1 byte) */
+ if (ModbusExtractUint8(modbus, &mei, input, input_len, &offset))
+ goto end;
+
+ if (mei != tx->mei)
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_VALUE_MISMATCH);
+ goto end;
+ }
+
+ if (tx->type & MODBUS_TYP_READ)
+ ModbusParseReadResponse(tx, modbus, input, input_len, &offset);
+ /* Read/Write response contents none write response part */
+ else if (tx->type & MODBUS_TYP_WRITE)
+ ModbusParseWriteResponse(tx, modbus, input, input_len, &offset);
+ }
+ }
+
+end:
+ SCReturn;
+}
+
+/** \internal
+ * \brief Parse the Modbus Application Protocol (MBAP) header
+ *
+ * \param header Pointer the Modbus header state in which the value to be stored
+ * \param input Pointer the received input data
+ */
+static int ModbusParseHeader(ModbusState *modbus,
+ ModbusHeader *header,
+ uint8_t *input,
+ uint32_t input_len)
+{
+ SCEnter();
+ uint16_t offset = 0;
+
+ /* Transaction Identifier (2 bytes) */
+ if (ModbusExtractUint16(modbus, &(header->transactionId), input, input_len, &offset) ||
+ /* Protocol Identifier (2 bytes) */
+ ModbusExtractUint16(modbus, &(header->protocolId), input, input_len, &offset) ||
+ /* Length (2 bytes) */
+ ModbusExtractUint16(modbus, &(header->length), input, input_len, &offset) ||
+ /* Unit Identifier (1 byte) */
+ ModbusExtractUint8(modbus, &(header->unitId), input, input_len, &offset))
+ SCReturnInt(-1);
+
+ SCReturnInt(0);
+}
+
+/** \internal
+ *
+ * \brief This function is called to retrieve a Modbus Request
+ *
+ * \param state Modbus state structure for the parser
+ * \param input Input line of the command
+ * \param input_len Length of the request
+ *
+ * \retval 1 when the command is parsed, 0 otherwise
+ */
+static int ModbusParseRequest(Flow *f,
+ void *state,
+ AppLayerParserState *pstate,
+ uint8_t *input,
+ uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ ModbusState *modbus = (ModbusState *) state;
+ ModbusTransaction *tx;
+ ModbusHeader header;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ while (input_len > 0) {
+ uint32_t adu_len = input_len;
+ uint8_t *adu = input;
+
+ /* Extract MODBUS Header */
+ if (ModbusParseHeader(modbus, &header, adu, adu_len))
+ SCReturnInt(0);
+
+ /* Update ADU length with length in Modbus header. */
+ adu_len = (uint32_t) sizeof(ModbusHeader) + (uint32_t) header.length - 1;
+ if (adu_len > input_len)
+ SCReturnInt(0);
+
+ /* Allocate a Transaction Context and add it to Transaction list */
+ tx = ModbusTxAlloc(modbus);
+ if (tx == NULL)
+ SCReturnInt(0);
+
+ /* Check MODBUS Header */
+ ModbusCheckHeader(modbus, &header);
+
+ /* Store Transaction ID & PDU length */
+ tx->transactionId = header.transactionId;
+ tx->length = header.length;
+
+ /* Extract MODBUS PDU and fill Transaction Context */
+ ModbusParseRequestPDU(tx, modbus, adu, adu_len);
+
+ /* Update input line and remaining input length of the command */
+ input += adu_len;
+ input_len -= adu_len;
+ }
+
+ SCReturnInt(1);
+}
+
+/** \internal
+ * \brief This function is called to retrieve a Modbus response
+ *
+ * \param state Pointer to Modbus state structure for the parser
+ * \param input Input line of the command
+ * \param input_len Length of the request
+ *
+ * \retval 1 when the command is parsed, 0 otherwise
+ */
+static int ModbusParseResponse(Flow *f,
+ void *state,
+ AppLayerParserState *pstate,
+ uint8_t *input,
+ uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ ModbusHeader header;
+ ModbusState *modbus = (ModbusState *) state;
+ ModbusTransaction *tx;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ while (input_len > 0) {
+ uint32_t adu_len = input_len;
+ uint8_t *adu = input;
+
+ /* Extract MODBUS Header */
+ if (ModbusParseHeader(modbus, &header, adu, adu_len))
+ SCReturnInt(0);
+
+ /* Update ADU length with length in Modbus header. */
+ adu_len = (uint32_t) sizeof(ModbusHeader) + (uint32_t) header.length - 1;
+ if (adu_len > input_len)
+ SCReturnInt(0);
+
+ /* Find the transaction context thanks to transaction ID (and function code) */
+ tx = ModbusTxFindByTransaction(modbus, header.transactionId);
+ if (tx == NULL) {
+ /* Allocate a Transaction Context if not previous request */
+ /* and add it to Transaction list */
+ tx = ModbusTxAlloc(modbus);
+ if (tx == NULL)
+ SCReturnInt(0);
+
+ SCLogDebug("MODBUS_DECODER_EVENT_UNSOLICITED_RESPONSE");
+ ModbusSetEvent(modbus, MODBUS_DECODER_EVENT_UNSOLICITED_RESPONSE);
+ } else {
+ /* Store PDU length */
+ tx->length = header.length;
+
+ /* Extract MODBUS PDU and fill Transaction Context */
+ ModbusParseResponsePDU(tx, modbus, adu, adu_len);
+ }
+
+ /* Check and store MODBUS Header */
+ ModbusCheckHeader(modbus, &header);
+
+ /* Mark as completed */
+ tx->replied = 1;
+
+ /* Update input line and remaining input length of the command */
+ input += adu_len;
+ input_len -= adu_len;
+ }
+
+ SCReturnInt(1);
+}
+
+/** \internal
+ * \brief Function to allocate the Modbus state memory
+ */
+static void *ModbusStateAlloc(void)
+{
+ ModbusState *modbus;
+
+ modbus = (ModbusState *) SCCalloc(1, sizeof(ModbusState));
+ if (unlikely(modbus == NULL))
+ return NULL;
+
+ TAILQ_INIT(&modbus->tx_list);
+
+ return (void *) modbus;
+}
+
+/** \internal
+ * \brief Function to free the Modbus state memory
+ */
+static void ModbusStateFree(void *state)
+{
+ SCEnter();
+ ModbusState *modbus = (ModbusState *) state;
+ ModbusTransaction *tx = NULL, *ttx;
+
+ if (state) {
+ TAILQ_FOREACH_SAFE(tx, &modbus->tx_list, next, ttx) {
+ ModbusTxFree(tx);
+ }
+
+ SCFree(state);
+ }
+ SCReturn;
+}
+
+static uint16_t ModbusProbingParser(uint8_t *input,
+ uint32_t input_len,
+ uint32_t *offset)
+{
+ ModbusHeader *header = (ModbusHeader *) input;
+
+ /* Modbus header is 7 bytes long */
+ if (input_len < sizeof(ModbusHeader))
+ return ALPROTO_UNKNOWN;
+
+ /* MODBUS protocol is identified by the value 0. */
+ if (header->protocolId != 0)
+ return ALPROTO_FAILED;
+
+ return ALPROTO_MODBUS;
+}
+
+DetectEngineState *ModbusGetTxDetectState(void *vtx)
+{
+ ModbusTransaction *tx = (ModbusTransaction *)vtx;
+ return tx->de_state;
+}
+
+int ModbusSetTxDetectState(void *state, void *vtx, DetectEngineState *s)
+{
+ ModbusTransaction *tx = (ModbusTransaction *)vtx;
+ tx->de_state = s;
+ return 0;
+}
+
+/**
+ * \brief Function to register the Modbus protocol parsers and other functions
+ */
+void RegisterModbusParsers(void)
+{
+ SCEnter();
+ char *proto_name = "modbus";
+
+ /* Modbus application protocol V1.1b3 */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_MODBUS, proto_name);
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "502",
+ ALPROTO_MODBUS,
+ 0, sizeof(ModbusHeader),
+ STREAM_TOSERVER,
+ ModbusProbingParser);
+ } else {
+ /* if we have no config, we enable the default port 502 */
+ if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
+ proto_name, ALPROTO_MODBUS,
+ 0, sizeof(ModbusHeader),
+ ModbusProbingParser)) {
+ SCLogWarning(SC_ERR_MODBUS_CONFIG, "no Modbus TCP config found, "
+ "enabling Modbus detection on "
+ "port 502.");
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "502",
+ ALPROTO_MODBUS,
+ 0, sizeof(ModbusHeader),
+ STREAM_TOSERVER,
+ ModbusProbingParser);
+ }
+ }
+
+ ConfNode *p = ConfGetNode("app-layer.protocols.modbus.request-flood");
+ if (p != NULL) {
+ uint32_t value;
+ if (ParseSizeStringU32(p->val, &value) < 0) {
+ SCLogError(SC_ERR_MODBUS_CONFIG, "invalid value for request-flood %s", p->val);
+ } else {
+ request_flood = value;
+ }
+ }
+ SCLogInfo("Modbus request flood protection level: %u", request_flood);
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.", proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_MODBUS, STREAM_TOSERVER, ModbusParseRequest);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_MODBUS, STREAM_TOCLIENT, ModbusParseResponse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_MODBUS, ModbusStateAlloc, ModbusStateFree);
+
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_MODBUS, ModbusGetEvents);
+ AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_MODBUS, ModbusHasEvents);
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_MODBUS, NULL,
+ ModbusGetTxDetectState, ModbusSetTxDetectState);
+
+ AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_MODBUS, ModbusGetTx);
+ AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_MODBUS, ModbusGetTxCnt);
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_MODBUS, ModbusStateTxFree);
+
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_MODBUS, ModbusGetAlstateProgress);
+ AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, ALPROTO_MODBUS,
+ ModbusGetAlstateProgressCompletionStatus);
+
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_MODBUS, ModbusStateGetEventInfo);
+
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_MODBUS, STREAM_TOSERVER);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection" "still on.", proto_name);
+ }
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_MODBUS, ModbusParserRegisterTests);
+#endif
+
+ SCReturn;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+
+#include "flow-util.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+
+/* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */
+/* Example of a request to read discrete outputs 20-38 */
+static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x01,
+ /* Starting Address */ 0x78, 0x90,
+ /* Quantity of coils */ 0x00, 0x13 };
+
+static uint8_t readCoilsRsp[] = {/* Transaction ID */ 0x00, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x01,
+ /* Byte count */ 0x03,
+ /* Coil Status */ 0xCD, 0x6B, 0x05 };
+
+static uint8_t readCoilsErrorRsp[] = {/* Transaction ID */ 0x00, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x03,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x81,
+ /* Exception code */ 0x05};
+
+/* Modbus Application Protocol Specification V1.1b3 6.12: Write Multiple registers */
+/* Example of a request to write two registers starting at 2 to 00 0A and 01 02 hex */
+static uint8_t writeMultipleRegistersReq[] = {/* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x0B,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x10,
+ /* Starting Address */ 0x00, 0x01,
+ /* Quantity of Registers */ 0x00, 0x02,
+ /* Byte count */ 0x04,
+ /* Registers Value */ 0x00, 0x0A,
+ 0x01, 0x02};
+
+static uint8_t writeMultipleRegistersRsp[] = {/* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x10,
+ /* Starting Address */ 0x00, 0x01,
+ /* Quantity of Registers */ 0x00, 0x02};
+
+/* Modbus Application Protocol Specification V1.1b3 6.17: Read/Write Multiple registers */
+/* Example of a request to read six registers starting at register 4, */
+/* and to write three registers starting at register 15 */
+static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x11,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x17,
+ /* Read Starting Address */ 0x00, 0x03,
+ /* Quantity to Read */ 0x00, 0x06,
+ /* Write Starting Address */ 0x00, 0x0E,
+ /* Quantity to Write */ 0x00, 0x03,
+ /* Write Byte count */ 0x06,
+ /* Write Registers Value */ 0x12, 0x34,
+ 0x56, 0x78,
+ 0x9A, 0xBC};
+
+/* Mismatch value in Byte count 0x0B instead of 0x0C */
+static uint8_t readWriteMultipleRegistersRsp[] = {/* Transaction ID */ 0x12, 0x34,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x0E,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x17,
+ /* Byte count */ 0x0B,
+ /* Read Registers Value */ 0x00, 0xFE,
+ 0x0A, 0xCD,
+ 0x00, 0x01,
+ 0x00, 0x03,
+ 0x00, 0x0D,
+ 0x00};
+
+/* Modbus Application Protocol Specification V1.1b3 6.8.1: 04 Force Listen Only Mode */
+/* Example of a request to to remote device to its Listen Only MOde for Modbus Communications. */
+static uint8_t forceListenOnlyMode[] = {/* Transaction ID */ 0x0A, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x08,
+ /* Sub-function code */ 0x00, 0x04,
+ /* Data */ 0x00, 0x00};
+
+static uint8_t invalidProtocolIdReq[] = {/* Transaction ID */ 0x00, 0x00,
+ /* Protocol ID */ 0x00, 0x01,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x01,
+ /* Starting Address */ 0x78, 0x90,
+ /* Quantity of coils */ 0x00, 0x13 };
+
+static uint8_t invalidLengthWriteMultipleRegistersReq[] = {
+ /* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x09,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x10,
+ /* Starting Address */ 0x00, 0x01,
+ /* Quantity of Registers */ 0x00, 0x02,
+ /* Byte count */ 0x04,
+ /* Registers Value */ 0x00, 0x0A,
+ 0x01, 0x02};
+
+static uint8_t exceededLengthWriteMultipleRegistersReq[] = {
+ /* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0xff, 0xfa,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x10,
+ /* Starting Address */ 0x00, 0x01,
+ /* Quantity of Registers */ 0x7f, 0xf9,
+ /* Byte count */ 0xff};
+
+static uint8_t invalidLengthPDUWriteMultipleRegistersReq[] = {
+ /* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x02,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x10};
+
+/** \test Send Modbus Read Coils request/response. */
+static int ModbusParserTest01(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ Flow f;
+ TcpSession ssn;
+
+ int result = 0;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readCoilsReq, sizeof(readCoilsReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 1, tx->function);
+ printf("expected address %" PRIu8 ", got %" PRIu8 ": ", 0x7890, tx->read.address);
+ printf("expected quantity %" PRIu8 ", got %" PRIu8 ": ", 19, tx->read.quantity);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ readCoilsRsp, sizeof(readCoilsRsp));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (modbus_state->transaction_max !=1) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 1, modbus_state->transaction_max);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Send Modbus Write Multiple registers request/response. */
+static int ModbusParserTest02(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ Flow f;
+ TcpSession ssn;
+
+ int result = 0;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 16) || (tx->write.address != 0x01) || (tx->write.quantity != 2) ||
+ (tx->write.count != 4) || (tx->data[0] != 0x000A) || (tx->data[1] != 0x0102)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 16, tx->function);
+ printf("expected write address %" PRIu8 ", got %" PRIu8 ": ", 0x01, tx->write.address);
+ printf("expected write quantity %" PRIu8 ", got %" PRIu8 ": ", 2, tx->write.quantity);
+ printf("expected write count %" PRIu8 ", got %" PRIu8 ": ", 4, tx->write.count);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x000A, tx->data[0]);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x0102, tx->data[1]);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ writeMultipleRegistersRsp, sizeof(writeMultipleRegistersRsp));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (modbus_state->transaction_max !=1) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 1, modbus_state->transaction_max);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Send Modbus Read/Write Multiple registers request/response with mismatch value. */
+static int ModbusParserTest03(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus Data mismatch\"; "
+ "app-layer-event: "
+ "modbus.value_mismatch; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 23) || (tx->read.address != 0x03) || (tx->read.quantity != 6) ||
+ (tx->write.address != 0x0E) || (tx->write.quantity != 3) || (tx->write.count != 6) ||
+ (tx->data[0] != 0x1234) || (tx->data[1] != 0x5678) || (tx->data[2] != 0x9ABC)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 23, tx->function);
+ printf("expected read address %" PRIu8 ", got %" PRIu8 ": ", 0x03, tx->read.address);
+ printf("expected read quantity %" PRIu8 ", got %" PRIu8 ": ", 6, tx->read.quantity);
+ printf("expected write address %" PRIu8 ", got %" PRIu8 ": ", 0x0E, tx->write.address);
+ printf("expected write quantity %" PRIu8 ", got %" PRIu8 ": ", 3, tx->write.quantity);
+ printf("expected write count %" PRIu8 ", got %" PRIu8 ": ", 6, tx->write.count);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x1234, tx->data[0]);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x5678, tx->data[1]);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x9ABC, tx->data[2]);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ readWriteMultipleRegistersRsp, sizeof(readWriteMultipleRegistersRsp));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (modbus_state->transaction_max !=1) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 1, modbus_state->transaction_max);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Send Modbus Force Listen Only Mode request. */
+static int ModbusParserTest04(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ Flow f;
+ TcpSession ssn;
+
+ int result = 0;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ forceListenOnlyMode, sizeof(forceListenOnlyMode));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 8) || (tx->subFunction != 4)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 8, tx->function);
+ printf("expected sub-function %" PRIu8 ", got %" PRIu8 ": ", 0x04, tx->subFunction);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Send Modbus invalid Protocol version in request. */
+static int ModbusParserTest05(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus invalid Protocol version\"; "
+ "app-layer-event: "
+ "modbus.invalid_protocol_id; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ invalidProtocolIdReq, sizeof(invalidProtocolIdReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Send Modbus unsolicited response. */
+static int ModbusParserTest06(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus unsolicited response\"; "
+ "app-layer-event: "
+ "modbus.unsolicited_response; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ readCoilsRsp, sizeof(readCoilsRsp));
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Send Modbus invalid Length request. */
+static int ModbusParserTest07(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus invalid Length\"; "
+ "app-layer-event: "
+ "modbus.invalid_length; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ invalidLengthWriteMultipleRegistersReq,
+ sizeof(invalidLengthWriteMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Send Modbus Read Coils request and error response with Exception code invalid. */
+static int ModbusParserTest08(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus Exception code invalid\"; "
+ "app-layer-event: "
+ "modbus.invalid_exception_code; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readCoilsReq, sizeof(readCoilsReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 1, tx->function);
+ printf("expected address %" PRIu8 ", got %" PRIu8 ": ", 0x7890, tx->read.address);
+ printf("expected quantity %" PRIu8 ", got %" PRIu8 ": ", 19, tx->read.quantity);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ readCoilsErrorRsp, sizeof(readCoilsErrorRsp));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (modbus_state->transaction_max !=1) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 1, modbus_state->transaction_max);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Modbus fragmentation - 1 ADU over 2 TCP packets. */
+static int ModbusParserTest09(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ Flow f;
+ TcpSession ssn;
+
+ uint32_t input_len = sizeof(readCoilsReq), part2_len = 3;
+ uint8_t *input = readCoilsReq;
+
+ int result = 0;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ input, input_len - part2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ input, input_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 0);
+
+ if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 1, tx->function);
+ printf("expected address %" PRIu8 ", got %" PRIu8 ": ", 0x7890, tx->read.address);
+ printf("expected quantity %" PRIu8 ", got %" PRIu8 ": ", 19, tx->read.quantity);
+ goto end;
+ }
+
+ input_len = sizeof(readCoilsRsp);
+ part2_len = 10;
+ input = readCoilsRsp;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ input, input_len - part2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ input, input_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (modbus_state->transaction_max !=1) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 1, modbus_state->transaction_max);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Modbus fragmentation - 2 ADU in 1 TCP packet. */
+static int ModbusParserTest10(void) {
+ uint32_t input_len = sizeof(readCoilsReq) + sizeof(writeMultipleRegistersReq);
+ uint8_t *input, *ptr;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ Flow f;
+ TcpSession ssn;
+
+ int result = 0;
+
+ input = (uint8_t *) SCMalloc (input_len * sizeof(uint8_t));
+ if (unlikely(input == NULL))
+ goto end;
+
+ memcpy(input, readCoilsReq, sizeof(readCoilsReq));
+ memcpy(input + sizeof(readCoilsReq), writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq));
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ input, input_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ if (modbus_state->transaction_max !=2) {
+ printf("expected transaction_max %" PRIu8 ", got %" PRIu64 ": ", 2, modbus_state->transaction_max);
+ goto end;
+ }
+
+ ModbusTransaction *tx = ModbusGetTx(modbus_state, 1);
+
+ if ((tx->function != 16) || (tx->write.address != 0x01) || (tx->write.quantity != 2) ||
+ (tx->write.count != 4) || (tx->data[0] != 0x000A) || (tx->data[1] != 0x0102)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 16, tx->function);
+ printf("expected write address %" PRIu8 ", got %" PRIu8 ": ", 0x01, tx->write.address);
+ printf("expected write quantity %" PRIu8 ", got %" PRIu8 ": ", 2, tx->write.quantity);
+ printf("expected write count %" PRIu8 ", got %" PRIu8 ": ", 4, tx->write.count);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x000A, tx->data[0]);
+ printf("expected data %" PRIu8 ", got %" PRIu8 ": ", 0x0102, tx->data[1]);
+ goto end;
+ }
+
+ input_len = sizeof(readCoilsRsp) + sizeof(writeMultipleRegistersRsp);
+
+ ptr = (uint8_t *) SCRealloc (input, input_len * sizeof(uint8_t));
+ if (unlikely(ptr == NULL))
+ goto end;
+ input = ptr;
+
+ memcpy(input, readCoilsRsp, sizeof(readCoilsRsp));
+ memcpy(input + sizeof(readCoilsRsp), writeMultipleRegistersRsp, sizeof(writeMultipleRegistersRsp));
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
+ input, sizeof(input_len));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result = 1;
+end:
+ if (input != NULL)
+ SCFree(input);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Send Modbus exceed Length request. */
+static int ModbusParserTest11(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus invalid Length\"; "
+ "app-layer-event: "
+ "modbus.invalid_length; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ exceededLengthWriteMultipleRegistersReq,
+ sizeof(exceededLengthWriteMultipleRegistersReq) + 65523 /* header.length - 7 */ * sizeof(uint8_t));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Send Modbus invalid PDU Length. */
+static int ModbusParserTest12(void) {
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Modbus invalid Length\"; "
+ "app-layer-event: "
+ "modbus.invalid_length; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ invalidLengthPDUWriteMultipleRegistersReq,
+ sizeof(invalidLengthPDUWriteMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void ModbusParserRegisterTests(void) {
+#ifdef UNITTESTS
+ UtRegisterTest("ModbusParserTest01 - Modbus Read Coils request", ModbusParserTest01, 1);
+ UtRegisterTest("ModbusParserTest02 - Modbus Write Multiple registers request", ModbusParserTest02, 1);
+ UtRegisterTest("ModbusParserTest03 - Modbus Read/Write Multiple registers request", ModbusParserTest03, 1);
+ UtRegisterTest("ModbusParserTest04 - Modbus Force Listen Only Mode request", ModbusParserTest04, 1);
+ UtRegisterTest("ModbusParserTest05 - Modbus invalid Protocol version", ModbusParserTest05, 1);
+ UtRegisterTest("ModbusParserTest06 - Modbus unsolicited response", ModbusParserTest06, 1);
+ UtRegisterTest("ModbusParserTest07 - Modbus invalid Length request", ModbusParserTest07, 1);
+ UtRegisterTest("ModbusParserTest08 - Modbus Exception code invalid", ModbusParserTest08, 1);
+ UtRegisterTest("ModbusParserTest09 - Modbus fragmentation - 1 ADU in 2 TCP packets", ModbusParserTest09, 1);
+ UtRegisterTest("ModbusParserTest10 - Modbus fragmentation - 2 ADU in 1 TCP packet", ModbusParserTest10, 1);
+ UtRegisterTest("ModbusParserTest11 - Modbus exceeded Length request", ModbusParserTest11, 1);
+ UtRegisterTest("ModbusParserTest12 - Modbus invalid PDU Length", ModbusParserTest12, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/app-layer-modbus.h b/framework/src/suricata/src/app-layer-modbus.h
new file mode 100644
index 00000000..25ac519a
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-modbus.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ */
+
+#ifndef __APP_LAYER_MODBUS_H__
+#define __APP_LAYER_MODBUS_H__
+
+#include "decode.h"
+#include "detect-engine-state.h"
+#include "queue.h"
+
+/* Modbus Application Data Unit (ADU)
+ * and Protocol Data Unit (PDU) messages */
+enum {
+ MODBUS_DECODER_EVENT_INVALID_PROTOCOL_ID,
+ MODBUS_DECODER_EVENT_UNSOLICITED_RESPONSE,
+ MODBUS_DECODER_EVENT_INVALID_LENGTH,
+ MODBUS_DECODER_EVENT_INVALID_UNIT_IDENTIFIER,
+ MODBUS_DECODER_EVENT_INVALID_FUNCTION_CODE,
+ MODBUS_DECODER_EVENT_INVALID_VALUE,
+ MODBUS_DECODER_EVENT_INVALID_EXCEPTION_CODE,
+ MODBUS_DECODER_EVENT_VALUE_MISMATCH,
+ MODBUS_DECODER_EVENT_FLOODED,
+};
+
+/* Modbus Function Code Categories. */
+#define MODBUS_CAT_NONE 0x0
+#define MODBUS_CAT_PUBLIC_ASSIGNED (1<<0)
+#define MODBUS_CAT_PUBLIC_UNASSIGNED (1<<1)
+#define MODBUS_CAT_USER_DEFINED (1<<2)
+#define MODBUS_CAT_RESERVED (1<<3)
+#define MODBUS_CAT_ALL 0xFF
+
+/* Modbus Read/Write function and Access Types. */
+#define MODBUS_TYP_NONE 0x0
+#define MODBUS_TYP_ACCESS_MASK 0x03
+#define MODBUS_TYP_READ (1<<0)
+#define MODBUS_TYP_WRITE (1<<1)
+#define MODBUS_TYP_ACCESS_FUNCTION_MASK 0x3C
+#define MODBUS_TYP_BIT_ACCESS_MASK 0x0C
+#define MODBUS_TYP_DISCRETES (1<<2)
+#define MODBUS_TYP_COILS (1<<3)
+#define MODBUS_TYP_WORD_ACCESS_MASK 0x30
+#define MODBUS_TYP_INPUT (1<<4)
+#define MODBUS_TYP_HOLDING (1<<5)
+#define MODBUS_TYP_SINGLE (1<<6)
+#define MODBUS_TYP_MULTIPLE (1<<7)
+#define MODBUS_TYP_WRITE_SINGLE (MODBUS_TYP_WRITE | MODBUS_TYP_SINGLE)
+#define MODBUS_TYP_WRITE_MULTIPLE (MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
+#define MODBUS_TYP_READ_WRITE_MULTIPLE (MODBUS_TYP_READ | MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
+
+/* Modbus Transaction Structure, request/response. */
+typedef struct ModbusTransaction_ {
+ struct ModbusState_ *modbus;
+
+ uint64_t tx_num; /**< internal: id */
+ uint16_t transactionId;
+ uint16_t length;
+ uint8_t function;
+ uint8_t category;
+ uint8_t type;
+ uint8_t replied; /**< bool indicating request is replied to. */
+
+ union {
+ uint16_t subFunction;
+ uint8_t mei;
+ struct {
+ struct {
+ uint16_t address;
+ uint16_t quantity;
+ } read;
+ struct {
+ uint16_t address;
+ uint16_t quantity;
+ uint8_t count;
+ } write;
+ };
+ };
+ uint16_t *data; /**< to store data to write, bit is converted in 16bits. */
+
+ AppLayerDecoderEvents *decoder_events; /**< per tx events */
+ DetectEngineState *de_state;
+
+ TAILQ_ENTRY(ModbusTransaction_) next;
+} ModbusTransaction;
+
+/* Modbus State Structure. */
+typedef struct ModbusState_ {
+ TAILQ_HEAD(, ModbusTransaction_) tx_list; /**< transaction list */
+ ModbusTransaction *curr; /**< ptr to current tx */
+ uint64_t transaction_max;
+ uint32_t unreplied_cnt; /**< number of unreplied requests */
+ uint16_t events;
+ uint8_t givenup; /**< bool indicating flood. */
+} ModbusState;
+
+void RegisterModbusParsers(void);
+void ModbusParserRegisterTests(void);
+
+#endif /* __APP_LAYER_MODBUS_H__ */
diff --git a/framework/src/suricata/src/app-layer-nbss.h b/framework/src/suricata/src/app-layer-nbss.h
new file mode 100644
index 00000000..7ae8f2b4
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-nbss.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+
+#ifndef __APP_LAYER_NBSS_H__
+#define __APP_LAYER_NBSS_H__
+
+#include "suricata-common.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "flow.h"
+#include "stream.h"
+
+/*
+ http://ubiqx.org/cifs/rfc-draft/rfc1002.html#s4.3
+ All session packets are of the following general structure:
+
+ 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | TYPE | FLAGS | LENGTH |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ / TRAILER (Packet Type Dependent) /
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The TYPE, FLAGS, and LENGTH fields are present in every session
+ packet.
+*/
+
+#define NBSS_SESSION_MESSAGE 0x00
+#define NBSS_SESSION_REQUEST 0x81
+#define NBSS_POSITIVE_SESSION_RESPONSE 0x82
+#define NBSS_NEGATIVE_SESSION_RESPONSE 0x83
+#define NBSS_RETARGET_SESSION_RESPONSE 0x84
+#define NBSS_SESSION_KEEP_ALIVE 0x85
+
+typedef struct NBSSHdr_ {
+ uint8_t type;
+ uint8_t flags;
+ uint32_t length;
+} NBSSHdr;
+
+#define NBSS_HDR_LEN 4
+
+#endif /* __APP_LAYER_NBSS_H__ */
diff --git a/framework/src/suricata/src/app-layer-parser.c b/framework/src/suricata/src/app-layer-parser.c
new file mode 100644
index 00000000..134f9909
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-parser.c
@@ -0,0 +1,1385 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Generic App-layer parsing functions.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "util-unittest.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "flow-util.h"
+#include "flow-private.h"
+
+#include "detect-engine-state.h"
+#include "detect-engine-port.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream.h"
+#include "stream-tcp-reassemble.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-smb.h"
+#include "app-layer-smb2.h"
+#include "app-layer-dcerpc.h"
+#include "app-layer-dcerpc-udp.h"
+#include "app-layer-htp.h"
+#include "app-layer-ftp.h"
+#include "app-layer-ssl.h"
+#include "app-layer-ssh.h"
+#include "app-layer-smtp.h"
+#include "app-layer-dns-udp.h"
+#include "app-layer-dns-tcp.h"
+#include "app-layer-modbus.h"
+
+#include "conf.h"
+#include "util-spm.h"
+
+#include "util-debug.h"
+#include "decode-events.h"
+#include "util-unittest-helper.h"
+#include "util-validate.h"
+
+#include "runmodes.h"
+
+static GetActiveTxIdFunc AppLayerGetActiveTxIdFuncPtr = NULL;
+
+struct AppLayerParserThreadCtx_ {
+ void *alproto_local_storage[FLOW_PROTO_MAX][ALPROTO_MAX];
+};
+
+
+/**
+ * \brief App layer protocol parser context.
+ */
+typedef struct AppLayerParserProtoCtx_
+{
+ /* 0 - to_server, 1 - to_client. */
+ int (*Parser[2])(Flow *f, void *protocol_state,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_storage);
+ char logger;
+
+ void *(*StateAlloc)(void);
+ void (*StateFree)(void *);
+ void (*StateTransactionFree)(void *, uint64_t);
+ void *(*LocalStorageAlloc)(void);
+ void (*LocalStorageFree)(void *);
+
+ void (*Truncate)(void *, uint8_t);
+ FileContainer *(*StateGetFiles)(void *, uint8_t);
+ AppLayerDecoderEvents *(*StateGetEvents)(void *, uint64_t);
+ int (*StateHasEvents)(void *);
+
+ int (*StateGetProgress)(void *alstate, uint8_t direction);
+ uint64_t (*StateGetTxCnt)(void *alstate);
+ void *(*StateGetTx)(void *alstate, uint64_t tx_id);
+ int (*StateGetProgressCompletionStatus)(uint8_t direction);
+ int (*StateGetEventInfo)(const char *event_name,
+ int *event_id, AppLayerEventType *event_type);
+
+ int (*StateHasTxDetectState)(void *alstate);
+ DetectEngineState *(*GetTxDetectState)(void *tx);
+ int (*SetTxDetectState)(void *alstate, void *tx, DetectEngineState *);
+
+ /* Indicates the direction the parser is ready to see the data
+ * the first time for a flow. Values accepted -
+ * STREAM_TOSERVER, STREAM_TOCLIENT */
+ uint8_t first_data_dir;
+
+#ifdef UNITTESTS
+ void (*RegisterUnittests)(void);
+#endif
+} AppLayerParserProtoCtx;
+
+typedef struct AppLayerParserCtx_ {
+ AppLayerParserProtoCtx ctxs[FLOW_PROTO_MAX][ALPROTO_MAX];
+} AppLayerParserCtx;
+
+struct AppLayerParserState_ {
+ uint8_t flags;
+
+ /* State version, incremented for each update. Can wrap around. */
+ uint8_t version;
+ /* Indicates the current transaction that is being inspected.
+ * We have a var per direction. */
+ uint64_t inspect_id[2];
+ /* Indicates the current transaction being logged. Unlike inspect_id,
+ * we don't need a var per direction since we don't log a transaction
+ * unless we have the entire transaction. */
+ uint64_t log_id;
+
+ /* Used to store decoder events. */
+ AppLayerDecoderEvents *decoder_events;
+};
+
+/* Static global version of the parser context.
+ * Post 2.0 let's look at changing this to move it out to app-layer.c. */
+static AppLayerParserCtx alp_ctx;
+
+AppLayerParserState *AppLayerParserStateAlloc(void)
+{
+ SCEnter();
+
+ AppLayerParserState *pstate = (AppLayerParserState *)SCMalloc(sizeof(*pstate));
+ if (pstate == NULL)
+ goto end;
+ memset(pstate, 0, sizeof(*pstate));
+
+ end:
+ SCReturnPtr(pstate, "AppLayerParserState");
+}
+
+void AppLayerParserStateFree(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ if (pstate->decoder_events != NULL)
+ AppLayerDecoderEventsFreeEvents(&pstate->decoder_events);
+ SCFree(pstate);
+
+ SCReturn;
+}
+
+int AppLayerParserSetup(void)
+{
+ SCEnter();
+
+ memset(&alp_ctx, 0, sizeof(alp_ctx));
+
+ /* set the default tx handler if none was set explicitly */
+ if (AppLayerGetActiveTxIdFuncPtr == NULL) {
+ RegisterAppLayerGetActiveTxIdFunc(AppLayerTransactionGetActiveDetectLog);
+ }
+
+ SCReturnInt(0);
+}
+
+int AppLayerParserDeSetup(void)
+{
+ SCEnter();
+
+ SCReturnInt(0);
+}
+
+AppLayerParserThreadCtx *AppLayerParserThreadCtxAlloc(void)
+{
+ SCEnter();
+
+ AppProto alproto = 0;
+ int flow_proto = 0;
+ AppLayerParserThreadCtx *tctx;
+
+ tctx = SCMalloc(sizeof(*tctx));
+ if (tctx == NULL)
+ goto end;
+ memset(tctx, 0, sizeof(*tctx));
+
+ for (flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) {
+ for (alproto = 0; alproto < ALPROTO_MAX; alproto++) {
+ uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto);
+
+ tctx->alproto_local_storage[flow_proto][alproto] =
+ AppLayerParserGetProtocolParserLocalStorage(ipproto, alproto);
+ }
+ }
+
+ end:
+ SCReturnPtr(tctx, "void *");
+}
+
+void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
+{
+ SCEnter();
+
+ AppProto alproto = 0;
+ int flow_proto = 0;
+
+ for (flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) {
+ for (alproto = 0; alproto < ALPROTO_MAX; alproto++) {
+ uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto);
+
+ AppLayerParserDestroyProtocolParserLocalStorage(ipproto, alproto,
+ tctx->alproto_local_storage[flow_proto][alproto]);
+ }
+ }
+
+ SCFree(tctx);
+ SCReturn;
+}
+
+int AppLayerParserConfParserEnabled(const char *ipproto,
+ const char *alproto_name)
+{
+ SCEnter();
+
+ int enabled = 1;
+ char param[100];
+ ConfNode *node;
+ int r;
+
+ if (RunmodeIsUnittests())
+ goto enabled;
+
+ r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.",
+ alproto_name, ".enabled");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+
+ node = ConfGetNode(param);
+ if (node == NULL) {
+ SCLogDebug("Entry for %s not found.", param);
+ r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.",
+ alproto_name, ".", ipproto, ".enabled");
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(param)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+
+ node = ConfGetNode(param);
+ if (node == NULL) {
+ SCLogDebug("Entry for %s not found.", param);
+ goto enabled;
+ }
+ }
+
+ if (strcasecmp(node->val, "yes") == 0) {
+ goto enabled;
+ } else if (strcasecmp(node->val, "no") == 0) {
+ goto disabled;
+ } else if (strcasecmp(node->val, "detection-only") == 0) {
+ goto disabled;
+ } else {
+ SCLogError(SC_ERR_FATAL, "Invalid value found for %s.", param);
+ exit(EXIT_FAILURE);
+ }
+
+ disabled:
+ enabled = 0;
+ enabled:
+ SCReturnInt(enabled);
+}
+
+/***** Parser related registration *****/
+
+int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto,
+ uint8_t direction,
+ int (*Parser)(Flow *f, void *protocol_state,
+ AppLayerParserState *pstate,
+ uint8_t *buf, uint32_t buf_len,
+ void *local_storage))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ Parser[(direction & STREAM_TOSERVER) ? 0 : 1] = Parser;
+
+ SCReturnInt(0);
+}
+
+void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppProto alproto,
+ uint8_t direction)
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].first_data_dir |=
+ (direction & (STREAM_TOSERVER | STREAM_TOCLIENT));
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto,
+ void *(*StateAlloc)(void),
+ void (*StateFree)(void *))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateAlloc =
+ StateAlloc;
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateFree =
+ StateFree;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterLocalStorageFunc(uint8_t ipproto, AppProto alproto,
+ void *(*LocalStorageAlloc)(void),
+ void (*LocalStorageFree)(void *))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageAlloc =
+ LocalStorageAlloc;
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageFree =
+ LocalStorageFree;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto,
+ FileContainer *(*StateGetFiles)(void *, uint8_t))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetFiles =
+ StateGetFiles;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetEventsFunc(uint8_t ipproto, AppProto alproto,
+ AppLayerDecoderEvents *(*StateGetEvents)(void *, uint64_t))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetEvents =
+ StateGetEvents;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterHasEventsFunc(uint8_t ipproto, AppProto alproto,
+ int (*StateHasEvents)(void *))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasEvents =
+ StateHasEvents;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger = TRUE;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterTruncateFunc(uint8_t ipproto, AppProto alproto,
+ void (*Truncate)(void *, uint8_t))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate = Truncate;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto,
+ int (*StateGetProgress)(void *alstate, uint8_t direction))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetProgress = StateGetProgress;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto,
+ void (*StateTransactionFree)(void *, uint64_t))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateTransactionFree = StateTransactionFree;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto,
+ uint64_t (*StateGetTxCnt)(void *alstate))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetTxCnt = StateGetTxCnt;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto,
+ void *(StateGetTx)(void *alstate, uint64_t tx_id))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetTx = StateGetTx;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetStateProgressCompletionStatus(uint8_t ipproto,
+ AppProto alproto,
+ int (*StateGetProgressCompletionStatus)(uint8_t direction))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetProgressCompletionStatus = StateGetProgressCompletionStatus;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto,
+ int (*StateGetEventInfo)(const char *event_name, int *event_id,
+ AppLayerEventType *event_type))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetEventInfo = StateGetEventInfo;
+
+ SCReturn;
+}
+
+void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto,
+ int (*StateHasTxDetectState)(void *alstate),
+ DetectEngineState *(*GetTxDetectState)(void *tx),
+ int (*SetTxDetectState)(void *alstate, void *tx, DetectEngineState *))
+{
+ SCEnter();
+
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState = StateHasTxDetectState;
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState = GetTxDetectState;
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetTxDetectState = SetTxDetectState;
+
+ SCReturn;
+}
+
+/***** Get and transaction functions *****/
+
+void *AppLayerParserGetProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ void * r = NULL;
+
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ LocalStorageAlloc != NULL)
+ {
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ LocalStorageAlloc();
+ }
+
+ SCReturnPtr(r, "void *");
+}
+
+void AppLayerParserDestroyProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto,
+ void *local_data)
+{
+ SCEnter();
+
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ LocalStorageFree != NULL)
+ {
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ LocalStorageFree(local_data);
+ }
+
+ SCReturn;
+}
+
+uint64_t AppLayerParserGetTransactionLogId(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ SCReturnCT((pstate == NULL) ? 0 : pstate->log_id, "uint64_t");
+}
+
+void AppLayerParserSetTransactionLogId(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ if (pstate != NULL)
+ pstate->log_id++;
+
+ SCReturn;
+}
+
+uint64_t AppLayerParserGetTransactionInspectId(AppLayerParserState *pstate, uint8_t direction)
+{
+ SCEnter();
+
+ if (pstate == NULL)
+ SCReturnCT(0ULL, "uint64_t");
+
+ SCReturnCT(pstate->inspect_id[direction & STREAM_TOSERVER ? 0 : 1], "uint64_t");
+}
+
+void AppLayerParserSetTransactionInspectId(AppLayerParserState *pstate,
+ const uint8_t ipproto, const AppProto alproto,
+ void *alstate, const uint8_t flags)
+{
+ SCEnter();
+
+ int direction = (flags & STREAM_TOSERVER) ? 0 : 1;
+ uint64_t total_txs = AppLayerParserGetTxCnt(ipproto, alproto, alstate);
+ uint64_t idx = AppLayerParserGetTransactionInspectId(pstate, flags);
+ int state_done_progress = AppLayerParserGetStateProgressCompletionStatus(ipproto, alproto, flags);
+ void *tx;
+ int state_progress;
+
+ for (; idx < total_txs; idx++) {
+ tx = AppLayerParserGetTx(ipproto, alproto, alstate, idx);
+ if (tx == NULL)
+ continue;
+ state_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx, flags);
+ if (state_progress >= state_done_progress)
+ continue;
+ else
+ break;
+ }
+ pstate->inspect_id[direction] = idx;
+
+ SCReturn;
+}
+
+AppLayerDecoderEvents *AppLayerParserGetDecoderEvents(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ SCReturnPtr(pstate->decoder_events,
+ "AppLayerDecoderEvents *");
+}
+
+void AppLayerParserSetDecoderEvents(AppLayerParserState *pstate, AppLayerDecoderEvents *devents)
+{
+ pstate->decoder_events = devents;
+}
+
+AppLayerDecoderEvents *AppLayerParserGetEventsByTx(uint8_t ipproto, AppProto alproto,
+ void *alstate, uint64_t tx_id)
+{
+ SCEnter();
+
+ AppLayerDecoderEvents *ptr = NULL;
+
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetEvents != NULL)
+ {
+ ptr = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetEvents(alstate, tx_id);
+ }
+
+ SCReturnPtr(ptr, "AppLayerDecoderEvents *");
+}
+
+uint16_t AppLayerParserGetStateVersion(AppLayerParserState *pstate)
+{
+ SCEnter();
+ SCReturnCT((pstate == NULL) ? 0 : pstate->version, "uint8_t");
+}
+
+FileContainer *AppLayerParserGetFiles(uint8_t ipproto, AppProto alproto,
+ void *alstate, uint8_t direction)
+{
+ SCEnter();
+
+ FileContainer *ptr = NULL;
+
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetFiles != NULL)
+ {
+ ptr = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetFiles(alstate, direction);
+ }
+
+ SCReturnPtr(ptr, "FileContainer *");
+}
+
+/** \brief active TX retrieval for normal ops: so with detection and logging
+ *
+ * \retval tx_id lowest tx_id that still needs work */
+uint64_t AppLayerTransactionGetActiveDetectLog(Flow *f, uint8_t flags)
+{
+ AppLayerParserProtoCtx *p = &alp_ctx.ctxs[FlowGetProtoMapping(f->proto)][f->alproto];
+ uint64_t log_id = f->alparser->log_id;
+ uint64_t inspect_id = f->alparser->inspect_id[flags & STREAM_TOSERVER ? 0 : 1];
+ if (p->logger == TRUE) {
+ return (log_id < inspect_id) ? log_id : inspect_id;
+ } else {
+ return inspect_id;
+ }
+}
+
+/** \brief active TX retrieval for logging only: so NO detection
+ *
+ * If the logger is enabled, we simply return the log_id here.
+ *
+ * Otherwise, we go look for the tx id. There probably is no point
+ * in running this function in that case though. With no detection
+ * and no logging, why run a parser in the first place?
+ **/
+uint64_t AppLayerTransactionGetActiveLogOnly(Flow *f, uint8_t flags)
+{
+ AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][f->alproto];
+
+ if (p->logger == TRUE) {
+ uint64_t log_id = f->alparser->log_id;
+ SCLogDebug("returning %"PRIu64, log_id);
+ return log_id;
+ }
+
+ /* logger is disabled, return highest 'complete' tx id */
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, f->alproto, f->alstate);
+ uint64_t idx = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ int state_done_progress = AppLayerParserGetStateProgressCompletionStatus(f->proto, f->alproto, flags);
+ void *tx;
+ int state_progress;
+
+ for (; idx < total_txs; idx++) {
+ tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, idx);
+ if (tx == NULL)
+ continue;
+ state_progress = AppLayerParserGetStateProgress(f->proto, f->alproto, tx, flags);
+ if (state_progress >= state_done_progress)
+ continue;
+ else
+ break;
+ }
+ SCLogDebug("returning %"PRIu64, idx);
+ return idx;
+}
+
+void RegisterAppLayerGetActiveTxIdFunc(GetActiveTxIdFunc FuncPtr)
+{
+ //BUG_ON(AppLayerGetActiveTxIdFuncPtr != NULL);
+ AppLayerGetActiveTxIdFuncPtr = FuncPtr;
+ SCLogDebug("AppLayerGetActiveTxIdFuncPtr is now %p", AppLayerGetActiveTxIdFuncPtr);
+}
+
+/**
+ * \brief Get 'active' tx id, meaning the lowest id that still need work.
+ *
+ * \retval id tx id
+ */
+static uint64_t AppLayerTransactionGetActive(Flow *f, uint8_t flags)
+{
+ BUG_ON(AppLayerGetActiveTxIdFuncPtr == NULL);
+
+ return AppLayerGetActiveTxIdFuncPtr(f, flags);
+}
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/**
+ * \brief remove obsolete (inspected and logged) transactions
+ */
+static void AppLayerParserTransactionsCleanup(Flow *f)
+{
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ AppLayerParserProtoCtx *p = &alp_ctx.ctxs[FlowGetProtoMapping(f->proto)][f->alproto];
+ if (p->StateTransactionFree == NULL)
+ return;
+
+ uint64_t tx_id_ts = AppLayerTransactionGetActive(f, STREAM_TOSERVER);
+ uint64_t tx_id_tc = AppLayerTransactionGetActive(f, STREAM_TOCLIENT);
+
+ uint64_t min = MIN(tx_id_ts, tx_id_tc);
+ if (min > 0) {
+ SCLogDebug("freeing %"PRIu64" %p", min - 1, p->StateTransactionFree);
+ p->StateTransactionFree(f->alstate, min - 1);
+ }
+}
+
+#define IS_DISRUPTED(flags) \
+ ((flags) & (STREAM_DEPTH|STREAM_GAP))
+
+/**
+ * \brief get the progress value for a tx/protocol
+ *
+ * If the stream is disrupted, we return the 'completion' value.
+ */
+int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto,
+ void *alstate, uint8_t flags)
+{
+ SCEnter();
+ int r = 0;
+ if (unlikely(IS_DISRUPTED(flags))) {
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetProgressCompletionStatus(flags);
+ } else {
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetProgress(alstate, flags);
+ }
+ SCReturnInt(r);
+}
+
+uint64_t AppLayerParserGetTxCnt(uint8_t ipproto, AppProto alproto, void *alstate)
+{
+ SCEnter();
+ uint64_t r = 0;
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetTxCnt(alstate);
+ SCReturnCT(r, "uint64_t");
+}
+
+void *AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id)
+{
+ SCEnter();
+ void * r = NULL;
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetTx(alstate, tx_id);
+ SCReturnPtr(r, "void *");
+}
+
+int AppLayerParserGetStateProgressCompletionStatus(uint8_t ipproto, AppProto alproto,
+ uint8_t direction)
+{
+ SCEnter();
+ int r = 0;
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateGetProgressCompletionStatus(direction);
+ SCReturnInt(r);
+}
+
+int AppLayerParserGetEventInfo(uint8_t ipproto, AppProto alproto, const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ SCEnter();
+ int ipproto_map = FlowGetProtoMapping(ipproto);
+ int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo == NULL) ?
+ -1 : alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo(event_name, event_id, event_type);
+ SCReturnInt(r);
+}
+
+uint8_t AppLayerParserGetFirstDataDir(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ uint8_t r = 0;
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ first_data_dir;
+ SCReturnCT(r, "uint8_t");
+}
+
+uint64_t AppLayerParserGetTransactionActive(uint8_t ipproto, AppProto alproto,
+ AppLayerParserState *pstate, uint8_t direction)
+{
+ SCEnter();
+
+ uint64_t active_id;
+
+ uint64_t log_id = pstate->log_id;
+ uint64_t inspect_id = pstate->inspect_id[direction & STREAM_TOSERVER ? 0 : 1];
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger == TRUE) {
+ active_id = (log_id < inspect_id) ? log_id : inspect_id;
+ } else {
+ active_id = inspect_id;
+ }
+
+ SCReturnCT(active_id, "uint64_t");
+}
+
+int AppLayerParserSupportsTxDetectState(uint8_t ipproto, AppProto alproto)
+{
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState != NULL)
+ return TRUE;
+ return FALSE;
+}
+
+int AppLayerParserHasTxDetectState(uint8_t ipproto, AppProto alproto, void *alstate)
+{
+ int r;
+ SCEnter();
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState == NULL)
+ return -ENOSYS;
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState(alstate);
+ SCReturnInt(r);
+}
+
+DetectEngineState *AppLayerParserGetTxDetectState(uint8_t ipproto, AppProto alproto, void *tx)
+{
+ SCEnter();
+ DetectEngineState *s;
+ s = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState(tx);
+ SCReturnPtr(s, "DetectEngineState");
+}
+
+int AppLayerParserSetTxDetectState(uint8_t ipproto, AppProto alproto,
+ void *alstate, void *tx, DetectEngineState *s)
+{
+ int r;
+ SCEnter();
+ if ((alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState(tx) != NULL))
+ SCReturnInt(-EBUSY);
+ r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetTxDetectState(alstate, tx, s);
+ SCReturnInt(r);
+}
+
+/***** General *****/
+
+int AppLayerParserParse(AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto,
+ uint8_t flags, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+#ifdef DEBUG_VALIDATION
+ BUG_ON(f->protomap != FlowGetProtoMapping(f->proto));
+#endif
+ AppLayerParserState *pstate = NULL;
+ AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][alproto];
+ void *alstate = NULL;
+
+ /* we don't have the parser registered for this protocol */
+ if (p->StateAlloc == NULL)
+ goto end;
+
+ /* Do this check before calling AppLayerParse */
+ if (flags & STREAM_GAP) {
+ SCLogDebug("stream gap detected (missing packets), "
+ "this is not yet supported.");
+
+ if (f->alstate != NULL)
+ AppLayerParserStreamTruncated(f->proto, alproto, f->alstate, flags);
+ goto error;
+ }
+
+ /* Get the parser state (if any) */
+ pstate = f->alparser;
+ if (pstate == NULL) {
+ f->alparser = pstate = AppLayerParserStateAlloc();
+ if (pstate == NULL)
+ goto error;
+ }
+ pstate->version++;
+ SCLogDebug("app layer parser state version incremented to %"PRIu8,
+ pstate->version);
+
+ if (flags & STREAM_EOF)
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_EOF);
+
+ alstate = f->alstate;
+ if (alstate == NULL) {
+ f->alstate = alstate = p->StateAlloc();
+ if (alstate == NULL)
+ goto error;
+ SCLogDebug("alloced new app layer state %p (name %s)",
+ alstate, AppLayerGetProtoName(f->alproto));
+ } else {
+ SCLogDebug("using existing app layer state %p (name %s))",
+ alstate, AppLayerGetProtoName(f->alproto));
+ }
+
+ /* invoke the recursive parser, but only on data. We may get empty msgs on EOF */
+ if (input_len > 0 || (flags & STREAM_EOF)) {
+ /* invoke the parser */
+ if (p->Parser[(flags & STREAM_TOSERVER) ? 0 : 1](f, alstate, pstate,
+ input, input_len,
+ alp_tctx->alproto_local_storage[f->protomap][alproto]) < 0)
+ {
+ goto error;
+ }
+ }
+
+ /* set the packets to no inspection and reassembly if required */
+ if (pstate->flags & APP_LAYER_PARSER_NO_INSPECTION) {
+ AppLayerParserSetEOF(pstate);
+ FlowSetNoPayloadInspectionFlag(f);
+
+ if (f->proto == IPPROTO_TCP) {
+ StreamTcpDisableAppLayer(f);
+
+ /* Set the no reassembly flag for both the stream in this TcpSession */
+ if (pstate->flags & APP_LAYER_PARSER_NO_REASSEMBLY) {
+ /* Used only if it's TCP */
+ TcpSession *ssn = f->protoctx;
+ if (ssn != NULL) {
+ StreamTcpSetSessionNoReassemblyFlag(ssn,
+ flags & STREAM_TOCLIENT ? 1 : 0);
+ StreamTcpSetSessionNoReassemblyFlag(ssn,
+ flags & STREAM_TOSERVER ? 1 : 0);
+ }
+ }
+ }
+ }
+
+ /* In cases like HeartBleed for TLS we need to inspect AppLayer but not Payload */
+ if (!(f->flags & FLOW_NOPAYLOAD_INSPECTION) && pstate->flags & APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD) {
+ FlowSetNoPayloadInspectionFlag(f);
+ /* Set the no reassembly flag for both the stream in this TcpSession */
+ if (f->proto == IPPROTO_TCP) {
+ /* Used only if it's TCP */
+ TcpSession *ssn = f->protoctx;
+ if (ssn != NULL) {
+ StreamTcpSetDisableRawReassemblyFlag(ssn, 0);
+ StreamTcpSetDisableRawReassemblyFlag(ssn, 1);
+ }
+ }
+ }
+
+ /* next, see if we can get rid of transactions now */
+ AppLayerParserTransactionsCleanup(f);
+
+ /* stream truncated, inform app layer */
+ if (flags & STREAM_DEPTH)
+ AppLayerParserStreamTruncated(f->proto, alproto, alstate, flags);
+
+ end:
+ SCReturnInt(0);
+ error:
+ /* Set the no app layer inspection flag for both
+ * the stream in this Flow */
+ if (f->proto == IPPROTO_TCP) {
+ StreamTcpDisableAppLayer(f);
+ }
+ AppLayerParserSetEOF(pstate);
+ SCReturnInt(-1);
+}
+
+void AppLayerParserSetEOF(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ if (pstate == NULL)
+ goto end;
+
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_EOF);
+ /* increase version so we will inspect it one more time
+ * with the EOF flags now set */
+ pstate->version++;
+
+ end:
+ SCReturn;
+}
+
+int AppLayerParserHasDecoderEvents(uint8_t ipproto, AppProto alproto,
+ void *alstate, AppLayerParserState *pstate,
+ uint8_t flags)
+{
+ SCEnter();
+
+ if (alstate == NULL || pstate == NULL)
+ goto not_present;
+
+ AppLayerDecoderEvents *decoder_events;
+ uint64_t tx_id;
+ uint64_t max_id;
+
+ if (AppLayerParserProtocolIsTxEventAware(ipproto, alproto)) {
+ /* fast path if supported by alproto */
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasEvents != NULL) {
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ StateHasEvents(alstate) == 1)
+ {
+ goto present;
+ }
+ } else {
+ /* check each tx */
+ tx_id = AppLayerParserGetTransactionInspectId(pstate, flags);
+ max_id = AppLayerParserGetTxCnt(ipproto, alproto, alstate);
+ for ( ; tx_id < max_id; tx_id++) {
+ decoder_events = AppLayerParserGetEventsByTx(ipproto, alproto, alstate, tx_id);
+ if (decoder_events && decoder_events->cnt)
+ goto present;
+ }
+ }
+ }
+
+ decoder_events = AppLayerParserGetDecoderEvents(pstate);
+ if (decoder_events && decoder_events->cnt)
+ goto present;
+
+ /* if we have reached here, we don't have events */
+ not_present:
+ SCReturnInt(0);
+ present:
+ SCReturnInt(1);
+}
+
+int AppLayerParserProtocolIsTxAware(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ int ipproto_map = FlowGetProtoMapping(ipproto);
+ int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetTx == NULL) ? 0 : 1;
+ SCReturnInt(r);
+}
+
+int AppLayerParserProtocolIsTxEventAware(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ int ipproto_map = FlowGetProtoMapping(ipproto);
+ int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEvents == NULL) ? 0 : 1;
+ SCReturnInt(r);
+}
+
+int AppLayerParserProtocolSupportsTxs(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ int ipproto_map = FlowGetProtoMapping(ipproto);
+ int r = (alp_ctx.ctxs[ipproto_map][alproto].StateTransactionFree == NULL) ? 0 : 1;
+ SCReturnInt(r);
+}
+
+int AppLayerParserProtocolHasLogger(uint8_t ipproto, AppProto alproto)
+{
+ SCEnter();
+ int ipproto_map = FlowGetProtoMapping(ipproto);
+ int r = (alp_ctx.ctxs[ipproto_map][alproto].logger == 0) ? 0 : 1;
+ SCReturnInt(r);
+}
+
+void AppLayerParserTriggerRawStreamReassembly(Flow *f)
+{
+ SCEnter();
+
+ if (f != NULL && f->protoctx != NULL)
+ StreamTcpReassembleTriggerRawReassembly(f->protoctx);
+
+ SCReturn;
+}
+
+/***** Cleanup *****/
+
+void AppLayerParserStateCleanup(uint8_t ipproto, AppProto alproto, void *alstate,
+ AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto];
+
+ if (ctx->StateFree != NULL && alstate != NULL)
+ ctx->StateFree(alstate);
+
+ /* free the app layer parser api state */
+ if (pstate != NULL)
+ AppLayerParserStateFree(pstate);
+
+ SCReturn;
+}
+
+
+void AppLayerParserRegisterProtocolParsers(void)
+{
+ SCEnter();
+
+ RegisterHTPParsers();
+ RegisterSSLParsers();
+ RegisterSMBParsers();
+ /** \todo bug 719 */
+ //RegisterSMB2Parsers();
+ RegisterDCERPCParsers();
+ RegisterDCERPCUDPParsers();
+ RegisterFTPParsers();
+ RegisterSSHParsers();
+ RegisterSMTPParsers();
+ RegisterDNSUDPParsers();
+ RegisterDNSTCPParsers();
+ RegisterModbusParsers();
+
+ /** IMAP */
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap");
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "imap")) {
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_IMAP,
+ "1|20|capability", 12, 0, STREAM_TOSERVER) < 0)
+ {
+ SCLogInfo("imap proto registration failure\n");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ "imap");
+ }
+
+ /** MSN Messenger */
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_MSN, "msn");
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "msn")) {
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_MSN,
+ "msn", 10, 6, STREAM_TOSERVER) < 0)
+ {
+ SCLogInfo("msn proto registration failure\n");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ "msn");
+ }
+
+ return;
+}
+
+
+void AppLayerParserStateSetFlag(AppLayerParserState *pstate, uint8_t flag)
+{
+ SCEnter();
+ pstate->flags |= flag;
+ SCReturn;
+}
+
+int AppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint8_t flag)
+{
+ SCEnter();
+ SCReturnInt(pstate->flags & flag);
+}
+
+
+void AppLayerParserStreamTruncated(uint8_t ipproto, AppProto alproto, void *alstate,
+ uint8_t direction)
+{
+ SCEnter();
+
+
+ if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate != NULL)
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate(alstate, direction);
+
+ SCReturn;
+}
+
+#ifdef DEBUG
+void AppLayerParserStatePrintDetails(AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ if (pstate == NULL)
+ SCReturn;
+
+ AppLayerParserState *p = pstate;
+ SCLogDebug("AppLayerParser parser state information for parser state p(%p). "
+ "p->inspect_id[0](%"PRIu64"), "
+ "p->inspect_id[1](%"PRIu64"), "
+ "p->log_id(%"PRIu64"), "
+ "p->version(%"PRIu8"), "
+ "p->decoder_events(%p).",
+ pstate, p->inspect_id[0], p->inspect_id[1], p->log_id,
+ p->version, p->decoder_events);
+
+ SCReturn;
+}
+#endif
+
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+
+static AppLayerParserCtx alp_ctx_backup_unittest;
+
+typedef struct TestState_ {
+ uint8_t test;
+} TestState;
+
+/**
+ * \brief Test parser function to test the memory deallocation of app layer
+ * parser of occurence of an error.
+ */
+static int TestProtocolParser(Flow *f, void *test_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ SCReturnInt(-1);
+}
+
+/** \brief Function to allocates the Test protocol state memory
+ */
+static void *TestProtocolStateAlloc(void)
+{
+ SCEnter();
+ void *s = SCMalloc(sizeof(TestState));
+ if (unlikely(s == NULL))
+ goto end;
+ memset(s, 0, sizeof(TestState));
+ end:
+ SCReturnPtr(s, "TestState");
+}
+
+/** \brief Function to free the Test Protocol state memory
+ */
+static void TestProtocolStateFree(void *s)
+{
+ SCFree(s);
+}
+
+void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto,
+ void (*RegisterUnittests)(void))
+{
+ SCEnter();
+ alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].
+ RegisterUnittests = RegisterUnittests;
+ SCReturn;
+}
+
+void AppLayerParserBackupParserTable(void)
+{
+ SCEnter();
+ alp_ctx_backup_unittest = alp_ctx;
+ memset(&alp_ctx, 0, sizeof(alp_ctx));
+ SCReturn;
+}
+
+void AppLayerParserRestoreParserTable(void)
+{
+ SCEnter();
+ alp_ctx = alp_ctx_backup_unittest;
+ memset(&alp_ctx_backup_unittest, 0, sizeof(alp_ctx_backup_unittest));
+ SCReturn;
+}
+
+/**
+ * \test Test the deallocation of app layer parser memory on occurance of
+ * error in the parsing process.
+ */
+static int AppLayerParserTest01(void)
+{
+ AppLayerParserBackupParserTable();
+
+ int result = 0;
+ Flow *f = NULL;
+ uint8_t testbuf[] = { 0x11 };
+ uint32_t testlen = sizeof(testbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&ssn, 0, sizeof(ssn));
+
+ /* Register the Test protocol state and parser functions */
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEST, STREAM_TOSERVER,
+ TestProtocolParser);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEST,
+ TestProtocolStateAlloc, TestProtocolStateFree);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "4.3.2.1", 20, 40);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->alproto = ALPROTO_TEST;
+ f->proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_TEST, STREAM_TOSERVER|STREAM_EOF,
+ testbuf, testlen);
+ if (r != -1) {
+ printf("returned %" PRId32 ", expected -1: ", r);
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ if (!(ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) {
+ printf("flag should have been set, but is not: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ AppLayerParserRestoreParserTable();
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreeFlow(f);
+ return result;
+}
+
+/**
+ * \test Test the deallocation of app layer parser memory on occurance of
+ * error in the parsing process for UDP.
+ */
+static int AppLayerParserTest02(void)
+{
+ AppLayerParserBackupParserTable();
+
+ int result = 1;
+ Flow *f = NULL;
+ uint8_t testbuf[] = { 0x11 };
+ uint32_t testlen = sizeof(testbuf);
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ /* Register the Test protocol state and parser functions */
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TEST, STREAM_TOSERVER,
+ TestProtocolParser);
+ AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_TEST,
+ TestProtocolStateAlloc, TestProtocolStateFree);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "4.3.2.1", 20, 40);
+ if (f == NULL)
+ goto end;
+ f->alproto = ALPROTO_TEST;
+ f->proto = IPPROTO_UDP;
+ f->protomap = FlowGetProtoMapping(f->proto);
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_TEST, STREAM_TOSERVER|STREAM_EOF, testbuf,
+ testlen);
+ if (r != -1) {
+ printf("returned %" PRId32 ", expected -1: \n", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ end:
+ AppLayerParserRestoreParserTable();
+ StreamTcpFreeConfig(TRUE);
+ UTHFreeFlow(f);
+ return result;
+}
+
+
+void AppLayerParserRegisterUnittests(void)
+{
+ SCEnter();
+
+ int ip;
+ AppProto alproto;
+ AppLayerParserProtoCtx *ctx;
+
+ for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) {
+ for (alproto = 0; alproto < ALPROTO_MAX; alproto++) {
+ ctx = &alp_ctx.ctxs[ip][alproto];
+ if (ctx->RegisterUnittests == NULL)
+ continue;
+ ctx->RegisterUnittests();
+ }
+ }
+
+ UtRegisterTest("AppLayerParserTest01", AppLayerParserTest01, 1);
+ UtRegisterTest("AppLayerParserTest02", AppLayerParserTest02, 1);
+
+ SCReturn;
+}
+
+#endif
diff --git a/framework/src/suricata/src/app-layer-parser.h b/framework/src/suricata/src/app-layer-parser.h
new file mode 100644
index 00000000..62cb8f68
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-parser.h
@@ -0,0 +1,235 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_PARSER_H__
+#define __APP_LAYER_PARSER_H__
+
+#include "app-layer-events.h"
+#include "detect-engine-state.h"
+#include "util-file.h"
+
+#define APP_LAYER_PARSER_EOF 0x01
+#define APP_LAYER_PARSER_NO_INSPECTION 0x02
+#define APP_LAYER_PARSER_NO_REASSEMBLY 0x04
+#define APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD 0x08
+
+
+/***** transaction handling *****/
+
+/** \brief Function ptr type for getting active TxId from a flow
+ * Used by AppLayerTransactionGetActive.
+ */
+typedef uint64_t (*GetActiveTxIdFunc)(Flow *f, uint8_t flags);
+
+/** \brief Register GetActiveTxId Function
+ *
+ */
+void RegisterAppLayerGetActiveTxIdFunc(GetActiveTxIdFunc FuncPtr);
+
+/** \brief active TX retrieval for normal ops: so with detection and logging
+ *
+ * \retval tx_id lowest tx_id that still needs work
+ *
+ * This is the default function.
+ */
+uint64_t AppLayerTransactionGetActiveDetectLog(Flow *f, uint8_t flags);
+
+/** \brief active TX retrieval for logging only ops
+ *
+ * \retval tx_id lowest tx_id that still needs work
+ */
+uint64_t AppLayerTransactionGetActiveLogOnly(Flow *f, uint8_t flags);
+
+
+int AppLayerParserSetup(void);
+
+int AppLayerParserDeSetup(void);
+
+typedef struct AppLayerParserThreadCtx_ AppLayerParserThreadCtx;
+
+/**
+ * \brief Gets a new app layer protocol's parser thread context.
+ *
+ * \retval Non-NULL pointer on success.
+ * NULL pointer on failure.
+ */
+AppLayerParserThreadCtx *AppLayerParserThreadCtxAlloc(void);
+
+/**
+ * \brief Destroys the app layer parser thread context obtained
+ * using AppLayerParserThreadCtxAlloc().
+ *
+ * \param tctx Pointer to the thread context to be destroyed.
+ */
+void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx);
+
+/**
+ * \brief Given a protocol name, checks if the parser is enabled in
+ * the conf file.
+ *
+ * \param alproto_name Name of the app layer protocol.
+ *
+ * \retval 1 If enabled.
+ * \retval 0 If disabled.
+ */
+int AppLayerParserConfParserEnabled(const char *ipproto,
+ const char *alproto_name);
+
+/***** Parser related registration *****/
+
+/**
+ * \brief Register app layer parser for the protocol.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto,
+ uint8_t direction,
+ int (*Parser)(Flow *f, void *protocol_state,
+ AppLayerParserState *pstate,
+ uint8_t *buf, uint32_t buf_len,
+ void *local_storage));
+void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto,
+ AppProto alproto,
+ uint8_t direction);
+void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto,
+ void *(*StateAlloc)(void),
+ void (*StateFree)(void *));
+void AppLayerParserRegisterLocalStorageFunc(uint8_t ipproto, AppProto proto,
+ void *(*LocalStorageAlloc)(void),
+ void (*LocalStorageFree)(void *));
+void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto,
+ FileContainer *(*StateGetFiles)(void *, uint8_t));
+void AppLayerParserRegisterGetEventsFunc(uint8_t ipproto, AppProto proto,
+ AppLayerDecoderEvents *(*StateGetEvents)(void *, uint64_t));
+void AppLayerParserRegisterHasEventsFunc(uint8_t ipproto, AppProto alproto,
+ int (*StateHasEvents)(void *));
+void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto);
+void AppLayerParserRegisterTruncateFunc(uint8_t ipproto, AppProto alproto,
+ void (*Truncate)(void *, uint8_t));
+void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto,
+ int (*StateGetStateProgress)(void *alstate, uint8_t direction));
+void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto,
+ void (*StateTransactionFree)(void *, uint64_t));
+void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto,
+ uint64_t (*StateGetTxCnt)(void *alstate));
+void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto,
+ void *(StateGetTx)(void *alstate, uint64_t tx_id));
+void AppLayerParserRegisterGetStateProgressCompletionStatus(uint8_t ipproto,
+ AppProto alproto,
+ int (*StateGetStateProgressCompletionStatus)(uint8_t direction));
+void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto,
+ int (*StateGetEventInfo)(const char *event_name, int *event_id,
+ AppLayerEventType *event_type));
+void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto,
+ int (*StateHasTxDetectState)(void *alstate),
+ DetectEngineState *(*GetTxDetectState)(void *tx),
+ int (*SetTxDetectState)(void *alstate, void *tx, DetectEngineState *));
+
+/***** Get and transaction functions *****/
+
+void *AppLayerParserGetProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto);
+void AppLayerParserDestroyProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto,
+ void *local_data);
+
+
+uint64_t AppLayerParserGetTransactionLogId(AppLayerParserState *pstate);
+void AppLayerParserSetTransactionLogId(AppLayerParserState *pstate);
+uint64_t AppLayerParserGetTransactionInspectId(AppLayerParserState *pstate, uint8_t direction);
+void AppLayerParserSetTransactionInspectId(AppLayerParserState *pstate,
+ const uint8_t ipproto, const AppProto alproto, void *alstate,
+ const uint8_t flags);
+AppLayerDecoderEvents *AppLayerParserGetDecoderEvents(AppLayerParserState *pstate);
+void AppLayerParserSetDecoderEvents(AppLayerParserState *pstate, AppLayerDecoderEvents *devents);
+AppLayerDecoderEvents *AppLayerParserGetEventsByTx(uint8_t ipproto, AppProto alproto, void *alstate,
+ uint64_t tx_id);
+uint16_t AppLayerParserGetStateVersion(AppLayerParserState *pstate);
+FileContainer *AppLayerParserGetFiles(uint8_t ipproto, AppProto alproto,
+ void *alstate, uint8_t direction);
+int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto,
+ void *alstate, uint8_t direction);
+uint64_t AppLayerParserGetTxCnt(uint8_t ipproto, AppProto alproto, void *alstate);
+void *AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id);
+int AppLayerParserGetStateProgressCompletionStatus(uint8_t ipproto, AppProto alproto,
+ uint8_t direction);
+int AppLayerParserGetEventInfo(uint8_t ipproto, AppProto alproto, const char *event_name,
+ int *event_id, AppLayerEventType *event_type);
+
+uint64_t AppLayerParserGetTransactionActive(uint8_t ipproto, AppProto alproto, AppLayerParserState *pstate, uint8_t direction);
+
+uint8_t AppLayerParserGetFirstDataDir(uint8_t ipproto, AppProto alproto);
+
+int AppLayerParserSupportsTxDetectState(uint8_t ipproto, AppProto alproto);
+int AppLayerParserHasTxDetectState(uint8_t ipproto, AppProto alproto, void *alstate);
+DetectEngineState *AppLayerParserGetTxDetectState(uint8_t ipproto, AppProto alproto, void *tx);
+int AppLayerParserSetTxDetectState(uint8_t ipproto, AppProto alproto, void *alstate, void *tx, DetectEngineState *s);
+
+/***** General *****/
+
+int AppLayerParserParse(AppLayerParserThreadCtx *tctx, Flow *f, AppProto alproto,
+ uint8_t flags, uint8_t *input, uint32_t input_len);
+void AppLayerParserSetEOF(AppLayerParserState *pstate);
+int AppLayerParserHasDecoderEvents(uint8_t ipproto, AppProto alproto, void *alstate, AppLayerParserState *pstate,
+ uint8_t flags);
+int AppLayerParserProtocolIsTxAware(uint8_t ipproto, AppProto alproto);
+int AppLayerParserProtocolIsTxEventAware(uint8_t ipproto, AppProto alproto);
+int AppLayerParserProtocolSupportsTxs(uint8_t ipproto, AppProto alproto);
+int AppLayerParserProtocolHasLogger(uint8_t ipproto, AppProto alproto);
+void AppLayerParserTriggerRawStreamReassembly(Flow *f);
+
+/***** Cleanup *****/
+
+void AppLayerParserStateCleanup(uint8_t ipproto, AppProto alproto, void *alstate, AppLayerParserState *pstate);
+
+void AppLayerParserRegisterProtocolParsers(void);
+
+
+void AppLayerParserStateSetFlag(AppLayerParserState *pstate, uint8_t flag);
+int AppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint8_t flag);
+
+void AppLayerParserStreamTruncated(uint8_t ipproto, AppProto alproto, void *alstate,
+ uint8_t direction);
+
+
+
+AppLayerParserState *AppLayerParserStateAlloc(void);
+void AppLayerParserStateFree(AppLayerParserState *pstate);
+
+
+
+#ifdef DEBUG
+void AppLayerParserStatePrintDetails(AppLayerParserState *pstate);
+#endif
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto,
+ void (*RegisterUnittests)(void));
+void AppLayerParserRegisterUnittests(void);
+void AppLayerParserBackupParserTable(void);
+void AppLayerParserRestoreParserTable(void);
+#endif
+
+#endif /* __APP_LAYER_PARSER_H__ */
diff --git a/framework/src/suricata/src/app-layer-protos.c b/framework/src/suricata/src/app-layer-protos.c
new file mode 100644
index 00000000..0b8ed17b
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-protos.c
@@ -0,0 +1,88 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "app-layer-protos.h"
+
+#define CASE_CODE(E) case E: return #E
+
+const char *AppProtoToString(AppProto alproto)
+{
+ const char *proto_name = NULL;
+ enum AppProtoEnum proto = alproto;
+
+ switch (proto) {
+ case ALPROTO_HTTP:
+ proto_name = "http";
+ break;
+ case ALPROTO_FTP:
+ proto_name = "ftp";
+ break;
+ case ALPROTO_SMTP:
+ proto_name = "smtp";
+ break;
+ case ALPROTO_TLS:
+ proto_name = "tls";
+ break;
+ case ALPROTO_SSH:
+ proto_name = "ssh";
+ break;
+ case ALPROTO_IMAP:
+ proto_name = "imap";
+ break;
+ case ALPROTO_MSN:
+ proto_name = "msn";
+ break;
+ case ALPROTO_JABBER:
+ proto_name = "jabber";
+ break;
+ case ALPROTO_SMB:
+ proto_name = "smb";
+ break;
+ case ALPROTO_SMB2:
+ proto_name = "smb2";
+ break;
+ case ALPROTO_DCERPC:
+ proto_name = "dcerpc";
+ break;
+ case ALPROTO_IRC:
+ proto_name = "irc";
+ break;
+ case ALPROTO_DNS:
+ proto_name = "dns";
+ break;
+ case ALPROTO_MODBUS:
+ proto_name = "modbus";
+ break;
+ case ALPROTO_FAILED:
+#ifdef UNITTESTS
+ case ALPROTO_TEST:
+#endif
+ case ALPROTO_MAX:
+ case ALPROTO_UNKNOWN:
+ break;
+ }
+
+ return proto_name;
+}
diff --git a/framework/src/suricata/src/app-layer-protos.h b/framework/src/suricata/src/app-layer-protos.h
new file mode 100644
index 00000000..79973661
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-protos.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_PROTOS_H__
+#define __APP_LAYER_PROTOS_H__
+
+enum AppProtoEnum {
+ ALPROTO_UNKNOWN = 0,
+ ALPROTO_HTTP,
+ ALPROTO_FTP,
+ ALPROTO_SMTP,
+ ALPROTO_TLS, /* SSLv2, SSLv3 & TLSv1 */
+ ALPROTO_SSH,
+ ALPROTO_IMAP,
+ ALPROTO_MSN,
+ ALPROTO_JABBER,
+ ALPROTO_SMB,
+ ALPROTO_SMB2,
+ ALPROTO_DCERPC,
+ ALPROTO_IRC,
+
+ ALPROTO_DNS,
+ ALPROTO_MODBUS,
+
+ /* used by the probing parser when alproto detection fails
+ * permanently for that particular stream */
+ ALPROTO_FAILED,
+#ifdef UNITTESTS
+ ALPROTO_TEST,
+#endif /* UNITESTS */
+ /* keep last */
+ ALPROTO_MAX,
+};
+
+/* not using the enum as that is a unsigned int, so 4 bytes */
+typedef uint16_t AppProto;
+
+/**
+ * \brief Maps the ALPROTO_*, to its string equivalent.
+ *
+ * \param alproto App layer protocol id.
+ *
+ * \retval String equivalent for the alproto.
+ */
+const char *AppProtoToString(AppProto alproto);
+
+#endif /* __APP_LAYER_PROTOS_H__ */
diff --git a/framework/src/suricata/src/app-layer-smb.c b/framework/src/suricata/src/app-layer-smb.c
new file mode 100644
index 00000000..4d7aa845
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smb.c
@@ -0,0 +1,2717 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ *
+ * \brief SMBv1 parser/decoder
+ */
+
+#include "suricata-common.h"
+
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-debug.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+
+#include "app-layer-smb.h"
+
+enum {
+ SMB_FIELD_NONE = 0,
+ SMB_PARSE_NBSS_HEADER,
+ SMB_PARSE_SMB_HEADER,
+ SMB_PARSE_GET_WORDCOUNT,
+ SMB_PARSE_WORDCOUNT,
+ SMB_PARSE_GET_BYTECOUNT,
+ SMB_PARSE_BYTECOUNT,
+ /* must be last */
+ SMB_FIELD_MAX,
+};
+
+/**
+ * \brief SMB Write AndX Request Parsing
+ */
+/* For WriteAndX we need to get writeandxdataoffset */
+static uint32_t SMBParseWriteAndX(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ switch (sstate->andx.andxbytesprocessed) {
+ case 0:
+ sstate->andx.paddingparsed = 0;
+ if (input_len >= 28) {
+ sstate->andx.andxcommand = *p;
+ sstate->andx.andxoffset = *(p + 2);
+ sstate->andx.andxoffset |= *(p + 3) << 8;
+ sstate->andx.datalengthhigh = *(p + 18);
+ sstate->andx.datalengthhigh |= *(p + 19) << 8;
+ sstate->andx.datalength = *(p + 20);
+ sstate->andx.datalength |= *(p + 21) << 8;
+ sstate->andx.dataoffset = *(p + 22);
+ sstate->andx.dataoffset |= *(p + 23) << 8;
+ sstate->andx.dataoffset |= (uint64_t) *(p + 24) << 56;
+ sstate->andx.dataoffset |= (uint64_t) *(p + 25) << 48;
+ sstate->andx.dataoffset |= (uint64_t) *(p + 26) << 40;
+ sstate->andx.dataoffset |= (uint64_t) *(p + 27) << 32;
+ sstate->bytesprocessed += 28;
+ SCReturnUInt(28U);
+ } else {
+ sstate->andx.andxcommand = *(p++);
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ p++; // Reserved
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ sstate->andx.andxoffset = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ sstate->andx.andxoffset |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ // SMB_COM_WRITE_ANDX Fid 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ // SMB_COM_WRITE_ANDX Fid 2
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ // SMB_COM_WRITE_ANDX Offset 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ // SMB_COM_WRITE_ANDX Offset 2
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ // SMB_COM_WRITE_ANDX Offset 3
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ // SMB_COM_WRITE_ANDX Offset 4
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ // SMB_COM_WRITE_ANDX Reserved 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ // SMB_COM_WRITE_ANDX Reserved 2
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ // SMB_COM_WRITE_ANDX Reserved 3
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ // SMB_COM_WRITE_ANDX Reserved 4
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ // SMB_COM_WRITE_ANDX WriteMode 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ // SMB_COM_WRITE_ANDX WriteMode 2
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ // SMB_COM_WRITE_ANDX BytesRemaining 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ // SMB_COM_WRITE_ANDX BytesRemaining 2
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ // DataLengthHigh 1
+ sstate->andx.datalengthhigh = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ // DataLengthHigh 2
+ sstate->andx.datalengthhigh |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ // DataLength 1
+ sstate->andx.datalength = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ // DataLength 2
+ sstate->andx.datalength |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ sstate->andx.dataoffset = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ sstate->andx.dataoffset |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ sstate->andx.dataoffset |= (uint64_t) *(p++) << 56;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ sstate->andx.dataoffset |= (uint64_t) *(p++) << 48;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ sstate->andx.dataoffset |= (uint64_t) *(p++) << 40;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ sstate->andx.dataoffset |= (uint64_t) *(p++) << 32;
+ --input_len;
+ break;
+ /* fall through */
+ default:
+ sstate->bytesprocessed++;
+ SCReturnUInt(1);
+ break;
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief SMB Read AndX Response Parsing
+ */
+static uint32_t SMBParseReadAndX(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ switch (sstate->andx.andxbytesprocessed) {
+ case 0:
+ sstate->andx.paddingparsed = 0;
+ if (input_len >= 24) {
+ sstate->andx.andxcommand = *p;
+ sstate->andx.andxoffset = *(p + 2);
+ sstate->andx.andxoffset |= *(p + 3) << 8;
+ sstate->andx.datalength = *(p + 10);
+ sstate->andx.datalength |= *(p + 11) << 8;
+ sstate->andx.dataoffset = *(p + 12);
+ sstate->andx.dataoffset |= *(p + 13) << 8;
+ sstate->andx.datalength |= (uint64_t) *(p + 14) << 32;
+ sstate->andx.datalength |= (uint64_t) *(p + 15) << 40;
+ sstate->andx.datalength |= (uint64_t) *(p + 16) << 48;
+ sstate->andx.datalength |= (uint64_t) *(p + 17) << 56;
+ sstate->bytesprocessed += 24;
+ SCReturnUInt(24U);
+ } else {
+ sstate->andx.andxcommand = *(p++);
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ p++; // Reserved
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ sstate->andx.andxoffset |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ sstate->andx.andxoffset |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ // SMB_COM_READ_ANDX Remaining Reserved must be 0xff
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ // SMB_COM_READ_ANDX Remaining Reserved must be 0xff
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ // SMB_COM_READ_ANDX DataCompactionMode 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ // SMB_COM_READ_ANDX DataCompactionMode 1
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ // SMB_COM_READ_ANDX Reserved
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ // SMB_COM_READ_ANDX Reserved
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ sstate->andx.datalength = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ sstate->andx.datalength |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ sstate->andx.dataoffset = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ sstate->andx.dataoffset |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ sstate->andx.datalength |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ sstate->andx.datalength |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ // SMB_COM_READ_ANDX Reserved
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ // SMB_COM_READ_ANDX Reserved
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ // SMB_COM_READ_ANDX Reserved
+ p++;
+ --input_len;
+ break;
+ default:
+ sstate->bytesprocessed++;
+ SCReturnUInt(1);
+ break;
+
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t SMBParseTransact(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ switch (sstate->andx.andxbytesprocessed) {
+ case 0:
+ sstate->andx.paddingparsed = 0;
+ if (input_len >= sstate->wordcount.wordcount) {
+ sstate->andx.datalength = *(p + 22);
+ sstate->andx.datalength |= *(p + 23) << 8;
+ sstate->andx.dataoffset = *(p + 24);
+ sstate->andx.dataoffset |= *(p + 25) << 8;
+ sstate->andx.datalength |= (uint64_t) *(p + 14) << 56;
+ sstate->andx.datalength |= (uint64_t) *(p + 15) << 48;
+ sstate->andx.datalength |= (uint64_t) *(p + 16) << 40;
+ sstate->andx.datalength |= (uint64_t) *(p + 17) << 32;
+ sstate->bytesprocessed += sstate->wordcount.wordcount;
+ sstate->andx.andxbytesprocessed += sstate->wordcount.wordcount;
+ SCReturnUInt(sstate->wordcount.wordcount);
+ } else {
+ /* total parameter count 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ /* total parameter count 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ /* total data count 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ /* total data count 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 4:
+ /* max parameter count 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 5:
+ /* max parameter count 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ /* max data count 1 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ /* max data count 2 */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ /* max setup count */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ /* Reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ /* Flags */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ /* Flags */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ /* Timeout */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ /* Timeout */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ /* Timeout */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ /* Timeout */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ /* Reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ /* Reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ /* Parameter Count */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ /* Parameter Count */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ /* Parameter Offset */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ /* Parameter Offset */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ /* Data Count */
+ sstate->andx.datalength = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ /* Data Count */
+ sstate->andx.datalength |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ /* Data Offset */
+ sstate->andx.dataoffset = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ /* Data Offset */
+ sstate->andx.dataoffset |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ /* Setup Count */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ /* Reserved */
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 28:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 29:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 30:
+ p++;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 31:
+ p++;
+ --input_len;
+ break;
+ default:
+ SCLogDebug("SMB_COM_TRANSACTION AndX bytes processed is greater than 31 %u", sstate->andx.andxbytesprocessed);
+ sstate->bytesprocessed++;
+ sstate->andx.andxbytesprocessed++;
+ SCReturnUInt(1);
+ break;
+ }
+ sstate->bytesprocessed += (p - input);
+ sstate->andx.andxbytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * Handle variable length padding for WriteAndX and ReadAndX
+ */
+static uint32_t PaddingParser(void *smb_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ /* Check for validity of dataoffset */
+ if ((uint64_t)(sstate->bytesprocessed - NBSS_HDR_LEN) > sstate->andx.dataoffset) {
+ sstate->andx.paddingparsed = 1;
+ SCReturnUInt((uint32_t)(p - input));
+ }
+ while (((uint64_t)(sstate->bytesprocessed - NBSS_HDR_LEN) + (p - input))
+ < sstate->andx.dataoffset && sstate->bytecount.bytecountleft--
+ && input_len--) {
+ SCLogDebug("0x%02x ", *p);
+ p++;
+ }
+ if (((uint64_t)(sstate->bytesprocessed - NBSS_HDR_LEN) + (p - input))
+ == sstate->andx.dataoffset) {
+ sstate->andx.paddingparsed = 1;
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief Parse WriteAndX and ReadAndX Data
+ * \retval -1 f DCERPCParser does not validate
+ * \retval Number of bytes processed
+ */
+static int32_t DataParser(void *smb_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ int32_t parsed = 0;
+
+ if (sstate->andx.paddingparsed) {
+ parsed = DCERPCParser(&sstate->dcerpc, input, input_len);
+ if (parsed == -1 || parsed > sstate->bytecount.bytecountleft || parsed > (int32_t)input_len) {
+ SCReturnInt(-1);
+ } else {
+ sstate->dcerpc_present = 1;
+ sstate->bytesprocessed += parsed;
+ sstate->bytecount.bytecountleft -= parsed;
+ input_len -= parsed;
+ }
+ }
+ SCReturnInt(parsed);
+}
+
+/**
+ * \brief Obtain SMB WordCount which is 2 times the value.
+ * Reset bytecount.bytecountbytes to 0.
+ * Determine if this is an SMB AndX Command
+ */
+static uint32_t SMBGetWordCount(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ if (input_len > 0) {
+ SMBState *sstate = (SMBState *) smb_state;
+ sstate->wordcount.wordcount = *(input) * 2;
+ sstate->wordcount.wordcountleft = sstate->wordcount.wordcount;
+ sstate->bytesprocessed++;
+ sstate->bytecount.bytecountbytes = 0;
+ sstate->andx.isandx = isAndX(sstate);
+ SCLogDebug("Wordcount (%u):", sstate->wordcount.wordcount);
+ SCReturnUInt(1U);
+ }
+
+ SCReturnUInt(0);
+}
+
+/*
+ * Obtain SMB Bytecount. Handle the corner obfuscation case where a packet boundary
+ * is after the first bytecount byte.
+ */
+
+static uint32_t SMBGetByteCount(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ if (input_len && sstate->bytesprocessed == NBSS_HDR_LEN + SMB_HDR_LEN + 1
+ + sstate->wordcount.wordcount) {
+ sstate->bytecount.bytecount = *(p++);
+ sstate->bytesprocessed++;
+ --input_len;
+ }
+
+ if (input_len && sstate->bytesprocessed == NBSS_HDR_LEN + SMB_HDR_LEN + 2
+ + sstate->wordcount.wordcount) {
+ sstate->bytecount.bytecount |= *(p++) << 8;
+ sstate->bytecount.bytecountleft = sstate->bytecount.bytecount;
+ sstate->bytesprocessed++;
+ SCLogDebug("Bytecount %u", sstate->bytecount.bytecount);
+ --input_len;
+ }
+
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief SMBParseWordCount parses the SMB Wordcount portion of the SMB Transaction.
+ * until sstate->wordcount.wordcount bytes are parsed.
+ */
+static uint32_t SMBParseWordCount(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+ uint32_t retval = 0;
+
+ if ((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) && sstate->smb.command
+ == SMB_COM_READ_ANDX) {
+ retval = SMBParseReadAndX(f, sstate, pstate, input, input_len);
+ if (retval <= sstate->wordcount.wordcountleft) {
+ sstate->wordcount.wordcountleft -= retval;
+ SCLogDebug("SMB_COM_READ_ANDX returned %d - %u bytes at offset %"PRIu64"", retval, sstate->andx.datalength, sstate->andx.dataoffset);
+ SCReturnUInt(retval);
+ } else {
+ SCReturnUInt(0U);
+ }
+
+ } else if (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) == 0)
+ && sstate->smb.command == SMB_COM_WRITE_ANDX) {
+ retval = SMBParseWriteAndX(f, sstate, pstate, input, input_len);
+ if (retval <= sstate->wordcount.wordcountleft) {
+ sstate->wordcount.wordcountleft -= retval;
+ SCLogDebug("SMB_COM_WRITE_ANDX returned %d - %u bytes at offset %"PRIu64"", retval, sstate->andx.datalength, sstate->andx.dataoffset);
+ SCReturnUInt(retval);
+ } else {
+ SCReturnUInt(0U);
+ }
+
+ } else if (sstate->smb.command == SMB_COM_TRANSACTION) {
+ retval = SMBParseTransact(f, sstate, pstate, input, input_len);
+ if (retval <= sstate->wordcount.wordcountleft) {
+ sstate->wordcount.wordcountleft -= retval;
+ SCLogDebug("SMB_COM_TRANSACTION returned %d - %u bytes at offset %"PRIu64"", retval, sstate->andx.datalength, sstate->andx.dataoffset);
+ SCReturnUInt(retval);
+ } else {
+ SCReturnUInt(0U);
+ }
+
+ } else { /* Generic WordCount Handler */
+ while (sstate->wordcount.wordcountleft-- && input_len--) {
+ SCLogDebug("0x%02x wordcount %u/%u input_len %u", *p,
+ sstate->wordcount.wordcountleft,
+ sstate->wordcount.wordcount, input_len);
+ p++;
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+ }
+}
+
+/**
+ * \brief SMBParseByteCount parses the SMB ByteCount portion of the SMB Transaction.
+ * until sstate->bytecount.bytecount bytes are parsed.
+ */
+static uint32_t SMBParseByteCount(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+ uint32_t ures = 0; /* unsigned */
+ int32_t sres = 0; /* signed */
+ uint32_t parsed = 0;
+
+ if (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) &&
+ sstate->smb.command == SMB_COM_READ_ANDX) ||
+ (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) == 0)
+ && sstate->smb.command == SMB_COM_WRITE_ANDX) ||
+ (sstate->smb.command == SMB_COM_TRANSACTION))
+ {
+ if (sstate->andx.paddingparsed == 0) {
+ ures = PaddingParser(sstate, pstate, input + parsed, input_len);
+ if (ures <= input_len) {
+ parsed += ures;
+ input_len -= ures;
+ } else {
+ SCReturnUInt(0U);
+ }
+ }
+
+ if (sstate->andx.datalength && input_len) {
+ /* Uncomment the next line to help debug DCERPC over SMB */
+ //hexdump(f, input + parsed, input_len);
+ sres = DataParser(sstate, pstate, input + parsed, input_len);
+ if (sres != -1 && sres <= (int32_t)input_len) {
+ parsed += (uint32_t)sres;
+ input_len -= (uint32_t)sres;
+ } else { /* Did not Validate as DCERPC over SMB */
+ while (sstate->bytecount.bytecountleft-- && input_len--) {
+ SCLogDebug("0x%02x bytecount %"PRIu16"/%"PRIu16" input_len %"PRIu32, *p,
+ sstate->bytecount.bytecountleft,
+ sstate->bytecount.bytecount, input_len);
+ p++;
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((p - input));
+ }
+ }
+ SCReturnUInt(ures);
+ }
+
+ while (sstate->bytecount.bytecountleft-- && input_len--) {
+ SCLogDebug("0x%02x bytecount %u/%u input_len %u", *p,
+ sstate->bytecount.bytecountleft,
+ sstate->bytecount.bytecount, input_len);
+ p++;
+ }
+ sstate->bytesprocessed += (p - input);
+
+ SCReturnUInt((p - input));
+}
+
+/**
+ * \brief Parse a NBSS header.
+ *
+ * \retval 4 parsing of the header is done
+ * \retval 3 parsing partially done
+ * \retval 2 parsing partially done
+ * \retval 1 parsing partially done
+ * \retval 0 no input or already done
+ */
+static uint32_t NBSSParseHeader(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ if (input_len > 0 && sstate->bytesprocessed < (NBSS_HDR_LEN - 1)) {
+ switch (sstate->bytesprocessed) {
+ case 0:
+ /* Initialize */
+ sstate->andx.andxcommand = SMB_NO_SECONDARY_ANDX_COMMAND;
+ sstate->andx.maxchainedandx = 5;
+
+ /* fast track for having all bytes (common case) */
+ if (input_len >= NBSS_HDR_LEN) {
+ sstate->nbss.type = *p;
+ sstate->nbss.length = (*(p + 1) & 0x01) << 16;
+ sstate->nbss.length |= *(p + 2) << 8;
+ sstate->nbss.length |= *(p + 3);
+ sstate->bytesprocessed += NBSS_HDR_LEN;
+ SCReturnUInt(4U);
+ } else {
+ sstate->nbss.type = *(p++);
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ sstate->nbss.length = (*(p++) & 0x01) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ sstate->nbss.length |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ sstate->nbss.length |= *(p++);
+ --input_len;
+ break;
+ }
+ sstate->bytesprocessed += (p - input);
+ }
+
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+/**
+ * \brief parse and validate the 32 byte SMB Header
+ *
+ * \retval 32 parsing done
+ * \retval >0<32 parsing in progress
+ * \retval 0 no input or already fully parsed
+ * \retval -1 error
+ */
+static int SMBParseHeader(Flow *f, void *smb_state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint8_t *p = input;
+
+ if (input_len > 0) {
+ switch (sstate->bytesprocessed) {
+ case 4:
+ // fallthrough
+ /* above statement to prevent coverity FPs from the switch
+ * fall through */
+ if (input_len >= SMB_HDR_LEN) {
+ if (SCMemcmp(p, "\xff\x53\x4d\x42", 4) != 0) {
+ SCLogDebug("SMB Header did not validate");
+ SCReturnInt(-1);
+ }
+ sstate->smb.command = *(p + 4);
+ sstate->smb.status = *(p + 5) << 24;
+ sstate->smb.status |= *(p + 6) << 16;
+ sstate->smb.status |= *(p + 7) << 8;
+ sstate->smb.status |= *(p + 8);
+ sstate->smb.flags = *(p + 9);
+ sstate->smb.flags2 = *(p + 10) << 8;
+ sstate->smb.flags2 |= *(p + 11);
+ sstate->smb.pidhigh = *(p + 12) << 8;
+ sstate->smb.pidhigh |= *(p + 13);
+ sstate->smb.securitysignature = (uint64_t) *(p + 14) << 56;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 15) << 48;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 16) << 40;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 17) << 32;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 18) << 24;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 19) << 16;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 20) << 8;
+ sstate->smb.securitysignature |= (uint64_t) *(p + 21);
+ sstate->smb.tid = *(p + 24) << 8;
+ sstate->smb.tid |= *(p + 25);
+ sstate->smb.pid = *(p + 26) << 8;
+ sstate->smb.pid |= *(p + 27);
+ sstate->smb.uid = *(p + 28) << 8;
+ sstate->smb.uid |= *(p + 29);
+ sstate->smb.mid = *(p + 30) << 8;
+ sstate->smb.mid |= *(p + 31);
+ sstate->bytesprocessed += SMB_HDR_LEN;
+ SCReturnInt(32);
+ break;
+ } else {
+ if (*(p++) != 0xff) {
+ SCLogDebug("SMB Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* We fall through to the next case if we still have input.
+ * Same applies for other cases as well */
+ }
+ /* fall through */
+ case 5:
+ if (*(p++) != 'S') {
+ SCLogDebug("SMB Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ if (*(p++) != 'M') {
+ SCLogDebug("SMB Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ if (*(p++) != 'B') {
+ SCLogDebug("SMB Header did not validate");
+ SCReturnInt(-1);
+ }
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ sstate->smb.command = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ sstate->smb.status = *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ sstate->smb.status |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ sstate->smb.status |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ sstate->smb.status |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ sstate->smb.flags = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ sstate->smb.flags2 = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ sstate->smb.flags2 |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ sstate->smb.pidhigh = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ sstate->smb.pidhigh |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ sstate->smb.securitysignature = (uint64_t) *(p++) << 56;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 48;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 40;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 32;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ sstate->smb.securitysignature |= (uint64_t) *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ sstate->smb.securitysignature |= (uint64_t) *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ p++; // UNUSED
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ p++; // UNUSED
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 28:
+ sstate->smb.tid = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 29:
+ sstate->smb.tid |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 30:
+ sstate->smb.pid = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 31:
+ sstate->smb.pid |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 32:
+ sstate->smb.uid = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 33:
+ sstate->smb.uid |= *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 34:
+ sstate->smb.mid = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 35:
+ sstate->smb.mid |= *(p++);
+ --input_len;
+ break;
+ /* fall through */
+ }
+ }
+ sstate->bytesprocessed += (p - input);
+
+ SCReturnInt((p - input));
+}
+
+static int SMBParse(Flow *f, void *smb_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data, uint8_t dir)
+{
+ SCEnter();
+
+ SMBState *sstate = (SMBState *) smb_state;
+ uint64_t retval = 0;
+ uint64_t parsed = 0;
+ int hdrretval = 0;
+ int counter = 0;
+
+ if (pstate == NULL) {
+ SCLogDebug("pstate == NULL");
+ SCReturnInt(0);
+ }
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ }
+
+ if (sstate->bytesprocessed != 0 && sstate->data_needed_for_dir != dir) {
+ SCReturnInt(-1);
+ }
+
+ while (input_len) {
+ /* till we clear corner cases */
+ if (counter++ == 30) {
+ SCLogDebug("Somehow seem to be stuck inside the smb "
+ "parser for quite sometime. Let's get out of here.");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+
+ while (input_len && sstate->bytesprocessed < NBSS_HDR_LEN) {
+ retval = NBSSParseHeader(f, smb_state, pstate, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ SCLogDebug("[1] NBSS Header (%u/%u) Type 0x%02x Length 0x%04x "
+ "parsed %"PRIu64" input_len %u",
+ sstate->bytesprocessed, NBSS_HDR_LEN, sstate->nbss.type,
+ sstate->nbss.length, parsed, input_len);
+ } else if (input_len) {
+ SCLogDebug("Error parsing NBSS Header");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+ }
+
+ switch (sstate->nbss.type) {
+ case NBSS_SESSION_MESSAGE:
+ while (input_len &&
+ (sstate->bytesprocessed >= NBSS_HDR_LEN &&
+ sstate->bytesprocessed < NBSS_HDR_LEN + SMB_HDR_LEN)) {
+ /* inside while */
+ hdrretval = SMBParseHeader(f, smb_state, pstate, input + parsed,
+ input_len);
+ if (hdrretval == -1 || hdrretval > (int32_t)input_len) {
+ SCLogDebug("Error parsing SMB Header");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ } else {
+ parsed += hdrretval;
+ input_len -= hdrretval;
+ SCLogDebug("[2] SMB Header (%u/%u) Command 0x%02x "
+ "parsed %"PRIu64" input_len %u",
+ sstate->bytesprocessed, NBSS_HDR_LEN + SMB_HDR_LEN,
+ sstate->smb.command, parsed, input_len);
+ }
+ } /* while */
+
+ do {
+ if (input_len &&
+ (sstate->bytesprocessed == NBSS_HDR_LEN + SMB_HDR_LEN)) {
+ /* inside if */
+ retval = SMBGetWordCount(f, smb_state, pstate, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing SMB Word Count");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+ SCLogDebug("[3] WordCount (%u/%u) WordCount %u parsed "
+ "%"PRIu64" input_len %u",
+ sstate->bytesprocessed,
+ NBSS_HDR_LEN + SMB_HDR_LEN + 1,
+ sstate->wordcount.wordcount,
+ parsed, input_len);
+ } /* if (input_len && ..) */
+
+ while (input_len &&
+ (sstate->bytesprocessed >= NBSS_HDR_LEN + SMB_HDR_LEN + 1 &&
+ sstate->bytesprocessed < (NBSS_HDR_LEN + SMB_HDR_LEN + 1 +
+ sstate->wordcount.wordcount))) {
+ /* inside while */
+ retval = SMBParseWordCount(f, smb_state, pstate,
+ input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing SMB Word Count Data retval "
+ "%"PRIu64" input_len %u", retval, input_len);
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+ SCLogDebug("[4] Parsing WordCount (%u/%u) WordCount %u "
+ "parsed %"PRIu64" input_len %u",
+ sstate->bytesprocessed,
+ NBSS_HDR_LEN + SMB_HDR_LEN + 1 +
+ sstate->wordcount.wordcount,
+ sstate->wordcount.wordcount,
+ parsed, input_len);
+ } /* while (input_len && ..) */
+
+ while (input_len &&
+ (sstate->bytesprocessed >= (NBSS_HDR_LEN + SMB_HDR_LEN +
+ 1 + sstate->wordcount.wordcount) &&
+ sstate->bytesprocessed < (NBSS_HDR_LEN + SMB_HDR_LEN + 3
+ + sstate->wordcount.wordcount))) {
+ /* inside while */
+ retval = SMBGetByteCount(f, smb_state, pstate, input + parsed,
+ input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing SMB Byte Count");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+ SCLogDebug("[5] ByteCount (%u/%u) ByteCount %u parsed "
+ "%"PRIu64" input_len %u",
+ sstate->bytesprocessed,
+ NBSS_HDR_LEN + SMB_HDR_LEN + 3,
+ sstate->bytecount.bytecount,
+ parsed, input_len);
+
+ if (sstate->bytecount.bytecount == 0) {
+ sstate->bytesprocessed = 0;
+ input_len = 0;
+ }
+ } /* while (input_len && ..) */
+
+ while (input_len &&
+ (sstate->bytesprocessed >= (NBSS_HDR_LEN + SMB_HDR_LEN +
+ 3 + sstate->wordcount.wordcount)) &&
+ (sstate->bytesprocessed < (NBSS_HDR_LEN + SMB_HDR_LEN + 3
+ + sstate->wordcount.wordcount
+ + sstate->bytecount.bytecount))) {
+ /* inside while */
+ retval = SMBParseByteCount(f, smb_state, pstate,
+ input + parsed, input_len);
+ if (retval && retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else if (input_len) {
+ SCLogDebug("Error parsing SMB Byte Count Data");
+ sstate->bytesprocessed = 0;
+ SCReturnInt(0);
+ }
+ SCLogDebug("[6] Parsing ByteCount (%u/%u) ByteCount %u "
+ "parsed %"PRIu64" input_len %u",
+ sstate->bytesprocessed,
+ NBSS_HDR_LEN + SMB_HDR_LEN + 1 +
+ sstate->wordcount.wordcount + 2 +
+ sstate->bytecount.bytecount,
+ sstate->bytecount.bytecount, parsed, input_len);
+ } /* while (input_len && ..) */
+
+ } while (sstate->andx.andxcommand != SMB_NO_SECONDARY_ANDX_COMMAND &&
+ input_len && sstate->andx.maxchainedandx--);
+
+ if (sstate->bytesprocessed >= sstate->nbss.length + NBSS_HDR_LEN ||
+ sstate->andx.maxchainedandx == 0) {
+ /* inside if */
+ sstate->bytesprocessed = 0;
+ sstate->transaction_id++;
+ input_len = 0;
+ }
+ break;
+
+ case NBSS_SESSION_REQUEST:
+ case NBSS_POSITIVE_SESSION_RESPONSE:
+ case NBSS_NEGATIVE_SESSION_RESPONSE:
+ case NBSS_RETARGET_SESSION_RESPONSE:
+ case NBSS_SESSION_KEEP_ALIVE:
+ if (sstate->bytesprocessed < (sstate->nbss.length + NBSS_HDR_LEN)) {
+ if (input_len >= (sstate->nbss.length + NBSS_HDR_LEN -
+ sstate->bytesprocessed)) {
+ /* inside if */
+ input_len -= (sstate->nbss.length + NBSS_HDR_LEN -
+ sstate->bytesprocessed);
+ parsed += (sstate->nbss.length + NBSS_HDR_LEN -
+ sstate->bytesprocessed);
+ sstate->bytesprocessed = 0;
+ } else {
+ sstate->bytesprocessed += input_len;
+ input_len = 0;
+ }
+ } else {
+ sstate->bytesprocessed = 0;
+ }
+ break;
+
+ default:
+ sstate->bytesprocessed = 0;
+ break;
+ } /* switch */
+
+ } /* while (input_len) */
+
+ sstate->data_needed_for_dir = dir;
+ SCReturnInt(1);
+}
+
+static int SMBParseRequest(Flow *f, void *smb_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return SMBParse(f, smb_state, pstate, input, input_len, local_data, 0);
+}
+
+static int SMBParseResponse(Flow *f, void *smb_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return SMBParse(f, smb_state, pstate, input, input_len, local_data, 1);
+}
+
+
+/**
+ * \brief determines if the SMB command is an ANDX command
+ * \retval 1 if smb command is an AndX command
+ * \retval 0 if smb command is not an AndX command
+ */
+
+int isAndX(SMBState *smb_state)
+{
+ SCEnter();
+
+ switch (smb_state->smb.command) {
+ case SMB_NO_SECONDARY_ANDX_COMMAND:
+ case SMB_COM_LOCKING_ANDX:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ smb_state->andx.andxbytesprocessed = 0;
+ SCReturnInt(1);
+ default:
+ SCReturnInt(0);
+ }
+}
+
+/** \internal
+ * \brief Allocate a SMBState
+ * \retval s State, or NULL in case of error
+ */
+static void *SMBStateAlloc(void)
+{
+ SCEnter();
+
+ void *s = SCMalloc(sizeof(SMBState));
+ if (unlikely(s == NULL)) {
+ SCReturnPtr(NULL, "void");
+ }
+
+ memset(s, 0, sizeof(SMBState));
+
+ SCReturnPtr(s, "void");
+}
+
+/** \internal
+ * \brief Free a SMBState
+ */
+static void SMBStateFree(void *s)
+{
+ SCEnter();
+ SMBState *sstate = (SMBState *) s;
+
+ DCERPCUuidEntry *item;
+
+ while ((item = TAILQ_FIRST(&sstate->dcerpc.dcerpcbindbindack.uuid_list))) {
+ //printUUID("Free", item);
+ TAILQ_REMOVE(&sstate->dcerpc.dcerpcbindbindack.uuid_list, item, next);
+ SCFree(item);
+ }
+ if (sstate->dcerpc.dcerpcrequest.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcrequest.stub_data_buffer);
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcrequest.stub_data_buffer_len = 0;
+ }
+ if (sstate->dcerpc.dcerpcresponse.stub_data_buffer != NULL) {
+ SCFree(sstate->dcerpc.dcerpcresponse.stub_data_buffer);
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer = NULL;
+ sstate->dcerpc.dcerpcresponse.stub_data_buffer_len = 0;
+ }
+
+ SCFree(s);
+ SCReturn;
+}
+
+#define SMB_PROBING_PARSER_MIN_DEPTH 8
+
+static uint16_t SMBProbingParser(uint8_t *input, uint32_t ilen, uint32_t *offset)
+{
+ int32_t len;
+ int32_t input_len = ilen;
+
+ while (input_len >= SMB_PROBING_PARSER_MIN_DEPTH) {
+ switch (input[0]) {
+ case NBSS_SESSION_MESSAGE:
+ if (input[4] == 0xFF && input[5] == 'S' && input[6] == 'M' &&
+ input[7] == 'B') {
+ return ALPROTO_SMB;
+ }
+
+ /* fall through */
+ case NBSS_SESSION_REQUEST:
+ case NBSS_POSITIVE_SESSION_RESPONSE:
+ case NBSS_NEGATIVE_SESSION_RESPONSE:
+ case NBSS_RETARGET_SESSION_RESPONSE:
+ case NBSS_SESSION_KEEP_ALIVE:
+ len = (input[1] & 0x01) << 16;
+ len |= input[2] << 8;
+ len |= input[3];
+ break;
+ default:
+ /* -1 indicates a stream where the probing parser would be
+ * unable to find nbss, even if it exists. This should
+ * prevent the probing parser from beig invoked henceforth */
+ return ALPROTO_FAILED;
+ }
+
+ input_len -= 4;
+ if (len >= input_len) {
+ return ALPROTO_UNKNOWN;
+ }
+
+ input_len -= len;
+ input += 4 + len;
+ }
+
+ return ALPROTO_UNKNOWN;
+}
+
+static int SMBRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
+ "|ff|SMB", 8, 4, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2,
+ "|fe|SMB", 8, 4, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+void RegisterSMBParsers(void)
+{
+ char *proto_name = "smb";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_SMB, proto_name);
+ if (SMBRegisterPatternsForProtocolDetection() < 0)
+ return;
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "139",
+ ALPROTO_SMB,
+ SMB_PROBING_PARSER_MIN_DEPTH, 0,
+ STREAM_TOSERVER,
+ SMBProbingParser);
+ } else {
+ AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
+ proto_name, ALPROTO_SMB,
+ SMB_PROBING_PARSER_MIN_DEPTH, 0,
+ SMBProbingParser);
+ }
+
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_SMB, STREAM_TOSERVER);
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMB, STREAM_TOSERVER, SMBParseRequest);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMB, STREAM_TOCLIENT, SMBParseResponse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SMB, SMBStateAlloc, SMBStateFree);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SMB, SMBParserRegisterTests);
+#endif
+ return;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+/**
+ * \test SMBParserTest01 tests the NBSS and SMB header decoding
+ */
+int SMBParserTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf[] = "\x00\x00\x00\x85" // NBSS
+ "\xff\x53\x4d\x42\x72\x00\x00\x00" // SMB
+ "\x00\x18\x53\xc8\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\xff\xfe\x00\x00\x00\x00"
+ "\x00" // WordCount
+ "\x62\x00" // ByteCount
+ "\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20"
+ "\x31\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x57\x69\x6e\x64\x6f\x77\x73"
+ "\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00\x02\x4c"
+ "\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54"
+ "\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00";
+
+ uint32_t smblen = sizeof(smbbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER|STREAM_EOF, smbbuf, smblen);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->nbss.type != NBSS_SESSION_MESSAGE) {
+ printf("expected nbss type 0x%02x , got 0x%02x : ", NBSS_SESSION_MESSAGE, smb_state->nbss.type);
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 133) {
+ printf("expected nbss length 0x%02x , got 0x%02x : ", 133, smb_state->nbss.length);
+ goto end;
+ }
+
+ if (smb_state->smb.command != SMB_COM_NEGOTIATE) {
+ printf("expected SMB command 0x%02x , got 0x%02x : ", SMB_COM_NEGOTIATE, smb_state->smb.command);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test SMBParserTest02 tests the NBSS, SMB, and DCERPC over SMB header decoding
+ */
+int SMBParserTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf[] = {
+ 0x00, 0x00, 0x00, 0x92, 0xff, 0x53, 0x4d, 0x42,
+ 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x05,
+ 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x48,
+ 0x00, 0x00, 0x04, 0xe0, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x4a, 0x00, 0x48, 0x00, 0x4a, 0x00, 0x02,
+ 0x00, 0x26, 0x00, 0x00, 0x40, 0x4f, 0x00, 0x5c,
+ 0x50, 0x49, 0x50, 0x45, 0x5c, 0x00, 0x05, 0x00,
+ 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x16,
+ 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0xfd,
+ 0x2c, 0x34, 0x6c, 0x3c, 0xce, 0x11, 0xa8, 0x93,
+ 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c,
+ 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10,
+ 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 };
+
+ uint32_t smblen = sizeof(smbbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER|STREAM_EOF, smbbuf, smblen);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->nbss.type != NBSS_SESSION_MESSAGE) {
+ printf("expected nbss type 0x%02x , got 0x%02x : ", NBSS_SESSION_MESSAGE, smb_state->nbss.type);
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 146) {
+ printf("expected nbss length 0x%02x , got 0x%02x : ", 146, smb_state->nbss.length);
+ goto end;
+ }
+
+ if (smb_state->smb.command != SMB_COM_TRANSACTION) {
+ printf("expected SMB command 0x%02x , got 0x%02x : ", SMB_COM_TRANSACTION, smb_state->smb.command);
+ goto end;
+ }
+
+ printUUID("BIND", smb_state->dcerpc.dcerpcbindbindack.uuid_entry);
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+int SMBParserTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ 0x00, 0x00, 0x07, 0x57, 0xff, 0x53, 0x4d, 0x42,
+ 0x2f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7f, 0x13,
+ 0x01, 0x08, 0xc9, 0x29, 0x0e, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x55, 0x01, 0x00, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0x08, 0x00, 0x0e, 0x00, 0x00,
+ 0x00, 0x0e, 0x00, 0x49, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x07, 0xcc, 0x1b, 0x19, 0xb8, 0x75,
+ 0x2c, 0x85, 0x52, 0x39, 0x72, 0xfa, 0x9c, 0x5f,
+ 0x5a, 0xb7, 0x59, 0xa1, 0x83, 0xba, 0x87, 0xd3,
+ 0xc3, 0xbf, 0xf4, 0x5d, 0x08, 0x32, 0x22, 0x33,
+ 0x2e, 0x62, 0x46, 0x4d, 0x03, 0x48, 0x1f, 0xea,
+ 0x7c, 0x65, 0x3e, 0x71, 0xf8, 0xea, 0x20, 0x85,
+ 0x29, 0x6f, 0x3c, 0xf2, 0x19, 0xb5, 0x65, 0xb0,
+ 0xce, 0x06, 0xcc, 0x90, 0x86, 0x20, 0x77, 0xf5,
+ 0xa0, 0xbc, 0x45, 0x9d, 0x4e, 0x92, 0xb4, 0x24,
+ 0xc8, 0x58, 0x4a, 0xc3, 0x4e, 0xb8, 0x95, 0x8d,
+ 0x93, 0x0c, 0xce, 0xe0, 0xf9, 0x7d, 0x7e, 0xd3,
+ 0x46, 0x53, 0x32, 0x95, 0x7d, 0x22, 0x76, 0x0e,
+ 0x95, 0x23, 0x2e, 0xa6, 0x58, 0x1a, 0xb6, 0x74,
+ 0x54, 0x4f, 0x37, 0x5c, 0x60, 0x00, 0xb4, 0x55,
+ 0x5b, 0xda, 0xea, 0x2c, 0xf3, 0x9b, 0x91, 0x6f,
+ 0xa8, 0x20, 0xd3, 0x40, 0x0c, 0x7c, 0xc7, 0x85,
+ 0x8c, 0x44, 0x76, 0xbc, 0x22, 0x9d, 0xfd, 0x8e,
+ 0x21, 0x46, 0x05, 0x41, 0x73, 0x0c, 0x88, 0x62,
+ 0xdc, 0x62, 0xc1, 0xc8, 0x14, 0xbb, 0x96, 0x60,
+ 0x77, 0x6c, 0x5c, 0x31, 0x2a, 0xaa, 0x87, 0x69,
+ 0x99, 0xaa, 0x83, 0x5e, 0x71, 0x11, 0x2a, 0x85,
+ 0xca, 0x5d, 0xe1, 0x67, 0x4f, 0xa2, 0x3e, 0x4e,
+ 0x94, 0xe7, 0xa3, 0xe6, 0xa0, 0xdb, 0xc2, 0x05,
+ 0x01, 0x4f, 0xf5, 0xe9, 0xfc, 0xa2, 0x2a, 0x1c,
+ 0x63, 0x21, 0xd5, 0x27, 0x98, 0x86, 0x9c, 0x66,
+ 0x5e, 0xf1, 0x97, 0xb0, 0x86, 0x58, 0x5b, 0x94,
+ 0x51, 0xfd, 0xb9, 0x83, 0x4c, 0xc4, 0x0f, 0x5f,
+ 0xdd, 0xc8, 0xce, 0x43, 0xed, 0xe8, 0xae, 0xbc,
+ 0x52, 0x73, 0xf6, 0x0f, 0x0d, 0xb4, 0xd6, 0xa7,
+ 0xcf, 0xef, 0x0e, 0x72, 0x34, 0xff, 0x2b, 0x50,
+ 0x71, 0x2a, 0x98, 0xf0, 0x60, 0x58, 0xde, 0x1d,
+ 0x96, 0x50, 0xd8, 0xec, 0xeb, 0x40, 0xcb, 0x4c,
+ 0x3b, 0x2c, 0xee, 0x76, 0xd6, 0x97, 0x1c, 0x69,
+ 0x61, 0x89, 0xc1, 0x9b, 0x03, 0xda, 0x08, 0x0b,
+ 0x15, 0xba, 0xd3, 0x3d, 0x8c, 0xea, 0xf7, 0x17,
+ 0xc3, 0x77, 0xf8, 0x04, 0xca, 0x72, 0xed, 0xfe,
+ 0xd0, 0x02, 0x73, 0x1b, 0x71, 0x72, 0x17, 0x9f,
+ 0x14, 0x96, 0xe2, 0x5f, 0xae, 0x5b, 0x7d, 0x7f,
+ 0xc9, 0x72, 0x9f, 0xd5, 0x32, 0xf4, 0xf3, 0x39,
+ 0x89, 0x36, 0x00, 0x44, 0xa9, 0x18, 0x21, 0x4b,
+ 0x26, 0xf2, 0x5a, 0x2a, 0x80, 0xea, 0x6b, 0x3e,
+ 0x68, 0x27, 0xd0, 0xa0, 0x84, 0x81, 0xb5, 0xa6,
+ 0x3b, 0xd5, 0xdc, 0xdd, 0xd1, 0xd4, 0x5b, 0xad,
+ 0x80, 0x91, 0xf2, 0x30, 0x5e, 0x90, 0x17, 0x35,
+ 0x59, 0xad, 0x34, 0x65, 0x54, 0x04, 0x5a, 0x3c,
+ 0xe4, 0x68, 0xa7, 0x30, 0x06, 0x7a, 0x85, 0xe7,
+ 0xf4, 0x20, 0xe3, 0xd7, 0xa5, 0x8b, 0x60, 0xfe,
+ 0x51, 0xad, 0xda, 0xe2, 0xd1, 0x4f, 0xfb, 0x94,
+ 0xc9, 0xba, 0xa4, 0x09, 0x5c, 0xde, 0x78, 0xdc,
+ 0x78, 0x36, 0x96, 0x8b, 0xd6, 0x72, 0xc4, 0xa7,
+ 0x1c, 0xde, 0x45, 0x85, 0xdf, 0x84, 0xb1, 0x3f,
+ 0x2b, 0x3f, 0xfe, 0x56, 0x80, 0x8d, 0x26, 0x4a,
+ 0x39, 0x22, 0x1f, 0x10, 0x89, 0x2e, 0x4e, 0x87,
+ 0xf5, 0x9c, 0x0e, 0xd9, 0xdd, 0xb2, 0xc9, 0x9c,
+ 0x3f, 0xc5, 0xe3, 0xab, 0xdc, 0x85, 0x1c, 0xf9,
+ 0xda, 0xbb, 0x36, 0x9b, 0xe7, 0x21, 0x58, 0x44,
+ 0xee, 0xb3, 0xe7, 0x37, 0xd3, 0xc3, 0x76, 0x09,
+ 0x79, 0xe2, 0xf4, 0xf1, 0x27, 0x6b, 0x74, 0xc4,
+ 0x5f, 0x06, 0x76, 0x78, 0x56, 0xb9, 0x80, 0x7f,
+ 0x63, 0x53, 0xa2, 0xd1, 0xfc, 0xfb, 0x69, 0x38,
+ 0x0c, 0x13, 0x6e, 0x9e, 0xea, 0x79, 0xc9, 0x6d,
+ 0x45, 0x6b, 0xa3, 0xa8, 0x20, 0x21, 0x24, 0xff,
+ 0x0d, 0x8d, 0xd9, 0x0a, 0x9e, 0xf4, 0x3f, 0xf5,
+ 0x18, 0x39, 0xdd, 0x9f, 0xed, 0xd6, 0x2b, 0xb1,
+ 0x4b, 0x3f, 0x24, 0x7e, 0x11, 0x79, 0x37, 0x01,
+ 0x10, 0xe7, 0x34, 0x1d, 0x36, 0x5f, 0x26, 0x99,
+ 0x5a, 0x4d, 0xe9, 0x1a, 0x89, 0x24, 0xf8, 0xea,
+ 0xca, 0x16, 0x19, 0x6c, 0x3b, 0x8e, 0x44, 0x70,
+ 0x20, 0x5f, 0x46, 0x3c, 0x60, 0xbe, 0x03, 0xfc,
+ 0x99, 0x29, 0xd7, 0x30, 0x5e, 0xbe, 0x5b, 0x17,
+ 0x4f, 0xfe, 0x3f, 0xe0, 0x50, 0xa0, 0x1b, 0x1a,
+ 0x6b, 0x17, 0xf3, 0xf9, 0x01, 0xe8, 0xc6, 0xc8,
+ 0x0f, 0x81, 0xbd, 0x2d, 0xc5, 0x8c, 0xa1, 0xab,
+ 0x9d, 0x13, 0xce, 0x73, 0x14, 0x56, 0x56, 0xb4,
+ 0x68, 0xac, 0x35, 0xf8, 0x6a, 0x55, 0x3e, 0x50,
+ 0x34, 0x5a, 0x66, 0x17, 0x98, 0x4d, 0xd1, 0xa7,
+ 0xdf, 0x57, 0xd6, 0xd4, 0x44, 0x64, 0xa7, 0x74,
+ 0x18, 0x0a, 0x4f, 0xa9, 0xe4, 0xb4, 0x0f, 0x89,
+ 0xa2, 0xc5, 0xb8, 0xa7, 0x20, 0xa2, 0xb1, 0xf8,
+ 0x70, 0xaf, 0xee, 0x6e, 0x62, 0xa5, 0x89, 0x5d,
+ 0xc9, 0x8a, 0xb9, 0x87, 0xac, 0x4d, 0x4d, 0x81,
+ 0x1c, 0x62, 0xd3, 0xbf, 0x83, 0x79, 0x98, 0x81,
+ 0xbd, 0xcc, 0x1f, 0x76, 0xc8, 0x7e, 0x2c, 0xec,
+ 0xdb, 0xa7, 0xa5, 0xea, 0x05, 0x94, 0x3f, 0xef,
+ 0x66, 0x1c, 0x5d, 0xc4, 0xbd, 0x73, 0x53, 0x1f,
+ 0xf3, 0xac, 0x1f, 0xa4, 0xb9, 0x78, 0x1b, 0x93,
+ 0xcb, 0x17, 0xb6, 0xda, 0xbb, 0x45, 0x21, 0xfa,
+ 0x52, 0xc7, 0x71, 0x05, 0xb3, 0xeb, 0x82, 0x09,
+ 0x99, 0x90, 0x5d, 0xa9, 0x76, 0xd1, 0x63, 0x6a,
+ 0x14, 0x99, 0xe9, 0xa5, 0x98, 0x5d, 0xe0, 0xb5,
+ 0x2a, 0xd1, 0xf1, 0x2e, 0xe7, 0x85, 0xdb, 0x42,
+ 0xfc, 0x61, 0x09, 0x14, 0xe5, 0x8e, 0x92, 0x70,
+ 0x91, 0x15, 0x74, 0x2c, 0x16, 0x30, 0xc4, 0xb0,
+ 0xf1, 0x61, 0xd5, 0x55, 0xa8, 0xa3, 0xca, 0x88,
+ 0xe6, 0xb1, 0x58, 0x76, 0xa5, 0x4c, 0x48, 0xe3,
+ 0xdd, 0x7a, 0x5e, 0x0a, 0x86, 0xfd, 0xd6, 0xe8,
+ 0xc0, 0x47, 0x27, 0x1a, 0x58, 0x92, 0xad, 0xa6,
+ 0x51, 0x32, 0x4d, 0x0d, 0x29, 0xd3, 0xcf, 0xf1,
+ 0xcc, 0x29, 0x1a, 0xfe, 0xf6, 0xa0, 0xf3, 0xdd,
+ 0x98, 0x73, 0xcb, 0xbb, 0x8a, 0xe9, 0x55, 0xba,
+ 0x89, 0x2d, 0x31, 0x9b, 0x3d, 0x04, 0x1f, 0xb5,
+ 0x1c, 0x84, 0x63, 0xca, 0xde, 0x75, 0xac, 0x91,
+ 0x78, 0x1f, 0x8b, 0x37, 0x8d, 0x46, 0xaa, 0x79,
+ 0x51, 0xbf, 0x30, 0xfa, 0x3d, 0x9b, 0xd9, 0x20,
+ 0x25, 0x18, 0x46, 0xb6, 0xe7, 0x8e, 0xf7, 0x5e,
+ 0x7d, 0xf8, 0xd3, 0x01, 0x39, 0xe5, 0x9d, 0x46,
+ 0x6b, 0x8c, 0xcf, 0x9d, 0xc6, 0xb9, 0xe8, 0xd8,
+ 0x25, 0x2d, 0x96, 0x07, 0xc7, 0x4e, 0xa3, 0x3a,
+ 0x9a, 0xbc, 0x9d, 0x80, 0xa6, 0x5d, 0xb1, 0xc0,
+ 0x3e, 0x81, 0xe0, 0x52, 0x8f, 0x9a, 0x1a, 0xc2,
+ 0xdb, 0x9f, 0x91, 0x85, 0x56, 0xdb, 0xb8, 0x69,
+ 0x10, 0x35, 0xe4, 0xc4, 0xaf, 0xb6, 0x13, 0xf8,
+ 0x86, 0xe1, 0x2d, 0x3c, 0xf8, 0x94, 0x60, 0xb7,
+ 0xa1, 0xde, 0x25, 0x51, 0x7d, 0xff, 0xff, 0xa6,
+ 0x23, 0x68, 0x28, 0x1f, 0x79, 0x33, 0x60, 0x86,
+ 0xe9, 0x2c, 0x3a, 0xb9, 0x3c, 0x70, 0xb3, 0xe0,
+ 0x4c, 0x8c, 0x7e, 0x06, 0xdf, 0x4d, 0xf6, 0x88,
+ 0xda, 0x9e, 0x4f, 0x5b, 0xd2, 0x2e, 0x28, 0xb8,
+ 0xe0, 0x27, 0x7a, 0x43, 0xfb, 0x23, 0x4b, 0x8a,
+ 0xd9, 0x4f, 0x29, 0x53, 0x5d, 0x75, 0xc6, 0xfc };
+ uint8_t smbbuf2[] = {
+ 0x0a, 0x30, 0xe0, 0x74, 0x3c, 0x23, 0xc3, 0x11,
+ 0x95, 0x25, 0x04, 0xe4, 0x2d, 0x7b, 0x29, 0xa1,
+ 0x75, 0x69, 0x3f, 0x49, 0x9c, 0xfa, 0x66, 0x78,
+ 0x3c, 0xf1, 0xab, 0xee, 0xab, 0x9a, 0x75, 0x63,
+ 0x54, 0x80, 0x2b, 0x5c, 0x07, 0xf7, 0xec, 0x72,
+ 0xfb, 0xd0, 0x52, 0x5e, 0x7e, 0x99, 0xf5, 0x3b,
+ 0xc4, 0x77, 0x96, 0x12, 0xb8, 0x36, 0xb2, 0xcf,
+ 0xab, 0xf5, 0xd3, 0xf3, 0x19, 0x77, 0xbb, 0x03,
+ 0xdb, 0xf7, 0x4d, 0x81, 0xe3, 0xe8, 0x6c, 0x23,
+ 0x02, 0xe0, 0xcf, 0x24, 0xc1, 0xd5, 0x3d, 0x42,
+ 0xa4, 0xbc, 0x97, 0xf4, 0x83, 0xee, 0xff, 0x85,
+ 0x2c, 0xfd, 0xdd, 0xdc, 0x23, 0x1c, 0x87, 0x0c,
+ 0xe4, 0xd5, 0xfc, 0xc3, 0x8b, 0x10, 0xa5, 0x42,
+ 0x0f, 0x14, 0xd1, 0x89, 0xa6, 0xaf, 0xaa, 0x77,
+ 0xfc, 0x3b, 0xce, 0x6c, 0xbe, 0x62, 0xc9, 0xdd,
+ 0x16, 0xc6, 0x14, 0xc2, 0xa6, 0x13, 0x12, 0xfa,
+ 0x5a, 0x8b, 0x05, 0x88, 0x06, 0xf9, 0xef, 0x9c,
+ 0xce, 0xf7, 0x27, 0x46, 0x1d, 0x50, 0xe2, 0xeb,
+ 0x49, 0xb2, 0xb1, 0x7c, 0x6b, 0xaf, 0xe9, 0xc7,
+ 0xdd, 0x59, 0x8c, 0xda, 0x32, 0x55, 0xb5, 0xfe,
+ 0xdc, 0xe0, 0x47, 0xf4, 0xa0, 0xe7, 0xaa, 0x47,
+ 0x49, 0xdf, 0xcf, 0x9c, 0xd6, 0xfa, 0xd2, 0xca,
+ 0x55, 0xa7, 0x3f, 0x62, 0x14, 0x6c, 0xc8, 0x7f,
+ 0xad, 0x7c, 0xb1, 0x70, 0x88, 0xb3, 0x51, 0x13,
+ 0x2c, 0x3b, 0x78, 0x1d, 0xa2, 0x5e, 0xf7, 0x83,
+ 0x62, 0x6a, 0x51, 0xbd, 0xe9, 0x77, 0x62, 0xc6,
+ 0x06, 0x06, 0x51, 0x9d, 0x03, 0x95, 0x51, 0x7c,
+ 0xd3, 0x73, 0x50, 0x9b, 0x36, 0x5a, 0x28, 0x52,
+ 0xc0, 0x05, 0xee, 0xd5, 0x2d, 0xd5, 0x77, 0x52,
+ 0xab, 0x7c, 0x4a, 0x4c, 0x7e, 0xf6, 0xba, 0x52,
+ 0xc5, 0x4d, 0xb5, 0x74, 0x83, 0x77, 0x5f, 0xaa,
+ 0xba, 0x86, 0x94, 0xd2, 0x19, 0xca, 0xef, 0xc9,
+ 0x6e, 0x5b, 0x50, 0xee, 0x2c, 0xdd, 0x67, 0xc8,
+ 0xfd, 0xc3, 0xa4, 0x80, 0x63, 0x1d, 0xa2, 0x07,
+ 0x1e, 0x1a, 0x9d, 0x70, 0xe4, 0xab, 0x34, 0x7a,
+ 0xfb, 0x08, 0x82, 0x85, 0xec, 0x2d, 0x25, 0x3e,
+ 0x70, 0x22, 0x6e, 0x9d, 0x0f, 0xed, 0x60, 0x8f,
+ 0xc5, 0x06, 0x66, 0x42, 0x95, 0xcc, 0x77, 0xbe,
+ 0x4d, 0x19, 0x7c, 0xd1, 0x31, 0x26, 0xfb, 0x52,
+ 0xad, 0xbd, 0x19, 0x1d, 0x68, 0x56, 0x2c, 0xb9,
+ 0x5b, 0xaa, 0x92, 0x48, 0xcf, 0xdf, 0x65, 0x2d,
+ 0xdb, 0x87, 0x06, 0xbe, 0x51, 0x61, 0x6b, 0xf6,
+ 0x87, 0xdc, 0xbb, 0xa5, 0x48, 0x81, 0xaf, 0xd7,
+ 0xfc, 0x15, 0xf7, 0x41, 0xde, 0xe3, 0xe9, 0xd4,
+ 0xad, 0x5d, 0x64, 0x8f, 0x13, 0x68, 0xe5, 0x2b,
+ 0x4d, 0x87, 0x59, 0x7e, 0xcb, 0x2b, 0xbf, 0xbc,
+ 0xaa, 0xd2, 0xc7, 0x60, 0xef, 0xe1, 0x25, 0xe2,
+ 0x89, 0xb4, 0x78, 0x24, 0x52, 0xb4, 0x54, 0xe3,
+ 0xf0, 0xe5, 0x81, 0xba, 0xe3, 0x00, 0x62, 0x09,
+ 0x8a, 0x19, 0x7b, 0x9b, 0x0f, 0x50, 0x91, 0xa7,
+ 0x80, 0xdb, 0x0e, 0x68, 0xe1, 0x22, 0x54, 0x89,
+ 0x07, 0xc7, 0x39, 0x38, 0xca, 0xae, 0xbf, 0x5b,
+ 0xbb, 0xe4, 0x70, 0x28, 0xc5, 0x18, 0x98, 0xea };
+ uint8_t smbbuf3[] = {
+ 0x39, 0x99, 0x97, 0x1f, 0xf1, 0x6a, 0x72, 0x0d,
+ 0x35, 0xd5, 0x33, 0x42, 0x5a, 0x9f, 0xea, 0x0f,
+ 0x6f, 0x3b, 0xc7, 0xb9, 0xd3, 0x04, 0xdf, 0x44,
+ 0x45, 0xc7, 0xc6, 0x06, 0x0b, 0x77, 0x8e, 0x8e,
+ 0x9a, 0x3c, 0xa4, 0x15, 0x85, 0x80, 0xce, 0xd0,
+ 0x8c, 0x54, 0x60, 0xf9, 0x1f, 0xb3, 0x3e, 0xed,
+ 0x21, 0x3e, 0xfa, 0x30, 0xf4, 0x50, 0x2b, 0x00,
+ 0x00, 0xea, 0xd1, 0xb3, 0xd2, 0x7e, 0x6c, 0x14,
+ 0xe5, 0xf0, 0xf4, 0x9c, 0xb4, 0x2e, 0x32, 0x41,
+ 0x20, 0x2a, 0x18, 0x78, 0x1a, 0xed, 0x04, 0x94,
+ 0x83, 0xd1, 0x87, 0x39, 0xf6, 0xcb, 0xf4, 0xc1,
+ 0xc7, 0xe0, 0x50, 0x87, 0x65, 0x4f, 0x36, 0x73,
+ 0x70, 0xf5, 0x0a, 0xaa, 0x2b, 0x28, 0xad, 0x05,
+ 0x28, 0x8d, 0x3b, 0x42, 0xfb, 0xe2, 0xd3, 0xb8,
+ 0x82, 0x71, 0x25, 0xcd, 0xa2, 0xf2, 0x4b, 0x62,
+ 0xeb, 0x14, 0x3b, 0x81, 0xaf, 0xd4, 0x68, 0x5a,
+ 0xae, 0x8e, 0x10, 0x9a, 0x17, 0x4c, 0xf1, 0x3d,
+ 0x43, 0xb9, 0xd2, 0xd5, 0x86, 0xee, 0x3a, 0xf3,
+ 0xe5, 0x41, 0xe5, 0x52, 0xda, 0x61, 0xf3, 0x20,
+ 0x30, 0x5b, 0xe5, 0x1f, 0xe2, 0x4e, 0x9d, 0xd6,
+ 0xd6, 0x2e, 0x2a, 0x63, 0xbc, 0xf6, 0xb9, 0xc2,
+ 0xec, 0xd0, 0xe9, 0xfd, 0x07, 0xfb, 0x2d, 0x8e,
+ 0xbc, 0x43, 0xcb, 0x7e, 0x55, 0x63, 0x9f, 0xb6,
+ 0xf8, 0x8b, 0x4c, 0xcd, 0x4b, 0x28, 0x47, 0x56,
+ 0xc9, 0xd2, 0xfe, 0x0e, 0x63, 0x11, 0x09, 0xd9,
+ 0xd9, 0x97, 0x0a, 0x5a, 0x21, 0xad, 0xdb, 0x53,
+ 0x24, 0xee, 0x62, 0x4a, 0xaa, 0x49, 0x14, 0xdf,
+ 0xc0, 0x61, 0x85, 0x11, 0x57, 0x6e, 0x3b, 0x8c,
+ 0x37, 0x24, 0x13, 0xde, 0xc7, 0xf3, 0x44, 0x54,
+ 0x8a, 0x69, 0x78, 0x0c, 0xf3, 0xd1, 0xcd, 0xc5,
+ 0xad, 0x45, 0xc6, 0x06, 0x56, 0x0b, 0x53, 0x40,
+ 0x79, 0x12, 0x90, 0x6b, 0xdf, 0xc5, 0x80, 0xde,
+ 0x9c, 0x8e, 0xe1, 0x73, 0xdc, 0x92, 0xc2, 0xf1,
+ 0xeb, 0xd9, 0x66, 0x0a, 0x12, 0xd2, 0x3f, 0x04,
+ 0x03, 0xaa, 0x6f, 0xd0, 0x90, 0xfa, 0xb0, 0x6b,
+ 0x7d, 0xfc, 0x76, 0xf9, 0xe3, 0xa2, 0x17, 0x28,
+ 0x4e, 0x9d, 0x2d, 0xa6, 0x7e, 0xfa, 0x19, 0x91,
+ 0xeb, 0xe5, 0xe4, 0xca, 0x09, 0x77, 0xfe, 0xc0,
+ 0x1c, 0xaa, 0xc4, 0x7c, 0xc2, 0x6a, 0x0e, 0xf3,
+ 0x4e, 0x79, 0x9b, 0x82, 0x2a, 0x4b, 0xd3, 0x35,
+ 0x1d, 0x92, 0x6c, 0x3f, 0x85, 0x57, 0x5a, 0x16,
+ 0xa1, 0x0d, 0xc7, 0x64, 0xb8, 0x46, 0x73, 0xbf,
+ 0x91, 0x5f, 0x10, 0x2a, 0x2b, 0x51, 0x49, 0xe1,
+ 0xea, 0xda, 0x2f, 0x41, 0x7b, 0x96, 0xa3, 0xd2,
+ 0x7b, 0x72, 0xc0, 0x88, 0x84, 0xcb, 0xe0, 0xb7,
+ 0xae, 0x74, 0xc9, 0x78, 0x82, 0x47, 0xf3, 0x19,
+ 0x21, 0x53, 0xe6, 0xe1, 0x67, 0xbb, 0x39, 0x05,
+ 0x6e, 0x1c, 0x38, 0x33, 0x10, 0x60, 0x24, 0x48,
+ 0xb2, 0x7a, 0xb9, 0x4e, 0x8d, 0x36, 0xcf, 0xce,
+ 0xf6, 0x31, 0x3b, 0xa3, 0x18, 0x78, 0x49, 0x91,
+ 0xef, 0xed, 0x86, 0x2c, 0x98, 0x00, 0x18, 0x49,
+ 0x73, 0xb8, 0xe5, 0x2f, 0xc1, 0x58, 0xe0, 0x47,
+ 0x2b, 0x16, 0x41, 0xc3, 0x41, 0x05, 0x00, 0x0b,
+ 0x03, 0x10, 0x00, 0x00, 0x00, 0xb0, 0x02, 0x00,
+ 0x00, 0x00, 0x00 };
+
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint32_t smblen2 = sizeof(smbbuf2);
+ uint32_t smblen3 = sizeof(smbbuf3);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER|STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->smb.command != SMB_COM_WRITE_ANDX) {
+ printf("expected SMB command 0x%02x , got 0x%02x : ", SMB_COM_WRITE_ANDX, smb_state->smb.command);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf2, smblen2);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf3, smblen3);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ printUUID("BIND", smb_state->dcerpc.dcerpcbindbindack.uuid_entry);
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+int SMBParserTest04(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ 0x00, 0x00, 0x00, 0x88, 0xff, 0x53, 0x4d, 0x42,
+ 0x2f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x07, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7c, 0x05,
+ 0x00, 0x08, 0x00, 0x00, 0x0e, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00,
+ 0x00, 0x48, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x00, 0xab, 0x05, 0x00, 0x0b, 0x03,
+ 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xd0, 0x16, 0xd0, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x78, 0x56, 0x34, 0x12,
+ 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23,
+ 0x45, 0x67, 0x89, 0xab, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00 };
+ uint8_t smbbuf2[] = {
+ 0x00, 0x00, 0x00, 0x2f, 0xff, 0x53, 0x4d, 0x42,
+ 0x2f, 0x00, 0x00, 0x00, 0x00, 0x98, 0x07, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7c, 0x05,
+ 0x00, 0x08, 0x00, 0x00, 0x06, 0xff, 0x00, 0x2f,
+ 0x00, 0x48, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00 };
+ uint8_t smbbuf3[] = {
+ 0x00, 0x00, 0x00, 0x3b, 0xff, 0x53, 0x4d, 0x42,
+ 0x2e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7c, 0x05,
+ 0x00, 0x08, 0x00, 0x00, 0x0c, 0xff, 0x00, 0xde,
+ 0xde, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ uint8_t smbbuf4[] = {
+ 0x00, 0x00, 0x00, 0x80, 0xff, 0x53, 0x4d, 0x42,
+ 0x2e, 0x00, 0x00, 0x00, 0x00, 0x98, 0x03, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7c, 0x05,
+ 0x00, 0x08, 0x00, 0x00, 0x0c, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44,
+ 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00,
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x5d, 0xe0, 0x00, 0x00,
+ 0x0e, 0x00, 0x5c, 0x70, 0x69, 0x70, 0x65, 0x5c,
+ 0x73, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x73, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00 };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint32_t smblen2 = sizeof(smbbuf2);
+ uint32_t smblen3 = sizeof(smbbuf3);
+ uint32_t smblen4 = sizeof(smbbuf4);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER|STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->smb.command != SMB_COM_WRITE_ANDX) {
+ printf("expected SMB command 0x%02x , got 0x%02x : ", SMB_COM_WRITE_ANDX, smb_state->smb.command);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf2, smblen2);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf3, smblen3);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf4, smblen4);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+int SMBParserTest05(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t smbbuf1[] = {
+ /* session request */
+ 0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46,
+ 0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45,
+ 0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46,
+ 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint8_t smbbuf2[] = {
+ /* session request */
+ 0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46,
+ 0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45,
+ 0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46,
+ 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00,
+ /* session message */
+ 0x00, 0x00, 0x00, 0x60, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2d,
+ 0x00, 0x00, 0xdd, 0xca, 0x00, 0x3d, 0x00, 0x02,
+ 0x4d, 0x45, 0x54, 0x41, 0x53, 0x50, 0x4c, 0x4f,
+ 0x49, 0x54, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d,
+ 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4c,
+ 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30, 0x32,
+ 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x41, 0x4e,
+ 0x4d, 0x41, 0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00,
+ 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30,
+ 0x2e, 0x31, 0x32, 0x00
+ };
+ uint32_t smblen2 = sizeof(smbbuf2);
+
+ int result = 0;
+ AppProto alproto;
+ Flow f;
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+ memset(&f, 0, sizeof(f));
+ f.dp = 139;
+
+ /** SMB */
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, "|ff|SMB", 8, 4, STREAM_TOCLIENT);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, "|ff|SMB", 8, 4, STREAM_TOSERVER);
+
+ /** SMB2 */
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, "|fe|SMB", 8, 4, STREAM_TOCLIENT);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, "|fe|SMB", 8, 4, STREAM_TOSERVER);
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "139",
+ ALPROTO_SMB,
+ SMB_PROBING_PARSER_MIN_DEPTH, 0,
+ STREAM_TOSERVER,
+ SMBProbingParser);
+
+ AppLayerProtoDetectPrepareState();
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ alproto = AppLayerProtoDetectGetProto(alpd_tctx,
+ &f,
+ smbbuf1, smblen1,
+ IPPROTO_TCP, STREAM_TOSERVER);
+ if (alproto != ALPROTO_UNKNOWN) {
+ printf("alproto is %"PRIu16 ". Should be ALPROTO_UNKNOWN\n",
+ alproto);
+ goto end;
+ }
+
+ alproto = AppLayerProtoDetectGetProto(alpd_tctx,
+ &f,
+ smbbuf2, smblen2,
+ IPPROTO_TCP, STREAM_TOSERVER);
+ if (alproto != ALPROTO_SMB) {
+ printf("alproto is %"PRIu16 ". Should be ALPROTO_SMB\n",
+ alproto);
+ goto end;
+ }
+
+ result = 1;
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ return result;
+}
+
+int SMBParserTest06(void)
+{
+ AppLayerProtoDetectUnittestCtxBackup();
+ AppLayerProtoDetectSetup();
+
+ uint8_t smbbuf1[] = {
+ /* session request */
+ 0x83, 0x00, 0x00, 0x01, 0x82
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint8_t smbbuf2[] = {
+ /* session request */
+ 0x83, 0x00, 0x00, 0x01, 0x82,
+ /* session message */
+ 0x00, 0x00, 0x00, 0x55, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03,
+ 0x0a, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfd, 0xe3, 0x00, 0x80, 0xb8, 0xcb, 0x22, 0x5f,
+ 0xfd, 0xeb, 0xc3, 0x01, 0x68, 0x01, 0x00, 0x10,
+ 0x00, 0x50, 0xb5, 0xc3, 0x62, 0x59, 0x02, 0xd1,
+ 0x4d, 0x99, 0x6d, 0x85, 0x7d, 0xfa, 0x93, 0x2d,
+ 0xbb
+ };
+ uint32_t smblen2 = sizeof(smbbuf2);
+
+ int result = 0;
+ AppProto alproto;
+ Flow f;
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+ memset(&f, 0, sizeof(f));
+ f.dp = 139;
+
+ /** SMB */
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, "|ff|SMB", 8, 4, STREAM_TOCLIENT);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, "|ff|SMB", 8, 4, STREAM_TOSERVER);
+
+ /** SMB2 */
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, "|fe|SMB", 8, 4, STREAM_TOCLIENT);
+ AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, "|fe|SMB", 8, 4, STREAM_TOSERVER);
+
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "139",
+ ALPROTO_SMB,
+ SMB_PROBING_PARSER_MIN_DEPTH, 0,
+ STREAM_TOSERVER,
+ SMBProbingParser);
+
+ AppLayerProtoDetectPrepareState();
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+
+ alproto = AppLayerProtoDetectGetProto(alpd_tctx,
+ &f,
+ smbbuf1, smblen1,
+ IPPROTO_TCP, STREAM_TOSERVER);
+ if (alproto != ALPROTO_UNKNOWN) {
+ printf("alproto is %"PRIu16 ". Should be ALPROTO_UNKNOWN\n",
+ alproto);
+ goto end;
+ }
+
+ alproto = AppLayerProtoDetectGetProto(alpd_tctx,
+ &f,
+ smbbuf2, smblen2,
+ IPPROTO_TCP, STREAM_TOSERVER);
+ if (alproto != ALPROTO_SMB) {
+ printf("alproto is %"PRIu16 ". Should be ALPROTO_SMB\n",
+ alproto);
+ goto end;
+ }
+
+ result = 1;
+ end:
+ AppLayerProtoDetectDeSetup();
+ AppLayerProtoDetectUnittestCtxRestore();
+ if (alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+ return result;
+}
+
+int SMBParserTest07(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ /* negative session response */
+ 0x83, 0x00, 0x00, 0x01, 0x82
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOCLIENT | STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->smb.command != 0) {
+ printf("we shouldn't have any smb state as yet\n");
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 1 ||
+ smb_state->nbss.type != NBSS_NEGATIVE_SESSION_RESPONSE) {
+ printf("something wrong with nbss parsing\n");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed != 0) {
+ printf("smb parser bytesprocessed should be 0, but it is not\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+int SMBParserTest08(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ /* positive session response */
+ 0x82, 0x00, 0x00, 0x00
+ };
+ uint8_t smbbuf2[] = {
+ /* negotiate protocol */
+ 0x00, 0x00, 0x00, 0x55, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03,
+ 0x0a, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfd, 0xe3, 0x00, 0x80, 0x40, 0x8a, 0x57, 0x5c,
+ 0xfd, 0xeb, 0xc3, 0x01, 0x68, 0x01, 0x00, 0x10,
+ 0x00, 0x50, 0xb5, 0xc3, 0x62, 0x59, 0x02, 0xd1,
+ 0x4d, 0x99, 0x6d, 0x85, 0x7d, 0xfa, 0x93, 0x2d,
+ 0xbb
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint32_t smblen2 = sizeof(smbbuf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOCLIENT | STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->smb.command != 0) {
+ printf("we shouldn't have any smb state as yet\n");
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 0 ||
+ smb_state->nbss.type != NBSS_POSITIVE_SESSION_RESPONSE) {
+ printf("something wrong with nbss parsing\n");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed != 0) {
+ printf("smb parser bytesprocessed should be 0, but it is not\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOCLIENT, smbbuf2, smblen2);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (smb_state->smb.command != SMB_COM_NEGOTIATE) {
+ printf("we should expect SMB command 0x%02x , got 0x%02x : ",
+ SMB_COM_NEGOTIATE, smb_state->smb.command);
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 85 ||
+ smb_state->nbss.type != NBSS_SESSION_MESSAGE) {
+ printf("something wrong with nbss parsing\n");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed != 0) {
+ printf("smb parser bytesprocessed should be 0, but it is not\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+int SMBParserTest09(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ /* session request */
+ 0x81, 0x00, 0x00, 0x44, 0x20, 0x45, 0x44, 0x45,
+ 0x4a, 0x46, 0x44, 0x45, 0x44, 0x45, 0x50, 0x43,
+ 0x4e, 0x46, 0x48, 0x44, 0x43, 0x45, 0x4c, 0x43,
+ 0x4e, 0x46, 0x43, 0x46, 0x45, 0x45, 0x4e, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45,
+ 0x44, 0x45, 0x4a, 0x46, 0x44, 0x45, 0x44, 0x45,
+ 0x50, 0x43, 0x4e, 0x46, 0x49, 0x46, 0x41, 0x43,
+ 0x4e, 0x46, 0x43, 0x46, 0x45, 0x45, 0x4e, 0x43,
+ 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00
+ };
+ uint8_t smbbuf2[] = {
+ /* session service - negotiate protocol */
+ 0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02,
+ 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
+ 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52,
+ 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02,
+ 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e,
+ 0x30, 0x00, 0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f,
+ 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57,
+ 0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61, 0x00, 0x02,
+ 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30,
+ 0x32, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41,
+ 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54,
+ 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32,
+ 0x00
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint32_t smblen2 = sizeof(smbbuf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER | STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->smb.command != 0) {
+ printf("we shouldn't have any smb state as yet\n");
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 68 ||
+ smb_state->nbss.type != NBSS_SESSION_REQUEST) {
+ printf("something wrong with nbss parsing\n");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed != 0) {
+ printf("smb parser bytesprocessed should be 0, but it is not\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER, smbbuf2, smblen2);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (smb_state->smb.command != SMB_COM_NEGOTIATE) {
+ printf("we should expect SMB command 0x%02x , got 0x%02x : ",
+ SMB_COM_NEGOTIATE, smb_state->smb.command);
+ goto end;
+ }
+
+ if (smb_state->nbss.length != 133 ||
+ smb_state->nbss.type != NBSS_SESSION_MESSAGE) {
+ printf("something wrong with nbss parsing\n");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed != 0) {
+ printf("smb parser bytesprocessed should be 0, but it is not\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test to temporarily to show the direction demaraction issue in the
+ * smb parser.
+ */
+int SMBParserTest10(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t smbbuf1[] = {
+ /* partial request */
+ 0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02,
+ 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
+ 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52,
+ 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02,
+ 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e,
+ 0x30, 0x00, 0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f,
+ 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57,
+ 0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61, 0x00, 0x02,
+ 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30,
+ 0x32, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41,
+ };
+ //0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54,
+ //0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32,
+ //0x00
+
+ uint8_t smbbuf2[] = {
+ /* response */
+ 0x00, 0x00, 0x00, 0x55, 0xff, 0x53, 0x4d, 0x42,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x53, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03,
+ 0x32, 0x00, 0x01, 0x00, 0x04, 0x41, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfd, 0xf3, 0x00, 0x80, 0x20, 0x03, 0x1a, 0x2d,
+ 0x77, 0x98, 0xc5, 0x01, 0xa4, 0x01, 0x00, 0x10,
+ 0x00, 0xb7, 0xeb, 0x0b, 0x05, 0x21, 0x22, 0x50,
+ 0x42, 0x8c, 0x38, 0x2a, 0x7f, 0xc5, 0x6a, 0x7c,
+ 0x0c
+ };
+ uint32_t smblen1 = sizeof(smbbuf1);
+ uint32_t smblen2 = sizeof(smbbuf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int r = 0;
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOSERVER | STREAM_START, smbbuf1, smblen1);
+ if (r != 0) {
+ printf("smb header check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMBState *smb_state = f.alstate;
+ if (smb_state == NULL) {
+ printf("no smb state: ");
+ goto end;
+ }
+
+ if (smb_state->bytesprocessed == 0) {
+ printf("request - smb parser bytesprocessed should not be 0.\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB, STREAM_TOCLIENT, smbbuf2, smblen2);
+ if (r == 0) {
+ printf("smb parser didn't return fail\n");
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#endif
+
+void SMBParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SMBParserTest01", SMBParserTest01, 1);
+ UtRegisterTest("SMBParserTest02", SMBParserTest02, 1);
+ UtRegisterTest("SMBParserTest03", SMBParserTest03, 1);
+ UtRegisterTest("SMBParserTest04", SMBParserTest04, 1);
+ UtRegisterTest("SMBParserTest05", SMBParserTest05, 1);
+ UtRegisterTest("SMBParserTest06", SMBParserTest06, 1);
+ UtRegisterTest("SMBParserTest07", SMBParserTest07, 1);
+ UtRegisterTest("SMBParserTest08", SMBParserTest08, 1);
+ UtRegisterTest("SMBParserTest09", SMBParserTest09, 1);
+ UtRegisterTest("SMBParserTest10", SMBParserTest10, 1);
+#endif
+}
+
diff --git a/framework/src/suricata/src/app-layer-smb.h b/framework/src/suricata/src/app-layer-smb.h
new file mode 100644
index 00000000..48d4fa84
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smb.h
@@ -0,0 +1,166 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+#ifndef __APP_LAYER_SMB_H__
+#define __APP_LAYER_SMB_H__
+
+#include "suricata-common.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "flow.h"
+#include "stream.h"
+#include "app-layer-nbss.h"
+#include "app-layer-dcerpc-common.h"
+
+typedef struct SMBHdr_ {
+ uint8_t protocol[4];
+ uint8_t command;
+ uint32_t status;
+ uint8_t flags;
+ uint16_t flags2;
+ uint16_t pidhigh;
+ uint64_t securitysignature;
+ uint16_t unused;
+ uint16_t tid;
+ uint16_t pid;
+ uint16_t uid;
+ uint16_t mid;
+} SMBHdr;
+
+#define SMB_HDR_LEN 32
+#define MINIMUM_SMB_LEN 35
+#define NBSS_SMB_HDRS_LEN 36
+
+typedef struct SMBWordCount_ {
+ uint8_t wordcount;
+ uint8_t wordcountleft;
+ uint8_t *words;
+} SMBWordCount;
+
+typedef struct SMBByteCount_ {
+ uint8_t bytecountbytes;
+ uint16_t bytecount;
+ uint16_t bytecountleft;
+ uint8_t *bytes;
+} SMBByteCount;
+
+typedef struct SMBAndX_ {
+ uint8_t isandx;
+ uint8_t paddingparsed;
+ uint8_t andxcommand;
+ uint8_t maxchainedandx;
+ uint16_t andxoffset;
+ uint16_t andxbytesprocessed;
+ uint16_t datalength;
+ uint16_t datalengthhigh;
+ uint64_t dataoffset;
+} SMBAndX;
+
+typedef struct SMBState_ {
+ NBSSHdr nbss;
+ uint16_t transaction_id;
+ uint16_t bytesprocessed;
+ SMBHdr smb;
+ SMBWordCount wordcount;
+ SMBByteCount bytecount;
+ SMBAndX andx;
+ DCERPC dcerpc;
+ uint8_t dcerpc_present;
+ uint8_t data_needed_for_dir;
+} SMBState;
+
+#define SMB_FLAGS_SERVER_TO_REDIR 0x80
+#define SMB_NO_SECONDARY_ANDX_COMMAND 0xff
+
+/* http://msdn.microsoft.com/en-us/library/dd327674.aspx */
+#define SMB_COM_CREATE_DIRECTORY 0x00
+#define SMB_COM_DELETE_DIRECTORY 0x01
+#define SMB_COM_OPEN 0x02
+#define SMB_COM_CREATE 0x03
+#define SMB_COM_CLOSE 0x04
+#define SMB_COM_FLUSH 0x05
+#define SMB_COM_DELETE 0x06
+#define SMB_COM_RENAME 0x07
+#define SMB_COM_QUERY_INFORMATION 0x08
+#define SMB_COM_SET_INFORMATION 0x09
+#define SMB_COM_READ 0x0A
+#define SMB_COM_WRITE 0x0B
+#define SMB_COM_LOCK_BYTE_RANGE 0x0C
+#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D
+#define SMB_COM_CREATE_TEMPORARY 0x0E
+#define SMB_COM_CREATE_NEW 0x0F
+#define SMB_COM_CHECK_DIRECTORY 0x10
+#define SMB_COM_PROCESS_EXIT 0x11
+#define SMB_COM_SEEK 0x12
+#define SMB_COM_LOCK_AND_READ 0x13
+#define SMB_COM_WRITE_AND_UNLOCK 0x14
+#define SMB_COM_READ_RAW 0x1A
+#define SMB_COM_READ_MPX 0x1B
+#define SMB_COM_READ_MPX_SECONDARY 0x1C
+#define SMB_COM_WRITE_RAW 0x1D
+#define SMB_COM_WRITE_MPX 0x1E
+#define SMB_COM_WRITE_COMPLETE 0x20
+#define SMB_COM_SET_INFORMATION2 0x22
+#define SMB_COM_QUERY_INFORMATION2 0x23
+#define SMB_COM_LOCKING_ANDX 0x24
+#define SMB_COM_TRANSACTION 0x25
+#define SMB_COM_TRANSACTION_SECONDARY 0x26
+#define SMB_COM_IOCTL 0x27
+#define SMB_COM_IOCTL_SECONDARY 0x28
+#define SMB_COM_COPY 0x29
+#define SMB_COM_MOVE 0x2A
+#define SMB_COM_ECHO 0x2B
+#define SMB_COM_WRITE_AND_CLOSE 0x2C
+#define SMB_COM_OPEN_ANDX 0x2D
+#define SMB_COM_READ_ANDX 0x2E
+#define SMB_COM_WRITE_ANDX 0x2F
+#define SMB_COM_CLOSE_AND_TREE_DISC 0x31
+#define SMB_COM_TRANSACTION2 0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2 0x34
+#define SMB_COM_FIND_NOTIFY_CLOSE 0x35
+#define SMB_COM_TREE_CONNECT 0x70
+#define SMB_COM_TREE_DISCONNECT 0x71
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SESSION_SETUP_ANDX 0x73
+#define SMB_COM_LOGOFF_ANDX 0x74
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_QUERY_INFORMATION_DISK 0x80
+#define SMB_COM_SEARCH 0x81
+#define SMB_COM_FIND 0x82
+#define SMB_COM_FIND_UNIQUE 0x83
+#define SMB_COM_NT_TRANSACT 0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
+#define SMB_COM_NT_CREATE_ANDX 0xA2
+#define SMB_COM_NT_CANCEL 0xA4
+#define SMB_COM_NT_RENAME 0xA5
+#define SMB_COM_OPEN_PRINT_FILE 0xC0
+#define SMB_COM_WRITE_PRINT_FILE 0xC1
+#define SMB_COM_CLOSE_PRINT_FILE 0xC2
+#define SMB_COM_GET_PRINT_QUEUE 0xC3
+
+void RegisterSMBParsers(void);
+void SMBParserRegisterTests(void);
+int isAndX(SMBState *smb_state);
+
+#endif /* __APP_LAYER_SMB_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-smb2.c b/framework/src/suricata/src/app-layer-smb2.c
new file mode 100644
index 00000000..f412e1df
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smb2.c
@@ -0,0 +1,690 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ *
+ * SMBv2 parser/decoder
+ */
+
+#include "suricata-common.h"
+
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-memcmp.h"
+
+#include "app-layer-smb2.h"
+
+enum {
+ SMB2_FIELD_NONE = 0,
+ SMB2_PARSE_NBSS_HEADER,
+ SMB2_PARSE_SMB_HEADER,
+
+ /* must be last */
+ SMB_FIELD_MAX,
+};
+
+static uint32_t NBSSParseHeader(void *smb2_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ SMB2State *sstate = (SMB2State *) smb2_state;
+ uint8_t *p = input;
+
+ if (input_len && sstate->bytesprocessed < NBSS_HDR_LEN - 1) {
+ switch (sstate->bytesprocessed) {
+ case 0:
+ /* Initialize */
+ if (input_len >= NBSS_HDR_LEN) {
+ sstate->nbss.type = *p;
+ sstate->nbss.length = (*(p + 1) & 0x01) << 16;
+ sstate->nbss.length |= *(p + 2) << 8;
+ sstate->nbss.length |= *(p + 3);
+ sstate->bytesprocessed += NBSS_HDR_LEN;
+ SCReturnUInt(4U);
+ } else {
+ sstate->nbss.type = *(p++);
+ if (!(--input_len))
+ break;
+ }
+ /* fall through */
+ case 1:
+ sstate->nbss.length = (*(p++) & 0x01) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 2:
+ sstate->nbss.length |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 3:
+ sstate->nbss.length |= *(p++);
+ --input_len;
+ break;
+ }
+ sstate->bytesprocessed += (p - input);
+ }
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static uint32_t SMB2ParseHeader(void *smb2_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+
+ SMB2State *sstate = (SMB2State *) smb2_state;
+ uint8_t *p = input;
+
+ if (input_len) {
+ switch (sstate->bytesprocessed) {
+ case 4:
+ // fallthrough
+ /* above statement to prevent coverity FPs from the switch
+ * fall through */
+ if (input_len >= SMB2_HDR_LEN) {
+ if (SCMemcmp(p, "\xfe\x53\x4d\x42", 4) != 0) {
+ //printf("SMB2 Header did not validate\n");
+ return 0;
+ }
+ sstate->smb2.StructureSize = *(p + 4);
+ sstate->smb2.StructureSize |= *(p + 5) << 8;
+ sstate->smb2.CreditCharge = *(p + 6);
+ sstate->smb2.CreditCharge |= *(p + 7) << 8;
+ sstate->smb2.Status = *(p + 8);
+ sstate->smb2.Status |= *(p + 9) << 8;
+ sstate->smb2.Status |= *(p + 10) << 16;
+ sstate->smb2.Status |= *(p + 11) << 24;
+ sstate->smb2.Command = *(p + 12);
+ sstate->smb2.Command |= *(p + 13) << 8;
+ sstate->smb2.CreditRequestResponse = *(p + 14);
+ sstate->smb2.CreditRequestResponse |= *(p + 15) << 8;
+ sstate->smb2.Flags = *(p + 16);
+ sstate->smb2.Flags |= *(p + 17) << 8;
+ sstate->smb2.Flags |= *(p + 18) << 16;
+ sstate->smb2.Flags |= *(p + 19) << 24;
+ sstate->smb2.NextCommand = *(p + 20);
+ sstate->smb2.NextCommand |= *(p + 21) << 8;
+ sstate->smb2.NextCommand |= *(p + 22) << 16;
+ sstate->smb2.NextCommand |= *(p + 23) << 24;
+ sstate->smb2.MessageId = *(p + 24);
+ sstate->smb2.MessageId |= *(p + 25) << 8;
+ sstate->smb2.MessageId |= *(p + 26) << 16;
+ sstate->smb2.MessageId |= (uint64_t) *(p + 27) << 24;
+ sstate->smb2.MessageId |= (uint64_t) *(p + 28) << 32;
+ sstate->smb2.MessageId |= (uint64_t) *(p + 29) << 40;
+ sstate->smb2.MessageId |= (uint64_t) *(p + 30) << 48;
+ sstate->smb2.MessageId |= (uint64_t) *(p + 31) << 56;
+ sstate->smb2.ProcessId = *(p + 32);
+ sstate->smb2.ProcessId |= *(p + 33) << 8;
+ sstate->smb2.ProcessId |= *(p + 34) << 16;
+ sstate->smb2.ProcessId |= *(p + 35) << 24;
+ sstate->smb2.TreeId = *(p + 36);
+ sstate->smb2.TreeId |= *(p + 37) << 8;
+ sstate->smb2.TreeId |= *(p + 38) << 16;
+ sstate->smb2.TreeId |= *(p + 39) << 24;
+ sstate->smb2.SessionId = *(p + 40);
+ sstate->smb2.SessionId |= *(p + 41) << 8;
+ sstate->smb2.SessionId |= *(p + 42) << 16;
+ sstate->smb2.SessionId |= (uint64_t) *(p + 43) << 24;
+ sstate->smb2.SessionId |= (uint64_t) *(p + 44) << 32;
+ sstate->smb2.SessionId |= (uint64_t) *(p + 45) << 40;
+ sstate->smb2.SessionId |= (uint64_t) *(p + 46) << 48;
+ sstate->smb2.SessionId |= (uint64_t) *(p + 47) << 56;
+ sstate->smb2.Signature[0] = *(p + 48);
+ sstate->smb2.Signature[1] = *(p + 49);
+ sstate->smb2.Signature[2] = *(p + 50);
+ sstate->smb2.Signature[3] = *(p + 51);
+ sstate->smb2.Signature[4] = *(p + 52);
+ sstate->smb2.Signature[5] = *(p + 53);
+ sstate->smb2.Signature[6] = *(p + 54);
+ sstate->smb2.Signature[7] = *(p + 55);
+ sstate->smb2.Signature[8] = *(p + 56);
+ sstate->smb2.Signature[9] = *(p + 57);
+ sstate->smb2.Signature[10] = *(p + 58);
+ sstate->smb2.Signature[11] = *(p + 59);
+ sstate->smb2.Signature[12] = *(p + 60);
+ sstate->smb2.Signature[13] = *(p + 61);
+ sstate->smb2.Signature[14] = *(p + 62);
+ sstate->smb2.Signature[15] = *(p + 63);
+ sstate->bytesprocessed += SMB2_HDR_LEN;
+ SCReturnUInt(64U);
+ break;
+ } else {
+ //sstate->smb2.protocol[0] = *(p++);
+ if (*(p++) != 0xfe)
+ return 0;
+ if (!(--input_len))
+ break;
+ /* We fall through to the next case if we still have input.
+ * Same applies for other cases as well */
+ }
+ /* fall through */
+ case 5:
+ //sstate->smb2.protocol[1] = *(p++);
+ if (*(p++) != 'S')
+ return 0;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 6:
+ //sstate->smb2.protocol[2] = *(p++);
+ if (*(p++) != 'M')
+ return 0;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 7:
+ //sstate->smb2.protocol[3] = *(p++);
+ if (*(p++) != 'B')
+ return 0;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 8:
+ sstate->smb2.StructureSize = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 9:
+ sstate->smb2.StructureSize |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 10:
+ sstate->smb2.CreditCharge = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 11:
+ sstate->smb2.CreditCharge |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 12:
+ sstate->smb2.Status = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 13:
+ sstate->smb2.Status |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 14:
+ sstate->smb2.Status |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 15:
+ sstate->smb2.Status |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 16:
+ sstate->smb2.Command = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 17:
+ sstate->smb2.Command |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 18:
+ sstate->smb2.CreditRequestResponse = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 19:
+ sstate->smb2.CreditRequestResponse |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 20:
+ sstate->smb2.Flags = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 21:
+ sstate->smb2.Flags |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 22:
+ sstate->smb2.Flags |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 23:
+ sstate->smb2.Flags |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 24:
+ sstate->smb2.NextCommand = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 25:
+ sstate->smb2.NextCommand |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 26:
+ sstate->smb2.NextCommand |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 27:
+ sstate->smb2.NextCommand |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 28:
+ sstate->smb2.MessageId = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 29:
+ sstate->smb2.MessageId = *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 30:
+ sstate->smb2.MessageId = *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 31:
+ sstate->smb2.MessageId = (uint64_t) *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 32:
+ sstate->smb2.MessageId = (uint64_t) *(p++) << 32;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 33:
+ sstate->smb2.MessageId = (uint64_t) *(p++) << 40;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 34:
+ sstate->smb2.MessageId = (uint64_t) *(p++) << 48;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 35:
+ sstate->smb2.MessageId = (uint64_t) *(p++) << 56;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 36:
+ sstate->smb2.ProcessId = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 37:
+ sstate->smb2.ProcessId |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 38:
+ sstate->smb2.ProcessId |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 39:
+ sstate->smb2.ProcessId |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 40:
+ sstate->smb2.TreeId = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 41:
+ sstate->smb2.TreeId |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 42:
+ sstate->smb2.TreeId |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 43:
+ sstate->smb2.TreeId |= *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 44:
+ sstate->smb2.SessionId = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 45:
+ sstate->smb2.SessionId |= *(p++) << 8;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 46:
+ sstate->smb2.SessionId |= *(p++) << 16;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 47:
+ sstate->smb2.SessionId |= (uint64_t) *(p++) << 24;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 48:
+ sstate->smb2.SessionId |= (uint64_t) *(p++) << 32;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 49:
+ sstate->smb2.SessionId |= (uint64_t) *(p++) << 40;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 50:
+ sstate->smb2.SessionId |= (uint64_t) *(p++) << 48;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 51:
+ sstate->smb2.SessionId |= (uint64_t) *(p++) << 56;
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 52:
+ sstate->smb2.Signature[0] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 53:
+ sstate->smb2.Signature[1] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 54:
+ sstate->smb2.Signature[2] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 55:
+ sstate->smb2.Signature[3] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 56:
+ sstate->smb2.Signature[4] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 57:
+ sstate->smb2.Signature[5] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 58:
+ sstate->smb2.Signature[6] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 59:
+ sstate->smb2.Signature[7] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 60:
+ sstate->smb2.Signature[8] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 61:
+ sstate->smb2.Signature[9] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 62:
+ sstate->smb2.Signature[10] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 63:
+ sstate->smb2.Signature[11] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 64:
+ sstate->smb2.Signature[12] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 65:
+ sstate->smb2.Signature[13] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 66:
+ sstate->smb2.Signature[14] = *(p++);
+ if (!(--input_len))
+ break;
+ /* fall through */
+ case 67:
+ sstate->smb2.Signature[15] = *(p++);
+ --input_len;
+ break;
+ /* fall through */
+ }
+ }
+ sstate->bytesprocessed += (p - input);
+ SCReturnUInt((uint32_t)(p - input));
+}
+
+static int SMB2Parse(Flow *f, void *smb2_state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+ SMB2State *sstate = (SMB2State *) smb2_state;
+ uint32_t retval = 0;
+ uint32_t parsed = 0;
+
+ if (pstate == NULL)
+ return -1;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ while (sstate->bytesprocessed < NBSS_HDR_LEN && input_len) {
+ retval = NBSSParseHeader(smb2_state, pstate, input, input_len);
+ if (retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else {
+ return -1;
+ }
+
+ SCLogDebug("NBSS Header (%u/%u) Type 0x%02x Length 0x%04x parsed %u input_len %u",
+ sstate->bytesprocessed, NBSS_HDR_LEN, sstate->nbss.type,
+ sstate->nbss.length, parsed, input_len);
+ }
+
+ switch(sstate->nbss.type) {
+ case NBSS_SESSION_MESSAGE:
+ while (input_len && (sstate->bytesprocessed >= NBSS_HDR_LEN &&
+ sstate->bytesprocessed < NBSS_HDR_LEN + SMB2_HDR_LEN)) {
+ retval = SMB2ParseHeader(smb2_state, pstate, input + parsed, input_len);
+ if (retval <= input_len) {
+ parsed += retval;
+ input_len -= retval;
+ } else {
+ return -1;
+ }
+
+ SCLogDebug("SMB2 Header (%u/%u) Command 0x%04x parsed %u input_len %u",
+ sstate->bytesprocessed, NBSS_HDR_LEN + SMB2_HDR_LEN,
+ sstate->smb2.Command, parsed, input_len);
+ }
+ break;
+ default:
+ break;
+ }
+ SCReturnInt(1);
+}
+
+
+static void *SMB2StateAlloc(void)
+{
+ void *s = SCMalloc(sizeof(SMB2State));
+ if (unlikely(s == NULL))
+ return NULL;
+
+ memset(s, 0, sizeof(SMB2State));
+ return s;
+}
+
+static void SMB2StateFree(void *s)
+{
+ if (s) {
+ SCFree(s);
+ s = NULL;
+ }
+}
+
+void RegisterSMB2Parsers(void)
+{
+ /** SMB2 */
+ char *proto_name = "smb2";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMB2, STREAM_TOSERVER, SMB2Parse);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMB2, STREAM_TOCLIENT, SMB2Parse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SMB2, SMB2StateAlloc, SMB2StateFree);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SMB2, SMB2ParserRegisterTests);
+#endif
+ return;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+int SMB2ParserTest01(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t smb2buf[] =
+ "\x00\x00\x00\x66" // NBSS
+ "\xfe\x53\x4d\x42\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00" // SMB2
+ "\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x24\x00\x01\x00x00\x00\x00\x00\x00\x00\x0\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02";
+
+ uint32_t smb2len = sizeof(smb2buf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMB2, STREAM_TOSERVER|STREAM_EOF, smb2buf, smb2len);
+ if (r != 0) {
+ printf("smb2 header check returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SMB2State *smb2_state = f.alstate;
+ if (smb2_state == NULL) {
+ printf("no smb2 state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (smb2_state->nbss.type != NBSS_SESSION_MESSAGE) {
+ printf("expected nbss type 0x%02x , got 0x%02x : ", NBSS_SESSION_MESSAGE, smb2_state->nbss.type);
+ result = 0;
+ goto end;
+ }
+
+ if (smb2_state->nbss.length != 102) {
+ printf("expected nbss length 0x%02x , got 0x%02x : ", 102, smb2_state->nbss.length);
+ result = 0;
+ goto end;
+ }
+
+ if (smb2_state->smb2.Command != SMB2_NEGOTIATE) {
+ printf("expected SMB2 command 0x%04x , got 0x%04x : ", SMB2_NEGOTIATE, smb2_state->smb2.Command);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+void SMB2ParserRegisterTests(void)
+{
+ UtRegisterTest("SMB2ParserTest01", SMB2ParserTest01, 1);
+}
+#endif
+
diff --git a/framework/src/suricata/src/app-layer-smb2.h b/framework/src/suricata/src/app-layer-smb2.h
new file mode 100644
index 00000000..2eb86ca6
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smb2.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Kirby Kuehl <kkuehl@gmail.com>
+ */
+
+#ifndef __APP_LAYER_SMB2_H__
+#define __APP_LAYER_SMB2_H__
+
+#include "suricata-common.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-nbss.h"
+#include "flow.h"
+#include "stream.h"
+
+typedef struct SMB2Hdr {
+ uint32_t Protocol; /**< Contains 0xFE,'SMB' */
+ uint16_t StructureSize;
+ uint16_t CreditCharge;
+ uint32_t Status;
+ uint16_t Command;
+ uint16_t CreditRequestResponse;
+ uint32_t Flags;
+ uint32_t NextCommand;
+ uint64_t MessageId;
+ uint32_t ProcessId;
+ uint32_t TreeId;
+ uint64_t SessionId;
+ uint8_t Signature[16];
+} SMB2Hdr;
+
+#define SMB2_HDR_LEN 64
+
+typedef struct SMB2State_ {
+ NBSSHdr nbss;
+ SMB2Hdr smb2;
+ uint16_t bytesprocessed;
+} SMB2State;
+
+/** from http://msdn.microsoft.com/en-us/library/cc246528(PROT.13).aspx */
+#define SMB2_NEGOTIATE 0x0000
+#define SMB2_SESSION_SETUP 0x0001
+#define SMB2_LOGOFF 0x0002
+#define SMB2_TREE_CONNECT 0x0003
+#define SMB2_TREE_DISCONNECT 0x0004
+#define SMB2_CREATE 0x0005
+#define SMB2_CLOSE 0x0006
+#define SMB2_FLUSH 0x0007
+#define SMB2_READ 0x0008
+#define SMB2_WRITE 0x0009
+#define SMB2_LOCK 0x000A
+#define SMB2_IOCTL 0x000B
+#define SMB2_CANCEL 0x000C
+#define SMB2_ECHO 0x000D
+#define SMB2_QUERY_DIRECTORY 0x000E
+#define SMB2_CHANGE_NOTIFY 0x000F
+#define SMB2_QUERY_INFO 0x0010
+#define SMB2_SET_INFO 0x0011
+#define SMB2_OPLOCK_BREAK 0x0012
+
+void RegisterSMB2Parsers(void);
+void SMB2ParserRegisterTests(void);
+
+#endif /* __APP_LAYER_SMB2_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-smtp.c b/framework/src/suricata/src/app-layer-smtp.c
new file mode 100644
index 00000000..1951613d
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smtp.c
@@ -0,0 +1,4875 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-smtp.h"
+
+#include "util-mpm.h"
+#include "util-debug.h"
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-byte.h"
+#include "util-unittest-helper.h"
+#include "util-memcmp.h"
+#include "flow-util.h"
+
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+
+#include "decode-events.h"
+#include "conf.h"
+
+#include "util-mem.h"
+#include "util-misc.h"
+
+/* content-limit default value */
+#define FILEDATA_CONTENT_LIMIT 1000
+/* content-inspect-min-size default value */
+#define FILEDATA_CONTENT_INSPECT_MIN_SIZE 1000
+/* content-inspect-window default value */
+#define FILEDATA_CONTENT_INSPECT_WINDOW 1000
+
+#define SMTP_MAX_REQUEST_AND_REPLY_LINE_LENGTH 510
+
+#define SMTP_COMMAND_BUFFER_STEPS 5
+
+/* we are in process of parsing a fresh command. Just a placeholder. If we
+ * are not in STATE_COMMAND_DATA_MODE, we have to be in this mode */
+#define SMTP_PARSER_STATE_COMMAND_MODE 0x00
+/* we are in mode of parsing a command's data. Used when we are parsing tls
+ * or accepting the rfc 2822 mail after DATA command */
+#define SMTP_PARSER_STATE_COMMAND_DATA_MODE 0x01
+/* Used when we are still in the process of parsing a server command. Used
+ * with multi-line replies and the stream is fragmented before all the lines
+ * for a response is seen */
+#define SMTP_PARSER_STATE_PARSING_SERVER_RESPONSE 0x02
+/* Used to indicate that the parser has seen the first reply */
+#define SMTP_PARSER_STATE_FIRST_REPLY_SEEN 0x04
+/* Used to indicate that the parser is parsing a multiline reply */
+#define SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY 0x08
+
+/* Various SMTP commands
+ * We currently have var-ified just STARTTLS and DATA, since we need to them
+ * for state transitions. The rest are just indicate as OTHER_CMD. Other
+ * commands would be introduced as and when needed */
+#define SMTP_COMMAND_STARTTLS 1
+#define SMTP_COMMAND_DATA 2
+#define SMTP_COMMAND_BDAT 3
+/* not an actual command per se, but the mode where we accept the mail after
+ * DATA has it's own reply code for completion, from the server. We give this
+ * stage a pseudo command of it's own, so that we can add this to the command
+ * buffer to match with the reply */
+#define SMTP_COMMAND_DATA_MODE 4
+/* All other commands are represented by this var */
+#define SMTP_COMMAND_OTHER_CMD 5
+
+/* Different EHLO extensions. Not used now. */
+#define SMTP_EHLO_EXTENSION_PIPELINING
+#define SMTP_EHLO_EXTENSION_SIZE
+#define SMTP_EHLO_EXTENSION_DSN
+#define SMTP_EHLO_EXTENSION_STARTTLS
+#define SMTP_EHLO_EXTENSION_8BITMIME
+
+SCEnumCharMap smtp_decoder_event_table[ ] = {
+ { "INVALID_REPLY", SMTP_DECODER_EVENT_INVALID_REPLY },
+ { "UNABLE_TO_MATCH_REPLY_WITH_REQUEST",
+ SMTP_DECODER_EVENT_UNABLE_TO_MATCH_REPLY_WITH_REQUEST },
+ { "MAX_COMMAND_LINE_LEN_EXCEEDED",
+ SMTP_DECODER_EVENT_MAX_COMMAND_LINE_LEN_EXCEEDED },
+ { "MAX_REPLY_LINE_LEN_EXCEEDED",
+ SMTP_DECODER_EVENT_MAX_REPLY_LINE_LEN_EXCEEDED },
+ { "INVALID_PIPELINED_SEQUENCE",
+ SMTP_DECODER_EVENT_INVALID_PIPELINED_SEQUENCE },
+ { "BDAT_CHUNK_LEN_EXCEEDED",
+ SMTP_DECODER_EVENT_BDAT_CHUNK_LEN_EXCEEDED },
+ { "NO_SERVER_WELCOME_MESSAGE",
+ SMTP_DECODER_EVENT_NO_SERVER_WELCOME_MESSAGE },
+ { "TLS_REJECTED",
+ SMTP_DECODER_EVENT_TLS_REJECTED },
+ { "DATA_COMMAND_REJECTED",
+ SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED },
+
+ /* MIME Events */
+ { "MIME_PARSE_FAILED",
+ SMTP_DECODER_EVENT_MIME_PARSE_FAILED },
+ { "MIME_MALFORMED_MSG",
+ SMTP_DECODER_EVENT_MIME_MALFORMED_MSG },
+ { "MIME_INVALID_BASE64",
+ SMTP_DECODER_EVENT_MIME_INVALID_BASE64 },
+ { "MIME_INVALID_QP",
+ SMTP_DECODER_EVENT_MIME_INVALID_QP },
+ { "MIME_LONG_LINE",
+ SMTP_DECODER_EVENT_MIME_LONG_LINE },
+ { "MIME_LONG_ENC_LINE",
+ SMTP_DECODER_EVENT_MIME_LONG_ENC_LINE },
+ { "MIME_LONG_HEADER_NAME",
+ SMTP_DECODER_EVENT_MIME_LONG_HEADER_NAME },
+ { "MIME_LONG_HEADER_VALUE",
+ SMTP_DECODER_EVENT_MIME_LONG_HEADER_VALUE },
+ { "MIME_LONG_BOUNDARY",
+ SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG },
+
+ { NULL, -1 },
+};
+
+#define SMTP_MPM DEFAULT_MPM
+
+static MpmCtx *smtp_mpm_ctx = NULL;
+MpmThreadCtx *smtp_mpm_thread_ctx;
+
+/* smtp reply codes. If an entry is made here, please make a simultaneous
+ * entry in smtp_reply_map */
+enum {
+ SMTP_REPLY_211,
+ SMTP_REPLY_214,
+ SMTP_REPLY_220,
+ SMTP_REPLY_221,
+ SMTP_REPLY_235,
+ SMTP_REPLY_250,
+ SMTP_REPLY_251,
+ SMTP_REPLY_252,
+
+ SMTP_REPLY_334,
+ SMTP_REPLY_354,
+
+ SMTP_REPLY_421,
+ SMTP_REPLY_450,
+ SMTP_REPLY_451,
+ SMTP_REPLY_452,
+ SMTP_REPLY_455,
+
+ SMTP_REPLY_500,
+ SMTP_REPLY_501,
+ SMTP_REPLY_502,
+ SMTP_REPLY_503,
+ SMTP_REPLY_504,
+ SMTP_REPLY_550,
+ SMTP_REPLY_551,
+ SMTP_REPLY_552,
+ SMTP_REPLY_553,
+ SMTP_REPLY_554,
+ SMTP_REPLY_555,
+};
+
+SCEnumCharMap smtp_reply_map[ ] = {
+ { "211", SMTP_REPLY_211 },
+ { "214", SMTP_REPLY_214 },
+ { "220", SMTP_REPLY_220 },
+ { "221", SMTP_REPLY_221 },
+ { "235", SMTP_REPLY_235 },
+ { "250", SMTP_REPLY_250 },
+ { "251", SMTP_REPLY_251 },
+ { "252", SMTP_REPLY_252 },
+
+ { "334", SMTP_REPLY_334 },
+ { "354", SMTP_REPLY_354 },
+
+ { "421", SMTP_REPLY_421 },
+ { "450", SMTP_REPLY_450 },
+ { "451", SMTP_REPLY_451 },
+ { "452", SMTP_REPLY_452 },
+ { "455", SMTP_REPLY_455 },
+
+ { "500", SMTP_REPLY_500 },
+ { "501", SMTP_REPLY_501 },
+ { "502", SMTP_REPLY_502 },
+ { "503", SMTP_REPLY_503 },
+ { "504", SMTP_REPLY_504 },
+ { "550", SMTP_REPLY_550 },
+ { "551", SMTP_REPLY_551 },
+ { "552", SMTP_REPLY_552 },
+ { "553", SMTP_REPLY_553 },
+ { "554", SMTP_REPLY_554 },
+ { "555", SMTP_REPLY_555 },
+ { NULL, -1 },
+};
+
+/* Create SMTP config structure */
+SMTPConfig smtp_config = { 0, { 0, 0, 0, 0 }, 0, 0, 0};
+
+/**
+ * \brief Configure SMTP Mime Decoder by parsing out mime section of YAML
+ * config file
+ *
+ * \return none
+ */
+static void SMTPConfigure(void) {
+
+ SCEnter();
+ int ret = 0, val;
+ intmax_t imval;
+ uint32_t content_limit = 0;
+ uint32_t content_inspect_min_size = 0;
+ uint32_t content_inspect_window = 0;
+
+ ConfNode *config = ConfGetNode("app-layer.protocols.smtp.mime");
+ if (config != NULL) {
+
+ ret = ConfGetChildValueBool(config, "decode-mime", &val);
+ if (ret) {
+ smtp_config.decode_mime = val;
+ }
+
+ ret = ConfGetChildValueBool(config, "decode-base64", &val);
+ if (ret) {
+ smtp_config.mime_config.decode_base64 = val;
+ }
+
+ ret = ConfGetChildValueBool(config, "decode-quoted-printable", &val);
+ if (ret) {
+ smtp_config.mime_config.decode_quoted_printable = val;
+ }
+
+ ret = ConfGetChildValueInt(config, "header-value-depth", &imval);
+ if (ret) {
+ smtp_config.mime_config.header_value_depth = (uint32_t) imval;
+ }
+
+ ret = ConfGetChildValueBool(config, "extract-urls", &val);
+ if (ret) {
+ smtp_config.mime_config.extract_urls = val;
+ }
+ }
+
+ /* Pass mime config data to MimeDec API */
+ MimeDecSetConfig(&smtp_config.mime_config);
+
+ ConfNode *t = ConfGetNode("app-layer.protocols.smtp.inspected-tracker");
+ ConfNode *p = NULL;
+
+ if (t == NULL)
+ return;
+
+ TAILQ_FOREACH(p, &t->head, next) {
+ if (strcasecmp("content-limit", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &content_limit) < 0) {
+ SCLogWarning(SC_ERR_SIZE_PARSE, "Error parsing content-limit "
+ "from conf file - %s. Killing engine", p->val);
+ content_limit = FILEDATA_CONTENT_LIMIT;
+ }
+ }
+
+ if (strcasecmp("content-inspect-min-size", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &content_inspect_min_size) < 0) {
+ SCLogWarning(SC_ERR_SIZE_PARSE, "Error parsing content-inspect-min-size-limit "
+ "from conf file - %s. Killing engine", p->val);
+ content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
+ }
+ }
+
+ if (strcasecmp("content-inspect-window", p->name) == 0) {
+ if (ParseSizeStringU32(p->val, &content_inspect_window) < 0) {
+ SCLogWarning(SC_ERR_SIZE_PARSE, "Error parsing content-inspect-window "
+ "from conf file - %s. Killing engine", p->val);
+ content_inspect_window = FILEDATA_CONTENT_INSPECT_WINDOW;
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+void SMTPSetEvent(SMTPState *s, uint8_t e)
+{
+ SCLogDebug("setting event %u", e);
+
+ if (s->curr_tx != NULL) {
+ AppLayerDecoderEventsSetEventRaw(&s->curr_tx->decoder_events, e);
+// s->events++;
+ return;
+ }
+ SCLogDebug("couldn't set event %u", e);
+}
+
+static SMTPTransaction *SMTPTransactionCreate(void)
+{
+ SMTPTransaction *tx = SCCalloc(1, sizeof(*tx));
+ if (tx == NULL) {
+ return NULL;
+ }
+
+ tx->mime_state = NULL;
+ return tx;
+}
+
+int SMTPProcessDataChunk(const uint8_t *chunk, uint32_t len,
+ MimeDecParseState *state) {
+
+ int ret = MIME_DEC_OK;
+ Flow *flow = (Flow *) state->data;
+ SMTPState *smtp_state = (SMTPState *) flow->alstate;
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+ FileContainer *files = NULL;
+ uint16_t flags = 0;
+
+ /* Set flags */
+ if (flow->flags & FLOW_FILE_NO_STORE_TS) {
+ flags |= FILE_NOSTORE;
+ }
+
+ if (flow->flags & FLOW_FILE_NO_MAGIC_TS) {
+ flags |= FILE_NOMAGIC;
+ }
+
+ if (flow->flags & FLOW_FILE_NO_MD5_TS) {
+ flags |= FILE_NOMD5;
+ }
+
+ /* Determine whether to process files */
+ if ((flags & (FILE_NOSTORE | FILE_NOMAGIC | FILE_NOMD5)) ==
+ (FILE_NOSTORE | FILE_NOMAGIC | FILE_NOMD5)) {
+ SCLogDebug("File content ignored");
+ return 0;
+ }
+
+ /* Find file */
+ if (entity->ctnt_flags & CTNT_IS_ATTACHMENT) {
+
+ /* Make sure file container allocated */
+ if (smtp_state->files_ts == NULL) {
+ smtp_state->files_ts = FileContainerAlloc();
+ if (smtp_state->files_ts == NULL) {
+ ret = MIME_DEC_ERR_MEM;
+ SCLogError(SC_ERR_MEM_ALLOC, "Could not create file container");
+ goto end;
+ }
+ }
+ files = smtp_state->files_ts;
+
+ /* Open file if necessary */
+ if (state->body_begin) {
+
+ if (SCLogDebugEnabled()) {
+ SCLogDebug("Opening file...%u bytes", len);
+ printf("File - ");
+ for (uint32_t i = 0; i < entity->filename_len; i++) {
+ printf("%c", entity->filename[i]);
+ }
+ printf("\n");
+ }
+
+ /* Set storage flag if applicable since only the first file in the
+ * flow seems to be processed by the 'filestore' detector */
+ if (files->head != NULL && (files->head->flags & FILE_STORE)) {
+ flags |= FILE_STORE;
+ }
+
+ if (FileOpenFile(files, (uint8_t *) entity->filename, entity->filename_len,
+ (uint8_t *) chunk, len, flags) == NULL) {
+ ret = MIME_DEC_ERR_DATA;
+ SCLogDebug("FileOpenFile() failed");
+ }
+
+ /* If close in the same chunk, then pass in empty bytes */
+ if (state->body_end) {
+
+ SCLogDebug("Closing file...%u bytes", len);
+
+ if (files->tail->state == FILE_STATE_OPENED) {
+ ret = FileCloseFile(files, (uint8_t *) NULL, 0, flags);
+ if (ret != 0) {
+ SCLogDebug("FileCloseFile() failed: %d", ret);
+ ret = MIME_DEC_ERR_DATA;
+ }
+ } else {
+ SCLogDebug("File already closed");
+ }
+ }
+ } else if (state->body_end) {
+ /* Close file */
+ SCLogDebug("Closing file...%u bytes", len);
+
+ if (files && files->tail && files->tail->state == FILE_STATE_OPENED) {
+ ret = FileCloseFile(files, (uint8_t *) chunk, len, flags);
+ if (ret != 0) {
+ SCLogDebug("FileCloseFile() failed: %d", ret);
+ ret = MIME_DEC_ERR_DATA;
+ }
+ } else {
+ SCLogDebug("File already closed");
+ }
+ } else {
+ /* Append data chunk to file */
+ SCLogDebug("Appending file...%u bytes", len);
+
+ /* 0 is ok, -2 is not stored, -1 is error */
+ ret = FileAppendData(files, (uint8_t *) chunk, len);
+ if (ret == -2) {
+ ret = 0;
+ SCLogDebug("FileAppendData() - file no longer being extracted");
+ } else if (ret < 0) {
+ SCLogDebug("FileAppendData() failed: %d", ret);
+ ret = MIME_DEC_ERR_DATA;
+ }
+ }
+
+ if (ret == 0) {
+ SCLogDebug("Successfully processed file data!");
+ }
+ } else {
+ SCLogDebug("Body not a Ctnt_attachment");
+ }
+
+ if (files != NULL) {
+ FilePrune(files);
+ }
+end:
+ SCReturnInt(ret);
+}
+
+/**
+ * \internal
+ * \brief Get the next line from input. It doesn't do any length validation.
+ *
+ * \param state The smtp state.
+ *
+ * \retval 0 On suceess.
+ * \retval -1 Either when we don't have any new lines to supply anymore or
+ * on failure.
+ */
+static int SMTPGetLine(SMTPState *state)
+{
+ SCEnter();
+ void *ptmp;
+
+ /* we have run out of input */
+ if (state->input_len <= 0)
+ return -1;
+
+ /* toserver */
+ if (state->direction == 0) {
+ if (state->ts_current_line_lf_seen == 1) {
+ /* we have seen the lf for the previous line. Clear the parser
+ * details to parse new line */
+ state->ts_current_line_lf_seen = 0;
+ if (state->ts_current_line_db == 1) {
+ state->ts_current_line_db = 0;
+ SCFree(state->ts_db);
+ state->ts_db = NULL;
+ state->ts_db_len = 0;
+ state->current_line = NULL;
+ state->current_line_len = 0;
+ }
+ }
+
+ uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
+
+ if (lf_idx == NULL) {
+ /* fragmented lines. Decoder event for special cases. Not all
+ * fragmented lines should be treated as a possible evasion
+ * attempt. With multi payload smtp chunks we can have valid
+ * cases of fragmentation. But within the same segment chunk
+ * if we see fragmentation then it's definitely something you
+ * should alert about */
+ if (state->ts_current_line_db == 0) {
+ state->ts_db = SCMalloc(state->input_len);
+ if (state->ts_db == NULL) {
+ return -1;
+ }
+ state->ts_current_line_db = 1;
+ memcpy(state->ts_db, state->input, state->input_len);
+ state->ts_db_len = state->input_len;
+ } else {
+ ptmp = SCRealloc(state->ts_db,
+ (state->ts_db_len + state->input_len));
+ if (ptmp == NULL) {
+ SCFree(state->ts_db);
+ state->ts_db = NULL;
+ state->ts_db_len = 0;
+ return -1;
+ }
+ state->ts_db = ptmp;
+
+ memcpy(state->ts_db + state->ts_db_len,
+ state->input, state->input_len);
+ state->ts_db_len += state->input_len;
+ } /* else */
+ state->input += state->input_len;
+ state->input_len = 0;
+
+ return -1;
+
+ } else {
+ state->ts_current_line_lf_seen = 1;
+
+ if (state->ts_current_line_db == 1) {
+ ptmp = SCRealloc(state->ts_db,
+ (state->ts_db_len + (lf_idx + 1 - state->input)));
+ if (ptmp == NULL) {
+ SCFree(state->ts_db);
+ state->ts_db = NULL;
+ state->ts_db_len = 0;
+ return -1;
+ }
+ state->ts_db = ptmp;
+
+ memcpy(state->ts_db + state->ts_db_len,
+ state->input, (lf_idx + 1 - state->input));
+ state->ts_db_len += (lf_idx + 1 - state->input);
+
+ if (state->ts_db_len > 1 &&
+ state->ts_db[state->ts_db_len - 2] == 0x0D) {
+ state->ts_db_len -= 2;
+ state->current_line_delimiter_len = 2;
+ } else {
+ state->ts_db_len -= 1;
+ state->current_line_delimiter_len = 1;
+ }
+
+ state->current_line = state->ts_db;
+ state->current_line_len = state->ts_db_len;
+
+ } else {
+ state->current_line = state->input;
+ state->current_line_len = lf_idx - state->input;
+
+ if (state->input != lf_idx &&
+ *(lf_idx - 1) == 0x0D) {
+ state->current_line_len--;
+ state->current_line_delimiter_len = 2;
+ } else {
+ state->current_line_delimiter_len = 1;
+ }
+ }
+
+ state->input_len -= (lf_idx - state->input) + 1;
+ state->input = (lf_idx + 1);
+
+ return 0;
+ }
+
+ /* toclient */
+ } else {
+ if (state->tc_current_line_lf_seen == 1) {
+ /* we have seen the lf for the previous line. Clear the parser
+ * details to parse new line */
+ state->tc_current_line_lf_seen = 0;
+ if (state->tc_current_line_db == 1) {
+ state->tc_current_line_db = 0;
+ SCFree(state->tc_db);
+ state->tc_db = NULL;
+ state->tc_db_len = 0;
+ state->current_line = NULL;
+ state->current_line_len = 0;
+ }
+ }
+
+ uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
+
+ if (lf_idx == NULL) {
+ /* fragmented lines. Decoder event for special cases. Not all
+ * fragmented lines should be treated as a possible evasion
+ * attempt. With multi payload smtp chunks we can have valid
+ * cases of fragmentation. But within the same segment chunk
+ * if we see fragmentation then it's definitely something you
+ * should alert about */
+ if (state->tc_current_line_db == 0) {
+ state->tc_db = SCMalloc(state->input_len);
+ if (state->tc_db == NULL) {
+ return -1;
+ }
+ state->tc_current_line_db = 1;
+ memcpy(state->tc_db, state->input, state->input_len);
+ state->tc_db_len = state->input_len;
+ } else {
+ ptmp = SCRealloc(state->tc_db,
+ (state->tc_db_len + state->input_len));
+ if (ptmp == NULL) {
+ SCFree(state->tc_db);
+ state->tc_db = NULL;
+ state->tc_db_len = 0;
+ return -1;
+ }
+ state->tc_db = ptmp;
+
+ memcpy(state->tc_db + state->tc_db_len,
+ state->input, state->input_len);
+ state->tc_db_len += state->input_len;
+ } /* else */
+ state->input += state->input_len;
+ state->input_len = 0;
+
+ return -1;
+
+ } else {
+ state->tc_current_line_lf_seen = 1;
+
+ if (state->tc_current_line_db == 1) {
+ ptmp = SCRealloc(state->tc_db,
+ (state->tc_db_len + (lf_idx + 1 - state->input)));
+ if (ptmp == NULL) {
+ SCFree(state->tc_db);
+ state->tc_db = NULL;
+ state->tc_db_len = 0;
+ return -1;
+ }
+ state->tc_db = ptmp;
+
+ memcpy(state->tc_db + state->tc_db_len,
+ state->input, (lf_idx + 1 - state->input));
+ state->tc_db_len += (lf_idx + 1 - state->input);
+
+ if (state->tc_db_len > 1 &&
+ state->tc_db[state->tc_db_len - 2] == 0x0D) {
+ state->tc_db_len -= 2;
+ state->current_line_delimiter_len = 2;
+ } else {
+ state->tc_db_len -= 1;
+ state->current_line_delimiter_len = 1;
+ }
+
+ state->current_line = state->tc_db;
+ state->current_line_len = state->tc_db_len;
+
+ } else {
+ state->current_line = state->input;
+ state->current_line_len = lf_idx - state->input;
+
+ if (state->input != lf_idx &&
+ *(lf_idx - 1) == 0x0D) {
+ state->current_line_len--;
+ state->current_line_delimiter_len = 2;
+ } else {
+ state->current_line_delimiter_len = 1;
+ }
+ }
+
+ state->input_len -= (lf_idx - state->input) + 1;
+ state->input = (lf_idx + 1);
+
+ return 0;
+ } /* else - if (lf_idx == NULL) */
+ }
+
+}
+
+static int SMTPInsertCommandIntoCommandBuffer(uint8_t command, SMTPState *state, Flow *f)
+{
+ SCEnter();
+ void *ptmp;
+
+ if (state->cmds_cnt >= state->cmds_buffer_len) {
+ int increment = SMTP_COMMAND_BUFFER_STEPS;
+ if ((int)(state->cmds_buffer_len + SMTP_COMMAND_BUFFER_STEPS) > (int)USHRT_MAX) {
+ increment = USHRT_MAX - state->cmds_buffer_len;
+ }
+
+ ptmp = SCRealloc(state->cmds,
+ sizeof(uint8_t) * (state->cmds_buffer_len + increment));
+ if (ptmp == NULL) {
+ SCFree(state->cmds);
+ state->cmds = NULL;
+ SCLogDebug("SCRealloc failure");
+ return -1;
+ }
+ state->cmds = ptmp;
+
+ state->cmds_buffer_len += increment;
+ }
+ if (state->cmds_cnt >= 1 &&
+ ((state->cmds[state->cmds_cnt - 1] == SMTP_COMMAND_STARTTLS) ||
+ (state->cmds[state->cmds_cnt - 1] == SMTP_COMMAND_DATA))) {
+ /* decoder event */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_PIPELINED_SEQUENCE);
+ /* we have to have EHLO, DATA, VRFY, EXPN, TURN, QUIT, NOOP,
+ * STARTTLS as the last command in pipelined mode */
+ }
+
+ /** \todo decoder event */
+ if ((int)(state->cmds_cnt + 1) > (int)USHRT_MAX) {
+ SCLogDebug("command buffer overflow");
+ return -1;
+ }
+
+ state->cmds[state->cmds_cnt] = command;
+ state->cmds_cnt++;
+
+ return 0;
+}
+
+static int SMTPProcessCommandBDAT(SMTPState *state, Flow *f,
+ AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ state->bdat_chunk_idx += (state->current_line_len +
+ state->current_line_delimiter_len);
+ if (state->bdat_chunk_idx > state->bdat_chunk_len) {
+ state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ /* decoder event */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_BDAT_CHUNK_LEN_EXCEEDED);
+ SCReturnInt(-1);
+ } else if (state->bdat_chunk_idx == state->bdat_chunk_len) {
+ state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ }
+
+ SCReturnInt(0);
+}
+
+static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
+ AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ /* looks like are still waiting for a confirmination from the server */
+ return 0;
+ }
+
+ if (state->current_line_len == 1 && state->current_line[0] == '.') {
+ state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ /* kinda like a hack. The mail sent in DATA mode, would be
+ * acknowledged with a reply. We insert a dummy command to
+ * the command buffer to be used by the reply handler to match
+ * the reply received */
+ SMTPInsertCommandIntoCommandBuffer(SMTP_COMMAND_DATA_MODE, state, f);
+
+ if (smtp_config.decode_mime) {
+ /* Complete parsing task */
+ int ret = MimeDecParseComplete(state->curr_tx->mime_state);
+ if (ret != MIME_DEC_OK) {
+
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_PARSE_FAILED);
+ SCLogDebug("MimeDecParseComplete() function failed");
+ }
+
+ /* Generate decoder events */
+ MimeDecEntity *msg = state->curr_tx->mime_state->msg;
+ if (msg->anomaly_flags & ANOM_INVALID_BASE64) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_INVALID_BASE64);
+ }
+ if (msg->anomaly_flags & ANOM_INVALID_QP) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_INVALID_QP);
+ }
+ if (msg->anomaly_flags & ANOM_LONG_LINE) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_LINE);
+ }
+ if (msg->anomaly_flags & ANOM_LONG_ENC_LINE) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_ENC_LINE);
+ }
+ if (msg->anomaly_flags & ANOM_LONG_HEADER_NAME) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_HEADER_NAME);
+ }
+ if (msg->anomaly_flags & ANOM_LONG_HEADER_VALUE) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_HEADER_VALUE);
+ }
+ if (msg->anomaly_flags & ANOM_MALFORMED_MSG) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_MALFORMED_MSG);
+ }
+ if (msg->anomaly_flags & ANOM_LONG_BOUNDARY) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG);
+ }
+ }
+ state->curr_tx->done = 1;
+ SCLogDebug("marked tx as done");
+ }
+
+ /* If DATA, then parse out a MIME message */
+ if (state->current_command == SMTP_COMMAND_DATA &&
+ (state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ if (smtp_config.decode_mime && state->curr_tx->mime_state) {
+ int ret = MimeDecParseLine((const uint8_t *) state->current_line,
+ state->current_line_len, state->curr_tx->mime_state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("MimeDecParseLine() function returned an error code: %d", ret);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int SMTPProcessCommandSTARTTLS(SMTPState *state, Flow *f,
+ AppLayerParserState *pstate)
+{
+ return 0;
+}
+
+static int SMTPProcessReply(SMTPState *state, Flow *f,
+ AppLayerParserState *pstate)
+{
+ SCEnter();
+
+ uint64_t reply_code = 0;
+ PatternMatcherQueue *pmq = state->thread_local_data;
+
+ /* the reply code has to contain at least 3 bytes, to hold the 3 digit
+ * reply code */
+ if (state->current_line_len < 3) {
+ /* decoder event */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
+ return -1;
+ }
+
+ if (state->current_line_len >= 4) {
+ if (state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY) {
+ if (state->current_line[3] != '-') {
+ state->parser_state &= ~SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
+ }
+ } else {
+ if (state->current_line[3] == '-') {
+ state->parser_state |= SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
+ }
+ }
+ } else {
+ if (state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY) {
+ state->parser_state &= ~SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
+ }
+ }
+
+ /* I don't like this pmq reset here. We'll devise a method later, that
+ * should make the use of the mpm very efficient */
+ PmqReset(pmq);
+ int mpm_cnt = mpm_table[SMTP_MPM].Search(smtp_mpm_ctx, smtp_mpm_thread_ctx,
+ pmq, state->current_line,
+ 3);
+ if (mpm_cnt == 0) {
+ /* set decoder event - reply code invalid */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
+ SCLogDebug("invalid reply code %02x %02x %02x",
+ state->current_line[0], state->current_line[1], state->current_line[2]);
+ SCReturnInt(-1);
+ }
+ reply_code = smtp_reply_map[pmq->pattern_id_array[0]].enum_value;
+
+ if (state->cmds_idx == state->cmds_cnt) {
+ if (!(state->parser_state & SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ state->parser_state |= SMTP_PARSER_STATE_FIRST_REPLY_SEEN;
+ if (reply_code == SMTP_REPLY_220)
+ SCReturnInt(0);
+ else
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
+ } else {
+ /* decoder event - unable to match reply with request */
+ SCLogDebug("unable to match reply with request");
+ SCReturnInt(-1);
+ }
+ }
+
+ if (state->cmds_cnt == 0) {
+ /* reply but not a command we have stored, fall through */
+ } else if (state->cmds[state->cmds_idx] == SMTP_COMMAND_STARTTLS) {
+ if (reply_code == SMTP_REPLY_220) {
+ /* we are entering STARRTTLS data mode */
+ state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_INSPECTION |
+ APP_LAYER_PARSER_NO_REASSEMBLY);
+ } else {
+ /* decoder event */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_TLS_REJECTED);
+ }
+ } else if (state->cmds[state->cmds_idx] == SMTP_COMMAND_DATA) {
+ if (reply_code == SMTP_REPLY_354) {
+ /* Next comes the mail for the DATA command in toserver direction */
+ state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ } else {
+ /* decoder event */
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED);
+ }
+ } else {
+ /* we don't care for any other command for now */
+ /* check if reply falls in the valid list of replies for SMTP. If not
+ * decoder event */
+ }
+
+ /* if it is a multi-line reply, we need to move the index only once for all
+ * the line of the reply. We unset the multiline flag on the last
+ * line of the multiline reply, following which we increment the index */
+ if (!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) {
+ state->cmds_idx++;
+ }
+
+ /* if we have matched all the buffered commands, reset the cnt and index */
+ if (state->cmds_idx == state->cmds_cnt) {
+ state->cmds_cnt = 0;
+ state->cmds_idx = 0;
+ }
+
+ return 0;
+}
+
+static int SMTPParseCommandBDAT(SMTPState *state)
+{
+ SCEnter();
+
+ int i = 4;
+ while (i < state->current_line_len) {
+ if (state->current_line[i] != ' ') {
+ break;
+ }
+ i++;
+ }
+ if (i == 4) {
+ /* decoder event */
+ return -1;
+ }
+ if (i == state->current_line_len) {
+ /* decoder event */
+ return -1;
+ }
+ char *endptr = NULL;
+ state->bdat_chunk_len = strtoul((const char *)state->current_line + i,
+ (char **)&endptr, 10);
+ if ((uint8_t *)endptr == state->current_line + i) {
+ /* decoder event */
+ return -1;
+ }
+
+ return 0;
+}
+
+/* consider 'rset' and 'quit' to be part of the existing state */
+static int NoNewTx(SMTPState *state)
+{
+ if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ if (state->current_line_len >= 4 &&
+ SCMemcmpLowercase("rset", state->current_line, 4) == 0) {
+ return 1;
+ } else if (state->current_line_len >= 4 &&
+ SCMemcmpLowercase("quit", state->current_line, 4) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int SMTPProcessRequest(SMTPState *state, Flow *f,
+ AppLayerParserState *pstate)
+{
+ SCEnter();
+ SMTPTransaction *tx = state->curr_tx;
+
+ if (state->curr_tx == NULL || (state->curr_tx->done && !NoNewTx(state))) {
+ tx = SMTPTransactionCreate();
+ if (tx == NULL)
+ return -1;
+ state->curr_tx = tx;
+ TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
+ tx->tx_id = state->tx_cnt++;
+ }
+
+ if (!(state->parser_state & SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ SMTPSetEvent(state, SMTP_DECODER_EVENT_NO_SERVER_WELCOME_MESSAGE);
+ }
+
+ /* there are 2 commands that can push it into this COMMAND_DATA mode -
+ * STARTTLS and DATA */
+ if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ int r = 0;
+
+ if (state->current_line_len >= 8 &&
+ SCMemcmpLowercase("starttls", state->current_line, 8) == 0) {
+ state->current_command = SMTP_COMMAND_STARTTLS;
+ } else if (state->current_line_len >= 4 &&
+ SCMemcmpLowercase("data", state->current_line, 4) == 0) {
+ state->current_command = SMTP_COMMAND_DATA;
+ if (smtp_config.decode_mime) {
+ tx->mime_state = MimeDecInitParser(f, SMTPProcessDataChunk);
+ if (tx->mime_state == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "MimeDecInitParser() failed to "
+ "allocate data");
+ return MIME_DEC_ERR_MEM;
+ }
+
+ /* Add new MIME message to end of list */
+ if (tx->msg_head == NULL) {
+ tx->msg_head = tx->mime_state->msg;
+ tx->msg_tail = tx->mime_state->msg;
+ }
+ else {
+ tx->msg_tail->next = tx->mime_state->msg;
+ tx->msg_tail = tx->mime_state->msg;
+ }
+ }
+
+ } else if (state->current_line_len >= 4 &&
+ SCMemcmpLowercase("bdat", state->current_line, 4) == 0) {
+ r = SMTPParseCommandBDAT(state);
+ if (r == -1) {
+ SCReturnInt(-1);
+ }
+ state->current_command = SMTP_COMMAND_BDAT;
+ state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
+ } else {
+ state->current_command = SMTP_COMMAND_OTHER_CMD;
+ }
+
+ /* Every command is inserted into a command buffer, to be matched
+ * against reply(ies) sent by the server */
+ if (SMTPInsertCommandIntoCommandBuffer(state->current_command,
+ state, f) == -1) {
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(r);
+ }
+
+ switch (state->current_command) {
+ case SMTP_COMMAND_STARTTLS:
+ return SMTPProcessCommandSTARTTLS(state, f, pstate);
+
+ case SMTP_COMMAND_DATA:
+ return SMTPProcessCommandDATA(state, f, pstate);
+
+ case SMTP_COMMAND_BDAT:
+ return SMTPProcessCommandBDAT(state, f, pstate);
+
+ default:
+ /* we have nothing to do with any other command at this instant.
+ * Just let it go through */
+ SCReturnInt(0);
+ }
+}
+
+static int SMTPParse(int direction, Flow *f, SMTPState *state,
+ AppLayerParserState *pstate, uint8_t *input,
+ uint32_t input_len,
+ PatternMatcherQueue *local_data)
+{
+ SCEnter();
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ state->input = input;
+ state->input_len = input_len;
+ state->direction = direction;
+ state->thread_local_data = local_data;
+
+ /* toserver */
+ if (direction == 0) {
+ while (SMTPGetLine(state) >= 0) {
+ if (SMTPProcessRequest(state, f, pstate) == -1)
+ SCReturnInt(-1);
+ }
+
+ /* toclient */
+ } else {
+ while (SMTPGetLine(state) >= 0) {
+ if (SMTPProcessReply(state, f, pstate) == -1)
+ SCReturnInt(-1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+static int SMTPParseClientRecord(Flow *f, void *alstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+
+ /* first arg 0 is toserver */
+ return SMTPParse(0, f, alstate, pstate, input, input_len, local_data);
+}
+
+static int SMTPParseServerRecord(Flow *f, void *alstate,
+ AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCEnter();
+
+ /* first arg 1 is toclient */
+ return SMTPParse(1, f, alstate, pstate, input, input_len, local_data);
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Function to allocate SMTP state memory.
+ */
+void *SMTPStateAlloc(void)
+{
+ SMTPState *smtp_state = SCMalloc(sizeof(SMTPState));
+ if (unlikely(smtp_state == NULL))
+ return NULL;
+ memset(smtp_state, 0, sizeof(SMTPState));
+
+ smtp_state->cmds = SCMalloc(sizeof(uint8_t) *
+ SMTP_COMMAND_BUFFER_STEPS);
+ if (smtp_state->cmds == NULL) {
+ SCFree(smtp_state);
+ return NULL;
+ }
+ smtp_state->cmds_buffer_len = SMTP_COMMAND_BUFFER_STEPS;
+
+ TAILQ_INIT(&smtp_state->tx_list);
+
+ return smtp_state;
+}
+
+static void *SMTPLocalStorageAlloc(void)
+{
+ /* needed by the mpm */
+ PatternMatcherQueue *pmq = SCMalloc(sizeof(PatternMatcherQueue));
+ if (unlikely(pmq == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ PmqSetup(pmq,
+ sizeof(smtp_reply_map)/sizeof(SCEnumCharMap) - 2);
+
+ return pmq;
+}
+
+static void SMTPLocalStorageFree(void *pmq)
+{
+ if (pmq != NULL) {
+ PmqFree(pmq);
+ SCFree(pmq);
+ }
+
+ return;
+}
+
+static void SMTPTransactionFree(SMTPTransaction *tx, SMTPState *state)
+{
+ if (tx->mime_state != NULL) {
+ MimeDecDeInitParser(tx->mime_state);
+ }
+ /* Free list of MIME message recursively */
+ MimeDecFreeEntity(tx->msg_head);
+
+ if (tx->decoder_events != NULL)
+ AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
+
+ if (tx->de_state != NULL)
+ DetectEngineStateFree(tx->de_state);
+#if 0
+ if (tx->decoder_events->cnt <= smtp_state->events)
+ smtp_state->events -= tx->decoder_events->cnt;
+ else
+ smtp_state->events = 0;
+#endif
+ SCFree(tx);
+}
+
+/**
+ * \internal
+ * \brief Function to free SMTP state memory.
+ */
+static void SMTPStateFree(void *p)
+{
+ SMTPState *smtp_state = (SMTPState *)p;
+
+ if (smtp_state->cmds != NULL) {
+ SCFree(smtp_state->cmds);
+ }
+ if (smtp_state->ts_current_line_db) {
+ SCFree(smtp_state->ts_db);
+ }
+ if (smtp_state->tc_current_line_db) {
+ SCFree(smtp_state->tc_db);
+ }
+
+ FileContainerFree(smtp_state->files_ts);
+
+ SMTPTransaction *tx = NULL;
+ while ((tx = TAILQ_FIRST(&smtp_state->tx_list))) {
+ TAILQ_REMOVE(&smtp_state->tx_list, tx, next);
+ SMTPTransactionFree(tx, smtp_state);
+ }
+
+ SCFree(smtp_state);
+
+ return;
+}
+
+static void SMTPSetMpmState(void)
+{
+ smtp_mpm_ctx = SCMalloc(sizeof(MpmCtx));
+ if (unlikely(smtp_mpm_ctx == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(smtp_mpm_ctx, 0, sizeof(MpmCtx));
+ MpmInitCtx(smtp_mpm_ctx, SMTP_MPM);
+
+ smtp_mpm_thread_ctx = SCMalloc(sizeof(MpmThreadCtx));
+ if (unlikely(smtp_mpm_thread_ctx == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(smtp_mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitThreadCtx(smtp_mpm_thread_ctx, SMTP_MPM, 0);
+
+ uint32_t i = 0;
+ for (i = 0; i < sizeof(smtp_reply_map)/sizeof(SCEnumCharMap) - 1; i++) {
+ SCEnumCharMap *map = &smtp_reply_map[i];
+ /* The third argument is 3, because reply code is always 3 bytes. */
+ MpmAddPatternCI(smtp_mpm_ctx, (uint8_t *)map->enum_name, 3,
+ 0 /* defunct */, 0 /* defunct */,
+ i /* pattern id */, 0, 0 /* no flags */);
+ }
+
+ mpm_table[SMTP_MPM].Prepare(smtp_mpm_ctx);
+}
+
+int SMTPStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, smtp_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "smtp's enum map table.", event_name);
+ /* yes this is fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+static int SMTPRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
+ "EHLO", 4, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
+ "HELO", 4, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
+ "QUIT", 4, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void SMTPStateTransactionFree (void *state, uint64_t tx_id)
+{
+ SMTPState *smtp_state = state;
+ SMTPTransaction *tx = NULL;
+ TAILQ_FOREACH(tx, &smtp_state->tx_list, next) {
+ if (tx_id < tx->tx_id)
+ break;
+ else if (tx_id > tx->tx_id)
+ continue;
+
+ if (tx == smtp_state->curr_tx)
+ smtp_state->curr_tx = NULL;
+ TAILQ_REMOVE(&smtp_state->tx_list, tx, next);
+ SMTPTransactionFree(tx, state);
+ break;
+ }
+
+
+}
+
+/** \retval cnt highest tx id */
+static uint64_t SMTPStateGetTxCnt(void *state)
+{
+ uint64_t cnt = 0;
+ SMTPState *smtp_state = state;
+ if (smtp_state) {
+ cnt = smtp_state->tx_cnt;
+ }
+ SCLogDebug("returning %"PRIu64, cnt);
+ return cnt;
+}
+
+static void *SMTPStateGetTx(void *state, uint64_t id)
+{
+ SMTPState *smtp_state = state;
+ if (smtp_state) {
+ SMTPTransaction *tx = NULL;
+
+ if (smtp_state->curr_tx == NULL)
+ return NULL;
+ if (smtp_state->curr_tx->tx_id == id)
+ return smtp_state->curr_tx;
+
+ TAILQ_FOREACH(tx, &smtp_state->tx_list, next) {
+ if (tx->tx_id == id)
+ return tx;
+ }
+ }
+ return NULL;
+
+}
+
+static int SMTPStateGetAlstateProgressCompletionStatus(uint8_t direction) {
+ return 1;
+}
+
+static int SMTPStateGetAlstateProgress(void *vtx, uint8_t direction)
+{
+ SMTPTransaction *tx = vtx;
+ return tx->done;
+}
+
+static FileContainer *SMTPStateGetFiles(void *state, uint8_t direction)
+{
+ if (state == NULL)
+ return NULL;
+
+ SMTPState *smtp_state = (SMTPState *)state;
+
+ if (direction & STREAM_TOCLIENT) {
+ SCReturnPtr(NULL, "FileContainer");
+ } else {
+ SCLogDebug("smtp_state->files_ts %p", smtp_state->files_ts);
+ SCReturnPtr(smtp_state->files_ts, "FileContainer");
+ }
+}
+
+static void SMTPStateTruncate(void *state, uint8_t direction)
+{
+ FileContainer *fc = SMTPStateGetFiles(state, direction);
+ if (fc != NULL) {
+ SCLogDebug("truncating stream, closing files in %s direction (container %p)",
+ direction & STREAM_TOCLIENT ? "STREAM_TOCLIENT" : "STREAM_TOSERVER", fc);
+ FileTruncateAllOpenFiles(fc);
+ }
+}
+
+static AppLayerDecoderEvents *SMTPGetEvents(void *state, uint64_t tx_id)
+{
+ SCLogDebug("get SMTP events for TX %"PRIu64, tx_id);
+
+ SMTPTransaction *tx = SMTPStateGetTx(state, tx_id);
+ if (tx != NULL) {
+ return tx->decoder_events;
+ }
+ return NULL;
+}
+
+static DetectEngineState *SMTPGetTxDetectState(void *vtx)
+{
+ SMTPTransaction *tx = (SMTPTransaction *)vtx;
+ return tx->de_state;
+}
+
+static int SMTPSetTxDetectState(void *state, void *vtx, DetectEngineState *s)
+{
+ SMTPTransaction *tx = (SMTPTransaction *)vtx;
+ tx->de_state = s;
+ return 0;
+}
+
+/**
+ * \brief Register the SMTP Protocol parser.
+ */
+void RegisterSMTPParsers(void)
+{
+ char *proto_name = "smtp";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_SMTP, proto_name);
+ if (SMTPRegisterPatternsForProtocolDetection() < 0 )
+ return;
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateAlloc, SMTPStateFree);
+
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOSERVER,
+ SMTPParseClientRecord);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOCLIENT,
+ SMTPParseServerRecord);
+
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetEventInfo);
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetEvents);
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_SMTP, NULL,
+ SMTPGetTxDetectState, SMTPSetTxDetectState);
+
+ AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPLocalStorageAlloc,
+ SMTPLocalStorageFree);
+
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateTransactionFree);
+ AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetFiles);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetAlstateProgress);
+ AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTxCnt);
+ AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTx);
+ AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, ALPROTO_SMTP,
+ SMTPStateGetAlstateProgressCompletionStatus);
+ AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateTruncate);
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+ SMTPSetMpmState();
+
+ SMTPConfigure();
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SMTP, SMTPParserRegisterTests);
+#endif
+ return;
+}
+
+/***************************************Unittests******************************/
+
+#ifdef UNITTESTS
+
+/*
+ * \test Test STARTTLS.
+ */
+int SMTPParserTest01(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
+ 0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
+ 0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
+ 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO [192.168.0.158]<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x5b, 0x31, 0x39,
+ 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e,
+ 0x31, 0x35, 0x38, 0x5d, 0x0d, 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+ /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
+ * 250-SIZE 35882577<CR><LF>
+ * 250-8BITMIME<CR><LF>
+ * 250-STARTTLS<CR><LF>
+ * 250 ENHANCEDSTATUSCODES<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x6d, 0x78, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75,
+ 0x72, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x2c, 0x20, 0x5b, 0x31, 0x31, 0x37, 0x2e,
+ 0x31, 0x39, 0x38, 0x2e, 0x31, 0x31, 0x35, 0x2e,
+ 0x35, 0x30, 0x5d, 0x0d, 0x0a, 0x32, 0x35, 0x30,
+ 0x2d, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x33, 0x35,
+ 0x38, 0x38, 0x32, 0x35, 0x37, 0x37, 0x0d, 0x0a,
+ 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49, 0x54,
+ 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x54,
+ 0x4c, 0x53, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x20,
+ 0x45, 0x4e, 0x48, 0x41, 0x4e, 0x43, 0x45, 0x44,
+ 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x43, 0x4f,
+ 0x44, 0x45, 0x53, 0x0d, 0x0a
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* STARTTLS<CR><LF> */
+ uint8_t request2[] = {
+ 0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
+ 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+ /* 220 2.0.0 Ready to start TLS<CR><LF> */
+ uint8_t reply2[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x52, 0x65, 0x61, 0x64, 0x79, 0x20,
+ 0x74, 0x6f, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x20, 0x54, 0x4c, 0x53, 0x0d, 0x0a
+ };
+ uint32_t reply2_len = sizeof(reply2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_STARTTLS ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION) ||
+ !(ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !(((TcpSession *)f.protoctx)->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ !(((TcpSession *)f.protoctx)->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Test multiple DATA commands(full mail transactions).
+ */
+int SMTPParserTest02(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
+ 0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
+ 0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
+ 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+ /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
+ * 250-SIZE 35882577<CR><LF>
+ * 250-8BITMIME<CR><LF>
+ * 250-STARTTLS<CR><LF>
+ * 250 ENHANCEDSTATUSCODES<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
+ 0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
+ 0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
+ 0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
+ 0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
+ 0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
+ 0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
+ 0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* MAIL FROM:asdff@asdf.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
+ 0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+ /* 250 2.1.0 Ok<CR><LF> */
+ uint8_t reply2[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply2_len = sizeof(reply2);
+
+ /* RCPT TO:bimbs@gmail.com<CR><LF> */
+ uint8_t request3[] = {
+ 0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
+ 0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
+ 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
+ 0x0a
+ };
+ uint32_t request3_len = sizeof(request3);
+ /* 250 2.1.5 Ok<CR><LF> */
+ uint8_t reply3[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply3_len = sizeof(reply3);
+
+ /* DATA<CR><LF> */
+ uint8_t request4[] = {
+ 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
+ };
+ uint32_t request4_len = sizeof(request4);
+ /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
+ uint8_t reply4[] = {
+ 0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
+ 0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
+ 0x4c, 0x46, 0x3e, 0x0d, 0x0a
+ };
+ uint32_t reply4_len = sizeof(reply4);
+
+ /* FROM:asdff@asdf.com<CR><LF> */
+ uint8_t request5_1[] = {
+ 0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
+ 0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request5_1_len = sizeof(request5_1);
+ /* TO:bimbs@gmail.com<CR><LF> */
+ uint8_t request5_2[] = {
+ 0x54, 0x4f, 0x3a, 0x62, 0x69, 0x6d, 0x62, 0x73,
+ 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request5_2_len = sizeof(request5_2);
+ /* <CR><LF> */
+ uint8_t request5_3[] = {
+ 0x0d, 0x0a
+ };
+ uint32_t request5_3_len = sizeof(request5_3);
+ /* this is test mail1<CR><LF> */
+ uint8_t request5_4[] = {
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x69,
+ 0x6c, 0x31, 0x0d, 0x0a
+ };
+ uint32_t request5_4_len = sizeof(request5_4);
+ /* .<CR><LF> */
+ uint8_t request5_5[] = {
+ 0x2e, 0x0d, 0x0a
+ };
+ uint32_t request5_5_len = sizeof(request5_5);
+ /* 250 2.0.0 Ok: queued as 6A1AF20BF2<CR><LF> */
+ uint8_t reply5[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
+ 0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
+ 0x36, 0x41, 0x31, 0x41, 0x46, 0x32, 0x30, 0x42,
+ 0x46, 0x32, 0x0d, 0x0a
+ };
+ uint32_t reply5_len = sizeof(reply5);
+
+ /* MAIL FROM:asdfg@asdf.com<CR><LF> */
+ uint8_t request6[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x67, 0x40,
+ 0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a
+ };
+ uint32_t request6_len = sizeof(request6);
+ /* 250 2.1.0 Ok<CR><LF> */
+ uint8_t reply6[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply6_len = sizeof(reply6);
+
+ /* RCPT TO:bimbs@gmail.com<CR><LF> */
+ uint8_t request7[] = {
+ 0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
+ 0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
+ 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
+ 0x0a
+ };
+ uint32_t request7_len = sizeof(request7);
+ /* 250 2.1.5 Ok<CR><LF> */
+ uint8_t reply7[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply7_len = sizeof(reply7);
+
+ /* DATA<CR><LF> */
+ uint8_t request8[] = {
+ 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
+ };
+ uint32_t request8_len = sizeof(request8);
+ /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
+ uint8_t reply8[] = {
+ 0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
+ 0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
+ 0x4c, 0x46, 0x3e, 0x0d, 0x0a
+ };
+ uint32_t reply8_len = sizeof(reply8);
+
+ /* FROM:asdfg@gmail.com<CR><LF> */
+ uint8_t request9_1[] = {
+ 0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
+ 0x66, 0x67, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request9_1_len = sizeof(request9_1);
+ /* TO:bimbs@gmail.com<CR><LF> */
+ uint8_t request9_2[] = {
+ 0x54, 0x4f, 0x3a, 0x62, 0x69, 0x6d, 0x62, 0x73,
+ 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request9_2_len = sizeof(request9_2);
+ /* <CR><LF> */
+ uint8_t request9_3[] = {
+ 0x0d, 0x0a
+ };
+ uint32_t request9_3_len = sizeof(request9_3);
+ /* this is test mail2<CR><LF> */
+ uint8_t request9_4[] = {
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x69,
+ 0x6c, 0x32, 0x0d, 0x0a
+ };
+ uint32_t request9_4_len = sizeof(request9_4);
+ /* .<CR><LF> */
+ uint8_t request9_5[] = {
+ 0x2e, 0x0d, 0x0a
+ };
+ uint32_t request9_5_len = sizeof(request9_5);
+ /* 250 2.0.0 Ok: queued as 28CFF20BF2<CR><LF> */
+ uint8_t reply9[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
+ 0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
+ 0x32, 0x38, 0x43, 0x46, 0x46, 0x32, 0x30, 0x42,
+ 0x46, 0x32, 0x0d, 0x0a
+ };
+ uint32_t reply9_len = sizeof(reply9);
+
+ /* QUIT<CR><LF> */
+ uint8_t request10[] = {
+ 0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a
+ };
+ uint32_t request10_len = sizeof(request10);
+ /* 221 2.0.0 Bye<CR><LF> */
+ uint8_t reply10[] = {
+ 0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
+ };
+ uint32_t reply10_len = sizeof(reply10);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply3, reply3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request4, request4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply4, reply4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5_1, request5_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5_2, request5_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5_3, request5_3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5_4, request5_4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5_5, request5_5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply5, reply5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request6, request6_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply6, reply6_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request7, request7_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply7, reply7_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request8, request8_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply8, reply8_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request9_1, request9_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request9_2, request9_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request9_3, request9_3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request9_4, request9_4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request9_5, request9_5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply9, reply9_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request10, request10_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply10, reply10_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Testing parsing pipelined commands.
+ */
+int SMTPParserTest03(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
+ 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
+ 0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+ /* 250-poona_slack_vm1.localdomain<CR><LF>
+ * 250-PIPELINING<CR><LF>
+ * 250-SIZE 10240000<CR><LF>
+ * 250-VRFY<CR><LF>
+ * 250-ETRN<CR><LF>
+ * 250-ENHANCEDSTATUSCODES<CR><LF>
+ * 250-8BITMIME<CR><LF>
+ * 250 DSN<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
+ 0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
+ 0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
+ 0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
+ 0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
+ 0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
+ 0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
+ 0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* MAIL FROM:pbsf@asdfs.com<CR><LF>
+ * RCPT TO:pbsf@asdfs.com<CR><LF>
+ * DATA<CR><LF>
+ */
+ uint8_t request2[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
+ 0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a, 0x52, 0x43, 0x50, 0x54, 0x20, 0x54,
+ 0x4f, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
+ 0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+ /* 250 2.1.0 Ok<CR><LF>
+ * 250 2.1.5 Ok<CR><LF>
+ * 354 End data with <CR><LF>.<CR><LF>|<CR><LF>|
+ */
+ uint8_t reply2[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e, 0x35, 0x20,
+ 0x4f, 0x6b, 0x0d, 0x0a, 0x33, 0x35, 0x34, 0x20,
+ 0x45, 0x6e, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x3c, 0x43,
+ 0x52, 0x3e, 0x3c, 0x4c, 0x46, 0x3e, 0x2e, 0x3c,
+ 0x43, 0x52, 0x3e, 0x3c, 0x4c, 0x46, 0x3e, 0x0d,
+ 0x0a
+ };
+ uint32_t reply2_len = sizeof(reply2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 3 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->cmds[1] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->cmds[2] != SMTP_COMMAND_DATA ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test smtp with just <LF> delimter instead of <CR><LF>.
+ */
+int SMTPParserTest04(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
+ 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
+ 0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
+ 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
+ 0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test STARTTLS fail.
+ */
+int SMTPParserTest05(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
+ 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
+ 0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+ /* 250-poona_slack_vm1.localdomain<CR><LF>
+ * 250-PIPELINING<CR><LF>
+ * 250-SIZE 10240000<CR><LF>
+ * 250-VRFY<CR><LF>
+ * 250-ETRN<CR><LF>
+ * 250-ENHANCEDSTATUSCODES<CR><LF>
+ * 250-8BITMIME<CR><LF>
+ * 250 DSN<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
+ 0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
+ 0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
+ 0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
+ 0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
+ 0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
+ 0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
+ 0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* STARTTLS<CR><LF> */
+ uint8_t request2[] = {
+ 0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
+ 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+ /* 502 5.5.2 Error: command not recognized<CR><LF> */
+ uint8_t reply2[] = {
+ 0x35, 0x30, 0x32, 0x20, 0x35, 0x2e, 0x35, 0x2e,
+ 0x32, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x3a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+ 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x63,
+ 0x6f, 0x67, 0x6e, 0x69, 0x7a, 0x65, 0x64, 0x0d,
+ 0x0a
+ };
+ uint32_t reply2_len = sizeof(reply2);
+
+ /* QUIT<CR><LF> */
+ uint8_t request3[] = {
+ 0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a
+
+ };
+ uint32_t request3_len = sizeof(request3);
+ /* 221 2.0.0 Bye<CR><LF> */
+ uint8_t reply3[] = {
+ 0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
+ };
+ uint32_t reply3_len = sizeof(reply3);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_STARTTLS ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ if ((f.flags & FLOW_NOPAYLOAD_INSPECTION) ||
+ (ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ (((TcpSession *)f.protoctx)->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (((TcpSession *)f.protoctx)->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply3, reply3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Test multiple DATA commands(full mail transactions).
+ */
+int SMTPParserTest06(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x62, 0x61, 0x79, 0x30,
+ 0x2d, 0x6d, 0x63, 0x36, 0x2d, 0x66, 0x31, 0x30,
+ 0x2e, 0x62, 0x61, 0x79, 0x30, 0x2e, 0x68, 0x6f,
+ 0x74, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+ 0x67, 0x20, 0x75, 0x6e, 0x73, 0x6f, 0x6c, 0x69,
+ 0x63, 0x69, 0x74, 0x65, 0x64, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c,
+ 0x20, 0x6f, 0x72, 0x20, 0x62, 0x75, 0x6c, 0x6b,
+ 0x20, 0x65, 0x2d, 0x6d, 0x61, 0x69, 0x6c, 0x20,
+ 0x74, 0x6f, 0x20, 0x4d, 0x69, 0x63, 0x72, 0x6f,
+ 0x73, 0x6f, 0x66, 0x74, 0x27, 0x73, 0x20, 0x63,
+ 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x20,
+ 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x68, 0x69,
+ 0x62, 0x69, 0x74, 0x65, 0x64, 0x2e, 0x20, 0x4f,
+ 0x74, 0x68, 0x65, 0x72, 0x20, 0x72, 0x65, 0x73,
+ 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x66, 0x6f,
+ 0x75, 0x6e, 0x64, 0x20, 0x61, 0x74, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72,
+ 0x69, 0x76, 0x61, 0x63, 0x79, 0x2e, 0x6d, 0x73,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6e,
+ 0x74, 0x69, 0x2d, 0x73, 0x70, 0x61, 0x6d, 0x2f,
+ 0x2e, 0x20, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
+ 0x20, 0x69, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x65, 0x71, 0x75, 0x69, 0x70,
+ 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x63,
+ 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e,
+ 0x69, 0x61, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f,
+ 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x74, 0x61,
+ 0x74, 0x65, 0x73, 0x2e, 0x20, 0x46, 0x72, 0x69,
+ 0x2c, 0x20, 0x31, 0x36, 0x20, 0x46, 0x65, 0x62,
+ 0x20, 0x32, 0x30, 0x30, 0x37, 0x20, 0x30, 0x35,
+ 0x3a, 0x30, 0x33, 0x3a, 0x32, 0x33, 0x20, 0x2d,
+ 0x30, 0x38, 0x30, 0x30, 0x20, 0x0d, 0x0a
+ };
+ uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x45, 0x58, 0x43,
+ 0x48, 0x41, 0x4e, 0x47, 0x45, 0x32, 0x2e, 0x63,
+ 0x67, 0x63, 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x69,
+ 0x61, 0x6d, 0x69, 0x2e, 0x65, 0x64, 0x75, 0x0d,
+ 0x0a
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x62, 0x61, 0x79, 0x30,
+ 0x2d, 0x6d, 0x63, 0x36, 0x2d, 0x66, 0x31, 0x30,
+ 0x2e, 0x62, 0x61, 0x79, 0x30, 0x2e, 0x68, 0x6f,
+ 0x74, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x28, 0x33, 0x2e, 0x33, 0x2e, 0x31,
+ 0x2e, 0x34, 0x29, 0x20, 0x48, 0x65, 0x6c, 0x6c,
+ 0x6f, 0x20, 0x5b, 0x31, 0x32, 0x39, 0x2e, 0x31,
+ 0x37, 0x31, 0x2e, 0x33, 0x32, 0x2e, 0x35, 0x39,
+ 0x5d, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53,
+ 0x49, 0x5a, 0x45, 0x20, 0x32, 0x39, 0x36, 0x39,
+ 0x36, 0x30, 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x50, 0x49, 0x50, 0x45, 0x4c, 0x49,
+ 0x4e, 0x49, 0x4e, 0x47, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x38, 0x62, 0x69, 0x74, 0x6d, 0x69,
+ 0x6d, 0x65, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x4d, 0x49,
+ 0x4d, 0x45, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x49, 0x4e, 0x47,
+ 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x41, 0x55,
+ 0x54, 0x48, 0x20, 0x4c, 0x4f, 0x47, 0x49, 0x4e,
+ 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x41, 0x55,
+ 0x54, 0x48, 0x3d, 0x4c, 0x4f, 0x47, 0x49, 0x4e,
+ 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x20, 0x4f, 0x4b,
+ 0x0d, 0x0a
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* MAIL FROM:asdff@asdf.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
+ 0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+ /* 250 2.1.0 Ok<CR><LF> */
+ uint8_t reply2[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply2_len = sizeof(reply2);
+
+ /* RCPT TO:bimbs@gmail.com<CR><LF> */
+ uint8_t request3[] = {
+ 0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
+ 0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
+ 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
+ 0x0a
+ };
+ uint32_t request3_len = sizeof(request3);
+ /* 250 2.1.5 Ok<CR><LF> */
+ uint8_t reply3[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ uint32_t reply3_len = sizeof(reply3);
+
+ /* BDAT 51<CR><LF> */
+ uint8_t request4[] = {
+ 0x42, 0x44, 0x41, 0x54, 0x20, 0x35, 0x31, 0x0d,
+ 0x0a,
+ };
+ uint32_t request4_len = sizeof(request4);
+
+ uint8_t request5[] = {
+ 0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
+ 0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
+ 0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
+ 0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x0d, 0x0a,
+ };
+ uint32_t request5_len = sizeof(request5);
+
+ uint8_t request6[] = {
+ 0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
+ 0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
+ 0x66, 0x0d, 0x0a,
+ };
+ uint32_t request6_len = sizeof(request6);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply3, reply3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request4, request4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_BDAT ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE) ||
+ smtp_state->bdat_chunk_len != 51 ||
+ smtp_state->bdat_chunk_idx != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5, request5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE) ||
+ smtp_state->bdat_chunk_len != 51 ||
+ smtp_state->bdat_chunk_idx != 32) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request6, request6_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN ||
+ smtp_state->bdat_chunk_len != 51 ||
+ smtp_state->bdat_chunk_idx != 51) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test retrieving lines when frag'ed.
+ */
+int SMTPParserTest07(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ const char *request1_str = "EHLO boo.com";
+ /* EHLO boo.com<CR> */
+ uint8_t request1_1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
+ };
+ int32_t request1_1_len = sizeof(request1_1);
+
+ /* <LF> */
+ uint8_t request1_2[] = {
+ 0x0a
+ };
+ int32_t request1_2_len = sizeof(request1_2);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_1, request1_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->current_line != NULL ||
+ smtp_state->current_line_len != 0 ||
+ smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != request1_1_len ||
+ memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_2, request1_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
+ smtp_state->current_line != smtp_state->ts_db ||
+ smtp_state->current_line_len != smtp_state->ts_db_len) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 0 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test retrieving lines when frag'ed.
+ */
+int SMTPParserTest08(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ const char *request1_str = "EHLO boo.com";
+ /* EHLO boo.com */
+ uint8_t request1_1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d,
+ };
+ int32_t request1_1_len = sizeof(request1_1);
+
+ /* <CR><LF> */
+ uint8_t request1_2[] = {
+ 0x0d, 0x0a
+ };
+ int32_t request1_2_len = sizeof(request1_2);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_1, request1_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->current_line != NULL ||
+ smtp_state->current_line_len != 0 ||
+ smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != request1_1_len ||
+ memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_2, request1_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
+ smtp_state->current_line != smtp_state->ts_db ||
+ smtp_state->current_line_len != smtp_state->ts_db_len) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 0 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test retrieving lines when frag'ed.
+ */
+int SMTPParserTest09(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ const char *request1_str = "EHLO boo.com";
+ /* EHLO boo. */
+ uint8_t request1_1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e,
+ };
+ int32_t request1_1_len = sizeof(request1_1);
+
+ /* com<CR><LF> */
+ uint8_t request1_2[] = {
+ 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ int32_t request1_2_len = sizeof(request1_2);
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_1, request1_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->current_line != NULL ||
+ smtp_state->current_line_len != 0 ||
+ smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != request1_1_len ||
+ memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_2, request1_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
+ smtp_state->current_line != smtp_state->ts_db ||
+ smtp_state->current_line_len != smtp_state->ts_db_len) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 0 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test retrieving lines when frag'ed.
+ */
+int SMTPParserTest10(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ const char *request1_str = "";
+ /* EHLO boo. */
+ uint8_t request1_1[] = {
+ 0x0d,
+ };
+ int32_t request1_1_len = sizeof(request1_1);
+
+ /* com<CR><LF> */
+ uint8_t request1_2[] = {
+ 0x0a,
+ };
+ int32_t request1_2_len = sizeof(request1_2);
+
+ const char *request2_str = "EHLO boo.com";
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_1, request1_1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->current_line != NULL ||
+ smtp_state->current_line_len != 0 ||
+ smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != request1_1_len ||
+ memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1_2, request1_2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 1 ||
+ smtp_state->ts_db == NULL ||
+ smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
+ memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
+ smtp_state->current_line != smtp_state->ts_db ||
+ smtp_state->current_line_len != smtp_state->ts_db_len) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 0 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != (int32_t)strlen(request2_str) ||
+ memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/*
+ * \test Test retrieving lines when frag'ed.
+ */
+int SMTPParserTest11(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ const char *request1_str = "";
+ /* EHLO boo. */
+ uint8_t request1[] = {
+ 0x0a,
+ };
+ int32_t request1_len = sizeof(request1);
+
+ const char *request2_str = "EHLO boo.com";
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request2[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != 0 ||
+ smtp_state->ts_current_line_db == 1 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->ts_current_line_db != 0 ||
+ smtp_state->ts_db != NULL ||
+ smtp_state->ts_db_len != 0 ||
+ smtp_state->current_line == NULL ||
+ smtp_state->current_line_len != (int32_t)strlen(request2_str) ||
+ memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) {
+ printf("smtp parser in inconsistent state\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+int SMTPParserTest12(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ SMTPState *smtp_state = NULL;
+ int r = 0;
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request1_len = sizeof(request1);
+
+ /* 388<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x31, 0x38, 0x38, 0x0d, 0x0a,
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SMTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"SMTP event handling\"; "
+ "app-layer-event: smtp.invalid_reply; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER | STREAM_START,
+ request1, request1_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched. It shouldn't match: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT | STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r == 0) {
+ printf("AppLayerParse for smtp failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+int SMTPParserTest13(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ SMTPState *smtp_state = NULL;
+ int r = 0;
+
+ /* EHLO boo.com<CR><LF> */
+ uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
+ };
+ int32_t request1_len = sizeof(request1);
+
+ /* 250<CR><LF>
+ */
+ uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x0d, 0x0a,
+ };
+ uint32_t reply1_len = sizeof(reply1);
+
+ /* MAIL FROM:pbsf@asdfs.com<CR><LF>
+ * RCPT TO:pbsf@asdfs.com<CR><LF>
+ * DATA<CR><LF>
+ * STARTTLS<CR><LF>
+ */
+ uint8_t request2[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
+ 0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a, 0x52, 0x43, 0x50, 0x54, 0x20, 0x54,
+ 0x4f, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
+ 0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a,
+ 0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
+ 0x0d, 0x0a
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SMTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SMTP event handling\"; "
+ "app-layer-event: "
+ "smtp.invalid_pipelined_sequence; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER | STREAM_START,
+ request1, request1_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched. It shouldn't match: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched. It shouldn't match: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match. Should have matched: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test DATA command w/MIME message.
+ */
+int SMTPParserTest14(void)
+{
+ int result = 0;
+ Flow f;
+ int r = 0;
+
+ /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
+ static uint8_t welcome_reply[] = {
+ 0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
+ 0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
+ 0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
+ 0x0d, 0x0a
+ };
+ static uint32_t welcome_reply_len = sizeof(welcome_reply);
+
+ /* EHLO boo.com<CR><LF> */
+ static uint8_t request1[] = {
+ 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
+ };
+ static uint32_t request1_len = sizeof(request1);
+ /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
+ * 250-SIZE 35882577<CR><LF>
+ * 250-8BITMIME<CR><LF>
+ * 250-STARTTLS<CR><LF>
+ * 250 ENHANCEDSTATUSCODES<CR><LF>
+ */
+ static uint8_t reply1[] = {
+ 0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
+ 0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
+ 0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
+ 0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
+ 0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
+ 0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
+ 0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
+ 0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
+ 0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
+ 0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
+ 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
+ 0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
+ 0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
+ };
+ static uint32_t reply1_len = sizeof(reply1);
+
+ /* MAIL FROM:asdff@asdf.com<CR><LF> */
+ static uint8_t request2[] = {
+ 0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
+ 0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
+ 0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x0d, 0x0a
+ };
+ static uint32_t request2_len = sizeof(request2);
+ /* 250 2.1.0 Ok<CR><LF> */
+ static uint8_t reply2[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ static uint32_t reply2_len = sizeof(reply2);
+
+ /* RCPT TO:bimbs@gmail.com<CR><LF> */
+ static uint8_t request3[] = {
+ 0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
+ 0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
+ 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
+ 0x0a
+ };
+ static uint32_t request3_len = sizeof(request3);
+ /* 250 2.1.5 Ok<CR><LF> */
+ static uint8_t reply3[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
+ 0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
+ };
+ static uint32_t reply3_len = sizeof(reply3);
+
+ /* DATA<CR><LF> */
+ static uint8_t request4[] = {
+ 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
+ };
+ static uint32_t request4_len = sizeof(request4);
+ /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
+ static uint8_t reply4[] = {
+ 0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
+ 0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
+ 0x4c, 0x46, 0x3e, 0x0d, 0x0a
+ };
+ static uint32_t reply4_len = sizeof(reply4);
+
+ /* MIME_MSG */
+ static uint64_t filesize = 133;
+ static uint8_t request4_msg[] = {
+ 0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
+ 0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
+ 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74,
+ 0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61,
+ 0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73,
+ 0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F,
+ 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61,
+ 0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F,
+ 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x44, 0x69,
+ 0x73, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F,
+ 0x6E, 0x3A, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
+ 0x68, 0x6D, 0x65, 0x6E, 0x74, 0x3B, 0x20, 0x66,
+ 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+ 0x22, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x65, 0x78,
+ 0x65, 0x22, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x54,
+ 0x56, 0x6F, 0x41, 0x41, 0x46, 0x42, 0x46, 0x41,
+ 0x41, 0x42, 0x4D, 0x41, 0x51, 0x45, 0x41, 0x61,
+ 0x69, 0x70, 0x59, 0x77, 0x77, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
+ 0x41, 0x41, 0x44, 0x41, 0x51, 0x73, 0x42, 0x43,
+ 0x41, 0x41, 0x42, 0x41, 0x41, 0x43, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x48, 0x6B, 0x41, 0x41,
+ 0x41, 0x41, 0x4D, 0x41, 0x41, 0x41, 0x41, 0x65,
+ 0x51, 0x41, 0x41, 0x41, 0x41, 0x77, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x45, 0x41, 0x41, 0x42,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
+ 0x41, 0x42, 0x30, 0x41, 0x41, 0x41, 0x41, 0x49,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
+ 0x41, 0x45, 0x41, 0x41, 0x49, 0x67, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x67, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x42, 0x63, 0x58, 0x44, 0x59, 0x32, 0x4C,
+ 0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
+ 0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
+ 0x41, 0x3D, 0x3D, 0x0D,0x0A };
+ static uint32_t request4_msg_len = sizeof(request4_msg);
+
+ /* DATA COMPLETED */
+ static uint8_t request4_end[] = {
+ 0x0d, 0x0a, 0x2e, 0x0d, 0x0a
+ };
+ static uint32_t request4_end_len = sizeof(request4_end);
+ /* 250 2.0.0 Ok: queued as 6A1AF20BF2<CR><LF> */
+ static uint8_t reply4_end[] = {
+ 0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
+ 0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
+ 0x36, 0x41, 0x31, 0x41, 0x46, 0x32, 0x30, 0x42,
+ 0x46, 0x32, 0x0d, 0x0a
+ };
+ static uint32_t reply4_end_len = sizeof(reply4_end);
+
+ /* QUIT<CR><LF> */
+ static uint8_t request5[] = {
+ 0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a
+ };
+ static uint32_t request5_len = sizeof(request5);
+ /* 221 2.0.0 Bye<CR><LF> */
+ static uint8_t reply5[] = {
+ 0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
+ 0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
+ };
+ static uint32_t reply5_len = sizeof(reply5);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ /* Welcome reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ welcome_reply, welcome_reply_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SMTPState *smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request1, request1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* EHLO Reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply1, reply1_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* MAIL FROM Request */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request2, request2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* MAIL FROM Reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply2, reply2_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* RCPT TO Request */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request3, request3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* RCPT TO Reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply3, reply3_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ /* Enable mime decoding */
+ smtp_config.decode_mime = 1;
+ smtp_config.mime_config.decode_base64 = 1;
+ smtp_config.mime_config.decode_quoted_printable = 1;
+ MimeDecSetConfig(&smtp_config.mime_config);
+
+ SCMutexLock(&f.m);
+ /* DATA request */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request4, request4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* Data reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply4, reply4_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* DATA message */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request4_msg, request4_msg_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->curr_tx->mime_state == NULL || smtp_state->curr_tx->msg_head == NULL || /* MIME data structures */
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
+ SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* DATA . request */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request4_end, request4_end_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
+ smtp_state->curr_tx->mime_state == NULL || smtp_state->curr_tx->msg_head == NULL || /* MIME data structures */
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SMTPState *state = (SMTPState *) f.alstate;
+ FileContainer *files = state->files_ts;
+ if (files != NULL && files->head != NULL) {
+ File *file = files->head;
+
+ if(strncmp((const char *)file->name, "test.exe", 8) != 0){
+ printf("smtp-mime file name is incorrect");
+ goto end;
+ }
+ if(file->size != filesize){
+ printf("smtp-mime file size %"PRIu64" is incorrect", file->size);
+ goto end;
+ }
+ static uint8_t org_binary[] = {
+ 0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00,
+ 0x4C, 0x01, 0x01, 0x00, 0x6A, 0x2A, 0x58, 0xC3,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00,
+ 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, 0x36, 0x36,
+ 0x2E, 0x39, 0x33, 0x2E, 0x36, 0x38, 0x2E, 0x36,
+ 0x5C, 0x7A, 0x00, 0x00, 0x38,};
+ uint64_t z;
+ for (z=0; z < filesize; z++){
+ if(org_binary[z] != file->chunks_head->data[z]){
+ printf("smtp-mime file data incorrect\n");
+ goto end;
+ }
+ }
+ }
+
+ SCMutexLock(&f.m);
+ /* DATA . reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply4_end, reply4_end_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* QUIT Request */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER,
+ request5, request5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 1 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
+ smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ /* QUIT Reply */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOCLIENT,
+ reply5, reply5_len);
+ if (r != 0) {
+ printf("smtp check returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ if (smtp_state->input_len != 0 ||
+ smtp_state->cmds_cnt != 0 ||
+ smtp_state->cmds_idx != 0 ||
+ smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
+ printf("smtp parser in inconsistent state l.%d\n", __LINE__);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+int SMTPProcessDataChunkTest01(void){
+ Flow f;
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_FILE_NO_STORE_TS;
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ int ret;
+ ret = SMTPProcessDataChunk(NULL, 0, state);
+
+ return ret;
+}
+
+
+int SMTPProcessDataChunkTest02(void){
+ char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
+ 0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
+ 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74,
+ 0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61,
+ 0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73,
+ 0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F,
+ 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61,
+ 0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F,
+ 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x44, 0x69,
+ 0x73, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F,
+ 0x6E, 0x3A, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
+ 0x68, 0x6D, 0x65, 0x6E, 0x74, 0x3B, 0x20, 0x66,
+ 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+ 0x22, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x65, 0x78,
+ 0x65, 0x22, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x54,
+ 0x56, 0x6F, 0x41, 0x41, 0x46, 0x42, 0x46, 0x41,
+ 0x41, 0x42, 0x4D, 0x41, 0x51, 0x45, 0x41, 0x61,
+ 0x69, 0x70, 0x59, 0x77, 0x77, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
+ 0x41, 0x41, 0x44, 0x41, 0x51, 0x73, 0x42, 0x43,
+ 0x41, 0x41, 0x42, 0x41, 0x41, 0x43, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x48, 0x6B, 0x41, 0x41,
+ 0x41, 0x41, 0x4D, 0x41, 0x41, 0x41, 0x41, 0x65,
+ 0x51, 0x41, 0x41, 0x41, 0x41, 0x77, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x45, 0x41, 0x41, 0x42,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
+ 0x41, 0x42, 0x30, 0x41, 0x41, 0x41, 0x41, 0x49,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
+ 0x41, 0x45, 0x41, 0x41, 0x49, 0x67, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x67, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x42, 0x63, 0x58, 0x44, 0x59, 0x32, 0x4C,
+ 0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
+ 0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
+ 0x41, 0x3D, 0x3D, 0x0D, 0x0A,};
+
+ Flow f;
+ FLOW_INITIALIZE(&f);
+ f.alstate = SMTPStateAlloc();
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ state->body_begin = 1;
+ int ret;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
+
+
+ return ret;
+}
+
+
+
+int SMTPProcessDataChunkTest03(void){
+ char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, };
+ char mimemsg2[] = {0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, };
+ char mimemsg3[] = {0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
+ char mimemsg4[] = {0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A, };
+ char mimemsg5[] = {0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, };
+ char mimemsg6[] = {0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74, };
+ char mimemsg7[] = {0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61, };
+ char mimemsg8[] = {0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
+ char mimemsg9[] = {0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73, };
+ char mimemsg10[] = {0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F, };
+ char mimemsg11[] = {0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61, };
+ char mimemsg12[] = {0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F, };
+
+ Flow f;
+ FLOW_INITIALIZE(&f);
+ f.alstate = SMTPStateAlloc();
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ int ret;
+
+ state->body_begin = 1;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
+ if(ret) goto end;
+ state->body_begin = 0;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg2, sizeof(mimemsg2), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg3, sizeof(mimemsg3), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg4, sizeof(mimemsg4), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg5, sizeof(mimemsg5), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg6, sizeof(mimemsg6), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg7, sizeof(mimemsg7), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg8, sizeof(mimemsg8), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg9, sizeof(mimemsg9), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg10, sizeof(mimemsg10), state);
+ if(ret) goto end;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg11, sizeof(mimemsg11), state);
+ if(ret) goto end;
+ state->body_end = 1;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg12, sizeof(mimemsg12), state);
+ if(ret) goto end;
+
+ end:
+ return ret;
+}
+
+
+int SMTPProcessDataChunkTest04(void){
+ char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, };
+ char mimemsg2[] = {0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, };
+ char mimemsg3[] = {0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
+ char mimemsg4[] = {0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A, };
+ char mimemsg5[] = {0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, };
+ char mimemsg6[] = {0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74, };
+ char mimemsg7[] = {0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61, };
+ char mimemsg8[] = {0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
+ char mimemsg9[] = {0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73, };
+ char mimemsg10[] = {0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F, };
+ char mimemsg11[] = {0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61, };
+
+ Flow f;
+ FLOW_INITIALIZE(&f);
+ f.alstate = SMTPStateAlloc();
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ int ret = MIME_DEC_OK;
+
+ state->body_begin = 1;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg2, sizeof(mimemsg2), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg3, sizeof(mimemsg3), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg4, sizeof(mimemsg4), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg5, sizeof(mimemsg5), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg6, sizeof(mimemsg6), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg7, sizeof(mimemsg7), state) != 0) goto end;
+ state->body_begin = 0;
+ state->body_end = 1;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg8, sizeof(mimemsg8), state) != 0) goto end;
+ state->body_end = 0;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg9, sizeof(mimemsg9), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg10, sizeof(mimemsg10), state) != 0) goto end;
+ if(SMTPProcessDataChunk((uint8_t *)mimemsg11, sizeof(mimemsg11), state) != 0) goto end;
+
+ end:
+ return ret;
+}
+
+int SMTPProcessDataChunkTest05(void){
+ char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
+ 0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
+ 0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
+ 0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
+ 0x41, 0x3D, 0x3D, 0x0D, 0x0A,};
+
+ Flow f;
+ FLOW_INITIALIZE(&f);
+ f.alstate = SMTPStateAlloc();
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ state->body_begin = 1;
+ int ret;
+ uint64_t file_size = 0;
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
+ state->body_begin = 0;
+ if(ret){goto end;}
+ SMTPState *smtp_state = (SMTPState *)((Flow *)state->data)->alstate;
+ FileContainer *files = smtp_state->files_ts;
+ File *file = files->head;
+ file_size = file->size;
+
+ FileDisableStoring(&f, STREAM_TOSERVER);
+ FileDisableMagic(&f, STREAM_TOSERVER);
+ FileDisableMd5(&f, STREAM_TOSERVER);
+ ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
+ if(ret){goto end;}
+ printf("%u\t%u\n", (uint32_t) file->size, (uint32_t) file_size);
+ if(file->size == file_size){
+ return 0;
+ }else{
+ return 1;
+ }
+
+ end:
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void SMTPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SMTPParserTest01", SMTPParserTest01, 1);
+ UtRegisterTest("SMTPParserTest02", SMTPParserTest02, 1);
+ UtRegisterTest("SMTPParserTest03", SMTPParserTest03, 1);
+ UtRegisterTest("SMTPParserTest04", SMTPParserTest04, 1);
+ UtRegisterTest("SMTPParserTest05", SMTPParserTest05, 1);
+ UtRegisterTest("SMTPParserTest06", SMTPParserTest06, 1);
+ UtRegisterTest("SMTPParserTest07", SMTPParserTest07, 1);
+ UtRegisterTest("SMTPParserTest08", SMTPParserTest08, 1);
+ UtRegisterTest("SMTPParserTest09", SMTPParserTest09, 1);
+ UtRegisterTest("SMTPParserTest10", SMTPParserTest10, 1);
+ UtRegisterTest("SMTPParserTest11", SMTPParserTest11, 1);
+ UtRegisterTest("SMTPParserTest12", SMTPParserTest12, 1);
+ UtRegisterTest("SMTPParserTest13", SMTPParserTest13, 1);
+ UtRegisterTest("SMTPParserTest14", SMTPParserTest14, 1);
+ UtRegisterTest("SMTPProcessDataChunkTest01", SMTPProcessDataChunkTest01, 0);
+ UtRegisterTest("SMTPProcessDataChunkTest02", SMTPProcessDataChunkTest02, 0);
+ UtRegisterTest("SMTPProcessDataChunkTest03", SMTPProcessDataChunkTest03, 0);
+ UtRegisterTest("SMTPProcessDataChunkTest04", SMTPProcessDataChunkTest04, 0);
+ UtRegisterTest("SMTPProcessDataChunkTest05", SMTPProcessDataChunkTest05, 0);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/app-layer-smtp.h b/framework/src/suricata/src/app-layer-smtp.h
new file mode 100644
index 00000000..02090c61
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-smtp.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_SMTP_H__
+#define __APP_LAYER_SMTP_H__
+
+#include "decode-events.h"
+#include "util-decode-mime.h"
+#include "queue.h"
+
+enum {
+ SMTP_DECODER_EVENT_INVALID_REPLY,
+ SMTP_DECODER_EVENT_UNABLE_TO_MATCH_REPLY_WITH_REQUEST,
+ SMTP_DECODER_EVENT_MAX_COMMAND_LINE_LEN_EXCEEDED,
+ SMTP_DECODER_EVENT_MAX_REPLY_LINE_LEN_EXCEEDED,
+ SMTP_DECODER_EVENT_INVALID_PIPELINED_SEQUENCE,
+ SMTP_DECODER_EVENT_BDAT_CHUNK_LEN_EXCEEDED,
+ SMTP_DECODER_EVENT_NO_SERVER_WELCOME_MESSAGE,
+ SMTP_DECODER_EVENT_TLS_REJECTED,
+ SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED,
+
+ /* MIME Events */
+ SMTP_DECODER_EVENT_MIME_PARSE_FAILED,
+ SMTP_DECODER_EVENT_MIME_MALFORMED_MSG,
+ SMTP_DECODER_EVENT_MIME_INVALID_BASE64,
+ SMTP_DECODER_EVENT_MIME_INVALID_QP,
+ SMTP_DECODER_EVENT_MIME_LONG_LINE,
+ SMTP_DECODER_EVENT_MIME_LONG_ENC_LINE,
+ SMTP_DECODER_EVENT_MIME_LONG_HEADER_NAME,
+ SMTP_DECODER_EVENT_MIME_LONG_HEADER_VALUE,
+ SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG,
+};
+
+typedef struct SMTPTransaction_ {
+ /** id of this tx, starting at 0 */
+ uint64_t tx_id;
+ int done;
+ /** the first message contained in the session */
+ MimeDecEntity *msg_head;
+ /** the last message contained in the session */
+ MimeDecEntity *msg_tail;
+ /** the mime decoding parser state */
+ MimeDecParseState *mime_state;
+
+ AppLayerDecoderEvents *decoder_events; /**< per tx events */
+ DetectEngineState *de_state;
+
+ TAILQ_ENTRY(SMTPTransaction_) next;
+} SMTPTransaction;
+
+typedef struct SMTPConfig {
+
+ int decode_mime;
+ MimeDecConfig mime_config;
+ uint32_t content_limit;
+ uint32_t content_inspect_min_size;
+ uint32_t content_inspect_window;
+} SMTPConfig;
+
+typedef struct SMTPState_ {
+ SMTPTransaction *curr_tx;
+ TAILQ_HEAD(, SMTPTransaction_) tx_list; /**< transaction list */
+ uint64_t tx_cnt;
+
+ /* current input that is being parsed */
+ uint8_t *input;
+ int32_t input_len;
+ uint8_t direction;
+
+ /* --parser details-- */
+ /** current line extracted by the parser from the call to SMTPGetline() */
+ uint8_t *current_line;
+ /** length of the line in current_line. Doesn't include the delimiter */
+ int32_t current_line_len;
+ uint8_t current_line_delimiter_len;
+ PatternMatcherQueue *thread_local_data;
+
+ /** used to indicate if the current_line buffer is a malloced buffer. We
+ * use a malloced buffer, if a line is fragmented */
+ uint8_t *tc_db;
+ int32_t tc_db_len;
+ uint8_t tc_current_line_db;
+ /** we have see LF for the currently parsed line */
+ uint8_t tc_current_line_lf_seen;
+
+ /** used to indicate if the current_line buffer is a malloced buffer. We
+ * use a malloced buffer, if a line is fragmented */
+ uint8_t *ts_db;
+ int32_t ts_db_len;
+ uint8_t ts_current_line_db;
+ /** we have see LF for the currently parsed line */
+ uint8_t ts_current_line_lf_seen;
+
+ /** var to indicate parser state */
+ uint8_t parser_state;
+ /** current command in progress */
+ uint8_t current_command;
+ /** bdat chunk len */
+ uint32_t bdat_chunk_len;
+ /** bdat chunk idx */
+ uint32_t bdat_chunk_idx;
+
+ /* the request commands are store here and the reply handler uses these
+ * stored command in the buffer to match the reply(ies) with the command */
+ /** the command buffer */
+ uint8_t *cmds;
+ /** the buffer length */
+ uint16_t cmds_buffer_len;
+ /** no of commands stored in the above buffer */
+ uint16_t cmds_cnt;
+ /** index of the command in the buffer, currently in inspection by reply
+ * handler */
+ uint16_t cmds_idx;
+
+ /* SMTP Mime decoding and file extraction */
+ /** the list of files sent to the server */
+ FileContainer *files_ts;
+
+} SMTPState;
+
+/* Create SMTP config structure */
+extern SMTPConfig smtp_config;
+
+int SMTPProcessDataChunk(const uint8_t *chunk, uint32_t len, MimeDecParseState *state);
+void *SMTPStateAlloc(void);
+void RegisterSMTPParsers(void);
+void SMTPParserRegisterTests(void);
+
+#endif /* __APP_LAYER_SMTP_H__ */
diff --git a/framework/src/suricata/src/app-layer-ssh.c b/framework/src/suricata/src/app-layer-ssh.c
new file mode 100644
index 00000000..879c7746
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ssh.c
@@ -0,0 +1,2607 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * App-layer parser for SSH protocol
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-ssh.h"
+
+#include "conf.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "flow-private.h"
+
+#include "util-byte.h"
+#include "util-memcmp.h"
+
+/** \internal
+ * \brief Function to parse the SSH version string of the client
+ *
+ * The input to this function is a byte buffer starting with SSH-
+ *
+ * \param ssh_state Pointer the state in which the value to be stored
+ * \param input Pointer the received input data
+ * \param input_len Length in bytes of the received data
+ *
+ * \retval len remaining length in input
+ */
+static int SSHParseBanner(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len)
+{
+ const uint8_t *line_ptr = input;
+ uint32_t line_len = input_len;
+
+ /* is it the version line? */
+ if (SCMemcmp("SSH-", line_ptr, 4) != 0) {
+ SCReturnInt(-1);
+ }
+
+ const uint8_t *banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1);
+ if (banner_end == NULL) {
+ banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\n", 1);
+ if (banner_end == NULL) {
+ SCLogDebug("No EOL at the end of banner buffer");
+ SCReturnInt(-1);
+ }
+ }
+
+ if ((banner_end - line_ptr) > 255) {
+ SCLogDebug("Invalid version string, it should be less than 255 "
+ "characters including <CR><NL>, input value is %"PRIuMAX,
+ (banner_end - line_ptr));
+ SCReturnInt(-1);
+ }
+
+ /* don't search things behind the end of banner */
+ line_len = banner_end - line_ptr;
+
+ /* ok, we have found the version line/string, skip it and parse proto version */
+ line_ptr += 4;
+ line_len -= 4;
+
+ uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1);
+ if (proto_end == NULL) {
+ /* Strings starting with SSH- are not allowed
+ * if they are not the real version string */
+ SCLogDebug("Info Version String for SSH (invalid usage of SSH- prefix)");
+ SCReturnInt(-1);
+ }
+ uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr);
+ header->proto_version = SCMalloc(proto_ver_len + 1);
+ if (header->proto_version == NULL) {
+ SCReturnInt(-1);
+ }
+ memcpy(header->proto_version, line_ptr, proto_ver_len);
+ header->proto_version[proto_ver_len] = '\0';
+
+ /* Now lets parse the software & version */
+ line_ptr += proto_ver_len + 1;
+ line_len -= proto_ver_len + 1;
+ if (line_len < 1) {
+ SCLogDebug("No software version specified (weird)");
+ header->flags |= SSH_FLAG_VERSION_PARSED;
+ /* Return the remaining length */
+ SCReturnInt(0);
+ }
+
+ uint64_t sw_ver_len = (uint64_t)(banner_end - line_ptr);
+ /* sanity check on this arithmetic */
+ if ((sw_ver_len <= 1) || (sw_ver_len >= input_len)) {
+ SCLogDebug("Should not have sw version length '%" PRIu64 "'", sw_ver_len);
+ SCReturnInt(-1);
+ }
+
+ header->software_version = SCMalloc(sw_ver_len + 1);
+ if (header->software_version == NULL) {
+ SCReturnInt(-1);
+ }
+ memcpy(header->software_version, line_ptr, sw_ver_len);
+ header->software_version[sw_ver_len] = '\0';
+ if (header->software_version[sw_ver_len - 1] == 0x0d)
+ header->software_version[sw_ver_len - 1] = '\0';
+
+ header->flags |= SSH_FLAG_VERSION_PARSED;
+
+ /* Return the remaining length */
+ int len = input_len - (banner_end - input);
+ SCReturnInt(len);
+}
+
+static int SSHParseRecordHeader(SshState *state, SshHeader *header,
+ const uint8_t *input, uint32_t input_len)
+{
+#ifdef DEBUG
+ BUG_ON(input_len != 6);
+#else
+ if (input_len < 6)
+ SCReturnInt(-1);
+#endif
+ /* input and input_len now point past initial line */
+ uint32_t pkt_len = 0;
+ int r = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN,
+ 4, input);
+ if (r != 4) {
+ SCLogDebug("xtract 4 bytes failed %d", r);
+ SCReturnInt(-1);
+ }
+ if (pkt_len < 2) {
+ SCReturnInt(-1);
+ }
+
+ header->pkt_len = pkt_len;
+ SCLogDebug("pkt len: %"PRIu32, pkt_len);
+
+ input += 4;
+ //input_len -= 4;
+
+ header->padding_len = *input;
+
+ input += 1;
+ //input_len -= 1;
+
+ SCLogDebug("padding: %u", header->padding_len);
+
+ header->msg_code = *input;
+
+ SCLogDebug("msg code: %u", header->msg_code);
+
+ if (header->msg_code == SSH_MSG_NEWKEYS) {
+ /* done */
+ SCLogDebug("done");
+ header->flags |= SSH_FLAG_PARSER_DONE;
+ } else {
+ /* not yet done */
+ SCLogDebug("not done");
+ }
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Function to parse the SSH field in packet received from the client
+ *
+ * Input to this function is a byte buffer starting with SSH- up to at least
+ * a \r or \n character.
+ *
+ * \param ssh_state Pointer the state in which the value to be stored
+ * \param input Pointer the received input data
+ * \param input_len Length in bytes of the received data
+ */
+static int SSHParseRecord(SshState *state, SshHeader *header, uint8_t *input, uint32_t input_len)
+{
+ SCEnter();
+ int ret = 0;
+
+ if (header->flags & SSH_FLAG_PARSER_DONE) {
+ SCReturnInt(0);
+ }
+
+ SCLogDebug("state %p, input %p,input_len %" PRIu32,
+ state, input, input_len);
+ //PrintRawDataFp(stdout, input, input_len);
+
+ if (!(header->flags & SSH_FLAG_VERSION_PARSED)) {
+ ret = SSHParseBanner(state, header, input, input_len);
+ if (ret < 0) {
+ SCLogDebug("Invalid version string");
+ SCReturnInt(-1);
+ } else if (header->flags & SSH_FLAG_VERSION_PARSED) {
+ SCLogDebug("Version string parsed, remaining length %d", ret);
+ input += input_len - ret;
+ input_len -= (input_len - ret);
+
+ uint32_t u = 0;
+ while (u < input_len && (input[u] == '\r' || input[u] == '\n')) {
+ u++;
+ }
+ SCLogDebug("skipping %u EOL bytes", u);
+ input += u;
+ input_len -= u;
+
+ if (input_len == 0)
+ SCReturnInt(0);
+
+ } else {
+ BUG_ON(1);// we only call this when we have enough data
+ SCLogDebug("Version string not parsed yet");
+ //pstate->parse_field = 0;
+ SCReturnInt(0);
+ }
+ } else {
+ SCLogDebug("Version string already parsed");
+ }
+
+ /* skip bytes from the current record if we have to */
+ if (header->record_left > 0) {
+ SCLogDebug("skipping bytes part of the current record");
+ if (header->record_left > input_len) {
+ header->record_left -= input_len;
+ SCLogDebug("all input skipped, %u left in record", header->record_left);
+ SCReturnInt(0);
+ } else {
+ input_len -= header->record_left;
+ input += header->record_left;
+ header->record_left = 0;
+
+ if (input_len == 0) {
+ SCLogDebug("all input skipped");
+ SCReturnInt(0);
+ }
+ }
+ }
+
+again:
+ /* input is too small, even when combined with stored bytes */
+ if (header->buf_offset + input_len < 6) {
+ memcpy(header->buf + header->buf_offset, input, input_len);
+ header->buf_offset += input_len;
+ SCReturnInt(0);
+
+ /* we have enough bytes to parse 6 bytes, lets see if we have
+ * previously stored some */
+ } else if (header->buf_offset > 0) {
+ uint8_t needed = 6 - header->buf_offset;
+
+ SCLogDebug("parse stored");
+ memcpy(header->buf + header->buf_offset, input, needed);
+ header->buf_offset = 6;
+
+ // parse the 6
+ if (SSHParseRecordHeader(state, header, header->buf, 6) < 0)
+ SCReturnInt(-1);
+ header->buf_offset = 0;
+
+ uint32_t record_left = header->pkt_len - 2;
+ input_len -= needed;
+ input += needed;
+
+ if (record_left > input_len) {
+ header->record_left = record_left - input_len;
+ } else {
+ input_len -= record_left;
+ if (input_len == 0)
+ SCReturnInt(0);
+
+ input += record_left;
+
+ SCLogDebug("we have %u left to parse", input_len);
+ goto again;
+
+ }
+
+ /* nothing stored, lets parse this directly */
+ } else {
+ SCLogDebug("parse direct");
+ //PrintRawDataFp(stdout, input, input_len);
+ if (SSHParseRecordHeader(state, header, input, 6) < 0)
+ SCReturnInt(-1);
+
+ uint32_t record_left = header->pkt_len - 2;
+ SCLogDebug("record left %u", record_left);
+ input_len -= 6;
+ input += 6;
+
+ if (record_left > input_len) {
+ header->record_left = record_left - input_len;
+ } else {
+ input_len -= record_left;
+ if (input_len == 0)
+ SCReturnInt(0);
+ input += record_left;
+ //PrintRawDataFp(stdout, input, input_len);
+
+ SCLogDebug("we have %u left to parse", input_len);
+ goto again;
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+static int EnoughData(uint8_t *input, uint32_t input_len)
+{
+ uint32_t u;
+ for (u = 0; u < input_len; u++) {
+ if (input[u] == '\r' || input[u] == '\n')
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#define MAX_BANNER_LEN 256
+
+static int SSHParseData(SshState *state, SshHeader *header,
+ uint8_t *input, uint32_t input_len)
+{
+ /* we're looking for the banner */
+ if (!(header->flags & SSH_FLAG_VERSION_PARSED))
+ {
+ int banner_eol = EnoughData(input, input_len);
+
+ /* fast track normal case: no buffering */
+ if (header->banner_buffer == NULL && banner_eol)
+ {
+ SCLogDebug("enough data, parse now");
+ // parse now
+ int r = SSHParseRecord(state, header, input, input_len);
+ SCReturnInt(r);
+
+ /* banner EOL with existing buffer present. Time for magic. */
+ } else if (banner_eol) {
+ SCLogDebug("banner EOL with existing buffer");
+
+ uint32_t tocopy = MAX_BANNER_LEN - header->banner_len;
+ if (tocopy > input_len)
+ tocopy = input_len;
+
+ SCLogDebug("tocopy %u input_len %u", tocopy, input_len);
+ memcpy(header->banner_buffer + header->banner_len, input, tocopy);
+ header->banner_len += tocopy;
+
+ SCLogDebug("header->banner_len %u", header->banner_len);
+ int r = SSHParseRecord(state, header,
+ header->banner_buffer, header->banner_len);
+ if (r == 0) {
+ input += tocopy;
+ input_len -= tocopy;
+ if (input_len > 0) {
+ SCLogDebug("handling remaining data %u", input_len);
+ r = SSHParseRecord(state, header, input, input_len);
+ }
+ }
+ SCReturnInt(r);
+
+ /* no banner EOL, so we need to buffer */
+ } else if (!banner_eol) {
+ if (header->banner_buffer == NULL) {
+ header->banner_buffer = SCMalloc(MAX_BANNER_LEN);
+ if (header->banner_buffer == NULL)
+ SCReturnInt(-1);
+ }
+
+ uint32_t tocopy = MAX_BANNER_LEN - header->banner_len;
+ if (tocopy > input_len)
+ tocopy = input_len;
+ SCLogDebug("tocopy %u", tocopy);
+
+ memcpy(header->banner_buffer + header->banner_len, input, tocopy);
+ header->banner_len += tocopy;
+ SCLogDebug("header->banner_len %u", header->banner_len);
+ }
+
+ /* we have a banner, the rest is just records */
+ } else {
+ int r = SSHParseRecord(state, header, input, input_len);
+ SCReturnInt(r);
+ }
+
+ //PrintRawDataFp(stdout, input, input_len);
+ return 0;
+}
+
+static int SSHParseRequest(Flow *f, void *state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SshState *ssh_state = (SshState *)state;
+ SshHeader *ssh_header = &ssh_state->cli_hdr;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ int r = SSHParseData(ssh_state, ssh_header, input, input_len);
+
+ if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE &&
+ ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) {
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION);
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
+ }
+
+ SCReturnInt(r);
+}
+
+static int SSHParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SshState *ssh_state = (SshState *)state;
+ SshHeader *ssh_header = &ssh_state->srv_hdr;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ int r = SSHParseData(ssh_state, ssh_header, input, input_len);
+
+ if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE &&
+ ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) {
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION);
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
+ }
+
+ SCReturnInt(r);
+}
+
+/** \brief Function to allocates the SSH state memory
+ */
+static void *SSHStateAlloc(void)
+{
+ void *s = SCMalloc(sizeof(SshState));
+ if (unlikely(s == NULL))
+ return NULL;
+
+ memset(s, 0, sizeof(SshState));
+ return s;
+}
+
+/** \brief Function to free the SSH state memory
+ */
+static void SSHStateFree(void *state)
+{
+ SshState *s = (SshState *)state;
+ if (s->cli_hdr.proto_version != NULL)
+ SCFree(s->cli_hdr.proto_version);
+ if (s->cli_hdr.software_version != NULL)
+ SCFree(s->cli_hdr.software_version);
+ if (s->cli_hdr.banner_buffer != NULL)
+ SCFree(s->cli_hdr.banner_buffer);
+
+ if (s->srv_hdr.proto_version != NULL)
+ SCFree(s->srv_hdr.proto_version);
+ if (s->srv_hdr.software_version != NULL)
+ SCFree(s->srv_hdr.software_version);
+ if (s->srv_hdr.banner_buffer != NULL)
+ SCFree(s->srv_hdr.banner_buffer);
+
+ SCFree(s);
+}
+
+static int SSHRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_SSH,
+ "SSH-", 4, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_SSH,
+ "SSH-", 4, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/** \brief Function to register the SSH protocol parsers and other functions
+ */
+void RegisterSSHParsers(void)
+{
+ char *proto_name = "ssh";
+
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_SSH, proto_name);
+ if (SSHRegisterPatternsForProtocolDetection() < 0)
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOSERVER,
+ SSHParseRequest);
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOCLIENT,
+ SSHParseResponse);
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SSH, SSHStateAlloc, SSHStateFree);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP,
+ ALPROTO_SSH, STREAM_TOSERVER|STREAM_TOCLIENT);
+ } else {
+// SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+// "still on.", proto_name);
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SSH, SSHParserRegisterTests);
+#endif
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+/** \test Send a version string in one chunk (client version str). */
+static int SSHParserTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version string in one chunk but multiple lines and comments.
+ * (client version str)
+ */
+static int SSHParserTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a invalid version string in one chunk but multiple lines and comments.
+ * (client version str)
+ */
+static int SSHParserTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen);
+ if (r == 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected != 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) {
+ printf("Client version string parsed? It's not a valid string: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version != NULL) {
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version != NULL) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version string in one chunk (server version str). */
+static int SSHParserTest04(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version string in one chunk (server version str)
+ */
+static int SSHParserTest05(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a invalid version string in one chunk (server version str)
+ */
+static int SSHParserTest06(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen);
+ if (r == 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected != 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* Ok, it returned an error. Let's make sure we didn't parse the string at all */
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED) {
+ printf("Client version string parsed? It's not a valid string: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version != NULL) {
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version != NULL) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int SSHParserTest07(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"};
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version banner in three chunks. */
+static int SSHParserTest08(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"};
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int SSHParserTest09(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"};
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version banner in three chunks. */
+static int SSHParserTest10(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"};
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a banner and record in three chunks. */
+static int SSHParserTest11(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
+ uint32_t sshlen2 = sizeof(sshbuf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a banner and 2 records record in four chunks. */
+static int SSHParserTest12(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00};
+ uint32_t sshlen2 = sizeof(sshbuf2);
+ uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a banner and 2 records record in four chunks. */
+static int SSHParserTest13(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17};
+ uint32_t sshlen2 = sizeof(sshbuf2);
+ uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ uint32_t u;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ for (u = 0; u < sshlen2; u++) {
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, &sshbuf2[u], 1);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ }
+ for (u = 0; u < sshlen3; u++) {
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, &sshbuf3[u], 1);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ }
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a banner and 2 records record in four chunks. */
+static int SSHParserTest14(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
+ uint32_t sshlen2 = sizeof(sshbuf2);
+
+ uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4);
+
+ /* first byte of this record in sshbuf4 */
+ uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21};
+ uint32_t sshlen5 = sizeof(sshbuf5);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf5, sshlen5);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a banner and 2 records record in four chunks. */
+static int SSHParserTest15(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
+ uint32_t sshlen2 = sizeof(sshbuf2);
+
+ uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4);
+
+ /* first byte of this record in sshbuf4 */
+ uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21};
+ uint32_t sshlen5 = sizeof(sshbuf5);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf5, sshlen5);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send toserver a banner and record in three chunks. */
+static int SSHParserTest16(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send toserver a banner and 2 records record in four chunks. */
+static int SSHParserTest17(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00};
+ uint32_t sshlen3 = sizeof(sshbuf3);
+ uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test 2 directional test */
+static int SSHParserTest18(void)
+{
+ int result = 0;
+ Flow f;
+
+ uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n";
+ uint32_t serverlen1 = sizeof(server1) - 1;
+
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+
+ uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
+ uint32_t serverlen2 = sizeof(server2) - 1;
+
+ uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
+ uint32_t sshlen3 = sizeof(sshbuf3);
+
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, server1, serverlen1);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, server2, serverlen2);
+ if (r != 0) {
+ printf("toclient chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ if (!(AppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION))) {
+ printf("detection not disabled: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Really long banner handling: bannel exactly 255 */
+static int SSHParserTest19(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
+ uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//60
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//112
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//164
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//216
+ "abcdefghijklmnopqrstuvwxyz"//242
+ "abcdefghijkl\r";//255
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+
+ uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ char *name = SCMalloc(256);
+ if (name == NULL)
+ goto end;
+ memset(name, 0x00, 256);
+ strlcpy(name, (char *)sshbuf3, strlen((char *)sshbuf3) - 1);
+
+ if (strncmp((char*)ssh_state->srv_hdr.software_version, name, strlen(name)) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Really long banner handling: banner exactly 255,
+ * followed by malformed record */
+static int SSHParserTest20(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
+ uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//60
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//112
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//164
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//216
+ "abcdefghijklmnopqrstuvwxyz"//242
+ "abcdefghijklm\r";//256
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r',
+ 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCLogDebug("chunk 4:");
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ((ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("detected the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Fragmented banner handling: chunk has final part of bannel plus
+ * a record. */
+static int SSHParserTest21(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
+ uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//60
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//112
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//164
+ "abcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyz"//216
+ "abcdefghijklmnopqrstuvwxy";//241
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r',
+ 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCLogDebug("chunk 4:");
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Fragmented banner handling: chunk has final part of bannel plus
+ * a record. */
+static int SSHParserTest22(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "2.0-";
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
+ uint8_t sshbuf3[] = {
+ 'l', 'i', 'b', 's', 's', 'h', '\r', //7
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //50
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //100
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //150
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250
+
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300
+ };
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+#if 0
+ SCLogDebug("chunk 4:");
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+#endif
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.software_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->srv_hdr.proto_version == NULL) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
+ printf("Client version string not parsed correctly: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+ printf("Didn't detect the msg code of new keys (ciphered data starts): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version string in one chunk (client version str). */
+static int SSHParserTest23(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0\r-MySSHClient-0.5.1\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen);
+ if (r == 0) {
+ printf("toclient chunk 1 returned 0 expected non null: ");
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a version string in one chunk (client version str). */
+static int SSHParserTest24(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf[] = "SSH-2.0-\rMySSHClient-0.5.1\n";
+ uint32_t sshlen = sizeof(sshbuf) - 1;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ printf("Client version string not parsed: ");
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version) {
+ printf("Client version string should not be parsed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+
+#endif /* UNITTESTS */
+
+void SSHParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SSHParserTest01 - ToServer", SSHParserTest01, 1);
+ UtRegisterTest("SSHParserTest02 - ToServer", SSHParserTest02, 1);
+ UtRegisterTest("SSHParserTest03 - ToServer", SSHParserTest03, 1);
+ UtRegisterTest("SSHParserTest04 - ToClient", SSHParserTest04, 1);
+ UtRegisterTest("SSHParserTest05 - ToClient", SSHParserTest05, 1);
+ UtRegisterTest("SSHParserTest06 - ToClient", SSHParserTest06, 1);
+ UtRegisterTest("SSHParserTest07 - ToServer 2 chunks", SSHParserTest07, 1);
+ UtRegisterTest("SSHParserTest08 - ToServer 3 chunks", SSHParserTest08, 1);
+ UtRegisterTest("SSHParserTest09 - ToClient 2 chunks", SSHParserTest09, 1);
+ UtRegisterTest("SSHParserTest10 - ToClient 3 chunks", SSHParserTest10, 1);
+ UtRegisterTest("SSHParserTest11 - ToClient 4 chunks", SSHParserTest11, 1);
+ UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12, 1);
+ UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13, 1);
+ UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14, 1);
+ UtRegisterTest("SSHParserTest15", SSHParserTest15, 1);
+ UtRegisterTest("SSHParserTest16", SSHParserTest16, 1);
+ UtRegisterTest("SSHParserTest17", SSHParserTest17, 1);
+ UtRegisterTest("SSHParserTest18", SSHParserTest18, 1);
+ UtRegisterTest("SSHParserTest19", SSHParserTest19, 1);
+ UtRegisterTest("SSHParserTest20", SSHParserTest20, 1);
+ UtRegisterTest("SSHParserTest21", SSHParserTest21, 1);
+ UtRegisterTest("SSHParserTest22", SSHParserTest22, 1);
+ UtRegisterTest("SSHParserTest23", SSHParserTest23, 1);
+ UtRegisterTest("SSHParserTest24", SSHParserTest24, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/app-layer-ssh.h b/framework/src/suricata/src/app-layer-ssh.h
new file mode 100644
index 00000000..7a6a9b72
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ssh.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __APP_LAYER_SSH_H__
+#define __APP_LAYER_SSH_H__
+
+/* header flag */
+#define SSH_FLAG_VERSION_PARSED 0x01
+
+/* This flags indicate that the rest of the communication
+ * must be ciphered, so the parsing finish here */
+#define SSH_FLAG_PARSER_DONE 0x02
+
+#define SSH_FLAG_STATE_LOGGED 0x04
+
+/* MSG_CODE */
+#define SSH_MSG_NEWKEYS 21
+
+/** From SSH-TRANSP rfc
+
+ SSH Bunary packet structure:
+ uint32 packet_length
+ byte padding_length
+ byte[n1] payload; n1 = packet_length - padding_length - 1
+ byte[n2] random padding; n2 = padding_length
+ byte[m] mac (Message Authentication Code - MAC); m = mac_length
+
+ So we are going to do a header struct to store
+ the lenghts and msg_code (inside payload, if any)
+*/
+
+typedef struct SshHeader_ {
+ uint32_t pkt_len;
+ uint8_t padding_len;
+ uint8_t msg_code;
+ uint8_t buf[6];
+ uint8_t buf_offset;
+ uint8_t flags;
+ uint32_t record_left;
+ uint8_t *proto_version;
+ uint8_t *software_version;
+ uint8_t *banner_buffer;
+ uint16_t banner_len;
+} SshHeader;
+
+/** structure to store the SSH state values */
+typedef struct SshState_ {
+ SshHeader srv_hdr;
+ SshHeader cli_hdr;
+} SshState;
+
+void RegisterSSHParsers(void);
+void SSHParserRegisterTests(void);
+
+#endif /* __APP_LAYER_SSH_H__ */
+
diff --git a/framework/src/suricata/src/app-layer-ssl.c b/framework/src/suricata/src/app-layer-ssl.c
new file mode 100644
index 00000000..5fb41ba1
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ssl.c
@@ -0,0 +1,4220 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-ssl.h"
+
+#include "app-layer-tls-handshake.h"
+
+#include "decode-events.h"
+#include "conf.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "flow-util.h"
+#include "flow-private.h"
+
+#include "util-byte.h"
+
+SCEnumCharMap tls_decoder_event_table[ ] = {
+ /* TLS protocol messages */
+ { "INVALID_SSLV2_HEADER", TLS_DECODER_EVENT_INVALID_SSLV2_HEADER },
+ { "INVALID_TLS_HEADER", TLS_DECODER_EVENT_INVALID_TLS_HEADER },
+ { "INVALID_RECORD_VERSION", TLS_DECODER_EVENT_INVALID_RECORD_VERSION },
+ { "INVALID_RECORD_TYPE", TLS_DECODER_EVENT_INVALID_RECORD_TYPE },
+ { "INVALID_HANDSHAKE_MESSAGE", TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE },
+ { "HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_HEARTBEAT },
+ { "INVALID_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_INVALID_HEARTBEAT },
+ { "OVERFLOW_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT },
+ { "DATALEAK_HEARTBEAT_MISMATCH", TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH },
+ /* Certificates decoding messages */
+ { "INVALID_CERTIFICATE", TLS_DECODER_EVENT_INVALID_CERTIFICATE },
+ { "CERTIFICATE_MISSING_ELEMENT", TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT },
+ { "CERTIFICATE_UNKNOWN_ELEMENT", TLS_DECODER_EVENT_CERTIFICATE_UNKNOWN_ELEMENT },
+ { "CERTIFICATE_INVALID_LENGTH", TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH },
+ { "CERTIFICATE_INVALID_STRING", TLS_DECODER_EVENT_CERTIFICATE_INVALID_STRING },
+ { "ERROR_MESSAGE_ENCOUNTERED", TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED },
+ /* used as a generic error event */
+ { "INVALID_SSL_RECORD", TLS_DECODER_EVENT_INVALID_SSL_RECORD },
+ { NULL, -1 },
+};
+
+typedef struct SslConfig_ {
+ int no_reassemble;
+} SslConfig;
+
+SslConfig ssl_config;
+
+/* SSLv3 record types */
+#define SSLV3_CHANGE_CIPHER_SPEC 20
+#define SSLV3_ALERT_PROTOCOL 21
+#define SSLV3_HANDSHAKE_PROTOCOL 22
+#define SSLV3_APPLICATION_PROTOCOL 23
+#define SSLV3_HEARTBEAT_PROTOCOL 24
+
+/* SSLv3 handshake protocol types */
+#define SSLV3_HS_HELLO_REQUEST 0
+#define SSLV3_HS_CLIENT_HELLO 1
+#define SSLV3_HS_SERVER_HELLO 2
+#define SSLV3_HS_NEW_SESSION_TICKET 4
+#define SSLV3_HS_CERTIFICATE 11
+#define SSLV3_HS_SERVER_KEY_EXCHANGE 12
+#define SSLV3_HS_CERTIFICATE_REQUEST 13
+#define SSLV3_HS_SERVER_HELLO_DONE 14
+#define SSLV3_HS_CERTIFICATE_VERIFY 15
+#define SSLV3_HS_CLIENT_KEY_EXCHANGE 16
+#define SSLV3_HS_FINISHED 20
+#define SSLV3_HS_CERTIFICATE_URL 21
+#define SSLV3_HS_CERTIFICATE_STATUS 22
+
+/* SSLv2 protocol message types */
+#define SSLV2_MT_ERROR 0
+#define SSLV2_MT_CLIENT_HELLO 1
+#define SSLV2_MT_CLIENT_MASTER_KEY 2
+#define SSLV2_MT_CLIENT_FINISHED 3
+#define SSLV2_MT_SERVER_HELLO 4
+#define SSLV2_MT_SERVER_VERIFY 5
+#define SSLV2_MT_SERVER_FINISHED 6
+#define SSLV2_MT_REQUEST_CERTIFICATE 7
+#define SSLV2_MT_CLIENT_CERTIFICATE 8
+
+#define SSLV3_RECORD_HDR_LEN 5
+#define SSLV3_MESSAGE_HDR_LEN 4
+
+/* TLS heartbeat protocol types */
+#define TLS_HB_REQUEST 1
+#define TLS_HB_RESPONSE 2
+
+static void SSLParserReset(SSLState *ssl_state)
+{
+ ssl_state->curr_connp->bytes_processed = 0;
+}
+
+static int SSLv3ParseHandshakeType(SSLState *ssl_state, uint8_t *input,
+ uint32_t input_len)
+{
+ void *ptmp;
+ uint8_t *initial_input = input;
+ uint32_t parsed = 0;
+ int rc;
+
+ if (input_len == 0) {
+ return 0;
+ }
+
+ switch (ssl_state->curr_connp->handshake_type) {
+ case SSLV3_HS_CLIENT_HELLO:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_CLIENT_HELLO;
+ break;
+
+ case SSLV3_HS_SERVER_HELLO:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_SERVER_HELLO;
+
+ break;
+
+ case SSLV3_HS_SERVER_KEY_EXCHANGE:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_SERVER_KEYX;
+ break;
+
+ case SSLV3_HS_CLIENT_KEY_EXCHANGE:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_CLIENT_KEYX;
+ break;
+
+ case SSLV3_HS_CERTIFICATE:
+ if (ssl_state->curr_connp->trec == NULL) {
+ ssl_state->curr_connp->trec_len = 2 * ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN + 1;
+ ssl_state->curr_connp->trec = SCMalloc( ssl_state->curr_connp->trec_len );
+ }
+ if (ssl_state->curr_connp->trec_pos + input_len >= ssl_state->curr_connp->trec_len) {
+ ssl_state->curr_connp->trec_len = ssl_state->curr_connp->trec_len + 2 * input_len + 1;
+ ptmp = SCRealloc(ssl_state->curr_connp->trec,
+ ssl_state->curr_connp->trec_len);
+ if (unlikely(ptmp == NULL)) {
+ SCFree(ssl_state->curr_connp->trec);
+ }
+ ssl_state->curr_connp->trec = ptmp;
+ }
+ if (unlikely(ssl_state->curr_connp->trec == NULL)) {
+ ssl_state->curr_connp->trec_len = 0;
+ /* error, skip packet */
+ parsed += input_len;
+ ssl_state->curr_connp->bytes_processed += input_len;
+ return -1;
+ }
+
+ uint32_t write_len = 0;
+ if ((ssl_state->curr_connp->bytes_processed + input_len) > ssl_state->curr_connp->record_length + (SSLV3_RECORD_HDR_LEN)) {
+ if ((ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) < ssl_state->curr_connp->bytes_processed) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+ write_len = (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) - ssl_state->curr_connp->bytes_processed;
+ } else {
+ write_len = input_len;
+ }
+ memcpy(ssl_state->curr_connp->trec + ssl_state->curr_connp->trec_pos, initial_input, write_len);
+ ssl_state->curr_connp->trec_pos += write_len;
+
+ rc = DecodeTLSHandshakeServerCertificate(ssl_state, ssl_state->curr_connp->trec, ssl_state->curr_connp->trec_pos);
+ if (rc > 0) {
+ /* do not return normally if the packet was fragmented:
+ * we would return the size of the *entire* message,
+ * while we expect only the number of bytes parsed bytes
+ * from the *current* fragment
+ */
+ if (write_len < (ssl_state->curr_connp->trec_pos - rc)) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+ uint32_t diff = write_len - (ssl_state->curr_connp->trec_pos - rc);
+ ssl_state->curr_connp->bytes_processed += diff;
+
+ ssl_state->curr_connp->trec_pos = 0;
+ ssl_state->curr_connp->handshake_type = 0;
+ ssl_state->curr_connp->hs_bytes_processed = 0;
+ ssl_state->curr_connp->message_length = 0;
+
+ return diff;
+ } else {
+ ssl_state->curr_connp->bytes_processed += write_len;
+ parsed += write_len;
+ return parsed;
+ }
+
+ break;
+ case SSLV3_HS_HELLO_REQUEST:
+ case SSLV3_HS_CERTIFICATE_REQUEST:
+ case SSLV3_HS_CERTIFICATE_VERIFY:
+ case SSLV3_HS_FINISHED:
+ case SSLV3_HS_CERTIFICATE_URL:
+ case SSLV3_HS_CERTIFICATE_STATUS:
+ break;
+ case SSLV3_HS_NEW_SESSION_TICKET:
+ SCLogDebug("new session ticket");
+ break;
+ default:
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ uint32_t write_len = 0;
+ if ((ssl_state->curr_connp->bytes_processed + input_len) >= ssl_state->curr_connp->record_length + (SSLV3_RECORD_HDR_LEN)) {
+ if ((ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) < ssl_state->curr_connp->bytes_processed) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+ write_len = (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) - ssl_state->curr_connp->bytes_processed;
+ } else {
+ write_len = input_len;
+ }
+ if ((ssl_state->curr_connp->trec_pos + write_len) >= ssl_state->curr_connp->message_length) {
+ if (ssl_state->curr_connp->message_length < ssl_state->curr_connp->trec_pos) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+ parsed += ssl_state->curr_connp->message_length - ssl_state->curr_connp->trec_pos;
+
+ ssl_state->curr_connp->bytes_processed += ssl_state->curr_connp->message_length - ssl_state->curr_connp->trec_pos;
+
+ ssl_state->curr_connp->handshake_type = 0;
+ ssl_state->curr_connp->hs_bytes_processed = 0;
+ ssl_state->curr_connp->message_length = 0;
+ ssl_state->curr_connp->trec_pos = 0;
+
+ return parsed;
+ } else {
+ ssl_state->curr_connp->trec_pos += write_len;
+ ssl_state->curr_connp->bytes_processed += write_len;
+ parsed += write_len;
+ return parsed;
+ }
+}
+
+static int SSLv3ParseHandshakeProtocol(SSLState *ssl_state, uint8_t *input,
+ uint32_t input_len)
+{
+ uint8_t *initial_input = input;
+ int retval;
+
+ if (input_len == 0 ||
+ ssl_state->curr_connp->bytes_processed ==
+ (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN))
+ {
+ return 0;
+ }
+
+ switch (ssl_state->curr_connp->hs_bytes_processed) {
+ case 0:
+ ssl_state->curr_connp->handshake_type = *(input++);
+ ssl_state->curr_connp->bytes_processed++;
+ ssl_state->curr_connp->hs_bytes_processed++;
+ if (--input_len == 0 ||
+ ssl_state->curr_connp->bytes_processed ==
+ (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN))
+ {
+ return (input - initial_input);
+ }
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->message_length = *(input++) << 16;
+ ssl_state->curr_connp->bytes_processed++;
+ ssl_state->curr_connp->hs_bytes_processed++;
+ if (--input_len == 0 ||
+ ssl_state->curr_connp->bytes_processed ==
+ (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN))
+ {
+ return (input - initial_input);
+ }
+ /* fall through */
+ case 2:
+ ssl_state->curr_connp->message_length |= *(input++) << 8;
+ ssl_state->curr_connp->bytes_processed++;
+ ssl_state->curr_connp->hs_bytes_processed++;
+ if (--input_len == 0 ||
+ ssl_state->curr_connp->bytes_processed ==
+ (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN))
+ {
+ return (input - initial_input);
+ }
+ /* fall through */
+ case 3:
+ ssl_state->curr_connp->message_length |= *(input++);
+ ssl_state->curr_connp->bytes_processed++;
+ ssl_state->curr_connp->hs_bytes_processed++;
+ --input_len;
+ /* fall through */
+ }
+
+ retval = SSLv3ParseHandshakeType(ssl_state, input, input_len);
+ if (retval < 0) {
+ return retval;
+ }
+ input += retval;
+
+ return (input - initial_input);
+}
+
+/**
+ * \internal
+ * \brief TLS Heartbeat parser (see RFC 6520)
+ *
+ * \param sslstate Pointer to the SSL state.
+ * \param input Pointer the received input data.
+ * \param input_len Length in bytes of the received data.
+ * \param direction 1 toclient, 0 toserver
+ *
+ * \retval The number of bytes parsed on success, 0 if nothing parsed, -1 on failure.
+ */
+static int SSLv3ParseHeartbeatProtocol(SSLState *ssl_state, uint8_t *input,
+ uint32_t input_len, uint8_t direction)
+{
+ uint8_t hb_type;
+ uint16_t payload_len;
+ uint16_t padding_len;
+
+ // expect at least 3 bytes, heartbeat type (1) + length (2)
+ if (input_len < 3) {
+ return 0;
+ }
+ hb_type = *input++;
+
+ if (!(ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
+ if (!(hb_type == TLS_HB_REQUEST || hb_type == TLS_HB_RESPONSE)) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ }
+ }
+
+ if ((ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) == 0) {
+ ssl_state->flags |= SSL_AL_FLAG_HB_INFLIGHT;
+
+ if (direction) {
+ ssl_state->flags |= SSL_AL_FLAG_HB_SERVER_INIT;
+ SCLogDebug("HeartBeat Record type sent in the toclient "
+ "direction!");
+ } else {
+ ssl_state->flags |= SSL_AL_FLAG_HB_CLIENT_INIT;
+ SCLogDebug("HeartBeat Record type sent in the toserver "
+ "direction!");
+ }
+ /* if we reach this poin then can we assume that the HB request
+ * is encrypted if so lets set the heartbeat record len */
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
+ ssl_state->hb_record_len = ssl_state->curr_connp->record_length;
+ SCLogDebug("Encrypted HeartBeat Request In-flight. Storing len %u", ssl_state->hb_record_len);
+ return (ssl_state->curr_connp->record_length - 3);
+ }
+
+ payload_len = (*input++) << 8;
+ payload_len |= (*input++);
+
+ // check that the requested payload length is really present in record (CVE-2014-0160)
+ if ((uint32_t)(payload_len+3) > ssl_state->curr_connp->record_length) {
+ SCLogDebug("We have a short record in HeartBeat Request");
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT);
+ return -1;
+ }
+
+ // check the padding length
+ // it must be at least 16 bytes (RFC 6520, section 4)
+ padding_len = ssl_state->curr_connp->record_length - payload_len - 3;
+ if (padding_len < 16) {
+ SCLogDebug("We have a short record in HeartBeat Request");
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ }
+
+ if (input_len < payload_len+padding_len) { // we don't have the payload
+ return 0;
+ }
+
+ /* OpenSSL still seems to discard multiple in-flight
+ * heartbeats although some tools send multiple at once */
+ } else if (direction == 1 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
+ (ssl_state->flags & SSL_AL_FLAG_HB_SERVER_INIT)) {
+ SCLogDebug("Multiple In-Flight Server Intiated HeartBeats");
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ } else if (direction == 0 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
+ (ssl_state->flags & SSL_AL_FLAG_HB_CLIENT_INIT)) {
+ SCLogDebug("Multiple In-Flight Client Intiated HeartBeats");
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ } else {
+ /* we have a HB record in the opposite direction of the request
+ * lets reset our flags */
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_INFLIGHT;
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_SERVER_INIT;
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_CLIENT_INIT;
+
+ /* if we reach this poin then can we assume that the HB request is
+ *encrypted if so lets set the heartbeat record len */
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
+ /* check to see if the encrypted response is longer than the
+ * encrypted request */
+ if (ssl_state->hb_record_len > 0 &&
+ ssl_state->hb_record_len < ssl_state->curr_connp->record_length)
+ {
+ SCLogDebug("My Heart It's Bleeding.. OpenSSL HeartBleed Response (%u)",
+ ssl_state->hb_record_len);
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH);
+ ssl_state->hb_record_len = 0;
+ return -1;
+ }
+ }
+ /* reset the hb record len in-case we have legit hb's followed by a bad one */
+ ssl_state->hb_record_len = 0;
+ }
+
+ /* skip the heartbeat, 3 bytes were already parsed, e.g |18 03 02| for TLS 1.2 */
+ return (ssl_state->curr_connp->record_length - 3);
+}
+
+static int SSLv3ParseRecord(uint8_t direction, SSLState *ssl_state,
+ uint8_t *input, uint32_t input_len)
+{
+ uint8_t *initial_input = input;
+
+ if (input_len == 0) {
+ return 0;
+ }
+
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= 5) {
+ ssl_state->curr_connp->content_type = input[0];
+ ssl_state->curr_connp->version = input[1] << 8;
+ ssl_state->curr_connp->version |= input[2];
+ ssl_state->curr_connp->record_length = input[3] << 8;
+ ssl_state->curr_connp->record_length |= input[4];
+ ssl_state->curr_connp->bytes_processed += SSLV3_RECORD_HDR_LEN;
+ return SSLV3_RECORD_HDR_LEN;
+ } else {
+ ssl_state->curr_connp->content_type = *(input++);
+ if (--input_len == 0)
+ break;
+ }
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->version = *(input++) << 8;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 2:
+ ssl_state->curr_connp->version |= *(input++);
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 3:
+ ssl_state->curr_connp->record_length = *(input++) << 8;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 4:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+
+ ssl_state->curr_connp->bytes_processed += (input - initial_input);
+
+ return (input - initial_input);
+}
+
+static int SSLv2ParseRecord(uint8_t direction, SSLState *ssl_state,
+ uint8_t *input, uint32_t input_len)
+{
+ uint8_t *initial_input = input;
+
+ if (input_len == 0) {
+ return 0;
+ }
+
+ if (ssl_state->curr_connp->record_lengths_length == 2) {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
+ ssl_state->curr_connp->record_length = (0x7f & input[0]) << 8 | input[1];
+ ssl_state->curr_connp->content_type = input[2];
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ ssl_state->curr_connp->bytes_processed += 3;
+ return 3;
+ } else {
+ ssl_state->curr_connp->record_length = (0x7f & *(input++)) << 8;
+ if (--input_len == 0)
+ break;
+ }
+
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 2:
+ ssl_state->curr_connp->content_type = *(input++);
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+
+ } else {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
+ ssl_state->curr_connp->record_length = (0x3f & input[0]) << 8 | input[1];
+ ssl_state->curr_connp->content_type = input[3];
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ ssl_state->curr_connp->bytes_processed += 4;
+ return 4;
+ } else {
+ ssl_state->curr_connp->record_length = (0x3f & *(input++)) << 8;
+ if (--input_len == 0)
+ break;
+ }
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 2:
+ /* padding */
+ input++;
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 3:
+ ssl_state->curr_connp->content_type = *(input++);
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+ }
+
+ ssl_state->curr_connp->bytes_processed += (input - initial_input);
+
+ return (input - initial_input);
+}
+
+static int SSLv2Decode(uint8_t direction, SSLState *ssl_state,
+ AppLayerParserState *pstate, uint8_t *input,
+ uint32_t input_len)
+{
+ int retval = 0;
+ uint8_t *initial_input = input;
+
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ if (input[0] & 0x80) {
+ ssl_state->curr_connp->record_lengths_length = 2;
+ } else {
+ ssl_state->curr_connp->record_lengths_length = 3;
+ }
+ }
+
+ /* the + 1 because, we also read one extra byte inside SSLv2ParseRecord
+ * to read the msg_type */
+ if (ssl_state->curr_connp->bytes_processed < (ssl_state->curr_connp->record_lengths_length + 1)) {
+ retval = SSLv2ParseRecord(direction, ssl_state, input, input_len);
+ if (retval == -1) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
+ return -1;
+ } else {
+ input += retval;
+ input_len -= retval;
+ }
+ }
+
+ if (input_len == 0) {
+ return (input - initial_input);
+ }
+
+ switch (ssl_state->curr_connp->content_type) {
+ case SSLV2_MT_ERROR:
+ SCLogDebug("SSLV2_MT_ERROR msg_type received. "
+ "Error encountered in establishing the sslv2 "
+ "session, may be version");
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED);
+
+ break;
+
+ case SSLV2_MT_CLIENT_HELLO:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_CLIENT_HELLO;
+ ssl_state->flags |= SSL_AL_FLAG_SSL_CLIENT_HS;
+
+ if (ssl_state->curr_connp->record_lengths_length == 3) {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 4:
+ if (input_len >= 6) {
+ ssl_state->curr_connp->session_id_length = input[4] << 8;
+ ssl_state->curr_connp->session_id_length |= input[5];
+ input += 6;
+ input_len -= 6;
+ ssl_state->curr_connp->bytes_processed += 6;
+ if (ssl_state->curr_connp->session_id_length == 0) {
+ ssl_state->flags |= SSL_AL_FLAG_SSL_NO_SESSION_ID;
+ }
+ break;
+ } else {
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ }
+ /* fall through */
+ case 5:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 6:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 7:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 8:
+ ssl_state->curr_connp->session_id_length = *(input++) << 8;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ case 9:
+ ssl_state->curr_connp->session_id_length |= *(input++);
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ /* fall through */
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+
+ /* ssl_state->curr_connp->record_lengths_length is 3 */
+ } else {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 3:
+ if (input_len >= 6) {
+ ssl_state->curr_connp->session_id_length = input[4] << 8;
+ ssl_state->curr_connp->session_id_length |= input[5];
+ input += 6;
+ input_len -= 6;
+ ssl_state->curr_connp->bytes_processed += 6;
+ if (ssl_state->curr_connp->session_id_length == 0) {
+ ssl_state->flags |= SSL_AL_FLAG_SSL_NO_SESSION_ID;
+ }
+ break;
+ } else {
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ }
+ case 4:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ case 5:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ case 6:
+ input++;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ case 7:
+ ssl_state->curr_connp->session_id_length = *(input++) << 8;
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ case 8:
+ ssl_state->curr_connp->session_id_length |= *(input++);
+ ssl_state->curr_connp->bytes_processed++;
+ if (--input_len == 0)
+ break;
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+ } /* else - if (ssl_state->curr_connp->record_lengths_length == 3) */
+
+ break;
+
+ case SSLV2_MT_CLIENT_MASTER_KEY:
+ if ( !(ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS)) {
+ SCLogDebug("Client hello is not seen before master key "
+ "message!!");
+ }
+ ssl_state->flags |= SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY;
+
+ break;
+
+ case SSLV2_MT_CLIENT_CERTIFICATE:
+ if (direction == 1) {
+ SCLogDebug("Incorrect SSL Record type sent in the toclient "
+ "direction!");
+ } else {
+ ssl_state->flags |= SSL_AL_FLAG_STATE_CLIENT_KEYX;
+ }
+ /* fall through */
+ case SSLV2_MT_SERVER_VERIFY:
+ case SSLV2_MT_SERVER_FINISHED:
+ if (direction == 0 &&
+ !(ssl_state->curr_connp->content_type & SSLV2_MT_CLIENT_CERTIFICATE)) {
+ SCLogDebug("Incorrect SSL Record type sent in the toserver "
+ "direction!");
+ }
+ /* fall through */
+ case SSLV2_MT_CLIENT_FINISHED:
+ case SSLV2_MT_REQUEST_CERTIFICATE:
+ /* both ways hello seen */
+ if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS) &&
+ (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_HS)) {
+
+ if (direction == 0) {
+ if (ssl_state->flags & SSL_AL_FLAG_SSL_NO_SESSION_ID) {
+ ssl_state->flags |= SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 client side has started the encryption");
+ } else if (ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY) {
+ ssl_state->flags |= SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 client side has started the encryption");
+ }
+ } else {
+ ssl_state->flags |= SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 Server side has started the encryption");
+ }
+
+ if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED) &&
+ (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED)) {
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_INSPECTION);
+ if (ssl_config.no_reassemble == 1)
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
+ SCLogDebug("SSLv2 No reassembly & inspection has been set");
+ }
+ }
+
+ break;
+
+ case SSLV2_MT_SERVER_HELLO:
+ ssl_state->flags |= SSL_AL_FLAG_STATE_SERVER_HELLO;
+ ssl_state->flags |= SSL_AL_FLAG_SSL_SERVER_HS;
+
+ break;
+ }
+
+ if (input_len + ssl_state->curr_connp->bytes_processed >=
+ (ssl_state->curr_connp->record_length + ssl_state->curr_connp->record_lengths_length)) {
+ /* looks like we have another record after this*/
+ uint32_t diff = ssl_state->curr_connp->record_length +
+ ssl_state->curr_connp->record_lengths_length + - ssl_state->curr_connp->bytes_processed;
+ input += diff;
+ SSLParserReset(ssl_state);
+ return (input - initial_input);
+
+ /* we still don't have the entire record for the one we are
+ * currently parsing */
+ } else {
+ input += input_len;
+ ssl_state->curr_connp->bytes_processed += input_len;
+ return (input - initial_input);
+ }
+}
+
+static int SSLv3Decode(uint8_t direction, SSLState *ssl_state,
+ AppLayerParserState *pstate, uint8_t *input,
+ uint32_t input_len)
+{
+ int retval = 0;
+ uint32_t parsed = 0;
+
+ if (ssl_state->curr_connp->bytes_processed < SSLV3_RECORD_HDR_LEN) {
+ retval = SSLv3ParseRecord(direction, ssl_state, input, input_len);
+ if (retval < 0) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_TLS_HEADER);
+ return -1;
+ } else {
+ parsed += retval;
+ input_len -= retval;
+ }
+ }
+
+ if (input_len == 0) {
+ return parsed;
+ }
+
+ /* check record version */
+ if (ssl_state->curr_connp->version < SSL_VERSION_3 ||
+ ssl_state->curr_connp->version > TLS_VERSION_12) {
+
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_INVALID_RECORD_VERSION);
+ return -1;
+ }
+
+ switch (ssl_state->curr_connp->content_type) {
+
+ /* we don't need any data from these types */
+ case SSLV3_CHANGE_CIPHER_SPEC:
+ ssl_state->flags |= SSL_AL_FLAG_CHANGE_CIPHER_SPEC;
+
+ if (direction)
+ ssl_state->flags |= SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC;
+ else
+ ssl_state->flags |= SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC;
+
+ break;
+
+ case SSLV3_ALERT_PROTOCOL:
+ break;
+ case SSLV3_APPLICATION_PROTOCOL:
+ if ((ssl_state->flags & SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC) &&
+ (ssl_state->flags & SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC)) {
+ /*
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION);
+ if (ssl_config.no_reassemble == 1)
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
+ */
+ AppLayerParserStateSetFlag(pstate,APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD);
+ }
+
+ break;
+
+ case SSLV3_HANDSHAKE_PROTOCOL:
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC)
+ break;
+
+ if (ssl_state->curr_connp->record_length < 4) {
+ SSLParserReset(ssl_state);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ retval = SSLv3ParseHandshakeProtocol(ssl_state, input + parsed, input_len);
+ if (retval < 0) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ } else {
+ if ((uint32_t)retval > input_len) {
+ SCLogDebug("Error parsing SSLv3.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+ parsed += retval;
+ input_len -= retval;
+ if (ssl_state->curr_connp->bytes_processed == ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) {
+ SSLParserReset(ssl_state);
+ }
+
+ SCLogDebug("trigger RAW! (post HS)");
+ AppLayerParserTriggerRawStreamReassembly(ssl_state->f);
+ return parsed;
+ }
+
+ break;
+ case SSLV3_HEARTBEAT_PROTOCOL:
+ retval = SSLv3ParseHeartbeatProtocol(ssl_state, input + parsed, input_len, direction);
+ if (retval < 0)
+ return -1;
+ break;
+
+ default:
+ /* \todo fix the event from invalid rule to unknown rule */
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ if (input_len + ssl_state->curr_connp->bytes_processed >= ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) {
+ if ((ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) < ssl_state->curr_connp->bytes_processed) {
+ /* defensive checks. Something's wrong. */
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ SCLogDebug("record complete, trigger RAW");
+ AppLayerParserTriggerRawStreamReassembly(ssl_state->f);
+
+ /* looks like we have another record */
+ uint32_t diff = ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN - ssl_state->curr_connp->bytes_processed;
+ parsed += diff;
+ SSLParserReset(ssl_state);
+ return parsed;
+
+ /* we still don't have the entire record for the one we are
+ * currently parsing */
+ } else {
+ parsed += input_len;
+ ssl_state->curr_connp->bytes_processed += input_len;
+ return parsed;
+ }
+
+}
+
+/**
+ * \internal
+ * \brief SSLv2, SSLv23, SSLv3, TLSv1.1, TLSv1.2, TLSv1.3 parser.
+ *
+ * On parsing error, this should be the only function that should reset
+ * the parser state, to avoid multiple functions in the chain reseting
+ * the parser state.
+ *
+ * \param direction 0 for toserver, 1 for toclient.
+ * \param alstate Pointer to the state.
+ * \param pstate Application layer parser state for this session.
+ * \param input Pointer the received input data.
+ * \param input_len Length in bytes of the received data.
+ * \param output Pointer to the list of parsed output elements.
+ *
+ * \todo On reaching an inconsistent state, check if the input has
+ * another new record, instead of just returning after the reset
+ *
+ * \retval >=0 On success.
+ */
+static int SSLDecode(Flow *f, uint8_t direction, void *alstate, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t ilen)
+{
+ SSLState *ssl_state = (SSLState *)alstate;
+ int retval = 0;
+ uint8_t counter = 0;
+
+ int32_t input_len = (int32_t)ilen;
+
+ ssl_state->f = f;
+
+ if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ SCReturnInt(1);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ if (direction == 0)
+ ssl_state->curr_connp = &ssl_state->client_connp;
+ else
+ ssl_state->curr_connp = &ssl_state->server_connp;
+
+ /* if we have more than one record */
+ while (input_len > 0) {
+ if (counter++ == 30) {
+ SCLogDebug("Looks like we have looped quite a bit. Reset state "
+ "and get out of here");
+ SSLParserReset(ssl_state);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ /* ssl_state->bytes_processed is 0 for a
+ * fresh record or positive to indicate a record currently being
+ * parsed */
+ switch (ssl_state->curr_connp->bytes_processed) {
+ /* fresh record */
+ case 0:
+ /* only SSLv2, has one of the top 2 bits set */
+ if ((input[0] & 0x80) || (input[0] & 0x40)) {
+ SCLogDebug("SSLv2 detected");
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ retval = SSLv2Decode(direction, ssl_state, pstate, input,
+ input_len);
+ if (retval < 0) {
+ SCLogDebug("Error parsing SSLv2.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ } else {
+ input_len -= retval;
+ input += retval;
+ }
+ } else {
+ SCLogDebug("SSLv3.x detected");
+ /* we will keep it this way till our record parser tells
+ * us what exact version it is */
+ ssl_state->curr_connp->version = TLS_VERSION_UNKNOWN;
+ retval = SSLv3Decode(direction, ssl_state, pstate, input,
+ input_len);
+ if (retval < 0) {
+ SCLogDebug("Error parsing SSLv3.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ } else {
+ input_len -= retval;
+ input += retval;
+ if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN
+ && ssl_state->curr_connp->record_length == 0) {
+ /* empty record */
+ SSLParserReset(ssl_state);
+ }
+ }
+ }
+
+ break;
+
+ default:
+ /* we would have established by now if we are dealing with
+ * SSLv2 or above */
+ if (ssl_state->curr_connp->version == SSL_VERSION_2) {
+ SCLogDebug("Continuing parsing SSLv2 record from where we "
+ "previously left off");
+ retval = SSLv2Decode(direction, ssl_state, pstate, input,
+ input_len);
+ if (retval == -1) {
+ SCLogDebug("Error parsing SSLv2.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ return 0;
+ } else {
+ input_len -= retval;
+ input += retval;
+ }
+ } else {
+ SCLogDebug("Continuing parsing SSLv3.x record from where we "
+ "previously left off");
+ retval = SSLv3Decode(direction, ssl_state, pstate, input,
+ input_len);
+ if (retval < 0) {
+ SCLogDebug("Error parsing SSLv3.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ return 0;
+ } else {
+ if (retval > input_len) {
+ SCLogDebug("Error parsing SSLv3.x. Reseting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ }
+ input_len -= retval;
+ input += retval;
+ if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN
+ && ssl_state->curr_connp->record_length == 0) {
+ /* empty record */
+ SSLParserReset(ssl_state);
+ }
+ }
+ }
+
+ break;
+ } /* switch (ssl_state->curr_connp->bytes_processed) */
+ } /* while (input_len) */
+
+ return 1;
+}
+
+int SSLParseClientRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return SSLDecode(f, 0 /* toserver */, alstate, pstate, input, input_len);
+}
+
+int SSLParseServerRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ return SSLDecode(f, 1 /* toclient */, alstate, pstate, input, input_len);
+}
+
+/**
+ * \internal
+ * \brief Function to allocate the SSL state memory.
+ */
+void *SSLStateAlloc(void)
+{
+ SSLState *ssl_state = SCMalloc(sizeof(SSLState));
+ if (unlikely(ssl_state == NULL))
+ return NULL;
+ memset(ssl_state, 0, sizeof(SSLState));
+ ssl_state->client_connp.cert_log_flag = 0;
+ ssl_state->server_connp.cert_log_flag = 0;
+ TAILQ_INIT(&ssl_state->server_connp.certs);
+
+ return (void *)ssl_state;
+}
+
+/**
+ * \internal
+ * \brief Function to free the SSL state memory.
+ */
+void SSLStateFree(void *p)
+{
+ SSLState *ssl_state = (SSLState *)p;
+ SSLCertsChain *item;
+
+ if (ssl_state->client_connp.trec)
+ SCFree(ssl_state->client_connp.trec);
+ if (ssl_state->client_connp.cert0_subject)
+ SCFree(ssl_state->client_connp.cert0_subject);
+ if (ssl_state->client_connp.cert0_issuerdn)
+ SCFree(ssl_state->client_connp.cert0_issuerdn);
+ if (ssl_state->client_connp.cert0_fingerprint)
+ SCFree(ssl_state->client_connp.cert0_fingerprint);
+
+ if (ssl_state->server_connp.trec)
+ SCFree(ssl_state->server_connp.trec);
+ if (ssl_state->server_connp.cert0_subject)
+ SCFree(ssl_state->server_connp.cert0_subject);
+ if (ssl_state->server_connp.cert0_issuerdn)
+ SCFree(ssl_state->server_connp.cert0_issuerdn);
+ if (ssl_state->server_connp.cert0_fingerprint)
+ SCFree(ssl_state->server_connp.cert0_fingerprint);
+
+ /* Free certificate chain */
+ while ((item = TAILQ_FIRST(&ssl_state->server_connp.certs))) {
+ TAILQ_REMOVE(&ssl_state->server_connp.certs, item, next);
+ SCFree(item);
+ }
+ TAILQ_INIT(&ssl_state->server_connp.certs);
+
+ SCFree(ssl_state);
+
+ return;
+}
+
+static uint16_t SSLProbingParser(uint8_t *input, uint32_t ilen, uint32_t *offset)
+{
+ /* probably a rst/fin sending an eof */
+ if (ilen == 0)
+ return ALPROTO_UNKNOWN;
+
+ /* for now just the 3 byte header ones */
+ /* \todo Detect the 2 byte ones */
+ if ((input[0] & 0x80) && (input[2] == 0x01)) {
+ return ALPROTO_TLS;
+ }
+
+ return ALPROTO_FAILED;
+}
+
+int SSLStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, tls_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "ssl's enum map table.", event_name);
+ /* yes this is fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_GENERAL;
+
+ return 0;
+}
+
+static int SSLRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 00 02|", 5, 2, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** SSLv3 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 00|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 00|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 01|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 01|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 02|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 02|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.2 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 03|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 03|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /***** toclient direction *****/
+
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.2 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /* Subsection - SSLv2 style record by client, but informing the server
+ * the max version it supports.
+ * Updated by Anoop Saldanha. Disabled it for now. We'll get back to
+ * it after some tests */
+#if 0
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 00|", 5, 2, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|00 02|", 7, 5, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * \brief Function to register the SSL protocol parser and other functions
+ */
+void RegisterSSLParsers(void)
+{
+ char *proto_name = "tls";
+
+ /** SSLv2 and SSLv23*/
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_TLS, proto_name);
+
+ if (SSLRegisterPatternsForProtocolDetection() < 0)
+ return;
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "443",
+ ALPROTO_TLS,
+ 0, 3,
+ STREAM_TOSERVER,
+ SSLProbingParser);
+ } else {
+ AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
+ proto_name, ALPROTO_TLS,
+ 0, 3,
+ SSLProbingParser);
+ }
+ } else {
+ SCLogInfo("Protocol detection and parser disabled for %s protocol",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER,
+ SSLParseClientRecord);
+
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOCLIENT,
+ SSLParseServerRecord);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfo);
+
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateAlloc, SSLStateFree);
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER);
+
+ /* Get the value of no reassembly option from the config file */
+ if (ConfGetNode("app-layer.protocols.tls.no-reassemble") == NULL) {
+ if (ConfGetBool("tls.no-reassemble", &ssl_config.no_reassemble) != 1)
+ ssl_config.no_reassemble = 1;
+ } else {
+ if (ConfGetBool("app-layer.protocols.tls.no-reassemble", &ssl_config.no_reassemble) != 1)
+ ssl_config.no_reassemble = 1;
+ }
+ } else {
+ SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_TLS, SSLParserRegisterTests);
+#endif
+
+ /* Get the value of no reassembly option from the config file */
+ if (ConfGetBool("tls.no-reassemble", &ssl_config.no_reassemble) != 1)
+ ssl_config.no_reassemble = 1;
+
+ return;
+}
+
+/***************************************Unittests******************************/
+
+#ifdef UNITTESTS
+
+/**
+ *\test Send a get request in one chunk.
+ */
+static int SSLParserTest01(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf[] = { 0x16, 0x03, 0x01 };
+ uint32_t tlslen = sizeof(tlsbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER | STREAM_EOF, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a get request in two chunks. */
+static int SSLParserTest02(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03, 0x01 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a get request in three chunks. */
+static int SSLParserTest03(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ uint8_t tlsbuf3[] = { 0x01 };
+ uint32_t tlslen3 = sizeof(tlsbuf3);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test Send a get request in three chunks + more data. */
+static int SSLParserTest04(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ uint8_t tlsbuf3[] = { 0x01 };
+ uint32_t tlslen3 = sizeof(tlsbuf3);
+ uint8_t tlsbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x01 };
+ uint32_t tlslen4 = sizeof(tlsbuf4);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf4, tlslen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#if 0
+/** \test Test the setting up of no reassembly and no payload inspection flag
+ * after detection of the TLS handshake completion */
+static int SSLParserTest05(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf[] = { 0x16, 0x03, 0x01, 0x00, 0x01 };
+ uint32_t tlslen = sizeof(tlsbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x17;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x17) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.client_version);
+ result = 0;
+ goto end;
+ }
+
+ AppLayerParserStateStore *parser_state_store = (AppLayerParserStateStore *)
+ ssn.alparser;
+ AppLayerParserState *parser_state = &parser_state_store->to_server;
+
+ if (!(parser_state->flags & APP_LAYER_PARSER_NO_INSPECTION) &&
+ !(ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) &&
+ !(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY))
+ {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+ if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+#endif
+
+#if 0
+/** \test Test the setting up of no reassembly and no payload inspection flag
+ * after detection of the valid TLS handshake completion, the rouge
+ * 0x17 packet will not be considered in the detection process */
+static int SSLParserTest06(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf[] = { 0x16, 0x03, 0x01, 0x00, 0x01 };
+ uint32_t tlslen = sizeof(tlsbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x17;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x17) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp._content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ AppLayerParserStateStore *parser_state_store = (AppLayerParserStateStore *)
+ ssn.alparser;
+ AppLayerParserState *parser_state = &parser_state_store->to_server;
+
+ if ((parser_state->flags & APP_LAYER_PARSER_NO_INSPECTION) ||
+ (ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("The flags should not be set\n");
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x17;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ if (!(parser_state->flags & APP_LAYER_PARSER_NO_INSPECTION) &&
+ !(ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) &&
+ !(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+ if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+#endif
+
+/** \test multimsg test */
+static int SSLParserMultimsgTest01(void)
+{
+ int result = 1;
+ Flow f;
+ /* 3 msgs */
+ uint8_t tlsbuf1[] = {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0xd3, 0x6f, 0x1f, 0x63, 0x82,
+ 0x8d, 0x75, 0x77, 0x8c, 0x91, 0xbc, 0xa1, 0x3d,
+ 0xbb, 0xe1, 0xb5, 0xd3, 0x31, 0x92, 0x59, 0x2b,
+ 0x2c, 0x43, 0x96, 0xa3, 0xaa, 0x23, 0x92, 0xd0,
+ 0x91, 0x2a, 0x5e, 0x10, 0x5b, 0xc8, 0xc1, 0xe2,
+ 0xd3, 0x5c, 0x8b, 0x8c, 0x91, 0x9e, 0xc2, 0xf2,
+ 0x9c, 0x3c, 0x4f, 0x37, 0x1e, 0x20, 0x5e, 0x33,
+ 0xd5, 0xf0, 0xd6, 0xaf, 0x89, 0xf5, 0xcc, 0xb2,
+ 0xcf, 0xc1, 0x60, 0x3a, 0x46, 0xd5, 0x4e, 0x2a,
+ 0xb6, 0x6a, 0xb9, 0xfc, 0x32, 0x8b, 0xe0, 0x6e,
+ 0xa0, 0xed, 0x25, 0xa0, 0xa4, 0x82, 0x81, 0x73,
+ 0x90, 0xbf, 0xb5, 0xde, 0xeb, 0x51, 0x8d, 0xde,
+ 0x5b, 0x6f, 0x94, 0xee, 0xba, 0xe5, 0x69, 0xfa,
+ 0x1a, 0x80, 0x30, 0x54, 0xeb, 0x12, 0x01, 0xb9,
+ 0xfe, 0xbf, 0x82, 0x95, 0x01, 0x7b, 0xb0, 0x97,
+ 0x14, 0xc2, 0x06, 0x3c, 0x69, 0xfb, 0x1c, 0x66,
+ 0x47, 0x17, 0xd9, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xf6, 0xbc,
+ 0x0d, 0x6f, 0xe8, 0xbb, 0xaa, 0xbf, 0x14, 0xeb,
+ 0x7b, 0xcc, 0x6c, 0x28, 0xb0, 0xfc, 0xa6, 0x01,
+ 0x2a, 0x97, 0x96, 0x17, 0x5e, 0xe8, 0xb4, 0x4e,
+ 0x78, 0xc9, 0x04, 0x65, 0x53, 0xb6, 0x93, 0x3d,
+ 0xeb, 0x44, 0xee, 0x86, 0xf9, 0x80, 0x49, 0x45,
+ 0x21, 0x34, 0xd1, 0xee, 0xc8, 0x9c
+ };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/** \test multimsg test server */
+static int SSLParserMultimsgTest02(void)
+{
+ int result = 1;
+ Flow f;
+ /* 3 msgs */
+ uint8_t tlsbuf1[] = {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0xd3, 0x6f, 0x1f, 0x63, 0x82,
+ 0x8d, 0x75, 0x77, 0x8c, 0x91, 0xbc, 0xa1, 0x3d,
+ 0xbb, 0xe1, 0xb5, 0xd3, 0x31, 0x92, 0x59, 0x2b,
+ 0x2c, 0x43, 0x96, 0xa3, 0xaa, 0x23, 0x92, 0xd0,
+ 0x91, 0x2a, 0x5e, 0x10, 0x5b, 0xc8, 0xc1, 0xe2,
+ 0xd3, 0x5c, 0x8b, 0x8c, 0x91, 0x9e, 0xc2, 0xf2,
+ 0x9c, 0x3c, 0x4f, 0x37, 0x1e, 0x20, 0x5e, 0x33,
+ 0xd5, 0xf0, 0xd6, 0xaf, 0x89, 0xf5, 0xcc, 0xb2,
+ 0xcf, 0xc1, 0x60, 0x3a, 0x46, 0xd5, 0x4e, 0x2a,
+ 0xb6, 0x6a, 0xb9, 0xfc, 0x32, 0x8b, 0xe0, 0x6e,
+ 0xa0, 0xed, 0x25, 0xa0, 0xa4, 0x82, 0x81, 0x73,
+ 0x90, 0xbf, 0xb5, 0xde, 0xeb, 0x51, 0x8d, 0xde,
+ 0x5b, 0x6f, 0x94, 0xee, 0xba, 0xe5, 0x69, 0xfa,
+ 0x1a, 0x80, 0x30, 0x54, 0xeb, 0x12, 0x01, 0xb9,
+ 0xfe, 0xbf, 0x82, 0x95, 0x01, 0x7b, 0xb0, 0x97,
+ 0x14, 0xc2, 0x06, 0x3c, 0x69, 0xfb, 0x1c, 0x66,
+ 0x47, 0x17, 0xd9, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xf6, 0xbc,
+ 0x0d, 0x6f, 0xe8, 0xbb, 0xaa, 0xbf, 0x14, 0xeb,
+ 0x7b, 0xcc, 0x6c, 0x28, 0xb0, 0xfc, 0xa6, 0x01,
+ 0x2a, 0x97, 0x96, 0x17, 0x5e, 0xe8, 0xb4, 0x4e,
+ 0x78, 0xc9, 0x04, 0x65, 0x53, 0xb6, 0x93, 0x3d,
+ 0xeb, 0x44, 0xee, 0x86, 0xf9, 0x80, 0x49, 0x45,
+ 0x21, 0x34, 0xd1, 0xee, 0xc8, 0x9c
+ };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->server_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->server_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->server_connp.version != 0x0301) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301,
+ ssl_state->server_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test the detection of SSLv3 protocol from the given packet
+ */
+static int SSLParserTest07(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf[] = { 0x16, 0x03, 0x00, 0x00, 0x6f, 0x01,
+ 0x00, 0x00, 0x6b, 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00 };
+ uint32_t tlslen = sizeof(tlsbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#if 0
+/** \test Test the setting up of no reassembly and no payload inspection flag
+ * after detection of the SSLv3 handshake completion */
+static int SSLParserTest08(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t tlsbuf[] = { 0x16, 0x03, 0x00, 0x00, 0x01 };
+ uint32_t tlslen = sizeof(tlsbuf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x14;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ tlsbuf[0] = 0x17;
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf, tlslen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x17) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ AppLayerParserStateStore *parser_state_store = (AppLayerParserStateStore *)
+ ssn.alparser;
+ AppLayerParserState *parser_state = &parser_state_store->to_server;
+
+ if (!(parser_state->flags & APP_LAYER_PARSER_NO_INSPECTION) &&
+ !(ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) &&
+ !(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+ if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#endif
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest09(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x03, 0x00, 0x00, 0x6f, 0x01,
+ 0x00, 0x00, 0x6b, 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf2_len = sizeof(buf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest10(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16, 0x03,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x00, 0x00, 0x6f, 0x01,
+ 0x00, 0x00, 0x6b, 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf2_len = sizeof(buf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest11(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x6f, 0x01,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x00, 0x00, 0x6b, 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf2_len = sizeof(buf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest12(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x6f, 0x01,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x00, 0x00, 0x6b,
+ };
+ uint32_t buf2_len = sizeof(buf2);
+
+ uint8_t buf3[] = {
+ 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf3_len = sizeof(buf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf3, buf3_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest13(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x6f, 0x01,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x00, 0x00, 0x6b,
+ };
+ uint32_t buf2_len = sizeof(buf2);
+
+ uint8_t buf3[] = {
+ 0x03, 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7,
+ };
+ uint32_t buf3_len = sizeof(buf3);
+
+ uint8_t buf4[] = {
+ 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf4_len = sizeof(buf4);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf3, buf3_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf4, buf4_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x17,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest14(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t buf2_len = sizeof(buf2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest15(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x01, 0x01,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r == 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest16(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r == 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest17(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r == 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest18(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00,
+ 0x6b,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t buf2_len = sizeof(buf2);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest19(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00,
+ 0x6b, 0x16, 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest20(void)
+{
+ int result = 1;
+ Flow f;
+
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00,
+ 0x16, 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r == 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test SSLv2 Record parsing.
+ */
+static int SSLParserTest21(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t buf[] = {
+ 0x80, 0x31, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01,
+ };
+ uint32_t buf_len = sizeof(buf);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER | STREAM_EOF, buf,
+ buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ goto end;
+ }
+
+ if (app_state->client_connp.content_type != SSLV2_MT_CLIENT_HELLO) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV2_MT_SERVER_HELLO, app_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (app_state->client_connp.version != SSL_VERSION_2) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_2, app_state->client_connp.version);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test SSLv2 Record parsing.
+ */
+static int SSLParserTest22(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf[] = {
+ 0x80, 0x31, 0x04, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x10, 0x07, 0x00, 0xc0,
+ 0x05, 0x00, 0x80, 0x03, 0x00, 0x80, 0x01, 0x00,
+ 0x80, 0x08, 0x00, 0x80, 0x06, 0x00, 0x40, 0x04,
+ 0x00, 0x80, 0x02, 0x00, 0x80, 0x76, 0x64, 0x75,
+ 0x2d, 0xa7, 0x98, 0xfe, 0xc9, 0x12, 0x92, 0xc1,
+ 0x2f, 0x34, 0x84, 0x20, 0xc5};
+ uint32_t buf_len = sizeof(buf);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ //AppLayerDetectProtoThreadInit();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT | STREAM_EOF, buf,
+ buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->server_connp.content_type != SSLV2_MT_SERVER_HELLO) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV2_MT_SERVER_HELLO, app_state->server_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->server_connp.version != SSL_VERSION_2) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_2, app_state->server_connp.version);
+ result = 0;
+ goto end;
+ }
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test SSLv2 Record parsing.
+ */
+static int SSLParserTest23(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t chello_buf[] = {
+ 0x80, 0x67, 0x01, 0x03, 0x00, 0x00, 0x4e, 0x00,
+ 0x00, 0x00, 0x10, 0x01, 0x00, 0x80, 0x03, 0x00,
+ 0x80, 0x07, 0x00, 0xc0, 0x06, 0x00, 0x40, 0x02,
+ 0x00, 0x80, 0x04, 0x00, 0x80, 0x00, 0x00, 0x39,
+ 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00,
+ 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x13, 0x00, 0xfe, 0xff, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00,
+ 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64,
+ 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x06, 0xa8, 0xb8, 0x93, 0xbb, 0x90, 0xe9, 0x2a,
+ 0xa2, 0x4d, 0x6d, 0xcc, 0x1c, 0xe7, 0x2a, 0x80,
+ 0x21
+ };
+ uint32_t chello_buf_len = sizeof(chello_buf);
+
+ uint8_t shello_buf[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x4a, 0x02,
+ 0x00, 0x00, 0x46, 0x03, 0x00, 0x44, 0x4c, 0x94,
+ 0x8f, 0xfe, 0x81, 0xed, 0x93, 0x65, 0x02, 0x88,
+ 0xa3, 0xf8, 0xeb, 0x63, 0x86, 0x0e, 0x2c, 0xf6,
+ 0x8d, 0xd0, 0x0f, 0x2c, 0x2a, 0xd6, 0x4f, 0xcd,
+ 0x2d, 0x3c, 0x16, 0xd7, 0xd6, 0x20, 0xa0, 0xfb,
+ 0x60, 0x86, 0x3d, 0x1e, 0x76, 0xf3, 0x30, 0xfe,
+ 0x0b, 0x01, 0xfd, 0x1a, 0x01, 0xed, 0x95, 0xf6,
+ 0x7b, 0x8e, 0xc0, 0xd4, 0x27, 0xbf, 0xf0, 0x6e,
+ 0xc7, 0x56, 0xb1, 0x47, 0xce, 0x98, 0x00, 0x35,
+ 0x00, 0x16, 0x03, 0x00, 0x03, 0x44, 0x0b, 0x00,
+ 0x03, 0x40, 0x00, 0x03, 0x3d, 0x00, 0x03, 0x3a,
+ 0x30, 0x82, 0x03, 0x36, 0x30, 0x82, 0x02, 0x9f,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x58, 0x59, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0c, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20,
+ 0x44, 0x65, 0x73, 0x65, 0x72, 0x74, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x0a, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20, 0x54,
+ 0x6f, 0x77, 0x6e, 0x31, 0x17, 0x30, 0x15, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x53, 0x6e,
+ 0x61, 0x6b, 0x65, 0x20, 0x4f, 0x69, 0x6c, 0x2c,
+ 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1e, 0x30, 0x1c,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x31, 0x15, 0x30, 0x13,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0c, 0x53,
+ 0x6e, 0x61, 0x6b, 0x65, 0x20, 0x4f, 0x69, 0x6c,
+ 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x0f, 0x63, 0x61, 0x40, 0x73,
+ 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69, 0x6c, 0x2e,
+ 0x64, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30,
+ 0x33, 0x30, 0x33, 0x30, 0x35, 0x31, 0x36, 0x34,
+ 0x37, 0x34, 0x35, 0x5a, 0x17, 0x0d, 0x30, 0x38,
+ 0x30, 0x33, 0x30, 0x33, 0x31, 0x36, 0x34, 0x37,
+ 0x34, 0x35, 0x5a, 0x30, 0x81, 0xa7, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x58, 0x59, 0x31, 0x15, 0x30, 0x13, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x0c, 0x53, 0x6e,
+ 0x61, 0x6b, 0x65, 0x20, 0x44, 0x65, 0x73, 0x65,
+ 0x72, 0x74, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x6e, 0x61,
+ 0x6b, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0e, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20,
+ 0x4f, 0x69, 0x6c, 0x2c, 0x20, 0x4c, 0x74, 0x64,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0e, 0x57, 0x65, 0x62, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x54, 0x65, 0x61,
+ 0x6d, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69, 0x6c,
+ 0x2e, 0x64, 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x01, 0x16, 0x10, 0x77, 0x77, 0x77,
+ 0x40, 0x73, 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69,
+ 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x30, 0x81, 0x9f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81,
+ 0x81, 0x00, 0xa4, 0x6e, 0x53, 0x14, 0x0a, 0xde,
+ 0x2c, 0xe3, 0x60, 0x55, 0x9a, 0xf2, 0x42, 0xa6,
+ 0xaf, 0x47, 0x12, 0x2f, 0x17, 0xce, 0xfa, 0xba,
+ 0xdc, 0x4e, 0x63, 0x56, 0x34, 0xb9, 0xba, 0x73,
+ 0x4b, 0x78, 0x44, 0x3d, 0xc6, 0x6c, 0x69, 0xa4,
+ 0x25, 0xb3, 0x61, 0x02, 0x9d, 0x09, 0x04, 0x3f,
+ 0x72, 0x3d, 0xd8, 0x27, 0xd3, 0xb0, 0x5a, 0x45,
+ 0x77, 0xb7, 0x36, 0xe4, 0x26, 0x23, 0xcc, 0x12,
+ 0xb8, 0xae, 0xde, 0xa7, 0xb6, 0x3a, 0x82, 0x3c,
+ 0x7c, 0x24, 0x59, 0x0a, 0xf8, 0x96, 0x43, 0x8b,
+ 0xa3, 0x29, 0x36, 0x3f, 0x91, 0x7f, 0x5d, 0xc7,
+ 0x23, 0x94, 0x29, 0x7f, 0x0a, 0xce, 0x0a, 0xbd,
+ 0x8d, 0x9b, 0x2f, 0x19, 0x17, 0xaa, 0xd5, 0x8e,
+ 0xec, 0x66, 0xa2, 0x37, 0xeb, 0x3f, 0x57, 0x53,
+ 0x3c, 0xf2, 0xaa, 0xbb, 0x79, 0x19, 0x4b, 0x90,
+ 0x7e, 0xa7, 0xa3, 0x99, 0xfe, 0x84, 0x4c, 0x89,
+ 0xf0, 0x3d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x6e, 0x30, 0x6c, 0x30, 0x1b, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x14, 0x30, 0x12, 0x81, 0x10,
+ 0x77, 0x77, 0x77, 0x40, 0x73, 0x6e, 0x61, 0x6b,
+ 0x65, 0x6f, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d,
+ 0x30, 0x3a, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x2d, 0x16,
+ 0x2b, 0x6d, 0x6f, 0x64, 0x5f, 0x73, 0x73, 0x6c,
+ 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f,
+ 0x6d, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x30, 0x11, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01,
+ 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0xae, 0x79, 0x79, 0x22, 0x90, 0x75,
+ 0xfd, 0xa6, 0xd5, 0xc4, 0xb8, 0xc4, 0x99, 0x4e,
+ 0x1c, 0x05, 0x7c, 0x91, 0x59, 0xbe, 0x89, 0x0d,
+ 0x3d, 0xc6, 0x8c, 0xa3, 0xcf, 0xf6, 0xba, 0x23,
+ 0xdf, 0xb8, 0xae, 0x44, 0x68, 0x8a, 0x8f, 0xb9,
+ 0x8b, 0xcb, 0x12, 0xda, 0xe6, 0xa2, 0xca, 0xa5,
+ 0xa6, 0x55, 0xd9, 0xd2, 0xa1, 0xad, 0xba, 0x9b,
+ 0x2c, 0x44, 0x95, 0x1d, 0x4a, 0x90, 0x59, 0x7f,
+ 0x83, 0xae, 0x81, 0x5e, 0x3f, 0x92, 0xe0, 0x14,
+ 0x41, 0x82, 0x4e, 0x7f, 0x53, 0xfd, 0x10, 0x23,
+ 0xeb, 0x8a, 0xeb, 0xe9, 0x92, 0xea, 0x61, 0xf2,
+ 0x8e, 0x19, 0xa1, 0xd3, 0x49, 0xc0, 0x84, 0x34,
+ 0x1e, 0x2e, 0x6e, 0xf6, 0x98, 0xe2, 0x87, 0x53,
+ 0xd6, 0x55, 0xd9, 0x1a, 0x8a, 0x92, 0x5c, 0xad,
+ 0xdc, 0x1e, 0x1c, 0x30, 0xa7, 0x65, 0x9d, 0xc2,
+ 0x4f, 0x60, 0xd2, 0x6f, 0xdb, 0xe0, 0x9f, 0x9e,
+ 0xbc, 0x41, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e,
+ 0x00, 0x00, 0x00
+ };
+ uint32_t shello_buf_len = sizeof(shello_buf);
+
+ uint8_t client_change_cipher_spec_buf[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00,
+ 0x80, 0x65, 0x51, 0x2d, 0xa6, 0xd4, 0xa7, 0x38,
+ 0xdf, 0xac, 0x79, 0x1f, 0x0b, 0xd9, 0xb2, 0x61,
+ 0x7d, 0x73, 0x88, 0x32, 0xd9, 0xf2, 0x62, 0x3a,
+ 0x8b, 0x11, 0x04, 0x75, 0xca, 0x42, 0xff, 0x4e,
+ 0xd9, 0xcc, 0xb9, 0xfa, 0x86, 0xf3, 0x16, 0x2f,
+ 0x09, 0x73, 0x51, 0x66, 0xaa, 0x29, 0xcd, 0x80,
+ 0x61, 0x0f, 0xe8, 0x13, 0xce, 0x5b, 0x8e, 0x0a,
+ 0x23, 0xf8, 0x91, 0x5e, 0x5f, 0x54, 0x70, 0x80,
+ 0x8e, 0x7b, 0x28, 0xef, 0xb6, 0x69, 0xb2, 0x59,
+ 0x85, 0x74, 0x98, 0xe2, 0x7e, 0xd8, 0xcc, 0x76,
+ 0x80, 0xe1, 0xb6, 0x45, 0x4d, 0xc7, 0xcd, 0x84,
+ 0xce, 0xb4, 0x52, 0x79, 0x74, 0xcd, 0xe6, 0xd7,
+ 0xd1, 0x9c, 0xad, 0xef, 0x63, 0x6c, 0x0f, 0xf7,
+ 0x05, 0xe4, 0x4d, 0x1a, 0xd3, 0xcb, 0x9c, 0xd2,
+ 0x51, 0xb5, 0x61, 0xcb, 0xff, 0x7c, 0xee, 0xc7,
+ 0xbc, 0x5e, 0x15, 0xa3, 0xf2, 0x52, 0x0f, 0xbb,
+ 0x32, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16,
+ 0x03, 0x00, 0x00, 0x40, 0xa9, 0xd8, 0xd7, 0x35,
+ 0xbc, 0x39, 0x56, 0x98, 0xad, 0x87, 0x61, 0x2a,
+ 0xc4, 0x8f, 0xcc, 0x03, 0xcb, 0x93, 0x80, 0x81,
+ 0xb0, 0x4a, 0xc4, 0xd2, 0x09, 0x71, 0x3e, 0x90,
+ 0x3c, 0x8d, 0xe0, 0x95, 0x44, 0xfe, 0x56, 0xd1,
+ 0x7e, 0x88, 0xe2, 0x48, 0xfd, 0x76, 0x70, 0x76,
+ 0xe2, 0xcd, 0x06, 0xd0, 0xf3, 0x9d, 0x13, 0x79,
+ 0x67, 0x1e, 0x37, 0xf6, 0x98, 0xbe, 0x59, 0x18,
+ 0x4c, 0xfc, 0x75, 0x56
+ };
+ uint32_t client_change_cipher_spec_buf_len =
+ sizeof(client_change_cipher_spec_buf);
+
+ uint8_t server_change_cipher_spec_buf[] = {
+ 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x00, 0x00, 0x40, 0xce, 0x7c, 0x92, 0x43, 0x59,
+ 0xcc, 0x3d, 0x90, 0x91, 0x9c, 0x58, 0xf0, 0x7a,
+ 0xce, 0xae, 0x0d, 0x08, 0xe0, 0x76, 0xb4, 0x86,
+ 0xb1, 0x15, 0x5b, 0x32, 0xb8, 0x77, 0x53, 0xe7,
+ 0xa6, 0xf9, 0xd0, 0x95, 0x5f, 0xaa, 0x07, 0xc3,
+ 0x96, 0x7c, 0xc9, 0x88, 0xc2, 0x7a, 0x20, 0x89,
+ 0x4f, 0xeb, 0xeb, 0xb6, 0x19, 0xef, 0xaa, 0x27,
+ 0x73, 0x9d, 0xa6, 0xb4, 0x9f, 0xeb, 0x34, 0xe2,
+ 0x4d, 0x9f, 0x6b
+ };
+ uint32_t server_change_cipher_spec_buf_len =
+ sizeof(server_change_cipher_spec_buf);
+
+ uint8_t toserver_app_data_buf[] = {
+ 0x17, 0x03, 0x00, 0x01, 0xb0, 0x4a, 0xc3, 0x3e,
+ 0x9d, 0x77, 0x78, 0x01, 0x2c, 0xb4, 0xbc, 0x4c,
+ 0x9a, 0x84, 0xd7, 0xb9, 0x90, 0x0c, 0x21, 0x10,
+ 0xf0, 0xfa, 0x00, 0x7c, 0x16, 0xbb, 0x77, 0xfb,
+ 0x72, 0x42, 0x4f, 0xad, 0x50, 0x4a, 0xd0, 0xaa,
+ 0x6f, 0xaa, 0x44, 0x6c, 0x62, 0x94, 0x1b, 0xc5,
+ 0xfe, 0xe9, 0x1c, 0x5e, 0xde, 0x85, 0x0b, 0x0e,
+ 0x05, 0xe4, 0x18, 0x6e, 0xd2, 0xd3, 0xb5, 0x20,
+ 0xab, 0x81, 0xfd, 0x18, 0x9a, 0x73, 0xb8, 0xd7,
+ 0xef, 0xc3, 0xdd, 0x74, 0xd7, 0x9c, 0x1e, 0x6f,
+ 0x21, 0x6d, 0xf8, 0x24, 0xca, 0x3c, 0x70, 0x78,
+ 0x36, 0x12, 0x7a, 0x8a, 0x9c, 0xac, 0x4e, 0x1c,
+ 0xa8, 0xfb, 0x27, 0x30, 0xba, 0x9a, 0xf4, 0x2f,
+ 0x0a, 0xab, 0x80, 0x6a, 0xa1, 0x60, 0x74, 0xf0,
+ 0xe3, 0x91, 0x84, 0xe7, 0x90, 0x88, 0xcc, 0xf0,
+ 0x95, 0x7b, 0x0a, 0x22, 0xf2, 0xf9, 0x27, 0xe0,
+ 0xdd, 0x38, 0x0c, 0xfd, 0xe9, 0x03, 0x71, 0xdc,
+ 0x70, 0xa4, 0x6e, 0xdf, 0xe3, 0x72, 0x9e, 0xa1,
+ 0xf0, 0xc9, 0x00, 0xd6, 0x03, 0x55, 0x6a, 0x67,
+ 0x5d, 0x9c, 0xb8, 0x75, 0x01, 0xb0, 0x01, 0x9f,
+ 0xe6, 0xd2, 0x44, 0x18, 0xbc, 0xca, 0x7a, 0x10,
+ 0x39, 0xa6, 0xcf, 0x15, 0xc7, 0xf5, 0x35, 0xd4,
+ 0xb3, 0x6d, 0x91, 0x23, 0x84, 0x99, 0xba, 0xb0,
+ 0x7e, 0xd0, 0xc9, 0x4c, 0xbf, 0x3f, 0x33, 0x68,
+ 0x37, 0xb7, 0x7d, 0x44, 0xb0, 0x0b, 0x2c, 0x0f,
+ 0xd0, 0x75, 0xa2, 0x6b, 0x5b, 0xe1, 0x9f, 0xd4,
+ 0x69, 0x9a, 0x14, 0xc8, 0x29, 0xb7, 0xd9, 0x10,
+ 0xbb, 0x99, 0x30, 0x9a, 0xfb, 0xcc, 0x13, 0x1f,
+ 0x76, 0x4e, 0xe6, 0xdf, 0x14, 0xaa, 0xd5, 0x60,
+ 0xbf, 0x91, 0x49, 0x0d, 0x64, 0x42, 0x29, 0xa8,
+ 0x64, 0x27, 0xd4, 0x5e, 0x1b, 0x18, 0x03, 0xa8,
+ 0x73, 0xd6, 0x05, 0x6e, 0xf7, 0x50, 0xb0, 0x09,
+ 0x6b, 0x69, 0x7a, 0x12, 0x28, 0x58, 0xef, 0x5a,
+ 0x86, 0x11, 0xde, 0x71, 0x71, 0x9f, 0xca, 0xbd,
+ 0x79, 0x2a, 0xc2, 0xe5, 0x9b, 0x5e, 0x32, 0xe7,
+ 0xcb, 0x97, 0x6e, 0xa0, 0xea, 0xa4, 0xa4, 0x6a,
+ 0x32, 0xf9, 0x37, 0x39, 0xd8, 0x37, 0x6d, 0x63,
+ 0xf3, 0x08, 0x1c, 0xdd, 0x06, 0xdd, 0x2c, 0x2b,
+ 0x9f, 0x04, 0x88, 0x5f, 0x36, 0x42, 0xc1, 0xb1,
+ 0xc7, 0xe8, 0x2d, 0x5d, 0xa4, 0x6c, 0xe5, 0x60,
+ 0x94, 0xae, 0xd0, 0x90, 0x1e, 0x88, 0xa0, 0x87,
+ 0x52, 0xfb, 0xed, 0x97, 0xa5, 0x25, 0x5a, 0xb7,
+ 0x55, 0xc5, 0x13, 0x07, 0x85, 0x27, 0x40, 0xed,
+ 0xb8, 0xa0, 0x26, 0x13, 0x44, 0x0c, 0xfc, 0xcc,
+ 0x5a, 0x09, 0xe5, 0x44, 0xb5, 0x63, 0xa1, 0x43,
+ 0x51, 0x23, 0x4f, 0x17, 0x21, 0x89, 0x2e, 0x58,
+ 0xfd, 0xf9, 0x63, 0x74, 0x04, 0x70, 0x1e, 0x7d,
+ 0xd0, 0x66, 0xba, 0x40, 0x5e, 0x45, 0xdc, 0x39,
+ 0x7c, 0x53, 0x0f, 0xa8, 0x38, 0xb2, 0x13, 0x99,
+ 0x27, 0xd9, 0x4a, 0x51, 0xe9, 0x9f, 0x2a, 0x92,
+ 0xbb, 0x9c, 0x90, 0xab, 0xfd, 0xf1, 0xb7, 0x40,
+ 0x05, 0xa9, 0x7a, 0x20, 0x63, 0x36, 0xc1, 0xef,
+ 0xb9, 0xad, 0xa2, 0xe0, 0x1d, 0x20, 0x4f, 0xb2,
+ 0x34, 0xbd, 0xea, 0x07, 0xac, 0x21, 0xce, 0xf6,
+ 0x8a, 0xa2, 0x9e, 0xcd, 0xfa
+ };
+ uint32_t toserver_app_data_buf_len = sizeof(toserver_app_data_buf);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ //AppLayerDetectProtoThreadInit();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER | STREAM_START, chello_buf,
+ chello_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->client_connp.content_type != SSLV2_MT_CLIENT_HELLO) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV2_MT_CLIENT_HELLO, app_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->client_connp.version != SSL_VERSION_2) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_2, app_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->flags !=
+ (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_SSL_CLIENT_HS |
+ SSL_AL_FLAG_SSL_NO_SESSION_ID)) {
+ printf("flags not set\n");
+ result = 0;
+ goto end;
+ }
+
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, shello_buf,
+ shello_buf_len);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (app_state->server_connp.content_type != SSLV3_HANDSHAKE_PROTOCOL) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV3_HANDSHAKE_PROTOCOL, app_state->server_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->server_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, app_state->server_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->flags !=
+ (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_SSL_CLIENT_HS |
+ SSL_AL_FLAG_SSL_NO_SESSION_ID | SSL_AL_FLAG_STATE_SERVER_HELLO)) {
+ printf("flags not set\n");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, client_change_cipher_spec_buf,
+ client_change_cipher_spec_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* with multiple records the client content type hold the type from the last
+ * record */
+ if (app_state->client_connp.content_type != SSLV3_HANDSHAKE_PROTOCOL) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV3_HANDSHAKE_PROTOCOL, app_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, app_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->flags !=
+ (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_SSL_CLIENT_HS |
+ SSL_AL_FLAG_SSL_NO_SESSION_ID | SSL_AL_FLAG_STATE_SERVER_HELLO |
+ SSL_AL_FLAG_STATE_CLIENT_KEYX | SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC |
+ SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
+ printf("flags not set\n");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, server_change_cipher_spec_buf,
+ server_change_cipher_spec_buf_len);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* with multiple records the serve content type hold the type from the last
+ * record */
+ if (app_state->server_connp.content_type != SSLV3_HANDSHAKE_PROTOCOL) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV3_HANDSHAKE_PROTOCOL, app_state->server_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->server_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, app_state->server_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->flags !=
+ (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_SSL_CLIENT_HS |
+ SSL_AL_FLAG_SSL_NO_SESSION_ID | SSL_AL_FLAG_STATE_SERVER_HELLO |
+ SSL_AL_FLAG_STATE_CLIENT_KEYX | SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC |
+ SSL_AL_FLAG_CHANGE_CIPHER_SPEC | SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC |
+ SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
+ printf("flags not set\n");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, toserver_app_data_buf,
+ toserver_app_data_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (app_state->client_connp.content_type != SSLV3_APPLICATION_PROTOCOL) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ SSLV3_APPLICATION_PROTOCOL, app_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, app_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+ if (app_state->flags !=
+ (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_SSL_CLIENT_HS |
+ SSL_AL_FLAG_SSL_NO_SESSION_ID | SSL_AL_FLAG_STATE_SERVER_HELLO |
+ SSL_AL_FLAG_STATE_CLIENT_KEYX | SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC |
+ SSL_AL_FLAG_CHANGE_CIPHER_SPEC | SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC |
+ SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
+ printf("flags not set\n");
+ result = 0;
+ goto end;
+ }
+
+ if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION)) {
+ printf("The flags should be set\n");
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Tests the parser for handling fragmented records.
+ */
+static int SSLParserTest24(void)
+{
+ int result = 1;
+ Flow f;
+ uint8_t buf1[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x6f, 0x01, 0x00, 0x00,
+ 0x6b, 0x03,
+ };
+ uint32_t buf1_len = sizeof(buf1);
+
+ uint8_t buf2[] = {
+ 0x00, 0x4b, 0x2f, 0xdc,
+ 0x4e, 0xe6, 0x95, 0xf1, 0xa0, 0xc7, 0xcf, 0x8e,
+ 0xf6, 0xeb, 0x22, 0x6d, 0xce, 0x9c, 0x44, 0xfb,
+ 0xc8, 0xa0, 0x44, 0x31, 0x15, 0x4c, 0xe9, 0x97,
+ 0xa7, 0xa1, 0xfe, 0xea, 0xcc, 0x20, 0x4b, 0x5d,
+ 0xfb, 0xa5, 0x63, 0x7a, 0x73, 0x95, 0xf7, 0xff,
+ 0x42, 0xac, 0x8f, 0x46, 0xed, 0xe4, 0xb1, 0x35,
+ 0x35, 0x78, 0x1a, 0x9d, 0xaf, 0x10, 0xc5, 0x52,
+ 0xf3, 0x7b, 0xfb, 0xb5, 0xe9, 0xa8, 0x00, 0x24,
+ 0x00, 0x88, 0x00, 0x87, 0x00, 0x39, 0x00, 0x38,
+ 0x00, 0x84, 0x00, 0x35, 0x00, 0x45, 0x00, 0x44,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x96, 0x00, 0x41,
+ 0x00, 0x2f, 0x00, 0x16, 0x00, 0x13, 0xfe, 0xff,
+ 0x00, 0x0a, 0x00, 0x02, 0x01, 0x00
+ };
+ uint32_t buf2_len = sizeof(buf2);
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf1, buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf2, buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16,
+ ssl_state->client_connp.content_type);
+ result = 0;
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != SSL_VERSION_3) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ SSL_VERSION_3, ssl_state->client_connp.version);
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test for bug #955 and CVE-2013-5919. The data is from the
+ * pcap that was used to report this issue.
+ */
+static int SSLParserTest25(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t client_hello[] = {
+ 0x16, 0x03, 0x01, 0x00, 0xd3, 0x01, 0x00, 0x00,
+ 0xcf, 0x03, 0x01, 0x51, 0x60, 0xc2, 0x15, 0x36,
+ 0x73, 0xf5, 0xb8, 0x58, 0x55, 0x3b, 0x68, 0x12,
+ 0x7d, 0xe3, 0x28, 0xa3, 0xe1, 0x02, 0x79, 0x2d,
+ 0x12, 0xe1, 0xf4, 0x24, 0x12, 0xa2, 0x9e, 0xf1,
+ 0x08, 0x49, 0x68, 0x20, 0x0e, 0x96, 0x46, 0x3d,
+ 0x84, 0x5a, 0xc6, 0x55, 0xeb, 0x3b, 0x53, 0x77,
+ 0xf4, 0x8e, 0xf4, 0xd2, 0x8b, 0xec, 0xd6, 0x99,
+ 0x63, 0x64, 0x62, 0xf8, 0x3f, 0x3b, 0xd5, 0x35,
+ 0x45, 0x1b, 0x16, 0xac, 0x00, 0x46, 0x00, 0x04,
+ 0x00, 0x05, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x02,
+ 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x0c, 0xc0, 0x0e,
+ 0xc0, 0x0f, 0xc0, 0x07, 0xc0, 0x09, 0xc0, 0x0a,
+ 0xc0, 0x11, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33,
+ 0x00, 0x39, 0x00, 0x32, 0x00, 0x38, 0x00, 0x0a,
+ 0xc0, 0x03, 0xc0, 0x0d, 0xc0, 0x08, 0xc0, 0x12,
+ 0x00, 0x16, 0x00, 0x13, 0x00, 0x09, 0x00, 0x15,
+ 0x00, 0x12, 0x00, 0x03, 0x00, 0x08, 0x00, 0x14,
+ 0x00, 0x11, 0x00, 0xff, 0x01, 0x00, 0x00, 0x40,
+ 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02,
+ 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e,
+ 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c,
+ 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16,
+ 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00, 0x07,
+ 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00, 0x05,
+ 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00, 0x02,
+ 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11
+ };
+ uint32_t client_hello_len = sizeof(client_hello);
+
+ uint8_t server_hello_certificate_done[] = {
+ 0x16, 0x03, 0x01, 0x00, 0x51, 0x02, 0x00, 0x00,
+ 0x4d, 0x03, 0x01, 0x51, 0x60, 0xc2, 0x17, 0xb7,
+ 0x81, 0xaa, 0x27, 0xa1, 0xd5, 0xfa, 0x14, 0xc1,
+ 0xe0, 0x05, 0xab, 0x75, 0xf2, 0x51, 0xe7, 0x6e,
+ 0xe6, 0xf9, 0xc4, 0x8f, 0x16, 0x08, 0x26, 0x6c,
+ 0x1b, 0x86, 0x90, 0x20, 0x0a, 0x38, 0x90, 0x2d,
+ 0x17, 0x7d, 0xb7, 0x6b, 0x6b, 0xe5, 0xeb, 0x61,
+ 0x90, 0x35, 0xf8, 0xcd, 0xb1, 0x2a, 0x69, 0x6e,
+ 0x0e, 0x3e, 0x5f, 0x90, 0xdc, 0x2f, 0x51, 0x45,
+ 0x68, 0x63, 0xe3, 0xb3, 0x00, 0x05, 0x00, 0x00,
+ 0x05, 0xff, 0x01, 0x00, 0x01, 0x00, 0x16, 0x03,
+ 0x01, 0x07, 0x60, 0x0b, 0x00, 0x07, 0x5c, 0x00,
+ 0x07, 0x59, 0x00, 0x03, 0xcc, 0x30, 0x82, 0x03,
+ 0xc8, 0x30, 0x82, 0x03, 0x31, 0xa0, 0x03, 0x02,
+ 0x01, 0x02, 0x02, 0x10, 0x01, 0x7f, 0x77, 0xde,
+ 0xb3, 0xbc, 0xbb, 0x23, 0x5d, 0x44, 0xcc, 0xc7,
+ 0xdb, 0xa6, 0x2e, 0x72, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x81, 0xba, 0x31, 0x1f,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69,
+ 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x2a, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61,
+ 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x31, 0x49, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x40,
+ 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x43, 0x50, 0x53, 0x20, 0x49, 0x6e, 0x63,
+ 0x6f, 0x72, 0x70, 0x2e, 0x62, 0x79, 0x20, 0x52,
+ 0x65, 0x66, 0x2e, 0x20, 0x4c, 0x49, 0x41, 0x42,
+ 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, 0x4c, 0x54,
+ 0x44, 0x2e, 0x28, 0x63, 0x29, 0x39, 0x37, 0x20,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x36,
+ 0x32, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x32, 0x33,
+ 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f,
+ 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x50,
+ 0x61, 0x6c, 0x6f, 0x20, 0x41, 0x6c, 0x74, 0x6f,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0e, 0x46, 0x61, 0x63, 0x65, 0x62,
+ 0x6f, 0x6f, 0x6b, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55,
+ 0x04, 0x02, 0x14, 0x0e, 0x2a, 0x2e, 0x66, 0x61,
+ 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00,
+ 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xae,
+ 0x94, 0xb1, 0x71, 0xe2, 0xde, 0xcc, 0xc1, 0x69,
+ 0x3e, 0x05, 0x10, 0x63, 0x24, 0x01, 0x02, 0xe0,
+ 0x68, 0x9a, 0xe8, 0x3c, 0x39, 0xb6, 0xb3, 0xe7,
+ 0x4b, 0x97, 0xd4, 0x8d, 0x7b, 0x23, 0x68, 0x91,
+ 0x00, 0xb0, 0xb4, 0x96, 0xee, 0x62, 0xf0, 0xe6,
+ 0xd3, 0x56, 0xbc, 0xf4, 0xaa, 0x0f, 0x50, 0x64,
+ 0x34, 0x02, 0xf5, 0xd1, 0x76, 0x6a, 0xa9, 0x72,
+ 0x83, 0x5a, 0x75, 0x64, 0x72, 0x3f, 0x39, 0xbb,
+ 0xef, 0x52, 0x90, 0xde, 0xd9, 0xbc, 0xdb, 0xf9,
+ 0xd3, 0xd5, 0x5d, 0xfa, 0xd2, 0x3a, 0xa0, 0x3d,
+ 0xc6, 0x04, 0xc5, 0x4d, 0x29, 0xcf, 0x1d, 0x4b,
+ 0x3b, 0xdb, 0xd1, 0xa8, 0x09, 0xcf, 0xae, 0x47,
+ 0xb4, 0x4c, 0x7e, 0xae, 0x17, 0xc5, 0x10, 0x9b,
+ 0xee, 0x24, 0xa9, 0xcf, 0x4a, 0x8d, 0x91, 0x1b,
+ 0xb0, 0xfd, 0x04, 0x15, 0xae, 0x4c, 0x3f, 0x43,
+ 0x0a, 0xa1, 0x2a, 0x55, 0x7e, 0x2a, 0xe1, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1e,
+ 0x30, 0x82, 0x01, 0x1a, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30,
+ 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3d,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86,
+ 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x17,
+ 0x03, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x00, 0x01, 0x16,
+ 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x3c, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x35, 0x30, 0x33,
+ 0x30, 0x31, 0xa0, 0x2f, 0xa0, 0x2d, 0x86, 0x2b,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x53,
+ 0x56, 0x52, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63,
+ 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x53, 0x56, 0x52, 0x49, 0x6e, 0x74, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30,
+ 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x27, 0x06,
+ 0x03, 0x55, 0x1d, 0x11, 0x04, 0x20, 0x30, 0x1e,
+ 0x82, 0x0e, 0x2a, 0x2e, 0x66, 0x61, 0x63, 0x65,
+ 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x82, 0x0c, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f,
+ 0x6f, 0x6b, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x5b, 0x6c, 0x2b, 0x75, 0xf8, 0xed, 0x30,
+ 0xaa, 0x51, 0xaa, 0xd3, 0x6a, 0xba, 0x59, 0x5e,
+ 0x55, 0x51, 0x41, 0x95, 0x1f, 0x81, 0xa5, 0x3b,
+ 0x44, 0x79, 0x10, 0xac, 0x1f, 0x76, 0xff, 0x78,
+ 0xfc, 0x27, 0x81, 0x61, 0x6b, 0x58, 0xf3, 0x12,
+ 0x2a, 0xfc, 0x1c, 0x87, 0x01, 0x04, 0x25, 0xe9,
+ 0xed, 0x43, 0xdf, 0x1a, 0x7b, 0xa6, 0x49, 0x80,
+ 0x60, 0x67, 0xe2, 0x68, 0x8a, 0xf0, 0x3d, 0xb5,
+ 0x8c, 0x7d, 0xf4, 0xee, 0x03, 0x30, 0x9a, 0x6a,
+ 0xfc, 0x24, 0x7c, 0xcb, 0x13, 0x4d, 0xc3, 0x3e,
+ 0x54, 0xc6, 0xbc, 0x1d, 0x51, 0x33, 0xa5, 0x32,
+ 0xa7, 0x32, 0x73, 0xb1, 0xd7, 0x9c, 0xad, 0xc0,
+ 0x8e, 0x7e, 0x1a, 0x83, 0x11, 0x6d, 0x34, 0x52,
+ 0x33, 0x40, 0xb0, 0x30, 0x54, 0x27, 0xa2, 0x17,
+ 0x42, 0x82, 0x7c, 0x98, 0x91, 0x66, 0x98, 0xee,
+ 0x7e, 0xaf, 0x8c, 0x3b, 0xdd, 0x71, 0x70, 0x08,
+ 0x17, 0x00, 0x03, 0x87, 0x30, 0x82, 0x03, 0x83,
+ 0x30, 0x82, 0x02, 0xec, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x46, 0xfc, 0xeb, 0xba, 0xb4,
+ 0xd0, 0x2f, 0x0f, 0x92, 0x60, 0x98, 0x23, 0x3f,
+ 0x93, 0x07, 0x8f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x2e, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x64, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x39,
+ 0x37, 0x30, 0x34, 0x31, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36,
+ 0x31, 0x30, 0x32, 0x34, 0x32, 0x33, 0x35, 0x39,
+ 0x35, 0x39, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x1f,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69,
+ 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x2a, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61,
+ 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x31, 0x49, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x40,
+ 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69,
+ 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43,
+ 0x50, 0x53, 0x20, 0x49, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x62, 0x79, 0x20, 0x52, 0x65, 0x66,
+ 0x2e, 0x20, 0x4c, 0x49, 0x41, 0x42, 0x49, 0x4c,
+ 0x49, 0x54, 0x59, 0x20, 0x4c, 0x54, 0x44, 0x2e,
+ 0x28, 0x63, 0x29, 0x39, 0x37, 0x20, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x30, 0x81,
+ 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02,
+ 0x81, 0x81, 0x00, 0xd8, 0x82, 0x80, 0xe8, 0xd6,
+ 0x19, 0x02, 0x7d, 0x1f, 0x85, 0x18, 0x39, 0x25,
+ 0xa2, 0x65, 0x2b, 0xe1, 0xbf, 0xd4, 0x05, 0xd3,
+ 0xbc, 0xe6, 0x36, 0x3b, 0xaa, 0xf0, 0x4c, 0x6c,
+ 0x5b, 0xb6, 0xe7, 0xaa, 0x3c, 0x73, 0x45, 0x55,
+ 0xb2, 0xf1, 0xbd, 0xea, 0x97, 0x42, 0xed, 0x9a,
+ 0x34, 0x0a, 0x15, 0xd4, 0xa9, 0x5c, 0xf5, 0x40,
+ 0x25, 0xdd, 0xd9, 0x07, 0xc1, 0x32, 0xb2, 0x75,
+ 0x6c, 0xc4, 0xca, 0xbb, 0xa3, 0xfe, 0x56, 0x27,
+ 0x71, 0x43, 0xaa, 0x63, 0xf5, 0x30, 0x3e, 0x93,
+ 0x28, 0xe5, 0xfa, 0xf1, 0x09, 0x3b, 0xf3, 0xb7,
+ 0x4d, 0x4e, 0x39, 0xf7, 0x5c, 0x49, 0x5a, 0xb8,
+ 0xc1, 0x1d, 0xd3, 0xb2, 0x8a, 0xfe, 0x70, 0x30,
+ 0x95, 0x42, 0xcb, 0xfe, 0x2b, 0x51, 0x8b, 0x5a,
+ 0x3c, 0x3a, 0xf9, 0x22, 0x4f, 0x90, 0xb2, 0x02,
+ 0xa7, 0x53, 0x9c, 0x4f, 0x34, 0xe7, 0xab, 0x04,
+ 0xb2, 0x7b, 0x6f, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x81, 0xe3, 0x30, 0x81, 0xe0, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3d,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86,
+ 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x01,
+ 0x01, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x34, 0x06,
+ 0x03, 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45,
+ 0x01, 0x08, 0x01, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x31, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30, 0x26,
+ 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x40, 0x8e, 0x49, 0x97, 0x96, 0x8a, 0x73,
+ 0xdd, 0x8e, 0x4d, 0xef, 0x3e, 0x61, 0xb7, 0xca,
+ 0xa0, 0x62, 0xad, 0xf4, 0x0e, 0x0a, 0xbb, 0x75,
+ 0x3d, 0xe2, 0x6e, 0xd8, 0x2c, 0xc7, 0xbf, 0xf4,
+ 0xb9, 0x8c, 0x36, 0x9b, 0xca, 0xa2, 0xd0, 0x9c,
+ 0x72, 0x46, 0x39, 0xf6, 0xa6, 0x82, 0x03, 0x65,
+ 0x11, 0xc4, 0xbc, 0xbf, 0x2d, 0xa6, 0xf5, 0xd9,
+ 0x3b, 0x0a, 0xb5, 0x98, 0xfa, 0xb3, 0x78, 0xb9,
+ 0x1e, 0xf2, 0x2b, 0x4c, 0x62, 0xd5, 0xfd, 0xb2,
+ 0x7a, 0x1d, 0xdf, 0x33, 0xfd, 0x73, 0xf9, 0xa5,
+ 0xd8, 0x2d, 0x8c, 0x2a, 0xea, 0xd1, 0xfc, 0xb0,
+ 0x28, 0xb6, 0xe9, 0x49, 0x48, 0x13, 0x4b, 0x83,
+ 0x8a, 0x1b, 0x48, 0x7b, 0x24, 0xf7, 0x38, 0xde,
+ 0x6f, 0x41, 0x54, 0xb8, 0xab, 0x57, 0x6b, 0x06,
+ 0xdf, 0xc7, 0xa2, 0xd4, 0xa9, 0xf6, 0xf1, 0x36,
+ 0x62, 0x80, 0x88, 0xf2, 0x8b, 0x75, 0xd6, 0x80,
+ 0x75, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00,
+ 0x00, 0x00
+ };
+ uint32_t server_hello_certificate_done_len = sizeof(server_hello_certificate_done);
+
+ uint8_t client_key_exchange_cipher_enc_hs[] = {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x80, 0x00, 0x80, 0x14, 0x2b, 0x2f, 0x9f, 0x02,
+ 0x1d, 0x4e, 0x0d, 0xa7, 0x41, 0x0f, 0x99, 0xc5,
+ 0xe9, 0x49, 0x22, 0x14, 0xa0, 0x42, 0x7b, 0xb4,
+ 0x6d, 0x4f, 0x82, 0x3c, 0x3a, 0x6e, 0xed, 0xd5,
+ 0x6e, 0x72, 0x71, 0xae, 0x00, 0x4a, 0x9a, 0xc9,
+ 0x0e, 0x2d, 0x08, 0xa2, 0xd3, 0x3a, 0xb0, 0xb2,
+ 0x1a, 0x56, 0x01, 0x7c, 0x9a, 0xfa, 0xfb, 0x1a,
+ 0xd7, 0x7e, 0x20, 0x68, 0x51, 0xd0, 0xfe, 0xd9,
+ 0xdc, 0xa7, 0x0b, 0xeb, 0x1a, 0xb6, 0xd3, 0xc7,
+ 0x17, 0x1f, 0xf3, 0x6e, 0x91, 0xdd, 0x06, 0x0d,
+ 0x48, 0xde, 0xcd, 0x0c, 0x36, 0x8c, 0x83, 0x29,
+ 0x9a, 0x40, 0x03, 0xcd, 0xf3, 0x1b, 0xdb, 0xd8,
+ 0x44, 0x6b, 0x75, 0xf3, 0x5a, 0x9f, 0x26, 0x1a,
+ 0xc4, 0x16, 0x35, 0x8f, 0xc1, 0x15, 0x19, 0xa9,
+ 0xdf, 0x07, 0xa9, 0xe5, 0x56, 0x45, 0x6d, 0xca,
+ 0x20, 0x3c, 0xcf, 0x8e, 0xbe, 0x44, 0x68, 0x73,
+ 0xc8, 0x0b, 0xc7, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xf9, 0x7e,
+ 0x28, 0x77, 0xa9, 0x9a, 0x08, 0x0c, 0x2e, 0xa9,
+ 0x09, 0x15, 0x27, 0xcd, 0x93, 0x5f, 0xc0, 0x32,
+ 0x0a, 0x8d, 0x62, 0xd3, 0x54, 0x79, 0x6b, 0x51,
+ 0xd7, 0xba, 0x02, 0xd6, 0xdb, 0x66, 0xe8, 0x97,
+ 0x5d, 0x7a
+ };
+ uint32_t client_key_exchange_cipher_enc_hs_len = sizeof(client_key_exchange_cipher_enc_hs);
+
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, client_hello, client_hello_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ goto end;
+ }
+
+ if (ssl_state->client_connp.bytes_processed != 0 ||
+ ssl_state->client_connp.hs_bytes_processed != 0)
+ {
+ printf("client_hello error\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT,
+ server_hello_certificate_done,
+ server_hello_certificate_done_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ if (ssl_state->client_connp.bytes_processed != 0 ||
+ ssl_state->client_connp.hs_bytes_processed != 0)
+ {
+ printf("server_hello_certificate_done error\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER,
+ client_key_exchange_cipher_enc_hs,
+ client_key_exchange_cipher_enc_hs_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* The reason hs_bytes_processed is 2 is because, the record
+ * immediately after the client key exchange is 2 bytes long,
+ * and next time we see a new handshake, it is after we have
+ * seen a change cipher spec. Hence when we process the
+ * handshake, we immediately break and don't parse the pdu from
+ * where we left off, and leave the hs_bytes_processed var
+ * isn't reset. */
+ if (ssl_state->client_connp.bytes_processed != 0 ||
+ ssl_state->client_connp.hs_bytes_processed != 2)
+ {
+ printf("client_key_exchange_cipher_enc_hs error\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SSLParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SSLParserTest01", SSLParserTest01, 1);
+ UtRegisterTest("SSLParserTest02", SSLParserTest02, 1);
+ UtRegisterTest("SSLParserTest03", SSLParserTest03, 1);
+ UtRegisterTest("SSLParserTest04", SSLParserTest04, 1);
+ /* Updated by Anoop Saldanha. Faulty tests. Disable it for now */
+ //UtRegisterTest("SSLParserTest05", SSLParserTest05, 1);
+ //UtRegisterTest("SSLParserTest06", SSLParserTest06, 1);
+ UtRegisterTest("SSLParserTest07", SSLParserTest07, 1);
+ //UtRegisterTest("SSLParserTest08", SSLParserTest08, 1);
+ UtRegisterTest("SSLParserTest09", SSLParserTest09, 1);
+ UtRegisterTest("SSLParserTest10", SSLParserTest10, 1);
+ UtRegisterTest("SSLParserTest11", SSLParserTest11, 1);
+ UtRegisterTest("SSLParserTest12", SSLParserTest12, 1);
+ UtRegisterTest("SSLParserTest13", SSLParserTest13, 1);
+
+ UtRegisterTest("SSLParserTest14", SSLParserTest14, 1);
+ UtRegisterTest("SSLParserTest15", SSLParserTest15, 1);
+ UtRegisterTest("SSLParserTest16", SSLParserTest16, 1);
+ UtRegisterTest("SSLParserTest17", SSLParserTest17, 1);
+ UtRegisterTest("SSLParserTest18", SSLParserTest18, 1);
+ UtRegisterTest("SSLParserTest19", SSLParserTest19, 1);
+ UtRegisterTest("SSLParserTest20", SSLParserTest20, 1);
+ UtRegisterTest("SSLParserTest21", SSLParserTest21, 1);
+ UtRegisterTest("SSLParserTest22", SSLParserTest22, 1);
+ UtRegisterTest("SSLParserTest23", SSLParserTest23, 1);
+ UtRegisterTest("SSLParserTest24", SSLParserTest24, 1);
+ UtRegisterTest("SSLParserTest25", SSLParserTest25, 1);
+
+ UtRegisterTest("SSLParserMultimsgTest01", SSLParserMultimsgTest01, 1);
+ UtRegisterTest("SSLParserMultimsgTest02", SSLParserMultimsgTest02, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/app-layer-ssl.h b/framework/src/suricata/src/app-layer-ssl.h
new file mode 100644
index 00000000..5c6fe5b1
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-ssl.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __APP_LAYER_SSL_H__
+#define __APP_LAYER_SSL_H__
+
+#include "decode-events.h"
+#include "queue.h"
+
+enum {
+ /* TLS protocol messages */
+ TLS_DECODER_EVENT_INVALID_SSLV2_HEADER,
+ TLS_DECODER_EVENT_INVALID_TLS_HEADER,
+ TLS_DECODER_EVENT_INVALID_RECORD_VERSION,
+ TLS_DECODER_EVENT_INVALID_RECORD_TYPE,
+ TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE,
+ TLS_DECODER_EVENT_HEARTBEAT,
+ TLS_DECODER_EVENT_INVALID_HEARTBEAT,
+ TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT,
+ TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH,
+ /* Certificates decoding messages */
+ TLS_DECODER_EVENT_INVALID_CERTIFICATE,
+ TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT,
+ TLS_DECODER_EVENT_CERTIFICATE_UNKNOWN_ELEMENT,
+ TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH,
+ TLS_DECODER_EVENT_CERTIFICATE_INVALID_STRING,
+ TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED,
+ TLS_DECODER_EVENT_INVALID_SSL_RECORD,
+};
+
+/* Flag to indicate that server will now on send encrypted msgs */
+#define SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC 0x0001
+/* Flag to indicate that client will now on send encrypted msgs */
+#define SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC 0x0002
+#define SSL_AL_FLAG_CHANGE_CIPHER_SPEC 0x0004
+
+/* SSL related flags */
+#define SSL_AL_FLAG_SSL_CLIENT_HS 0x0008
+#define SSL_AL_FLAG_SSL_SERVER_HS 0x0010
+#define SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY 0x0020
+#define SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED 0x0040
+#define SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED 0x0080
+#define SSL_AL_FLAG_SSL_NO_SESSION_ID 0x0100
+
+/* flags specific to detect-ssl-state keyword */
+#define SSL_AL_FLAG_STATE_CLIENT_HELLO 0x0200
+#define SSL_AL_FLAG_STATE_SERVER_HELLO 0x0400
+#define SSL_AL_FLAG_STATE_CLIENT_KEYX 0x0800
+#define SSL_AL_FLAG_STATE_SERVER_KEYX 0x1000
+#define SSL_AL_FLAG_STATE_UNKNOWN 0x2000
+
+#define SSL_AL_FLAG_STATE_LOGGED 0x4000
+
+/* flags specific to HeartBeat state */
+#define SSL_AL_FLAG_HB_INFLIGHT 0x8000
+#define SSL_AL_FLAG_HB_CLIENT_INIT 0x10000
+#define SSL_AL_FLAG_HB_SERVER_INIT 0x20000
+
+/* flags for file storage */
+#define SSL_AL_FLAG_STATE_STORED 0x40000
+
+/* config flags */
+#define SSL_TLS_LOG_PEM (1 << 0)
+
+
+
+/* SSL versions. We'll use a unified format for all, with the top byte
+ * holding the major version and the lower byte the minor version */
+enum {
+ TLS_VERSION_UNKNOWN = 0x0000,
+ SSL_VERSION_2 = 0x0200,
+ SSL_VERSION_3 = 0x0300,
+ TLS_VERSION_10 = 0x0301,
+ TLS_VERSION_11 = 0x0302,
+ TLS_VERSION_12 = 0x0303,
+};
+
+typedef struct SSLCertsChain_ {
+ uint8_t *cert_data;
+ uint32_t cert_len;
+ TAILQ_ENTRY(SSLCertsChain_) next;
+} SSLCertsChain;
+
+
+typedef struct SSLStateConnp_ {
+ /* record length */
+ uint32_t record_length;
+ /* record length's length for SSLv2 */
+ uint32_t record_lengths_length;
+
+ /* offset of the beginning of the current message (including header) */
+ uint32_t message_start;
+ uint32_t message_length;
+
+ uint16_t version;
+ uint8_t content_type;
+
+ uint8_t handshake_type;
+ uint32_t handshake_length;
+
+ /* the no of bytes processed in the currently parsed record */
+ uint16_t bytes_processed;
+ /* the no of bytes processed in the currently parsed handshake */
+ uint16_t hs_bytes_processed;
+
+ /* sslv2 client hello session id length */
+ uint16_t session_id_length;
+
+ char *cert0_subject;
+ char *cert0_issuerdn;
+ char *cert0_fingerprint;
+
+ uint8_t *cert_input;
+ uint32_t cert_input_len;
+
+ TAILQ_HEAD(, SSLCertsChain_) certs;
+
+ uint32_t cert_log_flag;
+
+ /* buffer for the tls record.
+ * We use a malloced buffer, if the record is fragmented */
+ uint8_t *trec;
+ uint32_t trec_len;
+ uint32_t trec_pos;
+} SSLStateConnp;
+
+/**
+ * \brief SSLv[2.0|3.[0|1|2|3]] state structure.
+ *
+ * Structure to store the SSL state values.
+ */
+typedef struct SSLState_ {
+ Flow *f;
+
+ /* holds some state flags we need */
+ uint32_t flags;
+
+ SSLStateConnp *curr_connp;
+
+ SSLStateConnp client_connp;
+ SSLStateConnp server_connp;
+
+ /* there might be a better place to store this*/
+ uint16_t hb_record_len;
+} SSLState;
+
+void RegisterSSLParsers(void);
+void SSLParserRegisterTests(void);
+
+#endif /* __APP_LAYER_SSL_H__ */
diff --git a/framework/src/suricata/src/app-layer-tls-handshake.c b/framework/src/suricata/src/app-layer-tls-handshake.c
new file mode 100644
index 00000000..ca2e7f49
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-tls-handshake.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ * \brief Decode TLS Handshake messages, as described in RFC2246
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "app-layer-parser.h"
+#include "decode-events.h"
+
+#include "app-layer-ssl.h"
+
+#include "app-layer-tls-handshake.h"
+
+#include <stdint.h>
+
+#include "util-decode-der.h"
+#include "util-decode-der-get.h"
+
+#include "util-crypt.h"
+
+#define SSLV3_RECORD_LEN 5
+
+static void TLSCertificateErrCodeToWarning(SSLState *ssl_state, uint32_t errcode)
+{
+ if (errcode == 0)
+ return;
+
+ switch (errcode) {
+ case ERR_DER_ELEMENT_SIZE_TOO_BIG:
+ case ERR_DER_INVALID_SIZE:
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH);
+ break;
+ case ERR_DER_UNSUPPORTED_STRING:
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_CERTIFICATE_INVALID_STRING);
+ break;
+ case ERR_DER_UNKNOWN_ELEMENT:
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_CERTIFICATE_UNKNOWN_ELEMENT);
+ break;
+ case ERR_DER_MISSING_ELEMENT:
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT);
+ break;
+ case ERR_DER_GENERIC:
+ default:
+ AppLayerDecoderEventsSetEvent(ssl_state->f,
+ TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+ break;
+ };
+}
+
+int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uint32_t input_len)
+{
+ uint32_t certificates_length, cur_cert_length;
+ int i;
+ Asn1Generic *cert;
+ char buffer[256];
+ int rc;
+ int parsed;
+ uint8_t *start_data;
+ uint32_t errcode = 0;
+
+ if (input_len < 3)
+ return 1;
+
+ certificates_length = input[0]<<16 | input[1]<<8 | input[2];
+ /* check if the message is complete */
+ if (input_len < certificates_length + 3)
+ return 0;
+
+ start_data = input;
+ input += 3;
+ parsed = 3;
+
+ i = 0;
+ while (certificates_length > 0) {
+ cur_cert_length = input[0]<<16 | input[1]<<8 | input[2];
+ input += 3;
+ parsed += 3;
+
+ if (input - start_data + cur_cert_length > input_len) {
+ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+ return -1;
+ }
+ cert = DecodeDer(input, cur_cert_length, &errcode);
+ if (cert == NULL) {
+ TLSCertificateErrCodeToWarning(ssl_state, errcode);
+ }
+ if (cert != NULL) {
+ rc = Asn1DerGetSubjectDN(cert, buffer, sizeof(buffer), &errcode);
+ if (rc != 0) {
+ TLSCertificateErrCodeToWarning(ssl_state, errcode);
+ } else {
+ SSLCertsChain *ncert;
+ //SCLogInfo("TLS Cert %d: %s\n", i, buffer);
+ if (i == 0) {
+ if (ssl_state->server_connp.cert0_subject == NULL)
+ ssl_state->server_connp.cert0_subject = SCStrdup(buffer);
+ if (ssl_state->server_connp.cert0_subject == NULL) {
+ DerFree(cert);
+ return -1;
+ }
+ }
+ ncert = (SSLCertsChain *)SCMalloc(sizeof(SSLCertsChain));
+ if (ncert == NULL) {
+ DerFree(cert);
+ return -1;
+ }
+ memset(ncert, 0, sizeof(*ncert));
+ ncert->cert_data = input;
+ ncert->cert_len = cur_cert_length;
+ TAILQ_INSERT_TAIL(&ssl_state->server_connp.certs, ncert, next);
+ }
+ rc = Asn1DerGetIssuerDN(cert, buffer, sizeof(buffer), &errcode);
+ if (rc != 0) {
+ TLSCertificateErrCodeToWarning(ssl_state, errcode);
+ } else {
+ //SCLogInfo("TLS IssuerDN %d: %s\n", i, buffer);
+ if (i == 0) {
+ if (ssl_state->server_connp.cert0_issuerdn == NULL)
+ ssl_state->server_connp.cert0_issuerdn = SCStrdup(buffer);
+ if (ssl_state->server_connp.cert0_issuerdn == NULL) {
+ DerFree(cert);
+ return -1;
+ }
+ }
+ }
+ DerFree(cert);
+
+ if (i == 0 && ssl_state->server_connp.cert0_fingerprint == NULL) {
+ int msg_len = cur_cert_length;
+ int hash_len = 20;
+ int out_len = 60;
+ char out[out_len];
+ unsigned char *hash;
+ hash = ComputeSHA1((unsigned char *) input, (int) msg_len);
+ char *p = out;
+ int j = 0;
+
+ if (hash == NULL) {
+ SCLogWarning(SC_ERR_MEM_ALLOC, "Can not allocate fingerprint string");
+ } else {
+ for (j = 0; j < hash_len; j++, p += 3) {
+ snprintf(p, 4, j == hash_len - 1 ? "%02x" : "%02x:", hash[j]);
+ }
+ SCFree(hash);
+ ssl_state->server_connp.cert0_fingerprint = SCStrdup(out);
+ if (ssl_state->server_connp.cert0_fingerprint == NULL) {
+ SCLogWarning(SC_ERR_MEM_ALLOC, "Can not allocate fingerprint string");
+ }
+ }
+
+ ssl_state->server_connp.cert_input = input;
+ ssl_state->server_connp.cert_input_len = cur_cert_length;
+ }
+
+ }
+
+ i++;
+ certificates_length -= (cur_cert_length + 3);
+ parsed += cur_cert_length;
+ input += cur_cert_length;
+ }
+
+ return parsed;
+
+}
+
diff --git a/framework/src/suricata/src/app-layer-tls-handshake.h b/framework/src/suricata/src/app-layer-tls-handshake.h
new file mode 100644
index 00000000..6041f7fb
--- /dev/null
+++ b/framework/src/suricata/src/app-layer-tls-handshake.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __APP_LAYER_TLS_HANDSHAKE_H__
+#define __APP_LAYER_TLS_HANDSHAKE_H__
+
+int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uint32_t input_len);
+
+#endif /* __APP_LAYER_TLS_HANDSHAKE_H__ */
diff --git a/framework/src/suricata/src/app-layer.c b/framework/src/suricata/src/app-layer.c
new file mode 100644
index 00000000..c47c2dd6
--- /dev/null
+++ b/framework/src/suricata/src/app-layer.c
@@ -0,0 +1,3490 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Generic App-layer functions
+ */
+
+#include "suricata-common.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-detect-proto.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-inline.h"
+#include "flow.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-profiling.h"
+#include "util-validate.h"
+#include "decode-events.h"
+
+#include "app-layer-htp-mem.h"
+#include "app-layer-dns-common.h"
+
+/**
+ * \brief This is for the app layer in general and it contains per thread
+ * context relevant to both the alpd and alp.
+ */
+struct AppLayerThreadCtx_ {
+ /* App layer protocol detection thread context, from AppLayerProtoDetectGetCtxThread(). */
+ AppLayerProtoDetectThreadCtx *alpd_tctx;
+ /* App layer parser thread context, from AppLayerParserThreadCtxAlloc(). */
+ AppLayerParserThreadCtx *alp_tctx;
+
+#ifdef PROFILING
+ uint64_t ticks_start;
+ uint64_t ticks_end;
+ uint64_t ticks_spent;
+ AppProto alproto;
+ uint64_t proto_detect_ticks_start;
+ uint64_t proto_detect_ticks_end;
+ uint64_t proto_detect_ticks_spent;
+#endif
+};
+
+/***** L7 layer dispatchers *****/
+
+static void DisableAppLayer(Flow *f)
+{
+ SCLogDebug("disable app layer for flow %p", f);
+ StreamTcpDisableAppLayer(f);
+}
+
+int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ Packet *p, Flow *f,
+ TcpSession *ssn, TcpStream *stream,
+ uint8_t *data, uint32_t data_len,
+ uint8_t flags)
+{
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ AppLayerThreadCtx *app_tctx = ra_ctx->app_tctx;
+ AppProto *alproto;
+ AppProto *alproto_otherdir;
+ uint8_t dir;
+ uint32_t data_al_so_far;
+ int r = 0;
+ uint8_t first_data_dir;
+
+ SCLogDebug("data_len %u flags %02X", data_len, flags);
+ if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) {
+ SCLogDebug("STREAMTCP_FLAG_APP_LAYER_DISABLED is set");
+ goto end;
+ }
+
+ if (flags & STREAM_TOSERVER) {
+ alproto = &f->alproto_ts;
+ alproto_otherdir = &f->alproto_tc;
+ dir = 0;
+ } else {
+ alproto = &f->alproto_tc;
+ alproto_otherdir = &f->alproto_ts;
+ dir = 1;
+ }
+
+ /* if we don't know the proto yet and we have received a stream
+ * initializer message, we run proto detection.
+ * We receive 2 stream init msgs (one for each direction) but we
+ * only run the proto detection once. */
+ if (*alproto == ALPROTO_UNKNOWN && (flags & STREAM_GAP)) {
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
+ StreamTcpSetSessionNoReassemblyFlag(ssn, dir);
+ SCLogDebug("ALPROTO_UNKNOWN flow %p, due to GAP in stream start", f);
+ } else if (*alproto == ALPROTO_UNKNOWN && (flags & STREAM_START)) {
+ if (data_len == 0)
+ data_al_so_far = 0;
+ else
+ data_al_so_far = f->data_al_so_far[dir];
+
+ SCLogDebug("Stream initializer (len %" PRIu32 ")", data_len);
+#ifdef PRINT
+ if (data_len > 0) {
+ printf("=> Init Stream Data (app layer) -- start %s%s\n",
+ flags & STREAM_TOCLIENT ? "toclient" : "",
+ flags & STREAM_TOSERVER ? "toserver" : "");
+ PrintRawDataFp(stdout, data, data_len);
+ printf("=> Init Stream Data -- end\n");
+ }
+#endif
+
+ PACKET_PROFILING_APP_PD_START(app_tctx);
+ *alproto = AppLayerProtoDetectGetProto(app_tctx->alpd_tctx,
+ f,
+ data, data_len,
+ IPPROTO_TCP, flags);
+ PACKET_PROFILING_APP_PD_END(app_tctx);
+
+ if (*alproto != ALPROTO_UNKNOWN) {
+ if (*alproto_otherdir != ALPROTO_UNKNOWN && *alproto_otherdir != *alproto) {
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS);
+ /* it indicates some data has already been sent to the parser */
+ if (ssn->data_first_seen_dir == APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ f->alproto = *alproto = *alproto_otherdir;
+ } else {
+ if (flags & STREAM_TOCLIENT)
+ f->alproto = *alproto_otherdir = *alproto;
+ else
+ f->alproto = *alproto = *alproto_otherdir;
+ }
+ }
+
+ f->alproto = *alproto;
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
+
+ /* if we have seen data from the other direction first, send
+ * data for that direction first to the parser. This shouldn't
+ * be an issue, since each stream processing happens
+ * independently of the other stream direction. At this point of
+ * call, you need to know that this function's already being
+ * called by the very same StreamReassembly() function that we
+ * will now call shortly for the opposing direction. */
+ if ((ssn->data_first_seen_dir & (STREAM_TOSERVER | STREAM_TOCLIENT)) &&
+ !(flags & ssn->data_first_seen_dir)) {
+ TcpStream *opposing_stream = NULL;
+ if (stream == &ssn->client) {
+ opposing_stream = &ssn->server;
+ if (StreamTcpInlineMode()) {
+ p->flowflags &= ~FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ } else {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ }
+ } else {
+ opposing_stream = &ssn->client;
+ if (StreamTcpInlineMode()) {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ } else {
+ p->flowflags &= ~FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ }
+ }
+ int ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn,
+ opposing_stream, p);
+ if (stream == &ssn->client) {
+ if (StreamTcpInlineMode()) {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ } else {
+ p->flowflags &= ~FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ }
+ } else {
+ if (StreamTcpInlineMode()) {
+ p->flowflags &= ~FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ } else {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ }
+ }
+ if (ret < 0) {
+ DisableAppLayer(f);
+ goto failure;
+ }
+ }
+
+ /* if the parser operates such that it needs to see data from
+ * a particular direction first, we check if we have seen
+ * data from that direction first for the flow. IF it is not
+ * the same, we set an event and exit.
+ *
+ * \todo We need to figure out a more robust solution for this,
+ * as this can lead to easy evasion tactics, where the
+ * attackeer can first send some dummy data in the wrong
+ * direction first to mislead our proto detection process.
+ * While doing this we need to update the parsers as well,
+ * since the parsers must be robust to see such wrong
+ * direction data.
+ * Either ways the moment we see the
+ * APPLAYER_WRONG_DIRECTION_FIRST_DATA event set for the
+ * flow, it shows something's fishy.
+ */
+ if (ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto);
+
+ if (first_data_dir && !(first_data_dir & ssn->data_first_seen_dir)) {
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_WRONG_DIRECTION_FIRST_DATA);
+ DisableAppLayer(f);
+ /* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ goto failure;
+ }
+ /* This can happen if the current direction is not the
+ * right direction, and the data from the other(also
+ * the right direction) direction is available to be sent
+ * to the app layer, but it is not ack'ed yet and hence
+ * the forced call to STreamTcpAppLayerReassemble still
+ * hasn't managed to send data from the other direction
+ * to the app layer. */
+ if (first_data_dir && !(first_data_dir & flags)) {
+ BUG_ON(*alproto_otherdir != ALPROTO_UNKNOWN);
+ FlowCleanupAppLayer(f);
+ f->alproto = *alproto = ALPROTO_UNKNOWN;
+ StreamTcpResetStreamFlagAppProtoDetectionCompleted(stream);
+ FLOW_RESET_PP_DONE(f, flags);
+ FLOW_RESET_PM_DONE(f, flags);
+ goto failure;
+ }
+ }
+
+ /* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+
+ PACKET_PROFILING_APP_START(app_tctx, *alproto);
+ r = AppLayerParserParse(app_tctx->alp_tctx, f, *alproto, flags, data + data_al_so_far, data_len - data_al_so_far);
+ PACKET_PROFILING_APP_END(app_tctx, *alproto);
+ f->data_al_so_far[dir] = 0;
+ } else {
+ /* if the ssn is midstream, we may end up with a case where the
+ * start of an HTTP request is missing. We won't detect HTTP based
+ * on the request. However, the reply is fine, so we detect
+ * HTTP anyway. This leads to passing the incomplete request to
+ * the htp parser.
+ *
+ * This has been observed, where the http parser then saw many
+ * bogus requests in the incomplete data.
+ *
+ * To counter this case, a midstream session MUST find it's
+ * protocol in the toserver direction. If not, we assume the
+ * start of the request/toserver is incomplete and no reliable
+ * detection and parsing is possible. So we give up.
+ */
+ if ((ssn->flags & STREAMTCP_FLAG_MIDSTREAM) && !(ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)) {
+ if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER)) {
+ SCLogDebug("midstream end pd %p", ssn);
+ /* midstream and toserver detection failed: give up */
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ goto end;
+ }
+ }
+
+ if (*alproto_otherdir != ALPROTO_UNKNOWN) {
+ first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto_otherdir);
+
+ /* this would handle this test case -
+ * http parser which says it wants to see toserver data first only.
+ * tcp handshake
+ * toclient data first received. - RUBBISH DATA which
+ * we don't detect as http
+ * toserver data next sent - we detect this as http.
+ * at this stage we see that toclient is the first data seen
+ * for this session and we try and redetect the app protocol,
+ * but we are unable to detect the app protocol like before.
+ * But since we have managed to detect the protocol for the
+ * other direction as http, we try to use that. At this
+ * stage we check if the direction of this stream matches
+ * to that acceptable by the app parser. If it is not the
+ * acceptable direction we error out.
+ */
+ if ((ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) &&
+ (first_data_dir) && !(first_data_dir & flags))
+ {
+ DisableAppLayer(f);
+ goto failure;
+ }
+
+ if (data_len > 0)
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+
+ PACKET_PROFILING_APP_START(app_tctx, *alproto_otherdir);
+ r = AppLayerParserParse(app_tctx->alp_tctx, f, *alproto_otherdir, flags,
+ data + data_al_so_far, data_len - data_al_so_far);
+ PACKET_PROFILING_APP_END(app_tctx, *alproto_otherdir);
+ if (FLOW_IS_PM_DONE(f, flags) && FLOW_IS_PP_DONE(f, flags)) {
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION);
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
+ f->data_al_so_far[dir] = 0;
+ } else {
+ f->data_al_so_far[dir] = data_len;
+ }
+ } else {
+ /* See if we're going to have to give up:
+ *
+ * If we're getting a lot of data in one direction and the
+ * proto for this direction is unknown, proto detect will
+ * hold up segments in the segment list in the stream.
+ * They are held so that if we detect the protocol on the
+ * opposing stream, we can still parse this side of the stream
+ * as well. However, some sessions are very unbalanced. FTP
+ * data channels, large PUT/POST request and many others, can
+ * lead to cases where we would have to store many megabytes
+ * worth of segments before we see the opposing stream. This
+ * leads to risks of resource starvation.
+ *
+ * Here a cutoff point is enforced. If we've stored 100k in
+ * one direction and we've seen no data in the other direction,
+ * we give up. */
+ uint32_t size_ts = ssn->client.last_ack - ssn->client.isn - 1;
+ uint32_t size_tc = ssn->server.last_ack - ssn->server.isn - 1;
+ SCLogDebug("size_ts %u, size_tc %u", size_ts, size_tc);
+
+ if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER) &&
+ FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT)) {
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+
+ } else if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER) &&
+ size_ts > 100000 && size_tc == 0)
+ {
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ } else if (FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) &&
+ size_tc > 100000 && size_ts == 0)
+ {
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ /* little data in ts direction, pp done, pm not done (max
+ * depth not reached), ts direction done, lots of data in
+ * tc direction. */
+ } else if (size_tc > 100000 &&
+ FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
+ FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT))
+ {
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ /* little data in tc direction, pp done, pm not done (max
+ * depth not reached), tc direction done, lots of data in
+ * ts direction. */
+ } else if (size_ts > 100000 &&
+ FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) && !(FLOW_IS_PM_DONE(f, STREAM_TOCLIENT)) &&
+ FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER))
+ {
+ DisableAppLayer(f);
+ ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ }
+ }
+ }
+ } else {
+ SCLogDebug("stream data (len %" PRIu32 " alproto "
+ "%"PRIu16" (flow %p)", data_len, f->alproto, f);
+#ifdef PRINT
+ if (data_len > 0) {
+ printf("=> Stream Data (app layer) -- start %s%s\n",
+ flags & STREAM_TOCLIENT ? "toclient" : "",
+ flags & STREAM_TOSERVER ? "toserver" : "");
+ PrintRawDataFp(stdout, data, data_len);
+ printf("=> Stream Data -- end\n");
+ }
+#endif
+ /* if we don't have a data object here we are not getting it
+ * a start msg should have gotten us one */
+ if (f->alproto != ALPROTO_UNKNOWN) {
+ PACKET_PROFILING_APP_START(app_tctx, f->alproto);
+ r = AppLayerParserParse(app_tctx->alp_tctx, f, f->alproto, flags, data, data_len);
+ PACKET_PROFILING_APP_END(app_tctx, f->alproto);
+ } else {
+ SCLogDebug(" smsg not start, but no l7 data? Weird");
+ }
+ }
+
+ goto end;
+ failure:
+ r = -1;
+ end:
+ SCReturnInt(r);
+}
+
+/**
+ * \brief Handle a app layer UDP message
+ *
+ * If the protocol is yet unknown, the proto detection code is run first.
+ *
+ * \param dp_ctx Thread app layer detect context
+ * \param f unlocked flow
+ * \param p UDP packet
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *tctx, Packet *p, Flow *f)
+{
+ SCEnter();
+
+ int r = 0;
+
+ FLOWLOCK_WRLOCK(f);
+
+ uint8_t flags = 0;
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flags |= STREAM_TOSERVER;
+ } else {
+ flags |= STREAM_TOCLIENT;
+ }
+
+ /* if we don't know the proto yet and we have received a stream
+ * initializer message, we run proto detection.
+ * We receive 2 stream init msgs (one for each direction) but we
+ * only run the proto detection once. */
+ if (f->alproto == ALPROTO_UNKNOWN && !(f->flags & FLOW_ALPROTO_DETECT_DONE)) {
+ SCLogDebug("Detecting AL proto on udp mesg (len %" PRIu32 ")",
+ p->payload_len);
+
+ PACKET_PROFILING_APP_PD_START(tctx);
+ f->alproto = AppLayerProtoDetectGetProto(tctx->alpd_tctx,
+ f,
+ p->payload, p->payload_len,
+ IPPROTO_UDP, flags);
+ PACKET_PROFILING_APP_PD_END(tctx);
+
+ if (f->alproto != ALPROTO_UNKNOWN) {
+ f->flags |= FLOW_ALPROTO_DETECT_DONE;
+
+ PACKET_PROFILING_APP_START(tctx, f->alproto);
+ r = AppLayerParserParse(tctx->alp_tctx,
+ f, f->alproto, flags,
+ p->payload, p->payload_len);
+ PACKET_PROFILING_APP_END(tctx, f->alproto);
+ } else {
+ f->flags |= FLOW_ALPROTO_DETECT_DONE;
+ SCLogDebug("ALPROTO_UNKNOWN flow %p", f);
+ }
+ } else {
+ SCLogDebug("stream data (len %" PRIu32 " ), alproto "
+ "%"PRIu16" (flow %p)", p->payload_len, f->alproto, f);
+
+ /* if we don't have a data object here we are not getting it
+ * a start msg should have gotten us one */
+ if (f->alproto != ALPROTO_UNKNOWN) {
+ PACKET_PROFILING_APP_START(tctx, f->alproto);
+ r = AppLayerParserParse(tctx->alp_tctx,
+ f, f->alproto, flags,
+ p->payload, p->payload_len);
+ PACKET_PROFILING_APP_END(tctx, f->alproto);
+ } else {
+ SCLogDebug("udp session has started, but failed to detect alproto "
+ "for l7");
+ }
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ PACKET_PROFILING_APP_STORE(tctx, p);
+
+ SCReturnInt(r);
+}
+
+/***** Utility *****/
+
+AppProto AppLayerGetProtoByName(char *alproto_name)
+{
+ SCEnter();
+ AppProto r = AppLayerProtoDetectGetProtoByName(alproto_name);
+ SCReturnCT(r, "AppProto");
+}
+
+char *AppLayerGetProtoName(AppProto alproto)
+{
+ SCEnter();
+ char * r = AppLayerProtoDetectGetProtoName(alproto);
+ SCReturnCT(r, "char *");
+}
+
+void AppLayerListSupportedProtocols(void)
+{
+ SCEnter();
+
+ AppProto alproto;
+ AppProto alprotos[ALPROTO_MAX];
+
+ AppLayerProtoDetectSupportedAppProtocols(alprotos);
+
+ printf("=========Supported App Layer Protocols=========\n");
+ for (alproto = 0; alproto < ALPROTO_MAX; alproto++) {
+ if (alprotos[alproto] == 1)
+ printf("%s\n", AppLayerGetProtoName(alproto));
+ }
+
+ SCReturn;
+}
+
+/***** Setup/General Registration *****/
+
+int AppLayerSetup(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectSetup();
+ AppLayerParserSetup();
+
+ AppLayerParserRegisterProtocolParsers();
+ AppLayerProtoDetectPrepareState();
+
+ SCReturnInt(0);
+}
+
+int AppLayerDeSetup(void)
+{
+ SCEnter();
+
+ AppLayerProtoDetectDeSetup();
+ AppLayerParserDeSetup();
+
+ SCReturnInt(0);
+}
+
+AppLayerThreadCtx *AppLayerGetCtxThread(ThreadVars *tv)
+{
+ SCEnter();
+
+ AppLayerThreadCtx *app_tctx = SCMalloc(sizeof(*app_tctx));
+ if (app_tctx == NULL)
+ goto error;
+ memset(app_tctx, 0, sizeof(*app_tctx));
+
+ if ((app_tctx->alpd_tctx = AppLayerProtoDetectGetCtxThread()) == NULL)
+ goto error;
+ if ((app_tctx->alp_tctx = AppLayerParserThreadCtxAlloc()) == NULL)
+ goto error;
+
+ goto done;
+ error:
+ AppLayerDestroyCtxThread(app_tctx);
+ app_tctx = NULL;
+ done:
+ SCReturnPtr(app_tctx, "void *");
+}
+
+void AppLayerDestroyCtxThread(AppLayerThreadCtx *app_tctx)
+{
+ SCEnter();
+
+ if (app_tctx == NULL)
+ SCReturn;
+
+ if (app_tctx->alpd_tctx != NULL)
+ AppLayerProtoDetectDestroyCtxThread(app_tctx->alpd_tctx);
+ if (app_tctx->alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(app_tctx->alp_tctx);
+ SCFree(app_tctx);
+
+ SCReturn;
+}
+
+void AppLayerProfilingResetInternal(AppLayerThreadCtx *app_tctx)
+{
+ PACKET_PROFILING_APP_RESET(app_tctx);
+}
+
+void AppLayerProfilingStoreInternal(AppLayerThreadCtx *app_tctx, Packet *p)
+{
+ PACKET_PROFILING_APP_STORE(app_tctx, p);
+}
+
+/** \brief HACK to work around our broken unix manager (re)init loop
+ */
+void AppLayerRegisterGlobalCounters(void)
+{
+ StatsRegisterGlobalCounter("dns.memuse", DNSMemcapGetMemuseCounter);
+ StatsRegisterGlobalCounter("dns.memcap_state", DNSMemcapGetMemcapStateCounter);
+ StatsRegisterGlobalCounter("dns.memcap_global", DNSMemcapGetMemcapGlobalCounter);
+ StatsRegisterGlobalCounter("http.memuse", HTPMemuseGlobalCounter);
+ StatsRegisterGlobalCounter("http.memcap", HTPMemcapGlobalCounter);
+}
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp-inline.h"
+#include "stream-tcp-util.h"
+#include "stream.h"
+#include "util-unittest.h"
+
+/**
+ * \test GET -> HTTP/1.1
+ */
+static int AppLayerTest01(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request[] = {
+ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test GE -> T -> HTTP/1.1
+ */
+static int AppLayerTest02(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* handshake */
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* partial request */
+ uint8_t request1[] = { 0x47, 0x45, };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* response ack against partial request */
+ p->tcph->th_ack = htonl(3);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* complete partial request */
+ uint8_t request2[] = {
+ 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(3);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test GET -> RUBBISH(PM AND PP DONE IN ONE GO)
+ */
+static int AppLayerTest03(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* request */
+ uint8_t request[] = {
+ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* rubbish response */
+ uint8_t response[] = {
+ 0x58, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test GE -> RUBBISH(TC - PM AND PP NOT DONE) -> RUBBISH(TC - PM AND PP DONE).
+ */
+static int AppLayerTest04(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* handshake */
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* request */
+ uint8_t request[] = {
+ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* partial response */
+ uint8_t response1[] = { 0x58, 0x54, 0x54, 0x50, };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response1);
+ p->payload = response1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* partial response ack */
+ p->tcph->th_ack = htonl(5);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 4 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* remaining response */
+ uint8_t response2[] = {
+ 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response2);
+ p->payload = response2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 4 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test RUBBISH -> HTTP/1.1
+ */
+static int AppLayerTest05(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request[] = {
+ 0x48, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test HTTP/1.1 -> GET
+ */
+static int AppLayerTest06(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOCLIENT) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* full request - response ack*/
+ uint8_t request[] = {
+ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test GET -> DCERPC
+ */
+static int AppLayerTest07(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request[] = {
+ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x05, 0x00, 0x4d, 0x42, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test SMB -> HTTP/1.1
+ */
+static int AppLayerTest08(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request[] = {
+ 0x05, 0x00, 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request);
+ p->payload = request;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_DCERPC ||
+ f.alproto_ts != ALPROTO_DCERPC ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_DCERPC ||
+ f.alproto_ts != ALPROTO_DCERPC ||
+ f.alproto_tc != ALPROTO_DCERPC ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 6 %04x\n", ssn->flags);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test RUBBISH(TC - PM and PP NOT DONE) ->
+ * RUBBISH(TC - PM and PP DONE) ->
+ * RUBBISH(TS - PM and PP DONE)
+ */
+static int AppLayerTest09(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request1[] = {
+ 0x47, 0x47, 0x49, 0x20, 0x2f, 0x69, 0x6e, 0x64 };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ p->tcph->th_ack = htonl(9);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request2[] = {
+ 0x44, 0x44, 0x45, 0x20, 0x2f, 0x69, 0x6e, 0x64, 0xff };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(9);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x55, 0x74, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test RUBBISH(TC - PM and PP DONE) ->
+ * RUBBISH(TS - PM and PP DONE)
+ */
+static int AppLayerTest10(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request1[] = {
+ 0x47, 0x47, 0x49, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x47, 0x47, 0x49, 0x20, 0x2f, 0x69, 0x6e, 0x64, 0xff };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response[] = {
+ 0x55, 0x74, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test RUBBISH(TC - PM and PP DONE) ->
+ * RUBBISH(TS - PM and PP NOT DONE) ->
+ * RUBBISH(TS - PM and PP DONE)
+ */
+static int AppLayerTest11(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ /* handshake */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* full request */
+ uint8_t request1[] = {
+ 0x47, 0x47, 0x49, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x47, 0x47, 0x49, 0x20, 0x2f, 0x69, 0x6e, 0x64, 0xff };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* full response - request ack */
+ uint8_t response1[] = {
+ 0x55, 0x74, 0x54, 0x50, };
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response1);
+ p->payload = response1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(5);
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ uint8_t response2[] = {
+ 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(18);
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response2);
+ p->payload = response2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || !FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 9\n");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+ end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+void AppLayerUnittestsRegister(void)
+{
+ SCEnter();
+
+ UtRegisterTest("AppLayerTest01", AppLayerTest01, 1);
+ UtRegisterTest("AppLayerTest02", AppLayerTest02, 1);
+ UtRegisterTest("AppLayerTest03", AppLayerTest03, 1);
+ UtRegisterTest("AppLayerTest04", AppLayerTest04, 1);
+ UtRegisterTest("AppLayerTest05", AppLayerTest05, 1);
+ UtRegisterTest("AppLayerTest06", AppLayerTest06, 1);
+ UtRegisterTest("AppLayerTest07", AppLayerTest07, 1);
+ UtRegisterTest("AppLayerTest08", AppLayerTest08, 1);
+ UtRegisterTest("AppLayerTest09", AppLayerTest09, 1);
+ UtRegisterTest("AppLayerTest10", AppLayerTest10, 1);
+ UtRegisterTest("AppLayerTest11", AppLayerTest11, 1);
+
+ SCReturn;
+}
+
+#endif /* UNITTESTS */
diff --git a/framework/src/suricata/src/app-layer.h b/framework/src/suricata/src/app-layer.h
new file mode 100644
index 00000000..f45afe67
--- /dev/null
+++ b/framework/src/suricata/src/app-layer.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __APP_LAYER_H__
+#define __APP_LAYER_H__
+
+#include "threadvars.h"
+#include "decode.h"
+#include "flow.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream.h"
+
+#include "util-profiling.h"
+
+#define APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER \
+ (~STREAM_TOSERVER & ~STREAM_TOCLIENT)
+
+/***** L7 layer dispatchers *****/
+
+/**
+ * \brief Handles reassembled tcp stream.
+ */
+int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ Packet *p, Flow *f,
+ TcpSession *ssn, TcpStream *stream,
+ uint8_t *data, uint32_t data_len,
+ uint8_t flags);
+
+/**
+ * \brief Handles an udp chunk.
+ */
+int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *app_tctx,
+ Packet *p, Flow *f);
+
+/***** Utility *****/
+
+/**
+ * \brief Given a protocol string, returns the corresponding internal
+ * protocol id.
+ *
+ * \param The internal protocol id.
+ */
+AppProto AppLayerGetProtoByName(char *alproto_name);
+
+/**
+ * \brief Given the internal protocol id, returns a string representation
+ * of the protocol.
+ *
+ * \param alproto The internal protocol id.
+ *
+ * \retval String representation of the protocol.
+ */
+char *AppLayerGetProtoName(AppProto alproto);
+
+void AppLayerListSupportedProtocols(void);
+
+/***** Setup/General Registration *****/
+
+/**
+ * \brief Setup the app layer.
+ *
+ * Includes protocol detection setup and the protocol parser setup.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int AppLayerSetup(void);
+
+/**
+ * \brief De initializes the app layer.
+ *
+ * Includes de initializing protocol detection and the protocol parser.
+ */
+int AppLayerDeSetup(void);
+
+/**
+ * \brief Creates a new app layer thread context.
+ *
+ * \retval Pointer to the newly create thread context, on success;
+ * NULL, on failure.
+ */
+AppLayerThreadCtx *AppLayerGetCtxThread(ThreadVars *tv);
+
+/**
+ * \brief Destroys the context created by AppLayeGetCtxThread().
+ *
+ * \param tctx Pointer to the thread context to destroy.
+ */
+void AppLayerDestroyCtxThread(AppLayerThreadCtx *tctx);
+
+
+/***** Profiling *****/
+
+void AppLayerProfilingResetInternal(AppLayerThreadCtx *app_tctx);
+
+static inline void AppLayerProfilingReset(AppLayerThreadCtx *app_tctx)
+{
+#ifdef PROFILING
+ AppLayerProfilingResetInternal(app_tctx);
+#endif
+}
+
+void AppLayerProfilingStoreInternal(AppLayerThreadCtx *app_tctx, Packet *p);
+
+static inline void AppLayerProfilingStore(AppLayerThreadCtx *app_tctx, Packet *p)
+{
+#ifdef PROFILING
+ AppLayerProfilingStoreInternal(app_tctx, p);
+#endif
+}
+
+void AppLayerRegisterGlobalCounters(void);
+
+/***** Unittests *****/
+
+#ifdef UNITTESTS
+void AppLayerUnittestsRegister(void);
+#endif
+
+#endif
diff --git a/framework/src/suricata/src/conf-yaml-loader.c b/framework/src/suricata/src/conf-yaml-loader.c
new file mode 100644
index 00000000..13ec0488
--- /dev/null
+++ b/framework/src/suricata/src/conf-yaml-loader.c
@@ -0,0 +1,949 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com>
+ *
+ * YAML configuration loader.
+ */
+
+#include <yaml.h>
+#include "suricata-common.h"
+#include "conf.h"
+#include "util-path.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#define YAML_VERSION_MAJOR 1
+#define YAML_VERSION_MINOR 1
+
+/* Sometimes we'll have to create a node name on the fly (integer
+ * conversion, etc), so this is a default length to allocate that will
+ * work most of the time. */
+#define DEFAULT_NAME_LEN 16
+
+#define MANGLE_ERRORS_MAX 10
+static int mangle_errors = 0;
+
+static char *conf_dirname = NULL;
+
+static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq);
+
+/* Configuration processing states. */
+enum conf_state {
+ CONF_KEY = 0,
+ CONF_VAL,
+ CONF_INCLUDE,
+};
+
+/**
+ * \brief Mangle unsupported characters.
+ *
+ * \param string A pointer to an null terminated string.
+ *
+ * \retval none
+ */
+static void
+Mangle(char *string)
+{
+ char *c;
+
+ while ((c = strchr(string, '_')))
+ *c = '-';
+
+ return;
+}
+
+/**
+ * \brief Set the directory name of the configuration file.
+ *
+ * \param filename The configuration filename.
+ */
+static void
+ConfYamlSetConfDirname(const char *filename)
+{
+ char *ep;
+
+ ep = strrchr(filename, '\\');
+ if (ep == NULL)
+ ep = strrchr(filename, '/');
+
+ if (ep == NULL) {
+ conf_dirname = SCStrdup(".");
+ if (conf_dirname == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "ERROR: Failed to allocate memory while loading configuration.");
+ exit(EXIT_FAILURE);
+ }
+ }
+ else {
+ conf_dirname = SCStrdup(filename);
+ if (conf_dirname == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "ERROR: Failed to allocate memory while loading configuration.");
+ exit(EXIT_FAILURE);
+ }
+ conf_dirname[ep - filename] = '\0';
+ }
+}
+
+/**
+ * \brief Include a file in the configuration.
+ *
+ * \param parent The configuration node the included configuration will be
+ * placed at.
+ * \param filename The filename to include.
+ *
+ * \retval 0 on success, -1 on failure.
+ */
+static int
+ConfYamlHandleInclude(ConfNode *parent, const char *filename)
+{
+ yaml_parser_t parser;
+ char include_filename[PATH_MAX];
+ FILE *file;
+
+ if (yaml_parser_initialize(&parser) != 1) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "Failed to initialize YAML parser");
+ return -1;
+ }
+
+ if (PathIsAbsolute(filename)) {
+ strlcpy(include_filename, filename, sizeof(include_filename));
+ }
+ else {
+ snprintf(include_filename, sizeof(include_filename), "%s/%s",
+ conf_dirname, filename);
+ }
+
+ file = fopen(include_filename, "r");
+ if (file == NULL) {
+ SCLogError(SC_ERR_FOPEN,
+ "Failed to open configuration include file %s: %s",
+ include_filename, strerror(errno));
+ return -1;
+ }
+
+ yaml_parser_set_input_file(&parser, file);
+
+ if (ConfYamlParse(&parser, parent, 0) != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR,
+ "Failed to include configuration file %s", filename);
+ return -1;
+ }
+
+ yaml_parser_delete(&parser);
+ fclose(file);
+
+ return 0;
+}
+
+/**
+ * \brief Parse a YAML layer.
+ *
+ * \param parser A pointer to an active yaml_parser_t.
+ * \param parent The parent configuration node.
+ *
+ * \retval 0 on success, -1 on failure.
+ */
+static int
+ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq)
+{
+ ConfNode *node = parent;
+ yaml_event_t event;
+ int done = 0;
+ int state = 0;
+ int seq_idx = 0;
+
+ while (!done) {
+ if (!yaml_parser_parse(parser, &event)) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR,
+ "Failed to parse configuration file at line %" PRIuMAX ": %s\n",
+ (uintmax_t)parser->problem_mark.line, parser->problem);
+ return -1;
+ }
+
+ if (event.type == YAML_DOCUMENT_START_EVENT) {
+ SCLogDebug("event.type=YAML_DOCUMENT_START_EVENT; state=%d", state);
+ /* Verify YAML version - its more likely to be a valid
+ * Suricata configuration file if the version is
+ * correct. */
+ yaml_version_directive_t *ver =
+ event.data.document_start.version_directive;
+ if (ver == NULL) {
+ fprintf(stderr, "ERROR: Invalid configuration file.\n\n");
+ fprintf(stderr, "The configuration file must begin with the following two lines:\n\n");
+ fprintf(stderr, "%%YAML 1.1\n---\n\n");
+ goto fail;
+ }
+ int major = event.data.document_start.version_directive->major;
+ int minor = event.data.document_start.version_directive->minor;
+ if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) {
+ fprintf(stderr, "ERROR: Invalid YAML version. Must be 1.1\n");
+ goto fail;
+ }
+ }
+ else if (event.type == YAML_SCALAR_EVENT) {
+ char *value = (char *)event.data.scalar.value;
+ char *tag = (char *)event.data.scalar.tag;
+ SCLogDebug("event.type=YAML_SCALAR_EVENT; state=%d; value=%s; "
+ "tag=%s; inseq=%d", state, value, tag, inseq);
+ if (inseq) {
+ char sequence_node_name[DEFAULT_NAME_LEN];
+ snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
+ ConfNode *seq_node = ConfNodeLookupChild(parent,
+ sequence_node_name);
+ if (seq_node != NULL) {
+ /* The sequence node has already been set, probably
+ * from the command line. Remove it so it gets
+ * re-added in the expected order for iteration.
+ */
+ TAILQ_REMOVE(&parent->head, seq_node, next);
+ }
+ else {
+ seq_node = ConfNodeNew();
+ if (unlikely(seq_node == NULL)) {
+ return -1;
+ }
+ seq_node->name = SCStrdup(sequence_node_name);
+ if (unlikely(seq_node->name == NULL)) {
+ SCFree(seq_node);
+ return -1;
+ }
+ seq_node->val = SCStrdup(value);
+ if (unlikely(seq_node->val == NULL)) {
+ SCFree(seq_node->name);
+ return -1;
+ }
+ }
+ TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
+ }
+ else {
+ if (state == CONF_INCLUDE) {
+ SCLogInfo("Including configuration file %s.", value);
+ if (ConfYamlHandleInclude(parent, value) != 0) {
+ goto fail;
+ }
+ state = CONF_KEY;
+ }
+ else if (state == CONF_KEY) {
+
+ if (strcmp(value, "include") == 0) {
+ state = CONF_INCLUDE;
+ goto next;
+ }
+
+ if (parent->is_seq) {
+ if (parent->val == NULL) {
+ parent->val = SCStrdup(value);
+ if (parent->val && strchr(parent->val, '_'))
+ Mangle(parent->val);
+ }
+ }
+ ConfNode *existing = ConfNodeLookupChild(parent, value);
+ if (existing != NULL) {
+ if (!existing->final) {
+ SCLogInfo("Configuration node '%s' redefined.",
+ existing->name);
+ ConfNodePrune(existing);
+ }
+ node = existing;
+ }
+ else {
+ node = ConfNodeNew();
+ node->name = SCStrdup(value);
+ if (node->name && strchr(node->name, '_')) {
+ if (!(parent->name &&
+ ((strcmp(parent->name, "address-groups") == 0) ||
+ (strcmp(parent->name, "port-groups") == 0)))) {
+ Mangle(node->name);
+ if (mangle_errors < MANGLE_ERRORS_MAX) {
+ SCLogWarning(SC_WARN_DEPRECATED,
+ "%s is deprecated. Please use %s on line %"PRIuMAX".",
+ value, node->name, (uintmax_t)parser->mark.line+1);
+ mangle_errors++;
+ if (mangle_errors >= MANGLE_ERRORS_MAX)
+ SCLogWarning(SC_WARN_DEPRECATED, "not showing more "
+ "parameter name warnings.");
+ }
+ }
+ }
+ TAILQ_INSERT_TAIL(&parent->head, node, next);
+ }
+ state = CONF_VAL;
+ }
+ else {
+ if ((tag != NULL) && (strcmp(tag, "!include") == 0)) {
+ SCLogInfo("Including configuration file %s at "
+ "parent node %s.", value, node->name);
+ if (ConfYamlHandleInclude(node, value) != 0)
+ goto fail;
+ }
+ else if (!node->final) {
+ if (node->val != NULL)
+ SCFree(node->val);
+ node->val = SCStrdup(value);
+ }
+ state = CONF_KEY;
+ }
+ }
+ }
+ else if (event.type == YAML_SEQUENCE_START_EVENT) {
+ SCLogDebug("event.type=YAML_SEQUENCE_START_EVENT; state=%d", state);
+ if (ConfYamlParse(parser, node, 1) != 0)
+ goto fail;
+ node->is_seq = 1;
+ state = CONF_KEY;
+ }
+ else if (event.type == YAML_SEQUENCE_END_EVENT) {
+ SCLogDebug("event.type=YAML_SEQUENCE_END_EVENT; state=%d", state);
+ return 0;
+ }
+ else if (event.type == YAML_MAPPING_START_EVENT) {
+ SCLogDebug("event.type=YAML_MAPPING_START_EVENT; state=%d", state);
+ if (inseq) {
+ char sequence_node_name[DEFAULT_NAME_LEN];
+ snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
+ ConfNode *seq_node = ConfNodeLookupChild(node,
+ sequence_node_name);
+ if (seq_node != NULL) {
+ /* The sequence node has already been set, probably
+ * from the command line. Remove it so it gets
+ * re-added in the expected order for iteration.
+ */
+ TAILQ_REMOVE(&node->head, seq_node, next);
+ }
+ else {
+ seq_node = ConfNodeNew();
+ if (unlikely(seq_node == NULL)) {
+ return -1;
+ }
+ seq_node->name = SCStrdup(sequence_node_name);
+ if (unlikely(seq_node->name == NULL)) {
+ SCFree(seq_node);
+ return -1;
+ }
+ }
+ seq_node->is_seq = 1;
+ TAILQ_INSERT_TAIL(&node->head, seq_node, next);
+ if (ConfYamlParse(parser, seq_node, 0) != 0)
+ goto fail;
+ }
+ else {
+ if (ConfYamlParse(parser, node, inseq) != 0)
+ goto fail;
+ }
+ state = CONF_KEY;
+ }
+ else if (event.type == YAML_MAPPING_END_EVENT) {
+ SCLogDebug("event.type=YAML_MAPPING_END_EVENT; state=%d", state);
+ done = 1;
+ }
+ else if (event.type == YAML_STREAM_END_EVENT) {
+ SCLogDebug("event.type=YAML_STREAM_END_EVENT; state=%d", state);
+ done = 1;
+ }
+
+ next:
+ yaml_event_delete(&event);
+ continue;
+
+ fail:
+ yaml_event_delete(&event);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Load configuration from a YAML file.
+ *
+ * This function will load a configuration file. On failure -1 will
+ * be returned and it is suggested that the program then exit. Any
+ * errors while loading the configuration file will have already been
+ * logged.
+ *
+ * \param filename Filename of configuration file to load.
+ *
+ * \retval 0 on success, -1 on failure.
+ */
+int
+ConfYamlLoadFile(const char *filename)
+{
+ FILE *infile;
+ yaml_parser_t parser;
+ int ret;
+ ConfNode *root = ConfGetRootNode();
+
+ if (yaml_parser_initialize(&parser) != 1) {
+ SCLogError(SC_ERR_FATAL, "failed to initialize yaml parser.");
+ return -1;
+ }
+
+ struct stat stat_buf;
+ if (stat(filename, &stat_buf) == 0) {
+ if (stat_buf.st_mode & S_IFDIR) {
+ SCLogError(SC_ERR_FATAL, "yaml argument is not a file but a directory: %s. "
+ "Please specify the yaml file in your -c option.", filename);
+ return -1;
+ }
+ }
+
+ infile = fopen(filename, "r");
+ if (infile == NULL) {
+ SCLogError(SC_ERR_FATAL, "failed to open file: %s: %s", filename,
+ strerror(errno));
+ yaml_parser_delete(&parser);
+ return -1;
+ }
+
+ if (conf_dirname == NULL) {
+ ConfYamlSetConfDirname(filename);
+ }
+
+ yaml_parser_set_input_file(&parser, infile);
+ ret = ConfYamlParse(&parser, root, 0);
+ yaml_parser_delete(&parser);
+ fclose(infile);
+
+ return ret;
+}
+
+/**
+ * \brief Load configuration from a YAML string.
+ */
+int
+ConfYamlLoadString(const char *string, size_t len)
+{
+ ConfNode *root = ConfGetRootNode();
+ yaml_parser_t parser;
+ int ret;
+
+ if (yaml_parser_initialize(&parser) != 1) {
+ fprintf(stderr, "Failed to initialize yaml parser.\n");
+ exit(EXIT_FAILURE);
+ }
+ yaml_parser_set_input_string(&parser, (const unsigned char *)string, len);
+ ret = ConfYamlParse(&parser, root, 0);
+ yaml_parser_delete(&parser);
+
+ return ret;
+}
+
+/**
+ * \brief Load configuration from a YAML file, insert in tree at 'prefix'
+ *
+ * This function will load a configuration file and insert it into the
+ * config tree at 'prefix'. This means that if this is called with prefix
+ * "abc" and the file contains a parameter "def", it will be loaded as
+ * "abc.def".
+ *
+ * \param filename Filename of configuration file to load.
+ * \param prefix Name prefix to use.
+ *
+ * \retval 0 on success, -1 on failure.
+ */
+int
+ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix)
+{
+ FILE *infile;
+ yaml_parser_t parser;
+ int ret;
+ ConfNode *root = ConfGetNode(prefix);
+
+ if (yaml_parser_initialize(&parser) != 1) {
+ SCLogError(SC_ERR_FATAL, "failed to initialize yaml parser.");
+ return -1;
+ }
+
+ struct stat stat_buf;
+ if (stat(filename, &stat_buf) == 0) {
+ if (stat_buf.st_mode & S_IFDIR) {
+ SCLogError(SC_ERR_FATAL, "yaml argument is not a file but a directory: %s. "
+ "Please specify the yaml file in your -c option.", filename);
+ return -1;
+ }
+ }
+
+ infile = fopen(filename, "r");
+ if (infile == NULL) {
+ SCLogError(SC_ERR_FATAL, "failed to open file: %s: %s", filename,
+ strerror(errno));
+ yaml_parser_delete(&parser);
+ return -1;
+ }
+
+ if (conf_dirname == NULL) {
+ ConfYamlSetConfDirname(filename);
+ }
+
+ if (root == NULL) {
+ /* if node at 'prefix' doesn't yet exist, add a place holder */
+ ConfSet(prefix, "<prefix root node>");
+ root = ConfGetNode(prefix);
+ if (root == NULL) {
+ fclose(infile);
+ yaml_parser_delete(&parser);
+ return -1;
+ }
+ }
+ yaml_parser_set_input_file(&parser, infile);
+ ret = ConfYamlParse(&parser, root, 0);
+ yaml_parser_delete(&parser);
+ fclose(infile);
+
+ return ret;
+}
+
+#ifdef UNITTESTS
+
+static int
+ConfYamlSequenceTest(void)
+{
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+rule-files:\n\
+ - netbios.rules\n\
+ - x11.rules\n\
+\n\
+default-log-dir: /tmp\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ ConfNode *node;
+ node = ConfGetNode("rule-files");
+ if (node == NULL)
+ return 0;
+ if (!ConfNodeIsSequence(node))
+ return 0;
+ if (TAILQ_EMPTY(&node->head))
+ return 0;
+ int i = 0;
+ ConfNode *filename;
+ TAILQ_FOREACH(filename, &node->head, next) {
+ if (i == 0) {
+ if (strcmp(filename->val, "netbios.rules") != 0)
+ return 0;
+ if (ConfNodeIsSequence(filename))
+ return 0;
+ if (filename->is_seq != 0)
+ return 0;
+ }
+ else if (i == 1) {
+ if (strcmp(filename->val, "x11.rules") != 0)
+ return 0;
+ if (ConfNodeIsSequence(filename))
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ i++;
+ }
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+static int
+ConfYamlLoggingOutputTest(void)
+{
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+logging:\n\
+ output:\n\
+ - interface: console\n\
+ log-level: error\n\
+ - interface: syslog\n\
+ facility: local4\n\
+ log-level: info\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ ConfYamlLoadString(input, strlen(input));
+
+ ConfNode *outputs;
+ outputs = ConfGetNode("logging.output");
+ if (outputs == NULL)
+ return 0;
+
+ ConfNode *output;
+ ConfNode *output_param;
+
+ output = TAILQ_FIRST(&outputs->head);
+ if (output == NULL)
+ return 0;
+ if (strcmp(output->name, "0") != 0)
+ return 0;
+ output_param = TAILQ_FIRST(&output->head);
+ if (output_param == NULL)
+ return 0;
+ if (strcmp(output_param->name, "interface") != 0)
+ return 0;
+ if (strcmp(output_param->val, "console") != 0)
+ return 0;
+ output_param = TAILQ_NEXT(output_param, next);
+ if (strcmp(output_param->name, "log-level") != 0)
+ return 0;
+ if (strcmp(output_param->val, "error") != 0)
+ return 0;
+
+ output = TAILQ_NEXT(output, next);
+ if (output == NULL)
+ return 0;
+ if (strcmp(output->name, "1") != 0)
+ return 0;
+ output_param = TAILQ_FIRST(&output->head);
+ if (output_param == NULL)
+ return 0;
+ if (strcmp(output_param->name, "interface") != 0)
+ return 0;
+ if (strcmp(output_param->val, "syslog") != 0)
+ return 0;
+ output_param = TAILQ_NEXT(output_param, next);
+ if (strcmp(output_param->name, "facility") != 0)
+ return 0;
+ if (strcmp(output_param->val, "local4") != 0)
+ return 0;
+ output_param = TAILQ_NEXT(output_param, next);
+ if (strcmp(output_param->name, "log-level") != 0)
+ return 0;
+ if (strcmp(output_param->val, "info") != 0)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+/**
+ * Try to load something that is not a valid YAML file.
+ */
+static int
+ConfYamlNonYamlFileTest(void)
+{
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (ConfYamlLoadFile("/etc/passwd") != -1)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+static int
+ConfYamlBadYamlVersionTest(void)
+{
+ char input[] = "\
+%YAML 9.9\n\
+---\n\
+logging:\n\
+ output:\n\
+ - interface: console\n\
+ log-level: error\n\
+ - interface: syslog\n\
+ facility: local4\n\
+ log-level: info\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (ConfYamlLoadString(input, strlen(input)) != -1)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+static int
+ConfYamlSecondLevelSequenceTest(void)
+{
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+ server-config:\n\
+ - apache-php:\n\
+ address: [\"192.168.1.0/24\"]\n\
+ personality: [\"Apache_2_2\", \"PHP_5_3\"]\n\
+ path-parsing: [\"compress_separators\", \"lowercase\"]\n\
+ - iis-php:\n\
+ address:\n\
+ - 192.168.0.0/24\n\
+\n\
+ personality:\n\
+ - IIS_7_0\n\
+ - PHP_5_3\n\
+\n\
+ path-parsing:\n\
+ - compress_separators\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (ConfYamlLoadString(input, strlen(input)) != 0)
+ return 0;
+
+ ConfNode *outputs;
+ outputs = ConfGetNode("libhtp.server-config");
+ if (outputs == NULL)
+ return 0;
+
+ ConfNode *node;
+
+ node = TAILQ_FIRST(&outputs->head);
+ if (node == NULL)
+ return 0;
+ if (strcmp(node->name, "0") != 0)
+ return 0;
+ node = TAILQ_FIRST(&node->head);
+ if (node == NULL)
+ return 0;
+ if (strcmp(node->name, "apache-php") != 0)
+ return 0;
+
+ node = ConfNodeLookupChild(node, "address");
+ if (node == NULL)
+ return 0;
+ node = TAILQ_FIRST(&node->head);
+ if (node == NULL)
+ return 0;
+ if (strcmp(node->name, "0") != 0)
+ return 0;
+ if (strcmp(node->val, "192.168.1.0/24") != 0)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+/**
+ * Test file inclusion support.
+ */
+static int
+ConfYamlFileIncludeTest(void)
+{
+ int ret = 0;
+ FILE *config_file;
+
+ const char config_filename[] = "ConfYamlFileIncludeTest-config.yaml";
+ const char config_file_contents[] =
+ "%YAML 1.1\n"
+ "---\n"
+ "# Include something at the root level.\n"
+ "include: ConfYamlFileIncludeTest-include.yaml\n"
+ "# Test including under a mapping.\n"
+ "mapping: !include ConfYamlFileIncludeTest-include.yaml\n";
+
+ const char include_filename[] = "ConfYamlFileIncludeTest-include.yaml";
+ const char include_file_contents[] =
+ "%YAML 1.1\n"
+ "---\n"
+ "host-mode: auto\n"
+ "unix-command:\n"
+ " enabled: no\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ /* Write out the test files. */
+ if ((config_file = fopen(config_filename, "w")) == NULL) {
+ goto cleanup;
+ }
+ if (fwrite(config_file_contents, strlen(config_file_contents), 1,
+ config_file) != 1) {
+ goto cleanup;
+ }
+ fclose(config_file);
+ if ((config_file = fopen(include_filename, "w")) == NULL) {
+ goto cleanup;
+ }
+ if (fwrite(include_file_contents, strlen(include_file_contents), 1,
+ config_file) != 1) {
+ goto cleanup;
+ }
+ fclose(config_file);
+
+ /* Reset conf_dirname. */
+ if (conf_dirname != NULL) {
+ SCFree(conf_dirname);
+ conf_dirname = NULL;
+ }
+
+ if (ConfYamlLoadFile("ConfYamlFileIncludeTest-config.yaml") != 0)
+ goto cleanup;
+
+ /* Check values that should have been loaded into the root of the
+ * configuration. */
+ ConfNode *node;
+ node = ConfGetNode("host-mode");
+ if (node == NULL)
+ goto cleanup;
+ if (strcmp(node->val, "auto") != 0)
+ goto cleanup;
+ node = ConfGetNode("unix-command.enabled");
+ if (node == NULL)
+ goto cleanup;
+ if (strcmp(node->val, "no") != 0)
+ goto cleanup;
+
+ /* Check for values that were included under a mapping. */
+ node = ConfGetNode("mapping.host-mode");
+ if (node == NULL)
+ goto cleanup;
+ if (strcmp(node->val, "auto") != 0)
+ goto cleanup;
+ node = ConfGetNode("mapping.unix-command.enabled");
+ if (node == NULL)
+ goto cleanup;
+ if (strcmp(node->val, "no") != 0)
+ goto cleanup;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ ret = 1;
+
+cleanup:
+ unlink(config_filename);
+ unlink(include_filename);
+
+ return ret;
+}
+
+/**
+ * Test that a configuration section is overridden but subsequent
+ * occurrences.
+ */
+static int
+ConfYamlOverrideTest(void)
+{
+ char config[] =
+ "%YAML 1.1\n"
+ "---\n"
+ "some-log-dir: /var/log\n"
+ "some-log-dir: /tmp\n"
+ "\n"
+ "parent:\n"
+ " child0:\n"
+ " key: value\n"
+ "parent:\n"
+ " child1:\n"
+ " key: value\n"
+ ;
+ char *value;
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (ConfYamlLoadString(config, strlen(config)) != 0)
+ return 0;
+ if (!ConfGet("some-log-dir", &value))
+ return 0;
+ if (strcmp(value, "/tmp") != 0)
+ return 0;
+
+ /* Test that parent.child0 does not exist, but child1 does. */
+ if (ConfGetNode("parent.child0") != NULL)
+ return 0;
+ if (!ConfGet("parent.child1.key", &value))
+ return 0;
+ if (strcmp(value, "value") != 0)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+/**
+ * Test that a configuration parameter loaded from YAML doesn't
+ * override a 'final' value that may be set on the command line.
+ */
+static int
+ConfYamlOverrideFinalTest(void)
+{
+ ConfCreateContextBackup();
+ ConfInit();
+
+ char config[] =
+ "%YAML 1.1\n"
+ "---\n"
+ "default-log-dir: /var/log\n";
+
+ /* Set the log directory as if it was set on the command line. */
+ if (!ConfSetFinal("default-log-dir", "/tmp"))
+ return 0;
+ if (ConfYamlLoadString(config, strlen(config)) != 0)
+ return 0;
+
+ char *default_log_dir;
+
+ if (!ConfGet("default-log-dir", &default_log_dir))
+ return 0;
+ if (strcmp(default_log_dir, "/tmp") != 0) {
+ fprintf(stderr, "final value was reassigned\n");
+ return 0;
+ }
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+#endif /* UNITTESTS */
+
+void
+ConfYamlRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ConfYamlSequenceTest", ConfYamlSequenceTest, 1);
+ UtRegisterTest("ConfYamlLoggingOutputTest", ConfYamlLoggingOutputTest, 1);
+ UtRegisterTest("ConfYamlNonYamlFileTest", ConfYamlNonYamlFileTest, 1);
+ UtRegisterTest("ConfYamlBadYamlVersionTest", ConfYamlBadYamlVersionTest, 1);
+ UtRegisterTest("ConfYamlSecondLevelSequenceTest",
+ ConfYamlSecondLevelSequenceTest, 1);
+ UtRegisterTest("ConfYamlFileIncludeTest", ConfYamlFileIncludeTest, 1);
+ UtRegisterTest("ConfYamlOverrideTest", ConfYamlOverrideTest, 1);
+ UtRegisterTest("ConfYamlOverrideFinalTest", ConfYamlOverrideFinalTest, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/conf-yaml-loader.h b/framework/src/suricata/src/conf-yaml-loader.h
new file mode 100644
index 00000000..6c599d0a
--- /dev/null
+++ b/framework/src/suricata/src/conf-yaml-loader.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com>
+ */
+
+#ifndef __CONF_YAML_LOADER_H__
+#define __CONF_YAML_LOADER_H__
+
+int ConfYamlLoadFile(const char *);
+int ConfYamlLoadString(const char *, size_t);
+int ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix);
+
+void ConfYamlRegisterTests(void);
+
+#endif /* !__CONF_YAML_LOADER_H__ */
diff --git a/framework/src/suricata/src/conf.c b/framework/src/suricata/src/conf.c
new file mode 100644
index 00000000..88b9389c
--- /dev/null
+++ b/framework/src/suricata/src/conf.c
@@ -0,0 +1,1532 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com>
+ *
+ * This file provides a basic configuration system for the IDPS
+ * engine.
+ *
+ * NOTE: Setting values should only be done from one thread during
+ * engine initialization. Multiple threads should be able access read
+ * configuration data. Allowing run time changes to the configuration
+ * will require some locks.
+ *
+ * \todo Consider having the in-memory configuration database a direct
+ * reflection of the configuration file and moving command line
+ * parameters to a primary lookup table?
+ *
+ * \todo Get rid of allow override and go with a simpler first set,
+ * stays approach?
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-path.h"
+
+/** Maximum size of a complete domain name. */
+#define NODE_NAME_MAX 1024
+
+static ConfNode *root = NULL;
+static ConfNode *root_backup = NULL;
+
+/**
+ * \brief Helper function to get a node, creating it if it does not
+ * exist.
+ *
+ * This function exits on memory failure as creating configuration
+ * nodes is usually part of application initialization.
+ *
+ * \param name The name of the configuration node to get.
+ * \param final Flag to set created nodes as final or not.
+ *
+ * \retval The existing configuration node if it exists, or a newly
+ * created node for the provided name. On error, NULL will be returned.
+ */
+static ConfNode *ConfGetNodeOrCreate(const char *name, int final)
+{
+ ConfNode *parent = root;
+ ConfNode *node = NULL;
+ char node_name[NODE_NAME_MAX];
+ char *key;
+ char *next;
+
+ if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
+ SCLogError(SC_ERR_CONF_NAME_TOO_LONG,
+ "Configuration name too long: %s", name);
+ return NULL;
+ }
+
+ key = node_name;
+
+ do {
+ if ((next = strchr(key, '.')) != NULL)
+ *next++ = '\0';
+ if ((node = ConfNodeLookupChild(parent, key)) == NULL) {
+ node = ConfNodeNew();
+ if (unlikely(node == NULL)) {
+ SCLogWarning(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for configuration.");
+ goto end;
+ }
+ node->name = SCStrdup(key);
+ if (unlikely(node->name == NULL)) {
+ ConfNodeFree(node);
+ node = NULL;
+ SCLogWarning(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for configuration.");
+ goto end;
+ }
+ node->parent = parent;
+ node->final = final;
+ TAILQ_INSERT_TAIL(&parent->head, node, next);
+ }
+ key = next;
+ parent = node;
+ } while (next != NULL);
+
+end:
+ return node;
+}
+
+/**
+ * \brief Initialize the configuration system.
+ */
+void ConfInit(void)
+{
+ if (root != NULL) {
+ SCLogDebug("already initialized");
+ return;
+ }
+ root = ConfNodeNew();
+ if (root == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "ERROR: Failed to allocate memory for root configuration node, "
+ "aborting.");
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("configuration module initialized");
+}
+
+/**
+ * \brief Allocate a new configuration node.
+ *
+ * \retval An allocated configuration node on success, NULL on failure.
+ */
+ConfNode *ConfNodeNew(void)
+{
+ ConfNode *new;
+
+ new = SCCalloc(1, sizeof(*new));
+ if (unlikely(new == NULL)) {
+ return NULL;
+ }
+ TAILQ_INIT(&new->head);
+
+ return new;
+}
+
+/**
+ * \brief Free a ConfNode and all of its children.
+ *
+ * \param node The configuration node to SCFree.
+ */
+void ConfNodeFree(ConfNode *node)
+{
+ ConfNode *tmp;
+
+ while ((tmp = TAILQ_FIRST(&node->head))) {
+ TAILQ_REMOVE(&node->head, tmp, next);
+ ConfNodeFree(tmp);
+ }
+
+ if (node->name != NULL)
+ SCFree(node->name);
+ if (node->val != NULL)
+ SCFree(node->val);
+ SCFree(node);
+}
+
+/**
+ * \brief Get a ConfNode by name.
+ *
+ * \param name The full name of the configuration node to lookup.
+ *
+ * \retval A pointer to ConfNode is found or NULL if the configuration
+ * node does not exist.
+ */
+ConfNode *ConfGetNode(const char *name)
+{
+ ConfNode *node = root;
+ char node_name[NODE_NAME_MAX];
+ char *key;
+ char *next;
+
+ if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
+ SCLogError(SC_ERR_CONF_NAME_TOO_LONG,
+ "Configuration name too long: %s", name);
+ return NULL;
+ }
+
+ key = node_name;
+ do {
+ if ((next = strchr(key, '.')) != NULL)
+ *next++ = '\0';
+ node = ConfNodeLookupChild(node, key);
+ key = next;
+ } while (next != NULL && node != NULL);
+
+ return node;
+}
+
+/**
+ * \brief Get the root configuration node.
+ */
+ConfNode *ConfGetRootNode(void)
+{
+ return root;
+}
+
+/**
+ * \brief Set a configuration value.
+ *
+ * Configuration values set with this function may be overridden by
+ * subsequent calls, or if the value appears multiple times in a
+ * configuration file.
+ *
+ * \param name The name of the configuration parameter to set.
+ * \param val The value of the configuration parameter.
+ *
+ * \retval 1 if the value was set otherwise 0.
+ */
+int ConfSet(const char *name, char *val)
+{
+ ConfNode *node = ConfGetNodeOrCreate(name, 0);
+ if (node == NULL || node->final) {
+ return 0;
+ }
+ if (node->val != NULL)
+ SCFree(node->val);
+ node->val = SCStrdup(val);
+ if (unlikely(node->val == NULL)) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * \brief Set a configuration parameter from a string.
+ *
+ * Where the input string is something like:
+ * stream.midstream=true
+ *
+ * \param input the input string to be parsed.
+ *
+ * \retval 1 if the value of set, otherwise 0.
+ */
+int ConfSetFromString(const char *input, int final)
+{
+ int retval = 0;
+ char *name = SCStrdup(input), *val = NULL;
+ if (unlikely(name == NULL)) {
+ goto done;
+ }
+ val = strchr(name, '=');
+ if (val == NULL) {
+ goto done;
+ }
+ *val++ = '\0';
+
+ while (isspace((int)name[strlen(name) - 1])) {
+ name[strlen(name) - 1] = '\0';
+ }
+
+ while (isspace((int)*val)) {
+ val++;
+ }
+
+ if (final) {
+ if (!ConfSetFinal(name, val)) {
+ goto done;
+ }
+ }
+ else {
+ if (!ConfSet(name, val)) {
+ goto done;
+ }
+ }
+
+ retval = 1;
+done:
+ if (name != NULL) {
+ SCFree(name);
+ }
+ return retval;
+}
+
+/**
+ * \brief Set a final configuration value.
+ *
+ * A final configuration value is a value that cannot be overridden by
+ * the configuration file. Its mainly useful for setting values that
+ * are supplied on the command line prior to the configuration file
+ * being loaded. However, a subsequent call to this function can
+ * override a previously set value.
+ *
+ * \param name The name of the configuration parameter to set.
+ * \param val The value of the configuration parameter.
+ *
+ * \retval 1 if the value was set otherwise 0.
+ */
+int ConfSetFinal(const char *name, char *val)
+{
+ ConfNode *node = ConfGetNodeOrCreate(name, 1);
+ if (node == NULL) {
+ return 0;
+ }
+ if (node->val != NULL)
+ SCFree(node->val);
+ node->val = SCStrdup(val);
+ if (unlikely(node->val == NULL)) {
+ return 0;
+ }
+ node->final = 1;
+ return 1;
+}
+
+/**
+ * \brief Retrieve the value of a configuration node.
+ *
+ * This function will return the value for a configuration node based
+ * on the full name of the node. It is possible that the value
+ * returned could be NULL, this could happen if the requested node
+ * does exist but is not a node that contains a value, but contains
+ * children ConfNodes instead.
+ *
+ * \param name Name of configuration parameter to get.
+ * \param vptr Pointer that will be set to the configuration value parameter.
+ * Note that this is just a reference to the actual value, not a copy.
+ *
+ * \retval 1 will be returned if the name is found, otherwise 0 will
+ * be returned.
+ */
+int ConfGet(const char *name, char **vptr)
+{
+ ConfNode *node = ConfGetNode(name);
+ if (node == NULL) {
+ SCLogDebug("failed to lookup configuration parameter '%s'", name);
+ return 0;
+ }
+ else {
+ *vptr = node->val;
+ return 1;
+ }
+}
+
+int ConfGetChildValue(const ConfNode *base, const char *name, char **vptr)
+{
+ ConfNode *node = ConfNodeLookupChild(base, name);
+
+ if (node == NULL) {
+ SCLogDebug("failed to lookup configuration parameter '%s'", name);
+ return 0;
+ }
+ else {
+ *vptr = node->val;
+ return 1;
+ }
+}
+
+
+int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt,
+ const char *name, char **vptr)
+{
+ int ret = ConfGetChildValue(base, name, vptr);
+ /* Get 'default' value */
+ if (ret == 0 && dflt) {
+ return ConfGetChildValue(dflt, name, vptr);
+ }
+ return ret;
+}
+
+/**
+ * \brief Retrieve a configuration value as an integer.
+ *
+ * \param name Name of configuration parameter to get.
+ * \param val Pointer to an intmax_t that will be set the
+ * configuration value.
+ *
+ * \retval 1 will be returned if the name is found and was properly
+ * converted to an interger, otherwise 0 will be returned.
+ */
+int ConfGetInt(const char *name, intmax_t *val)
+{
+ char *strval;
+ intmax_t tmpint;
+ char *endptr;
+
+ if (ConfGet(name, &strval) == 0)
+ return 0;
+
+ errno = 0;
+ tmpint = strtoimax(strval, &endptr, 0);
+ if (strval[0] == '\0' || *endptr != '\0')
+ return 0;
+ if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN))
+ return 0;
+
+ *val = tmpint;
+ return 1;
+}
+
+int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val)
+{
+ char *strval;
+ intmax_t tmpint;
+ char *endptr;
+
+ if (ConfGetChildValue(base, name, &strval) == 0)
+ return 0;
+ errno = 0;
+ tmpint = strtoimax(strval, &endptr, 0);
+ if (strval[0] == '\0' || *endptr != '\0')
+ return 0;
+ if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN))
+ return 0;
+
+ *val = tmpint;
+ return 1;
+
+}
+
+int ConfGetChildValueIntWithDefault(const ConfNode *base, const ConfNode *dflt,
+ const char *name, intmax_t *val)
+{
+ int ret = ConfGetChildValueInt(base, name, val);
+ /* Get 'default' value */
+ if (ret == 0 && dflt) {
+ return ConfGetChildValueInt(dflt, name, val);
+ }
+ return ret;
+}
+
+
+/**
+ * \brief Retrieve a configuration value as an boolen.
+ *
+ * \param name Name of configuration parameter to get.
+ * \param val Pointer to an int that will be set to 1 for true, or 0
+ * for false.
+ *
+ * \retval 1 will be returned if the name is found and was properly
+ * converted to a boolean, otherwise 0 will be returned.
+ */
+int ConfGetBool(const char *name, int *val)
+{
+ char *strval;
+
+ *val = 0;
+ if (ConfGet(name, &strval) != 1)
+ return 0;
+
+ *val = ConfValIsTrue(strval);
+
+ return 1;
+}
+
+int ConfGetChildValueBool(const ConfNode *base, const char *name, int *val)
+{
+ char *strval;
+
+ *val = 0;
+ if (ConfGetChildValue(base, name, &strval) == 0)
+ return 0;
+
+ *val = ConfValIsTrue(strval);
+
+ return 1;
+}
+
+int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt,
+ const char *name, int *val)
+{
+ int ret = ConfGetChildValueBool(base, name, val);
+ /* Get 'default' value */
+ if (ret == 0 && dflt) {
+ return ConfGetChildValueBool(dflt, name, val);
+ }
+ return ret;
+}
+
+
+/**
+ * \brief Check if a value is true.
+ *
+ * The value is considered true if it is a string with the value of 1,
+ * yes, true or on. The test is not case sensitive, any other value
+ * is false.
+ *
+ * \param val The string to test for a true value.
+ *
+ * \retval 1 If the value is true, 0 if not.
+ */
+int ConfValIsTrue(const char *val)
+{
+ char *trues[] = {"1", "yes", "true", "on"};
+ size_t u;
+
+ for (u = 0; u < sizeof(trues) / sizeof(trues[0]); u++) {
+ if (strcasecmp(val, trues[u]) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Check if a value is false.
+ *
+ * The value is considered false if it is a string with the value of 0,
+ * no, false or off. The test is not case sensitive, any other value
+ * is not false.
+ *
+ * \param val The string to test for a false value.
+ *
+ * \retval 1 If the value is false, 0 if not.
+ */
+int ConfValIsFalse(const char *val)
+{
+ char *falses[] = {"0", "no", "false", "off"};
+ size_t u;
+
+ for (u = 0; u < sizeof(falses) / sizeof(falses[0]); u++) {
+ if (strcasecmp(val, falses[u]) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Retrieve a configuration value as a double
+ *
+ * \param name Name of configuration parameter to get.
+ * \param val Pointer to an double that will be set the
+ * configuration value.
+ *
+ * \retval 1 will be returned if the name is found and was properly
+ * converted to a double, otherwise 0 will be returned.
+ */
+int ConfGetDouble(const char *name, double *val)
+{
+ char *strval;
+ double tmpdo;
+ char *endptr;
+
+ if (ConfGet(name, &strval) == 0)
+ return 0;
+
+ errno = 0;
+ tmpdo = strtod(strval, &endptr);
+ if (strval[0] == '\0' || *endptr != '\0')
+ return 0;
+ if (errno == ERANGE)
+ return 0;
+
+ *val = tmpdo;
+ return 1;
+}
+
+/**
+ * \brief Retrieve a configuration value as a float
+ *
+ * \param name Name of configuration parameter to get.
+ * \param val Pointer to an float that will be set the
+ * configuration value.
+ *
+ * \retval 1 will be returned if the name is found and was properly
+ * converted to a double, otherwise 0 will be returned.
+ */
+int ConfGetFloat(const char *name, float *val)
+{
+ char *strval;
+ double tmpfl;
+ char *endptr;
+
+ if (ConfGet(name, &strval) == 0)
+ return 0;
+
+ errno = 0;
+ tmpfl = strtof(strval, &endptr);
+ if (strval[0] == '\0' || *endptr != '\0')
+ return 0;
+ if (errno == ERANGE)
+ return 0;
+
+ *val = tmpfl;
+ return 1;
+}
+
+/**
+ * \brief Remove (and SCFree) the provided configuration node.
+ */
+void ConfNodeRemove(ConfNode *node)
+{
+ if (node->parent != NULL)
+ TAILQ_REMOVE(&node->parent->head, node, next);
+ ConfNodeFree(node);
+}
+
+/**
+ * \brief Remove a configuration parameter from the configuration db.
+ *
+ * \param name The name of the configuration parameter to remove.
+ *
+ * \retval Returns 1 if the parameter was removed, otherwise 0 is returned
+ * most likely indicating the parameter was not set.
+ */
+int ConfRemove(const char *name)
+{
+ ConfNode *node;
+
+ node = ConfGetNode(name);
+ if (node == NULL)
+ return 0;
+ else {
+ ConfNodeRemove(node);
+ return 1;
+ }
+}
+
+/**
+ * \brief Creates a backup of the conf_hash hash_table used by the conf API.
+ */
+void ConfCreateContextBackup(void)
+{
+ root_backup = root;
+ root = NULL;
+
+ return;
+}
+
+/**
+ * \brief Restores the backup of the hash_table present in backup_conf_hash
+ * back to conf_hash.
+ */
+void ConfRestoreContextBackup(void)
+{
+ root = root_backup;
+ root_backup = NULL;
+
+ return;
+}
+
+/**
+ * \brief De-initializes the configuration system.
+ */
+void ConfDeInit(void)
+{
+ if (root != NULL) {
+ ConfNodeFree(root);
+ root = NULL;
+ }
+
+ SCLogDebug("configuration module de-initialized");
+}
+
+static char *ConfPrintNameArray(char **name_arr, int level)
+{
+ static char name[128*128];
+ int i;
+
+ name[0] = '\0';
+ for (i = 0; i <= level; i++) {
+ strlcat(name, name_arr[i], sizeof(name));
+ if (i < level)
+ strlcat(name, ".", sizeof(name));
+ }
+
+ return name;
+}
+
+/**
+ * \brief Dump a configuration node and all its children.
+ */
+void ConfNodeDump(const ConfNode *node, const char *prefix)
+{
+ ConfNode *child;
+
+ static char *name[128];
+ static int level = -1;
+
+ level++;
+ TAILQ_FOREACH(child, &node->head, next) {
+ name[level] = SCStrdup(child->name);
+ if (unlikely(name[level] == NULL)) {
+ continue;
+ }
+ if (prefix == NULL) {
+ printf("%s = %s\n", ConfPrintNameArray(name, level),
+ child->val);
+ }
+ else {
+ printf("%s.%s = %s\n", prefix,
+ ConfPrintNameArray(name, level), child->val);
+ }
+ ConfNodeDump(child, prefix);
+ SCFree(name[level]);
+ }
+ level--;
+}
+
+/**
+ * \brief Dump configuration to stdout.
+ */
+void ConfDump(void)
+{
+ ConfNodeDump(root, NULL);
+}
+
+/**
+ * \brief Lookup a child configuration node by name.
+ *
+ * Given a ConfNode this function will lookup an immediate child
+ * ConfNode by name and return the child ConfNode.
+ *
+ * \param node The parent configuration node.
+ * \param name The name of the child node to lookup.
+ *
+ * \retval A pointer the child ConfNode if found otherwise NULL.
+ */
+ConfNode *ConfNodeLookupChild(const ConfNode *node, const char *name)
+{
+ ConfNode *child;
+
+ TAILQ_FOREACH(child, &node->head, next) {
+ if (strcmp(child->name, name) == 0)
+ return child;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Lookup the value of a child configuration node by name.
+ *
+ * Given a parent ConfNode this function will return the value of a
+ * child configuration node by name returning a reference to that
+ * value.
+ *
+ * \param node The parent configuration node.
+ * \param name The name of the child node to lookup.
+ *
+ * \retval A pointer the child ConfNodes value if found otherwise NULL.
+ */
+const char *ConfNodeLookupChildValue(const ConfNode *node, const char *name)
+{
+ ConfNode *child;
+
+ child = ConfNodeLookupChild(node, name);
+ if (child != NULL)
+ return child->val;
+
+ return NULL;
+}
+
+/**
+ * \brief Lookup for a key value under a specific node
+ *
+ * \return the ConfNode matching or NULL
+ */
+
+ConfNode *ConfNodeLookupKeyValue(const ConfNode *base, const char *key,
+ const char *value)
+{
+ ConfNode *child;
+
+ TAILQ_FOREACH(child, &base->head, next) {
+ if (!strncmp(child->val, key, strlen(child->val))) {
+ ConfNode *subchild;
+ TAILQ_FOREACH(subchild, &child->head, next) {
+ if ((!strcmp(subchild->name, key)) && (!strcmp(subchild->val, value))) {
+ return child;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Test if a configuration node has a true value.
+ *
+ * \param node The parent configuration node.
+ * \param name The name of the child node to test.
+ *
+ * \retval 1 if the child node has a true value, otherwise 0 is
+ * returned, even if the child node does not exist.
+ */
+int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
+{
+ const char *val;
+
+ val = ConfNodeLookupChildValue(node, key);
+
+ return val != NULL ? ConfValIsTrue(val) : 0;
+}
+
+/**
+ * \brief Create the path for an include entry
+ * \param file The name of the file
+ * \retval str Pointer to the string path + sig_file
+ */
+char *ConfLoadCompleteIncludePath(const char *file)
+{
+ char *defaultpath = NULL;
+ char *path = NULL;
+
+ /* Path not specified */
+ if (PathIsRelative(file)) {
+ if (ConfGet("include-path", &defaultpath) == 1) {
+ SCLogDebug("Default path: %s", defaultpath);
+ size_t path_len = sizeof(char) * (strlen(defaultpath) +
+ strlen(file) + 2);
+ path = SCMalloc(path_len);
+ if (unlikely(path == NULL))
+ return NULL;
+ strlcpy(path, defaultpath, path_len);
+ if (path[strlen(path) - 1] != '/')
+ strlcat(path, "/", path_len);
+ strlcat(path, file, path_len);
+ } else {
+ path = SCStrdup(file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ } else {
+ path = SCStrdup(file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ return path;
+}
+
+/**
+ * \brief Prune a configuration node.
+ *
+ * Pruning a configuration is similar to freeing, but only fields that
+ * may be overridden are, leaving final type parameters. Additional
+ * the value of the provided node is also free'd, but the node itself
+ * is left.
+ *
+ * \param node The configuration node to prune.
+ */
+void ConfNodePrune(ConfNode *node)
+{
+ ConfNode *item, *it;
+
+ for (item = TAILQ_FIRST(&node->head); item != NULL; item = it) {
+ it = TAILQ_NEXT(item, next);
+ if (!item->final) {
+ ConfNodePrune(item);
+ if (TAILQ_EMPTY(&item->head)) {
+ TAILQ_REMOVE(&node->head, item, next);
+ if (item->name != NULL)
+ SCFree(item->name);
+ if (item->val != NULL)
+ SCFree(item->val);
+ SCFree(item);
+ }
+ }
+ }
+
+ if (node->val != NULL) {
+ SCFree(node->val);
+ node->val = NULL;
+ }
+}
+
+/**
+ * \brief Check if a node is a sequence or node.
+ *
+ * \param node the node to check.
+ *
+ * \return 1 if node is a seuence, otherwise 0.
+ */
+int ConfNodeIsSequence(const ConfNode *node)
+{
+ return node->is_seq == 0 ? 0 : 1;
+}
+
+#ifdef UNITTESTS
+
+/**
+ * Lookup a non-existant value.
+ */
+static int ConfTestGetNonExistant(void)
+{
+ char name[] = "non-existant-value";
+ char *value;
+
+ return !ConfGet(name, &value);
+}
+
+/**
+ * Set then lookup a value.
+ */
+static int ConfTestSetAndGet(void)
+{
+ char name[] = "some-name";
+ char value[] = "some-value";
+ char *value0;
+
+ if (ConfSet(name, value) != 1)
+ return 0;
+ if (ConfGet(name, &value0) != 1)
+ return 0;
+ if (strcmp(value, value0) != 0)
+ return 0;
+
+ /* Cleanup. */
+ ConfRemove(name);
+
+ return 1;
+}
+
+/**
+ * Test that overriding a value is allowed provided allow_override is
+ * true and that the config parameter gets the new value.
+ */
+static int ConfTestOverrideValue1(void)
+{
+ char name[] = "some-name";
+ char value0[] = "some-value";
+ char value1[] = "new-value";
+ char *val;
+ int rc;
+
+ if (ConfSet(name, value0) != 1)
+ return 0;
+ if (ConfSet(name, value1) != 1)
+ return 0;
+ if (ConfGet(name, &val) != 1)
+ return 0;
+
+ rc = !strcmp(val, value1);
+
+ /* Cleanup. */
+ ConfRemove(name);
+
+ return rc;
+}
+
+/**
+ * Test that a final value will not be overrided by a ConfSet.
+ */
+static int ConfTestOverrideValue2(void)
+{
+ char name[] = "some-name";
+ char value0[] = "some-value";
+ char value1[] = "new-value";
+ char *val;
+ int rc;
+
+ if (ConfSetFinal(name, value0) != 1)
+ return 0;
+ if (ConfSet(name, value1) != 0)
+ return 0;
+ if (ConfGet(name, &val) != 1)
+ return 0;
+
+ rc = !strcmp(val, value0);
+
+ /* Cleanup. */
+ ConfRemove(name);
+
+ return rc;
+}
+
+/**
+ * Test retrieving an integer value from the configuration db.
+ */
+static int ConfTestGetInt(void)
+{
+ char name[] = "some-int.x";
+ intmax_t val;
+
+ if (ConfSet(name, "0") != 1)
+ return 0;
+ if (ConfGetInt(name, &val) != 1)
+ return 0;
+
+ if (val != 0)
+ return 0;
+
+ if (ConfSet(name, "-1") != 1)
+ return 0;
+ if (ConfGetInt(name, &val) != 1)
+ return 0;
+ if (val != -1)
+ return 0;
+
+ if (ConfSet(name, "0xffff") != 1)
+ return 0;
+ if (ConfGetInt(name, &val) != 1)
+ return 0;
+ if (val != 0xffff)
+ return 0;
+
+ if (ConfSet(name, "not-an-int") != 1)
+ return 0;
+ if (ConfGetInt(name, &val) != 0)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Test retrieving a boolean value from the configuration db.
+ */
+static int ConfTestGetBool(void)
+{
+ char name[] = "some-bool";
+ char *trues[] = {
+ "1",
+ "on", "ON",
+ "yes", "YeS",
+ "true", "TRUE",
+ };
+ char *falses[] = {
+ "0",
+ "something",
+ "off", "OFF",
+ "false", "FalSE",
+ "no", "NO",
+ };
+ int val;
+ size_t u;
+
+ for (u = 0; u < sizeof(trues) / sizeof(trues[0]); u++) {
+ if (ConfSet(name, trues[u]) != 1)
+ return 0;
+ if (ConfGetBool(name, &val) != 1)
+ return 0;
+ if (val != 1)
+ return 0;
+ }
+
+ for (u = 0; u < sizeof(falses) / sizeof(falses[0]); u++) {
+ if (ConfSet(name, falses[u]) != 1)
+ return 0;
+ if (ConfGetBool(name, &val) != 1)
+ return 0;
+ if (val != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ConfNodeLookupChildTest(void)
+{
+ char *test_vals[] = { "one", "two", "three" };
+ size_t u;
+
+ ConfNode *parent = ConfNodeNew();
+ ConfNode *child;
+
+ for (u = 0; u < sizeof(test_vals)/sizeof(test_vals[0]); u++) {
+ child = ConfNodeNew();
+ child->name = SCStrdup(test_vals[u]);
+ child->val = SCStrdup(test_vals[u]);
+ TAILQ_INSERT_TAIL(&parent->head, child, next);
+ }
+
+ child = ConfNodeLookupChild(parent, "one");
+ if (child == NULL)
+ return 0;
+ if (strcmp(child->name, "one") != 0)
+ return 0;
+ if (strcmp(child->val, "one") != 0)
+ return 0;
+
+ child = ConfNodeLookupChild(parent, "two");
+ if (child == NULL)
+ return 0;
+ if (strcmp(child->name, "two") != 0)
+ return 0;
+ if (strcmp(child->val, "two") != 0)
+ return 0;
+
+ child = ConfNodeLookupChild(parent, "three");
+ if (child == NULL)
+ return 0;
+ if (strcmp(child->name, "three") != 0)
+ return 0;
+ if (strcmp(child->val, "three") != 0)
+ return 0;
+
+ child = ConfNodeLookupChild(parent, "four");
+ if (child != NULL)
+ return 0;
+
+ ConfNodeFree(parent);
+
+ return 1;
+}
+
+static int ConfNodeLookupChildValueTest(void)
+{
+ char *test_vals[] = { "one", "two", "three" };
+ size_t u;
+
+ ConfNode *parent = ConfNodeNew();
+ ConfNode *child;
+ const char *value;
+
+ for (u = 0; u < sizeof(test_vals)/sizeof(test_vals[0]); u++) {
+ child = ConfNodeNew();
+ child->name = SCStrdup(test_vals[u]);
+ child->val = SCStrdup(test_vals[u]);
+ TAILQ_INSERT_TAIL(&parent->head, child, next);
+ }
+
+ value = (char *)ConfNodeLookupChildValue(parent, "one");
+ if (value == NULL)
+ return 0;
+ if (strcmp(value, "one") != 0)
+ return 0;
+
+ value = (char *)ConfNodeLookupChildValue(parent, "two");
+ if (value == NULL)
+ return 0;
+ if (strcmp(value, "two") != 0)
+ return 0;
+
+ value = (char *)ConfNodeLookupChildValue(parent, "three");
+ if (value == NULL)
+ return 0;
+ if (strcmp(value, "three") != 0)
+ return 0;
+
+ value = (char *)ConfNodeLookupChildValue(parent, "four");
+ if (value != NULL)
+ return 0;
+
+ ConfNodeFree(parent);
+
+ return 1;
+}
+
+static int ConfGetChildValueWithDefaultTest(void)
+{
+ char *val = "";
+ int ret = 1;
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfSet("af-packet.0.interface", "eth0");
+ ConfSet("af-packet.1.interface", "default");
+ ConfSet("af-packet.1.cluster-type", "cluster_cpu");
+
+ ConfNode *root = ConfGetNode("af-packet.0");
+ ConfNode *dflt = ConfGetNode("af-packet.1");
+ ConfGetChildValueWithDefault(root, dflt, "cluster-type", &val);
+ if (strcmp(val, "cluster_cpu")) {
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return 0;
+ }
+
+ ConfSet("af-packet.0.cluster-type", "cluster_flow");
+ ConfGetChildValueWithDefault(root, dflt, "cluster-type", &val);
+
+ if (strcmp(val, "cluster_flow")) {
+ ret = 0;
+ }
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return ret;
+}
+
+static int ConfGetChildValueIntWithDefaultTest(void)
+{
+ intmax_t val = 0;
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfSet("af-packet.0.interface", "eth0");
+ ConfSet("af-packet.1.interface", "default");
+ ConfSet("af-packet.1.threads", "2");
+
+ ConfNode *root = ConfGetNode("af-packet.0");
+ ConfNode *dflt = ConfGetNode("af-packet.1");
+ ConfGetChildValueIntWithDefault(root, dflt, "threads", &val);
+ if (val != 2) {
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return 0;
+ }
+
+ ConfSet("af-packet.0.threads", "1");
+ ConfGetChildValueIntWithDefault(root, dflt, "threads", &val);
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ if (val != 1) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ConfGetChildValueBoolWithDefaultTest(void)
+{
+ int val;
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfSet("af-packet.0.interface", "eth0");
+ ConfSet("af-packet.1.interface", "default");
+ ConfSet("af-packet.1.use-mmap", "yes");
+
+ ConfNode *root = ConfGetNode("af-packet.0");
+ ConfNode *dflt = ConfGetNode("af-packet.1");
+ ConfGetChildValueBoolWithDefault(root, dflt, "use-mmap", &val);
+ if (val == 0) {
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return 0;
+ }
+
+ ConfSet("af-packet.0.use-mmap", "no");
+ ConfGetChildValueBoolWithDefault(root, dflt, "use-mmap", &val);
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ if (val) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Test the removal of a configuration node.
+ */
+static int ConfNodeRemoveTest(void)
+{
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (ConfSet("some.nested.parameter", "blah") != 1)
+ return 0;
+
+ ConfNode *node = ConfGetNode("some.nested.parameter");
+ if (node == NULL)
+ return 0;
+ ConfNodeRemove(node);
+
+ node = ConfGetNode("some.nested.parameter");
+ if (node != NULL)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+static int ConfSetTest(void)
+{
+ ConfCreateContextBackup();
+ ConfInit();
+
+ /* Set some value with 2 levels. */
+ if (ConfSet("one.two", "three") != 1)
+ return 0;
+ ConfNode *n = ConfGetNode("one.two");
+ if (n == NULL)
+ return 0;
+
+ /* Set another 2 level parameter with the same first level, this
+ * used to trigger a bug that caused the second level of the name
+ * to become a first level node. */
+ if (ConfSet("one.three", "four") != 1)
+ return 0;
+
+ n = ConfGetNode("one.three");
+ if (n == NULL)
+ return 0;
+
+ /* A top level node of "three" should not exist. */
+ n = ConfGetNode("three");
+ if (n != NULL)
+ return 0;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return 1;
+}
+
+static int ConfGetNodeOrCreateTest(void)
+{
+ ConfNode *node;
+ int ret = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ /* Get a node that should not exist, give it a value, re-get it
+ * and make sure the second time it returns the existing node. */
+ node = ConfGetNodeOrCreate("node0", 0);
+ if (node == NULL) {
+ fprintf(stderr, "returned null\n");
+ goto end;
+ }
+ if (node->parent == NULL || node->parent != root) {
+ fprintf(stderr, "unexpected parent node\n");
+ goto end;
+ }
+ if (node->val != NULL) {
+ fprintf(stderr, "node already existed\n");
+ goto end;
+ }
+ node->val = SCStrdup("node0");
+ node = ConfGetNodeOrCreate("node0", 0);
+ if (node == NULL) {
+ fprintf(stderr, "returned null\n");
+ goto end;
+ }
+ if (node->val == NULL) {
+ fprintf(stderr, "new node was allocated\n");
+ goto end;
+ }
+ if (strcmp(node->val, "node0") != 0) {
+ fprintf(stderr, "node did not have expected value\n");
+ goto end;
+ }
+
+ /* Do the same, but for something deeply nested. */
+ node = ConfGetNodeOrCreate("parent.child.grandchild", 0);
+ if (node == NULL) {
+ fprintf(stderr, "returned null\n");
+ goto end;
+ }
+ if (node->parent == NULL || node->parent == root) {
+ fprintf(stderr, "unexpected parent node\n");
+ goto end;
+ }
+ if (node->val != NULL) {
+ fprintf(stderr, "node already existed\n");
+ goto end;
+ }
+ node->val = SCStrdup("parent.child.grandchild");
+ node = ConfGetNodeOrCreate("parent.child.grandchild", 0);
+ if (node == NULL) {
+ fprintf(stderr, "returned null\n");
+ goto end;
+ }
+ if (node->val == NULL) {
+ fprintf(stderr, "new node was allocated\n");
+ goto end;
+ }
+ if (strcmp(node->val, "parent.child.grandchild") != 0) {
+ fprintf(stderr, "node did not have expected value\n");
+ goto end;
+ }
+
+ /* Test that 2 child nodes have the same root. */
+ ConfNode *child1 = ConfGetNodeOrCreate("parent.kids.child1", 0);
+ ConfNode *child2 = ConfGetNodeOrCreate("parent.kids.child2", 0);
+ if (child1 == NULL || child2 == NULL) {
+ fprintf(stderr, "returned null\n");
+ goto end;
+ }
+ if (child1->parent != child2->parent) {
+ fprintf(stderr, "child nodes have different parents\n");
+ goto end;
+ }
+ if (strcmp(child1->parent->name, "kids") != 0) {
+ fprintf(stderr, "parent node had unexpected name\n");
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return ret;
+}
+
+static int ConfNodePruneTest(void)
+{
+ int ret = 0;
+ ConfNode *node;
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ /* Test that final nodes exist after a prune. */
+ if (ConfSet("node.notfinal", "notfinal") != 1)
+ goto end;
+ if (ConfSetFinal("node.final", "final") != 1)
+ goto end;
+ if (ConfGetNode("node.notfinal") == NULL)
+ goto end;
+ if (ConfGetNode("node.final") == NULL)
+ goto end;
+ if ((node = ConfGetNode("node")) == NULL)
+ goto end;
+ ConfNodePrune(node);
+ if (ConfGetNode("node.notfinal") != NULL)
+ goto end;
+ if (ConfGetNode("node.final") == NULL)
+ goto end;
+
+ /* Test that everything under a final node exists after a prune. */
+ if (ConfSet("node.final.one", "one") != 1)
+ goto end;
+ if (ConfSet("node.final.two", "two") != 1)
+ goto end;
+ ConfNodePrune(node);
+ if (ConfNodeLookupChild(node, "final") == NULL)
+ goto end;
+ if (ConfGetNode("node.final.one") == NULL)
+ goto end;
+ if (ConfGetNode("node.final.two") == NULL)
+ goto end;
+
+ ret = 1;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return ret;
+}
+
+int ConfNodeIsSequenceTest(void)
+{
+ int retval = 0;
+ ConfNode *node = ConfNodeNew();
+ if (node == NULL) {
+ goto end;
+ }
+ if (ConfNodeIsSequence(node)) {
+ goto end;
+ }
+ node->is_seq = 1;
+ if (!ConfNodeIsSequence(node)) {
+ goto end;
+ }
+
+ retval = 1;
+
+end:
+ if (node != NULL) {
+ ConfNodeFree(node);
+ }
+ return retval;
+}
+
+static int ConfSetFromStringTest(void)
+{
+ int retval = 0;
+ ConfNode *n;
+
+ ConfCreateContextBackup();
+ ConfInit();
+
+ if (!ConfSetFromString("stream.midstream=true", 0)) {
+ goto end;
+ }
+ n = ConfGetNode("stream.midstream");
+ if (n == NULL) {
+ goto end;
+ }
+ if (n->val == NULL || strcmp("true", n->val)) {
+ goto end;
+ }
+
+ if (!ConfSetFromString("stream.midstream =false", 0)) {
+ goto end;
+ }
+ n = ConfGetNode("stream.midstream");
+ if (n == NULL) {
+ goto end;
+ }
+ if (n->val == NULL || strcmp("false", n->val)) {
+ goto end;
+ }
+
+ if (!ConfSetFromString("stream.midstream= true", 0)) {
+ goto end;
+ }
+ n = ConfGetNode("stream.midstream");
+ if (n == NULL) {
+ goto end;
+ }
+ if (n->val == NULL || strcmp("true", n->val)) {
+ goto end;
+ }
+
+ if (!ConfSetFromString("stream.midstream = false", 0)) {
+ goto end;
+ }
+ n = ConfGetNode("stream.midstream");
+ if (n == NULL) {
+ goto end;
+ }
+ if (n->val == NULL || strcmp("false", n->val)) {
+ goto end;
+ }
+
+ retval = 1;
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return retval;
+}
+
+void ConfRegisterTests(void)
+{
+ UtRegisterTest("ConfTestGetNonExistant", ConfTestGetNonExistant, 1);
+ UtRegisterTest("ConfSetTest", ConfSetTest, 1);
+ UtRegisterTest("ConfTestSetAndGet", ConfTestSetAndGet, 1);
+ UtRegisterTest("ConfTestOverrideValue1", ConfTestOverrideValue1, 1);
+ UtRegisterTest("ConfTestOverrideValue2", ConfTestOverrideValue2, 1);
+ UtRegisterTest("ConfTestGetInt", ConfTestGetInt, 1);
+ UtRegisterTest("ConfTestGetBool", ConfTestGetBool, 1);
+ UtRegisterTest("ConfNodeLookupChildTest", ConfNodeLookupChildTest, 1);
+ UtRegisterTest("ConfNodeLookupChildValueTest", ConfNodeLookupChildValueTest, 1);
+ UtRegisterTest("ConfNodeRemoveTest", ConfNodeRemoveTest, 1);
+ UtRegisterTest("ConfGetChildValueWithDefaultTest", ConfGetChildValueWithDefaultTest, 1);
+ UtRegisterTest("ConfGetChildValueIntWithDefaultTest", ConfGetChildValueIntWithDefaultTest, 1);
+ UtRegisterTest("ConfGetChildValueBoolWithDefaultTest", ConfGetChildValueBoolWithDefaultTest, 1);
+ UtRegisterTest("ConfGetNodeOrCreateTest", ConfGetNodeOrCreateTest, 1);
+ UtRegisterTest("ConfNodePruneTest", ConfNodePruneTest, 1);
+ UtRegisterTest("ConfNodeIsSequenceTest", ConfNodeIsSequenceTest, 1);
+ UtRegisterTest("ConfSetFromStringTest", ConfSetFromStringTest, 1);
+}
+
+#endif /* UNITTESTS */
diff --git a/framework/src/suricata/src/conf.h b/framework/src/suricata/src/conf.h
new file mode 100644
index 00000000..2318580a
--- /dev/null
+++ b/framework/src/suricata/src/conf.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com>
+ */
+
+#ifndef __CONF_H__
+#define __CONF_H__
+
+#include "queue.h"
+
+/**
+ * Structure of a configuration parameter.
+ */
+typedef struct ConfNode_ {
+ char *name;
+ char *val;
+
+ int is_seq;
+
+ /**< Flag that sets this nodes value as final. */
+ int final;
+
+ struct ConfNode_ *parent;
+ TAILQ_HEAD(, ConfNode_) head;
+ TAILQ_ENTRY(ConfNode_) next;
+} ConfNode;
+
+
+/**
+ * The default log directory.
+ */
+#ifdef OS_WIN32
+#define DEFAULT_LOG_DIR "C:\\WINDOWS\\Temp"
+#else
+#define DEFAULT_LOG_DIR "/var/log/suricata"
+#endif /* OS_WIN32 */
+
+void ConfInit(void);
+void ConfDeInit(void);
+ConfNode *ConfGetRootNode(void);
+int ConfGet(const char *name, char **vptr);
+int ConfGetInt(const char *name, intmax_t *val);
+int ConfGetBool(const char *name, int *val);
+int ConfGetDouble(const char *name, double *val);
+int ConfGetFloat(const char *name, float *val);
+int ConfSet(const char *name, char *val);
+int ConfSetFromString(const char *input, int final);
+int ConfSetFinal(const char *name, char *val);
+void ConfDump(void);
+void ConfNodeDump(const ConfNode *node, const char *prefix);
+ConfNode *ConfNodeNew(void);
+void ConfNodeFree(ConfNode *);
+ConfNode *ConfGetNode(const char *key);
+void ConfCreateContextBackup(void);
+void ConfRestoreContextBackup(void);
+ConfNode *ConfNodeLookupChild(const ConfNode *node, const char *key);
+const char *ConfNodeLookupChildValue(const ConfNode *node, const char *key);
+void ConfNodeRemove(ConfNode *);
+void ConfRegisterTests();
+int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key);
+int ConfValIsTrue(const char *val);
+int ConfValIsFalse(const char *val);
+void ConfNodePrune(ConfNode *node);
+
+ConfNode *ConfNodeLookupKeyValue(const ConfNode *base, const char *key, const char *value);
+int ConfGetChildValue(const ConfNode *base, const char *name, char **vptr);
+int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val);
+int ConfGetChildValueBool(const ConfNode *base, const char *name, int *val);
+int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, char **vptr);
+int ConfGetChildValueIntWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, intmax_t *val);
+int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, int *val);
+char *ConfLoadCompleteIncludePath(const char *);
+int ConfNodeIsSequence(const ConfNode *node);
+
+#endif /* ! __CONF_H__ */
diff --git a/framework/src/suricata/src/counters.c b/framework/src/suricata/src/counters.c
new file mode 100644
index 00000000..887fd7ca
--- /dev/null
+++ b/framework/src/suricata/src/counters.c
@@ -0,0 +1,1500 @@
+/* Copyright (C) 2007-2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Engine stats API
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "counters.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "util-time.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-privs.h"
+#include "util-signal.h"
+#include "unix-manager.h"
+#include "output.h"
+
+/* Time interval for syncing the local counters with the global ones */
+#define STATS_WUT_TTS 3
+
+/* Time interval at which the mgmt thread o/p the stats */
+#define STATS_MGMTT_TTS 8
+
+/**
+ * \brief Different kinds of qualifier that can be used to modify the behaviour
+ * of the counter to be registered
+ */
+enum {
+ STATS_TYPE_NORMAL = 1,
+ STATS_TYPE_AVERAGE = 2,
+ STATS_TYPE_MAXIMUM = 3,
+ STATS_TYPE_FUNC = 4,
+
+ STATS_TYPE_MAX = 5,
+};
+
+/**
+ * \brief per thread store of counters
+ */
+typedef struct StatsThreadStore_ {
+ /** thread name used in output */
+ const char *name;
+
+ StatsPublicThreadContext *ctx;
+
+ StatsPublicThreadContext **head;
+ uint32_t size;
+
+ struct StatsThreadStore_ *next;
+} StatsThreadStore;
+
+/**
+ * \brief Holds the output interface context for the counter api
+ */
+typedef struct StatsGlobalContext_ {
+ /** list of thread stores: one per thread plus one global */
+ StatsThreadStore *sts;
+ SCMutex sts_lock;
+ int sts_cnt;
+
+ HashTable *counters_id_hash;
+
+ StatsPublicThreadContext global_counter_ctx;
+} StatsGlobalContext;
+
+static void *stats_thread_data = NULL;
+static StatsGlobalContext *stats_ctx = NULL;
+static time_t stats_start_time;
+/** refresh interval in seconds */
+static uint32_t stats_tts = STATS_MGMTT_TTS;
+/** is the stats counter enabled? */
+static char stats_enabled = TRUE;
+
+static int StatsOutput(ThreadVars *tv);
+static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *);
+void StatsReleaseCounters(StatsCounter *head);
+
+/** stats table is filled each interval and passed to the
+ * loggers. Initialized at first use. */
+static StatsTable stats_table = { NULL, NULL, 0, 0, 0, {0 , 0}};
+
+static uint16_t counters_global_id = 0;
+
+static void StatsPublicThreadContextInit(StatsPublicThreadContext *t)
+{
+ SCMutexInit(&t->m, NULL);
+}
+
+static void StatsPublicThreadContextCleanup(StatsPublicThreadContext *t)
+{
+ SCMutexLock(&t->m);
+ StatsReleaseCounters(t->head);
+ t->head = NULL;
+ t->perf_flag = 0;
+ t->curr_id = 0;
+ SCMutexUnlock(&t->m);
+ SCMutexDestroy(&t->m);
+}
+
+/**
+ * \brief Adds a value of type uint64_t to the local counter.
+ *
+ * \param id ID of the counter as set by the API
+ * \param pca Counter array that holds the local counter for this TM
+ * \param x Value to add to this local counter
+ */
+void StatsAddUI64(ThreadVars *tv, uint16_t id, uint64_t x)
+{
+ StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
+#ifdef UNITTESTS
+ if (pca->initialized == 0)
+ return;
+#endif
+#ifdef DEBUG
+ BUG_ON ((id < 1) || (id > pca->size));
+#endif
+ pca->head[id].value += x;
+ pca->head[id].updates++;
+ return;
+}
+
+/**
+ * \brief Increments the local counter
+ *
+ * \param id Index of the counter in the counter array
+ * \param pca Counter array that holds the local counters for this TM
+ */
+void StatsIncr(ThreadVars *tv, uint16_t id)
+{
+ StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
+#ifdef UNITTESTS
+ if (pca->initialized == 0)
+ return;
+#endif
+#ifdef DEBUG
+ BUG_ON ((id < 1) || (id > pca->size));
+#endif
+ pca->head[id].value++;
+ pca->head[id].updates++;
+ return;
+}
+
+/**
+ * \brief Sets a value of type double to the local counter
+ *
+ * \param id Index of the local counter in the counter array
+ * \param pca Pointer to the StatsPrivateThreadContext
+ * \param x The value to set for the counter
+ */
+void StatsSetUI64(ThreadVars *tv, uint16_t id, uint64_t x)
+{
+ StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
+#ifdef UNITTESTS
+ if (pca->initialized == 0)
+ return;
+#endif
+#ifdef DEBUG
+ BUG_ON ((id < 1) || (id > pca->size));
+#endif
+
+ if ((pca->head[id].pc->type == STATS_TYPE_MAXIMUM) &&
+ (x > pca->head[id].value)) {
+ pca->head[id].value = x;
+ } else if (pca->head[id].pc->type == STATS_TYPE_NORMAL) {
+ pca->head[id].value = x;
+ }
+
+ pca->head[id].updates++;
+
+ return;
+}
+
+static ConfNode *GetConfig(void) {
+ ConfNode *stats = ConfGetNode("stats");
+ if (stats != NULL)
+ return stats;
+
+ ConfNode *root = ConfGetNode("outputs");
+ ConfNode *node = NULL;
+ if (root != NULL) {
+ TAILQ_FOREACH(node, &root->head, next) {
+ if (strcmp(node->val, "stats") == 0) {
+ return node->head.tqh_first;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * \brief Initializes stats context
+ */
+static void StatsInitCtx(void)
+{
+ SCEnter();
+ ConfNode *stats = GetConfig();
+ if (stats != NULL) {
+ const char *enabled = ConfNodeLookupChildValue(stats, "enabled");
+ if (enabled != NULL && ConfValIsFalse(enabled)) {
+ stats_enabled = FALSE;
+ SCLogDebug("Stats module has been disabled");
+ SCReturn;
+ }
+ const char *interval = ConfNodeLookupChildValue(stats, "interval");
+ if (interval != NULL)
+ stats_tts = (uint32_t) atoi(interval);
+ }
+
+ if (!OutputStatsLoggersRegistered()) {
+ SCLogWarning(SC_WARN_NO_STATS_LOGGERS, "stats are enabled but no loggers are active");
+ stats_enabled = FALSE;
+ SCReturn;
+ }
+
+ /* Store the engine start time */
+ time(&stats_start_time);
+
+ /* init the lock used by StatsThreadStore */
+ if (SCMutexInit(&stats_ctx->sts_lock, NULL) != 0) {
+ SCLogError(SC_ERR_INITIALIZATION, "error initializing sts mutex");
+ exit(EXIT_FAILURE);
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Releases the resources alloted to the output context of the
+ * Stats API
+ */
+static void StatsReleaseCtx()
+{
+ if (stats_ctx == NULL) {
+ SCLogDebug("Counter module has been disabled");
+ return;
+ }
+
+ StatsThreadStore *sts = NULL;
+ StatsThreadStore *temp = NULL;
+ sts = stats_ctx->sts;
+
+ while (sts != NULL) {
+ if (sts->head != NULL)
+ SCFree(sts->head);
+
+ temp = sts->next;
+ SCFree(sts);
+ sts = temp;
+ }
+
+ if (stats_ctx->counters_id_hash != NULL) {
+ HashTableFree(stats_ctx->counters_id_hash);
+ stats_ctx->counters_id_hash = NULL;
+ }
+
+ StatsPublicThreadContextCleanup(&stats_ctx->global_counter_ctx);
+ SCFree(stats_ctx);
+ stats_ctx = NULL;
+
+ /* free stats table */
+ if (stats_table.tstats != NULL) {
+ SCFree(stats_table.tstats);
+ stats_table.tstats = NULL;
+ }
+
+ if (stats_table.stats != NULL) {
+ SCFree(stats_table.stats);
+ stats_table.stats = NULL;
+ }
+ memset(&stats_table, 0, sizeof(stats_table));
+
+ return;
+}
+
+/**
+ * \brief management thread. This thread is responsible for writing the stats
+ *
+ * \param arg thread var
+ *
+ * \retval NULL This is the value that is always returned
+ */
+static void *StatsMgmtThread(void *arg)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ ThreadVars *tv_local = (ThreadVars *)arg;
+ uint8_t run = 1;
+ struct timespec cond_time;
+
+ /* Set the thread name */
+ if (SCSetThreadName(tv_local->name) < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ if (tv_local->thread_setup_flags != 0)
+ TmThreadSetupOptions(tv_local);
+
+ /* Set the threads capability */
+ tv_local->cap_flags = 0;
+
+ SCDropCaps(tv_local);
+
+ if (stats_ctx == NULL) {
+ SCLogError(SC_ERR_STATS_NOT_INIT, "Stats API not init"
+ "StatsInitCounterApi() has to be called first");
+ TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
+ return NULL;
+ }
+
+ TmModule *tm = &tmm_modules[TMM_STATSLOGGER];
+ BUG_ON(tm->ThreadInit == NULL);
+ int r = tm->ThreadInit(tv_local, NULL, &stats_thread_data);
+ if (r != 0 || stats_thread_data == NULL) {
+ SCLogError(SC_ERR_THREAD_INIT, "Stats API "
+ "ThreadInit failed");
+ TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
+ return NULL;
+ }
+ SCLogDebug("stats_thread_data %p", &stats_thread_data);
+
+ TmThreadsSetFlag(tv_local, THV_INIT_DONE);
+ while (run) {
+ if (TmThreadsCheckFlag(tv_local, THV_PAUSE)) {
+ TmThreadsSetFlag(tv_local, THV_PAUSED);
+ TmThreadTestThreadUnPaused(tv_local);
+ TmThreadsUnsetFlag(tv_local, THV_PAUSED);
+ }
+
+ cond_time.tv_sec = time(NULL) + stats_tts;
+ cond_time.tv_nsec = 0;
+
+ /* wait for the set time, or until we are woken up by
+ * the shutdown procedure */
+ SCCtrlMutexLock(tv_local->ctrl_mutex);
+ SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
+ SCCtrlMutexUnlock(tv_local->ctrl_mutex);
+
+ StatsOutput(tv_local);
+
+ if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
+ run = 0;
+ }
+ }
+
+ TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv_local, THV_DEINIT);
+
+ r = tm->ThreadDeinit(tv_local, stats_thread_data);
+ if (r != TM_ECODE_OK) {
+ SCLogError(SC_ERR_THREAD_DEINIT, "Stats Counter API "
+ "ThreadDeinit failed");
+ }
+
+ TmThreadsSetFlag(tv_local, THV_CLOSED);
+ return NULL;
+}
+
+/**
+ * \brief Wake up thread. This thread wakes up every TTS(time to sleep) seconds
+ * and sets the flag for every ThreadVars' StatsPublicThreadContext
+ *
+ * \param arg is NULL always
+ *
+ * \retval NULL This is the value that is always returned
+ */
+static void *StatsWakeupThread(void *arg)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ ThreadVars *tv_local = (ThreadVars *)arg;
+ uint8_t run = 1;
+ ThreadVars *tv = NULL;
+ PacketQueue *q = NULL;
+ struct timespec cond_time;
+
+ /* Set the thread name */
+ if (SCSetThreadName(tv_local->name) < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ if (tv_local->thread_setup_flags != 0)
+ TmThreadSetupOptions(tv_local);
+
+ /* Set the threads capability */
+ tv_local->cap_flags = 0;
+
+ SCDropCaps(tv_local);
+
+ if (stats_ctx == NULL) {
+ SCLogError(SC_ERR_STATS_NOT_INIT, "Stats API not init"
+ "StatsInitCounterApi() has to be called first");
+ TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
+ return NULL;
+ }
+
+ TmThreadsSetFlag(tv_local, THV_INIT_DONE);
+ while (run) {
+ if (TmThreadsCheckFlag(tv_local, THV_PAUSE)) {
+ TmThreadsSetFlag(tv_local, THV_PAUSED);
+ TmThreadTestThreadUnPaused(tv_local);
+ TmThreadsUnsetFlag(tv_local, THV_PAUSED);
+ }
+
+ cond_time.tv_sec = time(NULL) + STATS_WUT_TTS;
+ cond_time.tv_nsec = 0;
+
+ /* wait for the set time, or until we are woken up by
+ * the shutdown procedure */
+ SCCtrlMutexLock(tv_local->ctrl_mutex);
+ SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
+ SCCtrlMutexUnlock(tv_local->ctrl_mutex);
+
+ tv = tv_root[TVT_PPT];
+ while (tv != NULL) {
+ if (tv->perf_public_ctx.head == NULL) {
+ tv = tv->next;
+ continue;
+ }
+
+ /* assuming the assignment of an int to be atomic, and even if it's
+ * not, it should be okay */
+ tv->perf_public_ctx.perf_flag = 1;
+
+ if (tv->inq != NULL) {
+ q = &trans_q[tv->inq->id];
+ SCCondSignal(&q->cond_q);
+ }
+
+ tv = tv->next;
+ }
+
+ /* mgt threads for flow manager */
+ tv = tv_root[TVT_MGMT];
+ while (tv != NULL) {
+ if (tv->perf_public_ctx.head == NULL) {
+ tv = tv->next;
+ continue;
+ }
+
+ /* assuming the assignment of an int to be atomic, and even if it's
+ * not, it should be okay */
+ tv->perf_public_ctx.perf_flag = 1;
+
+ tv = tv->next;
+ }
+
+ if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
+ run = 0;
+ }
+ }
+
+ TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv_local, THV_DEINIT);
+
+ TmThreadsSetFlag(tv_local, THV_CLOSED);
+ return NULL;
+}
+
+/**
+ * \brief Releases a counter
+ *
+ * \param pc Pointer to the StatsCounter to be freed
+ */
+static void StatsReleaseCounter(StatsCounter *pc)
+{
+ if (pc != NULL) {
+ SCFree(pc);
+ }
+
+ return;
+}
+
+/**
+ * \brief Registers a counter.
+ *
+ * \param name Name of the counter, to be registered
+ * \param tm_name Thread module to which this counter belongs
+ * \param pctx StatsPublicThreadContext for this tm-tv instance
+ * \param type_q Qualifier describing the type of counter to be registered
+ *
+ * \retval the counter id for the newly registered counter, or the already
+ * present counter on success
+ * \retval 0 on failure
+ */
+static uint16_t StatsRegisterQualifiedCounter(char *name, char *tm_name,
+ StatsPublicThreadContext *pctx,
+ int type_q, uint64_t (*Func)(void))
+{
+ StatsCounter **head = &pctx->head;
+ StatsCounter *temp = NULL;
+ StatsCounter *prev = NULL;
+ StatsCounter *pc = NULL;
+
+ if (name == NULL || pctx == NULL) {
+ SCLogDebug("Counter name, StatsPublicThreadContext NULL");
+ return 0;
+ }
+
+ temp = prev = *head;
+ while (temp != NULL) {
+ prev = temp;
+
+ if (strcmp(name, temp->name) == 0) {
+ break;
+ }
+
+ temp = temp->next;
+ }
+
+ /* We already have a counter registered by this name */
+ if (temp != NULL)
+ return(temp->id);
+
+ /* if we reach this point we don't have a counter registered by this name */
+ if ( (pc = SCMalloc(sizeof(StatsCounter))) == NULL)
+ return 0;
+ memset(pc, 0, sizeof(StatsCounter));
+
+ /* assign a unique id to this StatsCounter. The id is local to this
+ * thread context. Please note that the id start from 1, and not 0 */
+ pc->id = ++(pctx->curr_id);
+ pc->name = name;
+ pc->type = type_q;
+ pc->Func = Func;
+
+ /* we now add the counter to the list */
+ if (prev == NULL)
+ *head = pc;
+ else
+ prev->next = pc;
+
+ return pc->id;
+}
+
+/**
+ * \brief Copies the StatsCounter value from the local counter present in the
+ * StatsPrivateThreadContext to its corresponding global counterpart. Used
+ * internally by StatsUpdateCounterArray()
+ *
+ * \param pcae Pointer to the StatsPrivateThreadContext which holds the local
+ * versions of the counters
+ */
+static void StatsCopyCounterValue(StatsLocalCounter *pcae)
+{
+ StatsCounter *pc = pcae->pc;
+
+ pc->value = pcae->value;
+ pc->updates = pcae->updates;
+ return;
+}
+
+/**
+ * \brief The output interface for the Stats API
+ */
+static int StatsOutput(ThreadVars *tv)
+{
+ const StatsThreadStore *sts = NULL;
+ const StatsCounter *pc = NULL;
+ void *td = stats_thread_data;
+
+ if (counters_global_id == 0)
+ return -1;
+
+ if (stats_table.nstats == 0) {
+ StatsThreadRegister("Global", &stats_ctx->global_counter_ctx);
+
+ uint32_t nstats = counters_global_id;
+
+ stats_table.nstats = nstats;
+ stats_table.stats = SCCalloc(stats_table.nstats, sizeof(StatsRecord));
+ if (stats_table.stats == NULL) {
+ stats_table.nstats = 0;
+ SCLogError(SC_ERR_MEM_ALLOC, "could not alloc memory for stats");
+ return -1;
+ }
+
+ stats_table.ntstats = stats_ctx->sts_cnt;
+ uint32_t array_size = stats_table.nstats * sizeof(StatsRecord);
+ stats_table.tstats = SCCalloc(stats_table.ntstats, array_size);
+ if (stats_table.tstats == NULL) {
+ stats_table.ntstats = 0;
+ SCLogError(SC_ERR_MEM_ALLOC, "could not alloc memory for stats");
+ return -1;
+ }
+
+ stats_table.start_time = stats_start_time;
+ }
+
+ const uint16_t max_id = counters_global_id;
+ if (max_id == 0)
+ return -1;
+
+ /** temporary local table to merge the per thread counters,
+ * especially needed for the average counters */
+ struct CountersMergeTable {
+ int type;
+ uint64_t value;
+ uint64_t updates;
+ } merge_table[max_id];
+ memset(&merge_table, 0x00,
+ max_id * sizeof(struct CountersMergeTable));
+
+ int thread = stats_ctx->sts_cnt - 1;
+ StatsRecord *table = stats_table.stats;
+
+ /* Loop through the thread counter stores. The global counters
+ * are in a separate store inside this list. */
+ sts = stats_ctx->sts;
+ SCLogDebug("sts %p", sts);
+ while (sts != NULL) {
+ BUG_ON(thread < 0);
+
+ SCLogDebug("Thread %d %s ctx %p", thread, sts->name, sts->ctx);
+
+ /* temporay table for quickly storing the counters for this
+ * thread store, so that we can post process them outside
+ * of the thread store lock */
+ struct CountersMergeTable thread_table[max_id];
+ memset(&thread_table, 0x00,
+ max_id * sizeof(struct CountersMergeTable));
+
+ SCMutexLock(&sts->ctx->m);
+ pc = sts->ctx->head;
+ while (pc != NULL) {
+ SCLogDebug("Counter %s (%u:%u) value %"PRIu64,
+ pc->name, pc->id, pc->gid, pc->value);
+
+ thread_table[pc->gid].type = pc->type;
+ switch (pc->type) {
+ case STATS_TYPE_FUNC:
+ if (pc->Func != NULL)
+ thread_table[pc->gid].value = pc->Func();
+ break;
+ case STATS_TYPE_AVERAGE:
+ default:
+ thread_table[pc->gid].value = pc->value;
+ break;
+ }
+ thread_table[pc->gid].updates = pc->updates;
+ table[pc->gid].name = pc->name;
+
+ pc = pc->next;
+ }
+ SCMutexUnlock(&sts->ctx->m);
+
+ /* update merge table */
+ uint16_t c;
+ for (c = 0; c < max_id; c++) {
+ struct CountersMergeTable *e = &thread_table[c];
+ /* thread only sets type if it has a counter
+ * of this type. */
+ if (e->type == 0)
+ continue;
+
+ switch (e->type) {
+ case STATS_TYPE_MAXIMUM:
+ if (e->value > merge_table[c].value)
+ merge_table[c].value = e->value;
+ break;
+ case STATS_TYPE_FUNC:
+ merge_table[c].value = e->value;
+ break;
+ case STATS_TYPE_AVERAGE:
+ default:
+ merge_table[c].value += e->value;
+ break;
+ }
+ merge_table[c].updates += e->updates;
+ merge_table[c].type = e->type;
+ }
+
+ /* update per thread stats table */
+ for (c = 0; c < max_id; c++) {
+ struct CountersMergeTable *e = &thread_table[c];
+ /* thread only sets type if it has a counter
+ * of this type. */
+ if (e->type == 0)
+ continue;
+
+ uint32_t offset = (thread * stats_table.nstats) + c;
+ StatsRecord *r = &stats_table.tstats[offset];
+ r->name = table[c].name;
+ r->tm_name = sts->name;
+
+ switch (e->type) {
+ case STATS_TYPE_AVERAGE:
+ if (e->value > 0 && e->updates > 0) {
+ r->value = (uint64_t)(e->value / e->updates);
+ }
+ break;
+ default:
+ r->value = e->value;
+ break;
+ }
+ }
+
+ sts = sts->next;
+ thread--;
+ }
+
+ /* transfer 'merge table' to final stats table */
+ uint16_t x;
+ for (x = 0; x < max_id; x++) {
+ /* xfer previous value to pvalue and reset value */
+ table[x].pvalue = table[x].value;
+ table[x].value = 0;
+ table[x].tm_name = "Total";
+
+ struct CountersMergeTable *m = &merge_table[x];
+ switch (m->type) {
+ case STATS_TYPE_MAXIMUM:
+ if (m->value > table[x].value)
+ table[x].value = m->value;
+ break;
+ case STATS_TYPE_AVERAGE:
+ if (m->value > 0 && m->updates > 0) {
+ table[x].value = (uint64_t)(m->value / m->updates);
+ }
+ break;
+ default:
+ table[x].value += m->value;
+ break;
+ }
+ }
+
+ /* invoke logger(s) */
+ OutputStatsLog(tv, td, &stats_table);
+ return 1;
+}
+
+#ifdef BUILD_UNIX_SOCKET
+/**
+ * \todo reimplement this, probably based on stats-json
+ */
+TmEcode StatsOutputCounterSocket(json_t *cmd,
+ json_t *answer, void *data)
+{
+ json_object_set_new(answer, "message",
+ json_string("not implemented"));
+ return TM_ECODE_FAILED;
+}
+#endif /* BUILD_UNIX_SOCKET */
+
+/**
+ * \brief Initializes the perf counter api. Things are hard coded currently.
+ * More work to be done when we implement multiple interfaces
+ */
+void StatsInit(void)
+{
+ BUG_ON(stats_ctx != NULL);
+ if ( (stats_ctx = SCMalloc(sizeof(StatsGlobalContext))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in StatsInitCtx. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(stats_ctx, 0, sizeof(StatsGlobalContext));
+
+ StatsPublicThreadContextInit(&stats_ctx->global_counter_ctx);
+}
+
+void StatsSetupPostConfig(void)
+{
+ StatsInitCtx();
+}
+
+/**
+ * \brief Spawns the wakeup, and the management thread used by the stats api
+ *
+ * The threads use the condition variable in the thread vars to control
+ * their wait loops to make sure the main thread can quickly kill them.
+ */
+void StatsSpawnThreads(void)
+{
+ SCEnter();
+
+ if (!stats_enabled) {
+ SCReturn;
+ }
+
+ ThreadVars *tv_wakeup = NULL;
+ ThreadVars *tv_mgmt = NULL;
+
+ /* spawn the stats wakeup thread */
+ tv_wakeup = TmThreadCreateMgmtThread("StatsWakeupThread",
+ StatsWakeupThread, 1);
+ if (tv_wakeup == NULL) {
+ SCLogError(SC_ERR_THREAD_CREATE, "TmThreadCreateMgmtThread "
+ "failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (TmThreadSpawn(tv_wakeup) != 0) {
+ SCLogError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed for "
+ "StatsWakeupThread");
+ exit(EXIT_FAILURE);
+ }
+
+ /* spawn the stats mgmt thread */
+ tv_mgmt = TmThreadCreateMgmtThread("StatsMgmtThread",
+ StatsMgmtThread, 1);
+ if (tv_mgmt == NULL) {
+ SCLogError(SC_ERR_THREAD_CREATE,
+ "TmThreadCreateMgmtThread failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (TmThreadSpawn(tv_mgmt) != 0) {
+ SCLogError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed for "
+ "StatsWakeupThread");
+ exit(EXIT_FAILURE);
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Registers a normal, unqualified counter
+ *
+ * \param name Name of the counter, to be registered
+ * \param tv Pointer to the ThreadVars instance for which the counter would
+ * be registered
+ *
+ * \retval id Counter id for the newly registered counter, or the already
+ * present counter
+ */
+uint16_t StatsRegisterCounter(char *name, struct ThreadVars_ *tv)
+{
+ uint16_t id = StatsRegisterQualifiedCounter(name,
+ (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->name,
+ &tv->perf_public_ctx,
+ STATS_TYPE_NORMAL, NULL);
+
+ return id;
+}
+
+/**
+ * \brief Registers a counter, whose value holds the average of all the values
+ * assigned to it.
+ *
+ * \param name Name of the counter, to be registered
+ * \param tv Pointer to the ThreadVars instance for which the counter would
+ * be registered
+ *
+ * \retval id Counter id for the newly registered counter, or the already
+ * present counter
+ */
+uint16_t StatsRegisterAvgCounter(char *name, struct ThreadVars_ *tv)
+{
+ uint16_t id = StatsRegisterQualifiedCounter(name,
+ (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->name,
+ &tv->perf_public_ctx,
+ STATS_TYPE_AVERAGE, NULL);
+
+ return id;
+}
+
+/**
+ * \brief Registers a counter, whose value holds the maximum of all the values
+ * assigned to it.
+ *
+ * \param name Name of the counter, to be registered
+ * \param tv Pointer to the ThreadVars instance for which the counter would
+ * be registered
+ *
+ * \retval the counter id for the newly registered counter, or the already
+ * present counter
+ */
+uint16_t StatsRegisterMaxCounter(char *name, struct ThreadVars_ *tv)
+{
+ uint16_t id = StatsRegisterQualifiedCounter(name,
+ (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->name,
+ &tv->perf_public_ctx,
+ STATS_TYPE_MAXIMUM, NULL);
+
+ return id;
+}
+
+/**
+ * \brief Registers a counter, which represents a global value
+ *
+ * \param name Name of the counter, to be registered
+ * \param Func Function Pointer returning a uint64_t
+ *
+ * \retval id Counter id for the newly registered counter, or the already
+ * present counter
+ */
+uint16_t StatsRegisterGlobalCounter(char *name, uint64_t (*Func)(void))
+{
+#ifdef UNITTESTS
+ if (stats_ctx == NULL)
+ return 0;
+#else
+ BUG_ON(stats_ctx == NULL);
+#endif
+ uint16_t id = StatsRegisterQualifiedCounter(name, NULL,
+ &(stats_ctx->global_counter_ctx),
+ STATS_TYPE_FUNC,
+ Func);
+ return id;
+}
+
+typedef struct CountersIdType_ {
+ uint16_t id;
+ const char *string;
+} CountersIdType;
+
+uint32_t CountersIdHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ CountersIdType *t = (CountersIdType *)data;
+ uint32_t hash = 0;
+ int i = 0;
+
+ int len = strlen(t->string);
+
+ for (i = 0; i < len; i++)
+ hash += tolower((unsigned char)t->string[i]);
+
+ hash = hash % ht->array_size;
+
+ return hash;
+}
+
+char CountersIdHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2)
+{
+ CountersIdType *t1 = (CountersIdType *)data1;
+ CountersIdType *t2 = (CountersIdType *)data2;
+ int len1 = 0;
+ int len2 = 0;
+
+ if (t1 == NULL || t2 == NULL)
+ return 0;
+
+ if (t1->string == NULL || t2->string == NULL)
+ return 0;
+
+ len1 = strlen(t1->string);
+ len2 = strlen(t2->string);
+
+ if (len1 == len2 && memcmp(t1->string, t2->string, len1) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void CountersIdHashFreeFunc(void *data)
+{
+ SCFree(data);
+}
+
+
+/** \internal
+ * \brief Adds a TM to the clubbed TM table. Multiple instances of the same TM
+ * are stacked together in a PCTMI container.
+ *
+ * \param tm_name Name of the tm to be added to the table
+ * \param pctx StatsPublicThreadContext associated with the TM tm_name
+ *
+ * \retval 1 on success, 0 on failure
+ */
+static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *pctx)
+{
+ if (stats_ctx == NULL) {
+ SCLogDebug("Counter module has been disabled");
+ return 0;
+ }
+
+ StatsThreadStore *temp = NULL;
+
+ if (thread_name == NULL || pctx == NULL) {
+ SCLogDebug("supplied argument(s) to StatsThreadRegister NULL");
+ return 0;
+ }
+
+ SCMutexLock(&stats_ctx->sts_lock);
+ if (stats_ctx->counters_id_hash == NULL) {
+ stats_ctx->counters_id_hash = HashTableInit(256, CountersIdHashFunc,
+ CountersIdHashCompareFunc,
+ CountersIdHashFreeFunc);
+ BUG_ON(stats_ctx->counters_id_hash == NULL);
+ }
+ StatsCounter *pc = pctx->head;
+ while (pc != NULL) {
+ CountersIdType t = { 0, pc->name }, *id = NULL;
+ id = HashTableLookup(stats_ctx->counters_id_hash, &t, sizeof(t));
+ if (id == NULL) {
+ id = SCCalloc(1, sizeof(*id));
+ BUG_ON(id == NULL);
+ id->id = counters_global_id++;
+ id->string = pc->name;
+ BUG_ON(HashTableAdd(stats_ctx->counters_id_hash, id, sizeof(*id)) < 0);
+ }
+ pc->gid = id->id;
+ pc = pc->next;
+ }
+
+
+ if ( (temp = SCMalloc(sizeof(StatsThreadStore))) == NULL) {
+ SCMutexUnlock(&stats_ctx->sts_lock);
+ return 0;
+ }
+ memset(temp, 0, sizeof(StatsThreadStore));
+
+ temp->ctx = pctx;
+ temp->name = thread_name;
+
+ temp->next = stats_ctx->sts;
+ stats_ctx->sts = temp;
+ stats_ctx->sts_cnt++;
+ SCLogDebug("stats_ctx->sts %p", stats_ctx->sts);
+
+ SCMutexUnlock(&stats_ctx->sts_lock);
+ return 1;
+}
+
+/** \internal
+ * \brief Returns a counter array for counters in this id range(s_id - e_id)
+ *
+ * \param s_id Counter id of the first counter to be added to the array
+ * \param e_id Counter id of the last counter to be added to the array
+ * \param pctx Pointer to the tv's StatsPublicThreadContext
+ *
+ * \retval a counter-array in this(s_id-e_id) range for this TM instance
+ */
+static int StatsGetCounterArrayRange(uint16_t s_id, uint16_t e_id,
+ StatsPublicThreadContext *pctx,
+ StatsPrivateThreadContext *pca)
+{
+ StatsCounter *pc = NULL;
+ uint32_t i = 0;
+
+ if (pctx == NULL || pca == NULL) {
+ SCLogDebug("pctx/pca is NULL");
+ return -1;
+ }
+
+ if (s_id < 1 || e_id < 1 || s_id > e_id) {
+ SCLogDebug("error with the counter ids");
+ return -1;
+ }
+
+ if (e_id > pctx->curr_id) {
+ SCLogDebug("end id is greater than the max id for this tv");
+ return -1;
+ }
+
+ if ( (pca->head = SCMalloc(sizeof(StatsLocalCounter) * (e_id - s_id + 2))) == NULL) {
+ return -1;
+ }
+ memset(pca->head, 0, sizeof(StatsLocalCounter) * (e_id - s_id + 2));
+
+ pc = pctx->head;
+ while (pc->id != s_id)
+ pc = pc->next;
+
+ i = 1;
+ while ((pc != NULL) && (pc->id <= e_id)) {
+ pca->head[i].pc = pc;
+ pca->head[i].id = pc->id;
+ pc = pc->next;
+ i++;
+ }
+ pca->size = i - 1;
+
+ pca->initialized = 1;
+ return 0;
+}
+
+/** \internal
+ * \brief Returns a counter array for all counters registered for this tm
+ * instance
+ *
+ * \param pctx Pointer to the tv's StatsPublicThreadContext
+ *
+ * \retval pca Pointer to a counter-array for all counter of this tm instance
+ * on success; NULL on failure
+ */
+static int StatsGetAllCountersArray(StatsPublicThreadContext *pctx, StatsPrivateThreadContext *private)
+{
+ if (pctx == NULL || private == NULL)
+ return -1;
+
+ return StatsGetCounterArrayRange(1, pctx->curr_id, pctx, private);
+}
+
+
+int StatsSetupPrivate(ThreadVars *tv)
+{
+ StatsGetAllCountersArray(&(tv)->perf_public_ctx, &(tv)->perf_private_ctx);
+
+ StatsThreadRegister(tv->name, &(tv)->perf_public_ctx);
+ return 0;
+}
+
+/**
+ * \brief Syncs the counter array with the global counter variables
+ *
+ * \param pca Pointer to the StatsPrivateThreadContext
+ * \param pctx Pointer the the tv's StatsPublicThreadContext
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int StatsUpdateCounterArray(StatsPrivateThreadContext *pca, StatsPublicThreadContext *pctx)
+{
+ StatsLocalCounter *pcae = NULL;
+ uint32_t i = 0;
+
+ if (pca == NULL || pctx == NULL) {
+ SCLogDebug("pca or pctx is NULL inside StatsUpdateCounterArray");
+ return -1;
+ }
+
+ pcae = pca->head;
+
+ SCMutexLock(&pctx->m);
+ for (i = 1; i <= pca->size; i++) {
+ StatsCopyCounterValue(&pcae[i]);
+ }
+ SCMutexUnlock(&pctx->m);
+
+ pctx->perf_flag = 0;
+
+ return 1;
+}
+
+/**
+ * \brief Get the value of the local copy of the counter that hold this id.
+ *
+ * \param tv threadvars
+ * \param id The counter id.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+uint64_t StatsGetLocalCounterValue(ThreadVars *tv, uint16_t id)
+{
+ StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
+#ifdef DEBUG
+ BUG_ON ((id < 1) || (id > pca->size));
+#endif
+ return pca->head[id].value;
+}
+
+/**
+ * \brief Releases the resources alloted by the Stats API
+ */
+void StatsReleaseResources()
+{
+ StatsReleaseCtx();
+
+ return;
+}
+
+/**
+ * \brief Releases counters
+ *
+ * \param head Pointer to the head of the list of perf counters that have to
+ * be freed
+ */
+void StatsReleaseCounters(StatsCounter *head)
+{
+ StatsCounter *pc = NULL;
+
+ while (head != NULL) {
+ pc = head;
+ head = head->next;
+ StatsReleaseCounter(pc);
+ }
+
+ return;
+}
+
+/**
+ * \brief Releases the StatsPrivateThreadContext allocated by the user, for storing and
+ * updating local counter values
+ *
+ * \param pca Pointer to the StatsPrivateThreadContext
+ */
+void StatsReleasePrivateThreadContext(StatsPrivateThreadContext *pca)
+{
+ if (pca != NULL) {
+ if (pca->head != NULL) {
+ SCFree(pca->head);
+ pca->head = NULL;
+ pca->size = 0;
+ }
+ pca->initialized = 0;
+ }
+
+ return;
+}
+
+void StatsThreadCleanup(ThreadVars *tv)
+{
+ StatsPublicThreadContextCleanup(&tv->perf_public_ctx);
+ StatsReleasePrivateThreadContext(&tv->perf_private_ctx);
+}
+
+/*----------------------------------Unit_Tests--------------------------------*/
+
+#ifdef UNITTESTS
+/** \internal
+ * \brief Registers a normal, unqualified counter
+ *
+ * \param name Name of the counter, to be registered
+ * \param tm_name Name of the engine module under which the counter has to be
+ * registered
+ * \param type Datatype of this counter variable
+ * \param pctx StatsPublicThreadContext corresponding to the tm_name key under which the
+ * key has to be registered
+ *
+ * \retval id Counter id for the newly registered counter, or the already
+ * present counter
+ */
+static uint16_t RegisterCounter(char *name, char *tm_name,
+ StatsPublicThreadContext *pctx)
+{
+ uint16_t id = StatsRegisterQualifiedCounter(name, tm_name, pctx,
+ STATS_TYPE_NORMAL, NULL);
+ return id;
+}
+
+static int StatsTestCounterReg02()
+{
+ StatsPublicThreadContext pctx;
+
+ memset(&pctx, 0, sizeof(StatsPublicThreadContext));
+
+ return RegisterCounter(NULL, NULL, &pctx);
+}
+
+static int StatsTestCounterReg03()
+{
+ StatsPublicThreadContext pctx;
+ int result;
+
+ memset(&pctx, 0, sizeof(StatsPublicThreadContext));
+
+ result = RegisterCounter("t1", "c1", &pctx);
+
+ StatsReleaseCounters(pctx.head);
+
+ return result;
+}
+
+static int StatsTestCounterReg04()
+{
+ StatsPublicThreadContext pctx;
+ int result;
+
+ memset(&pctx, 0, sizeof(StatsPublicThreadContext));
+
+ RegisterCounter("t1", "c1", &pctx);
+ RegisterCounter("t2", "c2", &pctx);
+ RegisterCounter("t3", "c3", &pctx);
+
+ result = RegisterCounter("t1", "c1", &pctx);
+
+ StatsReleaseCounters(pctx.head);
+
+ return result;
+}
+
+static int StatsTestGetCntArray05()
+{
+ ThreadVars tv;
+ int id;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ if (id != 1) {
+ printf("id %d: ", id);
+ return 0;
+ }
+
+ int r = StatsGetAllCountersArray(NULL, &tv.perf_private_ctx);
+ return (r == -1) ? 1 : 0;
+}
+
+static int StatsTestGetCntArray06()
+{
+ ThreadVars tv;
+ int id;
+ int result;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ if (id != 1)
+ return 0;
+
+ int r = StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+
+ result = (r == 0) ? 1 : 0;
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(&tv.perf_private_ctx);
+
+ return result;
+}
+
+static int StatsTestCntArraySize07()
+{
+ ThreadVars tv;
+ StatsPrivateThreadContext *pca = NULL;
+ int result;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ //pca = (StatsPrivateThreadContext *)&tv.perf_private_ctx;
+
+ RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ RegisterCounter("t2", "c2", &tv.perf_public_ctx);
+
+ StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+ pca = &tv.perf_private_ctx;
+
+ StatsIncr(&tv, 1);
+ StatsIncr(&tv, 2);
+
+ result = pca->size;
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(pca);
+
+ return result;
+}
+
+static int StatsTestUpdateCounter08()
+{
+ ThreadVars tv;
+ StatsPrivateThreadContext *pca = NULL;
+ int id;
+ int result;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+
+ StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+ pca = &tv.perf_private_ctx;
+
+ StatsIncr(&tv, id);
+ StatsAddUI64(&tv, id, 100);
+
+ result = pca->head[id].value;
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(pca);
+
+ return result;
+}
+
+static int StatsTestUpdateCounter09()
+{
+ ThreadVars tv;
+ StatsPrivateThreadContext *pca = NULL;
+ uint16_t id1, id2;
+ int result;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ RegisterCounter("t2", "c2", &tv.perf_public_ctx);
+ RegisterCounter("t3", "c3", &tv.perf_public_ctx);
+ RegisterCounter("t4", "c4", &tv.perf_public_ctx);
+ id2 = RegisterCounter("t5", "c5", &tv.perf_public_ctx);
+
+ StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+ pca = &tv.perf_private_ctx;
+
+ StatsIncr(&tv, id2);
+ StatsAddUI64(&tv, id2, 100);
+
+ result = (pca->head[id1].value == 0) && (pca->head[id2].value == 101);
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(pca);
+
+ return result;
+}
+
+static int StatsTestUpdateGlobalCounter10()
+{
+ ThreadVars tv;
+ StatsPrivateThreadContext *pca = NULL;
+
+ int result = 1;
+ uint16_t id1, id2, id3;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ id2 = RegisterCounter("t2", "c2", &tv.perf_public_ctx);
+ id3 = RegisterCounter("t3", "c3", &tv.perf_public_ctx);
+
+ StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+ pca = &tv.perf_private_ctx;
+
+ StatsIncr(&tv, id1);
+ StatsAddUI64(&tv, id2, 100);
+ StatsIncr(&tv, id3);
+ StatsAddUI64(&tv, id3, 100);
+
+ StatsUpdateCounterArray(pca, &tv.perf_public_ctx);
+
+ result = (1 == tv.perf_public_ctx.head->value);
+ result &= (100 == tv.perf_public_ctx.head->next->value);
+ result &= (101 == tv.perf_public_ctx.head->next->next->value);
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(pca);
+
+ return result;
+}
+
+static int StatsTestCounterValues11()
+{
+ ThreadVars tv;
+ StatsPrivateThreadContext *pca = NULL;
+
+ int result = 1;
+ uint16_t id1, id2, id3, id4;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
+ id2 = RegisterCounter("t2", "c2", &tv.perf_public_ctx);
+ id3 = RegisterCounter("t3", "c3", &tv.perf_public_ctx);
+ id4 = RegisterCounter("t4", "c4", &tv.perf_public_ctx);
+
+ StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
+ pca = &tv.perf_private_ctx;
+
+ StatsIncr(&tv, id1);
+ StatsAddUI64(&tv, id2, 256);
+ StatsAddUI64(&tv, id3, 257);
+ StatsAddUI64(&tv, id4, 16843024);
+
+ StatsUpdateCounterArray(pca, &tv.perf_public_ctx);
+
+ result &= (1 == tv.perf_public_ctx.head->value);
+
+ result &= (256 == tv.perf_public_ctx.head->next->value);
+
+ result &= (257 == tv.perf_public_ctx.head->next->next->value);
+
+ result &= (16843024 == tv.perf_public_ctx.head->next->next->next->value);
+
+ StatsReleaseCounters(tv.perf_public_ctx.head);
+ StatsReleasePrivateThreadContext(pca);
+
+ return result;
+}
+
+#endif
+
+void StatsRegisterTests()
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StatsTestCounterReg02", StatsTestCounterReg02, 0);
+ UtRegisterTest("StatsTestCounterReg03", StatsTestCounterReg03, 1);
+ UtRegisterTest("StatsTestCounterReg04", StatsTestCounterReg04, 1);
+ UtRegisterTest("StatsTestGetCntArray05", StatsTestGetCntArray05, 1);
+ UtRegisterTest("StatsTestGetCntArray06", StatsTestGetCntArray06, 1);
+ UtRegisterTest("StatsTestCntArraySize07", StatsTestCntArraySize07, 2);
+ UtRegisterTest("StatsTestUpdateCounter08", StatsTestUpdateCounter08, 101);
+ UtRegisterTest("StatsTestUpdateCounter09", StatsTestUpdateCounter09, 1);
+ UtRegisterTest("StatsTestUpdateGlobalCounter10",
+ StatsTestUpdateGlobalCounter10, 1);
+ UtRegisterTest("StatsTestCounterValues11", StatsTestCounterValues11, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/counters.h b/framework/src/suricata/src/counters.h
new file mode 100644
index 00000000..023d15aa
--- /dev/null
+++ b/framework/src/suricata/src/counters.h
@@ -0,0 +1,150 @@
+/* Copyright (C) 2007-2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __COUNTERS_H__
+#define __COUNTERS_H__
+
+/* forward declaration of the ThreadVars structure */
+struct ThreadVars_;
+
+/**
+ * \brief Container to hold the counter variable
+ */
+typedef struct StatsCounter_ {
+ int type;
+
+ /* local id for this counter in this thread */
+ uint16_t id;
+
+ /* global id, used in output */
+ uint16_t gid;
+
+ /* counter value(s): copies from the 'private' counter */
+ uint64_t value; /**< sum of updates/increments, or 'set' value */
+ uint64_t updates; /**< number of updates (for avg) */
+
+ /* when using type STATS_TYPE_Q_FUNC this function is called once
+ * to get the counter value, regardless of how many threads there are. */
+ uint64_t (*Func)(void);
+
+ /* name of the counter */
+ const char *name;
+
+ /* the next perfcounter for this tv's tm instance */
+ struct StatsCounter_ *next;
+} StatsCounter;
+
+/**
+ * \brief Stats Context for a ThreadVars instance
+ */
+typedef struct StatsPublicThreadContext_ {
+ /* flag set by the wakeup thread, to inform the client threads to sync */
+ uint32_t perf_flag;
+
+ /* pointer to the head of a list of counters assigned under this context */
+ StatsCounter *head;
+
+ /* holds the total no of counters already assigned for this perf context */
+ uint16_t curr_id;
+
+ /* mutex to prevent simultaneous access during update_counter/output_stat */
+ SCMutex m;
+} StatsPublicThreadContext;
+
+/**
+ * \brief Storage for local counters, with a link to the public counter used
+ * for syncs
+ */
+typedef struct StatsLocalCounter_ {
+ /* pointer to the counter that corresponds to this local counter */
+ StatsCounter *pc;
+
+ /* local counter id of the above counter */
+ uint16_t id;
+
+ /* total value of the adds/increments, or exact value in case of 'set' */
+ uint64_t value;
+
+ /* no of times the local counter has been updated */
+ uint64_t updates;
+} StatsLocalCounter;
+
+/**
+ * \brief used to hold the private version of the counters registered
+ */
+typedef struct StatsPrivateThreadContext_ {
+ /* points to the array holding local counters */
+ StatsLocalCounter *head;
+
+ /* size of head array in elements */
+ uint32_t size;
+
+ int initialized;
+} StatsPrivateThreadContext;
+
+/* the initialization functions */
+void StatsInit(void);
+void StatsSetupPostConfig(void);
+void StatsSpawnThreads(void);
+void StatsRegisterTests(void);
+
+/* functions used to free the resources alloted by the Stats API */
+void StatsReleaseResources(void);
+
+/* counter registration functions */
+uint16_t StatsRegisterCounter(char *, struct ThreadVars_ *);
+uint16_t StatsRegisterAvgCounter(char *, struct ThreadVars_ *);
+uint16_t StatsRegisterMaxCounter(char *, struct ThreadVars_ *);
+uint16_t StatsRegisterGlobalCounter(char *cname, uint64_t (*Func)(void));
+
+/* functions used to update local counter values */
+void StatsAddUI64(struct ThreadVars_ *, uint16_t, uint64_t);
+void StatsSetUI64(struct ThreadVars_ *, uint16_t, uint64_t);
+void StatsIncr(struct ThreadVars_ *, uint16_t);
+
+/* utility functions */
+int StatsUpdateCounterArray(StatsPrivateThreadContext *, StatsPublicThreadContext *);
+uint64_t StatsGetLocalCounterValue(struct ThreadVars_ *, uint16_t);
+int StatsSetupPrivate(struct ThreadVars_ *);
+void StatsThreadCleanup(struct ThreadVars_ *);
+
+#define StatsSyncCounters(tv) \
+ StatsUpdateCounterArray(&(tv)->perf_private_ctx, &(tv)->perf_public_ctx); \
+
+#define StatsSyncCountersIfSignalled(tv) \
+ do { \
+ if ((tv)->perf_public_ctx.perf_flag == 1) { \
+ StatsUpdateCounterArray(&(tv)->perf_private_ctx, \
+ &(tv)->perf_public_ctx); \
+ } \
+ } while (0)
+
+#ifdef BUILD_UNIX_SOCKET
+#include <jansson.h>
+TmEcode StatsOutputCounterSocket(json_t *cmd,
+ json_t *answer, void *data);
+#endif
+
+#endif /* __COUNTERS_H__ */
+
diff --git a/framework/src/suricata/src/data-queue.c b/framework/src/suricata/src/data-queue.c
new file mode 100644
index 00000000..a3afd4ac
--- /dev/null
+++ b/framework/src/suricata/src/data-queue.c
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2009, 2010 Open Information Security Foundation.
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "data-queue.h"
+#include "threads.h"
+
+/**
+ * \brief Enqueues data on the queue.
+ *
+ * \param q Pointer to the data queue.
+ * \param data Pointer to the data to be queued. It should be a pointer to a
+ * structure instance that implements the template structure
+ * struct SCDQGenericQData_ defined in data-queue.h.
+ */
+void SCDQDataEnqueue(SCDQDataQueue *q, SCDQGenericQData *data)
+{
+ /* we already have some data in queue */
+ if (q->top != NULL) {
+ data->next = q->top;
+ q->top->prev = data;
+ q->top = data;
+
+ /* the queue is empty */
+ } else {
+ q->top = data;
+ q->bot = data;
+ }
+
+ q->len++;
+
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+
+ return;
+}
+
+/**
+ * \brief Dequeues and returns an entry from the queue.
+ *
+ * \param q Pointer to the data queue.
+ * \param retval Pointer to the data that has been enqueued. The instance
+ * returned is/should be a pointer to a structure instance that
+ * implements the template structure struct SCDQGenericQData_
+ * defined in data-queue.h.
+ */
+SCDQGenericQData *SCDQDataDequeue(SCDQDataQueue *q)
+{
+ SCDQGenericQData *data = NULL;
+
+ /* if the queue is empty there are is no data left and we return NULL */
+ if (q->len == 0) {
+ return NULL;
+ }
+
+ /* If we are going to get the last packet, set len to 0
+ * before doing anything else (to make the threads to follow
+ * the SCondWait as soon as possible) */
+ q->len--;
+
+ /* pull the bottom packet from the queue */
+ data = q->bot;
+
+#ifdef OS_DARWIN
+ /* Weird issue in OS_DARWIN
+ * Sometimes it looks that two thread arrive here at the same time
+ * so the bot ptr is NULL */
+ if (data == NULL) {
+ printf("No data to dequeue!\n");
+ return NULL;
+ }
+#endif /* OS_DARWIN */
+
+ /* more data in queue */
+ if (q->bot->prev != NULL) {
+ q->bot = q->bot->prev;
+ q->bot->next = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+ data->next = NULL;
+ data->prev = NULL;
+
+ return data;
+}
diff --git a/framework/src/suricata/src/data-queue.h b/framework/src/suricata/src/data-queue.h
new file mode 100644
index 00000000..f1f6bb38
--- /dev/null
+++ b/framework/src/suricata/src/data-queue.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2009, 2010 Open Information Security Foundation.
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \file Generic queues. Any instance that wants to get itself on the generic
+ * queue, would have to implement the template struct SCDQGenericQData_
+ * defined below.
+ */
+
+#ifndef __DATA_QUEUE_H__
+#define __DATA_QUEUE_H__
+
+#include "threads.h"
+
+/**
+ * \brief Generic template for any data structure that wants to be on the
+ * queue. Any other data structure that wants to be on the queue
+ * needs to use this template and define its own members from
+ * <your_own_structure_members_from_here_on> onwards.
+ */
+typedef struct SCDQGenericQData_ {
+ /* this is needed when we want to supply a list of data items */
+ struct SCDQGenericQData_ *next;
+ struct SCDQGenericQData_ *prev;
+ /* if we want to consider this pointer as the head of a list, this var
+ * holds the no of elements in the list. Else it holds a <need_to_think>. */
+ //uint16_t len;
+ /* in case this data instance is the head of a list, we can refer the
+ * bottomost instance directly using this var */
+ //struct SCDQGenericaQData *bot;
+
+
+ /* any other data structure that wants to be on the queue can implement
+ * its own memebers from here on, in its structure definition. Just note
+ * that the first 2 members should always be next and prev in the same
+ * order */
+ // <your_own_structure_members_from_here_on>
+} SCDQGenericQData;
+
+/**
+ * \brief The data queue to hold instances that implement the template
+ * SCDQGenericQData.
+ */
+typedef struct SCDQDataQueue_ {
+ /* holds the item at the top of the queue */
+ SCDQGenericQData *top;
+ /* holds the item at the bottom of the queue */
+ SCDQGenericQData *bot;
+ /* no of items currently in the queue */
+ uint16_t len;
+#ifdef DBG_PERF
+ uint16_t dbg_maxlen;
+#endif /* DBG_PERF */
+
+ SCMutex mutex_q;
+ SCCondT cond_q;
+
+} __attribute__((aligned(CLS))) SCDQDataQueue;
+
+void SCDQDataEnqueue(SCDQDataQueue *, SCDQGenericQData *);
+SCDQGenericQData *SCDQDataDequeue(SCDQDataQueue *);
+
+#endif /* __DATA_QUEUE_H__ */
diff --git a/framework/src/suricata/src/debug.h b/framework/src/suricata/src/debug.h
new file mode 100644
index 00000000..839d48d3
--- /dev/null
+++ b/framework/src/suricata/src/debug.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#ifdef DEBUG
+
+#endif /* DEBUG */
+#endif /* __DEBUG_H__ */
+
diff --git a/framework/src/suricata/src/decode-erspan.c b/framework/src/suricata/src/decode-erspan.c
new file mode 100644
index 00000000..f2fb0eb1
--- /dev/null
+++ b/framework/src/suricata/src/decode-erspan.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decodes ERSPAN
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "decode-events.h"
+#include "decode-erspan.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/**
+ * \brief Function to decode ERSPAN packets
+ */
+
+int DecodeERSPAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_erspan);
+
+ if (len < sizeof(ErspanHdr)) {
+ ENGINE_SET_EVENT(p,ERSPAN_HEADER_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ const ErspanHdr *ehdr = (const ErspanHdr *)pkt;
+ uint16_t version = ntohs(ehdr->ver_vlan) >> 12;
+ uint16_t vlan_id = ntohs(ehdr->ver_vlan) & 0x0fff;
+
+ SCLogDebug("ERSPAN: version %u vlan %u", version, vlan_id);
+
+ /* only v1 is tested at this time */
+ if (version != 1) {
+ ENGINE_SET_EVENT(p,ERSPAN_UNSUPPORTED_VERSION);
+ return TM_ECODE_FAILED;
+ }
+
+ if (vlan_id > 0 && dtv->vlan_disabled == 0) {
+ if (p->vlan_idx >= 2) {
+ ENGINE_SET_EVENT(p,ERSPAN_TOO_MANY_VLAN_LAYERS);
+ return TM_ECODE_FAILED;
+ }
+ p->vlan_id[p->vlan_idx] = vlan_id;
+ p->vlan_idx++;
+ }
+
+ return DecodeEthernet(tv, dtv, p, pkt + sizeof(ErspanHdr), len - sizeof(ErspanHdr), pq);
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-erspan.h b/framework/src/suricata/src/decode-erspan.h
new file mode 100644
index 00000000..2f81d1e4
--- /dev/null
+++ b/framework/src/suricata/src/decode-erspan.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#ifndef __DECODE_ERSPAN_H__
+#define __DECODE_ERSPAN_H__
+
+#include "decode.h"
+#include "threadvars.h"
+
+typedef struct ErspanHdr_ {
+ uint16_t ver_vlan;
+ uint16_t flags_spanid;
+ uint32_t padding;
+} __attribute__((__packed__)) ErspanHdr;
+
+#endif /* __DECODE_ERSPAN_H__ */
diff --git a/framework/src/suricata/src/decode-ethernet.c b/framework/src/suricata/src/decode-ethernet.c
new file mode 100644
index 00000000..ee415723
--- /dev/null
+++ b/framework/src/suricata/src/decode-ethernet.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode Ethernet
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-ethernet.h"
+#include "decode-events.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+ uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_eth);
+
+ if (unlikely(len < ETHERNET_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, ETHERNET_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->ethh = (EthernetHdr *)pkt;
+ if (unlikely(p->ethh == NULL))
+ return TM_ECODE_FAILED;
+
+ SCLogDebug("p %p pkt %p ether type %04x", p, pkt, ntohs(p->ethh->eth_type));
+
+ switch (ntohs(p->ethh->eth_type)) {
+ case ETHERNET_TYPE_IP:
+ //printf("DecodeEthernet ip4\n");
+ DecodeIPV4(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_IPV6:
+ //printf("DecodeEthernet ip6\n");
+ DecodeIPV6(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_PPPOE_SESS:
+ //printf("DecodeEthernet PPPOE Session\n");
+ DecodePPPOESession(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_PPPOE_DISC:
+ //printf("DecodeEthernet PPPOE Discovery\n");
+ DecodePPPOEDiscovery(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_VLAN:
+ case ETHERNET_TYPE_8021QINQ:
+ DecodeVLAN(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_MPLS_UNICAST:
+ case ETHERNET_TYPE_MPLS_MULTICAST:
+ DecodeMPLS(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
+ len - ETHERNET_HEADER_LEN, pq);
+ break;
+ default:
+ SCLogDebug("p %p pkt %p ether type %04x not supported", p,
+ pkt, ntohs(p->ethh->eth_type));
+ }
+
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+/** DecodeEthernettest01
+ * \brief Valid Ethernet packet
+ * \retval 0 Expected test value
+ */
+static int DecodeEthernetTest01 (void)
+{
+ /* ICMP packet wrapped in PPPOE */
+ uint8_t raw_eth[] = {
+ 0x00, 0x10, 0x94, 0x55, 0x00, 0x01, 0x00, 0x10,
+ 0x94, 0x56, 0x00, 0x01, 0x88, 0x64, 0x11, 0x00,
+ 0x00, 0x01, 0x00, 0x68, 0x00, 0x21, 0x45, 0xc0,
+ 0x00, 0x64, 0x00, 0x1e, 0x00, 0x00, 0xff, 0x01,
+ 0xa7, 0x78, 0x0a, 0x00, 0x00, 0x02, 0x0a, 0x00,
+ 0x00, 0x01, 0x08, 0x00, 0x4a, 0x61, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
+ 0x3b, 0xd4, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+ SCFree(p);
+ return 0;
+}
+#endif /* UNITTESTS */
+
+
+/**
+ * \brief Registers Ethernet unit tests
+ * \todo More Ethernet tests
+ */
+void DecodeEthernetRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeEthernetTest01", DecodeEthernetTest01, 0);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-ethernet.h b/framework/src/suricata/src/decode-ethernet.h
new file mode 100644
index 00000000..f8ede880
--- /dev/null
+++ b/framework/src/suricata/src/decode-ethernet.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_ETHERNET_H__
+#define __DECODE_ETHERNET_H__
+
+#define ETHERNET_HEADER_LEN 14
+
+/* Ethernet types -- taken from Snort and Libdnet */
+#define ETHERNET_TYPE_PUP 0x0200 /* PUP protocol */
+#define ETHERNET_TYPE_IP 0x0800
+#define ETHERNET_TYPE_ARP 0x0806
+#define ETHERNET_TYPE_REVARP 0x8035
+#define ETHERNET_TYPE_EAPOL 0x888e
+#define ETHERNET_TYPE_IPV6 0x86dd
+#define ETHERNET_TYPE_IPX 0x8137
+#define ETHERNET_TYPE_PPPOE_DISC 0x8863 /* discovery stage */
+#define ETHERNET_TYPE_PPPOE_SESS 0x8864 /* session stage */
+#define ETHERNET_TYPE_8021AD 0x88a8
+#define ETHERNET_TYPE_8021Q 0x8100
+#define ETHERNET_TYPE_LOOP 0x9000
+#define ETHERNET_TYPE_8021QINQ 0x9100
+#define ETHERNET_TYPE_ERSPAN 0x88BE
+
+typedef struct EthernetHdr_ {
+ uint8_t eth_dst[6];
+ uint8_t eth_src[6];
+ uint16_t eth_type;
+} __attribute__((__packed__)) EthernetHdr;
+
+#endif /* __DECODE_ETHERNET_H__ */
+
diff --git a/framework/src/suricata/src/decode-events.c b/framework/src/suricata/src/decode-events.c
new file mode 100644
index 00000000..cecf77c5
--- /dev/null
+++ b/framework/src/suricata/src/decode-events.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+
+/* code moved to app-layer-events */
+
diff --git a/framework/src/suricata/src/decode-events.h b/framework/src/suricata/src/decode-events.h
new file mode 100644
index 00000000..c16d0d92
--- /dev/null
+++ b/framework/src/suricata/src/decode-events.h
@@ -0,0 +1,252 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DECODE_EVENTS_H__
+#define __DECODE_EVENTS_H__
+
+/* packet decoder events */
+enum {
+ /* IPV4 EVENTS */
+ IPV4_PKT_TOO_SMALL = 1, /**< ipv4 pkt smaller than minimum header size */
+ IPV4_HLEN_TOO_SMALL, /**< ipv4 header smaller than minimum size */
+ IPV4_IPLEN_SMALLER_THAN_HLEN, /**< ipv4 pkt len smaller than ip header size */
+ IPV4_TRUNC_PKT, /**< truncated ipv4 packet */
+
+ /* IPV4 OPTIONS */
+ IPV4_OPT_INVALID, /**< invalid ip options */
+ IPV4_OPT_INVALID_LEN, /**< ip options with invalid len */
+ IPV4_OPT_MALFORMED, /**< malformed ip options */
+ IPV4_OPT_PAD_REQUIRED, /**< pad bytes are needed in ip options */
+ IPV4_OPT_EOL_REQUIRED, /**< "end of list" needed in ip options */
+ IPV4_OPT_DUPLICATE, /**< duplicated ip option */
+ IPV4_OPT_UNKNOWN, /**< unknown ip option */
+ IPV4_WRONG_IP_VER, /**< wrong ip version in ip options */
+ IPV4_WITH_ICMPV6, /**< IPv4 packet with ICMPv6 header */
+
+ /* ICMP EVENTS */
+ ICMPV4_PKT_TOO_SMALL, /**< icmpv4 packet smaller than minimum size */
+ ICMPV4_UNKNOWN_TYPE, /**< icmpv4 unknown type */
+ ICMPV4_UNKNOWN_CODE, /**< icmpv4 unknown code */
+ ICMPV4_IPV4_TRUNC_PKT, /**< truncated icmpv4 packet */
+ ICMPV4_IPV4_UNKNOWN_VER, /**< unknown version in icmpv4 packet*/
+
+ /* ICMPv6 EVENTS */
+ ICMPV6_UNKNOWN_TYPE, /**< icmpv6 unknown type */
+ ICMPV6_UNKNOWN_CODE, /**< icmpv6 unknown code */
+ ICMPV6_PKT_TOO_SMALL, /**< icmpv6 smaller than minimum size */
+ ICMPV6_IPV6_UNKNOWN_VER, /**< unknown version in icmpv6 packet */
+ ICMPV6_IPV6_TRUNC_PKT, /**< truncated icmpv6 packet */
+ ICMPV6_MLD_MESSAGE_WITH_INVALID_HL, /**< invalid MLD that doesn't have HL 1 */
+
+ /* IPV6 EVENTS */
+ IPV6_PKT_TOO_SMALL, /**< ipv6 packet smaller than minimum size */
+ IPV6_TRUNC_PKT, /**< truncated ipv6 packet */
+ IPV6_TRUNC_EXTHDR, /**< truncated ipv6 extension header */
+ IPV6_EXTHDR_DUPL_FH, /**< duplicated "fragment" header in ipv6 extension headers */
+ IPV6_EXTHDR_USELESS_FH, /**< useless FH: offset 0 + no more fragments */
+ IPV6_EXTHDR_DUPL_RH, /**< duplicated "routing" header in ipv6 extension headers */
+ IPV6_EXTHDR_DUPL_HH, /**< duplicated "hop-by-hop" header in ipv6 extension headers */
+ IPV6_EXTHDR_DUPL_DH, /**< duplicated "destination" header in ipv6 extension headers */
+ IPV6_EXTHDR_DUPL_AH, /**< duplicated "authentication" header in ipv6 extension headers */
+ IPV6_EXTHDR_DUPL_EH, /**< duplicated "ESP" header in ipv6 extension headers */
+
+ IPV6_EXTHDR_INVALID_OPTLEN, /**< the opt len in an hop or dst hdr is invalid. */
+ IPV6_WRONG_IP_VER, /**< wrong version in ipv6 */
+ IPV6_EXTHDR_AH_RES_NOT_NULL, /**< AH hdr reserved fields not null (rfc 4302) */
+
+ IPV6_HOPOPTS_UNKNOWN_OPT, /**< unknown HOP opt */
+ IPV6_HOPOPTS_ONLY_PADDING, /**< all options in HOP opts are padding */
+ IPV6_DSTOPTS_UNKNOWN_OPT, /**< unknown DST opt */
+ IPV6_DSTOPTS_ONLY_PADDING, /**< all options in DST opts are padding */
+
+ IPV6_EXTHDR_RH_TYPE_0, /**< RH 0 is deprecated as per rfc5095 */
+ IPV6_EXTHDR_ZERO_LEN_PADN, /**< padN w/o data (0 len) */
+ IPV6_FH_NON_ZERO_RES_FIELD, /**< reserved field not zero */
+ IPV6_DATA_AFTER_NONE_HEADER, /**< data after 'none' (59) header */
+
+ IPV6_UNKNOWN_NEXT_HEADER, /**< unknown/unsupported next header */
+ IPV6_WITH_ICMPV4, /**< IPv6 packet with ICMPv4 header */
+
+ /* TCP EVENTS */
+ TCP_PKT_TOO_SMALL, /**< tcp packet smaller than minimum size */
+ TCP_HLEN_TOO_SMALL, /**< tcp header smaller than minimum size */
+ TCP_INVALID_OPTLEN, /**< invalid len in tcp options */
+
+ /* TCP OPTIONS */
+ TCP_OPT_INVALID_LEN, /**< tcp option with invalid len */
+ TCP_OPT_DUPLICATE, /**< duplicated tcp option */
+
+ /* UDP EVENTS */
+ UDP_PKT_TOO_SMALL, /**< udp packet smaller than minimum size */
+ UDP_HLEN_TOO_SMALL, /**< udp header smaller than minimum size */
+ UDP_HLEN_INVALID, /**< invalid len of upd header */
+
+ /* SLL EVENTS */
+ SLL_PKT_TOO_SMALL, /**< sll packet smaller than minimum size */
+
+ /* ETHERNET EVENTS */
+ ETHERNET_PKT_TOO_SMALL, /**< ethernet packet smaller than minimum size */
+
+ /* PPP EVENTS */
+ PPP_PKT_TOO_SMALL, /**< ppp packet smaller than minimum size */
+ PPPVJU_PKT_TOO_SMALL, /**< ppp vj uncompressed packet smaller than minimum size */
+ PPPIPV4_PKT_TOO_SMALL, /**< ppp ipv4 packet smaller than minimum size */
+ PPPIPV6_PKT_TOO_SMALL, /**< ppp ipv6 packet smaller than minimum size */
+ PPP_WRONG_TYPE, /**< wrong type in ppp frame */
+ PPP_UNSUP_PROTO, /**< protocol not supported for ppp */
+
+ /* PPPOE EVENTS */
+ PPPOE_PKT_TOO_SMALL, /**< pppoe packet smaller than minimum size */
+ PPPOE_WRONG_CODE, /**< wrong code for pppoe */
+ PPPOE_MALFORMED_TAGS, /**< malformed tags in pppoe */
+
+ /* GRE EVENTS */
+ GRE_PKT_TOO_SMALL, /**< gre packet smaller than minimum size */
+ GRE_WRONG_VERSION, /**< wrong version in gre header */
+ GRE_VERSION0_RECUR, /**< gre v0 recursion control */
+ GRE_VERSION0_FLAGS, /**< gre v0 flags */
+ GRE_VERSION0_HDR_TOO_BIG, /**< gre v0 header bigger than maximum size */
+ GRE_VERSION0_MALFORMED_SRE_HDR, /**< gre v0 malformed source route entry header */
+ GRE_VERSION1_CHKSUM, /**< gre v1 checksum */
+ GRE_VERSION1_ROUTE, /**< gre v1 routing */
+ GRE_VERSION1_SSR, /**< gre v1 strict source route */
+ GRE_VERSION1_RECUR, /**< gre v1 recursion control */
+ GRE_VERSION1_FLAGS, /**< gre v1 flags */
+ GRE_VERSION1_NO_KEY, /**< gre v1 no key present in header */
+ GRE_VERSION1_WRONG_PROTOCOL, /**< gre v1 wrong protocol */
+ GRE_VERSION1_MALFORMED_SRE_HDR, /**< gre v1 malformed source route entry header */
+ GRE_VERSION1_HDR_TOO_BIG, /**< gre v1 header too big */
+
+ /* VLAN EVENTS */
+ VLAN_HEADER_TOO_SMALL, /**< vlan header smaller than minimum size */
+ VLAN_UNKNOWN_TYPE, /**< vlan unknown type */
+ VLAN_HEADER_TOO_MANY_LAYERS,
+
+ /* RAW EVENTS */
+ IPRAW_INVALID_IPV, /**< invalid ip version in ip raw */
+
+ /* LINKTYPE NULL EVENTS */
+ LTNULL_PKT_TOO_SMALL, /**< pkt too small for lt:null */
+ LTNULL_UNSUPPORTED_TYPE, /**< pkt has a type that the decoder doesn't support */
+
+ /* STREAM EVENTS */
+ STREAM_3WHS_ACK_IN_WRONG_DIR,
+ STREAM_3WHS_ASYNC_WRONG_SEQ,
+ STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION,
+ STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION,
+ STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK,
+ STREAM_3WHS_SYNACK_RESEND_WITH_DIFF_SEQ,
+ STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV,
+ STREAM_3WHS_SYNACK_WITH_WRONG_ACK,
+ STREAM_3WHS_SYNACK_FLOOD,
+ STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV,
+ STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV,
+ STREAM_3WHS_WRONG_SEQ_WRONG_ACK,
+ STREAM_4WHS_SYNACK_WITH_WRONG_ACK,
+ STREAM_4WHS_SYNACK_WITH_WRONG_SYN,
+ STREAM_4WHS_WRONG_SEQ,
+ STREAM_4WHS_INVALID_ACK,
+ STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW,
+ STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW,
+ STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK,
+ STREAM_CLOSEWAIT_INVALID_ACK,
+ STREAM_CLOSING_ACK_WRONG_SEQ,
+ STREAM_CLOSING_INVALID_ACK,
+ STREAM_EST_PACKET_OUT_OF_WINDOW,
+ STREAM_EST_PKT_BEFORE_LAST_ACK,
+ STREAM_EST_SYNACK_RESEND,
+ STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK,
+ STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ,
+ STREAM_EST_SYNACK_TOSERVER,
+ STREAM_EST_SYN_RESEND,
+ STREAM_EST_SYN_RESEND_DIFF_SEQ,
+ STREAM_EST_SYN_TOCLIENT,
+ STREAM_EST_INVALID_ACK,
+ STREAM_FIN_INVALID_ACK,
+ STREAM_FIN1_ACK_WRONG_SEQ,
+ STREAM_FIN1_FIN_WRONG_SEQ,
+ STREAM_FIN1_INVALID_ACK,
+ STREAM_FIN2_ACK_WRONG_SEQ,
+ STREAM_FIN2_FIN_WRONG_SEQ,
+ STREAM_FIN2_INVALID_ACK,
+ STREAM_FIN_BUT_NO_SESSION,
+ STREAM_FIN_OUT_OF_WINDOW,
+ STREAM_LASTACK_ACK_WRONG_SEQ,
+ STREAM_LASTACK_INVALID_ACK,
+ STREAM_RST_BUT_NO_SESSION,
+ STREAM_TIMEWAIT_ACK_WRONG_SEQ,
+ STREAM_TIMEWAIT_INVALID_ACK,
+ STREAM_SHUTDOWN_SYN_RESEND,
+ STREAM_PKT_INVALID_TIMESTAMP,
+ STREAM_PKT_INVALID_ACK,
+ STREAM_PKT_BROKEN_ACK,
+ STREAM_RST_INVALID_ACK,
+ STREAM_PKT_RETRANSMISSION,
+ STREAM_PKT_BAD_WINDOW_UPDATE,
+
+ STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ,
+ STREAM_REASSEMBLY_NO_SEGMENT,
+
+ STREAM_REASSEMBLY_SEQ_GAP,
+
+ STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA,
+
+ /* SCTP EVENTS */
+ SCTP_PKT_TOO_SMALL, /**< sctp packet smaller than minimum size */
+
+ /* Fragmentation reasembly events. */
+ IPV4_FRAG_PKT_TOO_LARGE,
+ IPV6_FRAG_PKT_TOO_LARGE,
+ IPV4_FRAG_OVERLAP,
+ IPV6_FRAG_OVERLAP,
+ IPV4_FRAG_TOO_LARGE,
+ IPV6_FRAG_TOO_LARGE,
+ /* Fragment ignored due to internal error */
+ IPV4_FRAG_IGNORED,
+ IPV6_FRAG_IGNORED,
+
+ /* IPv4 in IPv6 events */
+ IPV4_IN_IPV6_PKT_TOO_SMALL,
+ IPV4_IN_IPV6_WRONG_IP_VER,
+ /* IPv6 in IPv6 events */
+ IPV6_IN_IPV6_PKT_TOO_SMALL,
+ IPV6_IN_IPV6_WRONG_IP_VER,
+
+ /* MPLS decode events. */
+ MPLS_HEADER_TOO_SMALL,
+ MPLS_BAD_LABEL_ROUTER_ALERT,
+ MPLS_BAD_LABEL_IMPLICIT_NULL,
+ MPLS_BAD_LABEL_RESERVED,
+ MPLS_UNKNOWN_PAYLOAD_TYPE,
+
+ /* ERSPAN events */
+ ERSPAN_HEADER_TOO_SMALL,
+ ERSPAN_UNSUPPORTED_VERSION,
+ ERSPAN_TOO_MANY_VLAN_LAYERS,
+
+ /* should always be last! */
+ DECODE_EVENT_MAX,
+};
+
+#endif /* __DECODE_EVENTS_H__ */
diff --git a/framework/src/suricata/src/decode-gre.c b/framework/src/suricata/src/decode-gre.c
new file mode 100644
index 00000000..6ad9e397
--- /dev/null
+++ b/framework/src/suricata/src/decode-gre.c
@@ -0,0 +1,400 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Decodes GRE
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "decode-events.h"
+#include "decode-gre.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/**
+ * \brief Function to decode GRE packets
+ */
+
+int DecodeGRE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ uint16_t header_len = GRE_HDR_LEN;
+ GRESreHdr *gsre = NULL;
+
+ StatsIncr(tv, dtv->counter_gre);
+
+ if(len < GRE_HDR_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, GRE_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->greh = (GREHdr *)pkt;
+ if(p->greh == NULL)
+ return TM_ECODE_FAILED;
+
+ SCLogDebug("p %p pkt %p GRE protocol %04x Len: %d GRE version %x",
+ p, pkt, GRE_GET_PROTO(p->greh), len,GRE_GET_VERSION(p->greh));
+
+ switch (GRE_GET_VERSION(p->greh))
+ {
+ case GRE_VERSION_0:
+
+ /* GRE version 0 doenst support the fields below RFC 1701 */
+
+ /**
+ * \todo We need to make sure this does not allow bypassing
+ * inspection. A server may just ignore these and
+ * continue processing the packet, but we will not look
+ * further into it.
+ */
+
+ if (GRE_FLAG_ISSET_RECUR(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_RECUR);
+ return TM_ECODE_OK;
+ }
+
+ if (GREV1_FLAG_ISSET_FLAGS(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_FLAGS);
+ return TM_ECODE_OK;
+ }
+
+ /* Adjust header length based on content */
+
+ if (GRE_FLAG_ISSET_KY(p->greh))
+ header_len += GRE_KEY_LEN;
+
+ if (GRE_FLAG_ISSET_SQ(p->greh))
+ header_len += GRE_SEQ_LEN;
+
+ if (GRE_FLAG_ISSET_CHKSUM(p->greh) || GRE_FLAG_ISSET_ROUTE(p->greh))
+ header_len += GRE_CHKSUM_LEN + GRE_OFFSET_LEN;
+
+ if (header_len > len) {
+ ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_HDR_TOO_BIG);
+ return TM_ECODE_OK;
+ }
+
+ if (GRE_FLAG_ISSET_ROUTE(p->greh))
+ {
+ while (1)
+ {
+ if ((header_len + GRE_SRE_HDR_LEN) > len) {
+ ENGINE_SET_INVALID_EVENT(p,
+ GRE_VERSION0_MALFORMED_SRE_HDR);
+ return TM_ECODE_OK;
+ }
+
+ gsre = (GRESreHdr *)(pkt + header_len);
+
+ header_len += GRE_SRE_HDR_LEN;
+
+ if ((ntohs(gsre->af) == 0) && (gsre->sre_length == 0))
+ break;
+
+ header_len += gsre->sre_length;
+ if (header_len > len) {
+ ENGINE_SET_INVALID_EVENT(p,
+ GRE_VERSION0_MALFORMED_SRE_HDR);
+ return TM_ECODE_OK;
+ }
+ }
+ }
+ break;
+
+ case GRE_VERSION_1:
+
+ /* GRE version 1 doenst support the fields below RFC 1701 */
+
+ /**
+ * \todo We need to make sure this does not allow bypassing
+ * inspection. A server may just ignore these and
+ * continue processing the packet, but we will not look
+ * further into it.
+ */
+
+ if (GRE_FLAG_ISSET_CHKSUM(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_CHKSUM);
+ return TM_ECODE_OK;
+ }
+
+ if (GRE_FLAG_ISSET_ROUTE(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_ROUTE);
+ return TM_ECODE_OK;
+ }
+
+ if (GRE_FLAG_ISSET_SSR(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_SSR);
+ return TM_ECODE_OK;
+ }
+
+ if (GRE_FLAG_ISSET_RECUR(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_RECUR);
+ return TM_ECODE_OK;
+ }
+
+ if (GREV1_FLAG_ISSET_FLAGS(p->greh)) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_FLAGS);
+ return TM_ECODE_OK;
+ }
+
+ if (GRE_GET_PROTO(p->greh) != GRE_PROTO_PPP) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_WRONG_PROTOCOL);
+ return TM_ECODE_OK;
+ }
+
+ if (!(GRE_FLAG_ISSET_KY(p->greh))) {
+ ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_NO_KEY);
+ return TM_ECODE_OK;
+ }
+
+ header_len += GRE_KEY_LEN;
+
+ /* Adjust header length based on content */
+
+ if (GRE_FLAG_ISSET_SQ(p->greh))
+ header_len += GRE_SEQ_LEN;
+
+ if (GREV1_FLAG_ISSET_ACK(p->greh))
+ header_len += GREV1_ACK_LEN;
+
+ if (header_len > len) {
+ ENGINE_SET_INVALID_EVENT(p, GRE_VERSION1_HDR_TOO_BIG);
+ return TM_ECODE_OK;
+ }
+
+ break;
+ default:
+ ENGINE_SET_INVALID_EVENT(p, GRE_WRONG_VERSION);
+ return TM_ECODE_OK;
+ }
+
+ switch (GRE_GET_PROTO(p->greh))
+ {
+ case ETHERNET_TYPE_IP:
+ {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
+ len - header_len, DECODE_TUNNEL_IPV4, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+
+ case GRE_PROTO_PPP:
+ {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
+ len - header_len, DECODE_TUNNEL_PPP, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+
+ case ETHERNET_TYPE_IPV6:
+ {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
+ len - header_len, DECODE_TUNNEL_IPV6, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+
+ case ETHERNET_TYPE_VLAN:
+ {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
+ len - header_len, DECODE_TUNNEL_VLAN, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+
+ case ETHERNET_TYPE_ERSPAN:
+ {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
+ len - header_len, DECODE_TUNNEL_ERSPAN, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+
+ default:
+ return TM_ECODE_OK;
+ }
+ return TM_ECODE_OK;
+}
+
+
+#ifdef UNITTESTS
+/**
+ * \test DecodeGRETest01 is a test for small gre packet
+ */
+
+static int DecodeGREtest01 (void)
+{
+
+ uint8_t raw_gre[] = { 0x00 ,0x6e ,0x62 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodeGRE(&tv, &dtv, p, raw_gre, sizeof(raw_gre), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,GRE_PKT_TOO_SMALL)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test DecodeGRETest02 is a test for wrong gre version
+ */
+
+static int DecodeGREtest02 (void)
+{
+ uint8_t raw_gre[] = {
+ 0x00, 0x6e, 0x62, 0xac, 0x40, 0x00, 0x40, 0x2f,
+ 0xc2, 0xc7, 0x0a, 0x00, 0x00, 0x64, 0x0a, 0x00,
+ 0x00, 0x8a, 0x30, 0x01, 0x0b, 0x00, 0x4e, 0x00,
+ 0x00, 0x00, 0x18, 0x4a, 0x50, 0xff, 0x03, 0x00,
+ 0x21, 0x45, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x40,
+ 0x00, 0x40, 0x11, 0x94, 0x22, 0x50, 0x7e, 0x2b,
+ 0x2d, 0xc2, 0x6d, 0x68, 0x68, 0x80, 0x0e, 0x00,
+ 0x35, 0x00, 0x36, 0x9f, 0x18, 0xdb, 0xc4, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x73, 0x31, 0x36, 0x09, 0x73, 0x69,
+ 0x74, 0x65, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodeGRE(&tv, &dtv, p, raw_gre, sizeof(raw_gre), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,GRE_WRONG_VERSION)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+
+/**
+ * \test DecodeGRETest03 is a test for valid gre packet
+ */
+
+static int DecodeGREtest03 (void)
+{
+ uint8_t raw_gre[] = {
+ 0x00, 0x6e, 0x62, 0xac, 0x40, 0x00, 0x40, 0x2f,
+ 0xc2, 0xc7, 0x0a, 0x00, 0x00, 0x64, 0x0a, 0x00,
+ 0x00, 0x8a, 0x30, 0x01, 0x88, 0x0b, 0x00, 0x4e,
+ 0x00, 0x00, 0x00, 0x18, 0x4a, 0x50, 0xff, 0x03,
+ 0x00, 0x21, 0x45, 0x00, 0x00, 0x4a, 0x00, 0x00,
+ 0x40, 0x00, 0x40, 0x11, 0x94, 0x22, 0x50, 0x7e,
+ 0x2b, 0x2d, 0xc2, 0x6d, 0x68, 0x68, 0x80, 0x0e,
+ 0x00, 0x35, 0x00, 0x36, 0x9f, 0x18, 0xdb, 0xc4,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x03, 0x73, 0x31, 0x36, 0x09, 0x73,
+ 0x69, 0x74, 0x65, 0x6d, 0x65, 0x74, 0x65, 0x72,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodeGRE(&tv, &dtv, p, raw_gre, sizeof(raw_gre), NULL);
+
+ if(p->greh == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+
+ SCFree(p);
+ return 1;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for GRE decoder
+ */
+
+void DecodeGRERegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeGREtest01", DecodeGREtest01, 1);
+ UtRegisterTest("DecodeGREtest02", DecodeGREtest02, 1);
+ UtRegisterTest("DecodeGREtest03", DecodeGREtest03, 1);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-gre.h b/framework/src/suricata/src/decode-gre.h
new file mode 100644
index 00000000..6f3db29d
--- /dev/null
+++ b/framework/src/suricata/src/decode-gre.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file decode-gre.h
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Generic Route Encapsulation (GRE) from RFC 1701.
+ */
+
+#ifndef __DECODE_GRE_H__
+#define __DECODE_GRE_H__
+
+#ifndef IPPROTO_GRE
+#define IPPROTO_GRE 47
+#endif
+
+#include "decode.h"
+#include "threadvars.h"
+
+typedef struct GREHdr_
+{
+ uint8_t flags; /**< GRE packet flags */
+ uint8_t version; /**< GRE version */
+ uint16_t ether_type; /**< ether type of the encapsulated traffic */
+
+} __attribute__((__packed__)) GREHdr;
+
+/* Generic Routing Encapsulation Source Route Entries (SREs).
+ * The header is followed by a variable amount of Routing Information.
+ */
+typedef struct GRESreHdr_
+{
+ uint16_t af; /**< Address family */
+ uint8_t sre_offset;
+ uint8_t sre_length;
+} __attribute__((__packed__)) GRESreHdr;
+
+#define GRE_VERSION_0 0x0000
+#define GRE_VERSION_1 0x0001
+
+#define GRE_HDR_LEN 4
+#define GRE_CHKSUM_LEN 2
+#define GRE_OFFSET_LEN 2
+#define GRE_KEY_LEN 4
+#define GRE_SEQ_LEN 4
+#define GRE_SRE_HDR_LEN 4
+#define GRE_PROTO_PPP 0x880b
+
+#define GRE_FLAG_ISSET_CHKSUM(r) (r->flags & 0x80)
+#define GRE_FLAG_ISSET_ROUTE(r) (r->flags & 0x40)
+#define GRE_FLAG_ISSET_KY(r) (r->flags & 0x20)
+#define GRE_FLAG_ISSET_SQ(r) (r->flags & 0x10)
+#define GRE_FLAG_ISSET_SSR(r) (r->flags & 0x08)
+#define GRE_FLAG_ISSET_RECUR(r) (r->flags & 0x07)
+#define GRE_GET_VERSION(r) (r->version & 0x07)
+#define GRE_GET_FLAGS(r) (r->version & 0xF8)
+#define GRE_GET_PROTO(r) ntohs(r->ether_type)
+
+#define GREV1_HDR_LEN 8
+#define GREV1_ACK_LEN 4
+#define GREV1_FLAG_ISSET_FLAGS(r) (r->version & 0x78)
+#define GREV1_FLAG_ISSET_ACK(r) (r->version & 0x80)
+
+void DecodeGRERegisterTests(void);
+
+#endif /* __DECODE_GRE_H__ */
+
diff --git a/framework/src/suricata/src/decode-icmpv4.c b/framework/src/suricata/src/decode-icmpv4.c
new file mode 100644
index 00000000..5af012ce
--- /dev/null
+++ b/framework/src/suricata/src/decode-icmpv4.c
@@ -0,0 +1,784 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode ICMPv4
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-icmpv4.h"
+
+#include "flow.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+/**
+ * Note, this is the IP header, plus a bit of the original packet, not the whole thing!
+ */
+void DecodePartialIPV4( Packet* p, uint8_t* partial_packet, uint16_t len )
+{
+ /** Check the sizes, the header must fit at least */
+ if (len < IPV4_HEADER_LEN) {
+ SCLogDebug("DecodePartialIPV4: ICMPV4_IPV4_TRUNC_PKT");
+ ENGINE_SET_INVALID_EVENT(p, ICMPV4_IPV4_TRUNC_PKT);
+ return;
+ }
+
+ IPV4Hdr *icmp4_ip4h = (IPV4Hdr*)partial_packet;
+
+ /** Check the embedded version */
+ if (IPV4_GET_RAW_VER(icmp4_ip4h) != 4) {
+ /** Check the embedded version */
+ SCLogDebug("DecodePartialIPV4: ICMPv4 contains Unknown IPV4 version "
+ "ICMPV4_IPV4_UNKNOWN_VER");
+ ENGINE_SET_INVALID_EVENT(p, ICMPV4_IPV4_UNKNOWN_VER);
+ return;
+ }
+
+ /** We need to fill icmpv4vars */
+ p->icmpv4vars.emb_ipv4h = icmp4_ip4h;
+
+ /** Get the IP address from the contained packet */
+ p->icmpv4vars.emb_ip4_src = IPV4_GET_RAW_IPSRC(icmp4_ip4h);
+ p->icmpv4vars.emb_ip4_dst = IPV4_GET_RAW_IPDST(icmp4_ip4h);
+
+ p->icmpv4vars.emb_ip4_hlen=IPV4_GET_RAW_HLEN(icmp4_ip4h) << 2;
+
+ switch (IPV4_GET_RAW_IPPROTO(icmp4_ip4h)) {
+ case IPPROTO_TCP:
+ if (len >= IPV4_HEADER_LEN + TCP_HEADER_LEN ) {
+ p->icmpv4vars.emb_tcph = (TCPHdr*)(partial_packet + IPV4_HEADER_LEN);
+ p->icmpv4vars.emb_sport = ntohs(p->icmpv4vars.emb_tcph->th_sport);
+ p->icmpv4vars.emb_dport = ntohs(p->icmpv4vars.emb_tcph->th_dport);
+ p->icmpv4vars.emb_ip4_proto = IPPROTO_TCP;
+
+ SCLogDebug("DecodePartialIPV4: ICMPV4->IPV4->TCP header sport: "
+ "%"PRIu8" dport %"PRIu8"", p->icmpv4vars.emb_sport,
+ p->icmpv4vars.emb_dport);
+ } else if (len >= IPV4_HEADER_LEN + 4) {
+ /* only access th_sport and th_dport */
+ TCPHdr *emb_tcph = (TCPHdr*)(partial_packet + IPV4_HEADER_LEN);
+
+ p->icmpv4vars.emb_tcph = NULL;
+ p->icmpv4vars.emb_sport = ntohs(emb_tcph->th_sport);
+ p->icmpv4vars.emb_dport = ntohs(emb_tcph->th_dport);
+ p->icmpv4vars.emb_ip4_proto = IPPROTO_TCP;
+ SCLogDebug("DecodePartialIPV4: ICMPV4->IPV4->TCP partial header sport: "
+ "%"PRIu8" dport %"PRIu8"", p->icmpv4vars.emb_sport,
+ p->icmpv4vars.emb_dport);
+ } else {
+ SCLogDebug("DecodePartialIPV4: Warning, ICMPV4->IPV4->TCP "
+ "header Didn't fit in the packet!");
+ p->icmpv4vars.emb_sport = 0;
+ p->icmpv4vars.emb_dport = 0;
+ }
+
+ break;
+ case IPPROTO_UDP:
+ if (len >= IPV4_HEADER_LEN + UDP_HEADER_LEN ) {
+ p->icmpv4vars.emb_udph = (UDPHdr*)(partial_packet + IPV4_HEADER_LEN);
+ p->icmpv4vars.emb_sport = ntohs(p->icmpv4vars.emb_udph->uh_sport);
+ p->icmpv4vars.emb_dport = ntohs(p->icmpv4vars.emb_udph->uh_dport);
+ p->icmpv4vars.emb_ip4_proto = IPPROTO_UDP;
+
+ SCLogDebug("DecodePartialIPV4: ICMPV4->IPV4->UDP header sport: "
+ "%"PRIu8" dport %"PRIu8"", p->icmpv4vars.emb_sport,
+ p->icmpv4vars.emb_dport);
+ } else {
+ SCLogDebug("DecodePartialIPV4: Warning, ICMPV4->IPV4->UDP "
+ "header Didn't fit in the packet!");
+ p->icmpv4vars.emb_sport = 0;
+ p->icmpv4vars.emb_dport = 0;
+ }
+
+ break;
+ case IPPROTO_ICMP:
+ p->icmpv4vars.emb_icmpv4h = (ICMPV4Hdr*)(partial_packet + IPV4_HEADER_LEN);
+ p->icmpv4vars.emb_sport = 0;
+ p->icmpv4vars.emb_dport = 0;
+ p->icmpv4vars.emb_ip4_proto = IPPROTO_ICMP;
+
+ SCLogDebug("DecodePartialIPV4: ICMPV4->IPV4->ICMP header");
+
+ break;
+ }
+
+ /* debug print */
+#ifdef DEBUG
+ char s[16], d[16];
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_src), s, sizeof(s));
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_dst), d, sizeof(d));
+ SCLogDebug("ICMPv4 embedding IPV4 %s->%s - PROTO: %" PRIu32 " ID: %" PRIu32 "", s,d,
+ IPV4_GET_RAW_IPPROTO(icmp4_ip4h), IPV4_GET_RAW_IPID(icmp4_ip4h));
+#endif
+
+ return;
+
+}
+
+/** DecodeICMPV4
+ * \brief Main ICMPv4 decoding function
+ */
+int DecodeICMPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_icmpv4);
+
+ if (len < ICMPV4_HEADER_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, ICMPV4_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->icmpv4h = (ICMPV4Hdr *)pkt;
+
+ SCLogDebug("ICMPV4 TYPE %" PRIu32 " CODE %" PRIu32 "", p->icmpv4h->type, p->icmpv4h->code);
+
+ p->proto = IPPROTO_ICMP;
+ p->type = p->icmpv4h->type;
+ p->code = p->icmpv4h->code;
+ p->payload = pkt + ICMPV4_HEADER_LEN;
+ p->payload_len = len - ICMPV4_HEADER_LEN;
+
+ ICMPV4ExtHdr* icmp4eh = (ICMPV4ExtHdr*) p->icmpv4h;
+
+ switch (p->icmpv4h->type)
+ {
+ case ICMP_ECHOREPLY:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_DEST_UNREACH:
+ if (p->icmpv4h->code > NR_ICMP_UNREACH) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ } else {
+ /* parse IP header plus 64 bytes */
+ if (len > ICMPV4_HEADER_PKT_OFFSET) {
+ DecodePartialIPV4( p, (uint8_t *)(pkt + ICMPV4_HEADER_PKT_OFFSET), len - ICMPV4_HEADER_PKT_OFFSET );
+
+ /* ICMP ICMP_DEST_UNREACH influence TCP/UDP flows */
+ if (ICMPV4_DEST_UNREACH_IS_VALID(p)) {
+ FlowHandlePacket(tv, dtv, p);
+ }
+ }
+ }
+
+
+ break;
+
+ case ICMP_SOURCE_QUENCH:
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ } else {
+ // parse IP header plus 64 bytes
+ if (len >= ICMPV4_HEADER_PKT_OFFSET)
+ DecodePartialIPV4( p, (uint8_t*) (pkt + ICMPV4_HEADER_PKT_OFFSET), len - ICMPV4_HEADER_PKT_OFFSET );
+ }
+ break;
+
+ case ICMP_REDIRECT:
+ if (p->icmpv4h->code>ICMP_REDIR_HOSTTOS) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ } else {
+ // parse IP header plus 64 bytes
+ if (len > ICMPV4_HEADER_PKT_OFFSET)
+ DecodePartialIPV4( p, (uint8_t*) (pkt + ICMPV4_HEADER_PKT_OFFSET), len - ICMPV4_HEADER_PKT_OFFSET );
+ }
+ break;
+
+ case ICMP_ECHO:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_TIME_EXCEEDED:
+ if (p->icmpv4h->code>ICMP_EXC_FRAGTIME) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ } else {
+ // parse IP header plus 64 bytes
+ if (len > ICMPV4_HEADER_PKT_OFFSET)
+ DecodePartialIPV4( p, (uint8_t*) (pkt + ICMPV4_HEADER_PKT_OFFSET), len - ICMPV4_HEADER_PKT_OFFSET );
+ }
+ break;
+
+ case ICMP_PARAMETERPROB:
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ } else {
+ // parse IP header plus 64 bytes
+ if (len > ICMPV4_HEADER_PKT_OFFSET)
+ DecodePartialIPV4( p, (uint8_t*) (pkt + ICMPV4_HEADER_PKT_OFFSET), len - ICMPV4_HEADER_PKT_OFFSET );
+ }
+ break;
+
+ case ICMP_TIMESTAMP:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_TIMESTAMPREPLY:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_INFO_REQUEST:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_INFO_REPLY:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_ADDRESS:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ case ICMP_ADDRESSREPLY:
+ p->icmpv4vars.id=icmp4eh->id;
+ p->icmpv4vars.seq=icmp4eh->seq;
+ if (p->icmpv4h->code!=0) {
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_CODE);
+ }
+ break;
+
+ default:
+ ENGINE_SET_EVENT(p,ICMPV4_UNKNOWN_TYPE);
+
+ }
+
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+/** DecodeICMPV4test01
+ * \brief
+ * \retval 1 Expected test value
+ */
+static int DecodeICMPV4test01(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x08, 0x00, 0x78, 0x47, 0xfc, 0x55, 0x00, 0x04,
+ 0x52, 0xab, 0x86, 0x4a, 0x84, 0x50, 0x0e, 0x00,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL!=p->icmpv4h) {
+ if (p->icmpv4h->type==8 && p->icmpv4h->code==0) {
+ ret = 1;
+ }
+ }
+
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+/** DecodeICMPV4test02
+ * \brief
+ * \retval 1 Expected test value
+ */
+static int DecodeICMPV4test02(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x00, 0x00, 0x57, 0x64, 0xfb, 0x55, 0x00, 0x03,
+ 0x43, 0xab, 0x86, 0x4a, 0xf6, 0x49, 0x02, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL!=p->icmpv4h) {
+ if (p->icmpv4h->type==0 && p->icmpv4h->code==0) {
+ ret = 1;
+ }
+ }
+
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+/** DecodeICMPV4test03
+ * \brief TTL exceeded
+ * \retval Expected test value: 1
+ */
+static int DecodeICMPV4test03(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x0b, 0x00, 0x6a, 0x3d, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x3c, 0x64, 0x15, 0x00, 0x00,
+ 0x01, 0x11, 0xde, 0xfd, 0xc0, 0xa8, 0x01, 0x0d,
+ 0xd1, 0x55, 0xe3, 0x93, 0x8b, 0x12, 0x82, 0xaa,
+ 0x00, 0x28, 0x7c, 0xdd };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL == p->icmpv4h) {
+ printf("NULL == p->icmpv4h: ");
+ goto end;
+ }
+
+ /* check it's type 11 code 0 */
+ if (p->icmpv4h->type != 11 || p->icmpv4h->code != 0) {
+ printf("p->icmpv4h->type %u, p->icmpv4h->code %u: ",
+ p->icmpv4h->type, p->icmpv4h->code);
+ goto end;
+ }
+
+ /* check it's source port 35602 to port 33450 */
+ if (p->icmpv4vars.emb_sport != 35602 ||
+ p->icmpv4vars.emb_dport != 33450) {
+ printf("p->icmpv4vars.emb_sport %u, p->icmpv4vars.emb_dport %u: ",
+ p->icmpv4vars.emb_sport, p->icmpv4vars.emb_dport);
+ goto end;
+ }
+
+ /* check the src,dst IPs contained inside */
+ char s[16], d[16];
+
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_src), s, sizeof(s));
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_dst), d, sizeof(d));
+
+ /* ICMPv4 embedding IPV4 192.168.1.13->209.85.227.147 pass */
+ if (strcmp(s, "192.168.1.13") == 0 && strcmp(d, "209.85.227.147") == 0) {
+ ret = 1;
+ }
+ else {
+ printf("s %s, d %s: ", s, d);
+ }
+
+end:
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+/** DecodeICMPV4test04
+ * \brief dest. unreachable, administratively prohibited
+ * \retval 1 Expected test value
+ */
+static int DecodeICMPV4test04(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x03, 0x0a, 0x36, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x3c, 0x62, 0xee, 0x40, 0x00,
+ 0x33, 0x06, 0xb4, 0x8f, 0xc0, 0xa8, 0x01, 0x0d,
+ 0x58, 0x60, 0x16, 0x29, 0xb1, 0x0a, 0x00, 0x32,
+ 0x3e, 0x36, 0x38, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0xa0, 0x02, 0x16, 0xd0, 0x72, 0x04, 0x00, 0x00,
+ 0x02, 0x04, 0x05, 0x8a, 0x04, 0x02, 0x08, 0x0a };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL == p->icmpv4h) {
+ goto end;
+ }
+
+ /* check the type,code pair is correct - type 3, code 10 */
+ if (p->icmpv4h->type != 3 || p->icmpv4h->code != 10) {
+ goto end;
+ }
+
+ /* check it's src port 45322 to dst port 50 */
+ if (p->icmpv4vars.emb_sport != 45322 ||
+ p->icmpv4vars.emb_dport != 50) {
+ goto end;
+ }
+
+ // check the src,dst IPs contained inside
+ char s[16], d[16];
+
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_src), s, sizeof(s));
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_dst), d, sizeof(d));
+
+ // ICMPv4 embedding IPV4 192.168.1.13->88.96.22.41
+ if (strcmp(s, "192.168.1.13") == 0 && strcmp(d, "88.96.22.41") == 0) {
+ ret = 1;
+ }
+
+end:
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+/** DecodeICMPV4test05
+ * \brief dest. unreachable, administratively prohibited
+ * \retval 1 Expected test value
+ */
+static int DecodeICMPV4test05(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x0b, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x00, 0x00, 0x45,
+ 0x00, 0x00, 0x30, 0x02, 0x17, 0x40, 0x00, 0x01, 0x06,
+ 0xd6, 0xbd, 0xc0, 0xa8, 0x02, 0x05, 0x3d, 0x23, 0xa1,
+ 0x23, 0x04, 0x18, 0x00, 0x50, 0xd2, 0x08, 0xc2, 0x48,
+ };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL == p->icmpv4h) {
+ goto end;
+ }
+
+ /* check the type,code pair is correct - type 11, code 0 */
+ if (p->icmpv4h->type != 11 || p->icmpv4h->code != 0) {
+ goto end;
+ }
+
+ /* check it's src port 1048 to dst port 80 */
+ if (p->icmpv4vars.emb_sport != 1048 ||
+ p->icmpv4vars.emb_dport != 80) {
+ goto end;
+ }
+
+ // check the src,dst IPs contained inside
+ char s[16], d[16];
+
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_src), s, sizeof(s));
+ PrintInet(AF_INET, &(p->icmpv4vars.emb_ip4_dst), d, sizeof(d));
+
+ // ICMPv4 embedding IPV4 192.168.2.5->61.35.161.35
+ if (strcmp(s, "192.168.2.5") == 0 && strcmp(d, "61.35.161.35") == 0) {
+ ret = 1;
+ }
+
+end:
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+static int ICMPV4CalculateValidChecksumtest05(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_icmpv4[] = {
+ 0x08, 0x00, 0xab, 0x9b, 0x7f, 0x2b, 0x05, 0x2c,
+ 0x3f, 0x72, 0x93, 0x4a, 0x00, 0x4d, 0x0a, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37};
+
+ csum = *( ((uint16_t *)raw_icmpv4) + 1);
+ return (csum == ICMPV4CalculateChecksum((uint16_t *)raw_icmpv4, sizeof(raw_icmpv4)));
+}
+
+static int ICMPV4CalculateInvalidChecksumtest06(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_icmpv4[] = {
+ 0x08, 0x00, 0xab, 0x9b, 0x7f, 0x2b, 0x05, 0x2c,
+ 0x3f, 0x72, 0x93, 0x4a, 0x00, 0x4d, 0x0a, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38};
+
+ csum = *( ((uint16_t *)raw_icmpv4) + 1);
+ return (csum == ICMPV4CalculateChecksum((uint16_t *)raw_icmpv4, sizeof(raw_icmpv4)));
+}
+
+static int ICMPV4InvalidType07(void)
+{
+
+ uint8_t raw_icmpv4[] = {
+ 0xff, 0x00, 0xab, 0x9b, 0x7f, 0x2b, 0x05, 0x2c,
+ 0x3f, 0x72, 0x93, 0x4a, 0x00, 0x4d, 0x0a, 0x00,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38};
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,ICMPV4_UNKNOWN_TYPE)) {
+ ret = 1;
+ }
+
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+/** DecodeICMPV4test08
+ * \brief
+ * \retval 1 Expected test value - what we really want is not to segfault
+ */
+static int DecodeICMPV4test08(void)
+{
+ uint8_t raw_icmpv4[] = {
+ 0x08, 0x00, 0x78, 0x47, 0xfc, 0x55, 0x00, 0x00
+ };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("4.3.2.1");;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&tv, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ if (NULL!=p->icmpv4h) {
+ if (p->icmpv4h->type==8 && p->icmpv4h->code==0) {
+ ret = 1;
+ }
+ }
+
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief Registers ICMPV4 unit test
+ */
+void DecodeICMPV4RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeICMPV4test01", DecodeICMPV4test01, 1);
+ UtRegisterTest("DecodeICMPV4test02", DecodeICMPV4test02, 1);
+ UtRegisterTest("DecodeICMPV4test03", DecodeICMPV4test03, 1);
+ UtRegisterTest("DecodeICMPV4test04", DecodeICMPV4test04, 1);
+ UtRegisterTest("DecodeICMPV4test05", DecodeICMPV4test05, 1);
+ UtRegisterTest("ICMPV4CalculateValidChecksumtest05",
+ ICMPV4CalculateValidChecksumtest05, 1);
+ UtRegisterTest("ICMPV4CalculateInvalidChecksumtest06",
+ ICMPV4CalculateInvalidChecksumtest06, 0);
+ UtRegisterTest("DecodeICMPV4InvalidType", ICMPV4InvalidType07, 1);
+ UtRegisterTest("DecodeICMPV4test08", DecodeICMPV4test08, 1);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-icmpv4.h b/framework/src/suricata/src/decode-icmpv4.h
new file mode 100644
index 00000000..f8cb97f4
--- /dev/null
+++ b/framework/src/suricata/src/decode-icmpv4.h
@@ -0,0 +1,345 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_ICMPV4_H__
+#define __DECODE_ICMPV4_H__
+
+#include "decode.h"
+#include "decode-tcp.h"
+#include "decode-sctp.h"
+#include "decode-udp.h"
+
+#define ICMPV4_HEADER_LEN 8
+
+#ifndef ICMP_ECHOREPLY
+#define ICMP_ECHOREPLY 0 /* Echo Reply */
+#endif
+#ifndef ICMP_DEST_UNREACH
+#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
+#endif
+#ifndef ICMP_SOURCE_QUENCH
+#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
+#endif
+#ifndef ICMP_REDIRECT
+#define ICMP_REDIRECT 5 /* Redirect (change route) */
+#endif
+#ifndef ICMP_ECHO
+#define ICMP_ECHO 8 /* Echo Request */
+#endif
+#ifndef ICMP_TIME_EXCEEDED
+#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
+#endif
+#ifndef ICMP_PARAMETERPROB
+#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
+#endif
+#ifndef ICMP_TIMESTAMP
+#define ICMP_TIMESTAMP 13 /* Timestamp Request */
+#endif
+#ifndef ICMP_TIMESTAMPREPLY
+#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
+#endif
+#ifndef ICMP_INFO_REQUEST
+#define ICMP_INFO_REQUEST 15 /* Information Request */
+#endif
+#ifndef ICMP_INFO_REPLY
+#define ICMP_INFO_REPLY 16 /* Information Reply */
+#endif
+#ifndef ICMP_ADDRESS
+#define ICMP_ADDRESS 17 /* Address Mask Request */
+#endif
+#ifndef ICMP_ADDRESSREPLY
+#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
+#endif
+#ifndef NR_ICMP_TYPES
+#define NR_ICMP_TYPES 18
+#endif
+
+
+/* Codes for UNREACH. */
+#ifndef ICMP_NET_UNREACH
+#define ICMP_NET_UNREACH 0 /* Network Unreachable */
+#endif
+#ifndef ICMP_HOST_UNREACH
+#define ICMP_HOST_UNREACH 1 /* Host Unreachable */
+#endif
+#ifndef ICMP_PROT_UNREACH
+#define ICMP_PROT_UNREACH 2 /* Protocol Unreachable */
+#endif
+#ifndef ICMP_PORT_UNREACH
+#define ICMP_PORT_UNREACH 3 /* Port Unreachable */
+#endif
+#ifndef ICMP_FRAG_NEEDED
+#define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
+#endif
+#ifndef ICMP_SR_FAILED
+#define ICMP_SR_FAILED 5 /* Source Route failed */
+#endif
+#ifndef ICMP_NET_UNKNOWN
+#define ICMP_NET_UNKNOWN 6
+#endif
+#ifndef ICMP_HOST_UNKNOWN
+#define ICMP_HOST_UNKNOWN 7
+#endif
+#ifndef ICMP_HOST_ISOLATED
+#define ICMP_HOST_ISOLATED 8
+#endif
+#ifndef ICMP_NET_ANO
+#define ICMP_NET_ANO 9
+#endif
+#ifndef ICMP_HOST_ANO
+#define ICMP_HOST_ANO 10
+#endif
+#ifndef ICMP_NET_UNR_TOS
+#define ICMP_NET_UNR_TOS 11
+#endif
+#ifndef ICMP_HOST_UNR_TOS
+#define ICMP_HOST_UNR_TOS 12
+#endif
+#ifndef ICMP_PKT_FILTERED
+#define ICMP_PKT_FILTERED 13 /* Packet filtered */
+#endif
+#ifndef ICMP_PREC_VIOLATION
+#define ICMP_PREC_VIOLATION 14 /* Precedence violation */
+
+#endif
+#ifndef ICMP_PREC_CUTOFF
+#define ICMP_PREC_CUTOFF 15 /* Precedence cut off */
+#endif
+#ifndef NR_ICMP_UNREACH
+#define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */
+#endif
+
+/* Codes for REDIRECT. */
+#ifndef ICMP_REDIR_NET
+#define ICMP_REDIR_NET 0 /* Redirect Net */
+#endif
+#ifndef ICMP_REDIR_HOST
+#define ICMP_REDIR_HOST 1 /* Redirect Host */
+#endif
+#ifndef ICMP_REDIR_NETTOS
+#define ICMP_REDIR_NETTOS 2 /* Redirect Net for TOS */
+#endif
+#ifndef ICMP_REDIR_HOSTTOS
+#define ICMP_REDIR_HOSTTOS 3 /* Redirect Host for TOS */
+#endif
+
+/* Codes for TIME_EXCEEDED. */
+#ifndef ICMP_EXC_TTL
+#define ICMP_EXC_TTL 0 /* TTL count exceeded */
+#endif
+#ifndef ICMP_EXC_FRAGTIME
+#define ICMP_EXC_FRAGTIME 1 /* Fragment Reass time exceeded */
+#endif
+
+/** marco for icmpv4 type access */
+#define ICMPV4_GET_TYPE(p) (p)->icmpv4h->type
+/** marco for icmpv4 code access */
+#define ICMPV4_GET_CODE(p) (p)->icmpv4h->code
+
+/* ICMPv4 header structure */
+typedef struct ICMPV4Hdr_
+{
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+} __attribute__((__packed__)) ICMPV4Hdr;
+
+/* ICMPv4 header structure */
+typedef struct ICMPV4ExtHdr_
+{
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ uint16_t id;
+ uint16_t seq;
+} ICMPV4ExtHdr;
+
+/* ICMPv4 vars */
+typedef struct ICMPV4Vars_
+{
+ uint16_t id;
+ uint16_t seq;
+ uint32_t mtu;
+ uint32_t error_ptr;
+
+ /** Pointers to the embedded packet headers */
+ IPV4Hdr *emb_ipv4h;
+ TCPHdr *emb_tcph;
+ UDPHdr *emb_udph;
+ ICMPV4Hdr *emb_icmpv4h;
+
+ /** IPv4 src and dst address */
+ struct in_addr emb_ip4_src;
+ struct in_addr emb_ip4_dst;
+ uint8_t emb_ip4_hlen;
+ uint8_t emb_ip4_proto;
+
+ /** TCP/UDP ports */
+ uint16_t emb_sport;
+ uint16_t emb_dport;
+} ICMPV4Vars;
+
+#define CLEAR_ICMPV4_PACKET(p) do { \
+ (p)->level4_comp_csum = -1; \
+ (p)->icmpv4vars.id = 0; \
+ (p)->icmpv4vars.seq = 0; \
+ (p)->icmpv4vars.mtu = 0; \
+ (p)->icmpv4vars.error_ptr = 0; \
+ (p)->icmpv4vars.emb_ipv4h = NULL; \
+ (p)->icmpv4vars.emb_tcph = NULL; \
+ (p)->icmpv4vars.emb_udph = NULL; \
+ (p)->icmpv4vars.emb_icmpv4h = NULL; \
+ (p)->icmpv4vars.emb_ip4_src.s_addr = 0; \
+ (p)->icmpv4vars.emb_ip4_dst.s_addr = 0; \
+ (p)->icmpv4vars.emb_sport = 0; \
+ (p)->icmpv4vars.emb_ip4_proto = 0; \
+ (p)->icmpv4vars.emb_sport = 0; \
+ (p)->icmpv4vars.emb_dport = 0; \
+ (p)->icmpv4h = NULL; \
+} while(0)
+
+#define ICMPV4_HEADER_PKT_OFFSET 8
+
+/** macro for icmpv4 "type" access */
+#define ICMPV4_GET_TYPE(p) (p)->icmpv4h->type
+/** macro for icmpv4 "code" access */
+#define ICMPV4_GET_CODE(p) (p)->icmpv4h->code
+/** macro for icmpv4 "csum" access */
+#define ICMPV4_GET_CSUM(p) (p)->icmpv4h->csum
+
+/* If message is informational */
+
+/** macro for icmpv4 "id" access */
+#define ICMPV4_GET_ID(p) ((p)->icmpv4vars.id)
+/** macro for icmpv4 "seq" access */
+#define ICMPV4_GET_SEQ(p) ((p)->icmpv4vars.seq)
+
+/* If message is Error */
+
+/** macro for icmpv4 "unused" access */
+#define ICMPV4_GET_UNUSED(p) (p)->icmpv4h->icmpv4b.icmpv4e.unused
+/** macro for icmpv4 "error_ptr" access */
+#define ICMPV4_GET_ERROR_PTR(p) (p)->icmpv4h->icmpv4b.icmpv4e.error_ptr
+/** macro for icmpv4 "mtu" access */
+#define ICMPV4_GET_MTU(p) (p)->icmpv4h->icmpv4b.icmpv4e.mtu
+
+/** macro for icmpv4 embedded "protocol" access */
+#define ICMPV4_GET_EMB_PROTO(p) (p)->icmpv4vars.emb_ip4_proto
+/** macro for icmpv4 embedded "ipv4h" header access */
+#define ICMPV4_GET_EMB_IPV4(p) (p)->icmpv4vars.emb_ipv4h
+/** macro for icmpv4 embedded "tcph" header access */
+#define ICMPV4_GET_EMB_TCP(p) (p)->icmpv4vars.emb_tcph
+/** macro for icmpv4 embedded "udph" header access */
+#define ICMPV4_GET_EMB_UDP(p) (p)->icmpv4vars.emb_udph
+/** macro for icmpv4 embedded "icmpv4h" header access */
+#define ICMPV4_GET_EMB_ICMPV4H(p) (p)->icmpv4vars.emb_icmpv4h
+
+/** macro for checking if a ICMP DEST UNREACH packet is valid for use
+ * in other parts of the engine, such as the flow engine.
+ *
+ * \warning use only _after_ the decoder has processed the packet
+ */
+#define ICMPV4_DEST_UNREACH_IS_VALID(p) (((p)->icmpv4h != NULL) && \
+ (ICMPV4_GET_TYPE((p)) == ICMP_DEST_UNREACH) && \
+ (ICMPV4_GET_EMB_IPV4((p)) != NULL) && \
+ ((ICMPV4_GET_EMB_TCP((p)) != NULL) || \
+ (ICMPV4_GET_EMB_UDP((p)) != NULL)))
+
+/**
+ * marco for checking if a ICMP packet is an error message or an
+ * query message.
+ *
+ * \todo This check is used in the flow engine and needs to be as
+ * cheap as possible. Consider setting a bitflag at the decoder
+ * stage so we can to a bit check instead of the more expensive
+ * check below.
+ */
+#define ICMPV4_IS_ERROR_MSG(p) (ICMPV4_GET_TYPE((p)) == ICMP_DEST_UNREACH || \
+ ICMPV4_GET_TYPE((p)) == ICMP_SOURCE_QUENCH || \
+ ICMPV4_GET_TYPE((p)) == ICMP_REDIRECT || \
+ ICMPV4_GET_TYPE((p)) == ICMP_TIME_EXCEEDED || \
+ ICMPV4_GET_TYPE((p)) == ICMP_PARAMETERPROB)
+
+typedef struct ICMPV4Cache_ {
+} ICMPV4Cache;
+
+void DecodeICMPV4RegisterTests(void);
+
+/** ------ Inline functions ------ */
+static inline uint16_t ICMPV4CalculateChecksum(uint16_t *, uint16_t);
+
+/**
+ * \brief Calculates the checksum for the ICMP packet
+ *
+ * \param pkt Pointer to the start of the ICMP packet
+ * \param hlen Total length of the ICMP packet(header + payload)
+ *
+ * \retval csum Checksum for the ICMP packet
+ */
+static inline uint16_t ICMPV4CalculateChecksum(uint16_t *pkt, uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = pkt[0];
+
+ tlen -= 4;
+ pkt += 2;
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ tlen -= 2;
+ pkt += 1;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ return (uint16_t) ~csum;
+}
+
+#endif /* __DECODE_ICMPV4_H__ */
+
diff --git a/framework/src/suricata/src/decode-icmpv6.c b/framework/src/suricata/src/decode-icmpv6.c
new file mode 100644
index 00000000..7972ea79
--- /dev/null
+++ b/framework/src/suricata/src/decode-icmpv6.c
@@ -0,0 +1,1642 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode ICMPv6
+ */
+
+#include "suricata-common.h"
+#include "decode-icmpv6.h"
+#include "decode.h"
+#include "decode-tcp.h"
+#include "decode-sctp.h"
+#include "decode-udp.h"
+#include "decode-events.h"
+#include "util-unittest.h"
+#include "flow.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+#include "pkt-var.h"
+#include "util-profiling.h"
+#include "host.h"
+
+
+/**
+ * \brief Get variables and do some checks of the embedded IPV6 packet
+ *
+ * \param p Pointer to the packet we are filling
+ * \param partial_packet Pointer to the raw packet buffer
+ * \param len the len of the rest of the packet not processed yet
+ *
+ * \retval void No return value
+ */
+void DecodePartialIPV6(Packet *p, uint8_t *partial_packet, uint16_t len )
+{
+ /** Check the sizes, the header must fit at least */
+ if (len < IPV6_HEADER_LEN) {
+ SCLogDebug("ICMPV6_IPV6_TRUNC_PKT");
+ ENGINE_SET_INVALID_EVENT(p, ICMPV6_IPV6_TRUNC_PKT);
+ return;
+ }
+
+ IPV6Hdr *icmp6_ip6h = (IPV6Hdr*)partial_packet;
+
+ /** Check the embedded version */
+ if(((icmp6_ip6h->s_ip6_vfc & 0xf0) >> 4) != 6)
+ {
+ SCLogDebug("ICMPv6 contains Unknown IPV6 version "
+ "ICMPV6_IPV6_UNKNOWN_VER");
+ ENGINE_SET_INVALID_EVENT(p, ICMPV6_IPV6_UNKNOWN_VER);
+ return;
+ }
+
+ /** We need to fill icmpv6vars */
+ p->icmpv6vars.emb_ipv6h = icmp6_ip6h;
+
+ /** Get the IP6 address */
+ p->icmpv6vars.emb_ip6_src[0] = icmp6_ip6h->s_ip6_src[0];
+ p->icmpv6vars.emb_ip6_src[1] = icmp6_ip6h->s_ip6_src[1];
+ p->icmpv6vars.emb_ip6_src[2] = icmp6_ip6h->s_ip6_src[2];
+ p->icmpv6vars.emb_ip6_src[3] = icmp6_ip6h->s_ip6_src[3];
+
+ p->icmpv6vars.emb_ip6_dst[0] = icmp6_ip6h->s_ip6_dst[0];
+ p->icmpv6vars.emb_ip6_dst[1] = icmp6_ip6h->s_ip6_dst[1];
+ p->icmpv6vars.emb_ip6_dst[2] = icmp6_ip6h->s_ip6_dst[2];
+ p->icmpv6vars.emb_ip6_dst[3] = icmp6_ip6h->s_ip6_dst[3];
+
+ /** Get protocol and ports inside the embedded ipv6 packet and set the pointers */
+ p->icmpv6vars.emb_ip6_proto_next = icmp6_ip6h->s_ip6_nxt;
+
+ switch (icmp6_ip6h->s_ip6_nxt) {
+ case IPPROTO_TCP:
+ if (len >= IPV6_HEADER_LEN + TCP_HEADER_LEN ) {
+ p->icmpv6vars.emb_tcph = (TCPHdr*)(partial_packet + IPV6_HEADER_LEN);
+ p->icmpv6vars.emb_sport = p->icmpv6vars.emb_tcph->th_sport;
+ p->icmpv6vars.emb_dport = p->icmpv6vars.emb_tcph->th_dport;
+
+ SCLogDebug("ICMPV6->IPV6->TCP header sport: "
+ "%"PRIu8" dport %"PRIu8"", p->icmpv6vars.emb_sport,
+ p->icmpv6vars.emb_dport);
+ } else {
+ SCLogDebug("Warning, ICMPV6->IPV6->TCP "
+ "header Didn't fit in the packet!");
+ p->icmpv6vars.emb_sport = 0;
+ p->icmpv6vars.emb_dport = 0;
+ }
+
+ break;
+ case IPPROTO_UDP:
+ if (len >= IPV6_HEADER_LEN + UDP_HEADER_LEN ) {
+ p->icmpv6vars.emb_udph = (UDPHdr*)(partial_packet + IPV6_HEADER_LEN);
+ p->icmpv6vars.emb_sport = p->icmpv6vars.emb_udph->uh_sport;
+ p->icmpv6vars.emb_dport = p->icmpv6vars.emb_udph->uh_dport;
+
+ SCLogDebug("ICMPV6->IPV6->UDP header sport: "
+ "%"PRIu8" dport %"PRIu8"", p->icmpv6vars.emb_sport,
+ p->icmpv6vars.emb_dport);
+ } else {
+ SCLogDebug("Warning, ICMPV6->IPV6->UDP "
+ "header Didn't fit in the packet!");
+ p->icmpv6vars.emb_sport = 0;
+ p->icmpv6vars.emb_dport = 0;
+ }
+
+ break;
+ case IPPROTO_ICMPV6:
+ p->icmpv6vars.emb_icmpv6h = (ICMPV6Hdr*)(partial_packet + IPV6_HEADER_LEN);
+ p->icmpv6vars.emb_sport = 0;
+ p->icmpv6vars.emb_dport = 0;
+
+ SCLogDebug("ICMPV6->IPV6->ICMP header");
+
+ break;
+ }
+
+ /* debug print */
+#ifdef DEBUG
+ char s[46], d[46];
+ PrintInet(AF_INET6, (const void *)p->icmpv6vars.emb_ip6_src, s, sizeof(s));
+ PrintInet(AF_INET6, (const void *)p->icmpv6vars.emb_ip6_dst, d, sizeof(d));
+ SCLogDebug("ICMPv6 embedding IPV6 %s->%s - CLASS: %" PRIu32 " FLOW: "
+ "%" PRIu32 " NH: %" PRIu32 " PLEN: %" PRIu32 " HLIM: %" PRIu32,
+ s, d, IPV6_GET_RAW_CLASS(icmp6_ip6h), IPV6_GET_RAW_FLOW(icmp6_ip6h),
+ IPV6_GET_RAW_NH(icmp6_ip6h), IPV6_GET_RAW_PLEN(icmp6_ip6h), IPV6_GET_RAW_HLIM(icmp6_ip6h));
+#endif
+
+ return;
+}
+
+/**
+ * \brief Decode ICMPV6 packets and fill the Packet with the decoded info
+ *
+ * \param tv Pointer to the thread variables
+ * \param dtv Pointer to the decode thread variables
+ * \param p Pointer to the packet we are filling
+ * \param pkt Pointer to the raw packet buffer
+ * \param len the len of the rest of the packet not processed yet
+ * \param pq the packet queue were this packet go
+ *
+ * \retval void No return value
+ */
+int DecodeICMPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+ uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ int full_hdr = 0;
+ StatsIncr(tv, dtv->counter_icmpv6);
+
+ if (len < ICMPV6_HEADER_LEN) {
+ SCLogDebug("ICMPV6_PKT_TOO_SMALL");
+ ENGINE_SET_INVALID_EVENT(p, ICMPV6_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->icmpv6h = (ICMPV6Hdr *)pkt;
+ p->proto = IPPROTO_ICMPV6;
+ p->type = p->icmpv6h->type;
+ p->code = p->icmpv6h->code;
+ p->payload_len = len - ICMPV6_HEADER_LEN;
+ p->payload = pkt + ICMPV6_HEADER_LEN;
+
+ SCLogDebug("ICMPV6 TYPE %" PRIu32 " CODE %" PRIu32 "", p->icmpv6h->type,
+ p->icmpv6h->code);
+
+ switch (ICMPV6_GET_TYPE(p)) {
+ case ICMP6_DST_UNREACH:
+ SCLogDebug("ICMP6_DST_UNREACH");
+
+ if (ICMPV6_GET_CODE(p) > ICMP6_DST_UNREACH_REJECTROUTE) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ DecodePartialIPV6(p, (uint8_t*) (pkt + ICMPV6_HEADER_LEN),
+ len - ICMPV6_HEADER_LEN );
+ full_hdr = 1;
+ }
+
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ SCLogDebug("ICMP6_PACKET_TOO_BIG");
+
+ if (ICMPV6_GET_CODE(p) != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ p->icmpv6vars.mtu = ICMPV6_GET_MTU(p);
+ DecodePartialIPV6(p, (uint8_t*) (pkt + ICMPV6_HEADER_LEN),
+ len - ICMPV6_HEADER_LEN );
+ full_hdr = 1;
+ }
+
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ SCLogDebug("ICMP6_TIME_EXCEEDED");
+
+ if (ICMPV6_GET_CODE(p) > ICMP6_TIME_EXCEED_REASSEMBLY) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ DecodePartialIPV6(p, (uint8_t*) (pkt + ICMPV6_HEADER_LEN),
+ len - ICMPV6_HEADER_LEN );
+ full_hdr = 1;
+ }
+
+ break;
+ case ICMP6_PARAM_PROB:
+ SCLogDebug("ICMP6_PARAM_PROB");
+
+ if (ICMPV6_GET_CODE(p) > ICMP6_PARAMPROB_OPTION) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ p->icmpv6vars.error_ptr= ICMPV6_GET_ERROR_PTR(p);
+ DecodePartialIPV6(p, (uint8_t*) (pkt + ICMPV6_HEADER_LEN),
+ len - ICMPV6_HEADER_LEN );
+ full_hdr = 1;
+ }
+
+ break;
+ case ICMP6_ECHO_REQUEST:
+ SCLogDebug("ICMP6_ECHO_REQUEST id: %u seq: %u",
+ p->icmpv6h->icmpv6b.icmpv6i.id, p->icmpv6h->icmpv6b.icmpv6i.seq);
+
+ if (ICMPV6_GET_CODE(p) != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ p->icmpv6vars.id = p->icmpv6h->icmpv6b.icmpv6i.id;
+ p->icmpv6vars.seq = p->icmpv6h->icmpv6b.icmpv6i.seq;
+ full_hdr = 1;
+ }
+
+ break;
+ case ICMP6_ECHO_REPLY:
+ SCLogDebug("ICMP6_ECHO_REPLY id: %u seq: %u",
+ p->icmpv6h->icmpv6b.icmpv6i.id, p->icmpv6h->icmpv6b.icmpv6i.seq);
+
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ } else {
+ p->icmpv6vars.id = p->icmpv6h->icmpv6b.icmpv6i.id;
+ p->icmpv6vars.seq = p->icmpv6h->icmpv6b.icmpv6i.seq;
+ full_hdr = 1;
+ }
+
+ break;
+ case ND_ROUTER_SOLICIT:
+ SCLogDebug("ND_ROUTER_SOLICIT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ break;
+ case ND_ROUTER_ADVERT:
+ SCLogDebug("ND_ROUTER_ADVERT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ SCLogDebug("ND_NEIGHBOR_SOLICIT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ SCLogDebug("ND_NEIGHBOR_ADVERT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ break;
+ case ND_REDIRECT:
+ SCLogDebug("ND_REDIRECT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ break;
+ case MLD_LISTENER_QUERY:
+ SCLogDebug("MLD_LISTENER_QUERY");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ if (IPV6_GET_HLIM(p) != 1) {
+ ENGINE_SET_EVENT(p, ICMPV6_MLD_MESSAGE_WITH_INVALID_HL);
+ }
+ break;
+ case MLD_LISTENER_REPORT:
+ SCLogDebug("MLD_LISTENER_REPORT");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ if (IPV6_GET_HLIM(p) != 1) {
+ ENGINE_SET_EVENT(p, ICMPV6_MLD_MESSAGE_WITH_INVALID_HL);
+ }
+ break;
+ case MLD_LISTENER_REDUCTION:
+ SCLogDebug("MLD_LISTENER_REDUCTION");
+ if (p->icmpv6h->code != 0) {
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_CODE);
+ }
+ if (IPV6_GET_HLIM(p) != 1) {
+ ENGINE_SET_EVENT(p, ICMPV6_MLD_MESSAGE_WITH_INVALID_HL);
+ }
+ break;
+ default:
+ SCLogDebug("ICMPV6 Message type %" PRIu8 " not "
+ "implemented yet", ICMPV6_GET_TYPE(p));
+ ENGINE_SET_EVENT(p, ICMPV6_UNKNOWN_TYPE);
+ }
+
+ /* for a info message the header is just 4 bytes */
+ if (!full_hdr) {
+ if (p->payload_len >= 4) {
+ p->payload_len -= 4;
+ p->payload = pkt + 4;
+ } else {
+ p->payload_len = 0;
+ p->payload = NULL;
+ }
+ }
+
+#ifdef DEBUG
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE))
+ SCLogDebug("Unknown Code, ICMPV6_UNKNOWN_CODE");
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_TYPE))
+ SCLogDebug("Unknown Type, ICMPV6_UNKNOWN_TYPE");
+#endif
+
+ /* Flow is an integral part of us */
+ FlowHandlePacket(tv, dtv, p);
+
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+static int ICMPV6CalculateValidChecksumtest01(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x00};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 56)));
+
+ return (csum == ICMPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 68));
+}
+
+static int ICMPV6CalculateInvalidChecksumtest02(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x01};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 56)));
+
+ return (csum == ICMPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 68));
+}
+
+
+/** \test icmpv6 message type: parameter problem, valid packet
+ *
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6ParamProbTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x38, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0xcc, 0x2a, 0x6d, 0x93, 0x0b, 0xdf,
+ 0x69, 0x70, 0x12, 0xb7, 0x00, 0x08, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x80, 0x00, 0x08, 0xb5, 0x99, 0xc3, 0xde, 0x40 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ uint32_t *ipv6src;
+ uint32_t *ipv6dst;
+ ipv6src = (uint32_t*) &raw_ipv6[8];
+ ipv6dst = (uint32_t*) &raw_ipv6[24];
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ if (ICMPV6_GET_TYPE(p) != 4 || ICMPV6_GET_CODE(p) != 0 ||
+ ICMPV6_GET_EMB_PROTO(p) != IPPROTO_ICMPV6) {
+ SCLogDebug("ICMPv6 not processed at all");
+ retval = 0;
+ goto end;
+ }
+
+ /* Let's check if we retrieved the embedded ipv6 addresses correctly */
+ uint32_t i=0;
+ for (i = 0; i < 4; i++) {
+ if (p->icmpv6vars.emb_ip6_src[i] != ipv6src[i] ||
+ p->icmpv6vars.emb_ip6_dst[i] != ipv6dst[i]) {
+ SCLogDebug("ICMPv6 DecodePartialICMPV6 (Embedded ip6h) didn't set "
+ "the src and dest ip addresses correctly");
+ retval = 0;
+ goto end;
+ }
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: packet too big, valid packet
+ *
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6PktTooBigTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x5c, 0x7a, 0x00, 0x00, 0x05, 0x00,
+ 0x64, 0x14, 0xfd, 0xff, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ uint32_t *ipv6src;
+ uint32_t *ipv6dst;
+ ipv6src = (uint32_t*) &raw_ipv6[8];
+ ipv6dst = (uint32_t*) &raw_ipv6[24];
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ /* Note: it has an embedded ipv6 packet but no protocol after ipv6 (IPPROTO_NONE) */
+ if (ICMPV6_GET_TYPE(p) != 2 || ICMPV6_GET_CODE(p) != 0 ) {
+ SCLogDebug("ICMPv6 Not processed at all");
+ retval = 0;
+ goto end;
+ }
+
+ /* Let's check if we retrieved the embedded ipv6 addresses correctly */
+ uint32_t i=0;
+ for (i = 0; i < 4; i++) {
+ if (p->icmpv6vars.emb_ip6_src[i] != ipv6src[i] ||
+ p->icmpv6vars.emb_ip6_dst[i] != ipv6dst[i]) {
+ SCLogDebug("ICMPv6 DecodePartialICMPV6 (Embedded ip6h) didn't set "
+ "the src and dest ip addresses correctly");
+ retval = 0;
+ goto end;
+ }
+ }
+
+ SCLogDebug("ICMPV6 IPV6 src and dst properly set");
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: time exceed, valid packet
+ *
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6TimeExceedTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x56, 0x2d, 0x00, 0x00, 0x00, 0x00,
+ 0x6d, 0x23, 0xff, 0x3d, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ uint32_t *ipv6src;
+ uint32_t *ipv6dst;
+ ipv6src = (uint32_t*) &raw_ipv6[8];
+ ipv6dst = (uint32_t*) &raw_ipv6[24];
+
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ /* Note: it has an embedded ipv6 packet but no protocol after ipv6 (IPPROTO_NONE) */
+ if (ICMPV6_GET_TYPE(p) != 3 || ICMPV6_GET_CODE(p) != 0 ||
+ ICMPV6_GET_EMB_IPV6(p)==NULL || ICMPV6_GET_EMB_PROTO(p) != IPPROTO_NONE ) {
+ SCLogDebug("ICMPv6 Not processed at all");
+ retval = 0;
+ goto end;
+ }
+
+ /* Let's check if we retrieved the embedded ipv6 addresses correctly */
+ uint32_t i=0;
+ for (i = 0; i < 4; i++) {
+ if (p->icmpv6vars.emb_ip6_src[i] != ipv6src[i] ||
+ p->icmpv6vars.emb_ip6_dst[i] != ipv6dst[i]) {
+ SCLogDebug("ICMPv6 DecodePartialICMPV6 (Embedded ip6h) didn't set "
+ "the src and dest ip addresses correctly");
+ retval = 0;
+ goto end;
+ }
+ }
+
+ SCLogDebug("ICMPV6 IPV6 src and dst properly set");
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: destination unreach, valid packet
+ *
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6DestUnreachTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x7b, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x4b, 0xe8, 0xbd, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ uint32_t *ipv6src;
+ uint32_t *ipv6dst;
+ ipv6src = (uint32_t*) &raw_ipv6[8];
+ ipv6dst = (uint32_t*) &raw_ipv6[24];
+
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ /* Note: it has an embedded ipv6 packet but no protocol after ipv6 (IPPROTO_NONE) */
+ if (ICMPV6_GET_TYPE(p) != 1 || ICMPV6_GET_CODE(p) != 0 ||
+ ICMPV6_GET_EMB_IPV6(p) == NULL || ICMPV6_GET_EMB_PROTO(p) != IPPROTO_NONE ) {
+ SCLogDebug("ICMPv6 Not processed at all");
+ retval = 0;
+ goto end;
+ }
+
+ /* Let's check if we retrieved the embedded ipv6 addresses correctly */
+ uint32_t i=0;
+ for (i = 0; i < 4; i++) {
+ if (p->icmpv6vars.emb_ip6_src[i] != ipv6src[i] ||
+ p->icmpv6vars.emb_ip6_dst[i] != ipv6dst[i]) {
+ SCLogDebug("ICMPv6 DecodePartialICMPV6 (Embedded ip6h) didn't set "
+ "the src and dest ip addresses correctly");
+ retval = 0;
+ goto end;
+ }
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 message type: echo request, valid packet
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6EchoReqTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x80, 0x00, 0xe5, 0xa5, 0x25, 0xf0, 0x75, 0x23 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ goto end;
+ }
+
+ SCLogDebug("ID: %u seq: %u", ICMPV6_GET_ID(p), ICMPV6_GET_SEQ(p));
+
+ if (ICMPV6_GET_TYPE(p) != 128 || ICMPV6_GET_CODE(p) != 0 ||
+ ntohs(ICMPV6_GET_ID(p)) != 9712 || ntohs(ICMPV6_GET_SEQ(p)) != 29987) {
+ printf("ICMPv6 Echo reply decode failed TYPE %u CODE %u ID %04x(%u) SEQ %04x(%u): ",
+ ICMPV6_GET_TYPE(p), ICMPV6_GET_CODE(p), ICMPV6_GET_ID(p), ntohs(ICMPV6_GET_ID(p)),
+ ICMPV6_GET_SEQ(p), ntohs(ICMPV6_GET_SEQ(p)));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 message type: echo reply, valid packet
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6EchoRepTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0x00,
+ 0xe5, 0xa5, 0x25, 0xf0, 0x75, 0x23 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ goto end;
+ }
+
+ SCLogDebug("type: %u code %u ID: %u seq: %u", ICMPV6_GET_TYPE(p),
+ ICMPV6_GET_CODE(p),ICMPV6_GET_ID(p), ICMPV6_GET_SEQ(p));
+
+ if (ICMPV6_GET_TYPE(p) != 129 || ICMPV6_GET_CODE(p) != 0 ||
+ ntohs(ICMPV6_GET_ID(p)) != 9712 || ntohs(ICMPV6_GET_SEQ(p)) != 29987) {
+ printf("ICMPv6 Echo reply decode failed TYPE %u CODE %u ID %04x(%u) SEQ %04x(%u): ",
+ ICMPV6_GET_TYPE(p), ICMPV6_GET_CODE(p), ICMPV6_GET_ID(p), ntohs(ICMPV6_GET_ID(p)),
+ ICMPV6_GET_SEQ(p), ntohs(ICMPV6_GET_SEQ(p)));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: parameter problem, invalid packet
+ * \brief set the event ICMPV6_IPV6_UNKNOWN_VER properly when the embedded packet has an unknown version
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6ParamProbTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x38, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0xcc, 0x2a, 0x6d, 0x93, 0x0b, 0xdf,
+ 0x38, 0x70, 0x12, 0xb7, 0x00, 0x08, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x80, 0x00, 0x08, 0xb5, 0x99, 0xc3, 0xde, 0x40 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ if (ICMPV6_GET_TYPE(p) != 4 || ICMPV6_GET_CODE(p) != 0) {
+ SCLogDebug("ICMPv6 Not processed at all");
+ retval = 0;
+ goto end;
+ }
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_IPV6_UNKNOWN_VER)) {
+ SCLogDebug("ICMPv6 Error: Unknown embedded ipv6 version event not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: packet too big, invalid packet
+ * \brief Set the event ICMPV6_UNKNOWN_CODE if code is invalid for this type
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6PktTooBigTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x10, 0x5c, 0x7a, 0x00, 0x00, 0x05, 0x00,
+ 0x64, 0x14, 0xfd, 0xff, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->icmpv6h == NULL) {
+ SCLogDebug("ICMPv6 Unable to detect icmpv6 layer from ipv6");
+ retval = 0;
+ goto end;
+ }
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test icmpv6 message type: time exceed, invalid packet
+ * \brief set the event ICMPV6_PKT_TOO_SMALL properly
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6TimeExceedTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x10, 0x5c };
+
+ /* The icmpv6 header is broken in the checksum (so we dont have a complete header) */
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_PKT_TOO_SMALL)) {
+ SCLogDebug("ICMPv6 Error: event packet too small not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 message type: destination unreach, invalid packet
+ * \brief The embedded packet header (ipv6) is truncated
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6DestUnreachTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x7b, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x4b, 0xe8, 0xbd, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_IPV6_TRUNC_PKT)) {
+ SCLogDebug("ICMPv6 Error: embedded ipv6 truncated packet event not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 message type: echo request, invalid packet
+ * \brief unknown code
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6EchoReqTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01,
+ 0xe5, 0xa5, 0x25, 0xf0, 0x75, 0x23 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 message type: echo reply, invalid packet
+ * \brief unknown code
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6EchoRepTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0x01,
+ 0xe5, 0xa5, 0x25, 0xf0, 0x75, 0x23 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/**\test icmpv6 packet decoding and setting up of payload_len and payload buufer
+ * \retval retval 0 = Error ; 1 = ok
+ */
+static int ICMPV6PayloadTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x7b, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x4b, 0xe8, 0xbd, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (p->payload == NULL) {
+ printf("payload == NULL, expected non-NULL: ");
+ goto end;
+ }
+
+ if (p->payload_len != 37) {
+ printf("payload_len %"PRIu16", expected 37: ", p->payload_len);
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RouterSolicitTestKnownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x85, 0x00, 0xbe, 0xb0, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RouterSolicitTestUnknownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x85, 0x01, 0xbe, 0xaf, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RouterAdvertTestKnownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x86, 0x00, 0xbd, 0xb0, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RouterAdvertTestUnknownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x86, 0x01, 0xbd, 0xaf, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6NeighbourSolicitTestKnownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x87, 0x00, 0xbc, 0xb0, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6NeighbourSolicitTestUnknownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x87, 0x01, 0xbc, 0xaf, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6NeighbourAdvertTestKnownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x88, 0x00, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6NeighbourAdvertTestUnknownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x88, 0x01, 0xbb, 0xaf, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RedirectTestKnownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x89, 0x00, 0xba, 0xb0, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int ICMPV6RedirectTestUnknownCode(void)
+{
+ int retval = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x24, 0x8c, 0xff, 0xfe, 0x0e, 0x31, 0x54,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x89, 0x01, 0xba, 0xaf, 0x00, 0x00, 0x00, 0x00
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeIPV6(&tv, &dtv, p, raw_ipv6, sizeof(raw_ipv6), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, ICMPV6_UNKNOWN_CODE)) {
+ SCLogDebug("ICMPv6 Error: Unknown code event is not set");
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+#endif /* UNITTESTS */
+/**
+ * \brief Registers ICMPV6 unit tests
+ * \todo More ICMPv6 tests
+ */
+void DecodeICMPV6RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ICMPV6CalculateValidChecksumtest01", ICMPV6CalculateValidChecksumtest01, 1);
+ UtRegisterTest("ICMPV6CalculateInValidChecksumtest02", ICMPV6CalculateInvalidChecksumtest02, 0);
+
+ UtRegisterTest("ICMPV6ParamProbTest01 (Valid)", ICMPV6ParamProbTest01, 1);
+ UtRegisterTest("ICMPV6DestUnreachTest01 (Valid)", ICMPV6DestUnreachTest01, 1);
+ UtRegisterTest("ICMPV6PktTooBigTest01 (Valid)", ICMPV6PktTooBigTest01, 1);
+ UtRegisterTest("ICMPV6TimeExceedTest01 (Valid)", ICMPV6TimeExceedTest01, 1);
+ UtRegisterTest("ICMPV6EchoReqTest01 (Valid)", ICMPV6EchoReqTest01, 1);
+ UtRegisterTest("ICMPV6EchoRepTest01 (Valid)", ICMPV6EchoRepTest01, 1);
+
+ UtRegisterTest("ICMPV6ParamProbTest02 (Invalid)", ICMPV6ParamProbTest02, 1);
+ UtRegisterTest("ICMPV6DestUnreachTest02 (Invalid)", ICMPV6DestUnreachTest02, 1);
+ UtRegisterTest("ICMPV6PktTooBigTest02 (Invalid)", ICMPV6PktTooBigTest02, 1);
+ UtRegisterTest("ICMPV6TimeExceedTest02 (Invalid)", ICMPV6TimeExceedTest02, 1);
+ UtRegisterTest("ICMPV6EchoReqTest02 (Invalid)", ICMPV6EchoReqTest02, 1);
+ UtRegisterTest("ICMPV6EchoRepTest02 (Invalid)", ICMPV6EchoRepTest02, 1);
+
+ UtRegisterTest("ICMPV6PayloadTest01", ICMPV6PayloadTest01, 1);
+
+ UtRegisterTest("ICMPV6RouterSolicitTestKnownCode",
+ ICMPV6RouterSolicitTestKnownCode, 1);
+ UtRegisterTest("ICMPV6RouterSolicitTestUnknownCode",
+ ICMPV6RouterSolicitTestUnknownCode, 1);
+ UtRegisterTest("ICMPV6RouterAdvertTestKnownCode",
+ ICMPV6RouterAdvertTestKnownCode, 1);
+ UtRegisterTest("ICMPV6RouterAdvertTestUnknownCode",
+ ICMPV6RouterAdvertTestUnknownCode, 1);
+
+ UtRegisterTest("ICMPV6NeighbourSolicitTestKnownCode",
+ ICMPV6NeighbourSolicitTestKnownCode, 1);
+ UtRegisterTest("ICMPV6NeighbourSolicitTestUnknownCode",
+ ICMPV6NeighbourSolicitTestUnknownCode, 1);
+ UtRegisterTest("ICMPV6NeighbourAdvertTestKnownCode",
+ ICMPV6NeighbourAdvertTestKnownCode, 1);
+ UtRegisterTest("ICMPV6NeighbourAdvertTestUnknownCode",
+ ICMPV6NeighbourAdvertTestUnknownCode, 1);
+
+ UtRegisterTest("ICMPV6RedirectTestKnownCode", ICMPV6RedirectTestKnownCode, 1);
+ UtRegisterTest("ICMPV6RedirectTestUnknownCode",
+ ICMPV6RedirectTestUnknownCode, 1);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-icmpv6.h b/framework/src/suricata/src/decode-icmpv6.h
new file mode 100644
index 00000000..af975006
--- /dev/null
+++ b/framework/src/suricata/src/decode-icmpv6.h
@@ -0,0 +1,259 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_ICMPV6_H__
+#define __DECODE_ICMPV6_H__
+
+#include "decode-tcp.h"
+#include "decode-sctp.h"
+#include "decode-udp.h"
+#include "decode-ipv6.h"
+
+#define ICMPV6_HEADER_LEN 8
+#define ICMPV6_HEADER_PKT_OFFSET 8
+
+/** ICMPV6 Message Types: */
+/** Error Messages: (type <128) */
+#define ICMP6_DST_UNREACH 1
+#define ICMP6_PACKET_TOO_BIG 2
+#define ICMP6_TIME_EXCEEDED 3
+#define ICMP6_PARAM_PROB 4
+
+/** Informational Messages (type>=128) */
+#define ICMP6_ECHO_REQUEST 128
+#define ICMP6_ECHO_REPLY 129
+
+#define MLD_LISTENER_QUERY 130
+#define MLD_LISTENER_REPORT 131
+#define MLD_LISTENER_REDUCTION 132
+
+#define ND_ROUTER_SOLICIT 133
+#define ND_ROUTER_ADVERT 134
+#define ND_NEIGHBOR_SOLICIT 135
+#define ND_NEIGHBOR_ADVERT 136
+#define ND_REDIRECT 137
+
+/** Destination Unreachable Message (type=1) Code: */
+
+#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
+#define ICMP6_DST_UNREACH_ADMIN 1 /* communication with destination */
+ /* administratively prohibited */
+#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */
+#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */
+#define ICMP6_DST_UNREACH_NOPORT 4 /* bad port */
+#define ICMP6_DST_UNREACH_FAILEDPOLICY 5 /* Source address failed ingress/egress policy */
+#define ICMP6_DST_UNREACH_REJECTROUTE 6 /* Reject route to destination */
+
+
+/** Time Exceeded Message (type=3) Code: */
+#define ICMP6_TIME_EXCEED_TRANSIT 0 /* Hop Limit == 0 in transit */
+#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* Reassembly time out */
+
+/** Parameter Problem Message (type=4) Code: */
+#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
+#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized Next Header */
+#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized IPv6 option */
+
+
+/** macro for icmpv6 "type" access */
+#define ICMPV6_GET_TYPE(p) (p)->icmpv6h->type
+/** macro for icmpv6 "code" access */
+#define ICMPV6_GET_CODE(p) (p)->icmpv6h->code
+/** macro for icmpv6 "csum" access */
+#define ICMPV6_GET_CSUM(p) (p)->icmpv6h->csum
+
+/** If message is informational */
+/** macro for icmpv6 "id" access */
+#define ICMPV6_GET_ID(p) (p)->icmpv6vars.id
+/** macro for icmpv6 "seq" access */
+#define ICMPV6_GET_SEQ(p) (p)->icmpv6vars.seq
+
+/** If message is Error */
+/** macro for icmpv6 "unused" access */
+#define ICMPV6_GET_UNUSED(p) (p)->icmpv6h->icmpv6b.icmpv6e.unused
+/** macro for icmpv6 "error_ptr" access */
+#define ICMPV6_GET_ERROR_PTR(p) (p)->icmpv6h->icmpv6b.icmpv6e.error_ptr
+/** macro for icmpv6 "mtu" access */
+#define ICMPV6_GET_MTU(p) (p)->icmpv6h->icmpv6b.icmpv6e.mtu
+
+/** macro for icmpv6 embedded "protocol" access */
+#define ICMPV6_GET_EMB_PROTO(p) (p)->icmpv6vars.emb_ip6_proto_next
+/** macro for icmpv6 embedded "ipv6h" header access */
+#define ICMPV6_GET_EMB_IPV6(p) (p)->icmpv6vars.emb_ipv6h
+/** macro for icmpv6 embedded "tcph" header access */
+#define ICMPV6_GET_EMB_TCP(p) (p)->icmpv6vars.emb_tcph
+/** macro for icmpv6 embedded "udph" header access */
+#define ICMPV6_GET_EMB_UDP(p) (p)->icmpv6vars.emb_udph
+/** macro for icmpv6 embedded "icmpv6h" header access */
+#define ICMPV6_GET_EMB_icmpv6h(p) (p)->icmpv6vars.emb_icmpv6h
+
+typedef struct ICMPV6Info_
+{
+ uint16_t id;
+ uint16_t seq;
+} ICMPV6Info;
+
+/** ICMPv6 header structure */
+typedef struct ICMPV6Hdr_
+{
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+
+ union {
+ ICMPV6Info icmpv6i; /** Informational message */
+ union
+ {
+ uint32_t unused; /** for types 1 and 3, should be zero */
+ uint32_t error_ptr; /** for type 4, pointer to the octet that originate the error */
+ uint32_t mtu; /** for type 2, the Maximum Transmission Unit of the next-hop link */
+ } icmpv6e; /** Error Message */
+ } icmpv6b;
+} ICMPV6Hdr;
+
+/** Data available from the decoded packet */
+typedef struct ICMPV6Vars_ {
+ /* checksum of the icmpv6 packet */
+ uint16_t id;
+ uint16_t seq;
+ uint32_t mtu;
+ uint32_t error_ptr;
+
+ /** Pointers to the embedded packet headers */
+ IPV6Hdr *emb_ipv6h;
+ TCPHdr *emb_tcph;
+ UDPHdr *emb_udph;
+ ICMPV6Hdr *emb_icmpv6h;
+
+ /** IPv6 src and dst address */
+ uint32_t emb_ip6_src[4];
+ uint32_t emb_ip6_dst[4];
+ uint8_t emb_ip6_proto_next;
+
+ /** TCP/UDP ports */
+ uint16_t emb_sport;
+ uint16_t emb_dport;
+
+} ICMPV6Vars;
+
+
+#define CLEAR_ICMPV6_PACKET(p) do { \
+ (p)->level4_comp_csum = -1; \
+ (p)->icmpv6vars.id = 0; \
+ (p)->icmpv6vars.seq = 0; \
+ (p)->icmpv6vars.mtu = 0; \
+ (p)->icmpv6vars.error_ptr = 0; \
+ (p)->icmpv6vars.emb_ipv6h = NULL; \
+ (p)->icmpv6vars.emb_tcph = NULL; \
+ (p)->icmpv6vars.emb_udph = NULL; \
+ (p)->icmpv6vars.emb_icmpv6h = NULL; \
+ (p)->icmpv6vars.emb_ip6_src[0] = 0; \
+ (p)->icmpv6vars.emb_ip6_src[1] = 0; \
+ (p)->icmpv6vars.emb_ip6_src[2] = 0; \
+ (p)->icmpv6vars.emb_ip6_src[3] = 0; \
+ (p)->icmpv6vars.emb_ip6_proto_next = 0; \
+ (p)->icmpv6vars.emb_sport = 0; \
+ (p)->icmpv6vars.emb_dport = 0; \
+ (p)->icmpv6h = NULL; \
+} while(0)
+
+void DecodeICMPV6RegisterTests(void);
+
+/** -------- Inline functions --------- */
+static inline uint16_t ICMPV6CalculateChecksum(uint16_t *, uint16_t *, uint16_t);
+
+/**
+ * \brief Calculates the checksum for the ICMPV6 packet
+ *
+ * \param shdr Pointer to source address field from the IPV6 packet. Used as a
+ * part of the psuedoheader for computing the checksum
+ * \param pkt Pointer to the start of the ICMPV6 packet
+ * \param tlen Total length of the ICMPV6 packet(header + payload)
+ *
+ * \retval csum Checksum for the ICMPV6 packet
+ */
+static inline uint16_t ICMPV6CalculateChecksum(uint16_t *shdr, uint16_t *pkt,
+ uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = shdr[0];
+
+ csum += shdr[1] + shdr[2] + shdr[3] + shdr[4] + shdr[5] + shdr[6] +
+ shdr[7] + shdr[8] + shdr[9] + shdr[10] + shdr[11] + shdr[12] +
+ shdr[13] + shdr[14] + shdr[15] + htons(58 + tlen);
+
+ csum += pkt[0];
+
+ tlen -= 4;
+ pkt += 2;
+
+ while (tlen >= 64) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15] + pkt[16] + pkt[17] + pkt[18] + pkt[19] +
+ pkt[20] + pkt[21] + pkt[22] + pkt[23] + pkt[24] + pkt[25] +
+ pkt[26] + pkt[27] + pkt[28] + pkt[29] + pkt[30] + pkt[31];
+ tlen -= 64;
+ pkt += 32;
+ }
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ tlen -= 2;
+ pkt += 1;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ return (uint16_t) ~csum;
+}
+
+
+#endif /* __DECODE_ICMPV6_H__ */
+
diff --git a/framework/src/suricata/src/decode-ipv4.c b/framework/src/suricata/src/decode-ipv4.c
new file mode 100644
index 00000000..63aae4ed
--- /dev/null
+++ b/framework/src/suricata/src/decode-ipv4.c
@@ -0,0 +1,1913 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Decode IPv4
+ */
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "decode-ipv4.h"
+#include "decode-events.h"
+#include "defrag.h"
+#include "pkt-var.h"
+#include "host.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-optimize.h"
+#include "util-print.h"
+#include "util-profiling.h"
+
+/* Generic validation
+ *
+ * [--type--][--len---]
+ *
+ * \todo This function needs removed in favor of specific validation.
+ *
+ * See: RFC 791
+ */
+static int IPV4OptValidateGeneric(Packet *p, const IPV4Opt *o)
+{
+ switch (o->type) {
+ /* See: RFC 4782 */
+ case IPV4_OPT_QS:
+ if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len < IPV4_OPT_QS_MIN) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+ break;
+ /* See: RFC 1108 */
+ case IPV4_OPT_SEC:
+ if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_SEC_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+ break;
+ case IPV4_OPT_SID:
+ if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_SID_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+ break;
+ /* See: RFC 2113 */
+ case IPV4_OPT_RTRALT:
+ if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_RTRALT_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+ break;
+ default:
+ /* Should never get here unless there is a coding error */
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_UNKNOWN);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Validate route type options
+ *
+ * [--type--][--len---][--ptr---][address1]...[addressN]
+ *
+ * See: RFC 791
+ */
+static int IPV4OptValidateRoute(Packet *p, const IPV4Opt *o)
+{
+ uint8_t ptr;
+
+ /* Check length */
+ if (unlikely(o->len < IPV4_OPT_ROUTE_MIN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ /* Data is required */
+ if (unlikely(o->data == NULL)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+ ptr = *o->data;
+
+ /* Address pointer is 1 based and points at least after type+len+ptr,
+ * must be a incremented by 4 bytes (address size) and cannot extend
+ * past option length.
+ */
+ if (unlikely((ptr < 4) || (ptr % 4) || (ptr > o->len + 1))) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Validate timestamp type options
+ *
+ * [--type--][--len---][--ptr---][ovfl][flag][rec1----...]...[recN----...]
+ * NOTE: rec could be 4 (ts only) or 8 (ip+ts) bytes in length.
+ *
+ * See: RFC 781
+ */
+static int IPV4OptValidateTimestamp(Packet *p, const IPV4Opt *o)
+{
+ uint8_t ptr;
+ uint8_t flag;
+ uint8_t rec_size;
+
+ /* Check length */
+ if (unlikely(o->len < IPV4_OPT_TS_MIN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ /* Data is required */
+ if (unlikely(o->data == NULL)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+ ptr = *o->data;
+
+ /* We need the flag to determine what is in the option payload */
+ if (unlikely(ptr < 5)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+ flag = *(o->data + 3) & 0x00ff;
+
+ /* A flag of 1|3 means we have both the ip+ts in each record */
+ rec_size = ((flag == 1) || (flag == 3)) ? 8 : 4;
+
+ /* Address pointer is 1 based and points at least after
+ * type+len+ptr+ovfl+flag, must be incremented by by the rec_size
+ * and cannot extend past option length.
+ */
+ if (unlikely(((ptr - 5) % rec_size) || (ptr > o->len + 1))) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Validate CIPSO option
+ *
+ * [--type--][--len---][--doi---][tags--...]
+ *
+ * See: draft-ietf-cipso-ipsecurity-01.txt
+ * See: FIPS 188 (tags 6 & 7)
+ */
+static int IPV4OptValidateCIPSO(Packet *p, const IPV4Opt *o)
+{
+// uint32_t doi;
+ uint8_t *tag;
+ uint16_t len;
+
+ /* Check length */
+ if (unlikely(o->len < IPV4_OPT_CIPSO_MIN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ /* Data is required */
+ if (unlikely(o->data == NULL)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+// doi = *o->data;
+ tag = o->data + 4;
+ len = o->len - 1 - 1 - 4; /* Length of tags after header */
+
+
+#if 0
+ /* Domain of Interest (DOI) of 0 is reserved and thus invalid */
+ /** \todo Aparently a DOI of zero is fine in practice - verify. */
+ if (doi == 0) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_MALFORMED);
+ return -1;
+ }
+#endif
+
+ /* NOTE: We know len has passed min tests prior to this call */
+
+ /* Check that tags are formatted correctly
+ * [-ttype--][--tlen--][-tagdata-...]
+ */
+ while (len) {
+ uint8_t ttype;
+ uint8_t tlen;
+
+ /* Tag header must fit within option length */
+ if (unlikely(len < 2)) {
+ //printf("CIPSO tag header too large %" PRIu16 " < 2\n", len);
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ /* Tag header is type+len */
+ ttype = *(tag++);
+ tlen = *(tag++);
+
+ /* Tag length must fit within the option length */
+ if (unlikely(tlen > len)) {
+ //printf("CIPSO tag len too large %" PRIu8 " > %" PRIu16 "\n", tlen, len);
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ switch(ttype) {
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ case 7:
+ /* Tag is at least 4 and at most the remainder of option len */
+ if (unlikely((tlen < 4) || (tlen > len))) {
+ //printf("CIPSO tag %" PRIu8 " bad tlen=%" PRIu8 " len=%" PRIu8 "\n", ttype, tlen, len);
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ /* The alignment octet is always 0 except tag
+ * type 7, which has no such field.
+ */
+ if (unlikely((ttype != 7) && (*tag != 0))) {
+ //printf("CIPSO tag %" PRIu8 " ao=%" PRIu8 "\n", ttype, tlen);
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ return -1;
+ }
+
+ /* Skip the rest of the tag payload */
+ tag += tlen - 2;
+ len -= tlen;
+
+ continue;
+ case 0:
+ /* Tag type 0 is reserved and thus invalid */
+ /** \todo Wireshark marks this a padding, but spec says reserved. */
+ ENGINE_SET_INVALID_EVENT(p,IPV4_OPT_MALFORMED);
+ return -1;
+ default:
+ //printf("CIPSO tag %" PRIu8 " unknown tag\n", ttype);
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_MALFORMED);
+ /** \todo May not want to return error here on unknown tag type (at least not for 3|4) */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Decode/Validate IPv4 Options.
+ */
+static int DecodeIPV4Options(Packet *p, uint8_t *pkt, uint16_t len)
+{
+ uint16_t plen = len;
+
+ p->IPV4_OPTS_CNT = 0;
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ uint16_t i;
+ char buf[256] = "";
+ int offset = 0;
+
+ for (i = 0; i < len; i++) {
+ offset += snprintf(buf + offset, (sizeof(buf) - offset), "%02" PRIx8 " ", pkt[i]);
+ }
+ SCLogDebug("IPV4OPTS: { %s}", buf);
+ }
+#endif
+
+ /* Options length must be padded to 8byte boundary */
+ if (plen % 8) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_PAD_REQUIRED);
+ /* Warn - we can keep going */
+ }
+
+ while (plen)
+ {
+ /* single byte options */
+ if (*pkt == IPV4_OPT_EOL) {
+ /** \todo What if more data exist after EOL (possible covert channel or data leakage)? */
+ SCLogDebug("IPV4OPT %" PRIu16 " len 1 @ %" PRIu16 "/%" PRIu16 "",
+ *pkt, (len - plen), (len - 1));
+ break;
+ } else if (*pkt == IPV4_OPT_NOP) {
+ SCLogDebug("IPV4OPT %" PRIu16 " len 1 @ %" PRIu16 "/%" PRIu16 "",
+ *pkt, (len - plen), (len - 1));
+ pkt++;
+ plen--;
+
+ /* multibyte options */
+ } else {
+ if (unlikely(plen < 2)) {
+ /** \todo What if padding is non-zero (possible covert channel or data leakage)? */
+ /** \todo Spec seems to indicate EOL required if there is padding */
+ ENGINE_SET_EVENT(p,IPV4_OPT_EOL_REQUIRED);
+ break;
+ }
+
+ /* Option length is too big for packet */
+ if (unlikely(*(pkt+1) > plen)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].type = *pkt;
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].len = *(pkt+1);
+ if (plen > 2)
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].data = (pkt+2);
+ else
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].data = NULL;
+
+ SCLogDebug("IPV4OPT %" PRIu16 " len %" PRIu16 " @ %" PRIu16 "/%" PRIu16 "",
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].type, p->IPV4_OPTS[p->IPV4_OPTS_CNT].len,
+ (len - plen), (len - 1));
+
+ /* we already know that the total options len is valid,
+ * so here the len of the specific option must be bad.
+ * Also check for invalid lengths 0 and 1. */
+ if (unlikely(p->IPV4_OPTS[p->IPV4_OPTS_CNT].len > plen ||
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].len < 2)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ /* we are parsing the most commonly used opts to prevent
+ * us from having to walk the opts list for these all the
+ * time. */
+ /** \todo Figure out which IP options are more common and list them first */
+ switch (p->IPV4_OPTS[p->IPV4_OPTS_CNT].type) {
+ case IPV4_OPT_TS:
+ if (p->ip4vars.o_ts != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateTimestamp(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT])) {
+ return -1;
+ }
+ p->ip4vars.o_ts = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_RR:
+ if (p->ip4vars.o_rr != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) {
+ return -1;
+ }
+ p->ip4vars.o_rr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_QS:
+ if (p->ip4vars.o_qs != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) {
+ return -1;
+ }
+ p->ip4vars.o_qs = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_SEC:
+ if (p->ip4vars.o_sec != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) {
+ return -1;
+ }
+ p->ip4vars.o_sec = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_LSRR:
+ if (p->ip4vars.o_lsrr != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) {
+ return -1;
+ }
+ p->ip4vars.o_lsrr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_CIPSO:
+ if (p->ip4vars.o_cipso != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateCIPSO(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) {
+ return -1;
+ }
+ p->ip4vars.o_cipso = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_SID:
+ if (p->ip4vars.o_sid != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) {
+ return -1;
+ }
+ p->ip4vars.o_sid = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_SSRR:
+ if (p->ip4vars.o_ssrr != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) {
+ return -1;
+ }
+ p->ip4vars.o_ssrr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ case IPV4_OPT_RTRALT:
+ if (p->ip4vars.o_rtralt != NULL) {
+ ENGINE_SET_EVENT(p,IPV4_OPT_DUPLICATE);
+ /* Warn - we can keep going */
+ break;
+ } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) {
+ return -1;
+ }
+ p->ip4vars.o_rtralt = &p->IPV4_OPTS[p->IPV4_OPTS_CNT];
+ break;
+ default:
+ SCLogDebug("IPV4OPT <unknown> (%" PRIu8 ") len %" PRIu8 "",
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].type,
+ p->IPV4_OPTS[p->IPV4_OPTS_CNT].len);
+ ENGINE_SET_EVENT(p,IPV4_OPT_INVALID);
+ /* Warn - we can keep going */
+ break;
+ }
+
+ pkt += p->IPV4_OPTS[p->IPV4_OPTS_CNT].len;
+ plen -= (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len);
+ p->IPV4_OPTS_CNT++;
+ }
+ }
+
+ return 0;
+}
+
+static int DecodeIPV4Packet(Packet *p, uint8_t *pkt, uint16_t len)
+{
+ if (unlikely(len < IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_PKT_TOO_SMALL);
+ return -1;
+ }
+
+ if (unlikely(IP_GET_RAW_VER(pkt) != 4)) {
+ SCLogDebug("wrong ip version %" PRIu8 "",IP_GET_RAW_VER(pkt));
+ ENGINE_SET_INVALID_EVENT(p, IPV4_WRONG_IP_VER);
+ return -1;
+ }
+
+ p->ip4h = (IPV4Hdr *)pkt;
+
+ if (unlikely(IPV4_GET_HLEN(p) < IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_HLEN_TOO_SMALL);
+ return -1;
+ }
+
+ if (unlikely(IPV4_GET_IPLEN(p) < IPV4_GET_HLEN(p))) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_IPLEN_SMALLER_THAN_HLEN);
+ return -1;
+ }
+
+ if (unlikely(len < IPV4_GET_IPLEN(p))) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_TRUNC_PKT);
+ return -1;
+ }
+
+ /* set the address struct */
+ SET_IPV4_SRC_ADDR(p,&p->src);
+ SET_IPV4_DST_ADDR(p,&p->dst);
+
+ /* save the options len */
+ uint8_t ip_opt_len = IPV4_GET_HLEN(p) - IPV4_HEADER_LEN;
+ if (ip_opt_len > 0) {
+ DecodeIPV4Options(p, pkt + IPV4_HEADER_LEN, ip_opt_len);
+ }
+
+ return 0;
+}
+
+int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_ipv4);
+
+ SCLogDebug("pkt %p len %"PRIu16"", pkt, len);
+
+ /* do the actual decoding */
+ if (unlikely(DecodeIPV4Packet (p, pkt, len) < 0)) {
+ SCLogDebug("decoding IPv4 packet failed");
+ p->ip4h = NULL;
+ return TM_ECODE_FAILED;
+ }
+ p->proto = IPV4_GET_IPPROTO(p);
+
+ /* If a fragment, pass off for re-assembly. */
+ if (unlikely(IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1)) {
+ Packet *rp = Defrag(tv, dtv, p, pq);
+ if (rp != NULL) {
+ PacketEnqueue(pq, rp);
+ }
+ p->flags |= PKT_IS_FRAGMENT;
+ return TM_ECODE_OK;
+ }
+
+ /* do hdr test, process hdr rules */
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) { /* only convert the addresses if debug is really enabled */
+ /* debug print */
+ char s[16], d[16];
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), s, sizeof(s));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), d, sizeof(d));
+ SCLogDebug("IPV4 %s->%s PROTO: %" PRIu32 " OFFSET: %" PRIu32 " RF: %" PRIu32 " DF: %" PRIu32 " MF: %" PRIu32 " ID: %" PRIu32 "", s,d,
+ IPV4_GET_IPPROTO(p), IPV4_GET_IPOFFSET(p), IPV4_GET_RF(p),
+ IPV4_GET_DF(p), IPV4_GET_MF(p), IPV4_GET_IPID(p));
+ }
+#endif /* DEBUG */
+
+ /* check what next decoder to invoke */
+ switch (IPV4_GET_IPPROTO(p)) {
+ case IPPROTO_TCP:
+ DecodeTCP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ break;
+ case IPPROTO_UDP:
+ DecodeUDP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ break;
+ case IPPROTO_ICMP:
+ DecodeICMPV4(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ break;
+ case IPPROTO_GRE:
+ DecodeGRE(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ break;
+ case IPPROTO_SCTP:
+ DecodeSCTP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ break;
+ case IPPROTO_IPV6:
+ {
+ if (pq != NULL) {
+ /* spawn off tunnel packet */
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p),
+ DECODE_TUNNEL_IPV6, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_IPV4);
+ PacketEnqueue(pq,tp);
+ }
+ }
+ break;
+ }
+ case IPPROTO_IP:
+ /* check PPP VJ uncompressed packets and decode tcp dummy */
+ if(p->ppph != NULL && ntohs(p->ppph->protocol) == PPP_VJ_UCOMP) {
+ DecodeTCP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
+ IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p), pq);
+ }
+ break;
+ case IPPROTO_ICMPV6:
+ ENGINE_SET_INVALID_EVENT(p, IPV4_WITH_ICMPV6);
+ break;
+ }
+
+ return TM_ECODE_OK;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+void DecodeIPV4OptionsPrint(Packet *p)
+{
+ IPV4Vars *pv = &p->ip4vars;
+
+ printf("DecodeIPV4Options: cnt=%" PRIu8
+ ",rr={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",qs={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",ts={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",sec={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",lsrr={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",cipso={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",sid={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",ssrr={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ ",rtralt={t=%" PRIu8 ",l=%" PRIu8 ",d=%p}"
+ "}\n",
+ pv->ip_opt_cnt,
+ (pv->o_rr ? pv->o_rr->type : 0), (pv->o_rr ? pv->o_rr->len : 0), (pv->o_rr ? pv->o_rr->data : 0),
+ (pv->o_qs ? pv->o_qs->type : 0), (pv->o_qs ? pv->o_qs->len : 0), (pv->o_qs ? pv->o_qs->data : 0),
+ (pv->o_ts ? pv->o_ts->type : 0), (pv->o_ts ? pv->o_ts->len : 0), (pv->o_ts ? pv->o_ts->data : 0),
+ (pv->o_sec ? pv->o_sec->type : 0), (pv->o_sec ? pv->o_sec->len : 0), (pv->o_sec ? pv->o_sec->data : 0),
+ (pv->o_lsrr ? pv->o_lsrr->type : 0), (pv->o_lsrr ? pv->o_lsrr->len : 0), (pv->o_lsrr ? pv->o_lsrr->data : 0),
+ (pv->o_cipso ? pv->o_cipso->type : 0), (pv->o_cipso ? pv->o_cipso->len : 0), (pv->o_cipso ? pv->o_cipso->data : 0),
+ (pv->o_sid ? pv->o_sid->type : 0), (pv->o_sid ? pv->o_sid->len : 0), (pv->o_sid ? pv->o_sid->data : 0),
+ (pv->o_ssrr ? pv->o_ssrr->type : 0), (pv->o_ssrr ? pv->o_ssrr->len : 0), (pv->o_ssrr ? pv->o_ssrr->data : 0),
+ (pv->o_rtralt ? pv->o_rtralt->type : 0), (pv->o_rtralt ? pv->o_rtralt->len : 0), (pv->o_rtralt ? pv->o_rtralt->data : 0));
+}
+
+/** \test IPV4 with no options. */
+int DecodeIPV4OptionsNONETest01(void)
+{
+ uint8_t raw_opts[] = { };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ uint8_t *data = (uint8_t *)p;
+ uint16_t i;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ if (rc != 0) {
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+
+ for (i = 0; i < (uint16_t)SIZE_OF_PACKET; i++) {
+ if (*data) {
+ /* Should not have modified packet data */
+ //printf("Data modified at offset %" PRIu16 "\n", i);
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** \test IPV4 with EOL option. */
+int DecodeIPV4OptionsEOLTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_EOL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ uint8_t *data = (uint8_t *)p;
+ uint16_t i;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ if (rc != 0) {
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+
+ for (i = 0; i < (uint16_t)SIZE_OF_PACKET; i++) {
+ if (*data) {
+ /* Should not have modified packet data */
+ //printf("Data modified at offset %" PRIu16 "\n", i);
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** \test IPV4 with NOP option. */
+int DecodeIPV4OptionsNOPTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_NOP, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ uint8_t *data = (uint8_t *)p;
+ uint16_t i;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ if (rc != 0) {
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+
+ for (i = 0; i < (uint16_t)SIZE_OF_PACKET; i++) {
+ if (*data) {
+ /* Should not have modified packet data */
+ //printf("Data modified at offset %" PRIu16 "\n", i);
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+ }
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** \test IPV4 with RR option. */
+int DecodeIPV4OptionsRRTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_RR)
+ && (p->IPV4_OPTS[0].len == 0x27)
+ && (p->ip4vars.o_rr == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with RR option (len too large). */
+int DecodeIPV4OptionsRRTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with RR option (ptr too large). */
+int DecodeIPV4OptionsRRTest03(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with RR option (ptr not in 4 byte increment). */
+int DecodeIPV4OptionsRRTest04(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with QS option. */
+int DecodeIPV4OptionsQSTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_QS, 0x08, 0x0d, 0x00, 0xbe, 0xef, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",qs=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_qs, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_QS)
+ && (p->IPV4_OPTS[0].len == 0x08)
+ && (p->ip4vars.o_qs == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with QS option (len too small) */
+int DecodeIPV4OptionsQSTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_QS, 0x07, 0x0d, 0x00, 0xbe, 0xef, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",qs=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_qs, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with TS option. */
+int DecodeIPV4OptionsTSTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_TS, 0x24, 0x0d, 0x01, 0x0a, 0x0a, 0x0a, 0x69,
+ 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_TS)
+ && (p->IPV4_OPTS[0].len == 0x24)
+ && (p->ip4vars.o_ts == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with TS option (ptr too small). */
+int DecodeIPV4OptionsTSTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_TS, 0x24, 0x04, 0x01, 0x0a, 0x0a, 0x0a, 0x69,
+ 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with TS option (ptr too large). */
+int DecodeIPV4OptionsTSTest03(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_TS, 0x24, 0xff, 0x01, 0x0a, 0x0a, 0x0a, 0x69,
+ 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with TS option (ptr not valid). */
+int DecodeIPV4OptionsTSTest04(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_TS, 0x24, 0x0a, 0x01, 0x0a, 0x0a, 0x0a, 0x69,
+ 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SEC option. */
+int DecodeIPV4OptionsSECTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SEC, 0x0b, 0xf1, 0x35, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sec=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sec, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_SEC)
+ && (p->IPV4_OPTS[0].len == 0x0b)
+ && (p->ip4vars.o_sec == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SEC option (invalid length). */
+int DecodeIPV4OptionsSECTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SEC, 0x0a, 0xf1, 0x35, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sec=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sec, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with LSRR option. */
+int DecodeIPV4OptionsLSRRTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_LSRR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_LSRR)
+ && (p->IPV4_OPTS[0].len == 0x27)
+ && (p->ip4vars.o_lsrr == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with LSRR option (len too large). */
+int DecodeIPV4OptionsLSRRTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_LSRR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with LSRR option (ptr too large). */
+int DecodeIPV4OptionsLSRRTest03(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_LSRR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with LSRR option (ptr not in 4 byte increment). */
+int DecodeIPV4OptionsLSRRTest04(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_LSRR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with CIPSO option. */
+int DecodeIPV4OptionsCIPSOTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_CIPSO, 0x18, 0x00, 0x00, 0x00, 0x05, 0x05, 0x12,
+ 0x00, 0x03, 0x00, 0xef, 0x00, 0xef, 0x00, 0x06,
+ 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_cipso, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_CIPSO)
+ && (p->IPV4_OPTS[0].len == 0x18)
+ && (p->ip4vars.o_cipso == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SID option. */
+int DecodeIPV4OptionsSIDTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SID, 0x04, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sid=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sid, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_SID)
+ && (p->IPV4_OPTS[0].len == 0x04)
+ && (p->ip4vars.o_sid == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SID option (len invalid. */
+int DecodeIPV4OptionsSIDTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SID, 0x05, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sid=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sid, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SSRR option. */
+int DecodeIPV4OptionsSSRRTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SSRR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_SSRR)
+ && (p->IPV4_OPTS[0].len == 0x27)
+ && (p->ip4vars.o_ssrr == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SSRR option (len too large). */
+int DecodeIPV4OptionsSSRRTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SSRR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SSRR option (ptr too large). */
+int DecodeIPV4OptionsSSRRTest03(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SSRR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with SSRR option (ptr not in 4 byte increment). */
+int DecodeIPV4OptionsSSRRTest04(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_SSRR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with RTRALT option. */
+int DecodeIPV4OptionsRTRALTTest01(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RTRALT, 0x04, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rtralt=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rtralt, (uintmax_t)&p.IPV4_OPTS[0]);
+ if ( (rc == 0)
+ && (p->IPV4_OPTS_CNT == 1)
+ && (p->IPV4_OPTS[0].type == IPV4_OPT_RTRALT)
+ && (p->IPV4_OPTS[0].len == 0x04)
+ && (p->ip4vars.o_rtralt == &p->IPV4_OPTS[0]))
+ {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+/** \test IPV4 with RTRALT option (len invalid. */
+int DecodeIPV4OptionsRTRALTTest02(void)
+{
+ uint8_t raw_opts[] = {
+ IPV4_OPT_RTRALT, 0x05, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00
+ };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ int rc;
+
+ rc = DecodeIPV4Options(p, raw_opts, sizeof(raw_opts));
+ //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rtralt=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rtralt, (uintmax_t)&p.IPV4_OPTS[0]);
+ if (rc != 0) {
+ SCFree(p);
+ return 1;
+ }
+
+ DecodeIPV4OptionsPrint(p);
+ SCFree(p);
+ return 0;
+}
+
+static int IPV4CalculateValidChecksumtest01(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ csum = *( ((uint16_t *)raw_ipv4) + 5);
+
+ return (csum == IPV4CalculateChecksum((uint16_t *)raw_ipv4, sizeof(raw_ipv4)));
+}
+
+static int IPV4CalculateInvalidChecksumtest02(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x07};
+
+ csum = *( ((uint16_t *)raw_ipv4) + 5);
+
+ return (csum == IPV4CalculateChecksum((uint16_t *)raw_ipv4, sizeof(raw_ipv4)));
+}
+
+/**
+ * \test IPV4 defrag and packet recursion level test
+ */
+int DecodeIPV4DefragTest01(void)
+{
+ uint8_t pkt1[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06,
+ 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5e
+ };
+ uint8_t pkt2[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x01, 0x40, 0x06,
+ 0x9a, 0xc7, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10,
+ 0x80, 0x00
+ };
+ uint8_t pkt3[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x18, 0xe9, 0xef, 0x00, 0x02, 0x40, 0x06,
+ 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0xb1, 0xa3, 0x00, 0x00
+ };
+ uint8_t tunnel_pkt[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x28, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06,
+ 0xba, 0xbc, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10,
+ 0x80, 0x00, 0xb1, 0xa3, 0x00, 0x00
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ int result = 1;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+
+ FlowInitConfig(FLOW_QUIET);
+ DefragInit();
+
+ PacketCopyData(p, pkt1, sizeof(pkt1));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt2, sizeof(pkt2));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt3, sizeof(pkt3));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+ Packet *tp = PacketDequeue(&pq);
+ if (tp == NULL) {
+ printf("Failed to get defragged pseudo packet\n");
+ result = 0;
+ goto end;
+ }
+ if (tp->recursion_level != p->recursion_level) {
+ printf("defragged pseudo packet's and parent packet's recursion "
+ "level don't match\n %d != %d",
+ tp->recursion_level, p->recursion_level);
+ result = 0;
+ goto end;
+ }
+ if (tp->ip4h == NULL || tp->tcph == NULL) {
+ printf("pseudo packet's ip header and tcp header shouldn't be NULL, "
+ "but it is\n");
+ result = 0;
+ goto end;
+ }
+ if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) {
+ printf("defragged pseudo packet's and parent packet's pkt lens "
+ "don't match\n %u != %"PRIuMAX,
+ GET_PKT_LEN(tp), (uintmax_t)sizeof(tunnel_pkt));
+ result = 0;
+ goto end;
+ }
+ if (memcmp(GET_PKT_DATA(tp), tunnel_pkt, sizeof(tunnel_pkt)) != 0) {
+ result = 0;
+ goto end;
+ }
+
+ PACKET_RECYCLE(tp);
+ SCFree(tp);
+
+end:
+ DefragDestroy();
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test Don't send IPv4 fragments to the upper layer decoder and
+ * and packet recursion level test.
+ */
+int DecodeIPV4DefragTest02(void)
+{
+ uint8_t pkt1[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x24, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06,
+ 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c,
+ /* first frag */
+ 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10,
+ 0x80, 0x00,
+ };
+ uint8_t pkt2[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x2c, 0xe9, 0xef, 0x20, 0x02, 0x40, 0x06,
+ 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c,
+ /* second frag */
+ 0xb1, 0xa3, 0x00, 0x10, 0x5b, 0xa3, 0x81, 0x5e,
+ 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00,
+ 0xb1, 0xa3, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04
+ };
+ uint8_t pkt3[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x16, 0xe9, 0xef, 0x00, 0x05, 0x40, 0x06,
+ 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c,
+ /* final frag */
+ 0xb1, 0xa3,
+ };
+
+ uint8_t tunnel_pkt[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3e, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06,
+ 0xba, 0xae, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c,
+ 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, 0x81, 0x5e,
+ 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00,
+ 0xb1, 0xa3, 0x00, 0x10, 0x5b, 0xa3, 0x81, 0x5e,
+ 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00,
+ 0xb1, 0xa3, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04,
+ 0xb1, 0xa3,
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+
+ FlowInitConfig(FLOW_QUIET);
+ DefragInit();
+
+ PacketCopyData(p, pkt1, sizeof(pkt1));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt2, sizeof(pkt2));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ p->recursion_level = 3;
+ PacketCopyData(p, pkt3, sizeof(pkt3));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ goto end;
+ }
+ Packet *tp = PacketDequeue(&pq);
+ if (tp == NULL) {
+ printf("Failed to get defragged pseudo packet\n");
+ goto end;
+ }
+ if (tp->recursion_level != p->recursion_level) {
+ printf("defragged pseudo packet's and parent packet's recursion "
+ "level don't match %d != %d: ",
+ tp->recursion_level, p->recursion_level);
+ goto end;
+ }
+ if (tp->ip4h == NULL || tp->tcph == NULL) {
+ printf("pseudo packet's ip header and tcp header shouldn't be NULL, "
+ "but it is\n");
+ goto end;
+ }
+ if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) {
+ printf("defragged pseudo packet's and parent packet's pkt lens "
+ "don't match %u != %"PRIuMAX": ",
+ GET_PKT_LEN(tp), (uintmax_t)sizeof(tunnel_pkt));
+ goto end;
+ }
+
+ if (memcmp(GET_PKT_DATA(tp), tunnel_pkt, sizeof(tunnel_pkt)) != 0) {
+ goto end;
+ }
+
+ result = 1;
+ PACKET_RECYCLE(tp);
+ SCFree(tp);
+
+end:
+ DefragDestroy();
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test IPV4 defrag and flow retrieval test.
+ */
+int DecodeIPV4DefragTest03(void)
+{
+ uint8_t pkt[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x28, 0xe9, 0xee, 0x00, 0x00, 0x40, 0x06,
+ 0xba, 0xbd, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+ 0x80, 0x00, 0x0c, 0xee, 0x00, 0x00
+ };
+ uint8_t pkt1[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06,
+ 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5e
+ };
+ uint8_t pkt2[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x01, 0x40, 0x06,
+ 0x9a, 0xc7, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10,
+ 0x80, 0x00
+ };
+ uint8_t pkt3[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x18, 0xe9, 0xef, 0x00, 0x02, 0x40, 0x06,
+ 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0xb1, 0xa3, 0x00, 0x00
+ };
+ uint8_t tunnel_pkt[] = {
+ 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad,
+ 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x28, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06,
+ 0xba, 0xbc, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00,
+ 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3,
+ 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10,
+ 0x80, 0x00, 0xb1, 0xa3, 0x00, 0x00
+ };
+
+ Flow *f = NULL;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ PacketQueue pq;
+ int result = 1;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&pq, 0, sizeof(PacketQueue));
+
+ FlowInitConfig(FLOW_QUIET);
+ DefragInit();
+
+ PacketCopyData(p, pkt, sizeof(pkt));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph == NULL) {
+ printf("tcp header shouldn't be NULL, but it is\n");
+ result = 0;
+ goto end;
+ }
+ if (p->flow == NULL) {
+ printf("packet flow shouldn't be NULL\n");
+ result = 0;
+ goto end;
+ }
+ f = p->flow;
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt1, sizeof(pkt1));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt2, sizeof(pkt2));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+ PACKET_RECYCLE(p);
+
+ PacketCopyData(p, pkt3, sizeof(pkt3));
+ DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
+ GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
+ if (p->tcph != NULL) {
+ printf("tcp header should be NULL for ip fragment, but it isn't\n");
+ result = 0;
+ goto end;
+ }
+
+ Packet *tp = PacketDequeue(&pq);
+ if (tp == NULL) {
+ printf("Failed to get defragged pseudo packet\n");
+ result = 0;
+ goto end;
+ }
+ if (tp->flow == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (tp->flow != f) {
+ result = 0;
+ goto end;
+ }
+ if (tp->recursion_level != p->recursion_level) {
+ printf("defragged pseudo packet's and parent packet's recursion "
+ "level don't match\n %d != %d",
+ tp->recursion_level, p->recursion_level);
+ result = 0;
+ goto end;
+ }
+ if (tp->ip4h == NULL || tp->tcph == NULL) {
+ printf("pseudo packet's ip header and tcp header shouldn't be NULL, "
+ "but it is\n");
+ result = 0;
+ goto end;
+ }
+ if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) {
+ printf("defragged pseudo packet's and parent packet's pkt lens "
+ "don't match\n %u != %"PRIuMAX,
+ GET_PKT_LEN(tp), (uintmax_t)sizeof(tunnel_pkt));
+ result = 0;
+ goto end;
+ }
+
+ if (memcmp(GET_PKT_DATA(tp), tunnel_pkt, sizeof(tunnel_pkt)) != 0) {
+ result = 0;
+ goto end;
+ }
+
+ PACKET_RECYCLE(tp);
+ SCFree(tp);
+
+end:
+ DefragDestroy();
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DecodeIPV4RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeIPV4OptionsNONETest01", DecodeIPV4OptionsNONETest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsEOLTest01", DecodeIPV4OptionsEOLTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsNOPTest01", DecodeIPV4OptionsNOPTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsRRTest01", DecodeIPV4OptionsRRTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsRRTest02", DecodeIPV4OptionsRRTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsRRTest03", DecodeIPV4OptionsRRTest03, 1);
+ UtRegisterTest("DecodeIPV4OptionsRRTest04", DecodeIPV4OptionsRRTest04, 1);
+ UtRegisterTest("DecodeIPV4OptionsQSTest01", DecodeIPV4OptionsQSTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsQSTest02", DecodeIPV4OptionsQSTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsTSTest01", DecodeIPV4OptionsTSTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsTSTest02", DecodeIPV4OptionsTSTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsTSTest03", DecodeIPV4OptionsTSTest03, 1);
+ UtRegisterTest("DecodeIPV4OptionsTSTest04", DecodeIPV4OptionsTSTest04, 1);
+ UtRegisterTest("DecodeIPV4OptionsSECTest01", DecodeIPV4OptionsSECTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsSECTest02", DecodeIPV4OptionsSECTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsLSRRTest01", DecodeIPV4OptionsLSRRTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsLSRRTest02", DecodeIPV4OptionsLSRRTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsLSRRTest03", DecodeIPV4OptionsLSRRTest03, 1);
+ UtRegisterTest("DecodeIPV4OptionsLSRRTest04", DecodeIPV4OptionsLSRRTest04, 1);
+ UtRegisterTest("DecodeIPV4OptionsCIPSOTest01", DecodeIPV4OptionsCIPSOTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsSIDTest01", DecodeIPV4OptionsSIDTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsSIDTest02", DecodeIPV4OptionsSIDTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsSSRRTest01", DecodeIPV4OptionsSSRRTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsSSRRTest02", DecodeIPV4OptionsSSRRTest02, 1);
+ UtRegisterTest("DecodeIPV4OptionsSSRRTest03", DecodeIPV4OptionsSSRRTest03, 1);
+ UtRegisterTest("DecodeIPV4OptionsSSRRTest04", DecodeIPV4OptionsSSRRTest04, 1);
+ UtRegisterTest("DecodeIPV4OptionsRTRALTTest01", DecodeIPV4OptionsRTRALTTest01, 1);
+ UtRegisterTest("DecodeIPV4OptionsRTRALTTest02", DecodeIPV4OptionsRTRALTTest02, 1);
+ UtRegisterTest("IPV4CalculateValidChecksumtest01",
+ IPV4CalculateValidChecksumtest01, 1);
+ UtRegisterTest("IPV4CalculateInvalidChecksumtest02",
+ IPV4CalculateInvalidChecksumtest02, 0);
+ UtRegisterTest("DecodeIPV4DefragTest01", DecodeIPV4DefragTest01, 1);
+ UtRegisterTest("DecodeIPV4DefragTest02", DecodeIPV4DefragTest02, 1);
+ UtRegisterTest("DecodeIPV4DefragTest03", DecodeIPV4DefragTest03, 1);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-ipv4.h b/framework/src/suricata/src/decode-ipv4.h
new file mode 100644
index 00000000..be212bf2
--- /dev/null
+++ b/framework/src/suricata/src/decode-ipv4.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DECODE_IPV4_H__
+#define __DECODE_IPV4_H__
+
+#define IPV4_HEADER_LEN 20 /**< Header length */
+#define IPV4_OPTMAX 40 /**< Max options length */
+#define IPV4_MAXPACKET_LEN 65535 /**< Maximum packet size */
+
+/** IP Option Types */
+#define IPV4_OPT_EOL 0x00 /**< Option: End of List */
+#define IPV4_OPT_NOP 0x01 /**< Option: No op */
+#define IPV4_OPT_RR 0x07 /**< Option: Record Route */
+#define IPV4_OPT_QS 0x19 /**< Option: Quick Start */
+#define IPV4_OPT_TS 0x44 /**< Option: Timestamp */
+#define IPV4_OPT_SEC 0x82 /**< Option: Security */
+#define IPV4_OPT_LSRR 0x83 /**< Option: Loose Source Route */
+#define IPV4_OPT_CIPSO 0x86 /**< Option: Commercial IP Security */
+#define IPV4_OPT_SID 0x88 /**< Option: Stream Identifier */
+#define IPV4_OPT_SSRR 0x89 /**< Option: Strict Source Route */
+#define IPV4_OPT_RTRALT 0x94 /**< Option: Router Alert */
+
+/** IP Option Lengths (fixed) */
+#define IPV4_OPT_SEC_LEN 11 /**< SEC Option Fixed Length */
+#define IPV4_OPT_SID_LEN 4 /**< SID Option Fixed Length */
+#define IPV4_OPT_RTRALT_LEN 4 /**< RTRALT Option Fixed Length */
+
+/** IP Option Lengths (variable) */
+#define IPV4_OPT_ROUTE_MIN 3 /**< RR, SRR, LTRR Option Min Length */
+#define IPV4_OPT_QS_MIN 8 /**< QS Option Min Length */
+#define IPV4_OPT_TS_MIN 5 /**< TS Option Min Length */
+#define IPV4_OPT_CIPSO_MIN 10 /**< CIPSO Option Min Length */
+
+/** IP Option fields */
+#define IPV4_OPTS ip4vars.ip_opts
+#define IPV4_OPTS_CNT ip4vars.ip_opt_cnt
+
+typedef struct IPV4Opt_ {
+ /** \todo We may want to break type up into its 3 fields
+ * as the reassembler may want to know which options
+ * must be copied to each fragment.
+ */
+ uint8_t type; /**< option type */
+ uint8_t len; /**< option length (type+len+data) */
+ uint8_t *data; /**< option data */
+} IPV4Opt;
+
+typedef struct IPV4Hdr_
+{
+ uint8_t ip_verhl; /**< version & header length */
+ uint8_t ip_tos; /**< type of service */
+ uint16_t ip_len; /**< length */
+ uint16_t ip_id; /**< id */
+ uint16_t ip_off; /**< frag offset */
+ uint8_t ip_ttl; /**< time to live */
+ uint8_t ip_proto; /**< protocol (tcp, udp, etc) */
+ uint16_t ip_csum; /**< checksum */
+ union {
+ struct {
+ struct in_addr ip_src;/**< source address */
+ struct in_addr ip_dst;/**< destination address */
+ } ip4_un1;
+ uint16_t ip_addrs[4];
+ } ip4_hdrun1;
+} __attribute__((__packed__)) IPV4Hdr;
+
+
+#define s_ip_src ip4_hdrun1.ip4_un1.ip_src
+#define s_ip_dst ip4_hdrun1.ip4_un1.ip_dst
+#define s_ip_addrs ip4_hdrun1.ip_addrs
+
+#define IPV4_GET_RAW_VER(ip4h) (((ip4h)->ip_verhl & 0xf0) >> 4)
+#define IPV4_GET_RAW_HLEN(ip4h) ((ip4h)->ip_verhl & 0x0f)
+#define IPV4_GET_RAW_IPTOS(ip4h) ((ip4h)->ip_tos)
+#define IPV4_GET_RAW_IPLEN(ip4h) ((ip4h)->ip_len)
+#define IPV4_GET_RAW_IPID(ip4h) ((ip4h)->ip_id)
+#define IPV4_GET_RAW_IPOFFSET(ip4h) ((ip4h)->ip_off)
+#define IPV4_GET_RAW_IPTTL(ip4h) ((ip4h)->ip_ttl)
+#define IPV4_GET_RAW_IPPROTO(ip4h) ((ip4h)->ip_proto)
+#define IPV4_GET_RAW_IPSRC(ip4h) ((ip4h)->s_ip_src)
+#define IPV4_GET_RAW_IPDST(ip4h) ((ip4h)->s_ip_dst)
+
+/** return the raw (directly from the header) src ip as uint32_t */
+#define IPV4_GET_RAW_IPSRC_U32(ip4h) (uint32_t)((ip4h)->s_ip_src.s_addr)
+/** return the raw (directly from the header) dst ip as uint32_t */
+#define IPV4_GET_RAW_IPDST_U32(ip4h) (uint32_t)((ip4h)->s_ip_dst.s_addr)
+
+/* we need to change them as well as get them */
+#define IPV4_SET_RAW_VER(ip4h, value) ((ip4h)->ip_verhl = (((ip4h)->ip_verhl & 0x0f) | (value << 4)))
+#define IPV4_SET_RAW_HLEN(ip4h, value) ((ip4h)->ip_verhl = (((ip4h)->ip_verhl & 0xf0) | (value & 0x0f)))
+#define IPV4_SET_RAW_IPTOS(ip4h, value) ((ip4h)->ip_tos = value)
+#define IPV4_SET_RAW_IPLEN(ip4h, value) ((ip4h)->ip_len = value)
+#define IPV4_SET_RAW_IPPROTO(ip4h, value) ((ip4h)->ip_proto = value)
+
+/* ONLY call these functions after making sure that:
+ * 1. p->ip4h is set
+ * 2. p->ip4h is valid (len is correct)
+ */
+#define IPV4_GET_VER(p) \
+ IPV4_GET_RAW_VER((p)->ip4h)
+#define IPV4_GET_HLEN(p) \
+ (IPV4_GET_RAW_HLEN((p)->ip4h) << 2)
+#define IPV4_GET_IPTOS(p) \
+ IPV4_GET_RAW_IPTOS((p)->ip4h)
+#define IPV4_GET_IPLEN(p) \
+ (ntohs(IPV4_GET_RAW_IPLEN((p)->ip4h)))
+#define IPV4_GET_IPID(p) \
+ (ntohs(IPV4_GET_RAW_IPID((p)->ip4h)))
+/* _IPV4_GET_IPOFFSET: get the content of the offset header field in host order */
+#define _IPV4_GET_IPOFFSET(p) \
+ (ntohs(IPV4_GET_RAW_IPOFFSET((p)->ip4h)))
+/* IPV4_GET_IPOFFSET: get the final offset */
+#define IPV4_GET_IPOFFSET(p) \
+ (_IPV4_GET_IPOFFSET(p) & 0x1fff)
+/* IPV4_GET_RF: get the RF flag. Use _IPV4_GET_IPOFFSET to save a ntohs call. */
+#define IPV4_GET_RF(p) \
+ (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x8000) >> 15)
+/* IPV4_GET_DF: get the DF flag. Use _IPV4_GET_IPOFFSET to save a ntohs call. */
+#define IPV4_GET_DF(p) \
+ (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x4000) >> 14)
+/* IPV4_GET_MF: get the MF flag. Use _IPV4_GET_IPOFFSET to save a ntohs call. */
+#define IPV4_GET_MF(p) \
+ (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x2000) >> 13)
+#define IPV4_GET_IPTTL(p) \
+ IPV4_GET_RAW_IPTTL(p->ip4h)
+#define IPV4_GET_IPPROTO(p) \
+ IPV4_GET_RAW_IPPROTO((p)->ip4h)
+
+#define CLEAR_IPV4_PACKET(p) do { \
+ (p)->ip4h = NULL; \
+ (p)->level3_comp_csum = -1; \
+ (p)->ip4vars.ip_src_u32 = 0; \
+ (p)->ip4vars.ip_dst_u32 = 0; \
+ (p)->ip4vars.ip_opt_cnt = 0; \
+ (p)->ip4vars.o_rr = NULL; \
+ (p)->ip4vars.o_qs = NULL; \
+ (p)->ip4vars.o_ts = NULL; \
+ (p)->ip4vars.o_sec = NULL; \
+ (p)->ip4vars.o_lsrr = NULL; \
+ (p)->ip4vars.o_cipso = NULL; \
+ (p)->ip4vars.o_sid = NULL; \
+ (p)->ip4vars.o_ssrr = NULL; \
+ (p)->ip4vars.o_rtralt = NULL; \
+} while (0)
+
+/* helper structure with parsed ipv4 info */
+typedef struct IPV4Vars_
+{
+ int32_t comp_csum; /* checksum computed over the ipv4 packet */
+ uint32_t ip_src_u32; /* source IP */
+ uint32_t ip_dst_u32; /* dest IP */
+
+ IPV4Opt ip_opts[IPV4_OPTMAX];
+ uint8_t ip_opt_cnt;
+
+ /* These are here for direct access and dup tracking */
+ IPV4Opt *o_rr;
+ IPV4Opt *o_qs;
+ IPV4Opt *o_ts;
+ IPV4Opt *o_sec;
+ IPV4Opt *o_lsrr;
+ IPV4Opt *o_cipso;
+ IPV4Opt *o_sid;
+ IPV4Opt *o_ssrr;
+ IPV4Opt *o_rtralt;
+} IPV4Vars;
+
+
+void DecodeIPV4RegisterTests(void);
+
+/** ----- Inline functions ----- */
+static inline uint16_t IPV4CalculateChecksum(uint16_t *, uint16_t);
+/**
+ * \brief Calculates the checksum for the IP packet
+ *
+ * \param pkt Pointer to the start of the IP packet
+ * \param hlen Length of the IP header
+ *
+ * \retval csum Checksum for the IP packet
+ */
+static inline uint16_t IPV4CalculateChecksum(uint16_t *pkt, uint16_t hlen)
+{
+ uint32_t csum = pkt[0];
+
+ csum += pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[6] + pkt[7] + pkt[8] +
+ pkt[9];
+
+ hlen -= 20;
+ pkt += 10;
+
+ if (hlen == 0) {
+ ;
+ } else if (hlen == 4) {
+ csum += pkt[0] + pkt[1];
+ } else if (hlen == 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ } else if (hlen == 12) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5];
+ } else if (hlen == 16) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7];
+ } else if (hlen == 20) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9];
+ } else if (hlen == 24) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11];
+ } else if (hlen == 28) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13];
+ } else if (hlen == 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ } else if (hlen == 36) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15] + pkt[16] + pkt[17];
+ } else if (hlen == 40) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15] + pkt[16] + pkt[17] + pkt[18] + pkt[19];
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ return (uint16_t) ~csum;
+}
+
+#endif /* __DECODE_IPV4_H__ */
+
diff --git a/framework/src/suricata/src/decode-ipv6.c b/framework/src/suricata/src/decode-ipv6.c
new file mode 100644
index 00000000..21ae5226
--- /dev/null
+++ b/framework/src/suricata/src/decode-ipv6.c
@@ -0,0 +1,1001 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode IPv6
+ */
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "decode-ipv6.h"
+#include "decode-icmpv6.h"
+#include "decode-events.h"
+#include "defrag.h"
+#include "pkt-var.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-unittest.h"
+#include "util-profiling.h"
+#include "host.h"
+
+#define IPV6_EXTHDRS ip6eh.ip6_exthdrs
+#define IPV6_EH_CNT ip6eh.ip6_exthdrs_cnt
+
+/**
+ * \brief Function to decode IPv4 in IPv6 packets
+ *
+ */
+static void DecodeIPv4inIPv6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t plen, PacketQueue *pq)
+{
+
+ if (unlikely(plen < IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_IN_IPV6_PKT_TOO_SMALL);
+ return;
+ }
+ if (IP_GET_RAW_VER(pkt) == 4) {
+ if (pq != NULL) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt, plen, DECODE_TUNNEL_IPV4, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_IPV6);
+ /* add the tp to the packet queue. */
+ PacketEnqueue(pq,tp);
+ StatsIncr(tv, dtv->counter_ipv4inipv6);
+ return;
+ }
+ }
+ } else {
+ ENGINE_SET_EVENT(p, IPV4_IN_IPV6_WRONG_IP_VER);
+ }
+ return;
+}
+
+/**
+ * \brief Function to decode IPv6 in IPv6 packets
+ *
+ */
+static int DecodeIP6inIP6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t plen, PacketQueue *pq)
+{
+
+ if (unlikely(plen < IPV6_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV6_IN_IPV6_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+ if (IP_GET_RAW_VER(pkt) == 6) {
+ if (unlikely(pq != NULL)) {
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt, plen, DECODE_TUNNEL_IPV6, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_IPV6);
+ PacketEnqueue(pq,tp);
+ StatsIncr(tv, dtv->counter_ipv6inipv6);
+ }
+ }
+ } else {
+ ENGINE_SET_EVENT(p, IPV6_IN_IPV6_WRONG_IP_VER);
+ }
+ return TM_ECODE_OK;
+}
+
+static void
+DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ SCEnter();
+
+ uint8_t *orig_pkt = pkt;
+ uint8_t nh = 0; /* careful, 0 is actually a real type */
+ uint16_t hdrextlen;
+ uint16_t plen;
+ char dstopts = 0;
+ char exthdr_fh_done = 0;
+
+ nh = IPV6_GET_NH(p);
+ plen = len;
+
+ while(1)
+ {
+ /* No upper layer, but we do have data. Suspicious. */
+ if (nh == IPPROTO_NONE && plen > 0) {
+ ENGINE_SET_EVENT(p, IPV6_DATA_AFTER_NONE_HEADER);
+ SCReturn;
+ }
+
+ if (plen < 2) { /* minimal needed in a hdr */
+ SCReturn;
+ }
+
+ switch(nh)
+ {
+ case IPPROTO_TCP:
+ IPV6_SET_L4PROTO(p,nh);
+ DecodeTCP(tv, dtv, p, pkt, plen, pq);
+ SCReturn;
+
+ case IPPROTO_UDP:
+ IPV6_SET_L4PROTO(p,nh);
+ DecodeUDP(tv, dtv, p, pkt, plen, pq);
+ SCReturn;
+
+ case IPPROTO_ICMPV6:
+ IPV6_SET_L4PROTO(p,nh);
+ DecodeICMPV6(tv, dtv, p, pkt, plen, pq);
+ SCReturn;
+
+ case IPPROTO_SCTP:
+ IPV6_SET_L4PROTO(p,nh);
+ DecodeSCTP(tv, dtv, p, pkt, plen, pq);
+ SCReturn;
+
+ case IPPROTO_ROUTING:
+ IPV6_SET_L4PROTO(p,nh);
+ hdrextlen = 8 + (*(pkt+1) * 8); /* 8 bytes + length in 8 octet units */
+
+ SCLogDebug("hdrextlen %"PRIu8, hdrextlen);
+
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+
+ if (p->IPV6_EH_CNT < IPV6_MAX_OPT)
+ {
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].type = nh;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].next = *pkt;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].len = hdrextlen;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].data = pkt+2;
+ p->IPV6_EH_CNT++;
+ }
+
+ if (IPV6_EXTHDR_ISSET_RH(p)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_RH);
+ /* skip past this extension so we can continue parsing the rest
+ * of the packet */
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ IPV6_EXTHDR_SET_RH(p, pkt);
+
+ /** \todo move into own function and load on demand */
+ if (IPV6_EXTHDR_RH(p)->ip6rh_type == 0) {
+#if 0 // XXX usused and broken, original packet is modified in the memcpy
+ uint8_t i;
+
+ uint8_t n = hdrextlen / 2;
+ /* because we devide the header len by 2 (as rfc 2460 tells us to)
+ * we devide the result by 8 and not 16 as the header fields are
+ * sized */
+ for (i = 0; i < (n/8) && i < sizeof(IPV6_EXTHDR_RH(p)->ip6rh0_addr)/sizeof(struct in6_addr); ++i) {
+ /* the address header fields are 16 bytes in size */
+ /** \todo do this without memcpy since it's expensive */
+ memcpy(&IPV6_EXTHDR_RH(p)->ip6rh0_addr[i], pkt+(i*16)+8, sizeof(IPV6_EXTHDR_RH(p)->ip6rh0_addr[i]));
+ }
+ IPV6_EXTHDR_RH(p)->ip6rh0_num_addrs = i;
+#endif
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_RH_TYPE_0);
+ }
+
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ {
+ IPV6OptHAO *hao = NULL;
+ IPV6OptRA *ra = NULL;
+ IPV6OptJumbo *jumbo = NULL;
+ uint16_t optslen = 0;
+
+ IPV6_SET_L4PROTO(p,nh);
+ hdrextlen = (*(pkt+1) + 1) << 3;
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+
+ if (p->IPV6_EH_CNT < IPV6_MAX_OPT)
+ {
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].type = nh;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].next = *pkt;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].len = hdrextlen;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].data = pkt+2;
+ p->IPV6_EH_CNT++;
+ }
+
+ uint8_t *ptr = pkt + 2; /* +2 to go past nxthdr and len */
+
+ /* point the pointers to right structures
+ * in Packet. */
+ if (nh == IPPROTO_HOPOPTS) {
+ if (IPV6_EXTHDR_ISSET_HH(p)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_HH);
+ /* skip past this extension so we can continue parsing the rest
+ * of the packet */
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ IPV6_EXTHDR_SET_HH(p, pkt);
+ hao = &IPV6_EXTHDR_HH_HAO(p);
+ ra = &IPV6_EXTHDR_HH_RA(p);
+ jumbo = &IPV6_EXTHDR_HH_JUMBO(p);
+
+ optslen = ((IPV6_EXTHDR_HH(p)->ip6hh_len+1)<<3)-2;
+ }
+ else if (nh == IPPROTO_DSTOPTS)
+ {
+ if (dstopts == 0) {
+ IPV6_EXTHDR_SET_DH1(p, pkt);
+ hao = &IPV6_EXTHDR_DH1_HAO(p);
+ ra = &IPV6_EXTHDR_DH1_RA(p);
+ jumbo = &IPV6_EXTHDR_DH2_JUMBO(p);
+ optslen = ((IPV6_EXTHDR_DH1(p)->ip6dh_len+1)<<3)-2;
+ dstopts = 1;
+ } else if (dstopts == 1) {
+ IPV6_EXTHDR_SET_DH2(p, pkt);
+ hao = &IPV6_EXTHDR_DH2_HAO(p);
+ ra = &IPV6_EXTHDR_DH2_RA(p);
+ jumbo = &IPV6_EXTHDR_DH2_JUMBO(p);
+ optslen = ((IPV6_EXTHDR_DH2(p)->ip6dh_len+1)<<3)-2;
+ dstopts = 2;
+ } else {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_DH);
+ /* skip past this extension so we can continue parsing the rest
+ * of the packet */
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+ }
+
+ if (optslen > plen) {
+ /* since the packet is long enough (we checked
+ * plen against hdrlen, the optlen must be malformed. */
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ /* skip past this extension so we can continue parsing the rest
+ * of the packet */
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+/** \todo move into own function to loaded on demand */
+ uint16_t padn_cnt = 0;
+ uint16_t other_cnt = 0;
+ uint16_t offset = 0;
+ while(offset < optslen)
+ {
+ if (*ptr == IPV6OPT_PAD1)
+ {
+ padn_cnt++;
+ offset++;
+ ptr++;
+ continue;
+ }
+
+ if (offset + 1 >= optslen) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ break;
+ }
+
+ /* length field for each opt */
+ uint8_t ip6_optlen = *(ptr + 1);
+
+ /* see if the optlen from the packet fits the total optslen */
+ if ((offset + 1 + ip6_optlen) > optslen) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ break;
+ }
+
+ if (*ptr == IPV6OPT_PADN) /* PadN */
+ {
+ //printf("PadN option\n");
+ padn_cnt++;
+
+ /* a zero padN len would be weird */
+ if (ip6_optlen == 0)
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_ZERO_LEN_PADN);
+ }
+ else if (*ptr == IPV6OPT_RA) /* RA */
+ {
+ ra->ip6ra_type = *(ptr);
+ ra->ip6ra_len = ip6_optlen;
+
+ if (ip6_optlen < sizeof(ra->ip6ra_value)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ break;
+ }
+
+ memcpy(&ra->ip6ra_value, (ptr + 2), sizeof(ra->ip6ra_value));
+ ra->ip6ra_value = ntohs(ra->ip6ra_value);
+ //printf("RA option: type %" PRIu32 " len %" PRIu32 " value %" PRIu32 "\n",
+ // ra->ip6ra_type, ra->ip6ra_len, ra->ip6ra_value);
+ other_cnt++;
+ }
+ else if (*ptr == IPV6OPT_JUMBO) /* Jumbo */
+ {
+ jumbo->ip6j_type = *(ptr);
+ jumbo->ip6j_len = ip6_optlen;
+
+ if (ip6_optlen < sizeof(jumbo->ip6j_payload_len)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ break;
+ }
+
+ memcpy(&jumbo->ip6j_payload_len, (ptr+2), sizeof(jumbo->ip6j_payload_len));
+ jumbo->ip6j_payload_len = ntohl(jumbo->ip6j_payload_len);
+ //printf("Jumbo option: type %" PRIu32 " len %" PRIu32 " payload len %" PRIu32 "\n",
+ // jumbo->ip6j_type, jumbo->ip6j_len, jumbo->ip6j_payload_len);
+ }
+ else if (*ptr == IPV6OPT_HAO) /* HAO */
+ {
+ hao->ip6hao_type = *(ptr);
+ hao->ip6hao_len = ip6_optlen;
+
+ if (ip6_optlen < sizeof(hao->ip6hao_hoa)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
+ break;
+ }
+
+ memcpy(&hao->ip6hao_hoa, (ptr+2), sizeof(hao->ip6hao_hoa));
+ //printf("HAO option: type %" PRIu32 " len %" PRIu32 " ",
+ // hao->ip6hao_type, hao->ip6hao_len);
+ //char addr_buf[46];
+ //PrintInet(AF_INET6, (char *)&(hao->ip6hao_hoa),
+ // addr_buf,sizeof(addr_buf));
+ //printf("home addr %s\n", addr_buf);
+ other_cnt++;
+ } else {
+ if (nh == IPPROTO_HOPOPTS)
+ ENGINE_SET_EVENT(p, IPV6_HOPOPTS_UNKNOWN_OPT);
+ else
+ ENGINE_SET_EVENT(p, IPV6_DSTOPTS_UNKNOWN_OPT);
+
+ other_cnt++;
+ }
+ uint16_t optlen = (*(ptr + 1) + 2);
+ ptr += optlen; /* +2 for opt type and opt len fields */
+ offset += optlen;
+ }
+ /* flag packets that have only padding */
+ if (padn_cnt > 0 && other_cnt == 0) {
+ if (nh == IPPROTO_HOPOPTS)
+ ENGINE_SET_EVENT(p, IPV6_HOPOPTS_ONLY_PADDING);
+ else
+ ENGINE_SET_EVENT(p, IPV6_DSTOPTS_ONLY_PADDING);
+ }
+
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ case IPPROTO_FRAGMENT:
+ IPV6_SET_L4PROTO(p,nh);
+ /* store the offset of this extension into the packet
+ * past the ipv6 header. We use it in defrag for creating
+ * a defragmented packet without the frag header */
+ if (exthdr_fh_done == 0) {
+ p->ip6eh.fh_offset = pkt - orig_pkt;
+ exthdr_fh_done = 1;
+ }
+
+ hdrextlen = sizeof(IPV6FragHdr);
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+
+ /* for the frag header, the length field is reserved */
+ if (*(pkt + 1) != 0) {
+ ENGINE_SET_EVENT(p, IPV6_FH_NON_ZERO_RES_FIELD);
+ /* non fatal, lets try to continue */
+ }
+
+ if(p->IPV6_EH_CNT<IPV6_MAX_OPT)
+ {
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].type = nh;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].next = *pkt;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].len = hdrextlen;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].data = pkt+2;
+ p->IPV6_EH_CNT++;
+ }
+
+ if (IPV6_EXTHDR_ISSET_FH(p)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_FH);
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ /* set the header ptr first */
+ IPV6_EXTHDR_SET_FH(p, pkt);
+
+ /* if FH has offset 0 and no more fragments are coming, we
+ * parse this packet further right away, no defrag will be
+ * needed. It is a useless FH then though, so we do set an
+ * decoder event. */
+ if (IPV6_EXTHDR_GET_FH_FLAG(p) == 0 && IPV6_EXTHDR_GET_FH_OFFSET(p) == 0) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_USELESS_FH);
+
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ /* the rest is parsed upon reassembly */
+ p->flags |= PKT_IS_FRAGMENT;
+ SCReturn;
+
+ case IPPROTO_ESP:
+ {
+ IPV6_SET_L4PROTO(p,nh);
+ hdrextlen = sizeof(IPV6EspHdr);
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+
+ if(p->IPV6_EH_CNT<IPV6_MAX_OPT)
+ {
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].type = nh;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].next = IPPROTO_NONE;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].len = hdrextlen;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].data = pkt+2;
+ p->IPV6_EH_CNT++;
+ }
+
+ if (IPV6_EXTHDR_ISSET_EH(p)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_EH);
+ SCReturn;
+ }
+
+ IPV6_EXTHDR_SET_EH(p, pkt);
+
+ nh = IPPROTO_NONE;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+ case IPPROTO_AH:
+ {
+ IPV6_SET_L4PROTO(p,nh);
+ /* we need the header as a minimum */
+ hdrextlen = sizeof(IPV6AuthHdr);
+ /* the payload len field is the number of extra 4 byte fields,
+ * IPV6AuthHdr already contains the first */
+ if (*(pkt+1) > 0)
+ hdrextlen += ((*(pkt+1) - 1) * 4);
+
+ SCLogDebug("hdrextlen %"PRIu8, hdrextlen);
+
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+
+ IPV6AuthHdr *ahhdr = (IPV6AuthHdr *)pkt;
+ if (ahhdr->ip6ah_reserved != 0x0000) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_AH_RES_NOT_NULL);
+ }
+
+ if(p->IPV6_EH_CNT < IPV6_MAX_OPT)
+ {
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].type = nh;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].next = *pkt;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].len = hdrextlen;
+ p->IPV6_EXTHDRS[p->IPV6_EH_CNT].data = pkt+2;
+ p->IPV6_EH_CNT++;
+ }
+
+ if (IPV6_EXTHDR_ISSET_AH(p)) {
+ ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_AH);
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+
+ IPV6_EXTHDR_SET_AH(p, pkt);
+
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ }
+ case IPPROTO_IPIP:
+ IPV6_SET_L4PROTO(p,nh);
+ DecodeIPv4inIPv6(tv, dtv, p, pkt, plen, pq);
+ SCReturn;
+ /* none, last header */
+ case IPPROTO_NONE:
+ IPV6_SET_L4PROTO(p,nh);
+ SCReturn;
+ case IPPROTO_ICMP:
+ ENGINE_SET_EVENT(p,IPV6_WITH_ICMPV4);
+ SCReturn;
+ /* no parsing yet, just skip it */
+ case IPPROTO_MH:
+ case IPPROTO_HIP:
+ case IPPROTO_SHIM6:
+ hdrextlen = 8 + (*(pkt+1) * 8); /* 8 bytes + length in 8 octet units */
+ if (hdrextlen > plen) {
+ ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
+ SCReturn;
+ }
+ nh = *pkt;
+ pkt += hdrextlen;
+ plen -= hdrextlen;
+ break;
+ default:
+ ENGINE_SET_EVENT(p, IPV6_UNKNOWN_NEXT_HEADER);
+ IPV6_SET_L4PROTO(p,nh);
+ SCReturn;
+ }
+ }
+
+ SCReturn;
+}
+
+static int DecodeIPV6Packet (ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len)
+{
+ if (unlikely(len < IPV6_HEADER_LEN)) {
+ return -1;
+ }
+
+ if (unlikely(IP_GET_RAW_VER(pkt) != 6)) {
+ SCLogDebug("wrong ip version %" PRIu8 "",IP_GET_RAW_VER(pkt));
+ ENGINE_SET_INVALID_EVENT(p, IPV6_WRONG_IP_VER);
+ return -1;
+ }
+
+ p->ip6h = (IPV6Hdr *)pkt;
+
+ if (unlikely(len < (IPV6_HEADER_LEN + IPV6_GET_PLEN(p))))
+ {
+ ENGINE_SET_INVALID_EVENT(p, IPV6_TRUNC_PKT);
+ return -1;
+ }
+
+ SET_IPV6_SRC_ADDR(p,&p->src);
+ SET_IPV6_DST_ADDR(p,&p->dst);
+
+ return 0;
+}
+
+int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ int ret;
+
+ StatsIncr(tv, dtv->counter_ipv6);
+
+ /* do the actual decoding */
+ ret = DecodeIPV6Packet (tv, dtv, p, pkt, len);
+ if (unlikely(ret < 0)) {
+ p->ip6h = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) { /* only convert the addresses if debug is really enabled */
+ /* debug print */
+ char s[46], d[46];
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), s, sizeof(s));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), d, sizeof(d));
+ SCLogDebug("IPV6 %s->%s - CLASS: %" PRIu32 " FLOW: %" PRIu32 " NH: %" PRIu32 " PLEN: %" PRIu32 " HLIM: %" PRIu32 "", s,d,
+ IPV6_GET_CLASS(p), IPV6_GET_FLOW(p), IPV6_GET_NH(p), IPV6_GET_PLEN(p),
+ IPV6_GET_HLIM(p));
+ }
+#endif /* DEBUG */
+
+ /* now process the Ext headers and/or the L4 Layer */
+ switch(IPV6_GET_NH(p)) {
+ case IPPROTO_TCP:
+ IPV6_SET_L4PROTO (p, IPPROTO_TCP);
+ DecodeTCP(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_UDP:
+ IPV6_SET_L4PROTO (p, IPPROTO_UDP);
+ DecodeUDP(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_ICMPV6:
+ IPV6_SET_L4PROTO (p, IPPROTO_ICMPV6);
+ DecodeICMPV6(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_SCTP:
+ IPV6_SET_L4PROTO (p, IPPROTO_SCTP);
+ DecodeSCTP(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_IPIP:
+ IPV6_SET_L4PROTO(p, IPPROTO_IPIP);
+ DecodeIPv4inIPv6(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_IPV6:
+ DecodeIP6inIP6(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ return TM_ECODE_OK;
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_NONE:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_AH:
+ case IPPROTO_ESP:
+ case IPPROTO_MH:
+ case IPPROTO_HIP:
+ case IPPROTO_SHIM6:
+ DecodeIPV6ExtHdrs(tv, dtv, p, pkt + IPV6_HEADER_LEN, IPV6_GET_PLEN(p), pq);
+ break;
+ case IPPROTO_ICMP:
+ ENGINE_SET_EVENT(p,IPV6_WITH_ICMPV4);
+ break;
+ default:
+ ENGINE_SET_EVENT(p, IPV6_UNKNOWN_NEXT_HEADER);
+ IPV6_SET_L4PROTO (p, IPV6_GET_NH(p));
+ break;
+ }
+ p->proto = IPV6_GET_L4PROTO (p);
+
+ /* Pass to defragger if a fragment. */
+ if (IPV6_EXTHDR_ISSET_FH(p)) {
+ Packet *rp = Defrag(tv, dtv, p, pq);
+ if (rp != NULL) {
+ PacketEnqueue(pq,rp);
+ }
+ }
+
+#ifdef DEBUG
+ if (IPV6_EXTHDR_ISSET_FH(p)) {
+ SCLogDebug("IPV6 FRAG - HDRLEN: %" PRIuMAX " NH: %" PRIu32 " OFFSET: %" PRIu32 " ID: %" PRIu32 "",
+ (uintmax_t)IPV6_EXTHDR_GET_FH_HDRLEN(p), IPV6_EXTHDR_GET_FH_NH(p),
+ IPV6_EXTHDR_GET_FH_OFFSET(p), IPV6_EXTHDR_GET_FH_ID(p));
+ }
+ if (IPV6_EXTHDR_ISSET_RH(p)) {
+ SCLogDebug("IPV6 ROUTE - HDRLEN: %" PRIu32 " NH: %" PRIu32 " TYPE: %" PRIu32 "",
+ IPV6_EXTHDR_GET_RH_HDRLEN(p), IPV6_EXTHDR_GET_RH_NH(p),
+ IPV6_EXTHDR_GET_RH_TYPE(p));
+ }
+ if (IPV6_EXTHDR_ISSET_HH(p)) {
+ SCLogDebug("IPV6 HOPOPT - HDRLEN: %" PRIu32 " NH: %" PRIu32 "",
+ IPV6_EXTHDR_GET_HH_HDRLEN(p), IPV6_EXTHDR_GET_HH_NH(p));
+ }
+ if (IPV6_EXTHDR_ISSET_DH1(p)) {
+ SCLogDebug("IPV6 DSTOPT1 - HDRLEN: %" PRIu32 " NH: %" PRIu32 "",
+ IPV6_EXTHDR_GET_DH1_HDRLEN(p), IPV6_EXTHDR_GET_DH1_NH(p));
+ }
+ if (IPV6_EXTHDR_ISSET_DH2(p)) {
+ SCLogDebug("IPV6 DSTOPT2 - HDRLEN: %" PRIu32 " NH: %" PRIu32 "",
+ IPV6_EXTHDR_GET_DH2_HDRLEN(p), IPV6_EXTHDR_GET_DH2_NH(p));
+ }
+#endif
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test fragment decoding
+ */
+static int DecodeIPV6FragTest01 (void)
+{
+
+ uint8_t raw_frag1[] = {
+ 0x60, 0x0f, 0x1a, 0xcf, 0x05, 0xa8, 0x2c, 0x36, 0x20, 0x01, 0x04, 0x70, 0x00, 0x01, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x09, 0x80, 0x32, 0xb2, 0x00, 0x01,
+ 0x2e, 0x41, 0x38, 0xff, 0xfe, 0xa7, 0xea, 0xeb, 0x06, 0x00, 0x00, 0x01, 0xdf, 0xf8, 0x11, 0xd7,
+ 0x00, 0x50, 0xa6, 0x5c, 0xcc, 0xd7, 0x28, 0x9f, 0xc3, 0x34, 0xc6, 0x58, 0x80, 0x10, 0x20, 0x13,
+ 0x18, 0x1f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xcd, 0xf9, 0x3a, 0x41, 0x00, 0x1a, 0x91, 0x8a,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x30, 0x32, 0x20, 0x44,
+ 0x65, 0x63, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20, 0x30, 0x38, 0x3a, 0x33, 0x32, 0x3a, 0x35, 0x37,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x0d, 0x0a, 0x43, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x43, 0x6f, 0x6e, 0x74,
+ 0x72, 0x6f, 0x6c, 0x3a, 0x20, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x0d, 0x0a, 0x50,
+ 0x72, 0x61, 0x67, 0x6d, 0x61, 0x3a, 0x20, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x0d,
+ 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x3a, 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30,
+ 0x31, 0x20, 0x4a, 0x61, 0x6e, 0x20, 0x31, 0x39, 0x37, 0x31, 0x20, 0x30, 0x30, 0x3a, 0x30, 0x30,
+ 0x3a, 0x30, 0x30, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+ 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x35, 0x39, 0x39, 0x0d, 0x0a, 0x4b,
+ 0x65, 0x65, 0x70, 0x2d, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x3a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f,
+ 0x75, 0x74, 0x3d, 0x35, 0x2c, 0x20, 0x6d, 0x61, 0x78, 0x3d, 0x39, 0x39, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x4b, 0x65, 0x65, 0x70, 0x2d, 0x41,
+ 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
+ 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3b, 0x63, 0x68, 0x61, 0x72, 0x73,
+ 0x65, 0x74, 0x3d, 0x61, 0x73, 0x63, 0x69, 0x69, 0x0d, 0x0a, 0x0d, 0x0a, 0x5f, 0x6a, 0x71, 0x6a,
+ 0x73, 0x70, 0x28, 0x7b, 0x22, 0x69, 0x70, 0x22, 0x3a, 0x22, 0x32, 0x30, 0x30, 0x31, 0x3a, 0x39,
+ 0x38, 0x30, 0x3a, 0x33, 0x32, 0x62, 0x32, 0x3a, 0x31, 0x3a, 0x32, 0x65, 0x34, 0x31, 0x3a, 0x33,
+ 0x38, 0x66, 0x66, 0x3a, 0x66, 0x65, 0x61, 0x37, 0x3a, 0x65, 0x61, 0x65, 0x62, 0x22, 0x2c, 0x22,
+ 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x69, 0x70, 0x76, 0x36, 0x22, 0x2c, 0x22, 0x73, 0x75,
+ 0x62, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x76, 0x69, 0x61, 0x22, 0x3a,
+ 0x22, 0x22, 0x2c, 0x22, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x22, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ };
+ uint8_t raw_frag2[] = {
+ 0x60, 0x0f, 0x1a, 0xcf, 0x00, 0x1c, 0x2c, 0x36, 0x20, 0x01, 0x04, 0x70, 0x00, 0x01, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x09, 0x80, 0x32, 0xb2, 0x00, 0x01,
+ 0x2e, 0x41, 0x38, 0xff, 0xfe, 0xa7, 0xea, 0xeb, 0x06, 0x00, 0x05, 0xa0, 0xdf, 0xf8, 0x11, 0xd7,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20,
+ };
+ Packet *pkt;
+ Packet *p1 = PacketGetFromAlloc();
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = PacketGetFromAlloc();
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int result = 0;
+ PacketQueue pq;
+
+ FlowInitConfig(FLOW_QUIET);
+ DefragInit();
+
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ PacketCopyData(p1, raw_frag1, sizeof(raw_frag1));
+ PacketCopyData(p2, raw_frag2, sizeof(raw_frag2));
+
+ DecodeIPV6(&tv, &dtv, p1, GET_PKT_DATA(p1), GET_PKT_LEN(p1), &pq);
+
+ if (!(IPV6_EXTHDR_ISSET_FH(p1))) {
+ printf("ipv6 frag header not detected: ");
+ goto end;
+ }
+
+ DecodeIPV6(&tv, &dtv, p2, GET_PKT_DATA(p2), GET_PKT_LEN(p2), &pq);
+
+ if (!(IPV6_EXTHDR_ISSET_FH(p2))) {
+ printf("ipv6 frag header not detected: ");
+ goto end;
+ }
+
+ if (pq.len != 1) {
+ printf("no reassembled packet: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ PACKET_RECYCLE(p1);
+ PACKET_RECYCLE(p2);
+ SCFree(p1);
+ SCFree(p2);
+ pkt = PacketDequeue(&pq);
+ while (pkt != NULL) {
+ PACKET_RECYCLE(pkt);
+ SCFree(pkt);
+ pkt = PacketDequeue(&pq);
+ }
+ DefragDestroy();
+ FlowShutdown();
+ return result;
+}
+
+/**
+ * \test routing header decode
+ */
+static int DecodeIPV6RouteTest01 (void)
+{
+
+ uint8_t raw_pkt1[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x2b, 0x40,
+ 0x20, 0x01, 0xaa, 0xaa, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x20, 0x01, 0xaa, 0xaa, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0xb2, 0xed, 0x00, 0x50, 0x1b, 0xc7, 0x6a, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x20, 0x00,
+ 0xfa, 0x87, 0x00, 0x00,
+ };
+ Packet *p1 = PacketGetFromAlloc();
+ if (unlikely(p1 == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int result = 0;
+ PacketQueue pq;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ PacketCopyData(p1, raw_pkt1, sizeof(raw_pkt1));
+
+ DecodeIPV6(&tv, &dtv, p1, GET_PKT_DATA(p1), GET_PKT_LEN(p1), &pq);
+
+ if (!(IPV6_EXTHDR_ISSET_RH(p1))) {
+ printf("ipv6 routing header not detected: ");
+ goto end;
+ }
+
+ if (p1->ip6eh.ip6_exthdrs[0].len != 8) {
+ printf("ipv6 routing length incorrect: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ PACKET_RECYCLE(p1);
+ SCFree(p1);
+ FlowShutdown();
+ return result;
+}
+
+/**
+ * \test HOP header decode
+ */
+static int DecodeIPV6HopTest01 (void)
+{
+ uint8_t raw_pkt1[] = {
+ 0x60,0x00,0x00,0x00,0x00,0x20,0x00,0x01,0xfe,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x02,0x0f,0xfe,0xff,0xfe,0x98,0x3d,0x01,0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x3a,0x00,0x05,0x02,0x00,0x00,0x00,0x00,
+ 0x82,0x00,0x1c,0x6f,0x27,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ };
+ Packet *p1 = PacketGetFromAlloc();
+ if (unlikely(p1 == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int result = 0;
+ PacketQueue pq;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ memset(&pq, 0, sizeof(PacketQueue));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ PacketCopyData(p1, raw_pkt1, sizeof(raw_pkt1));
+
+ DecodeIPV6(&tv, &dtv, p1, GET_PKT_DATA(p1), GET_PKT_LEN(p1), &pq);
+
+ if (!(IPV6_EXTHDR_ISSET_HH(p1))) {
+ printf("ipv6 routing header not detected: ");
+ goto end;
+ }
+
+ if (p1->ip6eh.ip6_exthdrs[0].len != 8) {
+ printf("ipv6 routing length incorrect: ");
+ goto end;
+ }
+
+ if (ENGINE_ISSET_EVENT(p1, IPV6_HOPOPTS_UNKNOWN_OPT)) {
+ printf("engine event IPV6_HOPOPTS_UNKNOWN_OPT set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ PACKET_RECYCLE(p1);
+ SCFree(p1);
+ FlowShutdown();
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for IPV6 decoder
+ */
+
+void DecodeIPV6RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeIPV6FragTest01", DecodeIPV6FragTest01, 1);
+ UtRegisterTest("DecodeIPV6RouteTest01", DecodeIPV6RouteTest01, 1);
+ UtRegisterTest("DecodeIPV6HopTest01", DecodeIPV6HopTest01, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-ipv6.h b/framework/src/suricata/src/decode-ipv6.h
new file mode 100644
index 00000000..da58d9e2
--- /dev/null
+++ b/framework/src/suricata/src/decode-ipv6.h
@@ -0,0 +1,334 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_IPV6_H__
+#define __DECODE_IPV6_H__
+
+#define IPV6_HEADER_LEN 40
+#define IPV6_MAXPACKET 65535 /* maximum packet size */
+#define IPV6_MAX_OPT 40
+
+typedef struct IPV6Hdr_
+{
+ union {
+ struct ip6_un1_ {
+ uint32_t ip6_un1_flow; /* 20 bits of flow-ID */
+ uint16_t ip6_un1_plen; /* payload length */
+ uint8_t ip6_un1_nxt; /* next header */
+ uint8_t ip6_un1_hlim; /* hop limit */
+ } ip6_un1;
+ uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */
+ } ip6_hdrun;
+
+ union {
+ struct {
+ uint32_t ip6_src[4];
+ uint32_t ip6_dst[4];
+ } ip6_un2;
+ uint16_t ip6_addrs[16];
+ } ip6_hdrun2;
+} __attribute__((__packed__)) IPV6Hdr;
+
+#define s_ip6_src ip6_hdrun2.ip6_un2.ip6_src
+#define s_ip6_dst ip6_hdrun2.ip6_un2.ip6_dst
+#define s_ip6_addrs ip6_hdrun2.ip6_addrs
+
+#define s_ip6_vfc ip6_hdrun.ip6_un2_vfc
+#define s_ip6_flow ip6_hdrun.ip6_un1.ip6_un1_flow
+#define s_ip6_plen ip6_hdrun.ip6_un1.ip6_un1_plen
+#define s_ip6_nxt ip6_hdrun.ip6_un1.ip6_un1_nxt
+#define s_ip6_hlim ip6_hdrun.ip6_un1.ip6_un1_hlim
+
+#define IPV6_GET_RAW_VER(ip6h) (((ip6h)->s_ip6_vfc & 0xf0) >> 4)
+#define IPV6_GET_RAW_CLASS(ip6h) ((ntohl((ip6h)->s_ip6_flow) & 0x0FF00000) >> 20)
+#define IPV6_GET_RAW_FLOW(ip6h) (ntohl((ip6h)->s_ip6_flow) & 0x000FFFFF)
+#define IPV6_GET_RAW_NH(ip6h) ((ip6h)->s_ip6_nxt)
+#define IPV6_GET_RAW_PLEN(ip6h) (ntohs((ip6h)->s_ip6_plen))
+#define IPV6_GET_RAW_HLIM(ip6h) ((ip6h)->s_ip6_hlim)
+
+#define IPV6_SET_RAW_VER(ip6h, value) ((ip6h)->s_ip6_vfc = (((ip6h)->s_ip6_vfc & 0x0f) | (value << 4)))
+#define IPV6_SET_RAW_NH(ip6h, value) ((ip6h)->s_ip6_nxt = (value))
+
+#define IPV6_SET_L4PROTO(p,proto) (p)->ip6vars.l4proto = proto
+
+
+/* ONLY call these functions after making sure that:
+ * 1. p->ip6h is set
+ * 2. p->ip6h is valid (len is correct)
+ */
+#define IPV6_GET_VER(p) \
+ IPV6_GET_RAW_VER((p)->ip6h)
+#define IPV6_GET_CLASS(p) \
+ IPV6_GET_RAW_CLASS((p)->ip6h)
+#define IPV6_GET_FLOW(p) \
+ IPV6_GET_RAW_FLOW((p)->ip6h)
+#define IPV6_GET_NH(p) \
+ (IPV6_GET_RAW_NH((p)->ip6h))
+#define IPV6_GET_PLEN(p) \
+ IPV6_GET_RAW_PLEN((p)->ip6h)
+#define IPV6_GET_HLIM(p) \
+ (IPV6_GET_RAW_HLIM((p)->ip6h))
+/* XXX */
+#define IPV6_GET_L4PROTO(p) \
+ ((p)->ip6vars.l4proto)
+
+/** \brief get the highest proto/next header field we know */
+//#define IPV6_GET_UPPER_PROTO(p) (p)->ip6eh.ip6_exthdrs_cnt ?
+// (p)->ip6eh.ip6_exthdrs[(p)->ip6eh.ip6_exthdrs_cnt - 1].next : IPV6_GET_NH((p))
+
+/* helper structure with parsed ipv6 info */
+typedef struct IPV6Vars_
+{
+ uint8_t ip_opts_len;
+ uint8_t l4proto; /* the proto after the extension headers
+ * store while decoding so we don't have
+ * to loop through the exthdrs all the time */
+} IPV6Vars;
+
+#define CLEAR_IPV6_PACKET(p) do { \
+ (p)->ip6h = NULL; \
+ (p)->ip6vars.ip_opts_len = 0; \
+ (p)->ip6vars.l4proto = 0; \
+ (p)->ip6eh.ip6fh = NULL; \
+ (p)->ip6eh.fh_offset = 0; \
+ (p)->ip6eh.ip6rh = NULL; \
+ (p)->ip6eh.ip6eh = NULL; \
+ (p)->ip6eh.ip6dh1 = NULL; \
+ (p)->ip6eh.ip6dh2 = NULL; \
+ (p)->ip6eh.ip6hh = NULL; \
+ (p)->ip6eh.ip6_exthdrs_cnt = 0; \
+} while (0)
+
+/* Fragment header */
+typedef struct IPV6FragHdr_
+{
+ uint8_t ip6fh_nxt; /* next header */
+ uint8_t ip6fh_reserved; /* reserved field */
+ uint16_t ip6fh_offlg; /* offset, reserved, and flag */
+ uint32_t ip6fh_ident; /* identification */
+} __attribute__((__packed__)) IPV6FragHdr;
+
+#define IPV6_EXTHDR_GET_RAW_FH_NH(p) ((p)->ip6eh.ip6fh->ip6fh_nxt)
+#define IPV6_EXTHDR_GET_RAW_FH_HDRLEN(p) sizeof(IPV6FragHdr)
+#define IPV6_EXTHDR_GET_RAW_FH_OFFSET(p) (ntohs((p)->ip6eh.ip6fh->ip6fh_offlg) & 0xFFF8)
+#define IPV6_EXTHDR_GET_RAW_FH_FLAG(p) (ntohs((p)->ip6eh.ip6fh->ip6fh_offlg) & 0x0001)
+#define IPV6_EXTHDR_GET_RAW_FH_ID(p) (ntohl((p)->ip6eh.ip6fh->ip6fh_ident))
+
+#define IPV6_EXTHDR_GET_FH_NH(p) IPV6_EXTHDR_GET_RAW_FH_NH((p))
+#define IPV6_EXTHDR_GET_FH_HDRLEN(p) IPV6_EXTHDR_GET_RAW_FH_HDRLEN((p))
+#define IPV6_EXTHDR_GET_FH_OFFSET(p) IPV6_EXTHDR_GET_RAW_FH_OFFSET((p))
+#define IPV6_EXTHDR_GET_FH_FLAG(p) IPV6_EXTHDR_GET_RAW_FH_FLAG((p))
+#define IPV6_EXTHDR_GET_FH_ID(p) IPV6_EXTHDR_GET_RAW_FH_ID((p))
+
+/* rfc 1826 */
+typedef struct IPV6AuthHdr_
+{
+ uint8_t ip6ah_nxt; /* next header */
+ uint8_t ip6ah_len; /* header length in units of 8 bytes, not
+ including first 8 bytes. */
+ uint16_t ip6ah_reserved; /* reserved for future use */
+ uint32_t ip6ah_spi; /* SECURITY PARAMETERS INDEX (SPI) */
+ uint32_t ip6ah_seq; /* sequence number */
+} __attribute__((__packed__)) IPV6AuthHdr;
+
+typedef struct IPV6EspHdr_
+{
+ uint32_t ip6esph_spi; /* SECURITY PARAMETERS INDEX (SPI) */
+ uint32_t ip6esph_seq; /* sequence number */
+} __attribute__((__packed__)) IPV6EspHdr;
+
+typedef struct IPV6RouteHdr_
+{
+ uint8_t ip6rh_nxt; /* next header */
+ uint8_t ip6rh_len; /* header length in units of 8 bytes, not
+ including first 8 bytes. */
+ uint8_t ip6rh_type; /* routing type */
+ uint8_t ip6rh_segsleft; /* segments left */
+#if 0
+ struct in6_addr ip6rh0_addr[23]; /* type 0 addresses */
+ uint8_t ip6rh0_num_addrs; /* number of actual addresses in the
+ array/packet. The array is guarranteed
+ to be filled up to this number. */
+#endif
+} __attribute__((__packed__)) IPV6RouteHdr;
+
+#define IPV6_EXTHDR_GET_RAW_RH_NH(p) ((p)->ip6eh.ip6rh->ip6rh_nxt)
+#define IPV6_EXTHDR_GET_RAW_RH_HDRLEN(p) ((p)->ip6eh.ip6rh->ip6rh_len)
+#define IPV6_EXTHDR_GET_RAW_RH_TYPE(p) (ntohs((p)->ip6eh.ip6rh->ip6rh_type))
+/* XXX */
+
+#define IPV6_EXTHDR_GET_RH_NH(p) IPV6_EXTHDR_GET_RAW_RH_NH((p))
+#define IPV6_EXTHDR_GET_RH_HDRLEN(p) IPV6_EXTHDR_GET_RAW_RH_HDRLEN((p))
+#define IPV6_EXTHDR_GET_RH_TYPE(p) IPV6_EXTHDR_GET_RAW_RH_TYPE((p))
+/* XXX */
+
+
+/* Hop-by-Hop header and Destination Options header use options that are
+ * defined here. */
+
+#define IPV6OPT_PAD1 0x00
+#define IPV6OPT_PADN 0x01
+#define IPV6OPT_RA 0x05
+#define IPV6OPT_JUMBO 0xC2
+#define IPV6OPT_HAO 0xC9
+
+/* Home Address Option */
+typedef struct IPV6OptHAO_
+{
+ uint8_t ip6hao_type; /* Option type */
+ uint8_t ip6hao_len; /* Option Data len (excludes type and len) */
+ struct in6_addr ip6hao_hoa; /* Home address. */
+} IPV6OptHAO;
+
+/* Router Alert Option */
+typedef struct IPV6OptRA_
+{
+ uint8_t ip6ra_type; /* Option type */
+ uint8_t ip6ra_len; /* Option Data len (excludes type and len) */
+ uint16_t ip6ra_value; /* Router Alert value */
+} IPV6OptRA;
+
+/* Jumbo Option */
+typedef struct IPV6OptJumbo_
+{
+ uint8_t ip6j_type; /* Option type */
+ uint8_t ip6j_len; /* Option Data len (excludes type and len) */
+ uint32_t ip6j_payload_len; /* Jumbo Payload Length */
+} IPV6OptJumbo;
+
+typedef struct IPV6HopOptsHdr_
+{
+ uint8_t ip6hh_nxt; /* next header */
+ uint8_t ip6hh_len; /* header length in units of 8 bytes, not
+ including first 8 bytes. */
+} __attribute__((__packed__)) IPV6HopOptsHdr;
+
+#define IPV6_EXTHDR_GET_RAW_HH_NH(p) ((p)->ip6eh.ip6hh->ip6hh_nxt)
+#define IPV6_EXTHDR_GET_RAW_HH_HDRLEN(p) ((p)->ip6eh.ip6hh->ip6hh_len)
+/* XXX */
+
+#define IPV6_EXTHDR_GET_HH_NH(p) IPV6_EXTHDR_GET_RAW_HH_NH((p))
+#define IPV6_EXTHDR_GET_HH_HDRLEN(p) IPV6_EXTHDR_GET_RAW_HH_HDRLEN((p))
+/* XXX */
+
+typedef struct IPV6DstOptsHdr_
+{
+ uint8_t ip6dh_nxt; /* next header */
+ uint8_t ip6dh_len; /* header length in units of 8 bytes, not
+ including first 8 bytes. */
+} __attribute__((__packed__)) IPV6DstOptsHdr;
+
+#define IPV6_EXTHDR_GET_RAW_DH1_NH(p) ((p)->ip6eh.ip6dh1->ip6dh_nxt)
+#define IPV6_EXTHDR_GET_RAW_DH1_HDRLEN(p) ((p)->ip6eh.ip6dh1->ip6dh_len)
+/* XXX */
+
+#define IPV6_EXTHDR_GET_DH1_NH(p) IPV6_EXTHDR_GET_RAW_DH1_NH((p))
+#define IPV6_EXTHDR_GET_DH1_HDRLEN(p) IPV6_EXTHDR_GET_RAW_DH1_HDRLEN((p))
+/* XXX */
+
+#define IPV6_EXTHDR_GET_RAW_DH2_NH(p) ((p)->ip6eh.ip6dh2->ip6dh_nxt)
+#define IPV6_EXTHDR_GET_RAW_DH2_HDRLEN(p) ((p)->ip6eh.ip6dh2->ip6dh_len)
+/* XXX */
+
+#define IPV6_EXTHDR_GET_DH2_NH(p) IPV6_EXTHDR_GET_RAW_DH2_NH((p))
+#define IPV6_EXTHDR_GET_DH2_HDRLEN(p) IPV6_EXTHDR_GET_RAW_DH2_HDRLEN((p))
+/* XXX */
+
+typedef struct IPV6GenOptHdr_
+{
+ uint8_t type;
+ uint8_t next;
+ uint8_t len;
+ uint8_t *data;
+} IPV6GenOptHdr;
+
+typedef struct IPV6ExtHdrs_
+{
+ const IPV6FragHdr *ip6fh;
+ /* In fh_offset we store the offset of this extension into the packet past
+ * the ipv6 header. We use it in defrag for creating a defragmented packet
+ * without the frag header */
+ uint16_t fh_offset;
+
+ const IPV6RouteHdr *ip6rh;
+ const IPV6AuthHdr *ip6ah;
+ const IPV6EspHdr *ip6eh;
+ const IPV6DstOptsHdr *ip6dh1;
+ const IPV6DstOptsHdr *ip6dh2;
+ const IPV6HopOptsHdr *ip6hh;
+
+ /* Hop-By-Hop options */
+ IPV6OptHAO ip6hh_opt_hao;
+ IPV6OptRA ip6hh_opt_ra;
+ IPV6OptJumbo ip6hh_opt_jumbo;
+ /* Dest Options 1 */
+ IPV6OptHAO ip6dh1_opt_hao;
+ IPV6OptRA ip6dh1_opt_ra;
+ IPV6OptJumbo ip6dh1_opt_jumbo;
+ /* Dest Options 2 */
+ IPV6OptHAO ip6dh2_opt_hao;
+ IPV6OptRA ip6dh2_opt_ra;
+ IPV6OptJumbo ip6dh2_opt_jumbo;
+
+ IPV6GenOptHdr ip6_exthdrs[IPV6_MAX_OPT];
+ uint8_t ip6_exthdrs_cnt;
+
+} IPV6ExtHdrs;
+
+#define IPV6_EXTHDR_FH(p) (p)->ip6eh.ip6fh
+#define IPV6_EXTHDR_RH(p) (p)->ip6eh.ip6rh
+#define IPV6_EXTHDR_AH(p) (p)->ip6eh.ip6ah
+#define IPV6_EXTHDR_EH(p) (p)->ip6eh.ip6eh
+#define IPV6_EXTHDR_DH1(p) (p)->ip6eh.ip6dh1
+#define IPV6_EXTHDR_DH2(p) (p)->ip6eh.ip6dh2
+#define IPV6_EXTHDR_HH(p) (p)->ip6eh.ip6hh
+
+#define IPV6_EXTHDR_HH_HAO(p) (p)->ip6eh.ip6hh_opt_hao
+#define IPV6_EXTHDR_DH1_HAO(p) (p)->ip6eh.ip6dh1_opt_hao
+#define IPV6_EXTHDR_DH2_HAO(p) (p)->ip6eh.ip6dh2_opt_hao
+#define IPV6_EXTHDR_HH_RA(p) (p)->ip6eh.ip6hh_opt_ra
+#define IPV6_EXTHDR_DH1_RA(p) (p)->ip6eh.ip6dh1_opt_ra
+#define IPV6_EXTHDR_DH2_RA(p) (p)->ip6eh.ip6dh2_opt_ra
+#define IPV6_EXTHDR_HH_JUMBO(p) (p)->ip6eh.ip6hh_opt_jumbo
+#define IPV6_EXTHDR_DH1_JUMBO(p) (p)->ip6eh.ip6dh1_opt_jumbo
+#define IPV6_EXTHDR_DH2_JUMBO(p) (p)->ip6eh.ip6dh2_opt_jumbo
+
+#define IPV6_EXTHDR_SET_FH(p,pkt) IPV6_EXTHDR_FH((p)) = (IPV6FragHdr *)pkt
+#define IPV6_EXTHDR_ISSET_FH(p) (IPV6_EXTHDR_FH((p)) != NULL)
+#define IPV6_EXTHDR_SET_RH(p,pkt) IPV6_EXTHDR_RH((p)) = (IPV6RouteHdr *)pkt
+#define IPV6_EXTHDR_ISSET_RH(p) (IPV6_EXTHDR_RH((p)) != NULL)
+#define IPV6_EXTHDR_SET_AH(p,pkt) IPV6_EXTHDR_AH((p)) = (IPV6AuthHdr *)pkt
+#define IPV6_EXTHDR_ISSET_AH(p) (IPV6_EXTHDR_AH((p)) != NULL)
+#define IPV6_EXTHDR_SET_EH(p,pkt) IPV6_EXTHDR_EH((p)) = (IPV6EspHdr *)pkt
+#define IPV6_EXTHDR_ISSET_EH(p) (IPV6_EXTHDR_EH((p)) != NULL)
+#define IPV6_EXTHDR_SET_DH1(p,pkt) IPV6_EXTHDR_DH1((p)) = (IPV6DstOptsHdr *)pkt
+#define IPV6_EXTHDR_ISSET_DH1(p) (IPV6_EXTHDR_DH1((p)) != NULL)
+#define IPV6_EXTHDR_SET_DH2(p,pkt) IPV6_EXTHDR_DH2((p)) = (IPV6DstOptsHdr *)pkt
+#define IPV6_EXTHDR_ISSET_DH2(p) (IPV6_EXTHDR_DH2((p)) != NULL)
+#define IPV6_EXTHDR_SET_HH(p,pkt) IPV6_EXTHDR_HH((p)) = (IPV6HopOptsHdr *)pkt
+#define IPV6_EXTHDR_ISSET_HH(p) (IPV6_EXTHDR_HH((p)) != NULL)
+
+void DecodeIPV6RegisterTests(void);
+
+#endif /* __DECODE_IPV6_H__ */
+
diff --git a/framework/src/suricata/src/decode-mpls.c b/framework/src/suricata/src/decode-mpls.c
new file mode 100644
index 00000000..1569ebfb
--- /dev/null
+++ b/framework/src/suricata/src/decode-mpls.c
@@ -0,0 +1,325 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Jason Ish <jason.ish@emulex.com>
+ *
+ * MPLS decoder.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "util-unittest.h"
+
+#define MPLS_HEADER_LEN 4
+#define MPLS_PW_LEN 4
+#define MPLS_MAX_RESERVED_LABEL 15
+
+#define MPLS_LABEL_IPV4 0
+#define MPLS_LABEL_ROUTER_ALERT 1
+#define MPLS_LABEL_IPV6 2
+#define MPLS_LABEL_NULL 3
+
+#define MPLS_LABEL(shim) ntohl(shim) >> 12
+#define MPLS_BOTTOM(shim) ((ntohl(shim) >> 8) & 0x1)
+
+/* Inner protocol guessing values. */
+#define MPLS_PROTO_ETHERNET_PW 0
+#define MPLS_PROTO_IPV4 4
+#define MPLS_PROTO_IPV6 6
+
+int DecodeMPLS(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt,
+ uint16_t len, PacketQueue *pq)
+{
+ uint32_t shim;
+ int label;
+ int event = 0;
+
+ StatsIncr(tv, dtv->counter_mpls);
+
+ do {
+ if (len < MPLS_HEADER_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, MPLS_HEADER_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+ shim = *(uint32_t *)pkt;
+ pkt += MPLS_HEADER_LEN;
+ len -= MPLS_HEADER_LEN;
+ } while (MPLS_BOTTOM(shim) == 0);
+
+ label = MPLS_LABEL(shim);
+ if (label == MPLS_LABEL_IPV4) {
+ return DecodeIPV4(tv, dtv, p, pkt, len, pq);
+ }
+ else if (label == MPLS_LABEL_ROUTER_ALERT) {
+ /* Not valid at the bottom of the stack. */
+ event = MPLS_BAD_LABEL_ROUTER_ALERT;
+ }
+ else if (label == MPLS_LABEL_IPV6) {
+ return DecodeIPV6(tv, dtv, p, pkt, len, pq);
+ }
+ else if (label == MPLS_LABEL_NULL) {
+ /* Shouldn't appear on the wire. */
+ event = MPLS_BAD_LABEL_IMPLICIT_NULL;
+ }
+ else if (label < MPLS_MAX_RESERVED_LABEL) {
+ event = MPLS_BAD_LABEL_RESERVED;
+ }
+
+ if (event) {
+ goto end;
+ }
+
+ /* Best guess at inner packet. */
+ switch (pkt[0] >> 4) {
+ case MPLS_PROTO_IPV4:
+ DecodeIPV4(tv, dtv, p, pkt, len, pq);
+ break;
+ case MPLS_PROTO_IPV6:
+ DecodeIPV6(tv, dtv, p, pkt, len, pq);
+ break;
+ case MPLS_PROTO_ETHERNET_PW:
+ DecodeEthernet(tv, dtv, p, pkt + MPLS_PW_LEN, len - MPLS_PW_LEN,
+ pq);
+ break;
+ default:
+ ENGINE_SET_INVALID_EVENT(p, MPLS_UNKNOWN_PAYLOAD_TYPE);
+ return TM_ECODE_OK;
+ }
+
+end:
+ if (event) {
+ ENGINE_SET_EVENT(p, event);
+ }
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+static int DecodeMPLSTestHeaderTooSmall(void)
+{
+ int ret = 1;
+
+ /* A packet that is too small to have a complete MPLS header. */
+ uint8_t pkt[] = {
+ 0x00, 0x00, 0x11
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeMPLS(&tv, &dtv, p, pkt, sizeof(pkt), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, MPLS_HEADER_TOO_SMALL)) {
+ ret = 0;
+ }
+
+ SCFree(p);
+ return ret;
+}
+
+static int DecodeMPLSTestBadLabelRouterAlert(void)
+{
+ int ret = 1;
+ uint8_t pkt[] = {
+ 0x00, 0x00, 0x11, 0xff, 0x45, 0x00, 0x00, 0x64,
+ 0x00, 0x0a, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x6a,
+ 0x0a, 0x01, 0x02, 0x01, 0x0a, 0x22, 0x00, 0x01,
+ 0x08, 0x00, 0x3a, 0x77, 0x0a, 0x39, 0x06, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x33, 0x50,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeMPLS(&tv, &dtv, p, pkt, sizeof(pkt), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, MPLS_BAD_LABEL_ROUTER_ALERT)) {
+ ret = 0;
+ }
+
+ SCFree(p);
+ return ret;
+}
+
+static int DecodeMPLSTestBadLabelImplicitNull(void)
+{
+ int ret = 1;
+ uint8_t pkt[] = {
+ 0x00, 0x00, 0x31, 0xff, 0x45, 0x00, 0x00, 0x64,
+ 0x00, 0x0a, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x6a,
+ 0x0a, 0x01, 0x02, 0x01, 0x0a, 0x22, 0x00, 0x01,
+ 0x08, 0x00, 0x3a, 0x77, 0x0a, 0x39, 0x06, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x33, 0x50,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeMPLS(&tv, &dtv, p, pkt, sizeof(pkt), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, MPLS_BAD_LABEL_IMPLICIT_NULL)) {
+ ret = 0;
+ }
+
+ SCFree(p);
+ return ret;
+}
+
+static int DecodeMPLSTestBadLabelReserved(void)
+{
+ int ret = 1;
+ uint8_t pkt[] = {
+ 0x00, 0x00, 0x51, 0xff, 0x45, 0x00, 0x00, 0x64,
+ 0x00, 0x0a, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x6a,
+ 0x0a, 0x01, 0x02, 0x01, 0x0a, 0x22, 0x00, 0x01,
+ 0x08, 0x00, 0x3a, 0x77, 0x0a, 0x39, 0x06, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x33, 0x50,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeMPLS(&tv, &dtv, p, pkt, sizeof(pkt), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, MPLS_BAD_LABEL_RESERVED)) {
+ ret = 0;
+ }
+
+ SCFree(p);
+ return ret;
+}
+
+static int DecodeMPLSTestUnknownPayloadType(void)
+{
+ int ret = 1;
+
+ /* Valid label: 21.
+ * Unknown payload type: 1.
+ */
+ uint8_t pkt[] = {
+ 0x00, 0x01, 0x51, 0xff, 0x15, 0x00, 0x00, 0x64,
+ 0x00, 0x0a, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x6a,
+ 0x0a, 0x01, 0x02, 0x01, 0x0a, 0x22, 0x00, 0x01,
+ 0x08, 0x00, 0x3a, 0x77, 0x0a, 0x39, 0x06, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x33, 0x50,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return 0;
+ }
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeMPLS(&tv, &dtv, p, pkt, sizeof(pkt), NULL);
+
+ if (!ENGINE_ISSET_EVENT(p, MPLS_UNKNOWN_PAYLOAD_TYPE)) {
+ ret = 0;
+ }
+
+ SCFree(p);
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void DecodeMPLSRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeMPLSTestHeaderTooSmall",
+ DecodeMPLSTestHeaderTooSmall, 1);
+ UtRegisterTest("DecodeMPLSTestBadLabelRouterAlert",
+ DecodeMPLSTestBadLabelRouterAlert, 1);
+ UtRegisterTest("DecodeMPLSTestBadLabelImplicitNull",
+ DecodeMPLSTestBadLabelImplicitNull, 1);
+ UtRegisterTest("DecodeMPLSTestBadLabelReserved",
+ DecodeMPLSTestBadLabelReserved, 1);
+ UtRegisterTest("DecodeMPLSTestUnknownPayloadType",
+ DecodeMPLSTestUnknownPayloadType, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/decode-mpls.h b/framework/src/suricata/src/decode-mpls.h
new file mode 100644
index 00000000..c701d3df
--- /dev/null
+++ b/framework/src/suricata/src/decode-mpls.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Jason Ish <jason.ish@emulex.com>
+ *
+ * MPLS decoder.
+ */
+
+#ifndef __DECODE_MPLS_H__
+#define __DECODE_MPLS_H__
+
+#define ETHERNET_TYPE_MPLS_UNICAST 0x8847
+#define ETHERNET_TYPE_MPLS_MULTICAST 0x8848
+
+void DecodeMPLSRegisterTests(void);
+
+#endif /* !__DECODE_MPLS_H__ */
diff --git a/framework/src/suricata/src/decode-null.c b/framework/src/suricata/src/decode-null.c
new file mode 100644
index 00000000..b3bcb066
--- /dev/null
+++ b/framework/src/suricata/src/decode-null.c
@@ -0,0 +1,89 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode linkype null:
+ * http://www.tcpdump.org/linktypes.html
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-raw.h"
+#include "decode-events.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "util-profiling.h"
+#include "host.h"
+
+#define HDR_SIZE 4
+
+int DecodeNull(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_null);
+
+ if (unlikely(len < HDR_SIZE)) {
+ ENGINE_SET_INVALID_EVENT(p, LTNULL_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ uint32_t type = *((uint32_t *)pkt);
+ switch(type) {
+ case AF_INET:
+ SCLogDebug("IPV4 Packet");
+ DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p)+HDR_SIZE, GET_PKT_LEN(p)-HDR_SIZE, pq);
+ break;
+ case AF_INET6:
+ SCLogDebug("IPV6 Packet");
+ DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p)+HDR_SIZE, GET_PKT_LEN(p)-HDR_SIZE, pq);
+ break;
+ default:
+ SCLogDebug("Unknown Null packet type version %" PRIu32 "", type);
+ ENGINE_SET_EVENT(p, LTNULL_UNSUPPORTED_TYPE);
+ break;
+ }
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Registers Null unit tests
+ */
+void DecodeNullRegisterTests(void)
+{
+#ifdef UNITTESTS
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-null.h b/framework/src/suricata/src/decode-null.h
new file mode 100644
index 00000000..22d988c7
--- /dev/null
+++ b/framework/src/suricata/src/decode-null.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_NULL_H__
+#define __DECODE_NULL_H__
+void DecodeNullRegisterTests(void);
+#endif /* __DECODE_NULL_H__ */
diff --git a/framework/src/suricata/src/decode-ppp.c b/framework/src/suricata/src/decode-ppp.c
new file mode 100644
index 00000000..f4ea670c
--- /dev/null
+++ b/framework/src/suricata/src/decode-ppp.c
@@ -0,0 +1,312 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Breno Silva Pinto <breno.silva@gmail.com>
+ *
+ * Decode PPP
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-ppp.h"
+#include "decode-events.h"
+
+#include "flow.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_ppp);
+
+ if (unlikely(len < PPP_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->ppph = (PPPHdr *)pkt;
+ if (unlikely(p->ppph == NULL))
+ return TM_ECODE_FAILED;
+
+ SCLogDebug("p %p pkt %p PPP protocol %04x Len: %" PRId32 "",
+ p, pkt, ntohs(p->ppph->protocol), len);
+
+ switch (ntohs(p->ppph->protocol))
+ {
+ case PPP_VJ_UCOMP:
+ if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) {
+ ENGINE_SET_INVALID_EVENT(p,PPPVJU_PKT_TOO_SMALL);
+ p->ppph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+ if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPP_HEADER_LEN)) == 4)) {
+ return DecodeIPV4(tv, dtv, p, pkt + PPP_HEADER_LEN, len - PPP_HEADER_LEN, pq);
+ } else
+ return TM_ECODE_FAILED;
+ break;
+
+ case PPP_IP:
+ if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) {
+ ENGINE_SET_INVALID_EVENT(p,PPPIPV4_PKT_TOO_SMALL);
+ p->ppph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+ return DecodeIPV4(tv, dtv, p, pkt + PPP_HEADER_LEN, len - PPP_HEADER_LEN, pq);
+
+ /* PPP IPv6 was not tested */
+ case PPP_IPV6:
+ if (unlikely(len < (PPP_HEADER_LEN + IPV6_HEADER_LEN))) {
+ ENGINE_SET_INVALID_EVENT(p,PPPIPV6_PKT_TOO_SMALL);
+ p->ppph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+ return DecodeIPV6(tv, dtv, p, pkt + PPP_HEADER_LEN, len - PPP_HEADER_LEN, pq);
+
+ case PPP_VJ_COMP:
+ case PPP_IPX:
+ case PPP_OSI:
+ case PPP_NS:
+ case PPP_DECNET:
+ case PPP_APPLE:
+ case PPP_BRPDU:
+ case PPP_STII:
+ case PPP_VINES:
+ case PPP_HELLO:
+ case PPP_LUXCOM:
+ case PPP_SNS:
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ case PPP_IPCP:
+ case PPP_OSICP:
+ case PPP_NSCP:
+ case PPP_DECNETCP:
+ case PPP_APPLECP:
+ case PPP_IPXCP:
+ case PPP_STIICP:
+ case PPP_VINESCP:
+ case PPP_IPV6CP:
+ case PPP_MPLSCP:
+ case PPP_LCP:
+ case PPP_PAP:
+ case PPP_LQM:
+ case PPP_CHAP:
+ ENGINE_SET_EVENT(p,PPP_UNSUP_PROTO);
+ return TM_ECODE_OK;
+
+ default:
+ SCLogDebug("unknown PPP protocol: %" PRIx32 "",ntohs(p->ppph->protocol));
+ ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE);
+ return TM_ECODE_OK;
+ }
+
+}
+
+/* TESTS BELOW */
+#ifdef UNITTESTS
+
+/* DecodePPPtest01
+ * Decode malformed ip layer PPP packet
+ * Expected test value: 1
+ */
+static int DecodePPPtest01 (void)
+{
+ uint8_t raw_ppp[] = { 0xff, 0x03, 0x00, 0x21, 0x45, 0xc0, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPP(&tv, &dtv, p, raw_ppp, sizeof(raw_ppp), NULL);
+
+ /* Function my returns here with expected value */
+
+ if(ENGINE_ISSET_EVENT(p,PPPIPV4_PKT_TOO_SMALL)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/* DecodePPPtest02
+ * Decode malformed ppp layer packet
+ * Expected test value: 1
+ */
+static int DecodePPPtest02 (void)
+{
+ uint8_t raw_ppp[] = { 0xff, 0x03, 0x00, 0xff, 0x45, 0xc0, 0x00, 0x2c, 0x4d,
+ 0xed, 0x00, 0x00, 0xff, 0x06, 0xd5, 0x17, 0xbf, 0x01,
+ 0x0d, 0x01, 0xbf, 0x01, 0x0d, 0x03, 0xea, 0x37, 0x00,
+ 0x17, 0x6d, 0x0b, 0xba, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x02, 0x10, 0x20, 0xdd, 0xe1, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPP(&tv, &dtv, p, raw_ppp, sizeof(raw_ppp), NULL);
+
+ /* Function must returns here */
+
+ if(ENGINE_ISSET_EVENT(p,PPP_WRONG_TYPE)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/** DecodePPPtest03
+ * \brief Decode good PPP packet, additionally the IPv4 packet inside is
+ * 4 bytes short.
+ * \retval 0 Test failed
+ * \retval 1 Test succeeded
+ */
+static int DecodePPPtest03 (void)
+{
+ uint8_t raw_ppp[] = { 0xff, 0x03, 0x00, 0x21, 0x45, 0xc0, 0x00, 0x2c, 0x4d,
+ 0xed, 0x00, 0x00, 0xff, 0x06, 0xd5, 0x17, 0xbf, 0x01,
+ 0x0d, 0x01, 0xbf, 0x01, 0x0d, 0x03, 0xea, 0x37, 0x00,
+ 0x17, 0x6d, 0x0b, 0xba, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x02, 0x10, 0x20, 0xdd, 0xe1, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodePPP(&tv, &dtv, p, raw_ppp, sizeof(raw_ppp), NULL);
+
+ FlowShutdown();
+
+ if(p->ppph == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ if(ENGINE_ISSET_EVENT(p,PPP_PKT_TOO_SMALL)) {
+ SCFree(p);
+ return 0;
+ }
+
+ if(ENGINE_ISSET_EVENT(p,PPPIPV4_PKT_TOO_SMALL)) {
+ SCFree(p);
+ return 0;
+ }
+
+ if(ENGINE_ISSET_EVENT(p,PPP_WRONG_TYPE)) {
+ SCFree(p);
+ return 0;
+ }
+
+ if (!(ENGINE_ISSET_EVENT(p,IPV4_TRUNC_PKT))) {
+ SCFree(p);
+ return 0;
+ }
+ /* Function must return here */
+
+ SCFree(p);
+ return 1;
+}
+
+
+/* DecodePPPtest04
+ * Check if ppp header is null
+ * Expected test value: 1
+ */
+
+static int DecodePPPtest04 (void)
+{
+ uint8_t raw_ppp[] = { 0xff, 0x03, 0x00, 0x21, 0x45, 0xc0, 0x00, 0x2c, 0x4d,
+ 0xed, 0x00, 0x00, 0xff, 0x06, 0xd5, 0x17, 0xbf, 0x01,
+ 0x0d, 0x01, 0xbf, 0x01, 0x0d, 0x03, 0xea, 0x37, 0x00,
+ 0x17, 0x6d, 0x0b, 0xba, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x02, 0x10, 0x20, 0xdd, 0xe1, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodePPP(&tv, &dtv, p, raw_ppp, sizeof(raw_ppp), NULL);
+
+ FlowShutdown();
+
+ if(p->ppph == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ if (!(ENGINE_ISSET_EVENT(p,IPV4_TRUNC_PKT))) {
+ SCFree(p);
+ return 0;
+ }
+
+ /* Function must returns here */
+
+ SCFree(p);
+ return 1;
+}
+#endif /* UNITTESTS */
+
+void DecodePPPRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodePPPtest01", DecodePPPtest01, 1);
+ UtRegisterTest("DecodePPPtest02", DecodePPPtest02, 1);
+ UtRegisterTest("DecodePPPtest03", DecodePPPtest03, 1);
+ UtRegisterTest("DecodePPPtest04", DecodePPPtest04, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-ppp.h b/framework/src/suricata/src/decode-ppp.h
new file mode 100644
index 00000000..00e61de9
--- /dev/null
+++ b/framework/src/suricata/src/decode-ppp.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva Pinto <breno.silva@gmail.com>
+ */
+
+#ifndef __DECODE_PPP_H__
+#define __DECODE_PPP_H__
+
+/** Point to Point Protocol RFC1331 - Supported tyes */
+#define PPP_IP 0x0021 /* Internet Protocol */
+#define PPP_IPV6 0x0057 /* Internet Protocol version 6 */
+#define PPP_VJ_UCOMP 0x002f /* VJ uncompressed TCP/IP */
+
+/** Unsupported PPP types (libpcap source reference) */
+#define PPP_IPX 0x002b /* Novell IPX Protocol */
+#define PPP_VJ_COMP 0x002d /* VJ compressed TCP/IP */
+#define PPP_IPX 0x002b /* Novell IPX Protocol */
+#define PPP_OSI 0x0023 /* OSI Network Layer */
+#define PPP_NS 0x0025 /* Xerox NS IDP */
+#define PPP_DECNET 0x0027 /* DECnet Phase IV */
+#define PPP_APPLE 0x0029 /* Appletalk */
+#define PPP_BRPDU 0x0031 /* Bridging PDU */
+#define PPP_STII 0x0033 /* Stream Protocol (ST-II) */
+#define PPP_VINES 0x0035 /* Banyan Vines */
+#define PPP_HELLO 0x0201 /* 802.1d Hello Packets */
+#define PPP_LUXCOM 0x0231 /* Luxcom */
+#define PPP_SNS 0x0233 /* Sigma Network Systems */
+#define PPP_MPLS_UCAST 0x0281 /* rfc 3032 */
+#define PPP_MPLS_MCAST 0x0283 /* rfc 3022 */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_OSICP 0x8023 /* OSI Network Layer Control Protocol */
+#define PPP_NSCP 0x8025 /* Xerox NS IDP Control Protocol */
+#define PPP_DECNETCP 0x8027 /* DECnet Control Protocol */
+#define PPP_APPLECP 0x8029 /* Appletalk Control Protocol */
+#define PPP_IPXCP 0x802b /* Novell IPX Control Protocol */
+#define PPP_STIICP 0x8033 /* Strean Protocol Control Protocol */
+#define PPP_VINESCP 0x8035 /* Banyan Vines Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_MPLSCP 0x8281 /* rfc 3022 */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQM 0xc025 /* Link Quality Monitoring */
+#define PPP_CHAP 0xc223 /* Challenge Handshake Authentication Protocol */
+
+/** PPP Packet header */
+typedef struct PPPHdr_ {
+ uint8_t address;
+ uint8_t control;
+ uint16_t protocol;
+} __attribute__((__packed__)) PPPHdr;
+
+/** PPP Packet header length */
+#define PPP_HEADER_LEN 4
+
+void DecodePPPRegisterTests(void);
+
+#endif /* __DECODE_PPP_H__ */
+
diff --git a/framework/src/suricata/src/decode-pppoe.c b/framework/src/suricata/src/decode-pppoe.c
new file mode 100644
index 00000000..b6f5031f
--- /dev/null
+++ b/framework/src/suricata/src/decode-pppoe.c
@@ -0,0 +1,460 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author James Riden <jamesr@europe.com>
+ *
+ * PPPOE Decoder
+ */
+
+#include "suricata-common.h"
+
+#include "packet-queue.h"
+
+#include "decode.h"
+#include "decode-ppp.h"
+#include "decode-pppoe.h"
+#include "decode-events.h"
+
+#include "flow.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/**
+ * \brief Main decoding function for PPPOE Discovery packets
+ */
+int DecodePPPOEDiscovery(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_pppoe);
+
+ if (len < PPPOE_DISCOVERY_HEADER_MIN_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->pppoedh = (PPPOEDiscoveryHdr *)pkt;
+ if (p->pppoedh == NULL)
+ return TM_ECODE_FAILED;
+
+ /* parse the PPPOE code */
+ switch (p->pppoedh->pppoe_code)
+ {
+ case PPPOE_CODE_PADI:
+ break;
+ case PPPOE_CODE_PADO:
+ break;
+ case PPPOE_CODE_PADR:
+ break;
+ case PPPOE_CODE_PADS:
+ break;
+ case PPPOE_CODE_PADT:
+ break;
+ default:
+ SCLogDebug("unknown PPPOE code: 0x%0"PRIX8"", p->pppoedh->pppoe_code);
+ ENGINE_SET_INVALID_EVENT(p, PPPOE_WRONG_CODE);
+ return TM_ECODE_OK;
+ }
+
+ /* parse any tags we have in the packet */
+
+ uint16_t tag_length = 0;
+ PPPOEDiscoveryTag* pppoedt = (PPPOEDiscoveryTag*) (p->pppoedh + PPPOE_DISCOVERY_HEADER_MIN_LEN);
+
+ uint16_t pppoe_length = ntohs(p->pppoedh->pppoe_length);
+ uint16_t packet_length = len - PPPOE_DISCOVERY_HEADER_MIN_LEN ;
+
+ SCLogDebug("pppoe_length %"PRIu16", packet_length %"PRIu16"",
+ pppoe_length, packet_length);
+
+ if (pppoe_length > packet_length) {
+ SCLogDebug("malformed PPPOE tags");
+ ENGINE_SET_INVALID_EVENT(p, PPPOE_MALFORMED_TAGS);
+ return TM_ECODE_OK;
+ }
+
+ while (pppoedt < (PPPOEDiscoveryTag*) (pkt + (len - sizeof(PPPOEDiscoveryTag))) && pppoe_length >=4 && packet_length >=4)
+ {
+#ifdef DEBUG
+ uint16_t tag_type = ntohs(pppoedt->pppoe_tag_type);
+#endif
+ tag_length = ntohs(pppoedt->pppoe_tag_length);
+
+ SCLogDebug ("PPPoE Tag type %x, length %u", tag_type, tag_length);
+
+ if (pppoe_length >= (4 + tag_length)) {
+ pppoe_length -= (4 + tag_length);
+ } else {
+ pppoe_length = 0; // don't want an underflow
+ }
+
+ if (packet_length >= 4 + tag_length) {
+ packet_length -= (4 + tag_length);
+ } else {
+ packet_length = 0; // don't want an underflow
+ }
+
+ pppoedt = pppoedt + (4 + tag_length);
+ }
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Main decoding function for PPPOE Session packets
+ */
+int DecodePPPOESession(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_pppoe);
+
+ if (len < PPPOE_SESSION_HEADER_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ p->pppoesh = (PPPOESessionHdr *)pkt;
+ if (p->pppoesh == NULL)
+ return TM_ECODE_FAILED;
+
+ SCLogDebug("PPPOE VERSION %" PRIu32 " TYPE %" PRIu32 " CODE %" PRIu32 " SESSIONID %" PRIu32 " LENGTH %" PRIu32 "",
+ PPPOE_SESSION_GET_VERSION(p->pppoesh), PPPOE_SESSION_GET_TYPE(p->pppoesh), p->pppoesh->pppoe_code, ntohs(p->pppoesh->session_id), ntohs(p->pppoesh->pppoe_length));
+
+ /* can't use DecodePPP() here because we only get a single 2-byte word to indicate protocol instead of the full PPP header */
+
+ if (ntohs(p->pppoesh->pppoe_length) > 0) {
+ /* decode contained PPP packet */
+
+ switch (ntohs(p->pppoesh->protocol))
+ {
+ case PPP_VJ_COMP:
+ case PPP_IPX:
+ case PPP_OSI:
+ case PPP_NS:
+ case PPP_DECNET:
+ case PPP_APPLE:
+ case PPP_BRPDU:
+ case PPP_STII:
+ case PPP_VINES:
+ case PPP_HELLO:
+ case PPP_LUXCOM:
+ case PPP_SNS:
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ case PPP_IPCP:
+ case PPP_OSICP:
+ case PPP_NSCP:
+ case PPP_DECNETCP:
+ case PPP_APPLECP:
+ case PPP_IPXCP:
+ case PPP_STIICP:
+ case PPP_VINESCP:
+ case PPP_IPV6CP:
+ case PPP_MPLSCP:
+ case PPP_LCP:
+ case PPP_PAP:
+ case PPP_LQM:
+ case PPP_CHAP:
+ ENGINE_SET_EVENT(p,PPP_UNSUP_PROTO);
+ break;
+
+ case PPP_VJ_UCOMP:
+
+ if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
+ return TM_ECODE_OK;
+ }
+
+ if(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPPOE_SESSION_HEADER_LEN)) == 4) {
+ DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN, pq );
+ }
+ break;
+
+ case PPP_IP:
+ if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, PPPIPV4_PKT_TOO_SMALL);
+ return TM_ECODE_OK;
+ }
+
+ DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN, pq );
+ break;
+
+ /* PPP IPv6 was not tested */
+ case PPP_IPV6:
+ if(len < (PPPOE_SESSION_HEADER_LEN + IPV6_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL);
+ return TM_ECODE_OK;
+ }
+
+ DecodeIPV6(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN, pq );
+ break;
+
+ default:
+ SCLogDebug("unknown PPP protocol: %" PRIx32 "",ntohs(p->ppph->protocol));
+ ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE);
+ return TM_ECODE_OK;
+ }
+ }
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+/** DecodePPPOEtest01
+ * \brief Decode malformed PPPOE packet (too short)
+ * \retval 1 Expected test value
+ */
+static int DecodePPPOEtest01 (void)
+{
+
+ uint8_t raw_pppoe[] = { 0x11, 0x00, 0x00, 0x00, 0x00 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe), NULL);
+
+ if (ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/** DecodePPPOEtest02
+ * \brief Valid PPPOE packet - check the invalid ICMP type encapsulated is flagged
+ * \retval 0 Expected test value
+ */
+static int DecodePPPOEtest02 (void)
+{
+
+ uint8_t raw_pppoe[] = {
+ 0x11, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x21,
+ 0x45, 0x00, 0x00, 0x3c, 0x05, 0x5c, 0x00, 0x00,
+ 0x20, 0x01, 0xff, 0x30, 0xc0, 0xa8, 0x0a, 0x7f,
+ 0xc0, 0xa8, 0x0a, 0x65, 0xab, 0xcd, 0x16, 0x5e,
+ 0x02, 0x00, 0x37, 0x00, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+ 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49 };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ int ret = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL)) {
+ goto end;
+ }
+
+ // and we insist that the invalid ICMP encapsulated (type 0xab, code 0xcd) is flagged
+
+ if(! ENGINE_ISSET_EVENT(p,ICMPV4_UNKNOWN_TYPE)) {
+ goto end;
+ }
+
+ ret = 1;
+end:
+ FlowShutdown();
+ SCFree(p);
+ return ret;
+}
+
+
+/** DecodePPPOEtest03
+ * \brief Valid example PADO packet PPPOE packet taken from RFC2516
+ * \retval 0 Expected test value
+ */
+static int DecodePPPOEtest03 (void)
+{
+
+ /* example PADO packet taken from RFC2516 */
+ uint8_t raw_pppoe[] = {
+ 0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
+ 0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
+ 0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68,
+ 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe), NULL);
+ if (p->pppoedh == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** DecodePPPOEtest04
+ * \brief Valid example PPPOE packet taken from RFC2516 - but with wrong PPPOE code
+ * \retval 1 Expected test value
+ */
+static int DecodePPPOEtest04 (void)
+{
+
+ /* example PADI packet taken from RFC2516, but with wrong code */
+ uint8_t raw_pppoe[] = {
+ 0x11, 0xbb, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01,
+ 0x00, 0x00
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,PPPOE_WRONG_CODE)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/** DecodePPPOEtest05
+ * \brief Valid exaple PADO PPPOE packet taken from RFC2516, but too short for given length
+ * \retval 0 Expected test value
+ */
+static int DecodePPPOEtest05 (void)
+{
+
+ /* example PADI packet taken from RFC2516 */
+ uint8_t raw_pppoe[] = {
+ 0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
+ 0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
+ 0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
+ 0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,PPPOE_MALFORMED_TAGS)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/** DecodePPPOEtest06
+ * \brief Check that the macros work as expected. Type and version are
+ * fields of 4 bits length. So they are sharing the same var and the macros
+ * should extract the first 4 bits for version and the second 4 bits for type
+ * \retval 1 Expected test value
+ */
+static int DecodePPPOEtest06 (void)
+{
+
+ PPPOESessionHdr pppoesh;
+ PPPOEDiscoveryHdr pppoedh;
+ pppoesh.pppoe_version_type = 0xAB;
+ pppoedh.pppoe_version_type = 0xCD;
+
+ if (PPPOE_SESSION_GET_VERSION(&pppoesh) != 0x0A) {
+ printf("Error, PPPOE macro pppoe_session_get_version failed: ");
+ return 0;
+ }
+ if (PPPOE_SESSION_GET_TYPE(&pppoesh) != 0x0B) {
+ printf("Error, PPPOE macro pppoe_session_get_type failed: ");
+ return 0;
+ }
+ if (PPPOE_DISCOVERY_GET_VERSION(&pppoedh) != 0x0C) {
+ printf("Error, PPPOE macro pppoe_discovery_get_version failed: ");
+ return 0;
+ }
+ if (PPPOE_DISCOVERY_GET_TYPE(&pppoedh) != 0x0D) {
+ printf("Error, PPPOE macro pppoe_discovery_get_type failed: ");
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* UNITTESTS */
+
+
+
+/**
+ * \brief Registers PPPOE unit tests
+ * \todo More PPPOE tests
+ */
+void DecodePPPOERegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodePPPOEtest01", DecodePPPOEtest01, 1);
+ UtRegisterTest("DecodePPPOEtest02", DecodePPPOEtest02, 1);
+ UtRegisterTest("DecodePPPOEtest03", DecodePPPOEtest03, 1);
+ UtRegisterTest("DecodePPPOEtest04", DecodePPPOEtest04, 1);
+ UtRegisterTest("DecodePPPOEtest05", DecodePPPOEtest05, 1);
+ UtRegisterTest("DecodePPPOEtest06", DecodePPPOEtest06, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-pppoe.h b/framework/src/suricata/src/decode-pppoe.h
new file mode 100644
index 00000000..6aecf741
--- /dev/null
+++ b/framework/src/suricata/src/decode-pppoe.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author James Riden <jamesr@europe.com>
+ */
+
+#ifndef __DECODE_PPPOE_H__
+#define __DECODE_PPPOE_H__
+
+#include "decode.h"
+#include "threadvars.h"
+
+#define PPPOE_SESSION_HEADER_LEN 8
+#define PPPOE_DISCOVERY_HEADER_MIN_LEN 6
+#define PPPOE_SESSION_GET_VERSION(hdr) ((hdr)->pppoe_version_type & 0xF0) >> 4
+#define PPPOE_SESSION_GET_TYPE(hdr) ((hdr)->pppoe_version_type & 0x0F)
+#define PPPOE_DISCOVERY_GET_VERSION(hdr) ((hdr)->pppoe_version_type & 0xF0) >> 4
+#define PPPOE_DISCOVERY_GET_TYPE(hdr) ((hdr)->pppoe_version_type & 0x0F)
+
+typedef struct PPPOESessionHdr_
+{
+ uint8_t pppoe_version_type;
+ uint8_t pppoe_code;
+ uint16_t session_id;
+ uint16_t pppoe_length;
+ uint16_t protocol;
+} PPPOESessionHdr;
+
+typedef struct PPPOEDiscoveryTag_
+{
+ uint16_t pppoe_tag_type;
+ uint16_t pppoe_tag_length;
+} __attribute__((__packed__)) PPPOEDiscoveryTag;
+
+typedef struct PPPOEDiscoveryHdr_
+{
+ uint8_t pppoe_version_type;
+ uint8_t pppoe_code;
+ uint16_t discovery_id;
+ uint16_t pppoe_length;
+} __attribute__((__packed__)) PPPOEDiscoveryHdr;
+
+/* see RFC 2516 - discovery codes */
+#define PPPOE_CODE_PADI 0x09
+#define PPPOE_CODE_PADO 0x07
+#define PPPOE_CODE_PADR 0x19
+#define PPPOE_CODE_PADS 0x65
+#define PPPOE_CODE_PADT 0xa7
+
+/* see RFC 2516 Appendix A */
+#define PPPOE_TAG_END_OF_LIST 0x0000 /* End-Of-List */
+#define PPPOE_TAG_SERVICE_NAME 0x0101 /* Service-Name */
+#define PPPOE_TAG_AC_NAME 0x0102 /* AC-Name */
+#define PPPOE_TAG_HOST_UNIQ 0x0103 /* Host-Uniq */
+#define PPPOE_TAG_AC_COOKIE 0x0104 /* AC-Cookie */
+#define PPPOE_TAG_VENDOR_SPECIFIC 0x0105 /* Vendor-Specific */
+#define PPPOE_TAG_RELAY_SESSION_ID 0x0110 /* Relay-Session-Id */
+#define PPPOE_TAG_SERVICE_NAME_ERROR 0x0201 /* Service-Name-Error */
+#define PPPOE_TAG_AC_SYS_ERROR 0x0202 /* AC-System Error */
+#define PPPOE_TAG_GEN_ERROR 0x0203 /* Generic-Error */
+
+void DecodePPPOERegisterTests(void);
+
+#endif /* __DECODE_PPPOE_H__ */
+
diff --git a/framework/src/suricata/src/decode-raw.c b/framework/src/suricata/src/decode-raw.c
new file mode 100644
index 00000000..22e1b9e1
--- /dev/null
+++ b/framework/src/suricata/src/decode-raw.c
@@ -0,0 +1,232 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ *
+ * Decode RAW
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-raw.h"
+#include "decode-events.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "util-profiling.h"
+#include "host.h"
+
+
+int DecodeRaw(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_raw);
+
+ /* If it is ipv4 or ipv6 it should at least be the size of ipv4 */
+ if (unlikely(len < IPV4_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, IPV4_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ if (IP_GET_RAW_VER(pkt) == 4) {
+ SCLogDebug("IPV4 Packet");
+ DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else if (IP_GET_RAW_VER(pkt) == 6) {
+ SCLogDebug("IPV6 Packet");
+ DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else {
+ SCLogDebug("Unknown ip version %" PRIu8 "", IP_GET_RAW_VER(pkt));
+ ENGINE_SET_EVENT(p,IPRAW_INVALID_IPV);
+ }
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+#include "flow.h"
+#include "flow-util.h"
+
+/** DecodeRawtest01
+ * \brief Valid Raw packet
+ * \retval 0 Expected test value
+ */
+static int DecodeRawTest01 (void)
+{
+
+ /* IPV6/TCP/no eth header */
+ uint8_t raw_ip[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x06, 0x40,
+ 0x20, 0x01, 0x06, 0x18, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x51, 0x99, 0xcc, 0x70,
+ 0x20, 0x01, 0x06, 0x18, 0x00, 0x01, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x8c, 0x9b, 0x00, 0x50, 0x6a, 0xe7, 0x07, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0x30,
+ 0x29, 0x9c, 0x00, 0x00, 0x02, 0x04, 0x05, 0x8c,
+ 0x04, 0x02, 0x08, 0x0a, 0x00, 0xdd, 0x1a, 0x39,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ if (PacketCopyData(p, raw_ip, sizeof(raw_ip)) == -1) {
+ SCFree(p);
+ return 1;
+ }
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeRaw(&tv, &dtv, p, raw_ip, GET_PKT_LEN(p), NULL);
+ if (p->ip6h == NULL) {
+ printf("expected a valid ipv6 header but it was NULL: ");
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+ }
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 0;
+
+}
+/** DecodeRawtest02
+ * \brief Valid Raw packet
+ * \retval 0 Expected test value
+ */
+static int DecodeRawTest02 (void)
+{
+
+ /* IPV4/TCP/no eth header */
+ uint8_t raw_ip[] = {
+ 0x45, 0x00, 0x00, 0x30, 0x00, 0xad, 0x40, 0x00,
+ 0x7f, 0x06, 0xac, 0xc5, 0xc0, 0xa8, 0x67, 0x02,
+ 0xc0, 0xa8, 0x66, 0x02, 0x0b, 0xc7, 0x00, 0x50,
+ 0x1d, 0xb3, 0x12, 0x37, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x02, 0x40, 0x00, 0xb8, 0xc8, 0x00, 0x00,
+ 0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x04, 0x02 };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ if (PacketCopyData(p, raw_ip, sizeof(raw_ip)) == -1) {
+ SCFree(p);
+ return 1;
+ }
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeRaw(&tv, &dtv, p, raw_ip, GET_PKT_LEN(p), NULL);
+ if (p->ip4h == NULL) {
+ printf("expected a valid ipv4 header but it was NULL: ");
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+ }
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 0;
+}
+/** DecodeRawtest03
+ * \brief Valid Raw packet
+ * \retval 0 Expected test value
+ */
+static int DecodeRawTest03 (void)
+{
+
+ /* IPV13 */
+ uint8_t raw_ip[] = {
+ 0xdf, 0x00, 0x00, 0x3d, 0x49, 0x42, 0x40, 0x00,
+ 0x40, 0x06, 0xcf, 0x8a, 0x0a, 0x1f, 0x03, 0xaf,
+ 0x0a, 0x1f, 0x0a, 0x02, 0xa5, 0xe7, 0xde, 0xad,
+ 0x00, 0x0c, 0xe2, 0x0e, 0x8b, 0xfe, 0x0c, 0xe7,
+ 0x80, 0x18, 0x00, 0xb7, 0xaf, 0xeb, 0x00, 0x00,
+ 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, 0xab, 0x4f,
+ 0x34, 0x40, 0x67, 0x31, 0x3b, 0x63, 0x61, 0x74,
+ 0x20, 0x6b, 0x65, 0x79, 0x3b };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ if (PacketCopyData(p, raw_ip, sizeof(raw_ip)) == -1) {
+ SCFree(p);
+ return 1;
+ }
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeRaw(&tv, &dtv, p, raw_ip, GET_PKT_LEN(p), NULL);
+ if (ENGINE_ISSET_EVENT(p,IPRAW_INVALID_IPV)) {
+ FlowShutdown();
+ SCFree(p);
+ return 0;
+ } else {
+ printf("expected IPRAW_INVALID_IPV to be set but it wasn't: ");
+ }
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Registers Raw unit tests
+ * \todo More Raw tests
+ */
+void DecodeRawRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeRawTest01", DecodeRawTest01, 0);
+ UtRegisterTest("DecodeRawTest02", DecodeRawTest02, 0);
+ UtRegisterTest("DecodeRawTest03", DecodeRawTest03, 0);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-raw.h b/framework/src/suricata/src/decode-raw.h
new file mode 100644
index 00000000..ff637870
--- /dev/null
+++ b/framework/src/suricata/src/decode-raw.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ */
+
+#ifndef __DECODE_RAW_H__
+#define __DECODE_RAW_H__
+void DecodeRawRegisterTests(void);
+#endif /* __DECODE_RAW_H__ */
+
diff --git a/framework/src/suricata/src/decode-sctp.c b/framework/src/suricata/src/decode-sctp.c
new file mode 100644
index 00000000..2d493c66
--- /dev/null
+++ b/framework/src/suricata/src/decode-sctp.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Decode SCTP
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-sctp.h"
+#include "decode-events.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-optimize.h"
+#include "flow.h"
+
+static int DecodeSCTPPacket(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t len)
+{
+ if (unlikely(len < SCTP_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, SCTP_PKT_TOO_SMALL);
+ return -1;
+ }
+
+ p->sctph = (SCTPHdr *)pkt;
+
+ SET_SCTP_SRC_PORT(p,&p->sp);
+ SET_SCTP_DST_PORT(p,&p->dp);
+
+ p->payload = pkt + sizeof(SCTPHdr);
+ p->payload_len = len - sizeof(SCTPHdr);
+
+ p->proto = IPPROTO_SCTP;
+
+ return 0;
+}
+
+int DecodeSCTP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_sctp);
+
+ if (unlikely(DecodeSCTPPacket(tv, p,pkt,len) < 0)) {
+ p->sctph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+#ifdef DEBUG
+ SCLogDebug("SCTP sp: %" PRIu32 " -> dp: %" PRIu32,
+ SCTP_GET_SRC_PORT(p), SCTP_GET_DST_PORT(p));
+#endif
+
+ /* Flow is an integral part of us */
+ FlowHandlePacket(tv, dtv, p);
+
+ return TM_ECODE_OK;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-sctp.h b/framework/src/suricata/src/decode-sctp.h
new file mode 100644
index 00000000..184a172e
--- /dev/null
+++ b/framework/src/suricata/src/decode-sctp.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __DECODE_SCTP_H__
+#define __DECODE_SCTP_H__
+
+/** size of the packet header without any chunk headers */
+#define SCTP_HEADER_LEN 12
+
+/* XXX RAW* needs to be really 'raw', so no ntohs there */
+#define SCTP_GET_RAW_SRC_PORT(sctph) ntohs((sctph)->sh_sport)
+#define SCTP_GET_RAW_DST_PORT(sctph) ntohs((sctph)->sh_dport)
+
+#define SCTP_GET_SRC_PORT(p) SCTP_GET_RAW_SRC_PORT(p->sctph)
+#define SCTP_GET_DST_PORT(p) SCTP_GET_RAW_DST_PORT(p->sctph)
+
+typedef struct SCTPHdr_
+{
+ uint16_t sh_sport; /* source port */
+ uint16_t sh_dport; /* destination port */
+ uint32_t sh_vtag; /* verification tag, defined per flow */
+ uint32_t sh_sum; /* checksum, computed via crc32 */
+} __attribute__((__packed__)) SCTPHdr;
+
+#define CLEAR_SCTP_PACKET(p) { \
+ (p)->sctph = NULL; \
+} while (0)
+
+void DecodeSCTPRegisterTests(void);
+
+#endif /* __DECODE_SCTP_H__ */
diff --git a/framework/src/suricata/src/decode-sll.c b/framework/src/suricata/src/decode-sll.c
new file mode 100644
index 00000000..eed61a4c
--- /dev/null
+++ b/framework/src/suricata/src/decode-sll.c
@@ -0,0 +1,76 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decodes Sll
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-sll.h"
+#include "decode-events.h"
+#include "util-debug.h"
+
+int DecodeSll(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_sll);
+
+ if (unlikely(len < SLL_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, SLL_PKT_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ SllHdr *sllh = (SllHdr *)pkt;
+ if (unlikely(sllh == NULL))
+ return TM_ECODE_FAILED;
+
+ SCLogDebug("p %p pkt %p sll_protocol %04x", p, pkt, ntohs(sllh->sll_protocol));
+
+ switch (ntohs(sllh->sll_protocol)) {
+ case ETHERNET_TYPE_IP:
+ DecodeIPV4(tv, dtv, p, pkt + SLL_HEADER_LEN,
+ len - SLL_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_IPV6:
+ DecodeIPV6(tv, dtv, p, pkt + SLL_HEADER_LEN,
+ len - SLL_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_VLAN:
+ DecodeVLAN(tv, dtv, p, pkt + SLL_HEADER_LEN,
+ len - SLL_HEADER_LEN, pq);
+ break;
+ default:
+ SCLogDebug("p %p pkt %p sll type %04x not supported", p,
+ pkt, ntohs(sllh->sll_protocol));
+ }
+
+ return TM_ECODE_OK;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-sll.h b/framework/src/suricata/src/decode-sll.h
new file mode 100644
index 00000000..babdd7ac
--- /dev/null
+++ b/framework/src/suricata/src/decode-sll.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_SLL_H__
+#define __DECODE_SLL_H__
+
+#define SLL_HEADER_LEN 16
+
+typedef struct SllHdr_ {
+ uint16_t sll_pkttype; /* packet type */
+ uint16_t sll_hatype; /* link-layer address type */
+ uint16_t sll_halen; /* link-layer address length */
+ uint8_t sll_addr[8]; /* link-layer address */
+ uint16_t sll_protocol; /* protocol */
+} __attribute__((__packed__)) SllHdr;
+
+#endif /* __DECODE_SLL_H__ */
+
diff --git a/framework/src/suricata/src/decode-tcp.c b/framework/src/suricata/src/decode-tcp.c
new file mode 100644
index 00000000..428e7ae4
--- /dev/null
+++ b/framework/src/suricata/src/decode-tcp.c
@@ -0,0 +1,521 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode TCP
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-tcp.h"
+#include "decode-events.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-optimize.h"
+#include "flow.h"
+#include "util-profiling.h"
+#include "pkt-var.h"
+#include "host.h"
+
+static int DecodeTCPOptions(Packet *p, uint8_t *pkt, uint16_t len)
+{
+ uint16_t plen = len;
+ while (plen)
+ {
+ /* single byte options */
+ if (*pkt == TCP_OPT_EOL) {
+ break;
+ } else if (*pkt == TCP_OPT_NOP) {
+ pkt++;
+ plen--;
+
+ /* multibyte options */
+ } else {
+ if (plen < 2) {
+ break;
+ }
+
+ /* we already know that the total options len is valid,
+ * so here the len of the specific option must be bad.
+ * Also check for invalid lengths 0 and 1. */
+ if (unlikely(*(pkt+1) > plen || *(pkt+1) < 2)) {
+ ENGINE_SET_INVALID_EVENT(p, TCP_OPT_INVALID_LEN);
+ return -1;
+ }
+
+ p->TCP_OPTS[p->TCP_OPTS_CNT].type = *pkt;
+ p->TCP_OPTS[p->TCP_OPTS_CNT].len = *(pkt+1);
+ if (plen > 2)
+ p->TCP_OPTS[p->TCP_OPTS_CNT].data = (pkt+2);
+ else
+ p->TCP_OPTS[p->TCP_OPTS_CNT].data = NULL;
+
+ /* we are parsing the most commonly used opts to prevent
+ * us from having to walk the opts list for these all the
+ * time. */
+ switch (p->TCP_OPTS[p->TCP_OPTS_CNT].type) {
+ case TCP_OPT_WS:
+ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len != TCP_OPT_WS_LEN) {
+ ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN);
+ } else {
+ if (p->tcpvars.ws != NULL) {
+ ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE);
+ } else {
+ p->tcpvars.ws = &p->TCP_OPTS[p->TCP_OPTS_CNT];
+ }
+ }
+ break;
+ case TCP_OPT_MSS:
+ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len != TCP_OPT_MSS_LEN) {
+ ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN);
+ } else {
+ if (p->tcpvars.mss != NULL) {
+ ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE);
+ } else {
+ p->tcpvars.mss = &p->TCP_OPTS[p->TCP_OPTS_CNT];
+ }
+ }
+ break;
+ case TCP_OPT_SACKOK:
+ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len != TCP_OPT_SACKOK_LEN) {
+ ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN);
+ } else {
+ if (p->tcpvars.sackok != NULL) {
+ ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE);
+ } else {
+ p->tcpvars.sackok = &p->TCP_OPTS[p->TCP_OPTS_CNT];
+ }
+ }
+ break;
+ case TCP_OPT_TS:
+ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len != TCP_OPT_TS_LEN) {
+ ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN);
+ } else {
+ if (p->tcpvars.ts != NULL) {
+ ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE);
+ } else {
+ p->tcpvars.ts = &p->TCP_OPTS[p->TCP_OPTS_CNT];
+ }
+ }
+ break;
+ case TCP_OPT_SACK:
+ SCLogDebug("SACK option, len %u", p->TCP_OPTS[p->TCP_OPTS_CNT].len);
+ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len < TCP_OPT_SACK_MIN_LEN ||
+ p->TCP_OPTS[p->TCP_OPTS_CNT].len > TCP_OPT_SACK_MAX_LEN ||
+ !((p->TCP_OPTS[p->TCP_OPTS_CNT].len - 2) % 8 == 0))
+ {
+ ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN);
+ } else {
+ if (p->tcpvars.sack != NULL) {
+ ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE);
+ } else {
+ p->tcpvars.sack = &p->TCP_OPTS[p->TCP_OPTS_CNT];
+ }
+ }
+ break;
+ }
+
+ pkt += p->TCP_OPTS[p->TCP_OPTS_CNT].len;
+ plen -= (p->TCP_OPTS[p->TCP_OPTS_CNT].len);
+ p->TCP_OPTS_CNT++;
+ }
+ }
+ return 0;
+}
+
+static int DecodeTCPPacket(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t len)
+{
+ if (unlikely(len < TCP_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, TCP_PKT_TOO_SMALL);
+ return -1;
+ }
+
+ p->tcph = (TCPHdr *)pkt;
+
+ uint8_t hlen = TCP_GET_HLEN(p);
+ if (unlikely(len < hlen)) {
+ ENGINE_SET_INVALID_EVENT(p, TCP_HLEN_TOO_SMALL);
+ return -1;
+ }
+
+ uint8_t tcp_opt_len = hlen - TCP_HEADER_LEN;
+ if (unlikely(tcp_opt_len > TCP_OPTLENMAX)) {
+ ENGINE_SET_INVALID_EVENT(p, TCP_INVALID_OPTLEN);
+ return -1;
+ }
+
+ if (likely(tcp_opt_len > 0)) {
+ DecodeTCPOptions(p, pkt + TCP_HEADER_LEN, tcp_opt_len);
+ }
+
+ SET_TCP_SRC_PORT(p,&p->sp);
+ SET_TCP_DST_PORT(p,&p->dp);
+
+ p->proto = IPPROTO_TCP;
+
+ p->payload = pkt + hlen;
+ p->payload_len = len - hlen;
+
+ return 0;
+}
+
+int DecodeTCP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_tcp);
+
+ if (unlikely(DecodeTCPPacket(tv, p,pkt,len) < 0)) {
+ SCLogDebug("invalid TCP packet");
+ p->tcph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+#ifdef DEBUG
+ SCLogDebug("TCP sp: %" PRIu32 " -> dp: %" PRIu32 " - HLEN: %" PRIu32 " LEN: %" PRIu32 " %s%s%s%s%s",
+ GET_TCP_SRC_PORT(p), GET_TCP_DST_PORT(p), TCP_GET_HLEN(p), len,
+ p->tcpvars.sackok ? "SACKOK " : "", p->tcpvars.sack ? "SACK " : "",
+ p->tcpvars.ws ? "WS " : "", p->tcpvars.ts ? "TS " : "",
+ p->tcpvars.mss ? "MSS " : "");
+#endif
+
+ /* Flow is an integral part of us */
+ FlowHandlePacket(tv, dtv, p);
+
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+static int TCPCalculateValidChecksumtest01(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipshdr[] = {
+ 0x40, 0x8e, 0x7e, 0xb2, 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0xa0, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 02};
+
+ csum = *( ((uint16_t *)raw_tcp) + 8);
+
+ return (csum == TCPCalculateChecksum((uint16_t *) raw_ipshdr,
+ (uint16_t *)raw_tcp, sizeof(raw_tcp)));
+}
+
+static int TCPCalculateInvalidChecksumtest02(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipshdr[] = {
+ 0x40, 0x8e, 0x7e, 0xb2, 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0xa0, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 03};
+
+ csum = *( ((uint16_t *)raw_tcp) + 8);
+
+ return (csum == TCPCalculateChecksum((uint16_t *) raw_ipshdr,
+ (uint16_t *)raw_tcp, sizeof(raw_tcp)));
+}
+
+static int TCPV6CalculateValidChecksumtest03(void)
+{
+ uint16_t csum = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0x03, 0xfe,
+ 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d, 0x0c, 0x7a,
+ 0x08, 0x77, 0x80, 0x10, 0x21, 0x5c, 0xc2, 0xf1,
+ 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08,
+ 0xca, 0x5a, 0x00, 0x01, 0x69, 0x27};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 70)));
+
+ return (csum == TCPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 32));
+}
+
+static int TCPV6CalculateInvalidChecksumtest04(void)
+{
+ uint16_t csum = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0x03, 0xfe,
+ 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d, 0x0c, 0x7a,
+ 0x08, 0x77, 0x80, 0x10, 0x21, 0x5c, 0xc2, 0xf1,
+ 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08,
+ 0xca, 0x5a, 0x00, 0x01, 0x69, 0x28};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 70)));
+
+ return (csum == TCPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 32));
+}
+
+/** \test Get the wscale of 2 */
+static int TCPGetWscaleTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0,
+ 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ip4h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->ip4h = &ip4h;
+
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp), NULL);
+
+ if (p->tcph == NULL) {
+ printf("tcp packet decode failed: ");
+ goto end;
+ }
+
+ uint8_t wscale = TCP_GET_WSCALE(p);
+ if (wscale != 2) {
+ printf("wscale %"PRIu8", expected 2: ", wscale);
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test Get the wscale of 15, so see if return 0 properly */
+static int TCPGetWscaleTest02(void)
+{
+ int retval = 0;
+ static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0,
+ 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0f};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ip4h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->ip4h = &ip4h;
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp), NULL);
+
+ if (p->tcph == NULL) {
+ printf("tcp packet decode failed: ");
+ goto end;
+ }
+
+ uint8_t wscale = TCP_GET_WSCALE(p);
+ if (wscale != 0) {
+ printf("wscale %"PRIu8", expected 0: ", wscale);
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+/** \test Get the wscale, but it's missing, so see if return 0 properly */
+static int TCPGetWscaleTest03(void)
+{
+ int retval = 0;
+ static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x59,
+ 0xdd, 0xa3, 0x6f, 0xf8, 0x80, 0x10, 0x05, 0xb4,
+ 0x7c, 0x70, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
+ 0x00, 0x62, 0x88, 0x9e, 0x00, 0x00, 0x00, 0x00};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ip4h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->ip4h = &ip4h;
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp), NULL);
+
+ if (p->tcph == NULL) {
+ printf("tcp packet decode failed: ");
+ goto end;
+ }
+
+ uint8_t wscale = TCP_GET_WSCALE(p);
+ if (wscale != 0) {
+ printf("wscale %"PRIu8", expected 0: ", wscale);
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+
+static int TCPGetSackTest01(void)
+{
+ int retval = 0;
+ static uint8_t raw_tcp[] = {
+ 0x00, 0x50, 0x06, 0xa6, 0xfa, 0x87, 0x0b, 0xf5,
+ 0xf1, 0x59, 0x02, 0xe0, 0xa0, 0x10, 0x3e, 0xbc,
+ 0x1d, 0xe7, 0x00, 0x00, 0x01, 0x01, 0x05, 0x12,
+ 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64,
+ 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 };
+ static uint8_t raw_tcp_sack[] = {
+ 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64,
+ 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ip4h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->ip4h = &ip4h;
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp), NULL);
+
+ if (p->tcph == NULL) {
+ printf("tcp packet decode failed: ");
+ goto end;
+ }
+
+ if (p->tcpvars.sack == NULL) {
+ printf("tcp packet sack not decoded: ");
+ goto end;
+ }
+
+ int sack = TCP_GET_SACK_CNT(p);
+ if (sack != 2) {
+ printf("expected 2 sack records, got %u: ", TCP_GET_SACK_CNT(p));
+ goto end;
+ }
+
+ uint8_t *sackptr = TCP_GET_SACK_PTR(p);
+ if (sackptr == NULL) {
+ printf("no sack data: ");
+ goto end;
+ }
+
+ if (memcmp(sackptr, raw_tcp_sack, 16) != 0) {
+ printf("malformed sack data: ");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return retval;
+}
+#endif /* UNITTESTS */
+
+void DecodeTCPRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("TCPCalculateValidChecksumtest01",
+ TCPCalculateValidChecksumtest01, 1);
+ UtRegisterTest("TCPCalculateInvalidChecksumtest02",
+ TCPCalculateInvalidChecksumtest02, 0);
+ UtRegisterTest("TCPV6CalculateValidChecksumtest03",
+ TCPV6CalculateValidChecksumtest03, 1);
+ UtRegisterTest("TCPV6CalculateInvalidChecksumtest04",
+ TCPV6CalculateInvalidChecksumtest04, 0);
+ UtRegisterTest("TCPGetWscaleTest01", TCPGetWscaleTest01, 1);
+ UtRegisterTest("TCPGetWscaleTest02", TCPGetWscaleTest02, 1);
+ UtRegisterTest("TCPGetWscaleTest03", TCPGetWscaleTest03, 1);
+ UtRegisterTest("TCPGetSackTest01", TCPGetSackTest01, 1);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-tcp.h b/framework/src/suricata/src/decode-tcp.h
new file mode 100644
index 00000000..29c02dfb
--- /dev/null
+++ b/framework/src/suricata/src/decode-tcp.h
@@ -0,0 +1,297 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \todo RAW* macro's should be returning the raw value, not the host order
+ */
+
+#ifndef __DECODE_TCP_H__
+#define __DECODE_TCP_H__
+
+#define TCP_HEADER_LEN 20
+#define TCP_OPTLENMAX 40
+#define TCP_OPTMAX 20 /* every opt is at least 2 bytes
+ * (type + len), except EOL and NOP */
+
+/* TCP flags */
+
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+/** Establish a new connection reducing window */
+#define TH_ECN 0x40
+/** Echo Congestion flag */
+#define TH_CWR 0x80
+
+/* tcp option codes */
+#define TCP_OPT_EOL 0x00
+#define TCP_OPT_NOP 0x01
+#define TCP_OPT_MSS 0x02
+#define TCP_OPT_WS 0x03
+#define TCP_OPT_SACKOK 0x04
+#define TCP_OPT_SACK 0x05
+#define TCP_OPT_TS 0x08
+
+#define TCP_OPT_SACKOK_LEN 2
+#define TCP_OPT_WS_LEN 3
+#define TCP_OPT_TS_LEN 10
+#define TCP_OPT_MSS_LEN 4
+#define TCP_OPT_SACK_MIN_LEN 10 /* hdr 2, 1 pair 8 = 10 */
+#define TCP_OPT_SACK_MAX_LEN 34 /* hdr 2, 4 pair 32= 34 */
+
+/** Max valid wscale value. */
+#define TCP_WSCALE_MAX 14
+
+#define TCP_OPTS tcpvars.tcp_opts
+#define TCP_OPTS_CNT tcpvars.tcp_opt_cnt
+
+#define TCP_GET_RAW_OFFSET(tcph) (((tcph)->th_offx2 & 0xf0) >> 4)
+#define TCP_GET_RAW_X2(tcph) (unsigned char)((tcph)->th_offx2 & 0x0f)
+#define TCP_GET_RAW_SRC_PORT(tcph) ntohs((tcph)->th_sport)
+#define TCP_GET_RAW_DST_PORT(tcph) ntohs((tcph)->th_dport)
+
+#define TCP_SET_RAW_TCP_OFFSET(tcph, value) ((tcph)->th_offx2 = (unsigned char)(((tcph)->th_offx2 & 0x0f) | (value << 4)))
+#define TCP_SET_RAW_TCP_X2(tcph, value) ((tcph)->th_offx2 = (unsigned char)(((tcph)->th_offx2 & 0xf0) | (value & 0x0f)))
+
+#define TCP_GET_RAW_SEQ(tcph) ntohl((tcph)->th_seq)
+#define TCP_GET_RAW_ACK(tcph) ntohl((tcph)->th_ack)
+
+#define TCP_GET_RAW_WINDOW(tcph) ntohs((tcph)->th_win)
+#define TCP_GET_RAW_URG_POINTER(tcph) ntohs((tcph)->th_urp)
+
+/** macro for getting the first timestamp from the packet. Timestamp is in host
+ * order and either returned from the cache or from the packet directly. */
+#define TCP_GET_TSVAL(p) \
+ (uint32_t)ntohl((*(uint32_t *)(p)->tcpvars.ts->data))
+
+/** macro for getting the second timestamp from the packet. Timestamp is in
+ * host order and either returned from the cache or from the packet directly. */
+#define TCP_GET_TSECR(p) \
+ (uint32_t)ntohl((*(uint32_t *)((p)->tcpvars.ts->data+4)))
+
+/** macro for getting the wscale from the packet. */
+#define TCP_GET_WSCALE(p) ((p)->tcpvars.ws ? (((*(uint8_t *)(p)->tcpvars.ws->data) <= TCP_WSCALE_MAX) ? (*(uint8_t *)((p)->tcpvars.ws->data)) : 0) : 0)
+
+#define TCP_GET_SACKOK(p) ((p)->tcpvars.sackok ? 1 : 0)
+#define TCP_GET_SACK_PTR(p) (p)->tcpvars.sack ? (p)->tcpvars.sack->data : NULL
+#define TCP_GET_SACK_CNT(p) ((p)->tcpvars.sack ? (((p)->tcpvars.sack->len - 2) / 8) : 0)
+
+#define TCP_GET_OFFSET(p) TCP_GET_RAW_OFFSET((p)->tcph)
+#define TCP_GET_HLEN(p) (TCP_GET_OFFSET((p)) << 2)
+#define TCP_GET_SRC_PORT(p) TCP_GET_RAW_SRC_PORT((p)->tcph)
+#define TCP_GET_DST_PORT(p) TCP_GET_RAW_DST_PORT((p)->tcph)
+#define TCP_GET_SEQ(p) TCP_GET_RAW_SEQ((p)->tcph)
+#define TCP_GET_ACK(p) TCP_GET_RAW_ACK((p)->tcph)
+#define TCP_GET_WINDOW(p) TCP_GET_RAW_WINDOW((p)->tcph)
+#define TCP_GET_URG_POINTER(p) TCP_GET_RAW_URG_POINTER((p)->tcph)
+
+#define TCP_ISSET_FLAG_FIN(p) ((p)->tcph->th_flags & TH_FIN)
+#define TCP_ISSET_FLAG_SYN(p) ((p)->tcph->th_flags & TH_SYN)
+#define TCP_ISSET_FLAG_RST(p) ((p)->tcph->th_flags & TH_RST)
+#define TCP_ISSET_FLAG_PUSH(p) ((p)->tcph->th_flags & TH_PUSH)
+#define TCP_ISSET_FLAG_ACK(p) ((p)->tcph->th_flags & TH_ACK)
+#define TCP_ISSET_FLAG_URG(p) ((p)->tcph->th_flags & TH_URG)
+#define TCP_ISSET_FLAG_RES2(p) ((p)->tcph->th_flags & TH_RES2)
+#define TCP_ISSET_FLAG_RES1(p) ((p)->tcph->th_flags & TH_RES1)
+
+typedef struct TCPOpt_ {
+ uint8_t type;
+ uint8_t len;
+ uint8_t *data;
+} TCPOpt;
+
+typedef struct TCPOptSackRecord_ {
+ uint32_t le; /**< left edge, network order */
+ uint32_t re; /**< right edge, network order */
+} TCPOptSackRecord;
+
+typedef struct TCPHdr_
+{
+ uint16_t th_sport; /**< source port */
+ uint16_t th_dport; /**< destination port */
+ uint32_t th_seq; /**< sequence number */
+ uint32_t th_ack; /**< acknowledgement number */
+ uint8_t th_offx2; /**< offset and reserved */
+ uint8_t th_flags; /**< pkt flags */
+ uint16_t th_win; /**< pkt window */
+ uint16_t th_sum; /**< checksum */
+ uint16_t th_urp; /**< urgent pointer */
+} __attribute__((__packed__)) TCPHdr;
+
+typedef struct TCPVars_
+{
+ uint8_t tcp_opt_cnt;
+ TCPOpt tcp_opts[TCP_OPTMAX];
+
+ /* ptrs to commonly used and needed opts */
+ TCPOpt *ts;
+ TCPOpt *sack;
+ TCPOpt *sackok;
+ TCPOpt *ws;
+ TCPOpt *mss;
+} TCPVars;
+
+#define CLEAR_TCP_PACKET(p) { \
+ (p)->tcph = NULL; \
+ (p)->level4_comp_csum = -1; \
+ (p)->tcpvars.tcp_opt_cnt = 0; \
+ (p)->tcpvars.ts = NULL; \
+ (p)->tcpvars.sack = NULL; \
+ (p)->tcpvars.sackok = NULL; \
+ (p)->tcpvars.ws = NULL; \
+ (p)->tcpvars.mss = NULL; \
+}
+
+void DecodeTCPRegisterTests(void);
+
+/** -------- Inline functions ------- */
+static inline uint16_t TCPCalculateChecksum(uint16_t *, uint16_t *, uint16_t);
+static inline uint16_t TCPV6CalculateChecksum(uint16_t *, uint16_t *, uint16_t);
+
+/**
+ * \brief Calculates the checksum for the TCP packet
+ *
+ * \param shdr Pointer to source address field from the IP packet. Used as a
+ * part of the pseudoheader for computing the checksum
+ * \param pkt Pointer to the start of the TCP packet
+ * \param tlen Total length of the TCP packet(header + payload)
+ *
+ * \retval csum Checksum for the TCP packet
+ */
+static inline uint16_t TCPCalculateChecksum(uint16_t *shdr, uint16_t *pkt,
+ uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = shdr[0];
+
+ csum += shdr[1] + shdr[2] + shdr[3] + htons(6) + htons(tlen);
+
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[9];
+
+ tlen -= 20;
+ pkt += 10;
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ pkt += 1;
+ tlen -= 2;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ return (uint16_t)~csum;
+}
+
+/**
+ * \brief Calculates the checksum for the TCP packet
+ *
+ * \param shdr Pointer to source address field from the IPV6 packet. Used as a
+ * part of the psuedoheader for computing the checksum
+ * \param pkt Pointer to the start of the TCP packet
+ * \param tlen Total length of the TCP packet(header + payload)
+ *
+ * \retval csum Checksum for the TCP packet
+ */
+static inline uint16_t TCPV6CalculateChecksum(uint16_t *shdr, uint16_t *pkt,
+ uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = shdr[0];
+
+ csum += shdr[1] + shdr[2] + shdr[3] + shdr[4] + shdr[5] + shdr[6] +
+ shdr[7] + shdr[8] + shdr[9] + shdr[10] + shdr[11] + shdr[12] +
+ shdr[13] + shdr[14] + shdr[15] + htons(6) + htons(tlen);
+
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[9];
+
+ tlen -= 20;
+ pkt += 10;
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ pkt += 1;
+ tlen -= 2;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ return (uint16_t)~csum;
+}
+
+
+#endif /* __DECODE_TCP_H__ */
+
diff --git a/framework/src/suricata/src/decode-template.c b/framework/src/suricata/src/decode-template.c
new file mode 100644
index 00000000..2673a2cd
--- /dev/null
+++ b/framework/src/suricata/src/decode-template.c
@@ -0,0 +1,97 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author XXX Your Name <your@email.com>
+ *
+ * Decodes XXX describe the protocol
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "decode-events.h"
+#include "decode-template.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/**
+ * \brief Function to decode XXX packets
+ * \param tv thread vars
+ * \param dtv decoder thread vars
+ * \param p packet
+ * \param pkt raw packet data
+ * \param len length in bytes of pkt array
+ * \retval TM_ECODE_OK or TM_ECODE_FAILED on serious error
+ */
+
+int DecodeTEMPLATE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+ const uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ /* TODO add counter for your type of packet to DecodeThreadVars,
+ * and register it in DecodeRegisterPerfCounters */
+ //StatsIncr(tv, dtv->counter_template);
+
+ /* Validation: make sure that the input data is big enough to hold
+ * the header */
+ if (len < sizeof(TemplateHdr)) {
+ /* in case of errors, we set events. Events are defined in
+ * decode-events.h, and are then exposed to the detection
+ * engine through detect-engine-events.h */
+ //ENGINE_SET_EVENT(p,TEMPLATE_HEADER_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Now we can access the header */
+ const TemplateHdr *hdr = (const TemplateHdr *)pkt;
+
+ /* lets assume we have UDP encapsulated */
+ if (hdr->proto == 17) {
+ /* we need to pass on the pkt and it's length minus the current
+ * header */
+ size_t hdr_len = sizeof(TemplateHdr);
+
+ /* in this example it's clear that hdr_len can't be bigger than
+ * 'len', but in more complex cases checking that we can't underflow
+ * len is very important
+ if (hdr_len < len) {
+ */
+
+ /* invoke the next decoder on the remainder of the data */
+ return DecodeUDP(tv, dtv, p, (uint8_t *)pkt + hdr_len, len - hdr_len, pq);
+ //}
+ } else {
+ //ENGINE_SET_EVENT(p,TEMPLATE_UNSUPPORTED_PROTOCOL);
+ return TM_ECODE_FAILED;
+ }
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-template.h b/framework/src/suricata/src/decode-template.h
new file mode 100644
index 00000000..b6a976e0
--- /dev/null
+++ b/framework/src/suricata/src/decode-template.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author XXX
+ *
+ */
+
+#ifndef __DECODE_TEMPLATE_H__
+#define __DECODE_TEMPLATE_H__
+
+#include "decode.h"
+#include "threadvars.h"
+
+typedef struct TemplateHdr_ {
+ uint8_t proto;
+ uint8_t pad0;
+ uint16_t pad1;
+} __attribute__((__packed__)) TemplateHdr;
+
+#endif /* __DECODE_TEMPLATE_H__ */
diff --git a/framework/src/suricata/src/decode-teredo.c b/framework/src/suricata/src/decode-teredo.c
new file mode 100644
index 00000000..20876027
--- /dev/null
+++ b/framework/src/suricata/src/decode-teredo.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Decode Teredo Tunneling protocol.
+ *
+ * This implementation is based upon RFC 4380: http://www.ietf.org/rfc/rfc4380.txt
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-ipv6.h"
+#include "util-debug.h"
+
+#define TEREDO_ORIG_INDICATION_LENGTH 8
+
+/**
+ * \brief Function to decode Teredo packets
+ *
+ * \retval TM_ECODE_FAILED if packet is not a Teredo packet, TM_ECODE_OK if it is
+ */
+int DecodeTeredo(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+
+ uint8_t *start = pkt;
+
+ /* Is this packet to short to contain an IPv6 packet ? */
+ if (len < IPV6_HEADER_LEN)
+ return TM_ECODE_FAILED;
+
+ /* Teredo encapsulate IPv6 in UDP and can add some custom message
+ * part before the IPv6 packet. In our case, we just want to get
+ * over an ORIGIN indication. So we just make one offset if needed. */
+ if (start[0] == 0x0) {
+ switch (start[1]) {
+ /* origin indication: compatible with tunnel */
+ case 0x0:
+ /* offset is coherent with len and presence of an IPv6 header */
+ if (len >= TEREDO_ORIG_INDICATION_LENGTH + IPV6_HEADER_LEN)
+ start += TEREDO_ORIG_INDICATION_LENGTH;
+ else
+ return TM_ECODE_FAILED;
+ break;
+ /* authentication: negotiation not real tunnel */
+ case 0x1:
+ return TM_ECODE_FAILED;
+ /* this case is not possible in Teredo: not that protocol */
+ default:
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ /* There is no specific field that we can check to prove that the packet
+ * is a Teredo packet. We've zapped here all the possible Teredo header
+ * and we should have an IPv6 packet at the start pointer.
+ * We then can only do two checks before sending the encapsulated packets
+ * to decoding:
+ * - The packet has a protocol version which is IPv6.
+ * - The IPv6 length of the packet matches what remains in buffer.
+ */
+ if (IP_GET_RAW_VER(start) == 6) {
+ IPV6Hdr *thdr = (IPV6Hdr *)start;
+ if (len == IPV6_HEADER_LEN +
+ IPV6_GET_RAW_PLEN(thdr) + (start - pkt)) {
+ if (pq != NULL) {
+ int blen = len - (start - pkt);
+ /* spawn off tunnel packet */
+ Packet *tp = PacketTunnelPktSetup(tv, dtv, p, start, blen,
+ DECODE_TUNNEL_IPV6, pq);
+ if (tp != NULL) {
+ PKT_SET_SRC(tp, PKT_SRC_DECODER_TEREDO);
+ /* add the tp to the packet queue. */
+ PacketEnqueue(pq,tp);
+ StatsIncr(tv, dtv->counter_teredo);
+ return TM_ECODE_OK;
+ }
+ }
+ }
+ return TM_ECODE_FAILED;
+ }
+
+ return TM_ECODE_FAILED;
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-teredo.h b/framework/src/suricata/src/decode-teredo.h
new file mode 100644
index 00000000..142d13c2
--- /dev/null
+++ b/framework/src/suricata/src/decode-teredo.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+int DecodeTeredo(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+ uint8_t *pkt, uint16_t len, PacketQueue *pq);
diff --git a/framework/src/suricata/src/decode-udp.c b/framework/src/suricata/src/decode-udp.c
new file mode 100644
index 00000000..14ba7887
--- /dev/null
+++ b/framework/src/suricata/src/decode-udp.c
@@ -0,0 +1,218 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode UDP
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-udp.h"
+#include "decode-teredo.h"
+#include "decode-events.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "flow.h"
+#include "app-layer.h"
+
+static int DecodeUDPPacket(ThreadVars *t, Packet *p, uint8_t *pkt, uint16_t len)
+{
+ if (unlikely(len < UDP_HEADER_LEN)) {
+ ENGINE_SET_INVALID_EVENT(p, UDP_HLEN_TOO_SMALL);
+ return -1;
+ }
+
+ p->udph = (UDPHdr *)pkt;
+
+ if (unlikely(len < UDP_GET_LEN(p))) {
+ ENGINE_SET_INVALID_EVENT(p, UDP_PKT_TOO_SMALL);
+ return -1;
+ }
+
+ if (unlikely(len != UDP_GET_LEN(p))) {
+ ENGINE_SET_INVALID_EVENT(p, UDP_HLEN_INVALID);
+ return -1;
+ }
+
+ SET_UDP_SRC_PORT(p,&p->sp);
+ SET_UDP_DST_PORT(p,&p->dp);
+
+ p->payload = pkt + UDP_HEADER_LEN;
+ p->payload_len = len - UDP_HEADER_LEN;
+
+ p->proto = IPPROTO_UDP;
+
+ return 0;
+}
+
+int DecodeUDP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ StatsIncr(tv, dtv->counter_udp);
+
+ if (unlikely(DecodeUDPPacket(tv, p,pkt,len) < 0)) {
+ p->udph = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("UDP sp: %" PRIu32 " -> dp: %" PRIu32 " - HLEN: %" PRIu32 " LEN: %" PRIu32 "",
+ UDP_GET_SRC_PORT(p), UDP_GET_DST_PORT(p), UDP_HEADER_LEN, p->payload_len);
+
+ if (unlikely(DecodeTeredo(tv, dtv, p, p->payload, p->payload_len, pq) == TM_ECODE_OK)) {
+ /* Here we have a Teredo packet and don't need to handle app
+ * layer */
+ FlowHandlePacket(tv, dtv, p);
+ return TM_ECODE_OK;
+ }
+
+ /* Flow is an integral part of us */
+ FlowHandlePacket(tv, dtv, p);
+
+ /* handle the app layer part of the UDP packet payload */
+ if (unlikely(p->flow != NULL)) {
+ AppLayerHandleUdp(tv, dtv->app_tctx, p, p->flow);
+ }
+
+ return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+static int UDPV4CalculateValidChecksumtest01(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipshdr[] = {
+ 0xd0, 0x43, 0xdc, 0xdc, 0xc0, 0xa8, 0x01, 0x3};
+
+ uint8_t raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x26};
+
+ csum = *( ((uint16_t *)raw_udp) + 3);
+
+ return (csum == UDPV4CalculateChecksum((uint16_t *) raw_ipshdr,
+ (uint16_t *)raw_udp,
+ sizeof(raw_udp)));
+}
+
+static int UDPV4CalculateInvalidChecksumtest02(void)
+{
+ uint16_t csum = 0;
+
+ uint8_t raw_ipshdr[] = {
+ 0xd0, 0x43, 0xdc, 0xdc, 0xc0, 0xa8, 0x01, 0x3};
+
+ uint8_t raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x27};
+
+ csum = *( ((uint16_t *)raw_udp) + 3);
+
+ return (csum == UDPV4CalculateChecksum((uint16_t *) raw_ipshdr,
+ (uint16_t *)raw_udp,
+ sizeof(raw_udp)));
+}
+
+static int UDPV6CalculateValidChecksumtest03(void)
+{
+ uint16_t csum = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x00};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 60)));
+
+ return (csum == UDPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 20));
+}
+
+static int UDPV6CalculateInvalidChecksumtest04(void)
+{
+ uint16_t csum = 0;
+
+ static uint8_t raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x01};
+
+ csum = *( ((uint16_t *)(raw_ipv6 + 60)));
+
+ return (csum == UDPV6CalculateChecksum((uint16_t *)(raw_ipv6 + 14 + 8),
+ (uint16_t *)(raw_ipv6 + 54), 20));
+}
+#endif /* UNITTESTS */
+
+void DecodeUDPV4RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("UDPV4CalculateValidChecksumtest01",
+ UDPV4CalculateValidChecksumtest01, 1);
+ UtRegisterTest("UDPV4CalculateInvalidChecksumtest02",
+ UDPV4CalculateInvalidChecksumtest02, 0);
+ UtRegisterTest("UDPV6CalculateValidChecksumtest03",
+ UDPV6CalculateValidChecksumtest03, 1);
+ UtRegisterTest("UDPV6CalculateInvalidChecksumtest04",
+ UDPV6CalculateInvalidChecksumtest04, 0);
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-udp.h b/framework/src/suricata/src/decode-udp.h
new file mode 100644
index 00000000..5636c32b
--- /dev/null
+++ b/framework/src/suricata/src/decode-udp.h
@@ -0,0 +1,193 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_UDP_H__
+#define __DECODE_UDP_H__
+
+#define UDP_HEADER_LEN 8
+
+/* XXX RAW* needs to be really 'raw', so no ntohs there */
+#define UDP_GET_RAW_LEN(udph) ntohs((udph)->uh_len)
+#define UDP_GET_RAW_SRC_PORT(udph) ntohs((udph)->uh_sport)
+#define UDP_GET_RAW_DST_PORT(udph) ntohs((udph)->uh_dport)
+
+#define UDP_GET_LEN(p) UDP_GET_RAW_LEN(p->udph)
+#define UDP_GET_SRC_PORT(p) UDP_GET_RAW_SRC_PORT(p->udph)
+#define UDP_GET_DST_PORT(p) UDP_GET_RAW_DST_PORT(p->udph)
+
+/* UDP header structure */
+typedef struct UDPHdr_
+{
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ uint16_t uh_len; /* length */
+ uint16_t uh_sum; /* checksum */
+} __attribute__((__packed__)) UDPHdr;
+
+typedef struct UDPVars_
+{
+} UDPVars;
+
+#define CLEAR_UDP_PACKET(p) do { \
+ (p)->udph = NULL; \
+ (p)->level4_comp_csum = -1; \
+} while (0)
+
+void DecodeUDPV4RegisterTests(void);
+
+/** ------ Inline function ------ */
+static inline uint16_t UDPV4CalculateChecksum(uint16_t *, uint16_t *, uint16_t);
+static inline uint16_t UDPV6CalculateChecksum(uint16_t *, uint16_t *, uint16_t);
+
+/**
+ * \brief Calculates the checksum for the UDP packet
+ *
+ * \param shdr Pointer to source address field from the IP packet. Used as a
+ * part of the psuedoheader for computing the checksum
+ * \param pkt Pointer to the start of the UDP packet
+ * \param hlen Total length of the UDP packet(header + payload)
+ *
+ * \retval csum Checksum for the UDP packet
+ */
+static inline uint16_t UDPV4CalculateChecksum(uint16_t *shdr, uint16_t *pkt,
+ uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = shdr[0];
+
+ csum += shdr[1] + shdr[2] + shdr[3] + htons(17) + htons(tlen);
+
+ csum += pkt[0] + pkt[1] + pkt[2];
+
+ tlen -= 8;
+ pkt += 4;
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ pkt += 1;
+ tlen -= 2;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ uint16_t csum_u16 = (uint16_t)~csum;
+ if (csum_u16 == 0)
+ return 0xFFFF;
+ else
+ return csum_u16;
+}
+
+/**
+ * \brief Calculates the checksum for the UDP packet
+ *
+ * \param shdr Pointer to source address field from the IPV6 packet. Used as a
+ * part of the psuedoheader for computing the checksum
+ * \param pkt Pointer to the start of the UDP packet
+ * \param tlen Total length of the UDP packet(header + payload)
+ *
+ * \retval csum Checksum for the UDP packet
+ */
+static inline uint16_t UDPV6CalculateChecksum(uint16_t *shdr, uint16_t *pkt,
+ uint16_t tlen)
+{
+ uint16_t pad = 0;
+ uint32_t csum = shdr[0];
+
+ csum += shdr[1] + shdr[2] + shdr[3] + shdr[4] + shdr[5] + shdr[6] +
+ shdr[7] + shdr[8] + shdr[9] + shdr[10] + shdr[11] + shdr[12] +
+ shdr[13] + shdr[14] + shdr[15] + htons(17) + htons(tlen);
+
+ csum += pkt[0] + pkt[1] + pkt[2];
+
+ tlen -= 8;
+ pkt += 4;
+
+ while (tlen >= 32) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
+ pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
+ pkt[14] + pkt[15];
+ tlen -= 32;
+ pkt += 16;
+ }
+
+ while(tlen >= 8) {
+ csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
+ tlen -= 8;
+ pkt += 4;
+ }
+
+ while(tlen >= 4) {
+ csum += pkt[0] + pkt[1];
+ tlen -= 4;
+ pkt += 2;
+ }
+
+ while (tlen > 1) {
+ csum += pkt[0];
+ pkt += 1;
+ tlen -= 2;
+ }
+
+ if (tlen == 1) {
+ *(uint8_t *)(&pad) = (*(uint8_t *)pkt);
+ csum += pad;
+ }
+
+ csum = (csum >> 16) + (csum & 0x0000FFFF);
+ csum += (csum >> 16);
+
+ uint16_t csum_u16 = (uint16_t)~csum;
+ if (csum_u16 == 0)
+ return 0xFFFF;
+ else
+ return csum_u16;
+}
+
+
+#endif /* __DECODE_UDP_H__ */
diff --git a/framework/src/suricata/src/decode-vlan.c b/framework/src/suricata/src/decode-vlan.c
new file mode 100644
index 00000000..59495594
--- /dev/null
+++ b/framework/src/suricata/src/decode-vlan.c
@@ -0,0 +1,279 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Decode 802.1q
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "decode-vlan.h"
+#include "decode-events.h"
+
+#include "flow.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "util-profiling.h"
+#include "host.h"
+
+/**
+ * \internal
+ * \brief this function is used to decode IEEE802.1q packets
+ *
+ * \param tv pointer to the thread vars
+ * \param dtv pointer code thread vars
+ * \param p pointer to the packet struct
+ * \param pkt pointer to the raw packet
+ * \param len packet len
+ * \param pq pointer to the packet queue
+ *
+ */
+int DecodeVLAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
+{
+ uint32_t proto;
+
+ if (p->vlan_idx == 0)
+ StatsIncr(tv, dtv->counter_vlan);
+ else if (p->vlan_idx == 1)
+ StatsIncr(tv, dtv->counter_vlan_qinq);
+
+ if(len < VLAN_HEADER_LEN) {
+ ENGINE_SET_INVALID_EVENT(p, VLAN_HEADER_TOO_SMALL);
+ return TM_ECODE_FAILED;
+ }
+ if (p->vlan_idx >= 2) {
+ ENGINE_SET_EVENT(p,VLAN_HEADER_TOO_MANY_LAYERS);
+ return TM_ECODE_FAILED;
+ }
+
+ p->vlanh[p->vlan_idx] = (VLANHdr *)pkt;
+ if(p->vlanh[p->vlan_idx] == NULL)
+ return TM_ECODE_FAILED;
+
+ proto = GET_VLAN_PROTO(p->vlanh[p->vlan_idx]);
+
+ SCLogDebug("p %p pkt %p VLAN protocol %04x VLAN PRI %d VLAN CFI %d VLAN ID %d Len: %" PRId32 "",
+ p, pkt, proto, GET_VLAN_PRIORITY(p->vlanh[p->vlan_idx]),
+ GET_VLAN_CFI(p->vlanh[p->vlan_idx]), GET_VLAN_ID(p->vlanh[p->vlan_idx]), len);
+
+ /* only store the id for flow hashing if it's not disabled. */
+ if (dtv->vlan_disabled == 0)
+ p->vlan_id[p->vlan_idx] = (uint16_t)GET_VLAN_ID(p->vlanh[p->vlan_idx]);
+
+ p->vlan_idx++;
+
+ switch (proto) {
+ case ETHERNET_TYPE_IP:
+ DecodeIPV4(tv, dtv, p, pkt + VLAN_HEADER_LEN,
+ len - VLAN_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_IPV6:
+ DecodeIPV6(tv, dtv, p, pkt + VLAN_HEADER_LEN,
+ len - VLAN_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_PPPOE_SESS:
+ DecodePPPOESession(tv, dtv, p, pkt + VLAN_HEADER_LEN,
+ len - VLAN_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_PPPOE_DISC:
+ DecodePPPOEDiscovery(tv, dtv, p, pkt + VLAN_HEADER_LEN,
+ len - VLAN_HEADER_LEN, pq);
+ break;
+ case ETHERNET_TYPE_VLAN:
+ case ETHERNET_TYPE_8021AD:
+ if (p->vlan_idx >= 2) {
+ ENGINE_SET_EVENT(p,VLAN_HEADER_TOO_MANY_LAYERS);
+ return TM_ECODE_OK;
+ } else {
+ DecodeVLAN(tv, dtv, p, pkt + VLAN_HEADER_LEN,
+ len - VLAN_HEADER_LEN, pq);
+ }
+ break;
+ default:
+ SCLogDebug("unknown VLAN type: %" PRIx32 "", proto);
+ ENGINE_SET_INVALID_EVENT(p, VLAN_UNKNOWN_TYPE);
+ return TM_ECODE_OK;
+ }
+
+ return TM_ECODE_OK;
+}
+
+uint16_t DecodeVLANGetId(const Packet *p, uint8_t layer)
+{
+ if (unlikely(layer > 1))
+ return 0;
+
+ if (p->vlanh[layer] == NULL && (p->vlan_idx >= (layer + 1))) {
+ return p->vlan_id[layer];
+ } else {
+ return GET_VLAN_ID(p->vlanh[layer]);
+ }
+ return 0;
+}
+
+#ifdef UNITTESTS
+/** \todo Must GRE+VLAN and Multi-Vlan packets to
+ * create more tests
+ */
+
+/**
+ * \test DecodeVLANTest01 test if vlan header is too small.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int DecodeVLANtest01 (void)
+{
+ uint8_t raw_vlan[] = { 0x00, 0x20, 0x08 };
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan), NULL);
+
+ if(ENGINE_ISSET_EVENT(p,VLAN_HEADER_TOO_SMALL)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test DecodeVLANTest02 test if vlan header has unknown type.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int DecodeVLANtest02 (void)
+{
+ uint8_t raw_vlan[] = {
+ 0x00, 0x20, 0x01, 0x00, 0x45, 0x00, 0x00, 0x34,
+ 0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
+ 0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
+ 0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
+ 0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
+ 0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
+ 0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan), NULL);
+
+
+ if(ENGINE_ISSET_EVENT(p,VLAN_UNKNOWN_TYPE)) {
+ SCFree(p);
+ return 1;
+ }
+
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test DecodeVLANTest02 test a good vlan header.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int DecodeVLANtest03 (void)
+{
+ uint8_t raw_vlan[] = {
+ 0x00, 0x20, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34,
+ 0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
+ 0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
+ 0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
+ 0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
+ 0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
+ 0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3};
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan), NULL);
+
+
+ if(p->vlanh == NULL) {
+ goto error;
+ }
+
+ if(ENGINE_ISSET_EVENT(p,VLAN_HEADER_TOO_SMALL)) {
+ goto error;
+ }
+
+ if(ENGINE_ISSET_EVENT(p,VLAN_UNKNOWN_TYPE)) {
+ goto error;
+ }
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+
+error:
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 0;
+}
+#endif /* UNITTESTS */
+
+void DecodeVLANRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeVLANtest01", DecodeVLANtest01, 1);
+ UtRegisterTest("DecodeVLANtest02", DecodeVLANtest02, 1);
+ UtRegisterTest("DecodeVLANtest03", DecodeVLANtest03, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode-vlan.h b/framework/src/suricata/src/decode-vlan.h
new file mode 100644
index 00000000..baa36472
--- /dev/null
+++ b/framework/src/suricata/src/decode-vlan.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DECODE_VLAN_H__
+#define __DECODE_VLAN_H__
+
+/* return vlan id in host byte order */
+uint16_t DecodeVLANGetId(const struct Packet_ *, uint8_t layer);
+
+/** Vlan type */
+#define ETHERNET_TYPE_VLAN 0x8100
+
+/** Vlan macros to access Vlan priority, Vlan CFI and VID */
+#define GET_VLAN_PRIORITY(vlanh) ((ntohs((vlanh)->vlan_cfi) & 0xe000) >> 13)
+#define GET_VLAN_CFI(vlanh) ((ntohs((vlanh)->vlan_cfi) & 0x0100) >> 12)
+#define GET_VLAN_ID(vlanh) ((uint16_t)(ntohs((vlanh)->vlan_cfi) & 0x0FFF))
+#define GET_VLAN_PROTO(vlanh) ((ntohs((vlanh)->protocol)))
+
+/* return vlan id in host byte order */
+#define VLAN_GET_ID1(p) DecodeVLANGetId((p), 0)
+#define VLAN_GET_ID2(p) DecodeVLANGetId((p), 1)
+
+/** Vlan header struct */
+typedef struct VLANHdr_ {
+ uint16_t vlan_cfi;
+ uint16_t protocol; /**< protocol field */
+} __attribute__((__packed__)) VLANHdr;
+
+/** VLAN header length */
+#define VLAN_HEADER_LEN 4
+
+void DecodeVLANRegisterTests(void);
+
+#endif /* __DECODE_VLAN_H__ */
+
diff --git a/framework/src/suricata/src/decode.c b/framework/src/suricata/src/decode.c
new file mode 100644
index 00000000..0dd9fa86
--- /dev/null
+++ b/framework/src/suricata/src/decode.c
@@ -0,0 +1,572 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup decode Packet decoding
+ *
+ * \brief Code in charge of protocol decoding
+ *
+ * The task of decoding packets is made in different files and
+ * as Suricata is supporting encapsulation there is a potential
+ * recursivity in the call.
+ *
+ * For each protocol a DecodePROTO function is provided. For
+ * example we have DecodeIPV4() for IPv4 and DecodePPP() for
+ * PPP.
+ *
+ * These functions have all a pkt and and a len argument which
+ * are respectively a pointer to the protocol data and the length
+ * of this protocol data.
+ *
+ * \attention The pkt parameter must point to the effective data because
+ * it will be used later to set per protocol pointer like Packet::tcph
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Decode the raw packet
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "conf.h"
+#include "decode.h"
+#include "util-debug.h"
+#include "util-mem.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer.h"
+#include "tm-threads.h"
+#include "util-error.h"
+#include "util-print.h"
+#include "tmqh-packetpool.h"
+#include "util-profiling.h"
+#include "pkt-var.h"
+#include "util-mpm-ac.h"
+
+#include "output.h"
+#include "output-flow.h"
+
+int DecodeTunnel(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+ uint8_t *pkt, uint16_t len, PacketQueue *pq, enum DecodeTunnelProto proto)
+{
+ switch (proto) {
+ case DECODE_TUNNEL_PPP:
+ return DecodePPP(tv, dtv, p, pkt, len, pq);
+ case DECODE_TUNNEL_IPV4:
+ return DecodeIPV4(tv, dtv, p, pkt, len, pq);
+ case DECODE_TUNNEL_IPV6:
+ return DecodeIPV6(tv, dtv, p, pkt, len, pq);
+ case DECODE_TUNNEL_VLAN:
+ return DecodeVLAN(tv, dtv, p, pkt, len, pq);
+ case DECODE_TUNNEL_ETHERNET:
+ return DecodeEthernet(tv, dtv, p, pkt, len, pq);
+ case DECODE_TUNNEL_ERSPAN:
+ return DecodeERSPAN(tv, dtv, p, pkt, len, pq);
+ default:
+ SCLogInfo("FIXME: DecodeTunnel: protocol %" PRIu32 " not supported.", proto);
+ break;
+ }
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Return a malloced packet.
+ */
+void PacketFree(Packet *p)
+{
+ PACKET_DESTRUCTOR(p);
+ SCFree(p);
+}
+
+/**
+ * \brief Finalize decoding of a packet
+ *
+ * This function needs to be call at the end of decode
+ * functions when decoding has been succesful.
+ *
+ */
+
+void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
+{
+
+ if (p->flags & PKT_IS_INVALID)
+ StatsIncr(tv, dtv->counter_invalid);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (dtv->cuda_vars.mpm_is_cuda)
+ CudaBufferPacket(&dtv->cuda_vars, p);
+#endif
+
+}
+
+/**
+ * \brief Get a malloced packet.
+ *
+ * \retval p packet, NULL on error
+ */
+Packet *PacketGetFromAlloc(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL)) {
+ return NULL;
+ }
+
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+ p->ReleasePacket = PacketFree;
+ p->flags |= PKT_ALLOC;
+
+ SCLogDebug("allocated a new packet only using alloc...");
+
+ PACKET_PROFILING_START(p);
+ return p;
+}
+
+/**
+ * \brief Return a packet to where it was allocated.
+ */
+void PacketFreeOrRelease(Packet *p)
+{
+ if (p->flags & PKT_ALLOC)
+ PacketFree(p);
+ else
+ PacketPoolReturnPacket(p);
+}
+
+/**
+ * \brief Get a packet. We try to get a packet from the packetpool first, but
+ * if that is empty we alloc a packet that is free'd again after
+ * processing.
+ *
+ * \retval p packet, NULL on error
+ */
+Packet *PacketGetFromQueueOrAlloc(void)
+{
+ /* try the pool first */
+ Packet *p = PacketPoolGetPacket();
+
+ if (p == NULL) {
+ /* non fatal, we're just not processing a packet then */
+ p = PacketGetFromAlloc();
+ } else {
+ PACKET_PROFILING_START(p);
+ }
+
+ return p;
+}
+
+inline int PacketCallocExtPkt(Packet *p, int datalen)
+{
+ if (! p->ext_pkt) {
+ p->ext_pkt = SCCalloc(1, datalen);
+ if (unlikely(p->ext_pkt == NULL)) {
+ SET_PKT_LEN(p, 0);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * \brief Copy data to Packet payload at given offset
+ *
+ * This function copies data/payload to a Packet. It uses the
+ * space allocated at Packet creation (pointed by Packet::pkt)
+ * or allocate some memory (pointed by Packet::ext_pkt) if the
+ * data size is to big to fit in initial space (of size
+ * default_packet_size).
+ *
+ * \param Pointer to the Packet to modify
+ * \param Offset of the copy relatively to payload of Packet
+ * \param Pointer to the data to copy
+ * \param Length of the data to copy
+ */
+inline int PacketCopyDataOffset(Packet *p, int offset, uint8_t *data, int datalen)
+{
+ if (unlikely(offset + datalen > MAX_PAYLOAD_SIZE)) {
+ /* too big */
+ return -1;
+ }
+
+ /* Do we have already an packet with allocated data */
+ if (! p->ext_pkt) {
+ if (offset + datalen <= (int)default_packet_size) {
+ /* data will fit in memory allocated with packet */
+ memcpy(GET_PKT_DIRECT_DATA(p) + offset, data, datalen);
+ } else {
+ /* here we need a dynamic allocation */
+ p->ext_pkt = SCMalloc(MAX_PAYLOAD_SIZE);
+ if (unlikely(p->ext_pkt == NULL)) {
+ SET_PKT_LEN(p, 0);
+ return -1;
+ }
+ /* copy initial data */
+ memcpy(p->ext_pkt, GET_PKT_DIRECT_DATA(p), GET_PKT_DIRECT_MAX_SIZE(p));
+ /* copy data as asked */
+ memcpy(p->ext_pkt + offset, data, datalen);
+ }
+ } else {
+ memcpy(p->ext_pkt + offset, data, datalen);
+ }
+ return 0;
+}
+
+/**
+ * \brief Copy data to Packet payload and set packet length
+ *
+ * \param Pointer to the Packet to modify
+ * \param Pointer to the data to copy
+ * \param Length of the data to copy
+ */
+inline int PacketCopyData(Packet *p, uint8_t *pktdata, int pktlen)
+{
+ SET_PKT_LEN(p, (size_t)pktlen);
+ return PacketCopyDataOffset(p, 0, pktdata, pktlen);
+}
+
+/**
+ * \brief Setup a pseudo packet (tunnel)
+ *
+ * \param parent parent packet for this pseudo pkt
+ * \param pkt raw packet data
+ * \param len packet data length
+ * \param proto protocol of the tunneled packet
+ *
+ * \retval p the pseudo packet or NULL if out of memory
+ */
+Packet *PacketTunnelPktSetup(ThreadVars *tv, DecodeThreadVars *dtv, Packet *parent,
+ uint8_t *pkt, uint16_t len, enum DecodeTunnelProto proto,
+ PacketQueue *pq)
+{
+ int ret;
+
+ SCEnter();
+
+ /* get us a packet */
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (unlikely(p == NULL)) {
+ SCReturnPtr(NULL, "Packet");
+ }
+
+ /* copy packet and set lenght, proto */
+ PacketCopyData(p, pkt, len);
+ p->recursion_level = parent->recursion_level + 1;
+ p->ts.tv_sec = parent->ts.tv_sec;
+ p->ts.tv_usec = parent->ts.tv_usec;
+ p->datalink = DLT_RAW;
+ p->tenant_id = parent->tenant_id;
+
+ /* set the root ptr to the lowest layer */
+ if (parent->root != NULL)
+ p->root = parent->root;
+ else
+ p->root = parent;
+
+ /* tell new packet it's part of a tunnel */
+ SET_TUNNEL_PKT(p);
+
+ ret = DecodeTunnel(tv, dtv, p, GET_PKT_DATA(p),
+ GET_PKT_LEN(p), pq, proto);
+
+ if (unlikely(ret != TM_ECODE_OK)) {
+ /* Not a tunnel packet, just a pseudo packet */
+ p->root = NULL;
+ UNSET_TUNNEL_PKT(p);
+ TmqhOutputPacketpool(tv, p);
+ SCReturnPtr(NULL, "Packet");
+ }
+
+
+ /* tell parent packet it's part of a tunnel */
+ SET_TUNNEL_PKT(parent);
+
+ /* increment tunnel packet refcnt in the root packet */
+ TUNNEL_INCR_PKT_TPR(p);
+
+ /* disable payload (not packet) inspection on the parent, as the payload
+ * is the packet we will now run through the system separately. We do
+ * check it against the ip/port/other header checks though */
+ DecodeSetNoPayloadInspectionFlag(parent);
+ SCReturnPtr(p, "Packet");
+}
+
+/**
+ * \brief Setup a pseudo packet (reassembled frags)
+ *
+ * Difference with PacketPseudoPktSetup is that this func doesn't increment
+ * the recursion level. It needs to be on the same level as the frags because
+ * we run the flow engine against this and we need to get the same flow.
+ *
+ * \param parent parent packet for this pseudo pkt
+ * \param pkt raw packet data
+ * \param len packet data length
+ * \param proto protocol of the tunneled packet
+ *
+ * \retval p the pseudo packet or NULL if out of memory
+ */
+Packet *PacketDefragPktSetup(Packet *parent, uint8_t *pkt, uint16_t len, uint8_t proto)
+{
+ SCEnter();
+
+ /* get us a packet */
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (unlikely(p == NULL)) {
+ SCReturnPtr(NULL, "Packet");
+ }
+
+ /* set the root ptr to the lowest layer */
+ if (parent->root != NULL)
+ p->root = parent->root;
+ else
+ p->root = parent;
+
+ /* copy packet and set lenght, proto */
+ PacketCopyData(p, pkt, len);
+ p->recursion_level = parent->recursion_level; /* NOT incremented */
+ p->ts.tv_sec = parent->ts.tv_sec;
+ p->ts.tv_usec = parent->ts.tv_usec;
+ p->datalink = DLT_RAW;
+ p->tenant_id = parent->tenant_id;
+ /* tell new packet it's part of a tunnel */
+ SET_TUNNEL_PKT(p);
+ p->vlan_id[0] = parent->vlan_id[0];
+ p->vlan_id[1] = parent->vlan_id[1];
+ p->vlan_idx = parent->vlan_idx;
+
+ SCReturnPtr(p, "Packet");
+}
+
+/**
+ * \brief inform defrag "parent" that a pseudo packet is
+ * now assosiated to it.
+ */
+void PacketDefragPktSetupParent(Packet *parent)
+{
+ /* tell parent packet it's part of a tunnel */
+ SET_TUNNEL_PKT(parent);
+
+ /* increment tunnel packet refcnt in the root packet */
+ TUNNEL_INCR_PKT_TPR(parent);
+
+ /* disable payload (not packet) inspection on the parent, as the payload
+ * is the packet we will now run through the system separately. We do
+ * check it against the ip/port/other header checks though */
+ DecodeSetNoPayloadInspectionFlag(parent);
+}
+
+void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
+{
+ /* register counters */
+ dtv->counter_pkts = StatsRegisterCounter("decoder.pkts", tv);
+ dtv->counter_bytes = StatsRegisterCounter("decoder.bytes", tv);
+ dtv->counter_invalid = StatsRegisterCounter("decoder.invalid", tv);
+ dtv->counter_ipv4 = StatsRegisterCounter("decoder.ipv4", tv);
+ dtv->counter_ipv6 = StatsRegisterCounter("decoder.ipv6", tv);
+ dtv->counter_eth = StatsRegisterCounter("decoder.ethernet", tv);
+ dtv->counter_raw = StatsRegisterCounter("decoder.raw", tv);
+ dtv->counter_null = StatsRegisterCounter("decoder.null", tv);
+ dtv->counter_sll = StatsRegisterCounter("decoder.sll", tv);
+ dtv->counter_tcp = StatsRegisterCounter("decoder.tcp", tv);
+ dtv->counter_udp = StatsRegisterCounter("decoder.udp", tv);
+ dtv->counter_sctp = StatsRegisterCounter("decoder.sctp", tv);
+ dtv->counter_icmpv4 = StatsRegisterCounter("decoder.icmpv4", tv);
+ dtv->counter_icmpv6 = StatsRegisterCounter("decoder.icmpv6", tv);
+ dtv->counter_ppp = StatsRegisterCounter("decoder.ppp", tv);
+ dtv->counter_pppoe = StatsRegisterCounter("decoder.pppoe", tv);
+ dtv->counter_gre = StatsRegisterCounter("decoder.gre", tv);
+ dtv->counter_vlan = StatsRegisterCounter("decoder.vlan", tv);
+ dtv->counter_vlan_qinq = StatsRegisterCounter("decoder.vlan_qinq", tv);
+ dtv->counter_teredo = StatsRegisterCounter("decoder.teredo", tv);
+ dtv->counter_ipv4inipv6 = StatsRegisterCounter("decoder.ipv4_in_ipv6", tv);
+ dtv->counter_ipv6inipv6 = StatsRegisterCounter("decoder.ipv6_in_ipv6", tv);
+ dtv->counter_mpls = StatsRegisterCounter("decoder.mpls", tv);
+ dtv->counter_avg_pkt_size = StatsRegisterAvgCounter("decoder.avg_pkt_size", tv);
+ dtv->counter_max_pkt_size = StatsRegisterMaxCounter("decoder.max_pkt_size", tv);
+ dtv->counter_erspan = StatsRegisterMaxCounter("decoder.erspan", tv);
+
+ dtv->counter_defrag_ipv4_fragments =
+ StatsRegisterCounter("defrag.ipv4.fragments", tv);
+ dtv->counter_defrag_ipv4_reassembled =
+ StatsRegisterCounter("defrag.ipv4.reassembled", tv);
+ dtv->counter_defrag_ipv4_timeouts =
+ StatsRegisterCounter("defrag.ipv4.timeouts", tv);
+ dtv->counter_defrag_ipv6_fragments =
+ StatsRegisterCounter("defrag.ipv6.fragments", tv);
+ dtv->counter_defrag_ipv6_reassembled =
+ StatsRegisterCounter("defrag.ipv6.reassembled", tv);
+ dtv->counter_defrag_ipv6_timeouts =
+ StatsRegisterCounter("defrag.ipv6.timeouts", tv);
+ dtv->counter_defrag_max_hit =
+ StatsRegisterCounter("defrag.max_frag_hits", tv);
+
+ return;
+}
+
+void DecodeUpdatePacketCounters(ThreadVars *tv,
+ const DecodeThreadVars *dtv, const Packet *p)
+{
+ StatsIncr(tv, dtv->counter_pkts);
+ //StatsIncr(tv, dtv->counter_pkts_per_sec);
+ StatsAddUI64(tv, dtv->counter_bytes, GET_PKT_LEN(p));
+ StatsAddUI64(tv, dtv->counter_avg_pkt_size, GET_PKT_LEN(p));
+ StatsSetUI64(tv, dtv->counter_max_pkt_size, GET_PKT_LEN(p));
+}
+
+/**
+ * \brief Debug print function for printing addresses
+ *
+ * \param Address object
+ *
+ * \todo IPv6
+ */
+void AddressDebugPrint(Address *a)
+{
+ if (a == NULL)
+ return;
+
+ switch (a->family) {
+ case AF_INET:
+ {
+ char s[16];
+ PrintInet(AF_INET, (const void *)&a->addr_data32[0], s, sizeof(s));
+ SCLogDebug("%s", s);
+ break;
+ }
+ }
+}
+
+/** \brief Alloc and setup DecodeThreadVars */
+DecodeThreadVars *DecodeThreadVarsAlloc(ThreadVars *tv)
+{
+ DecodeThreadVars *dtv = NULL;
+
+ if ( (dtv = SCMalloc(sizeof(DecodeThreadVars))) == NULL)
+ return NULL;
+ memset(dtv, 0, sizeof(DecodeThreadVars));
+
+ dtv->app_tctx = AppLayerGetCtxThread(tv);
+
+ if (OutputFlowLogThreadInit(tv, NULL, &dtv->output_flow_thread_data) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_THREAD_INIT, "initializing flow log API for thread failed");
+ DecodeThreadVarsFree(tv, dtv);
+ return NULL;
+ }
+
+ /** set config defaults */
+ int vlanbool = 0;
+ if ((ConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
+ dtv->vlan_disabled = 1;
+ }
+ SCLogDebug("vlan tracking is %s", dtv->vlan_disabled == 0 ? "enabled" : "disabled");
+
+ return dtv;
+}
+
+void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
+{
+ if (dtv != NULL) {
+ if (dtv->app_tctx != NULL)
+ AppLayerDestroyCtxThread(dtv->app_tctx);
+
+ if (dtv->output_flow_thread_data != NULL)
+ OutputFlowLogThreadDeinit(tv, dtv->output_flow_thread_data);
+
+ SCFree(dtv);
+ }
+}
+
+/**
+ * \brief Set data for Packet and set length when zeo copy is used
+ *
+ * \param Pointer to the Packet to modify
+ * \param Pointer to the data
+ * \param Length of the data
+ */
+inline int PacketSetData(Packet *p, uint8_t *pktdata, int pktlen)
+{
+ SET_PKT_LEN(p, (size_t)pktlen);
+ if (unlikely(!pktdata)) {
+ return -1;
+ }
+ p->ext_pkt = pktdata;
+ p->flags |= PKT_ZERO_COPY;
+
+ return 0;
+}
+
+const char *PktSrcToString(enum PktSrcEnum pkt_src)
+{
+ char *pkt_src_str = "<unknown>";
+ switch (pkt_src) {
+ case PKT_SRC_WIRE:
+ pkt_src_str = "wire/pcap";
+ break;
+ case PKT_SRC_DECODER_GRE:
+ pkt_src_str = "gre tunnel";
+ break;
+ case PKT_SRC_DECODER_IPV4:
+ pkt_src_str = "ipv4 tunnel";
+ break;
+ case PKT_SRC_DECODER_IPV6:
+ pkt_src_str = "ipv6 tunnel";
+ break;
+ case PKT_SRC_DECODER_TEREDO:
+ pkt_src_str = "teredo tunnel";
+ break;
+ case PKT_SRC_DEFRAG:
+ pkt_src_str = "defrag";
+ break;
+ case PKT_SRC_STREAM_TCP_STREAM_END_PSEUDO:
+ pkt_src_str = "stream";
+ break;
+ case PKT_SRC_FFR:
+ pkt_src_str = "stream (flow timeout)";
+ break;
+ }
+ return pkt_src_str;
+}
+
+void CaptureStatsUpdate(ThreadVars *tv, CaptureStats *s, const Packet *p)
+{
+ if (unlikely(PACKET_TEST_ACTION(p, (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)))) {
+ StatsIncr(tv, s->counter_ips_rejected);
+ } else if (unlikely(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ StatsIncr(tv, s->counter_ips_blocked);
+ } else if (unlikely(p->flags & PKT_STREAM_MODIFIED)) {
+ StatsIncr(tv, s->counter_ips_replaced);
+ } else {
+ StatsIncr(tv, s->counter_ips_accepted);
+ }
+}
+
+void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s)
+{
+ s->counter_ips_accepted = StatsRegisterCounter("ips.accepted", tv);
+ s->counter_ips_blocked = StatsRegisterCounter("ips.blocked", tv);
+ s->counter_ips_rejected = StatsRegisterCounter("ips.rejected", tv);
+ s->counter_ips_replaced = StatsRegisterCounter("ips.replaced", tv);
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/decode.h b/framework/src/suricata/src/decode.h
new file mode 100644
index 00000000..2f322a02
--- /dev/null
+++ b/framework/src/suricata/src/decode.h
@@ -0,0 +1,1048 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DECODE_H__
+#define __DECODE_H__
+
+//#define DBG_THREADS
+#define COUNTERS
+
+#include "suricata-common.h"
+#include "threadvars.h"
+#include "decode-events.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+#include "util-cuda-buffer.h"
+#include "util-cuda-vars.h"
+#endif /* __SC_CUDA_SUPPORT__ */
+
+typedef enum {
+ CHECKSUM_VALIDATION_DISABLE,
+ CHECKSUM_VALIDATION_ENABLE,
+ CHECKSUM_VALIDATION_AUTO,
+ CHECKSUM_VALIDATION_RXONLY,
+ CHECKSUM_VALIDATION_KERNEL,
+} ChecksumValidationMode;
+
+enum PktSrcEnum {
+ PKT_SRC_WIRE = 1,
+ PKT_SRC_DECODER_GRE,
+ PKT_SRC_DECODER_IPV4,
+ PKT_SRC_DECODER_IPV6,
+ PKT_SRC_DECODER_TEREDO,
+ PKT_SRC_DEFRAG,
+ PKT_SRC_STREAM_TCP_STREAM_END_PSEUDO,
+ PKT_SRC_FFR,
+};
+
+#include "source-nflog.h"
+#include "source-nfq.h"
+#include "source-ipfw.h"
+#include "source-pcap.h"
+#include "source-af-packet.h"
+#include "source-mpipe.h"
+#include "source-netmap.h"
+
+#include "action-globals.h"
+
+#include "decode-erspan.h"
+#include "decode-ethernet.h"
+#include "decode-gre.h"
+#include "decode-ppp.h"
+#include "decode-pppoe.h"
+#include "decode-sll.h"
+#include "decode-ipv4.h"
+#include "decode-ipv6.h"
+#include "decode-icmpv4.h"
+#include "decode-icmpv6.h"
+#include "decode-tcp.h"
+#include "decode-udp.h"
+#include "decode-sctp.h"
+#include "decode-raw.h"
+#include "decode-null.h"
+#include "decode-vlan.h"
+#include "decode-mpls.h"
+
+#include "detect-reference.h"
+
+#include "app-layer-protos.h"
+
+/* forward declarations */
+struct DetectionEngineThreadCtx_;
+typedef struct AppLayerThreadCtx_ AppLayerThreadCtx;
+
+struct PktPool_;
+
+/* declare these here as they are called from the
+ * PACKET_RECYCLE and PACKET_CLEANUP macro's. */
+typedef struct AppLayerDecoderEvents_ AppLayerDecoderEvents;
+void AppLayerDecoderEventsResetEvents(AppLayerDecoderEvents *events);
+void AppLayerDecoderEventsFreeEvents(AppLayerDecoderEvents **events);
+
+/* Address */
+typedef struct Address_ {
+ char family;
+ union {
+ uint32_t address_un_data32[4]; /* type-specific field */
+ uint16_t address_un_data16[8]; /* type-specific field */
+ uint8_t address_un_data8[16]; /* type-specific field */
+ } address;
+} Address;
+
+#define addr_data32 address.address_un_data32
+#define addr_data16 address.address_un_data16
+#define addr_data8 address.address_un_data8
+
+#define COPY_ADDRESS(a, b) do { \
+ (b)->family = (a)->family; \
+ (b)->addr_data32[0] = (a)->addr_data32[0]; \
+ (b)->addr_data32[1] = (a)->addr_data32[1]; \
+ (b)->addr_data32[2] = (a)->addr_data32[2]; \
+ (b)->addr_data32[3] = (a)->addr_data32[3]; \
+ } while (0)
+
+/* Set the IPv4 addresses into the Addrs of the Packet.
+ * Make sure p->ip4h is initialized and validated.
+ *
+ * We set the rest of the struct to 0 so we can
+ * prevent using memset. */
+#define SET_IPV4_SRC_ADDR(p, a) do { \
+ (a)->family = AF_INET; \
+ (a)->addr_data32[0] = (uint32_t)(p)->ip4h->s_ip_src.s_addr; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+#define SET_IPV4_DST_ADDR(p, a) do { \
+ (a)->family = AF_INET; \
+ (a)->addr_data32[0] = (uint32_t)(p)->ip4h->s_ip_dst.s_addr; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+/* clear the address structure by setting all fields to 0 */
+#define CLEAR_ADDR(a) do { \
+ (a)->family = 0; \
+ (a)->addr_data32[0] = 0; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+/* Set the IPv6 addressesinto the Addrs of the Packet.
+ * Make sure p->ip6h is initialized and validated. */
+#define SET_IPV6_SRC_ADDR(p, a) do { \
+ (a)->family = AF_INET6; \
+ (a)->addr_data32[0] = (p)->ip6h->s_ip6_src[0]; \
+ (a)->addr_data32[1] = (p)->ip6h->s_ip6_src[1]; \
+ (a)->addr_data32[2] = (p)->ip6h->s_ip6_src[2]; \
+ (a)->addr_data32[3] = (p)->ip6h->s_ip6_src[3]; \
+ } while (0)
+
+#define SET_IPV6_DST_ADDR(p, a) do { \
+ (a)->family = AF_INET6; \
+ (a)->addr_data32[0] = (p)->ip6h->s_ip6_dst[0]; \
+ (a)->addr_data32[1] = (p)->ip6h->s_ip6_dst[1]; \
+ (a)->addr_data32[2] = (p)->ip6h->s_ip6_dst[2]; \
+ (a)->addr_data32[3] = (p)->ip6h->s_ip6_dst[3]; \
+ } while (0)
+
+/* Set the TCP ports into the Ports of the Packet.
+ * Make sure p->tcph is initialized and validated. */
+#define SET_TCP_SRC_PORT(pkt, prt) do { \
+ SET_PORT(TCP_GET_SRC_PORT((pkt)), *(prt)); \
+ } while (0)
+
+#define SET_TCP_DST_PORT(pkt, prt) do { \
+ SET_PORT(TCP_GET_DST_PORT((pkt)), *(prt)); \
+ } while (0)
+
+/* Set the UDP ports into the Ports of the Packet.
+ * Make sure p->udph is initialized and validated. */
+#define SET_UDP_SRC_PORT(pkt, prt) do { \
+ SET_PORT(UDP_GET_SRC_PORT((pkt)), *(prt)); \
+ } while (0)
+#define SET_UDP_DST_PORT(pkt, prt) do { \
+ SET_PORT(UDP_GET_DST_PORT((pkt)), *(prt)); \
+ } while (0)
+
+/* Set the SCTP ports into the Ports of the Packet.
+ * Make sure p->sctph is initialized and validated. */
+#define SET_SCTP_SRC_PORT(pkt, prt) do { \
+ SET_PORT(SCTP_GET_SRC_PORT((pkt)), *(prt)); \
+ } while (0)
+
+#define SET_SCTP_DST_PORT(pkt, prt) do { \
+ SET_PORT(SCTP_GET_DST_PORT((pkt)), *(prt)); \
+ } while (0)
+
+
+
+#define GET_IPV4_SRC_ADDR_U32(p) ((p)->src.addr_data32[0])
+#define GET_IPV4_DST_ADDR_U32(p) ((p)->dst.addr_data32[0])
+#define GET_IPV4_SRC_ADDR_PTR(p) ((p)->src.addr_data32)
+#define GET_IPV4_DST_ADDR_PTR(p) ((p)->dst.addr_data32)
+
+#define GET_IPV6_SRC_ADDR(p) ((p)->src.addr_data32)
+#define GET_IPV6_DST_ADDR(p) ((p)->dst.addr_data32)
+#define GET_TCP_SRC_PORT(p) ((p)->sp)
+#define GET_TCP_DST_PORT(p) ((p)->dp)
+
+#define GET_PKT_LEN(p) ((p)->pktlen)
+#define GET_PKT_DATA(p) ((((p)->ext_pkt) == NULL ) ? (uint8_t *)((p) + 1) : (p)->ext_pkt)
+#define GET_PKT_DIRECT_DATA(p) (uint8_t *)((p) + 1)
+#define GET_PKT_DIRECT_MAX_SIZE(p) (default_packet_size)
+
+#define SET_PKT_LEN(p, len) do { \
+ (p)->pktlen = (len); \
+ } while (0)
+
+
+/* Port is just a uint16_t */
+typedef uint16_t Port;
+#define SET_PORT(v, p) ((p) = (v))
+#define COPY_PORT(a,b) ((b) = (a))
+
+#define CMP_ADDR(a1, a2) \
+ (((a1)->addr_data32[3] == (a2)->addr_data32[3] && \
+ (a1)->addr_data32[2] == (a2)->addr_data32[2] && \
+ (a1)->addr_data32[1] == (a2)->addr_data32[1] && \
+ (a1)->addr_data32[0] == (a2)->addr_data32[0]))
+#define CMP_PORT(p1, p2) \
+ ((p1) == (p2))
+
+/*Given a packet pkt offset to the start of the ip header in a packet
+ *We determine the ip version. */
+#define IP_GET_RAW_VER(pkt) ((((pkt)[0] & 0xf0) >> 4))
+
+#define PKT_IS_IPV4(p) (((p)->ip4h != NULL))
+#define PKT_IS_IPV6(p) (((p)->ip6h != NULL))
+#define PKT_IS_TCP(p) (((p)->tcph != NULL))
+#define PKT_IS_UDP(p) (((p)->udph != NULL))
+#define PKT_IS_ICMPV4(p) (((p)->icmpv4h != NULL))
+#define PKT_IS_ICMPV6(p) (((p)->icmpv6h != NULL))
+#define PKT_IS_TOSERVER(p) (((p)->flowflags & FLOW_PKT_TOSERVER))
+#define PKT_IS_TOCLIENT(p) (((p)->flowflags & FLOW_PKT_TOCLIENT))
+
+#define IPH_IS_VALID(p) (PKT_IS_IPV4((p)) || PKT_IS_IPV6((p)))
+
+/* Retrieve proto regardless of IP version */
+#define IP_GET_IPPROTO(p) \
+ (p->proto ? p->proto : \
+ (PKT_IS_IPV4((p))? IPV4_GET_IPPROTO((p)) : (PKT_IS_IPV6((p))? IPV6_GET_L4PROTO((p)) : 0)))
+
+/* structure to store the sids/gids/etc the detection engine
+ * found in this packet */
+typedef struct PacketAlert_ {
+ SigIntId num; /* Internal num, used for sorting */
+ uint8_t action; /* Internal num, used for sorting */
+ uint8_t flags;
+ struct Signature_ *s;
+ uint64_t tx_id;
+} PacketAlert;
+
+/** After processing an alert by the thresholding module, if at
+ * last it gets triggered, we might want to stick the drop action to
+ * the flow on IPS mode */
+#define PACKET_ALERT_FLAG_DROP_FLOW 0x01
+/** alert was generated based on state */
+#define PACKET_ALERT_FLAG_STATE_MATCH 0x02
+/** alert was generated based on stream */
+#define PACKET_ALERT_FLAG_STREAM_MATCH 0x04
+/** alert is in a tx, tx_id set */
+#define PACKET_ALERT_FLAG_TX 0x08
+
+#define PACKET_ALERT_MAX 15
+
+typedef struct PacketAlerts_ {
+ uint16_t cnt;
+ PacketAlert alerts[PACKET_ALERT_MAX];
+ /* single pa used when we're dropping,
+ * so we can log it out in the drop log. */
+ PacketAlert drop;
+} PacketAlerts;
+
+/** number of decoder events we support per packet. Power of 2 minus 1
+ * for memory layout */
+#define PACKET_ENGINE_EVENT_MAX 15
+
+/** data structure to store decoder, defrag and stream events */
+typedef struct PacketEngineEvents_ {
+ uint8_t cnt; /**< number of events */
+ uint8_t events[PACKET_ENGINE_EVENT_MAX]; /**< array of events */
+} PacketEngineEvents;
+
+typedef struct PktVar_ {
+ char *name;
+ struct PktVar_ *next; /* right now just implement this as a list,
+ * in the long run we have thing of something
+ * faster. */
+ uint8_t *value;
+ uint16_t value_len;
+} PktVar;
+
+#ifdef PROFILING
+
+/** \brief Per TMM stats storage */
+typedef struct PktProfilingTmmData_ {
+ uint64_t ticks_start;
+ uint64_t ticks_end;
+#ifdef PROFILE_LOCKING
+ uint64_t mutex_lock_cnt;
+ uint64_t mutex_lock_wait_ticks;
+ uint64_t mutex_lock_contention;
+ uint64_t spin_lock_cnt;
+ uint64_t spin_lock_wait_ticks;
+ uint64_t spin_lock_contention;
+ uint64_t rww_lock_cnt;
+ uint64_t rww_lock_wait_ticks;
+ uint64_t rww_lock_contention;
+ uint64_t rwr_lock_cnt;
+ uint64_t rwr_lock_wait_ticks;
+ uint64_t rwr_lock_contention;
+#endif
+} PktProfilingTmmData;
+
+typedef struct PktProfilingDetectData_ {
+ uint64_t ticks_start;
+ uint64_t ticks_end;
+ uint64_t ticks_spent;
+} PktProfilingDetectData;
+
+typedef struct PktProfilingAppData_ {
+ uint64_t ticks_spent;
+} PktProfilingAppData;
+
+/** \brief Per pkt stats storage */
+typedef struct PktProfiling_ {
+ uint64_t ticks_start;
+ uint64_t ticks_end;
+
+ PktProfilingTmmData tmm[TMM_SIZE];
+ PktProfilingAppData app[ALPROTO_MAX];
+ PktProfilingDetectData detect[PROF_DETECT_SIZE];
+ uint64_t proto_detect;
+} PktProfiling;
+
+#endif /* PROFILING */
+
+/* forward declartion since Packet struct definition requires this */
+struct PacketQueue_;
+
+/* sizes of the members:
+ * src: 17 bytes
+ * dst: 17 bytes
+ * sp/type: 1 byte
+ * dp/code: 1 byte
+ * proto: 1 byte
+ * recurs: 1 byte
+ *
+ * sum of above: 38 bytes
+ *
+ * flow ptr: 4/8 bytes
+ * flags: 1 byte
+ * flowflags: 1 byte
+ *
+ * sum of above 44/48 bytes
+ */
+typedef struct Packet_
+{
+ /* Addresses, Ports and protocol
+ * these are on top so we can use
+ * the Packet as a hash key */
+ Address src;
+ Address dst;
+ union {
+ Port sp;
+ uint8_t type;
+ };
+ union {
+ Port dp;
+ uint8_t code;
+ };
+ uint8_t proto;
+ /* make sure we can't be attacked on when the tunneled packet
+ * has the exact same tuple as the lower levels */
+ uint8_t recursion_level;
+
+ uint16_t vlan_id[2];
+ uint8_t vlan_idx;
+
+ /* flow */
+ uint8_t flowflags;
+ /* coccinelle: Packet:flowflags:FLOW_PKT_ */
+
+ /* Pkt Flags */
+ uint32_t flags;
+
+ struct Flow_ *flow;
+
+ struct timeval ts;
+
+ union {
+ /* nfq stuff */
+#ifdef HAVE_NFLOG
+ NFLOGPacketVars nflog_v;
+#endif /* HAVE_NFLOG */
+#ifdef NFQ
+ NFQPacketVars nfq_v;
+#endif /* NFQ */
+#ifdef IPFW
+ IPFWPacketVars ipfw_v;
+#endif /* IPFW */
+#ifdef AF_PACKET
+ AFPPacketVars afp_v;
+#endif
+#ifdef HAVE_MPIPE
+ /* tilegx mpipe stuff */
+ MpipePacketVars mpipe_v;
+#endif
+#ifdef HAVE_NETMAP
+ NetmapPacketVars netmap_v;
+#endif
+
+ /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
+ PcapPacketVars pcap_v;
+ };
+
+ /** The release function for packet structure and data */
+ void (*ReleasePacket)(struct Packet_ *);
+
+ /* pkt vars */
+ PktVar *pktvar;
+
+ /* header pointers */
+ EthernetHdr *ethh;
+
+ /* Checksum for IP packets. */
+ int32_t level3_comp_csum;
+ /* Check sum for TCP, UDP or ICMP packets */
+ int32_t level4_comp_csum;
+
+ IPV4Hdr *ip4h;
+
+ IPV6Hdr *ip6h;
+
+ /* IPv4 and IPv6 are mutually exclusive */
+ union {
+ IPV4Vars ip4vars;
+ struct {
+ IPV6Vars ip6vars;
+ IPV6ExtHdrs ip6eh;
+ };
+ };
+ /* Can only be one of TCP, UDP, ICMP at any given time */
+ union {
+ TCPVars tcpvars;
+ UDPVars udpvars;
+ ICMPV4Vars icmpv4vars;
+ ICMPV6Vars icmpv6vars;
+ };
+
+ TCPHdr *tcph;
+
+ UDPHdr *udph;
+
+ SCTPHdr *sctph;
+
+ ICMPV4Hdr *icmpv4h;
+
+ ICMPV6Hdr *icmpv6h;
+
+ PPPHdr *ppph;
+ PPPOESessionHdr *pppoesh;
+ PPPOEDiscoveryHdr *pppoedh;
+
+ GREHdr *greh;
+
+ VLANHdr *vlanh[2];
+
+ /* ptr to the payload of the packet
+ * with it's length. */
+ uint8_t *payload;
+ uint16_t payload_len;
+
+ /* IPS action to take */
+ uint8_t action;
+
+ uint8_t pkt_src;
+
+ /* storage: set to pointer to heap and extended via allocation if necessary */
+ uint32_t pktlen;
+ uint8_t *ext_pkt;
+
+ /* Incoming interface */
+ struct LiveDevice_ *livedev;
+
+ PacketAlerts alerts;
+
+ struct Host_ *host_src;
+ struct Host_ *host_dst;
+
+ /** packet number in the pcap file, matches wireshark */
+ uint64_t pcap_cnt;
+
+
+ /* engine events */
+ PacketEngineEvents events;
+
+ AppLayerDecoderEvents *app_layer_events;
+
+ /* double linked list ptrs */
+ struct Packet_ *next;
+ struct Packet_ *prev;
+
+ /** data linktype in host order */
+ int datalink;
+
+ /* used to hold flowbits only if debuglog is enabled */
+ int debuglog_flowbits_names_len;
+ const char **debuglog_flowbits_names;
+
+ /* tunnel/encapsulation handling */
+ struct Packet_ *root; /* in case of tunnel this is a ptr
+ * to the 'real' packet, the one we
+ * need to set the verdict on --
+ * It should always point to the lowest
+ * packet in a encapsulated packet */
+
+ /** mutex to protect access to:
+ * - tunnel_rtv_cnt
+ * - tunnel_tpr_cnt
+ */
+ SCMutex tunnel_mutex;
+ /* ready to set verdict counter, only set in root */
+ uint16_t tunnel_rtv_cnt;
+ /* tunnel packet ref count */
+ uint16_t tunnel_tpr_cnt;
+
+ /** tenant id for this packet, if any. If 0 then no tenant was assigned. */
+ uint32_t tenant_id;
+
+ /* The Packet pool from which this packet was allocated. Used when returning
+ * the packet to its owner's stack. If NULL, then allocated with malloc.
+ */
+ struct PktPool_ *pool;
+
+#ifdef PROFILING
+ PktProfiling *profile;
+#endif
+#ifdef __SC_CUDA_SUPPORT__
+ CudaPacketVars cuda_pkt_vars;
+#endif
+}
+#ifdef HAVE_MPIPE
+ /* mPIPE requires packet buffers to be aligned to 128 byte boundaries. */
+ __attribute__((aligned(128)))
+#endif
+Packet;
+
+#define DEFAULT_PACKET_SIZE (1500 + ETHERNET_HEADER_LEN)
+/* storage: maximum ip packet size + link header */
+#define MAX_PAYLOAD_SIZE (IPV6_HEADER_LEN + 65536 + 28)
+uint32_t default_packet_size;
+#define SIZE_OF_PACKET (default_packet_size + sizeof(Packet))
+
+typedef struct PacketQueue_ {
+ Packet *top;
+ Packet *bot;
+ uint32_t len;
+#ifdef DBG_PERF
+ uint32_t dbg_maxlen;
+#endif /* DBG_PERF */
+ SCMutex mutex_q;
+ SCCondT cond_q;
+} PacketQueue;
+
+/** \brief Structure to hold thread specific data for all decode modules */
+typedef struct DecodeThreadVars_
+{
+ /** Specific context for udp protocol detection (here atm) */
+ AppLayerThreadCtx *app_tctx;
+
+ int vlan_disabled;
+
+ /** stats/counters */
+ uint16_t counter_pkts;
+ uint16_t counter_bytes;
+ uint16_t counter_avg_pkt_size;
+ uint16_t counter_max_pkt_size;
+
+ uint16_t counter_invalid;
+
+ uint16_t counter_eth;
+ uint16_t counter_ipv4;
+ uint16_t counter_ipv6;
+ uint16_t counter_tcp;
+ uint16_t counter_udp;
+ uint16_t counter_icmpv4;
+ uint16_t counter_icmpv6;
+
+ uint16_t counter_sll;
+ uint16_t counter_raw;
+ uint16_t counter_null;
+ uint16_t counter_sctp;
+ uint16_t counter_ppp;
+ uint16_t counter_gre;
+ uint16_t counter_vlan;
+ uint16_t counter_vlan_qinq;
+ uint16_t counter_pppoe;
+ uint16_t counter_teredo;
+ uint16_t counter_mpls;
+ uint16_t counter_ipv4inipv6;
+ uint16_t counter_ipv6inipv6;
+ uint16_t counter_erspan;
+
+ /** frag stats - defrag runs in the context of the decoder. */
+ uint16_t counter_defrag_ipv4_fragments;
+ uint16_t counter_defrag_ipv4_reassembled;
+ uint16_t counter_defrag_ipv4_timeouts;
+ uint16_t counter_defrag_ipv6_fragments;
+ uint16_t counter_defrag_ipv6_reassembled;
+ uint16_t counter_defrag_ipv6_timeouts;
+ uint16_t counter_defrag_max_hit;
+
+ /* thread data for flow logging api: only used at forced
+ * flow recycle during lookups */
+ void *output_flow_thread_data;
+
+#ifdef __SC_CUDA_SUPPORT__
+ CudaThreadVars cuda_vars;
+#endif
+} DecodeThreadVars;
+
+typedef struct CaptureStats_ {
+
+ uint16_t counter_ips_accepted;
+ uint16_t counter_ips_blocked;
+ uint16_t counter_ips_rejected;
+ uint16_t counter_ips_replaced;
+
+} CaptureStats;
+
+void CaptureStatsUpdate(ThreadVars *tv, CaptureStats *s, const Packet *p);
+void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s);
+
+/**
+ * \brief reset these to -1(indicates that the packet is fresh from the queue)
+ */
+#define PACKET_RESET_CHECKSUMS(p) do { \
+ (p)->level3_comp_csum = -1; \
+ (p)->level4_comp_csum = -1; \
+ } while (0)
+
+/* if p uses extended data, free them */
+#define PACKET_FREE_EXTDATA(p) do { \
+ if ((p)->ext_pkt) { \
+ if (!((p)->flags & PKT_ZERO_COPY)) { \
+ SCFree((p)->ext_pkt); \
+ } \
+ (p)->ext_pkt = NULL; \
+ } \
+ } while(0)
+
+/**
+ * \brief Initialize a packet structure for use.
+ */
+#ifdef __SC_CUDA_SUPPORT__
+#include "util-cuda-handlers.h"
+#include "util-mpm.h"
+
+#define PACKET_INITIALIZE(p) do { \
+ memset((p), 0x00, SIZE_OF_PACKET); \
+ SCMutexInit(&(p)->tunnel_mutex, NULL); \
+ PACKET_RESET_CHECKSUMS((p)); \
+ (p)->livedev = NULL; \
+ SCMutexInit(&(p)->cuda_pkt_vars.cuda_mutex, NULL); \
+ SCCondInit(&(p)->cuda_pkt_vars.cuda_cond, NULL); \
+ } while (0)
+#else
+#define PACKET_INITIALIZE(p) { \
+ SCMutexInit(&(p)->tunnel_mutex, NULL); \
+ PACKET_RESET_CHECKSUMS((p)); \
+ (p)->livedev = NULL; \
+}
+#endif
+
+#define PACKET_RELEASE_REFS(p) do { \
+ FlowDeReference(&((p)->flow)); \
+ HostDeReference(&((p)->host_src)); \
+ HostDeReference(&((p)->host_dst)); \
+ } while (0)
+
+/**
+ * \brief Recycle a packet structure for reuse.
+ */
+#define PACKET_REINIT(p) do { \
+ CLEAR_ADDR(&(p)->src); \
+ CLEAR_ADDR(&(p)->dst); \
+ (p)->sp = 0; \
+ (p)->dp = 0; \
+ (p)->proto = 0; \
+ (p)->recursion_level = 0; \
+ PACKET_FREE_EXTDATA((p)); \
+ (p)->flags = (p)->flags & PKT_ALLOC; \
+ (p)->flowflags = 0; \
+ (p)->pkt_src = 0; \
+ (p)->vlan_id[0] = 0; \
+ (p)->vlan_id[1] = 0; \
+ (p)->vlan_idx = 0; \
+ (p)->ts.tv_sec = 0; \
+ (p)->ts.tv_usec = 0; \
+ (p)->datalink = 0; \
+ (p)->action = 0; \
+ if ((p)->pktvar != NULL) { \
+ PktVarFree((p)->pktvar); \
+ (p)->pktvar = NULL; \
+ } \
+ (p)->ethh = NULL; \
+ if ((p)->ip4h != NULL) { \
+ CLEAR_IPV4_PACKET((p)); \
+ } \
+ if ((p)->ip6h != NULL) { \
+ CLEAR_IPV6_PACKET((p)); \
+ } \
+ if ((p)->tcph != NULL) { \
+ CLEAR_TCP_PACKET((p)); \
+ } \
+ if ((p)->udph != NULL) { \
+ CLEAR_UDP_PACKET((p)); \
+ } \
+ if ((p)->sctph != NULL) { \
+ CLEAR_SCTP_PACKET((p)); \
+ } \
+ if ((p)->icmpv4h != NULL) { \
+ CLEAR_ICMPV4_PACKET((p)); \
+ } \
+ if ((p)->icmpv6h != NULL) { \
+ CLEAR_ICMPV6_PACKET((p)); \
+ } \
+ (p)->ppph = NULL; \
+ (p)->pppoesh = NULL; \
+ (p)->pppoedh = NULL; \
+ (p)->greh = NULL; \
+ (p)->vlanh[0] = NULL; \
+ (p)->vlanh[1] = NULL; \
+ (p)->payload = NULL; \
+ (p)->payload_len = 0; \
+ (p)->pktlen = 0; \
+ (p)->alerts.cnt = 0; \
+ (p)->alerts.drop.action = 0; \
+ (p)->pcap_cnt = 0; \
+ (p)->tunnel_rtv_cnt = 0; \
+ (p)->tunnel_tpr_cnt = 0; \
+ (p)->events.cnt = 0; \
+ AppLayerDecoderEventsResetEvents((p)->app_layer_events); \
+ (p)->next = NULL; \
+ (p)->prev = NULL; \
+ (p)->root = NULL; \
+ (p)->livedev = NULL; \
+ PACKET_RESET_CHECKSUMS((p)); \
+ PACKET_PROFILING_RESET((p)); \
+ p->tenant_id = 0; \
+ } while (0)
+
+#define PACKET_RECYCLE(p) do { \
+ PACKET_RELEASE_REFS((p)); \
+ PACKET_REINIT((p)); \
+ } while (0)
+
+/**
+ * \brief Cleanup a packet so that we can free it. No memset needed..
+ */
+#define PACKET_DESTRUCTOR(p) do { \
+ if ((p)->pktvar != NULL) { \
+ PktVarFree((p)->pktvar); \
+ } \
+ PACKET_FREE_EXTDATA((p)); \
+ SCMutexDestroy(&(p)->tunnel_mutex); \
+ AppLayerDecoderEventsFreeEvents(&(p)->app_layer_events); \
+ PACKET_PROFILING_RESET((p)); \
+ } while (0)
+
+
+/* macro's for setting the action
+ * handle the case of a root packet
+ * for tunnels */
+
+#define PACKET_SET_ACTION(p, a) do { \
+ ((p)->root ? \
+ ((p)->root->action = a) : \
+ ((p)->action = a)); \
+} while (0)
+
+#define PACKET_ALERT(p) PACKET_SET_ACTION(p, ACTION_ALERT)
+
+#define PACKET_ACCEPT(p) PACKET_SET_ACTION(p, ACTION_ACCEPT)
+
+#define PACKET_DROP(p) PACKET_SET_ACTION(p, ACTION_DROP)
+
+#define PACKET_REJECT(p) PACKET_SET_ACTION(p, (ACTION_REJECT|ACTION_DROP))
+
+#define PACKET_REJECT_DST(p) PACKET_SET_ACTION(p, (ACTION_REJECT_DST|ACTION_DROP))
+
+#define PACKET_REJECT_BOTH(p) PACKET_SET_ACTION(p, (ACTION_REJECT_BOTH|ACTION_DROP))
+
+#define PACKET_PASS(p) PACKET_SET_ACTION(p, ACTION_PASS)
+
+#define PACKET_TEST_ACTION(p, a) \
+ ((p)->root ? \
+ ((p)->root->action & a) : \
+ ((p)->action & a))
+
+#define PACKET_UPDATE_ACTION(p, a) do { \
+ ((p)->root ? \
+ ((p)->root->action |= a) : \
+ ((p)->action |= a)); \
+} while (0)
+
+#define TUNNEL_INCR_PKT_RTV(p) do { \
+ SCMutexLock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ ((p)->root ? (p)->root->tunnel_rtv_cnt++ : (p)->tunnel_rtv_cnt++); \
+ SCMutexUnlock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ } while (0)
+
+#define TUNNEL_INCR_PKT_TPR(p) do { \
+ SCMutexLock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ ((p)->root ? (p)->root->tunnel_tpr_cnt++ : (p)->tunnel_tpr_cnt++); \
+ SCMutexUnlock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ } while (0)
+
+#define TUNNEL_DECR_PKT_TPR(p) do { \
+ SCMutexLock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ ((p)->root ? (p)->root->tunnel_tpr_cnt-- : (p)->tunnel_tpr_cnt--); \
+ SCMutexUnlock((p)->root ? &(p)->root->tunnel_mutex : &(p)->tunnel_mutex); \
+ } while (0)
+
+#define TUNNEL_DECR_PKT_TPR_NOLOCK(p) do { \
+ ((p)->root ? (p)->root->tunnel_tpr_cnt-- : (p)->tunnel_tpr_cnt--); \
+ } while (0)
+
+#define TUNNEL_PKT_RTV(p) ((p)->root ? (p)->root->tunnel_rtv_cnt : (p)->tunnel_rtv_cnt)
+#define TUNNEL_PKT_TPR(p) ((p)->root ? (p)->root->tunnel_tpr_cnt : (p)->tunnel_tpr_cnt)
+
+#define IS_TUNNEL_PKT(p) (((p)->flags & PKT_TUNNEL))
+#define SET_TUNNEL_PKT(p) ((p)->flags |= PKT_TUNNEL)
+#define UNSET_TUNNEL_PKT(p) ((p)->flags &= ~PKT_TUNNEL)
+#define IS_TUNNEL_ROOT_PKT(p) (IS_TUNNEL_PKT(p) && (p)->root == NULL)
+
+#define IS_TUNNEL_PKT_VERDICTED(p) (((p)->flags & PKT_TUNNEL_VERDICTED))
+#define SET_TUNNEL_PKT_VERDICTED(p) ((p)->flags |= PKT_TUNNEL_VERDICTED)
+
+enum DecodeTunnelProto {
+ DECODE_TUNNEL_ETHERNET,
+ DECODE_TUNNEL_ERSPAN,
+ DECODE_TUNNEL_VLAN,
+ DECODE_TUNNEL_IPV4,
+ DECODE_TUNNEL_IPV6,
+ DECODE_TUNNEL_PPP,
+};
+
+Packet *PacketTunnelPktSetup(ThreadVars *tv, DecodeThreadVars *dtv, Packet *parent,
+ uint8_t *pkt, uint16_t len, enum DecodeTunnelProto proto, PacketQueue *pq);
+Packet *PacketDefragPktSetup(Packet *parent, uint8_t *pkt, uint16_t len, uint8_t proto);
+void PacketDefragPktSetupParent(Packet *parent);
+void DecodeRegisterPerfCounters(DecodeThreadVars *, ThreadVars *);
+Packet *PacketGetFromQueueOrAlloc(void);
+Packet *PacketGetFromAlloc(void);
+void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p);
+void PacketFree(Packet *p);
+void PacketFreeOrRelease(Packet *p);
+int PacketCallocExtPkt(Packet *p, int datalen);
+int PacketCopyData(Packet *p, uint8_t *pktdata, int pktlen);
+int PacketSetData(Packet *p, uint8_t *pktdata, int pktlen);
+int PacketCopyDataOffset(Packet *p, int offset, uint8_t *data, int datalen);
+const char *PktSrcToString(enum PktSrcEnum pkt_src);
+
+DecodeThreadVars *DecodeThreadVarsAlloc(ThreadVars *);
+void DecodeThreadVarsFree(ThreadVars *, DecodeThreadVars *);
+void DecodeUpdatePacketCounters(ThreadVars *tv,
+ const DecodeThreadVars *dtv, const Packet *p);
+
+/* decoder functions */
+int DecodeEthernet(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeSll(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodePPP(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodePPPOESession(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodePPPOEDiscovery(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeTunnel(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *, enum DecodeTunnelProto) __attribute__ ((warn_unused_result));
+int DecodeNull(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeRaw(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeIPV4(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeIPV6(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeICMPV4(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeICMPV6(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeTCP(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeUDP(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeSCTP(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeGRE(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeVLAN(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeMPLS(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+int DecodeERSPAN(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+
+void AddressDebugPrint(Address *);
+
+/** \brief Set the No payload inspection Flag for the packet.
+ *
+ * \param p Packet to set the flag in
+ */
+#define DecodeSetNoPayloadInspectionFlag(p) do { \
+ (p)->flags |= PKT_NOPAYLOAD_INSPECTION; \
+ } while (0)
+
+#define DecodeUnsetNoPayloadInspectionFlag(p) do { \
+ (p)->flags &= ~PKT_NOPAYLOAD_INSPECTION; \
+ } while (0)
+
+/** \brief Set the No packet inspection Flag for the packet.
+ *
+ * \param p Packet to set the flag in
+ */
+#define DecodeSetNoPacketInspectionFlag(p) do { \
+ (p)->flags |= PKT_NOPACKET_INSPECTION; \
+ } while (0)
+#define DecodeUnsetNoPacketInspectionFlag(p) do { \
+ (p)->flags &= ~PKT_NOPACKET_INSPECTION; \
+ } while (0)
+
+
+#define ENGINE_SET_EVENT(p, e) do { \
+ SCLogDebug("p %p event %d", (p), e); \
+ if ((p)->events.cnt < PACKET_ENGINE_EVENT_MAX) { \
+ (p)->events.events[(p)->events.cnt] = e; \
+ (p)->events.cnt++; \
+ } \
+} while(0)
+
+#define ENGINE_SET_INVALID_EVENT(p, e) do { \
+ p->flags |= PKT_IS_INVALID; \
+ ENGINE_SET_EVENT(p, e); \
+} while(0)
+
+
+
+#define ENGINE_ISSET_EVENT(p, e) ({ \
+ int r = 0; \
+ uint8_t u; \
+ for (u = 0; u < (p)->events.cnt; u++) { \
+ if ((p)->events.events[u] == (e)) { \
+ r = 1; \
+ break; \
+ } \
+ } \
+ r; \
+})
+
+/* older libcs don't contain a def for IPPROTO_DCCP
+ * inside of <netinet/in.h>
+ * if it isn't defined let's define it here.
+ */
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33
+#endif
+
+/* older libcs don't contain a def for IPPROTO_SCTP
+ * inside of <netinet/in.h>
+ * if it isn't defined let's define it here.
+ */
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+#ifndef IPPROTO_MH
+#define IPPROTO_MH 135
+#endif
+
+/* Host Identity Protocol (rfc 5201) */
+#ifndef IPPROTO_HIP
+#define IPPROTO_HIP 139
+#endif
+
+#ifndef IPPROTO_SHIM6
+#define IPPROTO_SHIM6 140
+#endif
+
+/* pcap provides this, but we don't want to depend on libpcap */
+#ifndef DLT_EN10MB
+#define DLT_EN10MB 1
+#endif
+
+/* taken from pcap's bpf.h */
+#ifndef DLT_RAW
+#ifdef __OpenBSD__
+#define DLT_RAW 14 /* raw IP */
+#else
+#define DLT_RAW 12 /* raw IP */
+#endif
+#endif
+
+#ifndef DLT_NULL
+#define DLT_NULL 0
+#endif
+
+/** libpcap shows us the way to linktype codes
+ * \todo we need more & maybe put them in a separate file? */
+#define LINKTYPE_NULL DLT_NULL
+#define LINKTYPE_ETHERNET DLT_EN10MB
+#define LINKTYPE_LINUX_SLL 113
+#define LINKTYPE_PPP 9
+#define LINKTYPE_RAW DLT_RAW
+#define PPP_OVER_GRE 11
+#define VLAN_OVER_GRE 13
+
+/*Packet Flags*/
+#define PKT_NOPACKET_INSPECTION (1) /**< Flag to indicate that packet header or contents should not be inspected*/
+#define PKT_NOPAYLOAD_INSPECTION (1<<2) /**< Flag to indicate that packet contents should not be inspected*/
+#define PKT_ALLOC (1<<3) /**< Packet was alloc'd this run, needs to be freed */
+#define PKT_HAS_TAG (1<<4) /**< Packet has matched a tag */
+#define PKT_STREAM_ADD (1<<5) /**< Packet payload was added to reassembled stream */
+#define PKT_STREAM_EST (1<<6) /**< Packet is part of establised stream */
+#define PKT_STREAM_EOF (1<<7) /**< Stream is in eof state */
+#define PKT_HAS_FLOW (1<<8)
+#define PKT_PSEUDO_STREAM_END (1<<9) /**< Pseudo packet to end the stream */
+#define PKT_STREAM_MODIFIED (1<<10) /**< Packet is modified by the stream engine, we need to recalc the csum and reinject/replace */
+#define PKT_MARK_MODIFIED (1<<11) /**< Packet mark is modified */
+#define PKT_STREAM_NOPCAPLOG (1<<12) /**< Exclude packet from pcap logging as it's part of a stream that has reassembly depth reached. */
+
+#define PKT_TUNNEL (1<<13)
+#define PKT_TUNNEL_VERDICTED (1<<14)
+
+#define PKT_IGNORE_CHECKSUM (1<<15) /**< Packet checksum is not computed (TX packet for example) */
+#define PKT_ZERO_COPY (1<<16) /**< Packet comes from zero copy (ext_pkt must not be freed) */
+
+#define PKT_HOST_SRC_LOOKED_UP (1<<17)
+#define PKT_HOST_DST_LOOKED_UP (1<<18)
+
+#define PKT_IS_FRAGMENT (1<<19) /**< Packet is a fragment */
+#define PKT_IS_INVALID (1<<20)
+#define PKT_PROFILE (1<<21)
+
+/** \brief return 1 if the packet is a pseudo packet */
+#define PKT_IS_PSEUDOPKT(p) ((p)->flags & PKT_PSEUDO_STREAM_END)
+
+#define PKT_SET_SRC(p, src_val) ((p)->pkt_src = src_val)
+
+#endif /* __DECODE_H__ */
+
diff --git a/framework/src/suricata/src/defrag-config.c b/framework/src/suricata/src/defrag-config.c
new file mode 100644
index 00000000..5bc4be36
--- /dev/null
+++ b/framework/src/suricata/src/defrag-config.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "queue.h"
+#include "suricata.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-misc.h"
+#include "defrag-config.h"
+
+static SCRadixTree *defrag_tree = NULL;
+
+static int default_timeout = 0;
+
+static void DefragPolicyFreeUserData(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+static void DefragPolicyAddHostInfo(char *host_ip_range, uint64_t timeout)
+{
+ uint64_t *user_data = NULL;
+
+ if ( (user_data = SCMalloc(sizeof(uint64_t))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error allocating memory. Exiting");
+ exit(EXIT_FAILURE);
+ }
+
+ *user_data = timeout;
+
+ if (strchr(host_ip_range, ':') != NULL) {
+ SCLogDebug("adding ipv6 host %s", host_ip_range);
+ if (SCRadixAddKeyIPV6String(host_ip_range, defrag_tree, (void *)user_data) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "failed to add ipv6 host %s", host_ip_range);
+ }
+ } else {
+ SCLogDebug("adding ipv4 host %s", host_ip_range);
+ if (SCRadixAddKeyIPV4String(host_ip_range, defrag_tree, (void *)user_data) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "failed to add ipv4 host %s", host_ip_range);
+ }
+ }
+}
+
+static int DefragPolicyGetIPv4HostTimeout(uint8_t *ipv4_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch(ipv4_addr, defrag_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+
+ return *((int *)user_data);
+}
+
+static int DefragPolicyGetIPv6HostTimeout(uint8_t *ipv6_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6BestMatch(ipv6_addr, defrag_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+
+ return *((int *)user_data);
+}
+
+int DefragPolicyGetHostTimeout(Packet *p)
+{
+ int timeout = 0;
+
+ if (PKT_IS_IPV4(p))
+ timeout = DefragPolicyGetIPv4HostTimeout((uint8_t *)GET_IPV4_DST_ADDR_PTR(p));
+ else if (PKT_IS_IPV6(p))
+ timeout = DefragPolicyGetIPv6HostTimeout((uint8_t *)GET_IPV6_DST_ADDR(p));
+
+ if (timeout <= 0)
+ timeout = default_timeout;
+
+ return timeout;
+}
+
+static void DefragParseParameters(ConfNode *n)
+{
+ ConfNode *si;
+ uint64_t timeout = 0;
+
+ TAILQ_FOREACH(si, &n->head, next) {
+ if (strcasecmp("timeout", si->name) == 0) {
+ SCLogDebug("timeout value %s", si->val);
+ if (ParseSizeStringU64(si->val, &timeout) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing timeout "
+ "from conf file");
+ }
+ }
+ if (strcasecmp("address", si->name) == 0) {
+ ConfNode *pval;
+ TAILQ_FOREACH(pval, &si->head, next) {
+ DefragPolicyAddHostInfo(pval->val, timeout);
+ }
+ }
+ }
+}
+
+void DefragSetDefaultTimeout(intmax_t timeout)
+{
+ default_timeout = timeout;
+ SCLogDebug("default timeout %d", default_timeout);
+}
+
+void DefragPolicyLoadFromConfig(void)
+{
+ SCEnter();
+
+ defrag_tree = SCRadixCreateRadixTree(DefragPolicyFreeUserData, NULL);
+ if (defrag_tree == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Can't alloc memory for the defrag config tree.");
+ exit(EXIT_FAILURE);
+ }
+
+ ConfNode *server_config = ConfGetNode("defrag.host-config");
+ if (server_config == NULL) {
+ SCLogDebug("failed to read host config");
+ SCReturn;
+ }
+
+ SCLogDebug("configuring host config %p", server_config);
+ ConfNode *sc;
+
+ TAILQ_FOREACH(sc, &server_config->head, next) {
+ ConfNode *p = NULL;
+
+ TAILQ_FOREACH(p, &sc->head, next) {
+ SCLogDebug("parsing configuration for %s", p->name);
+ DefragParseParameters(p);
+ }
+ }
+}
diff --git a/framework/src/suricata/src/defrag-config.h b/framework/src/suricata/src/defrag-config.h
new file mode 100644
index 00000000..a6c086ff
--- /dev/null
+++ b/framework/src/suricata/src/defrag-config.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ */
+
+#ifndef DEFRAG_CONFIG_H_
+#define DEFRAG_CONFIG_H_
+
+void DefragSetDefaultTimeout(intmax_t timeout);
+void DefragPolicyLoadFromConfig(void);
+int DefragPolicyGetHostTimeout(Packet *p);
+
+#endif /* DEFRAG_CONFIG_H_ */
diff --git a/framework/src/suricata/src/defrag-hash.c b/framework/src/suricata/src/defrag-hash.c
new file mode 100644
index 00000000..9cb377e5
--- /dev/null
+++ b/framework/src/suricata/src/defrag-hash.c
@@ -0,0 +1,727 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "defrag-hash.h"
+#include "defrag-queue.h"
+#include "defrag-config.h"
+#include "util-random.h"
+#include "util-byte.h"
+#include "util-misc.h"
+#include "util-hash-lookup3.h"
+
+static DefragTracker *DefragTrackerGetUsedDefragTracker(void);
+
+/** queue with spare tracker */
+static DefragTrackerQueue defragtracker_spare_q;
+
+uint32_t DefragTrackerSpareQueueGetSize(void)
+{
+ return DefragTrackerQueueLen(&defragtracker_spare_q);
+}
+
+void DefragTrackerMoveToSpare(DefragTracker *h)
+{
+ DefragTrackerEnqueue(&defragtracker_spare_q, h);
+ (void) SC_ATOMIC_SUB(defragtracker_counter, 1);
+}
+
+DefragTracker *DefragTrackerAlloc(void)
+{
+ if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
+ return NULL;
+ }
+
+ (void) SC_ATOMIC_ADD(defrag_memuse, sizeof(DefragTracker));
+
+ DefragTracker *dt = SCMalloc(sizeof(DefragTracker));
+ if (unlikely(dt == NULL))
+ goto error;
+
+ memset(dt, 0x00, sizeof(DefragTracker));
+
+ SCMutexInit(&dt->lock, NULL);
+ SC_ATOMIC_INIT(dt->use_cnt);
+ return dt;
+
+error:
+ return NULL;
+}
+
+void DefragTrackerFree(DefragTracker *dt)
+{
+ if (dt != NULL) {
+ DefragTrackerClearMemory(dt);
+
+ SCMutexDestroy(&dt->lock);
+ SCFree(dt);
+ (void) SC_ATOMIC_SUB(defrag_memuse, sizeof(DefragTracker));
+ }
+}
+
+#define DefragTrackerIncrUsecnt(dt) \
+ SC_ATOMIC_ADD((dt)->use_cnt, 1)
+#define DefragTrackerDecrUsecnt(dt) \
+ SC_ATOMIC_SUB((dt)->use_cnt, 1)
+
+static void DefragTrackerInit(DefragTracker *dt, Packet *p)
+{
+ /* copy address */
+ COPY_ADDRESS(&p->src, &dt->src_addr);
+ COPY_ADDRESS(&p->dst, &dt->dst_addr);
+
+ if (PKT_IS_IPV4(p)) {
+ dt->id = (int32_t)IPV4_GET_IPID(p);
+ dt->af = AF_INET;
+ } else {
+ dt->id = (int32_t)IPV6_EXTHDR_GET_FH_ID(p);
+ dt->af = AF_INET6;
+ }
+ dt->vlan_id[0] = p->vlan_id[0];
+ dt->vlan_id[1] = p->vlan_id[1];
+ dt->policy = DefragGetOsPolicy(p);
+ dt->host_timeout = DefragPolicyGetHostTimeout(p);
+
+ TAILQ_INIT(&dt->frags);
+ (void) DefragTrackerIncrUsecnt(dt);
+}
+
+static DefragTracker *DefragTrackerNew(Packet *p)
+{
+ DefragTracker *dt = DefragTrackerAlloc();
+ if (dt == NULL)
+ goto error;
+
+ DefragTrackerInit(dt, p);
+ return dt;
+
+error:
+ return NULL;
+}
+
+void DefragTrackerRelease(DefragTracker *t)
+{
+ (void) DefragTrackerDecrUsecnt(t);
+ SCMutexUnlock(&t->lock);
+}
+
+void DefragTrackerClearMemory(DefragTracker *dt)
+{
+ DefragTrackerFreeFrags(dt);
+ SC_ATOMIC_DESTROY(dt->use_cnt);
+}
+
+#define DEFRAG_DEFAULT_HASHSIZE 4096
+#define DEFRAG_DEFAULT_MEMCAP 16777216
+#define DEFRAG_DEFAULT_PREALLOC 1000
+
+/** \brief initialize the configuration
+ * \warning Not thread safe */
+void DefragInitConfig(char quiet)
+{
+ SCLogDebug("initializing defrag engine...");
+
+ memset(&defrag_config, 0, sizeof(defrag_config));
+ //SC_ATOMIC_INIT(flow_flags);
+ SC_ATOMIC_INIT(defragtracker_counter);
+ SC_ATOMIC_INIT(defrag_memuse);
+ SC_ATOMIC_INIT(defragtracker_prune_idx);
+ DefragTrackerQueueInit(&defragtracker_spare_q);
+
+ unsigned int seed = RandomTimePreseed();
+ /* set defaults */
+ defrag_config.hash_rand = (int)(DEFRAG_DEFAULT_HASHSIZE * (rand_r(&seed) / RAND_MAX + 1.0));
+
+ defrag_config.hash_size = DEFRAG_DEFAULT_HASHSIZE;
+ defrag_config.memcap = DEFRAG_DEFAULT_MEMCAP;
+ defrag_config.prealloc = DEFRAG_DEFAULT_PREALLOC;
+
+ /* Check if we have memcap and hash_size defined at config */
+ char *conf_val;
+ uint32_t configval = 0;
+
+ /** set config values for memcap, prealloc and hash_size */
+ if ((ConfGet("defrag.memcap", &conf_val)) == 1)
+ {
+ if (ParseSizeStringU64(conf_val, &defrag_config.memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing defrag.memcap "
+ "from conf file - %s. Killing engine",
+ conf_val);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if ((ConfGet("defrag.hash-size", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ defrag_config.hash_size = configval;
+ } else {
+ WarnInvalidConfEntry("defrag.hash-size", "%"PRIu32, defrag_config.hash_size);
+ }
+ }
+
+
+ if ((ConfGet("defrag.trackers", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ defrag_config.prealloc = configval;
+ } else {
+ WarnInvalidConfEntry("defrag.trackers", "%"PRIu32, defrag_config.prealloc);
+ }
+ }
+ SCLogDebug("DefragTracker config from suricata.yaml: memcap: %"PRIu64", hash-size: "
+ "%"PRIu32", prealloc: %"PRIu32, defrag_config.memcap,
+ defrag_config.hash_size, defrag_config.prealloc);
+
+ /* alloc hash memory */
+ uint64_t hash_size = defrag_config.hash_size * sizeof(DefragTrackerHashRow);
+ if (!(DEFRAG_CHECK_MEMCAP(hash_size))) {
+ SCLogError(SC_ERR_DEFRAG_INIT, "allocating defrag hash failed: "
+ "max defrag memcap is smaller than projected hash size. "
+ "Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate "
+ "total hash size by multiplying \"defrag.hash-size\" with %"PRIuMAX", "
+ "which is the hash bucket size.", defrag_config.memcap, hash_size,
+ (uintmax_t)sizeof(DefragTrackerHashRow));
+ exit(EXIT_FAILURE);
+ }
+ defragtracker_hash = SCCalloc(defrag_config.hash_size, sizeof(DefragTrackerHashRow));
+ if (unlikely(defragtracker_hash == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in DefragTrackerInitConfig. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(defragtracker_hash, 0, defrag_config.hash_size * sizeof(DefragTrackerHashRow));
+
+ uint32_t i = 0;
+ for (i = 0; i < defrag_config.hash_size; i++) {
+ DRLOCK_INIT(&defragtracker_hash[i]);
+ }
+ (void) SC_ATOMIC_ADD(defrag_memuse, (defrag_config.hash_size * sizeof(DefragTrackerHashRow)));
+
+ if (quiet == FALSE) {
+ SCLogInfo("allocated %llu bytes of memory for the defrag hash... "
+ "%" PRIu32 " buckets of size %" PRIuMAX "",
+ SC_ATOMIC_GET(defrag_memuse), defrag_config.hash_size,
+ (uintmax_t)sizeof(DefragTrackerHashRow));
+ }
+
+ if ((ConfGet("defrag.prealloc", &conf_val)) == 1)
+ {
+ if (ConfValIsTrue(conf_val)) {
+ /* pre allocate defrag trackers */
+ for (i = 0; i < defrag_config.prealloc; i++) {
+ if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
+ SCLogError(SC_ERR_DEFRAG_INIT, "preallocating defrag trackers failed: "
+ "max defrag memcap reached. Memcap %"PRIu64", "
+ "Memuse %"PRIu64".", defrag_config.memcap,
+ ((uint64_t)SC_ATOMIC_GET(defrag_memuse) + (uint64_t)sizeof(DefragTracker)));
+ exit(EXIT_FAILURE);
+ }
+
+ DefragTracker *h = DefragTrackerAlloc();
+ if (h == NULL) {
+ SCLogError(SC_ERR_DEFRAG_INIT, "preallocating defrag failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ DefragTrackerEnqueue(&defragtracker_spare_q,h);
+ }
+ if (quiet == FALSE) {
+ SCLogInfo("preallocated %" PRIu32 " defrag trackers of size %" PRIuMAX "",
+ defragtracker_spare_q.len, (uintmax_t)sizeof(DefragTracker));
+ }
+ }
+ }
+
+ if (quiet == FALSE) {
+ SCLogInfo("defrag memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(defrag_memuse), defrag_config.memcap);
+ }
+
+ return;
+}
+
+/** \brief print some defrag stats
+ * \warning Not thread safe */
+static void DefragTrackerPrintStats (void)
+{
+}
+
+/** \brief shutdown the flow engine
+ * \warning Not thread safe */
+void DefragHashShutdown(void)
+{
+ DefragTracker *dt;
+ uint32_t u;
+
+ DefragTrackerPrintStats();
+
+ /* free spare queue */
+ while((dt = DefragTrackerDequeue(&defragtracker_spare_q))) {
+ BUG_ON(SC_ATOMIC_GET(dt->use_cnt) > 0);
+ DefragTrackerFree(dt);
+ }
+
+ /* clear and free the hash */
+ if (defragtracker_hash != NULL) {
+ for (u = 0; u < defrag_config.hash_size; u++) {
+ dt = defragtracker_hash[u].head;
+ while (dt) {
+ DefragTracker *n = dt->hnext;
+ DefragTrackerClearMemory(dt);
+ DefragTrackerFree(dt);
+ dt = n;
+ }
+
+ DRLOCK_DESTROY(&defragtracker_hash[u]);
+ }
+ SCFree(defragtracker_hash);
+ defragtracker_hash = NULL;
+ }
+ (void) SC_ATOMIC_SUB(defrag_memuse, defrag_config.hash_size * sizeof(DefragTrackerHashRow));
+ DefragTrackerQueueDestroy(&defragtracker_spare_q);
+
+ SC_ATOMIC_DESTROY(defragtracker_prune_idx);
+ SC_ATOMIC_DESTROY(defrag_memuse);
+ SC_ATOMIC_DESTROY(defragtracker_counter);
+ //SC_ATOMIC_DESTROY(flow_flags);
+ return;
+}
+
+/** \brief compare two raw ipv6 addrs
+ *
+ * \note we don't care about the real ipv6 ip's, this is just
+ * to consistently fill the DefragHashKey6 struct, without all
+ * the ntohl calls.
+ *
+ * \warning do not use elsewhere unless you know what you're doing.
+ * detect-engine-address-ipv6.c's AddressIPv6GtU32 is likely
+ * what you are looking for.
+ */
+static inline int DefragHashRawAddressIPv6GtU32(uint32_t *a, uint32_t *b)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (a[i] > b[i])
+ return 1;
+ if (a[i] < b[i])
+ break;
+ }
+
+ return 0;
+}
+
+typedef struct DefragHashKey4_ {
+ union {
+ struct {
+ uint32_t src, dst;
+ uint32_t id;
+ uint16_t vlan_id[2];
+ };
+ uint32_t u32[4];
+ };
+} DefragHashKey4;
+
+typedef struct DefragHashKey6_ {
+ union {
+ struct {
+ uint32_t src[4], dst[4];
+ uint32_t id;
+ uint16_t vlan_id[2];
+ };
+ uint32_t u32[10];
+ };
+} DefragHashKey6;
+
+/* calculate the hash key for this packet
+ *
+ * we're using:
+ * hash_rand -- set at init time
+ * source address
+ * destination address
+ * id
+ * vlan_id
+ */
+static inline uint32_t DefragHashGetKey(Packet *p)
+{
+ uint32_t key;
+
+ if (p->ip4h != NULL) {
+ DefragHashKey4 dhk;
+ if (p->src.addr_data32[0] > p->dst.addr_data32[0]) {
+ dhk.src = p->src.addr_data32[0];
+ dhk.dst = p->dst.addr_data32[0];
+ } else {
+ dhk.src = p->dst.addr_data32[0];
+ dhk.dst = p->src.addr_data32[0];
+ }
+ dhk.id = (uint32_t)IPV4_GET_IPID(p);
+ dhk.vlan_id[0] = p->vlan_id[0];
+ dhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(dhk.u32, 4, defrag_config.hash_rand);
+ key = hash % defrag_config.hash_size;
+ } else if (p->ip6h != NULL) {
+ DefragHashKey6 dhk;
+ if (DefragHashRawAddressIPv6GtU32(p->src.addr_data32, p->dst.addr_data32)) {
+ dhk.src[0] = p->src.addr_data32[0];
+ dhk.src[1] = p->src.addr_data32[1];
+ dhk.src[2] = p->src.addr_data32[2];
+ dhk.src[3] = p->src.addr_data32[3];
+ dhk.dst[0] = p->dst.addr_data32[0];
+ dhk.dst[1] = p->dst.addr_data32[1];
+ dhk.dst[2] = p->dst.addr_data32[2];
+ dhk.dst[3] = p->dst.addr_data32[3];
+ } else {
+ dhk.src[0] = p->dst.addr_data32[0];
+ dhk.src[1] = p->dst.addr_data32[1];
+ dhk.src[2] = p->dst.addr_data32[2];
+ dhk.src[3] = p->dst.addr_data32[3];
+ dhk.dst[0] = p->src.addr_data32[0];
+ dhk.dst[1] = p->src.addr_data32[1];
+ dhk.dst[2] = p->src.addr_data32[2];
+ dhk.dst[3] = p->src.addr_data32[3];
+ }
+ dhk.id = IPV6_EXTHDR_GET_FH_ID(p);
+ dhk.vlan_id[0] = p->vlan_id[0];
+ dhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(dhk.u32, 10, defrag_config.hash_rand);
+ key = hash % defrag_config.hash_size;
+ } else
+ key = 0;
+
+ return key;
+}
+
+/* Since two or more trackers can have the same hash key, we need to compare
+ * the tracker with the current tracker key. */
+#define CMP_DEFRAGTRACKER(d1,d2,id) \
+ (((CMP_ADDR(&(d1)->src_addr, &(d2)->src) && \
+ CMP_ADDR(&(d1)->dst_addr, &(d2)->dst)) || \
+ (CMP_ADDR(&(d1)->src_addr, &(d2)->dst) && \
+ CMP_ADDR(&(d1)->dst_addr, &(d2)->src))) && \
+ (d1)->id == (id) && \
+ (d1)->vlan_id[0] == (d2)->vlan_id[0] && \
+ (d1)->vlan_id[1] == (d2)->vlan_id[1])
+
+static inline int DefragTrackerCompare(DefragTracker *t, Packet *p)
+{
+ uint32_t id;
+ if (PKT_IS_IPV4(p)) {
+ id = (uint32_t)IPV4_GET_IPID(p);
+ } else {
+ id = IPV6_EXTHDR_GET_FH_ID(p);
+ }
+
+ return CMP_DEFRAGTRACKER(t, p, id);
+}
+
+/**
+ * \brief Get a new defrag tracker
+ *
+ * Get a new defrag tracker. We're checking memcap first and will try to make room
+ * if the memcap is reached.
+ *
+ * \retval dt *LOCKED* tracker on succes, NULL on error.
+ */
+static DefragTracker *DefragTrackerGetNew(Packet *p)
+{
+ DefragTracker *dt = NULL;
+
+ /* get a tracker from the spare queue */
+ dt = DefragTrackerDequeue(&defragtracker_spare_q);
+ if (dt == NULL) {
+ /* If we reached the max memcap, we get a used tracker */
+ if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
+ /* declare state of emergency */
+ //if (!(SC_ATOMIC_GET(defragtracker_flags) & DEFRAG_EMERGENCY)) {
+ // SC_ATOMIC_OR(defragtracker_flags, DEFRAG_EMERGENCY);
+
+ /* under high load, waking up the flow mgr each time leads
+ * to high cpu usage. Flows are not timed out much faster if
+ * we check a 1000 times a second. */
+ // FlowWakeupFlowManagerThread();
+ //}
+
+ dt = DefragTrackerGetUsedDefragTracker();
+ if (dt == NULL) {
+ return NULL;
+ }
+
+ /* freed a tracker, but it's unlocked */
+ } else {
+ /* now see if we can alloc a new tracker */
+ dt = DefragTrackerNew(p);
+ if (dt == NULL) {
+ return NULL;
+ }
+
+ /* tracker is initialized but *unlocked* */
+ }
+ } else {
+ /* tracker has been recycled before it went into the spare queue */
+
+ /* tracker is initialized (recylced) but *unlocked* */
+ }
+
+ (void) SC_ATOMIC_ADD(defragtracker_counter, 1);
+ SCMutexLock(&dt->lock);
+ return dt;
+}
+
+/* DefragGetTrackerFromHash
+ *
+ * Hash retrieval function for trackers. Looks up the hash bucket containing the
+ * tracker pointer. Then compares the packet with the found tracker to see if it is
+ * the tracker we need. If it isn't, walk the list until the right tracker is found.
+ *
+ * returns a *LOCKED* tracker or NULL
+ */
+DefragTracker *DefragGetTrackerFromHash (Packet *p)
+{
+ DefragTracker *dt = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = DefragHashGetKey(p);
+ /* get our hash bucket and lock it */
+ DefragTrackerHashRow *hb = &defragtracker_hash[key];
+ DRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a tracker */
+ if (hb->head == NULL) {
+ dt = DefragTrackerGetNew(p);
+ if (dt == NULL) {
+ DRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+
+ /* tracker is locked */
+ hb->head = dt;
+ hb->tail = dt;
+
+ /* got one, now lock, initialize and return */
+ DefragTrackerInit(dt,p);
+
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+
+ /* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
+ dt = hb->head;
+
+ /* see if this is the tracker we are looking for */
+ if (DefragTrackerCompare(dt, p) == 0) {
+ DefragTracker *pdt = NULL; /* previous tracker */
+
+ while (dt) {
+ pdt = dt;
+ dt = dt->hnext;
+
+ if (dt == NULL) {
+ dt = pdt->hnext = DefragTrackerGetNew(p);
+ if (dt == NULL) {
+ DRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+ hb->tail = dt;
+
+ /* tracker is locked */
+
+ dt->hprev = pdt;
+
+ /* initialize and return */
+ DefragTrackerInit(dt,p);
+
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+
+ if (DefragTrackerCompare(dt, p) != 0) {
+ /* we found our tracker, lets put it on top of the
+ * hash list -- this rewards active trackers */
+ if (dt->hnext) {
+ dt->hnext->hprev = dt->hprev;
+ }
+ if (dt->hprev) {
+ dt->hprev->hnext = dt->hnext;
+ }
+ if (dt == hb->tail) {
+ hb->tail = dt->hprev;
+ }
+
+ dt->hnext = hb->head;
+ dt->hprev = NULL;
+ hb->head->hprev = dt;
+ hb->head = dt;
+
+ /* found our tracker, lock & return */
+ SCMutexLock(&dt->lock);
+ (void) DefragTrackerIncrUsecnt(dt);
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&dt->lock);
+ (void) DefragTrackerIncrUsecnt(dt);
+ DRLOCK_UNLOCK(hb);
+ return dt;
+}
+
+/** \brief look up a tracker in the hash
+ *
+ * \param a address to look up
+ *
+ * \retval h *LOCKED* tracker or NULL
+ */
+DefragTracker *DefragLookupTrackerFromHash (Packet *p)
+{
+ DefragTracker *dt = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = DefragHashGetKey(p);
+ /* get our hash bucket and lock it */
+ DefragTrackerHashRow *hb = &defragtracker_hash[key];
+ DRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a tracker */
+ if (hb->head == NULL) {
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+
+ /* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
+ dt = hb->head;
+
+ /* see if this is the tracker we are looking for */
+ if (DefragTrackerCompare(dt, p) == 0) {
+ while (dt) {
+ dt = dt->hnext;
+
+ if (dt == NULL) {
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+
+ if (DefragTrackerCompare(dt, p) != 0) {
+ /* we found our tracker, lets put it on top of the
+ * hash list -- this rewards active tracker */
+ if (dt->hnext) {
+ dt->hnext->hprev = dt->hprev;
+ }
+ if (dt->hprev) {
+ dt->hprev->hnext = dt->hnext;
+ }
+ if (dt == hb->tail) {
+ hb->tail = dt->hprev;
+ }
+
+ dt->hnext = hb->head;
+ dt->hprev = NULL;
+ hb->head->hprev = dt;
+ hb->head = dt;
+
+ /* found our tracker, lock & return */
+ SCMutexLock(&dt->lock);
+ (void) DefragTrackerIncrUsecnt(dt);
+ DRLOCK_UNLOCK(hb);
+ return dt;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&dt->lock);
+ (void) DefragTrackerIncrUsecnt(dt);
+ DRLOCK_UNLOCK(hb);
+ return dt;
+}
+
+/** \internal
+ * \brief Get a tracker from the hash directly.
+ *
+ * Called in conditions where the spare queue is empty and memcap is reached.
+ *
+ * Walks the hash until a tracker can be freed. "defragtracker_prune_idx" atomic int makes
+ * sure we don't start at the top each time since that would clear the top of
+ * the hash leading to longer and longer search times under high pressure (observed).
+ *
+ * \retval dt tracker or NULL
+ */
+static DefragTracker *DefragTrackerGetUsedDefragTracker(void)
+{
+ uint32_t idx = SC_ATOMIC_GET(defragtracker_prune_idx) % defrag_config.hash_size;
+ uint32_t cnt = defrag_config.hash_size;
+
+ while (cnt--) {
+ if (++idx >= defrag_config.hash_size)
+ idx = 0;
+
+ DefragTrackerHashRow *hb = &defragtracker_hash[idx];
+
+ if (DRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ DefragTracker *dt = hb->tail;
+ if (dt == NULL) {
+ DRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ if (SCMutexTrylock(&dt->lock) != 0) {
+ DRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /** never prune a tracker that is used by a packets
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(dt->use_cnt) > 0) {
+ DRLOCK_UNLOCK(hb);
+ SCMutexUnlock(&dt->lock);
+ continue;
+ }
+
+ /* remove from the hash */
+ if (dt->hprev != NULL)
+ dt->hprev->hnext = dt->hnext;
+ if (dt->hnext != NULL)
+ dt->hnext->hprev = dt->hprev;
+ if (hb->head == dt)
+ hb->head = dt->hnext;
+ if (hb->tail == dt)
+ hb->tail = dt->hprev;
+
+ dt->hnext = NULL;
+ dt->hprev = NULL;
+ DRLOCK_UNLOCK(hb);
+
+ DefragTrackerClearMemory(dt);
+
+ SCMutexUnlock(&dt->lock);
+
+ (void) SC_ATOMIC_ADD(defragtracker_prune_idx, (defrag_config.hash_size - cnt));
+ return dt;
+ }
+
+ return NULL;
+}
+
+
diff --git a/framework/src/suricata/src/defrag-hash.h b/framework/src/suricata/src/defrag-hash.h
new file mode 100644
index 00000000..2d48393a
--- /dev/null
+++ b/framework/src/suricata/src/defrag-hash.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DEFRAG_HASH_H__
+#define __DEFRAG_HASH_H__
+
+#include "decode.h"
+#include "defrag.h"
+
+/** Spinlocks or Mutex for the flow buckets. */
+//#define DRLOCK_SPIN
+#define DRLOCK_MUTEX
+
+#ifdef DRLOCK_SPIN
+ #ifdef DRLOCK_MUTEX
+ #error Cannot enable both DRLOCK_SPIN and DRLOCK_MUTEX
+ #endif
+#endif
+
+#ifdef DRLOCK_SPIN
+ #define DRLOCK_TYPE SCSpinlock
+ #define DRLOCK_INIT(fb) SCSpinInit(&(fb)->lock, 0)
+ #define DRLOCK_DESTROY(fb) SCSpinDestroy(&(fb)->lock)
+ #define DRLOCK_LOCK(fb) SCSpinLock(&(fb)->lock)
+ #define DRLOCK_TRYLOCK(fb) SCSpinTrylock(&(fb)->lock)
+ #define DRLOCK_UNLOCK(fb) SCSpinUnlock(&(fb)->lock)
+#elif defined DRLOCK_MUTEX
+ #define DRLOCK_TYPE SCMutex
+ #define DRLOCK_INIT(fb) SCMutexInit(&(fb)->lock, NULL)
+ #define DRLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->lock)
+ #define DRLOCK_LOCK(fb) SCMutexLock(&(fb)->lock)
+ #define DRLOCK_TRYLOCK(fb) SCMutexTrylock(&(fb)->lock)
+ #define DRLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->lock)
+#else
+ #error Enable DRLOCK_SPIN or DRLOCK_MUTEX
+#endif
+
+typedef struct DefragTrackerHashRow_ {
+ DRLOCK_TYPE lock;
+ DefragTracker *head;
+ DefragTracker *tail;
+} DefragTrackerHashRow;
+
+/** defrag tracker hash table */
+DefragTrackerHashRow *defragtracker_hash;
+
+#define DEFRAG_VERBOSE 0
+#define DEFRAG_QUIET 1
+
+typedef struct DefragConfig_ {
+ uint64_t memcap;
+ uint32_t hash_rand;
+ uint32_t hash_size;
+ uint32_t prealloc;
+} DefragConfig;
+
+/** \brief check if a memory alloc would fit in the memcap
+ *
+ * \param size memory allocation size to check
+ *
+ * \retval 1 it fits
+ * \retval 0 no fit
+ */
+#define DEFRAG_CHECK_MEMCAP(size) \
+ ((((uint64_t)SC_ATOMIC_GET(defrag_memuse) + (uint64_t)(size)) <= defrag_config.memcap))
+
+DefragConfig defrag_config;
+SC_ATOMIC_DECLARE(unsigned long long int,defrag_memuse);
+SC_ATOMIC_DECLARE(unsigned int,defragtracker_counter);
+SC_ATOMIC_DECLARE(unsigned int,defragtracker_prune_idx);
+
+void DefragInitConfig(char quiet);
+void DefragHashShutdown(void);
+
+DefragTracker *DefragLookupTrackerFromHash (Packet *);
+DefragTracker *DefragGetTrackerFromHash (Packet *);
+void DefragTrackerRelease(DefragTracker *);
+void DefragTrackerClearMemory(DefragTracker *);
+void DefragTrackerMoveToSpare(DefragTracker *);
+uint32_t DefragTrackerSpareQueueGetSize(void);
+
+#endif /* __DEFRAG_HASH_H__ */
+
diff --git a/framework/src/suricata/src/defrag-queue.c b/framework/src/suricata/src/defrag-queue.c
new file mode 100644
index 00000000..71dcb932
--- /dev/null
+++ b/framework/src/suricata/src/defrag-queue.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Defrag tracker queue handler functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "defrag-queue.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+DefragTrackerQueue *DefragTrackerQueueInit (DefragTrackerQueue *q)
+{
+ if (q != NULL) {
+ memset(q, 0, sizeof(DefragTrackerQueue));
+ DQLOCK_INIT(q);
+ }
+ return q;
+}
+
+DefragTrackerQueue *DefragTrackerQueueNew()
+{
+ DefragTrackerQueue *q = (DefragTrackerQueue *)SCMalloc(sizeof(DefragTrackerQueue));
+ if (q == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in DefragTrackerQueueNew. Exiting...");
+ exit(EXIT_SUCCESS);
+ }
+ q = DefragTrackerQueueInit(q);
+ return q;
+}
+
+/**
+ * \brief Destroy a tracker queue
+ *
+ * \param q the tracker queue to destroy
+ */
+void DefragTrackerQueueDestroy (DefragTrackerQueue *q)
+{
+ DQLOCK_DESTROY(q);
+}
+
+/**
+ * \brief add a tracker to a queue
+ *
+ * \param q queue
+ * \param dt tracker
+ */
+void DefragTrackerEnqueue (DefragTrackerQueue *q, DefragTracker *dt)
+{
+#ifdef DEBUG
+ BUG_ON(q == NULL || dt == NULL);
+#endif
+
+ DQLOCK_LOCK(q);
+
+ /* more trackers in queue */
+ if (q->top != NULL) {
+ dt->lnext = q->top;
+ q->top->lprev = dt;
+ q->top = dt;
+ /* only tracker */
+ } else {
+ q->top = dt;
+ q->bot = dt;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ DQLOCK_UNLOCK(q);
+}
+
+/**
+ * \brief remove a tracker from the queue
+ *
+ * \param q queue
+ *
+ * \retval dt tracker or NULL if empty list.
+ */
+DefragTracker *DefragTrackerDequeue (DefragTrackerQueue *q)
+{
+ DQLOCK_LOCK(q);
+
+ DefragTracker *dt = q->bot;
+ if (dt == NULL) {
+ DQLOCK_UNLOCK(q);
+ return NULL;
+ }
+
+ /* more packets in queue */
+ if (q->bot->lprev != NULL) {
+ q->bot = q->bot->lprev;
+ q->bot->lnext = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+#ifdef DEBUG
+ BUG_ON(q->len == 0);
+#endif
+ if (q->len > 0)
+ q->len--;
+
+ dt->lnext = NULL;
+ dt->lprev = NULL;
+
+ DQLOCK_UNLOCK(q);
+ return dt;
+}
+
+uint32_t DefragTrackerQueueLen(DefragTrackerQueue *q)
+{
+ uint32_t len;
+ DQLOCK_LOCK(q);
+ len = q->len;
+ DQLOCK_UNLOCK(q);
+ return len;
+}
+
diff --git a/framework/src/suricata/src/defrag-queue.h b/framework/src/suricata/src/defrag-queue.h
new file mode 100644
index 00000000..87b5f4d1
--- /dev/null
+++ b/framework/src/suricata/src/defrag-queue.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DEFRAG_QUEUE_H__
+#define __DEFRAG_QUEUE_H__
+
+#include "suricata-common.h"
+#include "defrag.h"
+
+/** Spinlocks or Mutex for the defrag tracker queues. */
+//#define DQLOCK_SPIN
+#define DQLOCK_MUTEX
+
+#ifdef DQLOCK_SPIN
+ #ifdef DQLOCK_MUTEX
+ #error Cannot enable both DQLOCK_SPIN and DQLOCK_MUTEX
+ #endif
+#endif
+
+/* Define a queue for storing defrag trackers */
+typedef struct DefragTrackerQueue_
+{
+ DefragTracker *top;
+ DefragTracker *bot;
+ uint32_t len;
+#ifdef DBG_PERF
+ uint32_t dbg_maxlen;
+#endif /* DBG_PERF */
+#ifdef DQLOCK_MUTEX
+ SCMutex m;
+#elif defined DQLOCK_SPIN
+ SCSpinlock s;
+#else
+ #error Enable DQLOCK_SPIN or DQLOCK_MUTEX
+#endif
+} DefragTrackerQueue;
+
+#ifdef DQLOCK_SPIN
+ #define DQLOCK_INIT(q) SCSpinInit(&(q)->s, 0)
+ #define DQLOCK_DESTROY(q) SCSpinDestroy(&(q)->s)
+ #define DQLOCK_LOCK(q) SCSpinLock(&(q)->s)
+ #define DQLOCK_TRYLOCK(q) SCSpinTrylock(&(q)->s)
+ #define DQLOCK_UNLOCK(q) SCSpinUnlock(&(q)->s)
+#elif defined DQLOCK_MUTEX
+ #define DQLOCK_INIT(q) SCMutexInit(&(q)->m, NULL)
+ #define DQLOCK_DESTROY(q) SCMutexDestroy(&(q)->m)
+ #define DQLOCK_LOCK(q) SCMutexLock(&(q)->m)
+ #define DQLOCK_TRYLOCK(q) SCMutexTrylock(&(q)->m)
+ #define DQLOCK_UNLOCK(q) SCMutexUnlock(&(q)->m)
+#else
+ #error Enable DQLOCK_SPIN or DQLOCK_MUTEX
+#endif
+
+/* prototypes */
+DefragTrackerQueue *DefragTrackerQueueNew();
+DefragTrackerQueue *DefragTrackerQueueInit(DefragTrackerQueue *);
+void DefragTrackerQueueDestroy (DefragTrackerQueue *);
+
+void DefragTrackerEnqueue (DefragTrackerQueue *, DefragTracker *);
+DefragTracker *DefragTrackerDequeue (DefragTrackerQueue *);
+uint32_t DefragTrackerQueueLen(DefragTrackerQueue *);
+
+#endif /* __DEFRAG_QUEUE_H__ */
+
diff --git a/framework/src/suricata/src/defrag-timeout.c b/framework/src/suricata/src/defrag-timeout.c
new file mode 100644
index 00000000..663ee3b5
--- /dev/null
+++ b/framework/src/suricata/src/defrag-timeout.c
@@ -0,0 +1,153 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "defrag.h"
+#include "defrag-hash.h"
+
+uint32_t DefragTrackerGetSpareCount(void)
+{
+ return DefragTrackerSpareQueueGetSize();
+}
+
+uint32_t DefragTrackerGetActiveCount(void)
+{
+ return SC_ATOMIC_GET(defragtracker_counter);
+}
+
+/** \internal
+ * \brief See if we can really discard this tracker. Check use_cnt reference.
+ *
+ * \param dt tracker
+ * \param ts timestamp
+ *
+ * \retval 0 not timed out just yet
+ * \retval 1 fully timed out, lets kill it
+ */
+static int DefragTrackerTimedOut(DefragTracker *dt, struct timeval *ts)
+{
+ /** never prune a trackers that is used by a packet
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(dt->use_cnt) > 0) {
+ return 0;
+ }
+
+ /* retain if remove is not set and not timed out */
+ if (!dt->remove && timercmp(&dt->timeout, ts, >))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * \internal
+ *
+ * \brief check all trackers in a hash row for timing out
+ *
+ * \param hb tracker hash row *LOCKED*
+ * \param dt last tracker in the hash row
+ * \param ts timestamp
+ *
+ * \retval cnt timed out tracker
+ */
+static uint32_t DefragTrackerHashRowTimeout(DefragTrackerHashRow *hb, DefragTracker *dt, struct timeval *ts)
+{
+ uint32_t cnt = 0;
+
+ do {
+ if (SCMutexTrylock(&dt->lock) != 0) {
+ dt = dt->hprev;
+ continue;
+ }
+
+ DefragTracker *next_dt = dt->hprev;
+
+ /* check if the tracker is fully timed out and
+ * ready to be discarded. */
+ if (DefragTrackerTimedOut(dt, ts) == 1) {
+ /* remove from the hash */
+ if (dt->hprev != NULL)
+ dt->hprev->hnext = dt->hnext;
+ if (dt->hnext != NULL)
+ dt->hnext->hprev = dt->hprev;
+ if (hb->head == dt)
+ hb->head = dt->hnext;
+ if (hb->tail == dt)
+ hb->tail = dt->hprev;
+
+ dt->hnext = NULL;
+ dt->hprev = NULL;
+
+ DefragTrackerClearMemory(dt);
+
+ /* no one is referring to this tracker, use_cnt 0, removed from hash
+ * so we can unlock it and move it back to the spare queue. */
+ SCMutexUnlock(&dt->lock);
+
+ /* move to spare list */
+ DefragTrackerMoveToSpare(dt);
+
+ cnt++;
+ } else {
+ SCMutexUnlock(&dt->lock);
+ }
+
+ dt = next_dt;
+ } while (dt != NULL);
+
+ return cnt;
+}
+
+/**
+ * \brief time out tracker from the hash
+ *
+ * \param ts timestamp
+ *
+ * \retval cnt number of timed out tracker
+ */
+uint32_t DefragTimeoutHash(struct timeval *ts)
+{
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+
+ for (idx = 0; idx < defrag_config.hash_size; idx++) {
+ DefragTrackerHashRow *hb = &defragtracker_hash[idx];
+
+ if (DRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ /* defrag hash bucket is now locked */
+
+ if (hb->tail == NULL) {
+ DRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /* we have a tracker, or more than one */
+ cnt += DefragTrackerHashRowTimeout(hb, hb->tail, ts);
+ DRLOCK_UNLOCK(hb);
+ }
+
+ return cnt;
+}
+
diff --git a/framework/src/suricata/src/defrag-timeout.h b/framework/src/suricata/src/defrag-timeout.h
new file mode 100644
index 00000000..77de4572
--- /dev/null
+++ b/framework/src/suricata/src/defrag-timeout.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DEFRAG_TIMEOUT_H__
+#define __DEFRAG_TIMEOUT_H__
+
+uint32_t DefragTimeoutHash(struct timeval *ts);
+
+uint32_t DefragGetSpareCount(void);
+uint32_t DefragGetActiveCount(void);
+
+#endif
+
diff --git a/framework/src/suricata/src/defrag.c b/framework/src/suricata/src/defrag.c
new file mode 100644
index 00000000..7418b93c
--- /dev/null
+++ b/framework/src/suricata/src/defrag.c
@@ -0,0 +1,2381 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited, Jason Ish <jason.ish@endace.com>
+ *
+ * Defragmentation module.
+ * References:
+ * - RFC 815
+ * - OpenBSD PF's IP normalizaton (pf_norm.c)
+ *
+ * \todo pool for frag packet storage
+ * \todo policy bsd-right
+ * \todo profile hash function
+ * \todo log anomalies
+ */
+
+#include "suricata-common.h"
+
+#include "queue.h"
+
+#include "suricata.h"
+#include "threads.h"
+#include "conf.h"
+#include "decode-ipv6.h"
+#include "util-hashlist.h"
+#include "util-pool.h"
+#include "util-time.h"
+#include "util-print.h"
+#include "util-debug.h"
+#include "util-fix_checksum.h"
+#include "util-random.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "util-host-os-info.h"
+
+#include "defrag.h"
+#include "defrag-hash.h"
+#include "defrag-queue.h"
+#include "defrag-config.h"
+
+#include "tmqh-packetpool.h"
+#include "decode.h"
+
+#ifdef UNITTESTS
+#include "util-unittest.h"
+#endif
+
+#define DEFAULT_DEFRAG_HASH_SIZE 0xffff
+#define DEFAULT_DEFRAG_POOL_SIZE 0xffff
+
+/**
+ * Default timeout (in seconds) before a defragmentation tracker will
+ * be released.
+ */
+#define TIMEOUT_DEFAULT 60
+
+/**
+ * Maximum allowed timeout, 24 hours.
+ */
+#define TIMEOUT_MAX (60 * 60 * 24)
+
+/**
+ * Minimum allowed timeout, 1 second.
+ */
+#define TIMEOUT_MIN 1
+
+/** Fragment reassembly policies. */
+enum defrag_policies {
+ DEFRAG_POLICY_FIRST = 1,
+ DEFRAG_POLICY_LAST,
+ DEFRAG_POLICY_BSD,
+ DEFRAG_POLICY_BSD_RIGHT,
+ DEFRAG_POLICY_LINUX,
+ DEFRAG_POLICY_WINDOWS,
+ DEFRAG_POLICY_SOLARIS,
+
+ DEFRAG_POLICY_DEFAULT = DEFRAG_POLICY_BSD,
+};
+
+static int default_policy = DEFRAG_POLICY_BSD;
+
+/** The global DefragContext so all threads operate from the same
+ * context. */
+static DefragContext *defrag_context;
+
+/**
+ * Utility/debugging function to dump the frags associated with a
+ * tracker. Only enable when unit tests are enabled.
+ */
+#if 0
+#ifdef UNITTESTS
+static void
+DumpFrags(DefragTracker *tracker)
+{
+ Frag *frag;
+
+ printf("Dumping frags for packet: ID=%d\n", tracker->id);
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ printf("-> Frag: frag_offset=%d, frag_len=%d, data_len=%d, ltrim=%d, skip=%d\n", frag->offset, frag->len, frag->data_len, frag->ltrim, frag->skip);
+ PrintRawDataFp(stdout, frag->pkt, frag->len);
+ }
+}
+#endif /* UNITTESTS */
+#endif
+
+/**
+ * \brief Reset a frag for reuse in a pool.
+ */
+static void
+DefragFragReset(Frag *frag)
+{
+ if (frag->pkt != NULL)
+ SCFree(frag->pkt);
+ memset(frag, 0, sizeof(*frag));
+}
+
+/**
+ * \brief Allocate a new frag for use in a pool.
+ */
+static int
+DefragFragInit(void *data, void *initdata)
+{
+ Frag *frag = data;
+
+ memset(frag, 0, sizeof(*frag));
+ return 1;
+}
+
+/**
+ * \brief Free all frags associated with a tracker.
+ */
+void
+DefragTrackerFreeFrags(DefragTracker *tracker)
+{
+ Frag *frag;
+
+ /* Lock the frag pool as we'll be return items to it. */
+ SCMutexLock(&defrag_context->frag_pool_lock);
+
+ while ((frag = TAILQ_FIRST(&tracker->frags)) != NULL) {
+ TAILQ_REMOVE(&tracker->frags, frag, next);
+
+ /* Don't SCFree the frag, just give it back to its pool. */
+ DefragFragReset(frag);
+ PoolReturn(defrag_context->frag_pool, frag);
+ }
+
+ SCMutexUnlock(&defrag_context->frag_pool_lock);
+}
+
+/**
+ * \brief Create a new DefragContext.
+ *
+ * \retval On success a return an initialized DefragContext, otherwise
+ * NULL will be returned.
+ */
+static DefragContext *
+DefragContextNew(void)
+{
+ DefragContext *dc;
+
+ dc = SCCalloc(1, sizeof(*dc));
+ if (unlikely(dc == NULL))
+ return NULL;
+
+ /* Initialize the pool of trackers. */
+ intmax_t tracker_pool_size;
+ if (!ConfGetInt("defrag.trackers", &tracker_pool_size) || tracker_pool_size == 0) {
+ tracker_pool_size = DEFAULT_DEFRAG_HASH_SIZE;
+ }
+
+ /* Initialize the pool of frags. */
+ intmax_t frag_pool_size;
+ if (!ConfGetInt("defrag.max-frags", &frag_pool_size) || frag_pool_size == 0) {
+ frag_pool_size = DEFAULT_DEFRAG_POOL_SIZE;
+ }
+ intmax_t frag_pool_prealloc = frag_pool_size / 2;
+ dc->frag_pool = PoolInit(frag_pool_size, frag_pool_prealloc,
+ sizeof(Frag),
+ NULL, DefragFragInit, dc, NULL, NULL);
+ if (dc->frag_pool == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Defrag: Failed to initialize fragment pool.");
+ exit(EXIT_FAILURE);
+ }
+ if (SCMutexInit(&dc->frag_pool_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX,
+ "Defrag: Failed to initialize frag pool mutex.");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set the default timeout. */
+ intmax_t timeout;
+ if (!ConfGetInt("defrag.timeout", &timeout)) {
+ dc->timeout = TIMEOUT_DEFAULT;
+ }
+ else {
+ if (timeout < TIMEOUT_MIN) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "defrag: Timeout less than minimum allowed value.");
+ exit(EXIT_FAILURE);
+ }
+ else if (timeout > TIMEOUT_MAX) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "defrag: Tiemout greater than maximum allowed value.");
+ exit(EXIT_FAILURE);
+ }
+ dc->timeout = timeout;
+ }
+
+ SCLogDebug("Defrag Initialized:");
+ SCLogDebug("\tTimeout: %"PRIuMAX, (uintmax_t)dc->timeout);
+ SCLogDebug("\tMaximum defrag trackers: %"PRIuMAX, tracker_pool_size);
+ SCLogDebug("\tPreallocated defrag trackers: %"PRIuMAX, tracker_pool_size);
+ SCLogDebug("\tMaximum fragments: %"PRIuMAX, (uintmax_t)frag_pool_size);
+ SCLogDebug("\tPreallocated fragments: %"PRIuMAX, (uintmax_t)frag_pool_prealloc);
+
+ return dc;
+}
+
+static void
+DefragContextDestroy(DefragContext *dc)
+{
+ if (dc == NULL)
+ return;
+
+ PoolFree(dc->frag_pool);
+ SCFree(dc);
+}
+
+/**
+ * Attempt to re-assemble a packet.
+ *
+ * \param tracker The defragmentation tracker to reassemble from.
+ */
+static Packet *
+Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p)
+{
+ Packet *rp = NULL;
+
+ /* Should not be here unless we have seen the last fragment. */
+ if (!tracker->seen_last)
+ return NULL;
+
+ /* Check that we have all the data. Relies on the fact that
+ * fragments are inserted if frag_offset order. */
+ Frag *frag;
+ int len = 0;
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ if (frag->skip)
+ continue;
+
+ if (frag == TAILQ_FIRST(&tracker->frags)) {
+ if (frag->offset != 0) {
+ goto done;
+ }
+ len = frag->data_len;
+ }
+ else {
+ if (frag->offset > len) {
+ /* This fragment starts after the end of the previous
+ * fragment. We have a hole. */
+ goto done;
+ }
+ else {
+ len += frag->data_len;
+ }
+ }
+ }
+
+ /* Allocate a Packet for the reassembled packet. On failure we
+ * SCFree all the resources held by this tracker. */
+ rp = PacketDefragPktSetup(p, NULL, 0, IPV4_GET_IPPROTO(p));
+ if (rp == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate packet for "
+ "fragmentation re-assembly, dumping fragments.");
+ goto error_remove_tracker;
+ }
+ PKT_SET_SRC(rp, PKT_SRC_DEFRAG);
+ rp->recursion_level = p->recursion_level;
+
+ int fragmentable_offset = 0;
+ int fragmentable_len = 0;
+ int hlen = 0;
+ int ip_hdr_offset = 0;
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ SCLogDebug("frag %p, data_len %u, offset %u, pcap_cnt %"PRIu64,
+ frag, frag->data_len, frag->offset, frag->pcap_cnt);
+
+ if (frag->skip)
+ continue;
+ if (frag->data_len - frag->ltrim <= 0)
+ continue;
+ if (frag->offset == 0) {
+
+ if (PacketCopyData(rp, frag->pkt, frag->len) == -1)
+ goto error_remove_tracker;
+
+ hlen = frag->hlen;
+ ip_hdr_offset = frag->ip_hdr_offset;
+
+ /* This is the start of the fragmentable portion of the
+ * first packet. All fragment offsets are relative to
+ * this. */
+ fragmentable_offset = frag->ip_hdr_offset + frag->hlen;
+ fragmentable_len = frag->data_len;
+ }
+ else {
+ int pkt_end = fragmentable_offset + frag->offset + frag->data_len;
+ if (pkt_end > (int)MAX_PAYLOAD_SIZE) {
+ SCLogWarning(SC_ERR_REASSEMBLY, "Failed re-assemble "
+ "fragmented packet, exceeds size of packet buffer.");
+ goto error_remove_tracker;
+ }
+ if (PacketCopyDataOffset(rp, fragmentable_offset + frag->offset + frag->ltrim,
+ frag->pkt + frag->data_offset + frag->ltrim,
+ frag->data_len - frag->ltrim) == -1) {
+ goto error_remove_tracker;
+ }
+ if (frag->offset + frag->data_len > fragmentable_len)
+ fragmentable_len = frag->offset + frag->data_len;
+ }
+ }
+
+ SCLogDebug("ip_hdr_offset %u, hlen %u, fragmentable_len %u",
+ ip_hdr_offset, hlen, fragmentable_len);
+
+ rp->ip4h = (IPV4Hdr *)(GET_PKT_DATA(rp) + ip_hdr_offset);
+ int old = rp->ip4h->ip_len + rp->ip4h->ip_off;
+ rp->ip4h->ip_len = htons(fragmentable_len + hlen);
+ rp->ip4h->ip_off = 0;
+ rp->ip4h->ip_csum = FixChecksum(rp->ip4h->ip_csum,
+ old, rp->ip4h->ip_len + rp->ip4h->ip_off);
+ SET_PKT_LEN(rp, ip_hdr_offset + hlen + fragmentable_len);
+
+ tracker->remove = 1;
+ DefragTrackerFreeFrags(tracker);
+done:
+ return rp;
+
+error_remove_tracker:
+ tracker->remove = 1;
+ DefragTrackerFreeFrags(tracker);
+ if (rp != NULL)
+ PacketFreeOrRelease(rp);
+ return NULL;
+}
+
+/**
+ * Attempt to re-assemble a packet.
+ *
+ * \param tracker The defragmentation tracker to reassemble from.
+ */
+static Packet *
+Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p)
+{
+ Packet *rp = NULL;
+
+ /* Should not be here unless we have seen the last fragment. */
+ if (!tracker->seen_last)
+ return NULL;
+
+ /* Check that we have all the data. Relies on the fact that
+ * fragments are inserted if frag_offset order. */
+ Frag *frag;
+ int len = 0;
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ if (frag->skip)
+ continue;
+
+ if (frag == TAILQ_FIRST(&tracker->frags)) {
+ if (frag->offset != 0) {
+ goto done;
+ }
+ len = frag->data_len;
+ }
+ else {
+ if (frag->offset > len) {
+ /* This fragment starts after the end of the previous
+ * fragment. We have a hole. */
+ goto done;
+ }
+ else {
+ len += frag->data_len;
+ }
+ }
+ }
+
+ /* Allocate a Packet for the reassembled packet. On failure we
+ * SCFree all the resources held by this tracker. */
+ rp = PacketDefragPktSetup(p, (uint8_t *)p->ip6h,
+ IPV6_GET_PLEN(p) + sizeof(IPV6Hdr), 0);
+ if (rp == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate packet for "
+ "fragmentation re-assembly, dumping fragments.");
+ goto error_remove_tracker;
+ }
+ PKT_SET_SRC(rp, PKT_SRC_DEFRAG);
+
+ int unfragmentable_len = 0;
+ int fragmentable_offset = 0;
+ int fragmentable_len = 0;
+ int ip_hdr_offset = 0;
+ uint8_t next_hdr = 0;
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ if (frag->skip)
+ continue;
+ if (frag->data_len - frag->ltrim <= 0)
+ continue;
+ if (frag->offset == 0) {
+ IPV6FragHdr *frag_hdr = (IPV6FragHdr *)(frag->pkt +
+ frag->frag_hdr_offset);
+ next_hdr = frag_hdr->ip6fh_nxt;
+
+ /* This is the first packet, we use this packets link and
+ * IPv6 headers. We also copy in its data, but remove the
+ * fragmentation header. */
+ if (PacketCopyData(rp, frag->pkt, frag->frag_hdr_offset) == -1)
+ goto error_remove_tracker;
+ if (PacketCopyDataOffset(rp, frag->frag_hdr_offset,
+ frag->pkt + frag->frag_hdr_offset + sizeof(IPV6FragHdr),
+ frag->data_len) == -1)
+ goto error_remove_tracker;
+ ip_hdr_offset = frag->ip_hdr_offset;
+
+ /* This is the start of the fragmentable portion of the
+ * first packet. All fragment offsets are relative to
+ * this. */
+ fragmentable_offset = frag->frag_hdr_offset;
+ fragmentable_len = frag->data_len;
+
+ /* unfragmentable part is the part between the ipv6 header
+ * and the frag header. */
+ unfragmentable_len = (fragmentable_offset - ip_hdr_offset) - IPV6_HEADER_LEN;
+ if (unfragmentable_len >= fragmentable_offset)
+ goto error_remove_tracker;
+ }
+ else {
+ if (PacketCopyDataOffset(rp, fragmentable_offset + frag->offset + frag->ltrim,
+ frag->pkt + frag->data_offset + frag->ltrim,
+ frag->data_len - frag->ltrim) == -1)
+ goto error_remove_tracker;
+ if (frag->offset + frag->data_len > fragmentable_len)
+ fragmentable_len = frag->offset + frag->data_len;
+ }
+ }
+
+ rp->ip6h = (IPV6Hdr *)(GET_PKT_DATA(rp) + ip_hdr_offset);
+ rp->ip6h->s_ip6_plen = htons(fragmentable_len + unfragmentable_len);
+ /* if we have no unfragmentable part, so no ext hdrs before the frag
+ * header, we need to update the ipv6 headers next header field. This
+ * points to the frag header, and we will make it point to the layer
+ * directly after the frag header. */
+ if (unfragmentable_len == 0)
+ rp->ip6h->s_ip6_nxt = next_hdr;
+ SET_PKT_LEN(rp, ip_hdr_offset + sizeof(IPV6Hdr) +
+ unfragmentable_len + fragmentable_len);
+
+ tracker->remove = 1;
+ DefragTrackerFreeFrags(tracker);
+done:
+ return rp;
+
+error_remove_tracker:
+ tracker->remove = 1;
+ DefragTrackerFreeFrags(tracker);
+ if (rp != NULL)
+ PacketFreeOrRelease(rp);
+ return NULL;
+}
+
+/**
+ * Insert a new IPv4/IPv6 fragment into a tracker.
+ *
+ * \todo Allocate packet buffers from a pool.
+ */
+static Packet *
+DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, Packet *p, PacketQueue *pq)
+{
+ Packet *r = NULL;
+ int ltrim = 0;
+
+ uint8_t more_frags;
+ uint16_t frag_offset;
+
+ /* IPv4 header length - IPv4 only. */
+ uint16_t hlen = 0;
+
+ /* This is the offset of the start of the data in the packet that
+ * falls after the IP header. */
+ uint16_t data_offset;
+
+ /* The length of the (fragmented) data. This is the length of the
+ * data that falls after the IP header. */
+ uint16_t data_len;
+
+ /* Where the fragment ends. */
+ uint16_t frag_end;
+
+ /* Offset in the packet to the IPv6 header. */
+ uint16_t ip_hdr_offset;
+
+ /* Offset in the packet to the IPv6 frag header. IPv6 only. */
+ uint16_t frag_hdr_offset = 0;
+
+ /* Address family */
+ int af = tracker->af;
+
+ /* settings for updating a payload when an ip6 fragment with
+ * unfragmentable exthdrs are encountered. */
+ int ip6_nh_set_offset = 0;
+ uint8_t ip6_nh_set_value = 0;
+
+#ifdef DEBUG
+ uint64_t pcap_cnt = p->pcap_cnt;
+#endif
+
+ if (tracker->af == AF_INET) {
+ more_frags = IPV4_GET_MF(p);
+ frag_offset = IPV4_GET_IPOFFSET(p) << 3;
+ hlen = IPV4_GET_HLEN(p);
+ data_offset = (uint8_t *)p->ip4h + hlen - GET_PKT_DATA(p);
+ data_len = IPV4_GET_IPLEN(p) - hlen;
+ frag_end = frag_offset + data_len;
+ ip_hdr_offset = (uint8_t *)p->ip4h - GET_PKT_DATA(p);
+
+ /* Ignore fragment if the end of packet extends past the
+ * maximum size of a packet. */
+ if (IPV4_HEADER_LEN + frag_offset + data_len > IPV4_MAXPACKET_LEN) {
+ ENGINE_SET_EVENT(p, IPV4_FRAG_PKT_TOO_LARGE);
+ return NULL;
+ }
+ }
+ else if (tracker->af == AF_INET6) {
+ more_frags = IPV6_EXTHDR_GET_FH_FLAG(p);
+ frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p);
+ data_offset = (uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr) - GET_PKT_DATA(p);
+ data_len = IPV6_GET_PLEN(p) - (
+ ((uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr)) -
+ ((uint8_t *)p->ip6h + sizeof(IPV6Hdr)));
+ frag_end = frag_offset + data_len;
+ ip_hdr_offset = (uint8_t *)p->ip6h - GET_PKT_DATA(p);
+ frag_hdr_offset = (uint8_t *)p->ip6eh.ip6fh - GET_PKT_DATA(p);
+
+ /* handle unfragmentable exthdrs */
+ if (ip_hdr_offset + IPV6_HEADER_LEN < frag_hdr_offset) {
+ SCLogDebug("we have exthdrs before fraghdr %u bytes (%u hdrs total)",
+ (uint32_t)(frag_hdr_offset - (ip_hdr_offset + IPV6_HEADER_LEN)),
+ p->ip6eh.ip6_exthdrs_cnt);
+
+ /* get the offset of the 'next' field in exthdr before the FH,
+ * relative to the buffer start */
+ int t_offset = (int)((p->ip6eh.ip6_exthdrs[p->ip6eh.ip6_exthdrs_cnt - 2].data - 2) - GET_PKT_DATA(p));
+
+ /* store offset and FH 'next' value for updating frag buffer below */
+ ip6_nh_set_offset = (int)t_offset;
+ ip6_nh_set_value = IPV6_EXTHDR_GET_FH_NH(p);
+ SCLogDebug("offset %d, value %u", ip6_nh_set_offset, ip6_nh_set_value);
+ }
+
+ /* Ignore fragment if the end of packet extends past the
+ * maximum size of a packet. */
+ if (frag_offset + data_len > IPV6_MAXPACKET) {
+ ENGINE_SET_EVENT(p, IPV6_FRAG_PKT_TOO_LARGE);
+ return NULL;
+ }
+ }
+ else {
+ /* Abort - should not happen. */
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid address family, aborting.");
+ return NULL;
+ }
+
+ /* Update timeout. */
+ tracker->timeout.tv_sec = p->ts.tv_sec + tracker->host_timeout;
+ tracker->timeout.tv_usec = p->ts.tv_usec;
+
+ Frag *prev = NULL, *next;
+ int overlap = 0;
+ if (!TAILQ_EMPTY(&tracker->frags)) {
+ TAILQ_FOREACH(prev, &tracker->frags, next) {
+ ltrim = 0;
+ next = TAILQ_NEXT(prev, next);
+
+ switch (tracker->policy) {
+ case DEFRAG_POLICY_BSD:
+ if (frag_offset < prev->offset + prev->data_len) {
+ if (frag_offset >= prev->offset) {
+ ltrim = prev->offset + prev->data_len - frag_offset;
+ overlap++;
+ }
+ if ((next != NULL) && (frag_end > next->offset)) {
+ next->ltrim = frag_end - next->offset;
+ overlap++;
+ }
+ if ((frag_offset < prev->offset) &&
+ (frag_end >= prev->offset + prev->data_len)) {
+ prev->skip = 1;
+ overlap++;
+ }
+ goto insert;
+ }
+ break;
+ case DEFRAG_POLICY_LINUX:
+ if (frag_offset < prev->offset + prev->data_len) {
+ if (frag_offset > prev->offset) {
+ ltrim = prev->offset + prev->data_len - frag_offset;
+ overlap++;
+ }
+ if ((next != NULL) && (frag_end > next->offset)) {
+ next->ltrim = frag_end - next->offset;
+ overlap++;
+ }
+ if ((frag_offset < prev->offset) &&
+ (frag_end >= prev->offset + prev->data_len)) {
+ prev->skip = 1;
+ overlap++;
+ }
+ goto insert;
+ }
+ break;
+ case DEFRAG_POLICY_WINDOWS:
+ if (frag_offset < prev->offset + prev->data_len) {
+ if (frag_offset >= prev->offset) {
+ ltrim = prev->offset + prev->data_len - frag_offset;
+ overlap++;
+ }
+ if ((frag_offset < prev->offset) &&
+ (frag_end > prev->offset + prev->data_len)) {
+ prev->skip = 1;
+ overlap++;
+ }
+ goto insert;
+ }
+ break;
+ case DEFRAG_POLICY_SOLARIS:
+ if (frag_offset < prev->offset + prev->data_len) {
+ if (frag_offset >= prev->offset) {
+ ltrim = prev->offset + prev->data_len - frag_offset;
+ overlap++;
+ }
+ if ((frag_offset < prev->offset) &&
+ (frag_end >= prev->offset + prev->data_len)) {
+ prev->skip = 1;
+ overlap++;
+ }
+ goto insert;
+ }
+ break;
+ case DEFRAG_POLICY_FIRST:
+ if ((frag_offset >= prev->offset) &&
+ (frag_end <= prev->offset + prev->data_len)) {
+ overlap++;
+ goto done;
+ }
+ if (frag_offset < prev->offset) {
+ goto insert;
+ }
+ if (frag_offset < prev->offset + prev->data_len) {
+ ltrim = prev->offset + prev->data_len - frag_offset;
+ overlap++;
+ goto insert;
+ }
+ break;
+ case DEFRAG_POLICY_LAST:
+ if (frag_offset <= prev->offset) {
+ if (frag_end > prev->offset) {
+ prev->ltrim = frag_end - prev->offset;
+ overlap++;
+ }
+ goto insert;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+insert:
+ if (data_len - ltrim <= 0) {
+ if (af == AF_INET) {
+ ENGINE_SET_EVENT(p, IPV4_FRAG_TOO_LARGE);
+ } else {
+ ENGINE_SET_EVENT(p, IPV6_FRAG_TOO_LARGE);
+ }
+ goto done;
+ }
+
+ /* Allocate fragment and insert. */
+ SCMutexLock(&defrag_context->frag_pool_lock);
+ Frag *new = PoolGet(defrag_context->frag_pool);
+ SCMutexUnlock(&defrag_context->frag_pool_lock);
+ if (new == NULL) {
+ if (af == AF_INET) {
+ ENGINE_SET_EVENT(p, IPV4_FRAG_IGNORED);
+ } else {
+ ENGINE_SET_EVENT(p, IPV6_FRAG_IGNORED);
+ }
+ goto done;
+ }
+ new->pkt = SCMalloc(GET_PKT_LEN(p));
+ if (new->pkt == NULL) {
+ SCMutexLock(&defrag_context->frag_pool_lock);
+ PoolReturn(defrag_context->frag_pool, new);
+ SCMutexUnlock(&defrag_context->frag_pool_lock);
+ if (af == AF_INET) {
+ ENGINE_SET_EVENT(p, IPV4_FRAG_IGNORED);
+ } else {
+ ENGINE_SET_EVENT(p, IPV6_FRAG_IGNORED);
+ }
+ goto done;
+ }
+ memcpy(new->pkt, GET_PKT_DATA(p) + ltrim, GET_PKT_LEN(p) - ltrim);
+ new->len = GET_PKT_LEN(p) - ltrim;
+ /* in case of unfragmentable exthdrs, update the 'next hdr' field
+ * in the raw buffer so the reassembled packet will point to the
+ * correct next header after stripping the frag header */
+ if (ip6_nh_set_offset > 0 && frag_offset == 0 && ltrim == 0) {
+ if (new->len > ip6_nh_set_offset) {
+ SCLogDebug("updating frag to have 'correct' nh value: %u -> %u",
+ new->pkt[ip6_nh_set_offset], ip6_nh_set_value);
+ new->pkt[ip6_nh_set_offset] = ip6_nh_set_value;
+ }
+ }
+
+ new->hlen = hlen;
+ new->offset = frag_offset + ltrim;
+ new->data_offset = data_offset;
+ new->data_len = data_len - ltrim;
+ new->ip_hdr_offset = ip_hdr_offset;
+ new->frag_hdr_offset = frag_hdr_offset;
+#ifdef DEBUG
+ new->pcap_cnt = pcap_cnt;
+#endif
+
+ Frag *frag;
+ TAILQ_FOREACH(frag, &tracker->frags, next) {
+ if (new->offset < frag->offset)
+ break;
+ }
+ if (frag == NULL) {
+ TAILQ_INSERT_TAIL(&tracker->frags, new, next);
+ }
+ else {
+ TAILQ_INSERT_BEFORE(frag, new, next);
+ }
+
+ if (!more_frags) {
+ tracker->seen_last = 1;
+ }
+
+ if (tracker->seen_last) {
+ if (tracker->af == AF_INET) {
+ r = Defrag4Reassemble(tv, tracker, p);
+ if (r != NULL && tv != NULL && dtv != NULL) {
+ StatsIncr(tv, dtv->counter_defrag_ipv4_reassembled);
+ if (pq && DecodeIPV4(tv, dtv, r, (void *)r->ip4h,
+ IPV4_GET_IPLEN(r), pq) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(tv, r);
+ } else {
+ PacketDefragPktSetupParent(p);
+ }
+ }
+ }
+ else if (tracker->af == AF_INET6) {
+ r = Defrag6Reassemble(tv, tracker, p);
+ if (r != NULL && tv != NULL && dtv != NULL) {
+ StatsIncr(tv, dtv->counter_defrag_ipv6_reassembled);
+ if (pq && DecodeIPV6(tv, dtv, r, (uint8_t *)r->ip6h,
+ IPV6_GET_PLEN(r) + IPV6_HEADER_LEN,
+ pq) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(tv, r);
+ } else {
+ PacketDefragPktSetupParent(p);
+ }
+ }
+ }
+ }
+
+
+done:
+ if (overlap) {
+ if (af == AF_INET) {
+ ENGINE_SET_EVENT(p, IPV4_FRAG_OVERLAP);
+ }
+ else {
+ ENGINE_SET_EVENT(p, IPV6_FRAG_OVERLAP);
+ }
+ }
+ return r;
+}
+
+/**
+ * \brief Get the defrag policy based on the destination address of
+ * the packet.
+ *
+ * \param p The packet used to get the destination address.
+ *
+ * \retval The defrag policy to use.
+ */
+uint8_t
+DefragGetOsPolicy(Packet *p)
+{
+ int policy = -1;
+
+ if (PKT_IS_IPV4(p)) {
+ policy = SCHInfoGetIPv4HostOSFlavour((uint8_t *)GET_IPV4_DST_ADDR_PTR(p));
+ }
+ else if (PKT_IS_IPV6(p)) {
+ policy = SCHInfoGetIPv6HostOSFlavour((uint8_t *)GET_IPV6_DST_ADDR(p));
+ }
+
+ if (policy == -1) {
+ return default_policy;
+ }
+
+ /* Map the OS policies returned from the configured host info to
+ * defrag specific policies. */
+ switch (policy) {
+ /* BSD. */
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ return DEFRAG_POLICY_BSD;
+
+ /* BSD-Right. */
+ case OS_POLICY_BSD_RIGHT:
+ return DEFRAG_POLICY_BSD_RIGHT;
+
+ /* Linux. */
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ return DEFRAG_POLICY_LINUX;
+
+ /* First. */
+ case OS_POLICY_OLD_SOLARIS:
+ case OS_POLICY_HPUX11:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_FIRST:
+ return DEFRAG_POLICY_FIRST;
+
+ /* Solaris. */
+ case OS_POLICY_SOLARIS:
+ return DEFRAG_POLICY_SOLARIS;
+
+ /* Windows. */
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_WINDOWS2K3:
+ return DEFRAG_POLICY_WINDOWS;
+
+ /* Last. */
+ case OS_POLICY_LAST:
+ return DEFRAG_POLICY_LAST;
+
+ default:
+ return default_policy;
+ }
+}
+
+/** \internal
+ *
+ * \retval NULL or a *LOCKED* tracker */
+static DefragTracker *
+DefragGetTracker(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
+{
+ return DefragGetTrackerFromHash(p);
+}
+
+/**
+ * \brief Entry point for IPv4 and IPv6 fragments.
+ *
+ * \param tv ThreadVars for the calling decoder.
+ * \param p The packet fragment.
+ *
+ * \retval A new Packet resembling the re-assembled packet if the most
+ * recent fragment allowed the packet to be re-assembled, otherwise
+ * NULL is returned.
+ */
+Packet *
+Defrag(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, PacketQueue *pq)
+{
+ uint16_t frag_offset;
+ uint8_t more_frags;
+ DefragTracker *tracker;
+ int af;
+
+ if (PKT_IS_IPV4(p)) {
+ af = AF_INET;
+ more_frags = IPV4_GET_MF(p);
+ frag_offset = IPV4_GET_IPOFFSET(p);
+ }
+ else if (PKT_IS_IPV6(p)) {
+ af = AF_INET6;
+ frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p);
+ more_frags = IPV6_EXTHDR_GET_FH_FLAG(p);
+ }
+ else {
+ return NULL;
+ }
+
+ if (frag_offset == 0 && more_frags == 0) {
+ return NULL;
+ }
+
+ if (tv != NULL && dtv != NULL) {
+ if (af == AF_INET) {
+ StatsIncr(tv, dtv->counter_defrag_ipv4_fragments);
+ }
+ else if (af == AF_INET6) {
+ StatsIncr(tv, dtv->counter_defrag_ipv6_fragments);
+ }
+ }
+
+ /* return a locked tracker or NULL */
+ tracker = DefragGetTracker(tv, dtv, p);
+ if (tracker == NULL)
+ return NULL;
+
+ Packet *rp = DefragInsertFrag(tv, dtv, tracker, p, pq);
+ DefragTrackerRelease(tracker);
+
+ return rp;
+}
+
+void
+DefragInit(void)
+{
+ intmax_t tracker_pool_size;
+ if (!ConfGetInt("defrag.trackers", &tracker_pool_size)) {
+ tracker_pool_size = DEFAULT_DEFRAG_HASH_SIZE;
+ }
+
+ /* Load the defrag-per-host lookup. */
+ DefragPolicyLoadFromConfig();
+
+ /* Allocate the DefragContext. */
+ defrag_context = DefragContextNew();
+ if (defrag_context == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for the Defrag module.");
+ exit(EXIT_FAILURE);
+ }
+
+ DefragSetDefaultTimeout(defrag_context->timeout);
+ DefragInitConfig(FALSE);
+}
+
+void DefragDestroy(void)
+{
+ DefragHashShutdown();
+ DefragContextDestroy(defrag_context);
+ defrag_context = NULL;
+}
+
+#ifdef UNITTESTS
+#define IP_MF 0x2000
+
+/**
+ * Allocate a test packet. Nothing to fancy, just a simple IP packet
+ * with some payload of no particular protocol.
+ */
+static Packet *
+BuildTestPacket(uint16_t id, uint16_t off, int mf, const char content,
+ int content_len)
+{
+ Packet *p = NULL;
+ int hlen = 20;
+ int ttl = 64;
+ uint8_t *pcontent;
+ IPV4Hdr ip4h;
+
+ p = SCCalloc(1, sizeof(*p) + default_packet_size);
+ if (unlikely(p == NULL))
+ return NULL;
+
+ PACKET_INITIALIZE(p);
+
+ gettimeofday(&p->ts, NULL);
+ //p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ ip4h.ip_verhl = 4 << 4;
+ ip4h.ip_verhl |= hlen >> 2;
+ ip4h.ip_len = htons(hlen + content_len);
+ ip4h.ip_id = htons(id);
+ ip4h.ip_off = htons(off);
+ if (mf)
+ ip4h.ip_off = htons(IP_MF | off);
+ else
+ ip4h.ip_off = htons(off);
+ ip4h.ip_ttl = ttl;
+ ip4h.ip_proto = IPPROTO_ICMP;
+
+ ip4h.s_ip_src.s_addr = 0x01010101; /* 1.1.1.1 */
+ ip4h.s_ip_dst.s_addr = 0x02020202; /* 2.2.2.2 */
+
+ /* copy content_len crap, we need full length */
+ PacketCopyData(p, (uint8_t *)&ip4h, sizeof(ip4h));
+ p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ SET_IPV4_SRC_ADDR(p, &p->src);
+ SET_IPV4_DST_ADDR(p, &p->dst);
+
+ pcontent = SCCalloc(1, content_len);
+ if (unlikely(pcontent == NULL))
+ return NULL;
+ memset(pcontent, content, content_len);
+ PacketCopyDataOffset(p, hlen, pcontent, content_len);
+ SET_PKT_LEN(p, hlen + content_len);
+ SCFree(pcontent);
+
+ p->ip4h->ip_csum = IPV4CalculateChecksum((uint16_t *)GET_PKT_DATA(p), hlen);
+
+ /* Self test. */
+ if (IPV4_GET_VER(p) != 4)
+ goto error;
+ if (IPV4_GET_HLEN(p) != hlen)
+ goto error;
+ if (IPV4_GET_IPLEN(p) != hlen + content_len)
+ goto error;
+ if (IPV4_GET_IPID(p) != id)
+ goto error;
+ if (IPV4_GET_IPOFFSET(p) != off)
+ goto error;
+ if (IPV4_GET_MF(p) != mf)
+ goto error;
+ if (IPV4_GET_IPTTL(p) != ttl)
+ goto error;
+ if (IPV4_GET_IPPROTO(p) != IPPROTO_ICMP)
+ goto error;
+
+ return p;
+error:
+ if (p != NULL)
+ SCFree(p);
+ return NULL;
+}
+
+static Packet *
+IPV6BuildTestPacket(uint32_t id, uint16_t off, int mf, const char content,
+ int content_len)
+{
+ Packet *p = NULL;
+ uint8_t *pcontent;
+ IPV6Hdr ip6h;
+
+ p = SCCalloc(1, sizeof(*p) + default_packet_size);
+ if (unlikely(p == NULL))
+ return NULL;
+
+ PACKET_INITIALIZE(p);
+
+ gettimeofday(&p->ts, NULL);
+
+ ip6h.s_ip6_nxt = 44;
+ ip6h.s_ip6_hlim = 2;
+
+ /* Source and dest address - very bogus addresses. */
+ ip6h.s_ip6_src[0] = 0x01010101;
+ ip6h.s_ip6_src[1] = 0x01010101;
+ ip6h.s_ip6_src[2] = 0x01010101;
+ ip6h.s_ip6_src[3] = 0x01010101;
+ ip6h.s_ip6_dst[0] = 0x02020202;
+ ip6h.s_ip6_dst[1] = 0x02020202;
+ ip6h.s_ip6_dst[2] = 0x02020202;
+ ip6h.s_ip6_dst[3] = 0x02020202;
+
+ /* copy content_len crap, we need full length */
+ PacketCopyData(p, (uint8_t *)&ip6h, sizeof(IPV6Hdr));
+
+ p->ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+ IPV6_SET_RAW_VER(p->ip6h, 6);
+ /* Fragmentation header. */
+ IPV6FragHdr *fh = (IPV6FragHdr *)(GET_PKT_DATA(p) + sizeof(IPV6Hdr));
+ fh->ip6fh_nxt = IPPROTO_ICMP;
+ fh->ip6fh_ident = htonl(id);
+ fh->ip6fh_offlg = htons((off << 3) | mf);
+ p->ip6eh.ip6fh = fh;
+
+ pcontent = SCCalloc(1, content_len);
+ if (unlikely(pcontent == NULL))
+ return NULL;
+ memset(pcontent, content, content_len);
+ PacketCopyDataOffset(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr), pcontent, content_len);
+ SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr) + content_len);
+ SCFree(pcontent);
+
+ p->ip6h->s_ip6_plen = htons(sizeof(IPV6FragHdr) + content_len);
+
+ SET_IPV6_SRC_ADDR(p, &p->src);
+ SET_IPV6_DST_ADDR(p, &p->dst);
+
+ /* Self test. */
+ if (IPV6_GET_VER(p) != 6)
+ goto error;
+ if (IPV6_GET_NH(p) != 44)
+ goto error;
+ if (IPV6_GET_PLEN(p) != sizeof(IPV6FragHdr) + content_len)
+ goto error;
+
+ return p;
+error:
+ fprintf(stderr, "Error building test packet.\n");
+ if (p != NULL)
+ SCFree(p);
+ return NULL;
+}
+
+/**
+ * Test the simplest possible re-assembly scenario. All packet in
+ * order and no overlaps.
+ */
+static int
+DefragInOrderSimpleTest(void)
+{
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Packet *reassembled = NULL;
+ int id = 12;
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ p1 = BuildTestPacket(id, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = BuildTestPacket(id, 1, 1, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+ p3 = BuildTestPacket(id, 2, 0, 'C', 3);
+ if (p3 == NULL)
+ goto end;
+
+ if (Defrag(NULL, NULL, p1, NULL) != NULL)
+ goto end;
+ if (Defrag(NULL, NULL, p2, NULL) != NULL)
+ goto end;
+
+ reassembled = Defrag(NULL, NULL, p3, NULL);
+ if (reassembled == NULL) {
+ goto end;
+ }
+
+ if (IPV4_GET_HLEN(reassembled) != 20) {
+ goto end;
+ }
+ if (IPV4_GET_IPLEN(reassembled) != 39) {
+ goto end;
+ }
+
+ /* 20 bytes in we should find 8 bytes of A. */
+ for (i = 20; i < 20 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'A') {
+ goto end;
+ }
+ }
+
+ /* 28 bytes in we should find 8 bytes of B. */
+ for (i = 28; i < 28 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'B') {
+ goto end;
+ }
+ }
+
+ /* And 36 bytes in we should find 3 bytes of C. */
+ for (i = 36; i < 36 + 3; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'C')
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ if (p3 != NULL)
+ SCFree(p3);
+ if (reassembled != NULL)
+ SCFree(reassembled);
+
+ DefragDestroy();
+ return ret;
+}
+
+/**
+ * Simple fragmented packet in reverse order.
+ */
+static int
+DefragReverseSimpleTest(void)
+{
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Packet *reassembled = NULL;
+ int id = 12;
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ p1 = BuildTestPacket(id, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = BuildTestPacket(id, 1, 1, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+ p3 = BuildTestPacket(id, 2, 0, 'C', 3);
+ if (p3 == NULL)
+ goto end;
+
+ if (Defrag(NULL, NULL, p3, NULL) != NULL)
+ goto end;
+ if (Defrag(NULL, NULL, p2, NULL) != NULL)
+ goto end;
+
+ reassembled = Defrag(NULL, NULL, p1, NULL);
+ if (reassembled == NULL)
+ goto end;
+
+ if (IPV4_GET_HLEN(reassembled) != 20)
+ goto end;
+ if (IPV4_GET_IPLEN(reassembled) != 39)
+ goto end;
+
+ /* 20 bytes in we should find 8 bytes of A. */
+ for (i = 20; i < 20 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'A')
+ goto end;
+ }
+
+ /* 28 bytes in we should find 8 bytes of B. */
+ for (i = 28; i < 28 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'B')
+ goto end;
+ }
+
+ /* And 36 bytes in we should find 3 bytes of C. */
+ for (i = 36; i < 36 + 3; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'C')
+ goto end;
+ }
+
+ ret = 1;
+end:
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ if (p3 != NULL)
+ SCFree(p3);
+ if (reassembled != NULL)
+ SCFree(reassembled);
+
+ DefragDestroy();
+ return ret;
+}
+
+/**
+ * Test the simplest possible re-assembly scenario. All packet in
+ * order and no overlaps.
+ */
+static int
+IPV6DefragInOrderSimpleTest(void)
+{
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Packet *reassembled = NULL;
+ int id = 12;
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ p1 = IPV6BuildTestPacket(id, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = IPV6BuildTestPacket(id, 1, 1, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+ p3 = IPV6BuildTestPacket(id, 2, 0, 'C', 3);
+ if (p3 == NULL)
+ goto end;
+
+ if (Defrag(NULL, NULL, p1, NULL) != NULL)
+ goto end;
+ if (Defrag(NULL, NULL, p2, NULL) != NULL)
+ goto end;
+ reassembled = Defrag(NULL, NULL, p3, NULL);
+ if (reassembled == NULL)
+ goto end;
+
+ if (IPV6_GET_PLEN(reassembled) != 19)
+ goto end;
+
+ /* 40 bytes in we should find 8 bytes of A. */
+ for (i = 40; i < 40 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'A')
+ goto end;
+ }
+
+ /* 28 bytes in we should find 8 bytes of B. */
+ for (i = 48; i < 48 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'B')
+ goto end;
+ }
+
+ /* And 36 bytes in we should find 3 bytes of C. */
+ for (i = 56; i < 56 + 3; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'C')
+ goto end;
+ }
+
+ ret = 1;
+end:
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ if (p3 != NULL)
+ SCFree(p3);
+ if (reassembled != NULL)
+ SCFree(reassembled);
+
+ DefragDestroy();
+ return ret;
+}
+
+static int
+IPV6DefragReverseSimpleTest(void)
+{
+ DefragContext *dc = NULL;
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Packet *reassembled = NULL;
+ int id = 12;
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ dc = DefragContextNew();
+ if (dc == NULL)
+ goto end;
+
+ p1 = IPV6BuildTestPacket(id, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = IPV6BuildTestPacket(id, 1, 1, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+ p3 = IPV6BuildTestPacket(id, 2, 0, 'C', 3);
+ if (p3 == NULL)
+ goto end;
+
+ if (Defrag(NULL, NULL, p3, NULL) != NULL)
+ goto end;
+ if (Defrag(NULL, NULL, p2, NULL) != NULL)
+ goto end;
+ reassembled = Defrag(NULL, NULL, p1, NULL);
+ if (reassembled == NULL)
+ goto end;
+
+ /* 40 bytes in we should find 8 bytes of A. */
+ for (i = 40; i < 40 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'A')
+ goto end;
+ }
+
+ /* 28 bytes in we should find 8 bytes of B. */
+ for (i = 48; i < 48 + 8; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'B')
+ goto end;
+ }
+
+ /* And 36 bytes in we should find 3 bytes of C. */
+ for (i = 56; i < 56 + 3; i++) {
+ if (GET_PKT_DATA(reassembled)[i] != 'C')
+ goto end;
+ }
+
+ ret = 1;
+end:
+ if (dc != NULL)
+ DefragContextDestroy(dc);
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ if (p3 != NULL)
+ SCFree(p3);
+ if (reassembled != NULL)
+ SCFree(reassembled);
+
+ DefragDestroy();
+ return ret;
+}
+
+static int
+DefragDoSturgesNovakTest(int policy, u_char *expected, size_t expected_len)
+{
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ /*
+ * Build the packets.
+ */
+
+ int id = 1;
+ Packet *packets[17];
+ memset(packets, 0x00, sizeof(packets));
+
+ /*
+ * Original fragments.
+ */
+
+ /* A*24 at 0. */
+ packets[0] = BuildTestPacket(id, 0, 1, 'A', 24);
+
+ /* B*15 at 32. */
+ packets[1] = BuildTestPacket(id, 32 >> 3, 1, 'B', 16);
+
+ /* C*24 at 48. */
+ packets[2] = BuildTestPacket(id, 48 >> 3, 1, 'C', 24);
+
+ /* D*8 at 80. */
+ packets[3] = BuildTestPacket(id, 80 >> 3, 1, 'D', 8);
+
+ /* E*16 at 104. */
+ packets[4] = BuildTestPacket(id, 104 >> 3, 1, 'E', 16);
+
+ /* F*24 at 120. */
+ packets[5] = BuildTestPacket(id, 120 >> 3, 1, 'F', 24);
+
+ /* G*16 at 144. */
+ packets[6] = BuildTestPacket(id, 144 >> 3, 1, 'G', 16);
+
+ /* H*16 at 160. */
+ packets[7] = BuildTestPacket(id, 160 >> 3, 1, 'H', 16);
+
+ /* I*8 at 176. */
+ packets[8] = BuildTestPacket(id, 176 >> 3, 1, 'I', 8);
+
+ /*
+ * Overlapping subsequent fragments.
+ */
+
+ /* J*32 at 8. */
+ packets[9] = BuildTestPacket(id, 8 >> 3, 1, 'J', 32);
+
+ /* K*24 at 48. */
+ packets[10] = BuildTestPacket(id, 48 >> 3, 1, 'K', 24);
+
+ /* L*24 at 72. */
+ packets[11] = BuildTestPacket(id, 72 >> 3, 1, 'L', 24);
+
+ /* M*24 at 96. */
+ packets[12] = BuildTestPacket(id, 96 >> 3, 1, 'M', 24);
+
+ /* N*8 at 128. */
+ packets[13] = BuildTestPacket(id, 128 >> 3, 1, 'N', 8);
+
+ /* O*8 at 152. */
+ packets[14] = BuildTestPacket(id, 152 >> 3, 1, 'O', 8);
+
+ /* P*8 at 160. */
+ packets[15] = BuildTestPacket(id, 160 >> 3, 1, 'P', 8);
+
+ /* Q*16 at 176. */
+ packets[16] = BuildTestPacket(id, 176 >> 3, 0, 'Q', 16);
+
+ default_policy = policy;
+
+ /* Send all but the last. */
+ for (i = 0; i < 9; i++) {
+ Packet *tp = Defrag(NULL, NULL, packets[i], NULL);
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+ if (ENGINE_ISSET_EVENT(packets[i], IPV4_FRAG_OVERLAP)) {
+ goto end;
+ }
+ }
+ int overlap = 0;
+ for (; i < 16; i++) {
+ Packet *tp = Defrag(NULL, NULL, packets[i], NULL);
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+ if (ENGINE_ISSET_EVENT(packets[i], IPV4_FRAG_OVERLAP)) {
+ overlap++;
+ }
+ }
+ if (!overlap) {
+ goto end;
+ }
+
+ /* And now the last one. */
+ Packet *reassembled = Defrag(NULL, NULL, packets[16], NULL);
+ if (reassembled == NULL) {
+ goto end;
+ }
+
+ if (IPV4_GET_HLEN(reassembled) != 20) {
+ goto end;
+ }
+ if (IPV4_GET_IPLEN(reassembled) != 20 + 192) {
+ goto end;
+ }
+
+ if (memcmp(GET_PKT_DATA(reassembled) + 20, expected, expected_len) != 0) {
+ goto end;
+ }
+ SCFree(reassembled);
+
+ /* Make sure all frags were returned back to the pool. */
+ if (defrag_context->frag_pool->outstanding != 0) {
+ goto end;
+ }
+
+ ret = 1;
+end:
+ for (i = 0; i < 17; i++) {
+ SCFree(packets[i]);
+ }
+ DefragDestroy();
+ return ret;
+}
+
+static int
+IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, size_t expected_len)
+{
+ int i;
+ int ret = 0;
+
+ DefragInit();
+
+ /*
+ * Build the packets.
+ */
+
+ int id = 1;
+ Packet *packets[17];
+ memset(packets, 0x00, sizeof(packets));
+
+ /*
+ * Original fragments.
+ */
+
+ /* A*24 at 0. */
+ packets[0] = IPV6BuildTestPacket(id, 0, 1, 'A', 24);
+
+ /* B*15 at 32. */
+ packets[1] = IPV6BuildTestPacket(id, 32 >> 3, 1, 'B', 16);
+
+ /* C*24 at 48. */
+ packets[2] = IPV6BuildTestPacket(id, 48 >> 3, 1, 'C', 24);
+
+ /* D*8 at 80. */
+ packets[3] = IPV6BuildTestPacket(id, 80 >> 3, 1, 'D', 8);
+
+ /* E*16 at 104. */
+ packets[4] = IPV6BuildTestPacket(id, 104 >> 3, 1, 'E', 16);
+
+ /* F*24 at 120. */
+ packets[5] = IPV6BuildTestPacket(id, 120 >> 3, 1, 'F', 24);
+
+ /* G*16 at 144. */
+ packets[6] = IPV6BuildTestPacket(id, 144 >> 3, 1, 'G', 16);
+
+ /* H*16 at 160. */
+ packets[7] = IPV6BuildTestPacket(id, 160 >> 3, 1, 'H', 16);
+
+ /* I*8 at 176. */
+ packets[8] = IPV6BuildTestPacket(id, 176 >> 3, 1, 'I', 8);
+
+ /*
+ * Overlapping subsequent fragments.
+ */
+
+ /* J*32 at 8. */
+ packets[9] = IPV6BuildTestPacket(id, 8 >> 3, 1, 'J', 32);
+
+ /* K*24 at 48. */
+ packets[10] = IPV6BuildTestPacket(id, 48 >> 3, 1, 'K', 24);
+
+ /* L*24 at 72. */
+ packets[11] = IPV6BuildTestPacket(id, 72 >> 3, 1, 'L', 24);
+
+ /* M*24 at 96. */
+ packets[12] = IPV6BuildTestPacket(id, 96 >> 3, 1, 'M', 24);
+
+ /* N*8 at 128. */
+ packets[13] = IPV6BuildTestPacket(id, 128 >> 3, 1, 'N', 8);
+
+ /* O*8 at 152. */
+ packets[14] = IPV6BuildTestPacket(id, 152 >> 3, 1, 'O', 8);
+
+ /* P*8 at 160. */
+ packets[15] = IPV6BuildTestPacket(id, 160 >> 3, 1, 'P', 8);
+
+ /* Q*16 at 176. */
+ packets[16] = IPV6BuildTestPacket(id, 176 >> 3, 0, 'Q', 16);
+
+ default_policy = policy;
+
+ /* Send all but the last. */
+ for (i = 0; i < 9; i++) {
+ Packet *tp = Defrag(NULL, NULL, packets[i], NULL);
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+ if (ENGINE_ISSET_EVENT(packets[i], IPV6_FRAG_OVERLAP)) {
+ goto end;
+ }
+ }
+ int overlap = 0;
+ for (; i < 16; i++) {
+ Packet *tp = Defrag(NULL, NULL, packets[i], NULL);
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+ if (ENGINE_ISSET_EVENT(packets[i], IPV6_FRAG_OVERLAP)) {
+ overlap++;
+ }
+ }
+ if (!overlap)
+ goto end;
+
+ /* And now the last one. */
+ Packet *reassembled = Defrag(NULL, NULL, packets[16], NULL);
+ if (reassembled == NULL)
+ goto end;
+ if (memcmp(GET_PKT_DATA(reassembled) + 40, expected, expected_len) != 0)
+ goto end;
+
+ if (IPV6_GET_PLEN(reassembled) != 192)
+ goto end;
+
+ SCFree(reassembled);
+
+ /* Make sure all frags were returned to the pool. */
+ if (defrag_context->frag_pool->outstanding != 0) {
+ printf("defrag_context->frag_pool->outstanding %u: ", defrag_context->frag_pool->outstanding);
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ for (i = 0; i < 17; i++) {
+ SCFree(packets[i]);
+ }
+ DefragDestroy();
+ return ret;
+}
+
+static int
+DefragSturgesNovakBsdTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakBsdTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, sizeof(expected));
+}
+
+static int
+DefragSturgesNovakLinuxTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "PPPPPPPP"
+ "HHHHHHHH"
+ "QQQQQQQQ"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakLinuxTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "PPPPPPPP"
+ "HHHHHHHH"
+ "QQQQQQQQ"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected,
+ sizeof(expected));
+}
+
+static int
+DefragSturgesNovakWindowsTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "EEEEEEEE"
+ "EEEEEEEE"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakWindowsTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "EEEEEEEE"
+ "EEEEEEEE"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected,
+ sizeof(expected));
+}
+
+static int
+DefragSturgesNovakSolarisTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakSolarisTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected,
+ sizeof(expected));
+}
+
+static int
+DefragSturgesNovakFirstTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "DDDDDDDD"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "EEEEEEEE"
+ "EEEEEEEE"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakFirstTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "BBBBBBBB"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "CCCCCCCC"
+ "LLLLLLLL"
+ "DDDDDDDD"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "EEEEEEEE"
+ "EEEEEEEE"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "GGGGGGGG"
+ "HHHHHHHH"
+ "HHHHHHHH"
+ "IIIIIIII"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected,
+ sizeof(expected));
+}
+
+static int
+DefragSturgesNovakLastTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "NNNNNNNN"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "OOOOOOOO"
+ "PPPPPPPP"
+ "HHHHHHHH"
+ "QQQQQQQQ"
+ "QQQQQQQQ"
+ };
+
+ return DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected, sizeof(expected));
+}
+
+static int
+IPV6DefragSturgesNovakLastTest(void)
+{
+ /* Expected data. */
+ u_char expected[] = {
+ "AAAAAAAA"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "JJJJJJJJ"
+ "BBBBBBBB"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "KKKKKKKK"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "LLLLLLLL"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "MMMMMMMM"
+ "FFFFFFFF"
+ "NNNNNNNN"
+ "FFFFFFFF"
+ "GGGGGGGG"
+ "OOOOOOOO"
+ "PPPPPPPP"
+ "HHHHHHHH"
+ "QQQQQQQQ"
+ "QQQQQQQQ"
+ };
+
+ return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected,
+ sizeof(expected));
+}
+
+static int
+DefragTimeoutTest(void)
+{
+ int i;
+ int ret = 0;
+
+ /* Setup a small numberr of trackers. */
+ if (ConfSet("defrag.trackers", "16") != 1) {
+ printf("ConfSet failed: ");
+ goto end;
+ }
+
+ DefragInit();
+
+ /* Load in 16 packets. */
+ for (i = 0; i < 16; i++) {
+ Packet *p = BuildTestPacket(i, 0, 1, 'A' + i, 16);
+ if (p == NULL)
+ goto end;
+
+ Packet *tp = Defrag(NULL, NULL, p, NULL);
+
+ SCFree(p);
+
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+ }
+
+ /* Build a new packet but push the timestamp out by our timeout.
+ * This should force our previous fragments to be timed out. */
+ Packet *p = BuildTestPacket(99, 0, 1, 'A' + i, 16);
+ if (p == NULL)
+ goto end;
+
+ p->ts.tv_sec += (defrag_context->timeout + 1);
+ Packet *tp = Defrag(NULL, NULL, p, NULL);
+
+ if (tp != NULL) {
+ SCFree(tp);
+ goto end;
+ }
+
+ DefragTracker *tracker = DefragLookupTrackerFromHash(p);
+ if (tracker == NULL)
+ goto end;
+
+ if (tracker->id != 99)
+ goto end;
+
+ SCFree(p);
+
+ ret = 1;
+end:
+ DefragDestroy();
+ return ret;
+}
+
+/**
+ * QA found that if you send a packet where more frags is 0, offset is
+ * > 0 and there is no data in the packet that the re-assembler will
+ * fail. The fix was simple, but this unit test is just to make sure
+ * its not introduced.
+ */
+static int
+DefragIPv4NoDataTest(void)
+{
+ DefragContext *dc = NULL;
+ Packet *p = NULL;
+ int id = 12;
+ int ret = 0;
+
+ DefragInit();
+
+ dc = DefragContextNew();
+ if (dc == NULL)
+ goto end;
+
+ /* This packet has an offset > 0, more frags set to 0 and no data. */
+ p = BuildTestPacket(id, 1, 0, 'A', 0);
+ if (p == NULL)
+ goto end;
+
+ /* We do not expect a packet returned. */
+ if (Defrag(NULL, NULL, p, NULL) != NULL)
+ goto end;
+
+ /* The fragment should have been ignored so no fragments should
+ * have been allocated from the pool. */
+ if (dc->frag_pool->outstanding != 0)
+ return 0;
+
+ ret = 1;
+end:
+ if (dc != NULL)
+ DefragContextDestroy(dc);
+ if (p != NULL)
+ SCFree(p);
+
+ DefragDestroy();
+ return ret;
+}
+
+static int
+DefragIPv4TooLargeTest(void)
+{
+ DefragContext *dc = NULL;
+ Packet *p = NULL;
+ int ret = 0;
+
+ DefragInit();
+
+ dc = DefragContextNew();
+ if (dc == NULL)
+ goto end;
+
+ /* Create a fragment that would extend past the max allowable size
+ * for an IPv4 packet. */
+ p = BuildTestPacket(1, 8183, 0, 'A', 71);
+ if (p == NULL)
+ goto end;
+
+ /* We do not expect a packet returned. */
+ if (Defrag(NULL, NULL, p, NULL) != NULL)
+ goto end;
+ if (!ENGINE_ISSET_EVENT(p, IPV4_FRAG_PKT_TOO_LARGE))
+ goto end;
+
+ /* The fragment should have been ignored so no fragments should have
+ * been allocated from the pool. */
+ if (dc->frag_pool->outstanding != 0)
+ return 0;
+
+ ret = 1;
+end:
+ if (dc != NULL)
+ DefragContextDestroy(dc);
+ if (p != NULL)
+ SCFree(p);
+
+ DefragDestroy();
+ return ret;
+}
+
+/**
+ * Test that fragments in different VLANs that would otherwise be
+ * re-assembled, are not re-assembled. Just use simple in-order
+ * fragments.
+ */
+static int
+DefragVlanTest(void)
+{
+ Packet *p1 = NULL, *p2 = NULL, *r = NULL;
+ int ret = 0;
+
+ DefragInit();
+
+ p1 = BuildTestPacket(1, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = BuildTestPacket(1, 1, 0, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+
+ /* With no VLAN IDs set, packets should re-assemble. */
+ if ((r = Defrag(NULL, NULL, p1, NULL)) != NULL)
+ goto end;
+ if ((r = Defrag(NULL, NULL, p2, NULL)) == NULL)
+ goto end;
+ SCFree(r);
+
+ /* With mismatched VLANs, packets should not re-assemble. */
+ p1->vlan_id[0] = 1;
+ p2->vlan_id[0] = 2;
+ if ((r = Defrag(NULL, NULL, p1, NULL)) != NULL)
+ goto end;
+ if ((r = Defrag(NULL, NULL, p2, NULL)) != NULL)
+ goto end;
+
+ /* Pass. */
+ ret = 1;
+
+end:
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ DefragDestroy();
+
+ return ret;
+}
+
+/**
+ * Like DefragVlanTest, but for QinQ, testing the second level VLAN ID.
+ */
+static int
+DefragVlanQinQTest(void)
+{
+ Packet *p1 = NULL, *p2 = NULL, *r = NULL;
+ int ret = 0;
+
+ DefragInit();
+
+ p1 = BuildTestPacket(1, 0, 1, 'A', 8);
+ if (p1 == NULL)
+ goto end;
+ p2 = BuildTestPacket(1, 1, 0, 'B', 8);
+ if (p2 == NULL)
+ goto end;
+
+ /* With no VLAN IDs set, packets should re-assemble. */
+ if ((r = Defrag(NULL, NULL, p1, NULL)) != NULL)
+ goto end;
+ if ((r = Defrag(NULL, NULL, p2, NULL)) == NULL)
+ goto end;
+ SCFree(r);
+
+ /* With mismatched VLANs, packets should not re-assemble. */
+ p1->vlan_id[0] = 1;
+ p2->vlan_id[0] = 1;
+ p1->vlan_id[1] = 1;
+ p2->vlan_id[1] = 2;
+ if ((r = Defrag(NULL, NULL, p1, NULL)) != NULL)
+ goto end;
+ if ((r = Defrag(NULL, NULL, p2, NULL)) != NULL)
+ goto end;
+
+ /* Pass. */
+ ret = 1;
+
+end:
+ if (p1 != NULL)
+ SCFree(p1);
+ if (p2 != NULL)
+ SCFree(p2);
+ DefragDestroy();
+
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void
+DefragRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DefragInOrderSimpleTest",
+ DefragInOrderSimpleTest, 1);
+ UtRegisterTest("DefragReverseSimpleTest",
+ DefragReverseSimpleTest, 1);
+ UtRegisterTest("DefragSturgesNovakBsdTest",
+ DefragSturgesNovakBsdTest, 1);
+ UtRegisterTest("DefragSturgesNovakLinuxTest",
+ DefragSturgesNovakLinuxTest, 1);
+ UtRegisterTest("DefragSturgesNovakWindowsTest",
+ DefragSturgesNovakWindowsTest, 1);
+ UtRegisterTest("DefragSturgesNovakSolarisTest",
+ DefragSturgesNovakSolarisTest, 1);
+ UtRegisterTest("DefragSturgesNovakFirstTest",
+ DefragSturgesNovakFirstTest, 1);
+ UtRegisterTest("DefragSturgesNovakLastTest",
+ DefragSturgesNovakLastTest, 1);
+
+ UtRegisterTest("DefragIPv4NoDataTest", DefragIPv4NoDataTest, 1);
+ UtRegisterTest("DefragIPv4TooLargeTest", DefragIPv4TooLargeTest, 1);
+
+ UtRegisterTest("IPV6DefragInOrderSimpleTest",
+ IPV6DefragInOrderSimpleTest, 1);
+ UtRegisterTest("IPV6DefragReverseSimpleTest",
+ IPV6DefragReverseSimpleTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakBsdTest",
+ IPV6DefragSturgesNovakBsdTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakLinuxTest",
+ IPV6DefragSturgesNovakLinuxTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakWindowsTest",
+ IPV6DefragSturgesNovakWindowsTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakSolarisTest",
+ IPV6DefragSturgesNovakSolarisTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakFirstTest",
+ IPV6DefragSturgesNovakFirstTest, 1);
+ UtRegisterTest("IPV6DefragSturgesNovakLastTest",
+ IPV6DefragSturgesNovakLastTest, 1);
+
+ UtRegisterTest("DefragVlanTest", DefragVlanTest, 1);
+ UtRegisterTest("DefragVlanQinQTest", DefragVlanQinQTest, 1);
+
+ UtRegisterTest("DefragTimeoutTest",
+ DefragTimeoutTest, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/defrag.h b/framework/src/suricata/src/defrag.h
new file mode 100644
index 00000000..8bd0325a
--- /dev/null
+++ b/framework/src/suricata/src/defrag.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited, Jason Ish <jason.ish@endace.com>
+ */
+
+#ifndef __DEFRAG_H__
+#define __DEFRAG_H__
+
+#include "util-pool.h"
+
+/**
+ * A context for an instance of a fragmentation re-assembler, in case
+ * we ever need more than one.
+ */
+typedef struct DefragContext_ {
+ Pool *frag_pool; /**< Pool of fragments. */
+ SCMutex frag_pool_lock;
+
+ time_t timeout; /**< Default timeout. */
+} DefragContext;
+
+/**
+ * Storage for an individual fragment.
+ */
+typedef struct Frag_ {
+ uint16_t offset; /**< The offset of this fragment, already
+ * multiplied by 8. */
+
+ uint16_t len; /**< The length of this fragment. */
+
+ uint8_t hlen; /**< The length of this fragments IP header. */
+
+ uint8_t more_frags:4; /**< More frags? */
+ uint8_t skip:4; /**< Skip this fragment during re-assembly. */
+
+ uint16_t ip_hdr_offset; /**< Offset in the packet where the IP
+ * header starts. */
+ uint16_t frag_hdr_offset; /**< Offset in the packet where the frag
+ * header starts. */
+
+ uint16_t data_offset; /**< Offset to the packet data. */
+ uint16_t data_len; /**< Length of data. */
+
+ uint16_t ltrim; /**< Number of leading bytes to trim when
+ * re-assembling the packet. */
+
+ uint8_t *pkt; /**< The actual packet. */
+
+#ifdef DEBUG
+ uint64_t pcap_cnt; /**< pcap_cnt of original packet */
+#endif
+
+ TAILQ_ENTRY(Frag_) next; /**< Pointer to next fragment for tailq. */
+} Frag;
+
+/** \brief Reset tracker fields except "lock" */
+#define DEFRAG_TRACKER_RESET(t) { \
+ (t)->timeout = 0; \
+ (t)->id = 0; \
+ (t)->policy = 0; \
+ (t)->af = 0; \
+ (t)->seen_last = 0; \
+ (t)->remove = 0; \
+ CLEAR_ADDR(&(t)->src_addr); \
+ CLEAR_ADDR(&(t)->dst_addr); \
+ (t)->frags.tqh_first = NULL; \
+ (t)->frags.tqh_last = NULL; \
+}
+
+/**
+ * A defragmentation tracker. Used to track fragments that make up a
+ * single packet.
+ */
+typedef struct DefragTracker_ {
+ SCMutex lock; /**< Mutex for locking list operations on
+ * this tracker. */
+
+ uint16_t vlan_id[2]; /**< VLAN ID tracker applies to. */
+
+ uint32_t id; /**< IP ID for this tracker. 32 bits for IPv6, 16
+ * for IPv4. */
+
+ uint8_t policy; /**< Reassembly policy this tracker will use. */
+
+ uint8_t af; /**< Address family for this tracker, AF_INET or
+ * AF_INET6. */
+
+ uint8_t seen_last; /**< Has this tracker seen the last fragment? */
+
+ uint8_t remove; /**< remove */
+
+ Address src_addr; /**< Source address for this tracker. */
+ Address dst_addr; /**< Destination address for this tracker. */
+
+ struct timeval timeout; /**< When this tracker will timeout. */
+ uint32_t host_timeout; /**< Host timeout, statically assigned from the yaml */
+
+ /** use cnt, reference counter */
+ SC_ATOMIC_DECLARE(unsigned int, use_cnt);
+
+ TAILQ_HEAD(frag_tailq, Frag_) frags; /**< Head of list of fragments. */
+
+ /** hash pointers, protected by hash row mutex/spin */
+ struct DefragTracker_ *hnext;
+ struct DefragTracker_ *hprev;
+
+ /** list pointers, protected by tracker-queue mutex/spin */
+ struct DefragTracker_ *lnext;
+ struct DefragTracker_ *lprev;
+} DefragTracker;
+
+void DefragInit(void);
+void DefragDestroy(void);
+void DefragReload(void); /**< use only in unittests */
+
+uint8_t DefragGetOsPolicy(Packet *);
+void DefragTrackerFreeFrags(DefragTracker *);
+Packet *Defrag(ThreadVars *, DecodeThreadVars *, Packet *, PacketQueue *);
+void DefragRegisterTests(void);
+
+#endif /* __DEFRAG_H__ */
diff --git a/framework/src/suricata/src/detect-ack.c b/framework/src/suricata/src/detect-ack.c
new file mode 100644
index 00000000..5a84c6f9
--- /dev/null
+++ b/framework/src/suricata/src/detect-ack.c
@@ -0,0 +1,302 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements the "ack" keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-ack.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/* prototypes */
+static int DetectAckSetup(DetectEngineCtx *, Signature *, char *);
+static int DetectAckMatch(ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, const SigMatchCtx *);
+static void DetectAckRegisterTests(void);
+static void DetectAckFree(void *);
+
+void DetectAckRegister(void)
+{
+ sigmatch_table[DETECT_ACK].name = "ack";
+ sigmatch_table[DETECT_ACK].desc = "check for a specific TCP acknowledgement number";
+ sigmatch_table[DETECT_ACK].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#ack";
+ sigmatch_table[DETECT_ACK].Match = DetectAckMatch;
+ sigmatch_table[DETECT_ACK].Setup = DetectAckSetup;
+ sigmatch_table[DETECT_ACK].Free = DetectAckFree;
+ sigmatch_table[DETECT_ACK].RegisterTests = DetectAckRegisterTests;
+}
+
+/**
+ * \internal
+ * \brief This function is used to match packets with a given Ack number
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectAckData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectAckMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectAckData *data = (const DetectAckData *)ctx;
+
+ /* This is only needed on TCP packets */
+ if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
+ return 0;
+ }
+
+ return (data->ack == TCP_GET_ACK(p)) ? 1 : 0;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the ack option into the signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectAckSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ DetectAckData *data = NULL;
+ SigMatch *sm = NULL;
+
+ data = SCMalloc(sizeof(DetectAckData));
+ if (unlikely(data == NULL))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ACK;
+
+ if (-1 == ByteExtractStringUint32(&data->ack, 10, 0, optstr)) {
+ goto error;
+ }
+ sm->ctx = (SigMatchCtx*)data;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (data)
+ SCFree(data);
+ if (sm)
+ SigMatchFree(sm);
+ return -1;
+
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with ack option
+ *
+ * \param data pointer to ack configuration data
+ */
+static void DetectAckFree(void *ptr)
+{
+ DetectAckData *data = (DetectAckData *)ptr;
+ SCFree(data);
+}
+
+
+#ifdef UNITTESTS
+/**
+ * \internal
+ * \brief This test tests sameip success and failure.
+ */
+static int DetectAckSigTest01Real(int mpm_type)
+{
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Packet *p3 = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ /* TCP w/ack=42 */
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p1->tcph->th_ack = htonl(42);
+
+ /* TCP w/ack=100 */
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2->tcph->th_ack = htonl(100);
+
+ /* ICMP */
+ p3 = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ /* These three are crammed in here as there is no Parse */
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing ack\";ack:foo;sid:1;)") != NULL)
+ {
+ printf("invalid ack accepted: ");
+ goto cleanup_engine;
+ }
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing ack\";ack:9999999999;sid:1;)") != NULL)
+ {
+ printf("overflowing ack accepted: ");
+ goto cleanup_engine;
+ }
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing ack\";ack:-100;sid:1;)") != NULL)
+ {
+ printf("negative ack accepted: ");
+ goto cleanup_engine;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing ack\";ack:41;sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto cleanup_engine;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing ack\";ack:42;sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto cleanup_engine;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1) != 0) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p1, 2) == 0) {
+ printf("sid 2 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 1) != 0) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p2, 2) != 0) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
+ if (PacketAlertCheck(p3, 1) != 0) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p3, 2) != 0) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+cleanup_engine:
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+/**
+ * \test DetectAckSigTest01B2g tests sameip under B2g MPM
+ */
+static int DetectAckSigTest01B2g(void)
+{
+ return DetectAckSigTest01Real(MPM_B2G);
+}
+
+/**
+ * \test DetectAckSigTest01B2g tests sameip under B3g MPM
+ */
+static int DetectAckSigTest01B3g(void)
+{
+ return DetectAckSigTest01Real(MPM_B3G);
+}
+
+/**
+ * \test DetectAckSigTest01B2g tests sameip under WuManber MPM
+ */
+static int DetectAckSigTest01Wm(void)
+{
+ return DetectAckSigTest01Real(MPM_WUMANBER);
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \internal
+ * \brief This function registers unit tests for DetectAck
+ */
+static void DetectAckRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectAckSigTest01B2g", DetectAckSigTest01B2g, 1);
+ UtRegisterTest("DetectAckSigTest01B3g", DetectAckSigTest01B3g, 1);
+ UtRegisterTest("DetectAckSigTest01Wm", DetectAckSigTest01Wm, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-ack.h b/framework/src/suricata/src/detect-ack.h
new file mode 100644
index 00000000..6a8465b8
--- /dev/null
+++ b/framework/src/suricata/src/detect-ack.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_ACK_H__
+#define __DETECT_ACK_H__
+
+/**
+ * \brief ack data
+ */
+typedef struct DetectAckData_ {
+ uint32_t ack; /**< ack to match */
+} DetectAckData;
+
+/**
+ * \brief Registration function for ack: keyword
+ */
+void DetectAckRegister(void);
+
+#endif /* __DETECT_ACK_H__ */
+
diff --git a/framework/src/suricata/src/detect-app-layer-event.c b/framework/src/suricata/src/detect-app-layer-event.c
new file mode 100644
index 00000000..63d40117
--- /dev/null
+++ b/framework/src/suricata/src/detect-app-layer-event.c
@@ -0,0 +1,836 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-smtp.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-app-layer-event.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "decode-events.h"
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+
+static int DetectAppLayerEventPktMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx);
+static int DetectAppLayerEventAppMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, void *, Signature *, SigMatch *);
+static int DetectAppLayerEventSetupP1(DetectEngineCtx *, Signature *, char *);
+static void DetectAppLayerEventRegisterTests(void);
+static void DetectAppLayerEventFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "app-layer-event" keyword.
+ */
+void DetectAppLayerEventRegister(void)
+{
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].name = "app-layer-event";
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Match =
+ DetectAppLayerEventPktMatch;
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].AppLayerMatch =
+ DetectAppLayerEventAppMatch;
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Setup = DetectAppLayerEventSetupP1;
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Free = DetectAppLayerEventFree;
+ sigmatch_table[DETECT_AL_APP_LAYER_EVENT].RegisterTests =
+ DetectAppLayerEventRegisterTests;
+
+ return;
+}
+
+
+static int DetectAppLayerEventPktMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectAppLayerEventData *aled = (const DetectAppLayerEventData *)ctx;
+
+ return AppLayerDecoderEventsIsEventSet(p->app_layer_events,
+ aled->event_id);
+}
+
+static int DetectAppLayerEventAppMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state, Signature *s,
+ SigMatch *m)
+{
+ SCEnter();
+ AppLayerDecoderEvents *decoder_events = NULL;
+ int r = 0;
+ DetectAppLayerEventData *aled = (DetectAppLayerEventData *)m->ctx;
+
+ if (r == 0) {
+ decoder_events = AppLayerParserGetDecoderEvents(f->alparser);
+ if (decoder_events != NULL &&
+ AppLayerDecoderEventsIsEventSet(decoder_events, aled->event_id)) {
+ r = 1;
+ }
+ }
+
+ SCReturnInt(r);
+}
+
+static DetectAppLayerEventData *DetectAppLayerEventParsePkt(const char *arg,
+ AppLayerEventType *event_type)
+{
+ DetectAppLayerEventData *aled;
+
+ int event_id = 0;
+ int r = 0;
+
+ r = AppLayerGetPktEventInfo(arg, &event_id);
+ if (r < 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-event keyword "
+ "supplied with packet based event - \"%s\" that isn't "
+ "supported yet.", arg);
+ return NULL;
+ }
+
+ aled = SCMalloc(sizeof(DetectAppLayerEventData));
+ if (unlikely(aled == NULL))
+ return NULL;
+ memset(aled,0x00,sizeof(*aled));
+ aled->event_id = event_id;
+ *event_type = APP_LAYER_EVENT_TYPE_PACKET;
+
+ return aled;
+}
+
+static int DetectAppLayerEventParseAppP2(DetectAppLayerEventData *data,
+ uint8_t *ipproto_bitarray,
+ AppLayerEventType *event_type)
+{
+ int event_id = 0;
+ const char *p_idx;
+ uint8_t ipproto;
+ char alproto_name[50];
+ int r = 0;
+
+ p_idx = strchr(data->arg, '.');
+ strlcpy(alproto_name, data->arg, p_idx - data->arg + 1);
+
+ if (ipproto_bitarray[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8)) {
+ ipproto = IPPROTO_TCP;
+ } else if (ipproto_bitarray[IPPROTO_UDP / 8] & 1 << (IPPROTO_UDP % 8)) {
+ ipproto = IPPROTO_UDP;
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "protocol %s is disabled", alproto_name);
+ return -1;
+ }
+
+ r = AppLayerParserGetEventInfo(ipproto, data->alproto,
+ p_idx + 1, &event_id, event_type);
+ if (r < 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-event keyword's "
+ "protocol \"%s\" doesn't have event \"%s\" registered",
+ alproto_name, p_idx + 1);
+ return -1;
+ }
+ data->event_id = event_id;
+
+ return 0;
+}
+
+static DetectAppLayerEventData *DetectAppLayerEventParseAppP1(const char *arg)
+{
+ /* period index */
+ DetectAppLayerEventData *aled;
+ AppProto alproto;
+ const char *p_idx;
+ char alproto_name[50];
+
+ p_idx = strchr(arg, '.');
+ /* + 1 for trailing \0 */
+ strlcpy(alproto_name, arg, p_idx - arg + 1);
+
+ alproto = AppLayerGetProtoByName(alproto_name);
+ if (alproto == ALPROTO_UNKNOWN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-event keyword "
+ "supplied with unknown protocol \"%s\"",
+ alproto_name);
+ return NULL;
+ }
+
+ aled = SCMalloc(sizeof(*aled));
+ if (unlikely(aled == NULL))
+ return NULL;
+ memset(aled, 0x00, sizeof(*aled));
+ aled->alproto = alproto;
+ aled->arg = SCStrdup(arg);
+ if (aled->arg == NULL) {
+ SCFree(aled);
+ return NULL;
+ }
+
+ return aled;
+}
+
+static DetectAppLayerEventData *DetectAppLayerEventParse(const char *arg,
+ AppLayerEventType *event_type)
+{
+ *event_type = 0;
+
+ if (arg == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-event keyword supplied "
+ "with no arguments. This keyword needs an argument.");
+ return NULL;
+ }
+
+ while (*arg != '\0' && isspace((unsigned char)*arg))
+ arg++;
+
+ if (strchr(arg, '.') == NULL) {
+ return DetectAppLayerEventParsePkt(arg, event_type);
+ } else {
+ return DetectAppLayerEventParseAppP1(arg);
+ }
+}
+
+static int DetectAppLayerEventSetupP2(Signature *s,
+ SigMatch *sm)
+{
+ AppLayerEventType event_type = 0;
+
+ if (DetectAppLayerEventParseAppP2((DetectAppLayerEventData *)sm->ctx, s->proto.proto,
+ &event_type) < 0) {
+ /* DetectAppLayerEventParseAppP2 prints errors */
+ return -1;
+ }
+ if (event_type == APP_LAYER_EVENT_TYPE_GENERAL)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+ else
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_APP_EVENT);
+ /* We should have set this flag already in SetupP1 */
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ return 0;
+}
+
+static int DetectAppLayerEventSetupP1(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ DetectAppLayerEventData *data = NULL;
+ SigMatch *sm = NULL;
+ AppLayerEventType event_type;
+
+ data = DetectAppLayerEventParse(arg, &event_type);
+ if (data == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_APP_LAYER_EVENT;
+ sm->ctx = (SigMatchCtx *)data;
+
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ if (s->alproto != data->alproto) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains "
+ "conflicting keywords needing different alprotos");
+ goto error;
+ }
+ } else {
+ s->alproto = data->alproto;
+ }
+
+ if (event_type == APP_LAYER_EVENT_TYPE_PACKET) {
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ } else {
+ /* We push it to this list temporarily. We deal with
+ * these in DetectAppLayerEventPrepare(). */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_APP_EVENT);
+ s->flags |= SIG_FLAG_APPLAYER;
+ }
+
+ return 0;
+
+error:
+ if (data)
+ SCFree(data);
+ if (sm) {
+ sm->ctx = NULL;
+ SigMatchFree(sm);
+ }
+ return -1;
+}
+
+static void DetectAppLayerEventFree(void *ptr)
+{
+ DetectAppLayerEventData *data = (DetectAppLayerEventData *)ptr;
+ if (data->arg != NULL)
+ SCFree(data->arg);
+
+ SCFree(ptr);
+
+ return;
+}
+
+int DetectAppLayerEventPrepare(Signature *s)
+{
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_APP_EVENT];
+ s->sm_lists[DETECT_SM_LIST_APP_EVENT] = NULL;
+ s->sm_lists_tail[DETECT_SM_LIST_APP_EVENT] = NULL;
+
+ while (sm != NULL) {
+ sm->next = sm->prev = NULL;
+ if (DetectAppLayerEventSetupP2(s, sm) < 0)
+ return -1;
+ sm = sm->next;
+ }
+
+ return 0;
+}
+
+/**********************************Unittests***********************************/
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "app-layer.h"
+
+#define APP_LAYER_EVENT_TEST_MAP_EVENT1 0
+#define APP_LAYER_EVENT_TEST_MAP_EVENT2 1
+#define APP_LAYER_EVENT_TEST_MAP_EVENT3 2
+#define APP_LAYER_EVENT_TEST_MAP_EVENT4 3
+#define APP_LAYER_EVENT_TEST_MAP_EVENT5 4
+#define APP_LAYER_EVENT_TEST_MAP_EVENT6 5
+
+SCEnumCharMap app_layer_event_test_map[ ] = {
+ { "event1", APP_LAYER_EVENT_TEST_MAP_EVENT1 },
+ { "event2", APP_LAYER_EVENT_TEST_MAP_EVENT2 },
+ { "event3", APP_LAYER_EVENT_TEST_MAP_EVENT3 },
+ { "event4", APP_LAYER_EVENT_TEST_MAP_EVENT4 },
+ { "event5", APP_LAYER_EVENT_TEST_MAP_EVENT5 },
+ { "event6", APP_LAYER_EVENT_TEST_MAP_EVENT6 },
+};
+
+static int DetectAppLayerEventTestGetEventInfo(const char *event_name,
+ int *event_id,
+ AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, app_layer_event_test_map);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "app-layer-event's test enum map table.", event_name);
+ /* this should be treated as fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_GENERAL;
+
+ return 0;
+}
+
+
+int DetectAppLayerEventTest01(void)
+{
+ AppLayerParserBackupParserTable();
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMTP,
+ DetectAppLayerEventTestGetEventInfo);
+
+ AppLayerEventType event_type;
+ int result = 0;
+ uint8_t ipproto_bitarray[256 / 8];
+ memset(ipproto_bitarray, 0, sizeof(ipproto_bitarray));
+ ipproto_bitarray[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
+
+ DetectAppLayerEventData *aled = DetectAppLayerEventParse("smtp.event1",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_SMTP ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT1) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ AppLayerParserRestoreParserTable();
+ if (aled != NULL)
+ DetectAppLayerEventFree(aled);
+ return result;
+}
+
+int DetectAppLayerEventTest02(void)
+{
+ AppLayerParserBackupParserTable();
+
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMTP,
+ DetectAppLayerEventTestGetEventInfo);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP,
+ DetectAppLayerEventTestGetEventInfo);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMB,
+ DetectAppLayerEventTestGetEventInfo);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_FTP,
+ DetectAppLayerEventTestGetEventInfo);
+
+ AppLayerEventType event_type;
+ int result = 0;
+ uint8_t ipproto_bitarray[256 / 8];
+ memset(ipproto_bitarray, 0, sizeof(ipproto_bitarray));
+ ipproto_bitarray[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
+
+ DetectAppLayerEventData *aled = DetectAppLayerEventParse("smtp.event1",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_SMTP ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT1) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ aled = DetectAppLayerEventParse("smtp.event4",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_SMTP ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT4) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ aled = DetectAppLayerEventParse("http.event2",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_HTTP ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT2) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ aled = DetectAppLayerEventParse("smb.event3",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_SMB ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT3) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ aled = DetectAppLayerEventParse("ftp.event5",
+ &event_type);
+ if (aled == NULL)
+ goto end;
+ if (DetectAppLayerEventParseAppP2(aled, ipproto_bitarray, &event_type) < 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (aled->alproto != ALPROTO_FTP ||
+ aled->event_id != APP_LAYER_EVENT_TEST_MAP_EVENT5) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ AppLayerParserRestoreParserTable();
+ if (aled != NULL)
+ DetectAppLayerEventFree(aled);
+ return result;
+}
+
+int DetectAppLayerEventTest03(void)
+{
+ int result = 0;
+ ThreadVars tv;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ Packet *p = NULL;
+ Flow *f = NULL;
+ TcpSession ssn;
+ TcpStream stream_ts, stream_tc;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ uint8_t buf_ts[] = "GET /index.html HTTP/1.1\r\n"
+ "Host: 127.0.0.1\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100402 Firefox/3.6.3\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
+ "Accept-Language: en-us,en;q=0.5\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Keep-Alive: 115\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n";
+ uint8_t buf_tc[] = "HTTP/1.1 200 OK\r\n"
+ "Date: Fri, 22 Oct 2010 12:31:08 GMT\r\n"
+ "Server: Apache/2.2.15 (Unix) DAV/2\r\n"
+ "Last-Modified: Sat, 20 Nov 2004 20:16:24 GMT\r\n"
+ "ETag: \"ab8486-2c-3e9564c23b600\"\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Content-Length: 44\r\n"
+ "Keep-Alive: timeout=5, max=100\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<html><body><h1>It works!</h1></body></html>";
+
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&stream_ts, 0, sizeof(TcpStream));
+ memset(&stream_tc, 0, sizeof(TcpStream));
+
+ ssn.data_first_seen_dir = STREAM_TOSERVER;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-event: applayer_mismatch_protocol_both_directions; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ FLOW_INITIALIZE(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->flags |= FLOW_IPV4;
+
+ p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ goto end;
+ p->flow = f;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+
+ ra_ctx = StreamTcpReassembleInitThreadCtx(&tv);
+ if (ra_ctx == NULL)
+ goto end;
+ StreamTcpInitConfig(TRUE);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_ts, buf_ts,
+ sizeof(buf_ts), STREAM_TOSERVER | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_tc, buf_tc,
+ sizeof(buf_tc), STREAM_TOCLIENT | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+int DetectAppLayerEventTest04(void)
+{
+ int result = 0;
+ ThreadVars tv;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ Packet *p = NULL;
+ Flow *f = NULL;
+ TcpSession ssn;
+ TcpStream stream_ts, stream_tc;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ uint8_t buf_ts[] = "GET /index.html HTTP/1.1\r\n"
+ "Host: 127.0.0.1\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100402 Firefox/3.6.3\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
+ "Accept-Language: en-us,en;q=0.5\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Keep-Alive: 115\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n";
+ uint8_t buf_tc[] = "XTTP/1.1 200 OK\r\n"
+ "Date: Fri, 22 Oct 2010 12:31:08 GMT\r\n"
+ "Server: Apache/2.2.15 (Unix) DAV/2\r\n"
+ "Last-Modified: Sat, 20 Nov 2004 20:16:24 GMT\r\n"
+ "ETag: \"ab8486-2c-3e9564c23b600\"\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Content-Length: 44\r\n"
+ "Keep-Alive: timeout=5, max=100\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<html><body><h1>It works!</h1></body></html>";
+
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&stream_ts, 0, sizeof(TcpStream));
+ memset(&stream_tc, 0, sizeof(TcpStream));
+
+ ssn.data_first_seen_dir = STREAM_TOSERVER;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-event: applayer_detect_protocol_only_one_direction; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ FLOW_INITIALIZE(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->flags |= FLOW_IPV4;
+
+ p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ goto end;
+ p->flow = f;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+
+ ra_ctx = StreamTcpReassembleInitThreadCtx(&tv);
+ if (ra_ctx == NULL)
+ goto end;
+ StreamTcpInitConfig(TRUE);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_ts, buf_ts,
+ sizeof(buf_ts), STREAM_TOSERVER | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_tc, buf_tc,
+ sizeof(buf_tc), STREAM_TOCLIENT | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+int DetectAppLayerEventTest05(void)
+{
+ int result = 0;
+ ThreadVars tv;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ Packet *p = NULL;
+ Flow *f = NULL;
+ TcpSession ssn;
+ TcpStream stream_ts, stream_tc;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ uint8_t buf_ts[] = "GET /index.html HTTP/1.1\r\n"
+ "Host: 127.0.0.1\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100402 Firefox/3.6.3\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
+ "Accept-Language: en-us,en;q=0.5\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Keep-Alive: 115\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n";
+ /* tls */
+ uint8_t buf_tc[] = {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0xd3, 0x6f, 0x1f, 0x63, 0x82,
+ 0x8d, 0x75, 0x77, 0x8c, 0x91, 0xbc, 0xa1, 0x3d,
+ 0xbb, 0xe1, 0xb5, 0xd3, 0x31, 0x92, 0x59, 0x2b,
+ 0x2c, 0x43, 0x96, 0xa3, 0xaa, 0x23, 0x92, 0xd0,
+ 0x91, 0x2a, 0x5e, 0x10, 0x5b, 0xc8, 0xc1, 0xe2,
+ 0xd3, 0x5c, 0x8b, 0x8c, 0x91, 0x9e, 0xc2, 0xf2,
+ 0x9c, 0x3c, 0x4f, 0x37, 0x1e, 0x20, 0x5e, 0x33,
+ 0xd5, 0xf0, 0xd6, 0xaf, 0x89, 0xf5, 0xcc, 0xb2,
+ 0xcf, 0xc1, 0x60, 0x3a, 0x46, 0xd5, 0x4e, 0x2a,
+ 0xb6, 0x6a, 0xb9, 0xfc, 0x32, 0x8b, 0xe0, 0x6e,
+ 0xa0, 0xed, 0x25, 0xa0, 0xa4, 0x82, 0x81, 0x73,
+ 0x90, 0xbf, 0xb5, 0xde, 0xeb, 0x51, 0x8d, 0xde,
+ 0x5b, 0x6f, 0x94, 0xee, 0xba, 0xe5, 0x69, 0xfa,
+ 0x1a, 0x80, 0x30, 0x54, 0xeb, 0x12, 0x01, 0xb9,
+ 0xfe, 0xbf, 0x82, 0x95, 0x01, 0x7b, 0xb0, 0x97,
+ 0x14, 0xc2, 0x06, 0x3c, 0x69, 0xfb, 0x1c, 0x66,
+ 0x47, 0x17, 0xd9, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xf6, 0xbc,
+ 0x0d, 0x6f, 0xe8, 0xbb, 0xaa, 0xbf, 0x14, 0xeb,
+ 0x7b, 0xcc, 0x6c, 0x28, 0xb0, 0xfc, 0xa6, 0x01,
+ 0x2a, 0x97, 0x96, 0x17, 0x5e, 0xe8, 0xb4, 0x4e,
+ 0x78, 0xc9, 0x04, 0x65, 0x53, 0xb6, 0x93, 0x3d,
+ 0xeb, 0x44, 0xee, 0x86, 0xf9, 0x80, 0x49, 0x45,
+ 0x21, 0x34, 0xd1, 0xee, 0xc8, 0x9c,
+ };
+
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&stream_ts, 0, sizeof(TcpStream));
+ memset(&stream_tc, 0, sizeof(TcpStream));
+
+ ssn.data_first_seen_dir = STREAM_TOSERVER;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-event: applayer_mismatch_protocol_both_directions; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ FLOW_INITIALIZE(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->flags |= FLOW_IPV4;
+
+ p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ goto end;
+ p->flow = f;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+
+ ra_ctx = StreamTcpReassembleInitThreadCtx(&tv);
+ if (ra_ctx == NULL)
+ goto end;
+ StreamTcpInitConfig(TRUE);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_ts, buf_ts,
+ sizeof(buf_ts), STREAM_TOSERVER | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (AppLayerHandleTCPData(&tv, ra_ctx, p, f, &ssn, &stream_tc, buf_tc,
+ sizeof(buf_tc), STREAM_TOCLIENT | STREAM_START) < 0) {
+ printf("AppLayerHandleTCPData failure\n");
+ goto end;
+ }
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for "app-layer-event" keyword.
+ */
+void DetectAppLayerEventRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectAppLayerEventTest01", DetectAppLayerEventTest01, 1);
+ UtRegisterTest("DetectAppLayerEventTest02", DetectAppLayerEventTest02, 1);
+ UtRegisterTest("DetectAppLayerEventTest03", DetectAppLayerEventTest03, 1);
+ UtRegisterTest("DetectAppLayerEventTest04", DetectAppLayerEventTest04, 1);
+ UtRegisterTest("DetectAppLayerEventTest05", DetectAppLayerEventTest05, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-app-layer-event.h b/framework/src/suricata/src/detect-app-layer-event.h
new file mode 100644
index 00000000..a3ed6088
--- /dev/null
+++ b/framework/src/suricata/src/detect-app-layer-event.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_APP_LAYER_EVENT_H__
+#define __DETECT_APP_LAYER_EVENT_H__
+
+typedef struct DetectAppLayerEventData_ {
+ AppProto alproto;
+ int event_id;
+
+ char *arg;
+} DetectAppLayerEventData;
+
+int DetectAppLayerEventPrepare(Signature *s);
+void DetectAppLayerEventRegister(void);
+
+#endif /* __DETECT_APP_LAYER_EVENT_H__ */
diff --git a/framework/src/suricata/src/detect-app-layer-protocol.c b/framework/src/suricata/src/detect-app-layer-protocol.c
new file mode 100644
index 00000000..65af621e
--- /dev/null
+++ b/framework/src/suricata/src/detect-app-layer-protocol.c
@@ -0,0 +1,407 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-app-layer-protocol.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+void DetectAppLayerProtocolRegisterTests(void);
+
+int DetectAppLayerProtocolMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state,
+ Signature *s, SigMatch *m)
+{
+ int r = 0;
+ DetectAppLayerProtocolData *data = (DetectAppLayerProtocolData *)m->ctx;
+
+ r = (data->negated) ? (f->alproto != data->alproto) :
+ (f->alproto == data->alproto);
+
+ return r;
+}
+
+static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg)
+{
+ DetectAppLayerProtocolData *data;
+ AppProto alproto = ALPROTO_UNKNOWN;
+ uint8_t negated = 0;
+
+ if (arg == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-protocol keyword "
+ "supplied with no arguments. This keyword needs "
+ "an argument.");
+ return NULL;
+ }
+
+ while (*arg != '\0' && isspace((unsigned char)*arg))
+ arg++;
+
+ if (arg[0] == '!') {
+ negated = 1;
+ arg++;
+ }
+
+ while (*arg != '\0' && isspace((unsigned char)*arg))
+ arg++;
+
+ alproto = AppLayerGetProtoByName((char *)arg);
+ if (alproto == ALPROTO_UNKNOWN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-protocol "
+ "keyword supplied with unknown protocol \"%s\"", arg);
+ return NULL;
+ }
+
+ data = SCMalloc(sizeof(DetectAppLayerProtocolData));
+ if (unlikely(data == NULL))
+ return NULL;
+ data->alproto = alproto;
+ data->negated = negated;
+
+ return data;
+}
+
+int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx, Signature *s,
+ char *arg)
+{
+ DetectAppLayerProtocolData *data = NULL;
+ SigMatch *sm = NULL;
+
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Either we already "
+ "have the rule match on an app layer protocol set through "
+ "other keywords that match on this protocol, or have "
+ "already seen a non-negated app-layer-protocol.");
+ goto error;
+ }
+
+ data = DetectAppLayerProtocolParse(arg);
+ if (data == NULL)
+ goto error;
+
+ if (!data->negated)
+ s->alproto = data->alproto;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_APP_LAYER_PROTOCOL;
+ sm->ctx = (void *)data;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ return 0;
+
+error:
+ if (data != NULL)
+ SCFree(data);
+ return -1;
+}
+
+void DetectAppLayerProtocolFree(void *ptr)
+{
+ SCFree(ptr);
+
+ return;
+}
+
+void DetectAppLayerProtocolRegister(void)
+{
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].name = "app-layer-protocol";
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Match = NULL;
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].AppLayerMatch =
+ DetectAppLayerProtocolMatch;
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Setup =
+ DetectAppLayerProtocolSetup;
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Free =
+ DetectAppLayerProtocolFree;
+ sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].RegisterTests =
+ DetectAppLayerProtocolRegisterTests;
+
+ return;
+}
+
+/**********************************Unittests***********************************/
+
+#ifdef UNITTESTS
+
+int DetectAppLayerProtocolTest01(void)
+{
+ int result = 0;
+
+ DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("http");
+ if (data == NULL)
+ goto end;
+ if (data->alproto != ALPROTO_HTTP || data->negated) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (data != NULL)
+ DetectAppLayerProtocolFree(data);
+ return result;
+}
+
+int DetectAppLayerProtocolTest02(void)
+{
+ int result = 0;
+
+ DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("!http");
+ if (data == NULL)
+ goto end;
+ if (data->alproto != ALPROTO_HTTP || !data->negated) {
+ printf("test failure. Holding wrong state\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (data != NULL)
+ DetectAppLayerProtocolFree(data);
+ return result;
+}
+
+int DetectAppLayerProtocolTest03(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectAppLayerProtocolData *data = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-protocol:http; sid:1;)");
+ if (s->alproto != ALPROTO_HTTP) {
+ printf("signature alproto should be http\n");
+ goto end;
+ }
+ data = (DetectAppLayerProtocolData *)s->sm_lists[DETECT_SM_LIST_AMATCH]->ctx;
+ if (data->alproto != ALPROTO_HTTP || data->negated) {
+ printf("if (data->alproto != ALPROTO_HTTP || data->negated)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest04(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectAppLayerProtocolData *data = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-protocol:!http; sid:1;)");
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ printf("signature alproto should be unknown\n");
+ goto end;
+ }
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ printf("if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL)\n");
+ goto end;
+ }
+ data = (DetectAppLayerProtocolData*)s->sm_lists[DETECT_SM_LIST_AMATCH]->ctx;
+ if (data == NULL) {
+ printf("if (data == NULL)\n");
+ goto end;
+ }
+ if (data->alproto != ALPROTO_HTTP || !data->negated) {
+ printf("if (data->alproto != ALPROTO_HTTP || !data->negated)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest05(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-protocol:!http; app-layer-protocol:!smtp; sid:1;)");
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ printf("signature alproto should be unknown\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest06(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert http any any -> any any "
+ "(app-layer-protocol:smtp; sid:1;)");
+ if (s != NULL) {
+ printf("if (s != NULL)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest07(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert http any any -> any any "
+ "(app-layer-protocol:!smtp; sid:1;)");
+ if (s != NULL) {
+ printf("if (s != NULL)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest08(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-protocol:!smtp; app-layer-protocol:http; sid:1;)");
+ if (s != NULL) {
+ printf("if (s != NULL)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectAppLayerProtocolTest09(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(app-layer-protocol:http; app-layer-protocol:!smtp; sid:1;)");
+ if (s != NULL) {
+ printf("if (s != NULL)\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectAppLayerProtocolRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectAppLayerProtocolTest01", DetectAppLayerProtocolTest01, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest02", DetectAppLayerProtocolTest02, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest03", DetectAppLayerProtocolTest03, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest04", DetectAppLayerProtocolTest04, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest05", DetectAppLayerProtocolTest05, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest06", DetectAppLayerProtocolTest06, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest07", DetectAppLayerProtocolTest07, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest08", DetectAppLayerProtocolTest08, 1);
+ UtRegisterTest("DetectAppLayerProtocolTest09", DetectAppLayerProtocolTest09, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-app-layer-protocol.h b/framework/src/suricata/src/detect-app-layer-protocol.h
new file mode 100644
index 00000000..616c4f2a
--- /dev/null
+++ b/framework/src/suricata/src/detect-app-layer-protocol.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_APP_LAYER_PROTOCOL__H__
+#define __DETECT_APP_LAYER_PROTOCOL__H__
+
+typedef struct DetectAppLayerProtocolData_ {
+ AppProto alproto;
+ uint8_t negated;
+} DetectAppLayerProtocolData;
+
+void DetectAppLayerProtocolRegister(void);
+
+#endif /* __DETECT_APP_LAYER_PROTOCOL__H__ */
diff --git a/framework/src/suricata/src/detect-asn1.c b/framework/src/suricata/src/detect-asn1.c
new file mode 100644
index 00000000..69d90829
--- /dev/null
+++ b/framework/src/suricata/src/detect-asn1.c
@@ -0,0 +1,1366 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-asn1.c
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements "asn1" keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow.h"
+#include "detect-asn1.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-decode-asn1.h"
+
+/* delimiters for functions/arguments */
+const char *ASN_DELIM = " \t,\n";
+
+int DetectAsn1Match(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectAsn1Setup (DetectEngineCtx *, Signature *, char *);
+void DetectAsn1RegisterTests(void);
+void DetectAsn1Free(void *);
+
+/**
+ * \brief Registration function for asn1
+ */
+void DetectAsn1Register(void)
+{
+ sigmatch_table[DETECT_ASN1].name = "asn1";
+ sigmatch_table[DETECT_ASN1].Match = DetectAsn1Match;
+ sigmatch_table[DETECT_ASN1].Setup = DetectAsn1Setup;
+ sigmatch_table[DETECT_ASN1].Free = DetectAsn1Free;
+ sigmatch_table[DETECT_ASN1].RegisterTests = DetectAsn1RegisterTests;
+
+ return;
+}
+
+/**
+ * \brief The main checks are done here
+ * This function implements the detection of the following options:
+ * - oversize_length
+ * - bitstring_overflow
+ * - double_overflow
+ * We can add more checks here easily since we have all the data of the
+ * node avaliable. If we need all the tree, we can just pass the
+ * ASN1 ctx as argument and perform the checks here
+ * \param node pointer to the Asn1Node to inspect
+ * \param ad pointer to the parsed options of the asn1 keyword (which hold the
+ * checks that we want to perform, and the lenght of oversize check
+ * \retval 1 if any of the options match, 0 if not
+ */
+static uint8_t DetectAsn1Checks(Asn1Node *node, const DetectAsn1Data *ad)
+{
+
+ /* oversize_length will check if a node has a length greater than
+ * the user supplied length */
+ if (ad->flags & ASN1_OVERSIZE_LEN) {
+ if (node->len.len > ad->oversize_length
+ || node->data.len > ad->oversize_length)
+ return 1;
+ }
+
+ /* 8.6 */
+ /* bitstring_overflow check a malformed option where the number of bits
+ * to ignore is greater than the length decoded (in bits) */
+ if (ad->flags & ASN1_BITSTRING_OVF) {
+ if (node->id.class_tag == ASN1_BER_CLASS_UNIV &&
+ node->id.tag_num == ASN1_UNITAG_BIT_STRING &&
+ node->id.tag_type == ASN1_TAG_TYPE_PRIMITIVE)
+ {
+ if (node->len.len > 0 && node->data.ptr != NULL
+ && (node->len.len) * 8 < (uint8_t) *node->data.ptr)
+ {
+ return 1;
+ }
+ }
+ }
+
+ /* double_overflow checks a known issue that affect the MSASN1 library
+ * when decoding double/real types. If the endoding is ASCII,
+ * and the buffer is greater than 256, the array is overflown
+ */
+ if (ad->flags & ASN1_DOUBLE_OVF) {
+ if (node->id.class_tag == ASN1_BER_CLASS_UNIV &&
+ node->id.tag_num == ASN1_UNITAG_REAL &&
+ node->id.tag_type == ASN1_TAG_TYPE_PRIMITIVE)
+ {
+ if (node->len.len > 0 && node->data.ptr != NULL
+ && !((uint8_t) *node->data.ptr & 0xC0)
+ && (node->len.len > 256 || node->data.len > 256))
+ {
+ return 1;
+ }
+ }
+ }
+
+ /* Good to know :) */
+ return 0;
+}
+
+/**
+ * \brief This function will decode the asn1 data and inspect the resulting
+ * nodes to detect if any of the specified checks match this data
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectAsn1Data
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectAsn1Match(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s, const SigMatchCtx *ctx)
+{
+ uint8_t ret = 0;
+
+ if (p->payload_len == 0) {
+ /* No error, parser done, no data in bounds to decode */
+ return 0;
+ }
+
+ const DetectAsn1Data *ad = (const DetectAsn1Data *)ctx;
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ if (ad->flags & ASN1_ABSOLUTE_OFFSET) {
+ SCAsn1CtxInit(ac, p->payload + ad->absolute_offset,
+ p->payload_len - ad->absolute_offset);
+ } else if (ad->flags & ASN1_RELATIVE_OFFSET) {
+ SCAsn1CtxInit(ac, p->payload + ad->relative_offset,
+ p->payload_len - ad->relative_offset);
+ } else {
+ SCAsn1CtxInit(ac, p->payload, p->payload_len);
+ }
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ /* Ok, now we have all the data. Let's check the nodes */
+
+ if (ac->cur_frame > 0 || (ac->asn1_stack[0] != NULL && ac->asn1_stack[0]->id.ptr != NULL)) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+ ret = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ ret = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (ret == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse asn1 options passed via asn1: keyword
+ *
+ * \param asn1str Pointer to the user provided asn1 options
+ *
+ * \retval fd pointer to DetectAsn1Data on success
+ * \retval NULL on failure
+ */
+DetectAsn1Data *DetectAsn1Parse(char *asn1str)
+{
+ DetectAsn1Data *fd = NULL;
+ char *tok = NULL;
+ uint32_t ov_len = 0;
+ uint32_t abs_off = 0;
+ int32_t rel_off = 0;
+ uint8_t flags = 0;
+ char *saveptr = NULL;
+
+ tok = strtok_r(asn1str, ASN_DELIM, &saveptr);
+ if (tok == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed asn1 argument: %s",
+ asn1str);
+ return NULL;
+ }
+
+ while (tok != NULL) {
+ if (strcasecmp("bitstring_overflow", tok) == 0) {
+ /* No arg here, just set the flag */
+ flags |= ASN1_BITSTRING_OVF;
+ } else if (strcasecmp("double_overflow", tok) == 0) {
+ /* No arg here, just set the flag */
+ flags |= ASN1_DOUBLE_OVF;
+ } else if (strcasecmp("oversize_length", tok) == 0) {
+ flags |= ASN1_OVERSIZE_LEN;
+ /* get the param */
+ tok = strtok_r(NULL, ASN_DELIM, &saveptr);
+ if ( tok == NULL ||
+ ByteExtractStringUint32(&ov_len, 10, 0, tok) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for "
+ "oversize_length: %s", tok);
+ goto error;
+ }
+ } else if (strcasecmp("absolute_offset", tok) == 0) {
+ flags |= ASN1_ABSOLUTE_OFFSET;
+ /* get the param */
+ tok = strtok_r(NULL, ASN_DELIM, &saveptr);
+ if (tok == NULL ||
+ ByteExtractStringUint32(&abs_off, 10, 0, tok) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for "
+ "absolute_offset: %s", tok);
+ goto error;
+ }
+ } else if (strcasecmp("relative_offset",tok) == 0) {
+ flags |= ASN1_RELATIVE_OFFSET;
+ /* get the param */
+ tok = strtok_r(NULL, ASN_DELIM, &saveptr);
+ if (tok == NULL ||
+ ByteExtractStringInt32(&rel_off, 10, 0, tok) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for "
+ "relative_offset: %s", tok);
+ goto error;
+ }
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed asn1 argument: %s",
+ asn1str);
+ return NULL;
+ }
+ tok = strtok_r(NULL, ASN_DELIM, &saveptr);
+ }
+
+ fd = SCMalloc(sizeof(DetectAsn1Data));
+ if (unlikely(fd == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(fd, 0x00, sizeof(DetectAsn1Data));
+
+ fd->flags = flags;
+ fd->oversize_length = ov_len; /* Length argument if needed */
+ fd->absolute_offset = abs_off; /* Length argument if needed */
+ fd->relative_offset = rel_off; /* Length argument if needed */
+ return fd;
+
+error:
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed asn1 data into
+ * the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param asn1str pointer to the user provided asn1 options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, char *asn1str)
+{
+ DetectAsn1Data *ad = NULL;
+ SigMatch *sm = NULL;
+
+ ad = DetectAsn1Parse(asn1str);
+ if (ad == NULL) goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ASN1;
+ sm->ctx = (SigMatchCtx *)ad;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (ad != NULL) DetectAsn1Free(ad);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectAsn1Data
+ *
+ * \param ad pointer to DetectAsn1Data
+ */
+void DetectAsn1Free(void *ptr)
+{
+ DetectAsn1Data *ad = (DetectAsn1Data *)ptr;
+ SCFree(ad);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectAsn1TestParse01 check that we parse oversize_length correctly
+ */
+int DetectAsn1TestParse01(void)
+{
+ int result = 0;
+ char str[] = "oversize_length 1024";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL) {
+ if (ad->oversize_length == 1024 && (ad->flags & ASN1_OVERSIZE_LEN)) {
+ result = 1;
+ }
+ DetectAsn1Free(ad);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse02 check that we parse absolute_offset correctly
+ */
+int DetectAsn1TestParse02(void)
+{
+ int result = 0;
+ DetectAsn1Data *ad = NULL;
+ char str[] = "absolute_offset 1024";
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->absolute_offset == 1024
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET)) {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse03 check that we parse relative_offset correctly
+ */
+int DetectAsn1TestParse03(void)
+{
+ int result = 0;
+ char str[] = "relative_offset 1024";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->relative_offset == 1024
+ && (ad->flags & ASN1_RELATIVE_OFFSET)) {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse04 check that we parse bitstring_overflow correctly
+ */
+int DetectAsn1TestParse04(void)
+{
+ int result = 0;
+ char str[] = "bitstring_overflow";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF)) {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse05 check that we parse double_overflow correctly
+ */
+int DetectAsn1TestParse05(void)
+{
+ int result = 0;
+ char str[] = "double_overflow";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF)) {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse06 check that we fail if a needed arg is not given
+ */
+int DetectAsn1TestParse06(void)
+{
+ int result = 1;
+ char str[] = "absolute_offset";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL) {
+ DetectAsn1Free(ad);
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse07 check that we fail if a needed arg is not given
+ */
+int DetectAsn1TestParse07(void)
+{
+ int result = 1;
+ char str[] = "relative_offset";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL) {
+ DetectAsn1Free(ad);
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse08 check that we fail if a needed arg is not given
+ */
+int DetectAsn1TestParse08(void)
+{
+ int result = 1;
+ char str[] = "oversize_length";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL) {
+ DetectAsn1Free(ad);
+ result = 0;
+ }
+
+ return result;
+}
+
+
+
+/**
+ * \test DetectAsn1TestParse09 test that we break on invalid options
+ */
+int DetectAsn1TestParse09(void)
+{
+ int result = 1;
+ DetectAsn1Data *fd = NULL;
+ char str[] = "oversize_length 1024, lalala 360";
+
+ fd = DetectAsn1Parse(str);
+ if (fd != NULL) {
+ result = 0;
+ DetectAsn1Free(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse10 test that we break with a empty string
+ */
+int DetectAsn1TestParse10(void)
+{
+ int result = 1;
+ DetectAsn1Data *fd = NULL;
+ char str[] = "";
+
+ fd = DetectAsn1Parse(str);
+ if (fd != NULL) {
+ result = 0;
+ DetectAsn1Free(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse11 check for combinations of keywords
+ */
+int DetectAsn1TestParse11(void)
+{
+ int result = 0;
+ char str[] = "oversize_length 1024, relative_offset 10";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 1024
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && ad->relative_offset == 10
+ && (ad->flags & ASN1_RELATIVE_OFFSET))
+ {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse12 check for combinations of keywords
+ */
+int DetectAsn1TestParse12(void)
+{
+ int result = 0;
+ char str[] = "oversize_length 1024 absolute_offset 10";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 1024
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && ad->absolute_offset == 10
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse13 check for combinations of keywords
+ */
+int DetectAsn1TestParse13(void)
+{
+ int result = 0;
+ char str[] = "oversize_length 1024 absolute_offset 10, bitstring_overflow";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 1024
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && (ad->flags & ASN1_BITSTRING_OVF)
+ && ad->absolute_offset == 10
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse14 check for combinations of keywords
+ */
+int DetectAsn1TestParse14(void)
+{
+ int result = 0;
+ char str[] = "double_overflow, oversize_length 1024 absolute_offset 10,"
+ " bitstring_overflow";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 1024
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && (ad->flags & ASN1_BITSTRING_OVF)
+ && (ad->flags & ASN1_DOUBLE_OVF)
+ && ad->absolute_offset == 10
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestParse15 check for combinations of keywords
+ */
+int DetectAsn1TestParse15(void)
+{
+ int result = 0;
+ char str[] = "double_overflow, oversize_length 1024 relative_offset 10,"
+ " bitstring_overflow";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 1024
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && (ad->flags & ASN1_BITSTRING_OVF)
+ && (ad->flags & ASN1_DOUBLE_OVF)
+ && ad->relative_offset == 10
+ && (ad->flags & ASN1_RELATIVE_OFFSET))
+ {
+ DetectAsn1Free(ad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test01 Ensure that the checks work when they should
+ */
+int DetectAsn1Test01(void)
+{
+ int result = 0;
+ /* Match if any of the nodes after offset 0 has greater length than 10 */
+ char str[] = "oversize_length 132 absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 132
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ // Example from the specification X.690-0207 Appendix A.3
+ uint8_t *str = (uint8_t*) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = strlen((char *)str)-1;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ /* The first node has length 133, so it should match the oversize */
+ if (ac->cur_frame > 0) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (result == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ if (result == 0) {
+ printf("Error, oversize_length should match the first node: ");
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test02 Ensure that the checks work when they should
+ */
+int DetectAsn1Test02(void)
+{
+ int result = 0;
+ /* Match if any of the nodes has the bitstring overflow condition */
+ char str[] = "oversize_length 133, absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && ad->oversize_length == 133
+ && (ad->flags & ASN1_OVERSIZE_LEN)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ // Example from the specification X.690-0207 Appendix A.3
+ uint8_t *str = (uint8_t*) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = strlen((char *)str)-1;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ /* The first node has length 133, so it should match the oversize */
+ if (ac->cur_frame > 0) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result |= DetectAsn1Checks(node, ad);
+ }
+ }
+
+ /* Got a match? We don't have nodes greater than 133, it should not */
+ if (result == 1) {
+ printf("Error, oversize_length should not match"
+ " any of the nodes: ");
+ result = 0;
+ } else {
+ result = 1;
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test03 Ensure that the checks work when they should
+ */
+int DetectAsn1Test03(void)
+{
+ int result = 0;
+ /* Match if any of the nodes after offset 0 has a bitstring overflow */
+ char str[] = "bitstring_overflow, absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ /* Let's say tagnum bitstring, primitive, and as universal tag,
+ * and then length = 1 octet, but the next octet specify to ignore
+ * the last 256 bits... (let's match!) */
+ uint8_t *str = (uint8_t*) "\x03\x01\xFF";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = 3;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (result == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ if (result == 0) {
+ printf("Error, bitstring_overflow should match the first node: ");
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test04 Ensure that the checks work when they should
+ */
+int DetectAsn1Test04(void)
+{
+ int result = 0;
+ /* Match if any of the nodes after offset 0 has a bitstring overflow */
+ char str[] = "bitstring_overflow, absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ /* Let's say tagnum bitstring, primitive, and as universal tag,
+ * and then length = 1 octet, but the next octet specify to ignore
+ * the last 7 bits... (should not match) */
+ uint8_t *str = (uint8_t*) "\x03\x01\x07";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = 3;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (result == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ if (result == 1) {
+ printf("Error, bitstring_overflog should not match any node: ");
+ result = 0;
+ } else {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test05 Ensure that the checks work when they should
+ */
+int DetectAsn1Test05(void)
+{
+ int result = 0;
+ /* Match if any of the nodes after offset 0 has a double overflow */
+ char str[] = "double_overflow, absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ /* Let's say tag num 9 (type Real), and encoded as ASCII, with length
+ * 257, then we must match */
+ uint8_t str[261];
+ /* universal class, primitive type, tag_num = 9 (Data type Real) */
+ str[0] = '\x09';
+ /* length, definite form, 2 octets */
+ str[1] = '\x82';
+ /* length is the sum of the following octets (257): */
+ str[2] = '\xFE';
+ str[3] = '\x03';
+
+ /* Fill the content of the number */
+ uint16_t i = 4;
+ for (; i < 257;i++)
+ str[i] = '\x05';
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = 261;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (result == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ if (result == 0) {
+ printf("Error, double_overflow should match the first node: ");
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1Test06 Ensure that the checks work when they should
+ */
+int DetectAsn1Test06(void)
+{
+ int result = 0;
+ /* Match if any of the nodes after offset 0 has a double overflow */
+ char str[] = "double_overflow, absolute_offset 0";
+ DetectAsn1Data *ad = NULL;
+
+ ad = DetectAsn1Parse(str);
+ if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF)
+ && ad->absolute_offset == 0
+ && (ad->flags & ASN1_ABSOLUTE_OFFSET))
+ {
+ /* Let's say tag num 9 (type Real), and encoded as ASCII, with length
+ * 256, which fit in the buffer, so it should not match */
+ uint8_t str[260];
+ /* universal class, primitive type, tag_num = 9 (Data type Real) */
+ str[0] = '\x09';
+ /* length, definite form, 2 octets */
+ str[1] = '\x82';
+ /* length is the sum of the following octets (256): */
+ str[2] = '\xFE';
+ str[3] = '\x02';
+
+ /* Fill the content of the number */
+ uint16_t i = 4;
+ for (; i < 256;i++)
+ str[i] = '\x05';
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+
+ uint16_t len = 260;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+
+ if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) {
+ /* We spect at least one node */
+ uint16_t n_iter = 0;
+
+ for (; n_iter <= ac->cur_frame; n_iter++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter);
+
+ if (node == NULL || node->id.ptr == NULL)
+ continue; /* Should not happen */
+
+ result = DetectAsn1Checks(node, ad);
+ /* Got a match? */
+ if (result == 1)
+ break;
+ }
+ }
+
+ SCAsn1CtxDestroy(ac);
+ DetectAsn1Free(ad);
+
+ }
+
+ if (result == 1) {
+ printf("Error, double_overflow should not match any node: ");
+ result = 0 ;
+ } else {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestReal01 Ensure that all works together
+ */
+int DetectAsn1TestReal01(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen = strlen((char *)buf) - 1;
+
+ /* Check the start with AA (this is to test the relative_offset keyword) */
+ uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen2 = strlen((char *)buf2) - 1;
+
+ Packet *p[2];
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
+
+ if (p[0] == NULL || p[1] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
+ "content:\"Pablo\"; asn1:absolute_offset 0, "
+ "oversize_length 130; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
+ "content:\"AA\"; asn1:relative_offset 2, "
+ "oversize_length 130; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
+ "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[2][3] = {
+ /* packet 0 match sid 1 */
+ {1, 0, 0},
+ /* packet 1 match sid 2 */
+ {0, 1, 0}};
+ /* None of the packets should match sid 3 */
+
+ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
+
+ UTHFreePackets(p, 2);
+end:
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestReal02 Ensure that all works together
+ */
+int DetectAsn1TestReal02(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen = strlen((char *)buf) - 1;
+
+ /* Check the start with AA (this is to test the relative_offset keyword) */
+ uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen2 = strlen((char *)buf2) - 1;
+
+ Packet *p[2];
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
+
+ if (p[0] == NULL || p[1] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
+ "content:\"Pablo\"; asn1:absolute_offset 0, "
+ "oversize_length 140; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
+ "content:\"AA\"; asn1:relative_offset 2, "
+ "oversize_length 140; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
+ "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[2][3] = {
+ {0, 0, 0},
+ {0, 0, 0}};
+ /* None of the packets should match */
+
+ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
+
+ UTHFreePackets(p, 2);
+end:
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestReal03 Ensure that all works together
+ */
+int DetectAsn1TestReal03(void)
+{
+ int result = 0;
+ uint8_t buf[261] = "";
+ /* universal class, primitive type, tag_num = 9 (Data type Real) */
+ buf[0] = '\x09';
+ /* length, definite form, 2 octets */
+ buf[1] = '\x82';
+ /* length is the sum of the following octets (257): */
+ buf[2] = '\xFE';
+ buf[3] = '\x03';
+
+ /* Fill the content of the number */
+ uint16_t i = 4;
+ for (; i < 257;i++)
+ buf[i] = '\x05';
+
+ uint16_t buflen = 261;
+
+ /* Check the start with AA (this is to test the relative_offset keyword) */
+ uint8_t *buf2 = (uint8_t *) "AA\x03\x01\xFF";
+
+ uint16_t buflen2 = 5;
+
+ Packet *p[2] = { NULL, NULL };
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
+
+ if (p[0] == NULL || p[1] == NULL)
+ goto end;
+
+ char *sigs[3];
+ /* This should match the first packet */
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
+ "asn1:absolute_offset 0, double_overflow; sid:1;)";
+ /* This should match the second packet */
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
+ "asn1:relative_offset 2, bitstring_overflow,"
+ "oversize_length 140; sid:2;)";
+ /* This should match no packet */
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
+ "asn1: oversize_length 2000; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[2][3] = {{1, 0, 0},
+ {0, 1, 0}};
+
+ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
+
+ UTHFreePackets(p, 2);
+end:
+ return result;
+}
+
+/**
+ * \test DetectAsn1TestReal04 like the real test 02, but modified the
+ * relative offset to check negative offset values, in this case
+ * start decoding from -7 bytes respect the content match "John"
+ */
+int DetectAsn1TestReal04(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen = strlen((char *)buf) - 1;
+
+ /* Check the start with AA (this is to test the relative_offset keyword) */
+ uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ uint16_t buflen2 = strlen((char *)buf2) - 1;
+
+ Packet *p[2];
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
+
+ if (p[0] == NULL || p[1] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
+ "content:\"Pablo\"; asn1:absolute_offset 0, "
+ "oversize_length 140; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
+ "content:\"John\"; asn1:relative_offset -7, "
+ "oversize_length 140; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
+ "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[2][3] = {
+ {0, 0, 0},
+ {0, 0, 0}};
+ /* None of the packets should match */
+
+ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
+
+ UTHFreePackets(p, 2);
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectAsn1
+ */
+void DetectAsn1RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectAsn1TestParse01", DetectAsn1TestParse01, 1);
+ UtRegisterTest("DetectAsn1TestParse02", DetectAsn1TestParse02, 1);
+ UtRegisterTest("DetectAsn1TestParse03", DetectAsn1TestParse03, 1);
+
+ UtRegisterTest("DetectAsn1TestParse04", DetectAsn1TestParse04, 1);
+ UtRegisterTest("DetectAsn1TestParse05", DetectAsn1TestParse05, 1);
+ UtRegisterTest("DetectAsn1TestParse06", DetectAsn1TestParse06, 1);
+
+ UtRegisterTest("DetectAsn1TestParse07", DetectAsn1TestParse07, 1);
+ UtRegisterTest("DetectAsn1TestParse08", DetectAsn1TestParse08, 1);
+ UtRegisterTest("DetectAsn1TestParse09", DetectAsn1TestParse09, 1);
+
+ UtRegisterTest("DetectAsn1TestParse10", DetectAsn1TestParse10, 1);
+ UtRegisterTest("DetectAsn1TestParse11", DetectAsn1TestParse11, 1);
+ UtRegisterTest("DetectAsn1TestParse12", DetectAsn1TestParse12, 1);
+ UtRegisterTest("DetectAsn1TestParse13", DetectAsn1TestParse13, 1);
+ UtRegisterTest("DetectAsn1TestParse14", DetectAsn1TestParse14, 1);
+ UtRegisterTest("DetectAsn1TestParse15", DetectAsn1TestParse15, 1);
+ UtRegisterTest("DetectAsn1Test01 - oversize_len", DetectAsn1Test01, 1);
+ UtRegisterTest("DetectAsn1Test02 - oversize_len", DetectAsn1Test02, 1);
+ UtRegisterTest("DetectAsn1Test03 - bitstring_ovf", DetectAsn1Test03, 1);
+ UtRegisterTest("DetectAsn1Test04 - bitstring_ovf", DetectAsn1Test04, 1);
+ UtRegisterTest("DetectAsn1Test05 - double_ovf", DetectAsn1Test05, 1);
+ UtRegisterTest("DetectAsn1Test06 - double_ovf", DetectAsn1Test06, 1);
+ UtRegisterTest("DetectAsn1TestReal01", DetectAsn1TestReal01, 1);
+ UtRegisterTest("DetectAsn1TestReal02", DetectAsn1TestReal02, 1);
+ UtRegisterTest("DetectAsn1TestReal03", DetectAsn1TestReal03, 1);
+ UtRegisterTest("DetectAsn1TestReal04", DetectAsn1TestReal04, 1);
+
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-asn1.h b/framework/src/suricata/src/detect-asn1.h
new file mode 100644
index 00000000..c38d6439
--- /dev/null
+++ b/framework/src/suricata/src/detect-asn1.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-asn1.h
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements "asn1" keyword
+ */
+#ifndef __DETECT_ASN1_H__
+#define __DETECT_ASN1_H__
+
+
+/* Function check flags */
+#define ASN1_BITSTRING_OVF 0x01
+#define ASN1_DOUBLE_OVF 0x02
+#define ASN1_OVERSIZE_LEN 0x04
+#define ASN1_ABSOLUTE_OFFSET 0x10
+#define ASN1_RELATIVE_OFFSET 0x20
+
+typedef struct DetectAsn1Data_ {
+ uint8_t flags; /* flags indicating the checks loaded */
+ uint32_t oversize_length; /* Length argument if needed */
+ int32_t absolute_offset; /* Length argument if needed */
+ int32_t relative_offset; /* Length argument if needed */
+} DetectAsn1Data;
+
+/* prototypes */
+void DetectAsn1Register (void);
+
+#endif /* __DETECT_ASN1_H__ */
+
diff --git a/framework/src/suricata/src/detect-byte-extract.c b/framework/src/suricata/src/detect-byte-extract.c
new file mode 100644
index 00000000..bc8bdf2d
--- /dev/null
+++ b/framework/src/suricata/src/detect-byte-extract.c
@@ -0,0 +1,4897 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-bytejump.h"
+#include "detect-bytetest.h"
+#include "detect-byte-extract.h"
+#include "detect-isdataat.h"
+
+#include "app-layer-protos.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+/* the default value of endianess to be used, if none's specified */
+#define DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT DETECT_BYTE_EXTRACT_ENDIAN_BIG
+
+/* the base to be used if string mode is specified. These options would be
+ * specified in DetectByteParseData->base */
+#define DETECT_BYTE_EXTRACT_BASE_NONE 0
+#define DETECT_BYTE_EXTRACT_BASE_HEX 16
+#define DETECT_BYTE_EXTRACT_BASE_DEC 10
+#define DETECT_BYTE_EXTRACT_BASE_OCT 8
+
+/* the default value for multiplier. Either ways we always store a
+ * multiplier, 1 or otherwise, so that we can always multiply the extracted
+ * value and store it, instead of checking if a multiplier is set or not */
+#define DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT 1
+/* the min/max limit for multiplier */
+#define DETECT_BYTE_EXTRACT_MULTIPLIER_MIN_LIMIT 1
+#define DETECT_BYTE_EXTRACT_MULTIPLIER_MAX_LIMIT 65535
+
+/* the max no of bytes that can be extracted in string mode - (string, hex)
+ * (string, oct) or (string, dec) */
+#define STRING_MAX_BYTES_TO_EXTRACT_FOR_OCT 23
+#define STRING_MAX_BYTES_TO_EXTRACT_FOR_DEC 20
+#define STRING_MAX_BYTES_TO_EXTRACT_FOR_HEX 14
+/* the max no of bytes that can be extraced in non-string mode */
+#define NO_STRING_MAX_BYTES_TO_EXTRACT 8
+
+#define PARSE_REGEX "^" \
+ "\\s*([0-9]+)\\s*" \
+ ",\\s*(-?[0-9]+)\\s*" \
+ ",\\s*([^\\s,]+)\\s*" \
+ "(?:(?:,\\s*([^\\s,]+)\\s*)|(?:,\\s*([^\\s,]+)\\s+([^\\s,]+)\\s*))?" \
+ "(?:(?:,\\s*([^\\s,]+)\\s*)|(?:,\\s*([^\\s,]+)\\s+([^\\s,]+)\\s*))?" \
+ "(?:(?:,\\s*([^\\s,]+)\\s*)|(?:,\\s*([^\\s,]+)\\s+([^\\s,]+)\\s*))?" \
+ "(?:(?:,\\s*([^\\s,]+)\\s*)|(?:,\\s*([^\\s,]+)\\s+([^\\s,]+)\\s*))?" \
+ "(?:(?:,\\s*([^\\s,]+)\\s*)|(?:,\\s*([^\\s,]+)\\s+([^\\s,]+)\\s*))?" \
+ "$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectByteExtractMatch(ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, SigMatch *);
+int DetectByteExtractSetup(DetectEngineCtx *, Signature *, char *);
+void DetectByteExtractRegisterTests(void);
+void DetectByteExtractFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "byte_extract" keyword.
+ */
+void DetectByteExtractRegister(void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_BYTE_EXTRACT].name = "byte_extract";
+ sigmatch_table[DETECT_BYTE_EXTRACT].Match = NULL;
+ sigmatch_table[DETECT_BYTE_EXTRACT].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_BYTE_EXTRACT].Setup = DetectByteExtractSetup;
+ sigmatch_table[DETECT_BYTE_EXTRACT].Free = DetectByteExtractFree;
+ sigmatch_table[DETECT_BYTE_EXTRACT].RegisterTests = DetectByteExtractRegisterTests;
+
+ sigmatch_table[DETECT_BYTE_EXTRACT].flags |= SIGMATCH_PAYLOAD;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed "
+ "at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+ error:
+ return;
+}
+
+int DetectByteExtractDoMatch(DetectEngineThreadCtx *det_ctx, SigMatch *sm,
+ Signature *s, uint8_t *payload,
+ uint16_t payload_len, uint64_t *value,
+ uint8_t endian)
+{
+ DetectByteExtractData *data = (DetectByteExtractData *)sm->ctx;
+ uint8_t *ptr = NULL;
+ int32_t len = 0;
+ uint64_t val = 0;
+ int extbytes;
+
+ if (payload_len == 0) {
+ return 0;
+ }
+
+ /* Calculate the ptr value for the bytetest and length remaining in
+ * the packet from that point.
+ */
+ if (data->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE) {
+ SCLogDebug("relative, working with det_ctx->buffer_offset %"PRIu32", "
+ "data->offset %"PRIu32"", det_ctx->buffer_offset, data->offset);
+
+ ptr = payload + det_ctx->buffer_offset;
+ len = payload_len - det_ctx->buffer_offset;
+
+ ptr += data->offset;
+ len -= data->offset;
+
+ /* No match if there is no relative base */
+ if (len <= 0) {
+ return 0;
+ }
+ //PrintRawDataFp(stdout,ptr,len);
+ } else {
+ SCLogDebug("absolute, data->offset %"PRIu32"", data->offset);
+
+ ptr = payload + data->offset;
+ len = payload_len - data->offset;
+ }
+
+ /* Validate that the to-be-extracted is within the packet */
+ if (ptr < payload || data->nbytes > len) {
+ SCLogDebug("Data not within payload pkt=%p, ptr=%p, len=%"PRIu32", nbytes=%d",
+ payload, ptr, len, data->nbytes);
+ return 0;
+ }
+
+ /* Extract the byte data */
+ if (data->flags & DETECT_BYTE_EXTRACT_FLAG_STRING) {
+ extbytes = ByteExtractStringUint64(&val, data->base,
+ data->nbytes, (const char *)ptr);
+ if (extbytes <= 0) {
+ /* strtoull() return 0 if there is no numeric value in data string */
+ if (val == 0) {
+ SCLogDebug("No Numeric value");
+ return 0;
+ } else {
+ SCLogError(SC_ERR_INVALID_NUM_BYTES, "Error extracting %d "
+ "bytes of string data: %d", data->nbytes, extbytes);
+ return -1;
+ }
+ }
+ } else {
+ int endianness = (endian == DETECT_BYTE_EXTRACT_ENDIAN_BIG) ?
+ BYTE_BIG_ENDIAN : BYTE_LITTLE_ENDIAN;
+ extbytes = ByteExtractUint64(&val, endianness, data->nbytes, ptr);
+ if (extbytes != data->nbytes) {
+ SCLogError(SC_ERR_INVALID_NUM_BYTES, "Error extracting %d bytes "
+ "of numeric data: %d\n", data->nbytes, extbytes);
+ return 0;
+ }
+ }
+
+ /* Adjust the jump value based on flags */
+ val *= data->multiplier_value;
+ if (data->flags & DETECT_BYTE_EXTRACT_FLAG_ALIGN) {
+ if ((val % data->align_value) != 0) {
+ val += data->align_value - (val % data->align_value);
+ }
+ }
+
+ ptr += extbytes;
+
+ det_ctx->buffer_offset = ptr - payload;
+
+ *value = val;
+
+ return 1;
+}
+
+
+int DetectByteExtractMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, SigMatch *m)
+{
+ goto end;
+ end:
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief Used to parse byte_extract arg.
+ *
+ * \arg The argument to parse.
+ *
+ * \param bed On success an instance containing the parsed data.
+ * On failure, NULL.
+ */
+static inline DetectByteExtractData *DetectByteExtractParse(char *arg)
+{
+ DetectByteExtractData *bed = NULL;
+#define MAX_SUBSTRINGS 100
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i = 0;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, arg,
+ strlen(arg), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 19) {
+ SCLogError(SC_ERR_PCRE_PARSE, "parse error, ret %" PRId32
+ ", string \"%s\"", ret, arg);
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid arg to byte_extract : %s "
+ "for byte_extract", arg);
+ goto error;
+ }
+
+ bed = SCMalloc(sizeof(DetectByteExtractData));
+ if (unlikely(bed == NULL))
+ goto error;
+ memset(bed, 0, sizeof(DetectByteExtractData));
+
+ /* no of bytes to extract */
+ char nbytes_str[64] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, 1, nbytes_str, sizeof(nbytes_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg 1 for byte_extract");
+ goto error;
+ }
+ bed->nbytes = atoi(nbytes_str);
+
+ /* offset */
+ char offset_str[64] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, 2, offset_str, sizeof(offset_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg 2 for byte_extract");
+ goto error;
+ }
+ int offset = atoi(offset_str);
+ if (offset < -65535 || offset > 65535) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract offset invalid - %d. "
+ "The right offset range is -65535 to 65535", offset);
+ goto error;
+ }
+ bed->offset = offset;
+
+ /* var name */
+ char varname_str[256] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, 3, varname_str, sizeof(varname_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg 3 for byte_extract");
+ goto error;
+ }
+ bed->name = SCStrdup(varname_str);
+ if (bed->name == NULL)
+ goto error;
+
+ /* check out other optional args */
+ for (i = 4; i < ret; i++) {
+ char opt_str[64] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, i, opt_str, sizeof(opt_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg %d for byte_extract", i);
+ goto error;
+ }
+
+ if (strcmp("relative", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "relative specified more "
+ "than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_RELATIVE;
+ } else if (strcmp("multiplier", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "multiplier specified more "
+ "than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER;
+ i++;
+
+ char multiplier_str[16] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, i, multiplier_str, sizeof(multiplier_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg %d for byte_extract", i);
+ goto error;
+ }
+ int multiplier = atoi(multiplier_str);
+ if (multiplier < DETECT_BYTE_EXTRACT_MULTIPLIER_MIN_LIMIT ||
+ multiplier > DETECT_BYTE_EXTRACT_MULTIPLIER_MAX_LIMIT) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "multipiler_value invalid "
+ "- %d. The range is %d-%d",
+ multiplier,
+ DETECT_BYTE_EXTRACT_MULTIPLIER_MIN_LIMIT,
+ DETECT_BYTE_EXTRACT_MULTIPLIER_MAX_LIMIT);
+ goto error;
+ }
+ bed->multiplier_value = multiplier;
+ } else if (strcmp("big", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "endian option specified "
+ "more than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_ENDIAN;
+ bed->endian = DETECT_BYTE_EXTRACT_ENDIAN_BIG;
+ } else if (strcmp("little", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "endian option specified "
+ "more than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_ENDIAN;
+ bed->endian = DETECT_BYTE_EXTRACT_ENDIAN_LITTLE;
+ } else if (strcmp("dce", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "endian option specified "
+ "more than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_ENDIAN;
+ bed->endian = DETECT_BYTE_EXTRACT_ENDIAN_DCE;
+ } else if (strcmp("string", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_STRING) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "string specified more "
+ "than once for byte_extract");
+ goto error;
+ }
+ if (bed->base != DETECT_BYTE_EXTRACT_BASE_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "The right way to specify "
+ "base is (string, base) and not (base, string) "
+ "for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_STRING;
+ } else if (strcmp("hex", opt_str) == 0) {
+ if (!(bed->flags & DETECT_BYTE_EXTRACT_FLAG_STRING)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Base(hex) specified "
+ "without specifying string. The right way is "
+ "(string, base) and not (base, string)");
+ goto error;
+ }
+ if (bed->base != DETECT_BYTE_EXTRACT_BASE_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "More than one base "
+ "specified for byte_extract");
+ goto error;
+ }
+ bed->base = DETECT_BYTE_EXTRACT_BASE_HEX;
+ } else if (strcmp("oct", opt_str) == 0) {
+ if (!(bed->flags & DETECT_BYTE_EXTRACT_FLAG_STRING)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Base(oct) specified "
+ "without specifying string. The right way is "
+ "(string, base) and not (base, string)");
+ goto error;
+ }
+ if (bed->base != DETECT_BYTE_EXTRACT_BASE_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "More than one base "
+ "specified for byte_extract");
+ goto error;
+ }
+ bed->base = DETECT_BYTE_EXTRACT_BASE_OCT;
+ } else if (strcmp("dec", opt_str) == 0) {
+ if (!(bed->flags & DETECT_BYTE_EXTRACT_FLAG_STRING)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Base(dec) specified "
+ "without specifying string. The right way is "
+ "(string, base) and not (base, string)");
+ goto error;
+ }
+ if (bed->base != DETECT_BYTE_EXTRACT_BASE_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "More than one base "
+ "specified for byte_extract");
+ goto error;
+ }
+ bed->base = DETECT_BYTE_EXTRACT_BASE_DEC;
+ } else if (strcmp("align", opt_str) == 0) {
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_ALIGN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Align specified more "
+ "than once for byte_extract");
+ goto error;
+ }
+ bed->flags |= DETECT_BYTE_EXTRACT_FLAG_ALIGN;
+ i++;
+
+ char align_str[16] = "";
+ res = pcre_copy_substring((char *)arg, ov,
+ MAX_SUBSTRINGS, i, align_str, sizeof(align_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed "
+ "for arg %d in byte_extract", i);
+ goto error;
+ }
+ bed->align_value = atoi(align_str);
+ if (!(bed->align_value == 2 || bed->align_value == 4)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid align_value for "
+ "byte_extract - \"%d\"", bed->align_value);
+ goto error;
+ }
+ } else if (strcmp("", opt_str) == 0) {
+ ;
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid option - \"%s\" "
+ "specified in byte_extract", opt_str);
+ goto error;
+ }
+ } /* for (i = 4; i < ret; i++) */
+
+ /* validation */
+ if (!(bed->flags & DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER)) {
+ /* default value */
+ bed->multiplier_value = DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT;
+ }
+
+ if (bed->flags & DETECT_BYTE_EXTRACT_FLAG_STRING) {
+ if (bed->base == DETECT_BYTE_EXTRACT_BASE_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Base not specified for "
+ "byte_extract, though string was specified. "
+ "The right options are (string, hex), (string, oct) "
+ "or (string, dec)");
+ goto error;
+ }
+ if (bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract can't have "
+ "endian \"big\" or \"little\" specified along with "
+ "\"string\"");
+ goto error;
+ }
+ if (bed->base == DETECT_BYTE_EXTRACT_BASE_OCT) {
+ /* if are dealing with octal nos, the max no that can fit in a 8
+ * byte value is 01777777777777777777777 */
+ if (bed->nbytes > STRING_MAX_BYTES_TO_EXTRACT_FOR_OCT) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract can't process "
+ "more than %d bytes in \"string\" extraction",
+ STRING_MAX_BYTES_TO_EXTRACT_FOR_OCT);
+ goto error;
+ }
+ } else if (bed->base == DETECT_BYTE_EXTRACT_BASE_DEC) {
+ /* if are dealing with decimal nos, the max no that can fit in a 8
+ * byte value is 18446744073709551615 */
+ if (bed->nbytes > STRING_MAX_BYTES_TO_EXTRACT_FOR_DEC) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract can't process "
+ "more than %d bytes in \"string\" extraction",
+ STRING_MAX_BYTES_TO_EXTRACT_FOR_DEC);
+ goto error;
+ }
+ } else if (bed->base == DETECT_BYTE_EXTRACT_BASE_HEX) {
+ /* if are dealing with hex nos, the max no that can fit in a 8
+ * byte value is 0xFFFFFFFFFFFFFFFF */
+ if (bed->nbytes > STRING_MAX_BYTES_TO_EXTRACT_FOR_HEX) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract can't process "
+ "more than %d bytes in \"string\" extraction",
+ STRING_MAX_BYTES_TO_EXTRACT_FOR_HEX);
+ goto error;
+ }
+ } else {
+ ; // just a placeholder. we won't reach here.
+ }
+ } else {
+ if (bed->nbytes > NO_STRING_MAX_BYTES_TO_EXTRACT) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_extract can't process "
+ "more than %d bytes in \"non-string\" extraction",
+ NO_STRING_MAX_BYTES_TO_EXTRACT);
+ goto error;
+ }
+ /* if string has not been specified and no endian option has been
+ * specified, then set the default endian level of BIG */
+ if (!(bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN))
+ bed->endian = DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT;
+ }
+
+ return bed;
+ error:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return NULL;
+}
+
+/**
+ * \brief The setup function for the byte_extract keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param m Pointer to the head of the SigMatch for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectByteExtractSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ SigMatch *sm = NULL;
+ SigMatch *prev_pm = NULL;
+ DetectByteExtractData *data = NULL;
+ int ret = -1;
+
+ data = DetectByteExtractParse(arg);
+ if (data == NULL)
+ goto error;
+
+ int sm_list;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA) {
+ if (data->endian == DETECT_BYTE_EXTRACT_ENDIAN_DCE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "dce byte_extract specified "
+ "with file_data option set.");
+ goto error;
+ }
+ AppLayerHtpEnableResponseBodyCallback();
+ }
+ sm_list = s->list;
+ s->flags |= SIG_FLAG_APPLAYER;
+ if (data->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, s->sm_lists_tail[sm_list],
+ DETECT_PCRE, s->sm_lists_tail[sm_list]);
+ }
+ } else if (data->endian == DETECT_BYTE_EXTRACT_ENDIAN_DCE) {
+ if (data->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 12,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ }
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ } else if (data->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 168,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ if (sm_list != DETECT_SM_LIST_PMATCH)
+ s->flags |= SIG_FLAG_APPLAYER;
+ }
+
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ if (data->endian == DETECT_BYTE_EXTRACT_ENDIAN_DCE) {
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Non dce alproto sig has "
+ "byte_extract with dce enabled");
+ goto error;
+ }
+ s->alproto = ALPROTO_DCERPC;
+ if ((data->flags & DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ (data->base == DETECT_BYTE_EXTRACT_BASE_DEC) ||
+ (data->base == DETECT_BYTE_EXTRACT_BASE_HEX) ||
+ (data->base == DETECT_BYTE_EXTRACT_BASE_OCT) ) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. "
+ "A byte_jump keyword with dce holds other invalid modifiers.");
+ goto error;
+ }
+ }
+
+ SigMatch *prev_bed_sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[sm_list]);
+ if (prev_bed_sm == NULL)
+ data->local_id = 0;
+ else
+ data->local_id = ((DetectByteExtractData *)prev_bed_sm->ctx)->local_id + 1;
+ if (data->local_id > de_ctx->byte_extract_max_local_id)
+ de_ctx->byte_extract_max_local_id = data->local_id;
+
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_BYTE_EXTRACT;
+ sm->ctx = (void *)data;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+
+ if (!(data->flags & DETECT_BYTE_EXTRACT_FLAG_RELATIVE))
+ goto okay;
+
+ if (prev_pm == NULL)
+ goto okay;
+
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ okay:
+ ret = 0;
+ return ret;
+ error:
+ DetectByteExtractFree(data);
+ return ret;
+}
+
+/**
+ * \brief Used to free instances of DetectByteExtractData.
+ *
+ * \param ptr Instance of DetectByteExtractData to be freed.
+ */
+void DetectByteExtractFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectByteExtractData *bed = ptr;
+ if (bed->name != NULL)
+ SCFree((void *)bed->name);
+ SCFree(bed);
+ }
+
+ return;
+}
+
+/**
+ * \brief Lookup the SigMatch for a named byte_extract variable.
+ *
+ * \param arg The name of the byte_extract variable to lookup.
+ * \param s Pointer the signature to look in.
+ *
+ * \retval A pointer to the SigMatch if found, otherwise NULL.
+ */
+SigMatch *DetectByteExtractRetrieveSMVar(const char *arg, Signature *s)
+{
+ DetectByteExtractData *bed = NULL;
+ int list;
+
+ for (list = 0; list < DETECT_SM_LIST_MAX; list++) {
+ SigMatch *sm = s->sm_lists[list];
+ while (sm != NULL) {
+ if (sm->type == DETECT_BYTE_EXTRACT) {
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (strcmp(bed->name, arg) == 0) {
+ return sm;
+ }
+ }
+ sm = sm->next;
+ }
+ }
+
+ return NULL;
+}
+
+/*************************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+int DetectByteExtractTest01(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != 0 ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest02(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, relative");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_RELATIVE ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest03(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, multiplier 10");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != 10) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest04(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, relative, multiplier 10");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != 10) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest05(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, big");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_ENDIAN ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_BIG ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest06(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, little");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_ENDIAN ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_LITTLE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest07(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, dce");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_ENDIAN ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DCE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest08(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, string, hex");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest09(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, string, oct");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_OCT ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest10(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, string, dec");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_DEC ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest11(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_ALIGN ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest12(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, relative");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_ALIGN |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest13(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, relative, big");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_ALIGN |
+ DETECT_BYTE_EXTRACT_FLAG_ENDIAN |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_BIG ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest14(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, relative, dce");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_ALIGN |
+ DETECT_BYTE_EXTRACT_FLAG_ENDIAN |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DCE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest15(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, relative, little");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_ALIGN |
+ DETECT_BYTE_EXTRACT_FLAG_ENDIAN |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_LITTLE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest16(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, relative, little, multiplier 2");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_ALIGN |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_ENDIAN |
+ DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_LITTLE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 4 ||
+ bed->multiplier_value != 2) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest17(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "relative, little, "
+ "multiplier 2, string hex");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest18(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "relative, little, "
+ "multiplier 2, "
+ "relative");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest19(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "relative, little, "
+ "multiplier 2, "
+ "little");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest20(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "relative, "
+ "multiplier 2, "
+ "align 2");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest21(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "multiplier 2, "
+ "relative, "
+ "multiplier 2");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest22(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "string hex, "
+ "relative, "
+ "string hex");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest23(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "string hex, "
+ "relative, "
+ "string oct");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest24(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("24, 2, one, align 4, "
+ "string hex, "
+ "relative");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest25(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("9, 2, one, align 4, "
+ "little, "
+ "relative");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest26(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "little, "
+ "relative, "
+ "multiplier 65536");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest27(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, 2, one, align 4, "
+ "little, "
+ "relative, "
+ "multiplier 0");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest28(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("23, 2, one, string, oct");
+ if (bed == NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest29(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("24, 2, one, string, oct");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest30(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("20, 2, one, string, dec");
+ if (bed == NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest31(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("21, 2, one, string, dec");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest32(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("14, 2, one, string, hex");
+ if (bed == NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest33(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("15, 2, one, string, hex");
+ if (bed != NULL)
+ goto end;
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+int DetectByteExtractTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,2,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strncmp(bed->name, "two", cd->content_len) != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectPcreData *pd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; pcre:/asf/; "
+ "byte_extract:4,0,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_PCRE) {
+ result = 0;
+ goto end;
+ }
+ pd = (DetectPcreData *)sm->ctx;
+ if (pd->flags != DETECT_PCRE_RELATIVE_NEXT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectBytejumpData *bjd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; byte_jump:1,13; "
+ "byte_extract:4,0,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest37(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectContentData *ud = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; uricontent:\"two\"; "
+ "byte_extract:4,0,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)ud->content, "two", cd->content_len) != 0 ||
+ ud->flags & DETECT_CONTENT_NOCASE ||
+ ud->flags & DETECT_CONTENT_WITHIN ||
+ ud->flags & DETECT_CONTENT_DISTANCE ||
+ ud->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(ud->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ ud->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest38(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectContentData *ud = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; uricontent:\"two\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags !=DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)ud->content, "two", cd->content_len) != 0 ||
+ ud->flags & DETECT_CONTENT_NOCASE ||
+ ud->flags & DETECT_CONTENT_WITHIN ||
+ ud->flags & DETECT_CONTENT_DISTANCE ||
+ ud->flags & DETECT_CONTENT_FAST_PATTERN ||
+ ud->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ ud->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest39(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectContentData *ud = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; content:\"two\"; http_uri; "
+ "byte_extract:4,0,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)ud->content, "two", cd->content_len) != 0 ||
+ ud->flags & DETECT_CONTENT_NOCASE ||
+ ud->flags & DETECT_CONTENT_WITHIN ||
+ ud->flags & DETECT_CONTENT_DISTANCE ||
+ ud->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(ud->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ ud->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest40(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectContentData *ud = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; content:\"two\"; http_uri; "
+ "byte_extract:4,0,two,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags !=DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)ud->content, "two", cd->content_len) != 0 ||
+ ud->flags & DETECT_CONTENT_NOCASE ||
+ ud->flags & DETECT_CONTENT_WITHIN ||
+ ud->flags & DETECT_CONTENT_DISTANCE ||
+ ud->flags & DETECT_CONTENT_FAST_PATTERN ||
+ ud->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ ud->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest41(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "three") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 1) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest42(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectContentData *ud = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "uricontent: \"three\"; "
+ "byte_extract:4,0,four,string,hex,relative; "
+ "byte_extract:4,0,five,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "five") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 1) {
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)ud->content, "three", cd->content_len) != 0 ||
+ ud->flags & DETECT_CONTENT_NOCASE ||
+ ud->flags & DETECT_CONTENT_WITHIN ||
+ ud->flags & DETECT_CONTENT_DISTANCE ||
+ ud->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(ud->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ ud->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "four") != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_RELATIVE |
+ DETECT_BYTE_EXTRACT_FLAG_STRING) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest43(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "content: \"three\"; offset:two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "three", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_OFFSET_BE |
+ DETECT_CONTENT_OFFSET) ||
+ cd->offset != bed->local_id) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest44(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "content: \"four\"; offset:two; "
+ "content: \"five\"; offset:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_OFFSET_BE |
+ DETECT_CONTENT_OFFSET) ||
+ cd->offset != bed1->local_id) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "five", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_OFFSET_BE |
+ DETECT_CONTENT_OFFSET) ||
+ cd->offset != bed2->local_id) {
+ printf("five failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest45(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "content: \"three\"; depth:two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "three", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DEPTH_BE |
+ DETECT_CONTENT_DEPTH) ||
+ cd->depth != bed->local_id ||
+ cd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest46(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "content: \"four\"; depth:two; "
+ "content: \"five\"; depth:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DEPTH_BE |
+ DETECT_CONTENT_DEPTH) ||
+ cd->depth != bed1->local_id) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "five", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DEPTH_BE |
+ DETECT_CONTENT_DEPTH) ||
+ cd->depth != bed2->local_id) {
+ printf("five failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest47(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "content: \"three\"; distance:two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "three", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_DISTANCE) ||
+ cd->distance != bed->local_id ||
+ cd->offset != 0 ||
+ cd->depth != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest48(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "content: \"four\"; distance:two; "
+ "content: \"five\"; distance:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_DISTANCE |
+ DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->distance != bed1->local_id ||
+ cd->depth != 0 ||
+ cd->offset != 0) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "five", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_DISTANCE) ||
+ cd->distance != bed2->local_id ||
+ cd->depth != 0 ||
+ cd->offset != 0) {
+ printf("five failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest49(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "content: \"three\"; within:two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "three", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_WITHIN) ||
+ cd->within != bed->local_id ||
+ cd->offset != 0 ||
+ cd->depth != 0 ||
+ cd->distance != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest50(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "content: \"four\"; within:two; "
+ "content: \"five\"; within:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_WITHIN|
+ DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->within != bed1->local_id ||
+ cd->depth != 0 ||
+ cd->offset != 0 ||
+ cd->distance != 0) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "five", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_WITHIN) ||
+ cd->within != bed2->local_id ||
+ cd->depth != 0 ||
+ cd->offset != 0 ||
+ cd->distance != 0) {
+ printf("five failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest51(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_test: 2,=,10, two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags != DETECT_BYTETEST_OFFSET_BE ||
+ btd->value != 10 ||
+ btd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest52(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_test: 2,=,two,three; "
+ "byte_test: 3,=,10,three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags != (DETECT_BYTETEST_OFFSET_BE |
+ DETECT_BYTETEST_VALUE_BE) ||
+ btd->value != 0 ||
+ btd->offset != 1) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags != DETECT_BYTETEST_OFFSET_BE ||
+ btd->value != 10 ||
+ btd->offset != 1) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest53(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed = NULL;
+ DetectBytejumpData *bjd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_jump: 2,two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 0 ||
+ strcmp(bed->name, "two") != 0 ||
+ bed->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest54(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectBytejumpData *bjd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_jump: 2,two; "
+ "byte_jump: 3,three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 1) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest55(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing byte_extract\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_extract:4,0,four,string,hex; "
+ "byte_extract:4,0,five,string,hex; "
+ "content: \"four\"; within:two; distance:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed: ");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_DISTANCE |
+ DETECT_CONTENT_WITHIN) ||
+ cd->within != bed1->local_id ||
+ cd->distance != bed2->local_id) {
+ printf("four failed: ");
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest56(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "uricontent:\"urione\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_extract:4,0,four,string,hex; "
+ "byte_extract:4,0,five,string,hex; "
+ "content: \"four\"; within:two; distance:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "urione", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_DISTANCE |
+ DETECT_CONTENT_WITHIN) ||
+ cd->within != bed1->local_id ||
+ cd->distance != bed2->local_id ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest57(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectByteExtractData *bed2 = NULL;
+ DetectByteExtractData *bed3 = NULL;
+ DetectByteExtractData *bed4 = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "uricontent: \"urione\"; "
+ "byte_extract:4,0,two,string,hex,relative; "
+ "byte_extract:4,0,three,string,hex,relative; "
+ "byte_extract:4,0,four,string,hex,relative; "
+ "byte_extract:4,0,five,string,hex,relative; "
+ "uricontent: \"four\"; within:two; distance:three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "urione", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed2 = (DetectByteExtractData *)sm->ctx;
+ if (bed2->local_id != 1) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed3 = (DetectByteExtractData *)sm->ctx;
+ if (bed3->local_id != 2) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed4 = (DetectByteExtractData *)sm->ctx;
+ if (bed4->local_id != 3) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (strncmp((char *)cd->content, "four", cd->content_len) != 0 ||
+ cd->flags != (DETECT_CONTENT_DISTANCE_BE |
+ DETECT_CONTENT_WITHIN_BE |
+ DETECT_CONTENT_DISTANCE |
+ DETECT_CONTENT_WITHIN) ||
+ cd->within != bed1->local_id ||
+ cd->distance != bed2->local_id) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest58(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectBytejumpData *bjd = NULL;
+ DetectIsdataatData *isdd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_jump: 2,two; "
+ "byte_jump: 3,three; "
+ "isdataat: three; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 1) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isdd = (DetectIsdataatData *)sm->ctx;
+ if (isdd->flags != ISDATAAT_OFFSET_BE ||
+ isdd->dataat != 1) {
+ printf("isdataat failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest59(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectBytejumpData *bjd = NULL;
+ DetectIsdataatData *isdd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex; "
+ "byte_extract:4,0,three,string,hex; "
+ "byte_jump: 2,two; "
+ "byte_jump: 3,three; "
+ "isdataat: three,relative; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != DETECT_BYTE_EXTRACT_FLAG_STRING ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 0) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags != DETECT_BYTEJUMP_OFFSET_BE ||
+ bjd->offset != 1) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isdd = (DetectIsdataatData *)sm->ctx;
+ if (isdd->flags != (ISDATAAT_OFFSET_BE |
+ ISDATAAT_RELATIVE) ||
+ isdd->dataat != 1) {
+ printf("isdataat failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest60(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectIsdataatData *isdd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex,relative; "
+ "uricontent: \"three\"; "
+ "byte_extract:4,0,four,string,hex,relative; "
+ "isdataat: two; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isdd = (DetectIsdataatData *)sm->ctx;
+ if (isdd->flags != (ISDATAAT_OFFSET_BE) ||
+ isdd->dataat != bed1->local_id) {
+ printf("isdataat failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ strncmp((char *)cd->content, "three", cd->content_len) != 0) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "four") != 0 ||
+ bed1->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest61(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *cd = NULL;
+ DetectByteExtractData *bed1 = NULL;
+ DetectIsdataatData *isdd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "byte_extract:4,0,two,string,hex,relative; "
+ "uricontent: \"three\"; "
+ "byte_extract:4,0,four,string,hex,relative; "
+ "isdataat: four, relative; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES ||
+ strncmp((char *)cd->content, "one", cd->content_len) != 0 ||
+ cd->flags & DETECT_CONTENT_NOCASE ||
+ cd->flags & DETECT_CONTENT_WITHIN ||
+ cd->flags & DETECT_CONTENT_DISTANCE ||
+ cd->flags & DETECT_CONTENT_FAST_PATTERN ||
+ !(cd->flags & DETECT_CONTENT_RELATIVE_NEXT) ||
+ cd->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "two") != 0 ||
+ bed1->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ strncmp((char *)cd->content, "three", cd->content_len) != 0) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed1 = (DetectByteExtractData *)sm->ctx;
+ if (bed1->nbytes != 4 ||
+ bed1->offset != 0 ||
+ strcmp(bed1->name, "four") != 0 ||
+ bed1->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING |
+ DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed1->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed1->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed1->align_value != 0 ||
+ bed1->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+ if (bed1->local_id != 0) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isdd = (DetectIsdataatData *)sm->ctx;
+ if (isdd->flags != (ISDATAAT_OFFSET_BE |
+ ISDATAAT_RELATIVE) ||
+ isdd->dataat != bed1->local_id) {
+ printf("isdataat failed\n");
+ result = 0;
+ goto end;
+ }
+
+ if (sm->next != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectByteExtractTest62(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectByteExtractData *bed = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; byte_extract:4,2,two,relative,string,hex; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm->type != DETECT_BYTE_EXTRACT) {
+ result = 0;
+ goto end;
+ }
+ bed = (DetectByteExtractData *)sm->ctx;
+ if (bed->nbytes != 4 ||
+ bed->offset != 2 ||
+ strncmp(bed->name, "two", 3) != 0 ||
+ bed->flags != (DETECT_BYTE_EXTRACT_FLAG_STRING | DETECT_BYTE_EXTRACT_FLAG_RELATIVE) ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_NONE ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_HEX ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectByteExtractTest63(void)
+{
+ int result = 0;
+
+ DetectByteExtractData *bed = DetectByteExtractParse("4, -2, one");
+ if (bed == NULL)
+ goto end;
+
+ if (bed->nbytes != 4 ||
+ bed->offset != -2 ||
+ strcmp(bed->name, "one") != 0 ||
+ bed->flags != 0 ||
+ bed->endian != DETECT_BYTE_EXTRACT_ENDIAN_DEFAULT ||
+ bed->base != DETECT_BYTE_EXTRACT_BASE_NONE ||
+ bed->align_value != 0 ||
+ bed->multiplier_value != DETECT_BYTE_EXTRACT_MULTIPLIER_DEFAULT) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (bed != NULL)
+ DetectByteExtractFree(bed);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectByteExtractRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectByteExtractTest01", DetectByteExtractTest01, 1);
+ UtRegisterTest("DetectByteExtractTest02", DetectByteExtractTest02, 1);
+ UtRegisterTest("DetectByteExtractTest03", DetectByteExtractTest03, 1);
+ UtRegisterTest("DetectByteExtractTest04", DetectByteExtractTest04, 1);
+ UtRegisterTest("DetectByteExtractTest05", DetectByteExtractTest05, 1);
+ UtRegisterTest("DetectByteExtractTest06", DetectByteExtractTest06, 1);
+ UtRegisterTest("DetectByteExtractTest07", DetectByteExtractTest07, 1);
+ UtRegisterTest("DetectByteExtractTest08", DetectByteExtractTest08, 1);
+ UtRegisterTest("DetectByteExtractTest09", DetectByteExtractTest09, 1);
+ UtRegisterTest("DetectByteExtractTest10", DetectByteExtractTest10, 1);
+ UtRegisterTest("DetectByteExtractTest11", DetectByteExtractTest11, 1);
+ UtRegisterTest("DetectByteExtractTest12", DetectByteExtractTest12, 1);
+ UtRegisterTest("DetectByteExtractTest13", DetectByteExtractTest13, 1);
+ UtRegisterTest("DetectByteExtractTest14", DetectByteExtractTest14, 1);
+ UtRegisterTest("DetectByteExtractTest15", DetectByteExtractTest15, 1);
+ UtRegisterTest("DetectByteExtractTest16", DetectByteExtractTest16, 1);
+ UtRegisterTest("DetectByteExtractTest17", DetectByteExtractTest17, 1);
+ UtRegisterTest("DetectByteExtractTest18", DetectByteExtractTest18, 1);
+ UtRegisterTest("DetectByteExtractTest19", DetectByteExtractTest19, 1);
+ UtRegisterTest("DetectByteExtractTest20", DetectByteExtractTest20, 1);
+ UtRegisterTest("DetectByteExtractTest21", DetectByteExtractTest21, 1);
+ UtRegisterTest("DetectByteExtractTest22", DetectByteExtractTest22, 1);
+ UtRegisterTest("DetectByteExtractTest23", DetectByteExtractTest23, 1);
+ UtRegisterTest("DetectByteExtractTest24", DetectByteExtractTest24, 1);
+ UtRegisterTest("DetectByteExtractTest25", DetectByteExtractTest25, 1);
+ UtRegisterTest("DetectByteExtractTest26", DetectByteExtractTest26, 1);
+ UtRegisterTest("DetectByteExtractTest27", DetectByteExtractTest27, 1);
+ UtRegisterTest("DetectByteExtractTest28", DetectByteExtractTest28, 1);
+ UtRegisterTest("DetectByteExtractTest29", DetectByteExtractTest29, 1);
+ UtRegisterTest("DetectByteExtractTest30", DetectByteExtractTest30, 1);
+ UtRegisterTest("DetectByteExtractTest31", DetectByteExtractTest31, 1);
+ UtRegisterTest("DetectByteExtractTest32", DetectByteExtractTest32, 1);
+ UtRegisterTest("DetectByteExtractTest33", DetectByteExtractTest33, 1);
+ UtRegisterTest("DetectByteExtractTest34", DetectByteExtractTest34, 1);
+ UtRegisterTest("DetectByteExtractTest35", DetectByteExtractTest35, 1);
+ UtRegisterTest("DetectByteExtractTest36", DetectByteExtractTest36, 1);
+ UtRegisterTest("DetectByteExtractTest37", DetectByteExtractTest37, 1);
+ UtRegisterTest("DetectByteExtractTest38", DetectByteExtractTest38, 1);
+ UtRegisterTest("DetectByteExtractTest39", DetectByteExtractTest39, 1);
+ UtRegisterTest("DetectByteExtractTest40", DetectByteExtractTest40, 1);
+ UtRegisterTest("DetectByteExtractTest41", DetectByteExtractTest41, 1);
+ UtRegisterTest("DetectByteExtractTest42", DetectByteExtractTest42, 1);
+
+ UtRegisterTest("DetectByteExtractTest43", DetectByteExtractTest43, 1);
+ UtRegisterTest("DetectByteExtractTest44", DetectByteExtractTest44, 1);
+
+ UtRegisterTest("DetectByteExtractTest45", DetectByteExtractTest45, 1);
+ UtRegisterTest("DetectByteExtractTest46", DetectByteExtractTest46, 1);
+
+ UtRegisterTest("DetectByteExtractTest47", DetectByteExtractTest47, 1);
+ UtRegisterTest("DetectByteExtractTest48", DetectByteExtractTest48, 1);
+
+ UtRegisterTest("DetectByteExtractTest49", DetectByteExtractTest49, 1);
+ UtRegisterTest("DetectByteExtractTest50", DetectByteExtractTest50, 1);
+
+ UtRegisterTest("DetectByteExtractTest51", DetectByteExtractTest51, 1);
+ UtRegisterTest("DetectByteExtractTest52", DetectByteExtractTest52, 1);
+
+ UtRegisterTest("DetectByteExtractTest53", DetectByteExtractTest53, 1);
+ UtRegisterTest("DetectByteExtractTest54", DetectByteExtractTest54, 1);
+
+ UtRegisterTest("DetectByteExtractTest55", DetectByteExtractTest55, 1);
+ UtRegisterTest("DetectByteExtractTest56", DetectByteExtractTest56, 1);
+ UtRegisterTest("DetectByteExtractTest57", DetectByteExtractTest57, 1);
+
+ UtRegisterTest("DetectByteExtractTest58", DetectByteExtractTest58, 1);
+ UtRegisterTest("DetectByteExtractTest59", DetectByteExtractTest59, 1);
+ UtRegisterTest("DetectByteExtractTest60", DetectByteExtractTest60, 1);
+ UtRegisterTest("DetectByteExtractTest61", DetectByteExtractTest61, 1);
+ UtRegisterTest("DetectByteExtractTest62", DetectByteExtractTest62, 1);
+ UtRegisterTest("DetectByteExtractTest63", DetectByteExtractTest63, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-byte-extract.h b/framework/src/suricata/src/detect-byte-extract.h
new file mode 100644
index 00000000..020494da
--- /dev/null
+++ b/framework/src/suricata/src/detect-byte-extract.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_BYTEEXTRACT_H__
+#define __DETECT_BYTEEXTRACT_H__
+
+/* flags */
+#define DETECT_BYTE_EXTRACT_FLAG_RELATIVE 0x01
+#define DETECT_BYTE_EXTRACT_FLAG_MULTIPLIER 0x02
+#define DETECT_BYTE_EXTRACT_FLAG_STRING 0x04
+#define DETECT_BYTE_EXTRACT_FLAG_ALIGN 0x08
+#define DETECT_BYTE_EXTRACT_FLAG_ENDIAN 0x10
+
+/* endian value to be used. Would be stored in DetectByteParseData->endian */
+#define DETECT_BYTE_EXTRACT_ENDIAN_NONE 0
+#define DETECT_BYTE_EXTRACT_ENDIAN_BIG 1
+#define DETECT_BYTE_EXTRACT_ENDIAN_LITTLE 2
+#define DETECT_BYTE_EXTRACT_ENDIAN_DCE 3
+
+/**
+ * \brief Holds data related to byte_extract keyword.
+ */
+typedef struct DetectByteExtractData_ {
+ /* local id used by other keywords in the sig to reference this */
+ uint8_t local_id;
+
+ uint8_t nbytes;
+ int16_t pad;
+ int32_t offset;
+ const char *name;
+ uint8_t flags;
+ uint8_t endian;
+ uint8_t base;
+ uint8_t align_value;
+
+ uint16_t multiplier_value;
+ /* unique id used to reference this byte_extract keyword */
+ uint16_t id;
+
+} DetectByteExtractData;
+
+void DetectByteExtractRegister(void);
+int DetectByteExtractSetup(DetectEngineCtx *, Signature *, char *);
+void DetectByteExtractFree(void *);
+int DetectByteExtractMatch(ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, SigMatch *);
+SigMatch *DetectByteExtractRetrieveSMVar(const char *, Signature *);
+int DetectByteExtractDoMatch(DetectEngineThreadCtx *, SigMatch *, Signature *,
+ uint8_t *, uint16_t, uint64_t *, uint8_t);
+
+#endif /* __DETECT_BYTEEXTRACT_H__ */
diff --git a/framework/src/suricata/src/detect-bytejump.c b/framework/src/suricata/src/detect-bytejump.c
new file mode 100644
index 00000000..46a2a3e4
--- /dev/null
+++ b/framework/src/suricata/src/detect-bytejump.c
@@ -0,0 +1,1429 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements byte_jump keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "app-layer.h"
+
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "detect-pcre.h"
+
+/**
+ * \brief Regex for parsing our options
+ */
+#define PARSE_REGEX "^\\s*" \
+ "([^\\s,]+\\s*,\\s*[^\\s,]+)" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
+ "\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+void DetectBytejumpRegisterTests(void);
+
+void DetectBytejumpRegister (void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_BYTEJUMP].name = "byte_jump";
+ sigmatch_table[DETECT_BYTEJUMP].Match = DetectBytejumpMatch;
+ sigmatch_table[DETECT_BYTEJUMP].Setup = DetectBytejumpSetup;
+ sigmatch_table[DETECT_BYTEJUMP].Free = DetectBytejumpFree;
+ sigmatch_table[DETECT_BYTEJUMP].RegisterTests = DetectBytejumpRegisterTests;
+
+ sigmatch_table[DETECT_BYTEJUMP].flags |= SIGMATCH_PAYLOAD;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE,"pcre compile of \"%s\" failed "
+ "at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY,"pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/** \brief Byte jump match function
+ * \param det_ctx thread detect engine ctx
+ * \param s signature
+ * \param m byte jump sigmatch
+ * \param payload ptr to the payload
+ * \param payload_len length of the payload
+ * \retval 1 match
+ * \retval 0 no match
+ */
+int DetectBytejumpDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s,
+ const SigMatchCtx *ctx, uint8_t *payload, uint32_t payload_len,
+ uint8_t flags, int32_t offset)
+{
+ SCEnter();
+
+ const DetectBytejumpData *data = (const DetectBytejumpData *)ctx;
+ uint8_t *ptr = NULL;
+ uint8_t *jumpptr = NULL;
+ int32_t len = 0;
+ uint64_t val = 0;
+ int extbytes;
+
+ if (payload_len == 0) {
+ SCReturnInt(0);
+ }
+
+ /* Calculate the ptr value for the bytejump and length remaining in
+ * the packet from that point.
+ */
+ if (flags & DETECT_BYTEJUMP_RELATIVE) {
+ ptr = payload + det_ctx->buffer_offset;
+ len = payload_len - det_ctx->buffer_offset;
+
+ ptr += offset;
+ len -= offset;
+
+ /* No match if there is no relative base */
+ if (ptr == NULL || len <= 0) {
+ SCReturnInt(0);
+ }
+ }
+ else {
+ ptr = payload + offset;
+ len = payload_len - offset;
+ }
+
+ /* Verify the to-be-extracted data is within the packet */
+ if (ptr < payload || data->nbytes > len) {
+ SCLogDebug("Data not within payload "
+ "pkt=%p, ptr=%p, len=%d, nbytes=%d",
+ payload, ptr, len, data->nbytes);
+ SCReturnInt(0);
+ }
+
+ /* Extract the byte data */
+ if (flags & DETECT_BYTEJUMP_STRING) {
+ extbytes = ByteExtractStringUint64(&val, data->base,
+ data->nbytes, (const char *)ptr);
+ if(extbytes <= 0) {
+ SCLogError(SC_ERR_BYTE_EXTRACT_FAILED,"Error extracting %d bytes "
+ "of string data: %d", data->nbytes, extbytes);
+ SCReturnInt(-1);
+ }
+ }
+ else {
+ int endianness = (flags & DETECT_BYTEJUMP_LITTLE) ? BYTE_LITTLE_ENDIAN : BYTE_BIG_ENDIAN;
+ extbytes = ByteExtractUint64(&val, endianness, data->nbytes, ptr);
+ if (extbytes != data->nbytes) {
+ SCLogError(SC_ERR_BYTE_EXTRACT_FAILED,"Error extracting %d bytes "
+ "of numeric data: %d", data->nbytes, extbytes);
+ SCReturnInt(-1);
+ }
+ }
+
+ //printf("VAL: (%" PRIu64 " x %" PRIu32 ") + %d + %" PRId32 "\n", val, data->multiplier, extbytes, data->post_offset);
+
+ /* Adjust the jump value based on flags */
+ val *= data->multiplier;
+ if (flags & DETECT_BYTEJUMP_ALIGN) {
+ if ((val % 4) != 0) {
+ val += 4 - (val % 4);
+ }
+ }
+ val += data->post_offset;
+
+ /* Calculate the jump location */
+ if (flags & DETECT_BYTEJUMP_BEGIN) {
+ jumpptr = payload + val;
+ //printf("NEWVAL: payload %p + %ld = %p\n", p->payload, val, jumpptr);
+ }
+ else {
+ val += extbytes;
+ jumpptr = ptr + val;
+ //printf("NEWVAL: ptr %p + %ld = %p\n", ptr, val, jumpptr);
+ }
+
+
+ /* Validate that the jump location is still in the packet
+ * \todo Should this validate it is still in the *payload*?
+ */
+ if ((jumpptr < payload) || (jumpptr >= payload + payload_len)) {
+ SCLogDebug("Jump location (%p) is not within "
+ "payload (%p-%p)", jumpptr, payload, payload + payload_len - 1);
+ SCReturnInt(0);
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ uint8_t *sptr = (flags & DETECT_BYTEJUMP_BEGIN) ? payload : ptr;
+ SCLogDebug("jumping %" PRId64 " bytes from %p (%08x) to %p (%08x)",
+ val, sptr, (int)(sptr - payload),
+ jumpptr, (int)(jumpptr - payload));
+ }
+#endif /* DEBUG */
+
+ /* Adjust the detection context to the jump location. */
+ det_ctx->buffer_offset = jumpptr - payload;
+
+ SCReturnInt(1);
+}
+
+int DetectBytejumpMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectBytejumpData *data = (const DetectBytejumpData *)ctx;
+ uint8_t *ptr = NULL;
+ uint8_t *jumpptr = NULL;
+ uint16_t len = 0;
+ uint64_t val = 0;
+ int extbytes;
+
+ if (p->payload_len == 0) {
+ return 0;
+ }
+
+ /* Calculate the ptr value for the bytejump and length remaining in
+ * the packet from that point.
+ */
+ if (data->flags & DETECT_BYTEJUMP_RELATIVE) {
+ ptr = p->payload + det_ctx->buffer_offset;
+ len = p->payload_len - det_ctx->buffer_offset;
+
+ /* No match if there is no relative base */
+ if (ptr == NULL || len == 0) {
+ return 0;
+ }
+
+ ptr += data->offset;
+ len -= data->offset;
+ }
+ else {
+ ptr = p->payload + data->offset;
+ len = p->payload_len - data->offset;
+ }
+
+ /* Verify the to-be-extracted data is within the packet */
+ if (ptr < p->payload || data->nbytes > len) {
+ SCLogDebug("Data not within packet "
+ "payload=%p, ptr=%p, len=%d, nbytes=%d",
+ p->payload, ptr, len, data->nbytes);
+ return 0;
+ }
+
+ /* Extract the byte data */
+ if (data->flags & DETECT_BYTEJUMP_STRING) {
+ extbytes = ByteExtractStringUint64(&val, data->base,
+ data->nbytes, (const char *)ptr);
+ if(extbytes <= 0) {
+ SCLogError(SC_ERR_BYTE_EXTRACT_FAILED,"Error extracting %d bytes "
+ "of string data: %d", data->nbytes, extbytes);
+ return -1;
+ }
+ }
+ else {
+ int endianness = (data->flags & DETECT_BYTEJUMP_LITTLE) ? BYTE_LITTLE_ENDIAN : BYTE_BIG_ENDIAN;
+ extbytes = ByteExtractUint64(&val, endianness, data->nbytes, ptr);
+ if (extbytes != data->nbytes) {
+ SCLogError(SC_ERR_BYTE_EXTRACT_FAILED,"Error extracting %d bytes "
+ "of numeric data: %d", data->nbytes, extbytes);
+ return -1;
+ }
+ }
+
+ //printf("VAL: (%" PRIu64 " x %" PRIu32 ") + %d + %" PRId32 "\n", val, data->multiplier, extbytes, data->post_offset);
+
+ /* Adjust the jump value based on flags */
+ val *= data->multiplier;
+ if (data->flags & DETECT_BYTEJUMP_ALIGN) {
+ if ((val % 4) != 0) {
+ val += 4 - (val % 4);
+ }
+ }
+ val += data->post_offset;
+
+ /* Calculate the jump location */
+ if (data->flags & DETECT_BYTEJUMP_BEGIN) {
+ jumpptr = p->payload + val;
+ //printf("NEWVAL: payload %p + %ld = %p\n", p->payload, val, jumpptr);
+ }
+ else {
+ val += extbytes;
+ jumpptr = ptr + val;
+ //printf("NEWVAL: ptr %p + %ld = %p\n", ptr, val, jumpptr);
+ }
+
+
+ /* Validate that the jump location is still in the packet
+ * \todo Should this validate it is still in the *payload*?
+ */
+ if ((jumpptr < p->payload) || (jumpptr >= p->payload + p->payload_len)) {
+ SCLogDebug("Jump location (%p) is not within "
+ "packet (%p-%p)", jumpptr, p->payload, p->payload + p->payload_len - 1);
+ return 0;
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ uint8_t *sptr = (data->flags & DETECT_BYTEJUMP_BEGIN) ? p->payload
+ : ptr;
+ SCLogDebug("jumping %" PRId64 " bytes from %p (%08x) to %p (%08x)",
+ val, sptr, (int)(sptr - p->payload),
+ jumpptr, (int)(jumpptr - p->payload));
+ }
+#endif /* DEBUG */
+
+ /* Adjust the detection context to the jump location. */
+ det_ctx->buffer_offset = jumpptr - p->payload;
+
+ return 1;
+}
+
+DetectBytejumpData *DetectBytejumpParse(char *optstr, char **offset)
+{
+ DetectBytejumpData *data = NULL;
+ char args[10][64];
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int numargs = 0;
+ int i = 0;
+ uint32_t nbytes;
+ char *str_ptr;
+ char *end_ptr;
+
+ memset(args, 0x00, sizeof(args));
+
+ /* Execute the regex and populate args with captures. */
+ ret = pcre_exec(parse_regex, parse_regex_study, optstr,
+ strlen(optstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 2 || ret > 10) {
+ SCLogError(SC_ERR_PCRE_PARSE,"parse error, ret %" PRId32
+ ", string \"%s\"", ret, optstr);
+ goto error;
+ }
+
+ /* The first two arguments are stashed in the first PCRE substring.
+ * This is because byte_jump can take 10 arguments, but PCRE only
+ * supports 9 substrings, sigh.
+ */
+ char str[512] = "";
+ res = pcre_copy_substring((char *)optstr, ov,
+ MAX_SUBSTRINGS, 1, str, sizeof(str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed "
+ "for arg 1");
+ goto error;
+ }
+
+ /* Break up first substring into two parameters
+ *
+ * NOTE: Because of this, we cannot free args[1] as it is part of args[0],
+ * and *yes* this *is* ugly.
+ */
+ end_ptr = str;
+ while (!(isspace((unsigned char)*end_ptr) || (*end_ptr == ','))) end_ptr++;
+ *(end_ptr++) = '\0';
+ strlcpy(args[0], str, sizeof(args[0]));
+ numargs++;
+
+ str_ptr = end_ptr;
+ while (isspace((unsigned char)*str_ptr) || (*str_ptr == ',')) str_ptr++;
+ end_ptr = str_ptr;
+ while (!(isspace((unsigned char)*end_ptr) || (*end_ptr == ',')) && (*end_ptr != '\0'))
+ end_ptr++;
+ *(end_ptr++) = '\0';
+ strlcpy(args[1], str_ptr, sizeof(args[1]));
+ numargs++;
+
+ /* The remaining args are directly from PCRE substrings */
+ for (i = 1; i < (ret - 1); i++) {
+ res = pcre_copy_substring((char *)optstr, ov, MAX_SUBSTRINGS, i + 1, args[i+1], sizeof(args[0]));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed for arg %d", i + 1);
+ goto error;
+ }
+ numargs++;
+ }
+
+ /* Initialize the data */
+ data = SCMalloc(sizeof(DetectBytejumpData));
+ if (unlikely(data == NULL))
+ goto error;
+ data->base = DETECT_BYTEJUMP_BASE_UNSET;
+ data->flags = 0;
+ data->multiplier = 1;
+ data->post_offset = 0;
+
+ /*
+ * The first two options are required and positional. The
+ * remaining arguments are flags and are not positional.
+ */
+
+ /* Number of bytes */
+ if (ByteExtractStringUint32(&nbytes, 10, strlen(args[0]), args[0]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed number of bytes: %s", optstr);
+ goto error;
+ }
+
+ /* Offset */
+ if (args[1][0] != '-' && isalpha((unsigned char)args[1][0])) {
+ if (offset == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "byte_jump supplied with "
+ "var name for offset. \"value\" argument supplied to "
+ "this function has to be non-NULL");
+ goto error;
+ }
+ *offset = SCStrdup(args[1]);
+ if (*offset == NULL)
+ goto error;
+ } else {
+ if (ByteExtractStringInt32(&data->offset, 0, strlen(args[1]), args[1]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed offset: %s", optstr);
+ goto error;
+ }
+ }
+
+ /* The remaining options are flags. */
+ /** \todo Error on dups? */
+ for (i = 2; i < numargs; i++) {
+ if (strcmp("relative", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_RELATIVE;
+ } else if (strcasecmp("string", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_STRING;
+ } else if (strcasecmp("dec", args[i]) == 0) {
+ data->base |= DETECT_BYTEJUMP_BASE_DEC;
+ } else if (strcasecmp("hex", args[i]) == 0) {
+ data->base |= DETECT_BYTEJUMP_BASE_HEX;
+ } else if (strcasecmp("oct", args[i]) == 0) {
+ data->base |= DETECT_BYTEJUMP_BASE_OCT;
+ } else if (strcasecmp("big", args[i]) == 0) {
+ if (data->flags & DETECT_BYTEJUMP_LITTLE) {
+ data->flags ^= DETECT_BYTEJUMP_LITTLE;
+ }
+ data->flags |= DETECT_BYTEJUMP_BIG;
+ } else if (strcasecmp("little", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_LITTLE;
+ } else if (strcasecmp("from_beginning", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_BEGIN;
+ } else if (strcasecmp("align", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_ALIGN;
+ } else if (strncasecmp("multiplier ", args[i], 11) == 0) {
+ if (ByteExtractStringUint32(&data->multiplier, 10,
+ strlen(args[i]) - 11,
+ args[i] + 11) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed multiplier: %s", optstr);
+ goto error;
+ }
+ } else if (strncasecmp("post_offset ", args[i], 12) == 0) {
+ if (ByteExtractStringInt32(&data->post_offset, 10,
+ strlen(args[i]) - 12,
+ args[i] + 12) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed post_offset: %s", optstr);
+ goto error;
+ }
+ } else if (strcasecmp("dce", args[i]) == 0) {
+ data->flags |= DETECT_BYTEJUMP_DCE;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unknown option: \"%s\"", args[i]);
+ goto error;
+ }
+ }
+
+ if (data->flags & DETECT_BYTEJUMP_STRING) {
+ /* 23 - This is the largest string (octal, with a zero prefix) that
+ * will not overflow uint64_t. The only way this length
+ * could be over 23 and still not overflow is if it were zero
+ * prefixed and we only support 1 byte of zero prefix for octal.
+ *
+ * "01777777777777777777777" = 0xffffffffffffffff
+ */
+ if (nbytes > 23) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot test more than 23 bytes "
+ "with \"string\": %s", optstr);
+ goto error;
+ }
+ } else {
+ if (nbytes > 8) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot test more than 8 bytes "
+ "without \"string\": %s\n", optstr);
+ goto error;
+ }
+ if (data->base != DETECT_BYTEJUMP_BASE_UNSET) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot use a base "
+ "without \"string\": %s", optstr);
+ goto error;
+ }
+ }
+
+ /* This is max 23 so it will fit in a byte (see above) */
+ data->nbytes = (uint8_t)nbytes;
+
+ return data;
+
+error:
+ if (offset != NULL && *offset != NULL) {
+ SCFree(*offset);
+ *offset = NULL;
+ }
+ if (data != NULL)
+ DetectBytejumpFree(data);
+ return NULL;
+}
+
+int DetectBytejumpSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ SigMatch *sm = NULL;
+ SigMatch *prev_pm = NULL;
+ DetectBytejumpData *data = NULL;
+ char *offset = NULL;
+ int ret = -1;
+
+ data = DetectBytejumpParse(optstr, &offset);
+ if (data == NULL)
+ goto error;
+
+ int sm_list;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA) {
+ if (data->flags & DETECT_BYTEJUMP_DCE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "dce bytejump specified "
+ "with file_data option set.");
+ goto error;
+ }
+ AppLayerHtpEnableResponseBodyCallback();
+ }
+ sm_list = s->list;
+ s->flags |= SIG_FLAG_APPLAYER;
+ if (data->flags & DETECT_BYTEJUMP_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, s->sm_lists_tail[sm_list],
+ DETECT_PCRE, s->sm_lists_tail[sm_list]);
+
+ }
+ } else if (data->flags & DETECT_BYTEJUMP_DCE) {
+ if (data->flags & DETECT_BYTEJUMP_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 12,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ }
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ } else if (data->flags & DETECT_BYTEJUMP_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 168,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ }
+
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ if (data->flags & DETECT_BYTEJUMP_DCE) {
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Non dce alproto sig has "
+ "bytejump with dce enabled");
+ goto error;
+ }
+ if ((data->flags & DETECT_BYTEJUMP_STRING) ||
+ (data->flags & DETECT_BYTEJUMP_LITTLE) ||
+ (data->flags & DETECT_BYTEJUMP_BIG) ||
+ (data->flags & DETECT_BYTEJUMP_BEGIN) ||
+ (data->base == DETECT_BYTEJUMP_BASE_DEC) ||
+ (data->base == DETECT_BYTEJUMP_BASE_HEX) ||
+ (data->base == DETECT_BYTEJUMP_BASE_OCT) ) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. "
+ "A byte_jump keyword with dce holds other invalid modifiers.");
+ goto error;
+ }
+ }
+
+ if (offset != NULL) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(offset, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unknown byte_extract var "
+ "seen in byte_jump - %s\n", offset);
+ goto error;
+ }
+ data->offset = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ data->flags |= DETECT_BYTEJUMP_OFFSET_BE;
+ SCFree(offset);
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_BYTEJUMP;
+ sm->ctx = (SigMatchCtx *)data;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ if (!(data->flags & DETECT_BYTEJUMP_RELATIVE))
+ goto okay;
+
+ if (prev_pm == NULL)
+ goto okay;
+
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ okay:
+ ret = 0;
+ return ret;
+ error:
+ DetectBytejumpFree(data);
+ return ret;
+}
+
+/**
+ * \brief this function will free memory associated with DetectBytejumpData
+ *
+ * \param data pointer to DetectBytejumpData
+ */
+void DetectBytejumpFree(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ DetectBytejumpData *data = (DetectBytejumpData *)ptr;
+ SCFree(data);
+}
+
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+#include "util-unittest-helper.h"
+/**
+ * \test DetectBytejumpTestParse01 is a test to make sure that we return
+ * "something" when given valid bytejump opt
+ */
+int DetectBytejumpTestParse01(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse("4,0", NULL);
+ if (data != NULL) {
+ DetectBytejumpFree(data);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse02 is a test for setting the required opts
+ */
+int DetectBytejumpTestParse02(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse("4, 0", NULL);
+ if (data != NULL) {
+ if ( (data->nbytes == 4)
+ && (data->offset == 0)
+ && (data->multiplier == 1)
+ && (data->post_offset == 0)
+ && (data->flags == 0)
+ && (data->base == DETECT_BYTEJUMP_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytejumpFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse03 is a test for setting the optional flags
+ */
+int DetectBytejumpTestParse03(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse(" 4,0 , relative , little, string, "
+ "dec, align, from_beginning", NULL);
+ if (data != NULL) {
+ if ( (data->nbytes == 4)
+ && (data->offset == 0)
+ && (data->multiplier == 1)
+ && (data->post_offset == 0)
+ && (data->flags == ( DETECT_BYTEJUMP_RELATIVE
+ |DETECT_BYTEJUMP_LITTLE
+ |DETECT_BYTEJUMP_STRING
+ |DETECT_BYTEJUMP_ALIGN
+ |DETECT_BYTEJUMP_BEGIN))
+ && (data->base == DETECT_BYTEJUMP_BASE_DEC))
+ {
+ result = 1;
+ }
+ DetectBytejumpFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse04 is a test for setting the optional flags
+ * with parameters
+ *
+ * \todo This fails becuase we can only have 9 captures and there are 10.
+ */
+int DetectBytejumpTestParse04(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse(" 4,0 , relative , little, string, "
+ "dec, align, from_beginning , "
+ "multiplier 2 , post_offset -16 ", NULL);
+ if (data != NULL) {
+ if ( (data->nbytes == 4)
+ && (data->offset == 0)
+ && (data->multiplier == 2)
+ && (data->post_offset == -16)
+ && (data->flags == ( DETECT_BYTEJUMP_RELATIVE
+ |DETECT_BYTEJUMP_LITTLE
+ |DETECT_BYTEJUMP_ALIGN
+ |DETECT_BYTEJUMP_STRING
+ |DETECT_BYTEJUMP_BEGIN))
+ && (data->base == DETECT_BYTEJUMP_BASE_DEC))
+ {
+ result = 1;
+ }
+ DetectBytejumpFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse05 is a test for setting base without string
+ */
+int DetectBytejumpTestParse05(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse(" 4,0 , relative , little, dec, "
+ "align, from_beginning", NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse06 is a test for too many bytes to extract
+ */
+int DetectBytejumpTestParse06(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse("9, 0", NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse07 is a test for too many string bytes to extract
+ */
+int DetectBytejumpTestParse07(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse("24, 0, string, dec", NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytejumpTestParse08 is a test for offset too big
+ */
+int DetectBytejumpTestParse08(void)
+{
+ int result = 0;
+ DetectBytejumpData *data = NULL;
+ data = DetectBytejumpParse("4, 0xffffffffffffffff", NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytejumpTestParse09(void)
+{
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 1;
+
+ s->alproto = ALPROTO_DCERPC;
+
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, align, multiplier 2, "
+ "post_offset -16,dce") == 0);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, multiplier 2, "
+ "post_offset -16,dce") == 0);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0,post_offset -16,dce") == 0);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0,dce") == 0);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0,dce") == 0);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, string, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, big, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, little, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, string, dec, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, string, oct, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, string, hex, dce") == -1);
+ result &= (DetectBytejumpSetup(NULL, s, "4,0, from_beginning, dce") == -1);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ SigFree(s);
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytejumpTestParse10(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,relative,dce; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTEJUMP);
+ bd = (DetectBytejumpData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (!(bd->flags & DETECT_BYTEJUMP_DCE) &&
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) &&
+ (bd->flags & DETECT_BYTEJUMP_STRING) &&
+ (bd->flags & DETECT_BYTEJUMP_BIG) &&
+ (bd->flags & DETECT_BYTEJUMP_LITTLE) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,relative,dce; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTEJUMP);
+ bd = (DetectBytejumpData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (!(bd->flags & DETECT_BYTEJUMP_DCE) &&
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) &&
+ (bd->flags & DETECT_BYTEJUMP_STRING) &&
+ (bd->flags & DETECT_BYTEJUMP_BIG) &&
+ (bd->flags & DETECT_BYTEJUMP_LITTLE) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,relative; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTEJUMP);
+ bd = (DetectBytejumpData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if ((bd->flags & DETECT_BYTEJUMP_DCE) &&
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) &&
+ (bd->flags & DETECT_BYTEJUMP_STRING) &&
+ (bd->flags & DETECT_BYTEJUMP_BIG) &&
+ (bd->flags & DETECT_BYTEJUMP_LITTLE) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytejumpTestParse11(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,string,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_sub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,big,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,little,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,string,hex,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,string,dec,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,string,oct,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,from_beginning,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test file_data
+ */
+static int DetectBytejumpTestParse12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; byte_jump:4,0,align,multiplier 2, "
+ "post_offset -16,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_BYTEJUMP) {
+ goto end;
+ }
+
+ bd = (DetectBytejumpData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if ((bd->flags & DETECT_BYTEJUMP_DCE) &&
+ (bd->flags & DETECT_BYTEJUMP_RELATIVE) &&
+ (bd->flags & DETECT_BYTEJUMP_STRING) &&
+ (bd->flags & DETECT_BYTEJUMP_BIG) &&
+ (bd->flags & DETECT_BYTEJUMP_LITTLE) ) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectByteJumpTestPacket01 is a test to check matches of
+ * byte_jump and byte_jump relative works if the previous keyword is pcre
+ * (bug 142)
+ */
+int DetectByteJumpTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre + byte_test + "
+ "relative\"; pcre:\"/AllWorkAndNoPlayMakesWillADullBoy/\"; byte_jump:1,6,"
+ "relative,string,dec; content:\"0\"; sid:134; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test DetectByteJumpTestPacket02 is a test to check matches of
+ * byte_jump and byte_jump relative works if the previous keyword is byte_jump
+ * (bug 165)
+ */
+int DetectByteJumpTestPacket02 (void)
+{
+ int result = 0;
+ uint8_t buf[] = { 0x00, 0x00, 0x00, 0x77, 0xff, 0x53,
+ 0x4d, 0x42, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x92, 0xa4, 0x01, 0x08, 0x17, 0x5c, 0x0e, 0xff,
+ 0x00, 0x00, 0x00, 0x01, 0x40, 0x48, 0x00, 0x00,
+ 0x00, 0xff };
+ uint16_t buflen = sizeof(buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"byte_jump with byte_jump"
+ " + relative\"; byte_jump:1,13; byte_jump:4,0,relative; "
+ "content:\"|48 00 00|\"; within:3; sid:144; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+int DetectByteJumpTestPacket03(void)
+{
+ int result = 0;
+ uint8_t *buf = NULL;
+ uint16_t buflen = 0;
+ buf = SCMalloc(4);
+ if (unlikely(buf == NULL)) {
+ printf("malloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(buf, "boom", 4);
+ buflen = 4;
+
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"byte_jump\"; "
+ "byte_jump:1,214748364; sid:1; rev:1;)";
+
+ result = !UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+
+end:
+ if (buf != NULL)
+ SCFree(buf);
+ return result;
+}
+
+/**
+ * \test check matches of with from_beginning (bug 626/627)
+ */
+int DetectByteJumpTestPacket04 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"XYZ04abcdABCD";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (content:\"XYZ\"; byte_jump:2,0,relative,string,dec; content:\"ABCD\"; distance:0; within:4; sid:1; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test check matches of with from_beginning (bug 626/627)
+ */
+int DetectByteJumpTestPacket05 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"XYZ04abcdABCD";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (content:\"XYZ\"; byte_jump:2,0,relative,string,dec; content:\"cdABCD\"; within:6; sid:1; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig) ? 0 : 1;
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test check matches of with from_beginning (bug 626/627)
+ */
+int DetectByteJumpTestPacket06 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"XX04abcdABCD";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (content:\"XX\"; byte_jump:2,0,relative,string,dec,from_beginning; content:\"ABCD\"; distance:4; within:4; sid:1; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test check matches of with from_beginning (bug 626/627)
+ */
+int DetectByteJumpTestPacket07 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"XX04abcdABCD";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (content:\"XX\"; byte_jump:2,0,relative,string,dec,from_beginning; content:\"abcdABCD\"; distance:0; within:8; sid:1; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig) ? 1 : 0;
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+
+/**
+ * \brief this function registers unit tests for DetectBytejump
+ */
+void DetectBytejumpRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectBytejumpTestParse01", DetectBytejumpTestParse01, 1);
+ UtRegisterTest("DetectBytejumpTestParse02", DetectBytejumpTestParse02, 1);
+ UtRegisterTest("DetectBytejumpTestParse03", DetectBytejumpTestParse03, 1);
+ UtRegisterTest("DetectBytejumpTestParse04", DetectBytejumpTestParse04, 1);
+ UtRegisterTest("DetectBytejumpTestParse05", DetectBytejumpTestParse05, 1);
+ UtRegisterTest("DetectBytejumpTestParse06", DetectBytejumpTestParse06, 1);
+ UtRegisterTest("DetectBytejumpTestParse07", DetectBytejumpTestParse07, 1);
+ UtRegisterTest("DetectBytejumpTestParse08", DetectBytejumpTestParse08, 1);
+ UtRegisterTest("DetectBytejumpTestParse09", DetectBytejumpTestParse09, 1);
+ UtRegisterTest("DetectBytejumpTestParse10", DetectBytejumpTestParse10, 1);
+ UtRegisterTest("DetectBytejumpTestParse11", DetectBytejumpTestParse11, 1);
+ UtRegisterTest("DetectBytejumpTestParse12", DetectBytejumpTestParse12, 1);
+
+ UtRegisterTest("DetectByteJumpTestPacket01", DetectByteJumpTestPacket01, 1);
+ UtRegisterTest("DetectByteJumpTestPacket02", DetectByteJumpTestPacket02, 1);
+ UtRegisterTest("DetectByteJumpTestPacket03", DetectByteJumpTestPacket03, 1);
+ UtRegisterTest("DetectByteJumpTestPacket04", DetectByteJumpTestPacket04, 1);
+ UtRegisterTest("DetectByteJumpTestPacket05", DetectByteJumpTestPacket05, 1);
+ UtRegisterTest("DetectByteJumpTestPacket06", DetectByteJumpTestPacket06, 1);
+ UtRegisterTest("DetectByteJumpTestPacket07", DetectByteJumpTestPacket07, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-bytejump.h b/framework/src/suricata/src/detect-bytejump.h
new file mode 100644
index 00000000..35051486
--- /dev/null
+++ b/framework/src/suricata/src/detect-bytejump.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_BYTEJUMP_H__
+#define __DETECT_BYTEJUMP_H__
+
+/** Bytejump Base */
+#define DETECT_BYTEJUMP_BASE_UNSET 0 /**< Unset type value string (automatic)*/
+#define DETECT_BYTEJUMP_BASE_OCT 8 /**< "oct" type value string */
+#define DETECT_BYTEJUMP_BASE_DEC 10 /**< "dec" type value string */
+#define DETECT_BYTEJUMP_BASE_HEX 16 /**< "hex" type value string */
+
+/** Bytejump Flags */
+#define DETECT_BYTEJUMP_BEGIN 0x01 /**< "from_beginning" jump */
+#define DETECT_BYTEJUMP_LITTLE 0x02 /**< "little" endian value */
+#define DETECT_BYTEJUMP_BIG 0x04 /**< "big" endian value */
+#define DETECT_BYTEJUMP_STRING 0x08 /**< "string" value */
+#define DETECT_BYTEJUMP_RELATIVE 0x10 /**< "relative" offset */
+#define DETECT_BYTEJUMP_ALIGN 0x20 /**< "align" offset */
+#define DETECT_BYTEJUMP_DCE 0x40 /**< "dce" enabled */
+#define DETECT_BYTEJUMP_OFFSET_BE 0x80 /**< "byte extract" enabled */
+
+typedef struct DetectBytejumpData_ {
+ uint8_t nbytes; /**< Number of bytes to compare */
+ uint8_t base; /**< String value base (oct|dec|hex) */
+ uint8_t flags; /**< Flags (big|little|relative|string) */
+ uint32_t multiplier; /**< Multiplier for nbytes (multiplier n)*/
+ int32_t offset; /**< Offset in payload to extract value */
+ int32_t post_offset; /**< Offset to adjust post-jump */
+} DetectBytejumpData;
+
+/* prototypes */
+
+/**
+ * Registration function for byte_jump.
+ *
+ * \todo add support for no_stream and stream_only
+ */
+void DetectBytejumpRegister (void);
+
+/**
+ * This function is used to add the parsed byte_jump data
+ * into the current signature.
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectBytejumpSetup(DetectEngineCtx *, Signature *, char *);
+
+/**
+ * \brief this function will free memory associated with DetectBytejumpData
+ *
+ * \param data pointer to DetectBytejumpData
+ */
+void DetectBytejumpFree(void *ptr);
+
+/**
+ * This function is used to parse byte_jump options passed via
+ *
+ * byte_jump: bytes, offset [,flags [, ...]]
+ *
+ * flags: "big", "little", "relative", "string", "oct", "dec", "hex"
+ * "align", "from beginning", "multiplier N", "post_offset N"
+ *
+ * \param optstr Pointer to the user provided byte_jump options
+ * \param offset Used to pass the offset back, if byte_jump uses a byte_extract
+ * var.
+ *
+ * \retval data pointer to DetectBytejumpData on success
+ * \retval NULL on failure
+ */
+DetectBytejumpData *DetectBytejumpParse(char *optstr, char **offset);
+
+/**
+ * This function is used to match byte_jump
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectBytejumpData
+ *
+ * \retval -1 error
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \todo The return seems backwards. We should return a non-zero error code.
+ * One of the error codes is "no match". As-is if someone accidentally
+ * does: if (DetectBytejumpMatch(...)) { match }, then they catch an
+ * error as a match.
+ */
+int DetectBytejumpMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx);
+int DetectBytejumpDoMatch(DetectEngineThreadCtx *, Signature *, const SigMatchCtx *,
+ uint8_t *, uint32_t, uint8_t, int32_t);
+
+#endif /* __DETECT_BYTEJUMP_H__ */
+
diff --git a/framework/src/suricata/src/detect-bytetest.c b/framework/src/suricata/src/detect-bytetest.c
new file mode 100644
index 00000000..365ad55c
--- /dev/null
+++ b/framework/src/suricata/src/detect-bytetest.c
@@ -0,0 +1,1580 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements byte_test keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-bytetest.h"
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+#include "app-layer.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "detect-pcre.h"
+
+
+/**
+ * \brief Regex for parsing our options
+ */
+/** \todo We probably just need a simple tokenizer here */
+#define PARSE_REGEX "^\\s*" \
+ "([^\\s,]+)" \
+ "\\s*,\\s*(\\!?)\\s*([^\\s,]*)" \
+ "\\s*,\\s*([^\\s,]+)" \
+ "\\s*,\\s*([^\\s,]+)" \
+ "(?:\\s*,\\s*([^\\s,]+))?" \
+ "(?:\\s*,\\s*([^\\s,]+))?" \
+ "(?:\\s*,\\s*([^\\s,]+))?" \
+ "(?:\\s*,\\s*([^\\s,]+))?" \
+ "(?:\\s*,\\s*([^\\s,]+))?" \
+ "\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+void DetectBytetestRegisterTests(void);
+
+void DetectBytetestRegister (void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_BYTETEST].name = "byte_test";
+ sigmatch_table[DETECT_BYTETEST].Match = DetectBytetestMatch;
+ sigmatch_table[DETECT_BYTETEST].Setup = DetectBytetestSetup;
+ sigmatch_table[DETECT_BYTETEST].Free = DetectBytetestFree;
+ sigmatch_table[DETECT_BYTETEST].RegisterTests = DetectBytetestRegisterTests;
+
+ sigmatch_table[DETECT_BYTETEST].flags |= SIGMATCH_PAYLOAD;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/** \brief Bytetest detection code
+ *
+ * Byte test works on the packet payload.
+ *
+ * \param det_ctx thread de ctx
+ * \param s signature
+ * \param m sigmatch for this bytettest
+ * \param payload ptr to the start of the buffer to inspect
+ * \param payload_len length of the payload
+ * \retval 1 match
+ * \retval 0 no match
+ */
+int DetectBytetestDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s, const SigMatchCtx *ctx, uint8_t *payload, uint32_t payload_len,
+ uint8_t flags, int32_t offset, uint64_t value)
+{
+ SCEnter();
+
+ const DetectBytetestData *data = (const DetectBytetestData *)ctx;
+ uint8_t *ptr = NULL;
+ int32_t len = 0;
+ uint64_t val = 0;
+ int extbytes;
+ int neg;
+ int match;
+
+ if (payload_len == 0) {
+ SCReturnInt(0);
+ }
+
+ /* Calculate the ptr value for the bytetest and length remaining in
+ * the packet from that point.
+ */
+ if (flags & DETECT_BYTETEST_RELATIVE) {
+ SCLogDebug("relative, working with det_ctx->buffer_offset %"PRIu32", "
+ "data->offset %"PRIi32"", det_ctx->buffer_offset, data->offset);
+
+ ptr = payload + det_ctx->buffer_offset;
+ len = payload_len - det_ctx->buffer_offset;
+
+ ptr += offset;
+ len -= offset;
+
+ /* No match if there is no relative base */
+ if (ptr == NULL || len <= 0) {
+ SCReturnInt(0);
+ }
+ //PrintRawDataFp(stdout,ptr,len);
+ }
+ else {
+ SCLogDebug("absolute, data->offset %"PRIi32"", data->offset);
+
+ ptr = payload + offset;
+ len = payload_len - offset;
+ }
+
+ /* Validate that the to-be-extracted is within the packet
+ * \todo Should this validate it is in the *payload*?
+ */
+ if (ptr < payload || data->nbytes > len) {
+ SCLogDebug("Data not within payload pkt=%p, ptr=%p, len=%"PRIu32", nbytes=%d",
+ payload, ptr, len, data->nbytes);
+ SCReturnInt(0);
+ }
+
+ neg = flags & DETECT_BYTETEST_NEGOP;
+
+ /* Extract the byte data */
+ if (flags & DETECT_BYTETEST_STRING) {
+ extbytes = ByteExtractStringUint64(&val, data->base,
+ data->nbytes, (const char *)ptr);
+ if (extbytes <= 0) {
+ /* strtoull() return 0 if there is no numeric value in data string */
+ if (val == 0) {
+ SCLogDebug("No Numeric value");
+ SCReturnInt(0);
+ } else {
+ SCLogError(SC_ERR_INVALID_NUM_BYTES, "Error extracting %d "
+ "bytes of string data: %d", data->nbytes, extbytes);
+ SCReturnInt(-1);
+ }
+ }
+
+ SCLogDebug("comparing base %d string 0x%" PRIx64 " %s%c 0x%" PRIx64 "",
+ data->base, val, (neg ? "!" : ""), data->op, data->value);
+ }
+ else {
+ int endianness = (flags & DETECT_BYTETEST_LITTLE) ?
+ BYTE_LITTLE_ENDIAN : BYTE_BIG_ENDIAN;
+ extbytes = ByteExtractUint64(&val, endianness, data->nbytes, ptr);
+ if (extbytes != data->nbytes) {
+ SCLogError(SC_ERR_INVALID_NUM_BYTES, "Error extracting %d bytes "
+ "of numeric data: %d\n", data->nbytes, extbytes);
+ SCReturnInt(-1);
+ }
+
+ SCLogDebug("comparing numeric 0x%" PRIx64 " %s%c 0x%" PRIx64 "",
+ val, (neg ? "!" : ""), data->op, data->value);
+ }
+
+ /* Compare using the configured operator */
+ match = 0;
+ switch (data->op) {
+ case DETECT_BYTETEST_OP_EQ:
+ if (val == value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_LT:
+ if (val < value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_GT:
+ if (val > value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_AND:
+ if (val & value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_OR:
+ if (val ^ value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_GE:
+ if (val >= value) {
+ match = 1;
+ }
+ break;
+ case DETECT_BYTETEST_OP_LE:
+ if (val <= value) {
+ match = 1;
+ }
+ break;
+ default:
+ /* Should never get here as we handle this in parsing. */
+ SCReturnInt(-1);
+ }
+
+ /* A successful match depends on negation */
+ if ((!neg && match) || (neg && !match)) {
+ SCLogDebug("MATCH");
+ SCReturnInt(1);
+ }
+
+ SCLogDebug("NO MATCH");
+ SCReturnInt(0);
+
+}
+
+int DetectBytetestMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ return DetectBytetestDoMatch(det_ctx, s, ctx, p->payload, p->payload_len,
+ ((DetectBytetestData *)ctx)->flags, 0, 0);
+}
+
+DetectBytetestData *DetectBytetestParse(char *optstr, char **value, char **offset)
+{
+ DetectBytetestData *data = NULL;
+ char *args[9] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL
+ };
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i;
+ uint32_t nbytes;
+ const char *str_ptr = NULL;
+
+ /* Execute the regex and populate args with captures. */
+ ret = pcre_exec(parse_regex, parse_regex_study, optstr,
+ strlen(optstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 6 || ret > 10) {
+ SCLogError(SC_ERR_PCRE_PARSE, "parse error, ret %" PRId32
+ ", string %s", ret, optstr);
+ goto error;
+ }
+ for (i = 0; i < (ret - 1); i++) {
+ res = pcre_get_substring((char *)optstr, ov, MAX_SUBSTRINGS,
+ i + 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed "
+ "for arg %d", i + 1);
+ goto error;
+ }
+ args[i] = (char *)str_ptr;
+ }
+
+ /* Initialize the data */
+ data = SCMalloc(sizeof(DetectBytetestData));
+ if (unlikely(data == NULL))
+ goto error;
+ data->base = DETECT_BYTETEST_BASE_UNSET;
+ data->flags = 0;
+
+
+ /*
+ * The first four options are required and positional. The
+ * remaining arguments are flags and are not positional.
+ */
+
+ /* Number of bytes */
+ if (ByteExtractStringUint32(&nbytes, 10, 0, args[0]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed number of bytes: %s", str_ptr);
+ goto error;
+ }
+
+ /* Operator is next two args: neg + op */
+ data->op = 0;
+ if (args[1] != NULL && *args[1] == '!') {
+ data->flags |= DETECT_BYTETEST_NEGOP;
+ }
+
+ if (args[2] != NULL) {
+ if ((strcmp("=", args[2]) == 0) || ((data->flags & DETECT_BYTETEST_NEGOP)
+ && strcmp("", args[2]) == 0)) {
+ data->op |= DETECT_BYTETEST_OP_EQ;
+ } else if (strcmp("<", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_LT;
+ } else if (strcmp(">", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_GT;
+ } else if (strcmp("&", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_AND;
+ } else if (strcmp("^", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_OR;
+ } else if (strcmp(">=", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_GE;
+ } else if (strcmp("<=", args[2]) == 0) {
+ data->op |= DETECT_BYTETEST_OP_LE;
+ } else {
+ SCLogError(SC_ERR_INVALID_OPERATOR, "Invalid operator");
+ goto error;
+ }
+ }
+
+ /* Value */
+ if (args[3][0] != '-' && isalpha((unsigned char)args[3][0])) {
+ if (value == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "byte_test supplied with "
+ "var name for value. \"value\" argument supplied to "
+ "this function has to be non-NULL");
+ goto error;
+ }
+ *value = SCStrdup(args[3]);
+ if (*value == NULL)
+ goto error;
+ } else {
+ if (ByteExtractStringUint64(&data->value, 0, 0, args[3]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed value: %s", str_ptr);
+ goto error;
+ }
+ }
+
+ /* Offset */
+ if (args[4][0] != '-' && isalpha((unsigned char)args[4][0])) {
+ if (offset == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "byte_test supplied with "
+ "var name for offset. \"offset\" argument supplied to "
+ "this function has to be non-NULL");
+ goto error;
+ }
+ *offset = SCStrdup(args[4]);
+ if (*offset == NULL)
+ goto error;
+ } else {
+ if (ByteExtractStringInt32(&data->offset, 0, 0, args[4]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, " Malformed offset: %s", str_ptr);
+ goto error;
+ }
+ }
+
+ /* The remaining options are flags. */
+ /** \todo Error on dups? */
+ for (i = 5; i < (ret - 1); i++) {
+ if (args[i] != NULL) {
+ if (strcmp("relative", args[i]) == 0) {
+ data->flags |= DETECT_BYTETEST_RELATIVE;
+ } else if (strcasecmp("string", args[i]) == 0) {
+ data->flags |= DETECT_BYTETEST_STRING;
+ } else if (strcasecmp("dec", args[i]) == 0) {
+ data->base |= DETECT_BYTETEST_BASE_DEC;
+ } else if (strcasecmp("hex", args[i]) == 0) {
+ data->base |= DETECT_BYTETEST_BASE_HEX;
+ } else if (strcasecmp("oct", args[i]) == 0) {
+ data->base |= DETECT_BYTETEST_BASE_OCT;
+ } else if (strcasecmp("big", args[i]) == 0) {
+ if (data->flags & DETECT_BYTETEST_LITTLE) {
+ data->flags ^= DETECT_BYTETEST_LITTLE;
+ }
+ data->flags |= DETECT_BYTETEST_BIG;
+ } else if (strcasecmp("little", args[i]) == 0) {
+ data->flags |= DETECT_BYTETEST_LITTLE;
+ } else if (strcasecmp("dce", args[i]) == 0) {
+ data->flags |= DETECT_BYTETEST_DCE;
+ } else {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "Unknown value: \"%s\"",
+ args[i]);
+ goto error;
+ }
+ }
+ }
+
+ if (data->flags & DETECT_BYTETEST_STRING) {
+ /* 23 - This is the largest string (octal, with a zero prefix) that
+ * will not overflow uint64_t. The only way this length
+ * could be over 23 and still not overflow is if it were zero
+ * prefixed and we only support 1 byte of zero prefix for octal.
+ *
+ * "01777777777777777777777" = 0xffffffffffffffff
+ */
+ if (nbytes > 23) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot test more than 23 bytes with \"string\": %s",
+ optstr);
+ goto error;
+ }
+ } else {
+ if (nbytes > 8) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot test more than 8 bytes without \"string\": %s",
+ optstr);
+ goto error;
+ }
+ if (data->base != DETECT_BYTETEST_BASE_UNSET) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Cannot use a base without \"string\": %s", optstr);
+ goto error;
+ }
+ }
+
+ /* This is max 23 so it will fit in a byte (see above) */
+ data->nbytes = (uint8_t)nbytes;
+
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+ return data;
+
+error:
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+ if (data != NULL) DetectBytetestFree(data);
+ return NULL;
+}
+
+int DetectBytetestSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ SigMatch *sm = NULL;
+ SigMatch *prev_pm = NULL;
+ DetectBytetestData *data = NULL;
+ char *value = NULL;
+ char *offset = NULL;
+ int ret = -1;
+
+ data = DetectBytetestParse(optstr, &value, &offset);
+ if (data == NULL)
+ goto error;
+
+ int sm_list;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA) {
+ if (data->flags & DETECT_BYTETEST_DCE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "dce bytetest specified "
+ "with file_data option set.");
+ goto error;
+ }
+ AppLayerHtpEnableResponseBodyCallback();
+ }
+ sm_list = s->list;
+ s->flags |= SIG_FLAG_APPLAYER;
+ if (data->flags & DETECT_BYTETEST_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, s->sm_lists_tail[sm_list],
+ DETECT_PCRE, s->sm_lists_tail[sm_list]);
+ }
+
+ } else if (data->flags & DETECT_BYTETEST_DCE) {
+ if (data->flags & DETECT_BYTETEST_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 12,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ }
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ } else if (data->flags & DETECT_BYTETEST_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 168,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ if (prev_pm == NULL) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto error;
+ }
+
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ if (data->flags & DETECT_BYTETEST_DCE) {
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Non dce alproto sig has "
+ "bytetest with dce enabled");
+ goto error;
+ }
+ if ((data->flags & DETECT_BYTETEST_STRING) ||
+ (data->flags & DETECT_BYTETEST_LITTLE) ||
+ (data->flags & DETECT_BYTETEST_BIG) ||
+ (data->base == DETECT_BYTETEST_BASE_DEC) ||
+ (data->base == DETECT_BYTETEST_BASE_HEX) ||
+ (data->base == DETECT_BYTETEST_BASE_OCT) ) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. "
+ "A byte_test keyword with dce holds other invalid modifiers.");
+ goto error;
+ }
+ }
+
+ if (value != NULL) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(value, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unknown byte_extract var "
+ "seen in byte_test - %s\n", value);
+ goto error;
+ }
+ data->value = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ data->flags |= DETECT_BYTETEST_VALUE_BE;
+ SCFree(value);
+ }
+
+ if (offset != NULL) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(offset, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unknown byte_extract var "
+ "seen in byte_test - %s\n", offset);
+ goto error;
+ }
+ data->offset = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ data->flags |= DETECT_BYTETEST_OFFSET_BE;
+ SCFree(offset);
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_BYTETEST;
+ sm->ctx = (SigMatchCtx *)data;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ if (!(data->flags & DETECT_BYTETEST_RELATIVE))
+ goto okay;
+
+ if (prev_pm == NULL)
+ goto okay;
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ okay:
+ ret = 0;
+ return ret;
+ error:
+ DetectBytetestFree(data);
+ return ret;
+}
+
+/**
+ * \brief this function will free memory associated with DetectBytetestData
+ *
+ * \param data pointer to DetectBytetestData
+ */
+void DetectBytetestFree(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ DetectBytetestData *data = (DetectBytetestData *)ptr;
+ SCFree(data);
+}
+
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+#include "util-unittest-helper.h"
+/**
+ * \test DetectBytetestTestParse01 is a test to make sure that we return "something"
+ * when given valid bytetest opt
+ */
+int DetectBytetestTestParse01(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, =, 1 , 0", NULL, NULL);
+ if (data != NULL) {
+ DetectBytetestFree(data);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse02 is a test for setting the required opts
+ */
+int DetectBytetestTestParse02(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, !=, 1, 0", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 4)
+ && (data->value == 1)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_NEGOP)
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse03 is a test for setting the relative flag
+ */
+int DetectBytetestTestParse03(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, !=, 1, 0, relative", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 4)
+ && (data->value == 1)
+ && (data->offset == 0)
+ && (data->flags == ( DETECT_BYTETEST_NEGOP
+ |DETECT_BYTETEST_RELATIVE))
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse04 is a test for setting the string/oct flags
+ */
+int DetectBytetestTestParse04(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, !=, 1, 0, string, oct", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 4)
+ && (data->value == 1)
+ && (data->offset == 0)
+ && (data->flags == ( DETECT_BYTETEST_NEGOP
+ |DETECT_BYTETEST_STRING))
+ && (data->base == DETECT_BYTETEST_BASE_OCT))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse05 is a test for setting the string/dec flags
+ */
+int DetectBytetestTestParse05(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, =, 1, 0, string, dec", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 4)
+ && (data->value == 1)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_STRING)
+ && (data->base == DETECT_BYTETEST_BASE_DEC))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse06 is a test for setting the string/hex flags
+ */
+int DetectBytetestTestParse06(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, >, 1, 0, string, hex", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_GT)
+ && (data->nbytes == 4)
+ && (data->value == 1)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_STRING)
+ && (data->base == DETECT_BYTETEST_BASE_HEX))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse07 is a test for setting the big flag
+ */
+int DetectBytetestTestParse07(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, <, 5, 0, big", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_LT)
+ && (data->nbytes == 4)
+ && (data->value == 5)
+ && (data->offset == 0)
+ && (data->flags == 4)
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse08 is a test for setting the little flag
+ */
+int DetectBytetestTestParse08(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, <, 5, 0, little", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_LT)
+ && (data->nbytes == 4)
+ && (data->value == 5)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_LITTLE)
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse09 is a test for neg operator only
+ */
+int DetectBytetestTestParse09(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, !, 5, 0", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 4)
+ && (data->value == 5)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_NEGOP)
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse10 is a test for whitespace
+ */
+int DetectBytetestTestParse10(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse(" 4 , ! &, 5 , 0 , little ", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_AND)
+ && (data->nbytes == 4)
+ && (data->value == 5)
+ && (data->offset == 0)
+ && (data->flags == (DETECT_BYTETEST_NEGOP|DETECT_BYTETEST_LITTLE))
+ && (data->base == DETECT_BYTETEST_BASE_UNSET))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse11 is a test for whitespace
+ */
+int DetectBytetestTestParse11(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4,!^,5,0,little,string,relative,hex", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_OR)
+ && (data->nbytes == 4)
+ && (data->value == 5)
+ && (data->offset == 0)
+ && (data->flags == ( DETECT_BYTETEST_NEGOP
+ |DETECT_BYTETEST_LITTLE
+ |DETECT_BYTETEST_STRING
+ |DETECT_BYTETEST_RELATIVE))
+ && (data->base == DETECT_BYTETEST_BASE_HEX))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse12 is a test for hex w/o string
+ */
+int DetectBytetestTestParse12(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, =, 1, 0, hex", NULL, NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse13 is a test for too many bytes to extract
+ */
+int DetectBytetestTestParse13(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("9, =, 1, 0", NULL, NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse14 is a test for large string extraction
+ */
+int DetectBytetestTestParse14(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("23,=,0xffffffffffffffffULL,0,string,oct", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_EQ)
+ && (data->nbytes == 23)
+ && (data->value == 0xffffffffffffffffULL)
+ && (data->offset == 0)
+ && (data->flags == DETECT_BYTETEST_STRING)
+ && (data->base == DETECT_BYTETEST_BASE_OCT))
+ {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse15 is a test for too many bytes to extract (string)
+ */
+int DetectBytetestTestParse15(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("24, =, 0xffffffffffffffffULL, 0, string", NULL, NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectBytetestTestParse16 is a test for offset too big
+ */
+int DetectBytetestTestParse16(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4,=,0,0xffffffffffffffffULL", NULL, NULL);
+ if (data == NULL) {
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytetestTestParse17(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, <, 5, 0, dce", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_LT) &&
+ (data->nbytes == 4) &&
+ (data->value == 5) &&
+ (data->offset == 0) &&
+ (data->flags & DETECT_BYTETEST_DCE) ) {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytetestTestParse18(void)
+{
+ int result = 0;
+ DetectBytetestData *data = NULL;
+ data = DetectBytetestParse("4, <, 5, 0", NULL, NULL);
+ if (data != NULL) {
+ if ( (data->op == DETECT_BYTETEST_OP_LT) &&
+ (data->nbytes == 4) &&
+ (data->value == 5) &&
+ (data->offset == 0) &&
+ !(data->flags & DETECT_BYTETEST_DCE) ) {
+ result = 1;
+ }
+ DetectBytetestFree(data);
+ }
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytetestTestParse19(void)
+{
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 1;
+
+ s->alproto = ALPROTO_DCERPC;
+
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,dce") == 0);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,string,dce") == -1);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,big,dce") == -1);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,little,dce") == -1);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,hex,dce") == -1);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,oct,dce") == -1);
+ result &= (DetectBytetestSetup(NULL, s, "1,=,1,6,dec,dce") == -1);
+
+ SigFree(s);
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytetestTestParse20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ DetectBytetestData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_test:1,=,1,6,relative,dce; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTETEST);
+ bd = (DetectBytetestData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (!(bd->flags & DETECT_BYTETEST_DCE) &&
+ !(bd->flags & DETECT_BYTETEST_RELATIVE) &&
+ (bd->flags & DETECT_BYTETEST_STRING) &&
+ (bd->flags & DETECT_BYTETEST_BIG) &&
+ (bd->flags & DETECT_BYTETEST_LITTLE) &&
+ (bd->flags & DETECT_BYTETEST_NEGOP) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_test:1,=,1,6,relative,dce; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTETEST);
+ bd = (DetectBytetestData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (!(bd->flags & DETECT_BYTETEST_DCE) &&
+ !(bd->flags & DETECT_BYTETEST_RELATIVE) &&
+ (bd->flags & DETECT_BYTETEST_STRING) &&
+ (bd->flags & DETECT_BYTETEST_BIG) &&
+ (bd->flags & DETECT_BYTETEST_LITTLE) &&
+ (bd->flags & DETECT_BYTETEST_NEGOP) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "byte_test:1,=,1,6,relative; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_BYTETEST);
+ bd = (DetectBytetestData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if ((bd->flags & DETECT_BYTETEST_DCE) &&
+ !(bd->flags & DETECT_BYTETEST_RELATIVE) &&
+ (bd->flags & DETECT_BYTETEST_STRING) &&
+ (bd->flags & DETECT_BYTETEST_BIG) &&
+ (bd->flags & DETECT_BYTETEST_LITTLE) &&
+ (bd->flags & DETECT_BYTETEST_NEGOP) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test dce option.
+ */
+int DetectBytetestTestParse21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,string,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,big,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,little,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,hex,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,dec,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,oct,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,string,hex,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,big,string,hex,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,big,string,oct,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,little,string,hex,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytetest_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "content:\"one\"; byte_test:1,=,1,6,big,string,dec,dce; sid:1;)");
+ if (s != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test file_data
+ */
+static int DetectBytetestTestParse22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectBytetestData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; byte_test:1,=,1,6,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("empty server body list: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_BYTETEST) {
+ printf("bytetest not last sm in server body list: ");
+ goto end;
+ }
+
+ bd = (DetectBytetestData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (bd->flags & DETECT_BYTETEST_DCE &&
+ bd->flags & DETECT_BYTETEST_RELATIVE &&
+ (bd->flags & DETECT_BYTETEST_STRING) &&
+ (bd->flags & DETECT_BYTETEST_BIG) &&
+ (bd->flags & DETECT_BYTETEST_LITTLE) &&
+ (bd->flags & DETECT_BYTETEST_NEGOP) ) {
+ printf("wrong flags: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectByteTestTestPacket01 is a test to check matches of
+ * byte_test and byte_test relative works if the previous keyword is pcre
+ * (bug 142)
+ */
+int DetectByteTestTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre + byte_test + "
+ "relative\"; pcre:\"/AllWorkAndNoPlayMakesWillADullBoy/\"; byte_test:1,=,1"
+ ",6,relative,string,dec; sid:126; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test DetectByteTestTestPacket02 is a test to check matches of
+ * byte_test and byte_test relative works if the previous keyword is byte_jump
+ * (bug 158)
+ */
+int DetectByteTestTestPacket02 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"content + byte_test + "
+ "relative\"; byte_jump:1,44,string,dec; byte_test:1,=,0,0,relative,string,"
+ "dec; sid:777; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+int DetectByteTestTestPacket03(void)
+{
+ int result = 0;
+ uint8_t *buf = NULL;
+ uint16_t buflen = 0;
+ buf = SCMalloc(4);
+ if (unlikely(buf == NULL)) {
+ printf("malloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(buf, "boom", 4);
+ buflen = 4;
+
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"content + byte_test\"; "
+ "byte_test:1,=,65,214748364; sid:1; rev:1;)";
+
+ result = !UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+
+end:
+ return result;
+}
+
+/** \test Test the byte_test signature matching with operator <= */
+int DetectByteTestTestPacket04(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"content + byte_test +"
+ "relative\"; content:\"GET \"; depth:4; content:\"HTTP/1.\"; "
+ "byte_test:1,<=,0,0,relative,string,dec; sid:124; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+
+end:
+ return result;
+}
+
+/** \test Test the byte_test signature matching with operator >= */
+int DetectByteTestTestPacket05(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"content + byte_test +"
+ "relative\"; content:\"GET \"; depth:4; content:\"HTTP/1.\"; "
+ "byte_test:1,>=,0,0,relative,string,dec; sid:125; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+
+/**
+ * \brief this function registers unit tests for DetectBytetest
+ */
+void DetectBytetestRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectBytetestTestParse01", DetectBytetestTestParse01, 1);
+ UtRegisterTest("DetectBytetestTestParse02", DetectBytetestTestParse02, 1);
+ UtRegisterTest("DetectBytetestTestParse03", DetectBytetestTestParse03, 1);
+ UtRegisterTest("DetectBytetestTestParse04", DetectBytetestTestParse04, 1);
+ UtRegisterTest("DetectBytetestTestParse05", DetectBytetestTestParse05, 1);
+ UtRegisterTest("DetectBytetestTestParse06", DetectBytetestTestParse06, 1);
+ UtRegisterTest("DetectBytetestTestParse07", DetectBytetestTestParse07, 1);
+ UtRegisterTest("DetectBytetestTestParse08", DetectBytetestTestParse08, 1);
+ UtRegisterTest("DetectBytetestTestParse09", DetectBytetestTestParse09, 1);
+ UtRegisterTest("DetectBytetestTestParse10", DetectBytetestTestParse10, 1);
+ UtRegisterTest("DetectBytetestTestParse11", DetectBytetestTestParse11, 1);
+ UtRegisterTest("DetectBytetestTestParse12", DetectBytetestTestParse12, 1);
+ UtRegisterTest("DetectBytetestTestParse13", DetectBytetestTestParse13, 1);
+ UtRegisterTest("DetectBytetestTestParse14", DetectBytetestTestParse14, 1);
+ UtRegisterTest("DetectBytetestTestParse15", DetectBytetestTestParse15, 1);
+ UtRegisterTest("DetectBytetestTestParse17", DetectBytetestTestParse17, 1);
+ UtRegisterTest("DetectBytetestTestParse18", DetectBytetestTestParse18, 1);
+ UtRegisterTest("DetectBytetestTestParse19", DetectBytetestTestParse19, 1);
+ UtRegisterTest("DetectBytetestTestParse20", DetectBytetestTestParse20, 1);
+ UtRegisterTest("DetectBytetestTestParse21", DetectBytetestTestParse21, 1);
+ UtRegisterTest("DetectBytetestTestParse22", DetectBytetestTestParse22, 1);
+
+ UtRegisterTest("DetectByteTestTestPacket01", DetectByteTestTestPacket01, 1);
+ UtRegisterTest("DetectByteTestTestPacket02", DetectByteTestTestPacket02, 1);
+ UtRegisterTest("DetectByteTestTestPacket03", DetectByteTestTestPacket03, 1);
+ UtRegisterTest("DetectByteTestTestPacket04", DetectByteTestTestPacket04, 1);
+ UtRegisterTest("DetectByteTestTestPacket05", DetectByteTestTestPacket05, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-bytetest.h b/framework/src/suricata/src/detect-bytetest.h
new file mode 100644
index 00000000..09d453fa
--- /dev/null
+++ b/framework/src/suricata/src/detect-bytetest.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_BYTETEST_H__
+#define __DETECT_BYTETEST_H__
+
+/** Bytetest Operators */
+#define DETECT_BYTETEST_OP_LT 1 /**< "less than" operator */
+#define DETECT_BYTETEST_OP_GT 2 /**< "greater than" operator */
+#define DETECT_BYTETEST_OP_EQ 3 /**< "equals" operator */
+#define DETECT_BYTETEST_OP_AND 4 /**< "bitwise and" operator */
+#define DETECT_BYTETEST_OP_OR 5 /**< "bitwise or" operator */
+#define DETECT_BYTETEST_OP_GE 6 /**< greater than equal operator */
+#define DETECT_BYTETEST_OP_LE 7 /**< less than equal operator */
+
+/** Bytetest Base */
+#define DETECT_BYTETEST_BASE_UNSET 0 /**< Unset type value string (automatic)*/
+#define DETECT_BYTETEST_BASE_OCT 8 /**< "oct" type value string */
+#define DETECT_BYTETEST_BASE_DEC 10 /**< "dec" type value string */
+#define DETECT_BYTETEST_BASE_HEX 16 /**< "hex" type value string */
+
+/** Bytetest Flags */
+#define DETECT_BYTETEST_NEGOP 0x01 /**< "!" negated operator */
+#define DETECT_BYTETEST_LITTLE 0x02 /**< "little" endian value */
+#define DETECT_BYTETEST_BIG 0x04 /**< "bi" endian value */
+#define DETECT_BYTETEST_STRING 0x08 /**< "string" value */
+#define DETECT_BYTETEST_RELATIVE 0x10 /**< "relative" offset */
+#define DETECT_BYTETEST_DCE 0x20 /**< dce enabled */
+#define DETECT_BYTETEST_VALUE_BE 0x40 /**< byte extract value enabled */
+#define DETECT_BYTETEST_OFFSET_BE 0x80 /**< byte extract value enabled */
+
+typedef struct DetectBytetestData_ {
+ uint8_t nbytes; /**< Number of bytes to compare */
+ uint8_t op; /**< Operator used to compare */
+ uint8_t base; /**< String value base (oct|dec|hex) */
+ uint8_t flags; /**< Flags (big|little|relative|string) */
+ int32_t offset; /**< Offset in payload */
+ uint64_t value; /**< Value to compare against */
+} DetectBytetestData;
+
+/* prototypes */
+
+/**
+ * Registration function for byte_test.
+ *
+ * \todo add support for no_stream and stream_only
+ */
+void DetectBytetestRegister (void);
+
+/**
+ * This function is used to add the parsed byte_test data
+ * into the current signature.
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectBytetestSetup(DetectEngineCtx *, Signature *, char *);
+
+/**
+ * \brief this function will free memory associated with DetectBytetestData
+ *
+ * \param data pointer to DetectBytetestData
+ */
+void DetectBytetestFree(void *ptr);
+
+/**
+ * This function is used to parse byte_test options passed via
+ *
+ * byte_test: bytes, [!]op, value, offset [,flags [, ...]]
+ *
+ * flags: "big", "little", "relative", "string", "oct", "dec", "hex"
+ *
+ * \param optstr Pointer to the user provided byte_test options
+ * \param value Used to pass the value back, if byte_test uses a byte_extract
+ * var.
+ * \param offset Used to pass the offset back, if byte_test uses a byte_extract
+ * var.
+ *
+ * \retval data pointer to DetectBytetestData on success
+ * \retval NULL on failure
+ */
+DetectBytetestData *DetectBytetestParse(char *optstr, char **value,
+ char **offset);
+
+/**
+ * This function is used to match byte_test
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectBytetestData
+ *
+ * \retval -1 error
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \todo The return seems backwards. We should return a non-zero error code. One of the error codes is "no match". As-is if someone accidentally does: if (DetectBytetestMatch(...)) { match }, then they catch an error as a match.
+ */
+int DetectBytetestMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx);
+int DetectBytetestDoMatch(DetectEngineThreadCtx *, Signature *,
+ const SigMatchCtx *ctx, uint8_t *, uint32_t,
+ uint8_t, int32_t, uint64_t);
+
+#endif /* __DETECT_BYTETEST_H__ */
diff --git a/framework/src/suricata/src/detect-classtype.c b/framework/src/suricata/src/detect-classtype.c
new file mode 100644
index 00000000..bc773710
--- /dev/null
+++ b/framework/src/suricata/src/detect-classtype.c
@@ -0,0 +1,342 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements classtype keyword.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-classtype.h"
+#include "flow-var.h"
+#include "util-classification-config.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#define DETECT_CLASSTYPE_REGEX "^\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*$"
+
+static pcre *regex = NULL;
+static pcre_extra *regex_study = NULL;
+
+static int DetectClasstypeSetup(DetectEngineCtx *, Signature *, char *);
+void DetectClasstypeRegisterTests(void);
+
+/**
+ * \brief Registers the handler functions for the "Classtype" keyword.
+ */
+void DetectClasstypeRegister(void)
+{
+ const char *eb = NULL;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("Registering the Classtype keyword handler");
+
+ sigmatch_table[DETECT_CLASSTYPE].name = "classtype";
+ sigmatch_table[DETECT_CLASSTYPE].desc = "information about the classification of rules and alerts";
+ sigmatch_table[DETECT_CLASSTYPE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Classtype";
+ sigmatch_table[DETECT_CLASSTYPE].Match = NULL;
+ sigmatch_table[DETECT_CLASSTYPE].Setup = DetectClasstypeSetup;
+ sigmatch_table[DETECT_CLASSTYPE].Free = NULL;
+ sigmatch_table[DETECT_CLASSTYPE].RegisterTests = DetectClasstypeRegisterTests;
+
+ regex = pcre_compile(DETECT_CLASSTYPE_REGEX, opts, &eb, &eo, NULL);
+ if (regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_CLASSTYPE_REGEX, eo, eb);
+ goto end;
+ }
+
+ regex_study = pcre_study(regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto end;
+ }
+
+ end:
+ return;
+}
+
+/**
+ * \brief Parses the raw string supplied with the "Classtype" keyword.
+ *
+ * \param Pointer to the string to be parsed.
+ *
+ * \retval bool success or failure.
+ */
+static int DetectClasstypeParseRawString(char *rawstr, char *out, size_t outsize)
+{
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+ size_t len = strlen(rawstr);
+
+ /* get rid of the double quotes if present */
+ if (rawstr[0] == '\"' && rawstr[strlen(rawstr) - 1] == '\"') {
+ rawstr++;
+ rawstr[strlen(rawstr) - 1] = '\0';
+ len -= 2;
+ }
+
+ ret = pcre_exec(regex, regex_study, rawstr, len, 0, 0, ov, 30);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Invalid Classtype in Signature");
+ goto end;
+ }
+
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, out, outsize);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto end;
+ }
+
+ return 0;
+ end:
+ return -1;
+}
+
+/**
+ * \brief The setup function that would be called when the Signature parsing
+ * module encounters the "Classtype" keyword.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer the current Signature instance that is being parsed.
+ * \param rawstr Pointer to the argument supplied to the classtype keyword.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+static int DetectClasstypeSetup(DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ char parsed_ct_name[1024] = "";
+ SCClassConfClasstype *ct = NULL;
+
+ if (DetectClasstypeParseRawString(rawstr, parsed_ct_name, sizeof(parsed_ct_name)) < -1) {
+ SCLogError(SC_ERR_PCRE_PARSE, "Error parsing classtype argument supplied with the "
+ "classtype keyword");
+ goto error;
+ }
+
+ ct = SCClassConfGetClasstype(parsed_ct_name, de_ctx);
+ if (ct == NULL) {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "Unknown Classtype: \"%s\". Invalidating the Signature",
+ parsed_ct_name);
+ goto error;
+ }
+
+ /* if we have retrieved the classtype, assign the message to be displayed
+ * for this Signature by fast.log, if a Packet matches this Signature */
+ s->class = ct->classtype_id;
+ s->class_msg = ct->classtype_desc;
+
+ /* if a priority keyword has appeared before the classtype, s->prio would
+ * hold a value which is != -1, in which case we don't overwrite the value.
+ * Otherwise, overwrite the value */
+ if (s->prio == -1)
+ s->prio = ct->priority;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/*------------------------------Unittests-------------------------------------*/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Check that supplying an invalid classtype in the rule, results in the
+ * rule being invalidated.
+ */
+int DetectClasstypeTest01()
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; "
+ "Classtype:not_available; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+/**
+ * \test Check that both valid and invalid classtypes in a rule are handled
+ * properly, with rules containing invalid classtypes being rejected
+ * and the ones containing valid classtypes parsed and returned.
+ */
+int DetectClasstypeTest02()
+{
+ int result = 0;
+ Signature *last = NULL;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:bad-unknown; sid:1;)");
+ if (sig == NULL) {
+ printf("first sig failed to parse: ");
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list = last = sig;
+ result = (sig != NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:not-there; sid:1;)");
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:Bad-UnkNown; sid:1;)");
+ if (sig == NULL) {
+ printf("second sig failed to parse: ");
+ result = 0;
+ goto end;
+ }
+ last->next = sig;
+ last = sig;
+ result &= (sig != NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:nothing-wrong; sid:1;)");
+ if (sig == NULL) {
+ result = 0;
+ goto end;
+ }
+ last->next = sig;
+ last = sig;
+ result &= (sig != NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:attempted_dos; sid:1;)");
+ last->next = sig;
+ result &= (sig == NULL);
+
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+/**
+ * \test Check that the signatures are assigned priority based on classtype they
+ * are given.
+ */
+int DetectClasstypeTest03()
+{
+ int result = 0;
+ Signature *last = NULL;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:bad-unknown; priority:1; sid:1;)");
+ if (sig == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list = last = sig;
+ result = (sig != NULL);
+ result &= (sig->prio == 1);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Classtype test\"; Classtype:unKnoWn; "
+ "priority:3; sid:1;)");
+ if (sig == NULL) {
+ result = 0;
+ goto end;
+ }
+ last->next = sig;
+ last = sig;
+ result &= (sig != NULL);
+ result &= (sig->prio == 3);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
+ "Classtype:nothing-wrong; priority:1; sid:1;)");
+ if (sig == NULL) {
+ result = 0;
+ goto end;
+ }
+ last->next = sig;
+ last = sig;
+ result &= (sig != NULL);
+ result &= (sig->prio == 1);
+
+
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for Classification Config API.
+ */
+void DetectClasstypeRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("DetectClasstypeTest01", DetectClasstypeTest01, 1);
+ UtRegisterTest("DetectClasstypeTest02", DetectClasstypeTest02, 1);
+ UtRegisterTest("DetectClasstypeTest03", DetectClasstypeTest03, 1);
+
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/detect-classtype.h b/framework/src/suricata/src/detect-classtype.h
new file mode 100644
index 00000000..6e0dd509
--- /dev/null
+++ b/framework/src/suricata/src/detect-classtype.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_CLASSTYPE_H__
+#define __DETECT_CLASSTYPE_H__
+
+/* prototypes */
+void DetectClasstypeRegister(void);
+
+#endif /* __DETECT_CLASSTYPE_H__ */
+
diff --git a/framework/src/suricata/src/detect-content.c b/framework/src/suricata/src/detect-content.c
new file mode 100644
index 00000000..5f315cc5
--- /dev/null
+++ b/framework/src/suricata/src/detect-content.c
@@ -0,0 +1,2824 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Simple content match part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+#include "util-mpm.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "detect-flow.h"
+#include "app-layer.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "threads.h"
+#include "util-unittest-helper.h"
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+int DetectContentMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *);
+int DetectContentSetup(DetectEngineCtx *, Signature *, char *);
+void DetectContentRegisterTests(void);
+
+void DetectContentRegister (void)
+{
+ sigmatch_table[DETECT_CONTENT].name = "content";
+ sigmatch_table[DETECT_CONTENT].desc = "match on payload content";
+ sigmatch_table[DETECT_CONTENT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Content";
+ sigmatch_table[DETECT_CONTENT].Match = NULL;
+ sigmatch_table[DETECT_CONTENT].Setup = DetectContentSetup;
+ sigmatch_table[DETECT_CONTENT].Free = DetectContentFree;
+ sigmatch_table[DETECT_CONTENT].RegisterTests = DetectContentRegisterTests;
+
+ sigmatch_table[DETECT_CONTENT].flags |= SIGMATCH_PAYLOAD;
+}
+
+/* pass on the content_max_id */
+uint32_t DetectContentMaxId(DetectEngineCtx *de_ctx)
+{
+ return MpmPatternIdStoreGetMaxId(de_ctx->mpm_pattern_id_store);
+}
+
+/**
+ * \brief Parse a content string, ie "abc|DE|fgh"
+ *
+ * \param content_str null terminated string containing the content
+ * \param result result pointer to pass the fully parsed byte array
+ * \param result_len size of the resulted data
+ * \param flags flags to be set by this parsing function
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int DetectContentDataParse(const char *keyword, const char *contentstr,
+ uint8_t **pstr, uint16_t *plen, uint32_t *flags)
+{
+ char *str = NULL;
+ uint16_t len;
+ uint16_t pos = 0;
+ uint16_t slen = 0;
+
+ slen = strlen(contentstr);
+ if (slen == 0) {
+ return -1;
+ }
+
+ /* skip the first spaces */
+ while (pos < slen && isspace((unsigned char)contentstr[pos]))
+ pos++;
+
+ if (contentstr[pos] == '!') {
+ *flags = DETECT_CONTENT_NEGATED;
+ pos++;
+ } else
+ *flags = 0;
+
+ if (contentstr[pos] == '\"' && ((slen - pos) <= 1))
+ goto error;
+
+ if (!(contentstr[pos] == '\"' && contentstr[slen - 1] == '\"')) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "%s keyword arguments "
+ "should be always enclosed in double quotes. Invalid "
+ "content keyword passed in this rule - \"%s\"",
+ keyword, contentstr);
+ goto error;
+ }
+
+ if ((str = SCStrdup(contentstr + pos + 1)) == NULL)
+ goto error;
+ str[strlen(str) - 1] = '\0';
+
+ len = strlen(str);
+ if (len == 0)
+ goto error;
+
+ SCLogDebug("\"%s\", len %" PRIu32 "", str, len);
+
+ //SCLogDebug("DetectContentParse: \"%s\", len %" PRIu32 "", str, len);
+ char converted = 0;
+
+ {
+ uint16_t i, x;
+ uint8_t bin = 0;
+ uint8_t escape = 0;
+ uint8_t binstr[3] = "";
+ uint8_t binpos = 0;
+ uint16_t bin_count = 0;
+
+ for (i = 0, x = 0; i < len; i++) {
+ // SCLogDebug("str[%02u]: %c", i, str[i]);
+ if (str[i] == '|') {
+ bin_count++;
+ if (bin) {
+ bin = 0;
+ } else {
+ bin = 1;
+ }
+ } else if(!escape && str[i] == '\\') {
+ escape = 1;
+ } else {
+ if (bin) {
+ if (isdigit((unsigned char)str[i]) ||
+ str[i] == 'A' || str[i] == 'a' ||
+ str[i] == 'B' || str[i] == 'b' ||
+ str[i] == 'C' || str[i] == 'c' ||
+ str[i] == 'D' || str[i] == 'd' ||
+ str[i] == 'E' || str[i] == 'e' ||
+ str[i] == 'F' || str[i] == 'f')
+ {
+ // SCLogDebug("part of binary: %c", str[i]);
+
+ binstr[binpos] = (char)str[i];
+ binpos++;
+
+ if (binpos == 2) {
+ uint8_t c = strtol((char *)binstr, (char **) NULL, 16) & 0xFF;
+ binpos = 0;
+ str[x] = c;
+ x++;
+ converted = 1;
+ }
+ } else if (str[i] == ' ') {
+ // SCLogDebug("space as part of binary string");
+ }
+ else if (str[i] != ',') {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid hex code in "
+ "content - %s, hex %c. Invalidating signature", str, str[i]);
+ goto error;
+ }
+ } else if (escape) {
+ if (str[i] == ':' ||
+ str[i] == ';' ||
+ str[i] == '\\' ||
+ str[i] == '\"')
+ {
+ str[x] = str[i];
+ x++;
+ } else {
+ //SCLogDebug("Can't escape %c", str[i]);
+ goto error;
+ }
+ escape = 0;
+ converted = 1;
+ } else {
+ str[x] = str[i];
+ x++;
+ }
+ }
+ }
+
+ if (bin_count % 2 != 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid hex code assembly in "
+ "%s - %s. Invalidating signature", keyword, contentstr);
+ goto error;
+ }
+
+ if (converted) {
+ len = x;
+ }
+ }
+
+ *plen = len;
+ *pstr = (uint8_t *)str;
+ return 0;
+
+error:
+ if (str != NULL)
+ SCFree(str);
+ return -1;
+}
+/**
+ * \brief DetectContentParse
+ * \initonly
+ */
+DetectContentData *DetectContentParse (char *contentstr)
+{
+ DetectContentData *cd = NULL;
+ uint8_t *content = NULL;
+ uint16_t len = 0;
+ uint32_t flags = 0;
+ int ret;
+
+ ret = DetectContentDataParse("content", contentstr, &content, &len, &flags);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ cd = SCMalloc(sizeof(DetectContentData) + len);
+ if (unlikely(cd == NULL)) {
+ SCFree(content);
+ exit(EXIT_FAILURE);
+ }
+
+ memset(cd, 0, sizeof(DetectContentData) + len);
+
+ if (flags == DETECT_CONTENT_NEGATED)
+ cd->flags |= DETECT_CONTENT_NEGATED;
+
+ cd->content = (uint8_t *)cd + sizeof(DetectContentData);
+ memcpy(cd->content, content, len);
+ cd->content_len = len;
+
+ /* Prepare Boyer Moore context for searching faster */
+ cd->bm_ctx = BoyerMooreCtxInit(cd->content, cd->content_len);
+ cd->depth = 0;
+ cd->offset = 0;
+ cd->within = 0;
+ cd->distance = 0;
+
+ SCFree(content);
+ return cd;
+
+}
+
+DetectContentData *DetectContentParseEncloseQuotes(char *contentstr)
+{
+ char str[strlen(contentstr) + 3]; // 2 for quotes, 1 for \0
+
+ str[0] = '\"';
+ memcpy(str + 1, contentstr, strlen(contentstr));
+ str[strlen(contentstr) + 1] = '\"';
+ str[strlen(contentstr) + 2] = '\0';
+
+ return DetectContentParse(str);
+}
+
+/**
+ * \brief Helper function to print a DetectContentData
+ */
+void DetectContentPrint(DetectContentData *cd)
+{
+ int i = 0;
+ if (cd == NULL) {
+ SCLogDebug("DetectContentData \"cd\" is NULL");
+ return;
+ }
+ char *tmpstr=SCMalloc(sizeof(char) * cd->content_len + 1);
+
+ if (tmpstr != NULL) {
+ for (i = 0; i < cd->content_len; i++) {
+ if (isprint(cd->content[i]))
+ tmpstr[i] = cd->content[i];
+ else
+ tmpstr[i] = '.';
+ }
+ tmpstr[i] = '\0';
+ SCLogDebug("Content: \"%s\"", tmpstr);
+ SCFree(tmpstr);
+ } else {
+ SCLogDebug("Content: ");
+ for (i = 0; i < cd->content_len; i++)
+ SCLogDebug("%c", cd->content[i]);
+ }
+
+ SCLogDebug("Content_id: %"PRIu32, cd->id);
+ SCLogDebug("Content_len: %"PRIu16, cd->content_len);
+ SCLogDebug("Depth: %"PRIu16, cd->depth);
+ SCLogDebug("Offset: %"PRIu16, cd->offset);
+ SCLogDebug("Within: %"PRIi32, cd->within);
+ SCLogDebug("Distance: %"PRIi32, cd->distance);
+ SCLogDebug("flags: %u ", cd->flags);
+ SCLogDebug("negated: %s ", cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false");
+ SCLogDebug("relative match next: %s ", cd->flags & DETECT_CONTENT_RELATIVE_NEXT ? "true" : "false");
+ if (cd->replace && cd->replace_len) {
+ char *tmpstr=SCMalloc(sizeof(char) * cd->replace_len + 1);
+
+ if (tmpstr != NULL) {
+ for (i = 0; i < cd->replace_len; i++) {
+ if (isprint(cd->replace[i]))
+ tmpstr[i] = cd->replace[i];
+ else
+ tmpstr[i] = '.';
+ }
+ tmpstr[i] = '\0';
+ SCLogDebug("Replace: \"%s\"", tmpstr);
+ SCFree(tmpstr);
+ } else {
+ SCLogDebug("Replace: ");
+ for (i = 0; i < cd->replace_len; i++)
+ SCLogDebug("%c", cd->replace[i]);
+ }
+ }
+ SCLogDebug("-----------");
+}
+
+/**
+ * \brief Print list of DETECT_CONTENT SigMatch's allocated in a
+ * SigMatch list, from the current sm to the end
+ * \param sm pointer to the current SigMatch to start printing from
+ */
+void DetectContentPrintAll(SigMatch *sm)
+{
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ int i = 0;
+
+ if (sm == NULL)
+ return;
+
+ SigMatch *first_sm = sm;
+
+ /* Print all of them */
+ for (; first_sm != NULL; first_sm = first_sm->next) {
+ if (first_sm->type == DETECT_CONTENT) {
+ SCLogDebug("Printing SigMatch DETECT_CONTENT %d", ++i);
+ DetectContentPrint((DetectContentData*)first_sm->ctx);
+ }
+ }
+ }
+#endif /* DEBUG */
+}
+
+/**
+ * \brief Function to setup a content pattern.
+ *
+ * \param de_ctx pointer to the current detection_engine
+ * \param s pointer to the current Signature
+ * \param m pointer to the last parsed SigMatch
+ * \param contentstr pointer to the current keyword content string
+ * \retval -1 if error
+ * \retval 0 if all was ok
+ */
+int DetectContentSetup(DetectEngineCtx *de_ctx, Signature *s, char *contentstr)
+{
+ DetectContentData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ cd = DetectContentParse(contentstr);
+ if (cd == NULL)
+ goto error;
+ DetectContentPrint(cd);
+
+ int sm_list;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA && s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpEnableResponseBodyCallback();
+ s->alproto = ALPROTO_HTTP;
+ }
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ sm_list = s->list;
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->ctx = (void *)cd;
+ sm->type = DETECT_CONTENT;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ return 0;
+
+error:
+ DetectContentFree(cd);
+ return -1;
+}
+
+/**
+ * \brief this function will SCFree memory associated with DetectContentData
+ *
+ * \param cd pointer to DetectCotentData
+ */
+void DetectContentFree(void *ptr)
+{
+ SCEnter();
+ DetectContentData *cd = (DetectContentData *)ptr;
+
+ if (cd == NULL)
+ SCReturn;
+
+ BoyerMooreCtxDeInit(cd->bm_ctx);
+
+ SCFree(cd);
+ SCReturn;
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectCotentParseTest01 this is a test to make sure we can deal with escaped colons
+ */
+int DetectContentParseTest01 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"abc\\:def\"";
+ char *teststringparsed = "abc:def";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest02 this is a test to make sure we can deal with escaped semi-colons
+ */
+int DetectContentParseTest02 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"abc\\;def\"";
+ char *teststringparsed = "abc;def";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest03 this is a test to make sure we can deal with escaped double-quotes
+ */
+int DetectContentParseTest03 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"abc\\\"def\"";
+ char *teststringparsed = "abc\"def";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest04 this is a test to make sure we can deal with escaped backslashes
+ */
+int DetectContentParseTest04 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"abc\\\\def\"";
+ char *teststringparsed = "abc\\def";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ uint16_t len = (cd->content_len > strlen(teststringparsed));
+ if (memcmp(cd->content, teststringparsed, len) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest05 test illegal escape
+ */
+int DetectContentParseTest05 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"abc\\def\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got ");
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest06 test a binary content
+ */
+int DetectContentParseTest06 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"a|42|c|44|e|46|\"";
+ char *teststringparsed = "abcdef";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ uint16_t len = (cd->content_len > strlen(teststringparsed));
+ if (memcmp(cd->content, teststringparsed, len) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest07 test an empty content
+ */
+int DetectContentParseTest07 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got %p: ", cd);
+ result = 0;
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectCotentParseTest08 test an empty content
+ */
+int DetectContentParseTest08 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got %p: ", cd);
+ result = 0;
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+/**
+ * \test Test packet Matches
+ * \param raw_eth_pkt pointer to the ethernet packet
+ * \param pktsize size of the packet
+ * \param sig pointer to the signature to test
+ * \param sid sid number of the signature
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+int DetectContentLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize, char *sig,
+ uint32_t sid)
+{
+ int result = 0;
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth_pkt, pktsize, NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+ de_ctx->sig_list->next = NULL;
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) {
+ DetectContentData *co = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) {
+ printf("relative next flag set on final match which is content: ");
+ goto end;
+ }
+ }
+
+ SCLogDebug("---DetectContentLongPatternMatchTest---");
+ DetectContentPrintAll(de_ctx->sig_list->sm_lists[DETECT_SM_LIST_MATCH]);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, sid) != 1) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \brief Wrapper for DetectContentLongPatternMatchTest
+ */
+int DetectContentLongPatternMatchTestWrp(char *sig, uint32_t sid)
+{
+ /** Real packet with the following tcp data:
+ * "Hi, this is a big test to check content matches of splitted"
+ * "patterns between multiple chunks!"
+ * (without quotes! :) )
+ */
+ uint8_t raw_eth_pkt[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00,
+ 0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06,
+ 0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00,
+ 0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,
+ 0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66,
+ 0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65,
+ 0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+ 0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65,
+ 0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69,
+ 0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e,
+ 0x6b,0x73,0x21 }; /* end raw_eth_pkt */
+
+ return DetectContentLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
+ sig, sid);
+}
+
+/**
+ * \test Check if we match a normal pattern (not splitted)
+ */
+int DetectContentLongPatternMatchTest01()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test\"; sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match a splitted pattern
+ */
+int DetectContentLongPatternMatchTest02()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test to check content matches of"
+ " splitted patterns between multiple chunks!\"; sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we don't match the signature if one of the splitted
+ * chunks doesn't match the packet
+ */
+int DetectContentLongPatternMatchTest03()
+{
+ /** The last chunk of the content should not match */
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test to check content matches of"
+ " splitted patterns between multiple splitted chunks!\"; sid:1;)";
+ return (DetectContentLongPatternMatchTestWrp(sig, 1) == 0) ? 1: 0;
+}
+
+/**
+ * \test Check if we match multiple content (not splitted)
+ */
+int DetectContentLongPatternMatchTest04()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is\"; depth:15 ;content:\"a big test\"; "
+ " within:15; content:\"to check content matches of\"; "
+ " within:30; content:\"splitted patterns\"; distance:1; "
+ " within:30; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we match packets with multiple chunks and not chunks
+ * Here we should specify only contents that fit in 32 bytes
+ * Each of them with their modifier values
+ */
+int DetectContentLongPatternMatchTest05()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big\"; depth:17; "
+ " isdataat:30, relative; "
+ " content:\"test\"; within: 5; distance:1; "
+ " isdataat:15, relative; "
+ " content:\"of splitted\"; within:37; distance:15; "
+ " isdataat:20,relative; "
+ " content:\"patterns\"; within:9; distance:1; "
+ " isdataat:10, relative; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we match packets with multiple chunks and not chunks
+ * Here we should specify contents that fit and contents that must be splitted
+ * Each of them with their modifier values
+ */
+int DetectContentLongPatternMatchTest06()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big test to check cont\"; depth:36;"
+ " content:\"ent matches\"; within:11; distance:0; "
+ " content:\"of splitted patterns between multiple\"; "
+ " within:38; distance:1; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+int DetectContentLongPatternMatchTest07()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"chunks!\"; "
+ " content:\"content matches\"; offset:32; depth:47; "
+ " content:\"of splitted patterns between multiple\"; "
+ " content:\"Hi, this is a big\"; offset:0; depth:17; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+int DetectContentLongPatternMatchTest08()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"ent matches\"; "
+ " content:\"of splitted patterns between multiple\"; "
+ " within:38; distance:1; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " content:\"Hi, this is a big test to check cont\"; depth:36;"
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+int DetectContentLongPatternMatchTest09()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"ent matches\"; "
+ " content:\"of splitted patterns between multiple\"; "
+ " offset:47; depth:85; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " content:\"Hi, this is a big test to chec\"; depth:36;"
+ " content:\"k cont\"; distance:0; within:6;"
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match two consecutive simple contents
+ */
+int DetectContentLongPatternMatchTest10()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big test to check \"; "
+ " content:\"con\"; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match two contents of length 1
+ */
+int DetectContentLongPatternMatchTest11()
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"H\"; "
+ " content:\"i\"; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+int DetectContentParseTest09(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = "!\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+
+ return result;
+}
+
+int DetectContentParseTest10(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = "!\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest11(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (!(cd->flags & DETECT_CONTENT_NEGATED))
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest12(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = "\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (!(cd->flags & DETECT_CONTENT_NEGATED))
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest13(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = "!\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest14(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = " \"!boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (!(cd->flags & DETECT_CONTENT_NEGATED))
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest15(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = " !\"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ result = 1;
+
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+int DetectContentParseNegTest16(void)
+{
+ int result = 0;
+ DetectContentData *cd = NULL;
+ char *teststring = " \"boo\"";
+
+ cd = DetectContentParse(teststring);
+ if (cd != NULL) {
+ result = (cd->content_len == 3 && memcmp(cd->content,"boo",3) == 0);
+ DetectContentFree(cd);
+ }
+ return result;
+}
+
+/**
+ * \test Test cases where if within specified is < content lenggth we invalidate
+ * the sig.
+ */
+int DetectContentParseTest17(void)
+{
+ int result = 0;
+ char *sigstr = "alert tcp any any -> any any (msg:\"Dummy\"; "
+ "content:\"one\"; content:\"two\"; within:2; sid:1;)";
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->sig_list = SigInit(de_ctx, sigstr);
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+end:
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectContentParseTest18(void)
+{
+ Signature *s = SigAlloc();
+ int result = 1;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+
+ result &= (DetectContentSetup(de_ctx, s, "\"one\"") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ SigFree(s);
+
+ s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ result &= (DetectContentSetup(de_ctx, s, "\"one\"") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ end:
+ SigFree(s);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+
+int DetectContentParseTest19(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with content\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf ("failed dce iface, stub_data with content ");
+ result = 0;
+ goto end;
+ }
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with contents & distance, within\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; content:\"two\"; within:10; sid:1;)");
+ if (s->next == NULL) {
+ printf("failed dce iface, stub_data with content & distance, within");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->within == 10);
+/*
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with contents & offset, depth\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; offset:5; depth:9; "
+ "content:\"two\"; within:10; sid:1;)");
+ if (s->next == NULL) {
+ printf ("failed dce iface, stub_data with contents & offset, depth");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->offset == 5 && data->depth == 9);
+ data = (DetectContentData *)s->sm_lists[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub with contents, distance\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "content:\"two\"; distance:2; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->distance == 2);
+*/
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub with contents, distance, within\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "content:\"two\"; within:10; distance:2; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->within == 10 && data->distance == 2);
+/*
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with content, offset\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; offset:10; sid:1;)");
+ if (s->next == NULL) {
+ printf ("Failed dce iface, stub_data with content, offset ");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->offset == 10);
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with content, depth\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; depth:10; sid:1;)");
+ if (s->next == NULL) {
+ printf ("failed dce iface, stub_data with content, depth");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->depth == 10);
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with content, offset, depth\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; offset:10; depth:3; sid:1;)");
+ if (s->next == NULL) {
+ printf("failed dce iface, stub_data with content, offset, depth");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_CONTENT);
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL);
+ data = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ||
+ result == 0) {
+ result = 0;
+ goto end;
+ }
+ result &= (data->offset == 10 && data->depth == 13);
+*/
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing content\"; "
+ "content:\"one\"; sid:1;)");
+ if (s->next == NULL) {
+ printf ("failed testing content");
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectContentParseTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"boo; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:boo\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectContentData *cd = 0;
+ Signature *s = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content: !\"boo\"; sid:238012;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL || s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx == NULL) {
+ printf("de_ctx->pmatch_tail == NULL || de_ctx->pmatch_tail->ctx == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ cd = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ result = (strncmp("boo", (char *)cd->content, cd->content_len) == 0);
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|asdf\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectContentParseTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: file_data
+ */
+static int DetectContentParseTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: file_data
+ */
+static int DetectContentParseTest37(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; content:\"def\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: file_data
+ */
+static int DetectContentParseTest38(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; content:\"def\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int SigTestPositiveTestContent(char *rule, uint8_t *buf)
+{
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Parsing test: file_data, within relative to file_data
+ */
+static int DetectContentParseTest39(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: file_data, distance relative to file_data
+ */
+static int DetectContentParseTest40(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; distance:3; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectContentParseTest41(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 257;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '\"';
+ for (int i = 0; i < (patlen - 2); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\"';
+ teststring[idx++] = '\0';
+
+ cd = DetectContentParse(teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SCFree(teststring);
+ DetectContentFree(cd);
+ return result;
+}
+
+/**
+ * Tests that content lengths > 255 are supported.
+ */
+int DetectContentParseTest42(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 258;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '\"';
+ for (int i = 0; i < (patlen - 2); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\"';
+ teststring[idx++] = '\0';
+
+ cd = DetectContentParse(teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SCFree(teststring);
+ DetectContentFree(cd);
+ return result;
+}
+
+int DetectContentParseTest43(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 260;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '\"';
+ teststring[idx++] = '|';
+ teststring[idx++] = '4';
+ teststring[idx++] = '6';
+ teststring[idx++] = '|';
+ for (int i = 0; i < (patlen - 6); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\"';
+ teststring[idx++] = '\0';
+
+ cd = DetectContentParse(teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SCFree(teststring);
+ DetectContentFree(cd);
+ return result;
+}
+
+/**
+ * Tests that content lengths > 255 are supported.
+ */
+int DetectContentParseTest44(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 261;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '\"';
+ teststring[idx++] = '|';
+ teststring[idx++] = '4';
+ teststring[idx++] = '6';
+ teststring[idx++] = '|';
+ for (int i = 0; i < (patlen - 6); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\"';
+ teststring[idx++] = '\0';
+
+ cd = DetectContentParse(teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SCFree(teststring);
+ DetectContentFree(cd);
+ return result;
+}
+
+static int SigTestNegativeTestContent(char *rule, uint8_t *buf)
+{
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 0) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test A positive test that checks that the content string doesn't contain
+ * the negated content
+ */
+static int SigTest41TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:!\"GES\"; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A positive test that checks that the content string doesn't contain
+ * the negated content within the specified depth
+ */
+static int SigTest42TestNegatedContent(void)
+{ // 01 5 10 15 20 24
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:!\"twentythree\"; depth:22; offset:35; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that checks that the content string doesn't contain
+ * the negated content within the specified depth, and also after the
+ * specified offset. Since the content is there, the match fails.
+ *
+ * Match is at offset:23, depth:34
+ */
+static int SigTest43TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:!\"twentythree\"; depth:34; offset:23; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that checks that the content string doesn't contain
+ * the negated content after the specified offset and within the specified
+ * depth.
+ */
+static int SigTest44TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:!\"twentythree\"; offset:40; depth:35; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A positive test that uses a combination of content string with negated
+ * content string
+ */
+static int SigTest45TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; depth:5; content:!\"twentythree\"; depth:23; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that uses a combination of content string with negated
+ * content string, with we receiving a failure for 'onee' itself.
+ */
+static int SigTest46TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"onee\"; content:!\"twentythree\"; depth:23; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that uses a combination of content string with negated
+ * content string, with we receiving a failure of first content's offset
+ * condition
+ */
+static int SigTest47TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; offset:5; content:!\"twentythree\"; depth:23; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A positive test that checks that we don't have a negated content within
+ * the specified length from the previous content match.
+ */
+static int SigTest48TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET\"; content:!\"GES\"; within:26; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content with the use of within
+ */
+static int SigTest49TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET\"; content:!\"Host\"; within:26; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content with the use of distance
+ */
+static int SigTest50TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET\"; content:!\"GES\"; distance:25; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content with the use of distance
+ *
+ * First GET at offset 0
+ * First Host at offset 21
+ */
+static int SigTest51TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"GET\"; content:!\"Host\"; distance:17; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\nHost: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, with the content not being present
+ */
+static int SigTest52TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GES\"; content:!\"BOO\"; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest53TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:!\"fourty\"; within:56; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest54TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:!\"fourty\"; within:20; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that checks the use of negated content along with
+ * the presence of depth
+ */
+static int SigTest55TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:!\"one\"; depth:5; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A positive test that checks the combined use of 2 contents in the
+ * presence of within
+ */
+static int SigTest56TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:\"fourty\"; within:56; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest57TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:!\"fourty\"; within:56; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content, in the presence of distance
+ */
+static int SigTest58TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:!\"fourty\"; distance:57; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of distance
+ */
+static int SigTest59TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; content:!\"fourty\"; distance:30; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest60TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:!\"one\"; content:\"fourty\"; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest61TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"fourty\"; within:30; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/** \test Test negation in combination with within and depth
+ *
+ * Match of "one" at offset:0, depth:3
+ * Match of "fourty" at offset:46, depth:52
+ *
+ * This signature should not match for the test to pass.
+ */
+static int SigTest62TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"fourty\"; within:49; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest63TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; depth:10; content:!\"fourty\"; within:56; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest64TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"fourty\"; within:30; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/** \test Test negation in combination with within and depth
+ *
+ * Match of "one" at offset:0, depth:3
+ * Match of "fourty" at offset:46, depth:52
+ *
+ * This signature should not match for the test to pass.
+ */
+static int SigTest65TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"fourty\"; distance:0; within:49; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest66TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"fourty\"; within:30; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest67TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:!\"four\"; within:56; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest68TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:\"nine\"; offset:8; content:!\"fourty\"; within:28; content:\"fiftysix\"; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest69TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; depth:10; content:\"nine\"; offset:8; content:!\"fourty\"; within:48; content:\"fiftysix\"; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest70TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; content:!\"fourty\"; within:52; distance:45 sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+/** \test within and distance */
+static int SigTest71TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; content:!\"fourty\"; within:40; distance:43; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest72TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"one\"; content:!\"fourty\"; within:49; distance:43; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest73TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"one\"; depth:5; content:!\"twentythree\"; depth:35; sid:1;)", (uint8_t *)"one four nine fourteen twentythree thirtyfive fourtysix fiftysix");
+}
+
+static int SigTest74TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:!\"PASS\"; sid:1;)", (uint8_t *)"USER apple");
+}
+
+static int SigTest75TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:\"!PASS\"; sid:1;)", (uint8_t *)"USER !PASS");
+}
+
+static int SigTest76TestBug134(void)
+{
+ uint8_t *buf = (uint8_t *)"test detect ${IFS} in traffic";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+ Flow f;
+
+ memset(&f, 0, sizeof(Flow));
+ FLOW_INITIALIZE(&f);
+
+ p->dp = 515;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW;
+
+ char sig[] = "alert tcp any any -> any 515 "
+ "(msg:\"detect IFS\"; flow:to_server,established; content:\"${IFS}\";"
+ " depth:50; offset:0; sid:900091; rev:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+static int SigTest77TestBug139(void)
+{
+ uint8_t buf[] = {
+ 0x12, 0x23, 0x34, 0x35, 0x52, 0x52, 0x24, 0x42, 0x22, 0x24,
+ 0x52, 0x24, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x34 };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_UDP);
+ int result = 0;
+
+ p->dp = 53;
+ char sig[] = "alert udp any any -> any 53 (msg:\"dns testing\";"
+ " content:\"|00 00|\"; depth:5; offset:13; sid:9436601;"
+ " rev:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int DetectLongContentTestCommon(char *sig, uint32_t sid)
+{
+ /* Packet with 512 A's in it for testing long content. */
+ static uint8_t pkt[739] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x02, 0xd5, 0x4a, 0x18, 0x40, 0x00, 0x40, 0x06,
+ 0xd7, 0xd6, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10,
+ 0x01, 0x0a, 0xdb, 0x36, 0x00, 0x50, 0xca, 0xc5,
+ 0xcc, 0xd1, 0x95, 0x77, 0x0f, 0x7d, 0x80, 0x18,
+ 0x00, 0xe5, 0x77, 0x9d, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x1d, 0xe0, 0x86, 0xc6, 0xfc, 0x73,
+ 0x49, 0xf3, 0x50, 0x4f, 0x53, 0x54, 0x20, 0x2f,
+ 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e,
+ 0x31, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63,
+ 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x33, 0x37,
+ 0x2e, 0x30, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74,
+ 0x3a, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x36, 0x2e,
+ 0x31, 0x2e, 0x31, 0x30, 0x0d, 0x0a, 0x41, 0x63,
+ 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f,
+ 0x2a, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+ 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x3a, 0x20, 0x35, 0x32, 0x38, 0x0d, 0x0a,
+ 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+ 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x78, 0x2d, 0x77, 0x77, 0x77, 0x2d,
+ 0x66, 0x6f, 0x72, 0x6d, 0x2d, 0x75, 0x72, 0x6c,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58
+ };
+
+ return DetectContentLongPatternMatchTest(pkt, (uint16_t)sizeof(pkt), sig,
+ sid);
+}
+
+static int DetectLongContentTest1(void)
+{
+ /* Signature with 256 A's. */
+ char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return DetectLongContentTestCommon(sig, 1);
+}
+
+static int DetectLongContentTest2(void)
+{
+ /* Signature with 512 A's. */
+ char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return DetectLongContentTestCommon(sig, 1);
+}
+
+static int DetectLongContentTest3(void)
+{
+ /* Signature with 513 A's. */
+ char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return !DetectLongContentTestCommon(sig, 1);
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectContent
+ */
+void DetectContentRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectContentParseTest01", DetectContentParseTest01, 1);
+ UtRegisterTest("DetectContentParseTest02", DetectContentParseTest02, 1);
+ UtRegisterTest("DetectContentParseTest03", DetectContentParseTest03, 1);
+ UtRegisterTest("DetectContentParseTest04", DetectContentParseTest04, 1);
+ UtRegisterTest("DetectContentParseTest05", DetectContentParseTest05, 1);
+ UtRegisterTest("DetectContentParseTest06", DetectContentParseTest06, 1);
+ UtRegisterTest("DetectContentParseTest07", DetectContentParseTest07, 1);
+ UtRegisterTest("DetectContentParseTest08", DetectContentParseTest08, 1);
+ UtRegisterTest("DetectContentParseTest09", DetectContentParseTest09, 1);
+ UtRegisterTest("DetectContentParseTest10", DetectContentParseTest10, 1);
+ UtRegisterTest("DetectContentParseNegTest11", DetectContentParseNegTest11, 1);
+ UtRegisterTest("DetectContentParseNegTest12", DetectContentParseNegTest12, 1);
+ UtRegisterTest("DetectContentParseNegTest13", DetectContentParseNegTest13, 1);
+ UtRegisterTest("DetectContentParseNegTest14", DetectContentParseNegTest14, 1);
+ UtRegisterTest("DetectContentParseNegTest15", DetectContentParseNegTest15, 1);
+ UtRegisterTest("DetectContentParseNegTest16", DetectContentParseNegTest16, 1);
+ UtRegisterTest("DetectContentParseTest17", DetectContentParseTest17, 1);
+ UtRegisterTest("DetectContentParseTest18", DetectContentParseTest18, 1);
+ UtRegisterTest("DetectContentParseTest19", DetectContentParseTest19, 1);
+ UtRegisterTest("DetectContentParseTest20", DetectContentParseTest20, 1);
+ UtRegisterTest("DetectContentParseTest21", DetectContentParseTest21, 1);
+ UtRegisterTest("DetectContentParseTest22", DetectContentParseTest22, 1);
+ UtRegisterTest("DetectContentParseTest23", DetectContentParseTest23, 1);
+ UtRegisterTest("DetectContentParseTest24", DetectContentParseTest24, 1);
+ UtRegisterTest("DetectContentParseTest25", DetectContentParseTest25, 1);
+ UtRegisterTest("DetectContentParseTest26", DetectContentParseTest26, 1);
+ UtRegisterTest("DetectContentParseTest27", DetectContentParseTest27, 1);
+ UtRegisterTest("DetectContentParseTest28", DetectContentParseTest28, 1);
+ UtRegisterTest("DetectContentParseTest29", DetectContentParseTest29, 1);
+ UtRegisterTest("DetectContentParseTest30", DetectContentParseTest30, 1);
+ UtRegisterTest("DetectContentParseTest31", DetectContentParseTest31, 1);
+ UtRegisterTest("DetectContentParseTest32", DetectContentParseTest32, 1);
+ UtRegisterTest("DetectContentParseTest33", DetectContentParseTest33, 1);
+ UtRegisterTest("DetectContentParseTest34", DetectContentParseTest34, 1);
+ UtRegisterTest("DetectContentParseTest35", DetectContentParseTest35, 1);
+ UtRegisterTest("DetectContentParseTest36", DetectContentParseTest36, 1);
+ UtRegisterTest("DetectContentParseTest37", DetectContentParseTest37, 1);
+ UtRegisterTest("DetectContentParseTest38", DetectContentParseTest38, 1);
+ UtRegisterTest("DetectContentParseTest39", DetectContentParseTest39, 1);
+ UtRegisterTest("DetectContentParseTest40", DetectContentParseTest40, 1);
+ UtRegisterTest("DetectContentParseTest41", DetectContentParseTest41, 1);
+ UtRegisterTest("DetectContentParseTest42", DetectContentParseTest42, 1);
+ UtRegisterTest("DetectContentParseTest43", DetectContentParseTest43, 1);
+ UtRegisterTest("DetectContentParseTest44", DetectContentParseTest44, 1);
+
+ /* The reals */
+ UtRegisterTest("DetectContentLongPatternMatchTest01", DetectContentLongPatternMatchTest01, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest02", DetectContentLongPatternMatchTest02, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest03", DetectContentLongPatternMatchTest03, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest04", DetectContentLongPatternMatchTest04, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest05", DetectContentLongPatternMatchTest05, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest06", DetectContentLongPatternMatchTest06, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest07", DetectContentLongPatternMatchTest07, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest08", DetectContentLongPatternMatchTest08, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest09", DetectContentLongPatternMatchTest09, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest10", DetectContentLongPatternMatchTest10, 1);
+ UtRegisterTest("DetectContentLongPatternMatchTest11", DetectContentLongPatternMatchTest11, 1);
+
+ /* Negated content tests */
+ UtRegisterTest("SigTest41TestNegatedContent", SigTest41TestNegatedContent, 1);
+ UtRegisterTest("SigTest42TestNegatedContent", SigTest42TestNegatedContent, 1);
+ UtRegisterTest("SigTest43TestNegatedContent", SigTest43TestNegatedContent, 1);
+ UtRegisterTest("SigTest44TestNegatedContent", SigTest44TestNegatedContent, 1);
+ UtRegisterTest("SigTest45TestNegatedContent", SigTest45TestNegatedContent, 1);
+ UtRegisterTest("SigTest46TestNegatedContent", SigTest46TestNegatedContent, 1);
+ UtRegisterTest("SigTest47TestNegatedContent", SigTest47TestNegatedContent, 1);
+ UtRegisterTest("SigTest48TestNegatedContent", SigTest48TestNegatedContent, 1);
+ UtRegisterTest("SigTest49TestNegatedContent", SigTest49TestNegatedContent, 1);
+ UtRegisterTest("SigTest50TestNegatedContent", SigTest50TestNegatedContent, 1);
+ UtRegisterTest("SigTest51TestNegatedContent", SigTest51TestNegatedContent, 1);
+ UtRegisterTest("SigTest52TestNegatedContent", SigTest52TestNegatedContent, 1);
+ UtRegisterTest("SigTest53TestNegatedContent", SigTest53TestNegatedContent, 1);
+ UtRegisterTest("SigTest54TestNegatedContent", SigTest54TestNegatedContent, 1);
+ UtRegisterTest("SigTest55TestNegatedContent", SigTest55TestNegatedContent, 1);
+ UtRegisterTest("SigTest56TestNegatedContent", SigTest56TestNegatedContent, 1);
+ UtRegisterTest("SigTest57TestNegatedContent", SigTest57TestNegatedContent, 1);
+ UtRegisterTest("SigTest58TestNegatedContent", SigTest58TestNegatedContent, 1);
+ UtRegisterTest("SigTest59TestNegatedContent", SigTest59TestNegatedContent, 1);
+ UtRegisterTest("SigTest60TestNegatedContent", SigTest60TestNegatedContent, 1);
+ UtRegisterTest("SigTest61TestNegatedContent", SigTest61TestNegatedContent, 1);
+ UtRegisterTest("SigTest62TestNegatedContent", SigTest62TestNegatedContent, 1);
+ UtRegisterTest("SigTest63TestNegatedContent", SigTest63TestNegatedContent, 1);
+ UtRegisterTest("SigTest64TestNegatedContent", SigTest64TestNegatedContent, 1);
+ UtRegisterTest("SigTest65TestNegatedContent", SigTest65TestNegatedContent, 1);
+ UtRegisterTest("SigTest66TestNegatedContent", SigTest66TestNegatedContent, 1);
+ UtRegisterTest("SigTest67TestNegatedContent", SigTest67TestNegatedContent, 1);
+ UtRegisterTest("SigTest68TestNegatedContent", SigTest68TestNegatedContent, 1);
+ UtRegisterTest("SigTest69TestNegatedContent", SigTest69TestNegatedContent, 1);
+ UtRegisterTest("SigTest70TestNegatedContent", SigTest70TestNegatedContent, 1);
+ UtRegisterTest("SigTest71TestNegatedContent", SigTest71TestNegatedContent, 1);
+ UtRegisterTest("SigTest72TestNegatedContent", SigTest72TestNegatedContent, 1);
+ UtRegisterTest("SigTest73TestNegatedContent", SigTest73TestNegatedContent, 1);
+ UtRegisterTest("SigTest74TestNegatedContent", SigTest74TestNegatedContent, 1);
+ UtRegisterTest("SigTest75TestNegatedContent", SigTest75TestNegatedContent, 1);
+
+ UtRegisterTest("SigTest76TestBug134", SigTest76TestBug134, 1);
+ UtRegisterTest("SigTest77TestBug139", SigTest77TestBug139, 1);
+
+ UtRegisterTest("DetectLongContentTest1", DetectLongContentTest1, 1);
+ UtRegisterTest("DetectLongContentTest2", DetectLongContentTest2, 1);
+ UtRegisterTest("DetectLongContentTest3", DetectLongContentTest3, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-content.h b/framework/src/suricata/src/detect-content.h
new file mode 100644
index 00000000..b2e0f969
--- /dev/null
+++ b/framework/src/suricata/src/detect-content.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_CONTENT_H__
+#define __DETECT_CONTENT_H__
+
+/* Flags affecting this content */
+
+#define DETECT_CONTENT_NOCASE (1)
+#define DETECT_CONTENT_DISTANCE (1 << 1)
+#define DETECT_CONTENT_WITHIN (1 << 2)
+#define DETECT_CONTENT_OFFSET (1 << 3)
+#define DETECT_CONTENT_DEPTH (1 << 4)
+#define DETECT_CONTENT_FAST_PATTERN (1 << 5)
+#define DETECT_CONTENT_FAST_PATTERN_ONLY (1 << 6)
+#define DETECT_CONTENT_FAST_PATTERN_CHOP (1 << 7)
+/** content applies to a "raw"/undecoded field if applicable */
+#define DETECT_CONTENT_RAWBYTES (1 << 8)
+/** content is negated */
+#define DETECT_CONTENT_NEGATED (1 << 9)
+
+/** a relative match to this content is next, used in matching phase */
+#define DETECT_CONTENT_RELATIVE_NEXT (1 << 10)
+
+/* BE - byte extract */
+#define DETECT_CONTENT_OFFSET_BE (1 << 11)
+#define DETECT_CONTENT_DEPTH_BE (1 << 12)
+#define DETECT_CONTENT_DISTANCE_BE (1 << 13)
+#define DETECT_CONTENT_WITHIN_BE (1 << 14)
+
+/* replace data */
+#define DETECT_CONTENT_REPLACE (1 << 15)
+/* this flag is set during the staging phase. It indicates that a content
+ * has been added to the mpm phase and requires no further inspection inside
+ * the inspection phase */
+#define DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED (1 << 16)
+
+#define DETECT_CONTENT_IS_SINGLE(c) (!( ((c)->flags & DETECT_CONTENT_DISTANCE) || \
+ ((c)->flags & DETECT_CONTENT_WITHIN) || \
+ ((c)->flags & DETECT_CONTENT_RELATIVE_NEXT) || \
+ ((c)->flags & DETECT_CONTENT_DEPTH) || \
+ ((c)->flags & DETECT_CONTENT_OFFSET) ))
+
+#include "util-spm-bm.h"
+
+typedef struct DetectContentData_ {
+ uint8_t *content;
+ uint16_t content_len;
+ uint16_t replace_len;
+ /* for chopped fast pattern, the length */
+ uint16_t fp_chop_len;
+ /* would want to move PatIntId here and flags down to remove the padding
+ * gap, but I think the first four members was used as a template for
+ * casting. \todo check this and fix it if posssible */
+ uint32_t flags;
+ PatIntId id;
+ uint16_t depth;
+ uint16_t offset;
+ /* for chopped fast pattern, the offset */
+ uint16_t fp_chop_offset;
+ int32_t distance;
+ int32_t within;
+ /* Boyer Moore context (for spm search) */
+ BmCtx *bm_ctx;
+ /* pointer to replacement data */
+ uint8_t *replace;
+} DetectContentData;
+
+/* prototypes */
+void DetectContentRegister (void);
+uint32_t DetectContentMaxId(DetectEngineCtx *);
+DetectContentData *DetectContentParse (char *contentstr);
+int DetectContentDataParse(const char *keyword, const char *contentstr,
+ uint8_t **pstr, uint16_t *plen, uint32_t *flags);
+DetectContentData *DetectContentParseEncloseQuotes(char *);
+
+int DetectContentSetup(DetectEngineCtx *de_ctx, Signature *s, char *contentstr);
+void DetectContentPrint(DetectContentData *);
+
+void DetectContentFree(void *);
+
+#endif /* __DETECT_CONTENT_H__ */
diff --git a/framework/src/suricata/src/detect-csum.c b/framework/src/suricata/src/detect-csum.c
new file mode 100644
index 00000000..2cf5b4ce
--- /dev/null
+++ b/framework/src/suricata/src/detect-csum.c
@@ -0,0 +1,1647 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements checksum keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-csum.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+/* prototypes for the "ipv4-csum" rule keyword */
+int DetectIPV4CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectIPV4CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectIPV4CsumFree(void *);
+
+/* prototypes for the "tcpv4-csum" rule keyword */
+int DetectTCPV4CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectTCPV4CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectTCPV4CsumFree(void *);
+
+/* prototypes for the "tcpv6-csum" rule keyword */
+int DetectTCPV6CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectTCPV6CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectTCPV6CsumFree(void *);
+
+/* prototypes for the "udpv4-csum" rule keyword */
+int DetectUDPV4CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectUDPV4CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectUDPV4CsumFree(void *);
+
+/* prototypes for the "udpv6-csum" rule keyword */
+int DetectUDPV6CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectUDPV6CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectUDPV6CsumFree(void *);
+
+/* prototypes for the "icmpv4-csum" rule keyword */
+int DetectICMPV4CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectICMPV4CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectICMPV4CsumFree(void *);
+
+/* prototypes for the "icmpv6-csum" rule keyword */
+int DetectICMPV6CsumMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectICMPV6CsumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectICMPV6CsumFree(void *);
+
+void DetectCsumRegisterTests(void);
+
+/**
+ * \brief Registers handlers for all the checksum keywords. The checksum
+ * keywords that are registered are ipv4-sum, tcpv4-csum, tcpv6-csum,
+ * udpv4-csum, udpv6-csum, icmpv4-csum and icmpv6-csum.
+ *
+ * Each of the checksum keywords implemented here takes 2 arguments -
+ * "valid" or "invalid". If the rule keyword in the signature is
+ * specified as "valid", the Match function would return TRUE if the
+ * checksum for that particular packet and protocol is valid. Similarly
+ * for "invalid".
+ *
+ * The Setup functions takes 4 arguments -
+ *
+ * DetectEngineCtx * (de_ctx) - A pointer to the detection engine context
+ * Signature *(s) - Pointer to signature for the current Signature being
+ * parsed from the rules
+ * SigMatchCtx * (m) - Pointer to the head of the SigMatchs added to the
+ * current Signature being parsed
+ * char * (csum_str) - Pointer to a string holding the keyword value
+ *
+ * The Setup function returns 0 if it successfully parses the keyword
+ * value, and -1 otherwise.
+ *
+ * The Match function takes 5 arguments -
+ *
+ * ThreadVars * (t) - Pointer to the tv for the detection module instance
+ * DetectEngineThreadCtx * (det_ctx) - Pointer to the detection engine
+ * thread context
+ * Packet * (p) - Pointer to the Packet currently being handled
+ * Signature * (s) - Pointer to the Signature, the packet is being
+ * currently matched with
+ * SigMatchCtx * (m) - Pointer to the keyword structure from the above
+ * Signature, the Packet is being currently matched
+ * with
+ *
+ * The Match function returns 1 if the Packet contents match the keyword,
+ * and 0 otherwise
+ *
+ * The Free function takes a single argument -
+ *
+ * void * (ptr) - Pointer to the DetectCsumData for a keyword
+ */
+void DetectCsumRegister (void)
+{
+ sigmatch_table[DETECT_IPV4_CSUM].name = "ipv4-csum";
+ sigmatch_table[DETECT_IPV4_CSUM].Match = DetectIPV4CsumMatch;
+ sigmatch_table[DETECT_IPV4_CSUM].Setup = DetectIPV4CsumSetup;
+ sigmatch_table[DETECT_IPV4_CSUM].Free = DetectIPV4CsumFree;
+ sigmatch_table[DETECT_IPV4_CSUM].RegisterTests = DetectCsumRegisterTests;
+
+ sigmatch_table[DETECT_TCPV4_CSUM].name = "tcpv4-csum";
+ sigmatch_table[DETECT_TCPV4_CSUM].Match = DetectTCPV4CsumMatch;
+ sigmatch_table[DETECT_TCPV4_CSUM].Setup = DetectTCPV4CsumSetup;
+ sigmatch_table[DETECT_TCPV4_CSUM].Free = DetectTCPV4CsumFree;
+ sigmatch_table[DETECT_TCPV4_CSUM].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_TCPV6_CSUM].name = "tcpv6-csum";
+ sigmatch_table[DETECT_TCPV6_CSUM].Match = DetectTCPV6CsumMatch;
+ sigmatch_table[DETECT_TCPV6_CSUM].Setup = DetectTCPV6CsumSetup;
+ sigmatch_table[DETECT_TCPV6_CSUM].Free = DetectTCPV6CsumFree;
+ sigmatch_table[DETECT_TCPV6_CSUM].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_UDPV4_CSUM].name = "udpv4-csum";
+ sigmatch_table[DETECT_UDPV4_CSUM].Match = DetectUDPV4CsumMatch;
+ sigmatch_table[DETECT_UDPV4_CSUM].Setup = DetectUDPV4CsumSetup;
+ sigmatch_table[DETECT_UDPV4_CSUM].Free = DetectUDPV4CsumFree;
+ sigmatch_table[DETECT_UDPV4_CSUM].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_UDPV6_CSUM].name = "udpv6-csum";
+ sigmatch_table[DETECT_UDPV6_CSUM].Match = DetectUDPV6CsumMatch;
+ sigmatch_table[DETECT_UDPV6_CSUM].Setup = DetectUDPV6CsumSetup;
+ sigmatch_table[DETECT_UDPV6_CSUM].Free = DetectUDPV6CsumFree;
+ sigmatch_table[DETECT_UDPV6_CSUM].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_ICMPV4_CSUM].name = "icmpv4-csum";
+ sigmatch_table[DETECT_ICMPV4_CSUM].Match = DetectICMPV4CsumMatch;
+ sigmatch_table[DETECT_ICMPV4_CSUM].Setup = DetectICMPV4CsumSetup;
+ sigmatch_table[DETECT_ICMPV4_CSUM].Free = DetectICMPV4CsumFree;
+ sigmatch_table[DETECT_ICMPV4_CSUM].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_ICMPV6_CSUM].name = "icmpv6-csum";
+ sigmatch_table[DETECT_ICMPV6_CSUM].Match = DetectICMPV6CsumMatch;
+ sigmatch_table[DETECT_ICMPV6_CSUM].Setup = DetectICMPV6CsumSetup;
+ sigmatch_table[DETECT_ICMPV6_CSUM].Free = DetectICMPV6CsumFree;
+ sigmatch_table[DETECT_ICMPV6_CSUM].RegisterTests = NULL;
+
+ return;
+}
+
+/**
+ * \brief Validates and parses the argument supplied with the checksum keyword.
+ * Accepts strings both with and without quotes, i.e. valid, \"valid\",
+ * invalid and \"invalid\"
+ *
+ * \param key Pointer to a const character string holding the csum keyword value
+ * \param cd Pointer to the DetectCsumData structure that holds the keyword
+ * value sent as argument
+ *
+ * \retval 1 the keyvalue has been parsed successfully
+ * \retval 0 error
+ */
+static int DetectCsumParseArg(const char *key, DetectCsumData *cd)
+{
+ char *str;
+
+ if (key[0] == '\"' && key[strlen(key) - 1] == '\"') {
+ str = SCStrdup(key + 1);
+ if (unlikely(str == NULL)) {
+ goto error;
+ }
+ str[strlen(key) - 2] = '\0';
+ } else {
+ str = SCStrdup(key);
+ if (unlikely(str == NULL)) {
+ goto error;
+ }
+ }
+
+ if (strcasecmp(str, DETECT_CSUM_VALID) == 0 ||
+ strcasecmp(str, DETECT_CSUM_INVALID) == 0) {
+ cd->valid = (strcasecmp(key, DETECT_CSUM_VALID) == 0);
+ SCFree(str);
+ return 1;
+ }
+
+error:
+ if (str != NULL)
+ SCFree(str);
+ return 0;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * ipv4 checksum, based on whether ipv4-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectIPV4CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip4h == NULL || PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level3_comp_csum == -1)
+ p->level3_comp_csum = IPV4CalculateChecksum((uint16_t *)p->ip4h,
+ IPV4_GET_HLEN(p));
+
+ if (p->level3_comp_csum == p->ip4h->ip_csum && cd->valid == 1)
+ return 1;
+ else if (p->level3_comp_csum != p->ip4h->ip_csum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the ipv4-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectIPV4CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_IPV4_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectIPV4CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectIPV4CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * tcpv4 checksum, based on whether tcpv4-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectTCPV4CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip4h == NULL || p->tcph == NULL || p->proto != IPPROTO_TCP || PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = TCPCalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->tcph,
+ (p->payload_len + TCP_GET_HLEN(p)));
+
+ if (p->level4_comp_csum == p->tcph->th_sum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->tcph->th_sum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the tcpv4-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectTCPV4CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TCPV4_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectTCPV4CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectTCPV4CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * tcpv6 checksum, based on whether tcpv6-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectTCPV6CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip6h == NULL || p->tcph == NULL || p->proto != IPPROTO_TCP || PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = TCPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->tcph,
+ (p->payload_len + TCP_GET_HLEN(p)));
+
+ if (p->level4_comp_csum == p->tcph->th_sum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->tcph->th_sum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the tcpv6-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectTCPV6CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TCPV6_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectTCPV6CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectTCPV6CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * udpv4 checksum, based on whether udpv4-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectUDPV4CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip4h == NULL || p->udph == NULL || p->proto != IPPROTO_UDP || PKT_IS_PSEUDOPKT(p) || p->udph->uh_sum == 0)
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = UDPV4CalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->udph,
+ (p->payload_len +
+ UDP_HEADER_LEN) );
+
+ if (p->level4_comp_csum == p->udph->uh_sum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->udph->uh_sum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the udpv4-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectUDPV4CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_UDPV4_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectUDPV4CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectUDPV4CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * udpv6 checksum, based on whether udpv6-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectUDPV6CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip6h == NULL || p->udph == NULL || p->proto != IPPROTO_UDP || PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = UDPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->udph,
+ (p->payload_len +
+ UDP_HEADER_LEN) );
+
+ if (p->level4_comp_csum == p->udph->uh_sum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->udph->uh_sum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the udpv6-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectUDPV6CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_UDPV6_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (void *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectUDPV6CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectUDPV6CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * icmpv4 checksum, based on whether icmpv4-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectICMPV4CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip4h == NULL || p->icmpv4h == NULL || p->proto != IPPROTO_ICMP || PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = ICMPV4CalculateChecksum((uint16_t *)p->icmpv4h,
+ ntohs(IPV4_GET_RAW_IPLEN(p->ip4h)) -
+ IPV4_GET_RAW_HLEN(p->ip4h) * 4);
+
+ if (p->level4_comp_csum == p->icmpv4h->checksum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->icmpv4h->checksum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the icmpv4-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectICMPV4CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ //printf("DetectCsumSetup: \'%s\'\n", csum_str);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ICMPV4_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectICMPV4CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectICMPV4CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief Checks if the packet sent as the argument, has a valid or invalid
+ * icmpv6 checksum, based on whether icmpv6-csum option for this rule
+ * has been supplied with "valid" or "invalid" argument
+ *
+ * \param t Pointer to the tv for this detection module instance
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param p Pointer to the Packet currently being matched
+ * \param s Pointer to the Signature, the packet is being currently
+ * matched with
+ * \param m Pointer to the keyword_structure(SigMatch) from the above
+ * Signature, the Packet is being currently matched with
+ *
+ * \retval 1 if the Packet contents match the keyword option; 0 otherwise
+ */
+int DetectICMPV6CsumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectCsumData *cd = (const DetectCsumData *)ctx;
+
+ if (p->ip6h == NULL || p->icmpv6h == NULL || p->proto != IPPROTO_ICMPV6 || PKT_IS_PSEUDOPKT(p) ||
+ (GET_PKT_LEN(p) - ((uint8_t *)p->icmpv6h - GET_PKT_DATA(p))) <= 0) {
+ return 0;
+ }
+
+ if (p->flags & PKT_IGNORE_CHECKSUM) {
+ return cd->valid;
+ }
+
+ if (p->level4_comp_csum == -1)
+ p->level4_comp_csum = ICMPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->icmpv6h,
+ GET_PKT_LEN(p) - ((uint8_t *)p->icmpv6h - GET_PKT_DATA(p)));
+
+ if (p->level4_comp_csum == p->icmpv6h->csum && cd->valid == 1)
+ return 1;
+ else if (p->level4_comp_csum != p->icmpv6h->csum && cd->valid == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \brief Creates a SigMatch for the icmpv6-csum keyword being sent as argument,
+ * and appends it to the Signature(s). Accepts 2 values for the
+ * keyword - "valid" and "invalid", both with and without quotes
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param csum_str Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+static int DetectICMPV6CsumSetup(DetectEngineCtx *de_ctx, Signature *s, char *csum_str)
+{
+ DetectCsumData *cd = NULL;
+ SigMatch *sm = NULL;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ICMPV6_CSUM;
+
+ if ( (cd = SCMalloc(sizeof(DetectCsumData))) == NULL)
+ goto error;
+ memset(cd, 0, sizeof(DetectCsumData));
+
+ if (DetectCsumParseArg(csum_str, cd) == 0)
+ goto error;
+
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (cd != NULL) DetectICMPV6CsumFree(cd);
+ if (sm != NULL) SCFree(sm);
+
+ return -1;
+}
+
+void DetectICMPV6CsumFree(void *ptr)
+{
+ DetectCsumData *cd = (DetectCsumData *)ptr;
+
+ if (cd != NULL)
+ SCFree(cd);
+
+ return;
+}
+
+/* ---------------------------------- Unit Tests --------------------------- */
+
+#ifdef UNITTESTS
+
+int DetectCsumIPV4ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectIPV4CsumSetup(NULL, &s, "\"valid\"") == 0);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "\"invalid\"") == 0);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "\"vaLid\"") == 0);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "\"VALID\"") == 0);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "\"iNvaLid\"") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectIPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumIPV4InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectIPV4CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectIPV4CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectIPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumIPV4ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectIPV4CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectIPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectIPV4CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectIPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV4ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV4CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectICMPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV4InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV4CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectICMPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV4ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV4CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectICMPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectICMPV4CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectICMPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV4ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV4CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectTCPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV4InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV4CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectTCPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV4ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV4CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectTCPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectTCPV4CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectTCPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV4ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV4CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectUDPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV4InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV4CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectUDPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV4ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV4CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectUDPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectUDPV4CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectUDPV4CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV6ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV6CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectTCPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV6InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV6CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectTCPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumTCPV6ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectTCPV6CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectTCPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectTCPV6CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectTCPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV6ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV6CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectUDPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV6InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV6CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectUDPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumUDPV6ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectUDPV6CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectUDPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectUDPV6CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectUDPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV6ValidArgsTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV6CsumSetup(NULL, &s, "valid") == 0);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "invalid") == 0);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "vaLid") == 0);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "VALID") == 0);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "iNvaLid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectICMPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV6InValidArgsTestParse02(void)
+{
+ Signature s;
+ int result = -1;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV6CsumSetup(NULL, &s, "vaid") == -1);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "invaalid") == -1);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "vaLiid") == -1);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "VALieD") == -1);
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "iNvamid") == -1);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ DetectICMPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+int DetectCsumICMPV6ValidArgsTestParse03(void)
+{
+ Signature s;
+ DetectCsumData *cd = NULL;
+ int result = 0;
+ SigMatch *temp = NULL;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectICMPV6CsumSetup(NULL, &s, "valid") == 0);
+
+ while (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 1);
+ }
+ DetectICMPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+ s.sm_lists[DETECT_SM_LIST_MATCH] = NULL;
+
+ result &= (DetectICMPV6CsumSetup(NULL, &s, "INVALID") == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ if (s.sm_lists[DETECT_SM_LIST_MATCH]->ctx != NULL) {
+ cd = (DetectCsumData *)s.sm_lists[DETECT_SM_LIST_MATCH]->ctx;
+ result &= (cd->valid == 0);
+ }
+ DetectICMPV6CsumFree(s.sm_lists[DETECT_SM_LIST_MATCH]->ctx);
+ temp = s.sm_lists[DETECT_SM_LIST_MATCH];
+ s.sm_lists[DETECT_SM_LIST_MATCH] = s.sm_lists[DETECT_SM_LIST_MATCH]->next;
+ SCFree(temp);
+ }
+
+ return result;
+}
+
+#include "detect-engine.h"
+#include "stream-tcp.h"
+
+int DetectCsumICMPV6Test01(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DecodeThreadVars dtv;
+
+ Packet *p = PacketGetFromAlloc();
+ if (p == NULL) {
+ printf("failure PacketGetFromAlloc\n");
+ goto end;
+ }
+
+ uint8_t pkt[] = {
+ 0x00, 0x30, 0x18, 0xa8, 0x7c, 0x23, 0x2c, 0x41,
+ 0x38, 0xa7, 0xea, 0xeb, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x3c, 0x40, 0xad, 0xa1,
+ 0x09, 0x80, 0x00, 0x01, 0xd6, 0xf3, 0x20, 0x01,
+ 0xf4, 0xbe, 0xea, 0x3c, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x32, 0xb2, 0x00, 0x01, 0x32, 0xb2,
+ 0x09, 0x80, 0x20, 0x01, 0x00, 0x00, 0x3c, 0x00,
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00,
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x63, 0xc2, 0x00, 0x00, 0x00, 0x00 };
+
+ PacketCopyData(p, pkt, sizeof(pkt));
+
+ memset(&tv, 0, sizeof(tv));
+ memset(&dtv, 0, sizeof(dtv));
+
+ StreamTcpInitConfig(TRUE);
+ FlowInitConfig(FLOW_QUIET);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("DetectEngineCtxInit failure\n");
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert ip any any -> any any "
+ "(icmpv6-csum:valid; sid:1;)");
+ if (s == NULL) {
+ printf("SigInit failed\n");
+ goto end;
+ }
+ SigGroupBuild(de_ctx);
+
+ DecodeEthernet(&tv, &dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), NULL);
+
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert on p, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+
+ SCFree(p);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectCsumRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("DetectCsumIPV4ValidArgsTestParse01",
+ DetectCsumIPV4ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumIPV4InValidArgsTestParse02",
+ DetectCsumIPV4InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumIPV4ValidArgsTestParse03",
+ DetectCsumIPV4ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumICMPV4ValidArgsTestParse01",
+ DetectCsumICMPV4ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumICMPV4InValidArgsTestParse02",
+ DetectCsumICMPV4InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumICMPV4ValidArgsTestParse03",
+ DetectCsumICMPV4ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumTCPV4ValidArgsTestParse01",
+ DetectCsumTCPV4ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumTCPV4InValidArgsTestParse02",
+ DetectCsumTCPV4InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumTCPV4ValidArgsTestParse03",
+ DetectCsumTCPV4ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumUDPV4ValidArgsTestParse01",
+ DetectCsumUDPV4ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumUDPV4InValidArgsTestParse02",
+ DetectCsumUDPV4InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumUDPV4ValidArgsTestParse03",
+ DetectCsumUDPV4ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumUDPV6ValidArgsTestParse01",
+ DetectCsumUDPV6ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumUDPV6InValidArgsTestParse02",
+ DetectCsumUDPV6InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumUDPV6ValidArgsTestParse03",
+ DetectCsumUDPV6ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumTCPV6ValidArgsTestParse01",
+ DetectCsumTCPV6ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumTCPV6InValidArgsTestParse02",
+ DetectCsumTCPV6InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumTCPV6ValidArgsTestParse03",
+ DetectCsumTCPV6ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumICMPV6ValidArgsTestParse01",
+ DetectCsumICMPV6ValidArgsTestParse01, 1);
+ UtRegisterTest("DetectCsumICMPV6InValidArgsTestParse02",
+ DetectCsumICMPV6InValidArgsTestParse02, 1);
+ UtRegisterTest("DetectCsumICMPV6ValidArgsTestParse03",
+ DetectCsumICMPV6ValidArgsTestParse03, 1);
+
+ UtRegisterTest("DetectCsumICMPV6Test01",
+ DetectCsumICMPV6Test01, 1);
+
+
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/detect-csum.h b/framework/src/suricata/src/detect-csum.h
new file mode 100644
index 00000000..8a871745
--- /dev/null
+++ b/framework/src/suricata/src/detect-csum.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_CSUM_H__
+#define __DETECT_CSUM_H__
+
+#define DETECT_CSUM_VALID "valid"
+#define DETECT_CSUM_INVALID "invalid"
+
+typedef struct DetectCsumData_ {
+ /* Indicates if the csum-<protocol> keyword in a rule holds the
+ keyvalue "valid" or "invalid" */
+ int16_t valid;
+} DetectCsumData;
+
+void DetectCsumRegister(void);
+
+#endif /* __DETECT_CSUM_H__ */
+
diff --git a/framework/src/suricata/src/detect-dce-iface.c b/framework/src/suricata/src/detect-dce-iface.c
new file mode 100644
index 00000000..60da43ed
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-iface.c
@@ -0,0 +1,1837 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements dce_iface keyword.
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-dce-iface.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "app-layer.h"
+#include "app-layer-dcerpc.h"
+#include "queue.h"
+#include "stream-tcp-reassemble.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "stream-tcp.h"
+
+#define DETECT_DCE_IFACE_PCRE_PARSE_ARGS "^\\s*([0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12})(?:\\s*,(<|>|=|!)([0-9]{1,5}))?(?:\\s*,(any_frag))?\\s*$"
+
+static pcre *parse_regex = NULL;
+static pcre_extra *parse_regex_study = NULL;
+
+int DetectDceIfaceMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t,
+ void *, Signature *, SigMatch *);
+static int DetectDceIfaceSetup(DetectEngineCtx *, Signature *, char *);
+void DetectDceIfaceFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "dce_iface" keyword.
+ */
+void DetectDceIfaceRegister(void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_DCE_IFACE].name = "dce_iface";
+ sigmatch_table[DETECT_DCE_IFACE].alproto = ALPROTO_DCERPC;
+ sigmatch_table[DETECT_DCE_IFACE].Match = NULL;
+ sigmatch_table[DETECT_DCE_IFACE].AppLayerMatch = DetectDceIfaceMatch;
+ sigmatch_table[DETECT_DCE_IFACE].Setup = DetectDceIfaceSetup;
+ sigmatch_table[DETECT_DCE_IFACE].Free = DetectDceIfaceFree;
+ sigmatch_table[DETECT_DCE_IFACE].RegisterTests = DetectDceIfaceRegisterTests;
+
+ sigmatch_table[DETECT_DCE_IFACE].flags |= SIGMATCH_PAYLOAD;
+
+ parse_regex = pcre_compile(DETECT_DCE_IFACE_PCRE_PARSE_ARGS, opts, &eb,
+ &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogDebug("pcre compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_DCE_IFACE_PCRE_PARSE_ARGS, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogDebug("pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+ error:
+ /* we need to handle error?! */
+ return;
+}
+
+/**
+ * \internal
+ * \brief Parses the argument sent along with the "dce_iface" keyword.
+ *
+ * \param arg Pointer to the string containing the argument to be parsed.
+ *
+ * \retval did Pointer to a DetectDceIfaceData instance that holds the data
+ * from the parsed arg.
+ */
+static inline DetectDceIfaceData *DetectDceIfaceArgParse(const char *arg)
+{
+ DetectDceIfaceData *did = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ uint8_t hex_value;
+ char copy_str[128] = "";
+ int i = 0, j = 0;
+ int len = 0;
+ char temp_str[3] = "";
+ int version;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, arg, strlen(arg), 0, 0, ov,
+ MAX_SUBSTRINGS);
+ if (ret < 2) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, arg);
+ goto error;
+ }
+
+ if ( (did = SCMalloc(sizeof(DetectDceIfaceData))) == NULL)
+ goto error;
+ memset(did, 0, sizeof(DetectDceIfaceData));
+
+ /* retrieve the iface uuid string. iface uuid is a compulsion in the keyword */
+ res = pcre_copy_substring(arg, ov, MAX_SUBSTRINGS, 1, copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ /* parse the iface uuid string */
+ len = strlen(copy_str);
+ j = 0;
+ temp_str[2] = '\0';
+ for (i = 0; i < len; ) {
+ if (copy_str[i] == '-') {
+ i++;
+ continue;
+ }
+
+ temp_str[0] = copy_str[i];
+ temp_str[1] = copy_str[i + 1];
+
+ hex_value = strtol(temp_str, NULL, 16);
+ did->uuid[j] = hex_value;
+ i += 2;
+ j++;
+ }
+
+ /* if the regex has 3 or 5, any_frag option is present in the signature */
+ if (ret == 3 || ret == 5)
+ did->any_frag = 1;
+
+ /* if the regex has 4 or 5, version/operator is present in the signature */
+ if (ret == 4 || ret == 5) {
+ /* first handle the version number, so that we can do some additional
+ * validations of the version number, wrt. the operator */
+ res = pcre_copy_substring(arg, ov, MAX_SUBSTRINGS, 3, copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ version = atoi(copy_str);
+ if (version > UINT16_MAX) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "DCE_IFACE interface version "
+ "invalid: %d\n", version);
+ goto error;
+ }
+ did->version = version;
+
+ /* now let us handle the operator supplied with the version number */
+ res = pcre_copy_substring(arg, ov, MAX_SUBSTRINGS, 2, copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ switch (copy_str[0]) {
+ case '<':
+ if (version == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "DCE_IFACE interface "
+ "version invalid: %d. Version can't be less"
+ "than 0, with \"<\" operator", version);
+ goto error;
+ }
+
+ did->op = DETECT_DCE_IFACE_OP_LT;
+ break;
+ case '>':
+ if (version == UINT16_MAX) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "DCE_IFACE interface "
+ "version invalid: %d. Version can't be greater"
+ "than %d, with \">\" operator", version,
+ UINT16_MAX);
+ goto error;
+ }
+
+ did->op = DETECT_DCE_IFACE_OP_GT;
+ break;
+ case '=':
+ did->op = DETECT_DCE_IFACE_OP_EQ;
+ break;
+ case '!':
+ did->op = DETECT_DCE_IFACE_OP_NE;
+ break;
+ }
+ }
+
+ return did;
+
+ error:
+ if (did != NULL)
+ SCFree(did);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Internal function that compares the dce interface version for this
+ * flow, to the signature's interface version specified using the
+ * dce_iface keyword.
+ *
+ * \param version The dce interface version for this flow.
+ * \param dce_data Pointer to the Signature's dce_iface keyword
+ * state(DetectDceIfaceData *).
+ */
+static inline int DetectDceIfaceMatchIfaceVersion(uint16_t version,
+ DetectDceIfaceData *dce_data)
+{
+ switch (dce_data->op) {
+ case DETECT_DCE_IFACE_OP_LT:
+ return (version < dce_data->version);
+ case DETECT_DCE_IFACE_OP_GT:
+ return (version > dce_data->version);
+ case DETECT_DCE_IFACE_OP_EQ:
+ return (version == dce_data->version);
+ case DETECT_DCE_IFACE_OP_NE:
+ return (version != dce_data->version);
+ default:
+ return 1;
+ }
+}
+
+/**
+ * \brief App layer match function for the "dce_iface" keyword.
+ *
+ * \param t Pointer to the ThreadVars instance.
+ * \param det_ctx Pointer to the DetectEngineThreadCtx.
+ * \param f Pointer to the flow.
+ * \param flags Pointer to the flags indicating the flow direction.
+ * \param state Pointer to the app layer state data.
+ * \param s Pointer to the Signature instance.
+ * \param m Pointer to the SigMatch.
+ *
+ * \retval 1 On Match.
+ * \retval 0 On no match.
+ */
+int DetectDceIfaceMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ int ret = 0;
+ DCERPCUuidEntry *item = NULL;
+ int i = 0;
+ DetectDceIfaceData *dce_data = (DetectDceIfaceData *)m->ctx;
+ DCERPCState *dcerpc_state = (DCERPCState *)state;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("No DCERPCState for the flow");
+ SCReturnInt(0);
+ }
+
+ /* we still haven't seen a request */
+ if (!dcerpc_state->dcerpc.dcerpcrequest.first_request_seen)
+ goto end;
+
+ if (!(dcerpc_state->dcerpc.dcerpchdr.type == REQUEST))
+ goto end;
+
+ TAILQ_FOREACH(item, &dcerpc_state->dcerpc.dcerpcbindbindack.accepted_uuid_list, next) {
+ SCLogDebug("item %p", item);
+ ret = 1;
+
+ /* if any_frag is not enabled, we need to match only against the first
+ * fragment */
+ if (!dce_data->any_frag && !(item->flags & DCERPC_UUID_ENTRY_FLAG_FF))
+ continue;
+
+ /* if the uuid has been rejected(item->result == 1), we skip to the
+ * next uuid */
+ if (item->result != 0)
+ continue;
+
+ /* check the interface uuid */
+ for (i = 0; i < 16; i++) {
+ if (dce_data->uuid[i] != item->uuid[i]) {
+ ret = 0;
+ break;
+ }
+ }
+ ret &= (item->ctxid == dcerpc_state->dcerpc.dcerpcrequest.ctxid);
+ if (ret == 0)
+ continue;
+
+ /* check the interface version */
+ if (dce_data->op != DETECT_DCE_IFACE_OP_NONE &&
+ !DetectDceIfaceMatchIfaceVersion(item->version, dce_data)) {
+ ret &= 0;
+ }
+
+ /* we have a match. Time to leave with a match */
+ if (ret == 1)
+ goto end;
+ }
+
+end:
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Creates a SigMatch for the "dce_iface" keyword being sent as argument,
+ * and appends it to the Signature(s).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 on success, -1 on failure.
+ */
+
+static int DetectDceIfaceSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ DetectDceIfaceData *did = NULL;
+ SigMatch *sm = NULL;
+
+ did = DetectDceIfaceArgParse(arg);
+ if (did == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Error parsing dec_iface option in "
+ "signature");
+ goto error;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_DCE_IFACE;
+ sm->ctx = (void *)did;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+ /* Flagged the signature as to inspect the app layer data */
+ s->flags |= SIG_FLAG_APPLAYER;
+ return 0;
+
+ error:
+ DetectDceIfaceFree(did);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectDceIfaceFree(void *ptr)
+{
+ SCFree(ptr);
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+static int DetectDceIfaceTestParse01(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 0);
+ result &= (did->op == 0);
+ result &= (did->any_frag == 0);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse02(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>1") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 1);
+ result &= (did->op == DETECT_DCE_IFACE_OP_GT);
+ result &= (did->any_frag == 0);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse03(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,<10") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 10);
+ result &= (did->op == DETECT_DCE_IFACE_OP_LT);
+ result &= (did->any_frag == 0);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse04(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,!10") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 10);
+ result &= (did->op == DETECT_DCE_IFACE_OP_NE);
+ result &= (did->any_frag == 0);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse05(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,=10") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 10);
+ result &= (did->op == DETECT_DCE_IFACE_OP_EQ);
+ result &= (did->any_frag == 0);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse06(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,any_frag") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 0);
+ result &= (did->op == 0);
+ result &= (did->any_frag == 1);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse07(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>1,any_frag") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 1);
+ result &= (did->op == DETECT_DCE_IFACE_OP_GT);
+ result &= (did->any_frag == 1);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse08(void)
+{
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,<1,any_frag") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 1);
+ result &= (did->op == DETECT_DCE_IFACE_OP_LT);
+ result &= (did->any_frag == 1);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse09(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,=1,any_frag") == 0);
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 1);
+ result &= (did->op == DETECT_DCE_IFACE_OP_EQ);
+ result &= (did->any_frag == 1);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse10(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 0;
+ DetectDceIfaceData *did = NULL;
+ uint8_t test_uuid[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34,
+ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
+ SigMatch *temp = NULL;
+ int i = 0;
+
+ result = (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,!1,any_frag") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ did = (DetectDceIfaceData *)temp->ctx;
+ if (did == NULL) {
+ SCReturnInt(0);
+ }
+
+ result &= 1;
+ for (i = 0; i < 16; i++) {
+ if (did->uuid[i] != test_uuid[i]) {
+ result = 0;
+ break;
+ }
+ }
+
+ result &= (did->version == 1);
+ result &= (did->op == DETECT_DCE_IFACE_OP_NE);
+ result &= (did->any_frag == 1);
+
+ SigFree(s);
+ SCReturnInt(result);
+}
+
+static int DetectDceIfaceTestParse11(void)
+{
+ SCEnter();
+
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ int result = 1;
+
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>1,ay_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-12345679ABC,>1,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-134-123456789ABC,>1,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-123-124-1234-123456789ABC,>1,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "1234568-1234-1234-1234-123456789ABC,>1,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>65536,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>=1,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,<0,any_frag") == -1);
+ result &= (DetectDceIfaceSetup(NULL, s, "12345678-1234-1234-1234-123456789ABC,>65535,any_frag") == -1);
+
+ SigFree(s);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_iface entry for a bind and bind_ack
+ */
+static int DetectDceIfaceTestParse12(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x26, 0x3d, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5,=0,any_frag; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCLogDebug("handling to_server chunk");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match (1): ");
+ goto end;
+ }
+
+ SCLogDebug("handling to_client chunk");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched, but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_request,
+ dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched, but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+
+/**
+ * \test Test a valid dce_iface entry with a bind, bind_ack and 3 request/responses.
+ */
+static int DetectDceIfaceTestParse13(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0xd0, 0x8c, 0x33, 0x44, 0x22, 0xf1, 0x31,
+ 0xaa, 0xaa, 0x90, 0x00, 0x38, 0x00, 0x10, 0x03,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x65, 0x8e, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x77, 0x69, 0x6e, 0x72, 0x65, 0x67, 0x00, 0x6d,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ uint32_t dcerpc_request3_len = sizeof(dcerpc_request3);
+ uint32_t dcerpc_response3_len = sizeof(dcerpc_response3);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_iface:338cd001-2244-31f1-aaaa-900038001003,=1,any_frag; sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCLogDebug("chunk 1, bind");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't match after bind request: ");
+ goto end;
+ }
+
+ SCLogDebug("chunk 2, bind_ack");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched again after bind ack: ");
+ goto end;
+ }
+
+ SCLogDebug("chunk 3, request 1");
+
+ /* request1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request1,
+ dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't match after request1: ");
+ goto end;
+ }
+
+ SCLogDebug("sending response1");
+
+ /* response1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response1,
+ dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched after response1, but shouldn't: ");
+ goto end;
+ }
+
+ SCLogDebug("sending request2");
+
+ /* request2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request2,
+ dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't match after request2: ");
+ goto end;
+ }
+
+ /* response2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response2,
+ dcerpc_response2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched after response2, but shouldn't have: ");
+ goto end;
+ }
+
+ /* request3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request3,
+ dcerpc_request3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't match after request3: ");
+ goto end;
+ }
+
+ /* response3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF,
+ dcerpc_response3, dcerpc_response3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched after response3, but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif
+
+/**
+ * \test Test a valid dce_iface entry for a bind and bind_ack
+ */
+static int DetectDceIfaceTestParse14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x26, 0x3d, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5,=0; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_request,
+ dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_iface entry for a bind and bind_ack
+ */
+static int DetectDceIfaceTestParse15(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x40, 0xfd, 0x2c, 0x34, 0x6c, 0x3c, 0xce, 0x11,
+ 0xa8, 0x93, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x7d, 0xd8, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x70, 0x69, 0x70, 0x65, 0x5c,
+ 0x6c, 0x6c, 0x73, 0x72, 0x70, 0x63, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+
+ uint8_t dcerpc_alter_context[] = {
+ 0x05, 0x00, 0x0e, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0xd0, 0x4c, 0x67, 0x57, 0x00, 0x52, 0xce, 0x11,
+ 0xa8, 0x97, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t dcerpc_alter_context_len = sizeof(dcerpc_alter_context);
+
+ uint8_t dcerpc_alter_context_resp[] = {
+ 0x05, 0x00, 0x0f, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x7d, 0xd8, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t dcerpc_alter_context_resp_len = sizeof(dcerpc_alter_context_resp);
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x20, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xdd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf5, 0x66, 0xbf, 0x54,
+ 0xc4, 0xdb, 0xdb, 0x4f, 0xa0, 0x01, 0x6d, 0xf4,
+ 0xf6, 0xa8, 0x44, 0xb3, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x9f, 0x13, 0xd9,
+ };
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_iface:57674cd0-5200-11ce-a897-08002b2e9c6d; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_iface:342cfd40-3c6c-11ce-a893-08002b2e9c6d; "
+ "sid:2;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+ if (PacketAlertCheck(p, 2))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_alter_context,
+ dcerpc_alter_context_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_alter_context_resp,
+ dcerpc_alter_context_resp_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request1,
+ dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response1,
+ dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request2,
+ dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+ if (!PacketAlertCheck(p, 2)) {
+ printf("sig 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif
+
+void DetectDceIfaceRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDceIfaceTestParse01", DetectDceIfaceTestParse01, 1);
+ UtRegisterTest("DetectDceIfaceTestParse02", DetectDceIfaceTestParse02, 1);
+ UtRegisterTest("DetectDceIfaceTestParse03", DetectDceIfaceTestParse03, 1);
+ UtRegisterTest("DetectDceIfaceTestParse04", DetectDceIfaceTestParse04, 1);
+ UtRegisterTest("DetectDceIfaceTestParse05", DetectDceIfaceTestParse05, 1);
+ UtRegisterTest("DetectDceIfaceTestParse06", DetectDceIfaceTestParse06, 1);
+ UtRegisterTest("DetectDceIfaceTestParse07", DetectDceIfaceTestParse07, 1);
+ UtRegisterTest("DetectDceIfaceTestParse08", DetectDceIfaceTestParse08, 1);
+ UtRegisterTest("DetectDceIfaceTestParse09", DetectDceIfaceTestParse09, 1);
+ UtRegisterTest("DetectDceIfaceTestParse10", DetectDceIfaceTestParse10, 1);
+ UtRegisterTest("DetectDceIfaceTestParse11", DetectDceIfaceTestParse11, 1);
+ UtRegisterTest("DetectDceIfaceTestParse12", DetectDceIfaceTestParse12, 1);
+ /* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+ UtRegisterTest("DetectDceIfaceTestParse13", DetectDceIfaceTestParse13, 1);
+#endif
+ UtRegisterTest("DetectDceIfaceTestParse14", DetectDceIfaceTestParse14, 1);
+ UtRegisterTest("DetectDceIfaceTestParse15", DetectDceIfaceTestParse15, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-dce-iface.h b/framework/src/suricata/src/detect-dce-iface.h
new file mode 100644
index 00000000..6a051b0d
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-iface.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_DCE_IFACE_H__
+#define __DETECT_DCE_IFACE_H__
+
+typedef enum DetectDceIfaceOperators_ {
+ DETECT_DCE_IFACE_OP_NONE = 0,
+ DETECT_DCE_IFACE_OP_LT,
+ DETECT_DCE_IFACE_OP_GT,
+ DETECT_DCE_IFACE_OP_EQ,
+ DETECT_DCE_IFACE_OP_NE,
+} DetectDceIfaceOperators;
+
+typedef struct DetectDceIfaceData_ {
+ uint8_t uuid[16];
+ uint8_t op;
+ uint16_t version;
+ uint8_t any_frag;
+} DetectDceIfaceData;
+
+void DetectDceIfaceRegister(void);
+void DetectDceIfaceRegisterTests(void);
+
+#endif /* __DETECT_DCE_IFACE_H__ */
diff --git a/framework/src/suricata/src/detect-dce-opnum.c b/framework/src/suricata/src/detect-dce-opnum.c
new file mode 100644
index 00000000..08856663
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-opnum.c
@@ -0,0 +1,2950 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements dce_opnum keyword
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "app-layer.h"
+#include "app-layer-dcerpc.h"
+#include "queue.h"
+#include "stream-tcp-reassemble.h"
+#include "detect-dce-opnum.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "stream-tcp.h"
+
+#define DETECT_DCE_OPNUM_PCRE_PARSE_ARGS "^\\s*([0-9]{1,5}(\\s*-\\s*[0-9]{1,5}\\s*)?)(,\\s*[0-9]{1,5}(\\s*-\\s*[0-9]{1,5})?\\s*)*$"
+
+static pcre *parse_regex = NULL;
+static pcre_extra *parse_regex_study = NULL;
+
+int DetectDceOpnumMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t,
+ void *, Signature *, SigMatch *);
+static int DetectDceOpnumSetup(DetectEngineCtx *, Signature *, char *);
+void DetectDceOpnumFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "dce_opnum" keyword.
+ */
+void DetectDceOpnumRegister(void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_DCE_OPNUM].name = "dce_opnum";
+ sigmatch_table[DETECT_DCE_OPNUM].alproto = ALPROTO_DCERPC;
+ sigmatch_table[DETECT_DCE_OPNUM].Match = NULL;
+ sigmatch_table[DETECT_DCE_OPNUM].AppLayerMatch = DetectDceOpnumMatch;
+ sigmatch_table[DETECT_DCE_OPNUM].Setup = DetectDceOpnumSetup;
+ sigmatch_table[DETECT_DCE_OPNUM].Free = DetectDceOpnumFree;
+ sigmatch_table[DETECT_DCE_OPNUM].RegisterTests = DetectDceOpnumRegisterTests;
+
+ sigmatch_table[DETECT_DCE_OPNUM].flags |= SIGMATCH_PAYLOAD;
+
+ parse_regex = pcre_compile(DETECT_DCE_OPNUM_PCRE_PARSE_ARGS, opts, &eb,
+ &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_DCE_OPNUM_PCRE_PARSE_ARGS, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+ error:
+ /* we need to handle error?! */
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates and returns a new instance of DetectDceOpnumRange.
+ *
+ * \retval dor Pointer to the new instance DetectDceOpnumRange.
+ */
+static inline DetectDceOpnumRange *DetectDceOpnumAllocDetectDceOpnumRange(void)
+{
+ DetectDceOpnumRange *dor = NULL;
+
+ if ( (dor = SCMalloc(sizeof(DetectDceOpnumRange))) == NULL)
+ return NULL;
+ memset(dor, 0, sizeof(DetectDceOpnumRange));
+ dor->range1 = dor->range2 = DCE_OPNUM_RANGE_UNINITIALIZED;
+
+ return dor;
+}
+
+/**
+ * \internal
+ * \brief Parses the argument sent along with the "dce_opnum" keyword.
+ *
+ * \param arg Pointer to the string containing the argument to be parsed.
+ *
+ * \retval did Pointer to a DetectDceIfaceData instance that holds the data
+ * from the parsed arg.
+ */
+static inline DetectDceOpnumData *DetectDceOpnumArgParse(const char *arg)
+{
+ DetectDceOpnumData *dod = NULL;
+
+ DetectDceOpnumRange *dor = NULL;
+ DetectDceOpnumRange *prev_dor = NULL;
+
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *pcre_sub_str = NULL;
+
+ char *dup_str = NULL;
+ char *dup_str_temp = NULL;
+ char *dup_str_head = NULL;
+ char *comma_token = NULL;
+ char *hyphen_token = NULL;
+
+ if (arg == NULL) {
+ goto error;
+ }
+
+ ret = pcre_exec(parse_regex, parse_regex_study, arg, strlen(arg), 0, 0, ov,
+ MAX_SUBSTRINGS);
+ if (ret < 2) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, arg);
+ goto error;
+ }
+
+ res = pcre_get_substring(arg, ov, MAX_SUBSTRINGS, 0, &pcre_sub_str);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if ( (dod = SCMalloc(sizeof(DetectDceOpnumData))) == NULL)
+ goto error;
+ memset(dod, 0, sizeof(DetectDceOpnumData));
+
+ if ( (dup_str = SCStrdup(pcre_sub_str)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ goto error;
+ }
+
+ /* free the substring */
+ pcre_free_substring(pcre_sub_str);
+
+ /* keep a copy of the strdup string in dup_str_head so that we can free it
+ * once we are done using it */
+ dup_str_head = dup_str;
+ dup_str_temp = dup_str;
+ while ( (comma_token = index(dup_str, ',')) != NULL) {
+ comma_token[0] = '\0';
+ dup_str = comma_token + 1;
+
+ dor = DetectDceOpnumAllocDetectDceOpnumRange();
+ if (dor == NULL)
+ goto error;
+ if (prev_dor == NULL) {
+ prev_dor = dor;
+ dod->range = dor;
+ } else {
+ prev_dor->next = dor;
+ prev_dor = dor;
+ }
+
+ if ((hyphen_token = index(dup_str_temp, '-')) != NULL) {
+ hyphen_token[0] = '\0';
+ hyphen_token++;
+ dor->range1 = atoi(dup_str_temp);
+ if (dor->range1 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+ dor->range2 = atoi(hyphen_token);
+ if (dor->range2 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+ if (dor->range1 > dor->range2)
+ goto error;
+ }
+ dor->range1 = atoi(dup_str_temp);
+ if (dor->range1 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+
+ dup_str_temp = dup_str;
+ }
+
+ dor = DetectDceOpnumAllocDetectDceOpnumRange();
+ if (dor == NULL)
+ goto error;
+ if (prev_dor == NULL) {
+ dod->range = dor;
+ } else {
+ prev_dor->next = dor;
+ }
+
+ if ( (hyphen_token = index(dup_str, '-')) != NULL) {
+ hyphen_token[0] = '\0';
+ hyphen_token++;
+ dor->range1 = atoi(dup_str);
+ if (dor->range1 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+ dor->range2 = atoi(hyphen_token);
+ if (dor->range2 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+ if (dor->range1 > dor->range2)
+ goto error;
+ }
+ dor->range1 = atoi(dup_str);
+ if (dor->range1 > DCE_OPNUM_RANGE_MAX)
+ goto error;
+
+ if (dup_str_head != NULL)
+ SCFree(dup_str_head);
+
+ return dod;
+
+ error:
+ if (dup_str_head != NULL)
+ SCFree(dup_str_head);
+ DetectDceOpnumFree(dod);
+ return NULL;
+}
+
+/**
+ * \brief App layer match function for the "dce_opnum" keyword.
+ *
+ * \param t Pointer to the ThreadVars instance.
+ * \param det_ctx Pointer to the DetectEngineThreadCtx.
+ * \param f Pointer to the flow.
+ * \param flags Pointer to the flags indicating the flow direction.
+ * \param state Pointer to the app layer state data.
+ * \param s Pointer to the Signature instance.
+ * \param m Pointer to the SigMatch.
+ *
+ * \retval 1 On Match.
+ * \retval 0 On no match.
+ */
+int DetectDceOpnumMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectDceOpnumData *dce_data = (DetectDceOpnumData *)m->ctx;
+ DetectDceOpnumRange *dor = dce_data->range;
+
+ DCERPCState *dcerpc_state = (DCERPCState *)state;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("No DCERPCState for the flow");
+ SCReturnInt(0);
+ }
+
+ for ( ; dor != NULL; dor = dor->next) {
+ if (dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED) {
+ if (dor->range1 == dcerpc_state->dcerpc.dcerpcrequest.opnum) {
+ SCReturnInt(1);
+ }
+ } else {
+ if (dor->range1 <= dcerpc_state->dcerpc.dcerpcrequest.opnum &&
+ dor->range2 >= dcerpc_state->dcerpc.dcerpcrequest.opnum)
+ {
+ SCReturnInt(1);
+ }
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Creates a SigMatch for the "dce_opnum" keyword being sent as argument,
+ * and appends it to the Signature(s).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 on success, -1 on failure
+ */
+
+static int DetectDceOpnumSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ DetectDceOpnumData *dod = NULL;
+ SigMatch *sm = NULL;
+
+ if (arg == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Error parsing dce_opnum option in "
+ "signature, option needs a value");
+ goto error;
+ }
+
+ dod = DetectDceOpnumArgParse(arg);
+ if (dod == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Error parsing dce_opnum option in "
+ "signature");
+ goto error;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_DCE_OPNUM;
+ sm->ctx = (void *)dod;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+ /* Flagged the signature as to inspect the app layer data */
+ s->flags |= SIG_FLAG_APPLAYER;
+ return 0;
+
+ error:
+ DetectDceOpnumFree(dod);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectDceOpnumFree(void *ptr)
+{
+ DetectDceOpnumData *dod = ptr;
+ DetectDceOpnumRange *dor = NULL;
+ DetectDceOpnumRange *dor_temp = NULL;
+
+ if (dod != NULL) {
+ dor = dod->range;
+ while (dor != NULL) {
+ dor_temp = dor;
+ dor = dor->next;
+ SCFree(dor_temp);
+ }
+ SCFree(dod);
+ }
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+static int DetectDceOpnumTestParse01(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "12") == 0);
+ result &= (DetectDceOpnumSetup(NULL, s, "12,24") == 0);
+ result &= (DetectDceOpnumSetup(NULL, s, "12,12-24") == 0);
+ result &= (DetectDceOpnumSetup(NULL, s, "12-14,12,121,62-78") == 0);
+ result &= (DetectDceOpnumSetup(NULL, s, "12,26,62,61,6513-6666") == 0);
+ result &= (DetectDceOpnumSetup(NULL, s, "12,26,62,61,6513--") == -1);
+ result &= (DetectDceOpnumSetup(NULL, s, "12-14,12,121,62-8") == -1);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ SigFree(s);
+ result &= 1;
+ }
+
+ return result;
+}
+
+static int DetectDceOpnumTestParse02(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "12") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 12 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next == NULL);
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+static int DetectDceOpnumTestParse03(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "12-24") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 12 && dor->range2 == 24);
+ result &= (dor->next == NULL);
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+static int DetectDceOpnumTestParse04(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "12-24,24,62-72,623-635,62,25,213-235") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 12 && dor->range2 == 24);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 24 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 62 && dor->range2 == 72);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 623 && dor->range2 == 635);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 62 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 25 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 213 && dor->range2 == 235);
+ if (result == 0)
+ goto end;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+static int DetectDceOpnumTestParse05(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "1,2,3,4,5,6,7") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 1 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 2 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 3 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 4 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 5 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 6 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 7 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ if (result == 0)
+ goto end;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+static int DetectDceOpnumTestParse06(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "1-2,3-4,5-6,7-8") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 1 && dor->range2 == 2);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 3 && dor->range2 == 4);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 5 && dor->range2 == 6);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 7 && dor->range2 == 8);
+ if (result == 0)
+ goto end;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+static int DetectDceOpnumTestParse07(void)
+{
+ Signature *s = SigAlloc();
+ int result = 0;
+ DetectDceOpnumData *dod = NULL;
+ DetectDceOpnumRange *dor = NULL;
+ SigMatch *temp = NULL;
+
+ memset(s, 0, sizeof(Signature));
+
+ result = (DetectDceOpnumSetup(NULL, s, "1-2,3-4,5-6,7-8,9") == 0);
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ temp = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ dod = (DetectDceOpnumData *)temp->ctx;
+ if (dod == NULL)
+ goto end;
+ dor = dod->range;
+ result &= (dor->range1 == 1 && dor->range2 == 2);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 3 && dor->range2 == 4);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 5 && dor->range2 == 6);
+ result &= (dor->next != NULL);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 7 && dor->range2 == 8);
+ if (result == 0)
+ goto end;
+
+ dor = dor->next;
+ result &= (dor->range1 == 9 && dor->range2 == DCE_OPNUM_RANGE_UNINITIALIZED);
+ if (result == 0)
+ goto end;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigFree(s);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_opnum entry with a bind, bind_ack and a request.
+ */
+static int DetectDceOpnumTestParse08(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x26, 0x3d, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+
+ /* todo chop the request frag length and change the
+ * length related parameters in the frag */
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xec, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd4, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x83, 0xc7, 0x0b, 0x47,
+ 0x47, 0x47, 0x47, 0x81, 0x37, 0x22, 0xa5, 0x9b,
+ 0x4a, 0x75, 0xf4, 0xa3, 0x61, 0xd3, 0xbe, 0xdd,
+ 0x5a, 0xfb, 0x20, 0x1e, 0xfc, 0x10, 0x8e, 0x0f,
+ 0xa5, 0x9f, 0x4a, 0x22, 0x20, 0x9b, 0xa8, 0xd5,
+ 0xc4, 0xff, 0xc1, 0x3f, 0xbd, 0x9b, 0x4a, 0x22,
+ 0x2e, 0xc0, 0x7a, 0xa9, 0xfe, 0x97, 0xc9, 0xe1,
+ 0xa9, 0xf3, 0x2f, 0x22, 0xc9, 0x9b, 0x22, 0x50,
+ 0xa5, 0xf5, 0x4a, 0x4a, 0xce, 0x9b, 0x2f, 0x22,
+ 0x2e, 0x6f, 0xc1, 0xe1, 0xf3, 0xa8, 0x83, 0xa2,
+ 0x64, 0x98, 0xc1, 0x62, 0xa1, 0xa0, 0x89, 0x56,
+ 0xa8, 0x1b, 0x8b, 0x2b, 0x2e, 0xe3, 0x7a, 0xd1,
+ 0x03, 0xef, 0x58, 0x7c, 0x4e, 0x7d, 0x14, 0x76,
+ 0xfa, 0xc3, 0x7f, 0x02, 0xa5, 0xbb, 0x4a, 0x89,
+ 0x47, 0x6c, 0x12, 0xc9, 0x70, 0x18, 0x8e, 0x3a,
+ 0x2e, 0xcb, 0x52, 0xa9, 0x67, 0x98, 0x0a, 0x1e,
+ 0x2e, 0xc3, 0x32, 0x21, 0x7f, 0x10, 0x31, 0x3e,
+ 0xa6, 0x61, 0xc1, 0x61, 0x85, 0x98, 0x88, 0xa9,
+ 0xee, 0x83, 0x22, 0x51, 0xd6, 0xda, 0x4a, 0x4a,
+ 0xc1, 0xff, 0x38, 0x47, 0xcd, 0xe9, 0x25, 0x41,
+ 0xe4, 0xf3, 0x0d, 0x47, 0xd1, 0xcb, 0xc1, 0xd6,
+ 0x1e, 0x95, 0x4a, 0x22, 0xa5, 0x73, 0x08, 0x22,
+ 0xa5, 0x9b, 0xc9, 0xe6, 0xb5, 0xcd, 0x22, 0x43,
+ 0xd7, 0xe2, 0x0b, 0x4a, 0xe9, 0xf2, 0x28, 0x50,
+ 0xcd, 0xd7, 0x25, 0x43, 0xc1, 0x10, 0xbe, 0x99,
+ 0xa9, 0x9b, 0x4a, 0x22, 0x4d, 0xb8, 0x4a, 0x22,
+ 0xa5, 0x18, 0x8e, 0x2e, 0xf3, 0xc9, 0x22, 0x4e,
+ 0xc9, 0x9b, 0x4a, 0x4a, 0x96, 0xa9, 0x64, 0x46,
+ 0xcd, 0xec, 0x39, 0x10, 0xfa, 0xcf, 0xb5, 0x76,
+ 0x81, 0x8f, 0xc9, 0xe6, 0xa9, 0x10, 0x82, 0x7c,
+ 0xff, 0xc4, 0xa1, 0x0a, 0xf5, 0xcc, 0x1b, 0x74,
+ 0xf4, 0x10, 0x81, 0xa9, 0x9d, 0x98, 0xb0, 0xa1,
+ 0x65, 0x9f, 0xb9, 0x84, 0xd1, 0x9f, 0x13, 0x7c,
+ 0x47, 0x76, 0x12, 0x7c, 0xfc, 0x10, 0xbb, 0x09,
+ 0x55, 0x5a, 0xac, 0x20, 0xfa, 0x10, 0x7e, 0x15,
+ 0xa6, 0x69, 0x12, 0xe1, 0xf7, 0xca, 0x22, 0x57,
+ 0xd5, 0x9b, 0x4a, 0x4a, 0xd1, 0xfa, 0x38, 0x56,
+ 0xcd, 0xcc, 0x19, 0x63, 0xf6, 0xf3, 0x2f, 0x56,
+ 0xa5, 0x9b, 0x22, 0x51, 0xca, 0xf8, 0x21, 0x48,
+ 0xa5, 0xf3, 0x28, 0x4b, 0xcb, 0xff, 0x22, 0x47,
+ 0xcb, 0x9b, 0x4a, 0x4a, 0xc9, 0xf2, 0x39, 0x56,
+ 0xcd, 0xeb, 0x3e, 0x22, 0xa5, 0xf3, 0x2b, 0x41,
+ 0xc6, 0xfe, 0xc1, 0xfe, 0xf6, 0xca, 0xc9, 0xe1,
+ 0xad, 0xc8, 0x1b, 0xa1, 0x66, 0x93, 0x19, 0x73,
+ 0x26, 0x58, 0x42, 0x71, 0xf4, 0x18, 0x89, 0x2a,
+ 0xf6, 0xca, 0xb5, 0xf5, 0x2c, 0xd8, 0x42, 0xdd,
+ 0x72, 0x12, 0x09, 0x26, 0x5a, 0x4c, 0xc3, 0x21,
+ 0x5a, 0x4c, 0xc3, 0x61, 0x59, 0x64, 0x9d, 0xab,
+ 0xe6, 0x63, 0xc9, 0xc9, 0xad, 0x10, 0xa9, 0xa3,
+ 0x49, 0x0b, 0x4b, 0x22, 0xa5, 0xcf, 0x22, 0x23,
+ 0xa4, 0x9b, 0x4a, 0xdd, 0x31, 0xbf, 0xe2, 0x23,
+ 0xa5, 0x9b, 0xcb, 0xe6, 0x35, 0x9a, 0x4a, 0x22,
+ 0xcf, 0x9d, 0x20, 0x23, 0xcf, 0x99, 0xb5, 0x76,
+ 0x81, 0x83, 0x20, 0x22, 0xcf, 0x9b, 0x20, 0x22,
+ 0xcd, 0x99, 0x4a, 0xe6, 0x96, 0x10, 0x96, 0x71,
+ 0xf6, 0xcb, 0x20, 0x23, 0xf5, 0xf1, 0x5a, 0x71,
+ 0xf5, 0x64, 0x1e, 0x06, 0x9d, 0x64, 0x1e, 0x06,
+ 0x8d, 0x5c, 0x49, 0x32, 0xa5, 0x9b, 0x4a, 0xdd,
+ 0xf1, 0xbf, 0x56, 0xa1, 0x61, 0xbf, 0x13, 0x78,
+ 0xf4, 0xc9, 0x1a, 0x11, 0x77, 0xc9, 0x22, 0x51,
+ 0xc0, 0xf5, 0x2e, 0xa9, 0x61, 0xc9, 0x22, 0x50,
+ 0xc0, 0xf8, 0x3c, 0xa9, 0x71, 0xc9, 0x1b, 0x72,
+ 0xf4, 0x64, 0x9d, 0xb1, 0x5a, 0x4c, 0xdf, 0xa1,
+ 0x61, 0x8b, 0x12, 0x78, 0xfc, 0xc8, 0x1f, 0x72,
+ 0x2e, 0x77, 0x1a, 0x42, 0xcf, 0x9f, 0x10, 0x72,
+ 0x2e, 0x47, 0xa2, 0x63, 0xa5, 0x9b, 0x4a, 0x48,
+ 0xa5, 0xf3, 0x26, 0x4e, 0xca, 0xf8, 0x22, 0x57,
+ 0xc4, 0xf7, 0x0b, 0x4a, 0xf3, 0xf2, 0x38, 0x56,
+ 0xf1, 0xcd, 0xb5, 0xf5, 0x26, 0x5f, 0x5a, 0x78,
+ 0xf7, 0xf1, 0x0a, 0x4a, 0xa5, 0x8b, 0x4a, 0x22,
+ 0xf7, 0xf1, 0x4a, 0xdd, 0x75, 0x12, 0x0e, 0x06,
+ 0x81, 0xc1, 0xd9, 0xca, 0xb5, 0x9b, 0x4a, 0x22,
+ 0xc4, 0xc0, 0xb5, 0xc1, 0xc5, 0xa8, 0x8a, 0x92,
+ 0xa1, 0x73, 0x5c, 0x22, 0xa5, 0x9b, 0x2b, 0xe1,
+ 0xc5, 0xc9, 0x19, 0x11, 0x65, 0x73, 0x40, 0x22,
+ 0xa5, 0x9b, 0x11, 0x78, 0xa6, 0x43, 0x61, 0xf2,
+ 0xd0, 0x74, 0x2b, 0xe1, 0x96, 0x52, 0x1b, 0x70,
+ 0xf6, 0x64, 0x3f, 0x22, 0x5a, 0xcf, 0x4f, 0x26,
+ 0x20, 0x5b, 0x34, 0x23, 0x66, 0x64, 0x1f, 0xd2,
+ 0xa5, 0x9b, 0x4a, 0x22, 0xa5, 0x9b, 0x4a, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x54, 0x58,
+ 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d, 0x6f, 0x41,
+ 0x3f, 0x3f, 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d,
+ 0x6f, 0x43, 0x42, 0x42, 0x50, 0x5f, 0x57, 0xc3,
+ 0x33, 0x5f, 0x37, 0x74, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0xeb, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x53, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61,
+ 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x53, 0x79, 0x73, 0x74,
+ 0x65, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x44, 0x6e, 0x73, 0x44, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x65, 0x66, 0x31, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x72, 0x65, 0x66, 0x32, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01, 0x02, 0x03, 0x04
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_opnum:9; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_bindack, dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_EOF,
+ dcerpc_request, dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_opnum entry with only a request frag.
+ */
+static int DetectDceOpnumTestParse09(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ /* todo chop the request frag length and change the
+ * length related parameters in the frag */
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xec, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd4, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x83, 0xc7, 0x0b, 0x47,
+ 0x47, 0x47, 0x47, 0x81, 0x37, 0x22, 0xa5, 0x9b,
+ 0x4a, 0x75, 0xf4, 0xa3, 0x61, 0xd3, 0xbe, 0xdd,
+ 0x5a, 0xfb, 0x20, 0x1e, 0xfc, 0x10, 0x8e, 0x0f,
+ 0xa5, 0x9f, 0x4a, 0x22, 0x20, 0x9b, 0xa8, 0xd5,
+ 0xc4, 0xff, 0xc1, 0x3f, 0xbd, 0x9b, 0x4a, 0x22,
+ 0x2e, 0xc0, 0x7a, 0xa9, 0xfe, 0x97, 0xc9, 0xe1,
+ 0xa9, 0xf3, 0x2f, 0x22, 0xc9, 0x9b, 0x22, 0x50,
+ 0xa5, 0xf5, 0x4a, 0x4a, 0xce, 0x9b, 0x2f, 0x22,
+ 0x2e, 0x6f, 0xc1, 0xe1, 0xf3, 0xa8, 0x83, 0xa2,
+ 0x64, 0x98, 0xc1, 0x62, 0xa1, 0xa0, 0x89, 0x56,
+ 0xa8, 0x1b, 0x8b, 0x2b, 0x2e, 0xe3, 0x7a, 0xd1,
+ 0x03, 0xef, 0x58, 0x7c, 0x4e, 0x7d, 0x14, 0x76,
+ 0xfa, 0xc3, 0x7f, 0x02, 0xa5, 0xbb, 0x4a, 0x89,
+ 0x47, 0x6c, 0x12, 0xc9, 0x70, 0x18, 0x8e, 0x3a,
+ 0x2e, 0xcb, 0x52, 0xa9, 0x67, 0x98, 0x0a, 0x1e,
+ 0x2e, 0xc3, 0x32, 0x21, 0x7f, 0x10, 0x31, 0x3e,
+ 0xa6, 0x61, 0xc1, 0x61, 0x85, 0x98, 0x88, 0xa9,
+ 0xee, 0x83, 0x22, 0x51, 0xd6, 0xda, 0x4a, 0x4a,
+ 0xc1, 0xff, 0x38, 0x47, 0xcd, 0xe9, 0x25, 0x41,
+ 0xe4, 0xf3, 0x0d, 0x47, 0xd1, 0xcb, 0xc1, 0xd6,
+ 0x1e, 0x95, 0x4a, 0x22, 0xa5, 0x73, 0x08, 0x22,
+ 0xa5, 0x9b, 0xc9, 0xe6, 0xb5, 0xcd, 0x22, 0x43,
+ 0xd7, 0xe2, 0x0b, 0x4a, 0xe9, 0xf2, 0x28, 0x50,
+ 0xcd, 0xd7, 0x25, 0x43, 0xc1, 0x10, 0xbe, 0x99,
+ 0xa9, 0x9b, 0x4a, 0x22, 0x4d, 0xb8, 0x4a, 0x22,
+ 0xa5, 0x18, 0x8e, 0x2e, 0xf3, 0xc9, 0x22, 0x4e,
+ 0xc9, 0x9b, 0x4a, 0x4a, 0x96, 0xa9, 0x64, 0x46,
+ 0xcd, 0xec, 0x39, 0x10, 0xfa, 0xcf, 0xb5, 0x76,
+ 0x81, 0x8f, 0xc9, 0xe6, 0xa9, 0x10, 0x82, 0x7c,
+ 0xff, 0xc4, 0xa1, 0x0a, 0xf5, 0xcc, 0x1b, 0x74,
+ 0xf4, 0x10, 0x81, 0xa9, 0x9d, 0x98, 0xb0, 0xa1,
+ 0x65, 0x9f, 0xb9, 0x84, 0xd1, 0x9f, 0x13, 0x7c,
+ 0x47, 0x76, 0x12, 0x7c, 0xfc, 0x10, 0xbb, 0x09,
+ 0x55, 0x5a, 0xac, 0x20, 0xfa, 0x10, 0x7e, 0x15,
+ 0xa6, 0x69, 0x12, 0xe1, 0xf7, 0xca, 0x22, 0x57,
+ 0xd5, 0x9b, 0x4a, 0x4a, 0xd1, 0xfa, 0x38, 0x56,
+ 0xcd, 0xcc, 0x19, 0x63, 0xf6, 0xf3, 0x2f, 0x56,
+ 0xa5, 0x9b, 0x22, 0x51, 0xca, 0xf8, 0x21, 0x48,
+ 0xa5, 0xf3, 0x28, 0x4b, 0xcb, 0xff, 0x22, 0x47,
+ 0xcb, 0x9b, 0x4a, 0x4a, 0xc9, 0xf2, 0x39, 0x56,
+ 0xcd, 0xeb, 0x3e, 0x22, 0xa5, 0xf3, 0x2b, 0x41,
+ 0xc6, 0xfe, 0xc1, 0xfe, 0xf6, 0xca, 0xc9, 0xe1,
+ 0xad, 0xc8, 0x1b, 0xa1, 0x66, 0x93, 0x19, 0x73,
+ 0x26, 0x58, 0x42, 0x71, 0xf4, 0x18, 0x89, 0x2a,
+ 0xf6, 0xca, 0xb5, 0xf5, 0x2c, 0xd8, 0x42, 0xdd,
+ 0x72, 0x12, 0x09, 0x26, 0x5a, 0x4c, 0xc3, 0x21,
+ 0x5a, 0x4c, 0xc3, 0x61, 0x59, 0x64, 0x9d, 0xab,
+ 0xe6, 0x63, 0xc9, 0xc9, 0xad, 0x10, 0xa9, 0xa3,
+ 0x49, 0x0b, 0x4b, 0x22, 0xa5, 0xcf, 0x22, 0x23,
+ 0xa4, 0x9b, 0x4a, 0xdd, 0x31, 0xbf, 0xe2, 0x23,
+ 0xa5, 0x9b, 0xcb, 0xe6, 0x35, 0x9a, 0x4a, 0x22,
+ 0xcf, 0x9d, 0x20, 0x23, 0xcf, 0x99, 0xb5, 0x76,
+ 0x81, 0x83, 0x20, 0x22, 0xcf, 0x9b, 0x20, 0x22,
+ 0xcd, 0x99, 0x4a, 0xe6, 0x96, 0x10, 0x96, 0x71,
+ 0xf6, 0xcb, 0x20, 0x23, 0xf5, 0xf1, 0x5a, 0x71,
+ 0xf5, 0x64, 0x1e, 0x06, 0x9d, 0x64, 0x1e, 0x06,
+ 0x8d, 0x5c, 0x49, 0x32, 0xa5, 0x9b, 0x4a, 0xdd,
+ 0xf1, 0xbf, 0x56, 0xa1, 0x61, 0xbf, 0x13, 0x78,
+ 0xf4, 0xc9, 0x1a, 0x11, 0x77, 0xc9, 0x22, 0x51,
+ 0xc0, 0xf5, 0x2e, 0xa9, 0x61, 0xc9, 0x22, 0x50,
+ 0xc0, 0xf8, 0x3c, 0xa9, 0x71, 0xc9, 0x1b, 0x72,
+ 0xf4, 0x64, 0x9d, 0xb1, 0x5a, 0x4c, 0xdf, 0xa1,
+ 0x61, 0x8b, 0x12, 0x78, 0xfc, 0xc8, 0x1f, 0x72,
+ 0x2e, 0x77, 0x1a, 0x42, 0xcf, 0x9f, 0x10, 0x72,
+ 0x2e, 0x47, 0xa2, 0x63, 0xa5, 0x9b, 0x4a, 0x48,
+ 0xa5, 0xf3, 0x26, 0x4e, 0xca, 0xf8, 0x22, 0x57,
+ 0xc4, 0xf7, 0x0b, 0x4a, 0xf3, 0xf2, 0x38, 0x56,
+ 0xf1, 0xcd, 0xb5, 0xf5, 0x26, 0x5f, 0x5a, 0x78,
+ 0xf7, 0xf1, 0x0a, 0x4a, 0xa5, 0x8b, 0x4a, 0x22,
+ 0xf7, 0xf1, 0x4a, 0xdd, 0x75, 0x12, 0x0e, 0x06,
+ 0x81, 0xc1, 0xd9, 0xca, 0xb5, 0x9b, 0x4a, 0x22,
+ 0xc4, 0xc0, 0xb5, 0xc1, 0xc5, 0xa8, 0x8a, 0x92,
+ 0xa1, 0x73, 0x5c, 0x22, 0xa5, 0x9b, 0x2b, 0xe1,
+ 0xc5, 0xc9, 0x19, 0x11, 0x65, 0x73, 0x40, 0x22,
+ 0xa5, 0x9b, 0x11, 0x78, 0xa6, 0x43, 0x61, 0xf2,
+ 0xd0, 0x74, 0x2b, 0xe1, 0x96, 0x52, 0x1b, 0x70,
+ 0xf6, 0x64, 0x3f, 0x22, 0x5a, 0xcf, 0x4f, 0x26,
+ 0x20, 0x5b, 0x34, 0x23, 0x66, 0x64, 0x1f, 0xd2,
+ 0xa5, 0x9b, 0x4a, 0x22, 0xa5, 0x9b, 0x4a, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x54, 0x58,
+ 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d, 0x6f, 0x41,
+ 0x3f, 0x3f, 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d,
+ 0x6f, 0x43, 0x42, 0x42, 0x50, 0x5f, 0x57, 0xc3,
+ 0x33, 0x5f, 0x37, 0x74, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0xeb, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x53, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61,
+ 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x53, 0x79, 0x73, 0x74,
+ 0x65, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x44, 0x6e, 0x73, 0x44, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x65, 0x66, 0x31, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x72, 0x65, 0x66, 0x32, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01, 0x02, 0x03, 0x04
+ };
+
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_opnum:9; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_request, dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+
+/**
+ * \test Test a valid dce_opnum(with multiple values) with a bind, bind_ack,
+ * and multiple request/responses with a match test after each frag parsing.
+ */
+static int DetectDceOpnumTestParse10(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0xd0, 0x8c, 0x33, 0x44, 0x22, 0xf1, 0x31,
+ 0xaa, 0xaa, 0x90, 0x00, 0x38, 0x00, 0x10, 0x03,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x65, 0x8e, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x77, 0x69, 0x6e, 0x72, 0x65, 0x67, 0x00, 0x6d,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ uint32_t dcerpc_request3_len = sizeof(dcerpc_request3);
+ uint32_t dcerpc_response3_len = sizeof(dcerpc_response3);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_opnum:2,15,22; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCLogDebug("sending bind");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc bind failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ SCLogDebug("sending bind_ack");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_bindack, dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ SCLogDebug("sending request1");
+
+ /* request1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request1, dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't match, but should have: ");
+ goto end;
+ }
+
+ SCLogDebug("sending response1");
+
+ /* response1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response1, dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 did match, shouldn't have on response1: ");
+ goto end;
+ }
+
+ /* request2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request2, dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't match, but should have on request2: ");
+ goto end;
+ }
+
+ /* response2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response2, dcerpc_response2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 did match, shouldn't have on response2: ");
+ goto end;
+ }
+
+ /* request3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request3, dcerpc_request3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't match, but should have on request3: ");
+ goto end;
+ }
+
+ /* response3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF,
+ dcerpc_response3, dcerpc_response3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 did match, shouldn't have on response2: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerDestroyCtxThread(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_opnum entry(with multiple values) with multiple
+ * request/responses.
+ */
+static int DetectDceOpnumTestParse11(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ uint32_t dcerpc_request3_len = sizeof(dcerpc_request3);
+ uint32_t dcerpc_response3_len = sizeof(dcerpc_response3);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_opnum:2-22; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_request1, dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpcrequest1 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ /* response1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response1, dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpcresponse1 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ /* request2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request2, dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpcrequest2 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ /* response2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response2, dcerpc_response2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpcresponse2 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ /* request3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request3, dcerpc_request3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpc request3 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ /* response3 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF,
+ dcerpc_response3, dcerpc_response3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ printf("AppLayerParse for dcerpc response3 failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerDestroyCtxThread(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_opnum(with multiple values) with a bind, bind_ack,
+ * and multiple request/responses with a match test after each frag parsing.
+ */
+static int DetectDceOpnumTestParse12(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x40, 0xfd, 0x2c, 0x34, 0x6c, 0x3c, 0xce, 0x11,
+ 0xa8, 0x93, 0x08, 0x00, 0x2b, 0x2e, 0x9c, 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x7d, 0xd8, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x70, 0x69, 0x70, 0x65, 0x5c,
+ 0x6c, 0x6c, 0x73, 0x72, 0x70, 0x63, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x9a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, //opnum is 0x28 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x9f, 0x13, 0xd9,
+ 0x2d, 0x97, 0xf4, 0x4a, 0xac, 0xc2, 0xbc, 0x70,
+ 0xec, 0xaa, 0x9a, 0xd3, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x80, 0x40, 0x00,
+ 0x44, 0x80, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x00, 0x4e,
+ 0x61, 0x6d, 0x65, 0x00, 0x35, 0x39, 0x31, 0x63,
+ 0x64, 0x30, 0x35, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0xd0, 0x2e, 0x08, 0x00,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x00, 0x00
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x9f, 0x13, 0xd9,
+ 0x2d, 0x97, 0xf4, 0x4a, 0xac, 0xc2, 0xbc, 0x70,
+ 0xec, 0xaa, 0x9a, 0xd3, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x4d, 0x6f, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x35, 0x39, 0x31, 0x63, 0x64, 0x30, 0x35, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4e, 0x6f, 0x6e, 0x65
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd8, 0x17, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x58, 0x1d, 0x08, 0x00, 0xe8, 0x32, 0x08, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x4d, 0x6f, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x35, 0x39, 0x31, 0x63, 0x64, 0x30, 0x35, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0xd0, 0x2e, 0x08, 0x00, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_opnum:30, 40; sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* request1 */
+ SCLogDebug("Sending request1");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request1,
+ dcerpc_request1_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 40) {
+ printf("dcerpc state holding invalid opnum. Holding %d, while we are "
+ "expecting 40: ", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("signature 1 didn't match, should have: ");
+ goto end;
+ }
+
+ /* response1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response1,
+ dcerpc_response1_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 40) {
+ printf("dcerpc state holding invalid opnum. Holding %d, while we are "
+ "expecting 40\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched on response 1, but shouldn't: ");
+ goto end;
+ }
+
+ /* request2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request2,
+ dcerpc_request2_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 30) {
+ printf("dcerpc state holding invalid opnum. Holding %d, while we are "
+ "expecting 30\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't match on request 2: ");
+ goto end;
+ }
+
+ /* response2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF, dcerpc_response2,
+ dcerpc_response2_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 30) {
+ printf("dcerpc state holding invalid opnum. Holding %d, while we are "
+ "expecting 30\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched on response2, but shouldn't: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerDestroyCtxThread(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_opnum(with multiple values) with a bind, bind_ack,
+ * and multiple request/responses with a match test after each frag parsing.
+ */
+static int DetectDceOpnumTestParse13(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x9a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x9f, 0x13, 0xd9,
+ 0x2d, 0x97, 0xf4, 0x4a, 0xac, 0xc2, 0xbc, 0x70,
+ 0xec, 0xaa, 0x9a, 0xd3, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x80, 0x40, 0x00,
+ 0x44, 0x80, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x00, 0x4e,
+ 0x61, 0x6d, 0x65, 0x00, 0x35, 0x39, 0x31, 0x63,
+ 0x64, 0x30, 0x35, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0xd0, 0x2e, 0x08, 0x00,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x00, 0x00
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x9f, 0x13, 0xd9,
+ 0x2d, 0x97, 0xf4, 0x4a, 0xac, 0xc2, 0xbc, 0x70,
+ 0xec, 0xaa, 0x9a, 0xd3, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x4d, 0x6f, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x35, 0x39, 0x31, 0x63, 0x64, 0x30, 0x35, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4e, 0x6f, 0x6e, 0x65
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd8, 0x17, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x58, 0x1d, 0x08, 0x00, 0xe8, 0x32, 0x08, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x4d, 0x6f, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x35, 0x39, 0x31, 0x63, 0x64, 0x30, 0x35, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0xd0, 0x2e, 0x08, 0x00, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_opnum:30, 40; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request1,
+ dcerpc_request1_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 40) {
+ printf("dcerpc state holding invalid opnum after request1. Holding %d, while we are "
+ "expecting 40\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ /* response1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response1,
+ dcerpc_response1_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 40) {
+ printf("dcerpc state holding invalid opnum after response1. Holding %d, while we are "
+ "expecting 40\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ /* request2 */
+ printf("Sending Request2\n");
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request2,
+ dcerpc_request2_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 30) {
+ printf("dcerpc state holding invalid opnum after request2. Holding %d, while we are "
+ "expecting 30\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ /* response2 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF, dcerpc_response2,
+ dcerpc_response2_len);
+ if (r != 0) {
+ printf("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ goto end;
+ }
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ printf("no dcerpc state: ");
+ goto end;
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.opnum != 30) {
+ printf("dcerpc state holding invalid opnum after response2. Holding %d, while we are "
+ "expecting 30\n", dcerpc_state->dcerpc.dcerpcrequest.opnum);
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerDestroyCtxThread(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+#endif
+
+
+#endif
+void DetectDceOpnumRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDceOpnumTestParse01", DetectDceOpnumTestParse01, 1);
+ UtRegisterTest("DetectDceOpnumTestParse02", DetectDceOpnumTestParse02, 1);
+ UtRegisterTest("DetectDceOpnumTestParse03", DetectDceOpnumTestParse03, 1);
+ UtRegisterTest("DetectDceOpnumTestParse04", DetectDceOpnumTestParse04, 1);
+ UtRegisterTest("DetectDceOpnumTestParse05", DetectDceOpnumTestParse05, 1);
+ UtRegisterTest("DetectDceOpnumTestParse06", DetectDceOpnumTestParse06, 1);
+ UtRegisterTest("DetectDceOpnumTestParse07", DetectDceOpnumTestParse07, 1);
+ UtRegisterTest("DetectDceOpnumTestParse08", DetectDceOpnumTestParse08, 1);
+ UtRegisterTest("DetectDceOpnumTestParse09", DetectDceOpnumTestParse09, 1);
+ /* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+ UtRegisterTest("DetectDceOpnumTestParse10", DetectDceOpnumTestParse10, 1);
+ UtRegisterTest("DetectDceOpnumTestParse11", DetectDceOpnumTestParse11, 1);
+ UtRegisterTest("DetectDceOpnumTestParse12", DetectDceOpnumTestParse12, 1);
+ UtRegisterTest("DetectDceOpnumTestParse13", DetectDceOpnumTestParse13, 1);
+#endif
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-dce-opnum.h b/framework/src/suricata/src/detect-dce-opnum.h
new file mode 100644
index 00000000..a4c1a9b7
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-opnum.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_DCE_OPNUM_H__
+#define __DETECT_DCE_OPNUM_H__
+
+#define DCE_OPNUM_RANGE_MAX 65535
+#define DCE_OPNUM_RANGE_UNINITIALIZED 100000
+
+typedef struct DetectDceOpnumRange_ {
+ uint32_t range1;
+ uint32_t range2;
+ struct DetectDceOpnumRange_ *next;
+} DetectDceOpnumRange;
+
+typedef struct DetectDceOpnumData_ {
+ DetectDceOpnumRange *range;
+} DetectDceOpnumData;
+
+void DetectDceOpnumRegister(void);
+void DetectDceOpnumRegisterTests(void);
+
+#endif /* __DETECT_DCE_OPNUM_H__ */
diff --git a/framework/src/suricata/src/detect-dce-stub-data.c b/framework/src/suricata/src/detect-dce-stub-data.c
new file mode 100644
index 00000000..d5e660eb
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-stub-data.c
@@ -0,0 +1,1848 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements dce_stub_data keyword
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "app-layer.h"
+#include "app-layer-dcerpc.h"
+#include "queue.h"
+#include "stream-tcp-reassemble.h"
+#include "detect-dce-stub-data.h"
+
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "stream-tcp.h"
+
+int DetectDceStubDataMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t,
+ void *, Signature *, SigMatch *);
+static int DetectDceStubDataSetup(DetectEngineCtx *, Signature *, char *);
+
+/**
+ * \brief Registers the keyword handlers for the "dce_stub_data" keyword.
+ */
+void DetectDceStubDataRegister(void)
+{
+ sigmatch_table[DETECT_DCE_STUB_DATA].name = "dce_stub_data";
+ sigmatch_table[DETECT_DCE_STUB_DATA].alproto = ALPROTO_DCERPC;
+ sigmatch_table[DETECT_DCE_STUB_DATA].Match = NULL;
+ sigmatch_table[DETECT_DCE_STUB_DATA].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_DCE_STUB_DATA].Setup = DetectDceStubDataSetup;
+ sigmatch_table[DETECT_DCE_STUB_DATA].Free = NULL;
+ sigmatch_table[DETECT_DCE_STUB_DATA].RegisterTests = DetectDceStubDataRegisterTests;
+
+ sigmatch_table[DETECT_DCE_STUB_DATA].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_DCE_STUB_DATA].flags |= SIGMATCH_PAYLOAD;
+
+ return;
+}
+
+/**
+ * \brief Creates a SigMatch for the \"dce_stub_data\" keyword being sent as argument,
+ * and appends it to the Signature(s).
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param arg Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+
+static int DetectDceStubDataSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_DCERPC) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS,
+ "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ s->list = DETECT_SM_LIST_DMATCH;
+ s->alproto = ALPROTO_DCERPC;
+ s->flags |= SIG_FLAG_APPLAYER;
+ return 0;
+
+ error:
+ return -1;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+static int DetectDceStubDataTestParse01(void)
+{
+ Signature s;
+ int result = 0;
+
+ memset(&s, 0, sizeof(Signature));
+
+ result = (DetectDceStubDataSetup(NULL, &s, NULL) == 0);
+
+ if (s.sm_lists[DETECT_SM_LIST_AMATCH] == NULL) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * \test Test a valid dce_stub_data entry with bind, bind_ack, request frags.
+ */
+static int DetectDceStubDataTestParse02(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x26, 0x3d, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+
+ /* todo chop the request frag length and change the
+ * length related parameters in the frag */
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xec, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd4, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x83, 0xc7, 0x0b, 0x47,
+ 0x47, 0x47, 0x47, 0x81, 0x37, 0x22, 0xa5, 0x9b,
+ 0x4a, 0x75, 0xf4, 0xa3, 0x61, 0xd3, 0xbe, 0xdd,
+ 0x5a, 0xfb, 0x20, 0x1e, 0xfc, 0x10, 0x8e, 0x0f,
+ 0xa5, 0x9f, 0x4a, 0x22, 0x20, 0x9b, 0xa8, 0xd5,
+ 0xc4, 0xff, 0xc1, 0x3f, 0xbd, 0x9b, 0x4a, 0x22,
+ 0x2e, 0xc0, 0x7a, 0xa9, 0xfe, 0x97, 0xc9, 0xe1,
+ 0xa9, 0xf3, 0x2f, 0x22, 0xc9, 0x9b, 0x22, 0x50,
+ 0xa5, 0xf5, 0x4a, 0x4a, 0xce, 0x9b, 0x2f, 0x22,
+ 0x2e, 0x6f, 0xc1, 0xe1, 0xf3, 0xa8, 0x83, 0xa2,
+ 0x64, 0x98, 0xc1, 0x62, 0xa1, 0xa0, 0x89, 0x56,
+ 0xa8, 0x1b, 0x8b, 0x2b, 0x2e, 0xe3, 0x7a, 0xd1,
+ 0x03, 0xef, 0x58, 0x7c, 0x4e, 0x7d, 0x14, 0x76,
+ 0xfa, 0xc3, 0x7f, 0x02, 0xa5, 0xbb, 0x4a, 0x89,
+ 0x47, 0x6c, 0x12, 0xc9, 0x70, 0x18, 0x8e, 0x3a,
+ 0x2e, 0xcb, 0x52, 0xa9, 0x67, 0x98, 0x0a, 0x1e,
+ 0x2e, 0xc3, 0x32, 0x21, 0x7f, 0x10, 0x31, 0x3e,
+ 0xa6, 0x61, 0xc1, 0x61, 0x85, 0x98, 0x88, 0xa9,
+ 0xee, 0x83, 0x22, 0x51, 0xd6, 0xda, 0x4a, 0x4a,
+ 0xc1, 0xff, 0x38, 0x47, 0xcd, 0xe9, 0x25, 0x41,
+ 0xe4, 0xf3, 0x0d, 0x47, 0xd1, 0xcb, 0xc1, 0xd6,
+ 0x1e, 0x95, 0x4a, 0x22, 0xa5, 0x73, 0x08, 0x22,
+ 0xa5, 0x9b, 0xc9, 0xe6, 0xb5, 0xcd, 0x22, 0x43,
+ 0xd7, 0xe2, 0x0b, 0x4a, 0xe9, 0xf2, 0x28, 0x50,
+ 0xcd, 0xd7, 0x25, 0x43, 0xc1, 0x10, 0xbe, 0x99,
+ 0xa9, 0x9b, 0x4a, 0x22, 0x4d, 0xb8, 0x4a, 0x22,
+ 0xa5, 0x18, 0x8e, 0x2e, 0xf3, 0xc9, 0x22, 0x4e,
+ 0xc9, 0x9b, 0x4a, 0x4a, 0x96, 0xa9, 0x64, 0x46,
+ 0xcd, 0xec, 0x39, 0x10, 0xfa, 0xcf, 0xb5, 0x76,
+ 0x81, 0x8f, 0xc9, 0xe6, 0xa9, 0x10, 0x82, 0x7c,
+ 0xff, 0xc4, 0xa1, 0x0a, 0xf5, 0xcc, 0x1b, 0x74,
+ 0xf4, 0x10, 0x81, 0xa9, 0x9d, 0x98, 0xb0, 0xa1,
+ 0x65, 0x9f, 0xb9, 0x84, 0xd1, 0x9f, 0x13, 0x7c,
+ 0x47, 0x76, 0x12, 0x7c, 0xfc, 0x10, 0xbb, 0x09,
+ 0x55, 0x5a, 0xac, 0x20, 0xfa, 0x10, 0x7e, 0x15,
+ 0xa6, 0x69, 0x12, 0xe1, 0xf7, 0xca, 0x22, 0x57,
+ 0xd5, 0x9b, 0x4a, 0x4a, 0xd1, 0xfa, 0x38, 0x56,
+ 0xcd, 0xcc, 0x19, 0x63, 0xf6, 0xf3, 0x2f, 0x56,
+ 0xa5, 0x9b, 0x22, 0x51, 0xca, 0xf8, 0x21, 0x48,
+ 0xa5, 0xf3, 0x28, 0x4b, 0xcb, 0xff, 0x22, 0x47,
+ 0xcb, 0x9b, 0x4a, 0x4a, 0xc9, 0xf2, 0x39, 0x56,
+ 0xcd, 0xeb, 0x3e, 0x22, 0xa5, 0xf3, 0x2b, 0x41,
+ 0xc6, 0xfe, 0xc1, 0xfe, 0xf6, 0xca, 0xc9, 0xe1,
+ 0xad, 0xc8, 0x1b, 0xa1, 0x66, 0x93, 0x19, 0x73,
+ 0x26, 0x58, 0x42, 0x71, 0xf4, 0x18, 0x89, 0x2a,
+ 0xf6, 0xca, 0xb5, 0xf5, 0x2c, 0xd8, 0x42, 0xdd,
+ 0x72, 0x12, 0x09, 0x26, 0x5a, 0x4c, 0xc3, 0x21,
+ 0x5a, 0x4c, 0xc3, 0x61, 0x59, 0x64, 0x9d, 0xab,
+ 0xe6, 0x63, 0xc9, 0xc9, 0xad, 0x10, 0xa9, 0xa3,
+ 0x49, 0x0b, 0x4b, 0x22, 0xa5, 0xcf, 0x22, 0x23,
+ 0xa4, 0x9b, 0x4a, 0xdd, 0x31, 0xbf, 0xe2, 0x23,
+ 0xa5, 0x9b, 0xcb, 0xe6, 0x35, 0x9a, 0x4a, 0x22,
+ 0xcf, 0x9d, 0x20, 0x23, 0xcf, 0x99, 0xb5, 0x76,
+ 0x81, 0x83, 0x20, 0x22, 0xcf, 0x9b, 0x20, 0x22,
+ 0xcd, 0x99, 0x4a, 0xe6, 0x96, 0x10, 0x96, 0x71,
+ 0xf6, 0xcb, 0x20, 0x23, 0xf5, 0xf1, 0x5a, 0x71,
+ 0xf5, 0x64, 0x1e, 0x06, 0x9d, 0x64, 0x1e, 0x06,
+ 0x8d, 0x5c, 0x49, 0x32, 0xa5, 0x9b, 0x4a, 0xdd,
+ 0xf1, 0xbf, 0x56, 0xa1, 0x61, 0xbf, 0x13, 0x78,
+ 0xf4, 0xc9, 0x1a, 0x11, 0x77, 0xc9, 0x22, 0x51,
+ 0xc0, 0xf5, 0x2e, 0xa9, 0x61, 0xc9, 0x22, 0x50,
+ 0xc0, 0xf8, 0x3c, 0xa9, 0x71, 0xc9, 0x1b, 0x72,
+ 0xf4, 0x64, 0x9d, 0xb1, 0x5a, 0x4c, 0xdf, 0xa1,
+ 0x61, 0x8b, 0x12, 0x78, 0xfc, 0xc8, 0x1f, 0x72,
+ 0x2e, 0x77, 0x1a, 0x42, 0xcf, 0x9f, 0x10, 0x72,
+ 0x2e, 0x47, 0xa2, 0x63, 0xa5, 0x9b, 0x4a, 0x48,
+ 0xa5, 0xf3, 0x26, 0x4e, 0xca, 0xf8, 0x22, 0x57,
+ 0xc4, 0xf7, 0x0b, 0x4a, 0xf3, 0xf2, 0x38, 0x56,
+ 0xf1, 0xcd, 0xb5, 0xf5, 0x26, 0x5f, 0x5a, 0x78,
+ 0xf7, 0xf1, 0x0a, 0x4a, 0xa5, 0x8b, 0x4a, 0x22,
+ 0xf7, 0xf1, 0x4a, 0xdd, 0x75, 0x12, 0x0e, 0x06,
+ 0x81, 0xc1, 0xd9, 0xca, 0xb5, 0x9b, 0x4a, 0x22,
+ 0xc4, 0xc0, 0xb5, 0xc1, 0xc5, 0xa8, 0x8a, 0x92,
+ 0xa1, 0x73, 0x5c, 0x22, 0xa5, 0x9b, 0x2b, 0xe1,
+ 0xc5, 0xc9, 0x19, 0x11, 0x65, 0x73, 0x40, 0x22,
+ 0xa5, 0x9b, 0x11, 0x78, 0xa6, 0x43, 0x61, 0xf2,
+ 0xd0, 0x74, 0x2b, 0xe1, 0x96, 0x52, 0x1b, 0x70,
+ 0xf6, 0x64, 0x3f, 0x22, 0x5a, 0xcf, 0x4f, 0x26,
+ 0x20, 0x5b, 0x34, 0x23, 0x66, 0x64, 0x1f, 0xd2,
+ 0xa5, 0x9b, 0x4a, 0x22, 0xa5, 0x9b, 0x4a, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x54, 0x58,
+ 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d, 0x6f, 0x41,
+ 0x3f, 0x3f, 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d,
+ 0x6f, 0x43, 0x42, 0x42, 0x50, 0x5f, 0x57, 0xc3,
+ 0x33, 0x5f, 0x37, 0x74, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0xeb, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x53, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61,
+ 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x53, 0x79, 0x73, 0x74,
+ 0x65, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x44, 0x6e, 0x73, 0x44, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x65, 0x66, 0x31, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x72, 0x65, 0x66, 0x32, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01, 0x02, 0x03, 0x04
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_stub_data; content:\"|42 42 42 42|\";"
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* we shouldn't have any stub data */
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ /* do detect */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* we shouldn't have any stub data */
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_EOF,
+ dcerpc_request, dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* we should have the stub data since we previously parsed a request frag */
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test a valid dce_stub_data with just a request frag.
+ */
+static int DetectDceStubDataTestParse03(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ /* todo chop the request frag length and change the
+ * length related parameters in the frag */
+ uint8_t dcerpc_request[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xec, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd4, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe1, 0x03, 0x00, 0x00, 0x83, 0xc7, 0x0b, 0x47,
+ 0x47, 0x47, 0x47, 0x81, 0x37, 0x22, 0xa5, 0x9b,
+ 0x4a, 0x75, 0xf4, 0xa3, 0x61, 0xd3, 0xbe, 0xdd,
+ 0x5a, 0xfb, 0x20, 0x1e, 0xfc, 0x10, 0x8e, 0x0f,
+ 0xa5, 0x9f, 0x4a, 0x22, 0x20, 0x9b, 0xa8, 0xd5,
+ 0xc4, 0xff, 0xc1, 0x3f, 0xbd, 0x9b, 0x4a, 0x22,
+ 0x2e, 0xc0, 0x7a, 0xa9, 0xfe, 0x97, 0xc9, 0xe1,
+ 0xa9, 0xf3, 0x2f, 0x22, 0xc9, 0x9b, 0x22, 0x50,
+ 0xa5, 0xf5, 0x4a, 0x4a, 0xce, 0x9b, 0x2f, 0x22,
+ 0x2e, 0x6f, 0xc1, 0xe1, 0xf3, 0xa8, 0x83, 0xa2,
+ 0x64, 0x98, 0xc1, 0x62, 0xa1, 0xa0, 0x89, 0x56,
+ 0xa8, 0x1b, 0x8b, 0x2b, 0x2e, 0xe3, 0x7a, 0xd1,
+ 0x03, 0xef, 0x58, 0x7c, 0x4e, 0x7d, 0x14, 0x76,
+ 0xfa, 0xc3, 0x7f, 0x02, 0xa5, 0xbb, 0x4a, 0x89,
+ 0x47, 0x6c, 0x12, 0xc9, 0x70, 0x18, 0x8e, 0x3a,
+ 0x2e, 0xcb, 0x52, 0xa9, 0x67, 0x98, 0x0a, 0x1e,
+ 0x2e, 0xc3, 0x32, 0x21, 0x7f, 0x10, 0x31, 0x3e,
+ 0xa6, 0x61, 0xc1, 0x61, 0x85, 0x98, 0x88, 0xa9,
+ 0xee, 0x83, 0x22, 0x51, 0xd6, 0xda, 0x4a, 0x4a,
+ 0xc1, 0xff, 0x38, 0x47, 0xcd, 0xe9, 0x25, 0x41,
+ 0xe4, 0xf3, 0x0d, 0x47, 0xd1, 0xcb, 0xc1, 0xd6,
+ 0x1e, 0x95, 0x4a, 0x22, 0xa5, 0x73, 0x08, 0x22,
+ 0xa5, 0x9b, 0xc9, 0xe6, 0xb5, 0xcd, 0x22, 0x43,
+ 0xd7, 0xe2, 0x0b, 0x4a, 0xe9, 0xf2, 0x28, 0x50,
+ 0xcd, 0xd7, 0x25, 0x43, 0xc1, 0x10, 0xbe, 0x99,
+ 0xa9, 0x9b, 0x4a, 0x22, 0x4d, 0xb8, 0x4a, 0x22,
+ 0xa5, 0x18, 0x8e, 0x2e, 0xf3, 0xc9, 0x22, 0x4e,
+ 0xc9, 0x9b, 0x4a, 0x4a, 0x96, 0xa9, 0x64, 0x46,
+ 0xcd, 0xec, 0x39, 0x10, 0xfa, 0xcf, 0xb5, 0x76,
+ 0x81, 0x8f, 0xc9, 0xe6, 0xa9, 0x10, 0x82, 0x7c,
+ 0xff, 0xc4, 0xa1, 0x0a, 0xf5, 0xcc, 0x1b, 0x74,
+ 0xf4, 0x10, 0x81, 0xa9, 0x9d, 0x98, 0xb0, 0xa1,
+ 0x65, 0x9f, 0xb9, 0x84, 0xd1, 0x9f, 0x13, 0x7c,
+ 0x47, 0x76, 0x12, 0x7c, 0xfc, 0x10, 0xbb, 0x09,
+ 0x55, 0x5a, 0xac, 0x20, 0xfa, 0x10, 0x7e, 0x15,
+ 0xa6, 0x69, 0x12, 0xe1, 0xf7, 0xca, 0x22, 0x57,
+ 0xd5, 0x9b, 0x4a, 0x4a, 0xd1, 0xfa, 0x38, 0x56,
+ 0xcd, 0xcc, 0x19, 0x63, 0xf6, 0xf3, 0x2f, 0x56,
+ 0xa5, 0x9b, 0x22, 0x51, 0xca, 0xf8, 0x21, 0x48,
+ 0xa5, 0xf3, 0x28, 0x4b, 0xcb, 0xff, 0x22, 0x47,
+ 0xcb, 0x9b, 0x4a, 0x4a, 0xc9, 0xf2, 0x39, 0x56,
+ 0xcd, 0xeb, 0x3e, 0x22, 0xa5, 0xf3, 0x2b, 0x41,
+ 0xc6, 0xfe, 0xc1, 0xfe, 0xf6, 0xca, 0xc9, 0xe1,
+ 0xad, 0xc8, 0x1b, 0xa1, 0x66, 0x93, 0x19, 0x73,
+ 0x26, 0x58, 0x42, 0x71, 0xf4, 0x18, 0x89, 0x2a,
+ 0xf6, 0xca, 0xb5, 0xf5, 0x2c, 0xd8, 0x42, 0xdd,
+ 0x72, 0x12, 0x09, 0x26, 0x5a, 0x4c, 0xc3, 0x21,
+ 0x5a, 0x4c, 0xc3, 0x61, 0x59, 0x64, 0x9d, 0xab,
+ 0xe6, 0x63, 0xc9, 0xc9, 0xad, 0x10, 0xa9, 0xa3,
+ 0x49, 0x0b, 0x4b, 0x22, 0xa5, 0xcf, 0x22, 0x23,
+ 0xa4, 0x9b, 0x4a, 0xdd, 0x31, 0xbf, 0xe2, 0x23,
+ 0xa5, 0x9b, 0xcb, 0xe6, 0x35, 0x9a, 0x4a, 0x22,
+ 0xcf, 0x9d, 0x20, 0x23, 0xcf, 0x99, 0xb5, 0x76,
+ 0x81, 0x83, 0x20, 0x22, 0xcf, 0x9b, 0x20, 0x22,
+ 0xcd, 0x99, 0x4a, 0xe6, 0x96, 0x10, 0x96, 0x71,
+ 0xf6, 0xcb, 0x20, 0x23, 0xf5, 0xf1, 0x5a, 0x71,
+ 0xf5, 0x64, 0x1e, 0x06, 0x9d, 0x64, 0x1e, 0x06,
+ 0x8d, 0x5c, 0x49, 0x32, 0xa5, 0x9b, 0x4a, 0xdd,
+ 0xf1, 0xbf, 0x56, 0xa1, 0x61, 0xbf, 0x13, 0x78,
+ 0xf4, 0xc9, 0x1a, 0x11, 0x77, 0xc9, 0x22, 0x51,
+ 0xc0, 0xf5, 0x2e, 0xa9, 0x61, 0xc9, 0x22, 0x50,
+ 0xc0, 0xf8, 0x3c, 0xa9, 0x71, 0xc9, 0x1b, 0x72,
+ 0xf4, 0x64, 0x9d, 0xb1, 0x5a, 0x4c, 0xdf, 0xa1,
+ 0x61, 0x8b, 0x12, 0x78, 0xfc, 0xc8, 0x1f, 0x72,
+ 0x2e, 0x77, 0x1a, 0x42, 0xcf, 0x9f, 0x10, 0x72,
+ 0x2e, 0x47, 0xa2, 0x63, 0xa5, 0x9b, 0x4a, 0x48,
+ 0xa5, 0xf3, 0x26, 0x4e, 0xca, 0xf8, 0x22, 0x57,
+ 0xc4, 0xf7, 0x0b, 0x4a, 0xf3, 0xf2, 0x38, 0x56,
+ 0xf1, 0xcd, 0xb5, 0xf5, 0x26, 0x5f, 0x5a, 0x78,
+ 0xf7, 0xf1, 0x0a, 0x4a, 0xa5, 0x8b, 0x4a, 0x22,
+ 0xf7, 0xf1, 0x4a, 0xdd, 0x75, 0x12, 0x0e, 0x06,
+ 0x81, 0xc1, 0xd9, 0xca, 0xb5, 0x9b, 0x4a, 0x22,
+ 0xc4, 0xc0, 0xb5, 0xc1, 0xc5, 0xa8, 0x8a, 0x92,
+ 0xa1, 0x73, 0x5c, 0x22, 0xa5, 0x9b, 0x2b, 0xe1,
+ 0xc5, 0xc9, 0x19, 0x11, 0x65, 0x73, 0x40, 0x22,
+ 0xa5, 0x9b, 0x11, 0x78, 0xa6, 0x43, 0x61, 0xf2,
+ 0xd0, 0x74, 0x2b, 0xe1, 0x96, 0x52, 0x1b, 0x70,
+ 0xf6, 0x64, 0x3f, 0x22, 0x5a, 0xcf, 0x4f, 0x26,
+ 0x20, 0x5b, 0x34, 0x23, 0x66, 0x64, 0x1f, 0xd2,
+ 0xa5, 0x9b, 0x4a, 0x22, 0xa5, 0x9b, 0x4a, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x54, 0x58,
+ 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d, 0x6f, 0x41,
+ 0x3f, 0x3f, 0x2d, 0x6f, 0x41, 0x3f, 0x3f, 0x2d,
+ 0x6f, 0x43, 0x42, 0x42, 0x50, 0x5f, 0x57, 0xc3,
+ 0x33, 0x5f, 0x37, 0x74, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0xeb, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x53, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61,
+ 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x44, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x53, 0x79, 0x73, 0x74,
+ 0x65, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x44, 0x6e, 0x73, 0x44, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x65,
+ 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x65, 0x66, 0x31, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x72, 0x65, 0x66, 0x32, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01, 0x02, 0x03, 0x04
+ };
+
+ uint32_t dcerpc_request_len = sizeof(dcerpc_request);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_stub_data; content:\"|42 42 42 42|\";"
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_request, dcerpc_request_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectDceStubDataTestParse04(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0xd0, 0x8c, 0x33, 0x44, 0x22, 0xf1, 0x31,
+ 0xaa, 0xaa, 0x90, 0x00, 0x38, 0x00, 0x10, 0x03,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_bindack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x65, 0x8e, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x77, 0x69, 0x6e, 0x72, 0x65, 0x67, 0x00, 0x6d,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_bind_len = sizeof(dcerpc_bind);
+ uint32_t dcerpc_bindack_len = sizeof(dcerpc_bindack);
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ uint32_t dcerpc_request3_len = sizeof(dcerpc_request3);
+ uint32_t dcerpc_response3_len = sizeof(dcerpc_response3);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_stub_data; content:\"|00 02|\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_stub_data; content:\"|00 75|\"; sid:2;)");
+ if (s == NULL)
+ goto end;
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; dce_stub_data; content:\"|00 18|\"; sid:3;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_bind, dcerpc_bind_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_bindack,
+ dcerpc_bindack_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* request1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request1,
+ dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response1,
+ dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* request2 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request2,
+ dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || !PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response2 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, dcerpc_response2,
+ dcerpc_response2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* request3 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, dcerpc_request3,
+ dcerpc_request3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || !PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response3 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF,
+ dcerpc_response3, dcerpc_response3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectDceStubDataTestParse05(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DCERPCState *dcerpc_state = NULL;
+ int r = 0;
+
+ uint8_t dcerpc_request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ uint8_t dcerpc_response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+
+ uint8_t dcerpc_response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ uint32_t dcerpc_request1_len = sizeof(dcerpc_request1);
+ uint32_t dcerpc_response1_len = sizeof(dcerpc_response1);
+
+ uint32_t dcerpc_request2_len = sizeof(dcerpc_request2);
+ uint32_t dcerpc_response2_len = sizeof(dcerpc_response2);
+
+ uint32_t dcerpc_request3_len = sizeof(dcerpc_request3);
+ uint32_t dcerpc_response3_len = sizeof(dcerpc_response3);
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_stub_data; content:\"|00 02|\"; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+ s = de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_stub_data; content:\"|00 75|\"; "
+ "sid:2;)");
+ if (s == NULL)
+ goto end;
+ s = de_ctx->sig_list->next->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"DCERPC\"; "
+ "dce_stub_data; content:\"|00 18|\"; "
+ "sid:3;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER | STREAM_START,
+ dcerpc_request1, dcerpc_request1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dcerpc_state = f.alstate;
+ if (dcerpc_state == NULL) {
+ SCLogDebug("no dcerpc state: ");
+ goto end;
+ }
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response1, dcerpc_response1_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* request2 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request2, dcerpc_request2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || !PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response2 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT,
+ dcerpc_response2, dcerpc_response2_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || PacketAlertCheck(p, 3))
+ goto end;
+
+ /* request3 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER,
+ dcerpc_request3, dcerpc_request3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2) || !PacketAlertCheck(p, 3))
+ goto end;
+
+ /* response3 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT | STREAM_EOF,
+ dcerpc_response3, dcerpc_response3_len);
+ if (r != 0) {
+ SCLogDebug("AppLayerParse for dcerpc failed. Returned %" PRId32, r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ p->flowflags &=~ FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+
+#endif
+
+void DetectDceStubDataRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDceStubDataTestParse01", DetectDceStubDataTestParse01, 1);
+ UtRegisterTest("DetectDceStubDataTestParse02", DetectDceStubDataTestParse02, 1);
+ UtRegisterTest("DetectDceStubDataTestParse03", DetectDceStubDataTestParse03, 1);
+ UtRegisterTest("DetectDceStubDataTestParse04", DetectDceStubDataTestParse04, 1);
+ UtRegisterTest("DetectDceStubDataTestParse05", DetectDceStubDataTestParse05, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-dce-stub-data.h b/framework/src/suricata/src/detect-dce-stub-data.h
new file mode 100644
index 00000000..adc45388
--- /dev/null
+++ b/framework/src/suricata/src/detect-dce-stub-data.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_DCE_STUB_DATA_H__
+#define __DETECT_DCE_STUB_DATA_H__
+
+void DetectDceStubDataRegister(void);
+void DetectDceStubDataRegisterTests(void);
+
+#endif /* __DETECT_DCE_STUB_DATA_H__ */
diff --git a/framework/src/suricata/src/detect-depth.c b/framework/src/suricata/src/detect-depth.c
new file mode 100644
index 00000000..f58438da
--- /dev/null
+++ b/framework/src/suricata/src/detect-depth.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the depth keyword.
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-byte-extract.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "app-layer.h"
+
+#include "util-debug.h"
+
+static int DetectDepthSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectDepthRegister (void)
+{
+ sigmatch_table[DETECT_DEPTH].name = "depth";
+ sigmatch_table[DETECT_DEPTH].desc = "designate how many bytes from the beginning of the payload will be checked";
+ sigmatch_table[DETECT_DEPTH].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Depth";
+ sigmatch_table[DETECT_DEPTH].Match = NULL;
+ sigmatch_table[DETECT_DEPTH].Setup = DetectDepthSetup;
+ sigmatch_table[DETECT_DEPTH].Free = NULL;
+ sigmatch_table[DETECT_DEPTH].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_DEPTH].flags |= SIGMATCH_PAYLOAD;
+}
+
+static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, char *depthstr)
+{
+ char *str = depthstr;
+ char dubbed = 0;
+ SigMatch *pm = NULL;
+ int ret = -1;
+
+ /* strip "'s */
+ if (depthstr[0] == '\"' && depthstr[strlen(depthstr) - 1] == '\"') {
+ str = SCStrdup(depthstr + 1);
+ if (unlikely(str == NULL))
+ goto end;
+ str[strlen(depthstr) - 2] = '\0';
+ dubbed = 1;
+ }
+
+ /* retrive the sm to apply the depth against */
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ pm = SigMatchGetLastSMFromLists(s, 2, DETECT_CONTENT, s->sm_lists_tail[s->list]);
+ } else {
+ pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ }
+ if (pm == NULL) {
+ SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs "
+ "preceding content, uricontent option, http_client_body, "
+ "http_server_body, http_header option, http_raw_header option, "
+ "http_method option, http_cookie, http_raw_uri, "
+ "http_stat_msg, http_stat_code, http_user_agent, "
+ "http_host, http_raw_host or "
+ "file_data/dce_stub_data sticky buffer options");
+ goto end;
+ }
+
+ /* verify other conditions. */
+ DetectContentData *cd = (DetectContentData *)pm->ctx;
+
+ if (cd->flags & DETECT_CONTENT_DEPTH) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple depths for the same content.");
+ goto end;
+ }
+ if ((cd->flags & DETECT_CONTENT_WITHIN) || (cd->flags & DETECT_CONTENT_DISTANCE)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative "
+ "keyword like within/distance with a absolute "
+ "relative keyword like depth/offset for the same "
+ "content." );
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "negated keyword set along with a fast_pattern");
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "keyword set along with a fast_pattern:only;");
+ goto end;
+ }
+ if (str[0] != '-' && isalpha((unsigned char)str[0])) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(str, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "unknown byte_extract var "
+ "seen in depth - %s\n", str);
+ goto end;
+ }
+ cd->depth = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ cd->flags |= DETECT_CONTENT_DEPTH_BE;
+ } else {
+ cd->depth = (uint32_t)atoi(str);
+ if (cd->depth < cd->content_len) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "depth - %"PRIu16
+ " smaller than content length - %"PRIu32,
+ cd->depth, cd->content_len);
+ goto end;
+ }
+ /* Now update the real limit, as depth is relative to the offset */
+ cd->depth += cd->offset;
+ }
+ cd->flags |= DETECT_CONTENT_DEPTH;
+
+ ret = 0;
+ end:
+ if (dubbed)
+ SCFree(str);
+ return ret;
+}
diff --git a/framework/src/suricata/src/detect-depth.h b/framework/src/suricata/src/detect-depth.h
new file mode 100644
index 00000000..b864f621
--- /dev/null
+++ b/framework/src/suricata/src/detect-depth.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_DEPTH_H__
+#define __DETECT_DEPTH_H__
+
+/* prototypes */
+void DetectDepthRegister (void);
+
+#endif /* __DETECT_DEPTH_H__ */
+
diff --git a/framework/src/suricata/src/detect-detection-filter.c b/framework/src/suricata/src/detect-detection-filter.c
new file mode 100644
index 00000000..7e1ee8bc
--- /dev/null
+++ b/framework/src/suricata/src/detect-detection-filter.c
@@ -0,0 +1,665 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ *
+ * Implements the detection_filter keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "host.h"
+
+#include "detect-detection-filter.h"
+#include "detect-threshold.h"
+#include "detect-parse.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+#define TRACK_DST 1
+#define TRACK_SRC 2
+
+/**
+ *\brief Regex for parsing our detection_filter options
+ */
+#define PARSE_REGEX "^\\s*(track|count|seconds)\\s+(by_src|by_dst|\\d+)\\s*,\\s*(track|count|seconds)\\s+(by_src|by_dst|\\d+)\\s*,\\s*(track|count|seconds)\\s+(by_src|by_dst|\\d+)\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectDetectionFilterMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectDetectionFilterSetup(DetectEngineCtx *, Signature *, char *);
+void DetectDetectionFilterRegisterTests(void);
+void DetectDetectionFilterFree(void *);
+
+/**
+ * \brief Registration function for detection_filter: keyword
+ */
+void DetectDetectionFilterRegister (void)
+{
+ sigmatch_table[DETECT_DETECTION_FILTER].name = "detection_filter";
+ sigmatch_table[DETECT_DETECTION_FILTER].desc = "alert on every match after a threshold has been reached";
+ sigmatch_table[DETECT_DETECTION_FILTER].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Rule-Thresholding#detection_filter";
+ sigmatch_table[DETECT_DETECTION_FILTER].Match = DetectDetectionFilterMatch;
+ sigmatch_table[DETECT_DETECTION_FILTER].Setup = DetectDetectionFilterSetup;
+ sigmatch_table[DETECT_DETECTION_FILTER].Free = DetectDetectionFilterFree;
+ sigmatch_table[DETECT_DETECTION_FILTER].RegisterTests = DetectDetectionFilterRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_DETECTION_FILTER].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+int DetectDetectionFilterMatch (ThreadVars *thv, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse detection_filter options passed via detection_filter: keyword
+ *
+ * \param rawstr Pointer to the user provided detection_filter options
+ *
+ * \retval df pointer to DetectThresholdData on success
+ * \retval NULL on failure
+ */
+DetectThresholdData *DetectDetectionFilterParse (char *rawstr)
+{
+ DetectThresholdData *df = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+ char *args[6] = { NULL, NULL, NULL, NULL, NULL, NULL};
+ char *copy_str = NULL, *df_opt = NULL;
+ int seconds_found = 0, count_found = 0, track_found = 0;
+ int seconds_pos = 0, count_pos = 0;
+ uint16_t pos = 0;
+ int i = 0;
+ char *saveptr = NULL;
+
+ copy_str = SCStrdup(rawstr);
+ if (unlikely(copy_str == NULL)) {
+ goto error;
+ }
+
+ for (pos = 0, df_opt = strtok_r(copy_str,",", &saveptr);
+ pos < strlen(copy_str) && df_opt != NULL;
+ pos++, df_opt = strtok_r(NULL,",", &saveptr))
+ {
+ if(strstr(df_opt,"count"))
+ count_found++;
+ if(strstr(df_opt,"second"))
+ seconds_found++;
+ if(strstr(df_opt,"track"))
+ track_found++;
+ }
+ SCFree(copy_str);
+ copy_str = NULL;
+
+ if (count_found != 1 || seconds_found != 1 || track_found != 1)
+ goto error;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 5) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ df = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(df == NULL))
+ goto error;
+
+ memset(df,0,sizeof(DetectThresholdData));
+
+ df->type = TYPE_DETECTION;
+
+ for (i = 0; i < (ret - 1); i++) {
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, i + 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ args[i] = (char *)str_ptr;
+
+ if (strncasecmp(args[i],"by_dst",strlen("by_dst")) == 0)
+ df->track = TRACK_DST;
+ if (strncasecmp(args[i],"by_src",strlen("by_src")) == 0)
+ df->track = TRACK_SRC;
+ if (strncasecmp(args[i],"count",strlen("count")) == 0)
+ count_pos = i+1;
+ if (strncasecmp(args[i],"seconds",strlen("seconds")) == 0)
+ seconds_pos = i+1;
+ }
+
+ if (args[count_pos] == NULL || args[seconds_pos] == NULL) {
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&df->count, 10, strlen(args[count_pos]),
+ args[count_pos]) <= 0) {
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&df->seconds, 10, strlen(args[seconds_pos]),
+ args[seconds_pos]) <= 0) {
+ goto error;
+ }
+
+ if (df->count == 0 || df->seconds == 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "found an invalid value");
+ goto error;
+ }
+
+ for (i = 0; i < 6; i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ return df;
+
+error:
+ for (i = 0; i < 6; i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (df != NULL)
+ SCFree(df);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed detection_filter into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param rawstr pointer to the user provided detection_filter options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectDetectionFilterSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ SCEnter();
+ DetectThresholdData *df = NULL;
+ SigMatch *sm = NULL;
+ SigMatch *tmpm = NULL;
+
+ /* checks if there's a previous instance of threshold */
+ tmpm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists_tail[DETECT_SM_LIST_MATCH]);
+ if (tmpm != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "\"detection_filter\" and \"threshold\" are not allowed in the same rule");
+ SCReturnInt(-1);
+ }
+ /* checks there's no previous instance of detection_filter */
+ tmpm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists_tail[DETECT_SM_LIST_MATCH]);
+ if (tmpm != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "At most one \"detection_filter\" is allowed per rule");
+ SCReturnInt(-1);
+ }
+
+ df = DetectDetectionFilterParse(rawstr);
+ if (df == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_DETECTION_FILTER;
+ sm->ctx = (SigMatchCtx *)df;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
+
+ return 0;
+
+error:
+ if (df) SCFree(df);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectThresholdData
+ *
+ * \param df_ptr pointer to DetectDetectionFilterData
+ */
+void DetectDetectionFilterFree(void *df_ptr)
+{
+ DetectThresholdData *df = (DetectThresholdData *)df_ptr;
+ if (df) SCFree(df);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-threshold.h"
+#include "util-time.h"
+#include "util-hashlist.h"
+
+/**
+ * \test DetectDetectionFilterTestParse01 is a test for a valid detection_filter options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse01 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("track by_dst,count 10,seconds 60");
+ if (df && (df->track == TRACK_DST) && (df->count == 10) && (df->seconds == 60)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectDetectionFilterTestParse02 is a test for a invalid detection_filter options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse02 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("track both,count 10,seconds 60");
+ if (df && (df->track == TRACK_DST || df->track == TRACK_SRC) && (df->count == 10) && (df->seconds == 60)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectDetectionfilterTestParse03 is a test for a valid detection_filter options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse03 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("track by_dst, seconds 60, count 10");
+ if (df && (df->track == TRACK_DST) && (df->count == 10) && (df->seconds == 60)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * \test DetectDetectionFilterTestParse04 is a test for an invalid detection_filter options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse04 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("count 10, track by_dst, seconds 60, count 10");
+ if (df && (df->track == TRACK_DST) && (df->count == 10) && (df->seconds == 60)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectDetectionFilterTestParse05 is a test for a valid detection_filter options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse05 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("count 10, track by_dst, seconds 60");
+ if (df && (df->track == TRACK_DST) && (df->count == 10) && (df->seconds == 60)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectDetectionFilterTestParse06 is a test for an invalid value in detection_filter
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DetectDetectionFilterTestParse06 (void)
+{
+ DetectThresholdData *df = NULL;
+ df = DetectDetectionFilterParse("count 10, track by_dst, seconds 0");
+ if (df && (df->track == TRACK_DST) && (df->count == 10) && (df->seconds == 0)) {
+ DetectDetectionFilterFree(df);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectDetectionFilterTestSig1 is a test for checking the working of detection_filter keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int DetectDetectionFilterTestSig1(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"detection_filter Test\"; detection_filter: track by_dst, count 4, seconds 60; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+
+ if(alerts == 4)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test DetectDetectionFilterTestSig2 is a test for checking the working of detection_filter keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectDetectionFilterTestSig2(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"detection_filter Test 2\"; detection_filter: track by_dst, count 4, seconds 60; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+
+ if (alerts == 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test drops
+ */
+static int DetectDetectionFilterTestSig3(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (msg:\"detection_filter Test 2\"; detection_filter: track by_dst, count 2, seconds 60; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 3 && drops == 3)
+ result = 1;
+ else {
+ if (alerts != 3)
+ printf("alerts: %d != 3: ", alerts);
+ if (drops != 3)
+ printf("drops: %d != 3: ", drops);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+#endif /* UNITTESTS */
+
+void DetectDetectionFilterRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDetectionFilterTestParse01", DetectDetectionFilterTestParse01, 1);
+ UtRegisterTest("DetectDetectionFilterTestParse02", DetectDetectionFilterTestParse02, 0);
+ UtRegisterTest("DetectDetectionFilterTestParse03", DetectDetectionFilterTestParse03, 1);
+ UtRegisterTest("DetectDetectionFilterTestParse04", DetectDetectionFilterTestParse04, 0);
+ UtRegisterTest("DetectDetectionFilterTestParse05", DetectDetectionFilterTestParse05, 1);
+ UtRegisterTest("DetectDetectionFilterTestParse06", DetectDetectionFilterTestParse06, 0);
+ UtRegisterTest("DetectDetectionFilterTestSig1", DetectDetectionFilterTestSig1, 1);
+ UtRegisterTest("DetectDetectionFilterTestSig2", DetectDetectionFilterTestSig2, 1);
+ UtRegisterTest("DetectDetectionFilterTestSig3", DetectDetectionFilterTestSig3, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-detection-filter.h b/framework/src/suricata/src/detect-detection-filter.h
new file mode 100644
index 00000000..2c74d4f4
--- /dev/null
+++ b/framework/src/suricata/src/detect-detection-filter.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ */
+
+#ifndef __DETECT_DETECTION_FILTER_H__
+#define __DETECT_DETECTION_FILTER_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+/**
+ * Registration function for detection_filter: keyword
+ */
+
+void DetectDetectionFilterRegister (void);
+
+/**
+ * This function registers unit tests for detection_filter
+ */
+
+void DetectDetectionFilterRegisterTests(void);
+
+#endif /*__DETECT_DETECTION_FILTER_H__ */
+
diff --git a/framework/src/suricata/src/detect-distance.c b/framework/src/suricata/src/detect-distance.c
new file mode 100644
index 00000000..a8284dbe
--- /dev/null
+++ b/framework/src/suricata/src/detect-distance.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the distance keyword
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "app-layer.h"
+#include "detect-parse.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-pcre.h"
+#include "detect-byte-extract.h"
+
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "detect-bytejump.h"
+#include "util-unittest-helper.h"
+
+static int DetectDistanceSetup(DetectEngineCtx *, Signature *, char *);
+void DetectDistanceRegisterTests(void);
+
+void DetectDistanceRegister(void)
+{
+ sigmatch_table[DETECT_DISTANCE].name = "distance";
+ sigmatch_table[DETECT_DISTANCE].desc = "indicates a relation between this content keyword and the content preceding it";
+ sigmatch_table[DETECT_DISTANCE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Distance";
+ sigmatch_table[DETECT_DISTANCE].Match = NULL;
+ sigmatch_table[DETECT_DISTANCE].Setup = DetectDistanceSetup;
+ sigmatch_table[DETECT_DISTANCE].Free = NULL;
+ sigmatch_table[DETECT_DISTANCE].RegisterTests = DetectDistanceRegisterTests;
+
+ sigmatch_table[DETECT_DISTANCE].flags |= SIGMATCH_PAYLOAD;
+}
+
+static int DetectDistanceSetup (DetectEngineCtx *de_ctx, Signature *s,
+ char *distancestr)
+{
+ char *str = distancestr;
+ char dubbed = 0;
+ SigMatch *pm = NULL;
+ int ret = -1;
+
+ /* strip "'s */
+ if (distancestr[0] == '\"' && distancestr[strlen(distancestr) - 1] == '\"') {
+ str = SCStrdup(distancestr + 1);
+ if (unlikely(str == NULL))
+ goto end;
+ str[strlen(distancestr) - 2] = '\0';
+ dubbed = 1;
+ }
+
+ /* retrive the sm to apply the depth against */
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ pm = SigMatchGetLastSMFromLists(s, 2, DETECT_CONTENT, s->sm_lists_tail[s->list]);
+ } else {
+ pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ }
+ if (pm == NULL) {
+ SCLogError(SC_ERR_OFFSET_MISSING_CONTENT, "distance needs "
+ "preceding content, uricontent option, http_client_body, "
+ "http_server_body, http_header option, http_raw_header option, "
+ "http_method option, http_cookie, http_raw_uri, "
+ "http_stat_msg, http_stat_code, http_user_agent or "
+ "file_data/dce_stub_data sticky buffer option");
+ goto end;
+ }
+
+ /* verify other conditions */
+ DetectContentData *cd = (DetectContentData *)pm->ctx;
+ if (cd->flags & DETECT_CONTENT_DISTANCE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple distances for the same content.");
+ goto end;
+ }
+ if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative "
+ "keyword like within/distance with a absolute "
+ "relative keyword like depth/offset for the same "
+ "content." );
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "negated keyword set along with a fast_pattern");
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "keyword set along with a fast_pattern:only;");
+ goto end;
+ }
+ if (str[0] != '-' && isalpha((unsigned char)str[0])) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(str, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "unknown byte_extract var "
+ "seen in distance - %s\n", str);
+ goto end;
+ }
+ cd->distance = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ cd->flags |= DETECT_CONTENT_DISTANCE_BE;
+ } else {
+ cd->distance = strtol(str, NULL, 10);
+ }
+ cd->flags |= DETECT_CONTENT_DISTANCE;
+
+ SigMatch *prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, pm->prev,
+ DETECT_PCRE, pm->prev);
+ if (prev_pm == NULL) {
+ ret = 0;
+ goto end;
+ }
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "previous keyword "
+ "has a fast_pattern:only; set. Can't "
+ "have relative keywords around a fast_pattern "
+ "only content");
+ goto end;
+ }
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ ret = 0;
+ end:
+ if (dubbed)
+ SCFree(str);
+ return ret;
+}
+
+#ifdef UNITTESTS
+
+static int DetectDistanceTest01(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("no de_ctx: ");
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (content:\"|AA BB|\"; content:\"|CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE|\"; distance: 4; within: 19; sid:1; rev:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigMatch *sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm == NULL) {
+ printf("sm NULL: ");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm == NULL) {
+ printf("sm2 NULL: ");
+ goto end;
+ }
+
+ DetectContentData *co = (DetectContentData *)sm->ctx;
+ if (co == NULL) {
+ printf("co == NULL: ");
+ goto end;
+ }
+
+ if (co->distance != 4) {
+ printf("distance %"PRIi32", expected 4: ", co->distance);
+ goto end;
+ }
+
+ /* within needs to be 23: distance + content_len as Snort auto fixes this */
+ if (co->within != 19) {
+ printf("within %"PRIi32", expected 23: ", co->within);
+ goto end;
+ }
+
+ result = 1;
+end:
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectDistanceTestPacket01 is a test to check matches of
+ * distance works, if the previous keyword is byte_jump and content
+ * (bug 163)
+ */
+int DetectDistanceTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t buf[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ uint16_t buflen = sizeof(buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"suricata test\"; "
+ "byte_jump:1,2; content:\"|00|\"; "
+ "within:1; distance:2; sid:98711212; rev:1;)";
+
+ p->flowflags = FLOW_PKT_ESTABLISHED | FLOW_PKT_TOCLIENT;
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+#endif /* UNITTESTS */
+
+void DetectDistanceRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDistanceTest01 -- distance / within mix", DetectDistanceTest01, 1);
+ UtRegisterTest("DetectDistanceTestPacket01", DetectDistanceTestPacket01, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-distance.h b/framework/src/suricata/src/detect-distance.h
new file mode 100644
index 00000000..80aebe4f
--- /dev/null
+++ b/framework/src/suricata/src/detect-distance.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_DISTANCE_H__
+#define __DETECT_DISTANCE_H__
+
+/* prototypes */
+void DetectDistanceRegister (void);
+
+#endif /* __DETECT_DISTANCE_H__ */
+
diff --git a/framework/src/suricata/src/detect-dns-query.c b/framework/src/suricata/src/detect-dns-query.c
new file mode 100644
index 00000000..55742f89
--- /dev/null
+++ b/framework/src/suricata/src/detect-dns-query.c
@@ -0,0 +1,1180 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup dnslayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-util.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer.h"
+#include "app-layer-dns-common.h"
+#include "detect-dns-query.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+static int DetectDnsQuerySetup (DetectEngineCtx *, Signature *, char *);
+static void DetectDnsQueryRegisterTests(void);
+
+/**
+ * \brief Registration function for keyword: http_uri
+ */
+void DetectDnsQueryRegister (void)
+{
+ sigmatch_table[DETECT_AL_DNS_QUERY].name = "dns_query";
+ sigmatch_table[DETECT_AL_DNS_QUERY].desc = "content modifier to match specifically and only on the DNS query-buffer";
+ sigmatch_table[DETECT_AL_DNS_QUERY].Match = NULL;
+ sigmatch_table[DETECT_AL_DNS_QUERY].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_DNS_QUERY].alproto = ALPROTO_DNS;
+ sigmatch_table[DETECT_AL_DNS_QUERY].Setup = DetectDnsQuerySetup;
+ sigmatch_table[DETECT_AL_DNS_QUERY].Free = NULL;
+ sigmatch_table[DETECT_AL_DNS_QUERY].RegisterTests = DetectDnsQueryRegisterTests;
+
+ sigmatch_table[DETECT_AL_DNS_QUERY].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_DNS_QUERY].flags |= SIGMATCH_PAYLOAD;
+}
+
+
+/**
+ * \brief this function setups the dns_query modifier keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+static int DetectDnsQuerySetup(DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ s->list = DETECT_SM_LIST_DNSQUERYNAME_MATCH;
+ s->alproto = ALPROTO_DNS;
+ return 0;
+}
+
+/**
+ * \brief Run the pattern matcher against the queries
+ *
+ * \param f locked flow
+ * \param dns_state initialized dns state
+ *
+ * \warning Make sure the flow/state is locked
+ * \todo what should we return? Just the fact that we matched?
+ */
+uint32_t DetectDnsQueryInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ DNSState *dns_state, uint8_t flags, void *txv,
+ uint64_t tx_id)
+{
+ SCEnter();
+
+ DNSTransaction *tx = (DNSTransaction *)txv;
+ DNSQueryEntry *query = NULL;
+ uint8_t *buffer;
+ uint16_t buffer_len;
+ uint32_t cnt = 0;
+
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ SCLogDebug("tx %p query %p", tx, query);
+
+ buffer = (uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry));
+ buffer_len = query->len;
+
+ cnt += DnsQueryPatternSearch(det_ctx,
+ buffer, buffer_len,
+ flags);
+ }
+
+ SCReturnUInt(cnt);
+}
+
+#ifdef UNITTESTS
+/** \test simple google.com query matching */
+static int DetectDnsQueryTest01(void)
+{
+ /* google.com */
+ uint8_t buf[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+
+ p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ f.alproto = ALPROTO_DNS;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf, sizeof(buf));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multi tx google.(com|net) query matching */
+static int DetectDnsQueryTest02(void)
+{
+ /* google.com */
+ uint8_t buf1[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x01, 0x00, 0x01, };
+
+ uint8_t buf2[] = { 0x10, 0x32, /* tx id */
+ 0x81, 0x80, /* flags: resp, recursion desired, recusion available */
+ 0x00, 0x01, /* 1 query */
+ 0x00, 0x01, /* 1 answer */
+ 0x00, 0x00, 0x00, 0x00, /* no auth rr, additional rr */
+ /* query record */
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, /* name */
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* name cont */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ /* answer */
+ 0xc0, 0x0c, /* ref to name in query above */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ 0x00, 0x01, 0x40, 0xef, /* ttl */
+ 0x00, 0x04, /* data len */
+ 0x01, 0x02, 0x03, 0x04 }; /* addr */
+
+ /* google.net */
+ uint8_t buf3[] = { 0x11, 0x33, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+
+ p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p2 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p3 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+ f.alproto = ALPROTO_DNS;
+
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->pcap_cnt = 1;
+
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->pcap_cnt = 2;
+
+ p3->flow = &f;
+ p3->flags |= PKT_HAS_FLOW;
+ p3->flowflags |= FLOW_PKT_TOSERVER;
+ p3->pcap_cnt = 3;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.com\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.net\"; nocase; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf1, sizeof(buf1));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("(p1) sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p1, 2)) {
+ printf("(p1) sig 2 did alert, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOCLIENT, buf2, sizeof(buf2));
+ if (r != 0) {
+ printf("toserver client 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("(p2) sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p2, 2)) {
+ printf("(p2) sig 2 alerted, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf3, sizeof(buf3));
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p3);
+
+ if (PacketAlertCheck(p3, 1)) {
+ printf("(p3) sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p3, 2))) {
+ printf("(p3) sig 2 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p1);
+ UTHFreePacket(p2);
+ UTHFreePacket(p3);
+ return result;
+}
+
+/** \test simple google.com query matching (TCP) */
+static int DetectDnsQueryTest03(void)
+{
+ /* google.com */
+ uint8_t buf[] = { 0x00, 28,
+ 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_DNS;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "content:\"google\"; nocase; dns_query; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf, sizeof(buf));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test simple google.com query matching (TCP splicing) */
+static int DetectDnsQueryTest04(void)
+{
+ /* google.com */
+ uint8_t buf1[] = { 0x00, 28,
+ 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+ uint8_t buf2[] = { 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p1 = NULL, *p2 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p2 = UTHBuildPacketReal(buf2, sizeof(buf2), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+ f.alproto = ALPROTO_DNS;
+
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p1->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf1, sizeof(buf1));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf2, sizeof(buf2));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p1);
+ UTHFreePacket(p2);
+ return result;
+}
+
+/** \test simple google.com query matching (TCP splicing) */
+static int DetectDnsQueryTest05(void)
+{
+ /* google.com in 2 chunks (buf1 and buf2) */
+ uint8_t buf1[] = { 0x00, 28, /* len 28 */
+ 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+
+ uint8_t buf2[] = { 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+
+ uint8_t buf3[] = { 0x00, 44, /* len 44 */
+ 0x10, 0x32, /* tx id */
+ 0x81, 0x80, /* flags: resp, recursion desired, recusion available */
+ 0x00, 0x01, /* 1 query */
+ 0x00, 0x01, /* 1 answer */
+ 0x00, 0x00, 0x00, 0x00, /* no auth rr, additional rr */
+ /* query record */
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, /* name */
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* name cont */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ /* answer */
+ 0xc0, 0x0c, /* ref to name in query above */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ 0x00, 0x01, 0x40, 0xef, /* ttl */
+ 0x00, 0x04, /* data len */
+ 0x01, 0x02, 0x03, 0x04 }; /* addr */
+
+ /* google.net */
+ uint8_t buf4[] = { 0x00, 28, /* len 28 */
+ 0x11, 0x33, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p2 = UTHBuildPacketReal(buf2, sizeof(buf2), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p3 = UTHBuildPacketReal(buf3, sizeof(buf3), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p4 = UTHBuildPacketReal(buf4, sizeof(buf4), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+ f.alproto = ALPROTO_DNS;
+
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p1->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+
+ p3->flow = &f;
+ p3->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p3->flowflags |= FLOW_PKT_TOCLIENT|FLOW_PKT_ESTABLISHED;
+
+ p4->flow = &f;
+ p4->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p4->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.com\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.net\"; nocase; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf1, sizeof(buf1));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("(p1) sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p1, 2)) {
+ printf("(p1) sig 2 did alert, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf2, sizeof(buf2));
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p2, 2)) {
+ printf("(p2) sig 2 did alert, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOCLIENT, buf3, sizeof(buf3));
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p3);
+
+ if (PacketAlertCheck(p3, 1)) {
+ printf("sig 1 did alert, but it should not have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p3, 2)) {
+ printf("(p3) sig 2 did alert, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf4, sizeof(buf4));
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p4);
+
+ if (PacketAlertCheck(p4, 1)) {
+ printf("(p4) sig 1 did alert, but it should not have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p4, 2))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p1);
+ UTHFreePacket(p2);
+ UTHFreePacket(p3);
+ UTHFreePacket(p4);
+ return result;
+}
+
+/** \test simple google.com query matching, pcre */
+static int DetectDnsQueryTest06(void)
+{
+ /* google.com */
+ uint8_t buf[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+
+ p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ f.alproto = ALPROTO_DNS;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google\"; nocase; "
+ "pcre:\"/google\\.com$/i\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google\"; nocase; "
+ "pcre:\"/^\\.[a-z]{2,3}$/iR\"; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf, sizeof(buf));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sig 2 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multi tx google.(com|net) query matching +
+ * app layer event */
+static int DetectDnsQueryTest07(void)
+{
+ /* google.com */
+ uint8_t buf1[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
+ 0x00, 0x01, 0x00, 0x01, };
+
+ uint8_t buf2[] = { 0x10, 0x32, /* tx id */
+ 0x81, 0x80|0x40, /* flags: resp, recursion desired, recusion available */
+ 0x00, 0x01, /* 1 query */
+ 0x00, 0x01, /* 1 answer */
+ 0x00, 0x00, 0x00, 0x00, /* no auth rr, additional rr */
+ /* query record */
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, /* name */
+ 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* name cont */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ /* answer */
+ 0xc0, 0x0c, /* ref to name in query above */
+ 0x00, 0x01, 0x00, 0x01, /* type a, class in */
+ 0x00, 0x01, 0x40, 0xef, /* ttl */
+ 0x00, 0x04, /* data len */
+ 0x01, 0x02, 0x03, 0x04 }; /* addr */
+
+ /* google.net */
+ uint8_t buf3[] = { 0x11, 0x33, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
+ 0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
+ 0x00, 0x10, 0x00, 0x01, };
+ int result = 0;
+ Flow f;
+ DNSState *dns_state = NULL;
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+
+ p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p2 = UTHBuildPacketReal(buf2, sizeof(buf2), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+ p3 = UTHBuildPacketReal(buf3, sizeof(buf3), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 53);
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+ f.alproto = ALPROTO_DNS;
+
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->pcap_cnt = 1;
+
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->pcap_cnt = 2;
+
+ p3->flow = &f;
+ p3->flags |= PKT_HAS_FLOW;
+ p3->flowflags |= FLOW_PKT_TOSERVER;
+ p3->pcap_cnt = 3;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.com\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test dns_query option\"; "
+ "dns_query; content:\"google.net\"; nocase; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
+ "(msg:\"Test Z flag event\"; "
+ "app-layer-event:dns.z_flag_set; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf1, sizeof(buf1));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ dns_state = f.alstate;
+ if (dns_state == NULL) {
+ printf("no dns state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("(p1) sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p1, 2)) {
+ printf("(p1) sig 2 did alert, but it should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOCLIENT, buf2, sizeof(buf2));
+ if (r != -1) {
+ printf("toserver client 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("(p2) sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p2, 2)) {
+ printf("(p2) sig 2 alerted, but it should not have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p2, 3))) {
+ printf("(p2) sig 3 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf3, sizeof(buf3));
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p3);
+
+ if (PacketAlertCheck(p3, 1)) {
+ printf("(p3) sig 1 alerted, but it should not have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p3, 2))) {
+ printf("(p3) sig 2 didn't alert, but it should have: ");
+ goto end;
+ }
+ /** \todo should not alert, bug #839
+ if (PacketAlertCheck(p3, 3)) {
+ printf("(p3) sig 3 did alert, but it should not have: ");
+ goto end;
+ }
+ */
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p1);
+ UTHFreePacket(p2);
+ UTHFreePacket(p3);
+ return result;
+}
+
+#endif
+
+static void DetectDnsQueryRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectDnsQueryTest01", DetectDnsQueryTest01, 1);
+ UtRegisterTest("DetectDnsQueryTest02", DetectDnsQueryTest02, 1);
+ UtRegisterTest("DetectDnsQueryTest03 -- tcp", DetectDnsQueryTest03, 1);
+ UtRegisterTest("DetectDnsQueryTest04 -- tcp splicing", DetectDnsQueryTest04, 1);
+ UtRegisterTest("DetectDnsQueryTest05 -- tcp splicing/multi tx", DetectDnsQueryTest05, 1);
+ UtRegisterTest("DetectDnsQueryTest06 -- pcre", DetectDnsQueryTest06, 1);
+ UtRegisterTest("DetectDnsQueryTest07 -- app layer event", DetectDnsQueryTest07, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-dns-query.h b/framework/src/suricata/src/detect-dns-query.h
new file mode 100644
index 00000000..75605231
--- /dev/null
+++ b/framework/src/suricata/src/detect-dns-query.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_DNS_QUERY_H__
+#define __DETECT_DNS_QUERY_H__
+
+#include "app-layer-dns-common.h"
+
+void DetectDnsQueryRegister (void);
+uint32_t DetectDnsQueryInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ DNSState *dns_state, uint8_t flags, void *txv, uint64_t tx_id);
+
+#endif /* __DETECT_DNS_QUERY_H__ */
diff --git a/framework/src/suricata/src/detect-dsize.c b/framework/src/suricata/src/detect-dsize.c
new file mode 100644
index 00000000..9858a5f2
--- /dev/null
+++ b/framework/src/suricata/src/detect-dsize.c
@@ -0,0 +1,838 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the dsize keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+
+#include "detect-dsize.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-byte.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+/**
+ * dsize:[<>]<0-65535>[<><0-65535>];
+ */
+#define PARSE_REGEX "^\\s*(<|>)?\\s*([0-9]{1,5})\\s*(?:(<>)\\s*([0-9]{1,5}))?\\s*$"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectDsizeMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectDsizeSetup (DetectEngineCtx *, Signature *s, char *str);
+void DsizeRegisterTests(void);
+static void DetectDsizeFree(void *);
+
+/**
+ * \brief Registration function for dsize: keyword
+ */
+void DetectDsizeRegister (void)
+{
+ sigmatch_table[DETECT_DSIZE].name = "dsize";
+ sigmatch_table[DETECT_DSIZE].desc = "match on the size of the packet payload";
+ sigmatch_table[DETECT_DSIZE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Dsize";
+ sigmatch_table[DETECT_DSIZE].Match = DetectDsizeMatch;
+ sigmatch_table[DETECT_DSIZE].Setup = DetectDsizeSetup;
+ sigmatch_table[DETECT_DSIZE].Free = DetectDsizeFree;
+ sigmatch_table[DETECT_DSIZE].RegisterTests = DsizeRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE,"pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY,"pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \internal
+ * \brief This function is used to match flags on a packet with those passed via dsize:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the Signature
+ * \param m pointer to the sigmatch
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectDsizeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+ int ret = 0;
+
+ if (PKT_IS_PSEUDOPKT(p)) {
+ SCReturnInt(0);
+ }
+
+ const DetectDsizeData *dd = (const DetectDsizeData *)ctx;
+
+ SCLogDebug("p->payload_len %"PRIu16"", p->payload_len);
+
+ if (dd->mode == DETECTDSIZE_EQ && dd->dsize == p->payload_len)
+ ret = 1;
+ else if (dd->mode == DETECTDSIZE_LT && p->payload_len < dd->dsize)
+ ret = 1;
+ else if (dd->mode == DETECTDSIZE_GT && p->payload_len > dd->dsize)
+ ret = 1;
+ else if (dd->mode == DETECTDSIZE_RA && p->payload_len > dd->dsize && p->payload_len < dd->dsize2)
+ ret = 1;
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse dsize options passed via dsize: keyword
+ *
+ * \param rawstr Pointer to the user provided dsize options
+ *
+ * \retval dd pointer to DetectDsizeData on success
+ * \retval NULL on failure
+ */
+DetectDsizeData *DetectDsizeParse (char *rawstr)
+{
+ DetectDsizeData *dd = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char mode[2] = "";
+ char value1[6] = "";
+ char value2[6] = "";
+ char range[3] = "";
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 5) {
+ SCLogError(SC_ERR_PCRE_MATCH,"Parse error %s", rawstr);
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, mode, sizeof(mode));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("mode \"%s\"", mode);
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, value1, sizeof(value1));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("value1 \"%s\"", value1);
+
+ if (ret > 3) {
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 3, range, sizeof(range));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("range \"%s\"", range);
+
+ if (ret > 4) {
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 4, value2, sizeof(value2));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("value2 \"%s\"", value2);
+ }
+ }
+
+ dd = SCMalloc(sizeof(DetectDsizeData));
+ if (unlikely(dd == NULL))
+ goto error;
+ dd->dsize = 0;
+ dd->dsize2 = 0;
+ dd->mode = DETECTDSIZE_EQ; // default
+
+ if (strlen(mode) > 0) {
+ if (mode[0] == '<')
+ dd->mode = DETECTDSIZE_LT;
+ else if (mode[0] == '>')
+ dd->mode = DETECTDSIZE_GT;
+ else
+ dd->mode = DETECTDSIZE_EQ;
+ }
+
+ if (strcmp("<>", range) == 0) {
+ if (strlen(mode) != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Range specified but mode also set");
+ goto error;
+ }
+ dd->mode = DETECTDSIZE_RA;
+ }
+
+ /** set the first dsize value */
+ if (ByteExtractStringUint16(&dd->dsize,10,strlen(value1),value1) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid size value1:\"%s\"", value1);
+ goto error;
+ }
+
+ /** set the second dsize value if specified */
+ if (strlen(value2) > 0) {
+ if (dd->mode != DETECTDSIZE_RA) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Multiple dsize values specified but mode is not range");
+ goto error;
+ }
+
+ if (ByteExtractStringUint16(&dd->dsize2,10,strlen(value2),value2) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Invalid size value2:\"%s\"",value2);
+ goto error;
+ }
+
+ if (dd->dsize2 <= dd->dsize) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"dsize2:%"PRIu16" <= dsize:%"PRIu16"",dd->dsize2,dd->dsize);
+ goto error;
+ }
+ }
+
+ SCLogDebug("dsize parsed successfully dsize: %"PRIu16" dsize2: %"PRIu16"",dd->dsize,dd->dsize2);
+ return dd;
+
+error:
+ if (dd)
+ SCFree(dd);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed dsize into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided flags options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectDsizeSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectDsizeData *dd = NULL;
+ SigMatch *sm = NULL;
+
+ if (SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DSIZE,
+ s->sm_lists_tail[DETECT_SM_LIST_MATCH]) != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Can't use 2 or more dsizes in "
+ "the same sig. Invalidating signature.");
+ goto error;
+ }
+
+ SCLogDebug("\'%s\'", rawstr);
+
+ dd = DetectDsizeParse(rawstr);
+ if (dd == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Parsing \'%s\' failed", rawstr);
+ goto error;
+ }
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL){
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for SigMatch");
+ SCFree(dd);
+ goto error;
+ }
+
+ sm->type = DETECT_DSIZE;
+ sm->ctx = (SigMatchCtx *)dd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ SCLogDebug("dd->dsize %"PRIu16", dd->dsize2 %"PRIu16", dd->mode %"PRIu8"",
+ dd->dsize, dd->dsize2, dd->mode);
+ /* tell the sig it has a dsize to speed up engine init */
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+ s->flags |= SIG_FLAG_DSIZE;
+
+ if (s->dsize_sm == NULL) {
+ s->dsize_sm = sm;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectDsizeData
+ *
+ * \param de pointer to DetectDsizeData
+ */
+void DetectDsizeFree(void *de_ptr)
+{
+ DetectDsizeData *dd = (DetectDsizeData *)de_ptr;
+ if(dd) SCFree(dd);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+#include "detect.h"
+#include "detect-engine.h"
+/**
+ * \test this is a test for a valid dsize value 1
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse01 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test this is a test for a valid dsize value >10
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse02 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(">10");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test this is a test for a valid dsize value <100
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse03 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("<100");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test this is a test for a valid dsize value 1<>2
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse04 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1<>2");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test this is a test for a valid dsize value 1
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse05 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1");
+ if (dd) {
+ if (dd->dsize == 1)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is a test for a valid dsize value >10
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse06 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(">10");
+ if (dd) {
+ if (dd->dsize == 10 && dd->mode == DETECTDSIZE_GT)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is a test for a valid dsize value <100
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse07 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("<100");
+ if (dd) {
+ if (dd->dsize == 100 && dd->mode == DETECTDSIZE_LT)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is a test for a valid dsize value 1<>2
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse08 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1<>2");
+ if (dd) {
+ if (dd->dsize == 1 && dd->dsize2 == 2 && dd->mode == DETECTDSIZE_RA)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is a test for a invalid dsize value A
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse09 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("A");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a invalid dsize value >10<>10
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse10 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(">10<>10");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a invalid dsize value <>10
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse11 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("<>10");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a invalid dsize value 1<>
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse12 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1<>");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a valid dsize value 1
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse13 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("1");
+ if (dd) {
+ if (dd->dsize2 == 0)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is a test for a invalid dsize value ""
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse14 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a invalid dsize value " "
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse15 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(" ");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a invalid dsize value 2<>1
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse16 (void)
+{
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("2<>1");
+ if (dd) {
+ DetectDsizeFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test this is a test for a valid dsize value 1 <> 2
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse17 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(" 1 <> 2 ");
+ if (dd) {
+ if (dd->dsize == 1 && dd->dsize2 == 2 && dd->mode == DETECTDSIZE_RA)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test this is test for a valid dsize value > 2
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse18 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("> 2 ");
+ if (dd) {
+ if (dd->dsize == 2 && dd->mode == DETECTDSIZE_GT)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test test for a valid dsize value < 12
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse19 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse("< 12 ");
+ if (dd) {
+ if (dd->dsize == 12 && dd->mode == DETECTDSIZE_LT)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test test for a valid dsize value 12
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int DsizeTestParse20 (void)
+{
+ int result = 0;
+ DetectDsizeData *dd = NULL;
+ dd = DetectDsizeParse(" 12 ");
+ if (dd) {
+ if (dd->dsize == 12 && dd->mode == DETECTDSIZE_EQ)
+ result = 1;
+
+ DetectDsizeFree(dd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectDsizeIcmpv6Test01 is a test for checking the working of
+ * dsize keyword by creating 2 rules and matching a crafted packet
+ * against them. Only the first one shall trigger.
+ */
+int DetectDsizeIcmpv6Test01 (void)
+{
+ int result = 0;
+
+ static uint8_t raw_icmpv6[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x7b, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x4b, 0xe8, 0xbd, 0x00, 0x00, 0x3b, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV6Hdr ip6h;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ip6h, 0, sizeof(IPV6Hdr));
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+ p->src.family = AF_INET6;
+ p->dst.family = AF_INET6;
+ p->ip6h = &ip6h;
+
+ DecodeIPV6(&tv, &dtv, p, raw_icmpv6, sizeof(raw_icmpv6), NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(msg:\"ICMP Large ICMP Packet\"; dsize:>8; sid:1; rev:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(msg:\"ICMP Large ICMP Packet\"; dsize:>800; sid:2; rev:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+end:
+ SCFree(p);
+ return result;
+
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for dsize
+ */
+void DsizeRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DsizeTestParse01", DsizeTestParse01, 1);
+ UtRegisterTest("DsizeTestParse02", DsizeTestParse02, 1);
+ UtRegisterTest("DsizeTestParse03", DsizeTestParse03, 1);
+ UtRegisterTest("DsizeTestParse04", DsizeTestParse04, 1);
+ UtRegisterTest("DsizeTestParse05", DsizeTestParse05, 1);
+ UtRegisterTest("DsizeTestParse06", DsizeTestParse06, 1);
+ UtRegisterTest("DsizeTestParse07", DsizeTestParse07, 1);
+ UtRegisterTest("DsizeTestParse08", DsizeTestParse08, 1);
+ UtRegisterTest("DsizeTestParse09", DsizeTestParse09, 1);
+ UtRegisterTest("DsizeTestParse10", DsizeTestParse10, 1);
+ UtRegisterTest("DsizeTestParse11", DsizeTestParse11, 1);
+ UtRegisterTest("DsizeTestParse12", DsizeTestParse12, 1);
+ UtRegisterTest("DsizeTestParse13", DsizeTestParse13, 1);
+ UtRegisterTest("DsizeTestParse14", DsizeTestParse14, 1);
+ UtRegisterTest("DsizeTestParse15", DsizeTestParse15, 1);
+ UtRegisterTest("DsizeTestParse16", DsizeTestParse16, 1);
+ UtRegisterTest("DsizeTestParse17", DsizeTestParse17, 1);
+ UtRegisterTest("DsizeTestParse18", DsizeTestParse18, 1);
+ UtRegisterTest("DsizeTestParse19", DsizeTestParse19, 1);
+ UtRegisterTest("DsizeTestParse20", DsizeTestParse20, 1);
+
+ UtRegisterTest("DetectDsizeIcmpv6Test01", DetectDsizeIcmpv6Test01, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-dsize.h b/framework/src/suricata/src/detect-dsize.h
new file mode 100644
index 00000000..c0385dbc
--- /dev/null
+++ b/framework/src/suricata/src/detect-dsize.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_DSIZE_H__
+#define __DETECT_DSIZE_H__
+
+#define DETECTDSIZE_LT 0
+#define DETECTDSIZE_EQ 1
+#define DETECTDSIZE_GT 2
+#define DETECTDSIZE_RA 3
+
+typedef struct DetectDsizeData_ {
+ uint16_t dsize;
+ uint16_t dsize2;
+ uint8_t mode;
+} DetectDsizeData;
+
+/* prototypes */
+void DetectDsizeRegister (void);
+
+#endif /* __DETECT_DSIZE_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-address-ipv4.c b/framework/src/suricata/src/detect-engine-address-ipv4.c
new file mode 100644
index 00000000..20e9e57e
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address-ipv4.c
@@ -0,0 +1,1614 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * IPV4 Address part of the detection engine.
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "flow-var.h"
+
+#include "util-cidr.h"
+#include "util-unittest.h"
+
+#include "detect-engine-address.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-port.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+/**
+ * \brief Compares 2 addresses(address ranges) and returns the relationship
+ * between the 2 addresses.
+ *
+ * \param a Pointer to the first address instance to be compared.
+ * \param b Pointer to the second address instance to be compared.
+ *
+ * \retval ADDRESS_EQ If the 2 address ranges a and b, are equal.
+ * \retval ADDRESS_ES b encapsulates a. b_ip1[...a_ip1...a_ip2...]b_ip2.
+ * \retval ADDRESS_EB a encapsulates b. a_ip1[...b_ip1....b_ip2...]a_ip2.
+ * \retval ADDRESS_LE a_ip1(...b_ip1==a_ip2...)b_ip2
+ * \retval ADDRESS_LT a_ip1(...b_ip1...a_ip2...)b_ip2
+ * \retval ADDRESS_GE b_ip1(...a_ip1==b_ip2...)a_ip2
+ * \retval ADDRESS_GT a_ip1 > b_ip2, i.e. the address range for 'a' starts only
+ * after the end of the address range for 'b'
+ */
+int DetectAddressCmpIPv4(DetectAddress *a, DetectAddress *b)
+{
+ uint32_t a_ip1 = ntohl(a->ip.addr_data32[0]);
+ uint32_t a_ip2 = ntohl(a->ip2.addr_data32[0]);
+ uint32_t b_ip1 = ntohl(b->ip.addr_data32[0]);
+ uint32_t b_ip2 = ntohl(b->ip2.addr_data32[0]);
+
+ if (a_ip1 == b_ip1 && a_ip2 == b_ip2) {
+ SCLogDebug("ADDRESS_EQ");
+ return ADDRESS_EQ;
+ } else if (a_ip1 >= b_ip1 && a_ip1 <= b_ip2 && a_ip2 <= b_ip2) {
+ SCLogDebug("ADDRESS_ES");
+ return ADDRESS_ES;
+ } else if (a_ip1 <= b_ip1 && a_ip2 >= b_ip2) {
+ SCLogDebug("ADDRESS_EB");
+ return ADDRESS_EB;
+ } else if (a_ip1 < b_ip1 && a_ip2 < b_ip2 && a_ip2 >= b_ip1) {
+ SCLogDebug("ADDRESS_LE");
+ return ADDRESS_LE;
+ } else if (a_ip1 < b_ip1 && a_ip2 < b_ip2) {
+ SCLogDebug("ADDRESS_LT");
+ return ADDRESS_LT;
+ } else if (a_ip1 > b_ip1 && a_ip1 <= b_ip2 && a_ip2 > b_ip2) {
+ SCLogDebug("ADDRESS_GE");
+ return ADDRESS_GE;
+ } else if (a_ip1 > b_ip2) {
+ SCLogDebug("ADDRESS_GT");
+ return ADDRESS_GT;
+ } else {
+ /* should be unreachable */
+ SCLogDebug("Internal Error: should be unreachable");
+ }
+
+ return ADDRESS_ER;
+}
+
+/**
+ * \brief Cut groups and merge sigs
+ *
+ * a = 1.2.3.4, b = 1.2.3.4-1.2.3.5
+ * must result in: a == 1.2.3.4, b == 1.2.3.5, c == NULL
+ *
+ * a = 1.2.3.4, b = 1.2.3.3-1.2.3.5
+ * must result in: a == 1.2.3.3, b == 1.2.3.4, c == 1.2.3.5
+ *
+ * a = 1.2.3.0/24 b = 1.2.3.128-1.2.4.10
+ * must result in: a == 1.2.3.0/24, b == 1.2.4.0-1.2.4.10, c == NULL
+ *
+ * a = 1.2.3.4, b = 1.2.3.0/24
+ * must result in: a == 1.2.3.0-1.2.3.3, b == 1.2.3.4, c == 1.2.3.5-1.2.3.255
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressCutIPv4(DetectEngineCtx *de_ctx, DetectAddress *a,
+ DetectAddress *b, DetectAddress **c)
+{
+ uint32_t a_ip1 = ntohl(a->ip.addr_data32[0]);
+ uint32_t a_ip2 = ntohl(a->ip2.addr_data32[0]);
+ uint32_t b_ip1 = ntohl(b->ip.addr_data32[0]);
+ uint32_t b_ip2 = ntohl(b->ip2.addr_data32[0]);
+ DetectPort *port = NULL;
+ DetectAddress *tmp = NULL;
+ DetectAddress *tmp_c = NULL;
+ int r = 0;
+
+ /* default to NULL */
+ *c = NULL;
+
+ r = DetectAddressCmpIPv4(a, b);
+ if (r != ADDRESS_ES && r != ADDRESS_EB && r != ADDRESS_LE && r != ADDRESS_GE) {
+ SCLogDebug("we shouldn't be here");
+ goto error;
+ }
+
+ /* get a place to temporary put sigs lists */
+ tmp = DetectAddressInit();
+ if (tmp == NULL)
+ goto error;
+
+ /* we have 3 parts: [aaa[abab)bbb]
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ if (r == ADDRESS_LE) {
+ SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_LE");
+
+ a->ip.addr_data32[0] = htonl(a_ip1);
+ a->ip2.addr_data32[0] = htonl(b_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(b_ip1);
+ b->ip2.addr_data32[0] = htonl(a_ip2);
+
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->ip.family = AF_INET;
+ tmp_c->ip.addr_data32[0] = htonl(a_ip2 + 1);
+ tmp_c->ip2.addr_data32[0] = htonl(b_ip2);
+ *c = tmp_c;
+
+ if (de_ctx != NULL) {
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp_c->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp_c->port, port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+
+ tmp_c->cnt += b->cnt;
+ b->cnt += a->cnt;
+ }
+
+ /* we have 3 parts: [bbb[baba]aaa]
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_GE) {
+ SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_GE");
+
+ a->ip.addr_data32[0] = htonl(b_ip1);
+ a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(a_ip1);
+ b->ip2.addr_data32[0] = htonl(b_ip2);
+
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->ip.family = AF_INET;
+ tmp_c->ip.addr_data32[0] = htonl(b_ip2 + 1);
+ tmp_c->ip2.addr_data32[0] = htonl(a_ip2);
+ *c = tmp_c;
+
+ if (de_ctx != NULL) {
+ /* 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'a' sigs */
+ /* store old a list */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp->sh);
+ /* clean a list */
+ SigGroupHeadClearSigs(a->sh);
+ /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &tmp_c->sh);
+ /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+ /* prepend old a before b */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &b->sh);
+ /* clean tmp list */
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &a->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp_c->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += tmp->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[abab]bbb] or [bbb[baba]]
+ * part a: a_ip1 <-> a_ip2
+ * part b: a_ip2 + 1 <-> b_ip2
+ *
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ *
+ * 3 part [bbb[aaa]bbb]
+ * becomes[aaa[bbb]ccc]
+ *
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ } else if (r == ADDRESS_ES) {
+ SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_ES");
+
+ if (a_ip1 == b_ip1) {
+ SCLogDebug("DetectAddressCutIPv4: 1");
+
+ a->ip.addr_data32[0] = htonl(a_ip1);
+ a->ip2.addr_data32[0] = htonl(a_ip2);
+
+ b->ip.addr_data32[0] = htonl(a_ip2 + 1);
+ b->ip2.addr_data32[0] = htonl(b_ip2);
+
+ if (de_ctx != NULL) {
+ /* 'b' overlaps 'a' so 'a' needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &a->port, port);
+ a->cnt += b->cnt;
+ }
+ } else if (a_ip2 == b_ip2) {
+ SCLogDebug("DetectAddressCutIPv4: 2");
+
+ a->ip.addr_data32[0] = htonl(b_ip1);
+ a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(a_ip1);
+ b->ip2.addr_data32[0] = htonl(a_ip2);
+
+ if (de_ctx != NULL) {
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadClearSigs(a->sh);
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &a->sh);
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp->port, a->port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &a->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+ } else {
+ SCLogDebug("3");
+
+ a->ip.addr_data32[0] = htonl(b_ip1);
+ a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(a_ip1);
+ b->ip2.addr_data32[0] = htonl(a_ip2);
+
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->ip.family = AF_INET;
+ tmp_c->ip.addr_data32[0] = htonl(a_ip2 + 1);
+ tmp_c->ip2.addr_data32[0] = htonl(b_ip2);
+ *c = tmp_c;
+
+ if (de_ctx != NULL) {
+ /* 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'b' sigs */
+ /* store old a list */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp->sh);
+ /* clean a list */
+ SigGroupHeadClearSigs(a->sh);
+ /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp_c->sh);
+ /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+ /* prepend old a before b */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &b->sh);
+ /* clean tmp list */
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp_c->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &a->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += b->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+ }
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[baba]aaa] or [aaa[abab]]
+ * part a: b_ip1 <-> b_ip2
+ * part b: b_ip2 + 1 <-> a_ip2
+ *
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> b_ip2
+ *
+ * 3 part [aaa[bbb]aaa]
+ * becomes[aaa[bbb]ccc]
+ *
+ * part a: a_ip1 <-> b_ip2 - 1
+ * part b: b_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_EB) {
+ SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_EB");
+
+ if (a_ip1 == b_ip1) {
+ SCLogDebug("DetectAddressCutIPv4: 1");
+
+ a->ip.addr_data32[0] = htonl(b_ip1);
+ a->ip2.addr_data32[0] = htonl(b_ip2);
+
+ b->ip.addr_data32[0] = htonl(b_ip2 + 1);
+ b->ip2.addr_data32[0] = htonl(a_ip2);
+
+ if (de_ctx != NULL) {
+ /* 'b' overlaps 'a' so a needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp->sh);
+ SigGroupHeadClearSigs(b->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &a->sh);
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp->port, b->port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &a->port, port);
+
+ tmp->cnt += b->cnt;
+ b->cnt = 0;
+ b->cnt += a->cnt;
+ a->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+ } else if (a_ip2 == b_ip2) {
+ SCLogDebug("DetectAddressCutIPv4: 2");
+
+ a->ip.addr_data32[0] = htonl(a_ip1);
+ a->ip2.addr_data32[0] = htonl(b_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(b_ip1);
+ b->ip2.addr_data32[0] = htonl(b_ip2);
+
+ if (de_ctx != NULL) {
+ /* 'a' overlaps 'b' so a needs the 'a' sigs */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+
+ b->cnt += a->cnt;
+ }
+ } else {
+ SCLogDebug("DetectAddressCutIPv4: 3");
+
+ a->ip.addr_data32[0] = htonl(a_ip1);
+ a->ip2.addr_data32[0] = htonl(b_ip1 - 1);
+
+ b->ip.addr_data32[0] = htonl(b_ip1);
+ b->ip2.addr_data32[0] = htonl(b_ip2);
+
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->ip.family = AF_INET;
+ tmp_c->ip.addr_data32[0] = htonl(b_ip2 + 1);
+ tmp_c->ip2.addr_data32[0] = htonl(a_ip2);
+ *c = tmp_c;
+
+ if (de_ctx != NULL) {
+ /* 'a' stays the same wrt sigs
+ * 'b' keeps it's own sigs and gets a's sigs prepended
+ * 'c' gets 'a' sigs */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp_c->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp_c->port, port);
+
+ b->cnt += a->cnt;
+ tmp_c->cnt += a->cnt;
+ }
+ }
+ }
+
+ if (tmp != NULL)
+ DetectAddressFree(tmp);
+
+ return 0;
+
+error:
+ if (tmp != NULL)
+ DetectAddressFree(tmp);
+ return -1;
+}
+
+/**
+ * \brief Check if the address group list covers the complete IPv4 IP space.
+ *
+ * \param ag Pointer to a DetectAddress list head, which has to be checked to
+ * see if the address ranges in it, cover the entire IPv4 IP space.
+ *
+ * \retval 1 Yes, it covers the entire IPv4 address range.
+ * \retval 0 No, it doesn't cover the entire IPv4 address range.
+ */
+int DetectAddressIsCompleteIPSpaceIPv4(DetectAddress *ag)
+{
+ uint32_t next_ip = 0;
+
+ if (ag == NULL)
+ return 0;
+
+ /* if we don't start with 0.0.0.0 we know we're good */
+ if (ntohl(ag->ip.addr_data32[0]) != 0x00000000)
+ return 0;
+
+ /* if we're ending with 255.255.255.255 while we know we started with
+ * 0.0.0.0 it's the complete space */
+ if (ntohl(ag->ip2.addr_data32[0]) == 0xFFFFFFFF)
+ return 1;
+
+ next_ip = htonl(ntohl(ag->ip2.addr_data32[0]) + 1);
+ ag = ag->next;
+
+ for ( ; ag != NULL; ag = ag->next) {
+
+ if (ag->ip.addr_data32[0] != next_ip)
+ return 0;
+
+ if (ntohl(ag->ip2.addr_data32[0]) == 0xFFFFFFFF)
+ return 1;
+
+ next_ip = htonl(ntohl(ag->ip2.addr_data32[0]) + 1);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Cuts and returns an address range, which is the complement of the
+ * address range that is supplied as the argument.
+ *
+ * For example:
+ *
+ * If a = 0.0.0.0-1.2.3.4,
+ * then a = 1.2.3.4-255.255.255.255 and b = NULL
+ * If a = 1.2.3.4-255.255.255.255,
+ * then a = 0.0.0.0-1.2.3.4 and b = NULL
+ * If a = 1.2.3.4-192.168.1.1,
+ * then a = 0.0.0.0-1.2.3.3 and b = 192.168.1.2-255.255.255.255
+ *
+ * \param a Pointer to an address range (DetectAddress) instance whose complement
+ * has to be returned in a and b.
+ * \param b Pointer to DetectAddress pointer, that will be supplied back with a
+ * new DetectAddress instance, if the complement demands so.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressCutNotIPv4(DetectAddress *a, DetectAddress **b)
+{
+ uint32_t a_ip1 = ntohl(a->ip.addr_data32[0]);
+ uint32_t a_ip2 = ntohl(a->ip2.addr_data32[0]);
+ DetectAddress *tmp_b = NULL;
+
+ /* default to NULL */
+ *b = NULL;
+
+ if (a_ip1 != 0x00000000 && a_ip2 != 0xFFFFFFFF) {
+ a->ip.addr_data32[0] = htonl(0x00000000);
+ a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
+
+ tmp_b = DetectAddressInit();
+ if (tmp_b == NULL)
+ goto error;
+
+ tmp_b->ip.family = AF_INET;
+ tmp_b->ip.addr_data32[0] = htonl(a_ip2 + 1);
+ tmp_b->ip2.addr_data32[0] = htonl(0xFFFFFFFF);
+ *b = tmp_b;
+ } else if (a_ip1 == 0x00000000 && a_ip2 != 0xFFFFFFFF) {
+ a->ip.addr_data32[0] = htonl(a_ip2 + 1);
+ a->ip2.addr_data32[0] = htonl(0xFFFFFFFF);
+ } else if (a_ip1 != 0x00000000 && a_ip2 == 0xFFFFFFFF) {
+ a->ip.addr_data32[0] = htonl(0x00000000);
+ a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
+ } else {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Extends a target address range if the the source address range is
+ * wider than the target address range on either sides.
+ *
+ * Every address is a range, i.e. address->ip1....address->ip2. For
+ * example 1.2.3.4 to 192.168.1.1.
+ * if source->ip1 is smaller than target->ip1, it indicates that the
+ * source's left address limit is greater(range wise) than the target's
+ * left address limit, and hence we reassign the target's left address
+ * limit to source's left address limit.
+ * Similary if source->ip2 is greater than target->ip2, it indicates that
+ * the source's right address limit is greater(range wise) than the
+ * target's right address limit, and hence we reassign the target's right
+ * address limit to source's right address limit.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param target Pointer to the target DetectAddress instance that has to be
+ * updated.
+ * \param source Pointer to the source DetectAddress instance that is used
+ * to decided whether we extend the target's address range.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressJoinIPv4(DetectEngineCtx *de_ctx, DetectAddress *target,
+ DetectAddress *source)
+{
+ if (source == NULL || target == NULL)
+ return -1;
+
+ if (ntohl(source->ip.addr_data32[0]) < ntohl(target->ip.addr_data32[0]))
+ target->ip.addr_data32[0] = source->ip.addr_data32[0];
+
+ if (ntohl(source->ip2.addr_data32[0]) > ntohl(target->ip2.addr_data32[0]))
+ target->ip2.addr_data32[0] = source->ip2.addr_data32[0];
+
+ return 0;
+}
+
+/********************************Unittests*************************************/
+
+#ifdef UNITTESTS
+
+static int DetectAddressIPv4TestAddressCmp01(void)
+{
+ struct in_addr in;
+ int result = 1;
+
+ DetectAddress *a = DetectAddressInit();
+ if (a == NULL)
+ return 0;
+
+ DetectAddress *b = DetectAddressInit();
+ if (b == NULL) {
+ DetectAddressFree(a);
+ return 0;
+ }
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_EQ);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_ES);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_EB);
+
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LE);
+
+ if (inet_pton(AF_INET, "170.170.170.169", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET, "170.170.170.169", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LE);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "185.185.185.185", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ /* we could get a LE */
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ /* we could get a LE */
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "128.128.128.128", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.169", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "180.180.180.180", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "170.170.170.169", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.169.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "200.200.200.200", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "185.185.185.185", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) == ADDRESS_GT);
+
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "200.200.200.200", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_GT);
+
+ if (inet_pton(AF_INET, "182.168.1.2", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "200.200.200.200", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "170.170.170.170", &in) < 0)
+ goto error;
+ b->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ b->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCmpIPv4(a, b) != ADDRESS_GT);
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4IsCompleteIPSpace02(void)
+{
+ DetectAddress *a = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 1);
+
+ if (inet_pton(AF_INET, "0.0.0.1", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ DetectAddressFree(a);
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.254", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ DetectAddressFree(a);
+
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ return 0;
+}
+
+static int DetectAddressIPv4IsCompleteIPSpace03(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *temp = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ temp = a;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "126.36.62.61", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "126.36.62.62", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "222.52.21.62", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "222.52.21.63", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.254", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 1);
+
+ DetectAddressFree(a);
+
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ return 0;
+}
+
+static int DetectAddressIPv4IsCompleteIPSpace04(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *temp = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ temp = a;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "126.36.62.61", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "126.36.62.62", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "222.52.21.62", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "222.52.21.64", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.254", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ if ( (temp->next = DetectAddressInit()) == NULL)
+ goto error;
+ temp = temp->next;
+
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ temp->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ temp->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressIsCompleteIPSpaceIPv4(a) == 0);
+
+ DetectAddressFree(a);
+
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ return 0;
+}
+
+static int DetectAddressIPv4CutNot05(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCutNotIPv4(a, &b) == -1);
+
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4CutNot06(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCutNotIPv4(a, &b) == 0);
+
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ result = (a->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ result &= (a->ip2.addr_data32[0] = in.s_addr);
+
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4CutNot07(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCutNotIPv4(a, &b) == 0);
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ result = (a->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ result &= (a->ip2.addr_data32[0] = in.s_addr);
+
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4CutNot08(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCutNotIPv4(a, &b) == 0);
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ result &= (a->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ result &= (a->ip2.addr_data32[0] = in.s_addr);
+
+ if (b == NULL) {
+ result = 0;
+ goto error;
+ } else {
+ result &= 1;
+ }
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ result &= (b->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ result &= (b->ip2.addr_data32[0] = in.s_addr);
+
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4CutNot09(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in_addr in;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ a->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ a->ip2.addr_data32[0] = in.s_addr;
+ result &= (DetectAddressCutNotIPv4(a, &b) == 0);
+
+ if (inet_pton(AF_INET, "0.0.0.0", &in) < 0)
+ goto error;
+ result &= (a->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "1.2.3.3", &in) < 0)
+ goto error;
+ result &= (a->ip2.addr_data32[0] = in.s_addr);
+
+ if (b == NULL) {
+ result = 0;
+ goto error;
+ } else {
+ result &= 1;
+ }
+ if (inet_pton(AF_INET, "192.168.1.3", &in) < 0)
+ goto error;
+ result &= (b->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "255.255.255.255", &in) < 0)
+ goto error;
+ result &= (b->ip2.addr_data32[0] = in.s_addr);
+
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int DetectAddressIPv4Join10(void)
+{
+ struct in_addr in;
+ int result = 1;
+
+ DetectAddress *source = DetectAddressInit();
+ if (source == NULL)
+ return 0;
+
+ DetectAddress *target = DetectAddressInit();
+ if (target == NULL) {
+ DetectAddressFree(source);
+ return 0;
+ }
+
+ if (inet_pton(AF_INET, "128.51.61.124", &in) < 0)
+ goto error;
+ target->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ target->ip2.addr_data32[0] = in.s_addr;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ source->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ source->ip2.addr_data32[0] = in.s_addr;
+
+ result &= (DetectAddressJoinIPv4(NULL, target, source) == 0);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ result &= (target->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ result &= (target->ip2.addr_data32[0] == in.s_addr);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ target->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ target->ip2.addr_data32[0] = in.s_addr;
+
+ if (inet_pton(AF_INET, "1.2.3.5", &in) < 0)
+ goto error;
+ source->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.1", &in) < 0)
+ goto error;
+ source->ip2.addr_data32[0] = in.s_addr;
+
+ result &= (DetectAddressJoinIPv4(NULL, target, source) == 0);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ result &= (target->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ result &= (target->ip2.addr_data32[0] == in.s_addr);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ target->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ target->ip2.addr_data32[0] = in.s_addr;
+
+ if (inet_pton(AF_INET, "128.1.5.15", &in) < 0)
+ goto error;
+ source->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "200.202.200.200", &in) < 0)
+ goto error;
+ source->ip2.addr_data32[0] = in.s_addr;
+
+ result &= (DetectAddressJoinIPv4(NULL, target, source) == 0);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ result &= (target->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "200.202.200.200", &in) < 0)
+ goto error;
+ result &= (target->ip2.addr_data32[0] == in.s_addr);
+
+ if (inet_pton(AF_INET, "128.51.61.124", &in) < 0)
+ goto error;
+ target->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ target->ip2.addr_data32[0] = in.s_addr;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ source->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ source->ip2.addr_data32[0] = in.s_addr;
+
+ result &= (DetectAddressJoinIPv4(NULL, target, source) == 0);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ result &= (target->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ result &= (target->ip2.addr_data32[0] == in.s_addr);
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ target->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ target->ip2.addr_data32[0] = in.s_addr;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ source->ip.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ source->ip2.addr_data32[0] = in.s_addr;
+
+ result &= (DetectAddressJoinIPv4(NULL, target, source) == 0);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) < 0)
+ goto error;
+ result &= (target->ip.addr_data32[0] == in.s_addr);
+ if (inet_pton(AF_INET, "192.168.1.2", &in) < 0)
+ goto error;
+ result &= (target->ip2.addr_data32[0] == in.s_addr);
+
+ DetectAddressFree(source);
+ DetectAddressFree(target);
+ return result;
+
+ error:
+ DetectAddressFree(source);
+ DetectAddressFree(target);
+ return 0;
+}
+
+#endif
+
+void DetectAddressIPv4Tests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectAddressIPv4TestAddressCmp01",
+ DetectAddressIPv4TestAddressCmp01, 1);
+ UtRegisterTest("DetectAddressIPv4IsCompleteIPSpace02",
+ DetectAddressIPv4IsCompleteIPSpace02, 1);
+ UtRegisterTest("DetectAddressIPv4IsCompleteIPSpace03",
+ DetectAddressIPv4IsCompleteIPSpace03, 1);
+ UtRegisterTest("DetectAddressIPv4IsCompleteIPSpace04",
+ DetectAddressIPv4IsCompleteIPSpace04, 1);
+ UtRegisterTest("DetectAddressIPv4CutNot05", DetectAddressIPv4CutNot05, 1);
+ UtRegisterTest("DetectAddressIPv4CutNot06", DetectAddressIPv4CutNot06, 1);
+ UtRegisterTest("DetectAddressIPv4CutNot07", DetectAddressIPv4CutNot07, 1);
+ UtRegisterTest("DetectAddressIPv4CutNot08", DetectAddressIPv4CutNot08, 1);
+ UtRegisterTest("DetectAddressIPv4CutNot09", DetectAddressIPv4CutNot09, 1);
+ UtRegisterTest("DetectAddressIPv4Join10", DetectAddressIPv4Join10, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-engine-address-ipv4.h b/framework/src/suricata/src/detect-engine-address-ipv4.h
new file mode 100644
index 00000000..b8b7b344
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address-ipv4.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_ADDRESS_IPV4_H__
+#define __DETECT_ENGINE_ADDRESS_IPV4_H__
+
+int DetectAddressCutNotIPv4(DetectAddress *, DetectAddress **);
+int DetectAddressCmpIPv4(DetectAddress *a, DetectAddress *b);
+
+int DetectAddressCutIPv4(DetectEngineCtx *, DetectAddress *,
+ DetectAddress *, DetectAddress **);
+int DetectAddressJoinIPv4(DetectEngineCtx *, DetectAddress *target,
+ DetectAddress *source);
+int DetectAddressIsCompleteIPSpaceIPv4(DetectAddress *);
+
+void DetectAddressIPv4Tests(void);
+
+#endif /* __DETECT_ENGINE_ADDRESS_IPV4_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-address-ipv6.c b/framework/src/suricata/src/detect-engine-address-ipv6.c
new file mode 100644
index 00000000..84a04d59
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address-ipv6.c
@@ -0,0 +1,2279 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * IPV6 Address part of the detection engine.
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "flow-var.h"
+
+#include "util-cidr.h"
+#include "util-unittest.h"
+
+#include "detect-engine-address.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-port.h"
+
+#include "util-debug.h"
+
+/**
+ * \brief Compares 2 ipv6 addresses and returns if the first address(a) is less
+ * than the second address(b) or not.
+ *
+ * \param a The first ipv6 address to be compared.
+ * \param b The second ipv6 address to be compared.
+ *
+ * \retval 1 If a < b.
+ * \retval 0 Otherwise, i.e. a >= b.
+ */
+int AddressIPv6Lt(Address *a, Address *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a->addr_data32[i]) < ntohl(b->addr_data32[i]))
+ return 1;
+ if (ntohl(a->addr_data32[i]) > ntohl(b->addr_data32[i]))
+ break;
+ }
+
+ return 0;
+}
+
+int AddressIPv6LtU32(uint32_t *a, uint32_t *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a[i]) < ntohl(b[i]))
+ return 1;
+ if (ntohl(a[i]) > ntohl(b[i]))
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Compares 2 ipv6 addresses and returns if the first address(a) is
+ * greater than the second address(b) or not.
+ *
+ * \param a The first ipv6 address to be compared.
+ * \param b The second ipv6 address to be compared.
+ *
+ * \retval 1 If a > b.
+ * \retval 0 Otherwise, i.e. a <= b.
+ */
+int AddressIPv6Gt(Address *a, Address *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a->addr_data32[i]) > ntohl(b->addr_data32[i]))
+ return 1;
+ if (ntohl(a->addr_data32[i]) < ntohl(b->addr_data32[i]))
+ break;
+ }
+
+ return 0;
+}
+
+int AddressIPv6GtU32(uint32_t *a, uint32_t *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a[i]) > ntohl(b[i]))
+ return 1;
+ if (ntohl(a[i]) < ntohl(b[i]))
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Compares 2 ipv6 addresses and returns if the addresses are equal
+ * or not.
+ *
+ * \param a The first ipv6 address to be compared.
+ * \param b The second ipv6 address to be compared.
+ *
+ * \retval 1 If a == b.
+ * \retval 0 Otherwise.
+ */
+int AddressIPv6Eq(Address *a, Address *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (a->addr_data32[i] != b->addr_data32[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+int AddressIPv6EqU32(uint32_t *a, uint32_t *b)
+{
+ int i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (a[i] != b[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Compares 2 ipv6 addresses and returns if the first address(a) is less
+ * than or equal to the second address(b) or not.
+ *
+ * \param a The first ipv6 address to be compared.
+ * \param b The second ipv6 address to be compared.
+ *
+ * \retval 1 If a <= b.
+ * \retval 0 Otherwise, i.e. a > b.
+ */
+int AddressIPv6Le(Address *a, Address *b)
+{
+
+ if (AddressIPv6Eq(a, b) == 1)
+ return 1;
+ if (AddressIPv6Lt(a, b) == 1)
+ return 1;
+
+ return 0;
+}
+
+int AddressIPv6LeU32(uint32_t *a, uint32_t *b)
+{
+
+ if (AddressIPv6EqU32(a, b) == 1)
+ return 1;
+ if (AddressIPv6LtU32(a, b) == 1)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Compares 2 ipv6 addresses and returns if the first address(a) is
+ * greater than or equal to the second address(b) or not.
+ *
+ * \param a The first ipv6 address to be compared.
+ * \param b The second ipv6 address to be compared.
+ *
+ * \retval 1 If a >= b.
+ * \retval 0 Otherwise, i.e. a < b.
+ */
+int AddressIPv6Ge(Address *a, Address *b)
+{
+
+ if (AddressIPv6Eq(a, b) == 1)
+ return 1;
+ if (AddressIPv6Gt(a, b) == 1)
+ return 1;
+
+ return 0;
+}
+
+int AddressIPv6GeU32(uint32_t *a, uint32_t *b)
+{
+
+ if (AddressIPv6EqU32(a, b) == 1)
+ return 1;
+ if (AddressIPv6GtU32(a, b) == 1)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Compares 2 addresses(address ranges) and returns the relationship
+ * between the 2 addresses.
+ *
+ * \param a Pointer to the first address instance to be compared.
+ * \param b Pointer to the second address instance to be compared.
+ *
+ * \retval ADDRESS_EQ If the 2 address ranges a and b, are equal.
+ * \retval ADDRESS_ES b encapsulates a. b_ip1[...a_ip1...a_ip2...]b_ip2.
+ * \retval ADDRESS_EB a encapsulates b. a_ip1[...b_ip1....b_ip2...]a_ip2.
+ * \retval ADDRESS_LE a_ip1(...b_ip1==a_ip2...)b_ip2
+ * \retval ADDRESS_LT a_ip1(...b_ip1...a_ip2...)b_ip2
+ * \retval ADDRESS_GE b_ip1(...a_ip1==b_ip2...)a_ip2
+ * \retval ADDRESS_GT a_ip1 > b_ip2, i.e. the address range for 'a' starts only
+ * after the end of the address range for 'b'
+ */
+int DetectAddressCmpIPv6(DetectAddress *a, DetectAddress *b)
+{
+ if (AddressIPv6Eq(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Eq(&a->ip2, &b->ip2) == 1) {
+ return ADDRESS_EQ;
+ } else if (AddressIPv6Ge(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Le(&a->ip, &b->ip2) == 1 &&
+ AddressIPv6Le(&a->ip2, &b->ip2) == 1) {
+ return ADDRESS_ES;
+ } else if (AddressIPv6Le(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Ge(&a->ip2, &b->ip2) == 1) {
+ return ADDRESS_EB;
+ } else if (AddressIPv6Lt(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Lt(&a->ip2, &b->ip2) == 1 &&
+ AddressIPv6Ge(&a->ip2, &b->ip) == 1) {
+ return ADDRESS_LE;
+ } else if (AddressIPv6Lt(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Lt(&a->ip2, &b->ip2) == 1) {
+ return ADDRESS_LT;
+ } else if (AddressIPv6Gt(&a->ip, &b->ip) == 1 &&
+ AddressIPv6Le(&a->ip, &b->ip2) == 1 &&
+ AddressIPv6Gt(&a->ip2, &b->ip2) == 1) {
+ return ADDRESS_GE;
+ } else if (AddressIPv6Gt(&a->ip, &b->ip2) == 1) {
+ return ADDRESS_GT;
+ } else {
+ /* should be unreachable */
+ SCLogDebug("Internal Error: should be unreachable\n");
+ }
+
+ return ADDRESS_ER;
+}
+
+/**
+ * \brief Takes an IPv6 address in a, and returns in b an IPv6 address which is
+ * one less than the IPv6 address in a. The address sent in a is in host
+ * order, and the address in b will be returned in network order!
+ *
+ * \param a Pointer to an IPv6 address in host order.
+ * \param b Pointer to an IPv6 address store in memory which has to be updated
+ * with the new address(a - 1).
+ */
+static void AddressCutIPv6CopySubOne(uint32_t *a, uint32_t *b)
+{
+ uint32_t t = a[3];
+
+ b[0] = a[0];
+ b[1] = a[1];
+ b[2] = a[2];
+ b[3] = a[3];
+
+ b[3]--;
+ if (b[3] > t) {
+ t = b[2];
+ b[2]--;
+ if (b[2] > t) {
+ t = b[1];
+ b[1]--;
+ if (b[1] > t)
+ b[0]--;
+ }
+ }
+
+ b[0] = htonl(b[0]);
+ b[1] = htonl(b[1]);
+ b[2] = htonl(b[2]);
+ b[3] = htonl(b[3]);
+
+ return;
+}
+
+/**
+ * \brief Takes an IPv6 address in a, and returns in b an IPv6 address which is
+ * one more than the IPv6 address in a. The address sent in a is in host
+ * order, and the address in b will be returned in network order!
+ *
+ * \param a Pointer to an IPv6 address in host order.
+ * \param b Pointer to an IPv6 address store in memory which has to be updated
+ * with the new address(a + 1).
+ */
+static void AddressCutIPv6CopyAddOne(uint32_t *a, uint32_t *b)
+{
+ uint32_t t = a[3];
+
+ b[0] = a[0];
+ b[1] = a[1];
+ b[2] = a[2];
+ b[3] = a[3];
+
+ b[3]++;
+ if (b[3] < t) {
+ t = b[2];
+ b[2]++;
+ if (b[2] < t) {
+ t = b[1];
+ b[1]++;
+ if (b[1] < t)
+ b[0]++;
+ }
+ }
+
+ b[0] = htonl(b[0]);
+ b[1] = htonl(b[1]);
+ b[2] = htonl(b[2]);
+ b[3] = htonl(b[3]);
+
+ return;
+}
+
+/**
+ * \brief Copies an IPv6 address in a to the b. The address in a is in host
+ * order and will be copied in network order to b!
+ *
+ * \param a Pointer to the IPv6 address to be copied.
+ * \param b Pointer to an IPv6 address in memory which will be updated with the
+ * address in a.
+ */
+static void AddressCutIPv6Copy(uint32_t *a, uint32_t *b)
+{
+ b[0] = htonl(a[0]);
+ b[1] = htonl(a[1]);
+ b[2] = htonl(a[2]);
+ b[3] = htonl(a[3]);
+
+ return;
+}
+
+int DetectAddressCutIPv6(DetectEngineCtx *de_ctx, DetectAddress *a,
+ DetectAddress *b, DetectAddress **c)
+{
+ uint32_t a_ip1[4] = { ntohl(a->ip.addr_data32[0]), ntohl(a->ip.addr_data32[1]),
+ ntohl(a->ip.addr_data32[2]), ntohl(a->ip.addr_data32[3]) };
+ uint32_t a_ip2[4] = { ntohl(a->ip2.addr_data32[0]), ntohl(a->ip2.addr_data32[1]),
+ ntohl(a->ip2.addr_data32[2]), ntohl(a->ip2.addr_data32[3]) };
+ uint32_t b_ip1[4] = { ntohl(b->ip.addr_data32[0]), ntohl(b->ip.addr_data32[1]),
+ ntohl(b->ip.addr_data32[2]), ntohl(b->ip.addr_data32[3]) };
+ uint32_t b_ip2[4] = { ntohl(b->ip2.addr_data32[0]), ntohl(b->ip2.addr_data32[1]),
+ ntohl(b->ip2.addr_data32[2]), ntohl(b->ip2.addr_data32[3]) };
+
+ DetectPort *port = NULL;
+ DetectAddress *tmp = NULL;
+
+ /* default to NULL */
+ *c = NULL;
+
+ int r = DetectAddressCmpIPv6(a, b);
+ if (r != ADDRESS_ES && r != ADDRESS_EB && r != ADDRESS_LE && r != ADDRESS_GE) {
+ goto error;
+ }
+
+ /* get a place to temporary put sigs lists */
+ tmp = DetectAddressInit();
+ if (tmp == NULL)
+ goto error;
+ memset(tmp,0,sizeof(DetectAddress));
+
+ /* we have 3 parts: [aaa[abab]bbb]
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ if (r == ADDRESS_LE) {
+ AddressCutIPv6Copy(a_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(b_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, b->ip2.addr_data32);
+
+ DetectAddress *tmp_c;
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+ tmp_c->ip.family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(a_ip2, tmp_c->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, tmp_c->ip2.addr_data32);
+
+ *c = tmp_c;
+
+ /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp_c->sh);
+ /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &tmp_c->port, port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &b->port, port);
+
+ tmp_c->cnt += b->cnt;
+ b->cnt += a->cnt;
+
+ /* we have 3 parts: [bbb[baba]aaa]
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_GE) {
+ AddressCutIPv6Copy(b_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(a_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, b->ip2.addr_data32);
+
+ DetectAddress *tmp_c;
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+ tmp_c->ip.family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(b_ip2, tmp_c->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, tmp_c->ip2.addr_data32);
+ *c = tmp_c;
+
+ /* 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'a' sigs */
+ /* store old a list */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp->sh);
+ /* clean a list */
+ SigGroupHeadClearSigs(a->sh);
+ /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &tmp_c->sh);
+ /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+ /* prepend old a before b */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &b->sh);
+
+ /* clean tmp list */
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&a->port, port);
+
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp_c->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += tmp->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[abab]bbb] or [bbb[baba]]
+ * part a: a_ip1 <-> a_ip2
+ * part b: a_ip2 + 1 <-> b_ip2
+ *
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ *
+ * 3 part [bbb[aaa]bbb]
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ } else if (r == ADDRESS_ES) {
+ if (AddressIPv6EqU32(a_ip1, b_ip1) == 1) {
+ AddressCutIPv6Copy(a_ip1, a->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, a->ip2.addr_data32);
+
+ AddressCutIPv6CopyAddOne(a_ip2, b->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, b->ip2.addr_data32);
+
+ /* 'b' overlaps 'a' so 'a' needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&a->port, port);
+
+ a->cnt += b->cnt;
+
+ } else if (AddressIPv6EqU32(a_ip2, b_ip2) == 1) {
+ AddressCutIPv6Copy(b_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(a_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, b->ip2.addr_data32);
+
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadClearSigs(a->sh);
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &a->sh);
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp->port, a->port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&a->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ } else {
+ AddressCutIPv6Copy(b_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(a_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, b->ip2.addr_data32);
+
+ DetectAddress *tmp_c;
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL) {
+ goto error;
+ }
+ tmp_c->ip.family = AF_INET6;
+ AddressCutIPv6CopyAddOne(a_ip2, tmp_c->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, tmp_c->ip2.addr_data32);
+ *c = tmp_c;
+
+ /* 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'b' sigs */
+ /* store old a list */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp->sh);
+ /* clean a list */
+ SigGroupHeadClearSigs(a->sh);
+ /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp_c->sh);
+ /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &a->sh);
+ /* prepend old a before b */
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &b->sh);
+
+ /* clean tmp list */
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp_c->port, port);
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&a->port, port);
+
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += b->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[baba]aaa] or [aaa[abab]]
+ * part a: b_ip1 <-> b_ip2
+ * part b: b_ip2 + 1 <-> a_ip2
+ *
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> b_ip2
+ *
+ * 3 part [aaa[bbb]aaa]
+ * part a: a_ip1 <-> b_ip2 - 1
+ * part b: b_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_EB) {
+ if (AddressIPv6EqU32(a_ip1, b_ip1) == 1) {
+ AddressCutIPv6Copy(b_ip1, a->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, a->ip2.addr_data32);
+
+ AddressCutIPv6CopyAddOne(b_ip2, b->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, b->ip2.addr_data32);
+
+ /* 'b' overlaps 'a' so a needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx, b->sh, &tmp->sh);
+ SigGroupHeadClearSigs(b->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadCopySigs(de_ctx, tmp->sh, &a->sh);
+ SigGroupHeadClearSigs(tmp->sh);
+
+ for (port = b->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp->port, b->port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+ for (port = tmp->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&a->port, port);
+
+ tmp->cnt += b->cnt;
+ b->cnt = 0;
+ b->cnt += a->cnt;
+ a->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ } else if (AddressIPv6EqU32(a_ip2, b_ip2) == 1) {
+ AddressCutIPv6Copy(a_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(b_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, b->ip2.addr_data32);
+
+ /* 'a' overlaps 'b' so a needs the 'a' sigs */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+
+ b->cnt += a->cnt;
+ } else {
+ AddressCutIPv6Copy(a_ip1, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2.addr_data32);
+
+ AddressCutIPv6Copy(b_ip1, b->ip.addr_data32);
+ AddressCutIPv6Copy(b_ip2, b->ip2.addr_data32);
+
+ DetectAddress *tmp_c;
+ tmp_c = DetectAddressInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->ip.family = AF_INET6;
+ AddressCutIPv6CopyAddOne(b_ip2, tmp_c->ip.addr_data32);
+ AddressCutIPv6Copy(a_ip2, tmp_c->ip2.addr_data32);
+ *c = tmp_c;
+
+ /* 'a' stays the same wrt sigs
+ * 'b' keeps it's own sigs and gets a's sigs prepended
+ * 'c' gets 'a' sigs */
+ SigGroupHeadCopySigs(de_ctx, a->sh, &b->sh);
+ SigGroupHeadCopySigs(de_ctx, a->sh, &tmp_c->sh);
+
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&b->port, port);
+ for (port = a->port; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx,&tmp_c->port, port);
+
+ b->cnt += a->cnt;
+ tmp_c->cnt += a->cnt;
+ }
+ }
+
+ if (tmp != NULL)
+ DetectAddressFree(tmp);
+
+ return 0;
+
+error:
+ if (tmp != NULL)
+ DetectAddressFree(tmp);
+ return -1;
+}
+
+#if 0
+int DetectAddressCutIPv6(DetectAddressData *a, DetectAddressData *b,
+ DetectAddressData **c)
+{
+ uint32_t a_ip1[4] = { ntohl(a->ip[0]), ntohl(a->ip[1]),
+ ntohl(a->ip[2]), ntohl(a->ip[3]) };
+ uint32_t a_ip2[4] = { ntohl(a->ip2[0]), ntohl(a->ip2[1]),
+ ntohl(a->ip2[2]), ntohl(a->ip2[3]) };
+ uint32_t b_ip1[4] = { ntohl(b->ip[0]), ntohl(b->ip[1]),
+ ntohl(b->ip[2]), ntohl(b->ip[3]) };
+ uint32_t b_ip2[4] = { ntohl(b->ip2[0]), ntohl(b->ip2[1]),
+ ntohl(b->ip2[2]), ntohl(b->ip2[3]) };
+
+ /* default to NULL */
+ *c = NULL;
+
+ int r = DetectAddressCmpIPv6(a, b);
+ if (r != ADDRESS_ES && r != ADDRESS_EB && r != ADDRESS_LE && r != ADDRESS_GE) {
+ goto error;
+ }
+
+ /* we have 3 parts: [aaa[abab]bbb]
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ if (r == ADDRESS_LE) {
+ AddressCutIPv6Copy(a_ip1, a->ip);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2);
+
+ AddressCutIPv6Copy(b_ip1, b->ip);
+ AddressCutIPv6Copy(a_ip2, b->ip2);
+
+ DetectAddressData *tmp_c;
+ tmp_c = DetectAddressDataInit();
+ if (tmp_c == NULL)
+ goto error;
+ tmp_c->family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(a_ip2, tmp_c->ip);
+ AddressCutIPv6Copy(b_ip2, tmp_c->ip2);
+
+ *c = tmp_c;
+
+ /* we have 3 parts: [bbb[baba]aaa]
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_GE) {
+ AddressCutIPv6Copy(b_ip1, a->ip);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2);
+
+ AddressCutIPv6Copy(a_ip1, b->ip);
+ AddressCutIPv6Copy(b_ip2, b->ip2);
+
+ DetectAddressData *tmp_c;
+ tmp_c = DetectAddressDataInit();
+ if (tmp_c == NULL)
+ goto error;
+ tmp_c->family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(b_ip2, tmp_c->ip);
+ AddressCutIPv6Copy(a_ip2, tmp_c->ip2);
+
+ *c = tmp_c;
+
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[abab]bbb] or [bbb[baba]]
+ * part a: a_ip1 <-> a_ip2
+ * part b: a_ip2 + 1 <-> b_ip2
+ *
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ *
+ * 3 part [bbb[aaa]bbb]
+ * part a: b_ip1 <-> a_ip1 - 1
+ * part b: a_ip1 <-> a_ip2
+ * part c: a_ip2 + 1 <-> b_ip2
+ */
+ } else if (r == ADDRESS_ES) {
+ if (AddressIPv6Eq(a_ip1,b_ip1) == 1) {
+ AddressCutIPv6Copy(a_ip1, a->ip);
+ AddressCutIPv6Copy(a_ip2, a->ip2);
+
+ AddressCutIPv6CopyAddOne(a_ip2, b->ip);
+ AddressCutIPv6Copy(b_ip2, b->ip2);
+ } else if (AddressIPv6Eq(a_ip2, b_ip2) == 1) {
+ AddressCutIPv6Copy(b_ip1, a->ip);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2);
+
+ AddressCutIPv6Copy(a_ip1, b->ip);
+ AddressCutIPv6Copy(a_ip2, b->ip2);
+ } else {
+ AddressCutIPv6Copy(b_ip1, a->ip);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2);
+
+ AddressCutIPv6Copy(a_ip1, b->ip);
+ AddressCutIPv6Copy(a_ip2, b->ip2);
+
+ DetectAddressData *tmp_c;
+ tmp_c = DetectAddressDataInit();
+ if (tmp_c == NULL)
+ goto error;
+
+ tmp_c->family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(a_ip2, tmp_c->ip);
+ AddressCutIPv6Copy(b_ip2, tmp_c->ip2);
+ *c = tmp_c;
+ }
+ /* we have 2 or three parts:
+ *
+ * 2 part: [[baba]aaa] or [aaa[abab]]
+ * part a: b_ip1 <-> b_ip2
+ * part b: b_ip2 + 1 <-> a_ip2
+ *
+ * part a: a_ip1 <-> b_ip1 - 1
+ * part b: b_ip1 <-> b_ip2
+ *
+ * 3 part [aaa[bbb]aaa]
+ * part a: a_ip1 <-> b_ip2 - 1
+ * part b: b_ip1 <-> b_ip2
+ * part c: b_ip2 + 1 <-> a_ip2
+ */
+ } else if (r == ADDRESS_EB) {
+ if (AddressIPv6Eq(a_ip1, b_ip1) == 1) {
+ AddressCutIPv6Copy(b_ip1, a->ip);
+ AddressCutIPv6Copy(b_ip2, a->ip2);
+
+ AddressCutIPv6CopyAddOne(b_ip2, b->ip);
+ AddressCutIPv6Copy(a_ip2, b->ip2);
+ } else if (AddressIPv6Eq(a_ip2, b_ip2) == 1) {
+ AddressCutIPv6Copy(a_ip1, a->ip);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2);
+
+ AddressCutIPv6Copy(b_ip1, b->ip);
+ AddressCutIPv6Copy(b_ip2, b->ip2);
+ } else {
+ AddressCutIPv6Copy(a_ip1, a->ip);
+ AddressCutIPv6CopySubOne(b_ip1, a->ip2);
+
+ AddressCutIPv6Copy(b_ip1, b->ip);
+ AddressCutIPv6Copy(b_ip2, b->ip2);
+
+ DetectAddressData *tmp_c;
+ tmp_c = DetectAddressDataInit();
+ if (tmp_c == NULL)
+ goto error;
+ tmp_c->family = AF_INET6;
+
+ AddressCutIPv6CopyAddOne(b_ip2, tmp_c->ip);
+ AddressCutIPv6Copy(a_ip2, tmp_c->ip2);
+ *c = tmp_c;
+ }
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+#endif
+
+/**
+ * \brief Cuts and returns an address range, which is the complement of the
+ * address range that is supplied as the argument.
+ *
+ * For example:
+ *
+ * If a = ::-2000::,
+ * then a = 2000::1-FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF and b = NULL
+ * If a = 2000::1-FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF,
+ * then a = ::-2000:: and b = NULL
+ * If a = 2000::1-20FF::2,
+ * then a = ::-2000:: and
+ * b = 20FF::3-FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
+ *
+ * \param a Pointer to an address range (DetectAddress) instance whose complement
+ * has to be returned in a and b.
+ * \param b Pointer to DetectAddress pointer, that will be supplied back with a
+ * new DetectAddress instance, if the complement demands so.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressCutNotIPv6(DetectAddress *a, DetectAddress **b)
+{
+ uint32_t a_ip1[4] = { ntohl(a->ip.addr_data32[0]), ntohl(a->ip.addr_data32[1]),
+ ntohl(a->ip.addr_data32[2]), ntohl(a->ip.addr_data32[3]) };
+ uint32_t a_ip2[4] = { ntohl(a->ip2.addr_data32[0]), ntohl(a->ip2.addr_data32[1]),
+ ntohl(a->ip2.addr_data32[2]), ntohl(a->ip2.addr_data32[3]) };
+ uint32_t ip_nul[4] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
+ uint32_t ip_max[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+ /* default to NULL */
+ *b = NULL;
+
+ if (!(a_ip1[0] == 0x00000000 && a_ip1[1] == 0x00000000 &&
+ a_ip1[2] == 0x00000000 && a_ip1[3] == 0x00000000) &&
+ !(a_ip2[0] == 0xFFFFFFFF && a_ip2[1] == 0xFFFFFFFF &&
+ a_ip2[2] == 0xFFFFFFFF && a_ip2[3] == 0xFFFFFFFF)) {
+ AddressCutIPv6Copy(ip_nul, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2.addr_data32);
+
+ DetectAddress *tmp_b = DetectAddressInit();
+ if (tmp_b == NULL)
+ goto error;
+
+ tmp_b->ip.family = AF_INET6;
+ AddressCutIPv6CopyAddOne(a_ip2, tmp_b->ip.addr_data32);
+ AddressCutIPv6Copy(ip_max, tmp_b->ip2.addr_data32);
+ *b = tmp_b;
+ } else if ((a_ip1[0] == 0x00000000 && a_ip1[1] == 0x00000000 &&
+ a_ip1[2] == 0x00000000 && a_ip1[3] == 0x00000000) &&
+ !(a_ip2[0] == 0xFFFFFFFF && a_ip2[1] == 0xFFFFFFFF &&
+ a_ip2[2] == 0xFFFFFFFF && a_ip2[3] == 0xFFFFFFFF)) {
+ AddressCutIPv6CopyAddOne(a_ip2, a->ip.addr_data32);
+ AddressCutIPv6Copy(ip_max, a->ip2.addr_data32);
+ } else if (!(a_ip1[0] == 0x00000000 && a_ip1[1] == 0x00000000 &&
+ a_ip1[2] == 0x00000000 && a_ip1[3] == 0x00000000) &&
+ (a_ip2[0] == 0xFFFFFFFF && a_ip2[1] == 0xFFFFFFFF &&
+ a_ip2[2] == 0xFFFFFFFF && a_ip2[3] == 0xFFFFFFFF)) {
+ AddressCutIPv6Copy(ip_nul, a->ip.addr_data32);
+ AddressCutIPv6CopySubOne(a_ip1, a->ip2.addr_data32);
+ } else {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Extends a target address range if the the source address range is
+ * wider than the target address range on either sides.
+ *
+ * Every address is a range, i.e. address->ip1....address->ip2. For
+ * example 2000::-2010::
+ * if source->ip1 is smaller than target->ip1, it indicates that the
+ * source's left address limit is greater(range wise) than the target's
+ * left address limit, and hence we reassign the target's left address
+ * limit to source's left address limit.
+ * Similary if source->ip2 is greater than target->ip2, it indicates that
+ * the source's right address limit is greater(range wise) than the
+ * target's right address limit, and hence we reassign the target's right
+ * address limit to source's right address limit.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param target Pointer to the target DetectAddress instance that has to be
+ * updated.
+ * \param source Pointer to the source DetectAddress instance that is used
+ * to decided whether we extend the target's address range.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressJoinIPv6(DetectEngineCtx *de_ctx, DetectAddress *target,
+ DetectAddress *source)
+{
+ if (AddressIPv6Lt(&source->ip, &target->ip)) {
+ COPY_ADDRESS(&source->ip, &target->ip);
+ }
+
+ if (AddressIPv6Gt(&source->ip, &target->ip)) {
+ COPY_ADDRESS(&source->ip2, &target->ip2);
+ }
+
+ return 0;
+}
+
+
+/***************************************Unittests******************************/
+
+#ifdef UNITTESTS
+
+int AddressTestIPv6Gt01(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 0, 2, 3, 4 };
+
+ if (AddressIPv6GtU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Gt02(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 0, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6GtU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Gt03(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6GtU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Gt04(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 5 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6GtU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Lt01(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 0, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6LtU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Lt02(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 0, 2, 3, 4 };
+
+ if (AddressIPv6LtU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Lt03(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6LtU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Lt04(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 5 };
+
+ if (AddressIPv6LtU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Eq01(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 0, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6EqU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Eq02(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 0, 2, 3, 4 };
+
+ if (AddressIPv6EqU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Eq03(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6EqU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Eq04(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 5 };
+
+ if (AddressIPv6EqU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Le01(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 0, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6LeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Le02(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 0, 2, 3, 4 };
+
+ if (AddressIPv6LeU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Le03(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6LeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Le04(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 5 };
+
+ if (AddressIPv6LeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Le05(void)
+{
+ int result = 0;
+
+ uint32_t a[4];
+ uint32_t b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "1999:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &in6) != 1)
+ return 0;
+ memcpy(&a, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(&b, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (AddressIPv6LeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Ge01(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 0, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6GeU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Ge02(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 0, 2, 3, 4 };
+
+ if (AddressIPv6GeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Ge03(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 4 };
+
+ if (AddressIPv6GeU32(a, b) == 1)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Ge04(void)
+{
+ int result = 0;
+
+ uint32_t a[4] = { 1, 2, 3, 4 };
+ uint32_t b[4] = { 1, 2, 3, 5 };
+
+ if (AddressIPv6GeU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6Ge05(void)
+{
+ int result = 0;
+
+ uint32_t a[4];
+ uint32_t b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "1999:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &in6) != 1)
+ return 0;
+ memcpy(&a, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(&b, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (AddressIPv6GeU32(a, b) == 0)
+ result = 1;
+
+ return result;
+}
+
+int AddressTestIPv6SubOne01(void)
+{
+ int result = 0;
+
+ uint32_t a[4], b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+
+ a[0] = ntohl(a[0]);
+ a[1] = ntohl(a[1]);
+ a[2] = ntohl(a[2]);
+ a[3] = ntohl(a[3]);
+
+ AddressCutIPv6CopySubOne(a, b);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+ if (b[0] == a[0] && b[1] == a[1] &&
+ b[2] == a[2] && b[3] == a[3]) {
+ result = 1;
+ }
+
+ return result;
+}
+
+int AddressTestIPv6SubOne02(void)
+{
+ int result = 0;
+
+ uint32_t a[4], b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+
+ a[0] = ntohl(a[0]);
+ a[1] = ntohl(a[1]);
+ a[2] = ntohl(a[2]);
+ a[3] = ntohl(a[3]);
+
+ AddressCutIPv6CopySubOne(a, b);
+
+ if (inet_pton(AF_INET6, "1FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+ if (b[0] == a[0] && b[1] == a[1] &&
+ b[2] == a[2] && b[3] == a[3]) {
+ result = 1;
+ }
+
+ return result;
+}
+
+int AddressTestIPv6AddOne01(void)
+{
+ int result = 0;
+
+ uint32_t a[4], b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+
+ a[0] = ntohl(a[0]);
+ a[1] = ntohl(a[1]);
+ a[2] = ntohl(a[2]);
+ a[3] = ntohl(a[3]);
+
+ AddressCutIPv6CopyAddOne(a, b);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+ if (b[0] == a[0] && b[1] == a[1] &&
+ b[2] == a[2] && b[3] == a[3]) {
+ result = 1;
+ }
+
+ return result;
+}
+
+int AddressTestIPv6AddOne02(void)
+{
+ int result = 0;
+
+ uint32_t a[4], b[4];
+ struct in6_addr in6;
+
+ if (inet_pton(AF_INET6, "1FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+
+ a[0] = ntohl(a[0]);
+ a[1] = ntohl(a[1]);
+ a[2] = ntohl(a[2]);
+ a[3] = ntohl(a[3]);
+
+ AddressCutIPv6CopyAddOne(a, b);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ return 0;
+ memcpy(a, in6.s6_addr, sizeof(in6.s6_addr));
+ if (b[0] == a[0] && b[1] == a[1] &&
+ b[2] == a[2] && b[3] == a[3]) {
+ result = 1;
+ }
+
+ return result;
+}
+
+static int AddressTestIPv6AddressCmp01(void)
+{
+ DetectAddress *a = DetectAddressInit();
+ DetectAddress *b = DetectAddressInit();
+ struct in6_addr in6;
+ int result = 1;
+
+ if (a == NULL || b == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_EQ);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_ES);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_ES);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_EB);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::11", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_EB);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LE);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_LE);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ /* we could get a LE */
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ /* we could get a LE */
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::19", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_LT);
+
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::19", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_GE);
+
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) == ADDRESS_GT);
+
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_GT);
+
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&b->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&b->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCmpIPv6(a, b) != ADDRESS_GT);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int AddressTestIPv6CutNot01(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ struct in6_addr in6;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "::", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCutNotIPv6(a, &b) == -1);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ return 0;
+}
+
+static int AddressTestIPv6CutNot02(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ DetectAddress *temp = NULL;
+ struct in6_addr in6;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ if ( (temp = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "::", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCutNotIPv6(a, &b) == 0);
+
+ result &= (b == NULL);
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result = (DetectAddressCmpIPv6(a, temp) == ADDRESS_EQ);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return 0;
+}
+
+static int AddressTestIPv6CutNot03(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ DetectAddress *temp = NULL;
+ struct in6_addr in6;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ if ( (temp = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCutNotIPv6(a, &b) == 0);
+
+ result &= (b == NULL);
+
+ if (inet_pton(AF_INET6, "::", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result = (DetectAddressCmpIPv6(a, temp) == ADDRESS_EQ);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return 0;
+}
+
+static int AddressTestIPv6CutNot04(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ DetectAddress *temp = NULL;
+ struct in6_addr in6;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ if ( (temp = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCutNotIPv6(a, &b) == 0);
+
+ if (inet_pton(AF_INET6, "::", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(a, temp) == ADDRESS_EQ);
+
+ result &= (b != NULL);
+ if (result == 0)
+ goto error;
+ if (inet_pton(AF_INET6, "2000::2", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(b, temp) == ADDRESS_EQ);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return 0;
+}
+
+static int AddressTestIPv6CutNot05(void)
+{
+ DetectAddress *a = NULL;
+ DetectAddress *b = NULL;
+ DetectAddress *temp = NULL;
+ struct in6_addr in6;
+ int result = 1;
+
+ if ( (a = DetectAddressInit()) == NULL)
+ goto error;
+ if ( (temp = DetectAddressInit()) == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&a->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&a->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result &= (DetectAddressCutNotIPv6(a, &b) == 0);
+
+ if (inet_pton(AF_INET6, "::", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::0", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(a, temp) == ADDRESS_EQ);
+
+ result &= (b != NULL);
+ if (result == 0)
+ goto error;
+ if (inet_pton(AF_INET6, "2000::21", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(b, temp) == ADDRESS_EQ);
+
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return result;
+
+ error:
+ if (a != NULL)
+ DetectAddressFree(a);
+ if (b != NULL)
+ DetectAddressFree(b);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return 0;
+}
+
+static int AddressTestIPv6Join01(void)
+{
+ DetectAddress *source = DetectAddressInit();
+ DetectAddress *target = DetectAddressInit();
+ DetectAddress *temp = DetectAddressInit();
+ struct in6_addr in6;
+ int result = 1;
+
+ if (source == NULL || target == NULL || temp == NULL)
+ goto error;
+
+ /* case 1 */
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&target->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&target->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&source->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&source->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result &= (DetectAddressJoinIPv6(NULL, target, source) == 0);
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(target, temp) == ADDRESS_EQ);
+
+ /* case 2 */
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&target->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&target->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::2", &in6) != 1)
+ goto error;
+ memcpy(&source->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::19", &in6) != 1)
+ goto error;
+ memcpy(&source->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result &= (DetectAddressJoinIPv6(NULL, target, source) == 0);
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(target, temp) == ADDRESS_EQ);
+
+ /* case 3 */
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&target->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::15", &in6) != 1)
+ goto error;
+ memcpy(&target->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&source->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&source->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result &= (DetectAddressJoinIPv6(NULL, target, source) == 0);
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(target, temp) == ADDRESS_EQ);
+
+ /* case 4 */
+ if (inet_pton(AF_INET6, "2000::10", &in6) != 1)
+ goto error;
+ memcpy(&target->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&target->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&source->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&source->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result &= (DetectAddressJoinIPv6(NULL, target, source) == 0);
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(target, temp) == ADDRESS_EQ);
+
+ /* case 5 */
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&target->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&target->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&source->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&source->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+
+ result &= (DetectAddressJoinIPv6(NULL, target, source) == 0);
+ if (inet_pton(AF_INET6, "2000::1", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip.address, in6.s6_addr, sizeof(in6.s6_addr));
+ if (inet_pton(AF_INET6, "2000::20", &in6) != 1)
+ goto error;
+ memcpy(&temp->ip2.address, in6.s6_addr, sizeof(in6.s6_addr));
+ result = (DetectAddressCmpIPv6(target, temp) == ADDRESS_EQ);
+
+ if (source != NULL)
+ DetectAddressFree(source);
+ if (target != NULL)
+ DetectAddressFree(target);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+ return result;
+
+ error:
+ if (source != NULL)
+ DetectAddressFree(source);
+ if (target != NULL)
+ DetectAddressFree(target);
+ if (temp != NULL)
+ DetectAddressFree(temp);
+
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+void DetectAddressIPv6Tests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("AddressTestIPv6Gt01", AddressTestIPv6Gt01, 1);
+ UtRegisterTest("AddressTestIPv6Gt02", AddressTestIPv6Gt02, 1);
+ UtRegisterTest("AddressTestIPv6Gt03", AddressTestIPv6Gt03, 1);
+ UtRegisterTest("AddressTestIPv6Gt04", AddressTestIPv6Gt04, 1);
+
+ UtRegisterTest("AddressTestIPv6Lt01", AddressTestIPv6Lt01, 1);
+ UtRegisterTest("AddressTestIPv6Lt02", AddressTestIPv6Lt02, 1);
+ UtRegisterTest("AddressTestIPv6Lt03", AddressTestIPv6Lt03, 1);
+ UtRegisterTest("AddressTestIPv6Lt04", AddressTestIPv6Lt04, 1);
+
+ UtRegisterTest("AddressTestIPv6Eq01", AddressTestIPv6Eq01, 1);
+ UtRegisterTest("AddressTestIPv6Eq02", AddressTestIPv6Eq02, 1);
+ UtRegisterTest("AddressTestIPv6Eq03", AddressTestIPv6Eq03, 1);
+ UtRegisterTest("AddressTestIPv6Eq04", AddressTestIPv6Eq04, 1);
+
+ UtRegisterTest("AddressTestIPv6Le01", AddressTestIPv6Le01, 1);
+ UtRegisterTest("AddressTestIPv6Le02", AddressTestIPv6Le02, 1);
+ UtRegisterTest("AddressTestIPv6Le03", AddressTestIPv6Le03, 1);
+ UtRegisterTest("AddressTestIPv6Le04", AddressTestIPv6Le04, 1);
+ UtRegisterTest("AddressTestIPv6Le05", AddressTestIPv6Le05, 1);
+
+ UtRegisterTest("AddressTestIPv6Ge01", AddressTestIPv6Ge01, 1);
+ UtRegisterTest("AddressTestIPv6Ge02", AddressTestIPv6Ge02, 1);
+ UtRegisterTest("AddressTestIPv6Ge03", AddressTestIPv6Ge03, 1);
+ UtRegisterTest("AddressTestIPv6Ge04", AddressTestIPv6Ge04, 1);
+ UtRegisterTest("AddressTestIPv6Ge05", AddressTestIPv6Ge05, 1);
+
+ UtRegisterTest("AddressTestIPv6SubOne01", AddressTestIPv6SubOne01, 1);
+ UtRegisterTest("AddressTestIPv6SubOne02", AddressTestIPv6SubOne02, 1);
+
+ UtRegisterTest("AddressTestIPv6AddOne01", AddressTestIPv6AddOne01, 1);
+ UtRegisterTest("AddressTestIPv6AddOne02", AddressTestIPv6AddOne02, 1);
+
+ UtRegisterTest("AddressTestIPv6AddressCmp01",
+ AddressTestIPv6AddressCmp01, 1);
+
+ UtRegisterTest("AddressTestIPv6CutNot01", AddressTestIPv6CutNot01, 1);
+ UtRegisterTest("AddressTestIPv6CutNot02", AddressTestIPv6CutNot02, 1);
+ UtRegisterTest("AddressTestIPv6CutNot03", AddressTestIPv6CutNot03, 1);
+ UtRegisterTest("AddressTestIPv6CutNot04", AddressTestIPv6CutNot04, 1);
+ UtRegisterTest("AddressTestIPv6CutNot05", AddressTestIPv6CutNot05, 1);
+
+ UtRegisterTest("AddressTestIPv6Join01", AddressTestIPv6Join01, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-address-ipv6.h b/framework/src/suricata/src/detect-engine-address-ipv6.h
new file mode 100644
index 00000000..dedf090b
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address-ipv6.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_ADDRESS_IPV6_H__
+#define __DETECT_ENGINE_ADDRESS_IPV6_H__
+
+int AddressIPv6Lt(Address *, Address *);
+int AddressIPv6Gt(Address *, Address *);
+int AddressIPv6Eq(Address *, Address *);
+int AddressIPv6Le(Address *, Address *);
+int AddressIPv6Ge(Address *, Address *);
+
+int DetectAddressCutNotIPv6(DetectAddress *, DetectAddress **);
+int DetectAddressCmpIPv6(DetectAddress *a, DetectAddress *b);
+
+int DetectAddressCutIPv6(DetectEngineCtx *, DetectAddress *, DetectAddress *,
+ DetectAddress **);
+int DetectAddressJoinIPv6(DetectEngineCtx *, DetectAddress *, DetectAddress *);
+
+void DetectAddressIPv6Tests(void);
+
+#endif /* __DETECT_ENGINE_ADDRESS_IPV6_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-address.c b/framework/src/suricata/src/detect-engine-address.c
new file mode 100644
index 00000000..31e6a7ab
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address.c
@@ -0,0 +1,5419 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Address part of the detection engine.
+ *
+ * \todo Move this out of the detection plugin structure
+ * rename to detect-engine-address.c
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "flow-var.h"
+
+#include "util-cidr.h"
+#include "util-unittest.h"
+#include "util-rule-vars.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+#include "detect-engine-address-ipv4.h"
+#include "detect-engine-address-ipv6.h"
+#include "detect-engine-port.h"
+
+#include "util-debug.h"
+#include "util-print.h"
+
+/* prototypes */
+void DetectAddressPrint(DetectAddress *);
+static int DetectAddressCutNot(DetectAddress *, DetectAddress **);
+static int DetectAddressCut(DetectEngineCtx *, DetectAddress *, DetectAddress *,
+ DetectAddress **);
+int DetectAddressMergeNot(DetectAddressHead *gh, DetectAddressHead *ghn);
+
+/** memory usage counters
+ * \todo not MT safe */
+#ifdef DEBUG
+static uint32_t detect_address_group_memory = 0;
+static uint32_t detect_address_group_init_cnt = 0;
+static uint32_t detect_address_group_free_cnt = 0;
+
+static uint32_t detect_address_group_head_memory = 0;
+static uint32_t detect_address_group_head_init_cnt = 0;
+static uint32_t detect_address_group_head_free_cnt = 0;
+#endif
+
+/**
+ * \brief Creates and returns a new instance of a DetectAddress.
+ *
+ * \retval ag Pointer to the newly created DetectAddress on success;
+ * NULL on failure.
+ */
+DetectAddress *DetectAddressInit(void)
+{
+ DetectAddress *ag = SCMalloc(sizeof(DetectAddress));
+ if (unlikely(ag == NULL))
+ return NULL;
+ memset(ag, 0, sizeof(DetectAddress));
+
+#ifdef DEBUG
+ detect_address_group_memory += sizeof(DetectAddress);
+ detect_address_group_init_cnt++;
+#endif
+
+ return ag;
+}
+
+/**
+ * \brief Frees a DetectAddress instance.
+ *
+ * \param ag Pointer to the DetectAddress instance to be freed.
+ */
+void DetectAddressFree(DetectAddress *ag)
+{
+ if (ag == NULL)
+ return;
+
+ SCLogDebug("ag %p, sh %p", ag, ag->sh);
+
+ /* only free the head if we have the original */
+ if (ag->sh != NULL && !(ag->flags & ADDRESS_SIGGROUPHEAD_COPY)) {
+ SCLogDebug("- ag %p, sh %p not a copy, so call SigGroupHeadFree", ag,
+ ag->sh);
+ SigGroupHeadFree(ag->sh);
+ }
+ ag->sh = NULL;
+
+ if (!(ag->flags & ADDRESS_HAVEPORT)) {
+ SCLogDebug("- ag %p dst_gh %p", ag, ag->dst_gh);
+
+ if (ag->dst_gh != NULL)
+ DetectAddressHeadFree(ag->dst_gh);
+ ag->dst_gh = NULL;
+ } else {
+ SCLogDebug("- ag %p port %p", ag, ag->port);
+
+ if (ag->port != NULL && !(ag->flags & ADDRESS_PORTS_COPY)) {
+ SCLogDebug("- ag %p port %p, not a copy so call DetectPortCleanupList",
+ ag, ag->port);
+ DetectPortCleanupList(ag->port);
+ }
+ ag->port = NULL;
+ }
+#ifdef DEBUG
+ detect_address_group_memory -= sizeof(DetectAddress);
+ detect_address_group_free_cnt++;
+#endif
+ SCFree(ag);
+
+ return;
+}
+
+/**
+ * \brief Copies the contents of one Address group in DetectAddress and returns
+ * a new instance of the DetectAddress that contains the copied address.
+ *
+ * \param orig Pointer to the instance of DetectAddress that contains the
+ * address data to be copied to the new instance.
+ *
+ * \retval ag Pointer to the new instance of DetectAddress that contains the
+ * copied address.
+ */
+DetectAddress *DetectAddressCopy(DetectAddress *orig)
+{
+ DetectAddress *ag = DetectAddressInit();
+ if (ag == NULL)
+ return NULL;
+
+ ag->flags = orig->flags;
+
+ COPY_ADDRESS(&orig->ip, &ag->ip);
+ COPY_ADDRESS(&orig->ip2, &ag->ip2);
+
+ ag->cnt = 1;
+
+ return ag;
+}
+
+/**
+ * \brief Prints the memory statistics for the detection-engine-address section.
+ */
+void DetectAddressPrintMemory(void)
+{
+#ifdef DEBUG
+ SCLogDebug(" * Address group memory stats (DetectAddress %" PRIuMAX "):",
+ (uintmax_t)sizeof(DetectAddress));
+ SCLogDebug(" - detect_address_group_memory %" PRIu32,
+ detect_address_group_memory);
+ SCLogDebug(" - detect_address_group_init_cnt %" PRIu32,
+ detect_address_group_init_cnt);
+ SCLogDebug(" - detect_address_group_free_cnt %" PRIu32,
+ detect_address_group_free_cnt);
+ SCLogDebug(" - outstanding groups %" PRIu32,
+ detect_address_group_init_cnt - detect_address_group_free_cnt);
+ SCLogDebug(" * Address group memory stats done");
+ SCLogDebug(" * Address group head memory stats (DetectAddressHead %" PRIuMAX "):",
+ (uintmax_t)sizeof(DetectAddressHead));
+ SCLogDebug(" - detect_address_group_head_memory %" PRIu32,
+ detect_address_group_head_memory);
+ SCLogDebug(" - detect_address_group_head_init_cnt %" PRIu32,
+ detect_address_group_head_init_cnt);
+ SCLogDebug(" - detect_address_group_head_free_cnt %" PRIu32,
+ detect_address_group_head_free_cnt);
+ SCLogDebug(" - outstanding groups %" PRIu32,
+ (detect_address_group_head_init_cnt -
+ detect_address_group_head_free_cnt));
+ SCLogDebug(" * Address group head memory stats done");
+ SCLogDebug(" X Total %" PRIu32 "\n", (detect_address_group_memory +
+ detect_address_group_head_memory));
+#endif
+
+ return;
+}
+
+/**
+ * \brief Used to check if a DetectAddress list contains an instance with
+ * a similar DetectAddress. The comparison done is not the one that
+ * checks the memory for the same instance, but one that checks that the
+ * two instances hold the same content.
+ *
+ * \param head Pointer to the DetectAddress list.
+ * \param ad Pointer to the DetectAddress that has to be checked for in
+ * the DetectAddress list.
+ *
+ * \retval cur Returns a pointer to the DetectAddress on a match; NULL if
+ * no match.
+ */
+DetectAddress *DetectAddressLookupInList(DetectAddress *head, DetectAddress *gr)
+{
+ DetectAddress *cur;
+
+ if (head != NULL) {
+ for (cur = head; cur != NULL; cur = cur->next) {
+ if (DetectAddressCmp(cur, gr) == ADDRESS_EQ)
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Prints the address data information for all the DetectAddress
+ * instances in the DetectAddress list sent as the argument.
+ *
+ * \param head Pointer to a list of DetectAddress instances.
+ */
+void DetectAddressPrintList(DetectAddress *head)
+{
+ DetectAddress *cur;
+
+ SCLogInfo("list:");
+ if (head != NULL) {
+ for (cur = head; cur != NULL; cur = cur->next) {
+ SCLogInfo("SIGS %6u ", cur->sh ? cur->sh->sig_cnt : 0);
+ DetectAddressPrint(cur);
+ }
+ }
+ SCLogInfo("endlist");
+
+ return;
+}
+
+/**
+ * \brief Frees a list of DetectAddress instances.
+ *
+ * \param head Pointer to a list of DetectAddress instances to be freed.
+ */
+void DetectAddressCleanupList(DetectAddress *head)
+{
+ DetectAddress *cur, *next;
+
+ if (head == NULL)
+ return;
+
+ for (cur = head; cur != NULL; ) {
+ next = cur->next;
+ cur->next = NULL;
+ DetectAddressFree(cur);
+ cur = next;
+ }
+
+ return;
+}
+
+/**
+ * \brief Do a sorted insert, where the top of the list should be the biggest
+ * network/range.
+ *
+ * XXX current sorting only works for overlapping nets
+ *
+ * \param head Pointer to the list of DetectAddress.
+ * \param ag Pointer to the DetectAddress that has to be added to the
+ * above list.
+ *
+ * \retval 0 On successfully inserting the DetectAddress.
+ * \retval -1 On failure.
+ */
+
+int DetectAddressAdd(DetectAddress **head, DetectAddress *ag)
+{
+ DetectAddress *cur, *prev_cur = NULL;
+ int r = 0;
+
+ if (*head != NULL) {
+ for (cur = *head; cur != NULL; cur = cur->next) {
+ prev_cur = cur;
+ r = DetectAddressCmp(ag, cur);
+ if (r == ADDRESS_EB) {
+ /* insert here */
+ ag->prev = cur->prev;
+ ag->next = cur;
+
+ cur->prev = ag;
+ if (*head == cur)
+ *head = ag;
+ else
+ ag->prev->next = ag;
+
+ return 0;
+ }
+ }
+ ag->prev = prev_cur;
+ if (prev_cur != NULL)
+ prev_cur->next = ag;
+ } else {
+ *head = ag;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Helper function for DetectAddressInsert. Sets one of the
+ * DetectAddressHead head pointers, to the DetectAddress argument
+ * based on its address family.
+ *
+ * \param gh Pointer to the DetectAddressHead.
+ * \param newhead Pointer to the DetectAddress.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SetHeadPtr(DetectAddressHead *gh, DetectAddress *newhead)
+{
+ if (newhead->flags & ADDRESS_FLAG_ANY) {
+ gh->any_head = newhead;
+ } else if (newhead->ip.family == AF_INET) {
+ gh->ipv4_head = newhead;
+ } else if (newhead->ip.family == AF_INET6) {
+ gh->ipv6_head = newhead;
+ } else {
+ SCLogDebug("newhead->family %u not supported", newhead->ip.family);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Returns the DetectAddress head from the DetectAddressHeads,
+ * based on the address family of the incoming DetectAddress arg.
+ *
+ * \param gh Pointer to the DetectAddressHead.
+ * \param new Pointer to the DetectAddress.
+ *
+ * \retval head Pointer to the DetectAddress(the head from
+ * DetectAddressHead).
+ */
+static DetectAddress *GetHeadPtr(DetectAddressHead *gh, DetectAddress *new)
+{
+ DetectAddress *head = NULL;
+
+ if (new->flags & ADDRESS_FLAG_ANY)
+ head = gh->any_head;
+ else if (new->ip.family == AF_INET)
+ head = gh->ipv4_head;
+ else if (new->ip.family == AF_INET6)
+ head = gh->ipv6_head;
+
+ return head;
+}
+
+/**
+ * \brief Same as DetectAddressInsert, but then for inserting a address group
+ * object. This also makes sure SigGroupContainer lists are handled
+ * correctly.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param gh Pointer to the DetectAddressHead list to which it has to
+ * be inserted.
+ * \param new Pointer to the DetectAddress, that has to be inserted.
+ *
+ * \retval 1 On successfully inserting it.
+ * \retval -1 On error.
+ * \retval 0 Not inserted, memory of new is freed.
+ */
+int DetectAddressInsert(DetectEngineCtx *de_ctx, DetectAddressHead *gh,
+ DetectAddress *new)
+{
+ DetectAddress *head = NULL;
+ DetectAddress *cur = NULL;
+ DetectAddress *c = NULL;
+ int r = 0;
+
+ if (new == NULL)
+ return 0;
+
+ BUG_ON(new->ip.family == 0 && !(new->flags & ADDRESS_FLAG_ANY));
+
+ /* get our head ptr based on the address we want to insert */
+ head = GetHeadPtr(gh, new);
+
+ /* see if it already exists or overlaps with existing ag's */
+ if (head != NULL) {
+ cur = NULL;
+
+ for (cur = head; cur != NULL; cur = cur->next) {
+ r = DetectAddressCmp(new, cur);
+ BUG_ON(r == ADDRESS_ER);
+
+ /* if so, handle that */
+ if (r == ADDRESS_EQ) {
+ /* exact overlap/match */
+ if (cur != new) {
+ DetectPort *port = new->port;
+ for ( ; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &cur->port, port);
+ SigGroupHeadCopySigs(de_ctx, new->sh, &cur->sh);
+ cur->cnt += new->cnt;
+ DetectAddressFree(new);
+
+ return 0;
+ }
+
+ return 1;
+ } else if (r == ADDRESS_GT) {
+ /* only add it now if we are bigger than the last group.
+ * Otherwise we'll handle it later. */
+ if (cur->next == NULL) {
+ /* put in the list */
+ new->prev = cur;
+ cur->next = new;
+
+ return 1;
+ }
+ } else if (r == ADDRESS_LT) {
+ /* see if we need to insert the ag anywhere put in the list */
+ if (cur->prev != NULL)
+ cur->prev->next = new;
+ new->prev = cur->prev;
+ new->next = cur;
+ cur->prev = new;
+
+ /* update head if required */
+ if (head == cur) {
+ head = new;
+
+ if (SetHeadPtr(gh, head) < 0)
+ goto error;
+ }
+
+ return 1;
+ /* alright, those were the simple cases, lets handle the more
+ * complex ones now */
+ } else if (r == ADDRESS_ES) {
+ c = NULL;
+ r = DetectAddressCut(de_ctx, cur, new, &c);
+ if (r == -1)
+ goto error;
+
+ DetectAddressInsert(de_ctx, gh, new);
+ if (c != NULL)
+ DetectAddressInsert(de_ctx, gh, c);
+
+ return 1;
+ } else if (r == ADDRESS_EB) {
+ c = NULL;
+ r = DetectAddressCut(de_ctx, cur, new, &c);
+ if (r == -1)
+ goto error;
+
+ DetectAddressInsert(de_ctx, gh, new);
+ if (c != NULL)
+ DetectAddressInsert(de_ctx, gh, c);
+
+ return 1;
+ } else if (r == ADDRESS_LE) {
+ c = NULL;
+ r = DetectAddressCut(de_ctx, cur, new, &c);
+ if (r == -1)
+ goto error;
+
+ DetectAddressInsert(de_ctx, gh, new);
+ if (c != NULL)
+ DetectAddressInsert(de_ctx, gh, c);
+
+ return 1;
+ } else if (r == ADDRESS_GE) {
+ c = NULL;
+ r = DetectAddressCut(de_ctx, cur,new,&c);
+ if (r == -1)
+ goto error;
+
+ DetectAddressInsert(de_ctx, gh, new);
+ if (c != NULL)
+ DetectAddressInsert(de_ctx, gh, c);
+
+ return 1;
+ }
+ }
+
+ /* head is NULL, so get a group and set head to it */
+ } else {
+ head = new;
+ if (SetHeadPtr(gh, head) < 0) {
+ SCLogDebug("SetHeadPtr failed");
+ goto error;
+ }
+ }
+
+ return 1;
+
+error:
+ /* XXX */
+ return -1;
+}
+
+/**
+ * \brief Join two addresses groups together.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param target Pointer to the target address group.
+ * \param source Pointer to the source address group.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressJoin(DetectEngineCtx *de_ctx, DetectAddress *target,
+ DetectAddress *source)
+{
+ DetectPort *port = NULL;
+
+ if (target == NULL || source == NULL)
+ return -1;
+
+ if (target->ip.family != source->ip.family)
+ return -1;
+
+ target->cnt += source->cnt;
+ SigGroupHeadCopySigs(de_ctx, source->sh, &target->sh);
+
+ port = source->port;
+ for ( ; port != NULL; port = port->next)
+ DetectPortInsertCopy(de_ctx, &target->port, port);
+
+ if (target->ip.family == AF_INET)
+ return DetectAddressJoinIPv4(de_ctx, target, source);
+ else if (target->ip.family == AF_INET6)
+ return DetectAddressJoinIPv6(de_ctx, target, source);
+
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Creates a cidr ipv6 netblock, based on the cidr netblock value.
+ *
+ * For example if we send a cidr of 7 as argument, an ipv6 address
+ * mask of the value FE:00:00:00:00:00:00:00 is created and updated
+ * in the argument struct in6_addr *in6.
+ *
+ * \todo I think for the final section: while (cidr > 0), we can simply
+ * replace it with a
+ * if (cidr > 0) {
+ * in6->s6_addr[i] = -1 << (8 - cidr);
+ *
+ * \param cidr The value of the cidr.
+ * \param in6 Pointer to an ipv6 address structure(struct in6_addr) which will
+ * hold the cidr netblock result.
+ */
+static void DetectAddressParseIPv6CIDR(int cidr, struct in6_addr *in6)
+{
+ int i = 0;
+
+ memset(in6, 0, sizeof(struct in6_addr));
+
+ while (cidr > 8) {
+ in6->s6_addr[i] = 0xff;
+ cidr -= 8;
+ i++;
+ }
+
+ while (cidr > 0) {
+ in6->s6_addr[i] |= 0x80;
+ if (--cidr > 0)
+ in6->s6_addr[i] = in6->s6_addr[i] >> 1;
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Parses an ipv4/ipv6 address string and updates the result into the
+ * DetectAddress instance sent as the argument.
+ *
+ * \param dd Pointer to the DetectAddress instance which should be updated with
+ * the address range details from the parsed ip string.
+ * \param str Pointer to address string that has to be parsed.
+ *
+ * \retval 0 On successfully parsing the address string.
+ * \retval -1 On failure.
+ */
+int DetectAddressParseString(DetectAddress *dd, char *str)
+{
+ char *ip = NULL;
+ char *ip2 = NULL;
+ char *mask = NULL;
+ int r = 0;
+ char ipstr[256];
+
+ while (*str != '\0' && *str == ' ')
+ str++;
+
+ /* first handle 'any' */
+ if (strcasecmp(str, "any") == 0) {
+ dd->flags |= ADDRESS_FLAG_ANY;
+ SCLogDebug("address is \'any\'");
+ return 0;
+ }
+
+ strlcpy(ipstr, str, sizeof(ipstr));
+ SCLogDebug("str %s", str);
+
+ /* we work with a copy so that we can put a
+ * nul-termination in it later */
+ ip = ipstr;
+
+ /* handle the negation case */
+ if (ip[0] == '!') {
+ dd->flags |= ADDRESS_FLAG_NOT;
+ ip++;
+ }
+
+ /* see if the address is an ipv4 or ipv6 address */
+ if ((strchr(str, ':')) == NULL) {
+ /* IPv4 Address */
+ struct in_addr in;
+
+ dd->ip.family = AF_INET;
+
+ if ((mask = strchr(ip, '/')) != NULL) {
+ /* 1.2.3.4/xxx format (either dotted or cidr notation */
+ ip[mask - ip] = '\0';
+ mask++;
+ uint32_t ip4addr = 0;
+ uint32_t netmask = 0;
+ size_t u = 0;
+
+ if ((strchr (mask, '.')) == NULL) {
+ /* 1.2.3.4/24 format */
+
+ for (u = 0; u < strlen(mask); u++) {
+ if(!isdigit((unsigned char)mask[u]))
+ goto error;
+ }
+
+ int cidr = atoi(mask);
+ if (cidr < 0 || cidr > 32)
+ goto error;
+
+ netmask = CIDRGet(cidr);
+ } else {
+ /* 1.2.3.4/255.255.255.0 format */
+ r = inet_pton(AF_INET, mask, &in);
+ if (r <= 0)
+ goto error;
+
+ netmask = in.s_addr;
+ }
+
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+
+ ip4addr = in.s_addr;
+
+ dd->ip.addr_data32[0] = dd->ip2.addr_data32[0] = ip4addr & netmask;
+ dd->ip2.addr_data32[0] |=~ netmask;
+ } else if ((ip2 = strchr(ip, '-')) != NULL) {
+ /* 1.2.3.4-1.2.3.6 range format */
+ ip[ip2 - ip] = '\0';
+ ip2++;
+
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+ dd->ip.addr_data32[0] = in.s_addr;
+
+ r = inet_pton(AF_INET, ip2, &in);
+ if (r <= 0)
+ goto error;
+ dd->ip2.addr_data32[0] = in.s_addr;
+
+ /* a > b is illegal, a = b is ok */
+ if (ntohl(dd->ip.addr_data32[0]) > ntohl(dd->ip2.addr_data32[0]))
+ goto error;
+ } else {
+ /* 1.2.3.4 format */
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+ /* single host */
+ dd->ip.addr_data32[0] = in.s_addr;
+ dd->ip2.addr_data32[0] = in.s_addr;
+ }
+ } else {
+ /* IPv6 Address */
+ struct in6_addr in6, mask6;
+ uint32_t ip6addr[4], netmask[4];
+
+ dd->ip.family = AF_INET6;
+
+ if ((mask = strchr(ip, '/')) != NULL) {
+ ip[mask - ip] = '\0';
+ mask++;
+
+ int cidr = atoi(mask);
+ if (cidr < 0 || cidr > 128)
+ goto error;
+
+ r = inet_pton(AF_INET6, ip, &in6);
+ if (r <= 0)
+ goto error;
+ memcpy(&ip6addr, &in6.s6_addr, sizeof(ip6addr));
+
+ DetectAddressParseIPv6CIDR(cidr, &mask6);
+ memcpy(&netmask, &mask6.s6_addr, sizeof(netmask));
+
+ dd->ip2.addr_data32[0] = dd->ip.addr_data32[0] = ip6addr[0] & netmask[0];
+ dd->ip2.addr_data32[1] = dd->ip.addr_data32[1] = ip6addr[1] & netmask[1];
+ dd->ip2.addr_data32[2] = dd->ip.addr_data32[2] = ip6addr[2] & netmask[2];
+ dd->ip2.addr_data32[3] = dd->ip.addr_data32[3] = ip6addr[3] & netmask[3];
+
+ dd->ip2.addr_data32[0] |=~ netmask[0];
+ dd->ip2.addr_data32[1] |=~ netmask[1];
+ dd->ip2.addr_data32[2] |=~ netmask[2];
+ dd->ip2.addr_data32[3] |=~ netmask[3];
+ } else if ((ip2 = strchr(ip, '-')) != NULL) {
+ /* 2001::1-2001::4 range format */
+ ip[ip2 - ip] = '\0';
+ ip2++;
+
+ r = inet_pton(AF_INET6, ip, &in6);
+ if (r <= 0)
+ goto error;
+ memcpy(&dd->ip.address, &in6.s6_addr, sizeof(ip6addr));
+
+ r = inet_pton(AF_INET6, ip2, &in6);
+ if (r <= 0)
+ goto error;
+ memcpy(&dd->ip2.address, &in6.s6_addr, sizeof(ip6addr));
+
+ /* a > b is illegal, a=b is ok */
+ if (AddressIPv6Gt(&dd->ip, &dd->ip2))
+ goto error;
+ } else {
+ r = inet_pton(AF_INET6, ip, &in6);
+ if (r <= 0)
+ goto error;
+
+ memcpy(&dd->ip.address, &in6.s6_addr, sizeof(dd->ip.address));
+ memcpy(&dd->ip2.address, &in6.s6_addr, sizeof(dd->ip2.address));
+ }
+
+ }
+
+ BUG_ON(dd->ip.family == 0);
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Simply parse an address and return a DetectAddress instance containing
+ * the address ranges of the parsed ip addressstring
+ *
+ * \param str Pointer to a character string containing the ip address
+ *
+ * \retval dd Pointer to the DetectAddress instance containing the address
+ * range details from the parsed ip string
+ */
+static DetectAddress *DetectAddressParseSingle(char *str)
+{
+ DetectAddress *dd;
+
+ SCLogDebug("str %s", str);
+
+ dd = DetectAddressInit();
+ if (dd == NULL)
+ goto error;
+
+ if (DetectAddressParseString(dd, str) < 0) {
+ SCLogDebug("AddressParse failed");
+ goto error;
+ }
+
+ return dd;
+
+error:
+ if (dd != NULL)
+ DetectAddressFree(dd);
+ return NULL;
+}
+
+/**
+ * \brief Setup a single address string, parse it and add the resulting
+ * Address-Range(s) to the AddessHead(DetectAddressHead instance).
+ *
+ * \param gh Pointer to the Address-Head(DetectAddressHead) to which the
+ * resulting Address-Range(s) from the parsed ip string has to
+ * be added.
+ * \param s Pointer to the ip address string to be parsed.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressSetup(DetectAddressHead *gh, char *s)
+{
+ DetectAddress *ad = NULL;
+ DetectAddress *ad2 = NULL;
+ int r = 0;
+ char any = FALSE;
+
+ SCLogDebug("gh %p, s %s", gh, s);
+
+ /* parse the address */
+ ad = DetectAddressParseSingle(s);
+ if (ad == NULL) {
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC,
+ "failed to parse address \"%s\"", s);
+ return -1;
+ }
+
+ if (ad->flags & ADDRESS_FLAG_ANY)
+ any = TRUE;
+
+ /* handle the not case, we apply the negation then insert the part(s) */
+ if (ad->flags & ADDRESS_FLAG_NOT) {
+ ad2 = NULL;
+
+ if (DetectAddressCutNot(ad, &ad2) < 0) {
+ SCLogDebug("DetectAddressCutNot failed");
+ goto error;
+ }
+
+ /* normally a 'not' will result in two ad's unless the 'not' is on the start or end
+ * of the address space (e.g. 0.0.0.0 or 255.255.255.255). */
+ if (ad2 != NULL) {
+ if (DetectAddressInsert(NULL, gh, ad2) < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ }
+ }
+
+ r = DetectAddressInsert(NULL, gh, ad);
+ if (r < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ SCLogDebug("r %d",r);
+
+ /* if any, insert 0.0.0.0/0 and ::/0 as well */
+ if (r == 1 && any == TRUE) {
+ SCLogDebug("adding 0.0.0.0/0 and ::/0 as we\'re handling \'any\'");
+
+ ad = DetectAddressParseSingle("0.0.0.0/0");
+ if (ad == NULL)
+ goto error;
+
+ BUG_ON(ad->ip.family == 0);
+
+ if (DetectAddressInsert(NULL, gh, ad) < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ ad = DetectAddressParseSingle("::/0");
+ if (ad == NULL)
+ goto error;
+
+ BUG_ON(ad->ip.family == 0);
+
+ if (DetectAddressInsert(NULL, gh, ad) < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ }
+ return 0;
+
+error:
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC, "DetectAddressSetup error");
+ /* XXX cleanup */
+ return -1;
+}
+
+/**
+ * \brief Parses an address string and updates the 2 address heads with the
+ * address data.
+ *
+ * \todo We don't seem to be handling negated cases, like [addr,![!addr,addr]],
+ * since we pass around negate without keeping a count of ! with depth.
+ * Can solve this by keeping a count of the negations with depth, so that
+ * an even no of negations would count as no negation and an odd no of
+ * negations would count as a negation.
+ *
+ * \param gh Pointer to the address head that should hold address ranges
+ * that are not negated.
+ * \param ghn Pointer to the address head that should hold address ranges
+ * that are negated.
+ * \param s Pointer to the character string holding the address to be
+ * parsed.
+ * \param negate Flag that indicates if the receieved address string is negated
+ * or not. 0 if it is not, 1 it it is.
+ *
+ * \retval 0 On successfully parsing.
+ * \retval -1 On failure.
+ */
+static int DetectAddressParse2(const DetectEngineCtx *de_ctx,
+ DetectAddressHead *gh, DetectAddressHead *ghn,
+ char *s, int negate)
+{
+ size_t x = 0;
+ size_t u = 0;
+ int o_set = 0, n_set = 0, d_set = 0;
+ int depth = 0;
+ size_t size = strlen(s);
+ char address[8196] = "";
+ char *rule_var_address = NULL;
+ char *temp_rule_var_address = NULL;
+
+ SCLogDebug("s %s negate %s", s, negate ? "true" : "false");
+
+ for (u = 0, x = 0; u < size && x < sizeof(address); u++) {
+ if (x == (sizeof(address) - 1)) {
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC, "Hit the address buffer"
+ " limit for the supplied address. Invalidating sig. "
+ "Please file a bug report on this.");
+ goto error;
+ }
+ address[x] = s[u];
+ x++;
+
+ if (!o_set && s[u] == '!') {
+ n_set = 1;
+ x--;
+ } else if (s[u] == '[') {
+ if (!o_set) {
+ o_set = 1;
+ x = 0;
+ }
+ depth++;
+ } else if (s[u] == ']') {
+ if (depth == 1) {
+ address[x - 1] = '\0';
+ x = 0;
+ SCLogDebug("address %s negate %d, n_set %d", address, negate, n_set);
+ if (((negate + n_set) % 2) == 0) {
+ /* normal block */
+ SCLogDebug("normal block");
+
+ if (DetectAddressParse2(de_ctx, gh, ghn, address, (negate + n_set) % 2) < 0)
+ goto error;
+ } else {
+ /* negated block
+ *
+ * Extra steps are necessary. First consider it as a normal
+ * (non-negated) range. Merge the + and - ranges if
+ * applicable. Then insert the result into the ghn list. */
+ SCLogDebug("negated block");
+
+ DetectAddressHead tmp_gh = { NULL, NULL, NULL };
+ DetectAddressHead tmp_ghn = { NULL, NULL, NULL };
+
+ if (DetectAddressParse2(de_ctx, &tmp_gh, &tmp_ghn, address, 0) < 0)
+ goto error;
+
+ DetectAddress *tmp_ad;
+ DetectAddress *tmp_ad2;
+#ifdef DEBUG
+ SCLogDebug("tmp_gh: IPv4");
+ for (tmp_ad = tmp_gh.ipv4_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+ SCLogDebug("tmp_ghn: IPv4");
+ for (tmp_ad = tmp_ghn.ipv4_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+ SCLogDebug("tmp_gh: IPv6");
+ for (tmp_ad = tmp_gh.ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+ SCLogDebug("tmp_ghn: IPv6");
+ for (tmp_ad = tmp_ghn.ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+#endif
+ if (DetectAddressMergeNot(&tmp_gh, &tmp_ghn) < 0)
+ goto error;
+
+ SCLogDebug("merged succesfully");
+
+ /* insert the IPv4 addresses into the negated list */
+ for (tmp_ad = tmp_gh.ipv4_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ /* work with a copy of the address group */
+ tmp_ad2 = DetectAddressCopy(tmp_ad);
+ if (tmp_ad2 == NULL) {
+ SCLogDebug("DetectAddressCopy failed");
+ goto error;
+ }
+ DetectAddressPrint(tmp_ad2);
+ DetectAddressInsert(NULL, ghn, tmp_ad2);
+ }
+
+ /* insert the IPv6 addresses into the negated list */
+ for (tmp_ad = tmp_gh.ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ /* work with a copy of the address group */
+ tmp_ad2 = DetectAddressCopy(tmp_ad);
+ if (tmp_ad2 == NULL) {
+ SCLogDebug("DetectAddressCopy failed");
+ goto error;
+ }
+ DetectAddressPrint(tmp_ad2);
+ DetectAddressInsert(NULL, ghn, tmp_ad2);
+ }
+
+ DetectAddressHeadCleanup(&tmp_gh);
+ DetectAddressHeadCleanup(&tmp_ghn);
+ }
+ n_set = 0;
+ }
+ depth--;
+ } else if (depth == 0 && s[u] == ',') {
+ if (o_set == 1) {
+ o_set = 0;
+ } else if (d_set == 1) {
+ address[x - 1] = '\0';
+
+ rule_var_address = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_ADDRESS_GROUPS);
+ if (rule_var_address == NULL)
+ goto error;
+ if (strlen(rule_var_address) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "variable %s resolved "
+ "to nothing. This is likely a misconfiguration. "
+ "Note that a negated address needs to be quoted, "
+ "\"!$HOME_NET\" instead of !$HOME_NET. See issue #295.", s);
+ goto error;
+ }
+ SCLogDebug("rule_var_address %s", rule_var_address);
+ temp_rule_var_address = rule_var_address;
+ if ((negate + n_set) % 2) {
+ temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3);
+ if (unlikely(temp_rule_var_address == NULL))
+ goto error;
+ snprintf(temp_rule_var_address, strlen(rule_var_address) + 3,
+ "[%s]", rule_var_address);
+ }
+ DetectAddressParse2(de_ctx, gh, ghn, temp_rule_var_address,
+ (negate + n_set) % 2);
+ d_set = 0;
+ n_set = 0;
+ if (temp_rule_var_address != rule_var_address)
+ SCFree(temp_rule_var_address);
+ } else {
+ address[x - 1] = '\0';
+
+ if (!((negate + n_set) % 2)) {
+ SCLogDebug("DetectAddressSetup into gh, %s", address);
+ if (DetectAddressSetup(gh, address) < 0)
+ goto error;
+ } else {
+ SCLogDebug("DetectAddressSetup into ghn, %s", address);
+ if (DetectAddressSetup(ghn, address) < 0)
+ goto error;
+ }
+ n_set = 0;
+ }
+ x = 0;
+ } else if (depth == 0 && s[u] == '$') {
+ d_set = 1;
+ } else if (depth == 0 && u == size - 1) {
+ if (x == sizeof(address)) {
+ address[x - 1] = '\0';
+ } else {
+ address[x] = '\0';
+ }
+ x = 0;
+
+ if (d_set == 1) {
+ rule_var_address = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_ADDRESS_GROUPS);
+ if (rule_var_address == NULL)
+ goto error;
+ if (strlen(rule_var_address) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "variable %s resolved "
+ "to nothing. This is likely a misconfiguration. "
+ "Note that a negated address needs to be quoted, "
+ "\"!$HOME_NET\" instead of !$HOME_NET. See issue #295.", s);
+ goto error;
+ }
+ SCLogDebug("rule_var_address %s", rule_var_address);
+ temp_rule_var_address = rule_var_address;
+ if ((negate + n_set) % 2) {
+ temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3);
+ if (unlikely(temp_rule_var_address == NULL))
+ goto error;
+ snprintf(temp_rule_var_address, strlen(rule_var_address) + 3,
+ "[%s]", rule_var_address);
+ }
+ if (DetectAddressParse2(de_ctx, gh, ghn, temp_rule_var_address,
+ (negate + n_set) % 2) < 0) {
+ SCLogDebug("DetectAddressParse2 hates us");
+ goto error;
+ }
+ d_set = 0;
+ if (temp_rule_var_address != rule_var_address)
+ SCFree(temp_rule_var_address);
+ } else {
+ if (!((negate + n_set) % 2)) {
+ SCLogDebug("DetectAddressSetup into gh, %s", address);
+ if (DetectAddressSetup(gh, address) < 0) {
+ SCLogDebug("DetectAddressSetup gh fail");
+ goto error;
+ }
+ } else {
+ SCLogDebug("DetectAddressSetup into ghn, %s", address);
+ if (DetectAddressSetup(ghn, address) < 0) {
+ SCLogDebug("DetectAddressSetup ghn fail");
+ goto error;
+ }
+ }
+ }
+ n_set = 0;
+ }
+ }
+ if (depth > 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not every address block was "
+ "properly closed in \"%s\", %d missing closing brackets (]). "
+ "Note: problem might be in a variable.", s, depth);
+ goto error;
+ } else if (depth < 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not every address block was "
+ "properly opened in \"%s\", %d missing opening brackets ([). "
+ "Note: problem might be in a variable.", s, depth*-1);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief See if the addresses and ranges in an address head cover the
+ * entire ip space.
+ *
+ * \param gh Pointer to the DetectAddressHead to check.
+ *
+ * \retval 0 No.
+ * \retval 1 Yes.
+ *
+ * \todo do the same for IPv6
+ */
+static int DetectAddressIsCompleteIPSpace(DetectAddressHead *gh)
+{
+ int r = DetectAddressIsCompleteIPSpaceIPv4(gh->ipv4_head);
+ if (r == 1)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Merge the + and the - list (+ positive match, - 'not' match)
+ *
+ * \param gh Pointer to the address head containing the non-NOT groups.
+ * \param ghn Pointer to the address head containing the NOT groups.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressMergeNot(DetectAddressHead *gh, DetectAddressHead *ghn)
+{
+ DetectAddress *ad;
+ DetectAddress *ag, *ag2;
+ int r = 0;
+
+ SCLogDebug("gh->ipv4_head %p, ghn->ipv4_head %p", gh->ipv4_head,
+ ghn->ipv4_head);
+
+ /* check if the negated list covers the entire ip space. If so
+ * the user screwed up the rules/vars. */
+ if (DetectAddressIsCompleteIPSpace(ghn) == 1) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Complete IP space negated. "
+ "Rule address range is NIL. Probably have a !any or "
+ "an address range that supplies a NULL address range");
+ goto error;
+ }
+
+ /* step 0: if the gh list is empty, but the ghn list isn't we have a pure
+ * not thingy. In that case we add a 0.0.0.0/0 first. */
+ if (gh->ipv4_head == NULL && ghn->ipv4_head != NULL) {
+ r = DetectAddressSetup(gh, "0.0.0.0/0");
+ if (r < 0) {
+ SCLogDebug("DetectAddressSetup for 0.0.0.0/0 failed");
+ goto error;
+ }
+ }
+ /* ... or ::/0 for ipv6 */
+ if (gh->ipv6_head == NULL && ghn->ipv6_head != NULL) {
+ r = DetectAddressSetup(gh, "::/0");
+ if (r < 0) {
+ SCLogDebug("DetectAddressSetup for ::/0 failed");
+ goto error;
+ }
+ }
+
+ /* step 1: insert our ghn members into the gh list */
+ for (ag = ghn->ipv4_head; ag != NULL; ag = ag->next) {
+ /* work with a copy of the ad so we can easily clean up the ghn group
+ * later. */
+ ad = DetectAddressCopy(ag);
+ if (ad == NULL) {
+ SCLogDebug("DetectAddressCopy failed");
+ goto error;
+ }
+
+ r = DetectAddressInsert(NULL, gh, ad);
+ if (r < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ }
+ /* ... and the same for ipv6 */
+ for (ag = ghn->ipv6_head; ag != NULL; ag = ag->next) {
+ /* work with a copy of the ad so we can easily clean up the ghn group
+ * later. */
+ ad = DetectAddressCopy(ag);
+ if (ad == NULL) {
+ SCLogDebug("DetectAddressCopy failed");
+ goto error;
+ }
+
+ r = DetectAddressInsert(NULL, gh, ad);
+ if (r < 0) {
+ SCLogDebug("DetectAddressInsert failed");
+ goto error;
+ }
+ }
+#ifdef DEBUG
+ DetectAddress *tmp_ad;
+ for (tmp_ad = gh->ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+#endif
+ int ipv4_applied = 0;
+ int ipv6_applied = 0;
+
+ /* step 2: pull the address blocks that match our 'not' blocks */
+ for (ag = ghn->ipv4_head; ag != NULL; ag = ag->next) {
+ SCLogDebug("ag %p", ag);
+ DetectAddressPrint(ag);
+
+ int applied = 0;
+ for (ag2 = gh->ipv4_head; ag2 != NULL; ) {
+ SCLogDebug("ag2 %p", ag2);
+ DetectAddressPrint(ag2);
+
+ r = DetectAddressCmp(ag, ag2);
+ /* XXX more ??? */
+ if (r == ADDRESS_EQ || r == ADDRESS_EB) {
+ if (ag2->prev == NULL)
+ gh->ipv4_head = ag2->next;
+ else
+ ag2->prev->next = ag2->next;
+
+ if (ag2->next != NULL)
+ ag2->next->prev = ag2->prev;
+
+ /* store the next ptr and remove the group */
+ DetectAddress *next_ag2 = ag2->next;
+ DetectAddressFree(ag2);
+ ag2 = next_ag2;
+ applied = 1;
+ } else {
+ ag2 = ag2->next;
+ }
+ }
+
+ if (applied) {
+ ipv4_applied++;
+ }
+ }
+ /* ... and the same for ipv6 */
+ for (ag = ghn->ipv6_head; ag != NULL; ag = ag->next) {
+ int applied = 0;
+ for (ag2 = gh->ipv6_head; ag2 != NULL; ) {
+ r = DetectAddressCmp(ag, ag2);
+ if (r == ADDRESS_EQ || r == ADDRESS_EB) { /* XXX more ??? */
+ if (ag2->prev == NULL)
+ gh->ipv6_head = ag2->next;
+ else
+ ag2->prev->next = ag2->next;
+
+ if (ag2->next != NULL)
+ ag2->next->prev = ag2->prev;
+
+ /* store the next ptr and remove the group */
+ DetectAddress *next_ag2 = ag2->next;
+ DetectAddressFree(ag2);
+ ag2 = next_ag2;
+
+ SCLogDebug("applied");
+ applied = 1;
+ } else {
+ ag2 = ag2->next;
+ }
+ }
+ if (applied) {
+ ipv6_applied++;
+ }
+ }
+#ifdef DEBUG
+ for (tmp_ad = gh->ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+ for (tmp_ad = ghn->ipv6_head; tmp_ad; tmp_ad = tmp_ad->next) {
+ DetectAddressPrint(tmp_ad);
+ }
+#endif
+ if (ghn->ipv4_head != NULL || ghn->ipv6_head != NULL) {
+ int cnt = 0;
+ DetectAddress *ad;
+ for (ad = ghn->ipv4_head; ad; ad = ad->next)
+ cnt++;
+
+ if (ipv4_applied != cnt) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not all IPv4 negations "
+ "could be applied: %d != %d", cnt, ipv4_applied);
+ goto error;
+ }
+
+ cnt = 0;
+ for (ad = ghn->ipv6_head; ad; ad = ad->next)
+ cnt++;
+
+ if (ipv6_applied != cnt) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not all IPv6 negations "
+ "could be applied: %d != %d", cnt, ipv6_applied);
+ goto error;
+ }
+ }
+
+ /* if the result is that we have no addresses we return error */
+ if (gh->ipv4_head == NULL && gh->ipv6_head == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "no addresses left after "
+ "merging addresses and negated addresses");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+int DetectAddressTestConfVars(void)
+{
+ SCLogDebug("Testing address conf vars for any misconfigured values");
+
+ ConfNode *address_vars_node = ConfGetNode("vars.address-groups");
+ if (address_vars_node == NULL) {
+ return 0;
+ }
+
+ ConfNode *seq_node;
+ TAILQ_FOREACH(seq_node, &address_vars_node->head, next) {
+ SCLogDebug("Testing %s - %s", seq_node->name, seq_node->val);
+
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh == NULL) {
+ goto error;
+ }
+ DetectAddressHead *ghn = DetectAddressHeadInit();
+ if (ghn == NULL) {
+ goto error;
+ }
+
+ if (seq_node->val == NULL) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "Address var \"%s\" probably has a sequence(something "
+ "in brackets) value set without any quotes. Please "
+ "quote it using \"..\".", seq_node->name);
+ goto error;
+ }
+
+ int r = DetectAddressParse2(NULL, gh, ghn, seq_node->val, /* start with negate no */0);
+ if (r < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "failed to parse address var \"%s\" with value \"%s\". "
+ "Please check it's syntax", seq_node->name, seq_node->val);
+ goto error;
+ }
+
+ if (DetectAddressIsCompleteIPSpace(ghn)) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "address var - \"%s\" has the complete IP space negated "
+ "with it's value \"%s\". Rule address range is NIL. "
+ "Probably have a !any or an address range that supplies "
+ "a NULL address range", seq_node->name, seq_node->val);
+ goto error;
+ }
+
+ if (gh != NULL)
+ DetectAddressHeadFree(gh);
+ if (ghn != NULL)
+ DetectAddressHeadFree(ghn);
+ }
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Parses an address group sent as a character string and updates the
+ * DetectAddressHead sent as the argument with the relevant address
+ * ranges from the parsed string.
+ *
+ * \param gh Pointer to the DetectAddressHead.
+ * \param str Pointer to the character string containing the address group
+ * that has to be parsed.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressParse(const DetectEngineCtx *de_ctx,
+ DetectAddressHead *gh, char *str)
+{
+ int r;
+ DetectAddressHead *ghn = NULL;
+
+ SCLogDebug("gh %p, str %s", gh, str);
+
+ if (str == NULL) {
+ SCLogDebug("DetectAddressParse can not be run with NULL address");
+ goto error;
+ }
+
+ ghn = DetectAddressHeadInit();
+ if (ghn == NULL) {
+ SCLogDebug("DetectAddressHeadInit for ghn failed");
+ goto error;
+ }
+
+ r = DetectAddressParse2(de_ctx, gh, ghn, str, /* start with negate no */0);
+ if (r < 0) {
+ SCLogDebug("DetectAddressParse2 returned %d", r);
+ goto error;
+ }
+
+ SCLogDebug("gh->ipv4_head %p, ghn->ipv4_head %p", gh->ipv4_head,
+ ghn->ipv4_head);
+
+ /* merge the 'not' address groups */
+ if (DetectAddressMergeNot(gh, ghn) < 0) {
+ SCLogDebug("DetectAddressMergeNot failed");
+ goto error;
+ }
+
+ /* free the temp negate head */
+ DetectAddressHeadFree(ghn);
+ return 0;
+
+error:
+ if (ghn != NULL)
+ DetectAddressHeadFree(ghn);
+ return -1;
+}
+
+/**
+ * \brief Returns a new instance of DetectAddressHead.
+ *
+ * \retval gh Pointer to the new instance of DetectAddressHead.
+ */
+DetectAddressHead *DetectAddressHeadInit(void)
+{
+ DetectAddressHead *gh = SCMalloc(sizeof(DetectAddressHead));
+ if (unlikely(gh == NULL))
+ return NULL;
+ memset(gh, 0, sizeof(DetectAddressHead));
+
+#ifdef DEBUG
+ detect_address_group_head_init_cnt++;
+ detect_address_group_head_memory += sizeof(DetectAddressHead);
+#endif
+
+ return gh;
+}
+
+/**
+ * \brief Cleans a DetectAddressHead. The functions frees the 3 address
+ * group heads(any, ipv4 and ipv6) inside the DetectAddressHead
+ * instance.
+ *
+ * \param gh Pointer to the DetectAddressHead instance that has to be
+ * cleaned.
+ */
+void DetectAddressHeadCleanup(DetectAddressHead *gh)
+{
+ if (gh != NULL) {
+ if (gh->any_head != NULL) {
+ DetectAddressCleanupList(gh->any_head);
+ gh->any_head = NULL;
+ }
+ if (gh->ipv4_head != NULL) {
+ DetectAddressCleanupList(gh->ipv4_head);
+ gh->ipv4_head = NULL;
+ }
+ if (gh->ipv6_head != NULL) {
+ DetectAddressCleanupList(gh->ipv6_head);
+ gh->ipv6_head = NULL;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Frees a DetectAddressHead instance.
+ *
+ * \param gh Pointer to the DetectAddressHead instance to be freed.
+ */
+void DetectAddressHeadFree(DetectAddressHead *gh)
+{
+ if (gh != NULL) {
+ DetectAddressHeadCleanup(gh);
+ SCFree(gh);
+#ifdef DEBUG
+ detect_address_group_head_free_cnt++;
+ detect_address_group_head_memory -= sizeof(DetectAddressHead);
+#endif
+ }
+
+ return;
+}
+
+/**
+ * \brief Dispatcher function that calls the ipv4 and ipv6 address cut functions.
+ * Have a look at DetectAddressCutIPv4() and DetectAddressCutIPv6() for
+ * explanations on what these functions do.
+ *
+ * \param de_ctx Pointer to the DetectEngineCtx.
+ * \param a Pointer the the first address to be cut.
+ * \param b Pointer to the second address to be cut.
+ * \param c Pointer to a pointer to a third DetectAddressData, in case the
+ * ranges from a and b, demand a third address range.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressCut(DetectEngineCtx *de_ctx, DetectAddress *a,
+ DetectAddress *b, DetectAddress **c)
+{
+ if (a->ip.family == AF_INET)
+ return DetectAddressCutIPv4(de_ctx, a, b, c);
+ else if (a->ip.family == AF_INET6)
+ return DetectAddressCutIPv6(de_ctx, a, b, c);
+
+ return -1;
+}
+
+/**
+ * \brief Cuts a negated address range with respect to the entire ip range, and
+ * supplies with the address range that doesn't belong to the negated
+ * address range.
+ *
+ * There are 2 cases here -
+ *
+ * The first case includes the address being located at the extreme ends
+ * of the ip space, in which we get a single range.
+ * For example: !0.0.0.0, in which case we get 0.0.0.1 to 255.255.255.255.
+ *
+ * The second case includes the address not present at either of the
+ * ip space extremes, in which case we get 2 ranges. The second range
+ * would be supplied back with the argument "b" supplied to this function.
+ * For example: !10.20.30.40, in which case we the 2 ranges, 0.0.0.0 -
+ * 10.20.30.39 and 10.20.30.41 - 255.255.255.255.
+ *
+ * The above negation cases can similarly be extended to ranges, i.e.
+ * ![0.0.0.0 - 10.20.30.40], ![255.255.240.240 - 255.255.255.255] and
+ * ![10.20.30.40 - 10.20.30.50].
+ *
+ *
+ * \param a Pointer to the DetectAddressData instance, that contains the negated
+ * address range that has to be cut.
+ * \param b Pointer to a pointer to a DetectAddressData instance, that should be
+ * filled with the address range, if the argument "a", doesn't fall at
+ * the extreme ends of the ip address space.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectAddressCutNot(DetectAddress *a, DetectAddress **b)
+{
+ if (a->ip.family == AF_INET)
+ return DetectAddressCutNotIPv4(a, b);
+ else if (a->ip.family == AF_INET6)
+ return DetectAddressCutNotIPv6(a, b);
+
+ return -1;
+}
+
+/**
+ * \brief Used to compare 2 address ranges.
+ *
+ * \param a Pointer to the first DetectAddressData to be compared.
+ * \param b Pointer to the second DetectAddressData to be compared.
+ */
+int DetectAddressCmp(DetectAddress *a, DetectAddress *b)
+{
+ if (a->ip.family != b->ip.family)
+ return ADDRESS_ER;
+
+ /* check any */
+ if ((a->flags & ADDRESS_FLAG_ANY) && (b->flags & ADDRESS_FLAG_ANY))
+ return ADDRESS_EQ;
+ else if (a->ip.family == AF_INET)
+ return DetectAddressCmpIPv4(a, b);
+ else if (a->ip.family == AF_INET6)
+ return DetectAddressCmpIPv6(a, b);
+
+ return ADDRESS_ER;
+}
+
+/**
+ * \brief Match a packets address against a signatures addrs array
+ *
+ * \param addrs array of DetectMatchAddressIPv4's
+ * \param addrs_cnt array size in members
+ * \param a packets address
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \note addresses in addrs are in host order
+ *
+ * \todo array should be ordered, so we can break out of the loop
+ */
+int DetectAddressMatchIPv4(DetectMatchAddressIPv4 *addrs, uint16_t addrs_cnt, Address *a)
+{
+ SCEnter();
+
+ if (addrs == NULL || addrs_cnt == 0) {
+ SCReturnInt(0);
+ }
+
+ uint16_t idx;
+ for (idx = 0; idx < addrs_cnt; idx++) {
+ if (ntohl(a->addr_data32[0]) >= addrs[idx].ip &&
+ ntohl(a->addr_data32[0]) <= addrs[idx].ip2)
+ {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Match a packets address against a signatures addrs array
+ *
+ * \param addrs array of DetectMatchAddressIPv6's
+ * \param addrs_cnt array size in members
+ * \param a packets address
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \note addresses in addrs are in host order
+ *
+ * \todo array should be ordered, so we can break out of the loop
+ */
+int DetectAddressMatchIPv6(DetectMatchAddressIPv6 *addrs, uint16_t addrs_cnt, Address *a)
+{
+ SCEnter();
+
+ if (addrs == NULL || addrs_cnt == 0) {
+ SCReturnInt(0);
+ }
+
+ uint16_t idx;
+ int i = 0;
+ uint16_t result1, result2;
+
+ /* See if the packet address is within the range of any entry in the
+ * signature's address match array.
+ */
+ for (idx = 0; idx < addrs_cnt; idx++) {
+ result1 = result2 = 0;
+
+ /* See if packet address equals either limit. Return 1 if true. */
+ if (ntohl(a->addr_data32[0]) == addrs[idx].ip[0] &&
+ ntohl(a->addr_data32[1]) == addrs[idx].ip[1] &&
+ ntohl(a->addr_data32[2]) == addrs[idx].ip[2] &&
+ ntohl(a->addr_data32[3]) == addrs[idx].ip[3])
+ {
+ SCReturnInt(1);
+ }
+ if (ntohl(a->addr_data32[0]) == addrs[idx].ip2[0] &&
+ ntohl(a->addr_data32[1]) == addrs[idx].ip2[1] &&
+ ntohl(a->addr_data32[2]) == addrs[idx].ip2[2] &&
+ ntohl(a->addr_data32[3]) == addrs[idx].ip2[3])
+ {
+ SCReturnInt(1);
+ }
+
+ /* See if packet address is greater than lower limit
+ * of the current signature address match pair.
+ */
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a->addr_data32[i]) > addrs[idx].ip[i]) {
+ result1 = 1;
+ break;
+ }
+ if (ntohl(a->addr_data32[i]) < addrs[idx].ip[i]) {
+ result1 = 0;
+ break;
+ }
+ }
+
+ /* If not greater than lower limit, try next address match entry */
+ if (result1 == 0)
+ continue;
+
+ /* See if packet address is less than upper limit
+ * of the current signature address match pair.
+ */
+ for (i = 0; i < 4; i++) {
+ if (ntohl(a->addr_data32[i]) < addrs[idx].ip2[i]) {
+ result2 = 1;
+ break;
+ }
+ if (ntohl(a->addr_data32[i]) > addrs[idx].ip2[i]) {
+ result2 = 0;
+ break;
+ }
+ }
+
+ /* Return a match if packet address is between the two
+ * signature address match limits.
+ */
+ if (result1 == 1 && result2 == 1)
+ SCReturnInt(1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Check if a particular address(ipv4 or ipv6) matches the address
+ * range in the DetectAddress instance.
+ *
+ * We basically check that the address falls inbetween the address
+ * range in DetectAddress.
+ *
+ * \param dd Pointer to the DetectAddress instance.
+ * \param a Pointer to an Address instance.
+ *
+ * \param 1 On a match.
+ * \param 0 On no match.
+ */
+int DetectAddressMatch(DetectAddress *dd, Address *a)
+{
+ SCEnter();
+
+ if (dd->ip.family != a->family) {
+ SCReturnInt(0);
+ }
+
+ //DetectAddressPrint(dd);
+ //AddressDebugPrint(a);
+
+ switch (a->family) {
+ case AF_INET:
+
+ /* XXX figure out a way to not need to do this ntohl if we switch to
+ * Address inside DetectAddressData we can do uint8_t checks */
+ if (ntohl(a->addr_data32[0]) >= ntohl(dd->ip.addr_data32[0]) &&
+ ntohl(a->addr_data32[0]) <= ntohl(dd->ip2.addr_data32[0]))
+ {
+ SCReturnInt(1);
+ } else {
+ SCReturnInt(0);
+ }
+
+ break;
+ case AF_INET6:
+ if (AddressIPv6Ge(a, &dd->ip) == 1 &&
+ AddressIPv6Le(a, &dd->ip2) == 1)
+ {
+ SCReturnInt(1);
+ } else {
+ SCReturnInt(0);
+ }
+
+ break;
+ default:
+ SCLogDebug("What other address type can we have :-/");
+ break;
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Prints the address data held by the DetectAddress. If the
+ * address data family is any, we print "ANY". If the address data
+ * family is IPv4, we print the the ipv4 address and mask, and if the
+ * address data family is IPv6, we print the ipv6 address and mask.
+ *
+ * \param ad Pointer to the DetectAddress instance to be printed.
+ */
+void DetectAddressPrint(DetectAddress *gr)
+{
+ if (gr == NULL)
+ return;
+
+ if (gr->flags & ADDRESS_FLAG_ANY) {
+ SCLogDebug("ANY");
+ } else if (gr->ip.family == AF_INET) {
+ struct in_addr in;
+ char ip[16], mask[16];
+
+ memcpy(&in, &gr->ip.addr_data32[0], sizeof(in));
+ PrintInet(AF_INET, &in, ip, sizeof(ip));
+ memcpy(&in, &gr->ip2.addr_data32[0], sizeof(in));
+ PrintInet(AF_INET, &in, mask, sizeof(mask));
+
+ SCLogDebug("%s/%s", ip, mask);
+// printf("%s/%s", ip, mask);
+ } else if (gr->ip.family == AF_INET6) {
+ struct in6_addr in6;
+ char ip[66], mask[66];
+
+ memcpy(&in6, &gr->ip.addr_data32, sizeof(in6));
+ PrintInet(AF_INET6, &in6, ip, sizeof(ip));
+ memcpy(&in6, &gr->ip2.addr_data32, sizeof(in6));
+ PrintInet(AF_INET6, &in6, mask, sizeof(mask));
+
+ SCLogDebug("%s/%s", ip, mask);
+// printf("%s/%s", ip, mask);
+ }
+
+ return;
+}
+
+/**
+ * \brief Find the group matching address in a group head.
+ *
+ * \param gh Pointer to the address group head(DetectAddressHead instance).
+ * \param a Pointer to an Address instance.
+ *
+ * \retval g On success pointer to an DetectAddress if we find a match
+ * for the Address "a", in the DetectAddressHead "gh".
+ */
+DetectAddress *DetectAddressLookupInHead(DetectAddressHead *gh, Address *a)
+{
+ SCEnter();
+
+ DetectAddress *g;
+
+ if (gh == NULL) {
+ SCReturnPtr(NULL, "DetectAddress");
+ }
+
+ /* XXX should we really do this check every time we run this function? */
+ if (a->family == AF_INET) {
+ SCLogDebug("IPv4");
+ g = gh->ipv4_head;
+ } else if (a->family == AF_INET6) {
+ SCLogDebug("IPv6");
+ g = gh->ipv6_head;
+ } else {
+ SCLogDebug("ANY");
+ g = gh->any_head;
+ }
+
+ for ( ; g != NULL; g = g->next) {
+ if (DetectAddressMatch(g,a) == 1) {
+ SCReturnPtr(g, "DetectAddress");
+ }
+ }
+
+ SCReturnPtr(NULL, "DetectAddress");
+}
+
+/********************************Unittests*************************************/
+
+#ifdef UNITTESTS
+
+static int UTHValidateDetectAddress(DetectAddress *ad, const char *one, const char *two)
+{
+ char str1[46] = "", str2[46] = "";
+
+ if (ad == NULL)
+ return FALSE;
+
+ switch(ad->ip.family) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)&ad->ip.addr_data32[0], str1, sizeof(str1));
+ SCLogDebug("%s", str1);
+ PrintInet(AF_INET, (const void *)&ad->ip2.addr_data32[0], str2, sizeof(str2));
+ SCLogDebug("%s", str2);
+
+ if (strcmp(str1, one) != 0) {
+ SCLogInfo("%s != %s", str1, one);
+ return FALSE;
+ }
+
+ if (strcmp(str2, two) != 0) {
+ SCLogInfo("%s != %s", str2, two);
+ return FALSE;
+ }
+
+ return TRUE;
+ break;
+
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)&ad->ip.addr_data32[0], str1, sizeof(str1));
+ SCLogDebug("%s", str1);
+ PrintInet(AF_INET6, (const void *)&ad->ip2.addr_data32[0], str2, sizeof(str2));
+ SCLogDebug("%s", str2);
+
+ if (strcmp(str1, one) != 0) {
+ SCLogInfo("%s != %s", str1, one);
+ return FALSE;
+ }
+
+ if (strcmp(str2, two) != 0) {
+ SCLogInfo("%s != %s", str2, two);
+ return FALSE;
+ }
+
+ return TRUE;
+ break;
+ }
+
+ return FALSE;
+}
+
+typedef struct UTHValidateDetectAddressHeadRange_ {
+ const char *one;
+ const char *two;
+} UTHValidateDetectAddressHeadRange;
+
+int UTHValidateDetectAddressHead(DetectAddressHead *gh, int nranges, UTHValidateDetectAddressHeadRange *expectations)
+{
+ int expect = nranges;
+ int have = 0;
+
+ if (gh == NULL)
+ return FALSE;
+
+ DetectAddress *ad = NULL;
+ ad = gh->ipv4_head;
+ if (ad == NULL)
+ ad = gh->ipv6_head;
+ while (have < expect) {
+ if (ad == NULL) {
+ printf("bad head: have %d ranges, expected %d: ", have, expect);
+ return FALSE;
+ }
+
+ if (UTHValidateDetectAddress(ad, expectations[have].one, expectations[have].two) == FALSE)
+ return FALSE;
+
+ ad = ad->next;
+ have++;
+ }
+
+ return TRUE;
+}
+
+int AddressTestParse01(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse02(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4");
+
+ if (dd) {
+ if (dd->ip2.addr_data32[0] != ntohl(16909060) ||
+ dd->ip.addr_data32[0] != ntohl(16909060)) {
+ result = 0;
+ }
+
+ printf("ip %"PRIu32", ip2 %"PRIu32"\n", dd->ip.addr_data32[0], dd->ip2.addr_data32[0]);
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse03(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4/255.255.255.0");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse04(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4/255.255.255.0");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(16909056)||
+ dd->ip2.addr_data32[0] != ntohl(16909311)) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse05(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4/24");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse06(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4/24");
+
+ if (dd) {
+ if (dd->ip2.addr_data32[0] != ntohl(16909311) ||
+ dd->ip.addr_data32[0] != ntohl(16909056)) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse07(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::/3");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse08(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::/3");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(536870912) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != ntohl(1073741823) || dd->ip2.addr_data32[1] != 0xFFFFFFFF ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+ DetectAddressPrint(dd);
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse09(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::1/128");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse10(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::/128");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(536936448) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != ntohl(536936448) || dd->ip2.addr_data32[1] != 0x00000000 ||
+ dd->ip2.addr_data32[2] != 0x00000000 || dd->ip2.addr_data32[3] != 0x00000000) {
+ DetectAddressPrint(dd);
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse11(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::/48");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse12(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::/48");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(536936448) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != ntohl(536936448) || dd->ip2.addr_data32[1] != ntohl(65535) ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+ DetectAddressPrint(dd);
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+int AddressTestParse13(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::/16");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse14(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::/16");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(536936448) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != ntohl(537001983) || dd->ip2.addr_data32[1] != 0xFFFFFFFF ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse15(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::/0");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse16(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::/0");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != 0x00000000 || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != 0xFFFFFFFF || dd->ip2.addr_data32[1] != 0xFFFFFFFF ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse17(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4-1.2.3.6");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse18(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.4-1.2.3.6");
+
+ if (dd) {
+ if (dd->ip2.addr_data32[0] != ntohl(16909062) ||
+ dd->ip.addr_data32[0] != ntohl(16909060)) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse19(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("1.2.3.6-1.2.3.4");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int AddressTestParse20(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::1-2001::4");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse21(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("2001::1-2001::4");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(536936448) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != ntohl(1) ||
+
+ dd->ip2.addr_data32[0] != ntohl(536936448) || dd->ip2.addr_data32[1] != 0x00000000 ||
+ dd->ip2.addr_data32[2] != 0x00000000 || dd->ip2.addr_data32[3] != ntohl(4)) {
+ result = 0;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse22(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("2001::4-2001::1");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int AddressTestParse23(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("any");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse24(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("Any");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse25(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("ANY");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse26(void)
+{
+ int result = 0;
+ DetectAddress *dd = DetectAddressParseSingle("any");
+
+ if (dd) {
+ if (dd->flags & ADDRESS_FLAG_ANY)
+ result = 1;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse27(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("!192.168.0.1");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse28(void)
+{
+ int result = 0;
+ DetectAddress *dd = DetectAddressParseSingle("!1.2.3.4");
+
+ if (dd) {
+ if (dd->flags & ADDRESS_FLAG_NOT &&
+ dd->ip.addr_data32[0] == ntohl(16909060)) {
+ result = 1;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse29(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("!1.2.3.0/24");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse30(void)
+{
+ int result = 0;
+ DetectAddress *dd = DetectAddressParseSingle("!1.2.3.4/24");
+
+ if (dd) {
+ if (dd->flags & ADDRESS_FLAG_NOT &&
+ dd->ip.addr_data32[0] == ntohl(16909056) &&
+ dd->ip2.addr_data32[0] == ntohl(16909311)) {
+ result = 1;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * \test make sure !any is rejected
+ */
+int AddressTestParse31(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("!any");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int AddressTestParse32(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("!2001::1");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse33(void)
+{
+ int result = 0;
+ DetectAddress *dd = DetectAddressParseSingle("!2001::1");
+
+ if (dd) {
+ if (dd->flags & ADDRESS_FLAG_NOT &&
+ dd->ip.addr_data32[0] == ntohl(536936448) && dd->ip.addr_data32[1] == 0x00000000 &&
+ dd->ip.addr_data32[2] == 0x00000000 && dd->ip.addr_data32[3] == ntohl(1)) {
+ result = 1;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse34(void)
+{
+ DetectAddress *dd = DetectAddressParseSingle("!2001::/16");
+
+ if (dd) {
+ DetectAddressFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int AddressTestParse35(void)
+{
+ int result = 0;
+ DetectAddress *dd = DetectAddressParseSingle("!2001::/16");
+
+ if (dd) {
+ if (dd->flags & ADDRESS_FLAG_NOT &&
+ dd->ip.addr_data32[0] == ntohl(536936448) && dd->ip.addr_data32[1] == 0x00000000 &&
+ dd->ip.addr_data32[2] == 0x00000000 && dd->ip.addr_data32[3] == 0x00000000 &&
+
+ dd->ip2.addr_data32[0] == ntohl(537001983) && dd->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ dd->ip2.addr_data32[2] == 0xFFFFFFFF && dd->ip2.addr_data32[3] == 0xFFFFFFFF) {
+ result = 1;
+ }
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse36(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("ffff::/16");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != ntohl(0xFFFF0000) || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != 0xFFFFFFFF || dd->ip2.addr_data32[1] != 0xFFFFFFFF ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+
+ DetectAddressPrint(dd);
+ result = 0;
+ }
+ DetectAddressPrint(dd);
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestParse37(void)
+{
+ int result = 1;
+ DetectAddress *dd = DetectAddressParseSingle("::/0");
+
+ if (dd) {
+ if (dd->ip.addr_data32[0] != 0x00000000 || dd->ip.addr_data32[1] != 0x00000000 ||
+ dd->ip.addr_data32[2] != 0x00000000 || dd->ip.addr_data32[3] != 0x00000000 ||
+
+ dd->ip2.addr_data32[0] != 0xFFFFFFFF || dd->ip2.addr_data32[1] != 0xFFFFFFFF ||
+ dd->ip2.addr_data32[2] != 0xFFFFFFFF || dd->ip2.addr_data32[3] != 0xFFFFFFFF) {
+ DetectAddressPrint(dd);
+ result = 0;
+ }
+ DetectAddressPrint(dd);
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch01(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("1.2.3.4/24");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch02(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.3.127", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("1.2.3.4/25");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch03(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.3.128", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("1.2.3.4/25");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 1)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch04(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.2.255", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("1.2.3.4/25");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 1)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch05(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("1.2.3.4/32");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch06(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in_addr in;
+ Address a;
+
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET;
+ a.addr_data32[0] = in.s_addr;
+
+ dd = DetectAddressParseSingle("0.0.0.0/0.0.0.0");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch07(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in6_addr in6;
+ Address a;
+
+ if (inet_pton(AF_INET6, "2001::1", &in6) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET6;
+ memcpy(&a.addr_data32, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ dd = DetectAddressParseSingle("2001::/3");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch08(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in6_addr in6;
+ Address a;
+
+ if (inet_pton(AF_INET6, "1999:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &in6) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET6;
+ memcpy(&a.addr_data32, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ dd = DetectAddressParseSingle("2001::/3");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 1)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch09(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in6_addr in6;
+ Address a;
+
+ if (inet_pton(AF_INET6, "2001::2", &in6) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET6;
+ memcpy(&a.addr_data32, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ dd = DetectAddressParseSingle("2001::1/128");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 1)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch10(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in6_addr in6;
+ Address a;
+
+ if (inet_pton(AF_INET6, "2001::2", &in6) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET6;
+ memcpy(&a.addr_data32, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ dd = DetectAddressParseSingle("2001::1/126");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 0)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestMatch11(void)
+{
+ DetectAddress *dd = NULL;
+ int result = 1;
+ struct in6_addr in6;
+ Address a;
+
+ if (inet_pton(AF_INET6, "2001::3", &in6) != 1)
+ return 0;
+ memset(&a, 0, sizeof(Address));
+ a.family = AF_INET6;
+ memcpy(&a.addr_data32, &in6.s6_addr, sizeof(in6.s6_addr));
+
+ dd = DetectAddressParseSingle("2001::1/127");
+ if (dd) {
+ if (DetectAddressMatch(dd, &a) == 1)
+ result = 0;
+
+ DetectAddressFree(dd);
+ return result;
+ }
+
+ return 0;
+}
+
+int AddressTestCmp01(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EQ)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp02(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.0.0/255.255.0.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EB)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp03(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.0.0/255.255.0.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_ES)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp04(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.1.0/255.255.255.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_LT)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp05(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.1.0/255.255.255.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.0.0/255.255.255.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_GT)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp06(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.1.0/255.255.0.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.0.0/255.255.0.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EQ)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmpIPv407(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.1.0/255.255.255.0");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.1.128-192.168.2.128");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_LE)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmpIPv408(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("192.168.1.128-192.168.2.128");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("192.168.1.0/255.255.255.0");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_GE)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp07(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001::/3");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001::1/3");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EQ)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp08(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001::/3");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001::/8");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EB)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp09(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001::/8");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001::/3");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_ES)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp10(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001:1:2:3:0:0:0:0/64");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001:1:2:4:0:0:0:0/64");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_LT)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp11(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001:1:2:4:0:0:0:0/64");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001:1:2:3:0:0:0:0/64");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_GT)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestCmp12(void)
+{
+ DetectAddress *da = NULL, *db = NULL;
+ int result = 1;
+
+ da = DetectAddressParseSingle("2001:1:2:3:1:0:0:0/64");
+ if (da == NULL) goto error;
+ db = DetectAddressParseSingle("2001:1:2:3:2:0:0:0/64");
+ if (db == NULL) goto error;
+
+ if (DetectAddressCmp(da, db) != ADDRESS_EQ)
+ result = 0;
+
+ DetectAddressFree(da);
+ DetectAddressFree(db);
+ return result;
+
+error:
+ if (da) DetectAddressFree(da);
+ if (db) DetectAddressFree(db);
+ return 0;
+}
+
+int AddressTestAddressGroupSetup01(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.4");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup02(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.4");
+ if (r == 0 && gh->ipv4_head != NULL)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup03(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.4");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.3");
+ if (r == 0 && gh->ipv4_head != prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next == prev_head) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup04(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.4");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.3");
+ if (r == 0 && gh->ipv4_head != prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next == prev_head) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.2");
+ if (r == 0 && gh->ipv4_head != prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next == prev_head) {
+ result = 1;
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup05(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.2");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.3");
+ if (r == 0 && gh->ipv4_head == prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next != prev_head) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.4");
+ if (r == 0 && gh->ipv4_head == prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next != prev_head) {
+ result = 1;
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup06(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "1.2.3.2");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ DetectAddress *prev_head = gh->ipv4_head;
+
+ r = DetectAddressParse(NULL, gh, "1.2.3.2");
+ if (r == 0 && gh->ipv4_head == prev_head &&
+ gh->ipv4_head != NULL && gh->ipv4_head->next == NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup07(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.0.0.0/8");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.10");
+ if (r == 0 && gh->ipv4_head != NULL &&
+ gh->ipv4_head->next != NULL &&
+ gh->ipv4_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup08(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.10.10.10");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "10.0.0.0/8");
+ if (r == 0 && gh->ipv4_head != NULL &&
+ gh->ipv4_head->next != NULL &&
+ gh->ipv4_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup09(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.10.10.0/24");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.10-10.10.11.1");
+ if (r == 0 && gh->ipv4_head != NULL &&
+ gh->ipv4_head->next != NULL &&
+ gh->ipv4_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup10(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.10.10.10-10.10.11.1");
+ if (r == 0 && gh->ipv4_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.0/24");
+ if (r == 0 && gh->ipv4_head != NULL &&
+ gh->ipv4_head->next != NULL &&
+ gh->ipv4_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup11(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.10.10.10-10.10.11.1");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.0/24");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "0.0.0.0/0");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+
+ /* result should be:
+ * 0.0.0.0/10.10.9.255
+ * 10.10.10.0/10.10.10.9
+ * 10.10.10.10/10.10.10.255
+ * 10.10.11.0/10.10.11.1
+ * 10.10.11.2/255.255.255.255
+ */
+ if (one->ip.addr_data32[0] == 0x00000000 && one->ip2.addr_data32[0] == ntohl(168430079) &&
+ two->ip.addr_data32[0] == ntohl(168430080) && two->ip2.addr_data32[0] == ntohl(168430089) &&
+ three->ip.addr_data32[0] == ntohl(168430090) && three->ip2.addr_data32[0] == ntohl(168430335) &&
+ four->ip.addr_data32[0] == ntohl(168430336) && four->ip2.addr_data32[0] == ntohl(168430337) &&
+ five->ip.addr_data32[0] == ntohl(168430338) && five->ip2.addr_data32[0] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup12 (void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "10.10.10.10-10.10.11.1");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "0.0.0.0/0");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.0/24");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+
+ /* result should be:
+ * 0.0.0.0/10.10.9.255
+ * 10.10.10.0/10.10.10.9
+ * 10.10.10.10/10.10.10.255
+ * 10.10.11.0/10.10.11.1
+ * 10.10.11.2/255.255.255.255
+ */
+ if (one->ip.addr_data32[0] == 0x00000000 && one->ip2.addr_data32[0] == ntohl(168430079) &&
+ two->ip.addr_data32[0] == ntohl(168430080) && two->ip2.addr_data32[0] == ntohl(168430089) &&
+ three->ip.addr_data32[0] == ntohl(168430090) && three->ip2.addr_data32[0] == ntohl(168430335) &&
+ four->ip.addr_data32[0] == ntohl(168430336) && four->ip2.addr_data32[0] == ntohl(168430337) &&
+ five->ip.addr_data32[0] == ntohl(168430338) && five->ip2.addr_data32[0] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup13(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "0.0.0.0/0");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.10-10.10.11.1");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "10.10.10.0/24");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+
+ /* result should be:
+ * 0.0.0.0/10.10.9.255
+ * 10.10.10.0/10.10.10.9
+ * 10.10.10.10/10.10.10.255
+ * 10.10.11.0/10.10.11.1
+ * 10.10.11.2/255.255.255.255
+ */
+ if (one->ip.addr_data32[0] == 0x00000000 && one->ip2.addr_data32[0] == ntohl(168430079) &&
+ two->ip.addr_data32[0] == ntohl(168430080) && two->ip2.addr_data32[0] == ntohl(168430089) &&
+ three->ip.addr_data32[0] == ntohl(168430090) && three->ip2.addr_data32[0] == ntohl(168430335) &&
+ four->ip.addr_data32[0] == ntohl(168430336) && four->ip2.addr_data32[0] == ntohl(168430337) &&
+ five->ip.addr_data32[0] == ntohl(168430338) && five->ip2.addr_data32[0] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetupIPv414(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "!1.2.3.4");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head;
+ DetectAddress *two = one ? one->next : NULL;
+
+ if (one && two) {
+ /* result should be:
+ * 0.0.0.0/1.2.3.3
+ * 1.2.3.5/255.255.255.255
+ */
+ if (one->ip.addr_data32[0] == 0x00000000 && one->ip2.addr_data32[0] == ntohl(16909059) &&
+ two->ip.addr_data32[0] == ntohl(16909061) && two->ip2.addr_data32[0] == 0xFFFFFFFF) {
+ result = 1;
+ } else {
+ printf("unexpected addresses: ");
+ }
+ } else {
+ printf("one %p two %p: ", one, two);
+ }
+ } else {
+ printf("DetectAddressParse returned %d, expected 0: ", r);
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetupIPv415(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "!0.0.0.0");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head;
+
+ if (one && one->next == NULL) {
+ /* result should be:
+ * 0.0.0.1/255.255.255.255
+ */
+ if (one->ip.addr_data32[0] == ntohl(1) && one->ip2.addr_data32[0] == 0xFFFFFFFF)
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetupIPv416(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "!255.255.255.255");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv4_head;
+
+ if (one && one->next == NULL) {
+ /* result should be:
+ * 0.0.0.0/255.255.255.254
+ */
+ if (one->ip.addr_data32[0] == 0x00000000 && one->ip2.addr_data32[0] == ntohl(4294967294))
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup14(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::1");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup15(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::1");
+ if (r == 0 && gh->ipv6_head != NULL)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup16(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::3");
+ if (r == 0 && gh->ipv6_head != prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next == prev_head) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup17(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::3");
+ if (r == 0 && gh->ipv6_head != prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next == prev_head) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::2");
+ if (r == 0 && gh->ipv6_head != prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next == prev_head) {
+ result = 1;
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup18(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::2");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::3");
+ if (r == 0 && gh->ipv6_head == prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next != prev_head) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::4");
+ if (r == 0 && gh->ipv6_head == prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next != prev_head) {
+ result = 1;
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup19(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::2");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ DetectAddress *prev_head = gh->ipv6_head;
+
+ r = DetectAddressParse(NULL, gh, "2001::2");
+ if (r == 0 && gh->ipv6_head == prev_head &&
+ gh->ipv6_head != NULL && gh->ipv6_head->next == NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup20(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2000::/3");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "2001::4");
+ if (r == 0 && gh->ipv6_head != NULL &&
+ gh->ipv6_head->next != NULL &&
+ gh->ipv6_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup21(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "2000::/3");
+ if (r == 0 && gh->ipv6_head != NULL &&
+ gh->ipv6_head->next != NULL &&
+ gh->ipv6_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup22(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2000::/3");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "2001::4-2001::6");
+ if (r == 0 && gh->ipv6_head != NULL &&
+ gh->ipv6_head->next != NULL &&
+ gh->ipv6_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup23(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4-2001::6");
+ if (r == 0 && gh->ipv6_head != NULL) {
+ r = DetectAddressParse(NULL, gh, "2000::/3");
+ if (r == 0 && gh->ipv6_head != NULL &&
+ gh->ipv6_head->next != NULL &&
+ gh->ipv6_head->next->next != NULL) {
+ result = 1;
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup24(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4-2001::6");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "2001::/3");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "::/0");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv6_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+ if (one->ip.addr_data32[0] == 0x00000000 &&
+ one->ip.addr_data32[1] == 0x00000000 &&
+ one->ip.addr_data32[2] == 0x00000000 &&
+ one->ip.addr_data32[3] == 0x00000000 &&
+ one->ip2.addr_data32[0] == ntohl(536870911) &&
+ one->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ two->ip.addr_data32[0] == ntohl(536870912) &&
+ two->ip.addr_data32[1] == 0x00000000 &&
+ two->ip.addr_data32[2] == 0x00000000 &&
+ two->ip.addr_data32[3] == 0x00000000 &&
+ two->ip2.addr_data32[0] == ntohl(536936448) &&
+ two->ip2.addr_data32[1] == 0x00000000 &&
+ two->ip2.addr_data32[2] == 0x00000000 &&
+ two->ip2.addr_data32[3] == ntohl(3) &&
+
+ three->ip.addr_data32[0] == ntohl(536936448) &&
+ three->ip.addr_data32[1] == 0x00000000 &&
+ three->ip.addr_data32[2] == 0x00000000 &&
+ three->ip.addr_data32[3] == ntohl(4) &&
+ three->ip2.addr_data32[0] == ntohl(536936448) &&
+ three->ip2.addr_data32[1] == 0x00000000 &&
+ three->ip2.addr_data32[2] == 0x00000000 &&
+ three->ip2.addr_data32[3] == ntohl(6) &&
+
+ four->ip.addr_data32[0] == ntohl(536936448) &&
+ four->ip.addr_data32[1] == 0x00000000 &&
+ four->ip.addr_data32[2] == 0x00000000 &&
+ four->ip.addr_data32[3] == ntohl(7) &&
+ four->ip2.addr_data32[0] == ntohl(1073741823) &&
+ four->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ five->ip.addr_data32[0] == ntohl(1073741824) &&
+ five->ip.addr_data32[1] == 0x00000000 &&
+ five->ip.addr_data32[2] == 0x00000000 &&
+ five->ip.addr_data32[3] == 0x00000000 &&
+ five->ip2.addr_data32[0] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[3] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup25(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "2001::4-2001::6");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "::/0");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "2001::/3");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv6_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+ if (one->ip.addr_data32[0] == 0x00000000 &&
+ one->ip.addr_data32[1] == 0x00000000 &&
+ one->ip.addr_data32[2] == 0x00000000 &&
+ one->ip.addr_data32[3] == 0x00000000 &&
+ one->ip2.addr_data32[0] == ntohl(536870911) &&
+ one->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ two->ip.addr_data32[0] == ntohl(536870912) &&
+ two->ip.addr_data32[1] == 0x00000000 &&
+ two->ip.addr_data32[2] == 0x00000000 &&
+ two->ip.addr_data32[3] == 0x00000000 &&
+ two->ip2.addr_data32[0] == ntohl(536936448) &&
+ two->ip2.addr_data32[1] == 0x00000000 &&
+ two->ip2.addr_data32[2] == 0x00000000 &&
+ two->ip2.addr_data32[3] == ntohl(3) &&
+
+ three->ip.addr_data32[0] == ntohl(536936448) &&
+ three->ip.addr_data32[1] == 0x00000000 &&
+ three->ip.addr_data32[2] == 0x00000000 &&
+ three->ip.addr_data32[3] == ntohl(4) &&
+ three->ip2.addr_data32[0] == ntohl(536936448) &&
+ three->ip2.addr_data32[1] == 0x00000000 &&
+ three->ip2.addr_data32[2] == 0x00000000 &&
+ three->ip2.addr_data32[3] == ntohl(6) &&
+
+ four->ip.addr_data32[0] == ntohl(536936448) &&
+ four->ip.addr_data32[1] == 0x00000000 &&
+ four->ip.addr_data32[2] == 0x00000000 &&
+ four->ip.addr_data32[3] == ntohl(7) &&
+ four->ip2.addr_data32[0] == ntohl(1073741823) &&
+ four->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ five->ip.addr_data32[0] == ntohl(1073741824) &&
+ five->ip.addr_data32[1] == 0x00000000 &&
+ five->ip.addr_data32[2] == 0x00000000 &&
+ five->ip.addr_data32[3] == 0x00000000 &&
+ five->ip2.addr_data32[0] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[3] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup26(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "::/0");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "2001::4-2001::6");
+ if (r == 0) {
+ r = DetectAddressParse(NULL, gh, "2001::/3");
+ if (r == 0) {
+ DetectAddress *one = gh->ipv6_head, *two = one->next,
+ *three = two->next, *four = three->next,
+ *five = four->next;
+ if (one->ip.addr_data32[0] == 0x00000000 &&
+ one->ip.addr_data32[1] == 0x00000000 &&
+ one->ip.addr_data32[2] == 0x00000000 &&
+ one->ip.addr_data32[3] == 0x00000000 &&
+ one->ip2.addr_data32[0] == ntohl(536870911) &&
+ one->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ one->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ two->ip.addr_data32[0] == ntohl(536870912) &&
+ two->ip.addr_data32[1] == 0x00000000 &&
+ two->ip.addr_data32[2] == 0x00000000 &&
+ two->ip.addr_data32[3] == 0x00000000 &&
+ two->ip2.addr_data32[0] == ntohl(536936448) &&
+ two->ip2.addr_data32[1] == 0x00000000 &&
+ two->ip2.addr_data32[2] == 0x00000000 &&
+ two->ip2.addr_data32[3] == ntohl(3) &&
+
+ three->ip.addr_data32[0] == ntohl(536936448) &&
+ three->ip.addr_data32[1] == 0x00000000 &&
+ three->ip.addr_data32[2] == 0x00000000 &&
+ three->ip.addr_data32[3] == ntohl(4) &&
+ three->ip2.addr_data32[0] == ntohl(536936448) &&
+ three->ip2.addr_data32[1] == 0x00000000 &&
+ three->ip2.addr_data32[2] == 0x00000000 &&
+ three->ip2.addr_data32[3] == ntohl(6) &&
+
+ four->ip.addr_data32[0] == ntohl(536936448) &&
+ four->ip.addr_data32[1] == 0x00000000 &&
+ four->ip.addr_data32[2] == 0x00000000 &&
+ four->ip.addr_data32[3] == ntohl(7) &&
+ four->ip2.addr_data32[0] == ntohl(1073741823) &&
+ four->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ four->ip2.addr_data32[3] == 0xFFFFFFFF &&
+
+ five->ip.addr_data32[0] == ntohl(1073741824) &&
+ five->ip.addr_data32[1] == 0x00000000 &&
+ five->ip.addr_data32[2] == 0x00000000 &&
+ five->ip.addr_data32[3] == 0x00000000 &&
+ five->ip2.addr_data32[0] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[1] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[2] == 0xFFFFFFFF &&
+ five->ip2.addr_data32[3] == 0xFFFFFFFF) {
+ result = 1;
+ }
+ }
+ }
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup27(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.2.3.4]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup28(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.2.3.4,4.3.2.1]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup29(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.2.3.4,4.3.2.1,10.10.10.10]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup30(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[[1.2.3.4,2.3.4.5],4.3.2.1,[10.10.10.10,11.11.11.11]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup31(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[[1.2.3.4,[2.3.4.5,3.4.5.6]],4.3.2.1,[10.10.10.10,[11.11.11.11,12.12.12.12]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup32(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[[1.2.3.4,[2.3.4.5,[3.4.5.6,4.5.6.7]]],4.3.2.1,[10.10.10.10,[11.11.11.11,[12.12.12.12,13.13.13.13]]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup33(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "![1.1.1.1,[2.2.2.2,[3.3.3.3,4.4.4.4]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup34(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.0.0.0/8,![1.1.1.1,[1.2.1.1,1.3.1.1]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup35(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.0.0.0/8,[2.0.0.0/8,![1.1.1.1,2.2.2.2]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup36 (void)
+{
+ int result = 0;
+
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[1.0.0.0/8,[2.0.0.0/8,[3.0.0.0/8,!1.1.1.1]]]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestAddressGroupSetup37(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[0.0.0.0/0,::/0]");
+ if (r == 0)
+ result = 1;
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup38(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[3] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.14.0", "192.168.14.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "![192.168.0.0/16,!192.168.14.0/24]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 3, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup39(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[3] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.14.0", "192.168.14.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[![192.168.0.0/16,!192.168.14.0/24]]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 3, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup40(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[3] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.14.0", "192.168.14.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[![192.168.0.0/16,[!192.168.14.0/24]]]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 3, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup41(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[3] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.14.0", "192.168.14.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[![192.168.0.0/16,![192.168.14.0/24]]]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 3, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup42(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[1] = {
+ { "2000:0000:0000:0000:0000:0000:0000:0000", "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[2001::/3]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 1, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup43(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[2] = {
+ { "2000:0000:0000:0000:0000:0000:0000:0000", "2fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
+ { "3800:0000:0000:0000:0000:0000:0000:0000", "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[2001::/3,!3000::/5]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 2, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup44(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[2] = {
+ { "3ffe:ffff:7654:feda:1245:ba98:0000:0000", "3ffe:ffff:7654:feda:1245:ba98:ffff:ffff" }};
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "3ffe:ffff:7654:feda:1245:ba98:3210:4562/96");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 1, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup45(void)
+{
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[192.168.1.3,!192.168.0.0/16]");
+ if (r != 0) {
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+static int AddressTestAddressGroupSetup46(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[4] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.1.0", "192.168.1.255" },
+ { "192.168.3.0", "192.168.3.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[![192.168.0.0/16,![192.168.1.0/24,192.168.3.0/24]]]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 4, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+/** \test net with some negations, then all negated */
+static int AddressTestAddressGroupSetup47(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[5] = {
+ { "0.0.0.0", "192.167.255.255" },
+ { "192.168.1.0", "192.168.1.255" },
+ { "192.168.3.0", "192.168.3.255" },
+ { "192.168.5.0", "192.168.5.255" },
+ { "192.169.0.0", "255.255.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[![192.168.0.0/16,![192.168.1.0/24,192.168.3.0/24],!192.168.5.0/24]]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 5, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+/** \test same as AddressTestAddressGroupSetup47, but not negated */
+static int AddressTestAddressGroupSetup48(void)
+{
+ UTHValidateDetectAddressHeadRange expectations[4] = {
+ { "192.168.0.0", "192.168.0.255" },
+ { "192.168.2.0", "192.168.2.255" },
+ { "192.168.4.0", "192.168.4.255" },
+ { "192.168.6.0", "192.168.255.255" } };
+ int result = 0;
+ DetectAddressHead *gh = DetectAddressHeadInit();
+ if (gh != NULL) {
+ int r = DetectAddressParse(NULL, gh, "[192.168.0.0/16,![192.168.1.0/24,192.168.3.0/24],!192.168.5.0/24]");
+ if (r == 0) {
+ if (UTHValidateDetectAddressHead(gh, 4, expectations) == TRUE)
+ result = 1;
+ }
+
+ DetectAddressHeadFree(gh);
+ }
+ return result;
+}
+
+int AddressTestCutIPv401(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0/255.255.255.0");
+ b = DetectAddressParseSingle("1.2.2.0-1.2.3.4");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv402(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0/255.255.255.0");
+ b = DetectAddressParseSingle("1.2.2.0-1.2.3.4");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c == NULL)
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv403(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0/255.255.255.0");
+ b = DetectAddressParseSingle("1.2.2.0-1.2.3.4");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c == NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16908800) || a->ip2.addr_data32[0] != ntohl(16909055))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909056) || b->ip2.addr_data32[0] != ntohl(16909060))
+ goto error;
+ if (c->ip.addr_data32[0] != ntohl(16909061) || c->ip2.addr_data32[0] != ntohl(16909311))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv404(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.3-1.2.3.6");
+ b = DetectAddressParseSingle("1.2.3.0-1.2.3.5");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c == NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909058))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909059) || b->ip2.addr_data32[0] != ntohl(16909061))
+ goto error;
+ if (c->ip.addr_data32[0] != ntohl(16909062) || c->ip2.addr_data32[0] != ntohl(16909062))
+ goto error;
+
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv405(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.3-1.2.3.6");
+ b = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c == NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909058))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909059) || b->ip2.addr_data32[0] != ntohl(16909062))
+ goto error;
+ if (c->ip.addr_data32[0] != ntohl(16909063) || c->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv406(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+ b = DetectAddressParseSingle("1.2.3.3-1.2.3.6");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c == NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909058))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909059) || b->ip2.addr_data32[0] != ntohl(16909062))
+ goto error;
+ if (c->ip.addr_data32[0] != ntohl(16909063) || c->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv407(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0-1.2.3.6");
+ b = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c != NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909062))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909063) || b->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv408(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.3-1.2.3.9");
+ b = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c != NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909058))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909059) || b->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv409(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+ b = DetectAddressParseSingle("1.2.3.0-1.2.3.6");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c != NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909062))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909063) || b->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestCutIPv410(void)
+{
+ DetectAddress *a, *b, *c;
+ a = DetectAddressParseSingle("1.2.3.0-1.2.3.9");
+ b = DetectAddressParseSingle("1.2.3.3-1.2.3.9");
+
+ if (DetectAddressCut(NULL, a, b, &c) == -1)
+ goto error;
+
+ if (c != NULL)
+ goto error;
+
+ if (a->ip.addr_data32[0] != ntohl(16909056) || a->ip2.addr_data32[0] != ntohl(16909058))
+ goto error;
+ if (b->ip.addr_data32[0] != ntohl(16909059) || b->ip2.addr_data32[0] != ntohl(16909065))
+ goto error;
+
+ printf("ip %u ip2 %u ", htonl(a->ip.addr_data32[0]), htonl(a->ip2.addr_data32[0]));
+
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 1;
+
+error:
+ DetectAddressFree(a);
+ DetectAddressFree(b);
+ DetectAddressFree(c);
+ return 0;
+}
+
+int AddressTestParseInvalidMask01(void)
+{
+ int result = 1;
+ DetectAddress *dd = NULL;
+
+ dd = DetectAddressParseSingle("192.168.2.0/33");
+ if (dd != NULL) {
+ DetectAddressFree(dd);
+ result = 0;
+ }
+ return result;
+}
+
+int AddressTestParseInvalidMask02(void)
+{
+ int result = 1;
+ DetectAddress *dd = NULL;
+
+ dd = DetectAddressParseSingle("192.168.2.0/255.255.257.0");
+ if (dd != NULL) {
+ DetectAddressFree(dd);
+ result = 0;
+ }
+ return result;
+}
+
+int AddressTestParseInvalidMask03(void)
+{
+ int result = 1;
+ DetectAddress *dd = NULL;
+
+ dd = DetectAddressParseSingle("192.168.2.0/blue");
+ if (dd != NULL) {
+ DetectAddressFree(dd);
+ result = 0;
+ }
+ return result;
+}
+
+int AddressConfVarsTest01(void)
+{
+ static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"any\"\n"
+ "\n"
+ " EXTERNAL_NET: \"!any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"any\"\n"
+ "\n"
+ " SHELLCODE_PORTS: \"!any\"\n"
+ "\n";
+
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if (DetectAddressTestConfVars() < 0 && DetectPortTestConfVars() < 0)
+ result = 1;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+int AddressConfVarsTest02(void)
+{
+ static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"any\"\n"
+ "\n"
+ " EXTERNAL_NET: \"any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"any\"\n"
+ "\n"
+ " SHELLCODE_PORTS: \"!any\"\n"
+ "\n";
+
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if (DetectAddressTestConfVars() == 0 && DetectPortTestConfVars() < 0)
+ result = 1;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+int AddressConfVarsTest03(void)
+{
+ static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"any\"\n"
+ "\n"
+ " EXTERNAL_NET: \"!$HOME_NET\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"any\"\n"
+ "\n"
+ " SHELLCODE_PORTS: \"!$HTTP_PORTS\"\n"
+ "\n";
+
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if (DetectAddressTestConfVars() < 0 && DetectPortTestConfVars() < 0)
+ result = 1;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+int AddressConfVarsTest04(void)
+{
+ static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"any\"\n"
+ "\n"
+ " EXTERNAL_NET: \"$HOME_NET\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"any\"\n"
+ "\n"
+ " SHELLCODE_PORTS: \"$HTTP_PORTS\"\n"
+ "\n";
+
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if (DetectAddressTestConfVars() == 0 && DetectPortTestConfVars() == 0)
+ result = 1;
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+int AddressConfVarsTest05(void)
+{
+ static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"any\"\n"
+ "\n"
+ " EXTERNAL_NET: [192.168.0.1]\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"any\"\n"
+ "\n"
+ " SHELLCODE_PORTS: [80]\n"
+ "\n";
+
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if (DetectAddressTestConfVars() != -1 && DetectPortTestConfVars() != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+#include "detect-engine.h"
+
+/**
+ * \test Test sig distribution over address groups
+ */
+static int AddressTestFunctions01(void)
+{
+ DetectAddress *a1 = NULL;
+ DetectAddress *a2 = NULL;
+ DetectAddressHead *h = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ a1 = DetectAddressParseSingle("255.0.0.0/8");
+ if (a1 == NULL) {
+ printf("a1 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a1->sh, &s[0]);
+
+ a2 = DetectAddressParseSingle("0.0.0.0/0");
+ if (a2 == NULL) {
+ printf("a2 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a2->sh, &s[1]);
+
+ SCLogDebug("a1");
+ DetectAddressPrint(a1);
+ SCLogDebug("a2");
+ DetectAddressPrint(a2);
+
+ h = DetectAddressHeadInit();
+ if (h == NULL)
+ goto end;
+ DetectAddressInsert(de_ctx, h, a1);
+ DetectAddressInsert(de_ctx, h, a2);
+
+ if (h == NULL)
+ goto end;
+
+ DetectAddress *x = h->ipv4_head;
+ for ( ; x != NULL; x = x->next) {
+ SCLogDebug("x %p next %p", x, x->next);
+ DetectAddressPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectAddress *one = h->ipv4_head;
+ DetectAddress *two = one->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (h != NULL)
+ DetectAddressHeadFree(h);
+ return result;
+}
+
+/**
+ * \test Test sig distribution over address groups
+ */
+static int AddressTestFunctions02(void)
+{
+ DetectAddress *a1 = NULL;
+ DetectAddress *a2 = NULL;
+ DetectAddressHead *h = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ a1 = DetectAddressParseSingle("255.0.0.0/8");
+ if (a1 == NULL) {
+ printf("a1 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a1->sh, &s[0]);
+
+ a2 = DetectAddressParseSingle("0.0.0.0/0");
+ if (a2 == NULL) {
+ printf("a2 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a2->sh, &s[1]);
+
+ SCLogDebug("a1");
+ DetectAddressPrint(a1);
+ SCLogDebug("a2");
+ DetectAddressPrint(a2);
+
+ h = DetectAddressHeadInit();
+ if (h == NULL)
+ goto end;
+ DetectAddressInsert(de_ctx, h, a2);
+ DetectAddressInsert(de_ctx, h, a1);
+
+ BUG_ON(h == NULL);
+
+ SCLogDebug("dp3");
+
+ DetectAddress *x = h->ipv4_head;
+ for ( ; x != NULL; x = x->next) {
+ DetectAddressPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectAddress *one = h->ipv4_head;
+ DetectAddress *two = one->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (h != NULL)
+ DetectAddressHeadFree(h);
+ return result;
+}
+
+/**
+ * \test Test sig distribution over address groups
+ */
+static int AddressTestFunctions03(void)
+{
+ DetectAddress *a1 = NULL;
+ DetectAddress *a2 = NULL;
+ DetectAddressHead *h = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ a1 = DetectAddressParseSingle("ffff::/16");
+ if (a1 == NULL) {
+ printf("a1 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a1->sh, &s[0]);
+
+ a2 = DetectAddressParseSingle("::/0");
+ if (a2 == NULL) {
+ printf("a2 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a2->sh, &s[1]);
+
+ SCLogDebug("a1");
+ DetectAddressPrint(a1);
+ SCLogDebug("a2");
+ DetectAddressPrint(a2);
+
+ h = DetectAddressHeadInit();
+ if (h == NULL)
+ goto end;
+ DetectAddressInsert(de_ctx, h, a1);
+ DetectAddressInsert(de_ctx, h, a2);
+
+ if (h == NULL)
+ goto end;
+
+ DetectAddress *x = h->ipv6_head;
+ for ( ; x != NULL; x = x->next) {
+ SCLogDebug("x %p next %p", x, x->next);
+ DetectAddressPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectAddress *one = h->ipv6_head;
+ DetectAddress *two = one->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (h != NULL)
+ DetectAddressHeadFree(h);
+ return result;
+}
+
+/**
+ * \test Test sig distribution over address groups
+ */
+static int AddressTestFunctions04(void)
+{
+ DetectAddress *a1 = NULL;
+ DetectAddress *a2 = NULL;
+ DetectAddressHead *h = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ a1 = DetectAddressParseSingle("ffff::/16");
+ if (a1 == NULL) {
+ printf("a1 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a1->sh, &s[0]);
+
+ a2 = DetectAddressParseSingle("::/0");
+ if (a2 == NULL) {
+ printf("a2 == NULL: ");
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &a2->sh, &s[1]);
+
+ SCLogDebug("a1");
+ DetectAddressPrint(a1);
+ SCLogDebug("a2");
+ DetectAddressPrint(a2);
+
+ h = DetectAddressHeadInit();
+ if (h == NULL)
+ goto end;
+ DetectAddressInsert(de_ctx, h, a2);
+ DetectAddressInsert(de_ctx, h, a1);
+
+ BUG_ON(h == NULL);
+
+ SCLogDebug("dp3");
+
+ DetectAddress *x = h->ipv6_head;
+ for ( ; x != NULL; x = x->next) {
+ DetectAddressPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectAddress *one = h->ipv6_head;
+ DetectAddress *two = one->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (h != NULL)
+ DetectAddressHeadFree(h);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectAddressTests(void)
+{
+#ifdef UNITTESTS
+ DetectAddressIPv4Tests();
+ DetectAddressIPv6Tests();
+
+ UtRegisterTest("AddressTestParse01", AddressTestParse01, 1);
+ UtRegisterTest("AddressTestParse02", AddressTestParse02, 1);
+ UtRegisterTest("AddressTestParse03", AddressTestParse03, 1);
+ UtRegisterTest("AddressTestParse04", AddressTestParse04, 1);
+ UtRegisterTest("AddressTestParse05", AddressTestParse05, 1);
+ UtRegisterTest("AddressTestParse06", AddressTestParse06, 1);
+ UtRegisterTest("AddressTestParse07", AddressTestParse07, 1);
+ UtRegisterTest("AddressTestParse08", AddressTestParse08, 1);
+ UtRegisterTest("AddressTestParse09", AddressTestParse09, 1);
+ UtRegisterTest("AddressTestParse10", AddressTestParse10, 1);
+ UtRegisterTest("AddressTestParse11", AddressTestParse11, 1);
+ UtRegisterTest("AddressTestParse12", AddressTestParse12, 1);
+ UtRegisterTest("AddressTestParse13", AddressTestParse13, 1);
+ UtRegisterTest("AddressTestParse14", AddressTestParse14, 1);
+ UtRegisterTest("AddressTestParse15", AddressTestParse15, 1);
+ UtRegisterTest("AddressTestParse16", AddressTestParse16, 1);
+ UtRegisterTest("AddressTestParse17", AddressTestParse17, 1);
+ UtRegisterTest("AddressTestParse18", AddressTestParse18, 1);
+ UtRegisterTest("AddressTestParse19", AddressTestParse19, 1);
+ UtRegisterTest("AddressTestParse20", AddressTestParse20, 1);
+ UtRegisterTest("AddressTestParse21", AddressTestParse21, 1);
+ UtRegisterTest("AddressTestParse22", AddressTestParse22, 1);
+ UtRegisterTest("AddressTestParse23", AddressTestParse23, 1);
+ UtRegisterTest("AddressTestParse24", AddressTestParse24, 1);
+ UtRegisterTest("AddressTestParse25", AddressTestParse25, 1);
+ UtRegisterTest("AddressTestParse26", AddressTestParse26, 1);
+ UtRegisterTest("AddressTestParse27", AddressTestParse27, 1);
+ UtRegisterTest("AddressTestParse28", AddressTestParse28, 1);
+ UtRegisterTest("AddressTestParse29", AddressTestParse29, 1);
+ UtRegisterTest("AddressTestParse30", AddressTestParse30, 1);
+ UtRegisterTest("AddressTestParse31", AddressTestParse31, 1);
+ UtRegisterTest("AddressTestParse32", AddressTestParse32, 1);
+ UtRegisterTest("AddressTestParse33", AddressTestParse33, 1);
+ UtRegisterTest("AddressTestParse34", AddressTestParse34, 1);
+ UtRegisterTest("AddressTestParse35", AddressTestParse35, 1);
+ UtRegisterTest("AddressTestParse36", AddressTestParse36, 1);
+ UtRegisterTest("AddressTestParse37", AddressTestParse37, 1);
+
+ UtRegisterTest("AddressTestMatch01", AddressTestMatch01, 1);
+ UtRegisterTest("AddressTestMatch02", AddressTestMatch02, 1);
+ UtRegisterTest("AddressTestMatch03", AddressTestMatch03, 1);
+ UtRegisterTest("AddressTestMatch04", AddressTestMatch04, 1);
+ UtRegisterTest("AddressTestMatch05", AddressTestMatch05, 1);
+ UtRegisterTest("AddressTestMatch06", AddressTestMatch06, 1);
+ UtRegisterTest("AddressTestMatch07", AddressTestMatch07, 1);
+ UtRegisterTest("AddressTestMatch08", AddressTestMatch08, 1);
+ UtRegisterTest("AddressTestMatch09", AddressTestMatch09, 1);
+ UtRegisterTest("AddressTestMatch10", AddressTestMatch10, 1);
+ UtRegisterTest("AddressTestMatch11", AddressTestMatch11, 1);
+
+ UtRegisterTest("AddressTestCmp01", AddressTestCmp01, 1);
+ UtRegisterTest("AddressTestCmp02", AddressTestCmp02, 1);
+ UtRegisterTest("AddressTestCmp03", AddressTestCmp03, 1);
+ UtRegisterTest("AddressTestCmp04", AddressTestCmp04, 1);
+ UtRegisterTest("AddressTestCmp05", AddressTestCmp05, 1);
+ UtRegisterTest("AddressTestCmp06", AddressTestCmp06, 1);
+ UtRegisterTest("AddressTestCmpIPv407", AddressTestCmpIPv407, 1);
+ UtRegisterTest("AddressTestCmpIPv408", AddressTestCmpIPv408, 1);
+
+ UtRegisterTest("AddressTestCmp07", AddressTestCmp07, 1);
+ UtRegisterTest("AddressTestCmp08", AddressTestCmp08, 1);
+ UtRegisterTest("AddressTestCmp09", AddressTestCmp09, 1);
+ UtRegisterTest("AddressTestCmp10", AddressTestCmp10, 1);
+ UtRegisterTest("AddressTestCmp11", AddressTestCmp11, 1);
+ UtRegisterTest("AddressTestCmp12", AddressTestCmp12, 1);
+
+ UtRegisterTest("AddressTestAddressGroupSetup01",
+ AddressTestAddressGroupSetup01, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup02",
+ AddressTestAddressGroupSetup02, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup03",
+ AddressTestAddressGroupSetup03, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup04",
+ AddressTestAddressGroupSetup04, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup05",
+ AddressTestAddressGroupSetup05, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup06",
+ AddressTestAddressGroupSetup06, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup07",
+ AddressTestAddressGroupSetup07, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup08",
+ AddressTestAddressGroupSetup08, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup09",
+ AddressTestAddressGroupSetup09, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup10",
+ AddressTestAddressGroupSetup10, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup11",
+ AddressTestAddressGroupSetup11, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup12",
+ AddressTestAddressGroupSetup12, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup13",
+ AddressTestAddressGroupSetup13, 1);
+ UtRegisterTest("AddressTestAddressGroupSetupIPv414",
+ AddressTestAddressGroupSetupIPv414, 1);
+ UtRegisterTest("AddressTestAddressGroupSetupIPv415",
+ AddressTestAddressGroupSetupIPv415, 1);
+ UtRegisterTest("AddressTestAddressGroupSetupIPv416",
+ AddressTestAddressGroupSetupIPv416, 1);
+
+ UtRegisterTest("AddressTestAddressGroupSetup14",
+ AddressTestAddressGroupSetup14, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup15",
+ AddressTestAddressGroupSetup15, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup16",
+ AddressTestAddressGroupSetup16, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup17",
+ AddressTestAddressGroupSetup17, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup18",
+ AddressTestAddressGroupSetup18, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup19",
+ AddressTestAddressGroupSetup19, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup20",
+ AddressTestAddressGroupSetup20, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup21",
+ AddressTestAddressGroupSetup21, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup22",
+ AddressTestAddressGroupSetup22, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup23",
+ AddressTestAddressGroupSetup23, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup24",
+ AddressTestAddressGroupSetup24, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup25",
+ AddressTestAddressGroupSetup25, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup26",
+ AddressTestAddressGroupSetup26, 1);
+
+ UtRegisterTest("AddressTestAddressGroupSetup27",
+ AddressTestAddressGroupSetup27, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup28",
+ AddressTestAddressGroupSetup28, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup29",
+ AddressTestAddressGroupSetup29, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup30",
+ AddressTestAddressGroupSetup30, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup31",
+ AddressTestAddressGroupSetup31, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup32",
+ AddressTestAddressGroupSetup32, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup33",
+ AddressTestAddressGroupSetup33, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup34",
+ AddressTestAddressGroupSetup34, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup35",
+ AddressTestAddressGroupSetup35, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup36",
+ AddressTestAddressGroupSetup36, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup37",
+ AddressTestAddressGroupSetup37, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup38",
+ AddressTestAddressGroupSetup38, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup39",
+ AddressTestAddressGroupSetup39, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup40",
+ AddressTestAddressGroupSetup40, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup41",
+ AddressTestAddressGroupSetup41, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup42",
+ AddressTestAddressGroupSetup42, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup43",
+ AddressTestAddressGroupSetup43, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup44",
+ AddressTestAddressGroupSetup44, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup45",
+ AddressTestAddressGroupSetup45, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup46",
+ AddressTestAddressGroupSetup46, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup47",
+ AddressTestAddressGroupSetup47, 1);
+ UtRegisterTest("AddressTestAddressGroupSetup48",
+ AddressTestAddressGroupSetup48, 1);
+
+ UtRegisterTest("AddressTestCutIPv401", AddressTestCutIPv401, 1);
+ UtRegisterTest("AddressTestCutIPv402", AddressTestCutIPv402, 1);
+ UtRegisterTest("AddressTestCutIPv403", AddressTestCutIPv403, 1);
+ UtRegisterTest("AddressTestCutIPv404", AddressTestCutIPv404, 1);
+ UtRegisterTest("AddressTestCutIPv405", AddressTestCutIPv405, 1);
+ UtRegisterTest("AddressTestCutIPv406", AddressTestCutIPv406, 1);
+ UtRegisterTest("AddressTestCutIPv407", AddressTestCutIPv407, 1);
+ UtRegisterTest("AddressTestCutIPv408", AddressTestCutIPv408, 1);
+ UtRegisterTest("AddressTestCutIPv409", AddressTestCutIPv409, 1);
+ UtRegisterTest("AddressTestCutIPv410", AddressTestCutIPv410, 1);
+
+ UtRegisterTest("AddressTestParseInvalidMask01",
+ AddressTestParseInvalidMask01, 1);
+ UtRegisterTest("AddressTestParseInvalidMask02",
+ AddressTestParseInvalidMask02, 1);
+ UtRegisterTest("AddressTestParseInvalidMask03",
+ AddressTestParseInvalidMask03, 1);
+
+ UtRegisterTest("AddressConfVarsTest01 ", AddressConfVarsTest01, 1);
+ UtRegisterTest("AddressConfVarsTest02 ", AddressConfVarsTest02, 1);
+ UtRegisterTest("AddressConfVarsTest03 ", AddressConfVarsTest03, 1);
+ UtRegisterTest("AddressConfVarsTest04 ", AddressConfVarsTest04, 1);
+ UtRegisterTest("AddressConfVarsTest05 ", AddressConfVarsTest05, 1);
+
+ UtRegisterTest("AddressTestFunctions01", AddressTestFunctions01, 1);
+ UtRegisterTest("AddressTestFunctions02", AddressTestFunctions02, 1);
+ UtRegisterTest("AddressTestFunctions03", AddressTestFunctions03, 1);
+ UtRegisterTest("AddressTestFunctions04", AddressTestFunctions04, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-engine-address.h b/framework/src/suricata/src/detect-engine-address.h
new file mode 100644
index 00000000..b29cff1b
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-address.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ADDRESS_H__
+#define __DETECT_ADDRESS_H__
+
+/* prototypes */
+void DetectAddressRegister (void);
+void DetectAddressPrintMemory(void);
+
+DetectAddressHead *DetectAddressHeadInit(void);
+void DetectAddressHeadFree(DetectAddressHead *);
+void DetectAddressHeadCleanup(DetectAddressHead *);
+
+int DetectAddressParseString(DetectAddress *, char *);
+int DetectAddressParse(const DetectEngineCtx *, DetectAddressHead *, char *);
+
+DetectAddress *DetectAddressInit(void);
+void DetectAddressFree(DetectAddress *);
+
+void DetectAddressCleanupList (DetectAddress *);
+int DetectAddressAdd(DetectAddress **, DetectAddress *);
+void DetectAddressPrintList(DetectAddress *);
+
+int DetectAddressInsert(DetectEngineCtx *, DetectAddressHead *, DetectAddress *);
+int DetectAddressJoin(DetectEngineCtx *, DetectAddress *, DetectAddress *);
+
+DetectAddress *DetectAddressLookupInHead(DetectAddressHead *, Address *);
+DetectAddress *DetectAddressLookupInList(DetectAddress *, DetectAddress *);
+int DetectAddressMatch(DetectAddress *, Address *);
+
+DetectAddress *DetectAddressCopy(DetectAddress *);
+void DetectAddressPrint(DetectAddress *);
+int DetectAddressCmp(DetectAddress *, DetectAddress *);
+
+int DetectAddressMatchIPv4(DetectMatchAddressIPv4 *, uint16_t, Address *);
+int DetectAddressMatchIPv6(DetectMatchAddressIPv6 *, uint16_t, Address *);
+
+int DetectAddressTestConfVars(void);
+
+void DetectAddressTests(void);
+
+#endif /* __DETECT_ADDRESS_H__ */
diff --git a/framework/src/suricata/src/detect-engine-alert.c b/framework/src/suricata/src/detect-engine-alert.c
new file mode 100644
index 00000000..c2d7e420
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-alert.c
@@ -0,0 +1,337 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-engine-alert.h"
+#include "detect-engine-threshold.h"
+#include "detect-engine-tag.h"
+
+#include "decode.h"
+
+#include "flow.h"
+#include "flow-private.h"
+
+#include "util-profiling.h"
+
+/** tag signature we use for tag alerts */
+static Signature g_tag_signature;
+/** tag packet alert structure for tag alerts */
+static PacketAlert g_tag_pa;
+
+void PacketAlertTagInit(void)
+{
+ memset(&g_tag_signature, 0x00, sizeof(g_tag_signature));
+
+ g_tag_signature.id = TAG_SIG_ID;
+ g_tag_signature.gid = TAG_SIG_GEN;
+ g_tag_signature.num = TAG_SIG_ID;
+ g_tag_signature.rev = 1;
+ g_tag_signature.prio = 2;
+
+ memset(&g_tag_pa, 0x00, sizeof(g_tag_pa));
+
+ g_tag_pa.action = ACTION_ALERT;
+ g_tag_pa.s = &g_tag_signature;
+}
+
+PacketAlert *PacketAlertGetTag(void)
+{
+ return &g_tag_pa;
+}
+
+/**
+ * \brief Handle a packet and check if needs a threshold logic
+ * Also apply rule action if necessary.
+ *
+ * \param de_ctx Detection Context
+ * \param sig Signature pointer
+ * \param p Packet structure
+ *
+ * \retval 1 alert is not suppressed
+ * \retval 0 alert is suppressed
+ */
+static int PacketAlertHandle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, Packet *p, uint16_t pos)
+{
+ SCEnter();
+ int ret = 1;
+ DetectThresholdData *td = NULL;
+ SigMatch *sm;
+
+ if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) {
+ SCReturnInt(1);
+ }
+
+ /* handle suppressions first */
+ if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_SUPPRESS);
+ sm = NULL;
+ do {
+ td = SigGetThresholdTypeIter(s, p, &sm, DETECT_SM_LIST_SUPPRESS);
+ if (td != NULL) {
+ SCLogDebug("td %p", td);
+
+ /* PacketAlertThreshold returns 2 if the alert is suppressed but
+ * we do need to apply rule actions to the packet. */
+ KEYWORD_PROFILING_START;
+ ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s);
+ if (ret == 0 || ret == 2) {
+ KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 0);
+ /* It doesn't match threshold, remove it */
+ SCReturnInt(ret);
+ }
+ KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
+ }
+ } while (sm != NULL);
+ }
+
+ /* if we're still here, consider thresholding */
+ if (s->sm_lists[DETECT_SM_LIST_THRESHOLD] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_THRESHOLD);
+ sm = NULL;
+ do {
+ td = SigGetThresholdTypeIter(s, p, &sm, DETECT_SM_LIST_THRESHOLD);
+ if (td != NULL) {
+ SCLogDebug("td %p", td);
+
+ /* PacketAlertThreshold returns 2 if the alert is suppressed but
+ * we do need to apply rule actions to the packet. */
+ KEYWORD_PROFILING_START;
+ ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s);
+ if (ret == 0 || ret == 2) {
+ KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD ,0);
+ /* It doesn't match threshold, remove it */
+ SCReturnInt(ret);
+ }
+ KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
+ }
+ } while (sm != NULL);
+ }
+ SCReturnInt(1);
+}
+
+
+/**
+ * \brief Check if a certain sid alerted, this is used in the test functions
+ *
+ * \param p Packet on which we want to check if the signature alerted or not
+ * \param sid Signature id of the signature that thas to be checked for a match
+ *
+ * \retval match A value > 0 on a match; 0 on no match
+ */
+int PacketAlertCheck(Packet *p, uint32_t sid)
+{
+ uint16_t i = 0;
+ int match = 0;
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ if (p->alerts.alerts[i].s == NULL)
+ continue;
+
+ if (p->alerts.alerts[i].s->id == sid)
+ match++;
+ }
+
+ return match;
+}
+
+/**
+ * \brief Remove alert from the p->alerts.alerts array at pos
+ * \param p Pointer to the Packet
+ * \param pos Position in the array
+ * \retval 0 if the number of alerts is less than pos
+ * 1 if all goes well
+ */
+int PacketAlertRemove(Packet *p, uint16_t pos)
+{
+ uint16_t i = 0;
+ int match = 0;
+
+ if (pos > p->alerts.cnt) {
+ SCLogDebug("removing %u failed, pos > cnt %u", pos, p->alerts.cnt);
+ return 0;
+ }
+
+ for (i = pos; i <= p->alerts.cnt - 1; i++) {
+ memcpy(&p->alerts.alerts[i], &p->alerts.alerts[i + 1], sizeof(PacketAlert));
+ }
+
+ // Update it, since we removed 1
+ p->alerts.cnt--;
+
+ return match;
+}
+
+/** \brief append a signature match to a packet
+ *
+ * \param det_ctx thread detection engine ctx
+ * \param s the signature that matched
+ * \param p packet
+ * \param flags alert flags
+ * \param alert_msg ptr to StreamMsg object that the signature matched on
+ */
+int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p, uint64_t tx_id, uint8_t flags)
+{
+ int i = 0;
+
+ if (p->alerts.cnt == PACKET_ALERT_MAX)
+ return 0;
+
+ SCLogDebug("sid %"PRIu32"", s->id);
+
+ /* It should be usually the last, so check it before iterating */
+ if (p->alerts.cnt == 0 || (p->alerts.cnt > 0 &&
+ p->alerts.alerts[p->alerts.cnt - 1].num < s->num)) {
+ /* We just add it */
+ p->alerts.alerts[p->alerts.cnt].num = s->num;
+ p->alerts.alerts[p->alerts.cnt].action = s->action;
+ p->alerts.alerts[p->alerts.cnt].flags = flags;
+ p->alerts.alerts[p->alerts.cnt].s = s;
+ p->alerts.alerts[p->alerts.cnt].tx_id = tx_id;
+ } else {
+ /* We need to make room for this s->num
+ (a bit ugly with memcpy but we are planning changes here)*/
+ for (i = p->alerts.cnt - 1; i >= 0 && p->alerts.alerts[i].num > s->num; i--) {
+ memcpy(&p->alerts.alerts[i + 1], &p->alerts.alerts[i], sizeof(PacketAlert));
+ }
+
+ i++; /* The right place to store the alert */
+
+ p->alerts.alerts[i].num = s->num;
+ p->alerts.alerts[i].action = s->action;
+ p->alerts.alerts[i].flags = flags;
+ p->alerts.alerts[i].s = s;
+ p->alerts.alerts[i].tx_id = tx_id;
+ }
+
+ /* Update the count */
+ p->alerts.cnt++;
+
+ return 0;
+}
+
+/**
+ * \brief Check the threshold of the sigs that match, set actions, break on pass action
+ * This function iterate the packet alerts array, removing those that didn't match
+ * the threshold, and those that match after a signature with the action "pass".
+ * The array is sorted by action priority/order
+ * \param de_ctx detection engine context
+ * \param det_ctx detection engine thread context
+ * \param p pointer to the packet
+ */
+void PacketAlertFinalize(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+ SCEnter();
+ int i = 0;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+
+ while (i < p->alerts.cnt) {
+ SCLogDebug("Sig->num: %"PRIu16, p->alerts.alerts[i].num);
+ s = de_ctx->sig_array[p->alerts.alerts[i].num];
+
+ int res = PacketAlertHandle(de_ctx, det_ctx, s, p, i);
+ if (res > 0) {
+ /* Now, if we have an alert, we have to check if we want
+ * to tag this session or src/dst host */
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_TMATCH);
+ sm = s->sm_lists[DETECT_SM_LIST_TMATCH];
+ while (sm) {
+ /* tags are set only for alerts */
+ KEYWORD_PROFILING_START;
+ sigmatch_table[sm->type].Match(NULL, det_ctx, p, s, sm->ctx);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+ sm = sm->next;
+ }
+
+ if (s->flags & SIG_FLAG_IPONLY) {
+ if (((p->flowflags & FLOW_PKT_TOSERVER) && !(p->flowflags & FLOW_PKT_TOSERVER_IPONLY_SET)) ||
+ ((p->flowflags & FLOW_PKT_TOCLIENT) && !(p->flowflags & FLOW_PKT_TOCLIENT_IPONLY_SET))) {
+ SCLogDebug("testing against \"ip-only\" signatures");
+
+ if (p->flow != NULL) {
+ /* Update flow flags for iponly */
+ FLOWLOCK_WRLOCK(p->flow);
+ FlowSetIPOnlyFlagNoLock(p->flow, p->flowflags & FLOW_PKT_TOSERVER ? 1 : 0);
+
+ if (s->action & ACTION_DROP)
+ p->flow->flags |= FLOW_ACTION_DROP;
+ if (s->action & ACTION_REJECT)
+ p->flow->flags |= FLOW_ACTION_DROP;
+ if (s->action & ACTION_REJECT_DST)
+ p->flow->flags |= FLOW_ACTION_DROP;
+ if (s->action & ACTION_REJECT_BOTH)
+ p->flow->flags |= FLOW_ACTION_DROP;
+ if (s->action & ACTION_PASS) {
+ FlowSetNoPacketInspectionFlag(p->flow);
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+ }
+
+ /* set actions on packet */
+ DetectSignatureApplyActions(p, p->alerts.alerts[i].s);
+
+ if (PACKET_TEST_ACTION(p, ACTION_PASS)) {
+ /* Ok, reset the alert cnt to end in the previous of pass
+ * so we ignore the rest with less prio */
+ p->alerts.cnt = i;
+
+ /* if an stream/app-layer match we enforce the pass for the flow */
+ if ((p->flow != NULL) &&
+ (p->alerts.alerts[i].flags &
+ (PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_STREAM_MATCH)))
+ {
+ FlowLockSetNoPacketInspectionFlag(p->flow);
+ }
+ break;
+
+ /* if the signature wants to drop, check if the
+ * PACKET_ALERT_FLAG_DROP_FLOW flag is set. */
+ } else if ((PACKET_TEST_ACTION(p, ACTION_DROP)) &&
+ ((p->alerts.alerts[i].flags & PACKET_ALERT_FLAG_DROP_FLOW) ||
+ (s->flags & SIG_FLAG_APPLAYER))
+ && p->flow != NULL)
+ {
+ FLOWLOCK_WRLOCK(p->flow);
+ /* This will apply only on IPS mode (check StreamTcpPacket) */
+ p->flow->flags |= FLOW_ACTION_DROP;
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+
+ /* Thresholding removes this alert */
+ if (res == 0 || res == 2) {
+ PacketAlertRemove(p, i);
+
+ if (p->alerts.cnt == 0)
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ /* At this point, we should have all the new alerts. Now check the tag
+ * keyword context for sessions and hosts */
+ if (!(p->flags & PKT_PSEUDO_STREAM_END))
+ TagHandlePacket(de_ctx, det_ctx, p);
+}
+
+
diff --git a/framework/src/suricata/src/detect-engine-alert.h b/framework/src/suricata/src/detect-engine-alert.h
new file mode 100644
index 00000000..6f200b26
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-alert.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_ALERT_H__
+#define __DETECT_ENGINE_ALERT_H__
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+
+void PacketAlertFinalize(DetectEngineCtx *, DetectEngineThreadCtx *, Packet *);
+int PacketAlertAppend(DetectEngineThreadCtx *, Signature *, Packet *, uint64_t tx_id, uint8_t);
+int PacketAlertCheck(Packet *, uint32_t);
+int PacketAlertRemove(Packet *, uint16_t);
+void PacketAlertTagInit(void);
+PacketAlert *PacketAlertGetTag(void);
+
+#endif /* __DETECT_ENGINE_ALERT_H__ */
diff --git a/framework/src/suricata/src/detect-engine-analyzer.c b/framework/src/suricata/src/detect-engine-analyzer.c
new file mode 100644
index 00000000..066d41c6
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-analyzer.c
@@ -0,0 +1,926 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ *
+ * Rule analyzer for the detection engine
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-analyzer.h"
+#include "detect-engine-mpm.h"
+#include "conf.h"
+#include "detect-content.h"
+#include "detect-flow.h"
+#include "detect-flags.h"
+#include "util-print.h"
+
+static int rule_warnings_only = 0;
+static FILE *rule_engine_analysis_FD = NULL;
+static FILE *fp_engine_analysis_FD = NULL;
+static pcre *percent_re = NULL;
+static pcre_extra *percent_re_study = NULL;
+static char log_path[PATH_MAX];
+
+typedef struct FpPatternStats_ {
+ uint16_t min;
+ uint16_t max;
+ uint32_t cnt;
+ uint64_t tot;
+} FpPatternStats;
+
+static FpPatternStats fp_pattern_stats[DETECT_SM_LIST_MAX];
+
+static void FpPatternStatsAdd(int list, uint16_t patlen)
+{
+ if (list < 0 || list >= DETECT_SM_LIST_MAX)
+ return;
+
+ FpPatternStats *f = &fp_pattern_stats[list];
+
+ if (f->min == 0)
+ f->min = patlen;
+ else if (patlen < f->min)
+ f->min = patlen;
+
+ if (patlen > f->max)
+ f->max = patlen;
+
+ f->cnt++;
+ f->tot += patlen;
+}
+
+void EngineAnalysisFP(Signature *s, char *line)
+{
+ int fast_pattern_set = 0;
+ int fast_pattern_only_set = 0;
+ int fast_pattern_chop_set = 0;
+ DetectContentData *fp_cd = NULL;
+ SigMatch *mpm_sm = s->mpm_sm;
+
+ if (mpm_sm != NULL) {
+ fp_cd = (DetectContentData *)mpm_sm->ctx;
+ if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ fast_pattern_set = 1;
+ if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ fast_pattern_only_set = 1;
+ } else if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ fast_pattern_chop_set = 1;
+ }
+ }
+ }
+
+ fprintf(fp_engine_analysis_FD, "== Sid: %u ==\n", s->id);
+ fprintf(fp_engine_analysis_FD, "%s\n", line);
+
+ fprintf(fp_engine_analysis_FD, " Fast Pattern analysis:\n");
+ if (fp_cd == NULL) {
+ fprintf(fp_engine_analysis_FD, " No content present\n");
+ fprintf(fp_engine_analysis_FD, "\n");
+ return;
+ }
+
+ fprintf(fp_engine_analysis_FD, " Fast pattern matcher: ");
+ int list_type = SigMatchListSMBelongsTo(s, mpm_sm);
+ if (list_type == DETECT_SM_LIST_PMATCH)
+ fprintf(fp_engine_analysis_FD, "content\n");
+ else if (list_type == DETECT_SM_LIST_UMATCH)
+ fprintf(fp_engine_analysis_FD, "http uri content\n");
+ else if (list_type == DETECT_SM_LIST_HRUDMATCH)
+ fprintf(fp_engine_analysis_FD, "http raw uri content\n");
+ else if (list_type == DETECT_SM_LIST_HHDMATCH)
+ fprintf(fp_engine_analysis_FD, "http header content\n");
+ else if (list_type == DETECT_SM_LIST_HRHDMATCH)
+ fprintf(fp_engine_analysis_FD, "http raw header content\n");
+ else if (list_type == DETECT_SM_LIST_HMDMATCH)
+ fprintf(fp_engine_analysis_FD, "http method content\n");
+ else if (list_type == DETECT_SM_LIST_HCDMATCH)
+ fprintf(fp_engine_analysis_FD, "http cookie content\n");
+ else if (list_type == DETECT_SM_LIST_HCBDMATCH)
+ fprintf(fp_engine_analysis_FD, "http client body content\n");
+ else if (list_type == DETECT_SM_LIST_FILEDATA)
+ fprintf(fp_engine_analysis_FD, "http server body content\n");
+ else if (list_type == DETECT_SM_LIST_HSCDMATCH)
+ fprintf(fp_engine_analysis_FD, "http stat code content\n");
+ else if (list_type == DETECT_SM_LIST_HSMDMATCH)
+ fprintf(fp_engine_analysis_FD, "http stat msg content\n");
+ else if (list_type == DETECT_SM_LIST_HUADMATCH)
+ fprintf(fp_engine_analysis_FD, "http user agent content\n");
+
+ int flags_set = 0;
+ fprintf(fp_engine_analysis_FD, " Flags:");
+ if (fp_cd->flags & DETECT_CONTENT_OFFSET) {
+ fprintf(fp_engine_analysis_FD, " Offset");
+ flags_set = 1;
+ } if (fp_cd->flags & DETECT_CONTENT_DEPTH) {
+ fprintf(fp_engine_analysis_FD, " Depth");
+ flags_set = 1;
+ }
+ if (fp_cd->flags & DETECT_CONTENT_WITHIN) {
+ fprintf(fp_engine_analysis_FD, " Within");
+ flags_set = 1;
+ }
+ if (fp_cd->flags & DETECT_CONTENT_DISTANCE) {
+ fprintf(fp_engine_analysis_FD, " Distance");
+ flags_set = 1;
+ }
+ if (fp_cd->flags & DETECT_CONTENT_NOCASE) {
+ fprintf(fp_engine_analysis_FD, " Nocase");
+ flags_set = 1;
+ }
+ if (fp_cd->flags & DETECT_CONTENT_NEGATED) {
+ fprintf(fp_engine_analysis_FD, " Negated");
+ flags_set = 1;
+ }
+ if (flags_set == 0)
+ fprintf(fp_engine_analysis_FD, " None");
+ fprintf(fp_engine_analysis_FD, "\n");
+
+ fprintf(fp_engine_analysis_FD, " Fast pattern set: %s\n", fast_pattern_set ? "yes" : "no");
+ fprintf(fp_engine_analysis_FD, " Fast pattern only set: %s\n",
+ fast_pattern_only_set ? "yes" : "no");
+ fprintf(fp_engine_analysis_FD, " Fast pattern chop set: %s\n",
+ fast_pattern_chop_set ? "yes" : "no");
+ if (fast_pattern_chop_set) {
+ fprintf(fp_engine_analysis_FD, " Fast pattern offset, length: %u, %u\n",
+ fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
+ }
+
+ uint16_t patlen = fp_cd->content_len;
+ uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
+ if (unlikely(pat == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(pat, fp_cd->content, fp_cd->content_len);
+ pat[fp_cd->content_len] = '\0';
+ fprintf(fp_engine_analysis_FD, " Original content: ");
+ PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
+ fprintf(fp_engine_analysis_FD, "\n");
+
+ if (fast_pattern_chop_set) {
+ SCFree(pat);
+ patlen = fp_cd->fp_chop_len;
+ pat = SCMalloc(fp_cd->fp_chop_len + 1);
+ if (unlikely(pat == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
+ pat[fp_cd->fp_chop_len] = '\0';
+ fprintf(fp_engine_analysis_FD, " Final content: ");
+ PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
+ fprintf(fp_engine_analysis_FD, "\n");
+
+ FpPatternStatsAdd(list_type, patlen);
+ } else {
+ fprintf(fp_engine_analysis_FD, " Final content: ");
+ PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
+ fprintf(fp_engine_analysis_FD, "\n");
+
+ FpPatternStatsAdd(list_type, patlen);
+ }
+ SCFree(pat);
+
+ fprintf(fp_engine_analysis_FD, "\n");
+ return;
+}
+
+/**
+ * \brief Sets up the fast pattern analyzer according to the config.
+ *
+ * \retval 1 If rule analyzer successfully enabled.
+ * \retval 0 If not enabled.
+ */
+int SetupFPAnalyzer(void)
+{
+ int fp_engine_analysis_set = 0;
+
+ if ((ConfGetBool("engine-analysis.rules-fast-pattern",
+ &fp_engine_analysis_set)) == 0) {
+ return 0;
+ }
+
+ if (fp_engine_analysis_set == 0)
+ return 0;
+
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+ snprintf(log_path, sizeof(log_path), "%s/%s", log_dir,
+ "rules_fast_pattern.txt");
+
+ fp_engine_analysis_FD = fopen(log_path, "w");
+ if (fp_engine_analysis_FD == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path,
+ strerror(errno));
+ return 0;
+ }
+
+ SCLogInfo("Engine-Analysis for fast_pattern printed to file - %s",
+ log_path);
+
+ struct timeval tval;
+ struct tm *tms;
+ gettimeofday(&tval, NULL);
+ struct tm local_tm;
+ tms = SCLocalTime(tval.tv_sec, &local_tm);
+ fprintf(fp_engine_analysis_FD, "----------------------------------------------"
+ "---------------------\n");
+ fprintf(fp_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
+ "%02d:%02d:%02d\n",
+ tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour,
+ tms->tm_min, tms->tm_sec);
+ fprintf(fp_engine_analysis_FD, "----------------------------------------------"
+ "---------------------\n");
+
+ memset(&fp_pattern_stats, 0, sizeof(fp_pattern_stats));
+ return 1;
+}
+
+/**
+ * \brief Sets up the rule analyzer according to the config
+ * \retval 1 if rule analyzer successfully enabled
+ * \retval 0 if not enabled
+ */
+int SetupRuleAnalyzer(void)
+{
+ ConfNode *conf = ConfGetNode("engine-analysis");
+ int enabled = 0;
+ if (conf != NULL) {
+ const char *value = ConfNodeLookupChildValue(conf, "rules");
+ if (value && ConfValIsTrue(value)) {
+ enabled = 1;
+ } else if (value && strcasecmp(value, "warnings-only") == 0) {
+ enabled = 1;
+ rule_warnings_only = 1;
+ }
+ if (enabled) {
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+ snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, "rules_analysis.txt");
+ rule_engine_analysis_FD = fopen(log_path, "w");
+ if (rule_engine_analysis_FD == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path, strerror(errno));
+ return 0;
+ }
+
+ SCLogInfo("Engine-Analysis for rules printed to file - %s",
+ log_path);
+
+ struct timeval tval;
+ struct tm *tms;
+ gettimeofday(&tval, NULL);
+ struct tm local_tm;
+ tms = SCLocalTime(tval.tv_sec, &local_tm);
+ fprintf(rule_engine_analysis_FD, "----------------------------------------------"
+ "---------------------\n");
+ fprintf(rule_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
+ "%02d:%02d:%02d\n",
+ tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour,
+ tms->tm_min, tms->tm_sec);
+ fprintf(rule_engine_analysis_FD, "----------------------------------------------"
+ "---------------------\n");
+
+ /*compile regex's for rule analysis*/
+ if (PerCentEncodingSetup()== 0) {
+ fprintf(rule_engine_analysis_FD, "Error compiling regex; can't check for percent encoding in normalized http content.\n");
+ }
+ }
+ }
+ else {
+ SCLogInfo("Conf parameter \"engine-analysis.rules\" not found. "
+ "Defaulting to not printing the rules analysis report.");
+ }
+ if (!enabled) {
+ SCLogInfo("Engine-Analysis for rules disabled in conf file.");
+ return 0;
+ }
+ return 1;
+}
+
+void CleanupFPAnalyzer(void)
+{
+ fprintf(fp_engine_analysis_FD, "============\n"
+ "Summary:\n============\n");
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ FpPatternStats *f = &fp_pattern_stats[i];
+ if (f->cnt == 0)
+ continue;
+
+ fprintf(fp_engine_analysis_FD,
+ "%s, smallest pattern %u byte(s), longest pattern %u byte(s), number of patterns %u, avg pattern len %.2f byte(s)\n",
+ DetectSigmatchListEnumToString(i), f->min, f->max, f->cnt, (float)((double)f->tot/(float)f->cnt));
+ }
+
+ if (fp_engine_analysis_FD != NULL) {
+ fclose(fp_engine_analysis_FD);
+ fp_engine_analysis_FD = NULL;
+ }
+
+ return;
+}
+
+
+void CleanupRuleAnalyzer(void)
+{
+ if (rule_engine_analysis_FD != NULL) {
+ SCLogInfo("Engine-Analyis for rules printed to file - %s", log_path);
+ fclose(rule_engine_analysis_FD);
+ rule_engine_analysis_FD = NULL;
+ }
+}
+
+/**
+ * \brief Compiles regex for rule analysis
+ * \retval 1 if successful
+ * \retval 0 if on error
+ */
+int PerCentEncodingSetup ()
+{
+#define DETECT_PERCENT_ENCODING_REGEX "%[0-9|a-f|A-F]{2}"
+ const char *eb = NULL;
+ int eo = 0;
+ int opts = 0; //PCRE_NEWLINE_ANY??
+
+ percent_re = pcre_compile(DETECT_PERCENT_ENCODING_REGEX, opts, &eb, &eo, NULL);
+ if (percent_re == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_PERCENT_ENCODING_REGEX, eo, eb);
+ return 0;
+ }
+
+ percent_re_study = pcre_study(percent_re, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * \brief Checks for % encoding in content.
+ * \param Pointer to content
+ * \retval number of matches if content has % encoding
+ * \retval 0 if it doesn't have % encoding
+ * \retval -1 on error
+ */
+int PerCentEncodingMatch (uint8_t *content, uint8_t content_len)
+{
+#define MAX_ENCODED_CHARS 240
+ int ret = 0;
+ int ov[MAX_ENCODED_CHARS];
+
+ ret = pcre_exec(percent_re, percent_re_study, (char *)content, content_len, 0, 0, ov, MAX_ENCODED_CHARS);
+ if (ret == -1) {
+ return 0;
+ }
+ else if (ret < -1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Error parsing content - %s; error code is %d", content, ret);
+ return -1;
+ }
+ return ret;
+}
+
+static void EngineAnalysisRulesPrintFP(Signature *s)
+{
+ DetectContentData *fp_cd = NULL;
+ SigMatch *mpm_sm = s->mpm_sm;
+
+ if (mpm_sm != NULL) {
+ fp_cd = (DetectContentData *)mpm_sm->ctx;
+ }
+
+ if (fp_cd == NULL) {
+ return;
+ }
+
+ uint16_t patlen = fp_cd->content_len;
+ uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
+ if (unlikely(pat == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(pat, fp_cd->content, fp_cd->content_len);
+ pat[fp_cd->content_len] = '\0';
+
+ if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ SCFree(pat);
+ patlen = fp_cd->fp_chop_len;
+ pat = SCMalloc(fp_cd->fp_chop_len + 1);
+ if (unlikely(pat == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
+ pat[fp_cd->fp_chop_len] = '\0';
+ fprintf(rule_engine_analysis_FD, " Fast Pattern \"");
+ PrintRawUriFp(rule_engine_analysis_FD, pat, patlen);
+ } else {
+ fprintf(rule_engine_analysis_FD, " Fast Pattern \"");
+ PrintRawUriFp(rule_engine_analysis_FD, pat, patlen);
+ }
+ SCFree(pat);
+
+ fprintf(rule_engine_analysis_FD, "\" on \"");
+
+ int list_type = SigMatchListSMBelongsTo(s, mpm_sm);
+ if (list_type == DETECT_SM_LIST_PMATCH) {
+ int payload = 0;
+ int stream = 0;
+ if (SignatureHasPacketContent(s))
+ payload = 1;
+ if (SignatureHasStreamContent(s))
+ stream = 1;
+ fprintf(rule_engine_analysis_FD, "%s",
+ payload ? (stream ? "payload and reassembled stream" : "payload") : "reassembled stream");
+ }
+ else if (list_type == DETECT_SM_LIST_UMATCH)
+ fprintf(rule_engine_analysis_FD, "http uri content");
+ else if (list_type == DETECT_SM_LIST_HRUDMATCH)
+ fprintf(rule_engine_analysis_FD, "http raw uri content");
+ else if (list_type == DETECT_SM_LIST_HHDMATCH)
+ fprintf(rule_engine_analysis_FD, "http header content");
+ else if (list_type == DETECT_SM_LIST_HRHDMATCH)
+ fprintf(rule_engine_analysis_FD, "http raw header content");
+ else if (list_type == DETECT_SM_LIST_HMDMATCH)
+ fprintf(rule_engine_analysis_FD, "http method content");
+ else if (list_type == DETECT_SM_LIST_HCDMATCH)
+ fprintf(rule_engine_analysis_FD, "http cookie content");
+ else if (list_type == DETECT_SM_LIST_HCBDMATCH)
+ fprintf(rule_engine_analysis_FD, "http client body content");
+ else if (list_type == DETECT_SM_LIST_FILEDATA)
+ fprintf(rule_engine_analysis_FD, "http server body content");
+ else if (list_type == DETECT_SM_LIST_HSCDMATCH)
+ fprintf(rule_engine_analysis_FD, "http stat code content");
+ else if (list_type == DETECT_SM_LIST_HSMDMATCH)
+ fprintf(rule_engine_analysis_FD, "http stat msg content");
+ else if (list_type == DETECT_SM_LIST_HUADMATCH)
+ fprintf(rule_engine_analysis_FD, "http user agent content");
+ else if (list_type == DETECT_SM_LIST_DNSQUERYNAME_MATCH)
+ fprintf(rule_engine_analysis_FD, "dns query name content");
+
+ fprintf(rule_engine_analysis_FD, "\" buffer.\n");
+
+ return;
+}
+
+
+void EngineAnalysisRulesFailure(char *line, char *file, int lineno)
+{
+ fprintf(rule_engine_analysis_FD, "== Sid: UNKNOWN ==\n");
+ fprintf(rule_engine_analysis_FD, "%s\n", line);
+ fprintf(rule_engine_analysis_FD, " FAILURE: invalid rule.\n");
+ fprintf(rule_engine_analysis_FD, " File: %s.\n", file);
+ fprintf(rule_engine_analysis_FD, " Line: %d.\n", lineno);
+ fprintf(rule_engine_analysis_FD, "\n");
+}
+
+/**
+ * \brief Prints analysis of loaded rules.
+ *
+ * Warns if potential rule issues are detected. For example,
+ * warns if a rule uses a construct that may perform poorly,
+ * e.g. pcre without content or with http_method content only;
+ * warns if a rule uses a construct that may not be consistent with intent,
+ * e.g. client side ports only, http and content without any http_* modifiers, etc.
+ *
+ * \param s Pointer to the signature.
+ */
+void EngineAnalysisRules(Signature *s, char *line)
+{
+ uint32_t rule_bidirectional = 0;
+ uint32_t rule_pcre = 0;
+ uint32_t rule_pcre_http = 0;
+ uint32_t rule_content = 0;
+ uint32_t rule_flow = 0;
+ uint32_t rule_flags = 0;
+ uint32_t rule_flow_toserver = 0;
+ uint32_t rule_flow_toclient = 0;
+ uint32_t rule_flow_nostream = 0;
+ uint32_t rule_ipv4_only = 0;
+ uint32_t rule_ipv6_only = 0;
+ uint32_t rule_flowbits = 0;
+ uint32_t rule_flowint = 0;
+ //uint32_t rule_flowvar = 0;
+ uint32_t rule_content_http = 0;
+ uint32_t rule_content_offset_depth = 0;
+ uint32_t list_id = 0;
+ uint32_t rule_warning = 0;
+ uint32_t raw_http_buf = 0;
+ uint32_t norm_http_buf = 0;
+ uint32_t stream_buf = 0;
+ uint32_t packet_buf = 0;
+ uint32_t http_header_buf = 0;
+ uint32_t http_uri_buf = 0;
+ uint32_t http_method_buf = 0;
+ uint32_t http_cookie_buf = 0;
+ uint32_t http_client_body_buf = 0;
+ uint32_t http_server_body_buf = 0;
+ uint32_t http_stat_code_buf = 0;
+ uint32_t http_stat_msg_buf = 0;
+ uint32_t http_raw_header_buf = 0;
+ uint32_t http_raw_uri_buf = 0;
+ uint32_t http_ua_buf = 0;
+ uint32_t warn_pcre_no_content = 0;
+ uint32_t warn_pcre_http_content = 0;
+ uint32_t warn_pcre_http = 0;
+ uint32_t warn_content_http_content = 0;
+ uint32_t warn_content_http = 0;
+ uint32_t warn_tcp_no_flow = 0;
+ uint32_t warn_client_ports = 0;
+ uint32_t warn_direction = 0;
+ uint32_t warn_method_toclient = 0;
+ uint32_t warn_method_serverbody = 0;
+ uint32_t warn_pcre_method = 0;
+ uint32_t warn_encoding_norm_http_buf = 0;
+ uint32_t warn_offset_depth_pkt_stream = 0;
+ uint32_t warn_offset_depth_alproto = 0;
+ uint32_t warn_non_alproto_fp_for_alproto_sig = 0;
+
+ if (s->init_flags & SIG_FLAG_INIT_BIDIREC) {
+ rule_bidirectional = 1;
+ }
+
+ if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
+ packet_buf += 1;
+ }
+ if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
+ stream_buf += 1;
+ }
+
+ if (s->proto.flags & DETECT_PROTO_IPV4) {
+ rule_ipv4_only += 1;
+ }
+ if (s->proto.flags & DETECT_PROTO_IPV6) {
+ rule_ipv6_only += 1;
+ }
+
+ for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+
+ SigMatch *sm = NULL;
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_PCRE) {
+ if (list_id == DETECT_SM_LIST_HCBDMATCH) {
+ rule_pcre_http += 1;
+ http_client_body_buf += 1;
+ raw_http_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_UMATCH) {
+ rule_pcre_http += 1;
+ norm_http_buf += 1;
+ http_uri_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HHDMATCH) {
+ rule_pcre_http += 1;
+ norm_http_buf += 1;
+ http_header_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HCDMATCH) {
+ rule_pcre_http += 1;
+ norm_http_buf += 1;
+ http_cookie_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_FILEDATA) {
+ rule_pcre_http += 1;
+ http_server_body_buf += 1;
+ raw_http_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HRHDMATCH) {
+ rule_pcre_http += 1;
+ raw_http_buf += 1;
+ http_raw_header_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HMDMATCH) {
+ rule_pcre_http += 1;
+ raw_http_buf += 1;
+ http_method_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HRUDMATCH) {
+ rule_pcre_http += 1;
+ raw_http_buf += 1;
+ http_raw_uri_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HSMDMATCH) {
+ rule_pcre_http += 1;
+ raw_http_buf += 1;
+ http_stat_msg_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HSCDMATCH) {
+ rule_pcre_http += 1;
+ raw_http_buf += 1;
+ http_stat_code_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HUADMATCH) {
+ rule_pcre_http += 1;
+ norm_http_buf += 1;
+ http_ua_buf += 1;
+ }
+ else {
+ rule_pcre += 1;
+ }
+ }
+ else if (sm->type == DETECT_CONTENT) {
+
+ if (list_id == DETECT_SM_LIST_UMATCH
+ || list_id == DETECT_SM_LIST_HHDMATCH
+ || list_id == DETECT_SM_LIST_HCDMATCH) {
+ rule_content_http += 1;
+ norm_http_buf += 1;
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd != NULL && PerCentEncodingMatch(cd->content, cd->content_len) > 0) {
+ warn_encoding_norm_http_buf += 1;
+ rule_warning += 1;
+ }
+ if (list_id == DETECT_SM_LIST_UMATCH) {
+ http_uri_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HHDMATCH) {
+ http_header_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HCDMATCH) {
+ http_cookie_buf += 1;
+ }
+ }
+ else if (list_id == DETECT_SM_LIST_HCBDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_client_body_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_FILEDATA) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_server_body_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HRHDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_raw_header_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HRUDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_raw_uri_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HSMDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_stat_msg_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HSCDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_stat_code_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_HMDMATCH) {
+ rule_content_http += 1;
+ raw_http_buf += 1;
+ http_method_buf += 1;
+ }
+ else if (list_id == DETECT_SM_LIST_PMATCH) {
+ rule_content += 1;
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->flags &
+ (DETECT_CONTENT_OFFSET | DETECT_CONTENT_DEPTH)) {
+ rule_content_offset_depth++;
+ }
+ }
+ }
+ else if (sm->type == DETECT_FLOW) {
+ rule_flow += 1;
+ if ((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_TOCLIENT)) {
+ rule_flow_toserver = 1;
+ }
+ else if ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_TOSERVER)) {
+ rule_flow_toclient = 1;
+ }
+ DetectFlowData *fd = (DetectFlowData *)sm->ctx;
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_NOSTREAM)
+ rule_flow_nostream = 1;
+ }
+ }
+ else if (sm->type == DETECT_FLOWBITS) {
+ if (list_id == DETECT_SM_LIST_MATCH) {
+ rule_flowbits += 1;
+ }
+ }
+ else if (sm->type == DETECT_FLOWINT) {
+ if (list_id == DETECT_SM_LIST_MATCH) {
+ rule_flowint += 1;
+ }
+ }
+ else if (sm->type == DETECT_FLAGS) {
+ DetectFlagsData *fd = (DetectFlagsData *)sm->ctx;
+ if (fd != NULL) {
+ rule_flags = 1;
+ }
+ }
+ } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+
+ } /* for ( ; list_id < DETECT_SM_LIST_MAX; list_id++) */
+
+
+ if (rule_pcre > 0 && rule_content == 0 && rule_content_http == 0) {
+ rule_warning += 1;
+ warn_pcre_no_content = 1;
+ }
+
+ if (rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0) {
+ rule_warning += 1;
+ warn_pcre_http_content = 1;
+ }
+ else if (s->alproto == ALPROTO_HTTP && rule_pcre > 0 && rule_pcre_http == 0) {
+ rule_warning += 1;
+ warn_pcre_http = 1;
+ }
+
+ if (rule_content > 0 && rule_content_http > 0) {
+ rule_warning += 1;
+ warn_content_http_content = 1;
+ }
+ if (s->alproto == ALPROTO_HTTP && rule_content > 0 && rule_content_http == 0) {
+ rule_warning += 1;
+ warn_content_http = 1;
+ }
+ if (rule_content == 1) {
+ //todo: warning if content is weak, separate warning for pcre + weak content
+ }
+ if (rule_flow == 0 && rule_flags == 0
+ && !(s->proto.flags & DETECT_PROTO_ANY) && DetectProtoContainsProto(&s->proto, IPPROTO_TCP)
+ && (rule_content || rule_content_http || rule_pcre || rule_pcre_http || rule_flowbits)) {
+ rule_warning += 1;
+ warn_tcp_no_flow = 1;
+ }
+ if (rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
+ && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))) {
+ if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
+ || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))) {
+ rule_warning += 1;
+ warn_client_ports = 1;
+ }
+ }
+ if (rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)) {
+ rule_warning += 1;
+ warn_direction = 1;
+ }
+ if (http_method_buf) {
+ if (rule_flow && rule_flow_toclient) {
+ rule_warning += 1;
+ warn_method_toclient = 1;
+ }
+ if (http_server_body_buf) {
+ rule_warning += 1;
+ warn_method_serverbody = 1;
+ }
+ if (rule_content == 0 && rule_content_http == 0 && (rule_pcre > 0 || rule_pcre_http > 0)) {
+ rule_warning += 1;
+ warn_pcre_method = 1;
+ }
+ }
+ if (rule_content_offset_depth > 0 && stream_buf && packet_buf) {
+ rule_warning += 1;
+ warn_offset_depth_pkt_stream = 1;
+ }
+ if (rule_content_offset_depth > 0 && !stream_buf && packet_buf && s->alproto != ALPROTO_UNKNOWN) {
+ rule_warning += 1;
+ warn_offset_depth_alproto = 1;
+ }
+ if (s->mpm_sm != NULL && s->alproto == ALPROTO_HTTP &&
+ SigMatchListSMBelongsTo(s, s->mpm_sm) == DETECT_SM_LIST_PMATCH) {
+ rule_warning += 1;
+ warn_non_alproto_fp_for_alproto_sig = 1;
+ }
+
+ if (!rule_warnings_only || (rule_warnings_only && rule_warning > 0)) {
+ fprintf(rule_engine_analysis_FD, "== Sid: %u ==\n", s->id);
+ fprintf(rule_engine_analysis_FD, "%s\n", line);
+
+ if (s->flags & SIG_FLAG_IPONLY) fprintf(rule_engine_analysis_FD, " Rule is ip only.\n");
+ if (rule_ipv6_only) fprintf(rule_engine_analysis_FD, " Rule is IPv6 only.\n");
+ if (rule_ipv4_only) fprintf(rule_engine_analysis_FD, " Rule is IPv4 only.\n");
+ if (packet_buf) fprintf(rule_engine_analysis_FD, " Rule matches on packets.\n");
+ if (!rule_flow_nostream && stream_buf && (rule_flow || rule_flowbits || rule_content || rule_pcre)) {
+ fprintf(rule_engine_analysis_FD, " Rule matches on reassembled stream.\n");
+ }
+ if (http_uri_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http uri buffer.\n");
+ if (http_header_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http header buffer.\n");
+ if (http_cookie_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http cookie buffer.\n");
+ if (http_raw_uri_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http raw uri buffer.\n");
+ if (http_raw_header_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http raw header buffer.\n");
+ if (http_method_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http method buffer.\n");
+ if (http_server_body_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http server body buffer.\n");
+ if (http_client_body_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http client body buffer.\n");
+ if (http_stat_msg_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http stat msg buffer.\n");
+ if (http_stat_code_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http stat code buffer.\n");
+ if (http_ua_buf) fprintf(rule_engine_analysis_FD, " Rule matches on http user agent buffer.\n");
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ fprintf(rule_engine_analysis_FD, " App layer protocol is %s.\n", AppProtoToString(s->alproto));
+ }
+ if (rule_content || rule_content_http || rule_pcre || rule_pcre_http) {
+ fprintf(rule_engine_analysis_FD, " Rule contains %d content options, %d http content options, %d pcre options, and %d pcre options with http modifiers.\n", rule_content, rule_content_http, rule_pcre, rule_pcre_http);
+ }
+
+ /* print fast pattern info */
+ EngineAnalysisRulesPrintFP(s);
+
+ /* this is where the warnings start */
+ if (warn_pcre_no_content /*rule_pcre > 0 && rule_content == 0 && rule_content_http == 0*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule uses pcre without a content option present.\n"
+ " -Consider adding a content to improve performance of this rule.\n");
+ }
+ if (warn_pcre_http_content /*rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule uses content options with http_* and pcre options without http modifiers.\n"
+ " -Consider adding http pcre modifier.\n");
+ }
+ else if (warn_pcre_http /*s->alproto == ALPROTO_HTTP && rule_pcre > 0 && rule_pcre_http == 0*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule app layer protocol is http, but pcre options do not have http modifiers.\n"
+ " -Consider adding http pcre modifiers.\n");
+ }
+ if (warn_content_http_content /*rule_content > 0 && rule_content_http > 0*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule contains content with http_* and content without http_*.\n"
+ " -Consider adding http content modifiers.\n");
+ }
+ if (warn_content_http /*s->alproto == ALPROTO_HTTP && rule_content > 0 && rule_content_http == 0*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule app layer protocol is http, but content options do not have http_* modifiers.\n"
+ " -Consider adding http content modifiers.\n");
+ }
+ if (rule_content == 1) {
+ //todo: warning if content is weak, separate warning for pcre + weak content
+ }
+ if (warn_encoding_norm_http_buf) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule may contain percent encoded content for a normalized http buffer match.\n");
+ }
+ if (warn_tcp_no_flow /*rule_flow == 0 && rule_flow == 0
+ && !(s->proto.flags & DETECT_PROTO_ANY) && DetectProtoContainsProto(&s->proto, IPPROTO_TCP)*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: TCP rule without a flow or flags option.\n"
+ " -Consider adding flow or flags to improve performance of this rule.\n");
+ }
+ if (warn_client_ports /*rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
+ && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY)))
+ if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
+ || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule contains ports or port variables only on the client side.\n"
+ " -Flow direction possibly inconsistent with rule.\n");
+ }
+ if (warn_direction /*rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule is bidirectional and has a flow option with a specific direction.\n");
+ }
+ if (warn_method_toclient /*http_method_buf && rule_flow && rule_flow_toclient*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule uses content or pcre for http_method with flow:to_client or from_server\n");
+ }
+ if (warn_method_serverbody /*http_method_buf && http_server_body_buf*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule uses content or pcre for http_method with content or pcre for http_server_body.\n");
+ }
+ if (warn_pcre_method /*http_method_buf && rule_content == 0 && rule_content_http == 0
+ && (rule_pcre > 0 || rule_pcre_http > 0)*/) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule uses pcre with only a http_method content; possible performance issue.\n");
+ }
+ if (warn_offset_depth_pkt_stream) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule has depth"
+ "/offset with raw content keywords. Please note the "
+ "offset/depth will be checked against both packet "
+ "payloads and stream. If you meant to have the offset/"
+ "depth checked against just the payload, you can update "
+ "the signature as \"alert tcp-pkt...\"\n");
+ }
+ if (warn_offset_depth_alproto) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule has "
+ "offset/depth set along with a match on a specific "
+ "app layer protocol - %d. This can lead to FNs if we "
+ "have a offset/depth content match on a packet payload "
+ "before we can detect the app layer protocol for the "
+ "flow.\n", s->alproto);
+ }
+ if (warn_non_alproto_fp_for_alproto_sig) {
+ fprintf(rule_engine_analysis_FD, " Warning: Rule app layer "
+ "protocol is http, but the fast_pattern is set on the raw "
+ "stream. Consider adding fast_pattern over a http "
+ "buffer for increased performance.");
+ }
+ if (rule_warning == 0) {
+ fprintf(rule_engine_analysis_FD, " No warnings for this rule.\n");
+ }
+ fprintf(rule_engine_analysis_FD, "\n");
+ }
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-analyzer.h b/framework/src/suricata/src/detect-engine-analyzer.h
new file mode 100644
index 00000000..d92b2060
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-analyzer.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_ANALYZER_H__
+#define __DETECT_ENGINE_ANALYZER_H__
+
+#include <stdint.h>
+
+int SetupFPAnalyzer(void);
+void CleanupFPAnalyzer(void);
+
+int SetupRuleAnalyzer(void);
+void CleanupRuleAnalyzer (void);
+
+int PerCentEncodingSetup ();
+int PerCentEncodingMatch (uint8_t *content, uint8_t content_len);
+
+void EngineAnalysisFP(Signature *s, char *line);
+void EngineAnalysisRules(Signature *s, char *line);
+void EngineAnalysisRulesFailure(char *line, char *file, int lineno);
+
+#endif /* __DETECT_ENGINE_ANALYZER_H__ */
diff --git a/framework/src/suricata/src/detect-engine-apt-event.c b/framework/src/suricata/src/detect-engine-apt-event.c
new file mode 100644
index 00000000..5ca41689
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-apt-event.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "app-layer-parser.h"
+#include "detect-app-layer-event.h"
+#include "detect-engine-state.h"
+#include "stream.h"
+#include "detect-engine-apt-event.h"
+#include "util-profiling.h"
+#include "util-unittest.h"
+
+int DetectEngineAptEventInspect(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ AppLayerDecoderEvents *decoder_events = NULL;
+ int r = 0;
+ AppProto alproto;
+ SigMatch *sm;
+ DetectAppLayerEventData *aled = NULL;
+
+ alproto = f->alproto;
+ decoder_events = AppLayerParserGetEventsByTx(f->proto, alproto, alstate, tx_id);
+ if (decoder_events == NULL)
+ goto end;
+
+ for (sm = s->sm_lists[DETECT_SM_LIST_APP_EVENT]; sm != NULL; sm = sm->next) {
+ aled = (DetectAppLayerEventData *)sm->ctx;
+ KEYWORD_PROFILING_START;
+ if (AppLayerDecoderEventsIsEventSet(decoder_events, aled->event_id)) {
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+ continue;
+ }
+
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 0);
+ goto end;
+ }
+
+ r = 1;
+
+ end:
+ if (r == 1) {
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ } else {
+ if (AppLayerParserGetStateProgress(f->proto, alproto, tx, flags) ==
+ AppLayerParserGetStateProgressCompletionStatus(f->proto, alproto, flags))
+ {
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ } else {
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+ }
+}
+
diff --git a/framework/src/suricata/src/detect-engine-apt-event.h b/framework/src/suricata/src/detect-engine-apt-event.h
new file mode 100644
index 00000000..c264efe9
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-apt-event.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_APT_EVENT__H__
+#define __DETECT_ENGINE_APT_EVENT__H__
+
+int DetectEngineAptEventInspect(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineAptEventRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_APT_EVENT__H__ */
diff --git a/framework/src/suricata/src/detect-engine-content-inspection.c b/framework/src/suricata/src/detect-engine-content-inspection.c
new file mode 100644
index 00000000..a434ca5a
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-content-inspection.c
@@ -0,0 +1,576 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Performs content inspection on any buffer supplied.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-isdataat.h"
+#include "detect-bytetest.h"
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+#include "detect-replace.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-uricontent.h"
+#include "detect-urilen.h"
+#include "detect-lua.h"
+
+#include "app-layer-dcerpc.h"
+
+#include "util-spm.h"
+#include "util-spm-bm.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-profiling.h"
+
+#ifdef HAVE_LUA
+#include "util-lua.h"
+#endif
+
+/**
+ * \brief Run the actual payload match functions
+ *
+ * The following keywords are inspected:
+ * - content, including all the http and dce modified contents
+ * - isdaatat
+ * - pcre
+ * - bytejump
+ * - bytetest
+ * - byte_extract
+ * - urilen
+ * -
+ *
+ * All keywords are evaluated against the buffer with buffer_len.
+ *
+ * For accounting the last match in relative matching the
+ * det_ctx->buffer_offset int is used.
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param sm SigMatch to inspect
+ * \param f Flow (for pcre flowvar storage)
+ * \param buffer Ptr to the buffer to inspect
+ * \param buffer_len Length of the payload
+ * \param stream_start_offset Indicates the start of the current buffer in
+ * the whole buffer stream inspected. This
+ * applies if the current buffer is inspected
+ * in chunks.
+ * \param inspection_mode Refers to the engine inspection mode we are currently
+ * inspecting. Can be payload, stream, one of the http
+ * buffer inspection modes or dce inspection mode.
+ * \param data Used to send some custom data. For example in
+ * payload inspection mode, data contains packet ptr,
+ * and under dce inspection mode, contains dce state.
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, SigMatch *sm,
+ Flow *f,
+ uint8_t *buffer, uint32_t buffer_len,
+ uint32_t stream_start_offset,
+ uint8_t inspection_mode, void *data)
+{
+ SCEnter();
+ KEYWORD_PROFILING_START;
+
+ det_ctx->inspection_recursion_counter++;
+
+ if (det_ctx->inspection_recursion_counter == de_ctx->inspection_recursion_limit) {
+ det_ctx->discontinue_matching = 1;
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 0);
+ SCReturnInt(0);
+ }
+
+ if (sm == NULL || buffer_len == 0) {
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 0);
+ SCReturnInt(0);
+ }
+
+ /* \todo unify this which is phase 2 of payload inspection unification */
+ if (sm->type == DETECT_CONTENT) {
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ SCLogDebug("inspecting content %"PRIu32" buffer_len %"PRIu32, cd->id, buffer_len);
+
+ /* we might have already have this content matched by the mpm.
+ * (if there is any other reason why we'd want to avoid checking
+ * it here, please fill it in) */
+ //if (cd->flags & DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED) {
+ // goto match;
+ //}
+
+ /* rule parsers should take care of this */
+#ifdef DEBUG
+ BUG_ON(cd->depth != 0 && cd->depth <= cd->offset);
+#endif
+
+ /* search for our pattern, checking the matches recursively.
+ * if we match we look for the next SigMatch as well */
+ uint8_t *found = NULL;
+ uint32_t offset = 0;
+ uint32_t depth = buffer_len;
+ uint32_t prev_offset = 0; /**< used in recursive searching */
+ uint32_t prev_buffer_offset = det_ctx->buffer_offset;
+
+ do {
+ if ((cd->flags & DETECT_CONTENT_DISTANCE) ||
+ (cd->flags & DETECT_CONTENT_WITHIN)) {
+ SCLogDebug("det_ctx->buffer_offset %"PRIu32, det_ctx->buffer_offset);
+
+ offset = prev_buffer_offset;
+ depth = buffer_len;
+
+ int distance = cd->distance;
+ if (cd->flags & DETECT_CONTENT_DISTANCE) {
+ if (cd->flags & DETECT_CONTENT_DISTANCE_BE) {
+ distance = det_ctx->bj_values[cd->distance];
+ }
+ if (distance < 0 && (uint32_t)(abs(distance)) > offset)
+ offset = 0;
+ else
+ offset += distance;
+
+ SCLogDebug("cd->distance %"PRIi32", offset %"PRIu32", depth %"PRIu32,
+ distance, offset, depth);
+ }
+
+ if (cd->flags & DETECT_CONTENT_WITHIN) {
+ if (cd->flags & DETECT_CONTENT_WITHIN_BE) {
+ if ((int32_t)depth > (int32_t)(prev_buffer_offset + det_ctx->bj_values[cd->within] + distance)) {
+ depth = prev_buffer_offset + det_ctx->bj_values[cd->within] + distance;
+ }
+ } else {
+ if ((int32_t)depth > (int32_t)(prev_buffer_offset + cd->within + distance)) {
+ depth = prev_buffer_offset + cd->within + distance;
+ }
+
+ SCLogDebug("cd->within %"PRIi32", det_ctx->buffer_offset %"PRIu32", depth %"PRIu32,
+ cd->within, prev_buffer_offset, depth);
+ }
+
+ if (stream_start_offset != 0 && prev_buffer_offset == 0) {
+ if (depth <= stream_start_offset) {
+ goto no_match;
+ } else if (depth >= (stream_start_offset + buffer_len)) {
+ ;
+ } else {
+ depth = depth - stream_start_offset;
+ }
+ }
+ }
+
+ if (cd->flags & DETECT_CONTENT_DEPTH_BE) {
+ if ((det_ctx->bj_values[cd->depth] + prev_buffer_offset) < depth) {
+ depth = prev_buffer_offset + det_ctx->bj_values[cd->depth];
+ }
+ } else {
+ if (cd->depth != 0) {
+ if ((cd->depth + prev_buffer_offset) < depth) {
+ depth = prev_buffer_offset + cd->depth;
+ }
+
+ SCLogDebug("cd->depth %"PRIu32", depth %"PRIu32, cd->depth, depth);
+ }
+ }
+
+ if (cd->flags & DETECT_CONTENT_OFFSET_BE) {
+ if (det_ctx->bj_values[cd->offset] > offset)
+ offset = det_ctx->bj_values[cd->offset];
+ } else {
+ if (cd->offset > offset) {
+ offset = cd->offset;
+ SCLogDebug("setting offset %"PRIu32, offset);
+ }
+ }
+ } else { /* implied no relative matches */
+ /* set depth */
+ if (cd->flags & DETECT_CONTENT_DEPTH_BE) {
+ depth = det_ctx->bj_values[cd->depth];
+ } else {
+ if (cd->depth != 0) {
+ depth = cd->depth;
+ }
+ }
+
+ if (stream_start_offset != 0 && cd->flags & DETECT_CONTENT_DEPTH) {
+ if (depth <= stream_start_offset) {
+ goto no_match;
+ } else if (depth >= (stream_start_offset + buffer_len)) {
+ ;
+ } else {
+ depth = depth - stream_start_offset;
+ }
+ }
+
+ /* set offset */
+ if (cd->flags & DETECT_CONTENT_OFFSET_BE)
+ offset = det_ctx->bj_values[cd->offset];
+ else
+ offset = cd->offset;
+ prev_buffer_offset = 0;
+ }
+
+ /* update offset with prev_offset if we're searching for
+ * matches after the first occurence. */
+ SCLogDebug("offset %"PRIu32", prev_offset %"PRIu32, offset, prev_offset);
+ if (prev_offset != 0)
+ offset = prev_offset;
+
+ SCLogDebug("offset %"PRIu32", depth %"PRIu32, offset, depth);
+
+ if (depth > buffer_len)
+ depth = buffer_len;
+
+ /* if offset is bigger than depth we can never match on a pattern.
+ * We can however, "match" on a negated pattern. */
+ if (offset > depth || depth == 0) {
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ goto match;
+ } else {
+ goto no_match;
+ }
+ }
+
+ uint8_t *sbuffer = buffer + offset;
+ uint32_t sbuffer_len = depth - offset;
+ uint32_t match_offset = 0;
+ SCLogDebug("sbuffer_len %"PRIu32, sbuffer_len);
+#ifdef DEBUG
+ BUG_ON(sbuffer_len > buffer_len);
+#endif
+
+ /* \todo Add another optimization here. If cd->content_len is
+ * greater than sbuffer_len found is anyways NULL */
+
+ /* do the actual search */
+ if (cd->flags & DETECT_CONTENT_NOCASE)
+ found = BoyerMooreNocase(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx);
+ else
+ found = BoyerMoore(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx);
+
+ /* next we evaluate the result in combination with the
+ * negation flag. */
+ SCLogDebug("found %p cd negated %s", found, cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false");
+
+ if (found == NULL && !(cd->flags & DETECT_CONTENT_NEGATED)) {
+ goto no_match;
+ } else if (found == NULL && (cd->flags & DETECT_CONTENT_NEGATED)) {
+ goto match;
+ } else if (found != NULL && (cd->flags & DETECT_CONTENT_NEGATED)) {
+ SCLogDebug("content %"PRIu32" matched at offset %"PRIu32", but negated so no match", cd->id, match_offset);
+ /* don't bother carrying recursive matches now, for preceding
+ * relative keywords */
+ if (DETECT_CONTENT_IS_SINGLE(cd))
+ det_ctx->discontinue_matching = 1;
+ goto no_match;
+ } else {
+ match_offset = (uint32_t)((found - buffer) + cd->content_len);
+ SCLogDebug("content %"PRIu32" matched at offset %"PRIu32"", cd->id, match_offset);
+ det_ctx->buffer_offset = match_offset;
+
+ /* Match branch, add replace to the list if needed */
+ if (cd->flags & DETECT_CONTENT_REPLACE) {
+ if (inspection_mode == DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD) {
+ /* we will need to replace content if match is confirmed */
+ det_ctx->replist = DetectReplaceAddToList(det_ctx->replist, found, cd);
+ } else {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "Can't modify payload without packet");
+ }
+ }
+ if (!(cd->flags & DETECT_CONTENT_RELATIVE_NEXT)) {
+ SCLogDebug("no relative match coming up, so this is a match");
+ goto match;
+ }
+
+ /* bail out if we have no next match. Technically this is an
+ * error, as the current cd has the DETECT_CONTENT_RELATIVE_NEXT
+ * flag set. */
+ if (sm->next == NULL) {
+ goto no_match;
+ }
+
+ SCLogDebug("content %"PRIu32, cd->id);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+
+ /* see if the next buffer keywords match. If not, we will
+ * search for another occurence of this content and see
+ * if the others match then until we run out of matches */
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data);
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+
+ if (det_ctx->discontinue_matching)
+ goto no_match;
+
+ /* set the previous match offset to the start of this match + 1 */
+ prev_offset = (match_offset - (cd->content_len - 1));
+ SCLogDebug("trying to see if there is another match after prev_offset %"PRIu32, prev_offset);
+ }
+
+ } while(1);
+
+ } else if (sm->type == DETECT_ISDATAAT) {
+ SCLogDebug("inspecting isdataat");
+
+ DetectIsdataatData *id = (DetectIsdataatData *)sm->ctx;
+ if (id->flags & ISDATAAT_RELATIVE) {
+ if (det_ctx->buffer_offset + id->dataat > buffer_len) {
+ SCLogDebug("det_ctx->buffer_offset + id->dataat %"PRIu32" > %"PRIu32, det_ctx->buffer_offset + id->dataat, buffer_len);
+ if (id->flags & ISDATAAT_NEGATED)
+ goto match;
+ goto no_match;
+ } else {
+ SCLogDebug("relative isdataat match");
+ if (id->flags & ISDATAAT_NEGATED)
+ goto no_match;
+ goto match;
+ }
+ } else {
+ if (id->dataat < buffer_len) {
+ SCLogDebug("absolute isdataat match");
+ if (id->flags & ISDATAAT_NEGATED)
+ goto no_match;
+ goto match;
+ } else {
+ SCLogDebug("absolute isdataat mismatch, id->isdataat %"PRIu32", buffer_len %"PRIu32"", id->dataat, buffer_len);
+ if (id->flags & ISDATAAT_NEGATED)
+ goto match;
+ goto no_match;
+ }
+ }
+
+ } else if (sm->type == DETECT_PCRE) {
+ SCLogDebug("inspecting pcre");
+ DetectPcreData *pe = (DetectPcreData *)sm->ctx;
+ uint32_t prev_buffer_offset = det_ctx->buffer_offset;
+ uint32_t prev_offset = 0;
+ int r = 0;
+
+ det_ctx->pcre_match_start_offset = 0;
+ do {
+ Packet *p = NULL;
+ if (inspection_mode == DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD)
+ p = (Packet *)data;
+ r = DetectPcrePayloadMatch(det_ctx, s, sm, p, f,
+ buffer, buffer_len);
+ if (r == 0) {
+ goto no_match;
+ }
+
+ if (!(pe->flags & DETECT_PCRE_RELATIVE_NEXT)) {
+ SCLogDebug("no relative match coming up, so this is a match");
+ goto match;
+ }
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+
+ /* save it, in case we need to do a pcre match once again */
+ prev_offset = det_ctx->pcre_match_start_offset;
+
+ /* see if the next payload keywords match. If not, we will
+ * search for another occurence of this pcre and see
+ * if the others match, until we run out of matches */
+ r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next,
+ f, buffer, buffer_len, stream_start_offset, inspection_mode, data);
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+
+ if (det_ctx->discontinue_matching)
+ goto no_match;
+
+ det_ctx->buffer_offset = prev_buffer_offset;
+ det_ctx->pcre_match_start_offset = prev_offset;
+ } while (1);
+
+ } else if (sm->type == DETECT_BYTETEST) {
+ DetectBytetestData *btd = (DetectBytetestData *)sm->ctx;
+ uint8_t flags = btd->flags;
+ int32_t offset = btd->offset;
+ uint64_t value = btd->value;
+ if (flags & DETECT_BYTETEST_OFFSET_BE) {
+ offset = det_ctx->bj_values[offset];
+ }
+ if (flags & DETECT_BYTETEST_VALUE_BE) {
+ value = det_ctx->bj_values[value];
+ }
+
+ /* if we have dce enabled we will have to use the endianness
+ * specified by the dce header */
+ if (flags & DETECT_BYTETEST_DCE) {
+ DCERPCState *dcerpc_state = (DCERPCState *)data;
+ /* enable the endianness flag temporarily. once we are done
+ * processing we reset the flags to the original value*/
+ flags |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] & 0x10) ?
+ DETECT_BYTETEST_LITTLE: 0);
+ }
+
+ if (DetectBytetestDoMatch(det_ctx, s, sm->ctx, buffer, buffer_len, flags,
+ offset, value) != 1) {
+ goto no_match;
+ }
+
+ goto match;
+
+ } else if (sm->type == DETECT_BYTEJUMP) {
+ DetectBytejumpData *bjd = (DetectBytejumpData *)sm->ctx;
+ uint8_t flags = bjd->flags;
+ int32_t offset = bjd->offset;
+
+ if (flags & DETECT_BYTEJUMP_OFFSET_BE) {
+ offset = det_ctx->bj_values[offset];
+ }
+
+ /* if we have dce enabled we will have to use the endianness
+ * specified by the dce header */
+ if (flags & DETECT_BYTEJUMP_DCE) {
+ DCERPCState *dcerpc_state = (DCERPCState *)data;
+ /* enable the endianness flag temporarily. once we are done
+ * processing we reset the flags to the original value*/
+ flags |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] & 0x10) ?
+ DETECT_BYTEJUMP_LITTLE: 0);
+ }
+
+ if (DetectBytejumpDoMatch(det_ctx, s, sm->ctx, buffer, buffer_len,
+ flags, offset) != 1) {
+ goto no_match;
+ }
+
+ goto match;
+
+ } else if (sm->type == DETECT_BYTE_EXTRACT) {
+
+ DetectByteExtractData *bed = (DetectByteExtractData *)sm->ctx;
+ uint8_t endian = bed->endian;
+
+ /* if we have dce enabled we will have to use the endianness
+ * specified by the dce header */
+ if ((bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) &&
+ endian == DETECT_BYTE_EXTRACT_ENDIAN_DCE) {
+
+ DCERPCState *dcerpc_state = (DCERPCState *)data;
+ /* enable the endianness flag temporarily. once we are done
+ * processing we reset the flags to the original value*/
+ endian |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] == 0x10) ?
+ DETECT_BYTE_EXTRACT_ENDIAN_LITTLE : DETECT_BYTE_EXTRACT_ENDIAN_BIG);
+ }
+
+ if (DetectByteExtractDoMatch(det_ctx, sm, s, buffer,
+ buffer_len,
+ &det_ctx->bj_values[bed->local_id],
+ endian) != 1) {
+ goto no_match;
+ }
+
+ goto match;
+
+ /* we should never get here, but bail out just in case */
+ } else if (sm->type == DETECT_AL_URILEN) {
+ SCLogDebug("inspecting uri len");
+
+ int r = 0;
+ DetectUrilenData *urilend = (DetectUrilenData *) sm->ctx;
+
+ switch (urilend->mode) {
+ case DETECT_URILEN_EQ:
+ if (buffer_len == urilend->urilen1)
+ r = 1;
+ break;
+ case DETECT_URILEN_LT:
+ if (buffer_len < urilend->urilen1)
+ r = 1;
+ break;
+ case DETECT_URILEN_GT:
+ if (buffer_len > urilend->urilen1)
+ r = 1;
+ break;
+ case DETECT_URILEN_RA:
+ if (buffer_len > urilend->urilen1 &&
+ buffer_len < urilend->urilen2) {
+ r = 1;
+ }
+ break;
+ }
+
+ if (r == 1) {
+ goto match;
+ }
+
+ det_ctx->discontinue_matching = 0;
+
+ goto no_match;
+#ifdef HAVE_LUA
+ }
+ else if (sm->type == DETECT_LUA) {
+ SCLogDebug("lua starting");
+ /* for flowvar gets and sets we need to know the flow's lock status */
+ int flow_lock = LUA_FLOW_LOCKED_BY_PARENT;
+ if (inspection_mode <= DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM)
+ flow_lock = LUA_FLOW_NOT_LOCKED_BY_PARENT;
+
+ if (DetectLuaMatchBuffer(det_ctx, s, sm, buffer, buffer_len,
+ det_ctx->buffer_offset, f, flow_lock) != 1)
+ {
+ SCLogDebug("lua no_match");
+ goto no_match;
+ }
+ SCLogDebug("lua match");
+ goto match;
+#endif /* HAVE_LUA */
+ } else {
+ SCLogDebug("sm->type %u", sm->type);
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+
+no_match:
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 0);
+ SCReturnInt(0);
+
+match:
+ /* this sigmatch matched, inspect the next one. If it was the last,
+ * the buffer portion of the signature matched. */
+ if (sm->next != NULL) {
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data);
+ SCReturnInt(r);
+ } else {
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+ SCReturnInt(1);
+ }
+}
diff --git a/framework/src/suricata/src/detect-engine-content-inspection.h b/framework/src/suricata/src/detect-engine-content-inspection.h
new file mode 100644
index 00000000..6d3accaa
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-content-inspection.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_CONTENT_INSPECTION_H__
+#define __DETECT_ENGINE_CONTENT_INSPECTION_H__
+
+/** \warning make sure to add new entries to the proper position
+ * wrt flow lock status
+ */
+enum {
+ /* called with flow unlocked */
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD = 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM,
+
+ /* called with flow locked */
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_DCE,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_URI,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRL,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRUD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HHD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCBD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSBD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HMD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSCD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSMD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HUAD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HHHD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHHD,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_DNSQUERY,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_FD_SMTP,
+};
+
+int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, SigMatch *sm,
+ Flow *f,
+ uint8_t *buffer, uint32_t buffer_len,
+ uint32_t stream_start_offset,
+ uint8_t inspection_mode, void *data);
+
+#endif /* __DETECT_ENGINE_CONTENT_INSPECTION_H__ */
diff --git a/framework/src/suricata/src/detect-engine-dcepayload.c b/framework/src/suricata/src/detect-engine-dcepayload.c
new file mode 100644
index 00000000..c2f7610e
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-dcepayload.c
@@ -0,0 +1,10192 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-pcre.h"
+#include "detect-isdataat.h"
+#include "detect-bytetest.h"
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+#include "detect-content.h"
+#include "detect-engine-content-inspection.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer.h"
+#include "app-layer-dcerpc.h"
+#include "flow-util.h"
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/**
+ * \brief Do the content inspection & validation for a signature against dce stub.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param sm SigMatch to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectDcePayload(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Signature *s,
+ Flow *f, uint8_t flags, void *alstate)
+{
+ SCEnter();
+ DCERPCState *dcerpc_state = (DCERPCState *)alstate;
+ uint8_t *dce_stub_data = NULL;
+ uint16_t dce_stub_data_len;
+ int r = 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL || dcerpc_state == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcrequest.stub_data_fresh != 0) {
+ /* the request stub and stub_len */
+ dce_stub_data = dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer;
+ dce_stub_data_len = dcerpc_state->dcerpc.dcerpcrequest.stub_data_buffer_len;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+ r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_DMATCH],
+ f,
+ dce_stub_data,
+ dce_stub_data_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_DCE, dcerpc_state);
+ //r = DoInspectDcePayload(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_DMATCH], f,
+ //dce_stub_data, dce_stub_data_len, dcerpc_state);
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+ }
+
+ if (dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer != NULL &&
+ dcerpc_state->dcerpc.dcerpcresponse.stub_data_fresh == 0) {
+ /* the response stub and stub_len */
+ dce_stub_data = dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer;
+ dce_stub_data_len = dcerpc_state->dcerpc.dcerpcresponse.stub_data_buffer_len;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+ r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_DMATCH],
+ f,
+ dce_stub_data,
+ dce_stub_data_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_DCE, dcerpc_state);
+ //r = DoInspectDcePayload(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_DMATCH], f,
+ //dce_stub_data, dce_stub_data_len, dcerpc_state);
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**************************************Unittests*******************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest01(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t request3[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x26, 0x65, 0x3c, 0x6e, 0x6d, 0x64, 0x24, 0x39,
+ 0x56, 0x43, 0x3e, 0x61, 0x5c, 0x54, 0x42, 0x23,
+ 0x75, 0x6b, 0x71, 0x27, 0x66, 0x2e, 0x6e, 0x3d,
+ 0x58, 0x23, 0x54, 0x77, 0x3b, 0x52, 0x6b, 0x50,
+ 0x3b, 0x74, 0x2c, 0x54, 0x25, 0x5c, 0x51, 0x7c,
+ 0x29, 0x7c, 0x5f, 0x4a, 0x35, 0x5c, 0x3d, 0x3f,
+ 0x33, 0x55, 0x3b, 0x5a, 0x57, 0x31, 0x59, 0x4f,
+ 0x6d, 0x6d, 0x7b, 0x3e, 0x38, 0x4d, 0x68, 0x75,
+ 0x64, 0x21, 0x50, 0x63, 0x47, 0x42, 0x56, 0x39,
+ 0x6c, 0x6f, 0x61, 0x53, 0x32, 0x56, 0x43, 0x52,
+ 0x43, 0x67, 0x26, 0x45, 0x28, 0x6b, 0x77, 0x28,
+ 0x7c, 0x64, 0x61, 0x24, 0x38, 0x6b, 0x59, 0x2a,
+ 0x4f, 0x6e, 0x5b, 0x57, 0x24, 0x54, 0x33, 0x37,
+ 0x47, 0x58, 0x4b, 0x58, 0x3d, 0x21, 0x38, 0x7c,
+ 0x2c, 0x24, 0x5f, 0x67, 0x3a, 0x41, 0x3e, 0x2a,
+ 0x72, 0x66, 0x2d, 0x6b, 0x66, 0x7b, 0x2b, 0x75,
+ 0x78, 0x2f, 0x4d, 0x4c, 0x51, 0x70, 0x5d, 0x55,
+ 0x54, 0x3c, 0x63, 0x46, 0x6b, 0x64, 0x4d, 0x25,
+ 0x45, 0x21, 0x34, 0x65, 0x48, 0x32, 0x58, 0x4c,
+ 0x70, 0x4c, 0x4c, 0x75, 0x5c, 0x77, 0x68, 0x78,
+ 0x34, 0x5c, 0x2d, 0x39, 0x58, 0x3b, 0x40, 0x71,
+ 0x77, 0x47, 0x32, 0x2e, 0x3c, 0x61, 0x6f, 0x6d,
+ 0x5f, 0x43, 0x74, 0x36, 0x4f, 0x21, 0x44, 0x66,
+ 0x36, 0x62, 0x30, 0x29, 0x5a, 0x34, 0x66, 0x4e,
+ 0x51, 0x23, 0x4e, 0x38, 0x51, 0x78, 0x74, 0x58,
+ 0x2e, 0x6d, 0x51, 0x49, 0x55, 0x73, 0x2a, 0x71,
+ 0x3c, 0x74, 0x38, 0x6f, 0x5d, 0x4b, 0x74, 0x68,
+ 0x65, 0x4a, 0x58, 0x41, 0x55, 0x29, 0x42, 0x69,
+ 0x55, 0x3b, 0x2b, 0x47, 0x64, 0x3b, 0x77, 0x72,
+ 0x74, 0x38, 0x53, 0x5c, 0x69, 0x49, 0x49, 0x5b,
+ 0x31, 0x41, 0x6a, 0x4e, 0x2c, 0x6a, 0x63, 0x3f,
+ 0x58, 0x4e, 0x25, 0x3e, 0x57, 0x41, 0x61, 0x26,
+ 0x5e, 0x24, 0x69, 0x7a, 0x38, 0x60, 0x73, 0x70,
+ 0x7d, 0x63, 0x34, 0x78, 0x4d, 0x50, 0x35, 0x69,
+ 0x49, 0x22, 0x45, 0x44, 0x3f, 0x6e, 0x75, 0x64,
+ 0x57, 0x3a, 0x61, 0x60, 0x34, 0x21, 0x61, 0x21,
+ 0x2a, 0x78, 0x7b, 0x52, 0x43, 0x50, 0x5b, 0x76,
+ 0x5f, 0x4b, 0x6a, 0x5d, 0x23, 0x5b, 0x57, 0x40,
+ 0x53, 0x51, 0x33, 0x21, 0x35, 0x7d, 0x31, 0x46,
+ 0x65, 0x52, 0x28, 0x25, 0x30, 0x5a, 0x37, 0x7c,
+ 0x2c, 0x3d, 0x2a, 0x48, 0x24, 0x5a, 0x2f, 0x47,
+ 0x64, 0x73, 0x64, 0x3d, 0x7a, 0x5b, 0x34, 0x5e,
+ 0x42, 0x22, 0x32, 0x47, 0x6e, 0x58, 0x3b, 0x3e,
+ 0x25, 0x2f, 0x58, 0x78, 0x42, 0x66, 0x71, 0x56,
+ 0x2a, 0x66, 0x66, 0x5b, 0x55, 0x35, 0x7a, 0x41,
+ 0x7c, 0x7c, 0x6a, 0x2d, 0x59, 0x25, 0x22, 0x34,
+ 0x5a, 0x61, 0x37, 0x48, 0x39, 0x31, 0x4a, 0x55,
+ 0x6a, 0x68, 0x40, 0x2f, 0x45, 0x69, 0x46, 0x25,
+ 0x51, 0x7d, 0x4f, 0x71, 0x21, 0x33, 0x55, 0x50,
+ 0x56, 0x5f, 0x75, 0x27, 0x64, 0x36, 0x7a, 0x39,
+ 0x40, 0x6a, 0x77, 0x38, 0x5d, 0x39, 0x30, 0x5e,
+ 0x74, 0x54, 0x24, 0x3f, 0x3d, 0x79, 0x3b, 0x27,
+ 0x7d, 0x68, 0x7d, 0x40, 0x71, 0x7a, 0x65, 0x54,
+ 0x50, 0x66, 0x33, 0x3c, 0x42, 0x69, 0x6e, 0x3c,
+ 0x63, 0x63, 0x69, 0x7a, 0x5e, 0x7b, 0x76, 0x26,
+ 0x71, 0x6f, 0x4a, 0x6d, 0x70, 0x73, 0x66, 0x3b,
+ 0x26, 0x70, 0x43, 0x5b, 0x52, 0x4c, 0x6d, 0x51,
+ 0x2a, 0x66, 0x6c, 0x3e, 0x68, 0x6a, 0x31, 0x41,
+ 0x79, 0x72, 0x37, 0x47, 0x7d, 0x2b, 0x3c, 0x40,
+ 0x6b, 0x75, 0x56, 0x70, 0x7b, 0x2d, 0x5f, 0x33,
+ 0x30, 0x30, 0x21, 0x35, 0x7a, 0x7a, 0x67, 0x48,
+ 0x5e, 0x3b, 0x73, 0x50, 0x54, 0x47, 0x23, 0x2b,
+ 0x4c, 0x4e, 0x2f, 0x24, 0x44, 0x34, 0x23, 0x5d,
+ 0x76, 0x51, 0x5a, 0x73, 0x72, 0x3e, 0x47, 0x77,
+ 0x40, 0x28, 0x65, 0x2e, 0x2a, 0x75, 0x3c, 0x2a,
+ 0x27, 0x4a, 0x3f, 0x3c, 0x66, 0x2d, 0x21, 0x79,
+ 0x2d, 0x2b, 0x78, 0x7c, 0x5a, 0x73, 0x46, 0x6b,
+ 0x39, 0x65, 0x5e, 0x3d, 0x38, 0x40, 0x32, 0x3e,
+ 0x21, 0x62, 0x34, 0x41, 0x58, 0x53, 0x67, 0x34,
+ 0x58, 0x56, 0x61, 0x5b, 0x3e, 0x4e, 0x2c, 0x5b,
+ 0x73, 0x35, 0x34, 0x35, 0x21, 0x3a, 0x61, 0x5f,
+ 0x6e, 0x45, 0x78, 0x44, 0x28, 0x23, 0x48, 0x65,
+ 0x53, 0x47, 0x6e, 0x2c, 0x38, 0x5e, 0x2c, 0x57,
+ 0x58, 0x30, 0x7a, 0x3b, 0x4b, 0x4a, 0x74, 0x7d,
+ 0x3e, 0x4d, 0x30, 0x24, 0x76, 0x66, 0x6d, 0x2e,
+ 0x74, 0x75, 0x28, 0x48, 0x5c, 0x23, 0x6c, 0x46,
+ 0x27, 0x46, 0x6e, 0x34, 0x63, 0x21, 0x58, 0x54,
+ 0x50, 0x2f, 0x40, 0x47, 0x40, 0x32, 0x36, 0x48,
+ 0x5f, 0x7d, 0x4a, 0x41, 0x6e, 0x60, 0x2c, 0x4a,
+ 0x6a, 0x67, 0x6c, 0x41, 0x27, 0x23, 0x30, 0x48,
+ 0x6a, 0x49, 0x73, 0x26, 0x77, 0x75, 0x4d, 0x65,
+ 0x5b, 0x34, 0x79, 0x67, 0x61, 0x5b, 0x5c, 0x2b,
+ 0x71, 0x3f, 0x62, 0x51, 0x3a, 0x53, 0x42, 0x26,
+ 0x6f, 0x36, 0x57, 0x3f, 0x2b, 0x34, 0x24, 0x30,
+ 0x60, 0x55, 0x70, 0x65, 0x70, 0x57, 0x5d, 0x68,
+ 0x36, 0x52, 0x5d, 0x3f, 0x6a, 0x3a, 0x33, 0x31,
+ 0x6c, 0x4e, 0x57, 0x79, 0x49, 0x79, 0x69, 0x71,
+ 0x6f, 0x70, 0x6a, 0x76, 0x4b, 0x2f, 0x33, 0x51,
+ 0x68, 0x30, 0x2e, 0x77, 0x78, 0x55, 0x2f, 0x53,
+ 0x52, 0x5e, 0x57, 0x60, 0x3b, 0x6f, 0x69, 0x61,
+ 0x6c, 0x60, 0x5a, 0x34, 0x5a, 0x35, 0x4b, 0x28,
+ 0x54, 0x32, 0x6a, 0x35, 0x36, 0x6d, 0x68, 0x47,
+ 0x5c, 0x74, 0x2e, 0x5f, 0x6c, 0x6d, 0x55, 0x42,
+ 0x77, 0x64, 0x7d, 0x53, 0x4d, 0x39, 0x2c, 0x41,
+ 0x42, 0x23, 0x3a, 0x73, 0x40, 0x60, 0x5d, 0x38,
+ 0x6d, 0x36, 0x56, 0x57, 0x2a, 0x28, 0x3d, 0x3b,
+ 0x5c, 0x75, 0x35, 0x2d, 0x69, 0x2d, 0x44, 0x51,
+ 0x27, 0x63, 0x66, 0x33, 0x46, 0x42, 0x2e, 0x36,
+ 0x6b, 0x7b, 0x2c, 0x23, 0x3b, 0x5a, 0x50, 0x2a,
+ 0x65, 0x28, 0x3b, 0x3c, 0x51, 0x3f, 0x4d, 0x63,
+ 0x38, 0x25, 0x74, 0x2e, 0x51, 0x22, 0x31, 0x74,
+ 0x35, 0x33, 0x23, 0x2d, 0x3f, 0x77, 0x26, 0x2c,
+ 0x55, 0x6d, 0x27, 0x39, 0x79, 0x76, 0x63, 0x4b,
+ 0x43, 0x4a, 0x3a, 0x6b, 0x59, 0x55, 0x65, 0x26,
+ 0x2f, 0x3f, 0x56, 0x67, 0x5a, 0x77, 0x71, 0x22,
+ 0x51, 0x2b, 0x6d, 0x4c, 0x2c, 0x57, 0x66, 0x76,
+ 0x37, 0x70, 0x5f, 0x52, 0x29, 0x44, 0x52, 0x22,
+ 0x57, 0x37, 0x27, 0x79, 0x29, 0x5c, 0x57, 0x3b,
+ 0x54, 0x3c, 0x3f, 0x53, 0x35, 0x27, 0x5e, 0x7c,
+ 0x49, 0x77, 0x57, 0x5a, 0x22, 0x76, 0x7c, 0x5b,
+ 0x2f, 0x53, 0x5e, 0x55, 0x6d, 0x64, 0x67, 0x34,
+ 0x41, 0x23, 0x76, 0x67, 0x23, 0x78, 0x6a, 0x63,
+ 0x27, 0x68, 0x43, 0x7d, 0x58, 0x49, 0x2d, 0x79,
+ 0x2e, 0x75, 0x60, 0x6b, 0x34, 0x48, 0x6f, 0x4a,
+ 0x6c, 0x48, 0x40, 0x68, 0x5f, 0x35, 0x25, 0x6c,
+ 0x38, 0x5c, 0x30, 0x32, 0x4c, 0x36, 0x31, 0x29,
+ 0x74, 0x4a, 0x55, 0x56, 0x6d, 0x4e, 0x23, 0x54,
+ 0x2e, 0x69, 0x78, 0x61, 0x76, 0x66, 0x22, 0x44,
+ 0x73, 0x25, 0x44, 0x29, 0x2a, 0x28, 0x3b, 0x67,
+ 0x48, 0x58, 0x37, 0x4a, 0x76, 0x76, 0x51, 0x4a,
+ 0x61, 0x70, 0x51, 0x74, 0x40, 0x23, 0x29, 0x63,
+ 0x69, 0x4a, 0x29, 0x23, 0x34, 0x6a, 0x3b, 0x25,
+ 0x28, 0x54, 0x45, 0x33, 0x28, 0x44, 0x30, 0x61,
+ 0x5b, 0x60, 0x51, 0x3f, 0x68, 0x50, 0x70, 0x3d,
+ 0x58, 0x2e, 0x6e, 0x59, 0x5a, 0x62, 0x66, 0x4d,
+ 0x7a, 0x2e, 0x37, 0x37, 0x3d, 0x7b, 0x74, 0x79,
+ 0x48, 0x45, 0x77, 0x56, 0x33, 0x76, 0x71, 0x60,
+ 0x74, 0x3f, 0x61, 0x22, 0x52, 0x51, 0x71, 0x69
+ };
+ uint32_t request3_len = sizeof(request3);
+
+ uint8_t request4[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x75, 0x3e, 0x76, 0x3e, 0x66, 0x6b, 0x6b, 0x3e,
+ 0x6d, 0x59, 0x38, 0x2b, 0x63, 0x4d, 0x2c, 0x73,
+ 0x54, 0x57, 0x34, 0x25, 0x5b, 0x42, 0x7d, 0x5d,
+ 0x37, 0x34, 0x2c, 0x79, 0x24, 0x4b, 0x74, 0x73,
+ 0x25, 0x36, 0x73, 0x3a, 0x2c, 0x55, 0x69, 0x3c,
+ 0x58, 0x67, 0x33, 0x53, 0x67, 0x5c, 0x61, 0x7b,
+ 0x44, 0x2e, 0x42, 0x2d, 0x6b, 0x50, 0x55, 0x24,
+ 0x70, 0x58, 0x60, 0x38, 0x42, 0x45, 0x70, 0x6d,
+ 0x2f, 0x27, 0x27, 0x2c, 0x21, 0x6d, 0x57, 0x6e,
+ 0x43, 0x3c, 0x5b, 0x27, 0x7a, 0x34, 0x49, 0x5a,
+ 0x69, 0x30, 0x3f, 0x6f, 0x77, 0x70, 0x39, 0x2d,
+ 0x51, 0x74, 0x4b, 0x25, 0x70, 0x51, 0x64, 0x4d,
+ 0x75, 0x52, 0x5e, 0x3e, 0x37, 0x30, 0x5d, 0x3b,
+ 0x2c, 0x72, 0x25, 0x6c, 0x6f, 0x79, 0x69, 0x3c,
+ 0x5b, 0x73, 0x3d, 0x41, 0x28, 0x28, 0x64, 0x60,
+ 0x4b, 0x7a, 0x2c, 0x4a, 0x6b, 0x3d, 0x2e, 0x6c,
+ 0x7a, 0x54, 0x70, 0x61, 0x6f, 0x4b, 0x40, 0x28,
+ 0x59, 0x31, 0x25, 0x21, 0x57, 0x79, 0x4b, 0x31,
+ 0x6f, 0x4e, 0x71, 0x2b, 0x3c, 0x24, 0x30, 0x28,
+ 0x3c, 0x61, 0x28, 0x4b, 0x35, 0x61, 0x4d, 0x55,
+ 0x5e, 0x66, 0x34, 0x5f, 0x61, 0x70, 0x7b, 0x67,
+ 0x51, 0x55, 0x68, 0x78, 0x26, 0x3a, 0x27, 0x4e,
+ 0x71, 0x79, 0x4f, 0x67, 0x2c, 0x5a, 0x79, 0x75,
+ 0x59, 0x3a, 0x33, 0x4a, 0x36, 0x71, 0x72, 0x6d,
+ 0x49, 0x3e, 0x53, 0x59, 0x2b, 0x2b, 0x27, 0x4e,
+ 0x50, 0x5d, 0x21, 0x55, 0x64, 0x4b, 0x72, 0x73,
+ 0x25, 0x55, 0x26, 0x4f, 0x3a, 0x21, 0x54, 0x29,
+ 0x4f, 0x64, 0x51, 0x59, 0x60, 0x7b, 0x7c, 0x6f,
+ 0x3e, 0x65, 0x74, 0x6a, 0x5b, 0x52, 0x2c, 0x56,
+ 0x4e, 0x45, 0x53, 0x4b, 0x7c, 0x38, 0x49, 0x4b,
+ 0x4e, 0x4f, 0x4a, 0x47, 0x5e, 0x7c, 0x46, 0x3b,
+ 0x67, 0x2e, 0x43, 0x79, 0x35, 0x55, 0x59, 0x6d,
+ 0x38, 0x70, 0x2f, 0x59, 0x4f, 0x27, 0x63, 0x40,
+ 0x66, 0x2d, 0x39, 0x4f, 0x3d, 0x2e, 0x4c, 0x67,
+ 0x71, 0x7d, 0x34, 0x22, 0x52, 0x4e, 0x36, 0x7b,
+ 0x2c, 0x39, 0x4d, 0x42, 0x60, 0x75, 0x74, 0x72,
+ 0x4f, 0x72, 0x68, 0x3a, 0x51, 0x31, 0x2d, 0x21,
+ 0x4a, 0x35, 0x47, 0x6d, 0x69, 0x3c, 0x50, 0x4c,
+ 0x59, 0x66, 0x4c, 0x71, 0x24, 0x3a, 0x36, 0x67,
+ 0x24, 0x5a, 0x59, 0x28, 0x7c, 0x21, 0x5e, 0x77,
+ 0x68, 0x5e, 0x7b, 0x6e, 0x56, 0x62, 0x36, 0x29,
+ 0x6f, 0x4f, 0x5d, 0x57, 0x56, 0x2b, 0x75, 0x2a,
+ 0x2c, 0x69, 0x63, 0x51, 0x74, 0x6e, 0x5e, 0x46,
+ 0x50, 0x28, 0x2c, 0x3b, 0x32, 0x53, 0x28, 0x78,
+ 0x59, 0x72, 0x39, 0x5e, 0x44, 0x5c, 0x77, 0x60,
+ 0x72, 0x44, 0x3b, 0x75, 0x68, 0x39, 0x55, 0x3e,
+ 0x44, 0x50, 0x76, 0x3c, 0x48, 0x46, 0x43, 0x22,
+ 0x56, 0x27, 0x21, 0x31, 0x33, 0x4a, 0x5a, 0x74,
+ 0x41, 0x58, 0x3f, 0x39, 0x29, 0x71, 0x73, 0x30,
+ 0x57, 0x70, 0x33, 0x62, 0x7b, 0x4a, 0x75, 0x3e,
+ 0x4d, 0x4c, 0x4e, 0x55, 0x63, 0x38, 0x66, 0x7d,
+ 0x68, 0x7d, 0x6f, 0x23, 0x55, 0x50, 0x3d, 0x34,
+ 0x46, 0x5e, 0x2f, 0x55, 0x27, 0x62, 0x68, 0x7c,
+ 0x6c, 0x21, 0x2b, 0x63, 0x4b, 0x47, 0x6b, 0x6a,
+ 0x5b, 0x7b, 0x5c, 0x71, 0x37, 0x7c, 0x52, 0x2b,
+ 0x2f, 0x4a, 0x47, 0x70, 0x78, 0x50, 0x2f, 0x75,
+ 0x28, 0x4c, 0x60, 0x4c, 0x4c, 0x54, 0x6b, 0x68,
+ 0x63, 0x4f, 0x47, 0x39, 0x2a, 0x70, 0x51, 0x7d,
+ 0x28, 0x59, 0x52, 0x46, 0x4b, 0x38, 0x27, 0x49,
+ 0x50, 0x5d, 0x25, 0x22, 0x5f, 0x48, 0x2c, 0x2f,
+ 0x67, 0x59, 0x5d, 0x7d, 0x21, 0x3d, 0x72, 0x4f,
+ 0x5c, 0x5b, 0x41, 0x47, 0x5f, 0x56, 0x69, 0x42,
+ 0x55, 0x60, 0x68, 0x4b, 0x77, 0x44, 0x4c, 0x3b,
+ 0x7d, 0x5a, 0x58, 0x43, 0x7a, 0x33, 0x22, 0x58,
+ 0x58, 0x6f, 0x74, 0x53, 0x57, 0x6d, 0x6e, 0x29,
+ 0x6b, 0x33, 0x71, 0x68, 0x29, 0x48, 0x67, 0x35,
+ 0x52, 0x41, 0x6b, 0x36, 0x4f, 0x46, 0x31, 0x24,
+ 0x73, 0x56, 0x40, 0x48, 0x37, 0x51, 0x24, 0x2a,
+ 0x59, 0x21, 0x74, 0x76, 0x25, 0x2e, 0x4a, 0x74,
+ 0x32, 0x29, 0x5f, 0x57, 0x7c, 0x58, 0x30, 0x2c,
+ 0x7b, 0x70, 0x5b, 0x51, 0x73, 0x27, 0x4a, 0x28,
+ 0x77, 0x2a, 0x43, 0x28, 0x2e, 0x32, 0x3d, 0x38,
+ 0x36, 0x2e, 0x6b, 0x40, 0x6c, 0x76, 0x54, 0x66,
+ 0x4a, 0x5c, 0x25, 0x62, 0x2e, 0x61, 0x48, 0x30,
+ 0x28, 0x41, 0x40, 0x69, 0x3c, 0x39, 0x36, 0x4b,
+ 0x64, 0x50, 0x76, 0x3d, 0x52, 0x50, 0x77, 0x33,
+ 0x3b, 0x65, 0x59, 0x31, 0x5c, 0x48, 0x6a, 0x74,
+ 0x78, 0x5b, 0x74, 0x60, 0x47, 0x27, 0x60, 0x22,
+ 0x4a, 0x72, 0x25, 0x34, 0x5d, 0x3a, 0x21, 0x66,
+ 0x61, 0x7b, 0x34, 0x41, 0x3b, 0x3a, 0x27, 0x44,
+ 0x48, 0x7c, 0x7a, 0x74, 0x3a, 0x68, 0x59, 0x48,
+ 0x61, 0x32, 0x49, 0x61, 0x40, 0x22, 0x33, 0x75,
+ 0x29, 0x76, 0x5b, 0x24, 0x5b, 0x5c, 0x76, 0x5c,
+ 0x28, 0x75, 0x36, 0x26, 0x2c, 0x65, 0x5e, 0x51,
+ 0x7b, 0x3a, 0x7d, 0x4f, 0x35, 0x73, 0x6b, 0x5b,
+ 0x5c, 0x37, 0x35, 0x6b, 0x41, 0x35, 0x40, 0x3a,
+ 0x22, 0x28, 0x6c, 0x71, 0x46, 0x68, 0x7b, 0x66,
+ 0x56, 0x24, 0x7c, 0x54, 0x28, 0x30, 0x22, 0x4e,
+ 0x3c, 0x65, 0x69, 0x36, 0x44, 0x53, 0x3d, 0x6c,
+ 0x5f, 0x73, 0x6c, 0x6f, 0x5e, 0x27, 0x23, 0x4e,
+ 0x60, 0x45, 0x2f, 0x3d, 0x37, 0x28, 0x51, 0x29,
+ 0x77, 0x6a, 0x6b, 0x2a, 0x2a, 0x51, 0x26, 0x4c,
+ 0x4e, 0x71, 0x77, 0x73, 0x71, 0x2d, 0x5a, 0x2c,
+ 0x23, 0x3d, 0x5f, 0x62, 0x63, 0x2e, 0x72, 0x2a,
+ 0x75, 0x66, 0x43, 0x56, 0x5f, 0x21, 0x64, 0x66,
+ 0x35, 0x3b, 0x7a, 0x45, 0x3f, 0x4f, 0x57, 0x22,
+ 0x5a, 0x45, 0x65, 0x37, 0x58, 0x5b, 0x43, 0x66,
+ 0x4f, 0x5d, 0x6e, 0x41, 0x41, 0x62, 0x5e, 0x39,
+ 0x65, 0x6f, 0x43, 0x4b, 0x5e, 0x51, 0x42, 0x3f,
+ 0x2d, 0x68, 0x4b, 0x6e, 0x46, 0x6f, 0x21, 0x44,
+ 0x3c, 0x22, 0x46, 0x31, 0x31, 0x2e, 0x56, 0x2e,
+ 0x77, 0x48, 0x68, 0x23, 0x4a, 0x36, 0x52, 0x5d,
+ 0x61, 0x47, 0x71, 0x2e, 0x3a, 0x4a, 0x5b, 0x56,
+ 0x6b, 0x52, 0x2a, 0x4c, 0x4f, 0x24, 0x34, 0x60,
+ 0x70, 0x58, 0x7a, 0x76, 0x4b, 0x68, 0x24, 0x5f,
+ 0x51, 0x6d, 0x75, 0x45, 0x48, 0x21, 0x53, 0x4d,
+ 0x27, 0x75, 0x5f, 0x50, 0x3e, 0x40, 0x3f, 0x5e,
+ 0x64, 0x41, 0x5f, 0x68, 0x48, 0x30, 0x71, 0x4b,
+ 0x66, 0x2c, 0x2f, 0x76, 0x4b, 0x23, 0x46, 0x34,
+ 0x50, 0x58, 0x52, 0x69, 0x2b, 0x6e, 0x7a, 0x33,
+ 0x53, 0x43, 0x43, 0x35, 0x54, 0x30, 0x73, 0x63,
+ 0x3b, 0x43, 0x52, 0x29, 0x45, 0x37, 0x71, 0x79,
+ 0x5a, 0x26, 0x24, 0x72, 0x73, 0x4e, 0x44, 0x38,
+ 0x5b, 0x71, 0x36, 0x3a, 0x4f, 0x5b, 0x71, 0x28,
+ 0x71, 0x79, 0x72, 0x40, 0x6e, 0x51, 0x72, 0x29,
+ 0x3d, 0x4f, 0x33, 0x22, 0x73, 0x5a, 0x30, 0x71,
+ 0x58, 0x54, 0x59, 0x48, 0x29, 0x2b, 0x5c, 0x73,
+ 0x6f, 0x4e, 0x60, 0x2a, 0x72, 0x39, 0x50, 0x59,
+ 0x6f, 0x48, 0x3e, 0x62, 0x6c, 0x62, 0x49, 0x6c,
+ 0x2c, 0x3f, 0x43, 0x3f, 0x32, 0x7c, 0x6f, 0x6c,
+ 0x39, 0x26, 0x26, 0x7b, 0x5d, 0x65, 0x6f, 0x41,
+ 0x7c, 0x42, 0x2b, 0x65, 0x6f, 0x3e, 0x7b, 0x69,
+ 0x46, 0x4d, 0x68, 0x68, 0x5a, 0x33, 0x25, 0x5d,
+ 0x6f, 0x48, 0x7c, 0x77, 0x7d, 0x3f, 0x4e, 0x30,
+ 0x69, 0x65, 0x28, 0x2e, 0x34, 0x34, 0x41, 0x43,
+ 0x5e, 0x30, 0x23, 0x3b, 0x60, 0x79, 0x5b, 0x26,
+ 0x7c, 0x77, 0x3e, 0x43, 0x24, 0x31, 0x3a, 0x56,
+ 0x24, 0x3c, 0x60, 0x3f, 0x60, 0x55, 0x6a, 0x68
+ };
+ uint32_t request4_len = sizeof(request4);
+
+ uint8_t request5[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x69, 0x3e, 0x72, 0x44, 0x31, 0x6b, 0x28, 0x2f,
+ 0x79, 0x37, 0x58, 0x5d, 0x5f, 0x68, 0x71, 0x47,
+ 0x7a, 0x68, 0x7c, 0x6c, 0x65, 0x3c, 0x74, 0x67,
+ 0x59, 0x5c, 0x3d, 0x28, 0x65, 0x28, 0x58, 0x74,
+ 0x44, 0x62, 0x2e, 0x36, 0x54, 0x2f, 0x24, 0x34,
+ 0x4b, 0x6d, 0x3a, 0x7b, 0x60, 0x71, 0x5a, 0x77,
+ 0x4a, 0x27, 0x25, 0x70, 0x75, 0x56, 0x78, 0x73,
+ 0x2e, 0x38, 0x6c, 0x70, 0x66, 0x7b, 0x7b, 0x2d,
+ 0x78, 0x27, 0x65, 0x63, 0x58, 0x4f, 0x7d, 0x5c,
+ 0x31, 0x3e, 0x36, 0x6e, 0x65, 0x61, 0x2e, 0x4e,
+ 0x26, 0x68, 0x2b, 0x33, 0x7d, 0x54, 0x2c, 0x28,
+ 0x47, 0x3a, 0x31, 0x47, 0x56, 0x32, 0x74, 0x51,
+ 0x79, 0x65, 0x42, 0x45, 0x60, 0x55, 0x6f, 0x48,
+ 0x61, 0x23, 0x72, 0x62, 0x74, 0x3a, 0x5a, 0x26,
+ 0x2d, 0x41, 0x58, 0x62, 0x75, 0x4b, 0x37, 0x2e,
+ 0x3f, 0x2a, 0x6e, 0x2e, 0x2c, 0x43, 0x6f, 0x53,
+ 0x5f, 0x48, 0x7a, 0x53, 0x7b, 0x54, 0x28, 0x30,
+ 0x2b, 0x7a, 0x34, 0x33, 0x28, 0x2b, 0x23, 0x23,
+ 0x72, 0x38, 0x25, 0x30, 0x35, 0x66, 0x76, 0x46,
+ 0x2a, 0x57, 0x7a, 0x60, 0x38, 0x5a, 0x26, 0x4f,
+ 0x78, 0x43, 0x2c, 0x7d, 0x3d, 0x76, 0x7d, 0x66,
+ 0x48, 0x7d, 0x3e, 0x59, 0x31, 0x58, 0x6b, 0x30,
+ 0x76, 0x45, 0x6e, 0x70, 0x72, 0x5f, 0x3c, 0x70,
+ 0x6d, 0x77, 0x42, 0x75, 0x42, 0x73, 0x68, 0x5e,
+ 0x5f, 0x72, 0x2b, 0x2a, 0x70, 0x38, 0x7a, 0x4c,
+ 0x58, 0x2e, 0x5e, 0x2d, 0x2d, 0x78, 0x67, 0x5a,
+ 0x77, 0x34, 0x5a, 0x50, 0x76, 0x2d, 0x2b, 0x77,
+ 0x37, 0x6e, 0x38, 0x2d, 0x7b, 0x44, 0x78, 0x67,
+ 0x52, 0x57, 0x79, 0x43, 0x7d, 0x6d, 0x4d, 0x32,
+ 0x23, 0x37, 0x51, 0x4b, 0x41, 0x60, 0x6e, 0x53,
+ 0x4e, 0x78, 0x37, 0x37, 0x60, 0x56, 0x64, 0x52,
+ 0x25, 0x46, 0x53, 0x5f, 0x2b, 0x56, 0x2b, 0x3b,
+ 0x40, 0x37, 0x33, 0x37, 0x23, 0x43, 0x36, 0x6b,
+ 0x6b, 0x5d, 0x35, 0x28, 0x7d, 0x6a, 0x2c, 0x68,
+ 0x28, 0x4b, 0x4a, 0x6c, 0x27, 0x35, 0x51, 0x66,
+ 0x30, 0x39, 0x28, 0x4d, 0x61, 0x2f, 0x64, 0x36,
+ 0x59, 0x39, 0x68, 0x4b, 0x24, 0x51, 0x7b, 0x6e,
+ 0x38, 0x49, 0x55, 0x72, 0x5f, 0x33, 0x5c, 0x26,
+ 0x45, 0x2f, 0x71, 0x66, 0x33, 0x3d, 0x36, 0x68,
+ 0x65, 0x48, 0x42, 0x40, 0x58, 0x61, 0x4f, 0x50,
+ 0x70, 0x5e, 0x3c, 0x5d, 0x56, 0x43, 0x4c, 0x41,
+ 0x45, 0x54, 0x76, 0x4b, 0x21, 0x25, 0x45, 0x4c,
+ 0x5e, 0x58, 0x23, 0x7d, 0x34, 0x61, 0x5c, 0x53,
+ 0x2a, 0x47, 0x37, 0x22, 0x6d, 0x31, 0x42, 0x6e,
+ 0x22, 0x72, 0x62, 0x55, 0x59, 0x66, 0x28, 0x73,
+ 0x55, 0x50, 0x5c, 0x6f, 0x52, 0x40, 0x3e, 0x3b,
+ 0x44, 0x2a, 0x51, 0x3d, 0x4d, 0x47, 0x3a, 0x57,
+ 0x3e, 0x29, 0x29, 0x7d, 0x40, 0x36, 0x41, 0x3f,
+ 0x58, 0x77, 0x3b, 0x41, 0x2d, 0x64, 0x5a, 0x72,
+ 0x7c, 0x7d, 0x30, 0x68, 0x54, 0x34, 0x40, 0x21,
+ 0x7d, 0x2b, 0x2d, 0x2b, 0x6d, 0x5f, 0x49, 0x57,
+ 0x68, 0x65, 0x79, 0x2c, 0x21, 0x41, 0x31, 0x55,
+ 0x27, 0x4d, 0x78, 0x55, 0x2f, 0x61, 0x62, 0x78,
+ 0x58, 0x25, 0x3a, 0x4b, 0x3e, 0x67, 0x44, 0x7c,
+ 0x7d, 0x52, 0x3d, 0x3e, 0x3b, 0x62, 0x2d, 0x28,
+ 0x48, 0x70, 0x2c, 0x79, 0x31, 0x5a, 0x5e, 0x3f,
+ 0x6a, 0x30, 0x78, 0x41, 0x44, 0x60, 0x4e, 0x63,
+ 0x63, 0x2e, 0x31, 0x79, 0x2b, 0x47, 0x57, 0x26,
+ 0x22, 0x6a, 0x46, 0x43, 0x70, 0x30, 0x51, 0x7d,
+ 0x21, 0x3c, 0x68, 0x74, 0x40, 0x5a, 0x6e, 0x71,
+ 0x3f, 0x76, 0x73, 0x2e, 0x29, 0x3f, 0x6a, 0x55,
+ 0x21, 0x72, 0x65, 0x75, 0x5e, 0x6b, 0x39, 0x6e,
+ 0x3e, 0x76, 0x42, 0x41, 0x65, 0x3f, 0x2b, 0x37,
+ 0x70, 0x7a, 0x7a, 0x29, 0x50, 0x66, 0x21, 0x67,
+ 0x3f, 0x54, 0x32, 0x5f, 0x73, 0x27, 0x59, 0x6f,
+ 0x39, 0x4b, 0x4e, 0x23, 0x54, 0x3b, 0x39, 0x21,
+ 0x38, 0x41, 0x33, 0x44, 0x57, 0x6b, 0x51, 0x30,
+ 0x6a, 0x76, 0x62, 0x2c, 0x5c, 0x5e, 0x49, 0x3e,
+ 0x59, 0x38, 0x5e, 0x4a, 0x59, 0x77, 0x34, 0x25,
+ 0x4f, 0x76, 0x6a, 0x68, 0x6f, 0x73, 0x7c, 0x3d,
+ 0x2d, 0x64, 0x6c, 0x7a, 0x3d, 0x2c, 0x26, 0x28,
+ 0x58, 0x2b, 0x4b, 0x45, 0x68, 0x38, 0x74, 0x63,
+ 0x7b, 0x4a, 0x63, 0x52, 0x26, 0x54, 0x3c, 0x46,
+ 0x77, 0x2d, 0x6b, 0x78, 0x63, 0x7b, 0x6a, 0x50,
+ 0x26, 0x42, 0x62, 0x63, 0x65, 0x6b, 0x63, 0x54,
+ 0x4d, 0x47, 0x59, 0x48, 0x2e, 0x60, 0x7c, 0x4d,
+ 0x33, 0x4d, 0x61, 0x72, 0x76, 0x72, 0x21, 0x4d,
+ 0x2b, 0x43, 0x58, 0x47, 0x4a, 0x36, 0x2d, 0x7b,
+ 0x32, 0x72, 0x21, 0x78, 0x22, 0x38, 0x2c, 0x7a,
+ 0x34, 0x44, 0x45, 0x66, 0x31, 0x7b, 0x37, 0x68,
+ 0x62, 0x65, 0x62, 0x6d, 0x4e, 0x7c, 0x75, 0x38,
+ 0x2a, 0x73, 0x27, 0x64, 0x33, 0x4f, 0x21, 0x41,
+ 0x7c, 0x41, 0x3f, 0x60, 0x68, 0x34, 0x72, 0x5b,
+ 0x38, 0x33, 0x6f, 0x65, 0x3e, 0x5a, 0x7d, 0x25,
+ 0x49, 0x50, 0x60, 0x36, 0x59, 0x5e, 0x6b, 0x25,
+ 0x66, 0x7a, 0x7d, 0x71, 0x40, 0x6c, 0x2c, 0x6e,
+ 0x6a, 0x5a, 0x24, 0x5a, 0x76, 0x21, 0x67, 0x39,
+ 0x4b, 0x4a, 0x31, 0x24, 0x66, 0x66, 0x2e, 0x58,
+ 0x43, 0x46, 0x75, 0x6c, 0x47, 0x28, 0x4f, 0x21,
+ 0x75, 0x77, 0x6f, 0x71, 0x48, 0x3f, 0x4d, 0x4c,
+ 0x51, 0x37, 0x3b, 0x41, 0x4d, 0x41, 0x48, 0x28,
+ 0x71, 0x24, 0x2f, 0x7a, 0x22, 0x49, 0x4a, 0x39,
+ 0x44, 0x43, 0x68, 0x21, 0x3a, 0x34, 0x4e, 0x52,
+ 0x7a, 0x60, 0x71, 0x61, 0x6d, 0x51, 0x58, 0x2a,
+ 0x59, 0x4c, 0x4a, 0x59, 0x6b, 0x77, 0x78, 0x2e,
+ 0x27, 0x78, 0x76, 0x48, 0x4f, 0x46, 0x79, 0x2c,
+ 0x54, 0x42, 0x7b, 0x2c, 0x52, 0x41, 0x54, 0x2b,
+ 0x2c, 0x33, 0x6b, 0x70, 0x77, 0x2e, 0x2e, 0x41,
+ 0x25, 0x7a, 0x48, 0x6e, 0x71, 0x55, 0x6a, 0x43,
+ 0x5a, 0x2c, 0x6c, 0x76, 0x6d, 0x71, 0x72, 0x4d,
+ 0x76, 0x5b, 0x7b, 0x22, 0x4b, 0x45, 0x31, 0x30,
+ 0x26, 0x53, 0x75, 0x3f, 0x26, 0x59, 0x36, 0x2f,
+ 0x68, 0x4f, 0x34, 0x5e, 0x2b, 0x30, 0x63, 0x68,
+ 0x7b, 0x32, 0x5e, 0x77, 0x7d, 0x7b, 0x53, 0x5f,
+ 0x63, 0x53, 0x77, 0x7a, 0x7d, 0x35, 0x28, 0x3e,
+ 0x41, 0x6f, 0x5b, 0x31, 0x78, 0x7b, 0x2b, 0x51,
+ 0x23, 0x43, 0x46, 0x6a, 0x32, 0x32, 0x25, 0x45,
+ 0x57, 0x43, 0x22, 0x50, 0x60, 0x32, 0x70, 0x2e,
+ 0x79, 0x2e, 0x6b, 0x33, 0x67, 0x6c, 0x43, 0x5b,
+ 0x3b, 0x68, 0x53, 0x53, 0x6a, 0x48, 0x59, 0x5f,
+ 0x30, 0x72, 0x7d, 0x6b, 0x37, 0x24, 0x75, 0x52,
+ 0x50, 0x2b, 0x75, 0x35, 0x24, 0x3b, 0x6e, 0x53,
+ 0x56, 0x34, 0x23, 0x54, 0x65, 0x4f, 0x78, 0x3e,
+ 0x46, 0x7d, 0x25, 0x3f, 0x2f, 0x49, 0x6b, 0x49,
+ 0x47, 0x45, 0x24, 0x38, 0x3b, 0x68, 0x6c, 0x4f,
+ 0x29, 0x21, 0x50, 0x32, 0x67, 0x47, 0x5a, 0x72,
+ 0x76, 0x21, 0x39, 0x67, 0x3c, 0x72, 0x47, 0x43,
+ 0x4a, 0x2e, 0x31, 0x32, 0x34, 0x3c, 0x53, 0x2d,
+ 0x22, 0x5b, 0x5b, 0x6a, 0x77, 0x75, 0x31, 0x68,
+ 0x30, 0x45, 0x43, 0x5f, 0x60, 0x5d, 0x56, 0x67,
+ 0x66, 0x55, 0x6a, 0x72, 0x77, 0x7b, 0x44, 0x61,
+ 0x22, 0x64, 0x36, 0x39, 0x6e, 0x44, 0x37, 0x54,
+ 0x45, 0x46, 0x6f, 0x58, 0x35, 0x51, 0x3c, 0x62,
+ 0x49, 0x3a, 0x50, 0x58, 0x56, 0x5d, 0x77, 0x6f,
+ 0x56, 0x64, 0x7b, 0x49, 0x39, 0x21, 0x31, 0x2d,
+ 0x5f, 0x56, 0x56, 0x33, 0x31, 0x69, 0x4a, 0x52,
+ 0x62, 0x5b, 0x6e, 0x65, 0x7c, 0x3d, 0x31, 0x55,
+ 0x3d, 0x75, 0x25, 0x61, 0x50, 0x71, 0x45, 0x29
+ };
+ uint32_t request5_len = sizeof(request5);
+
+ uint8_t request6[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x5b, 0x56, 0x3d, 0x5a, 0x6b, 0x43, 0x73, 0x26,
+ 0x65, 0x3b, 0x38, 0x79, 0x26, 0x5e, 0x60, 0x59,
+ 0x40, 0x71, 0x7c, 0x72, 0x28, 0x29, 0x69, 0x32,
+ 0x72, 0x5a, 0x6c, 0x55, 0x43, 0x65, 0x3f, 0x4a,
+ 0x21, 0x66, 0x59, 0x30, 0x76, 0x39, 0x21, 0x69,
+ 0x4b, 0x25, 0x5d, 0x6e, 0x5f, 0x24, 0x2b, 0x38,
+ 0x70, 0x78, 0x35, 0x7d, 0x39, 0x36, 0x31, 0x72,
+ 0x44, 0x49, 0x45, 0x3d, 0x25, 0x50, 0x24, 0x3b,
+ 0x52, 0x27, 0x66, 0x46, 0x5d, 0x4f, 0x34, 0x50,
+ 0x26, 0x5a, 0x25, 0x3e, 0x3f, 0x34, 0x4b, 0x35,
+ 0x77, 0x3a, 0x3f, 0x3e, 0x23, 0x4e, 0x30, 0x23,
+ 0x70, 0x72, 0x33, 0x34, 0x60, 0x2a, 0x4a, 0x32,
+ 0x6e, 0x29, 0x54, 0x73, 0x5f, 0x26, 0x71, 0x3a,
+ 0x78, 0x5d, 0x3f, 0x31, 0x48, 0x59, 0x61, 0x44,
+ 0x5c, 0x38, 0x4f, 0x41, 0x73, 0x67, 0x62, 0x73,
+ 0x33, 0x52, 0x77, 0x73, 0x57, 0x49, 0x7a, 0x59,
+ 0x26, 0x21, 0x34, 0x38, 0x2b, 0x5f, 0x5f, 0x37,
+ 0x74, 0x28, 0x46, 0x3d, 0x43, 0x42, 0x26, 0x66,
+ 0x63, 0x37, 0x6d, 0x2a, 0x65, 0x3f, 0x71, 0x2d,
+ 0x4c, 0x72, 0x29, 0x4b, 0x3a, 0x77, 0x64, 0x6a,
+ 0x6b, 0x42, 0x70, 0x5c, 0x51, 0x38, 0x71, 0x25,
+ 0x4c, 0x7c, 0x6f, 0x74, 0x71, 0x39, 0x71, 0x25,
+ 0x3f, 0x62, 0x23, 0x45, 0x5f, 0x77, 0x59, 0x56,
+ 0x56, 0x67, 0x78, 0x3a, 0x2e, 0x4e, 0x27, 0x59,
+ 0x65, 0x2f, 0x64, 0x3c, 0x62, 0x40, 0x69, 0x52,
+ 0x36, 0x49, 0x3e, 0x3b, 0x2c, 0x47, 0x4f, 0x3e,
+ 0x61, 0x78, 0x2d, 0x45, 0x71, 0x3f, 0x7b, 0x55,
+ 0x34, 0x36, 0x47, 0x5e, 0x36, 0x51, 0x3d, 0x5a,
+ 0x4b, 0x75, 0x44, 0x72, 0x61, 0x44, 0x71, 0x4e,
+ 0x42, 0x6a, 0x2c, 0x34, 0x40, 0x3b, 0x40, 0x31,
+ 0x31, 0x75, 0x4b, 0x32, 0x71, 0x69, 0x3a, 0x5d,
+ 0x31, 0x25, 0x53, 0x2a, 0x61, 0x54, 0x68, 0x2a,
+ 0x76, 0x71, 0x57, 0x67, 0x56, 0x23, 0x7d, 0x70,
+ 0x7d, 0x28, 0x57, 0x5f, 0x2f, 0x4c, 0x71, 0x2e,
+ 0x40, 0x63, 0x49, 0x5b, 0x7c, 0x7b, 0x56, 0x76,
+ 0x77, 0x46, 0x69, 0x56, 0x3d, 0x75, 0x31, 0x3b,
+ 0x35, 0x40, 0x37, 0x2c, 0x51, 0x37, 0x49, 0x6a,
+ 0x79, 0x68, 0x53, 0x31, 0x4c, 0x6f, 0x57, 0x4c,
+ 0x48, 0x31, 0x6a, 0x30, 0x2b, 0x69, 0x30, 0x56,
+ 0x58, 0x4b, 0x76, 0x3b, 0x60, 0x6d, 0x35, 0x4d,
+ 0x74, 0x2f, 0x74, 0x2c, 0x54, 0x4f, 0x6e, 0x3f,
+ 0x38, 0x56, 0x5c, 0x67, 0x2b, 0x4a, 0x35, 0x30,
+ 0x67, 0x7d, 0x58, 0x24, 0x59, 0x54, 0x48, 0x2e,
+ 0x28, 0x7d, 0x6e, 0x51, 0x55, 0x68, 0x56, 0x54,
+ 0x59, 0x31, 0x4a, 0x65, 0x5a, 0x5e, 0x27, 0x76,
+ 0x76, 0x65, 0x6d, 0x2f, 0x75, 0x63, 0x67, 0x52,
+ 0x5e, 0x29, 0x58, 0x3d, 0x5c, 0x3f, 0x54, 0x7c,
+ 0x67, 0x21, 0x6e, 0x75, 0x67, 0x35, 0x77, 0x31,
+ 0x3d, 0x26, 0x3f, 0x60, 0x45, 0x2d, 0x2b, 0x45,
+ 0x5d, 0x3f, 0x55, 0x73, 0x59, 0x4c, 0x5e, 0x6c,
+ 0x30, 0x4a, 0x4e, 0x47, 0x55, 0x42, 0x6a, 0x4b,
+ 0x32, 0x3c, 0x75, 0x6e, 0x36, 0x51, 0x5f, 0x4c,
+ 0x68, 0x72, 0x72, 0x27, 0x3b, 0x51, 0x59, 0x7b,
+ 0x68, 0x7b, 0x3b, 0x54, 0x35, 0x37, 0x7c, 0x44,
+ 0x43, 0x36, 0x4c, 0x4f, 0x67, 0x62, 0x4e, 0x39,
+ 0x4b, 0x7a, 0x49, 0x36, 0x68, 0x38, 0x4c, 0x4a,
+ 0x64, 0x33, 0x35, 0x2f, 0x3e, 0x5c, 0x58, 0x61,
+ 0x23, 0x5b, 0x50, 0x6e, 0x34, 0x44, 0x60, 0x28,
+ 0x54, 0x41, 0x5c, 0x31, 0x53, 0x2d, 0x58, 0x58,
+ 0x54, 0x28, 0x77, 0x51, 0x6f, 0x64, 0x4c, 0x68,
+ 0x34, 0x79, 0x45, 0x66, 0x2c, 0x26, 0x77, 0x64,
+ 0x5f, 0x6c, 0x3b, 0x71, 0x28, 0x4d, 0x68, 0x2a,
+ 0x6b, 0x37, 0x6a, 0x34, 0x51, 0x27, 0x2a, 0x46,
+ 0x3a, 0x2e, 0x35, 0x21, 0x21, 0x79, 0x51, 0x44,
+ 0x58, 0x5d, 0x6f, 0x65, 0x6b, 0x76, 0x68, 0x3a,
+ 0x43, 0x70, 0x36, 0x41, 0x6b, 0x56, 0x64, 0x75,
+ 0x5b, 0x37, 0x24, 0x56, 0x7c, 0x6e, 0x6c, 0x41,
+ 0x3a, 0x60, 0x56, 0x38, 0x55, 0x63, 0x77, 0x4d,
+ 0x6e, 0x50, 0x3c, 0x3d, 0x7a, 0x44, 0x71, 0x42,
+ 0x4b, 0x55, 0x75, 0x72, 0x61, 0x60, 0x65, 0x6f,
+ 0x7a, 0x26, 0x64, 0x46, 0x67, 0x74, 0x29, 0x2a,
+ 0x5b, 0x62, 0x41, 0x28, 0x62, 0x30, 0x34, 0x33,
+ 0x40, 0x79, 0x7a, 0x38, 0x56, 0x38, 0x73, 0x22,
+ 0x7a, 0x7d, 0x73, 0x2a, 0x2a, 0x28, 0x2b, 0x63,
+ 0x27, 0x6f, 0x3d, 0x3e, 0x2c, 0x56, 0x23, 0x32,
+ 0x4b, 0x3b, 0x58, 0x4d, 0x72, 0x4c, 0x49, 0x6f,
+ 0x30, 0x76, 0x23, 0x21, 0x21, 0x3c, 0x49, 0x56,
+ 0x7a, 0x56, 0x79, 0x2f, 0x50, 0x7a, 0x5b, 0x21,
+ 0x21, 0x4a, 0x48, 0x61, 0x33, 0x52, 0x49, 0x2e,
+ 0x30, 0x7d, 0x2c, 0x2d, 0x67, 0x23, 0x55, 0x62,
+ 0x66, 0x52, 0x5a, 0x61, 0x75, 0x63, 0x3c, 0x39,
+ 0x69, 0x41, 0x31, 0x6b, 0x4e, 0x6f, 0x25, 0x34,
+ 0x74, 0x30, 0x21, 0x3a, 0x40, 0x72, 0x44, 0x40,
+ 0x60, 0x4c, 0x53, 0x74, 0x42, 0x64, 0x44, 0x49,
+ 0x76, 0x67, 0x21, 0x79, 0x36, 0x3c, 0x37, 0x70,
+ 0x4f, 0x58, 0x29, 0x71, 0x2a, 0x3a, 0x4d, 0x5d,
+ 0x67, 0x68, 0x52, 0x63, 0x23, 0x24, 0x4b, 0x21,
+ 0x3f, 0x68, 0x69, 0x6c, 0x66, 0x66, 0x42, 0x28,
+ 0x59, 0x35, 0x34, 0x6f, 0x2d, 0x6a, 0x25, 0x66,
+ 0x34, 0x54, 0x5d, 0x50, 0x26, 0x41, 0x22, 0x4f,
+ 0x34, 0x79, 0x3c, 0x50, 0x68, 0x2d, 0x5f, 0x7b,
+ 0x63, 0x7d, 0x58, 0x2e, 0x73, 0x46, 0x2f, 0x54,
+ 0x61, 0x27, 0x74, 0x45, 0x23, 0x72, 0x31, 0x7d,
+ 0x63, 0x4b, 0x43, 0x5e, 0x44, 0x54, 0x2c, 0x38,
+ 0x58, 0x24, 0x75, 0x6c, 0x50, 0x3c, 0x23, 0x5f,
+ 0x35, 0x57, 0x4f, 0x7b, 0x2f, 0x57, 0x29, 0x73,
+ 0x58, 0x2a, 0x66, 0x3e, 0x49, 0x42, 0x5a, 0x6b,
+ 0x75, 0x6a, 0x38, 0x3f, 0x73, 0x44, 0x42, 0x46,
+ 0x2d, 0x39, 0x66, 0x5b, 0x28, 0x3e, 0x63, 0x62,
+ 0x53, 0x75, 0x65, 0x64, 0x79, 0x32, 0x35, 0x71,
+ 0x22, 0x6a, 0x7b, 0x41, 0x2b, 0x26, 0x43, 0x79,
+ 0x58, 0x6f, 0x71, 0x25, 0x24, 0x34, 0x72, 0x5b,
+ 0x4a, 0x2c, 0x5c, 0x77, 0x23, 0x42, 0x27, 0x6a,
+ 0x67, 0x51, 0x5f, 0x3c, 0x75, 0x2c, 0x3f, 0x43,
+ 0x45, 0x5b, 0x48, 0x65, 0x6f, 0x6c, 0x27, 0x65,
+ 0x21, 0x3e, 0x33, 0x37, 0x5f, 0x2b, 0x2e, 0x24,
+ 0x22, 0x47, 0x4e, 0x33, 0x5b, 0x7b, 0x21, 0x3c,
+ 0x53, 0x69, 0x2e, 0x31, 0x3d, 0x48, 0x57, 0x3a,
+ 0x56, 0x48, 0x6b, 0x47, 0x5d, 0x33, 0x41, 0x6c,
+ 0x66, 0x4c, 0x61, 0x67, 0x32, 0x69, 0x53, 0x2c,
+ 0x2f, 0x3e, 0x36, 0x68, 0x37, 0x28, 0x40, 0x21,
+ 0x76, 0x27, 0x44, 0x26, 0x24, 0x6a, 0x30, 0x75,
+ 0x2a, 0x73, 0x48, 0x36, 0x52, 0x4a, 0x3b, 0x51,
+ 0x4e, 0x2f, 0x23, 0x36, 0x4b, 0x49, 0x33, 0x5a,
+ 0x70, 0x2c, 0x54, 0x5b, 0x67, 0x48, 0x53, 0x5d,
+ 0x21, 0x3e, 0x6b, 0x52, 0x6a, 0x3c, 0x48, 0x29,
+ 0x68, 0x27, 0x32, 0x75, 0x61, 0x7c, 0x51, 0x2e,
+ 0x7b, 0x49, 0x2f, 0x5b, 0x3d, 0x74, 0x5a, 0x28,
+ 0x26, 0x29, 0x2c, 0x30, 0x54, 0x74, 0x45, 0x55,
+ 0x4a, 0x3d, 0x39, 0x35, 0x66, 0x56, 0x28, 0x6d,
+ 0x6e, 0x38, 0x7b, 0x2b, 0x40, 0x31, 0x56, 0x61,
+ 0x74, 0x2b, 0x79, 0x5f, 0x63, 0x51, 0x53, 0x52,
+ 0x7d, 0x73, 0x4e, 0x2e, 0x45, 0x3b, 0x22, 0x28,
+ 0x6c, 0x2b, 0x47, 0x21, 0x50, 0x2a, 0x7c, 0x45,
+ 0x48, 0x57, 0x3e, 0x2f, 0x6d, 0x66, 0x6c, 0x51,
+ 0x23, 0x6c, 0x37, 0x4d, 0x4b, 0x4b, 0x66, 0x55,
+ 0x69, 0x2e, 0x4a, 0x69, 0x71, 0x7c, 0x71, 0x30,
+ 0x5c, 0x43, 0x46, 0x63, 0x5a, 0x23, 0x75, 0x40
+ };
+ uint32_t request6_len = sizeof(request6);
+
+ uint8_t request7[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x5d, 0x32, 0x55, 0x71, 0x51, 0x45, 0x4e, 0x54,
+ 0x34, 0x21, 0x46, 0x77, 0x5e, 0x5b, 0x75, 0x62,
+ 0x2b, 0x5c, 0x34, 0x26, 0x72, 0x2b, 0x2c, 0x64,
+ 0x4b, 0x65, 0x56, 0x72, 0x31, 0x7d, 0x6a, 0x5f,
+ 0x70, 0x26, 0x32, 0x29, 0x7d, 0x21, 0x5b, 0x3e,
+ 0x5e, 0x53, 0x3d, 0x48, 0x5e, 0x2a, 0x4c, 0x37,
+ 0x3d, 0x59, 0x79, 0x21, 0x4f, 0x56, 0x79, 0x2a,
+ 0x4e, 0x28, 0x61, 0x7d, 0x2c, 0x58, 0x2f, 0x78,
+ 0x5c, 0x3f, 0x5c, 0x42, 0x6d, 0x2f, 0x71, 0x54,
+ 0x25, 0x31, 0x73, 0x38, 0x6c, 0x31, 0x5a, 0x2e,
+ 0x42, 0x5b, 0x2d, 0x41, 0x24, 0x4c, 0x37, 0x40,
+ 0x39, 0x7d, 0x2a, 0x67, 0x60, 0x6a, 0x7a, 0x62,
+ 0x24, 0x4e, 0x3f, 0x2e, 0x69, 0x35, 0x28, 0x65,
+ 0x77, 0x53, 0x23, 0x44, 0x59, 0x71, 0x31, 0x5c,
+ 0x40, 0x5d, 0x3a, 0x27, 0x46, 0x55, 0x30, 0x56,
+ 0x21, 0x74, 0x3e, 0x73, 0x41, 0x22, 0x52, 0x68,
+ 0x40, 0x6c, 0x37, 0x3e, 0x62, 0x5a, 0x2e, 0x21,
+ 0x23, 0x33, 0x27, 0x73, 0x68, 0x26, 0x60, 0x67,
+ 0x70, 0x58, 0x50, 0x42, 0x58, 0x27, 0x3a, 0x35,
+ 0x6f, 0x51, 0x62, 0x78, 0x25, 0x2c, 0x7b, 0x66,
+ 0x34, 0x6a, 0x5a, 0x39, 0x60, 0x70, 0x41, 0x2d,
+ 0x65, 0x26, 0x5a, 0x67, 0x58, 0x2d, 0x3e, 0x56,
+ 0x6d, 0x30, 0x4b, 0x4d, 0x5d, 0x45, 0x41, 0x3d,
+ 0x6e, 0x27, 0x4e, 0x5a, 0x7d, 0x2e, 0x62, 0x4d,
+ 0x42, 0x70, 0x31, 0x24, 0x73, 0x5c, 0x78, 0x77,
+ 0x50, 0x73, 0x27, 0x48, 0x3d, 0x35, 0x2c, 0x4b,
+ 0x40, 0x2d, 0x25, 0x77, 0x5d, 0x3d, 0x6b, 0x50,
+ 0x6f, 0x57, 0x73, 0x2f, 0x4f, 0x6e, 0x4c, 0x6e,
+ 0x56, 0x7b, 0x55, 0x3c, 0x6d, 0x60, 0x47, 0x53,
+ 0x56, 0x39, 0x3b, 0x51, 0x61, 0x71, 0x75, 0x73,
+ 0x6b, 0x70, 0x58, 0x5f, 0x2c, 0x27, 0x74, 0x49,
+ 0x2c, 0x2b, 0x53, 0x2d, 0x5b, 0x79, 0x43, 0x34,
+ 0x39, 0x5a, 0x38, 0x3e, 0x2d, 0x66, 0x70, 0x3d,
+ 0x49, 0x51, 0x29, 0x4d, 0x5d, 0x4c, 0x57, 0x4a,
+ 0x2f, 0x41, 0x69, 0x56, 0x57, 0x77, 0x49, 0x58,
+ 0x75, 0x28, 0x29, 0x4a, 0x6d, 0x54, 0x4f, 0x4f,
+ 0x3f, 0x58, 0x5f, 0x58, 0x6f, 0x39, 0x22, 0x4d,
+ 0x5d, 0x31, 0x75, 0x43, 0x2f, 0x7d, 0x31, 0x3d,
+ 0x4c, 0x4d, 0x76, 0x74, 0x4d, 0x57, 0x3b, 0x56,
+ 0x57, 0x48, 0x2b, 0x5d, 0x32, 0x67, 0x51, 0x6e,
+ 0x60, 0x39, 0x6f, 0x64, 0x38, 0x37, 0x52, 0x4b,
+ 0x52, 0x42, 0x32, 0x4f, 0x24, 0x53, 0x31, 0x6e,
+ 0x4a, 0x68, 0x2f, 0x28, 0x2e, 0x27, 0x49, 0x75,
+ 0x77, 0x75, 0x26, 0x47, 0x7c, 0x5d, 0x72, 0x5a,
+ 0x77, 0x50, 0x2e, 0x6c, 0x27, 0x68, 0x6b, 0x7b,
+ 0x27, 0x63, 0x21, 0x3d, 0x30, 0x2d, 0x5c, 0x67,
+ 0x4d, 0x41, 0x79, 0x47, 0x42, 0x50, 0x6d, 0x32,
+ 0x74, 0x39, 0x62, 0x4d, 0x5f, 0x65, 0x78, 0x4f,
+ 0x67, 0x3a, 0x60, 0x26, 0x45, 0x61, 0x7c, 0x61,
+ 0x63, 0x40, 0x46, 0x79, 0x52, 0x47, 0x57, 0x49,
+ 0x53, 0x4c, 0x48, 0x36, 0x67, 0x47, 0x5c, 0x71,
+ 0x50, 0x4d, 0x4f, 0x58, 0x26, 0x40, 0x6d, 0x54,
+ 0x55, 0x67, 0x66, 0x23, 0x70, 0x23, 0x68, 0x70,
+ 0x4d, 0x2c, 0x7a, 0x3d, 0x60, 0x51, 0x35, 0x64,
+ 0x56, 0x2f, 0x26, 0x6d, 0x72, 0x6a, 0x59, 0x34,
+ 0x3a, 0x73, 0x4b, 0x27, 0x33, 0x61, 0x26, 0x45,
+ 0x61, 0x28, 0x74, 0x22, 0x54, 0x50, 0x2e, 0x39,
+ 0x6a, 0x2c, 0x27, 0x59, 0x26, 0x73, 0x44, 0x71,
+ 0x67, 0x4c, 0x37, 0x74, 0x2c, 0x63, 0x52, 0x2a,
+ 0x60, 0x4f, 0x7b, 0x32, 0x39, 0x21, 0x79, 0x54,
+ 0x79, 0x6d, 0x28, 0x27, 0x3a, 0x6a, 0x7d, 0x40,
+ 0x6a, 0x4f, 0x4b, 0x46, 0x61, 0x36, 0x6a, 0x22,
+ 0x3f, 0x77, 0x2d, 0x6a, 0x3b, 0x73, 0x71, 0x72,
+ 0x3c, 0x21, 0x2e, 0x3f, 0x33, 0x25, 0x76, 0x64,
+ 0x64, 0x70, 0x43, 0x32, 0x44, 0x73, 0x61, 0x51,
+ 0x3c, 0x3b, 0x45, 0x3a, 0x68, 0x46, 0x5b, 0x6e,
+ 0x36, 0x47, 0x4d, 0x38, 0x26, 0x4f, 0x5c, 0x7d,
+ 0x73, 0x29, 0x24, 0x78, 0x44, 0x75, 0x40, 0x42,
+ 0x41, 0x2a, 0x73, 0x2b, 0x24, 0x38, 0x51, 0x67,
+ 0x36, 0x67, 0x2f, 0x70, 0x58, 0x54, 0x6e, 0x5d,
+ 0x3b, 0x41, 0x59, 0x76, 0x7d, 0x2d, 0x40, 0x70,
+ 0x29, 0x4a, 0x4a, 0x31, 0x79, 0x2c, 0x4e, 0x22,
+ 0x31, 0x59, 0x31, 0x3c, 0x2f, 0x21, 0x29, 0x3f,
+ 0x65, 0x6c, 0x38, 0x55, 0x4f, 0x27, 0x66, 0x66,
+ 0x34, 0x45, 0x49, 0x41, 0x56, 0x24, 0x2e, 0x40,
+ 0x36, 0x23, 0x5a, 0x46, 0x40, 0x23, 0x7b, 0x2d,
+ 0x69, 0x54, 0x6c, 0x51, 0x58, 0x73, 0x56, 0x60,
+ 0x5f, 0x60, 0x63, 0x5f, 0x77, 0x6a, 0x4c, 0x2c,
+ 0x35, 0x39, 0x60, 0x73, 0x63, 0x3e, 0x2d, 0x55,
+ 0x5a, 0x26, 0x4b, 0x43, 0x3b, 0x56, 0x33, 0x58,
+ 0x74, 0x51, 0x4f, 0x5c, 0x2a, 0x44, 0x78, 0x66,
+ 0x78, 0x71, 0x40, 0x29, 0x5e, 0x26, 0x57, 0x51,
+ 0x49, 0x30, 0x29, 0x73, 0x38, 0x56, 0x6c, 0x41,
+ 0x78, 0x3d, 0x61, 0x3d, 0x2c, 0x33, 0x46, 0x57,
+ 0x54, 0x63, 0x3e, 0x79, 0x55, 0x4a, 0x7d, 0x2e,
+ 0x2a, 0x3c, 0x77, 0x47, 0x35, 0x29, 0x5a, 0x6d,
+ 0x69, 0x48, 0x6b, 0x73, 0x7d, 0x4f, 0x5f, 0x6f,
+ 0x3a, 0x7a, 0x4e, 0x54, 0x59, 0x38, 0x62, 0x44,
+ 0x72, 0x51, 0x57, 0x6a, 0x74, 0x54, 0x4f, 0x77,
+ 0x6b, 0x66, 0x4a, 0x6b, 0x39, 0x29, 0x69, 0x60,
+ 0x71, 0x52, 0x6a, 0x32, 0x66, 0x6c, 0x25, 0x76,
+ 0x27, 0x7a, 0x2c, 0x38, 0x72, 0x4e, 0x5f, 0x40,
+ 0x26, 0x74, 0x6a, 0x5e, 0x42, 0x38, 0x78, 0x34,
+ 0x4f, 0x4f, 0x35, 0x27, 0x39, 0x62, 0x52, 0x61,
+ 0x37, 0x54, 0x47, 0x38, 0x70, 0x31, 0x7a, 0x66,
+ 0x69, 0x72, 0x24, 0x52, 0x2a, 0x2a, 0x78, 0x72,
+ 0x2b, 0x2e, 0x2a, 0x57, 0x4a, 0x21, 0x52, 0x3c,
+ 0x2a, 0x2f, 0x24, 0x58, 0x34, 0x3c, 0x42, 0x5c,
+ 0x5b, 0x78, 0x27, 0x55, 0x63, 0x58, 0x3e, 0x26,
+ 0x50, 0x2c, 0x72, 0x60, 0x36, 0x6c, 0x46, 0x58,
+ 0x63, 0x59, 0x23, 0x2a, 0x2d, 0x63, 0x6a, 0x68,
+ 0x69, 0x74, 0x3f, 0x49, 0x4f, 0x48, 0x4a, 0x3b,
+ 0x59, 0x56, 0x77, 0x43, 0x6d, 0x57, 0x28, 0x5f,
+ 0x39, 0x73, 0x28, 0x74, 0x3c, 0x4f, 0x43, 0x48,
+ 0x6a, 0x57, 0x5d, 0x41, 0x73, 0x3f, 0x41, 0x7c,
+ 0x65, 0x5e, 0x2d, 0x38, 0x72, 0x3a, 0x53, 0x3e,
+ 0x33, 0x47, 0x69, 0x6a, 0x6e, 0x78, 0x67, 0x5d,
+ 0x35, 0x3b, 0x3f, 0x23, 0x7c, 0x71, 0x3d, 0x7c,
+ 0x3a, 0x3c, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x6a, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x6a, 0x40, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x6a, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x6a, 0x40, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x80, 0x23, 0x00, 0xdf, 0xaf, 0xff, 0x33,
+ 0x9b, 0x78, 0x70, 0x43, 0xc5, 0x0a, 0x4d, 0x98,
+ 0x96, 0x02, 0x64, 0x92, 0xc1, 0xee, 0x70, 0x32
+ };
+ uint32_t request7_len = sizeof(request7);
+
+ uint8_t request8[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x65, 0xc1, 0xef, 0x7b, 0xd6, 0xaa, 0xd6, 0x09,
+ 0x21, 0xf6, 0xe7, 0xd1, 0x4c, 0xdf, 0x6a, 0x2d,
+ 0x0a, 0xfb, 0x43, 0xea, 0xda, 0x07, 0x24, 0x84,
+ 0x88, 0x52, 0x9e, 0xa8, 0xa1, 0x7f, 0x4b, 0x60,
+ 0xec, 0x94, 0x57, 0x33, 0x06, 0x93, 0x92, 0x25,
+ 0xd6, 0xac, 0xdc, 0x89, 0x68, 0x5e, 0xbb, 0x32,
+ 0x2b, 0x17, 0x68, 0xf2, 0x06, 0xb7, 0x86, 0xac,
+ 0x81, 0xfe, 0x52, 0x27, 0xf5, 0x80, 0x11, 0x0d,
+ 0x4e, 0x2e, 0x1b, 0xa3, 0x44, 0x8a, 0x58, 0xed,
+ 0xf3, 0x9c, 0xe9, 0x31, 0x01, 0x72, 0xa6, 0xab,
+ 0xfa, 0xa8, 0x05, 0x00, 0x37, 0x60, 0x6b, 0x81,
+ 0xef, 0xf4, 0x96, 0x9a, 0xf7, 0x67, 0x95, 0x27,
+ 0x7a, 0x25, 0xef, 0x6f, 0x0e, 0xff, 0x2d, 0x15,
+ 0x7f, 0x23, 0x1c, 0xa7, 0x56, 0x94, 0x4a, 0x18,
+ 0x98, 0xc6, 0xd8, 0xd2, 0x29, 0x5b, 0x57, 0xb8,
+ 0x5d, 0x3a, 0x93, 0x58, 0x45, 0x77, 0x36, 0xe3,
+ 0xd1, 0x36, 0x87, 0xff, 0xe3, 0x94, 0x0f, 0x00,
+ 0xe6, 0x7c, 0x1a, 0x92, 0xc1, 0x5f, 0x40, 0xc3,
+ 0xa3, 0x25, 0xce, 0xd4, 0xaf, 0x39, 0xeb, 0x17,
+ 0xcf, 0x22, 0x43, 0xd9, 0x0c, 0xce, 0x37, 0x86,
+ 0x46, 0x54, 0xd6, 0xce, 0x00, 0x30, 0x36, 0xae,
+ 0xf9, 0xb5, 0x2b, 0x11, 0xa0, 0xfe, 0xa3, 0x4b,
+ 0x2e, 0x05, 0xbe, 0x54, 0xa9, 0xd8, 0xa5, 0x76,
+ 0x83, 0x5b, 0x63, 0x01, 0x1c, 0xd4, 0x56, 0x72,
+ 0xcd, 0xdc, 0x4a, 0x1d, 0x77, 0xda, 0x8a, 0x9e,
+ 0xba, 0xcb, 0x6c, 0xe8, 0x19, 0x5d, 0x68, 0xef,
+ 0x8e, 0xbc, 0x6a, 0x05, 0x53, 0x0b, 0xc7, 0xc5,
+ 0x96, 0x84, 0x04, 0xd9, 0xda, 0x4c, 0x42, 0x31,
+ 0xd9, 0xbd, 0x99, 0x06, 0xf7, 0xa3, 0x0a, 0x19,
+ 0x49, 0x07, 0x77, 0xf0, 0xdb, 0x7c, 0x43, 0xfa,
+ 0xb2, 0xad, 0xb0, 0xfa, 0x87, 0x52, 0xba, 0xc9,
+ 0x94, 0x61, 0xdc, 0xcf, 0x16, 0xac, 0x0f, 0x4a,
+ 0xa3, 0x6b, 0x5b, 0x6e, 0x27, 0x86, 0x1f, 0xfe,
+ 0x4d, 0x28, 0x3a, 0xa5, 0x10, 0x54, 0x6d, 0xed,
+ 0x53, 0xf9, 0x73, 0xc6, 0x6e, 0xa8, 0xc0, 0x97,
+ 0xcf, 0x56, 0x3b, 0x61, 0xdf, 0xab, 0x83, 0x18,
+ 0xe8, 0x09, 0xee, 0x6a, 0xb7, 0xf5, 0xc9, 0x62,
+ 0x55, 0x2d, 0xc7, 0x0c, 0x0d, 0xa0, 0x22, 0xd8,
+ 0xd4, 0xd6, 0xb2, 0x12, 0x21, 0xd7, 0x73, 0x3e,
+ 0x41, 0xb0, 0x5c, 0xd4, 0xcf, 0x98, 0xf3, 0x70,
+ 0xe6, 0x08, 0xe6, 0x2a, 0x4f, 0x24, 0x85, 0xe8,
+ 0x74, 0xa8, 0x41, 0x5f, 0x0e, 0xfd, 0xf1, 0xf3,
+ 0xbe, 0x9b, 0x14, 0xfd, 0xc0, 0x73, 0x11, 0xff,
+ 0xa5, 0x5b, 0x06, 0x34, 0xc3, 0x6c, 0x28, 0x42,
+ 0x07, 0xfe, 0x8a, 0xa5, 0xbe, 0x72, 0x7a, 0xf7,
+ 0xfa, 0x25, 0xec, 0x35, 0x5e, 0x98, 0x71, 0x50,
+ 0x60, 0x35, 0x76, 0x53, 0x40, 0x1a, 0x34, 0xa5,
+ 0x99, 0x09, 0xa2, 0xc6, 0xca, 0xa5, 0xce, 0x08,
+ 0x50, 0x45, 0xab, 0x8d, 0xfb, 0xe3, 0xb8, 0xe4,
+ 0x8a, 0x61, 0x48, 0x14, 0x6e, 0xf7, 0x58, 0x71,
+ 0xe5, 0x2e, 0xbc, 0x12, 0xd1, 0x25, 0xe9, 0x65,
+ 0x7a, 0xa1, 0x27, 0xbe, 0x3b, 0x8b, 0xe8, 0xe7,
+ 0xbc, 0xe1, 0x05, 0xe7, 0x92, 0xeb, 0xb9, 0xdf,
+ 0x5d, 0x53, 0x74, 0xc0, 0x63, 0x97, 0x80, 0xb8,
+ 0x3c, 0xae, 0xf3, 0xf2, 0x09, 0x12, 0x81, 0x6c,
+ 0x69, 0x10, 0x6f, 0xf6, 0xbe, 0x03, 0x7b, 0x88,
+ 0xcf, 0x26, 0x6b, 0x51, 0x06, 0x23, 0x68, 0x03,
+ 0xa1, 0xb7, 0xd3, 0x0c, 0xca, 0xbf, 0x29, 0x01,
+ 0xa9, 0x61, 0x34, 0x75, 0x98, 0x1e, 0x05, 0x59,
+ 0xb3, 0x46, 0x44, 0xff, 0x2b, 0x98, 0x04, 0x88,
+ 0x89, 0xfd, 0x7f, 0xd5, 0x19, 0x8a, 0xa6, 0xf3,
+ 0xd9, 0x44, 0xd5, 0xf9, 0x3a, 0x3c, 0xec, 0xd9,
+ 0x9b, 0x8c, 0x93, 0x93, 0x2b, 0x44, 0x86, 0x8b,
+ 0x80, 0x83, 0x23, 0x00, 0xdf, 0xaf, 0xff, 0x33,
+ 0x9b, 0x78, 0x70, 0x43, 0xf1, 0x55, 0x87, 0xb1,
+ 0xa1, 0xb3, 0x8e, 0x79, 0x02, 0x70, 0x82, 0x6c,
+ 0x0b, 0xc1, 0xef, 0x96, 0xf1, 0xef, 0xdd, 0xa2,
+ 0x69, 0x86, 0xc7, 0x85, 0x09, 0x7e, 0xf0, 0x2f,
+ 0x8e, 0xa0, 0x5f, 0xea, 0x39, 0x2e, 0x24, 0xf0,
+ 0x82, 0x30, 0x26, 0xa8, 0xa1, 0x4f, 0xc6, 0x5c,
+ 0xec, 0x94, 0x87, 0x52, 0x9b, 0x93, 0x92, 0xf3,
+ 0xa3, 0x1b, 0xc7, 0x8f, 0x9e, 0xb3, 0xbb, 0x32,
+ 0x2b, 0x17, 0x54, 0xf2, 0x06, 0x0c, 0x86, 0x92,
+ 0x0f, 0xb8, 0xe0, 0x27, 0x50, 0xaa, 0xeb, 0xf5,
+ 0x4e, 0x2b, 0x1b, 0xb2, 0x44, 0xe6, 0x58, 0x02,
+ 0xd7, 0x65, 0xdc, 0x31, 0x01, 0xec, 0xa6, 0xab,
+ 0xfa, 0xa8, 0x05, 0x00, 0x37, 0x60, 0x4f, 0xa1,
+ 0x3c, 0x4f, 0x7a, 0x9a, 0x10, 0x67, 0x95, 0xc2,
+ 0x5b, 0x25, 0xef, 0x76, 0x0e, 0xff, 0x2d, 0x15,
+ 0x7f, 0x23, 0x1c, 0x77, 0x56, 0x94, 0x4a, 0x18,
+ 0x98, 0xc6, 0xd8, 0xd2, 0x29, 0x44, 0x57, 0xb8,
+ 0x40, 0x3a, 0x93, 0x58, 0x45, 0x77, 0x36, 0x36,
+ 0x07, 0x35, 0x2a, 0xff, 0x00, 0x94, 0x5c, 0x80,
+ 0xe6, 0x7c, 0x1a, 0x92, 0xc1, 0x5f, 0x40, 0xc3,
+ 0xbc, 0xf8, 0xce, 0x05, 0x77, 0x39, 0x40, 0x17,
+ 0xcf, 0x63, 0x43, 0x77, 0x27, 0xce, 0x37, 0x86,
+ 0x46, 0x54, 0xd6, 0xce, 0x00, 0x30, 0x36, 0xae,
+ 0x9f, 0x24, 0x2b, 0x5a, 0xa0, 0xfe, 0xa3, 0x4b,
+ 0x2e, 0x7e, 0xf7, 0x54, 0xa9, 0xd8, 0xa5, 0x76,
+ 0x83, 0x7b, 0x63, 0x01, 0x1c, 0xd4, 0x56, 0x17,
+ 0x02, 0xdc, 0x4a, 0x89, 0x77, 0xda, 0x8f, 0x9e,
+ 0xba, 0xcb, 0x37, 0xe8, 0x19, 0x5d, 0x68, 0x38,
+ 0x8e, 0xbc, 0x6a, 0x05, 0x53, 0x0b, 0xc7, 0xc5,
+ 0x96, 0x84, 0x5a, 0xd9, 0x6d, 0x4c, 0x42, 0x31,
+ 0xd9, 0xf2, 0x99, 0x06, 0xf7, 0x0c, 0x99, 0xbe,
+ 0x49, 0x07, 0x77, 0xf0, 0x8b, 0x7c, 0x43, 0xfa,
+ 0xb2, 0xad, 0xb0, 0xfa, 0x87, 0x52, 0xba, 0xc9,
+ 0x94, 0x61, 0xdc, 0xcf, 0x16, 0xac, 0x0f, 0x4a,
+ 0xa3, 0x6b, 0x5b, 0x6e, 0x27, 0x86, 0x1f, 0xfe,
+ 0x4d, 0x28, 0x3a, 0xa5, 0x10, 0x98, 0x6d, 0xed,
+ 0x53, 0xf9, 0x73, 0xc6, 0xa5, 0xa8, 0xf7, 0x66,
+ 0xcf, 0x56, 0x3b, 0x61, 0xdf, 0xab, 0x83, 0x18,
+ 0xe8, 0x09, 0xee, 0x6a, 0xb7, 0xf5, 0xc9, 0x62,
+ 0x55, 0x2d, 0xc7, 0x0c, 0x0d, 0xa0, 0x22, 0xd8,
+ 0xd4, 0xd6, 0xb2, 0x12, 0x21, 0xd7, 0x73, 0x3e,
+ 0x41, 0xb0, 0x5c, 0xd4, 0xcf, 0x98, 0xf3, 0x70,
+ 0xe6, 0x08, 0xe6, 0x2a, 0x4f, 0x92, 0x85, 0xe8,
+ 0x74, 0xa8, 0x41, 0x5f, 0x0e, 0xfd, 0xf1, 0xf3,
+ 0xbe, 0x9b, 0x14, 0xfd, 0xc0, 0x73, 0x11, 0xff,
+ 0xa5, 0x5b, 0x06, 0x34, 0xc3, 0x5d, 0x28, 0x42,
+ 0x34, 0xfe, 0x8a, 0xa5, 0xbe, 0x72, 0x7a, 0xf7,
+ 0xfa, 0x25, 0x2b, 0x35, 0x5e, 0x98, 0x71, 0x50,
+ 0x2c, 0x35, 0x76, 0x53, 0x4e, 0x1a, 0x34, 0xa5,
+ 0x99, 0x09, 0xa2, 0xc6, 0xca, 0xa5, 0xce, 0x08,
+ 0x50, 0x45, 0xab, 0x8d, 0xfb, 0xe3, 0xb8, 0xe4,
+ 0x8a, 0x61, 0x48, 0x14, 0x6e, 0xf7, 0x58, 0x71,
+ 0xe5, 0x2e, 0xbc, 0x12, 0xd1, 0x25, 0xe9, 0x65,
+ 0x7a, 0xa1, 0x27, 0xbe, 0x3b, 0x8b, 0xe8, 0xe7,
+ 0xbc, 0x77, 0x05, 0xe7, 0x92, 0xeb, 0xb9, 0xdf,
+ 0x5d, 0x53, 0x74, 0xc0, 0x63, 0x97, 0x80, 0xb8,
+ 0x3c, 0xae, 0xf3, 0xf2, 0x09, 0x12, 0x81, 0x6c,
+ 0x69, 0x10, 0x6f, 0xf6, 0xbe, 0x03, 0x7b, 0x88,
+ 0xcf, 0x26, 0x6b, 0x51, 0x06, 0x23, 0x68, 0x03,
+ 0xa1, 0xb7, 0xd3, 0x0c, 0xca, 0xbf, 0x29, 0x01,
+ 0xa9, 0x61, 0x34, 0x75, 0x98, 0x1e, 0x6f, 0x59,
+ 0xb3, 0x46, 0x44, 0xff, 0x2b, 0x98, 0x04, 0x88,
+ 0x89, 0xfd, 0x1c, 0xd5, 0x19, 0x8a, 0xa6, 0xf3,
+ 0xd9, 0x44, 0xd5, 0xf9, 0x79, 0x26, 0x46, 0xf7
+ };
+ uint32_t request8_len = sizeof(request8);
+
+ uint8_t request9[] = {
+ 0x05, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xbf, 0xa1, 0x12, 0x73, 0x23, 0x44, 0x86, 0x8b,
+ 0x50, 0x6a, 0x40, 0x00
+ };
+ uint32_t request9_len = sizeof(request9);
+
+ TcpSession ssn;
+ Packet *p[11];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|26 d0 cf 80|\"; distance:0; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|43 5b 67 26 65|\"; distance:0; sid:2;)";
+ char *sig3 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|71 69 75 3e|\"; distance:0; sid:3;)";
+ char *sig4 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|6a 68 69 3e 72|\"; distance:0; sid:4;)";
+ char *sig5 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|61 50 71 45 29 5b 56 3d 5a|\"; distance:0; sid:5;)";
+ char *sig6 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|23 75 40 5d 32 55|\"; distance:0; sid:6;)";
+ char *sig7 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|ee 70 32 65 c1|\"; distance:0; sid:7;)";
+ char *sig8 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|79 26 46 f7 bf a1|\"; distance:0; sid:8;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 11; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig3);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig4);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig5);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig6);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig7);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig8);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if ((PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ SCLogDebug("sending request 2");
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if (!(PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 didn't match but should have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+
+ SCLogDebug("sending request 3");
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request3, request3_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SCLogDebug("inspecting packet 4");
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[4]);
+ if ((PacketAlertCheck(p[4], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[4], 2))) {
+ printf("sid 2 didn't match but should have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[4], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 4: ");
+ goto end;
+ }
+
+ SCLogDebug("sending request 4");
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request4, request4_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[5]);
+ if ((PacketAlertCheck(p[5], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[5], 3))) {
+ printf("sid 3 didn't match but should have packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[5], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request5, request5_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[6]);
+ if ((PacketAlertCheck(p[6], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[6], 4))) {
+ printf("sid 4 didn't match but should have packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[6], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request6, request6_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[7]);
+ if ((PacketAlertCheck(p[7], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[7], 5))) {
+ printf("sid 5 didn't match but should have paket 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[7], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request7, request7_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[8]);
+ if ((PacketAlertCheck(p[8], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[8], 6))) {
+ printf("sid 6 didn't match but should have paket 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[8], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 8: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request8, request8_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[9]);
+ if ((PacketAlertCheck(p[9], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[9], 7))) {
+ printf("sid 7 didn't match but should have for packet 9: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[9], 8))) {
+ printf("sid 8 matched but shouldn't have for packet 9: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request9, request9_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[10]);
+ if ((PacketAlertCheck(p[10], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 3))) {
+ printf("sid 3 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 4))) {
+ printf("sid 4 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 5))) {
+ printf("sid 5 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 6))) {
+ printf("sid 6 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[10], 7))) {
+ printf("sid 7 matched but shouldn't have for packet 10: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[10], 8))) {
+ printf("sid 8 didn't match but should have for paket 10: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 11);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest02(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "dce_stub_data; content:\"|2d 5e 63 2a 4c|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[1]->flowflags &=~ FLOW_PKT_TOSERVER;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (PacketAlertCheck(p[0], 1)) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p[0], 2)) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if (!(PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 didn't match but should have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 didn't match but should have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest03(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef4; "
+ "dce_stub_data; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|2d 5e 63 2a 4c|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if ((PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 didn't match but should have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest04(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; content:\"|91 27 27 40|\"; distance:0; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|2d 5e 63 2a 4c|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if (!(PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 didn't match but should have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 didn't match but should have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest05(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef4; "
+ "dce_stub_data; content:\"|91 27 27 40|\"; distance:0; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "dce_stub_data; content:\"|2d 5e 63 2a 4c|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if ((PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 2 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 didn't match but should have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest06(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; content:\"|91 27 27 30|\"; distance:0; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|2d 5e 63 2a 4c|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if ((PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 didn't match but should have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest07(void)
+{
+#if 0
+ int result = 0;
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x6a, 0x28, 0x19, 0x39, 0x0c, 0xb1, 0xd0, 0x11,
+ 0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x48, 0x1a, 0x00, 0x00,
+ 0x0c, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6c, 0x73, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ Packet *p[4];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; content:\"|91 27 27 30|\"; distance:0; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any (dce_stub_data; "
+ "content:\"|2d 5e 63 35 25|\"; distance:0; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 4; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ s->next = SigInit(de_ctx, sig2);
+ s = s->next;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[0], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[1], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if ((PacketAlertCheck(p[2], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[2], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if ((PacketAlertCheck(p[3], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p[3], 2))) {
+ printf("sid 2 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 4);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Positive test, to test the working of distance and within.
+ */
+int DcePayloadTest08(void)
+{
+#if 0
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p[1];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5d 5b 35|\"; distance:0; content:\"|9e a3|\"; "
+ "distance:0; within:2; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 1; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (!(PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 1);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Positive test, to test the working of distance and within.
+ */
+int DcePayloadTest09(void)
+{
+#if 0
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x5d, 0x5b, 0x35, 0x46, 0x9e, 0xa3,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p[1];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5d 5b 35|\"; distance:0; content:\"|9e a3|\"; "
+ "distance:0; within:2; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 1; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (!(PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 1);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Positive test, to test the working of distance and within.
+ */
+int DcePayloadTest10(void)
+{
+#if 0
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x5d, 0x5b, 0x35, 0x46, 0x9e, 0xa3,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p[1];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|ad 0d|\"; distance:0; content:\"|ad 0d 00|\"; "
+ "distance:-10; within:3; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 1; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (!(PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 1);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Postive test to check the working of disance and within across frags.
+ */
+int DcePayloadTest11(void)
+{
+#if 0
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ Packet *p[2];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|af, 26, d0|\"; distance:0; content:\"|80 98 6d|\"; "
+ "distance:1; within:3; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 2; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if (!(PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 didn't match but should have for pacekt 1: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 2);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Negative test the working of contents on stub data with invalid
+ * distance.
+ */
+int DcePayloadTest12(void)
+{
+#if 0 /* payload ticks off clamav */
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x0d, 0x00, 0x00, 0x91, 0xfc, 0x27, 0x40,
+ 0x4a, 0x97, 0x4a, 0x98, 0x4b, 0x41, 0x3f, 0x48,
+ 0x99, 0x90, 0xf8, 0x27, 0xfd, 0x3f, 0x27, 0x37,
+ 0x40, 0xd6, 0x27, 0xfc, 0x3f, 0x9f, 0x4f, 0xfd,
+ 0x42, 0x47, 0x47, 0x49, 0x3f, 0xf9, 0x9b, 0xd6,
+ 0x48, 0x37, 0x27, 0x46, 0x93, 0x49, 0xfd, 0x93,
+ 0x91, 0xfd, 0x93, 0x90, 0x92, 0x96, 0xf5, 0x92,
+ 0x4e, 0x91, 0x98, 0x46, 0x4f, 0x4b, 0x46, 0xf5,
+ 0xf5, 0xfd, 0x40, 0xf9, 0x9b, 0x40, 0x9f, 0x93,
+ 0x4e, 0xf8, 0x40, 0x40, 0x4e, 0xf5, 0x4b, 0x98,
+ 0xf5, 0x91, 0xd6, 0x42, 0x99, 0x96, 0x27, 0x49,
+ 0x48, 0x47, 0x4f, 0x46, 0x99, 0x4b, 0x92, 0x92,
+ 0x90, 0x47, 0x46, 0x4e, 0x43, 0x9b, 0x43, 0x42,
+ 0x3f, 0x4b, 0x27, 0x97, 0x93, 0xf9, 0x42, 0x9b,
+ 0x46, 0x9b, 0x4b, 0x98, 0x41, 0x98, 0x37, 0x41,
+ 0x9f, 0x98, 0x4e, 0x93, 0x48, 0x46, 0x46, 0x9f,
+ 0x97, 0x9b, 0x42, 0x37, 0x90, 0x46, 0xf9, 0x97,
+ 0x91, 0xf5, 0x4e, 0x97, 0x4e, 0x99, 0xf8, 0x99,
+ 0x41, 0xf5, 0x41, 0x9f, 0x49, 0xfd, 0x92, 0x96,
+ 0x3f, 0x3f, 0x42, 0x27, 0x27, 0x93, 0x47, 0x49,
+ 0x91, 0x27, 0x27, 0x40, 0x42, 0x99, 0x9f, 0xfc,
+ 0x97, 0x47, 0x99, 0x4a, 0xf9, 0x3f, 0x48, 0x91,
+ 0x47, 0x97, 0x91, 0x42, 0x4b, 0x9b, 0x4a, 0x48,
+ 0x9f, 0x43, 0x43, 0x40, 0x99, 0xf9, 0x48, 0x4e,
+ 0x92, 0x93, 0x92, 0x41, 0x46, 0x4b, 0x4a, 0x4a,
+ 0x49, 0x96, 0x4a, 0x4f, 0xf5, 0x42, 0x47, 0x98,
+ 0x9b, 0xf5, 0x91, 0xf9, 0xd6, 0x9b, 0x48, 0x4e,
+ 0x9f, 0x91, 0xd6, 0x93, 0x4b, 0x37, 0x3f, 0x43,
+ 0xf5, 0x41, 0x41, 0xf5, 0x37, 0x4f, 0x43, 0x92,
+ 0x97, 0x27, 0x93, 0x92, 0x46, 0x47, 0x4b, 0x96,
+ 0x41, 0x90, 0x90, 0x3f, 0x96, 0x27, 0x41, 0xd6,
+ 0xd6, 0xd6, 0xf9, 0xf8, 0x47, 0x27, 0x46, 0x37,
+ 0x41, 0x90, 0x91, 0xfc, 0x46, 0x41, 0x43, 0x97,
+ 0x9f, 0x4a, 0x49, 0x92, 0x41, 0x91, 0x41, 0x92,
+ 0x42, 0x4a, 0x3f, 0x93, 0x99, 0x9b, 0x9f, 0x4e,
+ 0x47, 0x93, 0xd6, 0x37, 0x37, 0x40, 0x98, 0xfd,
+ 0x41, 0x42, 0x97, 0x4e, 0x4e, 0x98, 0x9f, 0x4e,
+ 0x48, 0x3f, 0x48, 0x42, 0x96, 0x9f, 0x99, 0x4f,
+ 0x4e, 0x42, 0x97, 0xf9, 0x3f, 0x37, 0x27, 0x46,
+ 0x41, 0xf9, 0x92, 0x96, 0x41, 0x93, 0x91, 0x4b,
+ 0x96, 0x4f, 0x43, 0xfd, 0xf5, 0x9f, 0x43, 0x27,
+ 0x99, 0xd6, 0xf5, 0x4e, 0xfd, 0x97, 0x4b, 0x47,
+ 0x47, 0x92, 0x98, 0x4f, 0x47, 0x49, 0x37, 0x97,
+ 0x3f, 0x4e, 0x40, 0x46, 0x4e, 0x9f, 0x4e, 0x4e,
+ 0xfc, 0x41, 0x47, 0xf8, 0x37, 0x9b, 0x41, 0x4e,
+ 0x96, 0x99, 0x46, 0x99, 0x46, 0xf9, 0x4e, 0x4f,
+ 0x48, 0x97, 0x97, 0x93, 0xd6, 0x9b, 0x41, 0x40,
+ 0x97, 0x97, 0x4f, 0x92, 0x91, 0xd6, 0x96, 0x40,
+ 0x4f, 0x4b, 0x91, 0x46, 0x27, 0x92, 0x3f, 0xf5,
+ 0xfc, 0x3f, 0x91, 0x97, 0xf8, 0x43, 0x4e, 0xfd,
+ 0x9b, 0x27, 0xfd, 0x9b, 0xf5, 0x27, 0x47, 0x42,
+ 0x46, 0x93, 0x37, 0x93, 0x91, 0x91, 0x91, 0xf8,
+ 0x4f, 0x92, 0x4f, 0xf8, 0x93, 0xf5, 0x49, 0x91,
+ 0x4b, 0x3f, 0xfc, 0x37, 0x4f, 0x46, 0x98, 0x97,
+ 0x9f, 0x40, 0xfd, 0x9f, 0x98, 0xfd, 0x4e, 0x97,
+ 0x4f, 0x47, 0x91, 0x27, 0x4a, 0x90, 0x96, 0x40,
+ 0x98, 0x97, 0x41, 0x3f, 0xd6, 0xfd, 0x41, 0xfd,
+ 0x42, 0x97, 0x4b, 0x9b, 0x46, 0x4e, 0xfc, 0x96,
+ 0xf9, 0x37, 0x4b, 0x96, 0x9f, 0x9b, 0x42, 0x9f,
+ 0x93, 0x40, 0x42, 0x43, 0xf5, 0x93, 0x48, 0x3f,
+ 0x4b, 0xfd, 0x9f, 0x4b, 0x41, 0x4a, 0x90, 0x9b,
+ 0x46, 0x97, 0x98, 0x96, 0x9b, 0x98, 0x92, 0xd6,
+ 0x4e, 0x4a, 0x27, 0x90, 0x96, 0x99, 0x91, 0x46,
+ 0x49, 0x41, 0x4b, 0x90, 0x43, 0x91, 0xd6, 0x48,
+ 0x42, 0x90, 0x4f, 0x96, 0x43, 0x9b, 0xf9, 0x9b,
+ 0x9f, 0x9f, 0x27, 0x47, 0x4b, 0xf5, 0x43, 0x99,
+ 0x99, 0x91, 0x4e, 0x41, 0x42, 0x46, 0x97, 0x46,
+ 0x47, 0xf9, 0xf5, 0x48, 0x4a, 0xf8, 0x4e, 0xd6,
+ 0x43, 0x4a, 0x27, 0x9b, 0x42, 0x90, 0x46, 0x46,
+ 0x3f, 0x99, 0x96, 0x9b, 0x91, 0x9f, 0xf5, 0x48,
+ 0x43, 0x9f, 0x4a, 0x99, 0x96, 0xfd, 0x92, 0x49,
+ 0x46, 0x91, 0x40, 0xfd, 0x4a, 0x48, 0x4f, 0x90,
+ 0x91, 0x98, 0x48, 0x4b, 0x9f, 0x42, 0x27, 0x93,
+ 0x47, 0xf8, 0x4f, 0x48, 0x3f, 0x90, 0x47, 0x41,
+ 0xf5, 0xfc, 0x27, 0xf8, 0x97, 0x4a, 0x49, 0x37,
+ 0x40, 0x4f, 0x40, 0x37, 0x41, 0x27, 0x96, 0x37,
+ 0xfc, 0x42, 0xd6, 0x4b, 0x48, 0x37, 0x42, 0xf5,
+ 0x27, 0xf9, 0xd6, 0x48, 0x9b, 0xfd, 0x40, 0x96,
+ 0x4e, 0x43, 0xf8, 0x90, 0x40, 0x40, 0x49, 0x3f,
+ 0xfc, 0x4a, 0x42, 0x47, 0xf8, 0x49, 0x42, 0x97,
+ 0x4f, 0x91, 0xfd, 0x4b, 0x46, 0x4b, 0xfc, 0x48,
+ 0x49, 0x96, 0x4b, 0x96, 0x43, 0x9f, 0x90, 0x37,
+ 0xd6, 0x4a, 0xd6, 0x3f, 0xd6, 0x90, 0x49, 0x27,
+ 0x4e, 0x96, 0x96, 0xf8, 0x49, 0x96, 0xf8, 0x37,
+ 0x90, 0x4e, 0x4b, 0x4f, 0x99, 0xf8, 0x6a, 0x52,
+ 0x59, 0xd9, 0xee, 0xd9, 0x74, 0x24, 0xf4, 0x5b,
+ 0x81, 0x73, 0x13, 0x30, 0x50, 0xf0, 0x82, 0x83,
+ 0xeb, 0xfc, 0xe2, 0xf4, 0xb1, 0x94, 0x0f, 0x6d,
+ 0xcf, 0xaf, 0xb4, 0x7e, 0x5a, 0xbb, 0xbf, 0x6a,
+ 0xc9, 0xaf, 0x0f, 0x7d, 0x50, 0xdb, 0x9c, 0xa6,
+ 0x14, 0xdb, 0xb5, 0xbe, 0xbb, 0x2c, 0xf5, 0xfa,
+ 0x31, 0xbf, 0x7b, 0xcd, 0x28, 0xdb, 0xaf, 0xa2,
+ 0x31, 0xbb, 0x13, 0xb2, 0x79, 0xdb, 0xc4, 0x09,
+ 0x31, 0xbe, 0xc1, 0x42, 0xa9, 0xfc, 0x74, 0x42,
+ 0x44, 0x57, 0x31, 0x48, 0x3d, 0x51, 0x32, 0x69,
+ 0xc4, 0x6b, 0xa4, 0xa6, 0x18, 0x25, 0x13, 0x09,
+ 0x6f, 0x74, 0xf1, 0x69, 0x56, 0xdb, 0xfc, 0xc9,
+ 0xbb, 0x0f, 0xec, 0x83, 0xdb, 0x53, 0xdc, 0x09,
+ 0xb9, 0x3c, 0xd4, 0x9e, 0x51, 0x93, 0xc1, 0x42,
+ 0x54, 0xdb, 0xb0, 0xb2, 0xbb, 0x10, 0xfc, 0x09,
+ 0x40, 0x4c, 0x5d, 0x09, 0x70, 0x58, 0xae, 0xea,
+ 0xbe, 0x1e, 0xfe, 0x6e, 0x60, 0xaf, 0x26, 0xb3,
+ 0xeb, 0x36, 0xa3, 0xe4, 0x58, 0x63, 0xc2, 0xea,
+ 0x47, 0x23, 0xc2, 0xdd, 0x64, 0xaf, 0x20, 0xea,
+ 0xfb, 0xbd, 0x0c, 0xb9, 0x60, 0xaf, 0x26, 0xdd,
+ 0xb9, 0xb5, 0x96, 0x03, 0xdd, 0x58, 0xf2, 0xd7,
+ 0x5a, 0x52, 0x0f, 0x52, 0x58, 0x89, 0xf9, 0x77,
+ 0x9d, 0x07, 0x0f, 0x54, 0x63, 0x03, 0xa3, 0xd1,
+ 0x63, 0x13, 0xa3, 0xc1, 0x63, 0xaf, 0x20, 0xe4,
+ 0x58, 0x41, 0xac, 0xe4, 0x63, 0xd9, 0x11, 0x17,
+ 0x58, 0xf4, 0xea, 0xf2, 0xf7, 0x07, 0x0f, 0x54,
+ 0x5a, 0x40, 0xa1, 0xd7, 0xcf, 0x80, 0x98, 0x26,
+ 0x9d, 0x7e, 0x19, 0xd5, 0xcf, 0x86, 0xa3, 0xd7,
+ 0xcf, 0x80, 0x98, 0x67, 0x79, 0xd6, 0xb9, 0xd5,
+ 0xcf, 0x86, 0xa0, 0xd6, 0x64, 0x05, 0x0f, 0x52,
+ 0xa3, 0x38, 0x17, 0xfb, 0xf6, 0x29, 0xa7, 0x7d,
+ 0xe6, 0x05, 0x0f, 0x52, 0x56, 0x3a, 0x94, 0xe4,
+ 0x58, 0x33, 0x9d, 0x0b, 0xd5, 0x3a, 0xa0, 0xdb,
+ 0x19, 0x9c, 0x79, 0x65, 0x5a, 0x14, 0x79, 0x60,
+ 0x01, 0x90, 0x03, 0x28, 0xce, 0x12, 0xdd, 0x7c,
+ 0x72, 0x7c, 0x63, 0x0f, 0x4a, 0x68, 0x5b, 0x29,
+ 0x9b, 0x38, 0x82, 0x7c, 0x83, 0x46, 0x0f, 0xf7,
+ 0x74, 0xaf, 0x26, 0xd9, 0x67, 0x02, 0xa1, 0xd3,
+ 0x61, 0x3a, 0xf1, 0xd3, 0x61, 0x05, 0xa1, 0x7d,
+ 0xe0, 0x38, 0x5d, 0x5b, 0x35, 0x9e, 0xa3, 0x7d,
+ 0xe6, 0x3a, 0x0f, 0x7d, 0x07, 0xaf, 0x20, 0x09,
+ 0x67, 0xac, 0x73, 0x46, 0x54, 0xaf, 0x26, 0xd0
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0xcf, 0x80, 0x98, 0x6d, 0xfe, 0xb0, 0x90, 0xd1,
+ 0xcf, 0x86, 0x0f, 0x52, 0x2c, 0x23, 0x66, 0x28,
+ 0x27, 0x30, 0x48, 0x55, 0x42, 0x6a, 0x48, 0x4b,
+ 0x68, 0x22, 0x2e, 0x23, 0x64, 0x33, 0x2c, 0x2d,
+ 0x5c, 0x51, 0x48, 0x55, 0x24, 0x67, 0x6c, 0x4c,
+ 0x45, 0x71, 0x35, 0x72, 0x5a, 0x48, 0x5e, 0x35,
+ 0x61, 0x78, 0x35, 0x42, 0x2c, 0x7a, 0x75, 0x61,
+ 0x5b, 0x4e, 0x76, 0x30, 0x26, 0x2f, 0x2a, 0x34,
+ 0x48, 0x29, 0x25, 0x6e, 0x5c, 0x3a, 0x6c, 0x3e,
+ 0x79, 0x4e, 0x2a, 0x21, 0x6f, 0x6f, 0x34, 0x46,
+ 0x43, 0x26, 0x5b, 0x35, 0x78, 0x27, 0x69, 0x23,
+ 0x72, 0x21, 0x69, 0x56, 0x6a, 0x7d, 0x4b, 0x5e,
+ 0x65, 0x37, 0x60, 0x44, 0x7c, 0x5d, 0x5b, 0x72,
+ 0x7d, 0x73, 0x7b, 0x47, 0x57, 0x21, 0x41, 0x38,
+ 0x76, 0x38, 0x76, 0x5c, 0x58, 0x32, 0x4a, 0x37,
+ 0x2f, 0x40, 0x4b, 0x4c, 0x3d, 0x41, 0x33, 0x56,
+ 0x73, 0x38, 0x61, 0x71, 0x24, 0x49, 0x4c, 0x4a,
+ 0x44, 0x2e, 0x3a, 0x3f, 0x74, 0x54, 0x4c, 0x65,
+ 0x54, 0x2d, 0x3b, 0x28, 0x41, 0x45, 0x49, 0x2c,
+ 0x6e, 0x48, 0x44, 0x43, 0x37, 0x3d, 0x7b, 0x6d,
+ 0x2b, 0x4b, 0x32, 0x5a, 0x31, 0x61, 0x6e, 0x2b,
+ 0x27, 0x50, 0x6b, 0x66, 0x76, 0x4e, 0x55, 0x35,
+ 0x2b, 0x72, 0x2d, 0x5e, 0x42, 0x3e, 0x5a, 0x5d,
+ 0x36, 0x45, 0x32, 0x3a, 0x58, 0x78, 0x78, 0x3e,
+ 0x60, 0x6c, 0x5d, 0x63, 0x41, 0x7c, 0x52, 0x21,
+ 0x75, 0x6a, 0x5a, 0x70, 0x55, 0x45, 0x76, 0x58,
+ 0x33, 0x40, 0x38, 0x39, 0x21, 0x37, 0x7d, 0x77,
+ 0x21, 0x70, 0x2b, 0x72, 0x29, 0x6a, 0x31, 0x5f,
+ 0x38, 0x4a, 0x66, 0x65, 0x62, 0x2c, 0x39, 0x52,
+ 0x5f, 0x2a, 0x2b, 0x63, 0x4f, 0x76, 0x43, 0x25,
+ 0x6a, 0x50, 0x37, 0x52, 0x5e, 0x23, 0x3c, 0x42,
+ 0x28, 0x75, 0x75, 0x42, 0x25, 0x23, 0x28, 0x56,
+ 0x6c, 0x46, 0x5c, 0x5e, 0x6b, 0x7d, 0x48, 0x24,
+ 0x77, 0x6c, 0x70, 0x62, 0x2e, 0x28, 0x7d, 0x6b,
+ 0x69, 0x4a, 0x75, 0x3d, 0x5d, 0x56, 0x21, 0x49,
+ 0x56, 0x47, 0x64, 0x2b, 0x4c, 0x52, 0x43, 0x60,
+ 0x77, 0x49, 0x46, 0x46, 0x33, 0x2c, 0x4b, 0x4b,
+ 0x3d, 0x63, 0x5d, 0x33, 0x78, 0x76, 0x51, 0x56,
+ 0x77, 0x3c, 0x72, 0x74, 0x52, 0x27, 0x40, 0x6c,
+ 0x42, 0x79, 0x49, 0x24, 0x62, 0x5e, 0x26, 0x31,
+ 0x5c, 0x22, 0x2b, 0x4c, 0x64, 0x49, 0x52, 0x45,
+ 0x47, 0x49, 0x3a, 0x2a, 0x51, 0x71, 0x22, 0x22,
+ 0x70, 0x24, 0x34, 0x67, 0x4b, 0x6d, 0x58, 0x29,
+ 0x63, 0x26, 0x7b, 0x6f, 0x38, 0x78, 0x25, 0x62,
+ 0x4d, 0x3a, 0x7d, 0x40, 0x23, 0x57, 0x67, 0x33,
+ 0x38, 0x31, 0x4e, 0x54, 0x3c, 0x4b, 0x48, 0x69,
+ 0x3c, 0x39, 0x31, 0x2b, 0x26, 0x70, 0x44, 0x66,
+ 0x4a, 0x37, 0x2b, 0x75, 0x36, 0x45, 0x59, 0x34,
+ 0x3e, 0x3e, 0x29, 0x70, 0x71, 0x5a, 0x55, 0x49,
+ 0x3e, 0x4b, 0x68, 0x4e, 0x75, 0x70, 0x3c, 0x5c,
+ 0x50, 0x58, 0x28, 0x75, 0x3c, 0x2a, 0x41, 0x70,
+ 0x2f, 0x2b, 0x37, 0x26, 0x75, 0x71, 0x55, 0x22,
+ 0x3a, 0x44, 0x30, 0x48, 0x5d, 0x2f, 0x6c, 0x44,
+ 0x28, 0x4b, 0x34, 0x45, 0x21, 0x60, 0x44, 0x36,
+ 0x7b, 0x32, 0x39, 0x5f, 0x6d, 0x3f, 0x68, 0x73,
+ 0x25, 0x45, 0x56, 0x7c, 0x78, 0x7a, 0x49, 0x6a,
+ 0x46, 0x3d, 0x2d, 0x33, 0x6c, 0x6f, 0x23, 0x77,
+ 0x38, 0x33, 0x36, 0x74, 0x7b, 0x57, 0x4b, 0x6d,
+ 0x27, 0x75, 0x24, 0x6e, 0x43, 0x61, 0x4d, 0x44,
+ 0x6d, 0x27, 0x48, 0x58, 0x5e, 0x7b, 0x26, 0x6a,
+ 0x50, 0x7c, 0x51, 0x23, 0x3c, 0x4f, 0x37, 0x4c,
+ 0x47, 0x3e, 0x45, 0x56, 0x22, 0x33, 0x7c, 0x66,
+ 0x35, 0x54, 0x7a, 0x6e, 0x5a, 0x24, 0x70, 0x62,
+ 0x29, 0x3f, 0x69, 0x79, 0x24, 0x43, 0x41, 0x24,
+ 0x65, 0x25, 0x62, 0x4f, 0x73, 0x3e, 0x2b, 0x36,
+ 0x46, 0x69, 0x27, 0x55, 0x2a, 0x6e, 0x24, 0x6c,
+ 0x7d, 0x64, 0x7c, 0x61, 0x26, 0x67, 0x2a, 0x53,
+ 0x73, 0x60, 0x28, 0x2d, 0x6b, 0x44, 0x54, 0x61,
+ 0x34, 0x53, 0x22, 0x59, 0x6d, 0x73, 0x56, 0x55,
+ 0x25, 0x2c, 0x38, 0x4a, 0x3b, 0x4e, 0x78, 0x46,
+ 0x54, 0x6e, 0x6d, 0x4f, 0x47, 0x4f, 0x4f, 0x5a,
+ 0x67, 0x77, 0x39, 0x66, 0x28, 0x29, 0x4e, 0x43,
+ 0x55, 0x6e, 0x60, 0x59, 0x28, 0x3b, 0x65, 0x62,
+ 0x61, 0x5a, 0x29, 0x6e, 0x79, 0x60, 0x41, 0x53,
+ 0x2f, 0x5d, 0x44, 0x36, 0x7b, 0x3e, 0x7c, 0x2b,
+ 0x77, 0x36, 0x70, 0x3f, 0x40, 0x55, 0x48, 0x67,
+ 0x4b, 0x4d, 0x5d, 0x51, 0x79, 0x76, 0x48, 0x4a,
+ 0x2d, 0x21, 0x60, 0x40, 0x46, 0x55, 0x7a, 0x60,
+ 0x22, 0x25, 0x3f, 0x4b, 0x54, 0x6a, 0x6a, 0x3c,
+ 0x77, 0x22, 0x5b, 0x43, 0x67, 0x58, 0x71, 0x22,
+ 0x79, 0x4b, 0x32, 0x61, 0x44, 0x4d, 0x6f, 0x42,
+ 0x33, 0x2d, 0x53, 0x35, 0x3d, 0x6f, 0x57, 0x48,
+ 0x33, 0x3b, 0x5a, 0x53, 0x3f, 0x4e, 0x3f, 0x6b,
+ 0x4c, 0x27, 0x26, 0x3b, 0x73, 0x49, 0x22, 0x55,
+ 0x79, 0x2f, 0x47, 0x2f, 0x55, 0x5a, 0x7a, 0x71,
+ 0x6c, 0x31, 0x43, 0x40, 0x56, 0x7b, 0x21, 0x7a,
+ 0x6d, 0x4c, 0x43, 0x5e, 0x38, 0x47, 0x29, 0x38,
+ 0x62, 0x49, 0x45, 0x78, 0x70, 0x2b, 0x2e, 0x65,
+ 0x47, 0x71, 0x58, 0x79, 0x39, 0x67, 0x7d, 0x6d,
+ 0x6a, 0x67, 0x4a, 0x71, 0x27, 0x35, 0x2a, 0x4c,
+ 0x3e, 0x58, 0x55, 0x30, 0x4d, 0x75, 0x77, 0x48,
+ 0x5f, 0x4b, 0x59, 0x34, 0x65, 0x68, 0x57, 0x59,
+ 0x63, 0x23, 0x47, 0x38, 0x47, 0x5e, 0x56, 0x28,
+ 0x79, 0x58, 0x3e, 0x39, 0x66, 0x77, 0x67, 0x33,
+ 0x29, 0x61, 0x24, 0x7d, 0x37, 0x44, 0x37, 0x67,
+ 0x3a, 0x58, 0x76, 0x21, 0x51, 0x59, 0x61, 0x73,
+ 0x66, 0x75, 0x71, 0x53, 0x4d, 0x24, 0x2d, 0x4b,
+ 0x29, 0x30, 0x32, 0x26, 0x59, 0x64, 0x27, 0x55,
+ 0x2c, 0x5a, 0x4c, 0x3c, 0x6c, 0x53, 0x56, 0x4b,
+ 0x3e, 0x55, 0x2e, 0x44, 0x38, 0x6b, 0x47, 0x76,
+ 0x2d, 0x2c, 0x3f, 0x4d, 0x22, 0x7b, 0x6d, 0x61,
+ 0x34, 0x6b, 0x50, 0x73, 0x28, 0x6d, 0x41, 0x71,
+ 0x21, 0x76, 0x52, 0x2a, 0x6d, 0x53, 0x2a, 0x74,
+ 0x28, 0x27, 0x62, 0x2a, 0x66, 0x25, 0x6e, 0x5e,
+ 0x37, 0x4f, 0x27, 0x72, 0x28, 0x47, 0x63, 0x6e,
+ 0x5a, 0x6a, 0x41, 0x35, 0x3a, 0x42, 0x3f, 0x27,
+ 0x75, 0x3e, 0x26, 0x3e, 0x6b, 0x55, 0x59, 0x60,
+ 0x24, 0x70, 0x49, 0x3c, 0x4e, 0x2c, 0x39, 0x7a,
+ 0x36, 0x6c, 0x27, 0x3e, 0x6a, 0x4a, 0x59, 0x5a,
+ 0x3e, 0x21, 0x73, 0x4e, 0x59, 0x6e, 0x3d, 0x32,
+ 0x27, 0x45, 0x49, 0x58, 0x7d, 0x37, 0x39, 0x77,
+ 0x28, 0x51, 0x79, 0x54, 0x2b, 0x78, 0x46, 0x5a,
+ 0x21, 0x75, 0x33, 0x21, 0x63, 0x5a, 0x7b, 0x3e,
+ 0x33, 0x4f, 0x67, 0x75, 0x3a, 0x50, 0x48, 0x60,
+ 0x26, 0x64, 0x76, 0x5c, 0x42, 0x5c, 0x72, 0x38,
+ 0x6c, 0x52, 0x21, 0x2b, 0x25, 0x6b, 0x7c, 0x6b,
+ 0x2d, 0x5e, 0x63, 0x2a, 0x4c, 0x26, 0x5b, 0x4c,
+ 0x58, 0x52, 0x51, 0x55, 0x31, 0x79, 0x6c, 0x53,
+ 0x62, 0x3a, 0x36, 0x46, 0x7a, 0x29, 0x27, 0x78,
+ 0x1a, 0xbf, 0x49, 0x74, 0x68, 0x24, 0x51, 0x44,
+ 0x5b, 0x3e, 0x34, 0x44, 0x29, 0x5e, 0x4f, 0x2a,
+ 0xe9, 0x3f, 0xf8, 0xff, 0xff, 0x52, 0x7d, 0x47,
+ 0x67, 0x40, 0x27, 0x5e, 0x47, 0x46, 0x6d, 0x72,
+ 0x5d, 0x49, 0x26, 0x45, 0x33, 0x6b, 0x4d, 0x4a,
+ 0x6f, 0x62, 0x60, 0x45, 0x62, 0x27, 0x27, 0x7d,
+ 0x6a, 0x41, 0x2c, 0x6c, 0x5b, 0x2a, 0x2b, 0x36,
+ 0x29, 0x58, 0x7a, 0x4c, 0x6e, 0x2d, 0x74, 0x5c,
+ 0x38, 0x22, 0x5f, 0x49, 0x63, 0x43, 0x5b, 0x67
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ TcpSession ssn;
+ Packet *p[2];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|af, 26, d0|\"; distance:0; content:\"|80 98 6d|\"; "
+ "distance:2; within:3; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 2; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if ((PacketAlertCheck(p[0], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 0: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if ((PacketAlertCheck(p[1], 1))) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 2);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest13(void)
+{
+ int result = 0;
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x2c, 0xfd, 0xb5, 0x00, 0x40, 0xaa, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t response1[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+ uint32_t response1_len = sizeof(response1);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x5c, 0x00, 0x5c, 0x00,
+ 0xa8, 0xb9, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x54, 0x00,
+ 0x57, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00,
+ 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00,
+ 0x66, 0x00, 0x74, 0x00, 0x5c, 0x00, 0x57, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+ 0x77, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x43, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x5c, 0x00, 0x52, 0x00, 0x75, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x00, 0x00, 0x00, 0x00,
+ };
+ uint32_t response2_len = sizeof(response2);
+
+ uint8_t request3[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf7, 0x72, 0x28, 0x9c,
+ 0xf0, 0x57, 0xd8, 0x11, 0xb0, 0x05, 0x00, 0x0c,
+ 0x29, 0x87, 0xea, 0xe9, 0x0c, 0x00, 0x0c, 0x00,
+ 0x98, 0xda, 0x14, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x73, 0x00, 0x61, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x54, 0x00,
+ 0x4f, 0x00, 0x53, 0x00, 0x41, 0x00, 0x33, 0x00,
+ 0x32, 0x00, 0x2e, 0x00, 0x45, 0x00, 0x58, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ };
+ uint32_t request3_len = sizeof(request3);
+
+ uint8_t response3[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ uint32_t response3_len = sizeof(response3);
+
+ TcpSession ssn;
+ Packet *p[8];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|00 02|\"; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|00 75|\"; sid:2;)";
+ char *sig3 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|00 18|\"; sid:3;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ /* let the 7 and the 8th packet be dummy packets the client sends to the server */
+ for (i = 0; i < 8; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[1]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[1]->flowflags &=~ FLOW_PKT_TOSERVER;
+ p[3]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[3]->flowflags &=~ FLOW_PKT_TOSERVER;
+ p[5]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[5]->flowflags &=~ FLOW_PKT_TOSERVER;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, sig1);
+ if (s == NULL)
+ goto end;
+ s = de_ctx->sig_list->next = SigInit(de_ctx, sig2);
+ if (s == NULL)
+ goto end;
+ s = de_ctx->sig_list->next->next = SigInit(de_ctx, sig3);
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (!PacketAlertCheck(p[0], 1) || PacketAlertCheck(p[0], 2) || PacketAlertCheck(p[0], 3)) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[6]);
+ if (PacketAlertCheck(p[6], 1) || PacketAlertCheck(p[6], 2) || PacketAlertCheck(p[6], 3)) {
+ printf("sid 1 matched but shouldn't have for packet 6: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, response1, response1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if (PacketAlertCheck(p[1], 1) || PacketAlertCheck(p[1], 2) || PacketAlertCheck(p[1], 3)) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ /* we should have a match for the sig once again for the same flow, since
+ * the detection engine state for the flow has been reset because of a
+ * fresh transaction */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if (PacketAlertCheck(p[2], 1) || !PacketAlertCheck(p[2], 2) || PacketAlertCheck(p[2], 3)) {
+ printf("sid 1 didn't match but should have for packet 2: ");
+ goto end;
+ }
+
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[7]);
+ if (PacketAlertCheck(p[7], 1) || PacketAlertCheck(p[7], 2) || PacketAlertCheck(p[7], 3)) {
+ printf("sid 1 matched but shouldn't have for packet 7: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, response2, response2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if (PacketAlertCheck(p[3], 1) || PacketAlertCheck(p[3], 2) || PacketAlertCheck(p[3], 3)) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request3, request3_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ /* we should have a match for the sig once again for the same flow, since
+ * the detection engine state for the flow has been reset because of a
+ * fresh transaction */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[4]);
+ if (PacketAlertCheck(p[4], 1) || PacketAlertCheck(p[4], 2) || !PacketAlertCheck(p[4], 3)) {
+ printf("sid 1 didn't match but should have for packet 4: ");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, response3, response3_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[5]);
+ if (PacketAlertCheck(p[5], 1) || PacketAlertCheck(p[5], 2) || PacketAlertCheck(p[5], 3)) {
+ printf("sid 1 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 8);
+ return result;
+}
+
+/**
+ * \test Test the working of detection engien with respect to dce keywords.
+ */
+int DcePayloadTest14(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x37, 0x00,
+ 0x31, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ uint8_t bind[] = {
+ 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x81, 0xbb, 0x7a, 0x36, 0x44, 0x98, 0xf1, 0x35,
+ 0xad, 0x32, 0x98, 0xf0, 0x38, 0x00, 0x10, 0x03,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
+ 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
+ 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_len = sizeof(bind);
+
+ uint8_t bind_ack[] = {
+ 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb8, 0x10, 0xb8, 0x10, 0x74, 0x73, 0x00, 0x00,
+ 0x0d, 0x00, 0x5c, 0x50, 0x49, 0x50, 0x45, 0x5c,
+ 0x6e, 0x74, 0x73, 0x76, 0x63, 0x73, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
+ 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00
+ };
+ uint32_t bind_ack_len = sizeof(bind_ack);
+
+ uint8_t request2[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00,
+ 0x64, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x5c, 0x31, 0x37, 0x31, 0x2e, 0x37, 0x31,
+ 0x2e, 0x38, 0x34, 0x2e, 0x36, 0x37, 0x00, 0x8a,
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0f, 0x00
+ };
+ uint32_t request2_len = sizeof(request2);
+
+ uint8_t response2[] = {
+ 0x05, 0x00, 0x02, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xd2, 0xc4, 0x88, 0x14,
+ 0xef, 0x31, 0xbb, 0x4d, 0xa8, 0x13, 0xb7, 0x1b,
+ 0x47, 0x49, 0xb5, 0xd7, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t response2_len = sizeof(response2);
+
+ TcpSession ssn;
+ Packet *p[6];
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+ int i = 0;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|7f 01|\"; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|3f 00|\"; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ for (i = 0; i < 6; i++) {
+ p[i] = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p[i]->flow = &f;
+ p[i]->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p[i]->flowflags |= FLOW_PKT_TOSERVER;
+ p[i]->flowflags |= FLOW_PKT_ESTABLISHED;
+ }
+ p[3]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[3]->flowflags &=~ FLOW_PKT_TOSERVER;
+ p[5]->flowflags |= FLOW_PKT_TOCLIENT;
+ p[5]->flowflags &=~ FLOW_PKT_TOSERVER;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, sig1);
+ if (s == NULL)
+ goto end;
+ s = de_ctx->sig_list->next = SigInit(de_ctx, sig2);
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[0]);
+ if (!PacketAlertCheck(p[0], 1) || PacketAlertCheck(p[0], 2)) {
+ printf("sid 1 didn't match but should have for packet 0: ");
+ goto end;
+ }
+
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[1]);
+ if (PacketAlertCheck(p[1], 1) || PacketAlertCheck(p[1], 2)) {
+ printf("sid 1 matched but shouldn't have for packet 1: ");
+ goto end;
+ }
+
+ /* bind */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, bind, bind_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[2]);
+ if (PacketAlertCheck(p[2], 1) || PacketAlertCheck(p[2], 2)) {
+ printf("sid 1 matched but shouldn't have for packet 2: ");
+ goto end;
+ }
+
+ /* bind_ack. A new transaction initiation */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, bind_ack, bind_ack_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[3]);
+ if (PacketAlertCheck(p[3], 1) || PacketAlertCheck(p[3], 2)) {
+ printf("sid 1 matched but shouldn't have for packet 3: ");
+ goto end;
+ }
+
+ /* we should have a match for the sig once again for the same flow, since
+ * the detection engine state for the flow has been reset because of a
+ * fresh transaction */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request2, request2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[4]);
+ if (PacketAlertCheck(p[4], 1) || !PacketAlertCheck(p[4], 2)) {
+ printf("sid 1 didn't match but should have for packet 4: ");
+ goto end;
+ }
+
+ /* response */
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOCLIENT, response2, response2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p[5]);
+ if (PacketAlertCheck(p[5], 1) || PacketAlertCheck(p[5], 2)) {
+ printf("sid 1 matched but shouldn't have for packet 5: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(p, 6);
+ return result;
+}
+
+#endif
+
+/**
+ * \test Test the working of byte_test endianness.
+ */
+int DcePayloadTest15(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x37, 0x00,
+ 0x31, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,14080,0,relative,dce; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,46,5,relative,dce; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of byte_test endianness.
+ */
+int DcePayloadTest16(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x37, 0x00,
+ 0x31, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,55,0,relative; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,11776,5,relative; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of byte_test endianness.
+ */
+int DcePayloadTest17(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x37, 0x00,
+ 0x31, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,55,0,relative,big; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_test:2,=,46,5,relative,little; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of byte_jump endianness.
+ */
+int DcePayloadTest18(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x03, 0x00, 0x03,
+ 0x00, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,0,relative,dce; byte_test:2,=,46,0,relative,dce; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,2,relative,dce; byte_test:2,=,14080,0,relative; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of byte_jump endianness.
+ */
+int DcePayloadTest19(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x03, 0x00,
+ 0x03, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,0,relative; byte_test:2,=,46,0,relative,dce; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,2,relative; byte_test:2,=,14080,0,relative; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of byte_jump endianness.
+ */
+int DcePayloadTest20(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x76, 0x7e, 0x32, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x03, 0x03,
+ 0x00, 0x00, 0x2e, 0x00, 0x37, 0x00, 0x31, 0x00,
+ 0x2e, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2e, 0x00,
+ 0x36, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x84, 0xf9, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x14, 0xfa, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,0,relative,big; byte_test:2,=,46,0,relative,dce; sid:1;)";
+ char *sig2 = "alert tcp any any -> any any "
+ "(dce_stub_data; content:\"|5c 00 5c 00 31|\"; distance:0; "
+ "byte_jump:2,2,little,relative; byte_test:2,=,14080,0,relative; sid:2;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+ s->next = SigInit(de_ctx, sig2);
+ if (s->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of consecutive relative matches.
+ */
+int DcePayloadTest21(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x6e, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x69, 0x73, /* "now this" */
+ 0x20, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x62, /* " is is b" */
+ 0x69, 0x67, 0x20, 0x62, 0x69, 0x67, 0x20, 0x73, /* "ig big s" */
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, /* "tring no" */
+ 0x77 }; /* "w" */
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(msg:\"testing dce consecutive relative matches\"; dce_stub_data; "
+ "content:\"this\"; distance:0; content:\"is\"; within:6; content:\"big\"; within:8; "
+ "content:\"string\"; within:8; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of consecutive relative matches.
+ */
+int DcePayloadTest22(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x6e, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x69, 0x73, /* "now this" */
+ 0x20, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x69, /* " is is i" */
+ 0x73, 0x20, 0x62, 0x69, 0x67, 0x20, 0x62, 0x69, /* "s big bi" */
+ 0x67, 0x20, 0x62, 0x69, 0x67, 0x20, 0x73, 0x74, /* "g big st" */
+ 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x77 }; /* "ring now" */
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(msg:\"testing dce consecutive relative matches\"; dce_stub_data; "
+ "content:\"this\"; distance:0; content:\"is\"; within:9; content:\"big\"; within:12; "
+ "content:\"string\"; within:8; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of consecutive relative matches.
+ */
+int DcePayloadTest23(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x68, 0x69, /* "this thi" */
+ 0x73, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x69, 0x73, /* "s now is" */
+ 0x20, 0x69, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, /* " is " */
+ 0x62, 0x69, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, /* "big stri" */
+ 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x77 }; /* "ng now" */
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(msg:\"testing dce consecutive relative matches\"; dce_stub_data; "
+ "content:\"now\"; distance:0; content:\"this\"; distance:-20; "
+ "content:\"is\"; within:12; content:\"big\"; within:8; "
+ "content:\"string\"; within:8; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; content:\"two\"; "
+ "content:\"three\"; within:10; "
+ "content:\"four\"; distance:4; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "pkt_data; "
+ "content:\"one\"; "
+ "content:\"two\"; "
+ "content:\"three\"; within:5; "
+ "content:\"four\"; distance:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "content:\"three\"; within:5; "
+ "content:\"four\"; distance:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "pkt_data; "
+ "content:\"three\";"
+ "content:\"four\";"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectPcreData *pd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "pkt_data; "
+ "pcre:/boom/; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "content:\"three\";"
+ "content:\"four\";"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_PCRE) {
+ result = 0;
+ goto end;
+ }
+ pd = (DetectPcreData *)sm->ctx;
+ if (pd->flags & DETECT_PCRE_RAWBYTES ||
+ pd->flags & DETECT_PCRE_RELATIVE) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "pkt_data; "
+ "byte_jump:2,5; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "content:\"three\";"
+ "content:\"four\";"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bd = (DetectBytejumpData *)sm->ctx;
+ if (bd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bd->flags & DETECT_BYTEJUMP_BIG ||
+ bd->flags & DETECT_BYTEJUMP_STRING ||
+ bd->flags & DETECT_BYTEJUMP_RELATIVE ||
+ bd->flags & DETECT_BYTEJUMP_ALIGN ||
+ bd->flags & DETECT_BYTEJUMP_DCE ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "byte_jump:2,5,relative; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "pkt_data; "
+ "content:\"three\";"
+ "content:\"four\";"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bd = (DetectBytejumpData *)sm->ctx;
+ if (bd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bd->flags & DETECT_BYTEJUMP_BIG ||
+ bd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ bd->flags & DETECT_BYTEJUMP_ALIGN ||
+ bd->flags & DETECT_BYTEJUMP_DCE ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "byte_jump:2,5,relative; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "pkt_data; "
+ "content:\"three\";"
+ "content:\"four\"; within:4; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bd = (DetectBytejumpData *)sm->ctx;
+ if (bd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bd->flags & DETECT_BYTEJUMP_BIG ||
+ bd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ bd->flags & DETECT_BYTEJUMP_ALIGN ||
+ bd->flags & DETECT_BYTEJUMP_DCE ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectPcreData *pd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_stub_data; "
+ "pcre:/boom/R; "
+ "content:\"one\"; distance:10; within:5; "
+ "content:\"two\"; within:5;"
+ "pkt_data; "
+ "content:\"three\";"
+ "content:\"four\"; distance:5;"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_PCRE) {
+ result = 0;
+ goto end;
+ }
+ pd = (DetectPcreData *)sm->ctx;
+ if ( pd->flags & DETECT_PCRE_RAWBYTES ||
+ !(pd->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("four failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "four", 4) == 0);
+ if (result == 0)
+ goto end;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectPcreData *pd = NULL;
+ DetectBytejumpData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "pcre:/boom/R; "
+ "byte_jump:1,2,relative,align,dce; "
+ "content:\"one\"; within:4; distance:8; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_PCRE) {
+ result = 0;
+ goto end;
+ }
+ pd = (DetectPcreData *)sm->ctx;
+ if ( pd->flags & DETECT_PCRE_RAWBYTES ||
+ !(pd->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bd = (DetectBytejumpData *)sm->ctx;
+ if (bd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bd->flags & DETECT_BYTEJUMP_BIG ||
+ bd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ !(bd->flags & DETECT_BYTEJUMP_ALIGN) ||
+ !(bd->flags & DETECT_BYTEJUMP_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytetestData *bd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "byte_test:1,=,0,0,relative,dce; "
+ "pkt_data; "
+ "content:\"one\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ bd = (DetectBytetestData *)sm->ctx;
+ if (bd->flags & DETECT_BYTETEST_LITTLE ||
+ bd->flags & DETECT_BYTETEST_BIG ||
+ bd->flags & DETECT_BYTETEST_STRING ||
+ !(bd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ !(bd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectIsdataatData *isd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "isdataat:10,relative; "
+ "content:\"one\"; within:4; distance:8; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isd = (DetectIsdataatData *)sm->ctx;
+ if ( isd->flags & ISDATAAT_RAWBYTES ||
+ !(isd->flags & ISDATAAT_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest37(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytejumpData *bjd = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "byte_jump:1,2,relative,align,dce; "
+ "byte_test:1,=,2,0,relative,dce; "
+ "pkt_data; "
+ "content:\"one\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bjd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bjd->flags & DETECT_BYTEJUMP_BIG ||
+ bjd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bjd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ !(bjd->flags & DETECT_BYTEJUMP_ALIGN) ||
+ !(bjd->flags & DETECT_BYTEJUMP_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags & DETECT_BYTETEST_LITTLE ||
+ btd->flags & DETECT_BYTETEST_BIG ||
+ btd->flags & DETECT_BYTETEST_STRING ||
+ !(btd->flags & DETECT_BYTETEST_RELATIVE) ||
+ !(btd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest38(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectPcreData *pd = NULL;
+ DetectBytejumpData *bjd = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "pcre:/boom/R; "
+ "byte_jump:1,2,relative,align,dce; "
+ "byte_test:1,=,2,0,relative,dce; "
+ "pkt_data; "
+ "content:\"one\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_PCRE) {
+ result = 0;
+ goto end;
+ }
+ pd = (DetectPcreData *)sm->ctx;
+ if ( pd->flags & DETECT_PCRE_RAWBYTES ||
+ !(pd->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bjd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bjd->flags & DETECT_BYTEJUMP_BIG ||
+ bjd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bjd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ !(bjd->flags & DETECT_BYTEJUMP_ALIGN) ||
+ !(bjd->flags & DETECT_BYTEJUMP_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags & DETECT_BYTETEST_LITTLE ||
+ btd->flags & DETECT_BYTETEST_BIG ||
+ btd->flags & DETECT_BYTETEST_STRING ||
+ !(btd->flags & DETECT_BYTETEST_RELATIVE) ||
+ !(btd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest39(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "content:\"two\"; within:4; distance:8; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest40(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "content:\"one\"; within:10; "
+ "content:\"two\"; distance:20; within:30; "
+ "byte_test:1,=,2,0,relative,dce; "
+ "pkt_data; "
+ "content:\"three\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags & DETECT_BYTETEST_LITTLE ||
+ btd->flags & DETECT_BYTETEST_BIG ||
+ btd->flags & DETECT_BYTETEST_STRING ||
+ !(btd->flags & DETECT_BYTETEST_RELATIVE) ||
+ !(btd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest41(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "content:\"one\"; within:10; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "byte_test:1,=,2,0,relative,dce; "
+ "content:\"three\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags & DETECT_BYTETEST_LITTLE ||
+ btd->flags & DETECT_BYTETEST_BIG ||
+ btd->flags & DETECT_BYTETEST_STRING ||
+ !(btd->flags & DETECT_BYTETEST_RELATIVE) ||
+ !(btd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "three", 5) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test the working of consecutive relative matches with a negated content.
+ */
+int DcePayloadTest42(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, /* "we need " */
+ 0x74, 0x6f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x74, /* "to fix t" */
+ 0x68, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, /* "his and " */
+ 0x79, 0x65, 0x73, 0x20, 0x66, 0x69, 0x78, 0x20, /* "yes fix " */
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x77 /* "this now" */
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(msg:\"testing dce consecutive relative matches\"; dce_stub_data; "
+ "content:\"fix\"; distance:0; content:\"this\"; within:6; "
+ "content:!\"and\"; distance:0; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 matched but shouldn't have for packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test the working of consecutive relative pcres.
+ */
+int DcePayloadTest43(void)
+{
+ int result = 0;
+
+ uint8_t request1[] = {
+ 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x61, 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x20,
+ 0x64, 0x75, 0x70, 0x65, 0x72, 0x20, 0x6e, 0x6f,
+ 0x76, 0x61, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x75,
+ 0x70, 0x65, 0x72, 0x20, 0x6e, 0x6f, 0x76, 0x61,
+ 0x20, 0x6e, 0x6f, 0x77
+ };
+ uint32_t request1_len = sizeof(request1);
+
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ int r;
+
+ char *sig1 = "alert tcp any any -> any any "
+ "(msg:\"testing dce consecutive relative matches\"; dce_stub_data; "
+ "pcre:/super/R; content:\"nova\"; within:7; sid:1;)";
+
+ Signature *s;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_DCERPC;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ s = de_ctx->sig_list;
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request 1 */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_DCERPC, STREAM_TOSERVER, request1, request1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* detection phase */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+ if ( !(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest44(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectIsdataatData *isd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "dce_opnum:10; dce_stub_data; "
+ "isdataat:10,relative; "
+ "content:\"one\"; within:4; distance:8; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_ISDATAAT) {
+ result = 0;
+ goto end;
+ }
+ isd = (DetectIsdataatData *)sm->ctx;
+ if ( isd->flags & ISDATAAT_RAWBYTES ||
+ !(isd->flags & ISDATAAT_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ !(data->flags & DETECT_CONTENT_WITHIN) ||
+ !(data->flags & DETECT_CONTENT_DISTANCE) ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ result = 0;
+ printf("two failed\n");
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("three failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest45(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytejumpData *bjd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "content:\"one\"; "
+ "dce_opnum:10; dce_stub_data; "
+ "byte_jump:1,2,relative,align,dce; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTEJUMP) {
+ result = 0;
+ goto end;
+ }
+ bjd = (DetectBytejumpData *)sm->ctx;
+ if (bjd->flags & DETECT_BYTEJUMP_BEGIN ||
+ bjd->flags & DETECT_BYTEJUMP_LITTLE ||
+ bjd->flags & DETECT_BYTEJUMP_BIG ||
+ bjd->flags & DETECT_BYTEJUMP_STRING ||
+ !(bjd->flags & DETECT_BYTEJUMP_RELATIVE) ||
+ !(bjd->flags & DETECT_BYTEJUMP_ALIGN) ||
+ !(bjd->flags & DETECT_BYTEJUMP_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DcePayloadParseTest46(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectContentData *data = NULL;
+ DetectBytetestData *btd = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:12345678-1234-1234-1234-123456789012; "
+ "content:\"one\"; "
+ "dce_opnum:10; dce_stub_data; "
+ "byte_test:1,=,2,0,relative,dce; "
+ "pkt_data; "
+ "content:\"two\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_DMATCH];
+ if (sm->type != DETECT_BYTETEST) {
+ result = 0;
+ goto end;
+ }
+ btd = (DetectBytetestData *)sm->ctx;
+ if (btd->flags & DETECT_BYTETEST_LITTLE ||
+ btd->flags & DETECT_BYTETEST_BIG ||
+ btd->flags & DETECT_BYTETEST_STRING ||
+ !(btd->flags & DETECT_BYTETEST_RELATIVE) ||
+ !(btd->flags & DETECT_BYTETEST_DCE) ) {
+ result = 0;
+ printf("one failed\n");
+ goto end;
+ }
+
+ result &= (sm->next == NULL);
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("one failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "one", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ sm = sm->next;
+ if (sm->type != DETECT_CONTENT) {
+ result = 0;
+ goto end;
+ }
+ data = (DetectContentData *)sm->ctx;
+ if (data->flags & DETECT_CONTENT_RAWBYTES ||
+ data->flags & DETECT_CONTENT_NOCASE ||
+ data->flags & DETECT_CONTENT_WITHIN ||
+ data->flags & DETECT_CONTENT_DISTANCE ||
+ data->flags & DETECT_CONTENT_FAST_PATTERN ||
+ data->flags & DETECT_CONTENT_RELATIVE_NEXT ||
+ data->flags & DETECT_CONTENT_NEGATED ) {
+ printf("two failed\n");
+ result = 0;
+ goto end;
+ }
+ result &= (strncmp((char *)data->content, "two", 3) == 0);
+ if (result == 0)
+ goto end;
+
+ result &= (sm->next == NULL);
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DcePayloadRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DcePayloadTest01", DcePayloadTest01, 1);
+ UtRegisterTest("DcePayloadTest02", DcePayloadTest02, 1);
+ UtRegisterTest("DcePayloadTest03", DcePayloadTest03, 1);
+ UtRegisterTest("DcePayloadTest04", DcePayloadTest04, 1);
+ UtRegisterTest("DcePayloadTest05", DcePayloadTest05, 1);
+ UtRegisterTest("DcePayloadTest06", DcePayloadTest06, 1);
+ UtRegisterTest("DcePayloadTest07", DcePayloadTest07, 1);
+ UtRegisterTest("DcePayloadTest08", DcePayloadTest08, 1);
+ UtRegisterTest("DcePayloadTest09", DcePayloadTest09, 1);
+ UtRegisterTest("DcePayloadTest10", DcePayloadTest10, 1);
+ UtRegisterTest("DcePayloadTest11", DcePayloadTest11, 1);
+ UtRegisterTest("DcePayloadTest12", DcePayloadTest12, 1);
+ /* Disabled because of bug_753. Would be enabled, once we rewrite
+ * dce parser */
+#if 0
+ UtRegisterTest("DcePayloadTest13", DcePayloadTest13, 1);
+ UtRegisterTest("DcePayloadTest14", DcePayloadTest14, 1);
+#endif
+ UtRegisterTest("DcePayloadTest15", DcePayloadTest15, 1);
+ UtRegisterTest("DcePayloadTest16", DcePayloadTest16, 1);
+ UtRegisterTest("DcePayloadTest17", DcePayloadTest17, 1);
+ UtRegisterTest("DcePayloadTest18", DcePayloadTest18, 1);
+ UtRegisterTest("DcePayloadTest19", DcePayloadTest19, 1);
+ UtRegisterTest("DcePayloadTest20", DcePayloadTest20, 1);
+ UtRegisterTest("DcePayloadTest21", DcePayloadTest21, 1);
+ UtRegisterTest("DcePayloadTest22", DcePayloadTest22, 1);
+ UtRegisterTest("DcePayloadTest23", DcePayloadTest23, 1);
+
+ UtRegisterTest("DcePayloadParseTest25", DcePayloadParseTest25, 1);
+ UtRegisterTest("DcePayloadParseTest26", DcePayloadParseTest26, 1);
+ UtRegisterTest("DcePayloadParseTest27", DcePayloadParseTest27, 1);
+ UtRegisterTest("DcePayloadParseTest28", DcePayloadParseTest28, 1);
+ UtRegisterTest("DcePayloadParseTest29", DcePayloadParseTest29, 1);
+ UtRegisterTest("DcePayloadParseTest30", DcePayloadParseTest30, 1);
+ UtRegisterTest("DcePayloadParseTest31", DcePayloadParseTest31, 1);
+ UtRegisterTest("DcePayloadParseTest32", DcePayloadParseTest32, 1);
+ UtRegisterTest("DcePayloadParseTest33", DcePayloadParseTest33, 1);
+ UtRegisterTest("DcePayloadParseTest34", DcePayloadParseTest34, 1);
+ UtRegisterTest("DcePayloadParseTest35", DcePayloadParseTest35, 1);
+ UtRegisterTest("DcePayloadParseTest36", DcePayloadParseTest36, 1);
+ UtRegisterTest("DcePayloadParseTest37", DcePayloadParseTest37, 1);
+ UtRegisterTest("DcePayloadParseTest38", DcePayloadParseTest38, 1);
+ UtRegisterTest("DcePayloadParseTest39", DcePayloadParseTest39, 1);
+ UtRegisterTest("DcePayloadParseTest40", DcePayloadParseTest40, 1);
+ UtRegisterTest("DcePayloadParseTest41", DcePayloadParseTest41, 1);
+
+ UtRegisterTest("DcePayloadTest42", DcePayloadTest42, 1);
+ UtRegisterTest("DcePayloadTest43", DcePayloadTest43, 1);
+
+ UtRegisterTest("DcePayloadParseTest44", DcePayloadParseTest44, 1);
+ UtRegisterTest("DcePayloadParseTest45", DcePayloadParseTest45, 1);
+ UtRegisterTest("DcePayloadParseTest46", DcePayloadParseTest46, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-dcepayload.h b/framework/src/suricata/src/detect-engine-dcepayload.h
new file mode 100644
index 00000000..ccc4794b
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-dcepayload.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_DCEPAYLOAD_H__
+#define __DETECT_ENGINE_DCEPAYLOAD_H__
+
+int DetectEngineInspectDcePayload(DetectEngineCtx *, DetectEngineThreadCtx *,
+ Signature *, Flow *, uint8_t, void *);
+
+void DcePayloadRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_DCEPAYLOAD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-dns.c b/framework/src/suricata/src/detect-engine-dns.c
new file mode 100644
index 00000000..b08681c0
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-dns.c
@@ -0,0 +1,163 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Based on detect-engine-uri.c
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-dns-common.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/** \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param sm SigMatch to inspect
+ * \param f Flow
+ * \param flags app layer flags
+ * \param state App layer state
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectEngineInspectDnsQueryName(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id)
+{
+ DNSTransaction *tx = (DNSTransaction *)txv;
+ DNSQueryEntry *query = NULL;
+ uint8_t *buffer;
+ uint16_t buffer_len;
+ int r = 0;
+
+ SCLogDebug("start");
+
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ SCLogDebug("tx %p query %p", tx, query);
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+ buffer = (uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry));
+ buffer_len = query->len;
+
+ //PrintRawDataFp(stdout, buffer, buffer_len);
+
+ r = DetectEngineContentInspection(de_ctx, det_ctx,
+ s, s->sm_lists[DETECT_SM_LIST_DNSQUERYNAME_MATCH],
+ f, buffer, buffer_len, 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_DNSQUERY, NULL);
+ if (r == 1)
+ break;
+ }
+ return r;
+}
+
+
+/** \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param sm SigMatch to inspect
+ * \param f Flow
+ * \param flags app layer flags
+ * \param state App layer state
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectEngineInspectGenericList(ThreadVars *tv,
+ const DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ const Signature *s, Flow *f, const uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id, const int list)
+{
+ KEYWORD_PROFILING_SET_LIST(det_ctx, list);
+
+ SigMatchData *smd = s->sm_arrays[list];
+ SCLogDebug("running match functions, sm %p", smd);
+ if (smd != NULL) {
+ while (1) {
+ int match = 0;
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[smd->type].
+ AppLayerTxMatch(tv, det_ctx, f, flags, alstate, txv, s, smd->ctx);
+ KEYWORD_PROFILING_END(det_ctx, smd->type, (match == 1));
+
+ if (match == 0)
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ if (match == 2) {
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+
+ if (smd->is_last)
+ break;
+ smd++;
+ }
+ }
+
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+}
+
+int DetectEngineInspectDnsRequest(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, f, flags,
+ alstate, txv, tx_id,
+ DETECT_SM_LIST_DNSREQUEST_MATCH);
+}
+
+int DetectEngineInspectDnsResponse(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, f, flags,
+ alstate, txv, tx_id,
+ DETECT_SM_LIST_DNSRESPONSE_MATCH);
+}
diff --git a/framework/src/suricata/src/detect-engine-dns.h b/framework/src/suricata/src/detect-engine-dns.h
new file mode 100644
index 00000000..801a22d4
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-dns.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_DNS_H__
+#define __DETECT_ENGINE_DNS_H__
+
+int DetectEngineInspectDnsQueryName(ThreadVars *, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *, Signature *,
+ Flow *, uint8_t, void *, void *, uint64_t);
+int DetectEngineInspectDnsRequest(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id);
+int DetectEngineInspectDnsResponse(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id);
+
+#endif /* __DETECT_ENGINE_DNS_H__ */
diff --git a/framework/src/suricata/src/detect-engine-event.c b/framework/src/suricata/src/detect-engine-event.c
new file mode 100644
index 00000000..6b685d70
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-event.c
@@ -0,0 +1,412 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the decode-event keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "decode-events.h"
+
+#include "util-debug.h"
+
+#include "stream-tcp.h"
+
+
+/* Need to get the DEvents[] array */
+#define DETECT_EVENTS
+
+#include "detect-engine-event.h"
+#include "util-unittest.h"
+
+#define PARSE_REGEX "\\S[0-9A-z_]+[.][A-z0-9_+]+$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectEngineEventMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectEngineEventSetup (DetectEngineCtx *, Signature *, char *);
+static int DetectDecodeEventSetup (DetectEngineCtx *, Signature *, char *);
+static int DetectStreamEventSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectEngineEventFree (void *);
+void EngineEventRegisterTests(void);
+
+
+/**
+ * \brief Registration function for decode-event: keyword
+ */
+void DetectEngineEventRegister (void)
+{
+ sigmatch_table[DETECT_ENGINE_EVENT].name = "engine-event";
+ sigmatch_table[DETECT_ENGINE_EVENT].Match = DetectEngineEventMatch;
+ sigmatch_table[DETECT_ENGINE_EVENT].Setup = DetectEngineEventSetup;
+ sigmatch_table[DETECT_ENGINE_EVENT].Free = DetectEngineEventFree;
+ sigmatch_table[DETECT_ENGINE_EVENT].RegisterTests = EngineEventRegisterTests;
+
+ sigmatch_table[DETECT_DECODE_EVENT].name = "decode-event";
+ sigmatch_table[DETECT_DECODE_EVENT].Match = DetectEngineEventMatch;
+ sigmatch_table[DETECT_DECODE_EVENT].Setup = DetectDecodeEventSetup;
+ sigmatch_table[DETECT_DECODE_EVENT].Free = DetectEngineEventFree;
+ sigmatch_table[DETECT_DECODE_EVENT].flags |= SIGMATCH_DEONLY_COMPAT;
+
+ sigmatch_table[DETECT_STREAM_EVENT].name = "stream-event";
+ sigmatch_table[DETECT_STREAM_EVENT].Match = DetectEngineEventMatch;
+ sigmatch_table[DETECT_STREAM_EVENT].Setup = DetectStreamEventSetup;
+ sigmatch_table[DETECT_STREAM_EVENT].Free = DetectEngineEventFree;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s\n", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s\n", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+
+}
+
+/**
+ * \brief This function is used to match decoder event flags set on a packet with those passed via decode-event:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the Signature
+ * \param m pointer to the sigmatch
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectEngineEventMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ const DetectEngineEventData *de = (const DetectEngineEventData *)ctx;
+
+ if (ENGINE_ISSET_EVENT(p, de->event)) {
+ SCLogDebug("de->event matched %u", de->event);
+ SCReturnInt(1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief This function is used to parse decoder events options passed via decode-event: keyword
+ *
+ * \param rawstr Pointer to the user provided decode-event options
+ *
+ * \retval de pointer to DetectFlowData on success
+ * \retval NULL on failure
+ */
+DetectEngineEventData *DetectEngineEventParse (char *rawstr)
+{
+ int i;
+ DetectEngineEventData *de = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0, found = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32
+ ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ char copy_str[128] = "";
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 0,
+ copy_str, sizeof(copy_str));
+
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ for (i = 0; DEvents[i].event_name != NULL; i++) {
+ if (strcasecmp(DEvents[i].event_name,copy_str) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ SCLogError(SC_ERR_UNKNOWN_DECODE_EVENT, "unknown decode event \"%s\"",
+ copy_str);
+ goto error;
+ }
+
+ de = SCMalloc(sizeof(DetectEngineEventData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ de->event = DEvents[i].code;
+
+ if (de->event == STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA) {
+ StreamTcpReassembleConfigEnableOverlapCheck();
+ }
+ return de;
+
+error:
+ if (de)
+ SCFree(de);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed decode-event into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided decode-event options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int _DetectEngineEventSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr, int smtype)
+{
+ DetectEngineEventData *de = NULL;
+ SigMatch *sm = NULL;
+
+ de = DetectEngineEventParse(rawstr);
+ if (de == NULL)
+ goto error;
+
+ SCLogDebug("rawstr %s %u", rawstr, de->event);
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = smtype;
+ sm->ctx = (SigMatchCtx *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ return 0;
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+
+static int DetectEngineEventSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ return _DetectEngineEventSetup (de_ctx, s, rawstr, DETECT_ENGINE_EVENT);
+}
+
+/**
+ * \brief this function will free memory associated with DetectEngineEventData
+ *
+ * \param de pointer to DetectEngineEventData
+ */
+static void DetectEngineEventFree(void *ptr)
+{
+ DetectEngineEventData *de = (DetectEngineEventData *)ptr;
+ if (de)
+ SCFree(de);
+}
+
+
+/**
+ * \brief this function Setup the 'decode-event' keyword by setting the correct
+ * signature type
+*/
+static int DetectDecodeEventSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ return _DetectEngineEventSetup(de_ctx, s, rawstr, DETECT_DECODE_EVENT);
+}
+
+/**
+ * \brief this function Setup the 'stream-event' keyword by resolving the alias
+*/
+static int DetectStreamEventSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ char srawstr[MAX_SUBSTRINGS * 2] = "stream.";
+
+ /* stream:$EVENT alias command develop as decode-event:stream.$EVENT */
+ strlcat(srawstr, rawstr, 2 * MAX_SUBSTRINGS - strlen("stream.") - 1);
+
+ return DetectEngineEventSetup(de_ctx, s, srawstr);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+#ifdef UNITTESTS
+
+/**
+ * \test EngineEventTestParse01 is a test for a valid decode-event value
+ */
+int EngineEventTestParse01 (void)
+{
+ DetectEngineEventData *de = NULL;
+ de = DetectEngineEventParse("ipv4.pkt_too_small");
+ if (de) {
+ DetectEngineEventFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * \test EngineEventTestParse02 is a test for a valid upper + lower case decode-event value
+ */
+int EngineEventTestParse02 (void)
+{
+ DetectEngineEventData *de = NULL;
+ de = DetectEngineEventParse("PPP.pkt_too_small");
+ if (de) {
+ DetectEngineEventFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test EngineEventTestParse03 is a test for a valid upper case decode-event value
+ */
+int EngineEventTestParse03 (void)
+{
+ DetectEngineEventData *de = NULL;
+ de = DetectEngineEventParse("IPV6.PKT_TOO_SMALL");
+ if (de) {
+ DetectEngineEventFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test EngineEventTestParse04 is a test for an invalid upper case decode-event value
+ */
+int EngineEventTestParse04 (void)
+{
+ DetectEngineEventData *de = NULL;
+ de = DetectEngineEventParse("IPV6.INVALID_EVENT");
+ if (de) {
+ DetectEngineEventFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test EngineEventTestParse05 is a test for an invalid char into the decode-event value
+ */
+int EngineEventTestParse05 (void)
+{
+ DetectEngineEventData *de = NULL;
+ de = DetectEngineEventParse("IPV-6,INVALID_CHAR");
+ if (de) {
+ DetectEngineEventFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test EngineEventTestParse06 is a test for match function with valid decode-event value
+ */
+int EngineEventTestParse06 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectEngineEventData *de = NULL;
+ SigMatch *sm = NULL;
+
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ ENGINE_SET_EVENT(p,PPP_PKT_TOO_SMALL);
+
+ de = DetectEngineEventParse("ppp.pkt_too_small");
+ if (de == NULL)
+ goto error;
+
+ de->event = PPP_PKT_TOO_SMALL;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_DECODE_EVENT;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectEngineEventMatch(&tv,NULL,p,NULL,sm->ctx);
+
+ if(ret) {
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for EngineEvent
+ */
+void EngineEventRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("EngineEventTestParse01", EngineEventTestParse01, 1);
+ UtRegisterTest("EngineEventTestParse02", EngineEventTestParse02, 1);
+ UtRegisterTest("EngineEventTestParse03", EngineEventTestParse03, 1);
+ UtRegisterTest("EngineEventTestParse04", EngineEventTestParse04, 0);
+ UtRegisterTest("EngineEventTestParse05", EngineEventTestParse05, 0);
+ UtRegisterTest("EngineEventTestParse06", EngineEventTestParse06, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-engine-event.h b/framework/src/suricata/src/detect-engine-event.h
new file mode 100644
index 00000000..9d6424fb
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-event.h
@@ -0,0 +1,256 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_EVENT_H__
+#define __DETECT_ENGINE_EVENT_H__
+
+#include "decode-events.h"
+
+typedef struct DetectEngineEventData_ {
+ uint8_t event;
+} DetectEngineEventData;
+
+/* prototypes */
+void DetectEngineEventRegister (void);
+
+/* supported decoder events */
+
+#ifdef DETECT_EVENTS
+struct DetectEngineEvents_ {
+ char *event_name;
+ uint8_t code;
+} DEvents[] = {
+ /* IPV4 EVENTS */
+ { "ipv4.pkt_too_small", IPV4_PKT_TOO_SMALL, },
+ { "ipv4.hlen_too_small", IPV4_HLEN_TOO_SMALL, },
+ { "ipv4.iplen_smaller_than_hlen", IPV4_IPLEN_SMALLER_THAN_HLEN, },
+ { "ipv4.trunc_pkt", IPV4_TRUNC_PKT, },
+
+ /* IPV4 OPTIONS */
+ { "ipv4.opt_invalid", IPV4_OPT_INVALID, },
+ { "ipv4.opt_invalid_len", IPV4_OPT_INVALID_LEN, },
+ { "ipv4.opt_malformed", IPV4_OPT_MALFORMED, },
+ { "ipv4.opt_pad_required", IPV4_OPT_PAD_REQUIRED, },
+ { "ipv4.opt_eol_required", IPV4_OPT_EOL_REQUIRED, },
+ { "ipv4.opt_duplicate", IPV4_OPT_DUPLICATE, },
+ { "ipv4.opt_unknown", IPV4_OPT_UNKNOWN, },
+ { "ipv4.wrong_ip_version", IPV4_WRONG_IP_VER, },
+ { "ipv4.icmpv6", IPV4_WITH_ICMPV6, },
+
+ /* ICMP EVENTS */
+ { "icmpv4.pkt_too_small", ICMPV4_PKT_TOO_SMALL, },
+ { "icmpv4.unknown_type", ICMPV4_UNKNOWN_TYPE, },
+ { "icmpv4.unknown_code", ICMPV4_UNKNOWN_CODE, },
+ { "icmpv4.ipv4_trunc_pkt", ICMPV4_IPV4_TRUNC_PKT, },
+ { "icmpv4.ipv4_unknown_ver", ICMPV4_IPV4_UNKNOWN_VER, },
+
+ /* ICMPv6 EVENTS */
+ { "icmpv6.unknown_type", ICMPV6_UNKNOWN_TYPE,},
+ { "icmpv6.unknown_code", ICMPV6_UNKNOWN_CODE,},
+ { "icmpv6.pkt_too_small", ICMPV6_PKT_TOO_SMALL,},
+ { "icmpv6.ipv6_unknown_version", ICMPV6_IPV6_UNKNOWN_VER,},
+ { "icmpv6.ipv6_trunc_pkt", ICMPV6_IPV6_TRUNC_PKT,},
+ { "icmpv6.mld_message_with_invalid_hl", ICMPV6_MLD_MESSAGE_WITH_INVALID_HL,},
+
+ /* IPV6 EVENTS */
+ { "ipv6.pkt_too_small", IPV6_PKT_TOO_SMALL, },
+ { "ipv6.trunc_pkt", IPV6_TRUNC_PKT, },
+ { "ipv6.trunc_exthdr", IPV6_TRUNC_EXTHDR, },
+ { "ipv6.exthdr_dupl_fh", IPV6_EXTHDR_DUPL_FH, },
+ { "ipv6.exthdr_useless_fh", IPV6_EXTHDR_USELESS_FH, },
+ { "ipv6.exthdr_dupl_rh", IPV6_EXTHDR_DUPL_RH, },
+ { "ipv6.exthdr_dupl_hh", IPV6_EXTHDR_DUPL_HH, },
+ { "ipv6.exthdr_dupl_dh", IPV6_EXTHDR_DUPL_DH, },
+ { "ipv6.exthdr_dupl_ah", IPV6_EXTHDR_DUPL_AH, },
+ { "ipv6.exthdr_dupl_eh", IPV6_EXTHDR_DUPL_EH, },
+ { "ipv6.exthdr_invalid_optlen", IPV6_EXTHDR_INVALID_OPTLEN, },
+ { "ipv6.wrong_ip_version", IPV6_WRONG_IP_VER, },
+ { "ipv6.exthdr_ah_res_not_null", IPV6_EXTHDR_AH_RES_NOT_NULL, },
+ { "ipv6.hopopts_unknown_opt", IPV6_HOPOPTS_UNKNOWN_OPT, },
+ { "ipv6.hopopts_only_padding", IPV6_HOPOPTS_ONLY_PADDING, },
+ { "ipv6.dstopts_unknown_opt", IPV6_DSTOPTS_UNKNOWN_OPT, },
+ { "ipv6.dstopts_only_padding", IPV6_DSTOPTS_ONLY_PADDING, },
+ { "ipv6.rh_type_0", IPV6_EXTHDR_RH_TYPE_0, },
+ { "ipv6.zero_len_padn", IPV6_EXTHDR_ZERO_LEN_PADN, },
+ { "ipv6.fh_non_zero_reserved_field", IPV6_FH_NON_ZERO_RES_FIELD, },
+ { "ipv6.data_after_none_header", IPV6_DATA_AFTER_NONE_HEADER, },
+ { "ipv6.unknown_next_header", IPV6_UNKNOWN_NEXT_HEADER, },
+ { "ipv6.icmpv4", IPV6_WITH_ICMPV4, },
+
+ /* TCP EVENTS */
+ { "tcp.pkt_too_small", TCP_PKT_TOO_SMALL, },
+ { "tcp.hlen_too_small", TCP_HLEN_TOO_SMALL, },
+ { "tcp.invalid_optlen", TCP_INVALID_OPTLEN, },
+
+ /* TCP OPTIONS */
+ { "tcp.opt_invalid_len", TCP_OPT_INVALID_LEN, },
+ { "tcp.opt_duplicate", TCP_OPT_DUPLICATE, },
+
+ /* UDP EVENTS */
+ { "udp.pkt_too_small", UDP_PKT_TOO_SMALL, },
+ { "udp.hlen_too_small", UDP_HLEN_TOO_SMALL, },
+ { "udp.hlen_invalid", UDP_HLEN_INVALID, },
+
+ /* SLL EVENTS */
+ { "sll.pkt_too_small", SLL_PKT_TOO_SMALL, },
+
+ /* ETHERNET EVENTS */
+ { "ethernet.pkt_too_small", ETHERNET_PKT_TOO_SMALL, },
+
+ /* PPP EVENTS */
+ { "ppp.pkt_too_small", PPP_PKT_TOO_SMALL, },
+ { "ppp.vju_pkt_too_small", PPPVJU_PKT_TOO_SMALL, },
+ { "ppp.ip4_pkt_too_small", PPPIPV4_PKT_TOO_SMALL, },
+ { "ppp.ip6_pkt_too_small", PPPIPV6_PKT_TOO_SMALL, },
+ { "ppp.wrong_type", PPP_WRONG_TYPE, }, /** unknown & invalid protocol */
+ { "ppp.unsup_proto", PPP_UNSUP_PROTO, }, /** unsupported but valid protocol */
+
+ /* PPPOE EVENTS */
+ { "pppoe.pkt_too_small", PPPOE_PKT_TOO_SMALL, },
+ { "pppoe.wrong_code", PPPOE_WRONG_CODE, },
+ { "pppoe.malformed_tags", PPPOE_MALFORMED_TAGS, },
+
+ /* GRE EVENTS */
+ { "gre.pkt_too_small", GRE_PKT_TOO_SMALL, },
+ { "gre.wrong_version", GRE_WRONG_VERSION, },
+ { "gre.version0_recur", GRE_VERSION0_RECUR, },
+ { "gre.version0_flags", GRE_VERSION0_FLAGS, },
+ { "gre.version0_hdr_too_big", GRE_VERSION0_HDR_TOO_BIG, },
+ { "gre.version0_malformed_sre_hdr", GRE_VERSION0_MALFORMED_SRE_HDR, },
+ { "gre.version1_chksum", GRE_VERSION1_CHKSUM, },
+ { "gre.version1_route", GRE_VERSION1_ROUTE, },
+ { "gre.version1_ssr", GRE_VERSION1_SSR, },
+ { "gre.version1_recur", GRE_VERSION1_RECUR, },
+ { "gre.version1_flags", GRE_VERSION1_FLAGS, },
+ { "gre.version1_no_key", GRE_VERSION1_NO_KEY, },
+ { "gre.version1_wrong_protocol", GRE_VERSION1_WRONG_PROTOCOL, },
+ { "gre.version1_malformed_sre_hdr", GRE_VERSION1_MALFORMED_SRE_HDR, },
+ { "gre.version1_hdr_too_big", GRE_VERSION1_HDR_TOO_BIG, },
+
+ /* VLAN EVENTS */
+ { "vlan.header_too_small",VLAN_HEADER_TOO_SMALL, },
+ { "vlan.unknown_type",VLAN_UNKNOWN_TYPE, },
+ { "vlan.too_many_layers", VLAN_HEADER_TOO_MANY_LAYERS, },
+
+ /* RAW EVENTS */
+ { "ipraw.invalid_ip_version",IPRAW_INVALID_IPV, },
+
+ /* LINKTYPE NULL EVENTS */
+ { "ltnull.pkt_too_small", LTNULL_PKT_TOO_SMALL, },
+ { "ltnull.unsupported_type", LTNULL_UNSUPPORTED_TYPE, },
+
+ /* STREAM EVENTS */
+ { "stream.3whs_ack_in_wrong_dir", STREAM_3WHS_ACK_IN_WRONG_DIR, },
+ { "stream.3whs_async_wrong_seq", STREAM_3WHS_ASYNC_WRONG_SEQ, },
+ { "stream.3whs_right_seq_wrong_ack_evasion", STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION, },
+ { "stream.3whs_synack_in_wrong_direction", STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION, },
+ { "stream.3whs_synack_resend_with_different_ack", STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK, },
+ { "stream.3whs_synack_resend_with_diff_seq", STREAM_3WHS_SYNACK_RESEND_WITH_DIFF_SEQ, },
+ { "stream.3whs_synack_toserver_on_syn_recv", STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV, },
+ { "stream.3whs_synack_with_wrong_ack", STREAM_3WHS_SYNACK_WITH_WRONG_ACK, },
+ { "stream.3whs_synack_flood", STREAM_3WHS_SYNACK_FLOOD, },
+ { "stream.3whs_syn_resend_diff_seq_on_syn_recv", STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV, },
+ { "stream.3whs_syn_toclient_on_syn_recv", STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV, },
+ { "stream.3whs_wrong_seq_wrong_ack", STREAM_3WHS_WRONG_SEQ_WRONG_ACK, },
+ { "stream.4whs_synack_with_wrong_ack", STREAM_4WHS_SYNACK_WITH_WRONG_ACK, },
+ { "stream.4whs_synack_with_wrong_syn", STREAM_4WHS_SYNACK_WITH_WRONG_SYN, },
+ { "stream.4whs_wrong_seq", STREAM_4WHS_WRONG_SEQ, },
+ { "stream.4whs_invalid_ack", STREAM_4WHS_INVALID_ACK, },
+ { "stream.closewait_ack_out_of_window", STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW, },
+ { "stream.closewait_fin_out_of_window", STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW, },
+ { "stream.closewait_pkt_before_last_ack", STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK, },
+ { "stream.closewait_invalid_ack", STREAM_CLOSEWAIT_INVALID_ACK, },
+ { "stream.closing_ack_wrong_seq", STREAM_CLOSING_ACK_WRONG_SEQ, },
+ { "stream.closing_invalid_ack", STREAM_CLOSING_INVALID_ACK, },
+ { "stream.est_packet_out_of_window", STREAM_EST_PACKET_OUT_OF_WINDOW, },
+ { "stream.est_pkt_before_last_ack", STREAM_EST_PKT_BEFORE_LAST_ACK, },
+ { "stream.est_synack_resend", STREAM_EST_SYNACK_RESEND, },
+ { "stream.est_synack_resend_with_different_ack", STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK, },
+ { "stream.est_synack_resend_with_diff_seq", STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ, },
+ { "stream.est_synack_toserver", STREAM_EST_SYNACK_TOSERVER, },
+ { "stream.est_syn_resend", STREAM_EST_SYN_RESEND, },
+ { "stream.est_syn_resend_diff_seq", STREAM_EST_SYN_RESEND_DIFF_SEQ, },
+ { "stream.est_syn_toclient", STREAM_EST_SYN_TOCLIENT, },
+ { "stream.est_invalid_ack", STREAM_EST_INVALID_ACK, },
+ { "stream.fin_invalid_ack", STREAM_FIN_INVALID_ACK, },
+ { "stream.fin1_ack_wrong_seq", STREAM_FIN1_ACK_WRONG_SEQ, },
+ { "stream.fin1_fin_wrong_seq", STREAM_FIN1_FIN_WRONG_SEQ, },
+ { "stream.fin1_invalid_ack", STREAM_FIN1_INVALID_ACK, },
+ { "stream.fin2_ack_wrong_seq", STREAM_FIN2_ACK_WRONG_SEQ, },
+ { "stream.fin2_fin_wrong_seq", STREAM_FIN2_FIN_WRONG_SEQ, },
+ { "stream.fin2_invalid_ack", STREAM_FIN2_INVALID_ACK, },
+ { "stream.fin_but_no_session", STREAM_FIN_BUT_NO_SESSION, },
+ { "stream.fin_out_of_window", STREAM_FIN_OUT_OF_WINDOW, },
+ { "stream.lastack_ack_wrong_seq", STREAM_LASTACK_ACK_WRONG_SEQ, },
+ { "stream.lastack_invalid_ack", STREAM_LASTACK_INVALID_ACK, },
+ { "stream.rst_but_no_session", STREAM_RST_BUT_NO_SESSION, },
+ { "stream.timewait_ack_wrong_seq", STREAM_TIMEWAIT_ACK_WRONG_SEQ, },
+ { "stream.timewait_invalid_ack", STREAM_TIMEWAIT_INVALID_ACK, },
+ { "stream.pkt_invalid_timestamp", STREAM_PKT_INVALID_TIMESTAMP, },
+ { "stream.pkt_invalid_ack", STREAM_PKT_INVALID_ACK, },
+ { "stream.pkt_broken_ack", STREAM_PKT_BROKEN_ACK, },
+ { "stream.rst_invalid_ack", STREAM_RST_INVALID_ACK, },
+ { "stream.shutdown_syn_resend", STREAM_SHUTDOWN_SYN_RESEND, },
+ { "stream.pkt_retransmission", STREAM_PKT_RETRANSMISSION, },
+ { "stream.reassembly_segment_before_base_seq", STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ, },
+ { "stream.reassembly_no_segment", STREAM_REASSEMBLY_NO_SEGMENT, },
+ { "stream.reassembly_seq_gap", STREAM_REASSEMBLY_SEQ_GAP, },
+ { "stream.reassembly_overlap_different_data", STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA, },
+ { "stream.pkt_bad_window_update", STREAM_PKT_BAD_WINDOW_UPDATE, },
+
+ /* SCTP EVENTS */
+ { "sctp.pkt_too_small", SCTP_PKT_TOO_SMALL, },
+
+ /* Fragmentation reasembly events. */
+ { "ipv4.frag_too_large", IPV4_FRAG_PKT_TOO_LARGE, },
+ { "ipv6.frag_too_large", IPV6_FRAG_PKT_TOO_LARGE, },
+ { "ipv4.frag_overlap", IPV4_FRAG_OVERLAP, },
+ { "ipv6.frag_overlap", IPV6_FRAG_OVERLAP, },
+ /* Fragment ignored due to internal error */
+ { "ipv4.frag_ignored", IPV4_FRAG_IGNORED, },
+ { "ipv6.frag_ignored", IPV6_FRAG_IGNORED, },
+
+ /* IPv4 in IPv6 events */
+ { "ipv6.ipv4_in_ipv6_too_small", IPV4_IN_IPV6_PKT_TOO_SMALL, },
+ { "ipv6.ipv4_in_ipv6_wrong_version", IPV4_IN_IPV6_WRONG_IP_VER, },
+ /* IPv6 in IPv6 events */
+ { "ipv6.ipv6_in_ipv6_too_small", IPV6_IN_IPV6_PKT_TOO_SMALL, },
+ { "ipv6.ipv6_in_ipv6_wrong_version", IPV6_IN_IPV6_WRONG_IP_VER, },
+
+ /* MPLS events */
+ { "mpls.bad_label_router_alert", MPLS_BAD_LABEL_ROUTER_ALERT, },
+ { "mpls.bad_label_implicit_null", MPLS_BAD_LABEL_IMPLICIT_NULL, },
+ { "mpls.bad_label_reserved", MPLS_BAD_LABEL_RESERVED, },
+ { "mpls.unknown_payload_type", MPLS_UNKNOWN_PAYLOAD_TYPE, },
+
+ /* ERSPAN events */
+ { "erspan.header_too_small", ERSPAN_HEADER_TOO_SMALL, },
+ { "erspan.unsupported_version", ERSPAN_UNSUPPORTED_VERSION, },
+ { "erspan.too_many_vlan_layers", ERSPAN_TOO_MANY_VLAN_LAYERS, },
+
+ { NULL, 0 },
+};
+#endif /* DETECT_EVENTS */
+
+#endif /*__DETECT_ENGINE_EVENT_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-file.c b/framework/src/suricata/src/detect-engine-file.c
new file mode 100644
index 00000000..31993685
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-file.c
@@ -0,0 +1,301 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+
+#include "detect-filestore.h"
+
+#include "detect-engine-uri.h"
+#include "detect-engine-hcbd.h"
+#include "detect-engine-hhd.h"
+#include "detect-engine-hrhd.h"
+#include "detect-engine-hmd.h"
+#include "detect-engine-hcd.h"
+#include "detect-engine-hrud.h"
+#include "detect-engine-dcepayload.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+#include "app-layer-smb.h"
+#include "app-layer-dcerpc-common.h"
+#include "app-layer-dcerpc.h"
+#include "app-layer-smtp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-profiling.h"
+
+
+/**
+ * \brief Inspect the file inspecting keywords.
+ *
+ * \param tv thread vars
+ * \param det_ctx detection engine thread ctx
+ * \param f flow
+ * \param s signature to inspect
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ * \retval 2 can't match
+ * \retval 3 can't match filestore signature
+ *
+ * \note flow is not locked at this time
+ */
+static int DetectFileInspect(ThreadVars *tv, DetectEngineThreadCtx *det_ctx,
+ Flow *f, Signature *s, uint8_t flags, FileContainer *ffc)
+{
+ SigMatch *sm = NULL;
+ int r = 0;
+ int match = 0;
+ int store_r = 0;
+
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_FILEMATCH);
+ SCLogDebug("file inspection... %p", ffc);
+
+ if (ffc != NULL) {
+ File *file = ffc->head;
+ for (; file != NULL; file = file->next) {
+ SCLogDebug("file");
+
+ if (file->state == FILE_STATE_NONE) {
+ SCLogDebug("file state FILE_STATE_NONE");
+ continue;
+ }
+
+ if (file->txid < det_ctx->tx_id) {
+ SCLogDebug("file->txid < det_ctx->tx_id == %"PRIu64" < %"PRIu64, file->txid, det_ctx->tx_id);
+ continue;
+ }
+
+ if (file->txid > det_ctx->tx_id) {
+ SCLogDebug("file->txid > det_ctx->tx_id == %"PRIu64" > %"PRIu64, file->txid, det_ctx->tx_id);
+ break;
+ }
+
+ if ((s->file_flags & FILE_SIG_NEED_FILENAME) && file->name == NULL) {
+ SCLogDebug("sig needs filename, but we don't have any");
+ r = 0;
+ break;
+ }
+
+ if ((s->file_flags & FILE_SIG_NEED_MAGIC) && file->chunks_head == NULL) {
+ SCLogDebug("sig needs file content, but we don't have any");
+ r = 0;
+ break;
+ }
+
+ if ((s->file_flags & FILE_SIG_NEED_FILECONTENT) && file->chunks_head == NULL) {
+ SCLogDebug("sig needs file content, but we don't have any");
+ r = 0;
+ break;
+ }
+
+ if ((s->file_flags & FILE_SIG_NEED_MD5) && (!(file->flags & FILE_MD5))) {
+ SCLogDebug("sig needs file md5, but we don't have any");
+ r = 0;
+ break;
+ }
+
+ if ((s->file_flags & FILE_SIG_NEED_SIZE) && file->state < FILE_STATE_CLOSED) {
+ SCLogDebug("sig needs filesize, but state < FILE_STATE_CLOSED");
+ r = 0;
+ break;
+ }
+
+ /* run the file match functions. */
+ for (sm = s->sm_lists[DETECT_SM_LIST_FILEMATCH]; sm != NULL; sm = sm->next) {
+ SCLogDebug("sm %p, sm->next %p", sm, sm->next);
+
+ if (sigmatch_table[sm->type].FileMatch != NULL) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ FileMatch(tv, det_ctx, f, flags, file, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match > 0));
+ if (match == 0) {
+ r = 2;
+ break;
+ } else if (sm->next == NULL) {
+ r = 1;
+ break;
+ }
+ }
+ }
+
+ /* continue inspection for other files as we may want to store
+ * those as well. We'll return 1 (match) regardless of their
+ * results though */
+ if (r == 1)
+ store_r = 1;
+
+ /* if this is a filestore sig, and the sig can't match
+ * return 3 so we can distinguish */
+ if ((s->flags & SIG_FLAG_FILESTORE) && r == 2)
+ r = 3;
+
+ /* continue, this file may (or may not) be unable to match
+ * maybe we have more that can :) */
+ }
+ } else {
+ /* if we have a filestore sm with a scope > file (so tx, ssn) we
+ * run it here */
+ sm = s->sm_lists[DETECT_SM_LIST_FILEMATCH];
+ if (sm != NULL && sm->next == NULL && sm->type == DETECT_FILESTORE &&
+ sm->ctx != NULL)
+ {
+ DetectFilestoreData *fd = (DetectFilestoreData *)sm->ctx;
+ if (fd->scope > FILESTORE_SCOPE_DEFAULT) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ FileMatch(tv, det_ctx, f, flags, /* no file */NULL, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match > 0));
+
+ if (match == 1) {
+ r = 1;
+ }
+ }
+ }
+ }
+
+ if (store_r == 1)
+ r = 1;
+ SCReturnInt(r);
+}
+
+/**
+ * \brief Inspect the file inspecting keywords against the HTTP transactions.
+ *
+ * \param tv thread vars
+ * \param det_ctx detection engine thread ctx
+ * \param f flow
+ * \param s signature to inspect
+ * \param alstate state
+ * \param flags direction flag
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ * \retval 2 can't match
+ * \retval 3 can't match filestore signature
+ *
+ * \note flow should be locked when this function's called.
+ */
+int DetectFileInspectHttp(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ int r = DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ FileContainer *ffc;
+ HtpState *htp_state = (HtpState *)alstate;
+
+ if (flags & STREAM_TOCLIENT)
+ ffc = htp_state->files_tc;
+ else
+ ffc = htp_state->files_ts;
+
+ int match = DetectFileInspect(tv, det_ctx, f, s, flags, ffc);
+ if (match == 1) {
+ r = DETECT_ENGINE_INSPECT_SIG_MATCH;
+ } else if (match == 2) {
+ if (r != 1) {
+ SCLogDebug("sid %u can't match on this transaction", s->id);
+ r = DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+ } else if (match == 3) {
+ if (r != 1) {
+ SCLogDebug("sid %u can't match on this transaction (filestore sig)", s->id);
+ r = DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE;
+ }
+ }
+
+ return r;
+}
+
+/**
+ * \brief Inspect the file inspecting keywords against the SMTP transactions.
+ *
+ * \param tv thread vars
+ * \param det_ctx detection engine thread ctx
+ * \param f flow
+ * \param s signature to inspect
+ * \param alstate state
+ * \param flags direction flag
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ * \retval 2 can't match
+ * \retval 3 can't match filestore signature
+ *
+ * \note flow is not locked at this time
+ */
+int DetectFileInspectSmtp(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ SCEnter();
+ int r = DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ SMTPState *smtp_state = NULL;
+ FileContainer *ffc;
+
+ smtp_state = (SMTPState *)alstate;
+ if (smtp_state == NULL) {
+ SCLogDebug("no SMTP state");
+ goto end;
+ }
+
+ if (flags & STREAM_TOSERVER)
+ ffc = smtp_state->files_ts;
+ else
+ goto end;
+
+ int match = DetectFileInspect(tv, det_ctx, f, s, flags, ffc);
+ if (match == 1) {
+ r = DETECT_ENGINE_INSPECT_SIG_MATCH;
+ } else if (match == 2) {
+ if (r != 1) {
+ SCLogDebug("sid %u can't match on this transaction", s->id);
+ r = DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+ } else if (match == 3) {
+ if (r != 1) {
+ SCLogDebug("sid %u can't match on this transaction (filestore sig)", s->id);
+ r = DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE;
+ }
+ }
+
+
+end:
+ SCReturnInt(r);
+}
diff --git a/framework/src/suricata/src/detect-engine-file.h b/framework/src/suricata/src/detect-engine-file.h
new file mode 100644
index 00000000..365ed8a9
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-file.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_FILE_H__
+#define __DETECT_ENGINE_FILE_H__
+
+int DetectFileInspectHttp(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id);
+
+int DetectFileInspectSmtp(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Signature *s,
+ Flow *f, uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id);
+
+#endif /* __DETECT_ENGINE_FILE_H__ */
diff --git a/framework/src/suricata/src/detect-engine-filedata-smtp.c b/framework/src/suricata/src/detect-engine-filedata-smtp.c
new file mode 100644
index 00000000..dc50d8c7
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-filedata-smtp.c
@@ -0,0 +1,563 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/** \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-smtp.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#define BUFFER_STEP 50
+
+static inline int SMTPCreateSpace(DetectEngineThreadCtx *det_ctx, uint16_t size)
+{
+ void *ptmp;
+ if (size > det_ctx->smtp_buffers_size) {
+ ptmp = SCRealloc(det_ctx->smtp,
+ (det_ctx->smtp_buffers_size + BUFFER_STEP) * sizeof(FiledataReassembledBody));
+ if (ptmp == NULL) {
+ SCFree(det_ctx->smtp);
+ det_ctx->smtp = NULL;
+ det_ctx->smtp_buffers_size = 0;
+ det_ctx->smtp_buffers_list_len = 0;
+ return -1;
+ }
+ det_ctx->smtp = ptmp;
+
+ memset(det_ctx->smtp + det_ctx->smtp_buffers_size, 0, BUFFER_STEP * sizeof(FiledataReassembledBody));
+ det_ctx->smtp_buffers_size += BUFFER_STEP;
+ }
+ for (int i = det_ctx->smtp_buffers_list_len; i < (size); i++) {
+ det_ctx->smtp[i].buffer_len = 0;
+ det_ctx->smtp[i].offset = 0;
+ }
+
+ return 0;
+}
+
+static uint8_t *DetectEngineSMTPGetBufferForTX(uint64_t tx_id,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Flow *f, File *curr_file,
+ uint8_t flags,
+ uint32_t *buffer_len,
+ uint32_t *stream_start_offset)
+{
+ int index = 0;
+ uint8_t *buffer = NULL;
+ *buffer_len = 0;
+ *stream_start_offset = 0;
+ FileData *curr_chunk = NULL;
+
+ if (det_ctx->smtp_buffers_list_len == 0) {
+ if (SMTPCreateSpace(det_ctx, 1) < 0)
+ goto end;
+ index = 0;
+
+ if (det_ctx->smtp_buffers_list_len == 0) {
+ det_ctx->smtp_start_tx_id = tx_id;
+ }
+ det_ctx->smtp_buffers_list_len++;
+ } else {
+ if ((tx_id - det_ctx->smtp_start_tx_id) < det_ctx->smtp_buffers_list_len) {
+ if (det_ctx->smtp[(tx_id - det_ctx->smtp_start_tx_id)].buffer_len != 0) {
+ *buffer_len = det_ctx->smtp[(tx_id - det_ctx->smtp_start_tx_id)].buffer_len;
+ *stream_start_offset = det_ctx->smtp[(tx_id - det_ctx->smtp_start_tx_id)].offset;
+ return det_ctx->smtp[(tx_id - det_ctx->smtp_start_tx_id)].buffer;
+ }
+ } else {
+ if (SMTPCreateSpace(det_ctx, (tx_id - det_ctx->smtp_start_tx_id) + 1) < 0)
+ goto end;
+
+ if (det_ctx->smtp_buffers_list_len == 0) {
+ det_ctx->smtp_start_tx_id = tx_id;
+ }
+ det_ctx->smtp_buffers_list_len++;
+ }
+ index = (tx_id - det_ctx->smtp_start_tx_id);
+ }
+
+ /* no new data */
+ if (curr_file->content_inspected == curr_file->content_len_so_far) {
+ SCLogDebug("no new data");
+ goto end;
+ }
+
+ curr_chunk = curr_file->chunks_head;
+ if (curr_chunk == NULL) {
+ SCLogDebug("no data chunks to inspect for this transaction");
+ goto end;
+ }
+
+ if ((smtp_config.content_limit == 0 ||
+ curr_file->content_len_so_far < smtp_config.content_limit) &&
+ curr_file->content_len_so_far < smtp_config.content_inspect_min_size &&
+ !(flags & STREAM_EOF)) {
+ SCLogDebug("we still haven't seen the entire content. "
+ "Let's defer content inspection till we see the "
+ "entire content.");
+ goto end;
+ }
+
+ int first = 1;
+ curr_chunk = curr_file->chunks_head;
+ while (curr_chunk != NULL) {
+ /* see if we can filter out chunks */
+ if (curr_file->content_inspected > 0) {
+ if (curr_chunk->stream_offset < curr_file->content_inspected) {
+ if ((curr_file->content_inspected - curr_chunk->stream_offset) > smtp_config.content_inspect_window) {
+ curr_chunk = curr_chunk->next;
+ continue;
+ } else {
+ /* include this one */
+ }
+ } else {
+ /* include this one */
+ }
+ }
+
+ if (first) {
+ det_ctx->smtp[index].offset = curr_chunk->stream_offset;
+ first = 0;
+ }
+
+ /* see if we need to grow the buffer */
+ if (det_ctx->smtp[index].buffer == NULL || (det_ctx->smtp[index].buffer_len + curr_chunk->len) > det_ctx->smtp[index].buffer_size) {
+ void *ptmp;
+ det_ctx->smtp[index].buffer_size += curr_chunk->len * 2;
+
+ if ((ptmp = SCRealloc(det_ctx->smtp[index].buffer, det_ctx->smtp[index].buffer_size)) == NULL) {
+ SCFree(det_ctx->smtp[index].buffer);
+ det_ctx->smtp[index].buffer = NULL;
+ det_ctx->smtp[index].buffer_size = 0;
+ det_ctx->smtp[index].buffer_len = 0;
+ goto end;
+ }
+ det_ctx->smtp[index].buffer = ptmp;
+ }
+ memcpy(det_ctx->smtp[index].buffer + det_ctx->smtp[index].buffer_len, curr_chunk->data, curr_chunk->len);
+ det_ctx->smtp[index].buffer_len += curr_chunk->len;
+
+ curr_chunk = curr_chunk->next;
+ }
+
+ /* updat inspected tracker */
+ curr_file->content_inspected = curr_file->chunks_tail->stream_offset + curr_file->chunks_tail->len;
+
+ buffer = det_ctx->smtp[index].buffer;
+ *buffer_len = det_ctx->smtp[index].buffer_len;
+ *stream_start_offset = det_ctx->smtp[index].offset;
+end:
+ return buffer;
+}
+
+int DetectEngineInspectSMTPFiledata(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ SMTPState *smtp_state = (SMTPState *)alstate;
+ FileContainer *ffc = smtp_state->files_ts;
+ int r = 0;
+ int match = 0;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = 0;
+
+ if (ffc != NULL) {
+ File *file = ffc->head;
+ for (; file != NULL; file = file->next) {
+ buffer = DetectEngineSMTPGetBufferForTX(tx_id,
+ de_ctx, det_ctx,
+ f, file,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+ if (buffer_len == 0)
+ goto end;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ match = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_FILEDATA],
+ f,
+ buffer,
+ buffer_len,
+ stream_start_offset,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_FD_SMTP, NULL);
+ if (match == 1)
+ r = 1;
+ }
+ }
+
+end:
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+void DetectEngineCleanSMTPBuffers(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->smtp_buffers_list_len > 0) {
+ for (int i = 0; i < det_ctx->smtp_buffers_list_len; i++) {
+ det_ctx->smtp[i].buffer_len = 0;
+ det_ctx->smtp[i].offset = 0;
+ }
+ }
+ det_ctx->smtp_buffers_list_len = 0;
+ det_ctx->smtp_start_tx_id = 0;
+
+ return;
+}
+
+int DetectEngineRunSMTPMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ SMTPState *smtp_state, uint8_t flags,
+ void *tx, uint64_t idx)
+{
+ FileContainer *ffc = smtp_state->files_ts;
+ uint32_t cnt = 0;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = 0;
+
+ if (ffc != NULL) {
+ File *file = ffc->head;
+ for (; file != NULL; file = file->next) {
+ buffer = DetectEngineSMTPGetBufferForTX(idx,
+ de_ctx, det_ctx,
+ f, file,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+
+ if (buffer_len == 0)
+ goto end;
+
+ cnt = SMTPFiledataPatternSearch(det_ctx, buffer, buffer_len, flags);
+ }
+ }
+end:
+ return cnt;
+}
+
+#ifdef UNITTESTS
+
+static int DetectEngineSMTPFiledataTest01(void)
+{
+ uint8_t mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
+ 0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
+ 0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
+ 0x20, 0x74, 0x65, 0x78, 0x74, 0x2F, 0x70, 0x6C,
+ 0x61, 0x69, 0x6E, 0x3B, 0x20, 0x63, 0x68, 0x61,
+ 0x72, 0x73, 0x65, 0x74, 0x3D, 0x55, 0x54, 0x46,
+ 0x2D, 0x38, 0x3B, 0x0D, 0x0A, 0x43, 0x6F, 0x6E,
+ 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61,
+ 0x6E, 0x73, 0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E,
+ 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20,
+ 0x37, 0x62, 0x69, 0x74, 0x0D, 0x0A, 0x43, 0x6F,
+ 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x44, 0x69,
+ 0x73, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F,
+ 0x6E, 0x3A, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
+ 0x68, 0x6D, 0x65, 0x6E, 0x74, 0x3B, 0x20, 0x66,
+ 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+ 0x22, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x74, 0x78,
+ 0x74, 0x22, 0x0D, 0x0A, 0x0D, 0x0A, 0x6d, 0x65,
+ 0x73, 0x73, 0x61, 0x67, 0x65,};
+ uint32_t mimemsg_len = sizeof(mimemsg) - 1;
+ TcpSession ssn;
+ Packet *p;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ SMTPState *smtp_state = NULL;
+ Flow f;
+ int result = 0;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alstate = SMTPStateAlloc();
+
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ state->body_begin = 1;
+
+ if (SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state) != 0)
+ goto end;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SMTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert smtp any any -> any any "
+ "(msg:\"file_data smtp test\"; "
+ "file_data; content:\"message\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER, mimemsg, mimemsg_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %d", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineSMTPFiledataTest02(void)
+{
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert smtp any any -> any any "
+ "(msg:\"file_data smtp test\"; "
+ "file_data; content:\"message\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ if (s->flags & SIG_FLAG_TOSERVER)
+ result = 1;
+ else if (s->flags & SIG_FLAG_TOCLIENT)
+ printf("s->flags & SIG_FLAG_TOCLIENT");
+
+end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+
+}
+
+static int DetectEngineSMTPFiledataTest03(void)
+{
+ uint8_t mimemsg1[] = {0x65, 0x76,};
+ uint8_t mimemsg2[] = {0x69, 0x6C,};
+ uint32_t mimemsg1_len = sizeof(mimemsg1) - 1;
+ uint32_t mimemsg2_len = sizeof(mimemsg2) - 1;
+ TcpSession ssn;
+ Packet *p;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ SMTPState *smtp_state = NULL;
+ Flow f;
+ int result = 1;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alstate = SMTPStateAlloc();
+
+ MimeDecParseState *state = MimeDecInitParser(&f, NULL);
+ ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
+ state->body_begin = 1;
+
+ if (SMTPProcessDataChunk((uint8_t *)mimemsg1, sizeof(mimemsg1), state) != 0)
+ goto end;
+
+ if (SMTPProcessDataChunk((uint8_t *)mimemsg2, sizeof(mimemsg2), state) != 0)
+ goto end;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SMTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert smtp any any -> any any "
+ "(msg:\"file_data smtp test\"; "
+ "file_data; content:\"evil\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = 0;
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER, mimemsg1, mimemsg1_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %d", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SMTP, STREAM_TOSERVER, mimemsg2, mimemsg2_len);
+ if (r != 0) {
+ printf("AppLayerParse for smtp failed. Returned %d", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ smtp_state = f.alstate;
+ if (smtp_state == NULL) {
+ printf("no smtp state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ result = 0;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineSMTPFiledataRegisterTests(void)
+{
+ #ifdef UNITTESTS
+ UtRegisterTest("DetectEngineSMTPFiledataTest01",
+ DetectEngineSMTPFiledataTest01, 1);
+ UtRegisterTest("DetectEngineSMTPFiledataTest02",
+ DetectEngineSMTPFiledataTest02, 1);
+ UtRegisterTest("DetectEngineSMTPFiledataTest03",
+ DetectEngineSMTPFiledataTest03, 0);
+ #endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-filedata-smtp.h b/framework/src/suricata/src/detect-engine-filedata-smtp.h
new file mode 100644
index 00000000..e04832b7
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-filedata-smtp.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_FILEDATA_SMTP_H__
+#define __DETECT_ENGINE_FILEDATA_SMTP_H__
+
+#include "app-layer-smtp.h"
+
+int DetectEngineInspectSMTPFiledata(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineCleanSMTPBuffers(DetectEngineThreadCtx *det_ctx);
+
+int DetectEngineRunSMTPMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ SMTPState *smtp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+
+void DetectEngineSMTPFiledataRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_FILEDATA_SMTP_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hcbd.c b/framework/src/suricata/src/detect-engine-hcbd.c
new file mode 100644
index 00000000..09b7980a
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hcbd.c
@@ -0,0 +1,1116 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP request body match corresponding to http_client_body
+ * keyword.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "app-layer-parser.h"
+
+#include "stream-tcp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#define BUFFER_STEP 50
+
+static inline int HCBDCreateSpace(DetectEngineThreadCtx *det_ctx, uint64_t size)
+{
+ if (size >= (USHRT_MAX - BUFFER_STEP))
+ return -1;
+
+ void *ptmp;
+ if (size > det_ctx->hcbd_buffers_size) {
+ ptmp = SCRealloc(det_ctx->hcbd,
+ (det_ctx->hcbd_buffers_size + BUFFER_STEP) * sizeof(HttpReassembledBody));
+ if (ptmp == NULL) {
+ SCFree(det_ctx->hcbd);
+ det_ctx->hcbd = NULL;
+ det_ctx->hcbd_buffers_size = 0;
+ det_ctx->hcbd_buffers_list_len = 0;
+ return -1;
+ }
+ det_ctx->hcbd = ptmp;
+
+ memset(det_ctx->hcbd + det_ctx->hcbd_buffers_size, 0, BUFFER_STEP * sizeof(HttpReassembledBody));
+ det_ctx->hcbd_buffers_size += BUFFER_STEP;
+
+ uint16_t i;
+ for (i = det_ctx->hcbd_buffers_list_len; i < ((uint16_t)size); i++) {
+ det_ctx->hcbd[i].buffer_len = 0;
+ det_ctx->hcbd[i].offset = 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ */
+static uint8_t *DetectEngineHCBDGetBufferForTX(htp_tx_t *tx, uint64_t tx_id,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Flow *f, HtpState *htp_state,
+ uint8_t flags,
+ uint32_t *buffer_len,
+ uint32_t *stream_start_offset)
+{
+ int index = 0;
+ uint8_t *buffer = NULL;
+ *buffer_len = 0;
+ *stream_start_offset = 0;
+
+ if (det_ctx->hcbd_buffers_list_len == 0) {
+ /* get the inspect id to use as a 'base id' */
+ uint64_t base_inspect_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ BUG_ON(base_inspect_id > tx_id);
+ /* see how many space we need for the current tx_id */
+ uint64_t txs = (tx_id - base_inspect_id) + 1;
+ if (HCBDCreateSpace(det_ctx, txs) < 0)
+ goto end;
+
+ index = (tx_id - base_inspect_id);
+ det_ctx->hcbd_start_tx_id = base_inspect_id;
+ det_ctx->hcbd_buffers_list_len = txs;
+ } else {
+ if ((tx_id - det_ctx->hcbd_start_tx_id) < det_ctx->hcbd_buffers_list_len) {
+ if (det_ctx->hcbd[(tx_id - det_ctx->hcbd_start_tx_id)].buffer_len != 0) {
+ *buffer_len = det_ctx->hcbd[(tx_id - det_ctx->hcbd_start_tx_id)].buffer_len;
+ *stream_start_offset = det_ctx->hcbd[(tx_id - det_ctx->hcbd_start_tx_id)].offset;
+ return det_ctx->hcbd[(tx_id - det_ctx->hcbd_start_tx_id)].buffer;
+ }
+ } else {
+ uint64_t txs = (tx_id - det_ctx->hcbd_start_tx_id) + 1;
+ if (HCBDCreateSpace(det_ctx, txs) < 0)
+ goto end; /* let's consider it as stage not done for now */
+
+ det_ctx->hcbd_buffers_list_len = txs;
+ }
+ index = (tx_id - det_ctx->hcbd_start_tx_id);
+ }
+
+ HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (htud == NULL) {
+ SCLogDebug("no htud");
+ goto end;
+ }
+
+ /* no new data */
+ if (htud->request_body.body_inspected == htud->request_body.content_len_so_far) {
+ SCLogDebug("no new data");
+ goto end;
+ }
+
+ HtpBodyChunk *cur = htud->request_body.first;
+ if (cur == NULL) {
+ SCLogDebug("No http chunks to inspect for this transacation");
+ goto end;
+ }
+
+ /* inspect the body if the transfer is complete or we have hit
+ * our body size limit */
+ if ((htp_state->cfg->request_body_limit == 0 ||
+ htud->request_body.content_len_so_far < htp_state->cfg->request_body_limit) &&
+ htud->request_body.content_len_so_far < htp_state->cfg->request_inspect_min_size &&
+ !(AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_BODY) &&
+ !(flags & STREAM_EOF)) {
+ SCLogDebug("we still haven't seen the entire request body. "
+ "Let's defer body inspection till we see the "
+ "entire body.");
+ goto end;
+ }
+
+ int first = 1;
+ while (cur != NULL) {
+ /* see if we can filter out chunks */
+ if (htud->request_body.body_inspected > 0) {
+ if (cur->stream_offset < htud->request_body.body_inspected) {
+ if ((htud->request_body.body_inspected - cur->stream_offset) > htp_state->cfg->request_inspect_min_size) {
+ cur = cur->next;
+ continue;
+ } else {
+ /* include this one */
+ }
+ } else {
+ /* include this one */
+ }
+ }
+
+ if (first) {
+ det_ctx->hcbd[index].offset = cur->stream_offset;
+ first = 0;
+ }
+
+ /* see if we need to grow the buffer */
+ if (det_ctx->hcbd[index].buffer == NULL || (det_ctx->hcbd[index].buffer_len + cur->len) > det_ctx->hcbd[index].buffer_size) {
+ void *ptmp;
+ det_ctx->hcbd[index].buffer_size += cur->len * 2;
+
+ if ((ptmp = SCRealloc(det_ctx->hcbd[index].buffer, det_ctx->hcbd[index].buffer_size)) == NULL) {
+ SCFree(det_ctx->hcbd[index].buffer);
+ det_ctx->hcbd[index].buffer = NULL;
+ det_ctx->hcbd[index].buffer_size = 0;
+ det_ctx->hcbd[index].buffer_len = 0;
+ goto end;
+ }
+ det_ctx->hcbd[index].buffer = ptmp;
+ }
+ memcpy(det_ctx->hcbd[index].buffer + det_ctx->hcbd[index].buffer_len, cur->data, cur->len);
+ det_ctx->hcbd[index].buffer_len += cur->len;
+
+ cur = cur->next;
+ }
+
+ /* update inspected tracker */
+ htud->request_body.body_inspected = htud->request_body.last->stream_offset + htud->request_body.last->len;
+
+ buffer = det_ctx->hcbd[index].buffer;
+ *buffer_len = det_ctx->hcbd[index].buffer_len;
+ *stream_start_offset = det_ctx->hcbd[index].offset;
+ end:
+ return buffer;
+}
+
+int DetectEngineRunHttpClientBodyMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = DetectEngineHCBDGetBufferForTX(tx, idx,
+ de_ctx, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+ if (buffer_len == 0)
+ goto end;
+
+ cnt = HttpClientBodyPatternSearch(det_ctx, buffer, buffer_len, flags);
+
+ end:
+ return cnt;
+}
+
+int DetectEngineInspectHttpClientBody(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *tx, uint64_t tx_id)
+{
+ HtpState *htp_state = (HtpState *)alstate;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = DetectEngineHCBDGetBufferForTX(tx, tx_id,
+ de_ctx, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+ if (buffer_len == 0)
+ goto end;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HCBDMATCH],
+ f,
+ buffer,
+ buffer_len,
+ stream_start_offset,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCBD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+
+ end:
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_BODY)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+void DetectEngineCleanHCBDBuffers(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->hcbd_buffers_list_len > 0) {
+ for (int i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
+ det_ctx->hcbd[i].buffer_len = 0;
+ det_ctx->hcbd[i].offset = 0;
+ }
+ }
+ det_ctx->hcbd_buffers_list_len = 0;
+ det_ctx->hcbd_start_tx_id = 0;
+
+ return;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+struct TestSteps {
+ const uint8_t *input;
+ size_t input_size; /**< if 0 strlen will be used */
+ int direction; /**< STREAM_TOSERVER, STREAM_TOCLIENT */
+ int expect;
+};
+
+static int RunTest (struct TestSteps *steps, const char *sig, const char *yaml)
+{
+ TcpSession ssn;
+ Flow f;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ if (yaml) {
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(yaml, strlen(yaml));
+ HTPConfigure();
+ }
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ SCLogDebug("sig %s", sig);
+ DetectEngineAppendSig(de_ctx, (char *)sig);
+
+ de_ctx->flags |= DE_QUIET;
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ struct TestSteps *b = steps;
+ int i = 0;
+ while (b->input != NULL) {
+ SCLogDebug("chunk %p %d", b, i);
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+ p->flow = &f;
+ p->flowflags = (b->direction == STREAM_TOSERVER) ? FLOW_PKT_TOSERVER : FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, b->direction,
+ (uint8_t *)b->input,
+ b->input_size ? b->input_size : strlen((const char *)b->input));
+ if (r != 0) {
+ printf("toserver chunk %d returned %" PRId32 ", expected 0: ", i+1, r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ int match = PacketAlertCheck(p, 1);
+ if (b->expect != match) {
+ printf("rule matching mismatch: ");
+ goto end;
+ }
+
+ UTHFreePackets(&p, 1);
+ p = NULL;
+ b++;
+ i++;
+ }
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+
+ if (yaml) {
+ HtpConfigRestoreBackup();
+ ConfRestoreContextBackup();
+ }
+ return result;
+}
+
+static int DetectEngineHttpClientBodyTest01(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1This\"; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest02(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; offset:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest03(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; offset:16; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest04(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:!\"body1\"; http_client_body; offset:16; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest05(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; depth:25; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest06(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:!\"body1\"; http_client_body; depth:25; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest07(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:!\"body1\"; http_client_body; depth:15; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest08(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"This is dummy body1This is dummy message body2\"; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest09(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"This\"; http_client_body; within:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest10(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:!\"boom\"; http_client_body; within:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest11(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"boom\"; http_client_body; within:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest12(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:!\"This\"; http_client_body; within:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest13(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"dummy\"; http_client_body; distance:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest14(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:!\"dummy\"; http_client_body; distance:10; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest15(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"dummy\"; http_client_body; distance:10; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest16(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:!\"dummy\"; http_client_body; distance:5; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest17(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"bambu\"; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest18(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"body1\"; http_client_body; content:\"bambu\"; http_client_body; fast_pattern; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest19(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"bambu\"; http_client_body; content:\"is\"; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest20(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"is\"; http_client_body; fast_pattern; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest21(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:!\"dummy\"; http_client_body; within:7; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest22(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:!\"dummy\"; within:7; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest23(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:!\"dummy\"; distance:3; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest24(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:!\"dummy\"; distance:13; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest25(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:\"dummy\"; within:15; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest26(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:\"dummy\"; within:10; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest27(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:\"dummy\"; distance:8; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest28(void)
+{
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (pcre:/body1/P; content:\"dummy\"; distance:14; http_client_body; sid:1;)";
+ return RunTest(steps, sig, NULL);
+}
+
+static int DetectEngineHttpClientBodyTest29(void)
+{
+ const char *request_buffer = "GET /one HTTP/1.0\r\n"
+ "Host: localhost\r\n\r\n";
+#define TOTAL_REQUESTS 45
+ uint8_t *http_buf = SCMalloc(TOTAL_REQUESTS * strlen(request_buffer));
+ if (unlikely(http_buf == NULL))
+ return 0;
+ for (int i = 0; i < TOTAL_REQUESTS; i++) {
+ memcpy(http_buf + i * strlen(request_buffer), request_buffer,
+ strlen(request_buffer));
+ }
+ uint32_t http_buf_len = TOTAL_REQUESTS * strlen(request_buffer);
+#undef TOTAL_REQUESTS
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)http_buf,
+ (size_t)http_buf_len, STREAM_TOSERVER, 0 },
+
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 5\r\n"
+ "\r\n"
+ "dummy",
+ 0, STREAM_TOCLIENT, 0 },
+
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"dummyone\"; fast_pattern:0,3; http_server_body; sid:1;)";
+ int result = RunTest(steps, sig, NULL);
+ SCFree(http_buf);
+ return result;
+}
+
+static int DetectEngineHttpClientBodyTest30(void)
+{
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+\n\
+ request-body-inspect-window: 0\n\
+ response-body-inspect-window: 0\n\
+ request-body-minimal-inspect-size: 0\n\
+ response-body-minimal-inspect-size: 0\n\
+";
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"bags\"; within:4; http_client_body; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpClientBodyTest31(void)
+{
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+\n\
+ request-body-inspect-window: 0\n\
+ response-body-inspect-window: 0\n\
+ request-body-minimal-inspect-size: 0\n\
+ response-body-minimal-inspect-size: 0\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"This is dummy message body2",
+ 0, STREAM_TOSERVER, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (content:\"bags\"; depth:4; http_client_body; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpClientBodyRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpClientBodyTest01",
+ DetectEngineHttpClientBodyTest01, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest02",
+ DetectEngineHttpClientBodyTest02, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest03",
+ DetectEngineHttpClientBodyTest03, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest04",
+ DetectEngineHttpClientBodyTest04, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest05",
+ DetectEngineHttpClientBodyTest05, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest06",
+ DetectEngineHttpClientBodyTest06, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest07",
+ DetectEngineHttpClientBodyTest07, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest08",
+ DetectEngineHttpClientBodyTest08, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest09",
+ DetectEngineHttpClientBodyTest09, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest10",
+ DetectEngineHttpClientBodyTest10, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest11",
+ DetectEngineHttpClientBodyTest11, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest12",
+ DetectEngineHttpClientBodyTest12, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest13",
+ DetectEngineHttpClientBodyTest13, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest14",
+ DetectEngineHttpClientBodyTest14, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest15",
+ DetectEngineHttpClientBodyTest15, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest16",
+ DetectEngineHttpClientBodyTest16, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest17",
+ DetectEngineHttpClientBodyTest17, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest18",
+ DetectEngineHttpClientBodyTest18, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest19",
+ DetectEngineHttpClientBodyTest19, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest20",
+ DetectEngineHttpClientBodyTest20, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest21",
+ DetectEngineHttpClientBodyTest21, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest22",
+ DetectEngineHttpClientBodyTest22, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest23",
+ DetectEngineHttpClientBodyTest23, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest24",
+ DetectEngineHttpClientBodyTest24, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest25",
+ DetectEngineHttpClientBodyTest25, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest26",
+ DetectEngineHttpClientBodyTest26, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest27",
+ DetectEngineHttpClientBodyTest27, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest28",
+ DetectEngineHttpClientBodyTest28, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest29",
+ DetectEngineHttpClientBodyTest29, 1);
+
+ UtRegisterTest("DetectEngineHttpClientBodyTest30",
+ DetectEngineHttpClientBodyTest30, 1);
+ UtRegisterTest("DetectEngineHttpClientBodyTest31",
+ DetectEngineHttpClientBodyTest31, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hcbd.h b/framework/src/suricata/src/detect-engine-hcbd.h
new file mode 100644
index 00000000..6dce3230
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hcbd.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HCBD_H__
+#define __DETECT_ENGINE_HCBD_H__
+
+#define ENGINE_HCBD_BUFFER_LIMIT 20000
+
+#include "app-layer-htp.h"
+
+int DetectEngineRunHttpClientBodyMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+int DetectEngineInspectHttpClientBody(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineCleanHCBDBuffers(DetectEngineThreadCtx *);
+
+void DetectEngineHttpClientBodyRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HCBD_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-hcd.c b/framework/src/suricata/src/detect-engine-hcd.c
new file mode 100644
index 00000000..5fcfa51f
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hcd.c
@@ -0,0 +1,1878 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP cookie match
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-hcd.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+int DetectEngineRunHttpCookieMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_headers == NULL)
+ goto end;
+
+ htp_header_t *h = NULL;
+ if (flags & STREAM_TOSERVER) {
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "Cookie");
+ if (h == NULL) {
+ SCLogDebug("HTTP cookie header not present in this request");
+ goto end;
+ }
+ } else {
+ h = (htp_header_t *)htp_table_get_c(tx->response_headers,
+ "Set-Cookie");
+ if (h == NULL) {
+ SCLogDebug("HTTP Set-Cookie header not present in this request");
+ goto end;
+ }
+ }
+
+ cnt = HttpCookiePatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value), flags);
+ end:
+ return cnt;
+}
+
+/**
+ * \brief Do the http_cookie content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpCookie(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ htp_header_t *h = NULL;
+ if (flags & STREAM_TOSERVER) {
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "Cookie");
+ if (h == NULL) {
+ SCLogDebug("HTTP cookie header not present in this request");
+ goto end;
+ }
+ } else {
+ h = (htp_header_t *)htp_table_get_c(tx->response_headers,
+ "Set-Cookie");
+ if (h == NULL) {
+ SCLogDebug("HTTP Set-Cookie header not present in this request");
+ goto end;
+ }
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HCDMATCH],
+ f,
+ (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CONNECT\"; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; depth:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"ECT\"; depth:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"ECT\"; depth:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"CON\"; depth:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"ECT\"; offset:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"CO\"; offset:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"ECT\"; offset:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CON\"; offset:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:\"EC\"; within:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:!\"EC\"; within:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:\"EC\"; within:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:!\"EC\"; within:4; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:\"EC\"; distance:2; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:!\"EC\"; distance:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:\"EC\"; distance:3; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_cookie content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpCookieTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Cookie: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_cookie; "
+ "content:!\"EC\"; distance:2; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpCookieRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpCookieTest01",
+ DetectEngineHttpCookieTest01, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest02",
+ DetectEngineHttpCookieTest02, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest03",
+ DetectEngineHttpCookieTest03, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest04",
+ DetectEngineHttpCookieTest04, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest05",
+ DetectEngineHttpCookieTest05, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest06",
+ DetectEngineHttpCookieTest06, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest07",
+ DetectEngineHttpCookieTest07, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest08",
+ DetectEngineHttpCookieTest08, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest09",
+ DetectEngineHttpCookieTest09, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest10",
+ DetectEngineHttpCookieTest10, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest11",
+ DetectEngineHttpCookieTest11, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest12",
+ DetectEngineHttpCookieTest12, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest13",
+ DetectEngineHttpCookieTest13, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest14",
+ DetectEngineHttpCookieTest14, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest15",
+ DetectEngineHttpCookieTest15, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest16",
+ DetectEngineHttpCookieTest16, 1);
+ UtRegisterTest("DetectEngineHttpCookieTest17",
+ DetectEngineHttpCookieTest17, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hcd.h b/framework/src/suricata/src/detect-engine-hcd.h
new file mode 100644
index 00000000..78f92407
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hcd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HCD_H__
+#define __DETECT_ENGINE_HCD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpCookie(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpCookieMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpCookieRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HCD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hhd.c b/framework/src/suricata/src/detect-engine-hhd.c
new file mode 100644
index 00000000..3bec4fd2
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hhd.c
@@ -0,0 +1,3905 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP header match
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-hhd.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-memcmp.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+#define BUFFER_STEP 50
+
+static inline int HHDCreateSpace(DetectEngineThreadCtx *det_ctx, uint64_t size)
+{
+ if (size >= (USHRT_MAX - BUFFER_STEP))
+ return -1;
+
+ void *ptmp;
+ if (size > det_ctx->hhd_buffers_size) {
+ ptmp = SCRealloc(det_ctx->hhd_buffers,
+ (det_ctx->hhd_buffers_size + BUFFER_STEP) * sizeof(uint8_t *));
+ if (ptmp == NULL) {
+ SCFree(det_ctx->hhd_buffers);
+ det_ctx->hhd_buffers = NULL;
+ det_ctx->hhd_buffers_size = 0;
+ det_ctx->hhd_buffers_list_len = 0;
+ return -1;
+ }
+ det_ctx->hhd_buffers = ptmp;
+
+ memset(det_ctx->hhd_buffers + det_ctx->hhd_buffers_size, 0, BUFFER_STEP * sizeof(uint8_t *));
+ ptmp = SCRealloc(det_ctx->hhd_buffers_len,
+ (det_ctx->hhd_buffers_size + BUFFER_STEP) * sizeof(uint32_t));
+ if (ptmp == NULL) {
+ SCFree(det_ctx->hhd_buffers_len);
+ det_ctx->hhd_buffers_len = NULL;
+ det_ctx->hhd_buffers_size = 0;
+ det_ctx->hhd_buffers_list_len = 0;
+ return -1;
+ }
+ det_ctx->hhd_buffers_len = ptmp;
+
+ memset(det_ctx->hhd_buffers_len + det_ctx->hhd_buffers_size, 0, BUFFER_STEP * sizeof(uint32_t));
+ det_ctx->hhd_buffers_size += BUFFER_STEP;
+ }
+ memset(det_ctx->hhd_buffers_len + det_ctx->hhd_buffers_list_len, 0, (size - det_ctx->hhd_buffers_list_len) * sizeof(uint32_t));
+
+ return 0;
+}
+
+static uint8_t *DetectEngineHHDGetBufferForTX(htp_tx_t *tx, uint64_t tx_id,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Flow *f, HtpState *htp_state,
+ uint8_t flags,
+ uint32_t *buffer_len)
+{
+ uint8_t *headers_buffer = NULL;
+ int index = 0;
+ *buffer_len = 0;
+
+ if (det_ctx->hhd_buffers_list_len == 0) {
+ /* get the inspect id to use as a 'base id' */
+ uint64_t base_inspect_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ BUG_ON(base_inspect_id > tx_id);
+ /* see how many space we need for the current tx_id */
+ uint64_t txs = (tx_id - base_inspect_id) + 1;
+ if (HHDCreateSpace(det_ctx, txs) < 0)
+ goto end;
+
+ index = (tx_id - base_inspect_id);
+ det_ctx->hhd_start_tx_id = base_inspect_id;
+ det_ctx->hhd_buffers_list_len = txs;
+ } else {
+ /* tx fits in our current buffers */
+ if ((tx_id - det_ctx->hhd_start_tx_id) < det_ctx->hhd_buffers_list_len) {
+ /* if we previously reassembled, return that buffer */
+ if (det_ctx->hhd_buffers_len[(tx_id - det_ctx->hhd_start_tx_id)] != 0) {
+ *buffer_len = det_ctx->hhd_buffers_len[(tx_id - det_ctx->hhd_start_tx_id)];
+ return det_ctx->hhd_buffers[(tx_id - det_ctx->hhd_start_tx_id)];
+ }
+ /* otherwise fall through */
+ } else {
+ /* not enough space, lets expand */
+ uint64_t txs = (tx_id - det_ctx->hhd_start_tx_id) + 1;
+ if (HHDCreateSpace(det_ctx, txs) < 0)
+ goto end;
+
+ det_ctx->hhd_buffers_list_len = txs;
+ }
+ index = (tx_id - det_ctx->hhd_start_tx_id);
+ }
+
+ htp_table_t *headers;
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <= HTP_REQUEST_HEADERS)
+ goto end;
+ headers = tx->request_headers;
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <= HTP_RESPONSE_HEADERS)
+ goto end;
+ headers = tx->response_headers;
+ }
+ if (headers == NULL)
+ goto end;
+
+ htp_header_t *h = NULL;
+ headers_buffer = det_ctx->hhd_buffers[index];
+ size_t headers_buffer_len = 0;
+ size_t i = 0;
+
+ size_t no_of_headers = htp_table_size(headers);
+ for (; i < no_of_headers; i++) {
+ h = htp_table_get_index(headers, i, NULL);
+ size_t size1 = bstr_size(h->name);
+ size_t size2 = bstr_size(h->value);
+
+ if (flags & STREAM_TOSERVER) {
+ if (size1 == 6 &&
+ SCMemcmpLowercase("cookie", bstr_ptr(h->name), 6) == 0) {
+ continue;
+ }
+ } else {
+ if (size1 == 10 &&
+ SCMemcmpLowercase("set-cookie", bstr_ptr(h->name), 10) == 0) {
+ continue;
+ }
+ }
+
+ /* the extra 4 bytes if for ": " and "\r\n" */
+ uint8_t *new_headers_buffer = SCRealloc(headers_buffer, headers_buffer_len + size1 + size2 + 4);
+ if (unlikely(new_headers_buffer == NULL)) {
+ if (headers_buffer != NULL) {
+ SCFree(headers_buffer);
+ headers_buffer = NULL;
+ }
+ det_ctx->hhd_buffers[index] = NULL;
+ det_ctx->hhd_buffers_len[index] = 0;
+ goto end;
+ }
+ headers_buffer = new_headers_buffer;
+
+ memcpy(headers_buffer + headers_buffer_len, bstr_ptr(h->name), size1);
+ headers_buffer_len += size1;
+ headers_buffer[headers_buffer_len] = ':';
+ headers_buffer[headers_buffer_len + 1] = ' ';
+ headers_buffer_len += 2;
+ memcpy(headers_buffer + headers_buffer_len, bstr_ptr(h->value), size2);
+ headers_buffer_len += size2 + 2;
+ /* \r */
+ headers_buffer[headers_buffer_len - 2] = '\r';
+ /* \n */
+ headers_buffer[headers_buffer_len - 1] = '\n';
+ }
+
+ /* store the buffers. We will need it for further inspection */
+ det_ctx->hhd_buffers[index] = headers_buffer;
+ det_ctx->hhd_buffers_len[index] = headers_buffer_len;
+
+ *buffer_len = (uint32_t)headers_buffer_len;
+ end:
+ return headers_buffer;
+}
+
+int DetectEngineRunHttpHeaderMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ uint32_t buffer_len = 0;
+ uint8_t *buffer = DetectEngineHHDGetBufferForTX(tx, idx,
+ NULL, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len);
+ if (buffer_len == 0)
+ goto end;
+
+ cnt = HttpHeaderPatternSearch(det_ctx, buffer, buffer_len, flags);
+
+ end:
+ return cnt;
+}
+
+int DetectEngineInspectHttpHeader(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ HtpState *htp_state = (HtpState *)alstate;
+ uint32_t buffer_len = 0;
+ uint8_t *buffer = DetectEngineHHDGetBufferForTX(tx, tx_id,
+ de_ctx, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len);
+ if (buffer_len == 0)
+ goto end;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HHDMATCH],
+ f,
+ buffer,
+ buffer_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HHD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+void DetectEngineCleanHHDBuffers(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->hhd_buffers_list_len != 0) {
+ int i;
+ for (i = 0; i < det_ctx->hhd_buffers_list_len; i++) {
+ det_ctx->hhd_buffers_len[i] = 0;
+ }
+ det_ctx->hhd_buffers_list_len = 0;
+ }
+ det_ctx->hhd_start_tx_id = 0;
+
+ return;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; depth:15; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"one\"; depth:5; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; depth:5; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"one\"; depth:15; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; offset:10; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"one\"; offset:15; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; offset:15; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"one\"; offset:10; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:\"three\"; http_header; within:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:!\"three\"; http_header; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:!\"three\"; http_header; within:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:\"three\"; http_header; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:\"five\"; http_header; distance:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:!\"five\"; http_header; distance:15; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:!\"five\"; http_header; distance:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:\"five\"; http_header; distance:15; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest18(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "Host: www.onetwothreefourfivesixsevenfive.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; content:\"five\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = HttpHeaderPatternSearch(det_ctx, http_buf, http_len, STREAM_TOSERVER);
+ if (r != 2) {
+ printf("expected result 2, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHeaderTest19(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "Host: www.onetwothreefourfivesixsevenfive.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"one\"; http_header; fast_pattern; content:\"five\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = HttpHeaderPatternSearch(det_ctx, http_buf, http_len, STREAM_TOSERVER);
+ if (r != 1) {
+ printf("expected result 1, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest20(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:!\"dummy\"; http_header; within:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest21(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:!\"dummy\"; within:7; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest22(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:!\"dummy\"; distance:3; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest23(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:!\"dummy\"; distance:13; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest24(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:\"dummy\"; within:15; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest25(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:\"dummy\"; within:10; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest26(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:\"dummy\"; distance:8; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest27(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "pcre:/body1/H; "
+ "content:\"dummy\"; distance:14; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest28(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Content-Length: 6\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1,
+ http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpHeaderTest29(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Content-Length: 7\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1,
+ http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#if 0
+
+static int DetectEngineHttpHeaderTest30(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Content-Length: 6\"; http_header; "
+ "content:\"User-Agent: Mozilla\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* #if 0 */
+
+static int DetectEngineHttpHeaderTest30(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Set-Cookie: dummycookieset\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"dummycookieset\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1,
+ http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test reassembly bug where headers with names of length 6 were
+ * skipped
+ */
+static int DetectEngineHttpHeaderTest31(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Accept: blah\r\n"
+ "Cookie: blah\r\n"
+ "Crazy6: blah\r\n"
+ "SixZix: blah\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(content:\"Accept|3a|\"; http_header; "
+ "content:!\"Cookie|3a|\"; http_header; "
+ "content:\"Crazy6|3a|\"; http_header; "
+ "content:\"SixZix|3a|\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/**
+ * \test Trailing headers.
+ */
+static int DetectEngineHttpHeaderTest32(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "host: boom\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "13\r\n"
+ "This is dummy body1\r\n"
+ "0\r\n"
+ "Dummy-Header: kaboom\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(content:\"Dummy\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/**
+ * \test Trailing headers.
+ */
+static int DetectEngineHttpHeaderTest33(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "host: boom\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "13\r\n"
+ "This is dummy body1\r\n"
+ "0\r\n";
+ uint8_t http2_buf[] =
+ "Dummy-Header: kaboom\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(content:\"Dummy\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpHeaderRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpHeaderTest01",
+ DetectEngineHttpHeaderTest01, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest02",
+ DetectEngineHttpHeaderTest02, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest03",
+ DetectEngineHttpHeaderTest03, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest04",
+ DetectEngineHttpHeaderTest04, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest05",
+ DetectEngineHttpHeaderTest05, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest06",
+ DetectEngineHttpHeaderTest06, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest07",
+ DetectEngineHttpHeaderTest07, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest08",
+ DetectEngineHttpHeaderTest08, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest09",
+ DetectEngineHttpHeaderTest09, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest10",
+ DetectEngineHttpHeaderTest10, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest11",
+ DetectEngineHttpHeaderTest11, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest12",
+ DetectEngineHttpHeaderTest12, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest13",
+ DetectEngineHttpHeaderTest13, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest14",
+ DetectEngineHttpHeaderTest14, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest15",
+ DetectEngineHttpHeaderTest15, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest16",
+ DetectEngineHttpHeaderTest16, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest17",
+ DetectEngineHttpHeaderTest17, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest18",
+ DetectEngineHttpHeaderTest18, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest19",
+ DetectEngineHttpHeaderTest19, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest20",
+ DetectEngineHttpHeaderTest20, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest21",
+ DetectEngineHttpHeaderTest21, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest22",
+ DetectEngineHttpHeaderTest22, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest23",
+ DetectEngineHttpHeaderTest23, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest24",
+ DetectEngineHttpHeaderTest24, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest25",
+ DetectEngineHttpHeaderTest25, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest26",
+ DetectEngineHttpHeaderTest26, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest27",
+ DetectEngineHttpHeaderTest27, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest28",
+ DetectEngineHttpHeaderTest28, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest29",
+ DetectEngineHttpHeaderTest29, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest30",
+ DetectEngineHttpHeaderTest30, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest31",
+ DetectEngineHttpHeaderTest31, 1);
+#if 0
+ UtRegisterTest("DetectEngineHttpHeaderTest30",
+ DetectEngineHttpHeaderTest30, 1);
+#endif
+ UtRegisterTest("DetectEngineHttpHeaderTest32",
+ DetectEngineHttpHeaderTest32, 1);
+ UtRegisterTest("DetectEngineHttpHeaderTest33",
+ DetectEngineHttpHeaderTest33, 1);
+
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hhd.h b/framework/src/suricata/src/detect-engine-hhd.h
new file mode 100644
index 00000000..e163000c
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hhd.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HHD_H__
+#define __DETECT_ENGINE_HHD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpHeader(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpHeaderMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineCleanHHDBuffers(DetectEngineThreadCtx *det_ctx);
+
+void DetectEngineHttpHeaderRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HHD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hhhd.c b/framework/src/suricata/src/detect-engine-hhhd.c
new file mode 100644
index 00000000..65aebd07
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hhhd.c
@@ -0,0 +1,2616 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP host header.
+ * HHHD - Http Host Header Data
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+#include "detect-engine-hhhd.h"
+
+int DetectEngineRunHttpHHMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_hostname == NULL)
+ goto end;
+ uint8_t *hname = (uint8_t *)bstr_ptr(tx->request_hostname);
+ if (hname == NULL)
+ goto end;
+ uint32_t hname_len = bstr_len(tx->request_hostname);
+
+ cnt += HttpHHPatternSearch(det_ctx, hname, hname_len, flags);
+
+ end:
+ return cnt;
+}
+
+/**
+ * \brief Do the http_header content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpHH(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->parsed_uri == NULL || tx->request_hostname == NULL)
+ goto end;
+ uint8_t *hname = (uint8_t *)bstr_ptr(tx->request_hostname);
+ if (hname == NULL)
+ goto end;
+ uint32_t hname_len = bstr_len(tx->request_hostname);
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HHHDMATCH],
+ f,
+ hname, hname_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HHHD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"connect\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"co\"; depth:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:!\"ect\"; depth:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"ect\"; depth:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"con\"; depth:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"ect\"; offset:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"co\"; offset:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"ect\"; offset:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"con\"; offset:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:\"ec\"; within:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:!\"ec\"; within:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:\"ec\"; within:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:!\"ec\"; within:4; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:\"ec\"; distance:2; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:!\"ec\"; distance:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:\"ec\"; distance:3; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHHTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"co\"; http_host; "
+ "content:!\"ec\"; distance:2; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest18(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"kaboom\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest19(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com:8080\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"kaboom\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest20(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com:8080\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"8080\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest21(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"kaboom\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest22(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"kaboom\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest23(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"8080\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest24(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "Host: www.rabbit.com\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"kaboom\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHHTest25(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "Host: www.rabbit.com\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_host header test\"; "
+ "content:\"rabbit\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpHHRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpHHTest01",
+ DetectEngineHttpHHTest01, 1);
+ UtRegisterTest("DetectEngineHttpHHTest02",
+ DetectEngineHttpHHTest02, 1);
+ UtRegisterTest("DetectEngineHttpHHTest03",
+ DetectEngineHttpHHTest03, 1);
+ UtRegisterTest("DetectEngineHttpHHTest04",
+ DetectEngineHttpHHTest04, 1);
+ UtRegisterTest("DetectEngineHttpHHTest05",
+ DetectEngineHttpHHTest05, 1);
+ UtRegisterTest("DetectEngineHttpHHTest06",
+ DetectEngineHttpHHTest06, 1);
+ UtRegisterTest("DetectEngineHttpHHTest07",
+ DetectEngineHttpHHTest07, 1);
+ UtRegisterTest("DetectEngineHttpHHTest08",
+ DetectEngineHttpHHTest08, 1);
+ UtRegisterTest("DetectEngineHttpHHTest09",
+ DetectEngineHttpHHTest09, 1);
+ UtRegisterTest("DetectEngineHttpHHTest10",
+ DetectEngineHttpHHTest10, 1);
+ UtRegisterTest("DetectEngineHttpHHTest11",
+ DetectEngineHttpHHTest11, 1);
+ UtRegisterTest("DetectEngineHttpHHTest12",
+ DetectEngineHttpHHTest12, 1);
+ UtRegisterTest("DetectEngineHttpHHTest13",
+ DetectEngineHttpHHTest13, 1);
+ UtRegisterTest("DetectEngineHttpHHTest14",
+ DetectEngineHttpHHTest14, 1);
+ UtRegisterTest("DetectEngineHttpHHTest15",
+ DetectEngineHttpHHTest15, 1);
+ UtRegisterTest("DetectEngineHttpHHTest16",
+ DetectEngineHttpHHTest16, 1);
+ UtRegisterTest("DetectEngineHttpHHTest17",
+ DetectEngineHttpHHTest17, 1);
+ UtRegisterTest("DetectEngineHttpHHTest18",
+ DetectEngineHttpHHTest18, 1);
+ UtRegisterTest("DetectEngineHttpHHTest19",
+ DetectEngineHttpHHTest19, 1);
+ UtRegisterTest("DetectEngineHttpHHTest20",
+ DetectEngineHttpHHTest20, 1);
+ UtRegisterTest("DetectEngineHttpHHTest21",
+ DetectEngineHttpHHTest21, 1);
+ UtRegisterTest("DetectEngineHttpHHTest22",
+ DetectEngineHttpHHTest22, 1);
+ UtRegisterTest("DetectEngineHttpHHTest23",
+ DetectEngineHttpHHTest23, 1);
+ UtRegisterTest("DetectEngineHttpHHTest24",
+ DetectEngineHttpHHTest24, 1);
+ UtRegisterTest("DetectEngineHttpHHTest25",
+ DetectEngineHttpHHTest25, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hhhd.h b/framework/src/suricata/src/detect-engine-hhhd.h
new file mode 100644
index 00000000..e6cec907
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hhhd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HHHD_H__
+#define __DETECT_ENGINE_HHHD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpHH(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpHHMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpHHRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HHHD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hmd.c b/framework/src/suricata/src/detect-engine-hmd.c
new file mode 100644
index 00000000..5a4b7d8a
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hmd.c
@@ -0,0 +1,1827 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP method match
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-hmd.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+int DetectEngineRunHttpMethodMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_method == NULL)
+ goto end;
+ cnt = HttpMethodPatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(tx->request_method),
+ bstr_len(tx->request_method),
+ flags);
+
+ end:
+ return cnt;
+}
+
+/**
+ * \brief Do the http_method content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpMethod(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_method == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HMDMATCH],
+ f,
+ (uint8_t *)bstr_ptr(tx->request_method),
+ bstr_len(tx->request_method),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HMD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"GET\"; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; depth:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"ECT\"; depth:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"ECT\"; depth:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"CON\"; depth:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"ECT\"; offset:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"CO\"; offset:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"ECT\"; offset:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CON\"; offset:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:\"EC\"; within:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:!\"EC\"; within:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:\"EC\"; within:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:!\"EC\"; within:4; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:\"EC\"; distance:2; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:!\"EC\"; distance:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:\"EC\"; distance:3; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_method content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpMethodTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"CO\"; http_method; "
+ "content:!\"EC\"; distance:2; http_method; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpMethodRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpMethodTest01",
+ DetectEngineHttpMethodTest01, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest02",
+ DetectEngineHttpMethodTest02, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest03",
+ DetectEngineHttpMethodTest03, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest04",
+ DetectEngineHttpMethodTest04, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest05",
+ DetectEngineHttpMethodTest05, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest06",
+ DetectEngineHttpMethodTest06, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest07",
+ DetectEngineHttpMethodTest07, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest08",
+ DetectEngineHttpMethodTest08, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest09",
+ DetectEngineHttpMethodTest09, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest10",
+ DetectEngineHttpMethodTest10, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest11",
+ DetectEngineHttpMethodTest11, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest12",
+ DetectEngineHttpMethodTest12, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest13",
+ DetectEngineHttpMethodTest13, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest14",
+ DetectEngineHttpMethodTest14, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest15",
+ DetectEngineHttpMethodTest15, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest16",
+ DetectEngineHttpMethodTest16, 1);
+ UtRegisterTest("DetectEngineHttpMethodTest17",
+ DetectEngineHttpMethodTest17, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hmd.h b/framework/src/suricata/src/detect-engine-hmd.h
new file mode 100644
index 00000000..faf26aa2
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hmd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HMD_H__
+#define __DETECT_ENGINE_HMD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpMethod(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpMethodMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpMethodRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HMD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hrhd.c b/framework/src/suricata/src/detect-engine-hrhd.c
new file mode 100644
index 00000000..90cf1d5e
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrhd.c
@@ -0,0 +1,3545 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP raw header match.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-hrhd.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+
+int DetectEngineRunHttpRawHeaderMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ SCEnter();
+
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ if (tx_ud == NULL)
+ SCReturnInt(cnt);
+
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) <= HTP_REQUEST_HEADERS)
+ SCReturnInt(cnt);
+
+ if (tx_ud->request_headers_raw != NULL) {
+ cnt = HttpRawHeaderPatternSearch(det_ctx,
+ tx_ud->request_headers_raw,
+ tx_ud->request_headers_raw_len,
+ flags);
+ }
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) <= HTP_RESPONSE_HEADERS)
+ SCReturnInt(cnt);
+
+ if (tx_ud->response_headers_raw != NULL) {
+ cnt += HttpRawHeaderPatternSearch(det_ctx,
+ tx_ud->response_headers_raw,
+ tx_ud->response_headers_raw_len,
+ flags);
+ }
+ }
+
+ SCReturnInt(cnt);
+}
+
+/**
+ * \brief Do the http_raw_header content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpRawHeader(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ HtpTxUserData *tx_ud = NULL;
+ uint8_t *headers_raw = NULL;
+ uint32_t headers_raw_len = 0;
+
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) <= HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) <= HTP_RESPONSE_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ tx_ud = htp_tx_get_user_data(txv);
+ if (tx_ud == NULL)
+ goto end;
+ if (flags & STREAM_TOSERVER) {
+ headers_raw = tx_ud->request_headers_raw;
+ headers_raw_len = tx_ud->request_headers_raw_len;
+ } else {
+ headers_raw = tx_ud->response_headers_raw;
+ headers_raw_len = tx_ud->response_headers_raw_len;
+ }
+ if (headers_raw == NULL)
+ goto end;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HRHDMATCH],
+ f,
+ headers_raw,
+ headers_raw_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (flags & STREAM_TOSERVER) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ } else {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_RESPONSE_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; depth:15; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"one\"; depth:5; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; depth:5; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"one\"; depth:15; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; offset:10; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"one\"; offset:15; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; offset:15; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"one\"; offset:10; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:\"three\"; http_raw_header; within:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:!\"three\"; http_raw_header; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:!\"three\"; http_raw_header; within:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:\"three\"; http_raw_header; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:\"five\"; http_raw_header; distance:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:!\"five\"; http_raw_header; distance:15; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:!\"five\"; http_raw_header; distance:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:\"five\"; http_raw_header; distance:15; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest18(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "Host: www.onetwothreefourfivesixsevenfive.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; content:\"five\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = HttpRawHeaderPatternSearch(det_ctx, http_buf, http_len, STREAM_TOSERVER);
+ if (r != 2) {
+ printf("expected result 2, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpRawHeaderTest19(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "Host: www.onetwothreefourfivesixsevenfive.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; fast_pattern; content:\"five\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = HttpRawHeaderPatternSearch(det_ctx, http_buf, http_len, STREAM_TOSERVER);
+ if (r != 1) {
+ printf("expected result 1, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest20(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:!\"dummy\"; http_raw_header; within:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest21(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:!\"dummy\"; within:7; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest22(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:!\"dummy\"; distance:3; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest23(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:!\"dummy\"; distance:13; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest24(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:\"dummy\"; within:15; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest25(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:\"dummy\"; within:10; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest26(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:\"dummy\"; distance:8; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest27(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: This_is_dummy_body1";
+ uint8_t http2_buf[] =
+ "This_is_dummy_message_body2\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; flow:to_server; "
+ "pcre:/body1/D; "
+ "content:\"dummy\"; distance:14; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest28(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_client; "
+ "content:\"Content-Length: 6\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1,
+ http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawHeaderTest29(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_client; "
+ "content:\"Content-Length: 7\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1,
+ http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#if 0
+
+static int DetectEngineHttpRawHeaderTest30(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Content-Length: 6\"; http_raw_header; "
+ "content:\"User-Agent: Mozilla\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* #if 0 */
+
+/**
+ * \test Trailing headers.
+ */
+static int DetectEngineHttpRawHeaderTest31(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "host: boom\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "13\r\n"
+ "This is dummy body1\r\n"
+ "0\r\n"
+ "Dummy-Header: kaboom\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(flow:to_server; "
+ "content:\"Dummy\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/**
+ * \test Trailing headers.
+ */
+static int DetectEngineHttpRawHeaderTest32(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "host: boom\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "13\r\n"
+ "This is dummy body1\r\n"
+ "0\r\n";
+ uint8_t http2_buf[] =
+ "Dummy-Header: kaboom\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(flow:to_server; "
+ "content:\"Dummy\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpRawHeaderRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpRawHeaderTest01",
+ DetectEngineHttpRawHeaderTest01, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest02",
+ DetectEngineHttpRawHeaderTest02, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest03",
+ DetectEngineHttpRawHeaderTest03, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest04",
+ DetectEngineHttpRawHeaderTest04, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest05",
+ DetectEngineHttpRawHeaderTest05, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest06",
+ DetectEngineHttpRawHeaderTest06, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest07",
+ DetectEngineHttpRawHeaderTest07, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest08",
+ DetectEngineHttpRawHeaderTest08, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest09",
+ DetectEngineHttpRawHeaderTest09, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest10",
+ DetectEngineHttpRawHeaderTest10, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest11",
+ DetectEngineHttpRawHeaderTest11, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest12",
+ DetectEngineHttpRawHeaderTest12, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest13",
+ DetectEngineHttpRawHeaderTest13, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest14",
+ DetectEngineHttpRawHeaderTest14, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest15",
+ DetectEngineHttpRawHeaderTest15, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest16",
+ DetectEngineHttpRawHeaderTest16, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest17",
+ DetectEngineHttpRawHeaderTest17, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest18",
+ DetectEngineHttpRawHeaderTest18, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest19",
+ DetectEngineHttpRawHeaderTest19, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest20",
+ DetectEngineHttpRawHeaderTest20, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest21",
+ DetectEngineHttpRawHeaderTest21, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest22",
+ DetectEngineHttpRawHeaderTest22, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest23",
+ DetectEngineHttpRawHeaderTest23, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest24",
+ DetectEngineHttpRawHeaderTest24, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest25",
+ DetectEngineHttpRawHeaderTest25, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest26",
+ DetectEngineHttpRawHeaderTest26, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest27",
+ DetectEngineHttpRawHeaderTest27, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest28",
+ DetectEngineHttpRawHeaderTest28, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest29",
+ DetectEngineHttpRawHeaderTest29, 1);
+#if 0
+ UtRegisterTest("DetectEngineHttpRawHeaderTest30",
+ DetectEngineHttpRawHeaderTest30, 1);
+#endif
+ UtRegisterTest("DetectEngineHttpRawHeaderTest31",
+ DetectEngineHttpRawHeaderTest31, 1);
+ UtRegisterTest("DetectEngineHttpRawHeaderTest32",
+ DetectEngineHttpRawHeaderTest32, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hrhd.h b/framework/src/suricata/src/detect-engine-hrhd.h
new file mode 100644
index 00000000..c33d57e7
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrhd.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HRHD_H__
+#define __DETECT_ENGINE_HRHD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpRawHeader(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpRawHeaderMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpRawHeaderRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HHD_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-hrhhd.c b/framework/src/suricata/src/detect-engine-hrhhd.c
new file mode 100644
index 00000000..f1acf100
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrhhd.c
@@ -0,0 +1,2643 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP host header.
+ * HRHHD - Http Raw Host Header Data
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+#include "detect-engine-hrhhd.h"
+
+int DetectEngineRunHttpHRHMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ uint8_t *hname = NULL;
+ uint32_t hname_len = 0;
+
+ if (tx->parsed_uri == NULL || tx->parsed_uri->hostname == NULL) {
+ if (tx->request_headers == NULL)
+ goto end;
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers, "Host");
+ if (h != NULL) {
+ SCLogDebug("HTTP host header not present in this request");
+ hname = (uint8_t *)bstr_ptr(h->value);
+ hname_len = bstr_len(h->value);
+ } else {
+ goto end;
+ }
+ } else {
+ hname = (uint8_t *)bstr_ptr(tx->parsed_uri->hostname);
+ if (hname != NULL)
+ hname_len = bstr_len(tx->parsed_uri->hostname);
+ else
+ goto end;
+ }
+
+ cnt = HttpHRHPatternSearch(det_ctx, hname, hname_len, flags);
+
+ end:
+ return cnt;
+}
+
+/**
+ * \brief Do the http_header content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpHRH(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ uint8_t *hname;
+ uint32_t hname_len;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->parsed_uri == NULL || tx->parsed_uri->hostname == NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers, "Host");
+ if (h == NULL) {
+ SCLogDebug("HTTP host header not present in this request");
+ goto end;
+ }
+ hname = (uint8_t *)bstr_ptr(h->value);
+ hname_len = bstr_len(h->value);
+ } else {
+ hname = (uint8_t *)bstr_ptr(tx->parsed_uri->hostname);
+ if (hname == NULL)
+ goto end;
+ hname_len = bstr_len(tx->parsed_uri->hostname);
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HRHHDMATCH],
+ f,
+ hname, hname_len,
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHHD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"CONNECT\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"CO\"; depth:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:!\"ECT\"; depth:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"ECT\"; depth:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"CON\"; depth:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"ECT\"; offset:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"CO\"; offset:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:!\"ECT\"; offset:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host header test\"; "
+ "content:\"CON\"; offset:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:\"EC\"; within:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:!\"EC\"; within:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:\"EC\"; within:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:!\"EC\"; within:4; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:\"EC\"; distance:2; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:!\"EC\"; distance:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:\"EC\"; distance:3; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host header content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpHRHTest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: CONNECT\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"CO\"; http_raw_host; "
+ "content:!\"EC\"; distance:2; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest18(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com:8080\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"kaboom\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest19(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com:8080\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"kaboom\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest20(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.kaboom.com:8080\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"8080\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest21(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"kaboom\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest22(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"kaboom\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest23(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"8080\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest24(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "Host: www.rabbit.com\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"kaboom\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectEngineHttpHRHTest25(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET http://www.kaboom.com:8080/index.html HTTP/1.0\r\n"
+ "Host: www.rabbit.com\r\n"
+ "User-Agent: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_raw_host header test\"; "
+ "content:\"rabbit\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpHRHRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpHRHTest01",
+ DetectEngineHttpHRHTest01, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest02",
+ DetectEngineHttpHRHTest02, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest03",
+ DetectEngineHttpHRHTest03, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest04",
+ DetectEngineHttpHRHTest04, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest05",
+ DetectEngineHttpHRHTest05, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest06",
+ DetectEngineHttpHRHTest06, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest07",
+ DetectEngineHttpHRHTest07, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest08",
+ DetectEngineHttpHRHTest08, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest09",
+ DetectEngineHttpHRHTest09, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest10",
+ DetectEngineHttpHRHTest10, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest11",
+ DetectEngineHttpHRHTest11, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest12",
+ DetectEngineHttpHRHTest12, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest13",
+ DetectEngineHttpHRHTest13, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest14",
+ DetectEngineHttpHRHTest14, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest15",
+ DetectEngineHttpHRHTest15, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest16",
+ DetectEngineHttpHRHTest16, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest17",
+ DetectEngineHttpHRHTest17, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest18",
+ DetectEngineHttpHRHTest18, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest19",
+ DetectEngineHttpHRHTest19, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest20",
+ DetectEngineHttpHRHTest20, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest21",
+ DetectEngineHttpHRHTest21, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest22",
+ DetectEngineHttpHRHTest22, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest23",
+ DetectEngineHttpHRHTest23, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest24",
+ DetectEngineHttpHRHTest24, 1);
+ UtRegisterTest("DetectEngineHttpHRHTest25",
+ DetectEngineHttpHRHTest25, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hrhhd.h b/framework/src/suricata/src/detect-engine-hrhhd.h
new file mode 100644
index 00000000..24c3b43e
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrhhd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HRHHD_H__
+#define __DETECT_ENGINE_HRHHD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpHRH(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpHRHMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpHRHRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HRHHD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hrl.c b/framework/src/suricata/src/detect-engine-hrl.c
new file mode 100644
index 00000000..5e5c0cb6
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrl.c
@@ -0,0 +1,4221 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Based on detect-engine-uri.c
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+/**
+ * \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param sm SigMatch to inspect
+ * \param f Flow
+ * \param flags app layer flags
+ * \param state App layer state
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ * \retval 2 Sig can't match.
+ */
+int DetectEngineInspectHttpRequestLine(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+
+ if (tx->request_line == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_REQUEST_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+#if 0
+ PrintRawDataFp(stdout, (uint8_t *)bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+#endif
+
+ /* Inspect all the uricontents fetched on each
+ * transaction at the app layer */
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HRLMATCH],
+ f,
+ bstr_ptr(tx->request_line),
+ bstr_len(tx->request_line),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRL, NULL);
+ if (r == 1) {
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ } else {
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+/** \test Test a simple uricontent option */
+static int UriTestSig01(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig02(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /on HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/one/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted with payload2, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig03(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/blah/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the urilen option */
+static int UriTestSig04(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the urilen option */
+static int UriTestSig05(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>4; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig06(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/(oneself)+/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert on payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig07(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen \"; "
+ "pcre:/(one){2,}(self)?/U; urilen:3<>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig08(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen\"; "
+ "pcre:/(blabla){2,}(self)?/U; urilen:3<>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig09(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen \"; "
+ "pcre:/(one){2,}(self)?/U; urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the uricontent option in combination with urilen */
+static int UriTestSig10(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent with urilen option\"; "
+ "uricontent:\"one\"; urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test content, uricontent, urilen, pcre /U options */
+static int UriTestSig11(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test content, uricontent, pcre /U and urilen options\"; "
+ "content:\"one\"; uricontent:\"one\"; pcre:/(one){2,}(self)?/U;"
+ "urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, urilen, pcre /U options */
+static int UriTestSig12(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U, uricontent and urilen option\"; "
+ "uricontent:\"one\"; "
+ "pcre:/(one)+self/U; urilen:>2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, urilen */
+static int UriTestSig13(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>2; uricontent:\"one\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, pcre /U */
+static int UriTestSig14(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; pcre:/one(self)?/U;sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test pcre /U with anchored regex (bug 155) */
+static int UriTestSig15(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; pcre:/^\\/one(self)?$/U;sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test pcre /U with anchored regex (bug 155) */
+static int UriTestSig16(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /search?q=123&aq=7123abcee HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0/\r\n"
+ "Host: 1.2.3.4\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /search?q=123&aq=7123abcee HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any any (msg:\"ET TROJAN Downadup/Conficker A or B Worm reporting\"; flow:to_server,established; uricontent:\"/search?q=\"; pcre:\"/^\\/search\\?q=[0-9]{1,3}(&aq=7(\\?[0-9a-f]{8})?)?/U\"; pcre:\"/\\x0d\\x0aHost\\: \\d+\\.\\d+\\.\\d+\\.\\d+\\x0d\\x0a/\"; sid:2009024; rev:9;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 2009024)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+ p->payload = http_buf2;
+ p->payload_len = http_buf2_len;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 2009024)) {
+ printf("sig 1 alerted, but it should not (host should not match): ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig17(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /now_this_is_is_big_big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"this\"; uricontent:\"is\"; within:6; "
+ "uricontent:\"big\"; within:8; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig18(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /now_this_is_is_is_big_big_big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"this\"; uricontent:\"is\"; within:9; "
+ "uricontent:\"big\"; within:12; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig19(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_this_now_is_is_____big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"now\"; uricontent:\"this\"; "
+ "uricontent:\"is\"; within:12; "
+ "uricontent:\"big\"; within:8; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with offset
+ */
+static int UriTestSig20(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /_________thus_thus_is_a_big HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"thus\"; offset:8; "
+ "uricontent:\"is\"; within:6; "
+ "uricontent:\"big\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig21(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix\"; uricontent:\"this\"; within:6; "
+ "uricontent:!\"and\"; distance:0; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test relative pcre.
+ */
+static int UriTestSig22(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_is_a_super_duper_"
+ "nova_in_super_nova_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "pcre:/super/U; uricontent:\"nova\"; within:7; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig23(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:!\"fix_this_now\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig24(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"we_need_to\"; uricontent:!\"fix_this_now\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test normalized uricontents.
+ */
+static int UriTestSig25(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "pcre:/normalized/U; uricontent:\"normalized uri\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig26(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix_this\"; isdataat:4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig27(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix_this\"; isdataat:!10,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int UriTestSig28(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"ring\"; distance:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig29(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"ring\"; distance:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig30(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"_b5ig\"; offset:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig31(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"his\"; depth:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig32(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"g_st\"; within:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig33(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:15; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig34(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:15, norm; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig35(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:16; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig36(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:16, norm; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig37(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:17, raw; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig38(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:18, raw; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void HttpRequestLineRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("UriTestSig01", UriTestSig01, 1);
+ UtRegisterTest("UriTestSig02", UriTestSig02, 1);
+ UtRegisterTest("UriTestSig03", UriTestSig03, 1);
+ UtRegisterTest("UriTestSig04", UriTestSig04, 1);
+ UtRegisterTest("UriTestSig05", UriTestSig05, 1);
+ UtRegisterTest("UriTestSig06", UriTestSig06, 1);
+ UtRegisterTest("UriTestSig07", UriTestSig07, 1);
+ UtRegisterTest("UriTestSig08", UriTestSig08, 1);
+ UtRegisterTest("UriTestSig09", UriTestSig09, 1);
+ UtRegisterTest("UriTestSig10", UriTestSig10, 1);
+ UtRegisterTest("UriTestSig11", UriTestSig11, 1);
+ UtRegisterTest("UriTestSig12", UriTestSig12, 1);
+ UtRegisterTest("UriTestSig13", UriTestSig13, 1);
+ UtRegisterTest("UriTestSig14", UriTestSig14, 1);
+ UtRegisterTest("UriTestSig15", UriTestSig15, 1);
+ UtRegisterTest("UriTestSig16", UriTestSig16, 1);
+ UtRegisterTest("UriTestSig17", UriTestSig17, 1);
+ UtRegisterTest("UriTestSig18", UriTestSig18, 1);
+ UtRegisterTest("UriTestSig19", UriTestSig19, 1);
+ UtRegisterTest("UriTestSig20", UriTestSig20, 1);
+ UtRegisterTest("UriTestSig21", UriTestSig21, 1);
+ UtRegisterTest("UriTestSig22", UriTestSig22, 1);
+ UtRegisterTest("UriTestSig23", UriTestSig23, 1);
+ UtRegisterTest("UriTestSig24", UriTestSig24, 1);
+ UtRegisterTest("UriTestSig25", UriTestSig25, 1);
+ UtRegisterTest("UriTestSig26", UriTestSig26, 1);
+ UtRegisterTest("UriTestSig27", UriTestSig27, 1);
+
+ UtRegisterTest("UriTestSig28", UriTestSig28, 1);
+ UtRegisterTest("UriTestSig29", UriTestSig29, 1);
+ UtRegisterTest("UriTestSig30", UriTestSig30, 1);
+ UtRegisterTest("UriTestSig31", UriTestSig31, 1);
+ UtRegisterTest("UriTestSig32", UriTestSig32, 1);
+ UtRegisterTest("UriTestSig33", UriTestSig33, 1);
+ UtRegisterTest("UriTestSig34", UriTestSig34, 1);
+ UtRegisterTest("UriTestSig35", UriTestSig35, 1);
+ UtRegisterTest("UriTestSig36", UriTestSig36, 1);
+ UtRegisterTest("UriTestSig37", UriTestSig37, 1);
+ UtRegisterTest("UriTestSig38", UriTestSig38, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-hrl.h b/framework/src/suricata/src/detect-engine-hrl.h
new file mode 100644
index 00000000..cb5360d9
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrl.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_HRL_H__
+#define __DETECT_ENGINE_HRL_H__
+
+int DetectEngineInspectHttpRequestLine(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void HttpRequestLineRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HRL_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hrud.c b/framework/src/suricata/src/detect-engine-hrud.c
new file mode 100644
index 00000000..c715c029
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrud.c
@@ -0,0 +1,3726 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP raw uri match
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-hrud.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+
+/**
+ * \brief Run the mpm against raw http uris.
+ *
+ * \retval cnt Number of matches reported by the mpm algo.
+ */
+int DetectEngineRunHttpRawUriMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ SCEnter();
+
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ uint32_t cnt = 0;
+ if (tx->request_uri == NULL)
+ goto end;
+ cnt = HttpRawUriPatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(tx->request_uri),
+ bstr_len(tx->request_uri), flags);
+
+end:
+ SCReturnInt(cnt);
+}
+
+/**
+ * \brief Do the http_raw_uri content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpRawUri(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_uri == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ /* Inspect all the uricontents fetched on each
+ * transaction at the app layer */
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HRUDMATCH],
+ f,
+ (uint8_t *)bstr_ptr(tx->request_uri),
+ bstr_len(tx->request_uri),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRUD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+static int DetectEngineHttpRawUriTest01(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/../c";
+ uint8_t http2_buf[] =
+ "/./d.html HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"../c/./d\"; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 19\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"/c/./d\"; http_raw_uri; offset:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/../";
+ uint8_t http2_buf[] =
+ "c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"/a/b\"; http_raw_uri; offset:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest04(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/../";
+ uint8_t http2_buf[] =
+ "c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:!\"/a/b\"; http_raw_uri; offset:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest05(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/";
+ uint8_t http2_buf[] =
+ "../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"a/b\"; http_raw_uri; depth:10; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest06(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/";
+ uint8_t http2_buf[] =
+ "../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:!\"/a/b\"; http_raw_uri; depth:25; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/b/";
+ uint8_t http2_buf[] =
+ "../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:!\"/c/./d\"; http_raw_uri; depth:12; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a/";
+ uint8_t http2_buf[] =
+ "b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:!\"/c/./d\"; http_raw_uri; depth:18; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"/a\"; http_raw_uri; "
+ "content:\"./c/.\"; http_raw_uri; within:9; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"/a\"; http_raw_uri; "
+ "content:!\"boom\"; http_raw_uri; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest11(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:\"boom\"; http_raw_uri; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest12(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:!\"/b/..\"; http_raw_uri; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest13(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:\"/c/.\"; http_raw_uri; distance:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest14(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:!\"b/..\"; http_raw_uri; distance:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest15(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:\"/c/\"; http_raw_uri; distance:7; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest16(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"./a\"; http_raw_uri; "
+ "content:!\"/c/\"; http_raw_uri; distance:4; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest17(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http1_buf[] = "This_is_dummy_body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"body1\"; http_raw_uri; "
+ "content:\"bambu\"; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p1);
+ uint32_t r = HttpRawUriPatternSearch(det_ctx, http1_buf, http1_len, STREAM_TOSERVER);
+ if (r != 1) {
+ printf("expected 1 result, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest18(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http1_buf[] = "This_is_dummy_body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"body1\"; http_raw_uri; "
+ "content:\"bambu\"; http_raw_uri; fast_pattern; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p1);
+ uint32_t r = HttpRawUriPatternSearch(det_ctx, http1_buf, http1_len, STREAM_TOSERVER);
+ if (r != 0) {
+ printf("expected 0 result, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest19(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http1_buf[] = "This_is_dummy_body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"bambu\"; http_raw_uri; "
+ "content:\"is\"; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p1);
+ uint32_t r = HttpRawUriPatternSearch(det_ctx, http1_buf, http1_len, STREAM_TOSERVER);
+ if (r != 0) {
+ printf("expected 0 result, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest20(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http1_buf[] = "This_is_dummy_body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "content:\"bambu\"; http_raw_uri; "
+ "content:\"is\"; http_raw_uri; fast_pattern; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p1);
+ uint32_t r = HttpRawUriPatternSearch(det_ctx, http1_buf, http1_len, STREAM_TOSERVER);
+ if (r != 2) {
+ printf("expected 2 result, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest21(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:!\"/c/\"; http_raw_uri; within:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest22(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:!\"/c/\"; within:5; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest23(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:!\"/c/\"; distance:3; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest24(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:!\"/c/\"; distance:10; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest25(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:\"/c/\"; within:10; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest26(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:\"/c/\"; within:5; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest27(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:\"/c/\"; distance:5; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpRawUriTest28(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /../a";
+ uint8_t http2_buf[] =
+ "/b/../c/./d.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1"
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http raw uri test\"; "
+ "pcre:/\\.\\/a/I; "
+ "content:\"/c/\"; distance:10; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int DetectEngineHttpRawUriTest29(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /../a/b/../c/./d.html HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative raw uri contents\"; "
+ "content:\"/c/\"; http_raw_uri; "
+ "isdataat:4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int DetectEngineHttpRawUriTest30(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /../a/b/../c/./d.html HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative raw uri contents\"; "
+ "uricontent:\"/c/\"; isdataat:!10,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpRawUriRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpRawUriTest01",
+ DetectEngineHttpRawUriTest01, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest02",
+ DetectEngineHttpRawUriTest02, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest03",
+ DetectEngineHttpRawUriTest03, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest04",
+ DetectEngineHttpRawUriTest04, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest05",
+ DetectEngineHttpRawUriTest05, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest06",
+ DetectEngineHttpRawUriTest06, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest07",
+ DetectEngineHttpRawUriTest07, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest08",
+ DetectEngineHttpRawUriTest08, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest09",
+ DetectEngineHttpRawUriTest09, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest10",
+ DetectEngineHttpRawUriTest10, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest11",
+ DetectEngineHttpRawUriTest11, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest12",
+ DetectEngineHttpRawUriTest12, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest13",
+ DetectEngineHttpRawUriTest13, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest14",
+ DetectEngineHttpRawUriTest14, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest15",
+ DetectEngineHttpRawUriTest15, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest16",
+ DetectEngineHttpRawUriTest16, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest17",
+ DetectEngineHttpRawUriTest17, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest18",
+ DetectEngineHttpRawUriTest18, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest19",
+ DetectEngineHttpRawUriTest19, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest20",
+ DetectEngineHttpRawUriTest20, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest21",
+ DetectEngineHttpRawUriTest21, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest22",
+ DetectEngineHttpRawUriTest22, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest23",
+ DetectEngineHttpRawUriTest23, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest24",
+ DetectEngineHttpRawUriTest24, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest25",
+ DetectEngineHttpRawUriTest25, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest26",
+ DetectEngineHttpRawUriTest26, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest27",
+ DetectEngineHttpRawUriTest27, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest28",
+ DetectEngineHttpRawUriTest28, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest29",
+ DetectEngineHttpRawUriTest29, 1);
+ UtRegisterTest("DetectEngineHttpRawUriTest30",
+ DetectEngineHttpRawUriTest30, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hrud.h b/framework/src/suricata/src/detect-engine-hrud.h
new file mode 100644
index 00000000..85ad88e1
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hrud.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HRUD_H__
+#define __DETECT_ENGINE_HRUD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineRunHttpRawUriMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+int DetectEngineInspectHttpRawUri(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineHttpRawUriRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HRUD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hsbd.c b/framework/src/suricata/src/detect-engine-hsbd.c
new file mode 100644
index 00000000..7d52c7d8
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hsbd.c
@@ -0,0 +1,4515 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * \brief Handle HTTP response body match corresponding to http_server_body
+ * keyword.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-mem.h"
+#include "app-layer-protos.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#define BUFFER_STEP 50
+
+static inline int HSBDCreateSpace(DetectEngineThreadCtx *det_ctx, uint64_t size)
+{
+ if (size >= (USHRT_MAX - BUFFER_STEP))
+ return -1;
+
+ void *ptmp;
+ if (size > det_ctx->hsbd_buffers_size) {
+ ptmp = SCRealloc(det_ctx->hsbd,
+ (det_ctx->hsbd_buffers_size + BUFFER_STEP) * sizeof(HttpReassembledBody));
+ if (ptmp == NULL) {
+ SCFree(det_ctx->hsbd);
+ det_ctx->hsbd = NULL;
+ det_ctx->hsbd_buffers_size = 0;
+ det_ctx->hsbd_buffers_list_len = 0;
+ return -1;
+ }
+ det_ctx->hsbd = ptmp;
+
+ memset(det_ctx->hsbd + det_ctx->hsbd_buffers_size, 0, BUFFER_STEP * sizeof(HttpReassembledBody));
+ det_ctx->hsbd_buffers_size += BUFFER_STEP;
+ }
+ uint16_t i;
+ for (i = det_ctx->hsbd_buffers_list_len; i < ((uint16_t)size); i++) {
+ det_ctx->hsbd[i].buffer_len = 0;
+ det_ctx->hsbd[i].offset = 0;
+ }
+
+ return 0;
+}
+
+static void HSBDGetBufferForTXInIDSMode(DetectEngineThreadCtx *det_ctx,
+ HtpState *htp_state, HtpBodyChunk *cur,
+ HtpTxUserData *htud, int index)
+{
+ int first = 1;
+ while (cur != NULL) {
+ /* see if we can filter out chunks */
+ if (htud->response_body.body_inspected > 0) {
+ if (cur->stream_offset < htud->response_body.body_inspected) {
+ if ((htud->response_body.body_inspected - cur->stream_offset) > htp_state->cfg->response_inspect_window) {
+ cur = cur->next;
+ continue;
+ } else {
+ /* include this one */
+ }
+ } else {
+ /* include this one */
+ }
+ }
+
+ if (first) {
+ det_ctx->hsbd[index].offset = cur->stream_offset;
+ first = 0;
+ }
+
+ /* see if we need to grow the buffer */
+ if (det_ctx->hsbd[index].buffer == NULL || (det_ctx->hsbd[index].buffer_len + cur->len) > det_ctx->hsbd[index].buffer_size) {
+ void *ptmp;
+ uint32_t newsize = det_ctx->hsbd[index].buffer_size + (cur->len * 2);
+
+ if ((ptmp = HTPRealloc(det_ctx->hsbd[index].buffer, det_ctx->hsbd[index].buffer_size, newsize)) == NULL) {
+ HTPFree(det_ctx->hsbd[index].buffer, det_ctx->hsbd[index].buffer_size);
+ det_ctx->hsbd[index].buffer = NULL;
+ det_ctx->hsbd[index].buffer_size = 0;
+ det_ctx->hsbd[index].buffer_len = 0;
+ return;
+ }
+ det_ctx->hsbd[index].buffer = ptmp;
+ det_ctx->hsbd[index].buffer_size = newsize;
+ }
+ memcpy(det_ctx->hsbd[index].buffer + det_ctx->hsbd[index].buffer_len, cur->data, cur->len);
+ det_ctx->hsbd[index].buffer_len += cur->len;
+
+ cur = cur->next;
+ }
+
+ /* update inspected tracker */
+ htud->response_body.body_inspected = htud->response_body.last->stream_offset + htud->response_body.last->len;
+}
+
+#define MAX_WINDOW 10*1024*1024
+static void HSBDGetBufferForTXInIPSMode(DetectEngineThreadCtx *det_ctx,
+ HtpState *htp_state, HtpBodyChunk *cur,
+ HtpTxUserData *htud, int index)
+{
+ uint32_t window_size = 0;
+
+ /* how much from before body_inspected will we consider? */
+ uint32_t cfg_win =
+ htud->response_body.body_inspected >= htp_state->cfg->response_inspect_min_size ?
+ htp_state->cfg->response_inspect_window :
+ htp_state->cfg->response_inspect_min_size;
+
+ /* but less if we don't have that much before body_inspected */
+ if ((htud->response_body.body_inspected - htud->response_body.first->stream_offset) < cfg_win) {
+ cfg_win = htud->response_body.body_inspected - htud->response_body.first->stream_offset;
+ }
+ window_size = (htud->response_body.content_len_so_far - htud->response_body.body_inspected) + cfg_win;
+ if (window_size > MAX_WINDOW) {
+ SCLogDebug("weird: body size is %uk", window_size/1024);
+ window_size = MAX_WINDOW;
+ }
+
+ if (det_ctx->hsbd[index].buffer == NULL || window_size > det_ctx->hsbd[index].buffer_size) {
+ void *ptmp;
+
+ if ((ptmp = HTPRealloc(det_ctx->hsbd[index].buffer, det_ctx->hsbd[index].buffer_size, window_size)) == NULL) {
+ HTPFree(det_ctx->hsbd[index].buffer, det_ctx->hsbd[index].buffer_size);
+ det_ctx->hsbd[index].buffer = NULL;
+ det_ctx->hsbd[index].buffer_size = 0;
+ det_ctx->hsbd[index].buffer_len = 0;
+ return;
+ }
+ det_ctx->hsbd[index].buffer = ptmp;
+ det_ctx->hsbd[index].buffer_size = window_size;
+ }
+
+ uint32_t left_edge = htud->response_body.body_inspected - cfg_win;
+
+ int first = 1;
+ while (cur != NULL) {
+ if (first) {
+ det_ctx->hsbd[index].offset = cur->stream_offset;
+ first = 0;
+ }
+
+ /* entirely before our window */
+ if ((cur->stream_offset + cur->len) <= left_edge) {
+ cur = cur->next;
+ continue;
+ } else {
+ uint32_t offset = 0;
+ if (cur->stream_offset < left_edge && (cur->stream_offset + cur->len) > left_edge) {
+ offset = left_edge - cur->stream_offset;
+ BUG_ON(offset > cur->len);
+ }
+
+ /* unusual: if window isn't big enough, we just give up */
+ if (det_ctx->hsbd[index].buffer_len + (cur->len - offset) > window_size) {
+ htud->response_body.body_inspected = cur->stream_offset;
+ SCReturn;
+ }
+
+ BUG_ON(det_ctx->hsbd[index].buffer_len + (cur->len - offset) > window_size);
+
+ memcpy(det_ctx->hsbd[index].buffer + det_ctx->hsbd[index].buffer_len, cur->data + offset, cur->len - offset);
+ det_ctx->hsbd[index].buffer_len += (cur->len - offset);
+ det_ctx->hsbd[index].offset -= offset;
+ }
+
+ cur = cur->next;
+ }
+
+ /* update inspected tracker to point before the current window */
+ htud->response_body.body_inspected = htud->response_body.content_len_so_far;
+}
+
+static uint8_t *DetectEngineHSBDGetBufferForTX(htp_tx_t *tx, uint64_t tx_id,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Flow *f, HtpState *htp_state,
+ uint8_t flags,
+ uint32_t *buffer_len,
+ uint32_t *stream_start_offset)
+{
+ int index = 0;
+ uint8_t *buffer = NULL;
+ *buffer_len = 0;
+ *stream_start_offset = 0;
+
+ if (det_ctx->hsbd_buffers_list_len == 0) {
+ /* get the inspect id to use as a 'base id' */
+ uint64_t base_inspect_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ BUG_ON(base_inspect_id > tx_id);
+ /* see how many space we need for the current tx_id */
+ uint64_t txs = (tx_id - base_inspect_id) + 1;
+ if (HSBDCreateSpace(det_ctx, txs) < 0)
+ goto end;
+ index = (tx_id - base_inspect_id);
+ det_ctx->hsbd_start_tx_id = base_inspect_id;
+ det_ctx->hsbd_buffers_list_len = txs;
+ } else {
+ if ((tx_id - det_ctx->hsbd_start_tx_id) < det_ctx->hsbd_buffers_list_len) {
+ if (det_ctx->hsbd[(tx_id - det_ctx->hsbd_start_tx_id)].buffer_len != 0) {
+ *buffer_len = det_ctx->hsbd[(tx_id - det_ctx->hsbd_start_tx_id)].buffer_len;
+ *stream_start_offset = det_ctx->hsbd[(tx_id - det_ctx->hsbd_start_tx_id)].offset;
+ return det_ctx->hsbd[(tx_id - det_ctx->hsbd_start_tx_id)].buffer;
+ }
+ } else {
+ uint64_t txs = (tx_id - det_ctx->hsbd_start_tx_id) + 1;
+ if (HSBDCreateSpace(det_ctx, txs) < 0)
+ goto end;
+
+ det_ctx->hsbd_buffers_list_len = txs;
+ }
+ index = (tx_id - det_ctx->hsbd_start_tx_id);
+ }
+
+ HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
+ if (htud == NULL) {
+ SCLogDebug("no htud");
+ goto end;
+ }
+
+ /* no new data */
+ if (htud->response_body.body_inspected == htud->response_body.content_len_so_far) {
+ SCLogDebug("no new data");
+ goto end;
+ }
+
+ HtpBodyChunk *cur = htud->response_body.first;
+ if (cur == NULL) {
+ SCLogDebug("No http chunks to inspect for this transacation");
+ goto end;
+ }
+
+ SCLogDebug("response_body_limit %u response_body.content_len_so_far %"PRIu64
+ ", response_inspect_min_size %"PRIu32", EOF %s, progress > body? %s",
+ htp_state->cfg->response_body_limit,
+ htud->response_body.content_len_so_far,
+ htp_state->cfg->response_inspect_min_size,
+ flags & STREAM_EOF ? "true" : "false",
+ (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_BODY) ? "true" : "false");
+
+ if (!htp_state->cfg->http_body_inline) {
+ /* inspect the body if the transfer is complete or we have hit
+ * our body size limit */
+ if ((htp_state->cfg->response_body_limit == 0 ||
+ htud->response_body.content_len_so_far < htp_state->cfg->response_body_limit) &&
+ htud->response_body.content_len_so_far < htp_state->cfg->response_inspect_min_size &&
+ !(AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_BODY) &&
+ !(flags & STREAM_EOF)) {
+ SCLogDebug("we still haven't seen the entire response body. "
+ "Let's defer body inspection till we see the "
+ "entire body.");
+ goto end;
+ }
+ HSBDGetBufferForTXInIDSMode(det_ctx, htp_state, cur, htud, index);
+ } else {
+ HSBDGetBufferForTXInIPSMode(det_ctx, htp_state, cur, htud, index);
+ }
+
+ buffer = det_ctx->hsbd[index].buffer;
+ *buffer_len = det_ctx->hsbd[index].buffer_len;
+ *stream_start_offset = det_ctx->hsbd[index].offset;
+ end:
+ return buffer;
+}
+
+int DetectEngineRunHttpServerBodyMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = DetectEngineHSBDGetBufferForTX(tx, idx,
+ de_ctx, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+ if (buffer_len == 0)
+ goto end;
+
+ cnt = HttpServerBodyPatternSearch(det_ctx, buffer, buffer_len, flags);
+
+ end:
+ return cnt;
+}
+
+int DetectEngineInspectHttpServerBody(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ HtpState *htp_state = (HtpState *)alstate;
+ uint32_t buffer_len = 0;
+ uint32_t stream_start_offset = 0;
+ uint8_t *buffer = DetectEngineHSBDGetBufferForTX(tx, tx_id,
+ de_ctx, det_ctx,
+ f, htp_state,
+ flags,
+ &buffer_len,
+ &stream_start_offset);
+ if (buffer_len == 0)
+ goto end;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_FILEDATA],
+ f,
+ buffer,
+ buffer_len,
+ stream_start_offset,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSBD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_BODY)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+void DetectEngineCleanHSBDBuffers(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->hsbd_buffers_list_len > 0) {
+ for (int i = 0; i < det_ctx->hsbd_buffers_list_len; i++) {
+ det_ctx->hsbd[i].buffer_len = 0;
+ det_ctx->hsbd[i].offset = 0;
+ }
+ }
+ det_ctx->hsbd_buffers_list_len = 0;
+ det_ctx->hsbd_start_tx_id = 0;
+
+ return;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+struct TestSteps {
+ const uint8_t *input;
+ size_t input_size; /**< if 0 strlen will be used */
+ int direction; /**< STREAM_TOSERVER, STREAM_TOCLIENT */
+ int expect;
+};
+
+static int RunTest(struct TestSteps *steps, const char *sig, const char *yaml)
+{
+ TcpSession ssn;
+ Flow f;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+ int i = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ if (yaml) {
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(yaml, strlen(yaml));
+ HTPConfigure();
+ EngineModeSetIPS();
+ }
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ SCLogDebug("sig %s", sig);
+ DetectEngineAppendSig(de_ctx, (char *)sig);
+
+ de_ctx->flags |= DE_QUIET;
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ struct TestSteps *b = steps;
+ i = 0;
+ while (b->input != NULL) {
+ SCLogDebug("chunk %p %d", b, i);
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+ p->flow = &f;
+ p->flowflags = (b->direction == STREAM_TOSERVER) ? FLOW_PKT_TOSERVER : FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, b->direction,
+ (uint8_t *)b->input,
+ b->input_size ? b->input_size : strlen((const char *)b->input));
+ if (r != 0) {
+ printf("toserver chunk %d returned %" PRId32 ", expected 0: ", i+1, r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ int match = PacketAlertCheck(p, 1);
+ if (b->expect != match) {
+ printf("rule matching mismatch: ");
+ goto end;
+ }
+
+ UTHFreePackets(&p, 1);
+ p = NULL;
+ b++;
+ i++;
+ }
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+
+ if (yaml) {
+ HtpConfigRestoreBackup();
+ ConfRestoreContextBackup();
+ EngineModeSetIDS();
+ }
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest01(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"message\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "xxxxABC";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"ABC\"; http_server_body; offset:4; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ int result = 0;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 17\r\n"
+ "\r\n"
+ "1234567";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "8901234ABC";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"ABC\"; http_server_body; offset:14; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest04(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:!\"abc\"; http_server_body; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest05(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"abc\"; http_server_body; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest06(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:!\"def\"; http_server_body; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:!\"def\"; http_server_body; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:!\"abc\"; http_server_body; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"abc\"; http_server_body; depth:3; "
+ "content:\"def\"; http_server_body; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"abc\"; http_server_body; depth:3; "
+ "content:!\"xyz\"; http_server_body; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest11(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"abc\"; http_server_body; depth:3; "
+ "content:\"xyz\"; http_server_body; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did match but should not have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest12(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"ab\"; http_server_body; depth:2; "
+ "content:\"ef\"; http_server_body; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest13(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"ab\"; http_server_body; depth:3; "
+ "content:!\"yz\"; http_server_body; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest14(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "pcre:/ab/Q; "
+ "content:\"ef\"; http_server_body; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest15(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "pcre:/abc/Q; "
+ "content:!\"xyz\"; http_server_body; distance:0; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest16(void)
+{
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+\n\
+ request-body-inspect-window: 0\n\
+ response-body-inspect-window: 0\n\
+ request-body-minimal-inspect-size: 0\n\
+ response-body-minimal-inspect-size: 0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ int result = 0;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 17\r\n"
+ "\r\n"
+ "1234567";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "8901234ABC";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"890\"; within:3; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ HtpConfigRestoreBackup();
+ ConfRestoreContextBackup();
+
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyTest17(void)
+{
+ char input[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+ personality: IDS\n\
+ request-body-limit: 0\n\
+ response-body-limit: 0\n\
+\n\
+ request-body-inspect-window: 0\n\
+ response-body-inspect-window: 0\n\
+ request-body-minimal-inspect-size: 0\n\
+ response-body-minimal-inspect-size: 0\n\
+";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ HtpConfigCreateBackup();
+
+ ConfYamlLoadString(input, strlen(input));
+ HTPConfigure();
+
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ int result = 0;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 17\r\n"
+ "\r\n"
+ "1234567";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "8901234ABC";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"890\"; depth:3; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ HTPFreeConfig();
+ HtpConfigRestoreBackup();
+ ConfRestoreContextBackup();
+
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/*
+ * gzip stream
+ */
+static int DetectEngineHttpServerBodyTest18(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = {
+ 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '2', '0', '0', 'o', 'k', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h', ':', ' ', '5', '1', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'g', 'z', 'i', 'p', 0x0d, 0x0a,
+ 0x0d, 0x0a,
+ 0x1f, 0x8b, 0x08, 0x08, 0x27, 0x1e, 0xe5, 0x51,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x78, 0x74, 0x00, 0x2b, 0xc9, 0xc8, 0x2c, 0x56,
+ 0x00, 0xa2, 0x44, 0x85, 0xb4, 0xcc, 0x9c, 0x54,
+ 0x85, 0xcc, 0x3c, 0x20, 0x2b, 0x29, 0xbf, 0x42,
+ 0x8f, 0x0b, 0x00, 0xb2, 0x7d, 0xac, 0x9b, 0x19,
+ 0x00, 0x00, 0x00,
+ };
+ uint32_t http_len2 = sizeof(http_buf2);
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"file\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/*
+ * deflate stream
+ */
+static int DetectEngineHttpServerBodyTest19(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = {
+ 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '2', '0', '0', 'o', 'k', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h', ':', ' ', '2', '4', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'd', 'e', 'f', 'l', 'a', 't', 'e', 0x0d, 0x0a,
+ 0x0d, 0x0a,
+ 0x2b, 0xc9, 0xc8, 0x2c, 0x56,
+ 0x00, 0xa2, 0x44, 0x85, 0xb4, 0xcc, 0x9c, 0x54,
+ 0x85, 0xcc, 0x3c, 0x20, 0x2b, 0x29, 0xbf, 0x42,
+ 0x8f, 0x0b, 0x00,
+ };
+ // 0xb2, 0x7d, 0xac, 0x9b, 0x19, 0x00, 0x00, 0x00,
+ uint32_t http_len2 = sizeof(http_buf2);
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"file\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/*
+ * deflate stream with gzip set as content-encoding
+ */
+static int DetectEngineHttpServerBodyTest20(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = {
+ 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '2', '0', '0', 'o', 'k', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h', ':', ' ', '2', '4', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'g', 'z', 'i', 'p', 0x0d, 0x0a,
+ 0x0d, 0x0a,
+ 0x2b, 0xc9, 0xc8, 0x2c, 0x56,
+ 0x00, 0xa2, 0x44, 0x85, 0xb4, 0xcc, 0x9c, 0x54,
+ 0x85, 0xcc, 0x3c, 0x20, 0x2b, 0x29, 0xbf, 0x42,
+ 0x8f, 0x0b, 0x00,
+ };
+ // 0xb2, 0x7d, 0xac, 0x9b, 0x19, 0x00, 0x00, 0x00,
+ uint32_t http_len2 = sizeof(http_buf2);
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"file\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/*
+ * gzip stream with deflate set as content-encoding.
+ */
+static int DetectEngineHttpServerBodyTest21(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = {
+ 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '2', '0', '0', 'o', 'k', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h', ':', ' ', '5', '1', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'd', 'e', 'f', 'l', 'a', 't', 'e', 0x0d, 0x0a,
+ 0x0d, 0x0a,
+ 0x1f, 0x8b, 0x08, 0x08, 0x27, 0x1e, 0xe5, 0x51,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x78, 0x74, 0x00, 0x2b, 0xc9, 0xc8, 0x2c, 0x56,
+ 0x00, 0xa2, 0x44, 0x85, 0xb4, 0xcc, 0x9c, 0x54,
+ 0x85, 0xcc, 0x3c, 0x20, 0x2b, 0x29, 0xbf, 0x42,
+ 0x8f, 0x0b, 0x00, 0xb2, 0x7d, 0xac, 0x9b, 0x19,
+ 0x00, 0x00, 0x00,
+ };
+ uint32_t http_len2 = sizeof(http_buf2);
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"file\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/*
+ * gzip stream.
+ * We have 2 content-encoding headers. First gzip and second deflate.
+ */
+static int DetectEngineHttpServerBodyTest22(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = {
+ 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ', '2', '0', '0', 'o', 'k', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h', ':', ' ', '5', '1', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'g', 'z', 'i', 'p', 0x0d, 0x0a,
+ 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', 'd', 'e', 'f', 'l', 'a', 't', 'e', 0x0d, 0x0a,
+ 0x0d, 0x0a,
+ 0x1f, 0x8b, 0x08, 0x08, 0x27, 0x1e, 0xe5, 0x51,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x78, 0x74, 0x00, 0x2b, 0xc9, 0xc8, 0x2c, 0x56,
+ 0x00, 0xa2, 0x44, 0x85, 0xb4, 0xcc, 0x9c, 0x54,
+ 0x85, 0xcc, 0x3c, 0x20, 0x2b, 0x29, 0xbf, 0x42,
+ 0x8f, 0x0b, 0x00, 0xb2, 0x7d, 0xac, 0x9b, 0x19,
+ 0x00, 0x00, 0x00,
+ };
+ uint32_t http_len2 = sizeof(http_buf2);
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"file\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyFileDataTest01(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "file_data; pcre:/ab/; "
+ "content:\"ef\"; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyFileDataTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "file_data; pcre:/abc/; "
+ "content:!\"xyz\"; distance:0; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/* \test recursive relative byte test */
+static int DetectEngineHttpServerBodyFileDataTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 33\r\n"
+ "\r\n"
+ "XYZ_klm_1234abcd_XYZ_klm_5678abcd";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ if (!(DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
+ "(msg:\"match on 1st\"; "
+ "file_data; content:\"XYZ\"; content:\"_klm_\"; distance:0; content:\"abcd\"; distance:4; byte_test:4,=,1234,-8,relative,string;"
+ "sid:1;)")))
+ goto end;
+ if (!(DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
+ "(msg:\"match on 2nd\"; "
+ "file_data; content:\"XYZ\"; content:\"_klm_\"; distance:0; content:\"abcd\"; distance:4; byte_test:4,=,5678,-8,relative,string;"
+ "sid:2;)")))
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+ if (!PacketAlertCheck(p2, 2)) {
+ printf("sid 2 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpServerBodyFileDataTest04(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 1 },
+ { (const uint8_t *)"ef",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abcd\"; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest05(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"ef",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abcdef\"; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest06(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"ef",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"bcdef\"; offset:1; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest07(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 1 },
+ { (const uint8_t *)"123456789",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"bc\"; offset:1; depth:2; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest08(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"1234567890",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"d123456789\"; offset:3; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest09(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"cd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"123456789",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abcd12\"; depth:6; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest10(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 5\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"c",
+ 0, STREAM_TOCLIENT, 1 },
+ { (const uint8_t *)"de",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abc\"; depth:3; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest11(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 5\r\n"
+ "\r\n"
+ "ab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"c",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"de",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"bcde\"; offset:1; depth:4; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest12(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "a",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"b",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"c",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"d",
+ 0, STREAM_TOCLIENT, 1 },
+ { (const uint8_t *)"efghijklm",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abcd\"; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest13(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "a",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"b",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"c",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"d",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"efghijklm",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"abcdefghijklm\"; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest14(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 20\r\n"
+ "\r\n"
+ "1234567890",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"abcdefghi",
+ 0, STREAM_TOCLIENT, 1 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"890abcdefghi\"; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest15(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 20\r\n"
+ "\r\n"
+ "1234567890",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"abcdefghi",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"7890ab\"; depth:6; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest16(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 20\r\n"
+ "\r\n"
+ "aaaab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"bbbbc",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"ccccd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"dddde",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"aabb\"; depth:4; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest17(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 20\r\n"
+ "\r\n"
+ "aaaab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"bbbbc",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"ccccd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"dddde",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"bbbc\"; depth:4; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+
+static int DetectEngineHttpServerBodyFileDataTest18(void)
+{
+
+ const char yaml[] = "\
+%YAML 1.1\n\
+---\n\
+libhtp:\n\
+\n\
+ default-config:\n\
+\n\
+ http-body-inline: yes\n\
+ response-body-minimal-inspect-size: 6\n\
+ response-body-inspect-window: 3\n\
+";
+
+ struct TestSteps steps[] = {
+ { (const uint8_t *)"GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n",
+ 0, STREAM_TOSERVER, 0 },
+ { (const uint8_t *)"HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 20\r\n"
+ "\r\n"
+ "aaaab",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"bbbbc",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"ccccd",
+ 0, STREAM_TOCLIENT, 0 },
+ { (const uint8_t *)"dddde",
+ 0, STREAM_TOCLIENT, 0 },
+ { NULL, 0, 0, 0 },
+ };
+
+ const char *sig = "alert http any any -> any any (file_data; content:\"bccd\"; depth:4; sid:1;)";
+ return RunTest(steps, sig, yaml);
+}
+#endif /* UNITTESTS */
+
+void DetectEngineHttpServerBodyRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpServerBodyTest01",
+ DetectEngineHttpServerBodyTest01, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest02",
+ DetectEngineHttpServerBodyTest02, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest03",
+ DetectEngineHttpServerBodyTest03, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest04",
+ DetectEngineHttpServerBodyTest04, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest05",
+ DetectEngineHttpServerBodyTest05, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest06",
+ DetectEngineHttpServerBodyTest06, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest07",
+ DetectEngineHttpServerBodyTest07, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest08",
+ DetectEngineHttpServerBodyTest08, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest09",
+ DetectEngineHttpServerBodyTest09, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest10",
+ DetectEngineHttpServerBodyTest10, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest11",
+ DetectEngineHttpServerBodyTest11, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest12",
+ DetectEngineHttpServerBodyTest12, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest13",
+ DetectEngineHttpServerBodyTest13, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest14",
+ DetectEngineHttpServerBodyTest14, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest15",
+ DetectEngineHttpServerBodyTest15, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest16",
+ DetectEngineHttpServerBodyTest16, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest17",
+ DetectEngineHttpServerBodyTest17, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest18",
+ DetectEngineHttpServerBodyTest18, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest19",
+ DetectEngineHttpServerBodyTest19, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest20",
+ DetectEngineHttpServerBodyTest20, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest21",
+ DetectEngineHttpServerBodyTest21, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyTest22",
+ DetectEngineHttpServerBodyTest22, 1);
+
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest01",
+ DetectEngineHttpServerBodyFileDataTest01, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest02",
+ DetectEngineHttpServerBodyFileDataTest02, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest03",
+ DetectEngineHttpServerBodyFileDataTest03, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest04",
+ DetectEngineHttpServerBodyFileDataTest04, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest05",
+ DetectEngineHttpServerBodyFileDataTest05, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest06",
+ DetectEngineHttpServerBodyFileDataTest06, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest07",
+ DetectEngineHttpServerBodyFileDataTest07, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest08",
+ DetectEngineHttpServerBodyFileDataTest08, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest09",
+ DetectEngineHttpServerBodyFileDataTest09, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest10",
+ DetectEngineHttpServerBodyFileDataTest10, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest11",
+ DetectEngineHttpServerBodyFileDataTest11, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest12",
+ DetectEngineHttpServerBodyFileDataTest12, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest13",
+ DetectEngineHttpServerBodyFileDataTest13, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest14",
+ DetectEngineHttpServerBodyFileDataTest14, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest15",
+ DetectEngineHttpServerBodyFileDataTest15, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest16",
+ DetectEngineHttpServerBodyFileDataTest16, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest17",
+ DetectEngineHttpServerBodyFileDataTest17, 1);
+ UtRegisterTest("DetectEngineHttpServerBodyFileDataTest18",
+ DetectEngineHttpServerBodyFileDataTest18, 1);
+
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hsbd.h b/framework/src/suricata/src/detect-engine-hsbd.h
new file mode 100644
index 00000000..a4424824
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hsbd.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HSBD_H__
+#define __DETECT_ENGINE_HSBD_H__
+
+#define ENGINE_HSBD_BUFFER_LIMIT 20000
+
+#include "app-layer-htp.h"
+
+int DetectEngineRunHttpServerBodyMpm(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+int DetectEngineInspectHttpServerBody(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineCleanHSBDBuffers(DetectEngineThreadCtx *det_ctx);
+
+void DetectEngineHttpServerBodyRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HSBD_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-hscd.c b/framework/src/suricata/src/detect-engine-hscd.c
new file mode 100644
index 00000000..a38562b1
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hscd.c
@@ -0,0 +1,2097 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-hscd.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+/**
+ * \brief Run the mpm against http stat code.
+ *
+ * \retval cnt Number of matches reported by the mpm algo.
+ */
+int DetectEngineRunHttpStatCodeMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ SCEnter();
+
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->response_status == NULL)
+ goto end;
+
+ cnt = HttpStatCodePatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(tx->response_status),
+ bstr_len(tx->response_status), flags);
+
+end:
+ SCReturnInt(cnt);
+}
+
+/**
+ * \brief Do the http_stat_code content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpStatCode(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->response_status == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s,
+ s->sm_lists[DETECT_SM_LIST_HSCDMATCH],
+ f,
+ (uint8_t *)bstr_ptr(tx->response_status),
+ bstr_len(tx->response_status),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSCD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+static int DetectEngineHttpStatCodeTest01(void)
+ {
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 message\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"200\"; http_stat_code; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 2000123 xxxxABC\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "xxxxABC";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"123\"; http_stat_code; offset:4; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ int result = 0;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 123";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "456789\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 17\r\n"
+ "\r\n"
+ "12345678901234ABC";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"789\"; http_stat_code; offset:5; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest04(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:!\"200\"; http_stat_code; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest05(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"200\"; http_stat_code; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest06(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:!\"123\"; http_stat_code; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:!\"123\"; http_stat_code; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:!\"200\"; http_stat_code; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"200\"; http_stat_code; depth:3; "
+ "content:\"123\"; http_stat_code; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"200\"; http_stat_code; depth:3; "
+ "content:!\"124\"; http_stat_code; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest11(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"200\"; http_stat_code; depth:3; "
+ "content:\"124\"; http_stat_code; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did match but should not have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest12(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"20\"; http_stat_code; depth:2; "
+ "content:\"23\"; http_stat_code; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest13(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "content:\"20\"; http_stat_code; depth:3; "
+ "content:!\"25\"; http_stat_code; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest14(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "pcre:/20/S; "
+ "content:\"23\"; http_stat_code; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatCodeTest15(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200123 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat code test\"; "
+ "pcre:/200/S; "
+ "content:!\"124\"; http_stat_code; distance:0; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpStatCodeRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpStatCodeTest01",
+ DetectEngineHttpStatCodeTest01, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest02",
+ DetectEngineHttpStatCodeTest02, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest03",
+ DetectEngineHttpStatCodeTest03, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest04",
+ DetectEngineHttpStatCodeTest04, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest05",
+ DetectEngineHttpStatCodeTest05, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest06",
+ DetectEngineHttpStatCodeTest06, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest07",
+ DetectEngineHttpStatCodeTest07, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest08",
+ DetectEngineHttpStatCodeTest08, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest09",
+ DetectEngineHttpStatCodeTest09, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest10",
+ DetectEngineHttpStatCodeTest10, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest11",
+ DetectEngineHttpStatCodeTest11, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest12",
+ DetectEngineHttpStatCodeTest12, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest13",
+ DetectEngineHttpStatCodeTest13, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest14",
+ DetectEngineHttpStatCodeTest14, 1);
+ UtRegisterTest("DetectEngineHttpStatCodeTest15",
+ DetectEngineHttpStatCodeTest15, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hscd.h b/framework/src/suricata/src/detect-engine-hscd.h
new file mode 100644
index 00000000..2a97c2ff
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hscd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HSCD_H__
+#define __DETECT_ENGINE_HSCD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineRunHttpStatCodeMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+int DetectEngineInspectHttpStatCode(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineHttpStatCodeRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HSCD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hsmd.c b/framework/src/suricata/src/detect-engine-hsmd.c
new file mode 100644
index 00000000..61c8e2e9
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hsmd.c
@@ -0,0 +1,2097 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-hsmd.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+/**
+ * \brief Run the mpm against http stat msg.
+ *
+ * \retval cnt Number of matches reported by the mpm algo.
+ */
+int DetectEngineRunHttpStatMsgMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ SCEnter();
+
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->response_message == NULL)
+ goto end;
+
+ cnt = HttpStatMsgPatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(tx->response_message),
+ bstr_len(tx->response_message), flags);
+
+end:
+ SCReturnInt(cnt);
+}
+
+/**
+ * \brief Do the http_stat_msg content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpStatMsg(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->response_message == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s,
+ s->sm_lists[DETECT_SM_LIST_HSMDMATCH],
+ f,
+ (uint8_t *)bstr_ptr(tx->response_message),
+ bstr_len(tx->response_message),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HSMD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+static int DetectEngineHttpStatMsgTest01(void)
+ {
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 message\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"message\"; http_stat_msg; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 xxxxABC\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "xxxxABC";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"ABC\"; http_stat_msg; offset:4; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ int result = 0;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 1234567";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "8901234ABC\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 17\r\n"
+ "\r\n"
+ "12345678901234ABC";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"ABC\"; http_stat_msg; offset:14; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest04(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:!\"abc\"; http_stat_msg; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest05(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"abc\"; http_stat_msg; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest06(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:!\"def\"; http_stat_msg; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:!\"def\"; http_stat_msg; offset:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:!\"abc\"; http_stat_msg; depth:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"abc\"; http_stat_msg; depth:3; "
+ "content:\"def\"; http_stat_msg; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"abc\"; http_stat_msg; depth:3; "
+ "content:!\"xyz\"; http_stat_msg; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest11(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"abc\"; http_stat_msg; depth:3; "
+ "content:\"xyz\"; http_stat_msg; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did match but should not have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest12(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"ab\"; http_stat_msg; depth:2; "
+ "content:\"ef\"; http_stat_msg; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest13(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "content:\"ab\"; http_stat_msg; depth:3; "
+ "content:!\"yz\"; http_stat_msg; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest14(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "pcre:/ab/Y; "
+ "content:\"ef\"; http_stat_msg; distance:2; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectEngineHttpStatMsgTest15(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 abcdef\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "abcdef";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http stat msg test\"; "
+ "pcre:/abc/Y; "
+ "content:!\"xyz\"; http_stat_msg; distance:0; within:3; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!PacketAlertCheck(p2, 1)) {
+ printf("sid 1 did not match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpStatMsgRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpStatMsgTest01",
+ DetectEngineHttpStatMsgTest01, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest02",
+ DetectEngineHttpStatMsgTest02, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest03",
+ DetectEngineHttpStatMsgTest03, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest04",
+ DetectEngineHttpStatMsgTest04, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest05",
+ DetectEngineHttpStatMsgTest05, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest06",
+ DetectEngineHttpStatMsgTest06, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest07",
+ DetectEngineHttpStatMsgTest07, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest08",
+ DetectEngineHttpStatMsgTest08, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest09",
+ DetectEngineHttpStatMsgTest09, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest10",
+ DetectEngineHttpStatMsgTest10, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest11",
+ DetectEngineHttpStatMsgTest11, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest12",
+ DetectEngineHttpStatMsgTest12, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest13",
+ DetectEngineHttpStatMsgTest13, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest14",
+ DetectEngineHttpStatMsgTest14, 1);
+ UtRegisterTest("DetectEngineHttpStatMsgTest15",
+ DetectEngineHttpStatMsgTest15, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hsmd.h b/framework/src/suricata/src/detect-engine-hsmd.h
new file mode 100644
index 00000000..01fc3612
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hsmd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HSMD_H__
+#define __DETECT_ENGINE_HSMD_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineRunHttpStatMsgMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+int DetectEngineInspectHttpStatMsg(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void DetectEngineHttpStatMsgRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HSMD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-hua.c b/framework/src/suricata/src/detect-engine-hua.c
new file mode 100644
index 00000000..8cafa423
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hua.c
@@ -0,0 +1,1855 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief Handle HTTP user agent match
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+#include "detect-engine-hua.h"
+
+int DetectEngineRunHttpUAMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ uint32_t cnt = 0;
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ if (tx->request_headers == NULL)
+ goto end;
+
+ htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "User-Agent");
+ if (h == NULL) {
+ SCLogDebug("HTTP user agent header not present in this request");
+ goto end;
+ }
+ cnt = HttpUAPatternSearch(det_ctx,
+ (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value), flags);
+
+ end:
+ return cnt;
+}
+
+/**
+ * \brief Do the http_user_agent content inspection for a signature.
+ *
+ * \param de_ctx Detection engine context.
+ * \param det_ctx Detection engine thread context.
+ * \param s Signature to inspect.
+ * \param f Flow.
+ * \param flags App layer flags.
+ * \param state App layer state.
+ *
+ * \retval 0 No match.
+ * \retval 1 Match.
+ */
+int DetectEngineInspectHttpUA(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "User-Agent");
+ if (h == NULL) {
+ SCLogDebug("HTTP user agent header not present in this request");
+ goto end;
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HUADMATCH],
+ f,
+ (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HUAD, NULL);
+ if (r == 1)
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CONNECT\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest02(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CO\"; depth:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest03(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_user_agent test\"; "
+ "content:!\"ECT\"; depth:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest04(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"ECT\"; depth:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest05(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:!\"CON\"; depth:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"ECT\"; offset:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest07(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:!\"CO\"; offset:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:!\"ECT\"; offset:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest09(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CON\"; offset:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest10(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_user_agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:\"EC\"; within:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:!\"EC\"; within:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_user_agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:\"EC\"; within:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:!\"EC\"; within:4; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest14(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_user_agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:\"EC\"; distance:2; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest15(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:!\"EC\"; distance:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest16(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:\"EC\"; distance:3; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectEngineHttpUATest17(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "CONNECT /index.html HTTP/1.0\r\n"
+ "User-Agent: CONNECT\r\n"
+ "Host: www.onetwothreefourfivesixseven.org\r\n\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http_user_agent test\"; "
+ "content:\"CO\"; http_user_agent; "
+ "content:!\"EC\"; distance:2; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectEngineHttpUARegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineHttpUATest01",
+ DetectEngineHttpUATest01, 1);
+ UtRegisterTest("DetectEngineHttpUATest02",
+ DetectEngineHttpUATest02, 1);
+ UtRegisterTest("DetectEngineHttpUATest03",
+ DetectEngineHttpUATest03, 1);
+ UtRegisterTest("DetectEngineHttpUATest04",
+ DetectEngineHttpUATest04, 1);
+ UtRegisterTest("DetectEngineHttpUATest05",
+ DetectEngineHttpUATest05, 1);
+ UtRegisterTest("DetectEngineHttpUATest06",
+ DetectEngineHttpUATest06, 1);
+ UtRegisterTest("DetectEngineHttpUATest07",
+ DetectEngineHttpUATest07, 1);
+ UtRegisterTest("DetectEngineHttpUATest08",
+ DetectEngineHttpUATest08, 1);
+ UtRegisterTest("DetectEngineHttpUATest09",
+ DetectEngineHttpUATest09, 1);
+ UtRegisterTest("DetectEngineHttpUATest10",
+ DetectEngineHttpUATest10, 1);
+ UtRegisterTest("DetectEngineHttpUATest11",
+ DetectEngineHttpUATest11, 1);
+ UtRegisterTest("DetectEngineHttpUATest12",
+ DetectEngineHttpUATest12, 1);
+ UtRegisterTest("DetectEngineHttpUATest13",
+ DetectEngineHttpUATest13, 1);
+ UtRegisterTest("DetectEngineHttpUATest14",
+ DetectEngineHttpUATest14, 1);
+ UtRegisterTest("DetectEngineHttpUATest15",
+ DetectEngineHttpUATest15, 1);
+ UtRegisterTest("DetectEngineHttpUATest16",
+ DetectEngineHttpUATest16, 1);
+ UtRegisterTest("DetectEngineHttpUATest17",
+ DetectEngineHttpUATest17, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-hua.h b/framework/src/suricata/src/detect-engine-hua.h
new file mode 100644
index 00000000..c18ac761
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-hua.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_HUA_H__
+#define __DETECT_ENGINE_HUA_H__
+
+#include "app-layer-htp.h"
+
+int DetectEngineInspectHttpUA(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+int DetectEngineRunHttpUAMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+void DetectEngineHttpUARegisterTests(void);
+
+#endif /* __DETECT_ENGINE_HUA_H__ */
diff --git a/framework/src/suricata/src/detect-engine-iponly.c b/framework/src/suricata/src/detect-engine-iponly.c
new file mode 100644
index 00000000..06983fc0
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-iponly.c
@@ -0,0 +1,2321 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Signatures that only inspect IP addresses are processed here
+ * We use radix trees for src dst ipv4 and ipv6 adresses
+ * This radix trees hold information for subnets and hosts in a
+ * hierarchical distribution
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "decode.h"
+#include "flow.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+#include "detect-engine-proto.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-engine-threshold.h"
+#include "detect-engine-iponly.h"
+#include "detect-threshold.h"
+#include "util-classification-config.h"
+#include "util-rule-vars.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-print.h"
+#include "util-profiling.h"
+
+#ifdef OS_WIN32
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#endif /* OS_WIN32 */
+
+/**
+ * \brief This function creates a new IPOnlyCIDRItem
+ *
+ * \retval IPOnlyCIDRItem address of the new instance
+ */
+static IPOnlyCIDRItem *IPOnlyCIDRItemNew()
+{
+ SCEnter();
+ IPOnlyCIDRItem *item = NULL;
+
+ item = SCMalloc(sizeof(IPOnlyCIDRItem));
+ if (unlikely(item == NULL))
+ SCReturnPtr(NULL, "IPOnlyCIDRItem");
+ memset(item, 0, sizeof(IPOnlyCIDRItem));
+
+ SCReturnPtr(item, "IPOnlyCIDRItem");
+}
+
+static uint8_t IPOnlyCIDRItemCompare(IPOnlyCIDRItem *head,
+ IPOnlyCIDRItem *item)
+{
+ uint8_t i = 0;
+ for (; i < head->netmask / 32 || i < 1; i++) {
+ if (item->ip[i] < head->ip[i])
+ //if (*(uint8_t *)(item->ip + i) < *(uint8_t *)(head->ip + i))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Parses an ipv4/ipv6 address string and updates the result into the
+ * IPOnlyCIDRItem instance sent as the argument.
+ *
+ * \param dd Pointer to the IPOnlyCIDRItem instance which should be updated with
+ * the address (in cidr) details from the parsed ip string.
+ * \param str Pointer to address string that has to be parsed.
+ *
+ * \retval 0 On successfully parsing the address string.
+ * \retval -1 On failure.
+ */
+static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem *dd, char *str)
+{
+ char buf[256] = "";
+ char *ip = NULL, *ip2 = NULL;
+ char *mask = NULL;
+ int r = 0;
+
+ while (*str != '\0' && *str == ' ')
+ str++;
+
+ SCLogDebug("str %s", str);
+ strlcpy(buf, str, sizeof(buf));
+ ip = buf;
+
+ /* first handle 'any' */
+ if (strcasecmp(str, "any") == 0) {
+ /* if any, insert 0.0.0.0/0 and ::/0 as well */
+ SCLogDebug("adding 0.0.0.0/0 and ::/0 as we\'re handling \'any\'");
+
+ IPOnlyCIDRItemParseSingle(dd, "0.0.0.0/0");
+ BUG_ON(dd->family == 0);
+
+ dd->next = IPOnlyCIDRItemNew();
+ if (dd->next == NULL)
+ goto error;
+
+ IPOnlyCIDRItemParseSingle(dd->next, "::/0");
+ BUG_ON(dd->family == 0);
+
+ SCLogDebug("address is \'any\'");
+ return 0;
+ }
+
+ /* handle the negation case */
+ if (ip[0] == '!') {
+ dd->negated = (dd->negated)? 0 : 1;
+ ip++;
+ }
+
+ /* see if the address is an ipv4 or ipv6 address */
+ if ((strchr(str, ':')) == NULL) {
+ /* IPv4 Address */
+ struct in_addr in;
+
+ dd->family = AF_INET;
+
+ if ((mask = strchr(ip, '/')) != NULL) {
+ /* 1.2.3.4/xxx format (either dotted or cidr notation */
+ ip[mask - ip] = '\0';
+ mask++;
+ uint32_t netmask = 0;
+ size_t u = 0;
+
+ if ((strchr (mask, '.')) == NULL) {
+ /* 1.2.3.4/24 format */
+
+ for (u = 0; u < strlen(mask); u++) {
+ if(!isdigit((unsigned char)mask[u]))
+ goto error;
+ }
+
+ int cidr = atoi(mask);
+ if (cidr < 0 || cidr > 32)
+ goto error;
+
+ dd->netmask = cidr;
+ } else {
+ /* 1.2.3.4/255.255.255.0 format */
+ r = inet_pton(AF_INET, mask, &in);
+ if (r <= 0)
+ goto error;
+
+ netmask = in.s_addr;
+
+ /* Extract cidr netmask */
+ while ((0x01 & netmask) == 0) {
+ dd->netmask++;
+ netmask = netmask >> 1;
+ }
+ dd->netmask = 32 - dd->netmask;
+ }
+
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+
+ dd->ip[0] = in.s_addr;
+
+ } else if ((ip2 = strchr(ip, '-')) != NULL) {
+ /* 1.2.3.4-1.2.3.6 range format */
+ ip[ip2 - ip] = '\0';
+ ip2++;
+
+ uint32_t tmp_ip[4];
+ uint32_t tmp_ip2[4];
+ uint32_t first, last;
+
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+ tmp_ip[0] = in.s_addr;
+
+ r = inet_pton(AF_INET, ip2, &in);
+ if (r <= 0)
+ goto error;
+ tmp_ip2[0] = in.s_addr;
+
+ /* a > b is illegal, a = b is ok */
+ if (ntohl(tmp_ip[0]) > ntohl(tmp_ip2[0]))
+ goto error;
+
+ first = ntohl(tmp_ip[0]);
+ last = ntohl(tmp_ip2[0]);
+
+ dd->netmask = 32;
+ dd->ip[0] =htonl(first);
+
+ if (first < last) {
+ for (first++; first <= last; first++) {
+ IPOnlyCIDRItem *new = IPOnlyCIDRItemNew();
+ if (new == NULL)
+ goto error;
+ dd->next = new;
+ new->negated = dd->negated;
+ new->family= dd->family;
+ new->netmask = dd->netmask;
+ new->ip[0] = htonl(first);
+ dd = dd->next;
+ }
+ }
+
+ } else {
+ /* 1.2.3.4 format */
+ r = inet_pton(AF_INET, ip, &in);
+ if (r <= 0)
+ goto error;
+
+ /* single host */
+ dd->ip[0] = in.s_addr;
+ dd->netmask = 32;
+ }
+ } else {
+ /* IPv6 Address */
+ struct in6_addr in6;
+ uint32_t ip6addr[4];
+
+ dd->family = AF_INET6;
+
+ if ((mask = strchr(ip, '/')) != NULL) {
+ mask[0] = '\0';
+ mask++;
+
+ r = inet_pton(AF_INET6, ip, &in6);
+ if (r <= 0)
+ goto error;
+
+ /* Format is cidr val */
+ dd->netmask = atoi(mask);
+
+ memcpy(dd->ip, &in6.s6_addr, sizeof(ip6addr));
+ } else {
+ r = inet_pton(AF_INET6, ip, &in6);
+ if (r <= 0)
+ goto error;
+
+ memcpy(dd->ip, &in6.s6_addr, sizeof(dd->ip));
+ dd->netmask = 128;
+ }
+
+ }
+
+ BUG_ON(dd->family == 0);
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Setup a single address string, parse it and add the resulting
+ * Address items in cidr format to the list of gh
+ *
+ * \param gh Pointer to the IPOnlyCIDRItem list Head to which the
+ * resulting Address-Range(s) from the parsed ip string has to
+ * be added.
+ * \param s Pointer to the ip address string to be parsed.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int IPOnlyCIDRItemSetup(IPOnlyCIDRItem *gh, char *s)
+{
+ SCLogDebug("gh %p, s %s", gh, s);
+
+ /* parse the address */
+ if (IPOnlyCIDRItemParseSingle(gh, s) == -1) {
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC,
+ "DetectAddressParse error \"%s\"", s);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+
+/**
+ * \brief This function insert a IPOnlyCIDRItem
+ * to a list of IPOnlyCIDRItems sorted by netmask
+ * ascending
+ * \param head Pointer to the head of IPOnlyCIDRItems list
+ * \param item Pointer to the item to insert in the list
+ *
+ * \retval IPOnlyCIDRItem address of the new head if apply
+ */
+static IPOnlyCIDRItem *IPOnlyCIDRItemInsertReal(IPOnlyCIDRItem *head,
+ IPOnlyCIDRItem *item)
+{
+ IPOnlyCIDRItem *it, *prev = NULL;
+
+ if (item == NULL)
+ return head;
+
+ /* Compare with the head */
+ if (item->netmask < head->netmask || (item->netmask == head->netmask && IPOnlyCIDRItemCompare(head, item))) {
+ item->next = head;
+ return item;
+ }
+
+ if (item->netmask == head->netmask && !IPOnlyCIDRItemCompare(head, item)) {
+ item->next = head->next;
+ head->next = item;
+ return head;
+ }
+
+ for (prev = it = head;
+ it != NULL && it->netmask < item->netmask;
+ it = it->next)
+ prev = it;
+
+ if (it == NULL) {
+ prev->next = item;
+ item->next = NULL;
+ } else {
+ item->next = it;
+ prev->next = item;
+ }
+
+ return head;
+}
+
+/**
+ * \brief This function insert a IPOnlyCIDRItem list
+ * to a list of IPOnlyCIDRItems sorted by netmask
+ * ascending
+ * \param head Pointer to the head of IPOnlyCIDRItems list
+ * \param item Pointer to the list of items to insert in the list
+ *
+ * \retval IPOnlyCIDRItem address of the new head if apply
+ */
+static IPOnlyCIDRItem *IPOnlyCIDRItemInsert(IPOnlyCIDRItem *head,
+ IPOnlyCIDRItem *item)
+{
+ IPOnlyCIDRItem *it, *prev = NULL;
+
+ /* The first element */
+ if (head == NULL) {
+ SCLogDebug("Head is NULL to insert item (%p)",item);
+ return item;
+ }
+
+ if (item == NULL) {
+ SCLogDebug("Item is NULL");
+ return head;
+ }
+
+ SCLogDebug("Inserting item(%p)->netmask %u head %p", item, item->netmask, head);
+
+ prev = item;
+ while (prev != NULL) {
+ it = prev->next;
+
+ /* Separate from the item list */
+ prev->next = NULL;
+
+ //SCLogDebug("Before:");
+ //IPOnlyCIDRListPrint(head);
+ head = IPOnlyCIDRItemInsertReal(head, prev);
+ //SCLogDebug("After:");
+ //IPOnlyCIDRListPrint(head);
+ prev = it;
+ }
+
+ return head;
+}
+
+/**
+ * \brief This function free a IPOnlyCIDRItem list
+ * \param tmphead Pointer to the list
+ */
+void IPOnlyCIDRListFree(IPOnlyCIDRItem *tmphead)
+{
+ SCEnter();
+ uint32_t i = 0;
+
+ IPOnlyCIDRItem *it, *next = NULL;
+
+ if (tmphead == NULL) {
+ SCLogDebug("temphead is NULL");
+ return;
+ }
+
+ it = tmphead;
+ next = it->next;
+
+ while (it != NULL) {
+ i++;
+ SCFree(it);
+ SCLogDebug("Item(%p) %"PRIu32" removed\n", it, i);
+ it = next;
+
+ if (next != NULL)
+ next = next->next;
+ }
+ SCReturn;
+}
+
+/**
+ * \brief This function update a list of IPOnlyCIDRItems
+ * setting the signature internal id (signum) to "i"
+ *
+ * \param tmphead Pointer to the list
+ * \param i number of signature internal id
+ */
+static void IPOnlyCIDRListSetSigNum(IPOnlyCIDRItem *tmphead, SigIntId i)
+{
+ while (tmphead != NULL) {
+ tmphead->signum = i;
+ tmphead = tmphead->next;
+ }
+}
+
+#ifdef UNITTESTS
+/**
+ * \brief This function print a IPOnlyCIDRItem list
+ * \param tmphead Pointer to the head of IPOnlyCIDRItems list
+ */
+static void IPOnlyCIDRListPrint(IPOnlyCIDRItem *tmphead)
+{
+ uint32_t i = 0;
+
+ while (tmphead != NULL) {
+ i++;
+ SCLogDebug("Item %"PRIu32" has netmask %"PRIu16" negated:"
+ " %s; IP: %s; signum: %"PRIu16, i, tmphead->netmask,
+ (tmphead->negated) ? "yes":"no",
+ inet_ntoa(*(struct in_addr*)&tmphead->ip[0]),
+ tmphead->signum);
+ tmphead = tmphead->next;
+ }
+}
+#endif
+
+/**
+ * \brief This function print a SigNumArray, it's used with the
+ * radix tree print function to help debugging
+ * \param tmp Pointer to the head of SigNumArray
+ */
+static void SigNumArrayPrint(void *tmp)
+{
+ SigNumArray *sna = (SigNumArray *)tmp;
+ uint32_t u;
+
+ for (u = 0; u < sna->size; u++) {
+ uint8_t bitarray = sna->array[u];
+ uint8_t i = 0;
+
+ for (; i < 8; i++) {
+ if (bitarray & 0x01)
+ printf(", %"PRIu32"", u * 8 + i);
+ else
+ printf(", ");
+
+ bitarray = bitarray >> 1;
+ }
+ }
+}
+
+/**
+ * \brief This function creates a new SigNumArray with the
+ * size fixed to the io_ctx->max_idx
+ * \param de_ctx Pointer to the current detection context
+ * \param io_ctx Pointer to the current ip only context
+ *
+ * \retval SigNumArray address of the new instance
+ */
+static SigNumArray *SigNumArrayNew(DetectEngineCtx *de_ctx,
+ DetectEngineIPOnlyCtx *io_ctx)
+{
+ SigNumArray *new = SCMalloc(sizeof(SigNumArray));
+
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SigNumArrayNew. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(new, 0, sizeof(SigNumArray));
+
+ new->array = SCMalloc(io_ctx->max_idx / 8 + 1);
+ if (new->array == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memset(new->array, 0, io_ctx->max_idx / 8 + 1);
+ new->size = io_ctx->max_idx / 8 + 1;
+
+ SCLogDebug("max idx= %u", io_ctx->max_idx);
+
+ return new;
+}
+
+/**
+ * \brief This function creates a new SigNumArray with the
+ * same data as the argument
+ *
+ * \param orig Pointer to the original SigNumArray to copy
+ *
+ * \retval SigNumArray address of the new instance
+ */
+static SigNumArray *SigNumArrayCopy(SigNumArray *orig)
+{
+ SigNumArray *new = SCMalloc(sizeof(SigNumArray));
+
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SigNumArrayCopy. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(new, 0, sizeof(SigNumArray));
+ new->size = orig->size;
+
+ new->array = SCMalloc(orig->size);
+ if (new->array == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(new->array, orig->array, orig->size);
+ return new;
+}
+
+/**
+ * \brief This function free() a SigNumArray
+ * \param orig Pointer to the original SigNumArray to copy
+ */
+static void SigNumArrayFree(void *tmp)
+{
+ SigNumArray *sna = (SigNumArray *)tmp;
+
+ if (sna == NULL)
+ return;
+
+ if (sna->array != NULL)
+ SCFree(sna->array);
+
+ SCFree(sna);
+}
+
+/**
+ * \brief This function parses and return a list of IPOnlyCIDRItem
+ *
+ * \param s Pointer to the string of the addresses
+ * (in the format of signatures)
+ * \param negate flag to indicate if all this string is negated or not
+ *
+ * \retval 0 if success
+ * \retval -1 if fails
+ */
+static IPOnlyCIDRItem *IPOnlyCIDRListParse2(const DetectEngineCtx *de_ctx,
+ char *s, int negate)
+{
+ size_t x = 0;
+ size_t u = 0;
+ int o_set = 0, n_set = 0, d_set = 0;
+ int depth = 0;
+ size_t size = strlen(s);
+ char address[8196] = "";
+ char *rule_var_address = NULL;
+ char *temp_rule_var_address = NULL;
+ IPOnlyCIDRItem *head;
+ IPOnlyCIDRItem *subhead;
+ head = subhead = NULL;
+
+ SCLogDebug("s %s negate %s", s, negate ? "true" : "false");
+
+ for (u = 0, x = 0; u < size && x < sizeof(address); u++) {
+ address[x] = s[u];
+ x++;
+
+ if (!o_set && s[u] == '!') {
+ n_set = 1;
+ x--;
+ } else if (s[u] == '[') {
+ if (!o_set) {
+ o_set = 1;
+ x = 0;
+ }
+ depth++;
+ } else if (s[u] == ']') {
+ if (depth == 1) {
+ address[x - 1] = '\0';
+ x = 0;
+
+ if ( (subhead = IPOnlyCIDRListParse2(de_ctx, address,
+ (negate + n_set) % 2)) == NULL)
+ goto error;
+
+ head = IPOnlyCIDRItemInsert(head, subhead);
+ n_set = 0;
+ }
+ depth--;
+ } else if (depth == 0 && s[u] == ',') {
+ if (o_set == 1) {
+ o_set = 0;
+ } else if (d_set == 1) {
+ address[x - 1] = '\0';
+
+ rule_var_address = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_ADDRESS_GROUPS);
+ if (rule_var_address == NULL)
+ goto error;
+
+ temp_rule_var_address = rule_var_address;
+ if ((negate + n_set) % 2) {
+ temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3);
+
+ if (unlikely(temp_rule_var_address == NULL)) {
+ goto error;
+ }
+
+ snprintf(temp_rule_var_address, strlen(rule_var_address) + 3,
+ "[%s]", rule_var_address);
+ }
+
+ subhead = IPOnlyCIDRListParse2(de_ctx, temp_rule_var_address,
+ (negate + n_set) % 2);
+ head = IPOnlyCIDRItemInsert(head, subhead);
+
+ d_set = 0;
+ n_set = 0;
+
+ if (temp_rule_var_address != rule_var_address)
+ SCFree(temp_rule_var_address);
+
+ } else {
+ address[x - 1] = '\0';
+
+ subhead = IPOnlyCIDRItemNew();
+ if (subhead == NULL)
+ goto error;
+
+ if (!((negate + n_set) % 2))
+ subhead->negated = 0;
+ else
+ subhead->negated = 1;
+
+ if (IPOnlyCIDRItemSetup(subhead, address) < 0) {
+ IPOnlyCIDRListFree(subhead);
+ subhead = NULL;
+ goto error;
+ }
+ head = IPOnlyCIDRItemInsert(head, subhead);
+
+ n_set = 0;
+ }
+ x = 0;
+ } else if (depth == 0 && s[u] == '$') {
+ d_set = 1;
+ } else if (depth == 0 && u == size - 1) {
+ if (x == sizeof(address)) {
+ address[x - 1] = '\0';
+ } else {
+ address[x] = '\0';
+ }
+ x = 0;
+
+ if (d_set == 1) {
+ rule_var_address = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_ADDRESS_GROUPS);
+ if (rule_var_address == NULL)
+ goto error;
+
+ temp_rule_var_address = rule_var_address;
+ if ((negate + n_set) % 2) {
+ temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3);
+ if (unlikely(temp_rule_var_address == NULL)) {
+ goto error;
+ }
+ snprintf(temp_rule_var_address, strlen(rule_var_address) + 3,
+ "[%s]", rule_var_address);
+ }
+ subhead = IPOnlyCIDRListParse2(de_ctx, temp_rule_var_address,
+ (negate + n_set) % 2);
+ head = IPOnlyCIDRItemInsert(head, subhead);
+
+ d_set = 0;
+
+ if (temp_rule_var_address != rule_var_address)
+ SCFree(temp_rule_var_address);
+ } else {
+ subhead = IPOnlyCIDRItemNew();
+ if (subhead == NULL)
+ goto error;
+
+ if (!((negate + n_set) % 2))
+ subhead->negated = 0;
+ else
+ subhead->negated = 1;
+
+ if (IPOnlyCIDRItemSetup(subhead, address) < 0) {
+ IPOnlyCIDRListFree(subhead);
+ subhead = NULL;
+ goto error;
+ }
+ head = IPOnlyCIDRItemInsert(head, subhead);
+ }
+ n_set = 0;
+ }
+ }
+
+ return head;
+
+error:
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC,"Error parsing addresses");
+ return head;
+}
+
+
+/**
+ * \brief Parses an address group sent as a character string and updates the
+ * IPOnlyCIDRItem list
+ *
+ * \param gh Pointer to the IPOnlyCIDRItem list
+ * \param str Pointer to the character string containing the address group
+ * that has to be parsed.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int IPOnlyCIDRListParse(const DetectEngineCtx *de_ctx,
+ IPOnlyCIDRItem **gh, char *str)
+{
+ SCLogDebug("gh %p, str %s", gh, str);
+
+ if (gh == NULL)
+ goto error;
+
+ *gh = IPOnlyCIDRListParse2(de_ctx, str, 0);
+ if (*gh == NULL) {
+ SCLogDebug("DetectAddressParse2 returned null");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Parses an address group sent as a character string and updates the
+ * IPOnlyCIDRItem lists src and dst of the Signature *s
+ *
+ * \param s Pointer to the signature structure
+ * \param addrstr Pointer to the character string containing the address group
+ * that has to be parsed.
+ * \param flag to indicate if we are parsing the src string or the dst string
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int IPOnlySigParseAddress(const DetectEngineCtx *de_ctx,
+ Signature *s, const char *addrstr, char flag)
+{
+ SCLogDebug("Address Group \"%s\" to be parsed now", addrstr);
+ IPOnlyCIDRItem *tmp = NULL;
+
+ /* pass on to the address(list) parser */
+ if (flag == 0) {
+ if (strcasecmp(addrstr, "any") == 0) {
+ s->flags |= SIG_FLAG_SRC_ANY;
+
+ if (IPOnlyCIDRListParse(de_ctx, &s->CidrSrc, (char *)"0.0.0.0/0") < 0)
+ goto error;
+
+ if (IPOnlyCIDRListParse(de_ctx, &tmp, (char *)"::/0") < 0)
+ goto error;
+
+ s->CidrSrc = IPOnlyCIDRItemInsert(s->CidrSrc, tmp);
+
+ } else if (IPOnlyCIDRListParse(de_ctx, &s->CidrSrc, (char *)addrstr) < 0) {
+ goto error;
+ }
+
+ /* IPOnlyCIDRListPrint(s->CidrSrc); */
+ } else {
+ if (strcasecmp(addrstr, "any") == 0) {
+ s->flags |= SIG_FLAG_DST_ANY;
+
+ if (IPOnlyCIDRListParse(de_ctx, &tmp, (char *)"0.0.0.0/0") < 0)
+ goto error;
+
+ if (IPOnlyCIDRListParse(de_ctx, &s->CidrDst, (char *)"::/0") < 0)
+ goto error;
+
+ s->CidrDst = IPOnlyCIDRItemInsert(s->CidrDst, tmp);
+
+ } else if (IPOnlyCIDRListParse(de_ctx, &s->CidrDst, (char *)addrstr) < 0) {
+ goto error;
+ }
+
+ /* IPOnlyCIDRListPrint(s->CidrDst); */
+ }
+
+ return 0;
+
+error:
+ SCLogError(SC_ERR_ADDRESS_ENGINE_GENERIC, "failed to parse addresses");
+ return -1;
+}
+
+/**
+ * \brief Setup the IP Only detection engine context
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only detection engine
+ */
+void IPOnlyInit(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx)
+{
+ io_ctx->sig_init_size = DetectEngineGetMaxSigId(de_ctx) / 8 + 1;
+
+ if ( (io_ctx->sig_init_array = SCMalloc(io_ctx->sig_init_size)) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in IPOnlyInit. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(io_ctx->sig_init_array, 0, io_ctx->sig_init_size);
+
+ io_ctx->tree_ipv4src = SCRadixCreateRadixTree(SigNumArrayFree,
+ SigNumArrayPrint);
+ io_ctx->tree_ipv4dst = SCRadixCreateRadixTree(SigNumArrayFree,
+ SigNumArrayPrint);
+ io_ctx->tree_ipv6src = SCRadixCreateRadixTree(SigNumArrayFree,
+ SigNumArrayPrint);
+ io_ctx->tree_ipv6dst = SCRadixCreateRadixTree(SigNumArrayFree,
+ SigNumArrayPrint);
+}
+
+/**
+ * \brief Setup the IP Only thread detection engine context
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only thread detection engine
+ */
+void DetectEngineIPOnlyThreadInit(DetectEngineCtx *de_ctx,
+ DetectEngineIPOnlyThreadCtx *io_tctx)
+{
+ /* initialize the signature bitarray */
+ io_tctx->sig_match_size = de_ctx->io_ctx.max_idx / 8 + 1;
+ io_tctx->sig_match_array = SCMalloc(io_tctx->sig_match_size);
+ if (io_tctx->sig_match_array == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memset(io_tctx->sig_match_array, 0, io_tctx->sig_match_size);
+}
+
+/**
+ * \brief Print stats of the IP Only engine
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only detection engine
+ */
+void IPOnlyPrint(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx)
+{
+ /* XXX: how are we going to print the stats now? */
+}
+
+/**
+ * \brief Deinitialize the IP Only detection engine context
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only detection engine
+ */
+void IPOnlyDeinit(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx)
+{
+
+ if (io_ctx == NULL)
+ return;
+
+ if (io_ctx->tree_ipv4src != NULL)
+ SCRadixReleaseRadixTree(io_ctx->tree_ipv4src);
+ io_ctx->tree_ipv4src = NULL;
+
+ if (io_ctx->tree_ipv4dst != NULL)
+ SCRadixReleaseRadixTree(io_ctx->tree_ipv4dst);
+ io_ctx->tree_ipv4dst = NULL;
+
+ if (io_ctx->tree_ipv6src != NULL)
+ SCRadixReleaseRadixTree(io_ctx->tree_ipv6src);
+ io_ctx->tree_ipv6src = NULL;
+
+ if (io_ctx->tree_ipv6dst != NULL)
+ SCRadixReleaseRadixTree(io_ctx->tree_ipv6dst);
+ io_ctx->tree_ipv6dst = NULL;
+
+ if (io_ctx->sig_init_array)
+ SCFree(io_ctx->sig_init_array);
+ io_ctx->sig_init_array = NULL;
+}
+
+/**
+ * \brief Deinitialize the IP Only thread detection engine context
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only detection engine
+ */
+void DetectEngineIPOnlyThreadDeinit(DetectEngineIPOnlyThreadCtx *io_tctx)
+{
+ SCFree(io_tctx->sig_match_array);
+}
+
+static inline
+int IPOnlyMatchCompatSMs(ThreadVars *tv,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Packet *p)
+{
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH);
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+
+ while (sm != NULL) {
+ BUG_ON(!(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT));
+ KEYWORD_PROFILING_START;
+ if (sigmatch_table[sm->type].Match(tv, det_ctx, p, s, sm->ctx) > 0) {
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+ sm = sm->next;
+ continue;
+ }
+ KEYWORD_PROFILING_END(det_ctx, sm->type, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Match a packet against the IP Only detection engine contexts
+ *
+ * \param de_ctx Pointer to the current detection engine
+ * \param io_ctx Pointer to the current ip only detection engine
+ * \param io_ctx Pointer to the current ip only thread detection engine
+ * \param p Pointer to the Packet to match against
+ */
+void IPOnlyMatchPacket(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ DetectEngineIPOnlyCtx *io_ctx,
+ DetectEngineIPOnlyThreadCtx *io_tctx, Packet *p)
+{
+ SigNumArray *src = NULL;
+ SigNumArray *dst = NULL;
+ void *user_data_src = NULL, *user_data_dst = NULL;
+
+ if (p->src.family == AF_INET) {
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&GET_IPV4_SRC_ADDR_U32(p),
+ io_ctx->tree_ipv4src, &user_data_src);
+ } else if (p->src.family == AF_INET6) {
+ (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&GET_IPV6_SRC_ADDR(p),
+ io_ctx->tree_ipv6src, &user_data_src);
+ }
+
+ if (p->dst.family == AF_INET) {
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&GET_IPV4_DST_ADDR_U32(p),
+ io_ctx->tree_ipv4dst, &user_data_dst);
+ } else if (p->dst.family == AF_INET6) {
+ (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&GET_IPV6_DST_ADDR(p),
+ io_ctx->tree_ipv6dst, &user_data_dst);
+ }
+
+ src = user_data_src;
+ dst = user_data_dst;
+
+ if (src == NULL || dst == NULL)
+ return;
+
+ uint32_t u;
+ for (u = 0; u < src->size; u++) {
+ SCLogDebug("And %"PRIu8" & %"PRIu8, src->array[u], dst->array[u]);
+
+ /* The final results will be at io_tctx */
+ io_tctx->sig_match_array[u] = dst->array[u] & src->array[u];
+
+ /* We have to move the logic of the signature checking
+ * to the main detect loop, in order to apply the
+ * priority of actions (pass, drop, reject, alert) */
+ if (io_tctx->sig_match_array[u] != 0) {
+ /* We have a match :) Let's see from which signum's */
+ uint8_t bitarray = io_tctx->sig_match_array[u];
+ uint8_t i = 0;
+
+ for (; i < 8; i++, bitarray = bitarray >> 1) {
+ if (bitarray & 0x01) {
+ Signature *s = de_ctx->sig_array[u * 8 + i];
+
+ if ((s->proto.flags & DETECT_PROTO_IPV4) && !PKT_IS_IPV4(p)) {
+ SCLogDebug("ip version didn't match");
+ continue;
+ }
+ if ((s->proto.flags & DETECT_PROTO_IPV6) && !PKT_IS_IPV6(p)) {
+ SCLogDebug("ip version didn't match");
+ continue;
+ }
+
+ if (DetectProtoContainsProto(&s->proto, IP_GET_IPPROTO(p)) == 0) {
+ SCLogDebug("proto didn't match");
+ continue;
+ }
+
+ /* check the source & dst port in the sig */
+ if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
+ if (!(s->flags & SIG_FLAG_DP_ANY)) {
+ if (p->flags & PKT_IS_FRAGMENT)
+ continue;
+
+ DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp);
+ if (dport == NULL) {
+ SCLogDebug("dport didn't match.");
+ continue;
+ }
+ }
+ if (!(s->flags & SIG_FLAG_SP_ANY)) {
+ if (p->flags & PKT_IS_FRAGMENT)
+ continue;
+
+ DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp);
+ if (sport == NULL) {
+ SCLogDebug("sport didn't match.");
+ continue;
+ }
+ }
+ } else if ((s->flags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
+ SCLogDebug("port-less protocol and sig needs ports");
+ continue;
+ }
+
+ if (!IPOnlyMatchCompatSMs(tv, det_ctx, s, p)) {
+ continue;
+ }
+
+ SCLogDebug("Signum %"PRIu16" match (sid: %"PRIu16", msg: %s)",
+ u * 8 + i, s->id, s->msg);
+
+ if (s->sm_arrays[DETECT_SM_LIST_POSTMATCH] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
+ SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
+
+ SCLogDebug("running match functions, sm %p", smd);
+
+ if (smd != NULL) {
+ while (1) {
+ KEYWORD_PROFILING_START;
+ (void)sigmatch_table[smd->type].Match(tv, det_ctx, p, s, smd->ctx);
+ KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
+ if (smd->is_last)
+ break;
+ smd++;
+ }
+ }
+ }
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ if (s->action & ACTION_DROP)
+ PacketAlertAppend(det_ctx, s, p, 0, PACKET_ALERT_FLAG_DROP_FLOW);
+ else
+ PacketAlertAppend(det_ctx, s, p, 0, 0);
+ } else {
+ /* apply actions for noalert/rule suppressed as well */
+ DetectSignatureApplyActions(p, s);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * \brief Build the radix trees from the lists of parsed adresses in CIDR format
+ * the result should be 4 radix trees: src/dst ipv4 and src/dst ipv6
+ * holding SigNumArrays, each of them with a hierarchical relation
+ * of subnets and hosts
+ *
+ * \param de_ctx Pointer to the current detection engine
+ */
+void IPOnlyPrepare(DetectEngineCtx *de_ctx)
+{
+ SCLogDebug("Preparing Final Lists");
+
+ /*
+ IPOnlyCIDRListPrint((de_ctx->io_ctx).ip_src);
+ IPOnlyCIDRListPrint((de_ctx->io_ctx).ip_dst);
+ */
+
+ IPOnlyCIDRItem *src, *dst;
+ SCRadixNode *node = NULL;
+
+ /* Prepare Src radix trees */
+ for (src = (de_ctx->io_ctx).ip_src; src != NULL; ) {
+ if (src->family == AF_INET) {
+ /*
+ SCLogDebug("To IPv4");
+ SCLogDebug("Item has netmask %"PRIu16" negated: %s; IP: %s; "
+ "signum: %"PRIu16, src->netmask,
+ (src->negated) ? "yes":"no",
+ inet_ntoa( *(struct in_addr*)&src->ip[0]),
+ src->signum);
+ */
+
+ void *user_data = NULL;
+ if (src->netmask == 32)
+ (void)SCRadixFindKeyIPV4ExactMatch((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src,
+ &user_data);
+ else
+ (void)SCRadixFindKeyIPV4Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src,
+ src->netmask, &user_data);
+ if (user_data == NULL) {
+ SCLogDebug("Exact match not found");
+
+ /** Not found, look if there's a subnet of this range with
+ * bigger netmask */
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src,
+ &user_data);
+ if (user_data == NULL) {
+ SCLogDebug("best match not found");
+
+ /* Not found, insert a new one */
+ SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+
+ if (src->netmask == 32)
+ node = SCRadixAddKeyIPV4((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src, sna);
+ else
+ node = SCRadixAddKeyIPV4Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src,
+ sna, src->netmask);
+
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the "
+ "src ipv4 radix tree");
+ } else {
+ SCLogDebug("Best match found");
+
+ /* Found, copy the sig num table, add this signum and insert */
+ SigNumArray *sna = NULL;
+ sna = SigNumArrayCopy((SigNumArray *) user_data);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+
+ if (src->netmask == 32)
+ node = SCRadixAddKeyIPV4((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src, sna);
+ else
+ node = SCRadixAddKeyIPV4Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv4src, sna,
+ src->netmask);
+
+ if (node == NULL) {
+ char tmpstr[64];
+ PrintInet(src->family, &src->ip[0], tmpstr, sizeof(tmpstr));
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the"
+ " src ipv4 radix tree ip %s netmask %"PRIu8, tmpstr, src->netmask);
+ //SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4src);
+ exit(-1);
+ }
+ }
+ } else {
+ SCLogDebug("Exact match found");
+
+ /* it's already inserted. Update it */
+ SigNumArray *sna = (SigNumArray *)user_data;
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+ }
+ } else if (src->family == AF_INET6) {
+ SCLogDebug("To IPv6");
+
+ void *user_data = NULL;
+ if (src->netmask == 128)
+ (void)SCRadixFindKeyIPV6ExactMatch((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src,
+ &user_data);
+ else
+ (void)SCRadixFindKeyIPV6Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src,
+ src->netmask, &user_data);
+
+ if (user_data == NULL) {
+ /* Not found, look if there's a subnet of this range with bigger netmask */
+ (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src,
+ &user_data);
+
+ if (user_data == NULL) {
+ /* Not found, insert a new one */
+ SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+
+ if (src->netmask == 128)
+ node = SCRadixAddKeyIPV6((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src, sna);
+ else
+ node = SCRadixAddKeyIPV6Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src,
+ sna, src->netmask);
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the src "
+ "ipv6 radix tree");
+ } else {
+ /* Found, copy the sig num table, add this signum and insert */
+ SigNumArray *sna = NULL;
+ sna = SigNumArrayCopy((SigNumArray *)user_data);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+
+ if (src->netmask == 128)
+ node = SCRadixAddKeyIPV6((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src, sna);
+ else
+ node = SCRadixAddKeyIPV6Netblock((uint8_t *)&src->ip[0],
+ (de_ctx->io_ctx).tree_ipv6src,
+ sna, src->netmask);
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the src "
+ "ipv6 radix tree");
+ }
+ } else {
+ /* it's already inserted. Update it */
+ SigNumArray *sna = (SigNumArray *)user_data;
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (src->signum % 8);
+ if (src->negated > 0)
+ /* Unset it */
+ sna->array[src->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[src->signum / 8] |= tmp;
+ }
+ }
+ IPOnlyCIDRItem *tmpaux = src;
+ src = src->next;
+ SCFree(tmpaux);
+ }
+
+ SCLogDebug("dsts:");
+
+ /* Prepare Dst radix trees */
+ for (dst = (de_ctx->io_ctx).ip_dst; dst != NULL; ) {
+ if (dst->family == AF_INET) {
+
+ SCLogDebug("To IPv4");
+ SCLogDebug("Item has netmask %"PRIu16" negated: %s; IP: %s; signum:"
+ " %"PRIu16"", dst->netmask, (dst->negated)?"yes":"no",
+ inet_ntoa(*(struct in_addr*)&dst->ip[0]), dst->signum);
+
+ void *user_data = NULL;
+ if (dst->netmask == 32)
+ (void) SCRadixFindKeyIPV4ExactMatch((uint8_t *) &dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst,
+ &user_data);
+ else
+ (void) SCRadixFindKeyIPV4Netblock((uint8_t *) &dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst,
+ dst->netmask,
+ &user_data);
+
+ if (user_data == NULL) {
+ SCLogDebug("Exact match not found");
+
+ /**
+ * Not found, look if there's a subnet of this range
+ * with bigger netmask
+ */
+ (void) SCRadixFindKeyIPV4BestMatch((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst,
+ &user_data);
+ if (user_data == NULL) {
+ SCLogDebug("Best match not found");
+
+ /** Not found, insert a new one */
+ SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx);
+
+ /** Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /** Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /** Set it */
+ sna->array[dst->signum / 8] |= tmp;
+
+ if (dst->netmask == 32)
+ node = SCRadixAddKeyIPV4((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst, sna);
+ else
+ node = SCRadixAddKeyIPV4Netblock((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst,
+ sna, dst->netmask);
+
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the dst "
+ "ipv4 radix tree");
+ } else {
+ SCLogDebug("Best match found");
+
+ /* Found, copy the sig num table, add this signum and insert */
+ SigNumArray *sna = NULL;
+ sna = SigNumArrayCopy((SigNumArray *) user_data);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /* Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[dst->signum / 8] |= tmp;
+
+ if (dst->netmask == 32)
+ node = SCRadixAddKeyIPV4((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst, sna);
+ else
+ node = SCRadixAddKeyIPV4Netblock((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv4dst,
+ sna, dst->netmask);
+
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the dst "
+ "ipv4 radix tree");
+ }
+ } else {
+ SCLogDebug("Exact match found");
+
+ /* it's already inserted. Update it */
+ SigNumArray *sna = (SigNumArray *)user_data;
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /* Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[dst->signum / 8] |= tmp;
+ }
+ } else if (dst->family == AF_INET6) {
+ SCLogDebug("To IPv6");
+
+ void *user_data = NULL;
+ if (dst->netmask == 128)
+ (void) SCRadixFindKeyIPV6ExactMatch((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst,
+ &user_data);
+ else
+ (void) SCRadixFindKeyIPV6Netblock((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst,
+ dst->netmask, &user_data);
+
+ if (user_data == NULL) {
+ /** Not found, look if there's a subnet of this range with
+ * bigger netmask
+ */
+ (void) SCRadixFindKeyIPV6BestMatch((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst,
+ &user_data);
+
+ if (user_data == NULL) {
+ /* Not found, insert a new one */
+ SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /* Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[dst->signum / 8] |= tmp;
+
+ if (dst->netmask == 128)
+ node = SCRadixAddKeyIPV6((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst, sna);
+ else
+ node = SCRadixAddKeyIPV6Netblock((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst,
+ sna, dst->netmask);
+
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the dst "
+ "ipv6 radix tree");
+ } else {
+ /* Found, copy the sig num table, add this signum and insert */
+ SigNumArray *sna = NULL;
+ sna = SigNumArrayCopy((SigNumArray *)user_data);
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /* Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[dst->signum / 8] |= tmp;
+
+ if (dst->netmask == 128)
+ node = SCRadixAddKeyIPV6((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst, sna);
+ else
+ node = SCRadixAddKeyIPV6Netblock((uint8_t *)&dst->ip[0],
+ (de_ctx->io_ctx).tree_ipv6dst,
+ sna, dst->netmask);
+
+ if (node == NULL)
+ SCLogError(SC_ERR_IPONLY_RADIX, "Error inserting in the dst "
+ "ipv6 radix tree");
+ }
+ } else {
+ /* it's already inserted. Update it */
+ SigNumArray *sna = (SigNumArray *)user_data;
+
+ /* Update the sig */
+ uint8_t tmp = 1 << (dst->signum % 8);
+ if (dst->negated > 0)
+ /* Unset it */
+ sna->array[dst->signum / 8] &= ~tmp;
+ else
+ /* Set it */
+ sna->array[dst->signum / 8] |= tmp;
+ }
+ }
+ IPOnlyCIDRItem *tmpaux = dst;
+ dst = dst->next;
+ SCFree(tmpaux);
+ }
+
+ /* print all the trees: for debuggin it might print too much info
+ SCLogDebug("Radix tree src ipv4:");
+ SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4src);
+ SCLogDebug("Radix tree src ipv6:");
+ SCRadixPrintTree((de_ctx->io_ctx).tree_ipv6src);
+ SCLogDebug("__________________");
+
+ SCLogDebug("Radix tree dst ipv4:");
+ SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4dst);
+ SCLogDebug("Radix tree dst ipv6:");
+ SCRadixPrintTree((de_ctx->io_ctx).tree_ipv6dst);
+ SCLogDebug("__________________");
+ */
+}
+
+/**
+ * \brief Add a signature to the lists of Adrresses in CIDR format (sorted)
+ * this step is necesary to build the radix tree with a hierarchical
+ * relation between nodes
+ * \param de_ctx Pointer to the current detection engine context
+ * \param de_ctx Pointer to the current ip only detection engine contest
+ * \param s Pointer to the current signature
+ */
+void IPOnlyAddSignature(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx,
+ Signature *s)
+{
+ if (!(s->flags & SIG_FLAG_IPONLY))
+ return;
+
+ /* Set the internal signum to the list before merging */
+ IPOnlyCIDRListSetSigNum(s->CidrSrc, s->num);
+
+ IPOnlyCIDRListSetSigNum(s->CidrDst, s->num);
+
+ /**
+ * ipv4 and ipv6 are mixed, but later we will separate them into
+ * different trees
+ */
+ io_ctx->ip_src = IPOnlyCIDRItemInsert(io_ctx->ip_src, s->CidrSrc);
+ io_ctx->ip_dst = IPOnlyCIDRItemInsert(io_ctx->ip_dst, s->CidrDst);
+
+ if (s->num > io_ctx->max_idx)
+ io_ctx->max_idx = s->num;
+
+ /* enable the sig in the bitarray */
+ io_ctx->sig_init_array[(s->num/8)] |= 1 << (s->num % 8);
+
+ /** no longer ref to this, it's in the table now */
+ s->CidrSrc = NULL;
+ s->CidrDst = NULL;
+}
+
+#ifdef UNITTESTS
+/**
+ * \test check that we set a Signature as IPOnly because it has no rule
+ * option appending a SigMatch and no port is fixed
+ */
+
+static int IPOnlyTestSig01(void)
+{
+ int result = 0;
+ DetectEngineCtx de_ctx;
+
+ memset(&de_ctx, 0, sizeof(DetectEngineCtx));
+
+ de_ctx.flags |= DE_QUIET;
+
+ Signature *s = SigInit(&de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-01 sig is IPOnly \"; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(&de_ctx, s))
+ result = 1;
+ else
+ printf("expected a IPOnly signature: ");
+
+ SigFree(s);
+end:
+ return result;
+}
+
+/**
+ * \test check that we dont set a Signature as IPOnly because it has no rule
+ * option appending a SigMatch but a port is fixed
+ */
+
+static int IPOnlyTestSig02 (void)
+{
+ int result = 0;
+ DetectEngineCtx de_ctx;
+ memset (&de_ctx, 0, sizeof(DetectEngineCtx));
+
+ memset(&de_ctx, 0, sizeof(DetectEngineCtx));
+
+ de_ctx.flags |= DE_QUIET;
+
+ Signature *s = SigInit(&de_ctx,"alert tcp any any -> any 80 (msg:\"SigTest40-02 sig is not IPOnly \"; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if ((SignatureIsIPOnly(&de_ctx, s)))
+ result = 1;
+ else
+ printf("got a non-IPOnly signature: ");
+
+ SigFree(s);
+
+end:
+ return result;
+}
+
+/**
+ * \test check that we set dont set a Signature as IPOnly
+ * because it has rule options appending a SigMatch like content, and pcre
+ */
+
+static int IPOnlyTestSig03 (void)
+{
+ int result = 1;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ /* combination of pcre and content */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pcre and content) \"; content:\"php\"; pcre:\"/require(_once)?/i\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (content): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* content */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (content) \"; content:\"match something\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (content): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* uricontent */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (uricontent) \"; uricontent:\"match something\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (uricontent): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* pcre */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pcre) \"; pcre:\"/e?idps rule[sz]/i\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (pcre): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* flow */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flow) \"; flow:to_server; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (flow): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* dsize */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (dsize) \"; dsize:100; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (dsize): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* flowbits */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flowbits) \"; flowbits:unset; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (flowbits): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* flowvar */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flowvar) \"; pcre:\"/(?<flow_var>.*)/i\"; flowvar:var,\"str\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (flowvar): ");
+ result=0;
+ }
+ SigFree(s);
+
+ /* pktvar */
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pktvar) \"; pcre:\"/(?<pkt_var>.*)/i\"; pktvar:var,\"str\"; classtype:misc-activity; sid:400001; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if(SignatureIsIPOnly(de_ctx, s))
+ {
+ printf("got a IPOnly signature (pktvar): ");
+ result=0;
+ }
+ SigFree(s);
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test
+ */
+static int IPOnlyTestSig04 (void)
+{
+ int result = 1;
+
+ IPOnlyCIDRItem *head = NULL;
+ IPOnlyCIDRItem *new;
+
+ new = IPOnlyCIDRItemNew();
+ new->netmask= 10;
+
+ head = IPOnlyCIDRItemInsert(head, new);
+
+ new = IPOnlyCIDRItemNew();
+ new->netmask= 11;
+
+ head = IPOnlyCIDRItemInsert(head, new);
+
+ new = IPOnlyCIDRItemNew();
+ new->netmask= 9;
+
+ head = IPOnlyCIDRItemInsert(head, new);
+
+ new = IPOnlyCIDRItemNew();
+ new->netmask= 10;
+
+ head = IPOnlyCIDRItemInsert(head, new);
+
+ new = IPOnlyCIDRItemNew();
+ new->netmask= 10;
+
+ head = IPOnlyCIDRItemInsert(head, new);
+
+ IPOnlyCIDRListPrint(head);
+ new = head;
+ if (new->netmask != 9) {
+ result = 0;
+ goto end;
+ }
+ new = new->next;
+ if (new->netmask != 10) {
+ result = 0;
+ goto end;
+ }
+ new = new->next;
+ if (new->netmask != 10) {
+ result = 0;
+ goto end;
+ }
+ new = new->next;
+ if (new->netmask != 10) {
+ result = 0;
+ goto end;
+ }
+ new = new->next;
+ if (new->netmask != 11) {
+ result = 0;
+ goto end;
+ }
+
+end:
+ IPOnlyCIDRListFree(head);
+ return result;
+}
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (all should match)
+ */
+int IPOnlyTestSig05(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (none should match)
+ */
+int IPOnlyTestSig06(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "80.58.0.33", "195.235.113.3");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/* \todo fix it. We have disabled this unittest because 599 exposes 608,
+ * which is why these unittests fail. When we fix 608, we need to renable
+ * these sigs */
+#if 0
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (all should match)
+ */
+int IPOnlyTestSig07(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 192.168.1.5 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp [192.168.1.2,192.168.1.5,192.168.1.4] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp [192.168.1.0/24,!192.168.1.1] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp [192.0.0.0/8,!192.168.0.0/16,192.168.1.0/24,!192.168.1.1] any -> [192.168.1.0/24,!192.168.1.5] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> [192.168.0.0/16,!192.168.1.0/24,192.168.1.1] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp [78.129.202.0/24,192.168.1.5,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> 192.168.1.1 any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+#endif
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (none should match)
+ */
+int IPOnlyTestSig08(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"192.168.1.1","192.168.1.5");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 192.168.1.5 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp [192.168.1.2,192.168.1.5,192.168.1.4] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp [192.168.1.0/24,!192.168.1.1] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp [192.0.0.0/8,!192.168.0.0/16,192.168.1.0/24,!192.168.1.1] any -> [192.168.1.0/24,!192.168.1.5] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> !192.168.1.5 any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> [192.168.0.0/16,!192.168.1.0/24,192.168.1.1] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp [78.129.202.0/24,192.168.1.5,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> 192.168.1.1 any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (all should match)
+ */
+int IPOnlyTestSig09(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:0/96 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (none should match)
+ */
+int IPOnlyTestSig10(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> !3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562/96 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp !3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> !3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> 3FFE:FFFF:7654:FEDB:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/* \todo fix it. We have disabled this unittest because 599 exposes 608,
+ * which is why these unittests fail. When we fix 608, we need to renable
+ * these sigs */
+#if 0
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (all should match) with ipv4 and ipv6 mixed
+ */
+int IPOnlyTestSig11(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 2;
+ uint8_t numsigs = 7;
+
+ Packet *p[2];
+
+ p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562");
+ p[1] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"192.168.1.1","192.168.1.5");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.5 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp [192.168.1.1,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.4,192.168.1.5,!192.168.1.0/24] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.0/24] any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp [3FFE:FFFF:0:0:0:0:0:0/32,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.0/24,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp [78.129.202.0/24,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.0.0.0/8] any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[2][7] = {{ 1, 1, 1, 1, 1, 1, 1}, { 1, 1, 1, 1, 1, 1, 1}};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+#endif
+
+/**
+ * \test Test a set of ip only signatures making use a lot of
+ * addresses for src and dst (none should match) with ipv4 and ipv6 mixed
+ */
+int IPOnlyTestSig12(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 2;
+ uint8_t numsigs = 7;
+
+ Packet *p[2];
+
+ p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"3FBE:FFFF:7654:FEDA:1245:BA98:3210:4562","3FBE:FFFF:7654:FEDA:1245:BA98:3210:4565");
+ p[1] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"195.85.1.1","80.198.1.5");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.5 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp [192.168.1.1,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.4,192.168.1.5,!192.168.1.0/24] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.0/24] any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)";
+ sigs[2]= "alert tcp [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)";
+ sigs[3]= "alert tcp [3FFE:FFFF:0:0:0:0:0:0/32,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.0/24,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> [!3FBE:FFFF:7654:FEDA:1245:BA98:3210:4565,!80.198.1.5] any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)";
+ sigs[5]= "alert tcp any any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)";
+ sigs[6]= "alert tcp [78.129.202.0/24,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.0.0.0/8] any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[2][7] = {{ 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+static int IPOnlyTestSig13(void)
+{
+ int result = 0;
+ DetectEngineCtx de_ctx;
+
+ memset(&de_ctx, 0, sizeof(DetectEngineCtx));
+
+ de_ctx.flags |= DE_QUIET;
+
+ Signature *s = SigInit(&de_ctx,
+ "alert tcp any any -> any any (msg:\"Test flowbits ip only\"; "
+ "flowbits:set,myflow1; sid:1; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if (SignatureIsIPOnly(&de_ctx, s))
+ result = 1;
+ else
+ printf("expected a IPOnly signature: ");
+
+ SigFree(s);
+end:
+ return result;
+}
+
+static int IPOnlyTestSig14(void)
+{
+ int result = 0;
+ DetectEngineCtx de_ctx;
+
+ memset(&de_ctx, 0, sizeof(DetectEngineCtx));
+
+ de_ctx.flags |= DE_QUIET;
+
+ Signature *s = SigInit(&de_ctx,
+ "alert tcp any any -> any any (msg:\"Test flowbits ip only\"; "
+ "flowbits:set,myflow1; flowbits:isset,myflow2; sid:1; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ if (SignatureIsIPOnly(&de_ctx, s))
+ printf("expected a IPOnly signature: ");
+ else
+ result = 1;
+
+ SigFree(s);
+end:
+ return result;
+}
+
+int IPOnlyTestSig15(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 7;
+
+ Packet *p[1];
+ Flow f;
+ GenericVar flowvar;
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+ FLOW_INITIALIZE(&f);
+
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ p[0]->flow = &f;
+ p[0]->flow->flowvar = &flowvar;
+ p[0]->flags |= PKT_HAS_FLOW;
+ p[0]->flowflags |= FLOW_PKT_TOSERVER;
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; "
+ "flowbits:set,one; sid:1;)";
+ sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; "
+ "flowbits:set,two; sid:2;)";
+ sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; "
+ "flowbits:set,three; sid:3;)";
+ sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; "
+ "flowbits:set,four; sid:4;)";
+ sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; "
+ "flowbits:set,five; sid:5;)";
+ sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; "
+ "flowbits:set,six; sid:6;)";
+ sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; "
+ "flowbits:set,seven; content:\"Hi all\"; sid:7;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7};
+ uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \brief Unittest to show #599. We fail to match if we have negated addresses.
+ */
+int IPOnlyTestSig16(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 2;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "100.100.0.0", "50.0.0.0");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert tcp !100.100.0.1 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> !50.0.0.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+
+ /* Sid numbers (we could extract them from the sig) */
+ uint32_t sid[2] = { 1, 2};
+ uint32_t results[2] = { 1, 1};
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+/**
+ * \brief Unittest to show #611. Ports on portless protocols.
+ */
+int IPOnlyTestSig17(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ uint8_t numpkts = 1;
+ uint8_t numsigs = 2;
+
+ Packet *p[1];
+
+ p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_ICMP, "100.100.0.0", "50.0.0.0");
+
+ char *sigs[numsigs];
+ sigs[0]= "alert ip 100.100.0.0 80 -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)";
+ sigs[1]= "alert ip any any -> 50.0.0.0 123 (msg:\"Testing dst ip (sid 2)\"; sid:2;)";
+
+ uint32_t sid[2] = { 1, 2};
+ uint32_t results[2] = { 0, 0}; /* neither should match */
+
+ result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs);
+
+ UTHFreePackets(p, numpkts);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void IPOnlyRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IPOnlyTestSig01", IPOnlyTestSig01, 1);
+ UtRegisterTest("IPOnlyTestSig02", IPOnlyTestSig02, 1);
+ UtRegisterTest("IPOnlyTestSig03", IPOnlyTestSig03, 1);
+ UtRegisterTest("IPOnlyTestSig04", IPOnlyTestSig04, 1);
+
+ UtRegisterTest("IPOnlyTestSig05", IPOnlyTestSig05, 1);
+ UtRegisterTest("IPOnlyTestSig06", IPOnlyTestSig06, 1);
+/* \todo fix it. We have disabled this unittest because 599 exposes 608,
+ * which is why these unittests fail. When we fix 608, we need to renable
+ * these sigs */
+#if 0
+ UtRegisterTest("IPOnlyTestSig07", IPOnlyTestSig07, 1);
+#endif
+ UtRegisterTest("IPOnlyTestSig08", IPOnlyTestSig08, 1);
+
+ UtRegisterTest("IPOnlyTestSig09", IPOnlyTestSig09, 1);
+ UtRegisterTest("IPOnlyTestSig10", IPOnlyTestSig10, 1);
+/* \todo fix it. We have disabled this unittest because 599 exposes 608,
+ * which is why these unittests fail. When we fix 608, we need to renable
+ * these sigs */
+#if 0
+ UtRegisterTest("IPOnlyTestSig11", IPOnlyTestSig11, 1);
+#endif
+ UtRegisterTest("IPOnlyTestSig12", IPOnlyTestSig12, 1);
+ UtRegisterTest("IPOnlyTestSig13", IPOnlyTestSig13, 1);
+ UtRegisterTest("IPOnlyTestSig14", IPOnlyTestSig14, 1);
+ UtRegisterTest("IPOnlyTestSig15", IPOnlyTestSig15, 1);
+ UtRegisterTest("IPOnlyTestSig16", IPOnlyTestSig16, 1);
+
+ UtRegisterTest("IPOnlyTestSig17", IPOnlyTestSig17, 1);
+#endif
+
+ return;
+}
+
diff --git a/framework/src/suricata/src/detect-engine-iponly.h b/framework/src/suricata/src/detect-engine-iponly.h
new file mode 100644
index 00000000..b71a5933
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-iponly.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_IPONLY_H__
+#define __DETECT_ENGINE_IPONLY_H__
+
+/**
+ * SigNumArray is a bit array representing signatures
+ * it can be used linked to src/dst address to indicate
+ * which signatures apply to this addres
+ * at IP Only we store SigNumArrays at the radix trees
+ */
+typedef struct SigNumArray_ {
+ uint8_t *array; /* bit array of sig nums */
+ uint32_t size; /* size in bytes of the array */
+} SigNumArray;
+
+void IPOnlyCIDRListFree(IPOnlyCIDRItem *tmphead);
+int IPOnlySigParseAddress(const DetectEngineCtx *, Signature *, const char *, char);
+void IPOnlyMatchPacket(ThreadVars *tv, DetectEngineCtx *,
+ DetectEngineThreadCtx *, DetectEngineIPOnlyCtx *,
+ DetectEngineIPOnlyThreadCtx *, Packet *);
+void IPOnlyInit(DetectEngineCtx *, DetectEngineIPOnlyCtx *);
+void IPOnlyPrint(DetectEngineCtx *, DetectEngineIPOnlyCtx *);
+void IPOnlyDeinit(DetectEngineCtx *, DetectEngineIPOnlyCtx *);
+void IPOnlyPrepare(DetectEngineCtx *);
+void DetectEngineIPOnlyThreadInit(DetectEngineCtx *, DetectEngineIPOnlyThreadCtx *);
+void DetectEngineIPOnlyThreadDeinit(DetectEngineIPOnlyThreadCtx *);
+void IPOnlyAddSignature(DetectEngineCtx *, DetectEngineIPOnlyCtx *, Signature *);
+void IPOnlyRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_IPONLY_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-loader.c b/framework/src/suricata/src/detect-engine-loader.c
new file mode 100644
index 00000000..b360cd04
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-loader.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "conf.h"
+#include "debug.h"
+#include "detect.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "queue.h"
+#include "util-signal.h"
+
+#include "detect-engine-loader.h"
+
+#define NLOADERS 4
+static DetectLoaderControl *loaders = NULL;
+static int cur_loader = 0;
+void TmThreadWakeupDetectLoaderThreads(void);
+static int num_loaders = NLOADERS;
+
+/** \param loader -1 for auto select
+ * \retval loader_id or negative in case of error */
+int DetectLoaderQueueTask(int loader_id, LoaderFunc Func, void *func_ctx)
+{
+ if (loader_id == -1) {
+ loader_id = cur_loader;
+ cur_loader++;
+ if (cur_loader >= num_loaders)
+ cur_loader = 0;
+ }
+ if (loader_id >= num_loaders || loader_id < 0) {
+ return -ERANGE;
+ }
+
+ DetectLoaderControl *loader = &loaders[loader_id];
+
+ DetectLoaderTask *t = SCCalloc(1, sizeof(*t));
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->Func = Func;
+ t->ctx = func_ctx;
+
+ SCMutexLock(&loader->m);
+ TAILQ_INSERT_TAIL(&loader->task_list, t, next);
+ SCMutexUnlock(&loader->m);
+
+ TmThreadWakeupDetectLoaderThreads();
+
+ SCLogDebug("%d %p %p", loader_id, Func, func_ctx);
+ return loader_id;
+}
+
+/** \brief wait for loader tasks to complete
+ * \retval result 0 for ok, -1 for errors */
+int DetectLoadersSync(void)
+{
+ SCLogDebug("waiting");
+ int errors = 0;
+ int i;
+ for (i = 0; i < num_loaders; i++) {
+ int done = 0;
+ DetectLoaderControl *loader = &loaders[i];
+ while (!done) {
+ SCMutexLock(&loader->m);
+ if (TAILQ_EMPTY(&loader->task_list)) {
+ done = 1;
+ }
+ SCMutexUnlock(&loader->m);
+ }
+ SCMutexLock(&loader->m);
+ if (loader->result != 0) {
+ errors++;
+ loader->result = 0;
+ }
+ SCMutexUnlock(&loader->m);
+
+ }
+ if (errors) {
+ SCLogError(SC_ERR_INITIALIZATION, "%d loaders reported errors", errors);
+ return -1;
+ }
+ SCLogDebug("done");
+ return 0;
+}
+
+static void DetectLoaderInit(DetectLoaderControl *loader)
+{
+ memset(loader, 0x00, sizeof(*loader));
+ SCMutexInit(&loader->m, NULL);
+ TAILQ_INIT(&loader->task_list);
+}
+
+void DetectLoadersInit(void)
+{
+ intmax_t setting = NLOADERS;
+ (void)ConfGetInt("multi-detect.loaders", &setting);
+
+ if (setting < 1 || setting > 1024) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS,
+ "invalid multi-detect.loaders setting %"PRIdMAX, setting);
+ exit(EXIT_FAILURE);
+ }
+ num_loaders = (int32_t)setting;
+
+ SCLogInfo("using %d detect loader threads", num_loaders);
+
+ BUG_ON(loaders != NULL);
+ loaders = SCCalloc(num_loaders, sizeof(DetectLoaderControl));
+ BUG_ON(loaders == NULL);
+
+ int i;
+ for (i = 0; i < num_loaders; i++) {
+ DetectLoaderInit(&loaders[i]);
+ }
+}
+
+/**
+ * \brief Unpauses all threads present in tv_root
+ */
+void TmThreadWakeupDetectLoaderThreads()
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv != NULL) {
+ if (strcmp(tv->name,"DetectLoader") == 0) {
+ BUG_ON(tv->ctrl_cond == NULL);
+ pthread_cond_broadcast(tv->ctrl_cond);
+ }
+ tv = tv->next;
+ }
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Unpauses all threads present in tv_root
+ */
+void TmThreadContinueDetectLoaderThreads()
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv != NULL) {
+ if (strcmp(tv->name,"DetectLoader") == 0)
+ TmThreadContinue(tv);
+
+ tv = tv->next;
+ }
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+
+SC_ATOMIC_DECLARE(int, detect_loader_cnt);
+
+typedef struct DetectLoaderThreadData_ {
+ uint32_t instance;
+} DetectLoaderThreadData;
+
+static TmEcode DetectLoaderThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ DetectLoaderThreadData *ftd = SCCalloc(1, sizeof(DetectLoaderThreadData));
+ if (ftd == NULL)
+ return TM_ECODE_FAILED;
+
+ ftd->instance = SC_ATOMIC_ADD(detect_loader_cnt, 1) - 1; /* id's start at 0 */
+ SCLogDebug("detect loader instance %u", ftd->instance);
+
+ /* pass thread data back to caller */
+ *data = ftd;
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode DetectLoaderThreadDeinit(ThreadVars *t, void *data)
+{
+ SCFree(data);
+ return TM_ECODE_OK;
+}
+
+
+static TmEcode DetectLoader(ThreadVars *th_v, void *thread_data)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ DetectLoaderThreadData *ftd = (DetectLoaderThreadData *)thread_data;
+ BUG_ON(ftd == NULL);
+
+ SCLogDebug("loader thread started");
+ while (1)
+ {
+ if (TmThreadsCheckFlag(th_v, THV_PAUSE)) {
+ TmThreadsSetFlag(th_v, THV_PAUSED);
+ TmThreadTestThreadUnPaused(th_v);
+ TmThreadsUnsetFlag(th_v, THV_PAUSED);
+ }
+
+ /* see if we have tasks */
+
+ DetectLoaderControl *loader = &loaders[ftd->instance];
+ SCMutexLock(&loader->m);
+
+ DetectLoaderTask *task = NULL, *tmptask = NULL;
+ TAILQ_FOREACH_SAFE(task, &loader->task_list, next, tmptask) {
+ int r = task->Func(task->ctx, ftd->instance);
+ loader->result |= r;
+ TAILQ_REMOVE(&loader->task_list, task, next);
+ SCFree(task);
+ }
+
+ SCMutexUnlock(&loader->m);
+
+ if (TmThreadsCheckFlag(th_v, THV_KILL)) {
+ break;
+ }
+
+ /* just wait until someone wakes us up */
+ SCCtrlMutexLock(th_v->ctrl_mutex);
+ SCCtrlCondWait(th_v->ctrl_cond, th_v->ctrl_mutex);
+ SCCtrlMutexUnlock(th_v->ctrl_mutex);
+
+ SCLogDebug("woke up...");
+ }
+
+ return TM_ECODE_OK;
+}
+
+/** \brief spawn the detect loader manager thread */
+void DetectLoaderThreadSpawn()
+{
+ int i;
+ for (i = 0; i < num_loaders; i++) {
+ ThreadVars *tv_loader = NULL;
+
+ char name[32] = "";
+ snprintf(name, sizeof(name), "DetectLoader%02d", i+1);
+
+ tv_loader = TmThreadCreateCmdThreadByName("DetectLoader",
+ "DetectLoader", 1);
+ BUG_ON(tv_loader == NULL);
+
+ if (tv_loader == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(1);
+ }
+ if (TmThreadSpawn(tv_loader) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(1);
+ }
+ }
+ return;
+}
+
+void TmModuleDetectLoaderRegister (void)
+{
+ tmm_modules[TMM_DETECTLOADER].name = "DetectLoader";
+ tmm_modules[TMM_DETECTLOADER].ThreadInit = DetectLoaderThreadInit;
+ tmm_modules[TMM_DETECTLOADER].ThreadDeinit = DetectLoaderThreadDeinit;
+ tmm_modules[TMM_DETECTLOADER].Management = DetectLoader;
+ tmm_modules[TMM_DETECTLOADER].cap_flags = 0;
+ tmm_modules[TMM_DETECTLOADER].flags = TM_FLAG_MANAGEMENT_TM;
+ SCLogDebug("%s registered", tmm_modules[TMM_DETECTLOADER].name);
+
+ SC_ATOMIC_INIT(detect_loader_cnt);
+}
diff --git a/framework/src/suricata/src/detect-engine-loader.h b/framework/src/suricata/src/detect-engine-loader.h
new file mode 100644
index 00000000..de28bdaf
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-loader.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Detect loader API, for using multiple 'loader' threads
+ * that can load multiple detection engines in parallel.
+ */
+
+#ifndef __DETECT_ENGINE_LOADER_H__
+#define __DETECT_ENGINE_LOADER_H__
+
+/**
+ * \param ctx function specific data
+ * \param loader_id id of the loader that executed the task
+ */
+typedef int (*LoaderFunc)(void *ctx, int loader_id);
+
+typedef struct DetectLoaderTask_ {
+ LoaderFunc Func;
+ void *ctx;
+ TAILQ_ENTRY(DetectLoaderTask_) next;
+} DetectLoaderTask;
+
+typedef struct DetectLoaderControl_ {
+ int id;
+ int result; /* 0 for ok, error otherwise */
+ SCMutex m;
+ TAILQ_HEAD(, DetectLoaderTask_) task_list;
+} DetectLoaderControl;
+
+int DetectLoaderQueueTask(int loader_id, LoaderFunc Func, void *func_ctx);
+int DetectLoadersSync(void);
+void DetectLoadersInit(void);
+
+void TmThreadContinueDetectLoaderThreads();
+void DetectLoaderThreadSpawn();
+void TmModuleDetectLoaderRegister (void);
+
+#endif /* __DETECT_ENGINE_LOADER_H__ */
diff --git a/framework/src/suricata/src/detect-engine-modbus.c b/framework/src/suricata/src/detect-engine-modbus.c
new file mode 100644
index 00000000..8bbe5828
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-modbus.c
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ *
+ * Based on detect-engine-dns.c
+ */
+
+#include "suricata-common.h"
+
+#include "app-layer.h"
+#include "app-layer-modbus.h"
+
+#include "detect.h"
+#include "detect-modbus.h"
+
+#include "detect-engine-modbus.h"
+
+#include "flow.h"
+
+#include "util-debug.h"
+
+/** \internal
+ *
+ * \brief Value match detection code
+ *
+ * \param value Modbus value context (min, max and mode)
+ * \param min Minimum value to compare
+ * \param inter Interval or maximum (min + inter) value to compare
+ *
+ * \retval 1 match or 0 no match
+ */
+static int DetectEngineInspectModbusValueMatch(DetectModbusValue *value,
+ uint16_t min,
+ uint16_t inter)
+{
+ SCEnter();
+ uint16_t max = min + inter;
+
+ int ret = 0;
+
+ switch (value->mode) {
+ case DETECT_MODBUS_EQ:
+ if ((value->min >= min) && (value->min <= max))
+ ret = 1;
+ break;
+
+ case DETECT_MODBUS_LT:
+ if (value->min > min)
+ ret = 1;
+ break;
+
+ case DETECT_MODBUS_GT:
+ if (value->min < max)
+ ret = 1;
+ break;
+
+ case DETECT_MODBUS_RA:
+ if ((value->max > min) && (value->min < max))
+ ret = 1;
+ break;
+ }
+
+ SCReturnInt(ret);
+}
+
+/** \internal
+ *
+ * \brief Do data (and address) inspection & validation for a signature
+ *
+ * \param tx Pointer to Modbus Transaction
+ * \param address Address inspection
+ * \param data Pointer to data signature structure to match
+ *
+ * \retval 0 no match or 1 match
+ */
+static int DetectEngineInspectModbusData(ModbusTransaction *tx,
+ uint16_t address,
+ DetectModbusValue *data)
+{
+ SCEnter();
+ uint16_t offset, value = 0, type = tx->type;
+
+ if (type & MODBUS_TYP_SINGLE) {
+ /* Output/Register(s) Value */
+ if (type & MODBUS_TYP_COILS)
+ value = (tx->data[0])? 1 : 0;
+ else
+ value = tx->data[0];
+ } else if (type & MODBUS_TYP_MULTIPLE) {
+ int i, size = (int) sizeof(tx->data);
+
+ offset = address - (tx->write.address + 1);
+
+ /* In case of Coils, offset is in bit (convert in byte) */
+ if (type & MODBUS_TYP_COILS)
+ offset >>= 3;
+
+ for (i=0; i< size; i++) {
+ /* Select the correct register/coils amongst the output value */
+ if (!(offset--)) {
+ value = tx->data[i];
+ break;
+ }
+ }
+
+ /* In case of Coils, offset is now in the bit is the rest of previous convert */
+ if (type & MODBUS_TYP_COILS) {
+ offset = (address - (tx->write.address + 1)) & 0x7;
+ value = (value >> offset) & 0x1;
+ }
+ } else {
+ /* It is not possible to define the value that is writing for Mask */
+ /* Write Register function because the current content is not available.*/
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(DetectEngineInspectModbusValueMatch(data, value, 0));
+}
+
+/** \internal
+ *
+ * \brief Do address inspection & validation for a signature
+ *
+ * \param tx Pointer to Modbus Transaction
+ * \param address Pointer to address signature structure to match
+ * \param access Access mode (READ or WRITE)
+ *
+ * \retval 0 no match or 1 match
+ */
+static int DetectEngineInspectModbusAddress(ModbusTransaction *tx,
+ DetectModbusValue *address,
+ uint8_t access)
+{
+ SCEnter();
+ int ret = 0;
+
+ /* Check if read/write address of request is at/in the address range of signature */
+ if (access == MODBUS_TYP_READ) {
+ /* In the PDU Coils are addresses starting at zero */
+ /* therefore Coils numbered 1-16 are addressed as 0-15 */
+ ret = DetectEngineInspectModbusValueMatch(address,
+ tx->read.address + 1,
+ tx->read.quantity - 1);
+ } else {
+ /* In the PDU Registers are addresses starting at zero */
+ /* therefore Registers numbered 1-16 are addressed as 0-15 */
+ if (tx->type & MODBUS_TYP_SINGLE)
+ ret = DetectEngineInspectModbusValueMatch(address,
+ tx->write.address + 1,
+ 0);
+ else
+ ret = DetectEngineInspectModbusValueMatch(address,
+ tx->write.address + 1,
+ tx->write.quantity - 1);
+ }
+
+ SCReturnInt(ret);
+}
+
+/** \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect ( and sm: SigMatch to inspect)
+ * \param f Flow
+ * \param flags App layer flags
+ * \param alstate App layer state
+ * \param txv Pointer to Modbus Transaction structure
+ *
+ * \retval 0 no match or 1 match
+ */
+int DetectEngineInspectModbus(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s,
+ Flow *f,
+ uint8_t flags,
+ void *alstate,
+ void *txv,
+ uint64_t tx_id)
+{
+ SCEnter();
+ ModbusTransaction *tx = (ModbusTransaction *)txv;
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MODBUS_MATCH];
+ DetectModbus *modbus = (DetectModbus *) sm->ctx;
+
+ int ret = 0;
+
+ if (modbus == NULL) {
+ SCLogDebug("no modbus state, no match");
+ SCReturnInt(0);
+ }
+
+ if (modbus->type == MODBUS_TYP_NONE) {
+ if (modbus->category == MODBUS_CAT_NONE) {
+ if (modbus->function == tx->function) {
+ if (modbus->subfunction != NULL) {
+ SCLogDebug("looking for Modbus server function %d and subfunction %d",
+ modbus->function, *(modbus->subfunction));
+ ret = (*(modbus->subfunction) == (tx->subFunction))? 1 : 0;
+ } else {
+ SCLogDebug("looking for Modbus server function %d", modbus->function);
+ ret = 1;
+ }
+ }
+ } else {
+ SCLogDebug("looking for Modbus category function %d", modbus->category);
+ ret = (tx->category & modbus->category)? 1 : 0;
+ }
+ } else {
+ uint8_t access = modbus->type & MODBUS_TYP_ACCESS_MASK;
+ uint8_t function = modbus->type & MODBUS_TYP_ACCESS_FUNCTION_MASK;
+
+ if ((access & tx->type) && ((function == MODBUS_TYP_NONE) || (function & tx->type))) {
+ if (modbus->address != NULL) {
+ ret = DetectEngineInspectModbusAddress(tx, modbus->address, access);
+
+ if (ret && (modbus->data != NULL)) {
+ ret = DetectEngineInspectModbusData(tx, modbus->address->min, modbus->data);
+ }
+ } else {
+ SCLogDebug("looking for Modbus access type %d and function type %d", access, function);
+ ret = 1;
+ }
+ }
+ }
+
+ SCReturnInt(ret);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+#include "app-layer-parser.h"
+
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+
+#include "flow-util.h"
+
+#include "stream-tcp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */
+/* Example of a request to read discrete outputs 20-38 */
+static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x01,
+ /* Starting Address */ 0x78, 0x90,
+ /* Quantity of coils */ 0x00, 0x13 };
+
+/* Modbus Application Protocol Specification V1.1b3 6.4: Read Input Registers */
+/* Example of a request to read input register 9 */
+static uint8_t readInputsRegistersReq[] = {/* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x04,
+ /* Starting Address */ 0x00, 0x08,
+ /* Quantity of Registers */ 0x00, 0x60};
+
+/* Modbus Application Protocol Specification V1.1b3 6.17: Read/Write Multiple registers */
+/* Example of a request to read six registers starting at register 4, */
+/* and to write three registers starting at register 15 */
+static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x11,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x17,
+ /* Read Starting Address */ 0x00, 0x03,
+ /* Quantity to Read */ 0x00, 0x06,
+ /* Write Starting Address */ 0x00, 0x0E,
+ /* Quantity to Write */ 0x00, 0x03,
+ /* Write Byte count */ 0x06,
+ /* Write Registers Value */ 0x12, 0x34, /* 15 */
+ 0x56, 0x78, /* 16 */
+ 0x9A, 0xBC};/* 17 */
+
+/* Modbus Application Protocol Specification V1.1b3 6.8.1: 04 Force Listen Only Mode */
+/* Example of a request to to remote device to its Listen Only MOde for Modbus Communications. */
+static uint8_t forceListenOnlyMode[] = {/* Transaction ID */ 0x0A, 0x00,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x06,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x08,
+ /* Sub-function code */ 0x00, 0x04,
+ /* Data */ 0x00, 0x00};
+
+/* Modbus Application Protocol Specification V1.1b3 Annex A */
+/* Modbus Reserved Function codes, Subcodes and MEI types */
+static uint8_t encapsulatedInterfaceTransport[] = {
+ /* Transaction ID */ 0x00, 0x10,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x05,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x2B,
+ /* MEI Type */ 0x0F,
+ /* Data */ 0x00, 0x00};
+
+static uint8_t unassigned[] = {/* Transaction ID */ 0x00, 0x0A,
+ /* Protocol ID */ 0x00, 0x00,
+ /* Length */ 0x00, 0x02,
+ /* Unit ID */ 0x00,
+ /* Function code */ 0x12};
+
+/** \test Test code function. */
+static int DetectEngineInspectModbusTest01(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus code function\"; "
+ "modbus: function 23; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test code function and code subfunction. */
+static int DetectEngineInspectModbusTest02(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus function and subfunction\"; "
+ "modbus: function 8, subfunction 4; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, forceListenOnlyMode, sizeof(forceListenOnlyMode));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test function category. */
+static int DetectEngineInspectModbusTest03(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus category function\"; "
+ "modbus: function reserved; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ encapsulatedInterfaceTransport, sizeof(encapsulatedInterfaceTransport));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test negative function category. */
+static int DetectEngineInspectModbusTest04(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus category function\"; "
+ "modbus: function !assigned; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, unassigned, sizeof(unassigned));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test access type. */
+static int DetectEngineInspectModbusTest05(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access type\"; "
+ "modbus: access read; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readCoilsReq, sizeof(readCoilsReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test access function. */
+static int DetectEngineInspectModbusTest06(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access type\"; "
+ "modbus: access read input; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readInputsRegistersReq, sizeof(readInputsRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test read access at an address. */
+static int DetectEngineInspectModbusTest07(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus address access\"; "
+ "modbus: access read, address 30870; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test read access at a range of address. */
+static int DetectEngineInspectModbusTest08(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ /* readInputsRegistersReq, Starting Address = 0x08, Quantity of Registers = 0x60 */
+ /* Read access address from 9 to 104 */
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address <9; sid:1;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 9; sid:2;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 5<>9; sid:3;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address <10; sid:4;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 5<>10; sid:5;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address >103; sid:6;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 103<>110; sid:7;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 104; sid:8;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address >104; sid:9;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus access\"; "
+ "modbus: access read input, "
+ "address 104<>110; sid:10;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readInputsRegistersReq, sizeof(readInputsRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 did match but should not have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 3)) {
+ printf("sid 3 did match but should not have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 4))) {
+ printf("sid 4 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 5))) {
+ printf("sid 5 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 6))) {
+ printf("sid 6 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 7))) {
+ printf("sid 7 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 8))) {
+ printf("sid 8 didn't match but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 9)) {
+ printf("sid 9 did match but should not have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 10)) {
+ printf("sid 10 did match but should not have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test write access at a address in a range of value. */
+static int DetectEngineInspectModbusTest09(void)
+{
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+
+ int result = 0;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.alproto = ALPROTO_MODBUS;
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */
+ /* Write access register address 15 = 0x1234 (4660) */
+ /* Write access register address 16 = 0x5678 (22136) */
+ /* Write access register address 17 = 0x9ABC (39612) */
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 15, value <4660; sid:1;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 16, value <22137; sid:2;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 17, value 39612; sid:3;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 15, value 4661; sid:4;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 16, value 20000<>22136; sid:5;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 17, value 30000<>39613; sid:6;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 15, value 4659<>5000; sid:7;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 16, value 22136<>30000; sid:8;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 17, value >39611; sid:9;)");
+
+ s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus write access\"; "
+ "modbus: access write holding, "
+ "address 15, value >4660; sid:10;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
+ readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq));
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ModbusState *modbus_state = f.alstate;
+ if (modbus_state == NULL) {
+ printf("no modbus state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 did match but should not have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 3))) {
+ printf("sid 3 didn't match but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 4)) {
+ printf("sid 4 did match but should not have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 5)) {
+ printf("sid 5 did match but should not have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 6))) {
+ printf("sid 6 didn't match but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 7))) {
+ printf("sid 7 didn't match but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 8)) {
+ printf("sid 8 did match but should not have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p, 9))) {
+ printf("sid 9 didn't match but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 10)) {
+ printf("sid 10 did match but should not have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void DetectEngineInspectModbusRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineInspectModbusTest01 - Code function", DetectEngineInspectModbusTest01, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest02 - code function and code subfunction", DetectEngineInspectModbusTest02, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest03 - Function category", DetectEngineInspectModbusTest03, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest04 - Negative function category", DetectEngineInspectModbusTest04, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest05 - Access type", DetectEngineInspectModbusTest05, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest06 - Access function", DetectEngineInspectModbusTest06, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest07 - Read access at an address", DetectEngineInspectModbusTest07, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest08 - Read access at a range of address", DetectEngineInspectModbusTest08, 1);
+ UtRegisterTest("DetectEngineInspectModbusTest09 - Write access at an address a range of value", DetectEngineInspectModbusTest09, 1);
+#endif /* UNITTESTS */
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-modbus.h b/framework/src/suricata/src/detect-engine-modbus.h
new file mode 100644
index 00000000..e140f975
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-modbus.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ */
+
+#ifndef __DETECT_ENGINE_MODBUS_H__
+#define __DETECT_ENGINE_MODBUS_H__
+
+int DetectEngineInspectModbus(ThreadVars *, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *, Signature *,
+ Flow *, uint8_t, void *, void *, uint64_t);
+
+void DetectEngineInspectModbusRegisterTests(void);
+#endif /* __DETECT_ENGINE_MODBUS_H__ */
diff --git a/framework/src/suricata/src/detect-engine-mpm.c b/framework/src/suricata/src/detect-engine-mpm.c
new file mode 100644
index 00000000..c34ef252
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-mpm.c
@@ -0,0 +1,3030 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Multi pattern matcher
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+
+#include "app-layer-protos.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-iponly.h"
+#include "detect-parse.h"
+#include "util-mpm.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+#include "conf.h"
+#include "detect-fast-pattern.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "detect-flow.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "stream.h"
+
+#include "util-enum.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-memcmp.h"
+#ifdef __SC_CUDA_SUPPORT__
+#include "util-mpm-ac.h"
+#endif
+
+/** \todo make it possible to use multiple pattern matcher algorithms next to
+ each other. */
+
+#define POPULATE_MPM_AVOID_PACKET_MPM_PATTERNS 0x01
+#define POPULATE_MPM_AVOID_STREAM_MPM_PATTERNS 0x02
+#define POPULATE_MPM_AVOID_URI_MPM_PATTERNS 0x04
+
+/**
+ * \brief check if a signature has patterns that are to be inspected
+ * against a packets payload (as opposed to the stream payload)
+ *
+ * \param s signature
+ *
+ * \retval 1 true
+ * \retval 0 false
+ */
+int SignatureHasPacketContent(Signature *s)
+{
+ SCEnter();
+
+ if (s == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (!(s->proto.proto[6 / 8] & 1 << (6 % 8))) {
+ SCReturnInt(1);
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ SCLogDebug("no mpm");
+ SCReturnInt(0);
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief check if a signature has patterns that are to be inspected
+ * against the stream payload (as opposed to the individual packets
+ * payload(s))
+ *
+ * \param s signature
+ *
+ * \retval 1 true
+ * \retval 0 false
+ */
+int SignatureHasStreamContent(Signature *s)
+{
+ SCEnter();
+
+ if (s == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (!(s->proto.proto[6 / 8] & 1 << (6 % 8))) {
+ SCReturnInt(0);
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ SCLogDebug("no mpm");
+ SCReturnInt(0);
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+
+/**
+ * \brief Function to return the multi pattern matcher algorithm to be
+ * used by the engine, based on the mpm-algo setting in yaml
+ * Use the default mpm if none is specified in the yaml file.
+ *
+ * \retval mpm algo value
+ */
+uint16_t PatternMatchDefaultMatcher(void)
+{
+ char *mpm_algo;
+ uint16_t mpm_algo_val = DEFAULT_MPM;
+
+ /* Get the mpm algo defined in config file by the user */
+ if ((ConfGet("mpm-algo", &mpm_algo)) == 1) {
+ uint16_t u;
+
+ if (mpm_algo != NULL) {
+ for (u = 0; u < MPM_TABLE_SIZE; u++) {
+ if (mpm_table[u].name == NULL)
+ continue;
+
+ if (strcmp(mpm_table[u].name, mpm_algo) == 0) {
+ mpm_algo_val = u;
+ goto done;
+ }
+ }
+ }
+
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid mpm algo supplied "
+ "in the yaml conf file: \"%s\"", mpm_algo);
+ exit(EXIT_FAILURE);
+ }
+
+ done:
+#ifdef __tile__
+ if (mpm_algo_val == MPM_AC)
+ mpm_algo_val = MPM_AC_TILE;
+#endif
+
+ return mpm_algo_val;
+}
+
+uint32_t PacketPatternSearchWithStreamCtx(DetectEngineThreadCtx *det_ctx,
+ Packet *p)
+{
+ SCEnter();
+
+ uint32_t ret = 0;
+
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ if (det_ctx->sgh->mpm_stream_ctx_ts == NULL)
+ SCReturnInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_stream_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_stream_ctx_ts, &det_ctx->mtc, &det_ctx->pmq,
+ p->payload, p->payload_len);
+ } else {
+ if (det_ctx->sgh->mpm_stream_ctx_tc == NULL)
+ SCReturnInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_stream_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_stream_ctx_tc, &det_ctx->mtc, &det_ctx->pmq,
+ p->payload, p->payload_len);
+ }
+
+ SCReturnInt(ret);
+}
+
+/** \brief Pattern match -- searches for only one pattern per signature.
+ *
+ * \param det_ctx detection engine thread ctx
+ * \param p packet to inspect
+ *
+ * \retval ret number of matches
+ */
+uint32_t PacketPatternSearch(DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+ SCEnter();
+
+ uint32_t ret;
+ MpmCtx *mpm_ctx = NULL;
+
+ if (p->proto == IPPROTO_TCP) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ mpm_ctx = det_ctx->sgh->mpm_proto_tcp_ctx_ts;
+ } else {
+ mpm_ctx = det_ctx->sgh->mpm_proto_tcp_ctx_tc;
+ }
+ } else if (p->proto == IPPROTO_UDP) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ mpm_ctx = det_ctx->sgh->mpm_proto_udp_ctx_ts;
+ } else {
+ mpm_ctx = det_ctx->sgh->mpm_proto_udp_ctx_tc;
+ }
+ } else {
+ mpm_ctx = det_ctx->sgh->mpm_proto_other_ctx;
+ }
+
+ if (mpm_ctx == NULL)
+ SCReturnInt(0);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (p->cuda_pkt_vars.cuda_mpm_enabled && p->pkt_src == PKT_SRC_WIRE) {
+ ret = SCACCudaPacketResultsProcessing(p, mpm_ctx, &det_ctx->pmq);
+ } else {
+ ret = mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
+ &det_ctx->mtc,
+ &det_ctx->pmq,
+ p->payload,
+ p->payload_len);
+ }
+#else
+ ret = mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
+ &det_ctx->mtc,
+ &det_ctx->pmq,
+ p->payload,
+ p->payload_len);
+#endif
+
+ SCReturnInt(ret);
+}
+
+/** \brief Uri Pattern match -- searches for one pattern per signature.
+ *
+ * \param det_ctx detection engine thread ctx
+ * \param p packet to inspect
+ *
+ * \retval ret number of matches
+ */
+uint32_t UriPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *uri, uint16_t uri_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_uri_ctx_ts == NULL)
+ SCReturnUInt(0U);
+
+ ret = mpm_table[det_ctx->sgh->mpm_uri_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_uri_ctx_ts,
+ &det_ctx->mtcu, &det_ctx->pmq, uri, uri_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ //PrintRawDataFp(stdout, uri, uri_len);
+
+ SCReturnUInt(ret);
+}
+
+/** \brief Http client body pattern match -- searches for one pattern per
+ * signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param body The request body to inspect.
+ * \param body_len Body length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpClientBodyPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *body, uint32_t body_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hcbd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hcbd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hcbd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, body, body_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/** \brief Http server body pattern match -- searches for one pattern per
+ * signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param body The request body to inspect.
+ * \param body_len Body length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpServerBodyPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *body, uint32_t body_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ BUG_ON(1);
+ } else {
+ if (det_ctx->sgh->mpm_hsbd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hsbd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hsbd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, body, body_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http header match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param headers Headers to inspect.
+ * \param headers_len Headers length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpHeaderPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *headers, uint32_t headers_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hhd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hhd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hhd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, headers, headers_len);
+ } else {
+ if (det_ctx->sgh->mpm_hhd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hhd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hhd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, headers, headers_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http raw header match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param headers Raw headers to inspect.
+ * \param headers_len Raw headers length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpRawHeaderPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *raw_headers, uint32_t raw_headers_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hrhd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hrhd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hrhd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, raw_headers, raw_headers_len);
+ } else {
+ if (det_ctx->sgh->mpm_hrhd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hrhd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hrhd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, raw_headers, raw_headers_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http method match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param method Method to inspect.
+ * \param method_len Method length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpMethodPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *raw_method, uint32_t raw_method_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hmd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hmd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hmd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, raw_method, raw_method_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http cookie match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param cookie Cookie to inspect.
+ * \param cookie_len Cookie length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpCookiePatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *cookie, uint32_t cookie_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hcd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hcd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hcd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, cookie, cookie_len);
+ } else {
+ if (det_ctx->sgh->mpm_hcd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hcd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hcd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, cookie, cookie_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http raw uri match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param uri Raw uri to inspect.
+ * \param uri_len Raw uri length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpRawUriPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *uri, uint32_t uri_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hrud_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hrud_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hrud_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, uri, uri_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http stat msg match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param stat_msg Stat msg to inspect.
+ * \param stat_msg_len Stat msg length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpStatMsgPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *stat_msg, uint32_t stat_msg_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ BUG_ON(1);
+ } else {
+ if (det_ctx->sgh->mpm_hsmd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hsmd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hsmd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, stat_msg, stat_msg_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http stat code match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param stat_code Stat code to inspect.
+ * \param stat_code_len Stat code length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpStatCodePatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *stat_code, uint32_t stat_code_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ BUG_ON(1);
+ } else {
+ if (det_ctx->sgh->mpm_hscd_ctx_tc == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hscd_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_hscd_ctx_tc, &det_ctx->mtcu,
+ &det_ctx->pmq, stat_code, stat_code_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http user agent match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param cookie User-Agent to inspect.
+ * \param cookie_len User-Agent buffer length.
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpUAPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *ua, uint32_t ua_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_huad_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_huad_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_huad_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, ua, ua_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http host header match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param hh Host header to inspect.
+ * \param hh_len Host header buffer length.
+ * \param flags Flags
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpHHPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *hh, uint32_t hh_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hhhd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hhhd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hhhd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, hh, hh_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief Http raw host header match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param hrh Raw hostname to inspect.
+ * \param hrh_len Raw hostname buffer length.
+ * \param flags Flags
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t HttpHRHPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *hrh, uint32_t hrh_len, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret;
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_hrhhd_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_hrhhd_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_hrhhd_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, hrh, hrh_len);
+ } else {
+ BUG_ON(1);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/**
+ * \brief DNS query match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param hrh Buffer to inspect.
+ * \param hrh_len buffer length.
+ * \param flags Flags
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t DnsQueryPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *buffer, uint32_t buffer_len,
+ uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret = 0;
+
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_dnsquery_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_dnsquery_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_dnsquery_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, buffer, buffer_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/** \brief Pattern match -- searches for only one pattern per signature.
+ *
+ * \param det_ctx detection engine thread ctx
+ * \param p packet
+ * \param smsg stream msg (reassembled stream data)
+ * \param flags stream flags
+ *
+ * \retval ret number of matches
+ */
+uint32_t StreamPatternSearch(DetectEngineThreadCtx *det_ctx, Packet *p,
+ StreamMsg *smsg, uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret = 0;
+ uint8_t cnt = 0;
+
+ //PrintRawDataFp(stdout, smsg->data.data, smsg->data.data_len);
+
+ uint32_t r;
+ if (flags & STREAM_TOSERVER) {
+ for ( ; smsg != NULL; smsg = smsg->next) {
+ r = mpm_table[det_ctx->sgh->mpm_stream_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_stream_ctx_ts, &det_ctx->mtcs,
+ &det_ctx->smsg_pmq[cnt], smsg->data, smsg->data_len);
+ if (r > 0) {
+ ret += r;
+
+ SCLogDebug("smsg match stored in det_ctx->smsg_pmq[%u]", cnt);
+
+ /* merge results with overall pmq */
+ PmqMerge(&det_ctx->smsg_pmq[cnt], &det_ctx->pmq);
+ }
+
+ cnt++;
+ }
+ } else {
+ for ( ; smsg != NULL; smsg = smsg->next) {
+ r = mpm_table[det_ctx->sgh->mpm_stream_ctx_tc->mpm_type].
+ Search(det_ctx->sgh->mpm_stream_ctx_tc, &det_ctx->mtcs,
+ &det_ctx->smsg_pmq[cnt], smsg->data, smsg->data_len);
+ if (r > 0) {
+ ret += r;
+
+ SCLogDebug("smsg match stored in det_ctx->smsg_pmq[%u]", cnt);
+
+ /* merge results with overall pmq */
+ PmqMerge(&det_ctx->smsg_pmq[cnt], &det_ctx->pmq);
+ }
+
+ cnt++;
+ }
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief SMTP Filedata match -- searches for one pattern per signature.
+ *
+ * \param det_ctx Detection engine thread ctx.
+ * \param buffer Buffer to inspect.
+ * \param buffer_len buffer length.
+ * \param flags Flags
+ *
+ * \retval ret Number of matches.
+ */
+uint32_t SMTPFiledataPatternSearch(DetectEngineThreadCtx *det_ctx,
+ uint8_t *buffer, uint32_t buffer_len,
+ uint8_t flags)
+{
+ SCEnter();
+
+ uint32_t ret = 0;
+
+ if (flags & STREAM_TOSERVER) {
+ if (det_ctx->sgh->mpm_smtp_filedata_ctx_ts == NULL)
+ SCReturnUInt(0);
+
+ ret = mpm_table[det_ctx->sgh->mpm_smtp_filedata_ctx_ts->mpm_type].
+ Search(det_ctx->sgh->mpm_smtp_filedata_ctx_ts, &det_ctx->mtcu,
+ &det_ctx->pmq, buffer, buffer_len);
+ }
+
+ SCReturnUInt(ret);
+}
+
+/** \brief cleans up the mpm instance after a match */
+void PacketPatternCleanup(ThreadVars *t, DetectEngineThreadCtx *det_ctx)
+{
+ PmqReset(&det_ctx->pmq);
+
+ if (det_ctx->sgh == NULL)
+ return;
+
+ /* content */
+ if (det_ctx->sgh->mpm_proto_tcp_ctx_ts != NULL &&
+ mpm_table[det_ctx->sgh->mpm_proto_tcp_ctx_ts->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_proto_tcp_ctx_ts->mpm_type].Cleanup(&det_ctx->mtc);
+ }
+ if (det_ctx->sgh->mpm_proto_tcp_ctx_tc != NULL &&
+ mpm_table[det_ctx->sgh->mpm_proto_tcp_ctx_tc->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_proto_tcp_ctx_tc->mpm_type].Cleanup(&det_ctx->mtc);
+ }
+
+ if (det_ctx->sgh->mpm_proto_udp_ctx_ts != NULL &&
+ mpm_table[det_ctx->sgh->mpm_proto_udp_ctx_ts->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_proto_udp_ctx_ts->mpm_type].Cleanup(&det_ctx->mtc);
+ }
+ if (det_ctx->sgh->mpm_proto_udp_ctx_tc != NULL &&
+ mpm_table[det_ctx->sgh->mpm_proto_udp_ctx_tc->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_proto_udp_ctx_tc->mpm_type].Cleanup(&det_ctx->mtc);
+ }
+
+ if (det_ctx->sgh->mpm_proto_other_ctx != NULL &&
+ mpm_table[det_ctx->sgh->mpm_proto_other_ctx->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_proto_other_ctx->mpm_type].Cleanup(&det_ctx->mtc);
+ }
+
+ /* uricontent */
+ if (det_ctx->sgh->mpm_uri_ctx_ts != NULL && mpm_table[det_ctx->sgh->mpm_uri_ctx_ts->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_uri_ctx_ts->mpm_type].Cleanup(&det_ctx->mtcu);
+ }
+
+ /* stream content */
+ if (det_ctx->sgh->mpm_stream_ctx_ts != NULL && mpm_table[det_ctx->sgh->mpm_stream_ctx_ts->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_stream_ctx_ts->mpm_type].Cleanup(&det_ctx->mtcs);
+ }
+ if (det_ctx->sgh->mpm_stream_ctx_tc != NULL && mpm_table[det_ctx->sgh->mpm_stream_ctx_tc->mpm_type].Cleanup != NULL) {
+ mpm_table[det_ctx->sgh->mpm_stream_ctx_tc->mpm_type].Cleanup(&det_ctx->mtcs);
+ }
+
+ return;
+}
+
+void StreamPatternCleanup(ThreadVars *t, DetectEngineThreadCtx *det_ctx, StreamMsg *smsg)
+{
+ uint8_t cnt = 0;
+
+ while (smsg != NULL) {
+ PmqReset(&det_ctx->smsg_pmq[cnt]);
+
+ smsg = smsg->next;
+ cnt++;
+ }
+}
+
+void PatternMatchDestroy(MpmCtx *mpm_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_ctx %p, mpm_matcher %"PRIu16"", mpm_ctx, mpm_matcher);
+ mpm_table[mpm_matcher].DestroyCtx(mpm_ctx);
+}
+
+void PatternMatchPrepare(MpmCtx *mpm_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_ctx %p, mpm_matcher %"PRIu16"", mpm_ctx, mpm_matcher);
+ MpmInitCtx(mpm_ctx, mpm_matcher);
+}
+
+void PatternMatchThreadPrint(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_thread_ctx %p, mpm_matcher %"PRIu16" defunct", mpm_thread_ctx, mpm_matcher);
+ //mpm_table[mpm_matcher].PrintThreadCtx(mpm_thread_ctx);
+}
+void PatternMatchThreadDestroy(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_thread_ctx %p, mpm_matcher %"PRIu16"", mpm_thread_ctx, mpm_matcher);
+ if (mpm_table[mpm_matcher].DestroyThreadCtx != NULL)
+ mpm_table[mpm_matcher].DestroyThreadCtx(NULL, mpm_thread_ctx);
+}
+void PatternMatchThreadPrepare(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher, uint32_t max_id)
+{
+ SCLogDebug("mpm_thread_ctx %p, type %"PRIu16", max_id %"PRIu32"", mpm_thread_ctx, mpm_matcher, max_id);
+ MpmInitThreadCtx(mpm_thread_ctx, mpm_matcher, max_id);
+}
+
+
+/* free the pattern matcher part of a SigGroupHead */
+void PatternMatchDestroyGroup(SigGroupHead *sh)
+{
+ /* content */
+ if (!(sh->flags & SIG_GROUP_HEAD_MPM_COPY)) {
+ SCLogDebug("destroying mpm_ctx %p (sh %p)",
+ sh->mpm_proto_tcp_ctx_ts, sh);
+ if (sh->mpm_proto_tcp_ctx_ts != NULL &&
+ !sh->mpm_proto_tcp_ctx_ts->global) {
+ mpm_table[sh->mpm_proto_tcp_ctx_ts->mpm_type].
+ DestroyCtx(sh->mpm_proto_tcp_ctx_ts);
+ SCFree(sh->mpm_proto_tcp_ctx_ts);
+ }
+ /* ready for reuse */
+ sh->mpm_proto_tcp_ctx_ts = NULL;
+
+ SCLogDebug("destroying mpm_ctx %p (sh %p)",
+ sh->mpm_proto_tcp_ctx_tc, sh);
+ if (sh->mpm_proto_tcp_ctx_tc != NULL &&
+ !sh->mpm_proto_tcp_ctx_tc->global) {
+ mpm_table[sh->mpm_proto_tcp_ctx_tc->mpm_type].
+ DestroyCtx(sh->mpm_proto_tcp_ctx_tc);
+ SCFree(sh->mpm_proto_tcp_ctx_tc);
+ }
+ /* ready for reuse */
+ sh->mpm_proto_tcp_ctx_tc = NULL;
+
+ SCLogDebug("destroying mpm_ctx %p (sh %p)",
+ sh->mpm_proto_udp_ctx_ts, sh);
+ if (sh->mpm_proto_udp_ctx_ts != NULL &&
+ !sh->mpm_proto_udp_ctx_ts->global) {
+ mpm_table[sh->mpm_proto_udp_ctx_ts->mpm_type].
+ DestroyCtx(sh->mpm_proto_udp_ctx_ts);
+ SCFree(sh->mpm_proto_udp_ctx_ts);
+ }
+ /* ready for reuse */
+ sh->mpm_proto_udp_ctx_ts = NULL;
+
+ SCLogDebug("destroying mpm_ctx %p (sh %p)",
+ sh->mpm_proto_udp_ctx_tc, sh);
+ if (sh->mpm_proto_udp_ctx_tc != NULL &&
+ !sh->mpm_proto_udp_ctx_tc->global) {
+ mpm_table[sh->mpm_proto_udp_ctx_tc->mpm_type].
+ DestroyCtx(sh->mpm_proto_udp_ctx_tc);
+ SCFree(sh->mpm_proto_udp_ctx_tc);
+ }
+ /* ready for reuse */
+ sh->mpm_proto_udp_ctx_tc = NULL;
+
+ SCLogDebug("destroying mpm_ctx %p (sh %p)",
+ sh->mpm_proto_other_ctx, sh);
+ if (sh->mpm_proto_other_ctx != NULL &&
+ !sh->mpm_proto_other_ctx->global) {
+ mpm_table[sh->mpm_proto_other_ctx->mpm_type].
+ DestroyCtx(sh->mpm_proto_other_ctx);
+ SCFree(sh->mpm_proto_other_ctx);
+ }
+ /* ready for reuse */
+ sh->mpm_proto_other_ctx = NULL;
+ }
+
+ /* uricontent */
+ if ((sh->mpm_uri_ctx_ts != NULL) && !(sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY)) {
+ if (sh->mpm_uri_ctx_ts != NULL) {
+ SCLogDebug("destroying mpm_uri_ctx %p (sh %p)", sh->mpm_uri_ctx_ts, sh);
+ if (!sh->mpm_uri_ctx_ts->global) {
+ mpm_table[sh->mpm_uri_ctx_ts->mpm_type].DestroyCtx(sh->mpm_uri_ctx_ts);
+ SCFree(sh->mpm_uri_ctx_ts);
+ }
+ /* ready for reuse */
+ sh->mpm_uri_ctx_ts = NULL;
+ }
+ }
+
+ /* stream content */
+ if ((sh->mpm_stream_ctx_ts != NULL || sh->mpm_stream_ctx_tc != NULL) &&
+ !(sh->flags & SIG_GROUP_HEAD_MPM_STREAM_COPY)) {
+ if (sh->mpm_stream_ctx_ts != NULL) {
+ SCLogDebug("destroying mpm_stream_ctx %p (sh %p)", sh->mpm_stream_ctx_ts, sh);
+ if (!sh->mpm_stream_ctx_ts->global) {
+ mpm_table[sh->mpm_stream_ctx_ts->mpm_type].DestroyCtx(sh->mpm_stream_ctx_ts);
+ SCFree(sh->mpm_stream_ctx_ts);
+ }
+ /* ready for reuse */
+ sh->mpm_stream_ctx_ts = NULL;
+ }
+ if (sh->mpm_stream_ctx_tc != NULL) {
+ SCLogDebug("destroying mpm_stream_ctx %p (sh %p)", sh->mpm_stream_ctx_tc, sh);
+ if (!sh->mpm_stream_ctx_tc->global) {
+ mpm_table[sh->mpm_stream_ctx_tc->mpm_type].DestroyCtx(sh->mpm_stream_ctx_tc);
+ SCFree(sh->mpm_stream_ctx_tc);
+ }
+ /* ready for reuse */
+ sh->mpm_stream_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_hcbd_ctx_ts != NULL) {
+ if (sh->mpm_hcbd_ctx_ts != NULL) {
+ if (!sh->mpm_hcbd_ctx_ts->global) {
+ mpm_table[sh->mpm_hcbd_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hcbd_ctx_ts);
+ SCFree(sh->mpm_hcbd_ctx_ts);
+ }
+ sh->mpm_hcbd_ctx_ts = NULL;
+ }
+ }
+
+ if (sh->mpm_hsbd_ctx_tc != NULL) {
+ if (sh->mpm_hsbd_ctx_tc != NULL) {
+ if (!sh->mpm_hsbd_ctx_tc->global) {
+ mpm_table[sh->mpm_hsbd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hsbd_ctx_tc);
+ SCFree(sh->mpm_hsbd_ctx_tc);
+ }
+ sh->mpm_hsbd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_smtp_filedata_ctx_ts != NULL) {
+ if (sh->mpm_smtp_filedata_ctx_ts != NULL) {
+ if (!sh->mpm_smtp_filedata_ctx_ts->global) {
+ mpm_table[sh->mpm_smtp_filedata_ctx_ts->mpm_type].DestroyCtx(sh->mpm_smtp_filedata_ctx_ts);
+ SCFree(sh->mpm_smtp_filedata_ctx_ts);
+ }
+ sh->mpm_smtp_filedata_ctx_ts = NULL;
+ }
+ }
+
+ if (sh->mpm_hhd_ctx_ts != NULL || sh->mpm_hhd_ctx_tc != NULL) {
+ if (sh->mpm_hhd_ctx_ts != NULL) {
+ if (!sh->mpm_hhd_ctx_ts->global) {
+ mpm_table[sh->mpm_hhd_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hhd_ctx_ts);
+ SCFree(sh->mpm_hhd_ctx_ts);
+ }
+ sh->mpm_hhd_ctx_ts = NULL;
+ }
+ if (sh->mpm_hhd_ctx_tc != NULL) {
+ if (!sh->mpm_hhd_ctx_tc->global) {
+ mpm_table[sh->mpm_hhd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hhd_ctx_tc);
+ SCFree(sh->mpm_hhd_ctx_tc);
+ }
+ sh->mpm_hhd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_hrhd_ctx_ts != NULL || sh->mpm_hrhd_ctx_tc != NULL) {
+ if (sh->mpm_hrhd_ctx_ts != NULL) {
+ if (!sh->mpm_hrhd_ctx_ts->global) {
+ mpm_table[sh->mpm_hrhd_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hrhd_ctx_ts);
+ SCFree(sh->mpm_hrhd_ctx_ts);
+ }
+ sh->mpm_hrhd_ctx_ts = NULL;
+ }
+ if (sh->mpm_hrhd_ctx_tc != NULL) {
+ if (!sh->mpm_hrhd_ctx_tc->global) {
+ mpm_table[sh->mpm_hrhd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hrhd_ctx_tc);
+ SCFree(sh->mpm_hrhd_ctx_tc);
+ }
+ sh->mpm_hrhd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_hmd_ctx_ts != NULL) {
+ if (sh->mpm_hmd_ctx_ts != NULL) {
+ if (!sh->mpm_hmd_ctx_ts->global) {
+ mpm_table[sh->mpm_hmd_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hmd_ctx_ts);
+ SCFree(sh->mpm_hmd_ctx_ts);
+ }
+ sh->mpm_hmd_ctx_ts = NULL;
+ }
+ }
+
+ if (sh->mpm_hcd_ctx_ts != NULL || sh->mpm_hcd_ctx_tc != NULL) {
+ if (sh->mpm_hcd_ctx_ts != NULL) {
+ if (!sh->mpm_hcd_ctx_ts->global) {
+ mpm_table[sh->mpm_hcd_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hcd_ctx_ts);
+ SCFree(sh->mpm_hcd_ctx_ts);
+ }
+ sh->mpm_hcd_ctx_ts = NULL;
+ }
+ if (sh->mpm_hcd_ctx_tc != NULL) {
+ if (!sh->mpm_hcd_ctx_tc->global) {
+ mpm_table[sh->mpm_hcd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hcd_ctx_tc);
+ SCFree(sh->mpm_hcd_ctx_tc);
+ }
+ sh->mpm_hcd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_hrud_ctx_ts != NULL) {
+ if (sh->mpm_hrud_ctx_ts != NULL) {
+ if (!sh->mpm_hrud_ctx_ts->global) {
+ mpm_table[sh->mpm_hrud_ctx_ts->mpm_type].DestroyCtx(sh->mpm_hrud_ctx_ts);
+ SCFree(sh->mpm_hrud_ctx_ts);
+ }
+ sh->mpm_hrud_ctx_ts = NULL;
+ }
+ }
+
+ if (sh->mpm_hsmd_ctx_tc != NULL) {
+ if (sh->mpm_hsmd_ctx_tc != NULL) {
+ if (!sh->mpm_hsmd_ctx_tc->global) {
+ mpm_table[sh->mpm_hsmd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hsmd_ctx_tc);
+ SCFree(sh->mpm_hsmd_ctx_tc);
+ }
+ sh->mpm_hsmd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_hscd_ctx_tc != NULL) {
+ if (sh->mpm_hscd_ctx_tc != NULL) {
+ if (!sh->mpm_hscd_ctx_tc->global) {
+ mpm_table[sh->mpm_hscd_ctx_tc->mpm_type].DestroyCtx(sh->mpm_hscd_ctx_tc);
+ SCFree(sh->mpm_hscd_ctx_tc);
+ }
+ sh->mpm_hscd_ctx_tc = NULL;
+ }
+ }
+
+ if (sh->mpm_huad_ctx_ts != NULL) {
+ if (sh->mpm_huad_ctx_ts != NULL) {
+ if (!sh->mpm_huad_ctx_ts->global) {
+ mpm_table[sh->mpm_huad_ctx_ts->mpm_type].DestroyCtx(sh->mpm_huad_ctx_ts);
+ SCFree(sh->mpm_huad_ctx_ts);
+ }
+ sh->mpm_huad_ctx_ts = NULL;
+ }
+ }
+
+ /* dns query */
+ if (sh->mpm_dnsquery_ctx_ts != NULL) {
+ if (!sh->mpm_dnsquery_ctx_ts->global) {
+ mpm_table[sh->mpm_dnsquery_ctx_ts->mpm_type].DestroyCtx(sh->mpm_dnsquery_ctx_ts);
+ SCFree(sh->mpm_dnsquery_ctx_ts);
+ }
+ sh->mpm_dnsquery_ctx_ts = NULL;
+ }
+
+ return;
+}
+
+/** \brief Hash for looking up contents that are most used,
+ * always used, etc. */
+typedef struct ContentHash_ {
+ DetectContentData *ptr;
+ uint16_t cnt;
+ uint8_t use; /* use no matter what */
+} ContentHash;
+
+typedef struct UricontentHash_ {
+ DetectContentData *ptr;
+ uint16_t cnt;
+ uint8_t use; /* use no matter what */
+} UricontentHash;
+
+uint32_t ContentHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ ContentHash *ch = (ContentHash *)data;
+ DetectContentData *co = ch->ptr;
+ uint32_t hash = 0;
+ int i;
+ for (i = 0; i < co->content_len; i++) {
+ hash += co->content[i];
+ }
+ hash = hash % ht->array_size;
+ SCLogDebug("hash %" PRIu32 "", hash);
+ return hash;
+}
+
+uint32_t UricontentHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ UricontentHash *ch = (UricontentHash *)data;
+ DetectContentData *ud = ch->ptr;
+ uint32_t hash = 0;
+ int i;
+ for (i = 0; i < ud->content_len; i++) {
+ hash += ud->content[i];
+ }
+ hash = hash % ht->array_size;
+ SCLogDebug("hash %" PRIu32 "", hash);
+ return hash;
+}
+
+char ContentHashCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ ContentHash *ch1 = (ContentHash *)data1;
+ ContentHash *ch2 = (ContentHash *)data2;
+ DetectContentData *co1 = ch1->ptr;
+ DetectContentData *co2 = ch2->ptr;
+
+ if (co1->content_len == co2->content_len &&
+ ((co1->flags & DETECT_CONTENT_NOCASE) == (co2->flags & DETECT_CONTENT_NOCASE)) &&
+ SCMemcmp(co1->content, co2->content, co1->content_len) == 0)
+ return 1;
+
+ return 0;
+}
+
+char UricontentHashCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ UricontentHash *ch1 = (UricontentHash *)data1;
+ UricontentHash *ch2 = (UricontentHash *)data2;
+ DetectContentData *ud1 = ch1->ptr;
+ DetectContentData *ud2 = ch2->ptr;
+
+ if (ud1->content_len == ud2->content_len &&
+ ((ud1->flags & DETECT_CONTENT_NOCASE) == (ud2->flags & DETECT_CONTENT_NOCASE)) &&
+ SCMemcmp(ud1->content, ud2->content, ud1->content_len) == 0)
+ return 1;
+
+ return 0;
+}
+
+ContentHash *ContentHashAlloc(DetectContentData *ptr)
+{
+ ContentHash *ch = SCMalloc(sizeof(ContentHash));
+ if (unlikely(ch == NULL))
+ return NULL;
+
+ ch->ptr = ptr;
+ ch->cnt = 1;
+ ch->use = 0;
+
+ return ch;
+}
+
+UricontentHash *UricontentHashAlloc(DetectContentData *ptr)
+{
+ UricontentHash *ch = SCMalloc(sizeof(UricontentHash));
+ if (unlikely(ch == NULL))
+ return NULL;
+
+ ch->ptr = ptr;
+ ch->cnt = 1;
+ ch->use = 0;
+
+ return ch;
+}
+
+void ContentHashFree(void *ch)
+{
+ SCFree(ch);
+}
+
+void UricontentHashFree(void *ch)
+{
+ SCFree(ch);
+}
+
+/** \brief Predict a strength value for patterns
+ *
+ * Patterns with high character diversity score higher.
+ * Alpha chars score not so high
+ * Other printable + a few common codes a little higher
+ * Everything else highest.
+ * Longer patterns score better than short patters.
+ *
+ * \param pat pattern
+ * \param patlen length of the patternn
+ *
+ * \retval s pattern score
+ */
+uint32_t PatternStrength(uint8_t *pat, uint16_t patlen)
+{
+ uint8_t a[256];
+ memset(&a, 0 ,sizeof(a));
+
+ uint32_t s = 0;
+ uint16_t u = 0;
+ for (u = 0; u < patlen; u++) {
+ if (a[pat[u]] == 0) {
+ if (isalpha(pat[u]))
+ s += 3;
+ else if (isprint(pat[u]) || pat[u] == 0x00 || pat[u] == 0x01 || pat[u] == 0xFF)
+ s += 4;
+ else
+ s += 6;
+
+ a[pat[u]] = 1;
+ } else {
+ s++;
+ }
+ }
+
+ return s;
+}
+
+static void PopulateMpmHelperAddPatternToPktCtx(MpmCtx *mpm_ctx,
+ DetectContentData *cd,
+ Signature *s, uint8_t flags,
+ int chop)
+{
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (chop) {
+ MpmAddPatternCI(mpm_ctx,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ } else {
+ MpmAddPatternCI(mpm_ctx,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ } else {
+ if (chop) {
+ MpmAddPatternCS(mpm_ctx,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ } else {
+ MpmAddPatternCS(mpm_ctx,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ }
+
+ return;
+}
+
+static void PopulateMpmAddPatternToMpm(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh, Signature *s,
+ SigMatch *mpm_sm)
+{
+ s->mpm_sm = mpm_sm;
+
+ if (mpm_sm == NULL) {
+ SCLogDebug("%"PRIu32" no mpm pattern selected", s->id);
+ return;
+ }
+
+ int sm_list = SigMatchListSMBelongsTo(s, mpm_sm);
+ if (sm_list == -1)
+ BUG_ON(SigMatchListSMBelongsTo(s, mpm_sm) == -1);
+
+ uint8_t flags = 0;
+
+ DetectContentData *cd = NULL;
+
+ switch (sm_list) {
+ case DETECT_SM_LIST_PMATCH:
+ {
+ cd = (DetectContentData *)mpm_sm->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE) &&
+ cd->content_len == cd->fp_chop_len) {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+
+ /* add the content to the "packet" mpm */
+ if (SignatureHasPacketContent(s)) {
+ if (s->proto.proto[6 / 8] & 1 << (6 % 8)) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_tcp_ctx_ts,
+ cd, s, flags, 1);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_tcp_ctx_tc,
+ cd, s, flags, 1);
+ }
+ }
+ if (s->proto.proto[17 / 8] & 1 << (17 % 8)) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_udp_ctx_ts,
+ cd, s, flags, 1);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_udp_ctx_tc,
+ cd, s, flags, 1);
+ }
+ }
+ int i;
+ for (i = 0; i < 256; i++) {
+ if (i == 6 || i == 17)
+ continue;
+ if (s->proto.proto[i / 8] & (1 << (i % 8))) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_other_ctx,
+ cd, s, flags, 1);
+ break;
+ }
+ }
+ /* tell matcher we are inspecting packet */
+ s->flags |= SIG_FLAG_MPM_PACKET;
+ s->mpm_pattern_id_div_8 = cd->id / 8;
+ s->mpm_pattern_id_mod_8 = 1 << (cd->id % 8);
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("flagging sig %"PRIu32" to be looking for negated mpm", s->id);
+ s->flags |= SIG_FLAG_MPM_PACKET_NEG;
+ }
+ }
+ if (SignatureHasStreamContent(s)) {
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ MpmAddPatternCI(sgh->mpm_stream_ctx_ts,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ MpmAddPatternCI(sgh->mpm_stream_ctx_tc,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ } else {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ MpmAddPatternCS(sgh->mpm_stream_ctx_ts,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ MpmAddPatternCS(sgh->mpm_stream_ctx_tc,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ }
+ /* tell matcher we are inspecting stream */
+ s->flags |= SIG_FLAG_MPM_STREAM;
+ s->mpm_pattern_id_div_8 = cd->id / 8;
+ s->mpm_pattern_id_mod_8 = 1 << (cd->id % 8);
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("flagging sig %"PRIu32" to be looking for negated mpm", s->id);
+ s->flags |= SIG_FLAG_MPM_STREAM_NEG;
+ }
+ }
+ } else {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE)) {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+
+ if (SignatureHasPacketContent(s)) {
+ /* add the content to the "packet" mpm */
+ if (s->proto.proto[6 / 8] & 1 << (6 % 8)) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_tcp_ctx_ts,
+ cd, s, flags, 0);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_tcp_ctx_tc,
+ cd, s, flags, 0);
+ }
+ }
+ if (s->proto.proto[17 / 8] & 1 << (17 % 8)) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_udp_ctx_ts,
+ cd, s, flags, 0);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_udp_ctx_tc,
+ cd, s, flags, 0);
+ }
+ }
+ int i;
+ for (i = 0; i < 256; i++) {
+ if (i == 6 || i == 17)
+ continue;
+ if (s->proto.proto[i / 8] & (1 << (i % 8))) {
+ PopulateMpmHelperAddPatternToPktCtx(sgh->mpm_proto_other_ctx,
+ cd, s, flags, 0);
+ break;
+ }
+ }
+ /* tell matcher we are inspecting packet */
+ s->flags |= SIG_FLAG_MPM_PACKET;
+ s->mpm_pattern_id_div_8 = cd->id / 8;
+ s->mpm_pattern_id_mod_8 = 1 << (cd->id % 8);
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("flagging sig %"PRIu32" to be looking for negated mpm", s->id);
+ s->flags |= SIG_FLAG_MPM_PACKET_NEG;
+ }
+ }
+ if (SignatureHasStreamContent(s)) {
+ /* add the content to the "packet" mpm */
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ MpmAddPatternCI(sgh->mpm_stream_ctx_ts,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ MpmAddPatternCI(sgh->mpm_stream_ctx_tc,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ } else {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ MpmAddPatternCS(sgh->mpm_stream_ctx_ts,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ MpmAddPatternCS(sgh->mpm_stream_ctx_tc,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ }
+ /* tell matcher we are inspecting stream */
+ s->flags |= SIG_FLAG_MPM_STREAM;
+ s->mpm_pattern_id_div_8 = cd->id / 8;
+ s->mpm_pattern_id_mod_8 = 1 << (cd->id % 8);
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("flagging sig %"PRIu32" to be looking for negated mpm", s->id);
+ s->flags |= SIG_FLAG_MPM_STREAM_NEG;
+ }
+ }
+ }
+ if (SignatureHasPacketContent(s)) {
+ sgh->flags |= SIG_GROUP_HEAD_MPM_PACKET;
+ }
+ if (SignatureHasStreamContent(s)) {
+ sgh->flags |= SIG_GROUP_HEAD_MPM_STREAM;
+ }
+
+ break;
+ } /* case DETECT_CONTENT */
+
+ case DETECT_SM_LIST_UMATCH:
+ case DETECT_SM_LIST_HRUDMATCH:
+ case DETECT_SM_LIST_HCBDMATCH:
+ case DETECT_SM_LIST_FILEDATA:
+ case DETECT_SM_LIST_HHDMATCH:
+ case DETECT_SM_LIST_HRHDMATCH:
+ case DETECT_SM_LIST_HMDMATCH:
+ case DETECT_SM_LIST_HCDMATCH:
+ case DETECT_SM_LIST_HSMDMATCH:
+ case DETECT_SM_LIST_HSCDMATCH:
+ case DETECT_SM_LIST_HUADMATCH:
+ case DETECT_SM_LIST_HHHDMATCH:
+ case DETECT_SM_LIST_HRHHDMATCH:
+ case DETECT_SM_LIST_DNSQUERYNAME_MATCH:
+ {
+ MpmCtx *mpm_ctx_ts = NULL;
+ MpmCtx *mpm_ctx_tc = NULL;
+
+ cd = (DetectContentData *)mpm_sm->ctx;
+
+ if (sm_list == DETECT_SM_LIST_UMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_uri_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_URI;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HCBDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hcbd_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HCBD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_FILEDATA) {
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ mpm_ctx_tc = sgh->mpm_hsbd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HSBD;
+ }
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ mpm_ctx_ts = sgh->mpm_smtp_filedata_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_FD_SMTP;
+ }
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HHDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hhd_ctx_ts;
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = sgh->mpm_hhd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HHD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HRHDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hrhd_ctx_ts;
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = sgh->mpm_hrhd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HRHD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HMDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hmd_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HMD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HCDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hcd_ctx_ts;
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = sgh->mpm_hcd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HCD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HRUDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hrud_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HRUD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HSMDMATCH) {
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = sgh->mpm_hsmd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HSMD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HSCDMATCH) {
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = sgh->mpm_hscd_ctx_tc;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HSCD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HUADMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_huad_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HUAD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HHHDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hhhd_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HHHD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_HRHHDMATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_hrhhd_ctx_ts;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_HRHHD;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ } else if (sm_list == DETECT_SM_LIST_DNSQUERYNAME_MATCH) {
+ if (s->flags & SIG_FLAG_TOSERVER)
+ mpm_ctx_ts = sgh->mpm_dnsquery_ctx_ts;
+ if (s->flags & SIG_FLAG_TOCLIENT)
+ mpm_ctx_tc = NULL;
+ sgh->flags |= SIG_GROUP_HEAD_MPM_DNSQUERY;
+ s->flags |= SIG_FLAG_MPM_APPLAYER;
+ if (cd->flags & DETECT_CONTENT_NEGATED)
+ s->flags |= SIG_FLAG_MPM_APPLAYER_NEG;
+ }
+
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE) &&
+ cd->content_len == cd->fp_chop_len) {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+
+ /* add the content to the mpm */
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (mpm_ctx_ts != NULL) {
+ MpmAddPatternCI(mpm_ctx_ts,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (mpm_ctx_tc != NULL) {
+ MpmAddPatternCI(mpm_ctx_tc,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ } else {
+ if (mpm_ctx_ts != NULL) {
+ MpmAddPatternCS(mpm_ctx_ts,
+ cd->content + cd->fp_chop_offset,
+ cd->fp_chop_len,
+ 0, 0, cd->id, s->num, flags);
+ }
+ if (mpm_ctx_tc != NULL) {
+ MpmAddPatternCS(mpm_ctx_tc,
+ cd->content + cd->fp_chop_offset,
+ cd->fp_chop_len,
+ 0, 0, cd->id, s->num, flags);
+ }
+ }
+ } else {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE)) {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+
+ /* add the content to the "uri" mpm */
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (mpm_ctx_ts != NULL) {
+ MpmAddPatternCI(mpm_ctx_ts,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (mpm_ctx_tc != NULL) {
+ MpmAddPatternCI(mpm_ctx_tc,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ } else {
+ if (mpm_ctx_ts != NULL) {
+ MpmAddPatternCS(mpm_ctx_ts,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ if (mpm_ctx_tc != NULL) {
+ MpmAddPatternCS(mpm_ctx_tc,
+ cd->content, cd->content_len,
+ 0, 0,
+ cd->id, s->num, flags);
+ }
+ }
+ }
+ /* tell matcher we are inspecting uri */
+ s->mpm_pattern_id_div_8 = cd->id / 8;
+ s->mpm_pattern_id_mod_8 = 1 << (cd->id % 8);
+
+ break;
+ }
+ } /* switch (mpm_sm->type) */
+
+ SCLogDebug("%"PRIu32" adding cd->id %"PRIu32" to the mpm phase "
+ "(s->num %"PRIu32")", s->id,
+ ((DetectContentData *)mpm_sm->ctx)->id, s->num);
+
+ return;
+}
+
+SigMatch *RetrieveFPForSig(Signature *s)
+{
+ SigMatch *mpm_sm = NULL, *sm = NULL;
+ uint8_t has_non_negated_non_stream_pattern = 0;
+
+ if (s->mpm_sm != NULL)
+ return s->mpm_sm;
+
+ int list_id;
+ for (list_id = 0 ; list_id < DETECT_SM_LIST_MAX; list_id++) {
+ /* we have no keywords that support fp in this Signature sm list */
+ if (!FastPatternSupportEnabledForSigMatchList(list_id))
+ continue;
+
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ /* this keyword isn't registered for fp support */
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN)
+ return sm;
+ if (!(cd->flags & DETECT_CONTENT_NEGATED) &&
+ (list_id != DETECT_SM_LIST_PMATCH) &&
+ (list_id != DETECT_SM_LIST_HMDMATCH) &&
+ (list_id != DETECT_SM_LIST_HSMDMATCH) &&
+ (list_id != DETECT_SM_LIST_HSCDMATCH)) {
+ has_non_negated_non_stream_pattern = 1;
+ }
+ }
+ }
+
+ int max_len = 0;
+ int max_len_negated = 0;
+ int max_len_non_negated = 0;
+ for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+ if (!FastPatternSupportEnabledForSigMatchList(list_id))
+ continue;
+
+ if (has_non_negated_non_stream_pattern &&
+ ((list_id == DETECT_SM_LIST_PMATCH) ||
+ (list_id == DETECT_SM_LIST_HMDMATCH) ||
+ (list_id == DETECT_SM_LIST_HSMDMATCH) ||
+ (list_id == DETECT_SM_LIST_HSCDMATCH))) {
+ continue;
+ }
+
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ if (max_len_negated < cd->content_len)
+ max_len_negated = cd->content_len;
+ } else {
+ if (max_len_non_negated < cd->content_len)
+ max_len_non_negated = cd->content_len;
+ }
+ }
+ }
+
+ int skip_negated_content = 0;
+ if (max_len_non_negated == 0) {
+ max_len = max_len_negated;
+ skip_negated_content = 0;
+ } else {
+ max_len = max_len_non_negated;
+ skip_negated_content = 1;
+ }
+
+ for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+ if (!FastPatternSupportEnabledForSigMatchList(list_id))
+ continue;
+
+ if (has_non_negated_non_stream_pattern &&
+ ((list_id == DETECT_SM_LIST_PMATCH) ||
+ (list_id == DETECT_SM_LIST_HMDMATCH) ||
+ (list_id == DETECT_SM_LIST_HSMDMATCH) ||
+ (list_id == DETECT_SM_LIST_HSCDMATCH))) {
+ continue;
+ }
+
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ if (cd->content_len < max_len)
+ continue;
+
+ if (mpm_sm == NULL) {
+ mpm_sm = sm;
+ } else {
+ DetectContentData *data1 = (DetectContentData *)sm->ctx;
+ DetectContentData *data2 = (DetectContentData *)mpm_sm->ctx;
+ uint32_t ls = PatternStrength(data1->content, data1->content_len);
+ uint32_t ss = PatternStrength(data2->content, data2->content_len);
+ if (ls > ss) {
+ mpm_sm = sm;
+ } else if (ls == ss) {
+ /* if 2 patterns are of equal strength, we pick the longest */
+ if (data1->content_len > data2->content_len)
+ mpm_sm = sm;
+ } else {
+ SCLogDebug("sticking with mpm_sm");
+ }
+ } /* else - if (mpm == NULL) */
+ } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+ } /* for ( ; list_id < DETECT_SM_LIST_MAX; list_id++) */
+
+ return mpm_sm;
+}
+
+SigMatch *RetrieveFPForSigV2(Signature *s)
+{
+ if (s->mpm_sm != NULL)
+ return s->mpm_sm;
+
+
+ SigMatch *mpm_sm = NULL, *sm = NULL;
+ int nn_sm_list[DETECT_SM_LIST_MAX];
+ int n_sm_list[DETECT_SM_LIST_MAX];
+ memset(nn_sm_list, 0, sizeof(nn_sm_list));
+ memset(n_sm_list, 0, sizeof(n_sm_list));
+ int count_nn_sm_list = 0;
+ int count_n_sm_list = 0;
+ int list_id;
+
+ for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+ if (!FastPatternSupportEnabledForSigMatchList(list_id))
+ continue;
+
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if ((cd->flags & DETECT_CONTENT_FAST_PATTERN))
+ return sm;
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ n_sm_list[list_id] = 1;
+ count_n_sm_list++;
+ } else {
+ nn_sm_list[list_id] = 1;
+ count_nn_sm_list++;
+ }
+ } /* for */
+ } /* for */
+
+ int *curr_sm_list = NULL;
+ int skip_negated_content = 1;
+ if (count_nn_sm_list > 0) {
+ curr_sm_list = nn_sm_list;
+ } else if (count_n_sm_list > 0) {
+ curr_sm_list = n_sm_list;
+ skip_negated_content = 0;
+ } else {
+ return NULL;
+ }
+
+ int final_sm_list[DETECT_SM_LIST_MAX];
+ int count_final_sm_list = 0;
+ int priority;
+
+ SCFPSupportSMList *tmp = sm_fp_support_smlist_list;
+ while (tmp != NULL) {
+ for (priority = tmp->priority;
+ tmp != NULL && priority == tmp->priority;
+ tmp = tmp->next) {
+
+ if (curr_sm_list[tmp->list_id] == 0)
+ continue;
+ final_sm_list[count_final_sm_list++] = tmp->list_id;
+ }
+ if (count_final_sm_list != 0)
+ break;
+ }
+
+ BUG_ON(count_final_sm_list == 0);
+
+ int max_len = 0;
+ int i;
+ for (i = 0; i < count_final_sm_list; i++) {
+ for (sm = s->sm_lists[final_sm_list[i]]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* skip_negated_content is only set if there's absolutely no
+ * non-negated content present in the sig */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ if (max_len < cd->content_len)
+ max_len = cd->content_len;
+ }
+ }
+
+ for (i = 0; i < count_final_sm_list; i++) {
+ for (sm = s->sm_lists[final_sm_list[i]]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* skip_negated_content is only set if there's absolutely no
+ * non-negated content present in the sig */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ if (cd->content_len != max_len)
+ continue;
+
+ if (mpm_sm == NULL) {
+ mpm_sm = sm;
+ } else {
+ DetectContentData *data1 = (DetectContentData *)sm->ctx;
+ DetectContentData *data2 = (DetectContentData *)mpm_sm->ctx;
+ uint32_t ls = PatternStrength(data1->content, data1->content_len);
+ uint32_t ss = PatternStrength(data2->content, data2->content_len);
+ if (ls > ss) {
+ mpm_sm = sm;
+ } else if (ls == ss) {
+ /* if 2 patterns are of equal strength, we pick the longest */
+ if (data1->content_len > data2->content_len)
+ mpm_sm = sm;
+ } else {
+ SCLogDebug("sticking with mpm_sm");
+ }
+ } /* else - if */
+ } /* for */
+ } /* for */
+
+ return mpm_sm;
+}
+
+/**
+ * \internal
+ * \brief Setup the mpm content.
+ *
+ * \param de_ctx Pointer to the detect engine context.
+ * \param sgh Pointer to the signature group head against which we are
+ * adding patterns to the mpm ctx.
+ *
+ * \retval 0 Always.
+ */
+static int PatternMatchPreparePopulateMpm(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ uint32_t sig = 0;
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ Signature *s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+ PopulateMpmAddPatternToMpm(de_ctx, sgh, s, s->mpm_sm);
+ } /* for (sig = 0; sig < sgh->sig_cnt; sig++) */
+
+ return 0;
+}
+
+/** \brief Prepare the pattern matcher ctx in a sig group head.
+ *
+ * \todo determine if a content match can set the 'single' flag
+ * \todo do error checking
+ * \todo rewrite the COPY stuff
+ */
+int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh)
+{
+ Signature *s = NULL;
+ uint32_t has_co_packet = 0; /**< our sgh has packet payload inspecting content */
+ uint32_t has_co_stream = 0; /**< our sgh has stream inspecting content */
+ uint32_t has_co_uri = 0; /**< our sgh has uri inspecting content */
+ /* used to indicate if sgh has atleast one sig with http_client_body */
+ uint32_t has_co_hcbd = 0;
+ /* used to indicate if sgh has atleast one sig with http_server_body */
+ uint32_t has_co_hsbd = 0;
+ /* used to indicate if sgh has smtp file_data inspecting content */
+ uint32_t has_co_smtp = 0;
+ /* used to indicate if sgh has atleast one sig with http_header */
+ uint32_t has_co_hhd = 0;
+ /* used to indicate if sgh has atleast one sig with http_raw_header */
+ uint32_t has_co_hrhd = 0;
+ /* used to indicate if sgh has atleast one sig with http_method */
+ uint32_t has_co_hmd = 0;
+ /* used to indicate if sgh has atleast one sig with http_cookie */
+ uint32_t has_co_hcd = 0;
+ /* used to indicate if sgh has atleast one sig with http_raw_uri */
+ uint32_t has_co_hrud = 0;
+ /* used to indicate if sgh has atleast one sig with http_stat_msg */
+ uint32_t has_co_hsmd = 0;
+ /* used to indicate if sgh has atleast one sig with http_stat_code */
+ uint32_t has_co_hscd = 0;
+ /* used to indicate if sgh has atleast one sig with http_user_agent */
+ uint32_t has_co_huad = 0;
+ /* used to indicate if sgh has atleast one sig with http_host */
+ uint32_t has_co_hhhd = 0;
+ /* used to indicate if sgh has atleast one sig with http_raw_host */
+ uint32_t has_co_hrhhd = 0;
+ //uint32_t cnt = 0;
+ uint32_t sig = 0;
+ /* sgh has at least one sig with dns_query */
+ int has_co_dnsquery = 0;
+
+ /* see if this head has content and/or uricontent */
+ for (sig = 0; sig < sh->sig_cnt; sig++) {
+ s = sh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureHasPacketContent(s) == 1) {
+ has_co_packet = 1;
+ }
+ if (SignatureHasStreamContent(s) == 1) {
+ has_co_stream = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL) {
+ has_co_uri = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL) {
+ has_co_hcbd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL) {
+ if (s->alproto == ALPROTO_SMTP)
+ has_co_smtp = 1;
+ else if (s->alproto == ALPROTO_HTTP)
+ has_co_hsbd = 1;
+ else if (s->alproto == ALPROTO_UNKNOWN) {
+ has_co_smtp = 1;
+ has_co_hsbd = 1;
+ }
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] != NULL) {
+ has_co_hhd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL) {
+ has_co_hrhd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL) {
+ has_co_hmd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HCDMATCH] != NULL) {
+ has_co_hcd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL) {
+ has_co_hrud = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL) {
+ has_co_hsmd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL) {
+ has_co_hscd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL) {
+ has_co_huad = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL) {
+ has_co_hhhd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHHDMATCH] != NULL) {
+ has_co_hrhhd = 1;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_DNSQUERYNAME_MATCH] != NULL) {
+ has_co_dnsquery = 1;
+ }
+ }
+
+ /* intialize contexes */
+ if (has_co_packet) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_proto_tcp_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 0);
+ sh->mpm_proto_tcp_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 1);
+ } else {
+ sh->mpm_proto_tcp_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_proto_tcp_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_proto_tcp_ctx_ts == NULL || sh->mpm_proto_tcp_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_proto_tcp_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+ MpmInitCtx(sh->mpm_proto_tcp_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_proto_tcp_ctx_tc, de_ctx->mpm_matcher);
+
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_proto_udp_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 0);
+ sh->mpm_proto_udp_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 1);
+ } else {
+ sh->mpm_proto_udp_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_proto_udp_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_proto_udp_ctx_ts == NULL || sh->mpm_proto_udp_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_proto_udp_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+ MpmInitCtx(sh->mpm_proto_udp_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_proto_udp_ctx_tc, de_ctx->mpm_matcher);
+
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_proto_other_ctx =
+ MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_other_packet, 0);
+ } else {
+ sh->mpm_proto_other_ctx =
+ MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_proto_other_ctx == NULL) {
+ SCLogDebug("sh->mpm_proto_other_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+ MpmInitCtx(sh->mpm_proto_other_ctx, de_ctx->mpm_matcher);
+ } /* if (has_co_packet) */
+
+ if (has_co_stream) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_stream_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 0);
+ sh->mpm_stream_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 1);
+ } else {
+ sh->mpm_stream_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_stream_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_stream_ctx_tc == NULL || sh->mpm_stream_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_stream_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+ MpmInitCtx(sh->mpm_stream_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_stream_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_uri) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_uri_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_uri, 0);
+ } else {
+ sh->mpm_uri_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_uri_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_uri_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_uri_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hcbd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hcbd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcbd, 0);
+ } else {
+ sh->mpm_hcbd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_hcbd_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hcbd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hcbd_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hsbd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hsbd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsbd, 1);
+ } else {
+ sh->mpm_hsbd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hsbd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hsbd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hsbd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_smtp) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_smtp_filedata_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_smtp, 0);
+ } else {
+ sh->mpm_smtp_filedata_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_smtp_filedata_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_smtp_filedata_ctx_ts == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_smtp_filedata_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hhd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 0);
+ sh->mpm_hhd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 1);
+ } else {
+ sh->mpm_hhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_hhd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hhd_ctx_ts == NULL || sh->mpm_hhd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hhd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hhd_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_hhd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hrhd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hrhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 0);
+ sh->mpm_hrhd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 1);
+ } else {
+ sh->mpm_hrhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_hrhd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hrhd_ctx_ts == NULL || sh->mpm_hrhd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hrhd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hrhd_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_hrhd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hmd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hmd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hmd, 0);
+ } else {
+ sh->mpm_hmd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_hmd_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hmd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hmd_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hcd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hcd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 0);
+ sh->mpm_hcd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 1);
+ } else {
+ sh->mpm_hcd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ sh->mpm_hcd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hcd_ctx_ts == NULL || sh->mpm_hcd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hcd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hcd_ctx_ts, de_ctx->mpm_matcher);
+ MpmInitCtx(sh->mpm_hcd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hrud) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hrud_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrud, 0);
+ } else {
+ sh->mpm_hrud_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_hrud_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hrud_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hrud_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hsmd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hsmd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsmd, 1);
+ } else {
+ sh->mpm_hsmd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hsmd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hsmd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hsmd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hscd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hscd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hscd, 1);
+ } else {
+ sh->mpm_hscd_ctx_tc = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 1);
+ }
+ if (sh->mpm_hscd_ctx_tc == NULL) {
+ SCLogDebug("sh->mpm_hscd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hscd_ctx_tc, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_huad) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_huad_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_huad, 0);
+ } else {
+ sh->mpm_huad_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_huad_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_huad_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_huad_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hhhd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hhhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhhd, 0);
+ } else {
+ sh->mpm_hhhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_hhhd_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hhhd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hhhd_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_hrhhd) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_hrhhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhhd, 0);
+ } else {
+ sh->mpm_hrhhd_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_hrhhd_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hrhhd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_hrhhd_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_dnsquery) {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ sh->mpm_dnsquery_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_dnsquery, 0);
+ } else {
+ sh->mpm_dnsquery_ctx_ts = MpmFactoryGetMpmCtxForProfile(de_ctx, MPM_CTX_FACTORY_UNIQUE_CONTEXT, 0);
+ }
+ if (sh->mpm_dnsquery_ctx_ts == NULL) {
+ SCLogDebug("sh->mpm_hrhhd_ctx == NULL. This should never happen");
+ exit(EXIT_FAILURE);
+ }
+
+ MpmInitCtx(sh->mpm_dnsquery_ctx_ts, de_ctx->mpm_matcher);
+ }
+
+ if (has_co_packet ||
+ has_co_stream ||
+ has_co_uri ||
+ has_co_hcbd ||
+ has_co_hsbd ||
+ has_co_smtp ||
+ has_co_hhd ||
+ has_co_hrhd ||
+ has_co_hmd ||
+ has_co_hcd ||
+ has_co_hsmd ||
+ has_co_hscd ||
+ has_co_hrud ||
+ has_co_huad ||
+ has_co_hhhd ||
+ has_co_hrhhd ||
+ has_co_dnsquery)
+ {
+
+ PatternMatchPreparePopulateMpm(de_ctx, sh);
+
+ //if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (sh->mpm_proto_tcp_ctx_ts != NULL) {
+ if (sh->mpm_proto_tcp_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_tcp_ctx_ts);
+ sh->mpm_proto_tcp_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_proto_tcp_ctx_ts->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_proto_tcp_ctx_ts->mpm_type].
+ Prepare(sh->mpm_proto_tcp_ctx_ts);
+ }
+ }
+ }
+ }
+ if (sh->mpm_proto_tcp_ctx_tc != NULL) {
+ if (sh->mpm_proto_tcp_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_tcp_ctx_tc);
+ sh->mpm_proto_tcp_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_proto_tcp_ctx_tc->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_proto_tcp_ctx_tc->mpm_type].
+ Prepare(sh->mpm_proto_tcp_ctx_tc);
+ }
+ }
+ }
+ }
+
+ if (sh->mpm_proto_udp_ctx_ts != NULL) {
+ if (sh->mpm_proto_udp_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_udp_ctx_ts);
+ sh->mpm_proto_udp_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_proto_udp_ctx_ts->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_proto_udp_ctx_ts->mpm_type].
+ Prepare(sh->mpm_proto_udp_ctx_ts);
+ }
+ }
+ }
+ }
+ if (sh->mpm_proto_udp_ctx_tc != NULL) {
+ if (sh->mpm_proto_udp_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_udp_ctx_tc);
+ sh->mpm_proto_udp_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_proto_udp_ctx_tc->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_proto_udp_ctx_tc->mpm_type].
+ Prepare(sh->mpm_proto_udp_ctx_tc);
+ }
+ }
+ }
+ }
+
+ if (sh->mpm_proto_other_ctx != NULL) {
+ if (sh->mpm_proto_other_ctx->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_other_ctx);
+ sh->mpm_proto_other_ctx = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_proto_other_ctx->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_proto_other_ctx->mpm_type].
+ Prepare(sh->mpm_proto_other_ctx);
+ }
+ }
+ }
+ }
+
+ if (sh->mpm_stream_ctx_ts != NULL) {
+ if (sh->mpm_stream_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_stream_ctx_ts);
+ sh->mpm_stream_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_stream_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_stream_ctx_ts->mpm_type].Prepare(sh->mpm_stream_ctx_ts);
+ }
+ }
+ }
+ if (sh->mpm_stream_ctx_tc != NULL) {
+ if (sh->mpm_stream_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_stream_ctx_tc);
+ sh->mpm_stream_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_stream_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_stream_ctx_tc->mpm_type].Prepare(sh->mpm_stream_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_uri_ctx_ts != NULL) {
+ if (sh->mpm_uri_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_uri_ctx_ts);
+ sh->mpm_uri_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_uri_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_uri_ctx_ts->mpm_type].Prepare(sh->mpm_uri_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hcbd_ctx_ts != NULL) {
+ if (sh->mpm_hcbd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcbd_ctx_ts);
+ sh->mpm_hcbd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hcbd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hcbd_ctx_ts->mpm_type].Prepare(sh->mpm_hcbd_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hsbd_ctx_tc != NULL) {
+ if (sh->mpm_hsbd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hsbd_ctx_tc);
+ sh->mpm_hsbd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hsbd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hsbd_ctx_tc->mpm_type].Prepare(sh->mpm_hsbd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_smtp_filedata_ctx_ts != NULL) {
+ if (sh->mpm_smtp_filedata_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_smtp_filedata_ctx_ts);
+ sh->mpm_smtp_filedata_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_smtp_filedata_ctx_ts->mpm_type].Prepare != NULL) {
+ mpm_table[sh->mpm_smtp_filedata_ctx_ts->mpm_type].Prepare(sh->mpm_smtp_filedata_ctx_ts);
+ }
+ }
+ }
+ }
+
+ if (sh->mpm_hhd_ctx_ts != NULL) {
+ if (sh->mpm_hhd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhd_ctx_ts);
+ sh->mpm_hhd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hhd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hhd_ctx_ts->mpm_type].Prepare(sh->mpm_hhd_ctx_ts);
+ }
+ }
+ }
+ if (sh->mpm_hhd_ctx_tc != NULL) {
+ if (sh->mpm_hhd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhd_ctx_tc);
+ sh->mpm_hhd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hhd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hhd_ctx_tc->mpm_type].Prepare(sh->mpm_hhd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_hrhd_ctx_ts != NULL) {
+ if (sh->mpm_hrhd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhd_ctx_ts);
+ sh->mpm_hrhd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hrhd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hrhd_ctx_ts->mpm_type].Prepare(sh->mpm_hrhd_ctx_ts);
+ }
+ }
+ }
+ if (sh->mpm_hrhd_ctx_tc != NULL) {
+ if (sh->mpm_hrhd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhd_ctx_tc);
+ sh->mpm_hrhd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hrhd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hrhd_ctx_tc->mpm_type].Prepare(sh->mpm_hrhd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_hmd_ctx_ts != NULL) {
+ if (sh->mpm_hmd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hmd_ctx_ts);
+ sh->mpm_hmd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hmd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hmd_ctx_ts->mpm_type].Prepare(sh->mpm_hmd_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hcd_ctx_ts != NULL) {
+ if (sh->mpm_hcd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcd_ctx_ts);
+ sh->mpm_hcd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hcd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hcd_ctx_ts->mpm_type].Prepare(sh->mpm_hcd_ctx_ts);
+ }
+ }
+ }
+ if (sh->mpm_hcd_ctx_tc != NULL) {
+ if (sh->mpm_hcd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcd_ctx_tc);
+ sh->mpm_hcd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hcd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hcd_ctx_tc->mpm_type].Prepare(sh->mpm_hcd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_hrud_ctx_ts != NULL) {
+ if (sh->mpm_hrud_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrud_ctx_ts);
+ sh->mpm_hrud_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hrud_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hrud_ctx_ts->mpm_type].Prepare(sh->mpm_hrud_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hsmd_ctx_tc != NULL) {
+ if (sh->mpm_hsmd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hsmd_ctx_tc);
+ sh->mpm_hsmd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hsmd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hsmd_ctx_tc->mpm_type].Prepare(sh->mpm_hsmd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_hscd_ctx_tc != NULL) {
+ if (sh->mpm_hscd_ctx_tc->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hscd_ctx_tc);
+ sh->mpm_hscd_ctx_tc = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hscd_ctx_tc->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hscd_ctx_tc->mpm_type].Prepare(sh->mpm_hscd_ctx_tc);
+ }
+ }
+ }
+
+ if (sh->mpm_huad_ctx_ts != NULL) {
+ if (sh->mpm_huad_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_huad_ctx_ts);
+ sh->mpm_huad_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_huad_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_huad_ctx_ts->mpm_type].Prepare(sh->mpm_huad_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hhhd_ctx_ts != NULL) {
+ if (sh->mpm_hhhd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhhd_ctx_ts);
+ sh->mpm_hhhd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hhhd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hhhd_ctx_ts->mpm_type].Prepare(sh->mpm_hhhd_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_hrhhd_ctx_ts != NULL) {
+ if (sh->mpm_hrhhd_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhhd_ctx_ts);
+ sh->mpm_hrhhd_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_hrhhd_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_hrhhd_ctx_ts->mpm_type].Prepare(sh->mpm_hrhhd_ctx_ts);
+ }
+ }
+ }
+
+ if (sh->mpm_dnsquery_ctx_ts != NULL) {
+ if (sh->mpm_dnsquery_ctx_ts->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_dnsquery_ctx_ts);
+ sh->mpm_dnsquery_ctx_ts = NULL;
+ } else {
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) {
+ if (mpm_table[sh->mpm_dnsquery_ctx_ts->mpm_type].Prepare != NULL)
+ mpm_table[sh->mpm_dnsquery_ctx_ts->mpm_type].Prepare(sh->mpm_dnsquery_ctx_ts);
+ }
+ }
+ }
+ //} /* if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL) */
+ } else {
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_other_ctx);
+ sh->mpm_proto_other_ctx = NULL;
+
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_tcp_ctx_ts);
+ sh->mpm_proto_tcp_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_udp_ctx_ts);
+ sh->mpm_proto_udp_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_stream_ctx_ts);
+ sh->mpm_stream_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_uri_ctx_ts);
+ sh->mpm_uri_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcbd_ctx_ts);
+ sh->mpm_hcbd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhd_ctx_ts);
+ sh->mpm_hhd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhd_ctx_ts);
+ sh->mpm_hrhd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hmd_ctx_ts);
+ sh->mpm_hmd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcd_ctx_ts);
+ sh->mpm_hcd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrud_ctx_ts);
+ sh->mpm_hrud_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_huad_ctx_ts);
+ sh->mpm_huad_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhhd_ctx_ts);
+ sh->mpm_hhhd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhhd_ctx_ts);
+ sh->mpm_hrhhd_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_dnsquery_ctx_ts);
+ sh->mpm_dnsquery_ctx_ts = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_smtp_filedata_ctx_ts);
+ sh->mpm_smtp_filedata_ctx_ts = NULL;
+
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_tcp_ctx_tc);
+ sh->mpm_proto_tcp_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_proto_udp_ctx_tc);
+ sh->mpm_proto_udp_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_stream_ctx_tc);
+ sh->mpm_stream_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hhd_ctx_tc);
+ sh->mpm_hhd_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hrhd_ctx_tc);
+ sh->mpm_hrhd_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hcd_ctx_tc);
+ sh->mpm_hcd_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hsmd_ctx_tc);
+ sh->mpm_hsmd_ctx_tc = NULL;
+ MpmFactoryReClaimMpmCtx(de_ctx, sh->mpm_hscd_ctx_tc);
+ sh->mpm_hscd_ctx_tc = NULL;
+ }
+
+ return 0;
+}
+
+/** \brief Pattern ID Hash for sharing pattern id's
+ *
+ * A per detection engine hash to make sure each pattern has a unique
+ * global id but patterns that are the same share id's.
+ */
+typedef struct MpmPatternIdTableElmt_ {
+ uint8_t *pattern; /**< ptr to the pattern */
+ uint16_t pattern_len; /**< pattern len */
+ PatIntId id; /**< pattern id */
+ uint16_t dup_count; /**< duplicate count */
+ uint8_t sm_list; /**< SigMatch list */
+} MpmPatternIdTableElmt;
+
+/** \brief Hash compare func for MpmPatternId api
+ * \retval 1 patterns are the same
+ * \retval 0 patterns are not the same
+ **/
+static char MpmPatternIdCompare(void *p1, uint16_t len1, void *p2, uint16_t len2)
+{
+ SCEnter();
+ BUG_ON(len1 < sizeof(MpmPatternIdTableElmt));
+ BUG_ON(len2 < sizeof(MpmPatternIdTableElmt));
+
+ MpmPatternIdTableElmt *e1 = (MpmPatternIdTableElmt *)p1;
+ MpmPatternIdTableElmt *e2 = (MpmPatternIdTableElmt *)p2;
+
+ if (e1->pattern_len != e2->pattern_len ||
+ e1->sm_list != e2->sm_list) {
+ SCReturnInt(0);
+ }
+
+ if (SCMemcmp(e1->pattern, e2->pattern, e1->pattern_len) != 0) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+/** \brief Hash func for MpmPatternId api
+ * \retval hash hash value
+ */
+static uint32_t MpmPatternIdHashFunc(HashTable *ht, void *p, uint16_t len)
+{
+ SCEnter();
+ BUG_ON(len < sizeof(MpmPatternIdTableElmt));
+
+ MpmPatternIdTableElmt *e = (MpmPatternIdTableElmt *)p;
+ uint32_t hash = e->pattern_len;
+ uint16_t u = 0;
+
+ for (u = 0; u < e->pattern_len; u++) {
+ hash += e->pattern[u];
+ }
+
+ SCReturnUInt(hash % ht->array_size);
+}
+
+/** \brief free a MpmPatternIdTableElmt */
+static void MpmPatternIdTableElmtFree(void *e)
+{
+ SCEnter();
+ MpmPatternIdTableElmt *c = (MpmPatternIdTableElmt *)e;
+ SCFree(c->pattern);
+ SCFree(c);
+ SCReturn;
+}
+
+/** \brief alloc initialize the MpmPatternIdHash */
+MpmPatternIdStore *MpmPatternIdTableInitHash(void)
+{
+ SCEnter();
+
+ MpmPatternIdStore *ht = SCMalloc(sizeof(MpmPatternIdStore));
+ BUG_ON(ht == NULL);
+ memset(ht, 0x00, sizeof(MpmPatternIdStore));
+
+ ht->hash = HashTableInit(65536, MpmPatternIdHashFunc, MpmPatternIdCompare, MpmPatternIdTableElmtFree);
+ BUG_ON(ht->hash == NULL);
+
+ SCReturnPtr(ht, "MpmPatternIdStore");
+}
+
+void MpmPatternIdTableFreeHash(MpmPatternIdStore *ht)
+{
+ SCEnter();
+
+ if (ht == NULL) {
+ SCReturn;
+ }
+
+ if (ht->hash != NULL) {
+ HashTableFree(ht->hash);
+ }
+
+ SCFree(ht);
+ SCReturn;
+}
+
+uint32_t MpmPatternIdStoreGetMaxId(MpmPatternIdStore *ht)
+{
+ if (ht == NULL) {
+ return 0;
+ }
+
+ return ht->max_id;
+}
+
+/**
+ * \brief Get the pattern id for a content pattern
+ *
+ * \param ht mpm pattern id hash table store
+ * \param co content pattern data
+ *
+ * \retval id pattern id
+ * \initonly
+ */
+uint32_t DetectContentGetId(MpmPatternIdStore *ht, DetectContentData *co)
+{
+ SCEnter();
+
+ BUG_ON(ht == NULL || ht->hash == NULL);
+
+ MpmPatternIdTableElmt *e = NULL;
+ MpmPatternIdTableElmt *r = NULL;
+ uint32_t id = 0;
+
+ e = SCMalloc(sizeof(MpmPatternIdTableElmt));
+ BUG_ON(e == NULL);
+ memset(e, 0, sizeof(MpmPatternIdTableElmt));
+ e->pattern = SCMalloc(co->content_len);
+ BUG_ON(e->pattern == NULL);
+ memcpy(e->pattern, co->content, co->content_len);
+ e->pattern_len = co->content_len;
+ e->id = 0;
+
+ r = HashTableLookup(ht->hash, (void *)e, sizeof(MpmPatternIdTableElmt));
+ if (r == NULL) {
+ e->id = ht->max_id;
+ ht->max_id++;
+ id = e->id;
+
+ int ret = HashTableAdd(ht->hash, e, sizeof(MpmPatternIdTableElmt));
+ BUG_ON(ret != 0);
+
+ e = NULL;
+
+ ht->unique_patterns++;
+ } else {
+ id = r->id;
+
+ ht->shared_patterns++;
+ }
+
+ if (e != NULL)
+ MpmPatternIdTableElmtFree(e);
+
+ SCReturnUInt(id);
+}
+
+typedef struct DetectFPAndItsId_ {
+ PatIntId id;
+ uint16_t content_len;
+ uint32_t flags;
+ int sm_list;
+
+ uint8_t *content;
+} DetectFPAndItsId;
+
+/**
+ * \brief Figured out the FP and their respective content ids for all the
+ * sigs in the engine.
+ *
+ * \param de_ctx Detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectSetFastPatternAndItsId(DetectEngineCtx *de_ctx)
+{
+ uint32_t struct_total_size = 0;
+ uint32_t content_total_size = 0;
+ Signature *s = NULL;
+
+ /* Count the amount of memory needed to store all the structures
+ * and the content of those structures. This will over estimate the
+ * true size, since duplicates are removed below, but counted here.
+ */
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ s->mpm_sm = RetrieveFPForSigV2(s);
+ if (s->mpm_sm != NULL) {
+ DetectContentData *cd = (DetectContentData *)s->mpm_sm->ctx;
+ struct_total_size += sizeof(DetectFPAndItsId);
+ content_total_size += cd->content_len;
+ }
+ }
+
+ /* array hash buffer - i've run out of ideas to name it */
+ uint8_t *ahb = SCMalloc(sizeof(uint8_t) * (struct_total_size + content_total_size));
+ if (unlikely(ahb == NULL))
+ return -1;
+
+ uint8_t *content = NULL;
+ uint8_t content_len = 0;
+ PatIntId max_id = 0;
+ DetectFPAndItsId *struct_offset = (DetectFPAndItsId *)ahb;
+ uint8_t *content_offset = ahb + struct_total_size;
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->mpm_sm != NULL) {
+ int sm_list = SigMatchListSMBelongsTo(s, s->mpm_sm);
+ BUG_ON(sm_list == -1);
+
+ DetectContentData *cd = (DetectContentData *)s->mpm_sm->ctx;
+ DetectFPAndItsId *dup = (DetectFPAndItsId *)ahb;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ content = cd->content + cd->fp_chop_offset;
+ content_len = cd->fp_chop_len;
+ } else {
+ content = cd->content;
+ content_len = cd->content_len;
+ }
+ uint32_t flags = cd->flags & DETECT_CONTENT_NOCASE;
+ /* Check for content already found on the same list */
+ for (; dup != struct_offset; dup++) {
+ if (dup->content_len != content_len)
+ continue;
+ if (dup->sm_list != sm_list)
+ continue;
+ if (dup->flags != flags)
+ continue;
+ /* Check for pattern matching a duplicate. Use case insensitive matching
+ * for case insensitive patterns. */
+ if (flags & DETECT_CONTENT_NOCASE) {
+ if (SCMemcmpLowercase(dup->content, content, content_len) != 0)
+ continue;
+ } else {
+ /* Case sensitive matching */
+ if (SCMemcmp(dup->content, content, content_len) != 0)
+ continue;
+ }
+ /* Found a match with a previous pattern. */
+ break;
+ }
+ if (dup != struct_offset) {
+ /* Exited for-loop before the end, so found an existing match.
+ * Use its ID. */
+ cd->id = dup->id;
+ continue;
+ }
+
+ /* Not found, so new content. Give it a new ID and add it
+ * to the array. Copy the content at the end of the
+ * content array.
+ */
+ struct_offset->id = max_id++;
+ cd->id = struct_offset->id;
+ struct_offset->content_len = content_len;
+ struct_offset->sm_list = sm_list;
+ struct_offset->content = content_offset;
+ struct_offset->flags = flags;
+
+ content_offset += content_len;
+
+ if (flags & DETECT_CONTENT_NOCASE) {
+ /* Need to store case-insensitive patterns as lower case
+ * because SCMemcmpLowercase() above assumes that all
+ * patterns are stored lower case so that it doesn't
+ * need to relower its first argument.
+ */
+ memcpy_tolower(struct_offset->content, content, content_len);
+ } else {
+ memcpy(struct_offset->content, content, content_len);
+ }
+
+ struct_offset++;
+ } /* if (s->mpm_sm != NULL) */
+ } /* for */
+
+ de_ctx->max_fp_id = max_id;
+
+ SCFree(ahb);
+
+ return 0;
+}
diff --git a/framework/src/suricata/src/detect-engine-mpm.h b/framework/src/suricata/src/detect-engine-mpm.h
new file mode 100644
index 00000000..02df56f4
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-mpm.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_MPM_H__
+#define __DETECT_ENGINE_MPM_H__
+
+#include "tm-threads.h"
+
+#include "detect.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "stream.h"
+
+uint16_t PatternMatchDefaultMatcher(void);
+
+uint32_t PatternStrength(uint8_t *, uint16_t);
+uint32_t PacketPatternSearchWithStreamCtx(DetectEngineThreadCtx *, Packet *);
+uint32_t PacketPatternSearch(DetectEngineThreadCtx *, Packet *);
+uint32_t UriPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint16_t, uint8_t);
+uint32_t StreamPatternSearch(DetectEngineThreadCtx *, Packet *, StreamMsg *, uint8_t);
+uint32_t HttpClientBodyPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpServerBodyPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpHeaderPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpRawHeaderPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpMethodPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpCookiePatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpRawUriPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpStatMsgPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpStatCodePatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpUAPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpHHPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t HttpHRHPatternSearch(DetectEngineThreadCtx *, uint8_t *, uint32_t, uint8_t);
+uint32_t DnsQueryPatternSearch(DetectEngineThreadCtx *det_ctx, uint8_t *buffer, uint32_t buffer_len, uint8_t flags);
+uint32_t SMTPFiledataPatternSearch(DetectEngineThreadCtx *det_ctx, uint8_t *buffer, uint32_t buffer_len, uint8_t flags);
+
+void PacketPatternCleanup(ThreadVars *, DetectEngineThreadCtx *);
+void StreamPatternCleanup(ThreadVars *t, DetectEngineThreadCtx *det_ctx, StreamMsg *smsg);
+
+void PatternMatchPrepare(MpmCtx *, uint16_t);
+void PatternMatchThreadPrepare(MpmThreadCtx *, uint16_t type, uint32_t max_id);
+
+void PatternMatchDestroy(MpmCtx *, uint16_t);
+void PatternMatchThreadDestroy(MpmThreadCtx *mpm_thread_ctx, uint16_t);
+void PatternMatchThreadPrint(MpmThreadCtx *, uint16_t);
+
+int PatternMatchPrepareGroup(DetectEngineCtx *, SigGroupHead *);
+void DetectEngineThreadCtxInfo(ThreadVars *, DetectEngineThreadCtx *);
+void PatternMatchDestroyGroup(SigGroupHead *);
+
+TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **);
+TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *);
+
+void DbgPrintSearchStats();
+
+MpmPatternIdStore *MpmPatternIdTableInitHash(void);
+void MpmPatternIdTableFreeHash(MpmPatternIdStore *);
+uint32_t MpmPatternIdStoreGetMaxId(MpmPatternIdStore *);
+uint32_t DetectContentGetId(MpmPatternIdStore *, DetectContentData *);
+
+int SignatureHasPacketContent(Signature *);
+int SignatureHasStreamContent(Signature *);
+
+SigMatch *RetrieveFPForSig(Signature *s);
+SigMatch *RetrieveFPForSigV2(Signature *s);
+
+/**
+ * \brief Figured out the FP and their respective content ids for all the
+ * sigs in the engine.
+ *
+ * \param de_ctx Detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectSetFastPatternAndItsId(DetectEngineCtx *de_ctx);
+
+#endif /* __DETECT_ENGINE_MPM_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-payload.c b/framework/src/suricata/src/detect-engine-payload.c
new file mode 100644
index 00000000..af0d9c3e
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-payload.c
@@ -0,0 +1,1113 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Performs payload matching functions
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-content-inspection.h"
+
+#include "util-debug.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/**
+ * \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param f flow (for pcre flowvar storage)
+ * \param p Packet
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectEngineInspectPacketPayload(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f, Packet *p)
+{
+ SCEnter();
+ int r = 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ det_ctx->replist = NULL;
+ //det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_INSPECTING_PACKET;
+
+ r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_PMATCH],
+ f, p->payload, p->payload_len, 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD, p);
+ //r = DoInspectPacketPayload(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_PMATCH], p, f, p->payload, p->payload_len);
+ //det_ctx->flags &= ~DETECT_ENGINE_THREAD_CTX_INSPECTING_PACKET;
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Do the content inspection & validation for a signature for a stream chunk
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param f flow (for pcre flowvar storage)
+ * \param payload ptr to the payload to inspect
+ * \param payload_len length of the payload
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \todo we might also pass the packet to this function for the pktvar
+ * storage. Only, would that be right? We're not inspecting data
+ * from the current packet here.
+ */
+int DetectEngineInspectStreamPayload(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f,
+ uint8_t *payload, uint32_t payload_len)
+{
+ SCEnter();
+ int r = 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ SCReturnInt(0);
+ }
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+ //det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_INSPECTING_STREAM;
+
+ r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_PMATCH],
+ f, payload, payload_len, 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM, NULL);
+
+ //r = DoInspectPacketPayload(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_PMATCH], NULL, f, payload, payload_len);
+ //det_ctx->flags &= ~DETECT_ENGINE_THREAD_CTX_INSPECTING_STREAM;
+ if (r == 1) {
+ SCReturnInt(1);
+ }
+
+ SCReturnInt(0);
+}
+
+#ifdef UNITTESTS
+
+/** \test Not the first but the second occurence of "abc" should be used
+ * for the 2nd match */
+static int PayloadTestSig01 (void)
+{
+ uint8_t *buf = (uint8_t *)
+ "abcabcd";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"abc\"; content:\"d\"; distance:0; within:1; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Nocase matching */
+static int PayloadTestSig02 (void)
+{
+ uint8_t *buf = (uint8_t *)
+ "abcaBcd";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"abc\"; nocase; content:\"d\"; distance:0; within:1; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Negative distance matching */
+static int PayloadTestSig03 (void)
+{
+ uint8_t *buf = (uint8_t *)
+ "abcaBcd";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"aBc\"; nocase; content:\"abca\"; distance:-10; within:4; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative matches.
+ */
+static int PayloadTestSig04(void)
+{
+ uint8_t *buf = (uint8_t *)"now this is is big big string now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"this\"; content:\"is\"; within:6; content:\"big\"; within:8; "
+ "content:\"string\"; within:8; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative matches.
+ */
+static int PayloadTestSig05(void)
+{
+ uint8_t *buf = (uint8_t *)"now this is is is big big big string now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"this\"; content:\"is\"; within:9; content:\"big\"; within:12; "
+ "content:\"string\"; within:8; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative matches.
+ */
+static int PayloadTestSig06(void)
+{
+ uint8_t *buf = (uint8_t *)"this this now is is big string now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"now\"; content:\"this\"; content:\"is\"; within:12; content:\"big\"; within:8; "
+ "content:\"string\"; within:8; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative matches.
+ */
+static int PayloadTestSig07(void)
+{
+ uint8_t *buf = (uint8_t *)" thus thus is a big";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"thus\"; offset:8; content:\"is\"; within:6; content:\"big\"; within:8; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative matches with negative matches
+ * and show the need for det_ctx->discontinue_matching.
+ */
+static int PayloadTestSig08(void)
+{
+ uint8_t *buf = (uint8_t *)"we need to fix this and yes fix this now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"fix\"; content:\"this\"; within:6; content:!\"and\"; distance:0; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) != 1) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test pcre recursive matching.
+ */
+static int PayloadTestSig09(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "pcre:/super/; content:\"nova\"; within:7; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test invalid sig.
+ */
+static int PayloadTestSig10(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert udp any any -> any any (msg:\"crash\"; "
+ "byte_test:4,>,2,0,relative; sid:11;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 1) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test invalid sig.
+ */
+static int PayloadTestSig11(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert udp any any -> any any (msg:\"crash\"; "
+ "byte_jump:1,0,relative; sid:11;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 1) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test invalid sig.
+ */
+static int PayloadTestSig12(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert udp any any -> any any (msg:\"crash\"; "
+ "isdataat:10,relative; sid:11;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 1) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Used to check the working of recursion_limit counter.
+ */
+static int PayloadTestSig13(void)
+{
+ uint8_t *buf = (uint8_t *)"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+ uint16_t mpm_type = MPM_B2G;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"aa\"; content:\"aa\"; distance:0; content:\"aa\"; distance:0; "
+ "byte_test:1,>,200,0,relative; sid:1;)";
+
+ struct timeval tv_start, tv_end, tv_diff;
+
+ gettimeofday(&tv_start, NULL);
+
+ do {
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx == NULL: ");
+ goto end;
+ }
+ de_ctx->inspection_recursion_limit = 3000;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->mpm_matcher = mpm_type;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ printf("signature == NULL: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ } while (0);
+
+ gettimeofday(&tv_end, NULL);
+
+ tv_diff.tv_sec = tv_end.tv_sec - tv_start.tv_sec;
+ tv_diff.tv_usec = tv_end.tv_usec - tv_start.tv_usec;
+
+ printf("%ld.%06ld\n", (long int)tv_diff.tv_sec, (long int)tv_diff.tv_usec);
+
+ result = 1;
+
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test normal & negated matching, both absolute and relative
+ */
+static int PayloadTestSig14(void)
+{
+ uint8_t *buf = (uint8_t *)"User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.6 GTB5";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"User-Agent|3A| Mozilla/5.0 |28|Macintosh|3B| \"; content:\"Firefox/3.\"; distance:0; content:!\"Firefox/3.6.12\"; distance:-10; content:!\"Mozilla/5.0 |28|Macintosh|3B| U|3B| Intel Mac OS X 10.5|3B| en-US|3B| rv|3A|1.9.1b4|29| Gecko/20090423 Firefox/3.6 GTB5\"; sid:1; rev:1;)";
+
+ //char sig[] = "alert tcp any any -> any any (content:\"User-Agent: Mozilla/5.0 (Macintosh; \"; content:\"Firefox/3.\"; distance:0; content:!\"Firefox/3.6.12\"; distance:-10; content:!\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.6 GTB5\"; sid:1; rev:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 1) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig15(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"nova\"; isdataat:18,relative; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig16(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super duper nova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"nova\"; isdataat:!20,relative; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig17(void)
+{
+ uint8_t buf[] = { 0xEB, 0x29, 0x25, 0x38, 0x78, 0x25, 0x38, 0x78, 0x25 };
+ uint16_t buflen = 9;
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"%\"; depth:4; offset:0; "
+ "content:\"%\"; within:2; distance:1; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig18(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x35, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "content:\"|0C 0D 0E 0F|\"; distance:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig19(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x35, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,hex,relative; "
+ "content:\"|0C 0D 0E 0F|\"; distance:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig20(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x35, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "content:\"|06 35 07 08|\"; offset:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig21(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x36, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "content:\"|03 04 05 06|\"; depth:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig22(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x36, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "content:\"|09 0A 0B 0C|\"; within:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig23(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x32, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x33, 0x0B, 0x0C, 0x0D,
+ 0x32, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "byte_extract:1,3,two,string,dec,relative; "
+ "byte_test:1,=,one,two,string,dec,relative; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig24(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x32, /* the last byte is 2 */
+ 0x07, 0x08, 0x33, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|01 02 03 04|\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "byte_jump:1,one,string,dec,relative; "
+ "content:\"|0D 0E 0F|\"; distance:0; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/*
+ * \test Test negative byte extract.
+ */
+static int PayloadTestSig25(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x35, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|35 07 08 09|\"; "
+ "byte_extract:1,-4,one,string,dec,relative; "
+ "content:\"|0C 0D 0E 0F|\"; distance:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/*
+ * \test Test negative byte extract.
+ */
+static int PayloadTestSig26(void)
+{
+ uint8_t buf[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x35, /* the last byte is 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F,
+ };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"|35 07 08 09|\"; "
+ "byte_extract:1,-3000,one,string,dec,relative; "
+ "content:\"|0C 0D 0E 0F|\"; distance:one; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) != 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/*
+ * \test Test packet/stream sigs
+ */
+static int PayloadTestSig27(void)
+{
+ uint8_t buf[] = "dummypayload";
+ uint16_t buflen = sizeof(buf) - 1;
+ int result = 0;
+
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ char sig[] = "alert tcp any any -> any any (content:\"dummy\"; "
+ "depth:5; sid:1;)";
+
+ p->flags |= PKT_STREAM_ADD;
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) != 1)
+ goto end;
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/*
+ * \test Test packet/stream sigs
+ */
+static int PayloadTestSig28(void)
+{
+ uint8_t buf[] = "dummypayload";
+ uint16_t buflen = sizeof(buf) - 1;
+ int result = 0;
+
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ char sig[] = "alert tcp any any -> any any (content:\"payload\"; "
+ "offset:4; depth:12; sid:1;)";
+
+ p->flags |= PKT_STREAM_ADD;
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) != 1)
+ goto end;
+
+ result = 1;
+
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test pcre recursive matching - bug #529
+ */
+static int PayloadTestSig29(void)
+{
+ uint8_t *buf = (uint8_t *)"this is a super dupernova in super nova now";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "pcre:/^.{4}/; content:\"nova\"; within:4; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, DEFAULT_MPM) == 1) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig30(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "xyonexxxxxxtwojunkonetwo";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"one\"; pcre:\"/^two/R\"; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int PayloadTestSig31(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "xyonexxxxxxtwojunkonetwo";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (content:\"one\"; pcre:\"/(fiv|^two)/R\"; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test byte_jump.
+ */
+static int PayloadTestSig32(void)
+{
+ uint8_t *buf = (uint8_t *)"dummy2xxcardmessage";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"crash\"; "
+ "content:\"message\"; byte_jump:2,-14,string,dec,relative; content:\"card\"; within:4; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0)
+ goto end;
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test byte_test.
+ */
+static int PayloadTestSig33(void)
+{
+ uint8_t *buf = (uint8_t *)"dummy2xxcardmessage";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"crash\"; "
+ "content:\"message\"; byte_test:1,=,2,-14,string,dec,relative; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0)
+ goto end;
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test byte_extract.
+ */
+static int PayloadTestSig34(void)
+{
+ uint8_t *buf = (uint8_t *)"dummy2xxcardmessage";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"crash\"; "
+ "content:\"message\"; byte_extract:1,-14,boom,string,dec,relative; sid:1;)";
+
+ if (UTHPacketMatchSigMpm(p, sig, MPM_B2G) == 0)
+ goto end;
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void PayloadRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("PayloadTestSig01", PayloadTestSig01, 1);
+ UtRegisterTest("PayloadTestSig02", PayloadTestSig02, 1);
+ UtRegisterTest("PayloadTestSig03", PayloadTestSig03, 1);
+ UtRegisterTest("PayloadTestSig04", PayloadTestSig04, 1);
+ UtRegisterTest("PayloadTestSig05", PayloadTestSig05, 1);
+ UtRegisterTest("PayloadTestSig06", PayloadTestSig06, 1);
+ UtRegisterTest("PayloadTestSig07", PayloadTestSig07, 1);
+ UtRegisterTest("PayloadTestSig08", PayloadTestSig08, 1);
+ UtRegisterTest("PayloadTestSig09", PayloadTestSig09, 1);
+ UtRegisterTest("PayloadTestSig10", PayloadTestSig10, 1);
+ UtRegisterTest("PayloadTestSig11", PayloadTestSig11, 1);
+ UtRegisterTest("PayloadTestSig12", PayloadTestSig12, 1);
+ UtRegisterTest("PayloadTestSig13", PayloadTestSig13, 1);
+ UtRegisterTest("PayloadTestSig14", PayloadTestSig14, 1);
+ UtRegisterTest("PayloadTestSig15", PayloadTestSig15, 1);
+ UtRegisterTest("PayloadTestSig16", PayloadTestSig16, 1);
+ UtRegisterTest("PayloadTestSig17", PayloadTestSig17, 1);
+
+ UtRegisterTest("PayloadTestSig18", PayloadTestSig18, 1);
+ UtRegisterTest("PayloadTestSig19", PayloadTestSig19, 1);
+ UtRegisterTest("PayloadTestSig20", PayloadTestSig20, 1);
+ UtRegisterTest("PayloadTestSig21", PayloadTestSig21, 1);
+ UtRegisterTest("PayloadTestSig22", PayloadTestSig22, 1);
+ UtRegisterTest("PayloadTestSig23", PayloadTestSig23, 1);
+ UtRegisterTest("PayloadTestSig24", PayloadTestSig24, 1);
+ UtRegisterTest("PayloadTestSig25", PayloadTestSig25, 1);
+ UtRegisterTest("PayloadTestSig26", PayloadTestSig26, 1);
+ UtRegisterTest("PayloadTestSig27", PayloadTestSig27, 1);
+ UtRegisterTest("PayloadTestSig28", PayloadTestSig28, 1);
+ UtRegisterTest("PayloadTestSig29", PayloadTestSig29, 1);
+
+ UtRegisterTest("PayloadTestSig30", PayloadTestSig30, 1);
+ UtRegisterTest("PayloadTestSig31", PayloadTestSig31, 1);
+ UtRegisterTest("PayloadTestSig32", PayloadTestSig32, 1);
+ UtRegisterTest("PayloadTestSig33", PayloadTestSig33, 1);
+ UtRegisterTest("PayloadTestSig34", PayloadTestSig34, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-payload.h b/framework/src/suricata/src/detect-engine-payload.h
new file mode 100644
index 00000000..d6220b18
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-payload.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_PAYLOAD_H__
+#define __DETECT_ENGINE_PAYLOAD_H__
+
+int DetectEngineInspectPacketPayload(DetectEngineCtx *,
+ DetectEngineThreadCtx *, Signature *, Flow *, Packet *);
+int DetectEngineInspectStreamPayload(DetectEngineCtx *,
+ DetectEngineThreadCtx *, Signature *, Flow *,
+ uint8_t *, uint32_t);
+
+void PayloadRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_PAYLOAD_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-port.c b/framework/src/suricata/src/detect-engine-port.c
new file mode 100644
index 00000000..6d3d5208
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-port.c
@@ -0,0 +1,2850 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Ports part of the detection engine.
+ *
+ * \todo move this out of the detection plugin structure
+ * \todo more unittesting
+ *
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "flow-var.h"
+
+#include "util-cidr.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-rule-vars.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-port.h"
+
+#include "conf.h"
+#include "util-debug.h"
+#include "util-error.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+static int DetectPortCutNot(DetectPort *, DetectPort **);
+static int DetectPortCut(DetectEngineCtx *, DetectPort *, DetectPort *,
+ DetectPort **);
+DetectPort *PortParse(char *str);
+int DetectPortIsValidRange(char *);
+
+/** Memory usage counters */
+static uint32_t detect_port_memory = 0;
+static uint32_t detect_port_init_cnt = 0;
+static uint32_t detect_port_free_cnt = 0;
+
+/**
+ * \brief Alloc a DetectPort structure and update counters
+ *
+ * \retval sgh Pointer to the newly created DetectPort on success; or NULL in
+ * case of error.
+ */
+DetectPort *DetectPortInit(void)
+{
+ DetectPort *dp = SCMalloc(sizeof(DetectPort));
+ if (unlikely(dp == NULL))
+ return NULL;
+ memset(dp, 0, sizeof(DetectPort));
+
+ detect_port_memory += sizeof(DetectPort);
+ detect_port_init_cnt++;
+
+ return dp;
+}
+
+/**
+ * \brief Free a DetectPort and its members
+ *
+ * \param dp Pointer to the DetectPort that has to be freed.
+ */
+void DetectPortFree(DetectPort *dp)
+{
+ if (dp == NULL)
+ return;
+
+ /* only free the head if we have the original */
+ if (dp->sh != NULL && !(dp->flags & PORT_SIGGROUPHEAD_COPY)) {
+ SigGroupHeadFree(dp->sh);
+ }
+ dp->sh = NULL;
+
+ if (dp->dst_ph != NULL && !(dp->flags & PORT_GROUP_PORTS_COPY)) {
+ DetectPortCleanupList(dp->dst_ph);
+ }
+ dp->dst_ph = NULL;
+
+ //BUG_ON(dp->next != NULL);
+
+ detect_port_memory -= sizeof(DetectPort);
+ detect_port_free_cnt++;
+ SCFree(dp);
+}
+
+/**
+ * \brief Prints Memory statistics of the counters at detect-engine-port.[c,h]
+ */
+void DetectPortPrintMemory(void)
+{
+ SCLogDebug(" * Port memory stats (DetectPort %" PRIuMAX "):",
+ (uintmax_t)sizeof(DetectPort));
+ SCLogDebug(" - detect_port_memory %" PRIu32 "", detect_port_memory);
+ SCLogDebug(" - detect_port_init_cnt %" PRIu32 "", detect_port_init_cnt);
+ SCLogDebug(" - detect_port_free_cnt %" PRIu32 "", detect_port_free_cnt);
+ SCLogDebug(" - outstanding ports %" PRIu32 "",
+ detect_port_init_cnt - detect_port_free_cnt);
+ SCLogDebug(" * Port memory stats done");
+}
+
+/**
+ * \brief Used to see if the exact same portrange exists in the list
+ *
+ * \param head Pointer to the DetectPort list head
+ * \param dp DetectPort to search in the DetectPort list
+ *
+ * \retval returns a ptr to the match, or NULL if no match
+ */
+DetectPort *DetectPortLookup(DetectPort *head, DetectPort *dp)
+{
+ DetectPort *cur;
+
+ if (head != NULL) {
+ for (cur = head; cur != NULL; cur = cur->next) {
+ if (DetectPortCmp(cur, dp) == PORT_EQ)
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Helper function used to print the list of ports
+ * present in this DetectPort list.
+ *
+ * \param head Pointer to the DetectPort list head
+ */
+void DetectPortPrintList(DetectPort *head)
+{
+ DetectPort *cur;
+ uint16_t cnt = 0;
+
+ SCLogDebug("= list start:");
+ if (head != NULL) {
+ for (cur = head; cur != NULL; cur = cur->next) {
+ DetectPortPrint(cur);
+ cnt++;
+ }
+ SCLogDebug(" ");
+ }
+ SCLogDebug("= list end (cnt %" PRIu32 ")", cnt);
+}
+
+/**
+ * \brief Free a DetectPort list and each of its members
+ *
+ * \param head Pointer to the DetectPort list head
+ */
+void DetectPortCleanupList (DetectPort *head)
+{
+ if (head == NULL)
+ return;
+
+ DetectPort *cur, *next;
+
+ for (cur = head; cur != NULL; ) {
+ next = cur->next;
+ cur->next = NULL;
+ DetectPortFree(cur);
+ cur = next;
+ }
+}
+
+/**
+ * \brief Do a sorted insert, where the top of the list should be the biggest
+ * port range.
+ *
+ * \todo XXX current sorting only works for overlapping ranges
+ *
+ * \param head Pointer to the DetectPort list head
+ * \param dp Pointer to DetectPort to search in the DetectPort list
+ * \retval 0 if dp is added correctly
+ */
+int DetectPortAdd(DetectPort **head, DetectPort *dp)
+{
+ DetectPort *cur, *prev_cur = NULL;
+
+ //SCLogDebug("DetectPortAdd: adding "); DetectPortPrint(ag); SCLogDebug("");
+
+ if (*head != NULL) {
+ for (cur = *head; cur != NULL; cur = cur->next) {
+ prev_cur = cur;
+ int r = DetectPortCmp(dp,cur);
+ if (r == PORT_EB) {
+ /* insert here */
+ dp->prev = cur->prev;
+ dp->next = cur;
+
+ cur->prev = dp;
+ if (*head == cur) {
+ *head = dp;
+ } else {
+ dp->prev->next = dp;
+ }
+ return 0;
+ }
+ }
+ dp->prev = prev_cur;
+ if (prev_cur != NULL)
+ prev_cur->next = dp;
+ } else {
+ *head = dp;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Copy and insert the new DetectPort, with a copy list of sigs
+ *
+ * \param de_ctx Pointer to the current detection engine context
+ * \param head Pointer to the DetectPort list head
+ * \param new Pointer to DetectPort to search in the DetectPort list
+ *
+ * \retval 0 if dp is added correctly
+ */
+int DetectPortInsertCopy(DetectEngineCtx *de_ctx, DetectPort **head,
+ DetectPort *new)
+{
+ DetectPort *copy = DetectPortCopySingle(de_ctx,new);
+ if (copy == NULL)
+ return -1;
+
+ return DetectPortInsert(de_ctx, head, copy);
+}
+
+/**
+ * \brief function for inserting a port group object. This also makes sure
+ * SigGroupContainer lists are handled correctly.
+ *
+ * \param de_ctx Pointer to the current detection engine context
+ * \param head Pointer to the DetectPort list head
+ * \param dp DetectPort to search in the DetectPort list
+ *
+ * \retval 1 inserted
+ * \retval 0 not inserted, memory of new is freed
+ * \retval -1 error
+ * */
+int DetectPortInsert(DetectEngineCtx *de_ctx, DetectPort **head,
+ DetectPort *new)
+{
+ if (new == NULL)
+ return 0;
+
+ //BUG_ON(new->next != NULL);
+ //BUG_ON(new->prev != NULL);
+
+ /* see if it already exists or overlaps with existing ag's */
+ if (*head != NULL) {
+ DetectPort *cur = NULL;
+ int r = 0;
+
+ for (cur = *head; cur != NULL; cur = cur->next) {
+ r = DetectPortCmp(new,cur);
+ BUG_ON(r == PORT_ER);
+
+ /* if so, handle that */
+ if (r == PORT_EQ) {
+ SCLogDebug("PORT_EQ %p %p", cur, new);
+ /* exact overlap/match */
+ if (cur != new) {
+ SigGroupHeadCopySigs(de_ctx, new->sh, &cur->sh);
+ cur->cnt += new->cnt;
+ DetectPortFree(new);
+ return 0;
+ }
+ return 1;
+ } else if (r == PORT_GT) {
+ SCLogDebug("PORT_GT (cur->next %p)", cur->next);
+ /* only add it now if we are bigger than the last
+ * group. Otherwise we'll handle it later. */
+ if (cur->next == NULL) {
+ SCLogDebug("adding GT");
+ /* put in the list */
+ new->prev = cur;
+ cur->next = new;
+ return 1;
+ }
+ } else if (r == PORT_LT) {
+ SCLogDebug("PORT_LT");
+
+ /* see if we need to insert the ag anywhere */
+ /* put in the list */
+ if (cur->prev != NULL)
+ cur->prev->next = new;
+ new->prev = cur->prev;
+ new->next = cur;
+ cur->prev = new;
+
+ /* update head if required */
+ if (*head == cur) {
+ *head = new;
+ }
+ return 1;
+
+ /* alright, those were the simple cases,
+ * lets handle the more complex ones now */
+
+ } else {
+ DetectPort *c = NULL;
+ r = DetectPortCut(de_ctx, cur, new, &c);
+ if (r == -1)
+ goto error;
+
+ r = DetectPortInsert(de_ctx, head, new);
+ if (r == -1)
+ goto error;
+
+ if (c != NULL) {
+ SCLogDebug("inserting C (%p)", c);
+ if (SCLogDebugEnabled()) {
+ DetectPortPrint(c);
+ }
+ r = DetectPortInsert(de_ctx, head, c);
+ if (r == -1)
+ goto error;
+ }
+ return 1;
+
+ }
+ }
+
+ /* head is NULL, so get a group and set head to it */
+ } else {
+ SCLogDebug("setting new head %p", new);
+ *head = new;
+ }
+
+ return 1;
+error:
+ /* XXX */
+ return -1;
+}
+
+/**
+ * \brief Function that cuts port groups and merge them
+ *
+ * \param de_ctx Pointer to the current detection engine context
+ * \param a pointer to DetectPort "a"
+ * \param b pointer to DetectPort "b"
+ * \param c pointer to DetectPort "c"
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * */
+static int DetectPortCut(DetectEngineCtx *de_ctx, DetectPort *a,
+ DetectPort *b, DetectPort **c)
+{
+ uint32_t a_port1 = a->port;
+ uint32_t a_port2 = a->port2;
+ uint32_t b_port1 = b->port;
+ uint32_t b_port2 = b->port2;
+ DetectPort *tmp = NULL;
+
+ /* default to NULL */
+ *c = NULL;
+
+ int r = DetectPortCmp(a,b);
+ BUG_ON(r != PORT_ES && r != PORT_EB && r != PORT_LE && r != PORT_GE);
+
+ /* get a place to temporary put sigs lists */
+ tmp = DetectPortInit();
+ if (tmp == NULL) {
+ goto error;
+ }
+ memset(tmp, 0, sizeof(DetectPort));
+
+ /**
+ * We have 3 parts: [aaa[abab]bbb]
+ * part a: a_port1 <-> b_port1 - 1
+ * part b: b_port1 <-> a_port2
+ * part c: a_port2 + 1 <-> b_port2
+ */
+ if (r == PORT_LE) {
+ SCLogDebug("cut r == PORT_LE");
+ a->port = a_port1;
+ a->port2 = b_port1 - 1;
+
+ b->port = b_port1;
+ b->port2 = a_port2;
+
+ DetectPort *tmp_c;
+ tmp_c = DetectPortInit();
+ if (tmp_c == NULL) {
+ goto error;
+ }
+ *c = tmp_c;
+
+ tmp_c->port = a_port2 + 1;
+ tmp_c->port2 = b_port2;
+
+ SigGroupHeadCopySigs(de_ctx,b->sh,&tmp_c->sh); /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx,a->sh,&b->sh); /* copy a to b */
+
+ tmp_c->cnt += b->cnt;
+ b->cnt += a->cnt;
+
+ /**
+ * We have 3 parts: [bbb[baba]aaa]
+ * part a: b_port1 <-> a_port1 - 1
+ * part b: a_port1 <-> b_port2
+ * part c: b_port2 + 1 <-> a_port2
+ */
+ } else if (r == PORT_GE) {
+ SCLogDebug("cut r == PORT_GE");
+ a->port = b_port1;
+ a->port2 = a_port1 - 1;
+
+ b->port = a_port1;
+ b->port2 = b_port2;
+
+ DetectPort *tmp_c;
+ tmp_c = DetectPortInit();
+ if (tmp_c == NULL) {
+ goto error;
+ }
+ *c = tmp_c;
+
+ tmp_c->port = b_port2 + 1;
+ tmp_c->port2 = a_port2;
+
+ /**
+ * 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'a' sigs
+ */
+ SigGroupHeadCopySigs(de_ctx,a->sh,&tmp->sh); /* store old a list */
+ SigGroupHeadClearSigs(a->sh); /* clean a list */
+ SigGroupHeadCopySigs(de_ctx,tmp->sh,&tmp_c->sh); /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&a->sh); /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx,tmp->sh,&b->sh);/* prepend old a before b */
+
+ SigGroupHeadClearSigs(tmp->sh); /* clean tmp list */
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += tmp->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+
+ /**
+ * We have 2 or three parts:
+ *
+ * 2 part: [[abab]bbb] or [bbb[baba]]
+ * part a: a_port1 <-> a_port2
+ * part b: a_port2 + 1 <-> b_port2
+ *
+ * part a: b_port1 <-> a_port1 - 1
+ * part b: a_port1 <-> a_port2
+ *
+ * 3 part [bbb[aaa]bbb]
+ * becomes[aaa[bbb]ccc]
+ *
+ * part a: b_port1 <-> a_port1 - 1
+ * part b: a_port1 <-> a_port2
+ * part c: a_port2 + 1 <-> b_port2
+ */
+ } else if (r == PORT_ES) {
+ SCLogDebug("cut r == PORT_ES");
+ if (a_port1 == b_port1) {
+ SCLogDebug("1");
+ a->port = a_port1;
+ a->port2 = a_port2;
+
+ b->port = a_port2 + 1;
+ b->port2 = b_port2;
+
+ /** 'b' overlaps 'a' so 'a' needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&a->sh);
+ a->cnt += b->cnt;
+
+ } else if (a_port2 == b_port2) {
+ SCLogDebug("2");
+ a->port = b_port1;
+ a->port2 = a_port1 - 1;
+
+ b->port = a_port1;
+ b->port2 = a_port2;
+
+ /* [bbb[baba]] will be transformed into
+ * [aaa][bbb]
+ * steps: copy b sigs to tmp
+ * a overlaps b, so copy a to b
+ * clear a
+ * copy tmp to a */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&tmp->sh); /* store old a list */
+ tmp->cnt = b->cnt;
+ SigGroupHeadCopySigs(de_ctx,a->sh,&b->sh);
+ b->cnt += a->cnt;
+ SigGroupHeadClearSigs(a->sh); /* clean a list */
+ SigGroupHeadCopySigs(de_ctx,tmp->sh,&a->sh);/* merge old a with b */
+ a->cnt = tmp->cnt;
+ SigGroupHeadClearSigs(tmp->sh); /* clean tmp list */
+ } else {
+ SCLogDebug("3");
+ a->port = b_port1;
+ a->port2 = a_port1 - 1;
+
+ b->port = a_port1;
+ b->port2 = a_port2;
+
+ DetectPort *tmp_c;
+ tmp_c = DetectPortInit();
+ if (tmp_c == NULL) {
+ goto error;
+ }
+ *c = tmp_c;
+
+ tmp_c->port = a_port2 + 1;
+ tmp_c->port2 = b_port2;
+
+ /**
+ * 'a' gets clean and then 'b' sigs
+ * 'b' gets clean, then 'a' then 'b' sigs
+ * 'c' gets 'b' sigs
+ */
+ SigGroupHeadCopySigs(de_ctx,a->sh,&tmp->sh); /* store old a list */
+ SigGroupHeadClearSigs(a->sh); /* clean a list */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&tmp_c->sh); /* copy old b to c */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&a->sh); /* copy old b to a */
+ SigGroupHeadCopySigs(de_ctx,tmp->sh,&b->sh);/* merge old a with b */
+
+ SigGroupHeadClearSigs(tmp->sh); /* clean tmp list */
+
+ tmp->cnt += a->cnt;
+ a->cnt = 0;
+ tmp_c->cnt += b->cnt;
+ a->cnt += b->cnt;
+ b->cnt += tmp->cnt;
+ tmp->cnt = 0;
+ }
+ /**
+ * We have 2 or three parts:
+ *
+ * 2 part: [[baba]aaa] or [aaa[abab]]
+ * part a: b_port1 <-> b_port2
+ * part b: b_port2 + 1 <-> a_port2
+ *
+ * part a: a_port1 <-> b_port1 - 1
+ * part b: b_port1 <-> b_port2
+ *
+ * 3 part [aaa[bbb]aaa]
+ * becomes[aaa[bbb]ccc]
+ *
+ * part a: a_port1 <-> b_port2 - 1
+ * part b: b_port1 <-> b_port2
+ * part c: b_port2 + 1 <-> a_port2
+ */
+ } else if (r == PORT_EB) {
+ SCLogDebug("cut r == PORT_EB");
+ if (a_port1 == b_port1) {
+ SCLogDebug("1");
+ a->port = b_port1;
+ a->port2 = b_port2;
+
+ b->port = b_port2 + 1;
+ b->port2 = a_port2;
+
+ /** 'b' overlaps 'a' so 'a' needs the 'b' sigs */
+ SigGroupHeadCopySigs(de_ctx,b->sh,&tmp->sh);
+ SigGroupHeadClearSigs(b->sh);
+ SigGroupHeadCopySigs(de_ctx,a->sh,&b->sh);
+ SigGroupHeadCopySigs(de_ctx,tmp->sh,&a->sh);
+
+ SigGroupHeadClearSigs(tmp->sh);
+
+ tmp->cnt += b->cnt;
+ b->cnt = 0;
+ b->cnt += a->cnt;
+ a->cnt += tmp->cnt;
+ tmp->cnt = 0;
+
+ } else if (a_port2 == b_port2) {
+ SCLogDebug("2");
+
+ a->port = a_port1;
+ a->port2 = b_port1 - 1;
+
+ b->port = b_port1;
+ b->port2 = b_port2;
+
+ /** 'a' overlaps 'b' so 'b' needs the 'a' sigs */
+ SigGroupHeadCopySigs(de_ctx,a->sh,&b->sh);
+
+ b->cnt += a->cnt;
+
+ } else {
+ SCLogDebug("3");
+ a->port = a_port1;
+ a->port2 = b_port1 - 1;
+
+ b->port = b_port1;
+ b->port2 = b_port2;
+
+ DetectPort *tmp_c;
+ tmp_c = DetectPortInit();
+ if (tmp_c == NULL) {
+ goto error;
+ }
+ *c = tmp_c;
+
+ tmp_c->port = b_port2 + 1;
+ tmp_c->port2 = a_port2;
+
+ SigGroupHeadCopySigs(de_ctx,a->sh,&b->sh);
+ SigGroupHeadCopySigs(de_ctx,a->sh,&tmp_c->sh);
+
+ b->cnt += a->cnt;
+ tmp_c->cnt += a->cnt;
+ }
+ }
+
+ if (tmp != NULL) {
+ DetectPortFree(tmp);
+ }
+ return 0;
+
+error:
+ if (tmp != NULL)
+ DetectPortFree(tmp);
+ return -1;
+}
+
+/**
+ * \brief Function that cuts port groups implementing group negation
+ *
+ * \param a pointer to DetectPort "a"
+ * \param b pointer to DetectPort "b"
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * */
+static int DetectPortCutNot(DetectPort *a, DetectPort **b)
+{
+ uint16_t a_port1 = a->port;
+ uint16_t a_port2 = a->port2;
+
+ /* default to NULL */
+ *b = NULL;
+
+ if (a_port1 != 0x0000 && a_port2 != 0xFFFF) {
+ a->port = 0x0000;
+ a->port2 = a_port1 - 1;
+
+ DetectPort *tmp_b;
+ tmp_b = DetectPortInit();
+ if (tmp_b == NULL) {
+ goto error;
+ }
+
+ tmp_b->port = a_port2 + 1;
+ tmp_b->port2 = 0xFFFF;
+ *b = tmp_b;
+
+ } else if (a_port1 == 0x0000 && a_port2 != 0xFFFF) {
+ a->port = a_port2 + 1;
+ a->port2 = 0xFFFF;
+
+ } else if (a_port1 != 0x0000 && a_port2 == 0xFFFF) {
+ a->port = 0x0000;
+ a->port2 = a_port1 - 1;
+ } else {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Function that compare port groups
+ *
+ * \param a pointer to DetectPort "a"
+ * \param b pointer to DetectPort "b"
+ *
+ * \retval PORT_XX (Port enum value, XX is EQ, ES, EB, LE, etc)
+ * \retval PORT_ER on error
+ * */
+int DetectPortCmp(DetectPort *a, DetectPort *b)
+{
+ /* check any */
+ if ((a->flags & PORT_FLAG_ANY) && (b->flags & PORT_FLAG_ANY))
+ return PORT_EQ;
+ if ((a->flags & PORT_FLAG_ANY) && !(b->flags & PORT_FLAG_ANY))
+ return PORT_LT;
+ if (!(a->flags & PORT_FLAG_ANY) && (b->flags & PORT_FLAG_ANY))
+ return PORT_GT;
+
+ uint16_t a_port1 = a->port;
+ uint16_t a_port2 = a->port2;
+ uint16_t b_port1 = b->port;
+ uint16_t b_port2 = b->port2;
+
+ /* PORT_EQ */
+ if (a_port1 == b_port1 && a_port2 == b_port2) {
+ //SCLogDebug("PORT_EQ");
+ return PORT_EQ;
+ /* PORT_ES */
+ } else if (a_port1 >= b_port1 && a_port1 <= b_port2 && a_port2 <= b_port2) {
+ //SCLogDebug("PORT_ES");
+ return PORT_ES;
+ /* PORT_EB */
+ } else if (a_port1 <= b_port1 && a_port2 >= b_port2) {
+ //SCLogDebug("PORT_EB");
+ return PORT_EB;
+ } else if (a_port1 < b_port1 && a_port2 < b_port2 && a_port2 >= b_port1) {
+ //SCLogDebug("PORT_LE");
+ return PORT_LE;
+ } else if (a_port1 < b_port1 && a_port2 < b_port2) {
+ //SCLogDebug("PORT_LT");
+ return PORT_LT;
+ } else if (a_port1 > b_port1 && a_port1 <= b_port2 && a_port2 > b_port2) {
+ //SCLogDebug("PORT_GE");
+ return PORT_GE;
+ } else if (a_port1 > b_port2) {
+ //SCLogDebug("PORT_GT");
+ return PORT_GT;
+ } else {
+ /* should be unreachable */
+ BUG_ON(1);
+ }
+
+ return PORT_ER;
+}
+
+/**
+ * \brief Function that return a copy of DetectPort src
+ *
+ * \param de_ctx Pointer to the current Detection Engine Context
+ * \param src Pointer to a DetectPort group to copy
+ *
+ * \retval Pointer to a DetectPort instance (copy of src)
+ * \retval NULL on error
+ * */
+DetectPort *DetectPortCopy(DetectEngineCtx *de_ctx, DetectPort *src)
+{
+ if (src == NULL)
+ return NULL;
+
+ DetectPort *dst = DetectPortInit();
+ if (dst == NULL) {
+ goto error;
+ }
+
+ dst->port = src->port;
+ dst->port2 = src->port2;
+
+ if (src->next != NULL) {
+ dst->next = DetectPortCopy(de_ctx, src->next);
+ if (dst->next != NULL) {
+ dst->next->prev = dst;
+ }
+ }
+
+ return dst;
+error:
+ return NULL;
+}
+
+/**
+ * \brief Function that return a copy of DetectPort src sigs
+ *
+ * \param de_ctx Pointer to the current Detection Engine Context
+ * \param src Pointer to a DetectPort group to copy
+ *
+ * \retval Pointer to a DetectPort instance (copy of src)
+ * \retval NULL on error
+ * */
+DetectPort *DetectPortCopySingle(DetectEngineCtx *de_ctx,DetectPort *src)
+{
+ if (src == NULL)
+ return NULL;
+
+ DetectPort *dst = DetectPortInit();
+ if (dst == NULL) {
+ goto error;
+ }
+
+ dst->port = src->port;
+ dst->port2 = src->port2;
+
+ SigGroupHeadCopySigs(de_ctx,src->sh,&dst->sh);
+
+ return dst;
+error:
+ return NULL;
+}
+
+/**
+ * \brief Function Match to Match a port against a DetectPort group
+ *
+ * \param dp Pointer to DetectPort group where we try to match the port
+ * \param port To compare/match
+ *
+ * \retval 1 if port is in the range (it match)
+ * \retval 0 if port is not in the range
+ * */
+int DetectPortMatch(DetectPort *dp, uint16_t port)
+{
+ if (port >= dp->port &&
+ port <= dp->port2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Helper function that print the DetectPort info
+ * \retval none
+ */
+void DetectPortPrint(DetectPort *dp)
+{
+ if (dp == NULL)
+ return;
+
+ if (dp->flags & PORT_FLAG_ANY) {
+ SCLogDebug("=> port %p: ANY", dp);
+// printf("ANY");
+ } else {
+ SCLogDebug("=> port %p %" PRIu32 "-%" PRIu32 "", dp, dp->port, dp->port2);
+// printf("%" PRIu32 "-%" PRIu32 "", dp->port, dp->port2);
+ }
+}
+
+/**
+ * \brief Function that find the group matching address in a group head
+ *
+ * \param dp Pointer to DetectPort group where we try to find the group
+ * \param port port to search/lookup
+ *
+ * \retval Pointer to the DetectPort group of our port if it matched
+ * \retval NULL if port is not in the list
+ * */
+DetectPort *
+DetectPortLookupGroup(DetectPort *dp, uint16_t port)
+{
+ DetectPort *p = dp;
+
+ if (dp == NULL)
+ return NULL;
+
+ for ( ; p != NULL; p = p->next) {
+ if (DetectPortMatch(p,port) == 1) {
+ //SCLogDebug("match, port %" PRIu32 ", dp ", port);
+ //DetectPortPrint(p); SCLogDebug("");
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Function to join the source group to the target and its members
+ *
+ * \param de_ctx Pointer to the current Detection Engine Context
+ * \param target Pointer to DetectPort group where the source is joined
+ * \param source Pointer to DetectPort group that will join into the target
+ *
+ * \retval -1 on error
+ * \retval 0 on success
+ * */
+int DetectPortJoin(DetectEngineCtx *de_ctx, DetectPort *target,
+ DetectPort *source)
+{
+ if (target == NULL || source == NULL)
+ return -1;
+
+ target->cnt += source->cnt;
+ SigGroupHeadCopySigs(de_ctx,source->sh,&target->sh);
+
+ if (source->port < target->port)
+ target->port = source->port;
+
+ if (source->port2 > target->port2)
+ target->port2 = source->port2;
+
+ return 0;
+}
+
+/******************* parsing routines ************************/
+
+/**
+ * \brief Wrapper function that call the internal/real function
+ * to insert the new DetectPort
+ * \param head Pointer to the head of the DetectPort group list
+ * \param new Pointer to the new DetectPort group list
+ *
+ * \retval 1 inserted
+ * \retval 0 not inserted, memory of new is freed
+ * \retval -1 error
+ */
+static int DetectPortParseInsert(DetectPort **head, DetectPort *new)
+{
+ return DetectPortInsert(NULL, head, new);
+}
+
+/**
+ * \brief Function to parse and insert the string in the DetectPort head list
+ *
+ * \param head Pointer to the head of the DetectPort group list
+ * \param s Pointer to the port string
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+static int DetectPortParseInsertString(DetectPort **head, char *s)
+{
+ DetectPort *ad = NULL, *ad_any = NULL;
+ int r = 0;
+ char port_any = FALSE;
+
+ SCLogDebug("head %p, *head %p, s %s", head, *head, s);
+
+ /** parse the address */
+ ad = PortParse(s);
+ if (ad == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT," failed to parse port \"%s\"",s);
+ return -1;
+ }
+
+ if (ad->flags & PORT_FLAG_ANY) {
+ port_any = TRUE;
+ }
+
+ /** handle the not case, we apply the negation then insert the part(s) */
+ if (ad->flags & PORT_FLAG_NOT) {
+ DetectPort *ad2 = NULL;
+
+ if (DetectPortCutNot(ad, &ad2) < 0) {
+ goto error;
+ }
+
+ /** normally a 'not' will result in two ad's unless the 'not' is on the
+ * start or end of the address space(e.g. 0.0.0.0 or 255.255.255.255)
+ */
+ if (ad2 != NULL) {
+ if (DetectPortParseInsert(head, ad2) < 0) {
+ if (ad2 != NULL) SCFree(ad2);
+ goto error;
+ }
+ }
+ }
+
+ r = DetectPortParseInsert(head, ad);
+ if (r < 0)
+ goto error;
+
+ /** if any, insert 0.0.0.0/0 and ::/0 as well */
+ if (r == 1 && port_any == TRUE) {
+ SCLogDebug("inserting 0:65535 as port is \"any\"");
+
+ ad_any = PortParse("0:65535");
+ if (ad_any == NULL)
+ goto error;
+
+ if (DetectPortParseInsert(head, ad_any) < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ SCLogError(SC_ERR_PORT_PARSE_INSERT_STRING,"DetectPortParseInsertString error");
+ if (ad != NULL)
+ DetectPortCleanupList(ad);
+ if (ad_any != NULL)
+ DetectPortCleanupList(ad_any);
+ return -1;
+}
+
+/**
+ * \brief Parses a port string and updates the 2 port heads with the
+ * port groups.
+ *
+ * \todo We don't seem to be handling negated cases, like [port,![!port,port]],
+ * since we pass around negate without keeping a count of ! with depth.
+ * Can solve this by keeping a count of the negations with depth, so that
+ * an even no of negations would count as no negation and an odd no of
+ * negations would count as a negation.
+ *
+ * \param gh Pointer to the port group head that should hold port ranges
+ * that are not negated.
+ * \param ghn Pointer to the port group head that should hold port ranges
+ * that are negated.
+ * \param s Pointer to the character string holding the port to be
+ * parsed.
+ * \param negate Flag that indicates if the receieved address string is negated
+ * or not. 0 if it is not, 1 it it is.
+ *
+ * \retval 0 On successfully parsing.
+ * \retval -1 On failure.
+ */
+static int DetectPortParseDo(const DetectEngineCtx *de_ctx,
+ DetectPort **head, DetectPort **nhead,
+ char *s, int negate)
+{
+ size_t u = 0;
+ size_t x = 0;
+ int o_set = 0, n_set = 0, d_set = 0;
+ int range = 0;
+ int depth = 0;
+ size_t size = strlen(s);
+ char address[1024] = "";
+ char *rule_var_port = NULL;
+ int r = 0;
+
+ SCLogDebug("head %p, *head %p, negate %d", head, *head, negate);
+
+ for (u = 0, x = 0; u < size && x < sizeof(address); u++) {
+ address[x] = s[u];
+ x++;
+
+ if (s[u] == ':')
+ range = 1;
+
+ if (range == 1 && s[u] == '!') {
+ SCLogError(SC_ERR_NEGATED_VALUE_IN_PORT_RANGE,"Can't have a negated value in a range.");
+ return -1;
+ } else if (!o_set && s[u] == '!') {
+ SCLogDebug("negation encountered");
+ n_set = 1;
+ x--;
+ } else if (s[u] == '[') {
+ if (!o_set) {
+ o_set = 1;
+ x = 0;
+ }
+ depth++;
+ } else if (s[u] == ']') {
+ if (depth == 1) {
+ address[x - 1] = '\0';
+ SCLogDebug("Parsed port from DetectPortParseDo - %s", address);
+ x = 0;
+
+ r = DetectPortParseDo(de_ctx, head, nhead, address, negate? negate: n_set);
+ if (r == -1)
+ goto error;
+
+ n_set = 0;
+ }
+ depth--;
+ range = 0;
+ } else if (depth == 0 && s[u] == ',') {
+ if (o_set == 1) {
+ o_set = 0;
+ } else if (d_set == 1) {
+ char *temp_rule_var_port = NULL,
+ *alloc_rule_var_port = NULL;
+
+ address[x - 1] = '\0';
+
+ rule_var_port = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_PORT_GROUPS);
+ if (rule_var_port == NULL)
+ goto error;
+ if (strlen(rule_var_port) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "variable %s resolved "
+ "to nothing. This is likely a misconfiguration. "
+ "Note that a negated port needs to be quoted, "
+ "\"!$HTTP_PORTS\" instead of !$HTTP_PORTS. See issue #295.", s);
+ goto error;
+ }
+ temp_rule_var_port = rule_var_port;
+ if (negate == 1 || n_set == 1) {
+ alloc_rule_var_port = SCMalloc(strlen(rule_var_port) + 3);
+ if (unlikely(alloc_rule_var_port == NULL))
+ goto error;
+ snprintf(alloc_rule_var_port, strlen(rule_var_port) + 3,
+ "[%s]", rule_var_port);
+ temp_rule_var_port = alloc_rule_var_port;
+ }
+ r = DetectPortParseDo(de_ctx, head, nhead, temp_rule_var_port,
+ (negate + n_set) % 2);//negate? negate: n_set);
+ if (r == -1)
+ goto error;
+
+ d_set = 0;
+ n_set = 0;
+ if (alloc_rule_var_port != NULL)
+ SCFree(alloc_rule_var_port);
+ } else {
+ address[x - 1] = '\0';
+ SCLogDebug("Parsed port from DetectPortParseDo - %s", address);
+
+ if (negate == 0 && n_set == 0) {
+ r = DetectPortParseInsertString(head, address);
+ } else {
+ r = DetectPortParseInsertString(nhead, address);
+ }
+ if (r == -1)
+ goto error;
+
+ n_set = 0;
+ }
+ x = 0;
+ range = 0;
+ } else if (depth == 0 && s[u] == '$') {
+ d_set = 1;
+ } else if (depth == 0 && u == size-1) {
+ range = 0;
+ if (x == 1024) {
+ address[x - 1] = '\0';
+ } else {
+ address[x] = '\0';
+ }
+ SCLogDebug("%s", address);
+ x = 0;
+ if (d_set == 1) {
+ char *temp_rule_var_port = NULL,
+ *alloc_rule_var_port = NULL;
+
+ rule_var_port = SCRuleVarsGetConfVar(de_ctx, address,
+ SC_RULE_VARS_PORT_GROUPS);
+ if (rule_var_port == NULL)
+ goto error;
+ if (strlen(rule_var_port) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "variable %s resolved "
+ "to nothing. This is likely a misconfiguration. "
+ "Note that a negated port needs to be quoted, "
+ "\"!$HTTP_PORTS\" instead of !$HTTP_PORTS. See issue #295.", s);
+ goto error;
+ }
+ temp_rule_var_port = rule_var_port;
+ if ((negate + n_set) % 2) {
+ alloc_rule_var_port = SCMalloc(strlen(rule_var_port) + 3);
+ if (unlikely(alloc_rule_var_port == NULL))
+ goto error;
+ snprintf(alloc_rule_var_port, strlen(rule_var_port) + 3,
+ "[%s]", rule_var_port);
+ temp_rule_var_port = alloc_rule_var_port;
+ }
+ r = DetectPortParseDo(de_ctx, head, nhead, temp_rule_var_port,
+ (negate + n_set) % 2);
+ if (r == -1)
+ goto error;
+
+ d_set = 0;
+ if (alloc_rule_var_port != NULL)
+ SCFree(alloc_rule_var_port);
+ } else {
+ if (!((negate + n_set) % 2)) {
+ r = DetectPortParseInsertString(head,address);
+ } else {
+ r = DetectPortParseInsertString(nhead,address);
+ }
+ if (r == -1)
+ goto error;
+ }
+ n_set = 0;
+ }
+ }
+
+ if (depth > 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not every port block was "
+ "properly closed in \"%s\", %d missing closing brackets (]). "
+ "Note: problem might be in a variable.", s, depth);
+ goto error;
+ } else if (depth < 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "not every port block was "
+ "properly opened in \"%s\", %d missing opening brackets ([). "
+ "Note: problem might be in a variable.", s, depth*-1);
+ goto error;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * \brief Check if the port group list covers the complete port space.
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int DetectPortIsCompletePortSpace(DetectPort *p)
+{
+ uint16_t next_port = 0;
+
+ if (p == NULL)
+ return 0;
+
+ if (p->port != 0x0000)
+ return 0;
+
+ /* if we're ending with 0xFFFF while we know
+ we started with 0x0000 it's the complete space */
+ if (p->port2 == 0xFFFF)
+ return 1;
+
+ next_port = p->port2 + 1;
+ p = p->next;
+
+ for ( ; p != NULL; p = p->next) {
+ if (p->port != next_port)
+ return 0;
+
+ if (p->port2 == 0xFFFF)
+ return 1;
+
+ next_port = p->port2 + 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Helper function for the parsing process
+ *
+ * \param head Pointer to the head of the DetectPort group list
+ * \param nhead Pointer to the new head of the DetectPort group list
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int DetectPortParseMergeNotPorts(DetectPort **head, DetectPort **nhead)
+{
+ DetectPort *ad = NULL;
+ DetectPort *ag, *ag2;
+ int r = 0;
+
+ /** check if the full port space is negated */
+ if (DetectPortIsCompletePortSpace(*nhead) == 1) {
+ SCLogError(SC_ERR_COMPLETE_PORT_SPACE_NEGATED,"Complete port space is negated");
+ goto error;
+ }
+
+ /**
+ * step 0: if the head list is empty, but the nhead list isn't
+ * we have a pure not thingy. In that case we add a 0:65535
+ * first.
+ */
+ if (*head == NULL && *nhead != NULL) {
+ SCLogDebug("inserting 0:65535 into head");
+ r = DetectPortParseInsertString(head,"0:65535");
+ if (r < 0) {
+ goto error;
+ }
+ }
+
+ /** step 1: insert our ghn members into the gh list */
+ for (ag = *nhead; ag != NULL; ag = ag->next) {
+ /** work with a copy of the ad so we can easily clean up
+ * the ghn group later.
+ */
+ ad = DetectPortCopy(NULL, ag);
+ if (ad == NULL) {
+ goto error;
+ }
+ r = DetectPortParseInsert(head, ad);
+ if (r < 0) {
+ goto error;
+ }
+ ad = NULL;
+ }
+
+ /** step 2: pull the address blocks that match our 'not' blocks */
+ for (ag = *nhead; ag != NULL; ag = ag->next) {
+ SCLogDebug("ag %p", ag);
+ DetectPortPrint(ag);
+
+ for (ag2 = *head; ag2 != NULL; ) {
+ SCLogDebug("ag2 %p", ag2);
+ DetectPortPrint(ag2);
+
+ r = DetectPortCmp(ag, ag2);
+ if (r == PORT_EQ || r == PORT_EB) { /* XXX more ??? */
+ if (ag2->prev == NULL) {
+ *head = ag2->next;
+ } else {
+ ag2->prev->next = ag2->next;
+ }
+
+ if (ag2->next != NULL) {
+ ag2->next->prev = ag2->prev;
+ }
+ /** store the next ptr and remove the group */
+ DetectPort *next_ag2 = ag2->next;
+ DetectPortFree(ag2);
+ ag2 = next_ag2;
+ } else {
+ ag2 = ag2->next;
+ }
+ }
+ }
+
+ for (ag2 = *head; ag2 != NULL; ag2 = ag2->next) {
+ SCLogDebug("ag2 %p", ag2);
+ DetectPortPrint(ag2);
+ }
+
+ if (*head == NULL) {
+ SCLogError(SC_ERR_NO_PORTS_LEFT_AFTER_MERGE,"no ports left after merging ports with negated ports");
+ goto error;
+ }
+
+ return 0;
+error:
+ if (ad != NULL)
+ DetectPortFree(ad);
+ return -1;
+}
+
+int DetectPortTestConfVars(void)
+{
+ SCLogDebug("Testing port conf vars for any misconfigured values");
+
+ ConfNode *port_vars_node = ConfGetNode("vars.port-groups");
+ if (port_vars_node == NULL) {
+ return 0;
+ }
+
+ ConfNode *seq_node;
+ TAILQ_FOREACH(seq_node, &port_vars_node->head, next) {
+ SCLogDebug("Testing %s - %s\n", seq_node->name, seq_node->val);
+
+ DetectPort *gh = DetectPortInit();
+ if (gh == NULL) {
+ goto error;
+ }
+ DetectPort *ghn = NULL;
+
+ if (seq_node->val == NULL) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "Port var \"%s\" probably has a sequence(something "
+ "in brackets) value set without any quotes. Please "
+ "quote it using \"..\".", seq_node->name);
+ DetectPortCleanupList(gh);
+ goto error;
+ }
+
+ int r = DetectPortParseDo(NULL, &gh, &ghn, seq_node->val, /* start with negate no */0);
+ if (r < 0) {
+ DetectPortCleanupList(gh);
+ goto error;
+ }
+
+ if (DetectPortIsCompletePortSpace(ghn)) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "Port var - \"%s\" has the complete Port range negated "
+ "with it's value \"%s\". Port space range is NIL. "
+ "Probably have a !any or a port range that supplies "
+ "a NULL address range", seq_node->name, seq_node->val);
+ DetectPortCleanupList(gh);
+ DetectPortCleanupList(ghn);
+ goto error;
+ }
+
+ if (gh != NULL)
+ DetectPortCleanupList(gh);
+ if (ghn != NULL)
+ DetectPortCleanupList(ghn);
+ }
+
+ return 0;
+ error:
+ return -1;
+}
+
+
+/**
+ * \brief Function for parsing port strings
+ *
+ * \param head Pointer to the head of the DetectPort group list
+ * \param str Pointer to the port string
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int DetectPortParse(const DetectEngineCtx *de_ctx,
+ DetectPort **head, char *str)
+{
+ int r;
+
+ SCLogDebug("Port string to be parsed - str %s", str);
+
+ /* negate port list */
+ DetectPort *nhead = NULL;
+
+ r = DetectPortParseDo(de_ctx, head, &nhead, str,/* start with negate no */0);
+ if (r < 0)
+ goto error;
+
+ SCLogDebug("head %p %p, nhead %p", head, *head, nhead);
+
+ /* merge the 'not' address groups */
+ if (DetectPortParseMergeNotPorts(head, &nhead) < 0)
+ goto error;
+
+ /* free the temp negate head */
+ DetectPortCleanupList(nhead);
+ return 0;
+
+error:
+ DetectPortCleanupList(nhead);
+ return -1;
+}
+
+/**
+ * \brief Helper function for parsing port strings
+ *
+ * \param str Pointer to the port string
+ *
+ * \retval DetectPort pointer of the parse string on success
+ * \retval NULL on error
+ */
+DetectPort *PortParse(char *str)
+{
+ char *port2 = NULL;
+ DetectPort *dp = NULL;
+
+ char portstr[16];
+ strlcpy(portstr, str, sizeof(portstr));
+
+ dp = DetectPortInit();
+ if (dp == NULL)
+ goto error;
+
+ /* XXX better input validation */
+
+ /* we dup so we can put a nul-termination in it later */
+ char *port = portstr;
+
+ /* handle the negation case */
+ if (port[0] == '!') {
+ dp->flags |= PORT_FLAG_NOT;
+ port++;
+ }
+
+ /* see if the address is an ipv4 or ipv6 address */
+ if ((port2 = strchr(port, ':')) != NULL) {
+ /* 80:81 range format */
+ port2[0] = '\0';
+ port2++;
+
+ if(DetectPortIsValidRange(port))
+ dp->port = atoi(port);
+ else
+ goto error;
+
+ if (strcmp(port2, "") != 0) {
+ if (DetectPortIsValidRange(port2))
+ dp->port2 = atoi(port2);
+ else
+ goto error;
+ } else {
+ dp->port2 = 65535;
+ }
+
+ /* a > b is illegal, a == b is ok */
+ if (dp->port > dp->port2)
+ goto error;
+ } else {
+ if (strcasecmp(port,"any") == 0) {
+ dp->port = 0;
+ dp->port2 = 65535;
+ } else if(DetectPortIsValidRange(port)){
+ dp->port = dp->port2 = atoi(port);
+ } else {
+ goto error;
+ }
+ }
+
+ return dp;
+
+error:
+ if (dp != NULL)
+ DetectPortCleanupList(dp);
+ return NULL;
+}
+
+/**
+ * \brief Helper function to check if a parsed port is in the valid range
+ * of available ports
+ *
+ * \param str Pointer to the port string
+ *
+ * \retval 1 if port is in the valid range
+ * \retval 0 if invalid
+ */
+int DetectPortIsValidRange(char *port)
+{
+ if(atoi(port) >= 0 && atoi(port) <= 65535)
+ return 1;
+ else
+ return 0;
+}
+/********************** End parsing routines ********************/
+
+
+/********************* Hash function routines *******************/
+#define PORT_HASH_SIZE 1024
+
+/**
+ * \brief Generate a hash for a DetectPort group
+ *
+ * \param ht HashListTable
+ * \param data Pointer to the DetectPort
+ * \param datalen sizeof data (not used here atm)
+ *
+ * \retval uint32_t the value of the generated hash
+ */
+uint32_t DetectPortHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ DetectPort *p = (DetectPort *)data;
+ uint32_t hash = p->port * p->port2;
+
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief Function that return if the two DetectPort groups are equal or not
+ *
+ * \param data1 Pointer to the DetectPort 1
+ * \param len1 sizeof data 1 (not used here atm)
+ * \param data2 Pointer to the DetectPort 2
+ * \param len2 sizeof data 2 (not used here atm)
+ *
+ * \retval 1 if the DetectPort groups are equal
+ * \retval 0 if not equal
+ */
+char DetectPortCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ DetectPort *p1 = (DetectPort *)data1;
+ DetectPort *p2 = (DetectPort *)data2;
+
+ if (p1->port2 == p2->port2 && p1->port == p2->port &&
+ p1->flags == p2->flags)
+ return 1;
+
+ return 0;
+}
+
+void DetectPortFreeFunc(void *p)
+{
+ DetectPort *dp = (DetectPort *)p;
+ DetectPortFree(dp);
+}
+
+/**
+ * \brief Function that initialize the HashListTable of destination DetectPort
+ *
+ * \param de_ctx Pointer to the current DetectionEngineContext
+ *
+ * \retval 0 HashListTable initialization succes
+ * \retval -1 Error
+ */
+int DetectPortDpHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->dport_hash_table = HashListTableInit(PORT_HASH_SIZE,
+ DetectPortHashFunc, DetectPortCompareFunc, DetectPortFreeFunc);
+ if (de_ctx->dport_hash_table == NULL)
+ goto error;
+
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * \brief Function that free the HashListTable of destination DetectPort
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ */
+void DetectPortDpHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->dport_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->dport_hash_table);
+ de_ctx->dport_hash_table = NULL;
+}
+
+/**
+ * \brief Function that reset the HashListTable of destination DetectPort
+ * (Free and Initialize it)
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ */
+void DetectPortDpHashReset(DetectEngineCtx *de_ctx)
+{
+ DetectPortDpHashFree(de_ctx);
+ DetectPortDpHashInit(de_ctx);
+}
+
+/**
+ * \brief Function that add a destination DetectPort into the hashtable
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ * \param p Pointer to the DetectPort to add
+ */
+int DetectPortDpHashAdd(DetectEngineCtx *de_ctx, DetectPort *p)
+{
+ return HashListTableAdd(de_ctx->dport_hash_table, (void *)p, 0);
+}
+
+/**
+ * \brief Function that search a destination DetectPort in the hashtable
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ * \param p Pointer to the DetectPort to search
+ */
+DetectPort *DetectPortDpHashLookup(DetectEngineCtx *de_ctx, DetectPort *p)
+{
+ DetectPort *rp = HashListTableLookup(de_ctx->dport_hash_table,
+ (void *)p, 0);
+ return rp;
+}
+
+/**
+ * \brief Function that initialize the HashListTable of source DetectPort
+ *
+ * \param de_ctx Pointer to the current DetectionEngineContext
+ *
+ * \retval 0 HashListTable initialization succes
+ * \retval -1 Error
+ */
+int DetectPortSpHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sport_hash_table = HashListTableInit(PORT_HASH_SIZE,
+ DetectPortHashFunc, DetectPortCompareFunc, DetectPortFreeFunc);
+ if (de_ctx->sport_hash_table == NULL)
+ goto error;
+
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * \brief Function that free the HashListTable of source DetectPort
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ */
+void DetectPortSpHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sport_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sport_hash_table);
+ de_ctx->sport_hash_table = NULL;
+}
+
+/**
+ * \brief Function that reset the HashListTable of source DetectPort
+ * (Free and Initialize it)
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ */
+void DetectPortSpHashReset(DetectEngineCtx *de_ctx)
+{
+ DetectPortSpHashFree(de_ctx);
+ DetectPortSpHashInit(de_ctx);
+}
+
+/**
+ * \brief Function that add a source DetectPort into the hashtable
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ * \param p Pointer to the DetectPort to add
+ */
+int DetectPortSpHashAdd(DetectEngineCtx *de_ctx, DetectPort *p)
+{
+ return HashListTableAdd(de_ctx->sport_hash_table, (void *)p, 0);
+}
+
+/**
+ * \brief Function that search a source DetectPort in the hashtable
+ *
+ * \param de_ctx Pointer to the current DetectionEngineCtx
+ * \param p Pointer to the DetectPort to search
+ */
+DetectPort *DetectPortSpHashLookup(DetectEngineCtx *de_ctx, DetectPort *p)
+{
+ DetectPort *rp = HashListTableLookup(de_ctx->sport_hash_table,
+ (void *)p, 0);
+ return rp;
+}
+
+/*---------------------- Unittests -------------------------*/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Check if a DetectPort is properly allocated
+ */
+int PortTestParse01 (void)
+{
+ DetectPort *dd = NULL;
+
+ int r = DetectPortParse(NULL,&dd,"80");
+ if (r == 0) {
+ DetectPortFree(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test Check if two ports are properly allocated in the DetectPort group
+ */
+int PortTestParse02 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"80");
+ if (r == 0) {
+ r = DetectPortParse(NULL,&dd,"22");
+ if (r == 0) {
+ result = 1;
+ }
+
+ DetectPortCleanupList(dd);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * \test Check if two port ranges are properly allocated in the DetectPort group
+ */
+int PortTestParse03 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"80:88");
+ if (r == 0) {
+ r = DetectPortParse(NULL,&dd,"85:100");
+ if (r == 0) {
+ result = 1;
+ }
+
+ DetectPortCleanupList(dd);
+
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * \test Check if a negated port range is properly allocated in the DetectPort
+ */
+int PortTestParse04 (void)
+{
+ DetectPort *dd = NULL;
+
+ int r = DetectPortParse(NULL,&dd,"!80:81");
+ if (r == 0) {
+ DetectPortCleanupList(dd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test Check if a negated port range is properly fragmented in the allowed
+ * real groups, ex !80:81 should allow 0:79 and 82:65535
+ */
+int PortTestParse05 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"!80:81");
+ if (r != 0)
+ goto end;
+
+ if (dd->next == NULL)
+ goto end;
+
+ if (dd->port != 0 || dd->port2 != 79)
+ goto end;
+
+ if (dd->next->port != 82 || dd->next->port2 != 65535)
+ goto end;
+
+ DetectPortCleanupList(dd);
+ result = 1;
+end:
+ return result;
+}
+
+/**
+ * \test Check if we copy a DetectPort correctly
+ */
+int PortTestParse06 (void)
+{
+ DetectPort *dd = NULL, *copy = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"22");
+ if (r != 0)
+ goto end;
+
+ r = DetectPortParse(NULL,&dd,"80");
+ if (r != 0)
+ goto end;
+
+ r = DetectPortParse(NULL,&dd,"143");
+ if (r != 0)
+ goto end;
+
+ copy = DetectPortCopy(NULL,dd);
+ if (copy == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd,copy) != PORT_EQ)
+ goto end;
+
+ if (copy->next == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd->next,copy->next) != PORT_EQ)
+ goto end;
+
+ if (copy->next->next == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd->next->next,copy->next->next) != PORT_EQ)
+ goto end;
+
+ if (copy->port != 22 || copy->next->port != 80 ||
+ copy->next->next->port != 143)
+ goto end;
+
+ result = 1;
+
+end:
+ if (copy != NULL)
+ DetectPortCleanupList(copy);
+ if (dd != NULL)
+ DetectPortCleanupList(dd);
+ return result;
+}
+
+/**
+ * \test Check if a negated port range is properly fragmented in the allowed
+ * real groups
+ */
+int PortTestParse07 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"!21:902");
+ if (r != 0)
+ goto end;
+
+ if (dd->next == NULL)
+ goto end;
+
+ if (dd->port != 0 || dd->port2 != 20)
+ goto end;
+
+ if (dd->next->port != 903 || dd->next->port2 != 65535)
+ goto end;
+
+ DetectPortCleanupList(dd);
+ result = 1;
+end:
+ return result;
+}
+
+/**
+ * \test Check if we dont allow invalid port range specification
+ */
+int PortTestParse08 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"[80:!80]");
+ if (r == 0)
+ goto end;
+
+ DetectPortCleanupList(dd);
+ result = 1;
+end:
+ return result;
+}
+
+/**
+ * \test Check if we autocomplete correctly an open range
+ */
+int PortTestParse09 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"1024:");
+ if (r != 0)
+ goto end;
+
+ if (dd == NULL)
+ goto end;
+
+ if (dd->port != 1024 || dd->port2 != 0xffff)
+ goto end;
+
+ DetectPortCleanupList(dd);
+ result = 1;
+end:
+ return result;
+}
+
+/**
+ * \test Test we don't allow a port that is too big
+ */
+int PortTestParse10 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"77777777777777777777777777777777777777777777");
+ if (r != 0) {
+ result = 1 ;
+ goto end;
+ }
+
+ DetectPortFree(dd);
+
+end:
+ return result;
+}
+
+/**
+ * \test Test second port of range being too big
+ */
+int PortTestParse11 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"1024:65536");
+ if (r != 0) {
+ result = 1 ;
+ goto end;
+ }
+
+ DetectPortFree(dd);
+
+end:
+ return result;
+}
+
+/**
+ * \test Test second port of range being just right
+ */
+int PortTestParse12 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"1024:65535");
+ if (r != 0) {
+ goto end;
+ }
+
+ DetectPortFree(dd);
+
+ result = 1 ;
+end:
+ return result;
+}
+
+/**
+ * \test Test first port of range being too big
+ */
+int PortTestParse13 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"65536:65535");
+ if (r != 0) {
+ result = 1 ;
+ goto end;
+ }
+
+ DetectPortFree(dd);
+
+end:
+ return result;
+}
+
+/**
+ * \test Test merging port groups
+ */
+int PortTestParse14 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParseInsertString(&dd, "0:100");
+ if (r != 0)
+ goto end;
+ r = DetectPortParseInsertString(&dd, "1000:65535");
+ if (r != 0 || dd->next == NULL)
+ goto end;
+
+ result = 1;
+ result &= (dd->port == 0) ? 1 : 0;
+ result &= (dd->port2 == 100) ? 1 : 0;
+ result &= (dd->next->port == 1000) ? 1 : 0;
+ result &= (dd->next->port2 == 65535) ? 1 : 0;
+
+ DetectPortFree(dd);
+
+end:
+ return result;
+}
+
+/**
+ * \test Test merging negated port groups
+ */
+int PortTestParse15 (void)
+{
+ DetectPort *dd = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"![0:100,1000:3000]");
+ if (r != 0 || dd->next == NULL)
+ goto end;
+
+ result = 1;
+ result &= (dd->port == 101) ? 1 : 0;
+ result &= (dd->port2 == 999) ? 1 : 0;
+ result &= (dd->next->port == 3001) ? 1 : 0;
+ result &= (dd->next->port2 == 65535) ? 1 : 0;
+
+ DetectPortFree(dd);
+
+end:
+ return result;
+}
+
+/**
+ * \test Test parse, copy and cmp functions
+ */
+int PortTestParse16 (void)
+{
+ DetectPort *dd = NULL, *copy = NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL,&dd,"22");
+ if (r != 0)
+ goto end;
+
+ r = DetectPortParse(NULL,&dd,"80");
+ if (r != 0)
+ goto end;
+
+ r = DetectPortParse(NULL,&dd,"143");
+ if (r != 0)
+ goto end;
+
+ copy = DetectPortCopy(NULL,dd);
+ if (copy == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd,copy) != PORT_EQ)
+ goto end;
+
+ if (copy->next == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd->next,copy->next) != PORT_EQ)
+ goto end;
+
+ if (copy->next->next == NULL)
+ goto end;
+
+ if (DetectPortCmp(dd->next->next,copy->next->next) != PORT_EQ)
+ goto end;
+
+ if (copy->port != 22 || copy->next->port != 80 || copy->next->next->port != 143)
+ goto end;
+
+ if (copy->next->prev != copy)
+ goto end;
+
+ result = 1;
+
+end:
+ if (copy != NULL)
+ DetectPortCleanupList(copy);
+ if (dd != NULL)
+ DetectPortCleanupList(dd);
+ return result;
+}
+/**
+ * \test Test general functions
+ */
+int PortTestFunctions01(void)
+{
+ DetectPort *head = NULL;
+ DetectPort *dp1= NULL;
+ int result = 0;
+
+ /* Parse */
+ int r = DetectPortParse(NULL,&head,"![0:100,1000:65535]");
+ if (r != 0 || head->next != NULL)
+ goto end;
+
+ /* We should have only one DetectPort */
+ if (!(head->port == 101))
+ goto end;
+ if (!(head->port2 == 999))
+ goto end;
+ if (!(head->next == NULL))
+ goto end;
+
+ r = DetectPortParse(NULL, &dp1,"2000:3000");
+ if (r != 0 || dp1->next != NULL)
+ goto end;
+ if (!(dp1->port == 2000))
+ goto end;
+ if (!(dp1->port2 == 3000))
+ goto end;
+
+ /* Add */
+ r = DetectPortAdd(&head, dp1);
+ if (r != 0 || head->next == NULL)
+ goto end;
+ if (!(head->port == 101))
+ goto end;
+ if (!(head->port2 == 999))
+ goto end;
+ if (!(head->next->port == 2000))
+ goto end;
+ if (!(head->next->port2 == 3000))
+ goto end;
+
+ /* Match */
+ if (!DetectPortMatch(head, 150))
+ goto end;
+ if (DetectPortMatch(head->next, 1500))
+ goto end;
+ if ((DetectPortMatch(head, 3500)))
+ goto end;
+ if ((DetectPortMatch(head, 50)))
+ goto end;
+
+ result = 1;
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (head != NULL)
+ DetectPortFree(head);
+ return result;
+}
+
+/**
+ * \test Test general functions
+ */
+int PortTestFunctions02(void)
+{
+ DetectPort *head = NULL;
+ DetectPort *dp1= NULL;
+ DetectPort *dp2= NULL;
+ int result = 0;
+
+ /* Parse */
+ int r = DetectPortParse(NULL,&head, "![0:100,1000:65535]");
+ if (r != 0 || head->next != NULL)
+ goto end;
+
+ r = DetectPortParse(NULL, &dp1, "!200:300");
+ if (r != 0 || dp1->next == NULL)
+ goto end;
+
+ /* Merge Nots */
+ r = DetectPortParseMergeNotPorts(&head, &dp1);
+ if (r != 0 || head->next != NULL)
+ goto end;
+
+ r = DetectPortParse(NULL, &dp2, "!100:500");
+ if (r != 0 || dp2->next == NULL)
+ goto end;
+
+ /* Merge Nots */
+ r = DetectPortParseMergeNotPorts(&head, &dp2);
+ if (r != 0 || head->next != NULL)
+ goto end;
+
+ if (!(head->port == 200))
+ goto end;
+ if (!(head->port2 == 300))
+ goto end;
+
+ result = 1;
+
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (dp2 != NULL)
+ DetectPortFree(dp2);
+ if (head != NULL)
+ DetectPortFree(head);
+ return result;
+}
+
+/**
+ * \test Test general functions
+ */
+int PortTestFunctions03(void)
+{
+ DetectPort *dp1= NULL;
+ DetectPort *dp2= NULL;
+ DetectPort *dp3= NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL, &dp1, "200:300");
+ if (r != 0)
+ goto end;
+
+ r = DetectPortParse(NULL, &dp2, "250:300");
+ if (r != 0)
+ goto end;
+
+ /* Cut */
+ DetectPortCut(NULL, dp1, dp2, &dp3);
+ if (r != 0)
+ goto end;
+
+ if (!(dp1->port == 200))
+ goto end;
+ if (!(dp1->port2 == 249))
+ goto end;
+ if (!(dp2->port == 250))
+ goto end;
+ if (!(dp2->port2 == 300))
+ goto end;
+
+ dp1->port = 0;
+ dp1->port2 = 500;
+ dp2->port = 250;
+ dp2->port2 = 750;
+
+ /* Cut */
+ DetectPortCut(NULL, dp1, dp2, &dp3);
+ if (r != 0)
+ goto end;
+ if (!(dp1->port == 0))
+ goto end;
+ if (!(dp1->port2 == 249))
+ goto end;
+ if (!(dp2->port == 250))
+ goto end;
+ if (!(dp2->port2 == 500))
+ goto end;
+ if (!(dp3->port == 501))
+ goto end;
+ if (!(dp3->port2 == 750))
+ goto end;
+
+ result = 1;
+
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (dp2 != NULL)
+ DetectPortFree(dp2);
+ if (dp3 != NULL)
+ DetectPortFree(dp3);
+ return result;
+}
+
+/**
+ * \test Test general functions
+ */
+int PortTestFunctions04(void)
+{
+ DetectPort *dp1= NULL;
+ DetectPort *dp2= NULL;
+ int result = 0;
+
+ int r = DetectPortParse(NULL, &dp1, "200:300");
+ if (r != 0)
+ goto end;
+
+ dp2 = DetectPortInit();
+
+ /* Cut Not */
+ DetectPortCutNot(dp1, &dp2);
+ if (r != 0)
+ goto end;
+
+ if (!(dp1->port == 0))
+ goto end;
+ if (!(dp1->port2 == 199))
+ goto end;
+ if (!(dp2->port == 301))
+ goto end;
+ if (!(dp2->port2 == 65535))
+ goto end;
+
+ result = 1;
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (dp2 != NULL)
+ DetectPortFree(dp2);
+ return result;
+}
+
+/**
+ * \test Test general functions
+ */
+static int PortTestFunctions05(void)
+{
+ DetectPort *dp1 = NULL;
+ DetectPort *dp2 = NULL;
+ DetectPort *dp3 = NULL;
+ int result = 0;
+ int r = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ r = DetectPortParse(NULL, &dp1, "1024:65535");
+ if (r != 0) {
+ printf("r != 0 but %d: ", r);
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &dp1->sh, &s[0]);
+
+ r = DetectPortParse(NULL, &dp2, "any");
+ if (r != 0) {
+ printf("r != 0 but %d: ", r);
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &dp2->sh, &s[1]);
+
+ SCLogDebug("dp1");
+ DetectPortPrint(dp1);
+ SCLogDebug("dp2");
+ DetectPortPrint(dp2);
+
+ DetectPortInsert(de_ctx, &dp3, dp1);
+ DetectPortInsert(de_ctx, &dp3, dp2);
+
+ if (dp3 == NULL)
+ goto end;
+
+ SCLogDebug("dp3");
+ DetectPort *x = dp3;
+ for ( ; x != NULL; x = x->next) {
+ DetectPortPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectPort *one = dp3;
+ DetectPort *two = dp3->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (dp2 != NULL)
+ DetectPortFree(dp2);
+ return result;
+}
+
+/**
+ * \test Test general functions
+ */
+static int PortTestFunctions06(void)
+{
+ DetectPort *dp1 = NULL;
+ DetectPort *dp2 = NULL;
+ DetectPort *dp3 = NULL;
+ int result = 0;
+ int r = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature s[2];
+ memset(s,0x00,sizeof(s));
+
+ s[0].num = 0;
+ s[1].num = 1;
+
+ r = DetectPortParse(NULL, &dp1, "1024:65535");
+ if (r != 0) {
+ printf("r != 0 but %d: ", r);
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &dp1->sh, &s[0]);
+
+ r = DetectPortParse(NULL, &dp2, "any");
+ if (r != 0) {
+ printf("r != 0 but %d: ", r);
+ goto end;
+ }
+ SigGroupHeadAppendSig(de_ctx, &dp2->sh, &s[1]);
+
+ SCLogDebug("dp1");
+ DetectPortPrint(dp1);
+ SCLogDebug("dp2");
+ DetectPortPrint(dp2);
+
+ DetectPortInsert(de_ctx, &dp3, dp2);
+ DetectPortInsert(de_ctx, &dp3, dp1);
+
+ if (dp3 == NULL)
+ goto end;
+
+ SCLogDebug("dp3");
+ DetectPort *x = dp3;
+ for ( ; x != NULL; x = x->next) {
+ DetectPortPrint(x);
+ //SigGroupHeadPrintSigs(de_ctx, x->sh);
+ }
+
+ DetectPort *one = dp3;
+ DetectPort *two = dp3->next;
+
+ int sig = 0;
+ if ((one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(one->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'one', but it shouldn't: ", sig);
+ goto end;
+ }
+ sig = 1;
+ if (!(two->sh->init->sig_array[sig / 8] & (1 << (sig % 8)))) {
+ printf("sig %d part of 'two', but it shouldn't: ", sig);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (dp1 != NULL)
+ DetectPortFree(dp1);
+ if (dp2 != NULL)
+ DetectPortFree(dp2);
+ return result;
+}
+
+/**
+ * \test Test packet Matches
+ * \param raw_eth_pkt pointer to the ethernet packet
+ * \param pktsize size of the packet
+ * \param sig pointer to the signature to test
+ * \param sid sid number of the signature
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+int PortTestMatchReal(uint8_t *raw_eth_pkt, uint16_t pktsize, char *sig,
+ uint32_t sid)
+{
+ int result = 0;
+ FlowInitConfig(FLOW_QUIET);
+ Packet *p = UTHBuildPacketFromEth(raw_eth_pkt, pktsize);
+ result = UTHPacketMatchSig(p, sig);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ return result;
+}
+
+/**
+ * \brief Wrapper for PortTestMatchReal
+ */
+int PortTestMatchRealWrp(char *sig, uint32_t sid)
+{
+ /* Real HTTP packeth doing a GET method
+ * tcp.sport=47370 tcp.dport=80
+ * ip.src=192.168.28.131 ip.dst=192.168.1.1
+ */
+ uint8_t raw_eth_pkt[] = {
+ 0x00,0x50,0x56,0xea,0x00,0xbd,0x00,0x0c,
+ 0x29,0x40,0xc8,0xb5,0x08,0x00,0x45,0x00,
+ 0x01,0xa8,0xb9,0xbb,0x40,0x00,0x40,0x06,
+ 0xe0,0xbf,0xc0,0xa8,0x1c,0x83,0xc0,0xa8,
+ 0x01,0x01,0xb9,0x0a,0x00,0x50,0x6f,0xa2,
+ 0x92,0xed,0x7b,0xc1,0xd3,0x4d,0x50,0x18,
+ 0x16,0xd0,0xa0,0x6f,0x00,0x00,0x47,0x45,
+ 0x54,0x20,0x2f,0x20,0x48,0x54,0x54,0x50,
+ 0x2f,0x31,0x2e,0x31,0x0d,0x0a,0x48,0x6f,
+ 0x73,0x74,0x3a,0x20,0x31,0x39,0x32,0x2e,
+ 0x31,0x36,0x38,0x2e,0x31,0x2e,0x31,0x0d,
+ 0x0a,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,
+ 0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,
+ 0x69,0x6c,0x6c,0x61,0x2f,0x35,0x2e,0x30,
+ 0x20,0x28,0x58,0x31,0x31,0x3b,0x20,0x55,
+ 0x3b,0x20,0x4c,0x69,0x6e,0x75,0x78,0x20,
+ 0x78,0x38,0x36,0x5f,0x36,0x34,0x3b,0x20,
+ 0x65,0x6e,0x2d,0x55,0x53,0x3b,0x20,0x72,
+ 0x76,0x3a,0x31,0x2e,0x39,0x2e,0x30,0x2e,
+ 0x31,0x34,0x29,0x20,0x47,0x65,0x63,0x6b,
+ 0x6f,0x2f,0x32,0x30,0x30,0x39,0x30,0x39,
+ 0x30,0x32,0x31,0x37,0x20,0x55,0x62,0x75,
+ 0x6e,0x74,0x75,0x2f,0x39,0x2e,0x30,0x34,
+ 0x20,0x28,0x6a,0x61,0x75,0x6e,0x74,0x79,
+ 0x29,0x20,0x46,0x69,0x72,0x65,0x66,0x6f,
+ 0x78,0x2f,0x33,0x2e,0x30,0x2e,0x31,0x34,
+ 0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,0x74,
+ 0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,
+ 0x74,0x6d,0x6c,0x2c,0x61,0x70,0x70,0x6c,
+ 0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x2f,
+ 0x78,0x68,0x74,0x6d,0x6c,0x2b,0x78,0x6d,
+ 0x6c,0x2c,0x61,0x70,0x70,0x6c,0x69,0x63,
+ 0x61,0x74,0x69,0x6f,0x6e,0x2f,0x78,0x6d,
+ 0x6c,0x3b,0x71,0x3d,0x30,0x2e,0x39,0x2c,
+ 0x2a,0x2f,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x38,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x4c,0x61,0x6e,0x67,0x75,0x61,
+ 0x67,0x65,0x3a,0x20,0x65,0x6e,0x2d,0x75,
+ 0x73,0x2c,0x65,0x6e,0x3b,0x71,0x3d,0x30,
+ 0x2e,0x35,0x0d,0x0a,0x41,0x63,0x63,0x65,
+ 0x70,0x74,0x2d,0x45,0x6e,0x63,0x6f,0x64,
+ 0x69,0x6e,0x67,0x3a,0x20,0x67,0x7a,0x69,
+ 0x70,0x2c,0x64,0x65,0x66,0x6c,0x61,0x74,
+ 0x65,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x43,0x68,0x61,0x72,0x73,0x65,
+ 0x74,0x3a,0x20,0x49,0x53,0x4f,0x2d,0x38,
+ 0x38,0x35,0x39,0x2d,0x31,0x2c,0x75,0x74,
+ 0x66,0x2d,0x38,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x2c,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x0d,0x0a,0x4b,0x65,0x65,0x70,0x2d,
+ 0x41,0x6c,0x69,0x76,0x65,0x3a,0x20,0x33,
+ 0x30,0x30,0x0d,0x0a,0x43,0x6f,0x6e,0x6e,
+ 0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,
+ 0x6b,0x65,0x65,0x70,0x2d,0x61,0x6c,0x69,
+ 0x76,0x65,0x0d,0x0a,0x0d,0x0a };
+ /* end raw_eth_pkt */
+
+ return PortTestMatchReal(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
+ sig, sid);
+}
+
+/**
+ * \test Check if we match a dest port
+ */
+int PortTestMatchReal01()
+{
+ /* tcp.sport=47370 tcp.dport=80 */
+ char *sig = "alert tcp any any -> any 80 (msg:\"Nothing..\"; content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match a source port
+ */
+int PortTestMatchReal02()
+{
+ char *sig = "alert tcp any 47370 -> any any (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match both of them
+ */
+int PortTestMatchReal03()
+{
+ char *sig = "alert tcp any 47370 -> any 80 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we negate dest ports correctly
+ */
+int PortTestMatchReal04()
+{
+ char *sig = "alert tcp any any -> any !80 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we negate source ports correctly
+ */
+int PortTestMatchReal05()
+{
+ char *sig = "alert tcp any !47370 -> any any (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we negate both ports correctly
+ */
+int PortTestMatchReal06()
+{
+ char *sig = "alert tcp any !47370 -> any !80 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we match a dest port range
+ */
+int PortTestMatchReal07()
+{
+ char *sig = "alert tcp any any -> any 70:100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match a source port range
+ */
+int PortTestMatchReal08()
+{
+ char *sig = "alert tcp any 47000:50000 -> any any (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match both port ranges
+ */
+int PortTestMatchReal09()
+{
+ char *sig = "alert tcp any 47000:50000 -> any 70:100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we negate a dest port range
+ */
+int PortTestMatchReal10()
+{
+ char *sig = "alert tcp any any -> any !70:100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we negate a source port range
+ */
+int PortTestMatchReal11()
+{
+ char *sig = "alert tcp any !47000:50000 -> any any (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we negate both port ranges
+ */
+int PortTestMatchReal12()
+{
+ char *sig = "alert tcp any !47000:50000 -> any !70:100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we autocomplete ranges correctly
+ */
+int PortTestMatchReal13()
+{
+ char *sig = "alert tcp any 47000:50000 -> any !81: (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we autocomplete ranges correctly
+ */
+int PortTestMatchReal14()
+{
+ char *sig = "alert tcp any !48000:50000 -> any :100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we autocomplete ranges correctly
+ */
+int PortTestMatchReal15()
+{
+ char *sig = "alert tcp any :50000 -> any 81:100 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we separate ranges correctly
+ */
+int PortTestMatchReal16()
+{
+ char *sig = "alert tcp any 100: -> any ![0:79,81:65535] (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we separate ranges correctly
+ */
+int PortTestMatchReal17()
+{
+ char *sig = "alert tcp any ![0:39999,48000:50000] -> any ![0:80,82:65535] "
+ "(msg:\"Nothing..\"; content:\"GET\"; sid:1;)";
+ return (PortTestMatchRealWrp(sig, 1) == 0)? 1 : 0;
+}
+
+/**
+ * \test Check if we separate ranges correctly
+ */
+int PortTestMatchReal18()
+{
+ char *sig = "alert tcp any ![0:39999,48000:50000] -> any 80 (msg:\"Nothing"
+ " at all\"; content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+/**
+ * \test Check if we separate ranges correctly
+ */
+int PortTestMatchReal19()
+{
+ char *sig = "alert tcp any any -> any 80 (msg:\"Nothing..\";"
+ " content:\"GET\"; sid:1;)";
+ return PortTestMatchRealWrp(sig, 1);
+}
+
+static int PortTestMatchDoubleNegation(void)
+{
+ int result = 0;
+ DetectPort *head = NULL, *nhead = NULL;
+
+ if (DetectPortParseDo(NULL, &head, &nhead, "![!80]", 0) == -1)
+ return result;
+
+ result = (head != NULL);
+ result = (nhead == NULL);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectPortTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("PortTestParse01", PortTestParse01, 1);
+ UtRegisterTest("PortTestParse02", PortTestParse02, 1);
+ UtRegisterTest("PortTestParse03", PortTestParse03, 1);
+ UtRegisterTest("PortTestParse04", PortTestParse04, 1);
+ UtRegisterTest("PortTestParse05", PortTestParse05, 1);
+ UtRegisterTest("PortTestParse06", PortTestParse06, 1);
+ UtRegisterTest("PortTestParse07", PortTestParse07, 1);
+ UtRegisterTest("PortTestParse08", PortTestParse08, 1);
+ UtRegisterTest("PortTestParse09", PortTestParse09, 1);
+ UtRegisterTest("PortTestParse10", PortTestParse10, 1);
+ UtRegisterTest("PortTestParse11", PortTestParse11, 1);
+ UtRegisterTest("PortTestParse12", PortTestParse12, 1);
+ UtRegisterTest("PortTestParse13", PortTestParse13, 1);
+ UtRegisterTest("PortTestParse14", PortTestParse14, 1);
+ UtRegisterTest("PortTestParse15", PortTestParse15, 1);
+ UtRegisterTest("PortTestParse16", PortTestParse16, 1);
+ UtRegisterTest("PortTestFunctions01", PortTestFunctions01, 1);
+ UtRegisterTest("PortTestFunctions02", PortTestFunctions02, 1);
+ UtRegisterTest("PortTestFunctions03", PortTestFunctions03, 1);
+ UtRegisterTest("PortTestFunctions04", PortTestFunctions04, 1);
+ UtRegisterTest("PortTestFunctions05", PortTestFunctions05, 1);
+ UtRegisterTest("PortTestFunctions06", PortTestFunctions06, 1);
+ UtRegisterTest("PortTestMatchReal01", PortTestMatchReal01, 1);
+ UtRegisterTest("PortTestMatchReal02", PortTestMatchReal02, 1);
+ UtRegisterTest("PortTestMatchReal03", PortTestMatchReal03, 1);
+ UtRegisterTest("PortTestMatchReal04", PortTestMatchReal04, 1);
+ UtRegisterTest("PortTestMatchReal05", PortTestMatchReal05, 1);
+ UtRegisterTest("PortTestMatchReal06", PortTestMatchReal06, 1);
+ UtRegisterTest("PortTestMatchReal07", PortTestMatchReal07, 1);
+ UtRegisterTest("PortTestMatchReal08", PortTestMatchReal08, 1);
+ UtRegisterTest("PortTestMatchReal09", PortTestMatchReal09, 1);
+ UtRegisterTest("PortTestMatchReal10", PortTestMatchReal10, 1);
+ UtRegisterTest("PortTestMatchReal11", PortTestMatchReal11, 1);
+ UtRegisterTest("PortTestMatchReal12", PortTestMatchReal12, 1);
+ UtRegisterTest("PortTestMatchReal13", PortTestMatchReal13, 1);
+ UtRegisterTest("PortTestMatchReal14", PortTestMatchReal14, 1);
+ UtRegisterTest("PortTestMatchReal15", PortTestMatchReal15, 1);
+ UtRegisterTest("PortTestMatchReal16", PortTestMatchReal16, 1);
+ UtRegisterTest("PortTestMatchReal17", PortTestMatchReal17, 1);
+ UtRegisterTest("PortTestMatchReal18", PortTestMatchReal18, 1);
+ UtRegisterTest("PortTestMatchReal19",
+ PortTestMatchReal19, 1);
+ UtRegisterTest("PortTestMatchDoubleNegation", PortTestMatchDoubleNegation, 1);
+
+
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-engine-port.h b/framework/src/suricata/src/detect-engine-port.h
new file mode 100644
index 00000000..e23c1e47
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-port.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PORT_H__
+#define __DETECT_PORT_H__
+
+/* prototypes */
+int DetectPortParse(const DetectEngineCtx *, DetectPort **head, char *str);
+
+DetectPort *DetectPortCopy(DetectEngineCtx *, DetectPort *);
+DetectPort *DetectPortCopySingle(DetectEngineCtx *, DetectPort *);
+int DetectPortInsertCopy(DetectEngineCtx *,DetectPort **, DetectPort *);
+int DetectPortInsert(DetectEngineCtx *,DetectPort **, DetectPort *);
+void DetectPortCleanupList (DetectPort *head);
+
+DetectPort *DetectPortLookup(DetectPort *head, DetectPort *dp);
+int DetectPortAdd(DetectPort **head, DetectPort *dp);
+
+DetectPort *DetectPortLookupGroup(DetectPort *dp, uint16_t port);
+
+void DetectPortPrintMemory(void);
+
+DetectPort *DetectPortDpHashLookup(DetectEngineCtx *, DetectPort *);
+DetectPort *DetectPortDpHashGetListPtr(DetectEngineCtx *);
+int DetectPortDpHashInit(DetectEngineCtx *);
+void DetectPortDpHashFree(DetectEngineCtx *);
+int DetectPortDpHashAdd(DetectEngineCtx *, DetectPort *);
+void DetectPortDpHashReset(DetectEngineCtx *);
+
+DetectPort *DetectPortSpHashLookup(DetectEngineCtx *, DetectPort *);
+int DetectPortSpHashInit(DetectEngineCtx *);
+void DetectPortSpHashFree(DetectEngineCtx *);
+int DetectPortSpHashAdd(DetectEngineCtx *, DetectPort *);
+void DetectPortSpHashReset(DetectEngineCtx *);
+
+int DetectPortJoin(DetectEngineCtx *,DetectPort *target, DetectPort *source);
+
+void DetectPortPrint(DetectPort *);
+void DetectPortPrintList(DetectPort *head);
+int DetectPortCmp(DetectPort *, DetectPort *);
+void DetectPortFree(DetectPort *);
+
+int DetectPortTestConfVars(void);
+
+void DetectPortTests(void);
+
+#endif /* __DETECT_PORT_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-proto.c b/framework/src/suricata/src/detect-engine-proto.c
new file mode 100644
index 00000000..cacaeee3
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-proto.c
@@ -0,0 +1,627 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Proto part of the detection engine.
+ *
+ * \todo move this out of the detection plugin structure
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "detect.h"
+
+#include "app-layer-parser.h"
+
+#include "flow-util.h"
+#include "flow-var.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-state.h"
+
+#include "util-cidr.h"
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/**
+ * \brief Function to initialize the protocol detection and
+ * allocate memory to the DetectProto structure.
+ *
+ * \retval DetectProto instance pointer if successful otherwise NULL
+ */
+
+DetectProto *DetectProtoInit(void)
+{
+ DetectProto *dp = SCMalloc(sizeof(DetectProto));
+ if (unlikely(dp == NULL))
+ return NULL;
+ memset(dp,0,sizeof(DetectProto));
+
+ return dp;
+}
+
+/**
+ * \brief Free a DetectProto object
+ *
+ * \param dp Pointer to the DetectProto instance to be freed
+ */
+void DetectProtoFree(DetectProto *dp)
+{
+ if (dp == NULL)
+ return;
+
+ SCFree(dp);
+}
+
+/**
+ * \brief Parses a protocol sent as a string.
+ *
+ * \param dp Pointer to the DetectProto instance which will be updated with the
+ * incoming protocol information.
+ * \param str Pointer to the string containing the protocol name.
+ *
+ * \retval >=0 If proto is detected, -1 otherwise.
+ */
+int DetectProtoParse(DetectProto *dp, char *str)
+{
+ if (strcasecmp(str, "tcp") == 0) {
+ dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
+ SCLogDebug("TCP protocol detected");
+ } else if (strcasecmp(str, "tcp-pkt") == 0) {
+ dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
+ SCLogDebug("TCP protocol detected, packets only");
+ dp->flags |= DETECT_PROTO_ONLY_PKT;
+ } else if (strcasecmp(str, "tcp-stream") == 0) {
+ dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
+ SCLogDebug("TCP protocol detected, stream only");
+ dp->flags |= DETECT_PROTO_ONLY_STREAM;
+ } else if (strcasecmp(str, "udp") == 0) {
+ dp->proto[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8);
+ SCLogDebug("UDP protocol detected");
+ } else if (strcasecmp(str, "icmpv4") == 0) {
+ dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
+ SCLogDebug("ICMPv4 protocol detected");
+ } else if (strcasecmp(str, "icmpv6") == 0) {
+ dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
+ SCLogDebug("ICMPv6 protocol detected");
+ } else if (strcasecmp(str, "icmp") == 0) {
+ dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
+ dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
+ SCLogDebug("ICMP protocol detected, sig applies both to ICMPv4 and ICMPv6");
+ } else if (strcasecmp(str, "sctp") == 0) {
+ dp->proto[IPPROTO_SCTP / 8] |= 1 << (IPPROTO_SCTP % 8);
+ SCLogDebug("SCTP protocol detected");
+ } else if (strcasecmp(str,"ipv4") == 0 ||
+ strcasecmp(str,"ip4") == 0 ) {
+ dp->flags |= (DETECT_PROTO_IPV4 | DETECT_PROTO_ANY);
+ memset(dp->proto, 0xff, sizeof(dp->proto));
+ SCLogDebug("IPv4 protocol detected");
+ } else if (strcasecmp(str,"ipv6") == 0 ||
+ strcasecmp(str,"ip6") == 0 ) {
+ dp->flags |= (DETECT_PROTO_IPV6 | DETECT_PROTO_ANY);
+ memset(dp->proto, 0xff, sizeof(dp->proto));
+ SCLogDebug("IPv6 protocol detected");
+ } else if (strcasecmp(str,"ip") == 0 ||
+ strcasecmp(str,"pkthdr") == 0) {
+ /* Proto "ip" is treated as an "any" */
+ dp->flags |= DETECT_PROTO_ANY;
+ memset(dp->proto, 0xff, sizeof(dp->proto));
+ SCLogDebug("IP protocol detected");
+ } else {
+ goto error;
+
+ /** \todo are numeric protocols even valid? */
+#if 0
+ uint8_t proto_u8; /* Used to avoid sign extension */
+
+ /* Extract out a 0-256 value with validation checks */
+ if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) {
+ // XXX
+ SCLogDebug("DetectProtoParse: Error in extracting byte string");
+ goto error;
+ }
+ proto = (int)proto_u8;
+
+ /* Proto 0 is the same as "ip" above */
+ if (proto == IPPROTO_IP) {
+ dp->flags |= DETECT_PROTO_ANY;
+ } else {
+ dp->proto[proto / 8] |= 1<<(proto % 8);
+ }
+#endif
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+/** \brief see if a DetectProto contains a certain proto
+ * \param dp detect proto to inspect
+ * \param proto protocol (such as IPPROTO_TCP) to look for
+ * \retval 0 protocol not in the set
+ * \retval 1 protocol is in the set */
+int DetectProtoContainsProto(DetectProto *dp, int proto)
+{
+ if (dp->flags & DETECT_PROTO_ANY)
+ return 1;
+
+ if (dp->proto[proto / 8] & (1<<(proto % 8)))
+ return 1;
+
+ return 0;
+}
+
+/* TESTS */
+
+#ifdef UNITTESTS
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-mpm.h"
+/**
+ * \brief this function is used to initialize the detection engine context and
+ * setup the signature with passed values.
+ */
+static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
+ DetectProto *dp, char *str)
+{
+ char fullstr[1024];
+ int result = 0;
+
+ *de_ctx = NULL;
+ *sig = NULL;
+
+ if (snprintf(fullstr, 1024, "alert %s any any -> any any (msg:\"DetectProto"
+ " test\"; sid:1;)", str) >= 1024)
+ {
+ goto end;
+ }
+
+ *de_ctx = DetectEngineCtxInit();
+ if (*de_ctx == NULL) {
+ goto end;
+ }
+
+ (*de_ctx)->flags |= DE_QUIET;
+
+ (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
+ if ((*de_ctx)->sig_list == NULL) {
+ goto end;
+ }
+
+ *sig = (*de_ctx)->sig_list;
+
+ if (DetectProtoParse(dp, str) < 0)
+ goto end;
+
+ result = 1;
+
+end:
+ return result;
+}
+
+/**
+ * \test ProtoTestParse01 is a test to make sure that we parse the
+ * protocol correctly, when given valid proto option.
+ */
+static int ProtoTestParse01 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ int r = DetectProtoParse(&dp, "6");
+ if (r < 0) {
+ return 1;
+ }
+
+ SCLogDebug("DetectProtoParse should have rejected the \"6\" string");
+ return 0;
+}
+/**
+ * \test ProtoTestParse02 is a test to make sure that we parse the
+ * protocol correctly, when given "tcp" as proto option.
+ */
+static int ProtoTestParse02 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ int r = DetectProtoParse(&dp, "tcp");
+ if (r >= 0 && dp.proto[(IPPROTO_TCP/8)] & (1<<(IPPROTO_TCP%8))) {
+ return 1;
+ }
+
+ SCLogDebug("ProtoTestParse02: Error in parsing the \"tcp\" string");
+ return 0;
+}
+/**
+ * \test ProtoTestParse03 is a test to make sure that we parse the
+ * protocol correctly, when given "ip" as proto option.
+ */
+static int ProtoTestParse03 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ int r = DetectProtoParse(&dp, "ip");
+ if (r >= 0 && dp.flags & DETECT_PROTO_ANY) {
+ return 1;
+ }
+
+ SCLogDebug("ProtoTestParse03: Error in parsing the \"ip\" string");
+ return 0;
+}
+
+/**
+ * \test ProtoTestParse04 is a test to make sure that we do not parse the
+ * protocol, when given an invalid proto option.
+ */
+static int ProtoTestParse04 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ /* Check for a bad number */
+ int r = DetectProtoParse(&dp, "4242");
+ if (r < 0) {
+ return 1;
+ }
+
+ SCLogDebug("ProtoTestParse04: it should not parsing the \"4242\" string");
+ return 0;
+}
+
+/**
+ * \test ProtoTestParse05 is a test to make sure that we do not parse the
+ * protocol, when given an invalid proto option.
+ */
+static int ProtoTestParse05 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ /* Check for a bad string */
+ int r = DetectProtoParse(&dp, "tcp/udp");
+ if (r < 0) {
+ return 1;
+ }
+
+ SCLogDebug("ProtoTestParse05: it should not parsing the \"tcp/udp\" string");
+ return 0;
+}
+
+/**
+ * \test make sure that we properly parse tcp-pkt
+ */
+static int ProtoTestParse06 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ /* Check for a bad string */
+ int r = DetectProtoParse(&dp, "tcp-pkt");
+ if (r < 0) {
+ printf("parsing tcp-pkt failed: ");
+ return 0;
+ }
+
+ if (!(dp.flags & DETECT_PROTO_ONLY_PKT)) {
+ printf("DETECT_PROTO_ONLY_PKT flag not set: ");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test make sure that we properly parse tcp-stream
+ */
+static int ProtoTestParse07 (void)
+{
+ DetectProto dp;
+ memset(&dp,0,sizeof(DetectProto));
+
+ /* Check for a bad string */
+ int r = DetectProtoParse(&dp, "tcp-stream");
+ if (r < 0) {
+ printf("parsing tcp-stream failed: ");
+ return 0;
+ }
+
+ if (!(dp.flags & DETECT_PROTO_ONLY_STREAM)) {
+ printf("DETECT_PROTO_ONLY_STREAM flag not set: ");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
+ * signature.
+ */
+static int DetectProtoTestSetup01(void)
+{
+ DetectProto dp;
+ Signature *sig = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int i;
+
+ memset(&dp, 0, sizeof(dp));
+
+ result = DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp");
+ if (result == 0) {
+ goto end;
+ }
+
+ result = 0;
+
+ /* The signature proto should be TCP */
+ if (!(sig->proto.proto[(IPPROTO_TCP/8)] & (1<<(IPPROTO_TCP%8)))) {
+ printf("failed in sig matching\n");
+ goto cleanup;
+ }
+ for (i = 2; i < 256/8; i++) {
+ if (sig->proto.proto[i] != 0) {
+ printf("failed in sig clear\n");
+ goto cleanup;
+ }
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ return result;
+}
+
+/**
+ * \test DetectrotoTestSetup02 is a test for a icmpv4 and icmpv6
+ * protocol setting up in signature.
+ */
+static int DetectProtoTestSetup02(void)
+{
+ DetectProto dp;
+ Signature *sig_icmpv4 = NULL;
+ Signature *sig_icmpv6 = NULL;
+ Signature *sig_icmp = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int i;
+
+ memset(&dp, 0, sizeof(dp));
+
+ if (DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0) {
+ printf("failure - imcpv4.\n");
+ goto end;
+ }
+
+ if (DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0) {
+ printf("failure - imcpv6.\n");
+ goto end;
+ }
+
+ if (DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0) {
+ printf("failure - imcp.\n");
+ goto end;
+ }
+
+ for (i = 0; i < 256 / 8; i++) {
+ if (i == IPPROTO_ICMP) {
+ if (!(sig_icmpv4->proto.proto[i / 8] & (1 << (i % 8)))) {
+ printf("failed in sig matching - icmpv4 - icmpv4.\n");
+ goto end;
+ }
+ continue;
+ }
+ if (sig_icmpv4->proto.proto[i / 8] & (1 << (i % 8))) {
+ printf("failed in sig matching - icmpv4 - others.\n");
+ goto end;
+ }
+ }
+
+ for (i = 0; i < 256 / 8; i++) {
+ if (i == IPPROTO_ICMPV6) {
+ if (!(sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8)))) {
+ printf("failed in sig matching - icmpv6 - icmpv6.\n");
+ goto end;
+ }
+ continue;
+ }
+ if (sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8))) {
+ printf("failed in sig matching - icmpv6 - others.\n");
+ goto end;
+ }
+ }
+
+ for (i = 0; i < 256 / 8; i++) {
+ if (i == IPPROTO_ICMP || i == IPPROTO_ICMPV6) {
+ if (!(sig_icmp->proto.proto[i / 8] & (1 << (i % 8)))) {
+ printf("failed in sig matching - icmp - icmp.\n");
+ goto end;
+ }
+ continue;
+ }
+ if (sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8))) {
+ printf("failed in sig matching - icmp - others.\n");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectProtoTestSig01 is a test for checking the working of protocol
+ * detection by setting up the signature and later testing its working
+ * by matching the received packet against the sig.
+ */
+
+static int DetectProtoTestSig01(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ Flow f;
+
+ memset(&f, 0, sizeof(Flow));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FLOW_INITIALIZE(&f);
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flags |= PKT_HAS_FLOW;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert udp any any -> any any "
+ "(msg:\"Not tcp\"; flow:to_server; sid:1;)");
+
+ if (s == NULL)
+ goto end;
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any "
+ "(msg:\"IP\"; flow:to_server; sid:2;)");
+
+ if (s == NULL)
+ goto end;
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"TCP\"; flow:to_server; sid:3;)");
+
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ printf("sid 2 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3) == 0) {
+ printf("sid 3 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ FLOW_DESTROY(&f);
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+}
+
+/**
+ * \test signature parsing with tcp-pkt and tcp-stream
+ */
+
+static int DetectProtoTestSig02(void)
+{
+ Signature *s = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp-pkt any any -> any any "
+ "(msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
+ if (s == NULL) {
+ printf("tcp-pkt sig parsing failed: ");
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp-stream any any -> any any "
+ "(msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
+ if (s == NULL) {
+ printf("tcp-pkt sig parsing failed: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectProto
+ */
+void DetectProtoTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ProtoTestParse01", ProtoTestParse01, 1);
+ UtRegisterTest("ProtoTestParse02", ProtoTestParse02, 1);
+ UtRegisterTest("ProtoTestParse03", ProtoTestParse03, 1);
+ UtRegisterTest("ProtoTestParse04", ProtoTestParse04, 1);
+ UtRegisterTest("ProtoTestParse05", ProtoTestParse05, 1);
+ UtRegisterTest("ProtoTestParse06", ProtoTestParse06, 1);
+ UtRegisterTest("ProtoTestParse07", ProtoTestParse07, 1);
+
+ UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01, 1);
+ UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02, 1);
+
+ UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01, 1);
+ UtRegisterTest("DetectProtoTestSig02", DetectProtoTestSig02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-engine-proto.h b/framework/src/suricata/src/detect-engine-proto.h
new file mode 100644
index 00000000..4edfe3b5
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-proto.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PROTO_H__
+#define __DETECT_PROTO_H__
+
+#define DETECT_PROTO_ANY (1 << 0) /**< Indicate that given protocol
+ is considered as IP */
+#define DETECT_PROTO_ONLY_PKT (1 << 1) /**< Indicate that we only care
+ about packet payloads. */
+#define DETECT_PROTO_ONLY_STREAM (1 << 2) /**< Indicate that we only care
+ about stream payloads. */
+#define DETECT_PROTO_IPV4 (1 << 3) /**< IPv4 only */
+#define DETECT_PROTO_IPV6 (1 << 4) /**< IPv6 only */
+
+typedef struct DetectProto_ {
+ uint8_t proto[256/8]; /**< bit array for 256 protocol bits */
+ uint8_t flags;
+} DetectProto;
+
+/* prototypes */
+int DetectProtoParse(DetectProto *dp, char *str);
+int DetectProtoContainsProto(DetectProto *, int);
+
+void DetectProtoTests(void);
+
+#endif /* __DETECT_PROTO_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine-siggroup.c b/framework/src/suricata/src/detect-engine-siggroup.c
new file mode 100644
index 00000000..02602d90
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-siggroup.c
@@ -0,0 +1,2390 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Signature grouping part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "flow-var.h"
+
+#include "app-layer-protos.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-address.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-siggroup.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "util-hash.h"
+#include "util-hashlist.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-cidr.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-memcmp.h"
+
+/* prototypes */
+int SigGroupHeadClearSigs(SigGroupHead *);
+
+static uint32_t detect_siggroup_head_memory = 0;
+static uint32_t detect_siggroup_head_init_cnt = 0;
+static uint32_t detect_siggroup_head_free_cnt = 0;
+static uint32_t detect_siggroup_head_initdata_memory = 0;
+static uint32_t detect_siggroup_head_initdata_init_cnt = 0;
+static uint32_t detect_siggroup_head_initdata_free_cnt = 0;
+static uint32_t detect_siggroup_sigarray_memory = 0;
+static uint32_t detect_siggroup_sigarray_init_cnt = 0;
+static uint32_t detect_siggroup_sigarray_free_cnt = 0;
+static uint32_t detect_siggroup_matcharray_memory = 0;
+static uint32_t detect_siggroup_matcharray_init_cnt = 0;
+static uint32_t detect_siggroup_matcharray_free_cnt = 0;
+
+void SigGroupHeadInitDataFree(SigGroupHeadInitData *sghid)
+{
+ if (sghid->content_array != NULL) {
+ SCFree(sghid->content_array);
+ sghid->content_array = NULL;
+ sghid->content_size = 0;
+ }
+ if (sghid->uri_content_array != NULL) {
+ SCFree(sghid->uri_content_array);
+ sghid->uri_content_array = NULL;
+ sghid->uri_content_size = 0;
+ }
+ if (sghid->sig_array != NULL) {
+ SCFree(sghid->sig_array);
+ sghid->sig_array = NULL;
+
+ detect_siggroup_sigarray_free_cnt++;
+ detect_siggroup_sigarray_memory -= sghid->sig_size;
+ }
+ SCFree(sghid);
+
+ detect_siggroup_head_initdata_free_cnt++;
+ detect_siggroup_head_initdata_memory -= sizeof(SigGroupHeadInitData);
+}
+
+static SigGroupHeadInitData *SigGroupHeadInitDataAlloc(uint32_t size)
+{
+ SigGroupHeadInitData *sghid = SCMalloc(sizeof(SigGroupHeadInitData));
+ if (unlikely(sghid == NULL))
+ return NULL;
+
+ memset(sghid, 0x00, sizeof(SigGroupHeadInitData));
+
+ detect_siggroup_head_initdata_init_cnt++;
+ detect_siggroup_head_initdata_memory += sizeof(SigGroupHeadInitData);
+
+ /* initialize the signature bitarray */
+ sghid->sig_size = size;
+ if ( (sghid->sig_array = SCMalloc(sghid->sig_size)) == NULL)
+ goto error;
+
+ memset(sghid->sig_array, 0, sghid->sig_size);
+
+ detect_siggroup_sigarray_init_cnt++;
+ detect_siggroup_sigarray_memory += sghid->sig_size;
+
+ return sghid;
+error:
+ SigGroupHeadInitDataFree(sghid);
+ return NULL;
+}
+
+void SigGroupHeadStore(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ void *ptmp;
+ //printf("de_ctx->sgh_array_cnt %u, de_ctx->sgh_array_size %u, de_ctx->sgh_array %p\n", de_ctx->sgh_array_cnt, de_ctx->sgh_array_size, de_ctx->sgh_array);
+ if (de_ctx->sgh_array_cnt < de_ctx->sgh_array_size) {
+ de_ctx->sgh_array[de_ctx->sgh_array_cnt] = sgh;
+ } else {
+ int increase = 16;
+ ptmp = SCRealloc(de_ctx->sgh_array,
+ sizeof(SigGroupHead *) * (increase + de_ctx->sgh_array_size));
+ if (ptmp == NULL) {
+ SCFree(de_ctx->sgh_array);
+ de_ctx->sgh_array = NULL;
+ return;
+ }
+ de_ctx->sgh_array = ptmp;
+
+ de_ctx->sgh_array_size += increase;
+ de_ctx->sgh_array[de_ctx->sgh_array_cnt] = sgh;
+ }
+ de_ctx->sgh_array_cnt++;
+}
+
+/**
+ * \brief Alloc a SigGroupHead and its signature bit_array.
+ *
+ * \param size Size of the sig_array that has to be created for this
+ * SigGroupHead.
+ *
+ * \retval sgh Pointer to the newly init SigGroupHead on success; or NULL in
+ * case of error.
+ */
+static SigGroupHead *SigGroupHeadAlloc(DetectEngineCtx *de_ctx, uint32_t size)
+{
+ SigGroupHead *sgh = SCMalloc(sizeof(SigGroupHead));
+ if (unlikely(sgh == NULL))
+ return NULL;
+ memset(sgh, 0, sizeof(SigGroupHead));
+
+ sgh->init = SigGroupHeadInitDataAlloc(size);
+ if (sgh->init == NULL)
+ goto error;
+
+ detect_siggroup_head_init_cnt++;
+ detect_siggroup_head_memory += sizeof(SigGroupHead);
+
+ return sgh;
+
+error:
+ SigGroupHeadFree(sgh);
+ return NULL;
+}
+
+/**
+ * \brief Free a SigGroupHead and its members.
+ *
+ * \param sgh Pointer to the SigGroupHead that has to be freed.
+ */
+void SigGroupHeadFree(SigGroupHead *sgh)
+{
+ if (sgh == NULL)
+ return;
+
+ SCLogDebug("sgh %p", sgh);
+
+ PatternMatchDestroyGroup(sgh);
+
+ if (sgh->match_array != NULL) {
+ detect_siggroup_matcharray_free_cnt++;
+ detect_siggroup_matcharray_memory -= (sgh->sig_cnt * sizeof(Signature *));
+ SCFree(sgh->match_array);
+ sgh->match_array = NULL;
+ }
+
+ if (sgh->non_mpm_store_array != NULL) {
+ SCFree(sgh->non_mpm_store_array);
+ sgh->non_mpm_store_array = NULL;
+ sgh->non_mpm_store_cnt = 0;
+ }
+
+ sgh->sig_cnt = 0;
+
+ if (sgh->init != NULL) {
+ SigGroupHeadInitDataFree(sgh->init);
+ sgh->init = NULL;
+ }
+
+ SCFree(sgh);
+
+ detect_siggroup_head_free_cnt++;
+ detect_siggroup_head_memory -= sizeof(SigGroupHead);
+
+ return;
+}
+
+/**
+ * \brief The hash function to be the used by the mpm SigGroupHead hash table -
+ * DetectEngineCtx->sgh_mpm_hash_table.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the SigGroupHead.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+uint32_t SigGroupHeadMpmHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ SigGroupHead *sgh = (SigGroupHead *)data;
+ uint32_t hash = 0;
+ uint32_t b = 0;
+
+ for (b = 0; b < sgh->init->content_size; b++)
+ hash += sgh->init->content_array[b];
+
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief The Compare function to be used by the mpm SigGroupHead hash table -
+ * DetectEngineCtx->sgh_mpm_hash_table.
+ *
+ * \param data1 Pointer to the first SigGroupHead.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second SigGroupHead.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 SigGroupHeads sent as args match.
+ * \retval 0 If the 2 SigGroupHeads sent as args do not match.
+ */
+char SigGroupHeadMpmCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ SigGroupHead *sgh1 = (SigGroupHead *)data1;
+ SigGroupHead *sgh2 = (SigGroupHead *)data2;
+
+ if (sgh1->init->content_size != sgh2->init->content_size)
+ return 0;
+
+ if (SCMemcmp(sgh1->init->content_array, sgh2->init->content_array,
+ sgh1->init->content_size) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Initializes the SigGroupHead mpm hash table to be used by the detection
+ * engine context.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadMpmHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_mpm_hash_table = HashListTableInit(4096, SigGroupHeadMpmHashFunc,
+ SigGroupHeadMpmCompareFunc,
+ NULL);
+
+ if (de_ctx->sgh_mpm_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context SigGroupHead
+ * mpm hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh; -1 on failure.
+ */
+int SigGroupHeadMpmHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_mpm_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead from the detection engine context
+ * SigGroupHead mpm hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadMpmHashLookup(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_mpm_hash_table,
+ (void *)sgh, 0);
+
+ return rsgh;
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_mpm_hash_table, allocated by
+ * SigGroupHeadMpmHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadMpmHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_mpm_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_mpm_hash_table);
+ de_ctx->sgh_mpm_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief The hash function to be the used by the mpm uri SigGroupHead hash
+ * table - DetectEngineCtx->sgh_mpm_uri_hash_table.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the SigGroupHead.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+uint32_t SigGroupHeadMpmUriHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ SigGroupHead *sgh = (SigGroupHead *)data;
+ uint32_t hash = 0;
+ uint32_t b = 0;
+
+ for (b = 0; b < sgh->init->uri_content_size; b++)
+ hash += sgh->init->uri_content_array[b];
+
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief The Compare function to be used by the mpm uri SigGroupHead hash
+ * table - DetectEngineCtx->sgh_mpm_uri_hash_table.
+ *
+ * \param data1 Pointer to the first SigGroupHead.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second SigGroupHead.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 SigGroupHeads sent as args match.
+ * \retval 0 If the 2 SigGroupHeads sent as args do not match.
+ */
+char SigGroupHeadMpmUriCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ SigGroupHead *sgh1 = (SigGroupHead *)data1;
+ SigGroupHead *sgh2 = (SigGroupHead *)data2;
+
+ if (sgh1->init->uri_content_size != sgh2->init->uri_content_size)
+ return 0;
+
+ if (SCMemcmp(sgh1->init->uri_content_array, sgh2->init->uri_content_array,
+ sgh1->init->uri_content_size) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Initializes the mpm uri hash table to be used by the detection engine
+ * context.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadMpmUriHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_mpm_uri_hash_table = HashListTableInit(4096,
+ SigGroupHeadMpmUriHashFunc,
+ SigGroupHeadMpmUriCompareFunc,
+ NULL);
+ if (de_ctx->sgh_mpm_uri_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context SigGroupHead
+ * mpm uri hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh and -1 on failure.
+ */
+int SigGroupHeadMpmUriHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_mpm_uri_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead from the detection engine context
+ * SigGroupHead mpm uri hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadMpmUriHashLookup(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_mpm_uri_hash_table,
+ (void *)sgh, 0);
+
+ return rsgh;
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_mpm_uri_hash_table,
+ * allocated by SigGroupHeadMpmUriHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadMpmUriHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_mpm_uri_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_mpm_uri_hash_table);
+ de_ctx->sgh_mpm_uri_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief The hash function to be the used by the mpm uri SigGroupHead hash
+ * table - DetectEngineCtx->sgh_mpm_uri_hash_table.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the SigGroupHead.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+uint32_t SigGroupHeadMpmStreamHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ SigGroupHead *sgh = (SigGroupHead *)data;
+ uint32_t hash = 0;
+ uint32_t b = 0;
+
+ for (b = 0; b < sgh->init->stream_content_size; b++)
+ hash += sgh->init->stream_content_array[b];
+
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief The Compare function to be used by the mpm uri SigGroupHead hash
+ * table - DetectEngineCtx->sgh_mpm_uri_hash_table.
+ *
+ * \param data1 Pointer to the first SigGroupHead.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second SigGroupHead.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 SigGroupHeads sent as args match.
+ * \retval 0 If the 2 SigGroupHeads sent as args do not match.
+ */
+char SigGroupHeadMpmStreamCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ SigGroupHead *sgh1 = (SigGroupHead *)data1;
+ SigGroupHead *sgh2 = (SigGroupHead *)data2;
+
+ if (sgh1->init->stream_content_size != sgh2->init->stream_content_size)
+ return 0;
+
+ if (SCMemcmp(sgh1->init->stream_content_array, sgh2->init->stream_content_array,
+ sgh1->init->stream_content_size) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Initializes the mpm uri hash table to be used by the detection engine
+ * context.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadMpmStreamHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_mpm_stream_hash_table = HashListTableInit(4096,
+ SigGroupHeadMpmStreamHashFunc, SigGroupHeadMpmStreamCompareFunc, NULL);
+ if (de_ctx->sgh_mpm_stream_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context SigGroupHead
+ * mpm uri hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh and -1 on failure.
+ */
+int SigGroupHeadMpmStreamHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_mpm_stream_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead from the detection engine context
+ * SigGroupHead mpm uri hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadMpmStreamHashLookup(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_mpm_stream_hash_table,
+ (void *)sgh, 0);
+
+ return rsgh;
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_mpm_uri_hash_table,
+ * allocated by SigGroupHeadMpmUriHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadMpmStreamHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_mpm_stream_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_mpm_stream_hash_table);
+ de_ctx->sgh_mpm_stream_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief The hash function to be the used by the hash table -
+ * DetectEngineCtx->sgh_hash_table.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the SigGroupHead.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+uint32_t SigGroupHeadHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ SigGroupHead *sgh = (SigGroupHead *)data;
+ uint32_t hash = 0;
+ uint32_t b = 0;
+
+ SCLogDebug("hashing sgh %p (mpm_content_maxlen %u)", sgh, sgh->mpm_content_maxlen);
+
+ for (b = 0; b < sgh->init->sig_size; b++)
+ hash += sgh->init->sig_array[b];
+
+ hash %= ht->array_size;
+ SCLogDebug("hash %"PRIu32" (sig_size %"PRIu32")", hash, sgh->init->sig_size);
+ return hash;
+}
+
+/**
+ * \brief The Compare function to be used by the SigGroupHead hash table -
+ * DetectEngineCtx->sgh_hash_table.
+ *
+ * \param data1 Pointer to the first SigGroupHead.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second SigGroupHead.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 SigGroupHeads sent as args match.
+ * \retval 0 If the 2 SigGroupHeads sent as args do not match.
+ */
+char SigGroupHeadCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ SigGroupHead *sgh1 = (SigGroupHead *)data1;
+ SigGroupHead *sgh2 = (SigGroupHead *)data2;
+
+ if (data1 == NULL || data2 == NULL)
+ return 0;
+
+ if (sgh1->init->sig_size != sgh2->init->sig_size)
+ return 0;
+
+ if (SCMemcmp(sgh1->init->sig_array, sgh2->init->sig_array, sgh1->init->sig_size) != 0)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * \brief Initializes the hash table in the detection engine context to hold the
+ * SigGroupHeads.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_hash_table = HashListTableInit(4096, SigGroupHeadHashFunc,
+ SigGroupHeadCompareFunc, NULL);
+ if (de_ctx->sgh_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context SigGroupHead
+ * hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the SigGroupHead; -1 on failure.
+ */
+int SigGroupHeadHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+int SigGroupHeadHashRemove(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ return HashListTableRemove(de_ctx->sgh_hash_table, (void *)sgh, 0);
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead hash from the detection engine context
+ * SigGroupHead hash table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadHashLookup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ SCEnter();
+
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_hash_table,
+ (void *)sgh, 0);
+
+ SCReturnPtr(rsgh, "SigGroupHead");
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_hash_table, allocated by
+ * SigGroupHeadHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_hash_table);
+ de_ctx->sgh_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Initializes the dport based SigGroupHead hash table to hold the
+ * SigGroupHeads. The hash table that would be initialized is
+ * DetectEngineCtx->sgh_dport_hash_table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadDPortHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_dport_hash_table = HashListTableInit(4096, SigGroupHeadHashFunc,
+ SigGroupHeadCompareFunc,
+ NULL);
+ if (de_ctx->sgh_dport_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context dport based
+ * SigGroupHead hash table(DetectEngineCtx->sgh_dport_hash_table).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh and -1 on failure.
+ */
+int SigGroupHeadDPortHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_dport_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead hash from the detection engine ctx dport
+ * based SigGroupHead hash table(DetectEngineCtx->sgh_dport_hash_table).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadDPortHashLookup(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ SCEnter();
+
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_dport_hash_table,
+ (void *)sgh, 0);
+
+ SCReturnPtr(rsgh,"SigGroupHead");
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_dport_hash_table,
+ * allocated by the SigGroupHeadDPortHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadDPortHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_dport_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_dport_hash_table);
+ de_ctx->sgh_dport_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Initializes the sport based SigGroupHead hash table to hold the
+ * SigGroupHeads. The hash table that would be initialized is
+ * DetectEngineCtx->sgh_sport_hash_table.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadSPortHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_sport_hash_table = HashListTableInit(4096,
+ SigGroupHeadHashFunc,
+ SigGroupHeadCompareFunc,
+ NULL);
+ if (de_ctx->sgh_sport_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a SigGroupHead to the detection engine context dport based
+ * SigGroupHead hash table(DetectEngineCtx->sgh_sport_hash_table).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh and -1 on failure.
+ */
+int SigGroupHeadSPortHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ int ret = HashListTableAdd(de_ctx->sgh_sport_hash_table, (void *)sgh, 0);
+
+ return ret;
+}
+
+int SigGroupHeadSPortHashRemove(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ return HashListTableRemove(de_ctx->sgh_sport_hash_table, (void *)sgh, 0);
+}
+
+/**
+ * \brief Used to lookup a SigGroupHead hash from the detection engine ctx sport
+ * based SigGroupHead hash table(DetectEngineCtx->sgh_dport_hash_table).
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
+ * found in the hash table; NULL on failure.
+ */
+SigGroupHead *SigGroupHeadSPortHashLookup(DetectEngineCtx *de_ctx,
+ SigGroupHead *sgh)
+{
+ SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_sport_hash_table,
+ (void *)sgh, 0);
+
+ return rsgh;
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->sgh_sport_hash_table,
+ * allocated by the SigGroupHeadSPortHashInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadSPortHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->sgh_sport_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->sgh_sport_hash_table);
+ de_ctx->sgh_sport_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Used to free the signature array, content_array and uri_content_array
+ * members from the SigGroupHeads in the HashListTable.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param ht Pointer to the HashListTable
+ */
+static void SigGroupHeadFreeSigArraysHash2(DetectEngineCtx *de_ctx,
+ HashListTable *ht)
+{
+ HashListTableBucket *htb = NULL;
+ SigGroupHead *sgh = NULL;
+
+ for (htb = HashListTableGetListHead(ht);
+ htb != NULL;
+ htb = HashListTableGetListNext(htb))
+ {
+ sgh = (SigGroupHead *)HashListTableGetListData(htb);
+ if (sgh == NULL) {
+ continue;
+ }
+
+ if (sgh->init->sig_array != NULL) {
+ detect_siggroup_sigarray_free_cnt++;
+ detect_siggroup_sigarray_memory -= sgh->init->sig_size;
+
+ SCFree(sgh->init->sig_array);
+ sgh->init->sig_array = NULL;
+ sgh->init->sig_size = 0;
+ }
+
+ SigGroupHeadInitDataFree(sgh->init);
+ sgh->init = NULL;
+ }
+
+ return;
+}
+
+/**
+ * \brief Used to free the sig_array member of the SigGroupHeads present
+ * in the HashListTable.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param ht Pointer to the HashListTable
+ */
+static void SigGroupHeadFreeSigArraysHash(DetectEngineCtx *de_ctx,
+ HashListTable *ht)
+{
+ HashListTableBucket *htb = NULL;
+ SigGroupHead *sgh = NULL;
+
+ for (htb = HashListTableGetListHead(ht);
+ htb != NULL;
+ htb = HashListTableGetListNext(htb)) {
+ sgh = (SigGroupHead *)HashListTableGetListData(htb);
+
+ if (sgh->init != NULL) {
+ SigGroupHeadInitDataFree(sgh->init);
+ sgh->init = NULL;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Free the sigarrays in the sgh's. Those are only used during the init
+ * stage.
+ *
+ * \param de_ctx Pointer to the detection engine context whose sigarrays have to
+ * be freed.
+ */
+void SigGroupHeadFreeSigArrays(DetectEngineCtx *de_ctx)
+{
+ SigGroupHeadFreeSigArraysHash2(de_ctx, de_ctx->sgh_hash_table);
+ SigGroupHeadFreeSigArraysHash(de_ctx, de_ctx->sgh_dport_hash_table);
+ SigGroupHeadFreeSigArraysHash(de_ctx, de_ctx->sgh_sport_hash_table);
+
+ return;
+}
+
+/**
+ * \brief Free the mpm arrays that are only used during the init stage.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void SigGroupHeadFreeMpmArrays(DetectEngineCtx *de_ctx)
+{
+ HashListTableBucket *htb = NULL;
+ SigGroupHead *sgh = NULL;
+
+ for (htb = HashListTableGetListHead(de_ctx->sgh_dport_hash_table); htb != NULL; htb = HashListTableGetListNext(htb)) {
+ sgh = (SigGroupHead *)HashListTableGetListData(htb);
+ if (sgh->init != NULL) {
+ SigGroupHeadInitDataFree(sgh->init);
+ sgh->init = NULL;
+ }
+ }
+
+ for (htb = HashListTableGetListHead(de_ctx->sgh_sport_hash_table); htb != NULL; htb = HashListTableGetListNext(htb)) {
+ sgh = (SigGroupHead *)HashListTableGetListData(htb);
+ if (sgh->init != NULL) {
+ SigGroupHeadInitDataFree(sgh->init);
+ sgh->init = NULL;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Add a Signature to a SigGroupHead.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to a SigGroupHead. Can be NULL also.
+ * \param s Pointer to the Signature that has to be added to the
+ * SigGroupHead.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadAppendSig(DetectEngineCtx *de_ctx, SigGroupHead **sgh,
+ Signature *s)
+{
+ if (de_ctx == NULL)
+ return 0;
+
+ /* see if we have a head already */
+ if (*sgh == NULL) {
+ *sgh = SigGroupHeadAlloc(de_ctx, DetectEngineGetMaxSigId(de_ctx) / 8 + 1);
+ if (*sgh == NULL)
+ goto error;
+ }
+
+ /* enable the sig in the bitarray */
+ (*sgh)->init->sig_array[s->num / 8] |= 1 << (s->num % 8);
+
+ /* update maxlen for mpm */
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ /* check with the precalculated values from the sig */
+ if (s->mpm_content_maxlen > 0) {
+ if ((*sgh)->mpm_content_maxlen == 0)
+ (*sgh)->mpm_content_maxlen = s->mpm_content_maxlen;
+
+ if ((*sgh)->mpm_content_maxlen > s->mpm_content_maxlen)
+ (*sgh)->mpm_content_maxlen = s->mpm_content_maxlen;
+
+ SCLogDebug("(%p)->mpm_content_maxlen %u", *sgh, (*sgh)->mpm_content_maxlen);
+ }
+ }
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL) {
+ if (s->mpm_uricontent_maxlen > 0) {
+ if ((*sgh)->mpm_uricontent_maxlen == 0)
+ (*sgh)->mpm_uricontent_maxlen = s->mpm_uricontent_maxlen;
+
+ if ((*sgh)->mpm_uricontent_maxlen > s->mpm_uricontent_maxlen)
+ (*sgh)->mpm_uricontent_maxlen = s->mpm_uricontent_maxlen;
+
+ SCLogDebug("(%p)->mpm_uricontent_maxlen %u", *sgh, (*sgh)->mpm_uricontent_maxlen);
+ }
+ }
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Clears the bitarray holding the sids for this SigGroupHead.
+ *
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval 0 Always.
+ */
+int SigGroupHeadClearSigs(SigGroupHead *sgh)
+{
+ if (sgh == NULL)
+ return 0;
+
+ if (sgh->init->sig_array != NULL)
+ memset(sgh->init->sig_array, 0, sgh->init->sig_size);
+
+ sgh->sig_cnt = 0;
+
+ return 0;
+}
+
+/**
+ * \brief Copies the bitarray holding the sids from the source SigGroupHead to
+ * the destination SigGroupHead.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param src Pointer to the source SigGroupHead.
+ * \param dst Pointer to the destination SigGroupHead.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadCopySigs(DetectEngineCtx *de_ctx, SigGroupHead *src, SigGroupHead **dst)
+{
+ uint32_t idx = 0;
+
+ if (src == NULL || de_ctx == NULL)
+ return 0;
+
+ if (*dst == NULL) {
+ *dst = SigGroupHeadAlloc(de_ctx, DetectEngineGetMaxSigId(de_ctx) / 8 + 1);
+ if (*dst == NULL)
+ goto error;
+ }
+
+ /* do the copy */
+ for (idx = 0; idx < src->init->sig_size; idx++)
+ (*dst)->init->sig_array[idx] = (*dst)->init->sig_array[idx] | src->init->sig_array[idx];
+
+ if (src->mpm_content_maxlen != 0) {
+ if ((*dst)->mpm_content_maxlen == 0)
+ (*dst)->mpm_content_maxlen = src->mpm_content_maxlen;
+
+ if ((*dst)->mpm_content_maxlen > src->mpm_content_maxlen)
+ (*dst)->mpm_content_maxlen = src->mpm_content_maxlen;
+
+ SCLogDebug("src (%p)->mpm_content_maxlen %u", src, src->mpm_content_maxlen);
+ SCLogDebug("dst (%p)->mpm_content_maxlen %u", (*dst), (*dst)->mpm_content_maxlen);
+ BUG_ON((*dst)->mpm_content_maxlen == 0);
+ }
+ if (src->mpm_uricontent_maxlen != 0) {
+ if ((*dst)->mpm_uricontent_maxlen == 0)
+ (*dst)->mpm_uricontent_maxlen = src->mpm_uricontent_maxlen;
+
+ if ((*dst)->mpm_uricontent_maxlen > src->mpm_uricontent_maxlen)
+ (*dst)->mpm_uricontent_maxlen = src->mpm_uricontent_maxlen;
+ }
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Updates the SigGroupHead->sig_cnt with the total count of all the
+ * Signatures present in this SigGroupHead.
+ *
+ * \param sgh Pointer to the SigGroupHead.
+ * \param max_idx Maximum sid of the all the Signatures present in this
+ * SigGroupHead.
+ */
+void SigGroupHeadSetSigCnt(SigGroupHead *sgh, uint32_t max_idx)
+{
+ uint32_t sig;
+
+ sgh->sig_cnt = 0;
+ for (sig = 0; sig < max_idx + 1; sig++) {
+ if (sgh->init->sig_array[sig / 8] & (1 << (sig % 8)))
+ sgh->sig_cnt++;
+ }
+
+ return;
+}
+
+/**
+ * \brief Prints the memory statistics for the detect-engine-siggroup.[ch] module.
+ */
+void DetectSigGroupPrintMemory(void)
+{
+ SCLogDebug(" * Sig group head memory stats (SigGroupHead %" PRIuMAX "):",
+ (uintmax_t)sizeof(SigGroupHead));
+ SCLogDebug(" - detect_siggroup_head_memory %" PRIu32,
+ detect_siggroup_head_memory);
+ SCLogDebug(" - detect_siggroup_head_init_cnt %" PRIu32,
+ detect_siggroup_head_init_cnt);
+ SCLogDebug(" - detect_siggroup_head_free_cnt %" PRIu32,
+ detect_siggroup_head_free_cnt);
+ SCLogDebug(" - outstanding sig group heads %" PRIu32,
+ detect_siggroup_head_init_cnt - detect_siggroup_head_free_cnt);
+ SCLogDebug(" * Sig group head memory stats done");
+ SCLogDebug(" * Sig group head initdata memory stats (SigGroupHeadInitData %" PRIuMAX "):",
+ (uintmax_t)sizeof(SigGroupHeadInitData));
+ SCLogDebug(" - detect_siggroup_head_initdata_memory %" PRIu32,
+ detect_siggroup_head_initdata_memory);
+ SCLogDebug(" - detect_siggroup_head_initdata_init_cnt %" PRIu32,
+ detect_siggroup_head_initdata_init_cnt);
+ SCLogDebug(" - detect_siggroup_head_initdata_free_cnt %" PRIu32,
+ detect_siggroup_head_initdata_free_cnt);
+ SCLogDebug(" - outstanding sig group head initdatas %" PRIu32,
+ detect_siggroup_head_initdata_init_cnt - detect_siggroup_head_initdata_free_cnt);
+ SCLogDebug(" * Sig group head memory initdata stats done");
+ SCLogDebug(" * Sig group sigarray memory stats:");
+ SCLogDebug(" - detect_siggroup_sigarray_memory %" PRIu32,
+ detect_siggroup_sigarray_memory);
+ SCLogDebug(" - detect_siggroup_sigarray_init_cnt %" PRIu32,
+ detect_siggroup_sigarray_init_cnt);
+ SCLogDebug(" - detect_siggroup_sigarray_free_cnt %" PRIu32,
+ detect_siggroup_sigarray_free_cnt);
+ SCLogDebug(" - outstanding sig group sigarrays %" PRIu32,
+ (detect_siggroup_sigarray_init_cnt -
+ detect_siggroup_sigarray_free_cnt));
+ SCLogDebug(" * Sig group sigarray memory stats done");
+ SCLogDebug(" * Sig group matcharray memory stats:");
+ SCLogDebug(" - detect_siggroup_matcharray_memory %" PRIu32,
+ detect_siggroup_matcharray_memory);
+ SCLogDebug(" - detect_siggroup_matcharray_init_cnt %" PRIu32,
+ detect_siggroup_matcharray_init_cnt);
+ SCLogDebug(" - detect_siggroup_matcharray_free_cnt %" PRIu32,
+ detect_siggroup_matcharray_free_cnt);
+ SCLogDebug(" - outstanding sig group matcharrays %" PRIu32,
+ (detect_siggroup_matcharray_init_cnt -
+ detect_siggroup_matcharray_free_cnt));
+ SCLogDebug(" * Sig group sigarray memory stats done");
+ SCLogDebug(" X Total %" PRIu32,
+ (detect_siggroup_head_memory + detect_siggroup_sigarray_memory +
+ detect_siggroup_matcharray_memory));
+
+ return;
+}
+
+/**
+ * \brief Helper function used to print the list of sids for the Signatures
+ * present in this SigGroupHead.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ */
+void SigGroupHeadPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ SCEnter();
+
+ if (sgh == NULL) {
+ SCReturn;
+ }
+
+ uint32_t u;
+
+ SCLogDebug("The Signatures present in this SigGroupHead are: ");
+ for (u = 0; u < (sgh->init->sig_size * 8); u++) {
+ if (sgh->init->sig_array[u / 8] & (1 << (u % 8))) {
+ SCLogDebug("%" PRIu32, u);
+ printf("s->num %"PRIu32" ", u);
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Helper function used to print the content ids of all the contents that
+ * have been added to the bitarray of this SigGroupHead.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ */
+void SigGroupHeadPrintContent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ SCEnter();
+
+ uint32_t i = 0;
+
+ SCLogDebug("Contents with the following content ids are present in this "
+ "SigGroupHead - ");
+ for (i = 0; i < DetectContentMaxId(de_ctx); i++) {
+ if (sgh->init->content_array[i / 8] & (1 << (i % 8)))
+ SCLogDebug("%" PRIu32, i);
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Helper function used to print the total no of contents that have
+ * been added to the bitarray for this SigGroupHead.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ */
+void SigGroupHeadPrintContentCnt(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ SCEnter();
+
+ uint32_t i = 0;
+ uint32_t cnt = 0;
+
+ for (i = 0; i < DetectContentMaxId(de_ctx); i++) {
+ if (sgh->init->content_array[i / 8] & (1 << (i % 8)))
+ cnt++;
+ }
+
+ SCLogDebug("Total contents added to the SigGroupHead content bitarray: "
+ "%" PRIu32, cnt);
+
+ SCReturn;
+}
+
+/**
+ * \brief Loads all the content ids from all the contents belonging to all the
+ * Signatures in this SigGroupHead, into a bitarray. A fast and an
+ * efficient way of comparing pattern sets.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval 0 On success, i.e. on either the detection engine context being NULL
+ * or on successfully allocating memory and updating it with relevant
+ * data.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadLoadContent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ uint32_t sig = 0;
+ DetectContentData *co = NULL;
+
+ if (sgh == NULL)
+ return 0;
+
+ if (DetectContentMaxId(de_ctx) == 0)
+ return 0;
+
+ BUG_ON(sgh->init == NULL);
+
+ sgh->init->content_size = (DetectContentMaxId(de_ctx) / 8) + 1;
+ sgh->init->content_array = SCMalloc(sgh->init->content_size);
+ if (sgh->init->content_array == NULL)
+ return -1;
+
+ memset(sgh->init->content_array,0, sgh->init->content_size);
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (s->alproto != ALPROTO_UNKNOWN)
+ continue;
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm == NULL)
+ continue;
+
+ for ( ;sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ co = (DetectContentData *)sm->ctx;
+
+ sgh->init->content_array[co->id / 8] |= 1 << (co->id % 8);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Clears the memory allocated by SigGroupHeadLoadContent() for the
+ * bitarray to hold the content ids for a SigGroupHead.
+ *
+ * \param Pointer to the SigGroupHead whose content_array would to be cleared.
+ *
+ * \ret 0 Always.
+ */
+int SigGroupHeadClearContent(SigGroupHead *sh)
+{
+ if (sh == NULL)
+ return 0;
+
+ if (sh->init->content_array != NULL) {
+ SCFree(sh->init->content_array);
+ sh->init->content_array = NULL;
+ sh->init->content_size = 0;
+ }
+ return 0;
+}
+
+/**
+ * \brief Loads all the uri content ids from all the uri contents belonging to
+ * all the Signatures in this SigGroupHead, into a bitarray. A fast and
+ * an efficient way of comparing pattern sets.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval 0 On success, i.e. on either the detection engine context being NULL
+ * or on successfully allocating memory and updating it with relevant
+ * data.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadLoadUricontent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ uint32_t sig = 0;
+ DetectContentData *co = NULL;
+
+ if (sgh == NULL)
+ return 0;
+
+ if (DetectUricontentMaxId(de_ctx) == 0)
+ return 0;
+
+ BUG_ON(sgh->init == NULL);
+
+ sgh->init->uri_content_size = (DetectUricontentMaxId(de_ctx) / 8) + 1;
+ sgh->init->uri_content_array = SCMalloc(sgh->init->uri_content_size);
+ if (sgh->init->uri_content_array == NULL)
+ return -1;
+
+ memset(sgh->init->uri_content_array, 0, sgh->init->uri_content_size);
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+
+ if (s == NULL)
+ continue;
+
+ sm = s->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm == NULL)
+ continue;
+
+ for ( ;sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ co = (DetectContentData *)sm->ctx;
+
+ sgh->init->uri_content_array[co->id / 8] |= 1 << (co->id % 8);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Clears the memory allocated by SigGroupHeadLoadUriContent() for the
+ * bitarray to hold the uri content ids for a SigGroupHead.
+ *
+ * \param Pointer to the SigGroupHead whose uri_content_array would to be
+ * cleared.
+ *
+ * \retval 0 Always.
+ */
+int SigGroupHeadClearUricontent(SigGroupHead *sh)
+{
+ if (sh == NULL)
+ return 0;
+
+ if (sh->init->uri_content_array != NULL) {
+ SCFree(sh->init->uri_content_array);
+ sh->init->uri_content_array = NULL;
+ sh->init->uri_content_size = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Loads all the content ids from all the contents belonging to all the
+ * Signatures in this SigGroupHead, into a bitarray. A fast and an
+ * efficient way of comparing pattern sets.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ *
+ * \retval 0 On success, i.e. on either the detection engine context being NULL
+ * or on successfully allocating memory and updating it with relevant
+ * data.
+ * \retval -1 On failure.
+ */
+int SigGroupHeadLoadStreamContent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ SCEnter();
+
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ uint32_t sig = 0;
+ DetectContentData *co = NULL;
+
+ if (sgh == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (DetectContentMaxId(de_ctx) == 0) {
+ SCReturnInt(0);
+ }
+
+ BUG_ON(sgh->init == NULL);
+
+ sgh->init->stream_content_size = (DetectContentMaxId(de_ctx) / 8) + 1;
+ sgh->init->stream_content_array = SCMalloc(sgh->init->stream_content_size);
+ if (sgh->init->stream_content_array == NULL) {
+ SCReturnInt(-1);
+ }
+
+ memset(sgh->init->stream_content_array,0, sgh->init->stream_content_size);
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+
+ SCLogDebug("s %"PRIu32, s->id);
+
+ if (s == NULL)
+ continue;
+
+ if (SignatureHasPacketContent(s)) {
+ SCLogDebug("Sig has packet content");
+ continue;
+ }
+
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm == NULL)
+ continue;
+
+ for ( ;sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ co = (DetectContentData *)sm->ctx;
+
+ sgh->init->stream_content_array[co->id / 8] |= 1 << (co->id % 8);
+ }
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Clears the memory allocated by SigGroupHeadLoadContent() for the
+ * bitarray to hold the content ids for a SigGroupHead.
+ *
+ * \param Pointer to the SigGroupHead whose content_array would to be cleared.
+ *
+ * \ret 0 Always.
+ */
+int SigGroupHeadClearStreamContent(SigGroupHead *sh)
+{
+ if (sh == NULL)
+ return 0;
+
+ if (sh->init->stream_content_array != NULL) {
+ SCFree(sh->init->stream_content_array);
+ sh->init->stream_content_array = NULL;
+ sh->init->stream_content_size = 0;
+ }
+ return 0;
+}
+
+/**
+ * \brief Create an array with all the internal ids of the sigs that this
+ * sig group head will check for.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead.
+ * \param max_idx The maximum value of the sid in the SigGroupHead arg.
+ *
+ * \retval 0 success
+ * \retval -1 error
+ */
+int SigGroupHeadBuildMatchArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ uint32_t max_idx)
+{
+ Signature *s = NULL;
+ uint32_t idx = 0;
+ uint32_t sig = 0;
+
+ if (sgh == NULL)
+ return 0;
+
+ BUG_ON(sgh->match_array != NULL);
+
+ sgh->match_array = SCMalloc(sgh->sig_cnt * sizeof(Signature *));
+ if (sgh->match_array == NULL)
+ return -1;
+
+ memset(sgh->match_array,0, sgh->sig_cnt * sizeof(Signature *));
+
+ detect_siggroup_matcharray_init_cnt++;
+ detect_siggroup_matcharray_memory += (sgh->sig_cnt * sizeof(Signature *));
+
+ for (sig = 0; sig < max_idx + 1; sig++) {
+ if (!(sgh->init->sig_array[(sig / 8)] & (1 << (sig % 8))) )
+ continue;
+
+ s = de_ctx->sig_array[sig];
+ if (s == NULL)
+ continue;
+
+ sgh->match_array[idx] = s;
+ idx++;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Set the need md5 flag in the sgh.
+ *
+ * \param de_ctx detection engine ctx for the signatures
+ * \param sgh sig group head to set the flag in
+ */
+void SigGroupHeadSetFilemagicFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ uint32_t sig = 0;
+
+ if (sgh == NULL)
+ return;
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureIsFilemagicInspecting(s)) {
+ sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMAGIC;
+ break;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Set the need size flag in the sgh.
+ *
+ * \param de_ctx detection engine ctx for the signatures
+ * \param sgh sig group head to set the flag in
+ */
+void SigGroupHeadSetFilesizeFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ uint32_t sig = 0;
+
+ if (sgh == NULL)
+ return;
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureIsFilesizeInspecting(s)) {
+ sgh->flags |= SIG_GROUP_HEAD_HAVEFILESIZE;
+ break;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Set the need magic flag in the sgh.
+ *
+ * \param de_ctx detection engine ctx for the signatures
+ * \param sgh sig group head to set the flag in
+ */
+void SigGroupHeadSetFileMd5Flag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ uint32_t sig = 0;
+
+ if (sgh == NULL)
+ return;
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureIsFileMd5Inspecting(s)) {
+ sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMD5;
+ SCLogDebug("sgh %p has filemd5", sgh);
+ break;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Set the filestore_cnt in the sgh.
+ *
+ * \param de_ctx detection engine ctx for the signatures
+ * \param sgh sig group head to set the counter in
+ */
+void SigGroupHeadSetFilestoreCount(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ uint32_t sig = 0;
+
+ if (sgh == NULL)
+ return;
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureIsFilestoring(s)) {
+ sgh->filestore_cnt++;
+ }
+ }
+
+ return;
+}
+
+/** \brief build an array of rule id's for sigs with no mpm
+ * Also updated de_ctx::non_mpm_store_cnt_max to track the highest cnt
+ */
+int SigGroupHeadBuildNonMpmArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ Signature *s = NULL;
+ uint32_t sig = 0;
+ uint32_t non_mpm = 0;
+
+ if (sgh == NULL)
+ return 0;
+
+ BUG_ON(sgh->non_mpm_store_array != NULL);
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (s->mpm_sm == NULL)
+ non_mpm++;
+ else if (s->flags & (SIG_FLAG_MPM_PACKET_NEG|SIG_FLAG_MPM_STREAM_NEG|SIG_FLAG_MPM_APPLAYER_NEG))
+ non_mpm++;
+ }
+
+ if (non_mpm == 0) {
+ sgh->non_mpm_store_array = NULL;
+ return 0;
+ }
+
+ sgh->non_mpm_store_array = SCMalloc(non_mpm * sizeof(SignatureNonMpmStore));
+ BUG_ON(sgh->non_mpm_store_array == NULL);
+ memset(sgh->non_mpm_store_array, 0, non_mpm * sizeof(SignatureNonMpmStore));
+
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ s = sgh->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (s->mpm_sm == NULL) {
+ BUG_ON(sgh->non_mpm_store_cnt >= non_mpm);
+ sgh->non_mpm_store_array[sgh->non_mpm_store_cnt].id = s->num;
+ sgh->non_mpm_store_array[sgh->non_mpm_store_cnt].mask = s->mask;
+ sgh->non_mpm_store_cnt++;
+ } else if (s->flags & (SIG_FLAG_MPM_PACKET_NEG|SIG_FLAG_MPM_STREAM_NEG|SIG_FLAG_MPM_APPLAYER_NEG)) {
+ BUG_ON(sgh->non_mpm_store_cnt >= non_mpm);
+ sgh->non_mpm_store_array[sgh->non_mpm_store_cnt].id = s->num;
+ sgh->non_mpm_store_array[sgh->non_mpm_store_cnt].mask = s->mask;
+ sgh->non_mpm_store_cnt++;
+ }
+ }
+
+ /* track highest cnt for any sgh in our de_ctx */
+ if (sgh->non_mpm_store_cnt > de_ctx->non_mpm_store_cnt_max)
+ de_ctx->non_mpm_store_cnt_max = sgh->non_mpm_store_cnt;
+
+ return 0;
+}
+
+/**
+ * \brief Check if a SigGroupHead contains a Signature, whose sid is sent as an
+ * argument.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the SigGroupHead that has to be checked for the
+ * presence of a Signature.
+ * \param sid The Signature id(sid) that has to be checked in the SigGroupHead.
+ *
+ * \retval 1 On successfully finding the sid in the SigGroupHead.
+ * \retval 0 If the sid is not found in the SigGroupHead
+ */
+int SigGroupHeadContainsSigId(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ uint32_t sid)
+{
+ SCEnter();
+
+ uint32_t sig = 0;
+ Signature *s = NULL;
+ uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx);
+
+ if (sgh == NULL) {
+ SCReturnInt(0);
+ }
+
+ for (sig = 0; sig < max_sid; sig++) {
+ if (sgh->init->sig_array == NULL) {
+ SCReturnInt(0);
+ }
+
+ /* Check if the SigGroupHead has an entry for the sid */
+ if ( !(sgh->init->sig_array[sig / 8] & (1 << (sig % 8))) )
+ continue;
+
+ /* If we have reached here, we have an entry for sid in the SigGrouHead.
+ * Retrieve the Signature from the detection engine context */
+ s = de_ctx->sig_array[sig];
+ if (s == NULL)
+ continue;
+
+ /* If the retrieved Signature matches the sid arg, we have a match */
+ if (s->id == sid) {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/*----------------------------------Unittests---------------------------------*/
+
+#ifdef UNITTESTS
+
+int SigAddressPrepareStage1(DetectEngineCtx *);
+
+/**
+ * \test Check if a SigGroupHead mpm hash table is properly allocated and
+ * deallocated when calling SigGroupHeadMpmHashInit() and
+ * SigGroupHeadMpmHashFree() respectively.
+ */
+static int SigGroupHeadTest01(void)
+{
+ int result = 1;
+
+ DetectEngineCtx de_ctx;
+
+ SigGroupHeadMpmHashInit(&de_ctx);
+
+ result &= (de_ctx.sgh_mpm_hash_table != NULL);
+
+ SigGroupHeadMpmHashFree(&de_ctx);
+
+ result &= (de_ctx.sgh_mpm_hash_table == NULL);
+
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHead mpm uri hash table is properly allocated and
+ * deallocated when calling SigGroupHeadMpmUriHashInit() and
+ * SigGroupHeadMpmUriHashFree() respectively.
+ */
+static int SigGroupHeadTest02(void)
+{
+ int result = 1;
+
+ DetectEngineCtx de_ctx;
+
+ SigGroupHeadMpmUriHashInit(&de_ctx);
+
+ result &= (de_ctx.sgh_mpm_uri_hash_table != NULL);
+
+ SigGroupHeadMpmUriHashFree(&de_ctx);
+
+ result &= (de_ctx.sgh_mpm_uri_hash_table == NULL);
+
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHead hash table is properly allocated and
+ * deallocated when calling SigGroupHeadHashInit() and
+ * SigGroupHeadHashFree() respectively.
+ */
+static int SigGroupHeadTest03(void)
+{
+ int result = 1;
+
+ DetectEngineCtx de_ctx;
+
+ SigGroupHeadHashInit(&de_ctx);
+
+ result &= (de_ctx.sgh_hash_table != NULL);
+
+ SigGroupHeadHashFree(&de_ctx);
+
+ result &= (de_ctx.sgh_hash_table == NULL);
+
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHead dport hash table is properly allocated and
+ * deallocated when calling SigGroupHeadDPortHashInit() and
+ * SigGroupHeadDportHashFree() respectively.
+ */
+static int SigGroupHeadTest04(void)
+{
+ int result = 1;
+
+ DetectEngineCtx de_ctx;
+
+ SigGroupHeadDPortHashInit(&de_ctx);
+
+ result &= (de_ctx.sgh_dport_hash_table != NULL);
+
+ SigGroupHeadDPortHashFree(&de_ctx);
+
+ result &= (de_ctx.sgh_dport_hash_table == NULL);
+
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHead dport hash table is properly allocated and
+ * deallocated when calling SigGroupHeadSPortHashInit() and
+ * SigGroupHeadSportHashFree() respectively.
+ */
+static int SigGroupHeadTest05(void)
+{
+ int result = 1;
+
+ DetectEngineCtx de_ctx;
+
+ SigGroupHeadSPortHashInit(&de_ctx);
+
+ result &= (de_ctx.sgh_sport_hash_table != NULL);
+
+ SigGroupHeadSPortHashFree(&de_ctx);
+
+ result &= (de_ctx.sgh_sport_hash_table == NULL);
+
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHeadAppendSig() correctly appends a sid to a
+ * SigGroupHead() and SigGroupHeadContainsSigId() correctly indicates
+ * the presence of a sid.
+ */
+static int SigGroupHeadTest06(void)
+{
+ int result = 1;
+ SigGroupHead *sh = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *prev_sig = NULL;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:0;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = de_ctx->sig_list;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:1;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:2;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:3;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:4;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ SigAddressPrepareStage1(de_ctx);
+
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
+
+ SigGroupHeadSetSigCnt(sh, 4);
+
+ result &= (sh->sig_cnt == 3);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 1);
+
+ SigGroupHeadFree(sh);
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if a SigGroupHeadAppendSig(), correctly appends a sid to a
+ * SigGroupHead() and SigGroupHeadContainsSigId(), correctly indicates
+ * the presence of a sid and SigGroupHeadClearSigs(), correctly clears
+ * the SigGroupHead->sig_array and SigGroupHead->sig_cnt.
+ */
+static int SigGroupHeadTest07(void)
+{
+ int result = 1;
+ SigGroupHead *sh = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *prev_sig = NULL;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:0;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = de_ctx->sig_list;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:1;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:2;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:3;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:4;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ SigAddressPrepareStage1(de_ctx);
+
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
+
+ SigGroupHeadSetSigCnt(sh, 4);
+
+ result &= (sh->sig_cnt == 3);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 1);
+
+ SigGroupHeadClearSigs(sh);
+
+ result &= (sh->sig_cnt == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 0);
+
+ SigGroupHeadFree(sh);
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if SigGroupHeadCopySigs(), correctly copies the sig_array from
+ * the source to the destination SigGroupHead.
+ */
+static int SigGroupHeadTest08(void)
+{
+ int result = 1;
+ SigGroupHead *src_sh = NULL;
+ SigGroupHead *dst_sh = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *prev_sig = NULL;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:0;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = de_ctx->sig_list;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:1;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:2;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:3;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:4;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ SigAddressPrepareStage1(de_ctx);
+
+ SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list);
+ SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list->next->next);
+ SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list->next->next->next->next);
+
+ SigGroupHeadSetSigCnt(src_sh, 4);
+
+ result &= (src_sh->sig_cnt == 3);
+ result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 0) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 1) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 2) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 3) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 4) == 1);
+
+ SigGroupHeadCopySigs(de_ctx, src_sh, &dst_sh);
+
+ SigGroupHeadSetSigCnt(dst_sh, 4);
+
+ result &= (dst_sh->sig_cnt == 3);
+ result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 0) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 1) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 2) == 1);
+ result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 3) == 0);
+ result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 4) == 1);
+
+ SigGroupHeadFree(src_sh);
+ SigGroupHeadFree(dst_sh);
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if SigGroupHeadBuildMatchArray(), correctly updates the
+ * match array with the sids.
+ */
+static int SigGroupHeadTest09(void)
+{
+ int result = 1;
+ SigGroupHead *sh = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *prev_sig = NULL;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:0;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = de_ctx->sig_list;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:1;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:2;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:3;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"SigGroupHead tests\"; content:\"test1\"; "
+ "content:\"test2\"; content:\"test3\"; sid:4;)");
+ if (prev_sig->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ prev_sig = prev_sig->next;
+
+ SigAddressPrepareStage1(de_ctx);
+
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
+ SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
+
+ SigGroupHeadSetSigCnt(sh, 4);
+ SigGroupHeadBuildMatchArray(de_ctx, sh, 4);
+
+ result &= (sh->match_array[0] == de_ctx->sig_list);
+ result &= (sh->match_array[1] == de_ctx->sig_list->next->next);
+ result &= (sh->match_array[2] == de_ctx->sig_list->next->next->next->next);
+
+ SigGroupHeadFree(sh);
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test ICMP(?) sig grouping bug.
+ */
+static int SigGroupHeadTest10(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *s = NULL;
+ Packet *p = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ p = UTHBuildPacketSrcDst(NULL, 0, IPPROTO_ICMP, "192.168.1.1", "1.2.3.4");
+ p->icmpv4h->type = 5;
+ p->icmpv4h->code = 1;
+
+ /* originally ip's were
+ p.src.addr_data32[0] = 0xe08102d3;
+ p.dst.addr_data32[0] = 0x3001a8c0;
+ */
+
+ if (de_ctx == NULL)
+ return 0;
+
+ s = DetectEngineAppendSig(de_ctx, "alert icmp 192.168.0.0/16 any -> any any (icode:>1; itype:11; sid:1; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> 192.168.0.0/16 any (icode:1; itype:5; sid:2; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ AddressDebugPrint(&p->dst);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test sig grouping bug.
+ */
+static int SigGroupHeadTest11(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ Signature *s = NULL;
+ Packet *p = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "192.168.1.1", "1.2.3.4", 60000, 80);
+
+ if (de_ctx == NULL || p == NULL)
+ return 0;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any 1024: -> any 1024: (content:\"abc\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"def\"; http_client_body; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ AddressDebugPrint(&p->dst);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ goto end;
+ }
+
+ /* check if hcbd flag is set in sgh */
+ if (!(sgh->flags & SIG_GROUP_HEAD_MPM_HCBD)) {
+ printf("sgh has not SIG_GROUP_HEAD_MPM_HCBD flag set: ");
+ goto end;
+ }
+
+ /* check if sig 2 is part of the sgh */
+
+ result = 1;
+end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+#endif
+
+void SigGroupHeadRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SigGroupHeadTest01", SigGroupHeadTest01, 1);
+ UtRegisterTest("SigGroupHeadTest02", SigGroupHeadTest02, 1);
+ UtRegisterTest("SigGroupHeadTest03", SigGroupHeadTest03, 1);
+ UtRegisterTest("SigGroupHeadTest04", SigGroupHeadTest04, 1);
+ UtRegisterTest("SigGroupHeadTest05", SigGroupHeadTest05, 1);
+ UtRegisterTest("SigGroupHeadTest06", SigGroupHeadTest06, 1);
+ UtRegisterTest("SigGroupHeadTest07", SigGroupHeadTest07, 1);
+ UtRegisterTest("SigGroupHeadTest08", SigGroupHeadTest08, 1);
+ UtRegisterTest("SigGroupHeadTest09", SigGroupHeadTest09, 1);
+ UtRegisterTest("SigGroupHeadTest10", SigGroupHeadTest10, 1);
+ UtRegisterTest("SigGroupHeadTest11", SigGroupHeadTest11, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-engine-siggroup.h b/framework/src/suricata/src/detect-engine-siggroup.h
new file mode 100644
index 00000000..829b0cef
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-siggroup.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_SIGGROUP_H__
+#define __DETECT_ENGINE_SIGGROUP_H__
+
+void DetectSigGroupPrintMemory(void);
+
+int SigGroupHeadAppendSig(DetectEngineCtx *, SigGroupHead **, Signature *);
+int SigGroupHeadClearSigs(SigGroupHead *);
+int SigGroupHeadCopySigs(DetectEngineCtx *, SigGroupHead *, SigGroupHead **);
+
+int SigGroupHeadLoadContent(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadLoadUricontent(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadLoadStreamContent(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadClearContent(SigGroupHead *);
+int SigGroupHeadClearUricontent(SigGroupHead *);
+int SigGroupHeadClearStreamContent(SigGroupHead *);
+
+void SigGroupHeadFree(SigGroupHead *);
+
+void SigGroupHeadFreeMpmArrays(DetectEngineCtx *);
+
+SigGroupHead *SigGroupHeadHashLookup(DetectEngineCtx *, SigGroupHead *);
+SigGroupHead *SigGroupHeadMpmHashLookup(DetectEngineCtx *, SigGroupHead *);
+SigGroupHead *SigGroupHeadMpmUriHashLookup(DetectEngineCtx *, SigGroupHead *);
+SigGroupHead *SigGroupHeadMpmStreamHashLookup(DetectEngineCtx *, SigGroupHead *);
+SigGroupHead *SigGroupHeadDPortHashLookup(DetectEngineCtx *, SigGroupHead *);
+SigGroupHead *SigGroupHeadSPortHashLookup(DetectEngineCtx *, SigGroupHead *);
+
+int SigGroupHeadMpmHashAdd(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadMpmUriHashAdd(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadMpmStreamHashAdd(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadHashAdd(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadDPortHashAdd(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadSPortHashAdd(DetectEngineCtx *, SigGroupHead *);
+
+void SigGroupHeadHashFree(DetectEngineCtx *);
+void SigGroupHeadMpmHashFree(DetectEngineCtx *);
+void SigGroupHeadMpmUriHashFree(DetectEngineCtx *);
+void SigGroupHeadMpmStreamHashFree(DetectEngineCtx *);
+void SigGroupHeadDPortHashFree(DetectEngineCtx *);
+void SigGroupHeadSPortHashFree(DetectEngineCtx *);
+
+int SigGroupHeadHashInit(DetectEngineCtx *);
+int SigGroupHeadMpmHashInit(DetectEngineCtx *);
+int SigGroupHeadMpmUriHashInit(DetectEngineCtx *);
+int SigGroupHeadDPortHashInit(DetectEngineCtx *);
+int SigGroupHeadSPortHashInit(DetectEngineCtx *);
+
+int SigGroupHeadHashRemove(DetectEngineCtx *, SigGroupHead *);
+int SigGroupHeadSPortHashRemove(DetectEngineCtx *, SigGroupHead *);
+
+void SigGroupHeadInitDataFree(SigGroupHeadInitData *sghid);
+void SigGroupHeadSetSigCnt(SigGroupHead *sgh, uint32_t max_idx);
+int SigGroupHeadBuildMatchArray (DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ uint32_t max_idx);
+void SigGroupHeadFreeSigArrays(DetectEngineCtx *de_ctx);
+
+int SigGroupHeadContainsSigId (DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ uint32_t sid);
+
+void SigGroupHeadRegisterTests(void);
+void SigGroupHeadPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
+
+void SigGroupHeadStore(DetectEngineCtx *, SigGroupHead *);
+void SigGroupHeadSetFilemagicFlag(DetectEngineCtx *, SigGroupHead *);
+void SigGroupHeadSetFilestoreCount(DetectEngineCtx *, SigGroupHead *);
+void SigGroupHeadSetFileMd5Flag(DetectEngineCtx *, SigGroupHead *);
+void SigGroupHeadSetFilesizeFlag(DetectEngineCtx *, SigGroupHead *);
+
+int SigGroupHeadBuildNonMpmArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
+
+#endif /* __DETECT_ENGINE_SIGGROUP_H__ */
diff --git a/framework/src/suricata/src/detect-engine-sigorder.c b/framework/src/suricata/src/detect-engine-sigorder.c
new file mode 100644
index 00000000..0baa87a2
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-sigorder.c
@@ -0,0 +1,2201 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Signature ordering part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-xbits.h"
+#include "detect-flowbits.h"
+#include "detect-flowint.h"
+#include "detect-parse.h"
+#include "detect-engine-sigorder.h"
+#include "detect-pcre.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "util-action.h"
+#include "action-globals.h"
+#include "flow-util.h"
+
+#define DETECT_FLOWVAR_NOT_USED 1
+#define DETECT_FLOWVAR_TYPE_READ 2
+#define DETECT_FLOWVAR_TYPE_SET_READ 3
+#define DETECT_FLOWVAR_TYPE_SET 4
+
+#define DETECT_PKTVAR_NOT_USED 1
+#define DETECT_PKTVAR_TYPE_READ 2
+#define DETECT_PKTVAR_TYPE_SET_READ 3
+#define DETECT_PKTVAR_TYPE_SET 4
+
+#define DETECT_FLOWBITS_NOT_USED 1
+#define DETECT_FLOWBITS_TYPE_READ 2
+#define DETECT_FLOWBITS_TYPE_SET_READ 3
+#define DETECT_FLOWBITS_TYPE_SET 4
+
+#define DETECT_FLOWINT_NOT_USED 1
+#define DETECT_FLOWINT_TYPE_READ 2
+#define DETECT_FLOWINT_TYPE_SET_READ 3
+#define DETECT_FLOWINT_TYPE_SET 4
+
+#define DETECT_XBITS_NOT_USED 1
+#define DETECT_XBITS_TYPE_READ 2
+#define DETECT_XBITS_TYPE_SET_READ 3
+#define DETECT_XBITS_TYPE_SET 4
+
+
+/**
+ * \brief Registers a keyword-based, signature ordering function
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param FuncPtr Pointer to the signature ordering function. The prototype of
+ * the signature ordering function should accept a pointer to a
+ * SCSigSignatureWrapper as its argument and shouldn't return
+ * anything
+ */
+static void SCSigRegisterSignatureOrderingFunc(DetectEngineCtx *de_ctx,
+ int (*SWCompare)(SCSigSignatureWrapper *sw1, SCSigSignatureWrapper *sw2))
+{
+ SCSigOrderFunc *curr = NULL;
+ SCSigOrderFunc *prev = NULL;
+ SCSigOrderFunc *temp = NULL;
+
+ curr = de_ctx->sc_sig_order_funcs;
+
+ /* Walk to the end of the list, and leave prev pointing at the
+ last element. */
+ prev = curr;
+ while (curr != NULL) {
+ if (curr->SWCompare == SWCompare) {
+ /* Already specified this compare */
+ return;
+ }
+ prev = curr;
+ curr = curr->next;
+ }
+
+ if ( (temp = SCMalloc(sizeof(SCSigOrderFunc))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCSigRegisterSignatureOrderingFunc. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(temp, 0, sizeof(SCSigOrderFunc));
+
+ temp->SWCompare = SWCompare;
+
+ /* Append the new compare function at the end of the list. */
+ if (prev == NULL)
+ de_ctx->sc_sig_order_funcs = temp;
+ else
+ prev->next = temp;
+
+ return;
+}
+
+/**
+ * \brief Returns the flowbit type set for this signature. If more than one
+ * flowbit has been set for the same rule, we return the flowbit type of
+ * the maximum priority/value, where priority/value is maximum for the
+ * ones that set the value and the lowest for ones that read the value.
+ * If no flowbit has been set for the rule, we return 0, which indicates
+ * the least value amongst flowbit types.
+ *
+ * \param sig Pointer to the Signature from which the flowbit value has to be
+ * returned.
+ *
+ * \retval flowbits The flowbits type for this signature if it is set; if it is
+ * not set, return 0
+ */
+static inline int SCSigGetFlowbitsType(Signature *sig)
+{
+ DetectFlowbitsData *fb = NULL;
+ int flowbits_user_type = DETECT_FLOWBITS_NOT_USED;
+ int read = 0;
+ int write = 0;
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_FLOWBITS) {
+ fb = (DetectFlowbitsData *)sm->ctx;
+ if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET ||
+ fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
+ read++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ sm = sig->sm_lists[DETECT_SM_LIST_POSTMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_FLOWBITS) {
+ fb = (DetectFlowbitsData *)sm->ctx;
+ if (fb->cmd == DETECT_FLOWBITS_CMD_SET ||
+ fb->cmd == DETECT_FLOWBITS_CMD_UNSET ||
+ fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
+ write++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ if (read > 0 && write == 0) {
+ flowbits_user_type = DETECT_FLOWBITS_TYPE_READ;
+ } else if (read == 0 && write > 0) {
+ flowbits_user_type = DETECT_FLOWBITS_TYPE_SET;
+ } else if (read > 0 && write > 0) {
+ flowbits_user_type = DETECT_FLOWBITS_TYPE_SET_READ;
+ }
+
+ SCLogDebug("Sig %s typeval %d", sig->msg, flowbits_user_type);
+
+ return flowbits_user_type;
+}
+
+static inline int SCSigGetFlowintType(Signature *sig)
+{
+ DetectFlowintData *fi = NULL;
+ int flowint_user_type = DETECT_FLOWINT_NOT_USED;
+ int read = 0;
+ int write = 0;
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_FLOWINT) {
+ fi = (DetectFlowintData *)sm->ctx;
+ if (fi->modifier == FLOWINT_MODIFIER_LT ||
+ fi->modifier == FLOWINT_MODIFIER_LE ||
+ fi->modifier == FLOWINT_MODIFIER_EQ ||
+ fi->modifier == FLOWINT_MODIFIER_NE ||
+ fi->modifier == FLOWINT_MODIFIER_GE ||
+ fi->modifier == FLOWINT_MODIFIER_GT ||
+ fi->modifier == FLOWINT_MODIFIER_ISSET) {
+ read++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ sm = sig->sm_lists[DETECT_SM_LIST_POSTMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_FLOWINT) {
+ fi = (DetectFlowintData *)sm->ctx;
+ if (fi->modifier == FLOWINT_MODIFIER_SET ||
+ fi->modifier == FLOWINT_MODIFIER_ADD ||
+ fi->modifier == FLOWINT_MODIFIER_SUB) {
+ write++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ if (read > 0 && write == 0) {
+ flowint_user_type = DETECT_FLOWINT_TYPE_READ;
+ } else if (read == 0 && write > 0) {
+ flowint_user_type = DETECT_FLOWINT_TYPE_SET;
+ } else if (read > 0 && write > 0) {
+ flowint_user_type = DETECT_FLOWINT_TYPE_SET_READ;
+ }
+
+ SCLogDebug("Sig %s typeval %d", sig->msg, flowint_user_type);
+
+ return flowint_user_type;
+}
+
+/**
+ * \brief Returns whether the flowvar set for this rule, sets the flowvar or
+ * reads the flowvar. If the rule sets the flowvar the function returns
+ * DETECT_FLOWVAR_TYPE_SET(3), if it reads the flowvar the function
+ * returns DETECT_FLOWVAR_TYPE_READ(2), and if flowvar is not used in this
+ * rule the function returns DETECT_FLOWVAR_NOT_USED(1)
+ *
+ * \param sig Pointer to the Signature from which the flowvar type has to be
+ * returned.
+ *
+ * \retval type DETECT_FLOWVAR_TYPE_SET(3) if the rule sets the flowvar,
+ * DETECT_FLOWVAR_TYPE_READ(2) if it reads, and
+ * DETECT_FLOWVAR_NOT_USED(1) if flowvar is not used.
+ */
+static inline int SCSigGetFlowvarType(Signature *sig)
+{
+ DetectPcreData *pd = NULL;
+ int type = DETECT_FLOWVAR_NOT_USED;
+ int read = 0;
+ int write = 0;
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_PMATCH];
+
+ while (sm != NULL) {
+ pd = (DetectPcreData *)sm->ctx;
+ if (sm->type == DETECT_PCRE && (pd->flags & DETECT_PCRE_CAPTURE_FLOW)) {
+ write++;
+ }
+
+ sm = sm->next;
+ }
+
+ sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+ pd = NULL;
+ while (sm != NULL) {
+ if (sm->type == DETECT_FLOWVAR) {
+ read++;
+ }
+
+ sm = sm->next;
+ }
+
+ if (read > 0 && write == 0) {
+ type = DETECT_FLOWVAR_TYPE_READ;
+ } else if (read == 0 && write > 0) {
+ type = DETECT_FLOWVAR_TYPE_SET;
+ } else if (read > 0 && write > 0) {
+ type = DETECT_FLOWVAR_TYPE_SET_READ;
+ }
+
+ return type;
+}
+
+/**
+ * \brief Returns whether the pktvar set for this rule, sets the flowvar or
+ * reads the pktvar. If the rule sets the pktvar the function returns
+ * DETECT_PKTVAR_TYPE_SET(3), if it reads the pktvar the function
+ * returns DETECT_PKTVAR_TYPE_READ(2), and if pktvar is not used in this
+ * rule the function returns DETECT_PKTVAR_NOT_USED(1)
+ *
+ * \param sig Pointer to the Signature from which the pktvar type has to be
+ * returned.
+ *
+ * \retval type DETECT_PKTVAR_TYPE_SET(3) if the rule sets the flowvar,
+ * DETECT_PKTVAR_TYPE_READ(2) if it reads, and
+ * DETECT_PKTVAR_NOT_USED(1) if pktvar is not used.
+ */
+static inline int SCSigGetPktvarType(Signature *sig)
+{
+ DetectPcreData *pd = NULL;
+ int type = DETECT_PKTVAR_NOT_USED;
+ int read = 0;
+ int write = 0;
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_PMATCH];
+
+ while (sm != NULL) {
+ pd = (DetectPcreData *)sm->ctx;
+ if (sm->type == DETECT_PCRE && (pd->flags & DETECT_PCRE_CAPTURE_PKT)) {
+ write++;
+ }
+
+ sm = sm->next;
+ }
+
+ sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+ pd = NULL;
+ while (sm != NULL) {
+ if (sm->type == DETECT_PKTVAR) {
+ read++;
+ }
+
+ sm = sm->next;
+ }
+
+ if (read > 0 && write == 0) {
+ type = DETECT_PKTVAR_TYPE_READ;
+ } else if (read == 0 && write > 0) {
+ type = DETECT_PKTVAR_TYPE_SET;
+ } else if (read > 0 && write > 0) {
+ type = DETECT_PKTVAR_TYPE_SET_READ;
+ }
+
+ return type;
+}
+
+/**
+ * \brief Returns the xbit type set for this signature. If more than one
+ * xbit has been set for the same rule, we return the xbit type of
+ * the maximum priority/value, where priority/value is maximum for the
+ * ones that set the value and the lowest for ones that read the value.
+ * If no xbit has been set for the rule, we return 0, which indicates
+ * the least value amongst xbit types.
+ *
+ * \param sig Pointer to the Signature from which the xbit value has to be
+ * returned.
+ *
+ * \retval xbits The xbits type for this signature if it is set; if it is
+ * not set, return 0
+ */
+static inline int SCSigGetXbitsType(Signature *sig, enum VarTypes type)
+{
+ DetectXbitsData *fb = NULL;
+ int xbits_user_type = DETECT_XBITS_NOT_USED;
+ int read = 0;
+ int write = 0;
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_XBITS) {
+ fb = (DetectXbitsData *)sm->ctx;
+ if (fb->type == type) {
+ if (fb->cmd == DETECT_XBITS_CMD_ISNOTSET ||
+ fb->cmd == DETECT_XBITS_CMD_ISSET) {
+ read++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ sm = sig->sm_lists[DETECT_SM_LIST_POSTMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_HOSTBITS) {
+ fb = (DetectXbitsData *)sm->ctx;
+ if (fb->type == type) {
+ if (fb->cmd == DETECT_XBITS_CMD_SET ||
+ fb->cmd == DETECT_XBITS_CMD_UNSET ||
+ fb->cmd == DETECT_XBITS_CMD_TOGGLE) {
+ write++;
+ } else {
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ }
+ }
+
+ sm = sm->next;
+ }
+
+ if (read > 0 && write == 0) {
+ xbits_user_type = DETECT_XBITS_TYPE_READ;
+ } else if (read == 0 && write > 0) {
+ xbits_user_type = DETECT_XBITS_TYPE_SET;
+ } else if (read > 0 && write > 0) {
+ xbits_user_type = DETECT_XBITS_TYPE_SET_READ;
+ }
+
+ SCLogDebug("Sig %s typeval %d", sig->msg, xbits_user_type);
+
+ return xbits_user_type;
+}
+
+/**
+ * \brief Processes the flowbits data for this signature and caches it for
+ * future use. This is needed to optimize the sig_ordering module.
+ *
+ * \param sw The sigwrapper/signature for which the flowbits data has to be
+ * cached
+ */
+static inline void SCSigProcessUserDataForFlowbits(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_FLOWBITS] = SCSigGetFlowbitsType(sw->sig);
+}
+
+/**
+ * \brief Processes the flowvar data for this signature and caches it for
+ * future use. This is needed to optimize the sig_ordering module.
+ *
+ * \param sw The sigwrapper/signature for which the flowvar data has to be
+ * cached
+ */
+static inline void SCSigProcessUserDataForFlowvar(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_FLOWVAR] = SCSigGetFlowvarType(sw->sig);
+}
+
+static inline void SCSigProcessUserDataForFlowint(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_FLOWINT] = SCSigGetFlowintType(sw->sig);
+}
+
+/**
+ * \brief Processes the pktvar data for this signature and caches it for
+ * future use. This is needed to optimize the sig_ordering module.
+ *
+ * \param sw The sigwrapper/signature for which the pktvar data has to be
+ * cached
+ */
+static inline void SCSigProcessUserDataForPktvar(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_PKTVAR] = SCSigGetPktvarType(sw->sig);
+}
+
+/**
+ * \brief Processes the hostbits data for this signature and caches it for
+ * future use. This is needed to optimize the sig_ordering module.
+ *
+ * \param sw The sigwrapper/signature for which the hostbits data has to be
+ * cached
+ */
+static inline void SCSigProcessUserDataForHostbits(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_HOSTBITS] = SCSigGetXbitsType(sw->sig, VAR_TYPE_HOST_BIT);
+}
+
+/**
+ * \brief Processes the hostbits data for this signature and caches it for
+ * future use. This is needed to optimize the sig_ordering module.
+ *
+ * \param sw The sigwrapper/signature for which the hostbits data has to be
+ * cached
+ */
+static inline void SCSigProcessUserDataForIPPairbits(SCSigSignatureWrapper *sw)
+{
+ sw->user[SC_RADIX_USER_DATA_IPPAIRBITS] = SCSigGetXbitsType(sw->sig, VAR_TYPE_IPPAIR_BIT);
+}
+
+/* Return 1 if sw1 comes before sw2 in the final list. */
+static int SCSigLessThan(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2,
+ SCSigOrderFunc *cmp_func_list)
+{
+ SCSigOrderFunc *funcs = cmp_func_list;
+
+ while (funcs != NULL) {
+ int delta = funcs->SWCompare(sw1, sw2);
+ if (delta > 0)
+ return 1;
+ else if (delta < 0)
+ return 0;
+
+ funcs = funcs->next;
+ }
+ // They are equal, so use sid as the final decider.
+ return sw1->sig->id < sw2->sig->id;
+}
+
+/* Merge sort based on a list of compare functions */
+static SCSigSignatureWrapper *SCSigOrder(SCSigSignatureWrapper *sw,
+ SCSigOrderFunc *cmp_func_list)
+{
+ SCSigSignatureWrapper *subA = NULL;
+ SCSigSignatureWrapper *subB = NULL;
+ SCSigSignatureWrapper *first;
+ SCSigSignatureWrapper *second;
+ SCSigSignatureWrapper *result = NULL;
+ SCSigSignatureWrapper *last = NULL;
+ SCSigSignatureWrapper *new = NULL;
+
+ /* Divide input list into two sub-lists. */
+ while (sw != NULL) {
+ first = sw;
+ sw = sw->next;
+ /* Push the first element onto sub-list A */
+ first->next = subA;
+ subA = first;
+
+ if (sw == NULL)
+ break;
+ second = sw;
+ sw = sw->next;
+ /* Push the second element onto sub-list B */
+ second->next = subB;
+ subB = second;
+ }
+ if (subB == NULL) {
+ /* Only zero or one element on the list. */
+ return subA;
+ }
+
+ /* Now sort each list */
+ subA = SCSigOrder(subA, cmp_func_list);
+ subB = SCSigOrder(subB, cmp_func_list);
+
+ /* Merge the two sorted lists. */
+ while (subA != NULL && subB != NULL) {
+ if (SCSigLessThan(subA, subB, cmp_func_list)) {
+ new = subA;
+ subA = subA->next;
+ } else {
+ new = subB;
+ subB = subB->next;
+ }
+ /* Push onto the end of the output list. */
+ new->next = NULL;
+ if (result == NULL) {
+ result = new;
+ last = new;
+ } else {
+ last->next = new;
+ last = new;
+ }
+ }
+ /* Attach the rest of any remaining list. Only one can be non-NULL here. */
+ if (subA == NULL)
+ last->next = subB;
+ else if (subB == NULL)
+ last->next = subA;
+
+ return result;
+}
+
+/**
+ * \brief Orders an incoming Signature based on its action
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its action
+ */
+static int SCSigOrderByActionCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return ActionOrderVal(sw2->sig->action) - ActionOrderVal(sw1->sig->action);
+}
+
+/**
+ * \brief Orders an incoming Signature based on its flowbits type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its flowbits
+ */
+static int SCSigOrderByFlowbitsCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_FLOWBITS] -
+ sw2->user[SC_RADIX_USER_DATA_FLOWBITS];
+}
+
+/**
+ * \brief Orders an incoming Signature based on its flowvar type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its flowvar
+ */
+static int SCSigOrderByFlowvarCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_FLOWVAR] -
+ sw2->user[SC_RADIX_USER_DATA_FLOWVAR];
+}
+
+/**
+ * \brief Orders an incoming Signature based on its pktvar type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its pktvar
+ */
+static int SCSigOrderByPktvarCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_PKTVAR] -
+ sw2->user[SC_RADIX_USER_DATA_PKTVAR];
+}
+
+static int SCSigOrderByFlowintCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_FLOWINT] -
+ sw2->user[SC_RADIX_USER_DATA_FLOWINT];
+}
+
+/**
+ * \brief Orders an incoming Signature based on its hostbits type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its hostbits
+ */
+static int SCSigOrderByHostbitsCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_HOSTBITS] -
+ sw2->user[SC_RADIX_USER_DATA_HOSTBITS];
+}
+
+/**
+ * \brief Orders an incoming Signature based on its ippairbits (xbits) type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its bits
+ */
+static int SCSigOrderByIPPairbitsCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw1->user[SC_RADIX_USER_DATA_IPPAIRBITS] -
+ sw2->user[SC_RADIX_USER_DATA_IPPAIRBITS];
+}
+
+/**
+ * \brief Orders an incoming Signature based on its priority type
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ * \param sw The new signature that has to be ordered based on its priority
+ */
+static int SCSigOrderByPriorityCompare(SCSigSignatureWrapper *sw1,
+ SCSigSignatureWrapper *sw2)
+{
+ return sw2->sig->prio - sw1->sig->prio;
+}
+
+/**
+ * \brief Creates a Wrapper around the Signature
+ *
+ * \param Pointer to the Signature to be wrapped
+ *
+ * \retval sw Pointer to the wrapper that holds the signature
+ */
+static inline SCSigSignatureWrapper *SCSigAllocSignatureWrapper(Signature *sig)
+{
+ SCSigSignatureWrapper *sw = NULL;
+
+ if ( (sw = SCMalloc(sizeof(SCSigSignatureWrapper))) == NULL)
+ return NULL;
+ memset(sw, 0, sizeof(SCSigSignatureWrapper));
+
+ sw->sig = sig;
+
+ /* Process data from the signature into a cache for further use by the
+ * sig_ordering module */
+ SCSigProcessUserDataForFlowbits(sw);
+ SCSigProcessUserDataForFlowvar(sw);
+ SCSigProcessUserDataForFlowint(sw);
+ SCSigProcessUserDataForPktvar(sw);
+ SCSigProcessUserDataForHostbits(sw);
+ SCSigProcessUserDataForIPPairbits(sw);
+
+ return sw;
+}
+
+/**
+ * \brief Orders the signatures
+ *
+ * \param de_ctx Pointer to the Detection Engine Context that holds the
+ * signatures to be ordered
+ */
+void SCSigOrderSignatures(DetectEngineCtx *de_ctx)
+{
+ Signature *sig = NULL;
+ SCSigSignatureWrapper *sigw = NULL;
+ SCSigSignatureWrapper *sigw_list = NULL;
+
+ int i = 0;
+ SCLogDebug("ordering signatures in memory");
+
+ sig = de_ctx->sig_list;
+ while (sig != NULL) {
+ sigw = SCSigAllocSignatureWrapper(sig);
+ /* Push signature wrapper onto a list, order doesn't matter here. */
+ sigw->next = sigw_list;
+ sigw_list = sigw;
+
+ sig = sig->next;
+ i++;
+ }
+
+ /* Sort the list */
+ sigw_list = SCSigOrder(sigw_list, de_ctx->sc_sig_order_funcs);
+
+ SCLogDebug("Total Signatures to be processed by the"
+ "sigordering module: %d", i);
+
+ /* Recreate the sig list in order */
+ de_ctx->sig_list = NULL;
+ sigw = sigw_list;
+ i = 0;
+ while (sigw != NULL) {
+ i++;
+ sigw->sig->next = NULL;
+ if (de_ctx->sig_list == NULL) {
+ /* First entry on the list */
+ de_ctx->sig_list = sigw->sig;
+ sig = de_ctx->sig_list;
+ } else {
+ sig->next = sigw->sig;
+ sig = sig->next;
+ }
+ SCSigSignatureWrapper *sigw_to_free = sigw;
+ sigw = sigw->next;
+ SCFree(sigw_to_free);
+ }
+
+ SCLogDebug("total signatures reordered by the sigordering module: %d", i);
+}
+
+/**
+ * \brief Lets you register the Signature ordering functions. The order in
+ * which the functions are registered, show the priority. The first
+ * function registered provides more priority than the function
+ * registered after it. To add a new registration function, register
+ * it by listing it in the correct position in the below sequence,
+ * based on the priority you would want to offer to that keyword.
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures have to be ordered.
+ */
+void SCSigRegisterSignatureOrderingFuncs(DetectEngineCtx *de_ctx)
+{
+ SCLogDebug("registering signature ordering functions");
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowintCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByHostbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByIPPairbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+}
+
+/**
+ * \brief De-registers all the signature ordering functions registered
+ *
+ * \param de_ctx Pointer to the detection engine context from which the
+ * signatures were ordered.
+ */
+void SCSigSignatureOrderingModuleCleanup(DetectEngineCtx *de_ctx)
+{
+ SCSigOrderFunc *funcs;
+ void *temp;
+
+ /* clean the memory alloted to the signature ordering funcs */
+ funcs = de_ctx->sc_sig_order_funcs;
+ while (funcs != NULL) {
+ temp = funcs;
+ funcs = funcs->next;
+ SCFree(temp);
+ }
+ de_ctx->sc_sig_order_funcs = NULL;
+}
+
+/**********Unittests**********/
+
+DetectEngineCtx *DetectEngineCtxInit(void);
+Signature *SigInit(DetectEngineCtx *, char *);
+void SigFree(Signature *);
+void DetectEngineCtxFree(DetectEngineCtx *);
+
+#ifdef UNITTESTS
+
+static int SCSigOrderingTest01(void)
+{
+ SCSigOrderFunc *temp = NULL;
+ int i = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+
+ temp = de_ctx->sc_sig_order_funcs;
+ while (temp != NULL) {
+ i++;
+ temp = temp->next;
+ }
+
+ DetectEngineCtxFree(de_ctx);
+
+ return (i == 5);
+ end:
+ return 0;
+}
+
+static int SCSigOrderingTest02(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; flowvar:http_host,\"www.oisf.net\"; rev:4; priority:1; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:1; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering\"; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; content:\"220\"; offset:10; depth:4; rev:4; priority:3; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; rev:4; priority:3; flowbits:set,TEST.one; flowbits:noalert; sid:9;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:11;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:12;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; pktvar:http_host,\"www.oisf.net\"; priority:2; flowbits:isnotset,TEST.two; sid:13;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; flowbits:set,TEST.two; sid:14;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ /* pass */
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 10);
+ sig = sig->next;
+
+ /* drops */
+ result &= (sig->id == 9);
+ sig = sig->next;
+ result &= (sig->id == 13);
+ sig = sig->next;
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+
+ /* alerts */
+ result &= (sig->id == 14);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 11);
+ sig = sig->next;
+ result &= (sig->id == 12);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest03(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; flowbits:unset,TEST.one; rev:4; priority:2; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; flowbits:isset,TEST.one; rev:4; priority:1; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; priority:2; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; flowbits:isnotset,TEST.one; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; rev:4; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/220[- ]/\"; flowbits:unset,TEST.one; rev:4; priority:3; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/220[- ]/\"; flowbits:toggle,TEST.one; rev:4; priority:1; pktvar:http_host,\"www.oisf.net\"; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; rev:4; flowbits:set,TEST.one; flowbits:noalert; pktvar:http_host,\"www.oisf.net\"; sid:9;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:11;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:12;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; flowbits:isnotset,TEST.one; sid:13;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; flowbits:set,TEST.one; sid:14;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 9);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 14);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 13);
+ sig = sig->next;
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 10);
+ sig = sig->next;
+ result &= (sig->id == 11);
+ sig = sig->next;
+ result &= (sig->id == 12);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest04(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; content:\"220\"; offset:10; rev:4; priority:3; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; rev:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<flow_http_host>.*)\\r\\n/m\"; rev:4; priority:3; flowvar:http_host,\"www.oisf.net\"; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; pktvar:http_host,\"www.oisf.net\"; rev:4; priority:1; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; flowvar:http_host,\"www.oisf.net\"; pktvar:http_host,\"www.oisf.net\"; priority:1; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; flowvar:http_host,\"www.oisf.net\"; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; flowvar:http_host,\"www.oisf.net\"; sid:9;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ /* flowvar set */
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 9);
+ sig = sig->next;
+
+ /* pktvar */
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+
+ result &= (sig->id == 1);
+ sig = sig->next;
+
+end:
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest05(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; content:\"220\"; offset:10; rev:4; priority:3; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; rev:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; pcre:\"/^User-Agent: (?P<pkt_http_host>.*)\\r\\n/m\"; rev:4; priority:3; pktvar:http_host,\"www.oisf.net\"; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:3; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; pktvar:http_host,\"www.oisf.net\"; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; pktvar:http_host,\"www.oisf.net\"; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+ //#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+ //#endif
+
+ sig = de_ctx->sig_list;
+
+ /* pktvar set */
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ /* pktvar read */
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest06(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; rev:4; priority:2; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; rev:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:10; depth:4; rev:4; priority:2; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:1; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest07(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Order with a different Action priority
+ * (as specified from config)
+ */
+static int SCSigOrderingTest08(void)
+{
+#ifdef HAVE_LIBNET11
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+ extern uint8_t action_order_sigs[4];
+
+ /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */
+ action_order_sigs[0] = ACTION_REJECT;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_ALERT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "reject tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+
+end:
+ /* Restore the default pre-order definition */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+/**
+ * \test Order with a different Action priority
+ * (as specified from config)
+ */
+static int SCSigOrderingTest09(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+ extern uint8_t action_order_sigs[4];
+
+ /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_REJECT;
+ action_order_sigs[2] = ACTION_ALERT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; priority:2; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; rev:4; priority:2; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:1; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+
+end:
+ /* Restore the default pre-order definition */
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_REJECT;
+ action_order_sigs[2] = ACTION_PASS;
+ action_order_sigs[3] = ACTION_ALERT;
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Order with a different Action priority
+ * (as specified from config)
+ */
+static int SCSigOrderingTest10(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+ extern uint8_t action_order_sigs[4];
+
+ /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_ALERT;
+ action_order_sigs[2] = ACTION_DROP;
+ action_order_sigs[3] = ACTION_REJECT;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; rev:4; priority:2; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; rev:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; rev:4; priority:2; sid:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:1; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; rev:4; priority:2; sid:8;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 4);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+ result &= (sig->id == 8);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 7);
+ sig = sig->next;
+
+end:
+ /* Restore the default pre-order definition */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest11(void)
+{
+ int result = 0;
+ Signature *prevsig = NULL, *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering set\"; flowbits:isnotset,myflow1; rev:4; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig = sig;
+ de_ctx->sig_list = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering toggle\"; flowbits:toggle,myflow2; rev:4; sid:2;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+ prevsig = sig;
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering unset\"; flowbits:isset, myflow1; flowbits:unset,myflow2; rev:4; priority:3; sid:3;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ prevsig->next = sig;
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByActionCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare);
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+ sig = de_ctx->sig_list;
+
+#ifdef DEBUG
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 2);
+ sig = sig->next;
+ result &= (sig->id == 3);
+ sig = sig->next;
+ result &= (sig->id == 1);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SCSigOrderingTest12(void)
+{
+ Signature *sig = NULL;
+ Packet *p = NULL;
+ uint8_t buf[] = "test message";
+ int result = 0;
+ Flow f;
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_UNKNOWN;
+ f.proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ char *sigs[2];
+ sigs[0] = "alert tcp any any -> any any (content:\"test\"; dsize:>0; flowbits:isset,one; flowbits:set,two; sid:1;)";
+ sigs[1] = "alert tcp any any -> any any (content:\"test\"; dsize:>0; flowbits:set,one; sid:2;)";
+ UTHAppendSigs(de_ctx, sigs, 2);
+
+ sig = de_ctx->sig_list;
+ if (sig == NULL)
+ goto end;
+ if (sig->next == NULL)
+ goto end;
+ if (sig->next->next != NULL)
+ goto end;
+ if (de_ctx->signum != 2)
+ goto end;
+
+ FlowInitConfig(FLOW_QUIET);
+ p = UTHBuildPacket(buf, sizeof(buf), IPPROTO_TCP);
+ if (p == NULL) {
+ printf("Error building packet.");
+ goto end;
+ }
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ UTHMatchPackets(de_ctx, &p, 1);
+
+ uint32_t sids[2] = {1, 2};
+ uint32_t results[2] = {1, 1};
+ result = UTHCheckPacketMatchResults(p, sids, results, 2);
+
+end:
+ if (p != NULL)
+ SCFree(p);
+ if (de_ctx != NULL) {
+ SigCleanSignatures(de_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ FlowShutdown();
+
+ return result;
+}
+
+/** \test Bug 1061 */
+static int SCSigOrderingTest13(void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flowbits:isset,bit1; flowbits:set,bit2; flowbits:set,bit3; sid:6;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flowbits:set,bit1; flowbits:set,bit2; sid:7;)");
+ if (sig == NULL) {
+ goto end;
+ }
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flowbits:isset,bit1; flowbits:isset,bit2; flowbits:isset,bit3; sid:5;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare);
+ SCSigOrderSignatures(de_ctx);
+
+ result = 1;
+
+#ifdef DEBUG
+ sig = de_ctx->sig_list;
+ while (sig != NULL) {
+ printf("sid: %d\n", sig->id);
+ sig = sig->next;
+ }
+#endif
+
+ sig = de_ctx->sig_list;
+
+ result &= (sig->id == 7);
+ sig = sig->next;
+ result &= (sig->id == 6);
+ sig = sig->next;
+ result &= (sig->id == 5);
+ sig = sig->next;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif
+
+void SCSigRegisterSignatureOrderingTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCSigOrderingTest01", SCSigOrderingTest01, 1);
+ UtRegisterTest("SCSigOrderingTest02", SCSigOrderingTest02, 1);
+ UtRegisterTest("SCSigOrderingTest03", SCSigOrderingTest03, 1);
+ UtRegisterTest("SCSigOrderingTest04", SCSigOrderingTest04, 1);
+ UtRegisterTest("SCSigOrderingTest05", SCSigOrderingTest05, 1);
+ UtRegisterTest("SCSigOrderingTest06", SCSigOrderingTest06, 1);
+ UtRegisterTest("SCSigOrderingTest07", SCSigOrderingTest07, 1);
+ UtRegisterTest("SCSigOrderingTest08", SCSigOrderingTest08, 1);
+ UtRegisterTest("SCSigOrderingTest09", SCSigOrderingTest09, 1);
+ UtRegisterTest("SCSigOrderingTest10", SCSigOrderingTest10, 1);
+ UtRegisterTest("SCSigOrderingTest11", SCSigOrderingTest11, 1);
+ UtRegisterTest("SCSigOrderingTest12", SCSigOrderingTest12, 1);
+ UtRegisterTest("SCSigOrderingTest13", SCSigOrderingTest13, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-engine-sigorder.h b/framework/src/suricata/src/detect-engine-sigorder.h
new file mode 100644
index 00000000..686ce928
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-sigorder.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_SIGORDER_H__
+#define __DETECT_ENGINE_SIGORDER_H__
+
+/**
+ * \brief Different kinds of helper data that can be used by the signature
+ * ordering module. Used by the "user" field in SCSigSignatureWrapper
+ */
+typedef enum{
+ SC_RADIX_USER_DATA_FLOWBITS,
+ SC_RADIX_USER_DATA_FLOWVAR,
+ SC_RADIX_USER_DATA_PKTVAR,
+ SC_RADIX_USER_DATA_FLOWINT,
+ SC_RADIX_USER_DATA_HOSTBITS,
+ SC_RADIX_USER_DATA_IPPAIRBITS,
+ SC_RADIX_USER_DATA_MAX
+} SCRadixUserDataType;
+
+/**
+ * \brief Signature wrapper used by signature ordering module while ordering
+ * signatures
+ */
+typedef struct SCSigSignatureWrapper_ {
+ /* the wrapped signature */
+ Signature *sig;
+
+ /* used as the lower limit SCSigSignatureWrapper that is used by the next
+ * ordering function, which will order the incoming Sigwrapper after this
+ * (min) wrapper */
+ struct SCSigSignatureWrapper_ *min;
+ /* used as the upper limit SCSigSignatureWrapper that is used by the next
+ * ordering function, which will order the incoming Sigwrapper below this
+ * (max) wrapper */
+ struct SCSigSignatureWrapper_ *max;
+
+ /* user data that is to be associated with this sigwrapper */
+ int user[SC_RADIX_USER_DATA_MAX];
+
+ struct SCSigSignatureWrapper_ *next;
+ struct SCSigSignatureWrapper_ *prev;
+} SCSigSignatureWrapper;
+
+/**
+ * \brief Structure holding the signature ordering function used by the
+ * signature ordering module
+ */
+typedef struct SCSigOrderFunc_ {
+ /* Pointer to the Signature Ordering function */
+ int (*SWCompare)(SCSigSignatureWrapper *sw1, SCSigSignatureWrapper *sw2);
+
+ struct SCSigOrderFunc_ *next;
+} SCSigOrderFunc;
+
+void SCSigOrderSignatures(DetectEngineCtx *);
+void SCSigRegisterSignatureOrderingFuncs(DetectEngineCtx *);
+void SCSigRegisterSignatureOrderingTests(void);
+void SCSigSignatureOrderingModuleCleanup(DetectEngineCtx *);
+
+#endif /* __DETECT_ENGINE_SIGORDER_H__ */
diff --git a/framework/src/suricata/src/detect-engine-state.c b/framework/src/suricata/src/detect-engine-state.c
new file mode 100644
index 00000000..05ca25d5
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-state.c
@@ -0,0 +1,2346 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup sigstate State support
+ *
+ * It is possible to do matching on reconstructed applicative flow.
+ * This is done by this code. It uses the ::Flow structure to store
+ * the list of signatures to match on the reconstructed stream.
+ *
+ * The Flow::de_state is a ::DetectEngineState structure. This is
+ * basically a containter for storage item of type ::DeStateStore.
+ * They contains an array of ::DeStateStoreItem which store the
+ * state of match for an individual signature identified by
+ * DeStateStoreItem::sid.
+ *
+ * The state is constructed by DeStateDetectStartDetection() which
+ * also starts the matching. Work is continued by
+ * DeStateDetectContinueDetection().
+ *
+ * Once a transaction has been analysed DeStateRestartDetection()
+ * is used to reset the structures.
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief State based signature handling.
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-dcepayload.h"
+
+#include "detect-flowvar.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+#include "app-layer-smb.h"
+#include "app-layer-dcerpc-common.h"
+#include "app-layer-dcerpc.h"
+#include "app-layer-dns-common.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-profiling.h"
+
+#include "flow-util.h"
+
+/** convert enum to string */
+#define CASE_CODE(E) case E: return #E
+
+/** The DetectEngineThreadCtx::de_state_sig_array contains 2 separate values:
+ * 1. the first bit tells the prefilter engine to bypass the rule (or not)
+ * 2. the other bits allow 'ContinueDetect' to specify an offset again the
+ * base tx id. This offset will then be used by 'StartDetect' to not
+ * inspect transactions again for the same signature.
+ *
+ * The offset in (2) has a max value due to the limited data type. If it is
+ * set to max the code will fall back to a slower path that validates that
+ * we're not adding duplicate rules to the detection state.
+ */
+#define MAX_STORED_TXID_OFFSET 127
+
+/******** static internal helpers *********/
+
+static inline int StateIsValid(uint16_t alproto, void *alstate)
+{
+ if (alstate != NULL) {
+ if (alproto == ALPROTO_HTTP) {
+ HtpState *htp_state = (HtpState *)alstate;
+ if (htp_state->conn != NULL) {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static inline int TxIsLast(uint64_t tx_id, uint64_t total_txs)
+{
+ if (total_txs - tx_id <= 1)
+ return 1;
+ return 0;
+}
+
+static DeStateStore *DeStateStoreAlloc(void)
+{
+ DeStateStore *d = SCMalloc(sizeof(DeStateStore));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DeStateStore));
+
+ return d;
+}
+static DeStateStoreFlowRules *DeStateStoreFlowRulesAlloc(void)
+{
+ DeStateStoreFlowRules *d = SCMalloc(sizeof(DeStateStoreFlowRules));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DeStateStoreFlowRules));
+
+ return d;
+}
+
+static int DeStateSearchState(DetectEngineState *state, uint8_t direction, SigIntId num)
+{
+ DetectEngineStateDirection *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
+ DeStateStore *tx_store = dir_state->head;
+ SigIntId store_cnt;
+ SigIntId state_cnt = 0;
+
+ for (; tx_store != NULL; tx_store = tx_store->next) {
+ SCLogDebug("tx_store %p", tx_store);
+ for (store_cnt = 0;
+ store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreItem *item = &tx_store->store[store_cnt];
+ if (item->sid == num) {
+ SCLogDebug("sid %u already in state: %p %p %p %u %u, direction %s",
+ num, state, dir_state, tx_store, state_cnt,
+ store_cnt, direction & STREAM_TOSERVER ? "toserver" : "toclient");
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void DeStateSignatureAppend(DetectEngineState *state, Signature *s, uint32_t inspect_flags, uint8_t direction)
+{
+ int jump = 0;
+ int i = 0;
+ DetectEngineStateDirection *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
+
+#ifdef DEBUG_VALIDATION
+ BUG_ON(DeStateSearchState(state, direction, s->num));
+#endif
+ DeStateStore *store = dir_state->head;
+
+ if (store == NULL) {
+ store = DeStateStoreAlloc();
+ if (store != NULL) {
+ dir_state->head = store;
+ dir_state->tail = store;
+ }
+ } else {
+ jump = dir_state->cnt / DE_STATE_CHUNK_SIZE;
+ for (i = 0; i < jump; i++) {
+ store = store->next;
+ }
+ if (store == NULL) {
+ store = DeStateStoreAlloc();
+ if (store != NULL) {
+ dir_state->tail->next = store;
+ dir_state->tail = store;
+ }
+ }
+ }
+
+ if (store == NULL)
+ return;
+
+ SigIntId idx = dir_state->cnt++ % DE_STATE_CHUNK_SIZE;
+ store->store[idx].sid = s->num;
+ store->store[idx].flags = inspect_flags;
+
+ return;
+}
+
+static void DeStateFlowRuleAppend(DetectEngineStateFlow *state, Signature *s,
+ SigMatch *sm, uint32_t inspect_flags,
+ uint8_t direction)
+{
+ int jump = 0;
+ int i = 0;
+ DetectEngineStateDirectionFlow *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
+ DeStateStoreFlowRules *store = dir_state->head;
+
+ if (store == NULL) {
+ store = DeStateStoreFlowRulesAlloc();
+ if (store != NULL) {
+ dir_state->head = store;
+ dir_state->tail = store;
+ }
+ } else {
+ jump = dir_state->cnt / DE_STATE_CHUNK_SIZE;
+ for (i = 0; i < jump; i++) {
+ store = store->next;
+ }
+ if (store == NULL) {
+ store = DeStateStoreFlowRulesAlloc();
+ if (store != NULL) {
+ dir_state->tail->next = store;
+ dir_state->tail = store;
+ }
+ }
+ }
+
+ if (store == NULL)
+ return;
+
+ SigIntId idx = dir_state->cnt++ % DE_STATE_CHUNK_SIZE;
+ store->store[idx].sid = s->num;
+ store->store[idx].flags = inspect_flags;
+ store->store[idx].nm = sm;
+
+ return;
+}
+
+static void DeStateStoreStateVersion(Flow *f,
+ const uint8_t alversion, uint8_t direction)
+{
+ f->detect_alversion[direction & STREAM_TOSERVER ? 0 : 1] = alversion;
+}
+
+static void DeStateStoreFileNoMatchCnt(DetectEngineState *de_state, uint16_t file_no_match, uint8_t direction)
+{
+ de_state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].filestore_cnt += file_no_match;
+
+ return;
+}
+
+static int DeStateStoreFilestoreSigsCantMatch(SigGroupHead *sgh, DetectEngineState *de_state, uint8_t direction)
+{
+ if (de_state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].filestore_cnt == sgh->filestore_cnt)
+ return 1;
+ else
+ return 0;
+}
+
+DetectEngineState *DetectEngineStateAlloc(void)
+{
+ DetectEngineState *d = SCMalloc(sizeof(DetectEngineState));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DetectEngineState));
+
+ return d;
+}
+
+DetectEngineStateFlow *DetectEngineStateFlowAlloc(void)
+{
+ DetectEngineStateFlow *d = SCMalloc(sizeof(DetectEngineStateFlow));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DetectEngineStateFlow));
+
+ return d;
+}
+
+void DetectEngineStateFree(DetectEngineState *state)
+{
+ DeStateStore *store;
+ DeStateStore *store_next;
+ int i = 0;
+
+ for (i = 0; i < 2; i++) {
+ store = state->dir_state[i].head;
+ while (store != NULL) {
+ store_next = store->next;
+ SCFree(store);
+ store = store_next;
+ }
+ }
+ SCFree(state);
+
+ return;
+}
+
+void DetectEngineStateFlowFree(DetectEngineStateFlow *state)
+{
+ DeStateStoreFlowRules *store;
+ DeStateStoreFlowRules *store_next;
+ int i = 0;
+
+ for (i = 0; i < 2; i++) {
+ store = state->dir_state[i].head;
+ while (store != NULL) {
+ store_next = store->next;
+ SCFree(store);
+ store = store_next;
+ }
+ }
+ SCFree(state);
+
+ return;
+}
+
+static int HasStoredSigs(Flow *f, uint8_t flags)
+{
+ if (f->de_state != NULL && f->de_state->dir_state[flags & STREAM_TOSERVER ? 0 : 1].cnt != 0) {
+ SCLogDebug("global sigs present");
+ return 1;
+ }
+
+ if (AppLayerParserProtocolSupportsTxs(f->proto, f->alproto)) {
+ AppProto alproto = f->alproto;
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(f->alproto, alstate)) {
+ return 0;
+ }
+
+ int state = AppLayerParserHasTxDetectState(f->proto, alproto, f->alstate);
+ if (state == -ENOSYS) { /* proto doesn't support this API call */
+ /* fall through */
+ } else if (state == 0) {
+ return 0;
+ }
+ /* if state == 1 we also fall through */
+
+ uint64_t inspect_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ continue;
+ }
+ if (tx_de_state->dir_state[flags & STREAM_TOSERVER ? 0 : 1].cnt != 0) {
+ SCLogDebug("tx %u has sigs present", (uint)inspect_tx_id);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/** \brief Check if we need to inspect this state
+ *
+ * State needs to be inspected if:
+ * 1. state has been updated
+ * 2. we already have de_state in progress
+ *
+ * \retval 0 no inspectable state
+ * \retval 1 inspectable state
+ * \retval 2 inspectable state, but no update
+ */
+int DeStateFlowHasInspectableState(Flow *f, AppProto alproto,
+ const uint8_t alversion, uint8_t flags)
+{
+ int r = 0;
+
+ FLOWLOCK_WRLOCK(f);
+
+ if (!(flags & STREAM_EOF) && f->de_state &&
+ f->detect_alversion[flags & STREAM_TOSERVER ? 0 : 1] == alversion) {
+ SCLogDebug("unchanged state");
+ r = 2;
+ } else if (HasStoredSigs(f, flags)) {
+ r = 1;
+ } else {
+ r = 0;
+ }
+ FLOWLOCK_UNLOCK(f);
+
+ return r;
+}
+
+static int StoreState(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint8_t alversion,
+ Signature *s, SigMatch *sm, const uint32_t inspect_flags,
+ const uint16_t file_no_match)
+{
+ if (f->de_state == NULL) {
+ f->de_state = DetectEngineStateFlowAlloc();
+ if (f->de_state == NULL) {
+ return 0;
+ }
+ }
+
+ DeStateFlowRuleAppend(f->de_state, s, sm, inspect_flags, flags);
+ DeStateStoreStateVersion(f, alversion, flags);
+ return 1;
+}
+
+static void StoreStateTxHandleFiles(DetectEngineThreadCtx *det_ctx, Flow *f,
+ DetectEngineState *destate, const uint8_t flags,
+ const uint64_t tx_id, const uint16_t file_no_match)
+{
+ DeStateStoreFileNoMatchCnt(destate, file_no_match, flags);
+ if (DeStateStoreFilestoreSigsCantMatch(det_ctx->sgh, destate, flags) == 1) {
+ FileDisableStoringForTransaction(f, flags & (STREAM_TOCLIENT | STREAM_TOSERVER), tx_id);
+ destate->dir_state[flags & STREAM_TOSERVER ? 0 : 1].flags |= DETECT_ENGINE_STATE_FLAG_FILE_STORE_DISABLED;
+ }
+}
+
+static void StoreStateTxFileOnly(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint64_t tx_id, void *tx,
+ const uint16_t file_no_match)
+{
+ if (AppLayerParserSupportsTxDetectState(f->proto, f->alproto)) {
+ DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
+ if (destate == NULL) {
+ destate = DetectEngineStateAlloc();
+ if (destate == NULL)
+ return;
+ if (AppLayerParserSetTxDetectState(f->proto, f->alproto, f->alstate, tx, destate) < 0) {
+ DetectEngineStateFree(destate);
+ return;
+ }
+ SCLogDebug("destate created for %"PRIu64, tx_id);
+ }
+ StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
+ }
+}
+
+/**
+ * \param check_before_add check for duplicates before adding the sig
+ */
+static void StoreStateTx(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint8_t alversion,
+ const uint64_t tx_id, void *tx,
+ Signature *s, SigMatch *sm,
+ const uint32_t inspect_flags, const uint16_t file_no_match, int check_before_add)
+{
+ if (AppLayerParserSupportsTxDetectState(f->proto, f->alproto)) {
+ DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
+ if (destate == NULL) {
+ destate = DetectEngineStateAlloc();
+ if (destate == NULL)
+ return;
+ if (AppLayerParserSetTxDetectState(f->proto, f->alproto, f->alstate, tx, destate) < 0) {
+ DetectEngineStateFree(destate);
+ return;
+ }
+ SCLogDebug("destate created for %"PRIu64, tx_id);
+ }
+
+ if (check_before_add == 0 || DeStateSearchState(destate, flags, s->num) == 0)
+ DeStateSignatureAppend(destate, s, inspect_flags, flags);
+ DeStateStoreStateVersion(f, alversion, flags);
+
+ StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
+ }
+ SCLogDebug("Stored for TX %"PRIu64, tx_id);
+}
+
+int DeStateDetectStartDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, const uint8_t alversion)
+{
+ SigMatch *sm = NULL;
+ uint16_t file_no_match = 0;
+ uint32_t inspect_flags = 0;
+ uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
+ int alert_cnt = 0;
+ int check_before_add = 0;
+
+ FLOWLOCK_WRLOCK(f);
+ /* TX based matches (inspect engines) */
+ if (AppLayerParserProtocolSupportsTxs(f->proto, alproto)) {
+ uint64_t tx_id = 0;
+ uint64_t total_txs = 0;
+
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ goto end;
+ }
+
+ /* if continue detection already inspected this rule for this tx,
+ * continue with the first not-inspected tx */
+ uint8_t offset = det_ctx->de_state_sig_array[s->num] & 0xef;
+ tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ if (offset > 0) {
+ SCLogDebug("using stored_tx_id %u instead of %u", (uint)tx_id+offset, (uint)tx_id);
+ tx_id += offset;
+ }
+ if (offset == MAX_STORED_TXID_OFFSET) {
+ check_before_add = 1;
+ }
+
+ total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+ SCLogDebug("total_txs %"PRIu64, total_txs);
+
+ SCLogDebug("starting: start tx %u, packet %u", (uint)tx_id, (uint)p->pcap_cnt);
+
+ for (; tx_id < total_txs; tx_id++) {
+ int total_matches = 0;
+ void *tx = AppLayerParserGetTx(f->proto, alproto, alstate, tx_id);
+ if (tx == NULL)
+ continue;
+ det_ctx->tx_id = tx_id;
+ det_ctx->tx_id_set = 1;
+ DetectEngineAppInspectionEngine *engine = app_inspection_engine[f->protomap][alproto][direction];
+ inspect_flags = 0;
+ while (engine != NULL) {
+ if (s->sm_lists[engine->sm_list] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
+ int match = engine->Callback(tv, de_ctx, det_ctx, s, f,
+ flags, alstate,
+ tx, tx_id);
+ if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
+ inspect_flags |= engine->inspect_flags;
+ engine = engine->next;
+ total_matches++;
+ continue;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ file_no_match++;
+ }
+ break;
+ }
+ engine = engine->next;
+ }
+ /* all the engines seem to be exhausted at this point. If we
+ * didn't have a match in one of the engines we would have
+ * broken off and engine wouldn't be NULL. Hence the alert. */
+ if (engine == NULL && total_matches > 0) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, tx_id,
+ PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_TX);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ SCLogDebug("MATCH: tx %u packet %u", (uint)tx_id, (uint)p->pcap_cnt);
+ }
+
+ /* if this is the last tx in our list, and it's incomplete: then
+ * we store the state so that ContinueDetection knows about it */
+ int tx_is_done = (AppLayerParserGetStateProgress(f->proto, alproto, tx, flags) >=
+ AppLayerParserGetStateProgressCompletionStatus(f->proto, alproto, flags));
+ /* see if we need to consider the next tx in our decision to add
+ * a sig to the 'no inspect array'. */
+ int next_tx_no_progress = 0;
+ if (!TxIsLast(tx_id, total_txs)) {
+ void *next_tx = AppLayerParserGetTx(f->proto, alproto, alstate, tx_id+1);
+ if (next_tx != NULL) {
+ int c = AppLayerParserGetStateProgress(f->proto, alproto, next_tx, flags);
+ if (c == 0) {
+ next_tx_no_progress = 1;
+ }
+ }
+ }
+
+ SCLogDebug("tx %u, packet %u, rule %u, alert_cnt %u, last tx %d, tx_is_done %d, next_tx_no_progress %d",
+ (uint)tx_id, (uint)p->pcap_cnt, s->num, alert_cnt,
+ TxIsLast(tx_id, total_txs), tx_is_done, next_tx_no_progress);
+
+ /* if we have something to store (partial match or file store info),
+ * then we do it now. */
+ if (inspect_flags != 0) {
+ if (!(TxIsLast(tx_id, total_txs)) || !tx_is_done) {
+ if (engine == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ /* store */
+ StoreStateTx(det_ctx, f, flags, alversion, tx_id, tx,
+ s, sm, inspect_flags, file_no_match, check_before_add);
+ } else {
+ StoreStateTxFileOnly(det_ctx, f, flags, tx_id, tx, file_no_match);
+ }
+ } else {
+ SCLogDebug("no state to store");
+ }
+ if (next_tx_no_progress)
+ break;
+ } /* for */
+
+ /* DCERPC matches */
+ } else if (s->sm_lists[DETECT_SM_LIST_DMATCH] != NULL &&
+ (alproto == ALPROTO_DCERPC || alproto == ALPROTO_SMB ||
+ alproto == ALPROTO_SMB2))
+ {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ goto end;
+ }
+
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_DMATCH);
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present &&
+ DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
+ flags, &smb_state->dcerpc) == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ } else {
+ if (DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
+ flags, alstate) == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ }
+ }
+
+ /* flow based matches */
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_AMATCH);
+ sm = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ if (sm != NULL) {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ goto end;
+ }
+
+ int match = 0;
+ for ( ; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].AppLayerMatch != NULL) {
+ match = 0;
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, &smb_state->dcerpc, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+ } else {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, alstate, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+
+ if (match == 0)
+ break;
+ if (match == 2) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ break;
+ }
+ }
+ }
+
+ if (sm == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ if (match == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ StoreState(det_ctx, f, flags, alversion,
+ s, sm, inspect_flags, file_no_match);
+ }
+
+ end:
+ FLOWLOCK_UNLOCK(f);
+
+ det_ctx->tx_id = 0;
+ det_ctx->tx_id_set = 0;
+ return alert_cnt ? 1:0;
+}
+
+static int DoInspectItem(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ DeStateStoreItem *item, const uint8_t dir_state_flags,
+ Packet *p, Flow *f, AppProto alproto, uint8_t flags,
+ const uint64_t inspect_tx_id, const uint64_t total_txs,
+
+ uint16_t *file_no_match, int inprogress, // is current tx in progress?
+ const int next_tx_no_progress) // tx after current is still dormant
+{
+ Signature *s = de_ctx->sig_array[item->sid];
+
+ /* check if a sig in state 'full inspect' needs to be reconsidered
+ * as the result of a new file in the existing tx */
+ if (item->flags & DE_STATE_FLAG_FULL_INSPECT) {
+ if (item->flags & (DE_STATE_FLAG_FILE_TC_INSPECT|DE_STATE_FLAG_FILE_TS_INSPECT)) {
+ if ((flags & STREAM_TOCLIENT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TC_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ if ((flags & STREAM_TOSERVER) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TS_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
+ }
+ }
+
+ if (item->flags & DE_STATE_FLAG_FULL_INSPECT) {
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ return 0;
+ }
+ }
+
+ /* check if a sig in state 'cant match' needs to be reconsidered
+ * as the result of a new file in the existing tx */
+ if (item->flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ if ((flags & STREAM_TOSERVER) &&
+ (item->flags & DE_STATE_FLAG_FILE_TS_INSPECT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TS_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
+ } else if ((flags & STREAM_TOCLIENT) &&
+ (item->flags & DE_STATE_FLAG_FILE_TC_INSPECT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TC_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
+ } else {
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ return 0;
+ }
+ }
+
+ uint8_t alert = 0;
+ uint32_t inspect_flags = 0;
+ int total_matches = 0;
+
+ RULE_PROFILING_START(p);
+
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ RULE_PROFILING_END(det_ctx, s, 0, p);
+ return -1;
+ }
+
+ det_ctx->tx_id = inspect_tx_id;
+ det_ctx->tx_id_set = 1;
+ SCLogDebug("inspecting: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ DetectEngineAppInspectionEngine *engine = app_inspection_engine[f->protomap][alproto][(flags & STREAM_TOSERVER) ? 0 : 1];
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx == NULL) {
+ RULE_PROFILING_END(det_ctx, s, 0, p);
+ return -1;
+ }
+
+ while (engine != NULL) {
+ if (!(item->flags & engine->inspect_flags) &&
+ s->sm_lists[engine->sm_list] != NULL)
+ {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
+ int match = engine->Callback(tv, de_ctx, det_ctx, s, f,
+ flags, alstate, inspect_tx, inspect_tx_id);
+ if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
+ inspect_flags |= engine->inspect_flags;
+ engine = engine->next;
+ total_matches++;
+ continue;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ (*file_no_match)++;
+ }
+ break;
+ }
+ engine = engine->next;
+ }
+ if (total_matches > 0 && (engine == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ if (engine == NULL)
+ alert = 1;
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ item->flags |= inspect_flags;
+ /* flag this sig to don't inspect again from the detection loop it if
+ * there is no need for it */
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("inspected, now bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
+
+ if (alert) {
+ det_ctx->flow_locked = 1;
+ SigMatchSignaturesRunPostMatch(tv, de_ctx, det_ctx, p, s);
+ det_ctx->flow_locked = 0;
+
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, inspect_tx_id,
+ PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_TX);
+ } else {
+ PACKET_UPDATE_ACTION(p, s->action);
+ }
+ SCLogDebug("MATCH: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ }
+
+ DetectFlowvarProcessList(det_ctx, f);
+ return 1;
+}
+
+/** \internal
+ * \brief Continue Detection for a single "flow" rule (AMATCH)
+ */
+static int DoInspectFlowRule(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ DeStateStoreFlowRule *item, const uint8_t dir_state_flags,
+ Packet *p, Flow *f, AppProto alproto, uint8_t flags)
+{
+ /* flag rules that are either full inspected or unable to match
+ * in the de_state_sig_array so that prefilter filters them out */
+ if (item->flags & (DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ return 0;
+ }
+
+ uint8_t alert = 0;
+ uint32_t inspect_flags = 0;
+ int total_matches = 0;
+ SigMatch *sm = NULL;
+ Signature *s = de_ctx->sig_array[item->sid];
+
+ RULE_PROFILING_START(p);
+
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_AMATCH);
+ if (item->nm != NULL) {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ RULE_PROFILING_END(det_ctx, s, 0 /* no match */, p);
+ return -1;
+ }
+
+ for (sm = item->nm; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].AppLayerMatch != NULL)
+ {
+ int match = 0;
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, &smb_state->dcerpc, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+ } else {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, alstate, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+
+ if (match == 0)
+ break;
+ else if (match == 2)
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ else if (match == 1)
+ total_matches++;
+ }
+ }
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ if (total_matches > 0 && (sm == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ if (sm == NULL)
+ alert = 1;
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+ /* prevent the rule loop from reinspecting this rule */
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ }
+ RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
+
+ /* store the progress in the state */
+ item->flags |= inspect_flags;
+ item->nm = sm;
+
+ if (alert) {
+ det_ctx->flow_locked = 1;
+ SigMatchSignaturesRunPostMatch(tv, de_ctx, det_ctx, p, s);
+ det_ctx->flow_locked = 0;
+
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ }
+
+ DetectFlowvarProcessList(det_ctx, f);
+ return 1;
+}
+
+void DeStateDetectContinueDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, const uint8_t alversion)
+{
+ uint16_t file_no_match = 0;
+ SigIntId store_cnt = 0;
+ SigIntId state_cnt = 0;
+ uint64_t inspect_tx_id = 0;
+ uint64_t total_txs = 0;
+ uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
+
+ FLOWLOCK_WRLOCK(f);
+
+ SCLogDebug("starting continue detection for packet %"PRIu64, p->pcap_cnt);
+
+ if (AppLayerParserProtocolSupportsTxs(f->proto, alproto)) {
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ FLOWLOCK_UNLOCK(f);
+ return;
+ }
+
+ inspect_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ int inspect_tx_inprogress = 0;
+ int next_tx_no_progress = 0;
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ int a = AppLayerParserGetStateProgress(f->proto, alproto, inspect_tx, flags);
+ int b = AppLayerParserGetStateProgressCompletionStatus(f->proto, alproto, flags);
+ if (a < b) {
+ inspect_tx_inprogress = 1;
+ }
+ SCLogDebug("tx %"PRIu64" (%"PRIu64") => %s", inspect_tx_id, total_txs,
+ inspect_tx_inprogress ? "in progress" : "done");
+
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ SCLogDebug("NO STATE tx %"PRIu64" (%"PRIu64")", inspect_tx_id, total_txs);
+ continue;
+ }
+ DetectEngineStateDirection *tx_dir_state = &tx_de_state->dir_state[direction];
+ DeStateStore *tx_store = tx_dir_state->head;
+
+ /* see if we need to consider the next tx in our decision to add
+ * a sig to the 'no inspect array'. */
+ if (!TxIsLast(inspect_tx_id, total_txs)) {
+ void *next_inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id+1);
+ if (next_inspect_tx != NULL) {
+ int c = AppLayerParserGetStateProgress(f->proto, alproto, next_inspect_tx, flags);
+ if (c == 0) {
+ next_tx_no_progress = 1;
+ }
+ }
+ }
+
+ /* Loop through stored 'items' (stateful rules) and inspect them */
+ state_cnt = 0;
+ for (; tx_store != NULL; tx_store = tx_store->next) {
+ SCLogDebug("tx_store %p", tx_store);
+ for (store_cnt = 0;
+ store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx_dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreItem *item = &tx_store->store[store_cnt];
+ int r = DoInspectItem(tv, de_ctx, det_ctx,
+ item, tx_dir_state->flags,
+ p, f, alproto, flags,
+ inspect_tx_id, total_txs,
+ &file_no_match, inspect_tx_inprogress, next_tx_no_progress);
+ if (r < 0) {
+ SCLogDebug("failed");
+ goto end;
+ }
+ }
+ }
+ }
+ /* if the current tx is in progress, we won't advance to any newer
+ * tx' just yet. */
+ if (inspect_tx_inprogress) {
+ SCLogDebug("break out");
+ break;
+ }
+ }
+ }
+
+ /* continue on flow based state rules (AMATCH) */
+ if (f->de_state != NULL) {
+ DetectEngineStateDirectionFlow *dir_state = &f->de_state->dir_state[direction];
+ DeStateStoreFlowRules *store = dir_state->head;
+ /* Loop through stored 'items' (stateful rules) and inspect them */
+ for (; store != NULL; store = store->next) {
+ for (store_cnt = 0;
+ store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreFlowRule *rule = &store->store[store_cnt];
+
+ int r = DoInspectFlowRule(tv, de_ctx, det_ctx,
+ rule, dir_state->flags,
+ p, f, alproto, flags);
+ if (r < 0) {
+ goto end;
+ }
+ }
+ }
+ DeStateStoreStateVersion(f, alversion, flags);
+ }
+
+end:
+ FLOWLOCK_UNLOCK(f);
+ det_ctx->tx_id = 0;
+ det_ctx->tx_id_set = 0;
+ return;
+}
+/** \brief update flow's inspection id's
+ *
+ * \param f unlocked flow
+ * \param flags direction and disruption flags
+ *
+ * \note it is possible that f->alstate, f->alparser are NULL */
+void DeStateUpdateInspectTransactionId(Flow *f, const uint8_t flags)
+{
+ FLOWLOCK_WRLOCK(f);
+ if (f->alparser && f->alstate) {
+ AppLayerParserSetTransactionInspectId(f->alparser, f->proto,
+ f->alproto, f->alstate, flags);
+ }
+ FLOWLOCK_UNLOCK(f);
+
+ return;
+}
+
+void DetectEngineStateReset(DetectEngineStateFlow *state, uint8_t direction)
+{
+ if (state != NULL) {
+ if (direction & STREAM_TOSERVER) {
+ state->dir_state[0].cnt = 0;
+ state->dir_state[0].flags = 0;
+ }
+ if (direction & STREAM_TOCLIENT) {
+ state->dir_state[1].cnt = 0;
+ state->dir_state[1].flags = 0;
+ }
+ }
+
+ return;
+}
+
+/** \brief Reset de state for active tx'
+ * To be used on detect engine reload.
+ * \param f write LOCKED flow
+ */
+void DetectEngineStateResetTxs(Flow *f)
+{
+ if (AppLayerParserProtocolSupportsTxs(f->proto, f->alproto)) {
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(f->alproto, alstate)) {
+ return;
+ }
+
+ uint64_t inspect_ts = AppLayerParserGetTransactionInspectId(f->alparser, STREAM_TOCLIENT);
+ uint64_t inspect_tc = AppLayerParserGetTransactionInspectId(f->alparser, STREAM_TOSERVER);
+
+ uint64_t inspect_tx_id = MIN(inspect_ts, inspect_tc);
+
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, f->alproto, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ void *inspect_tx = AppLayerParserGetTx(f->proto, f->alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, f->alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ continue;
+ }
+
+ tx_de_state->dir_state[0].cnt = 0;
+ tx_de_state->dir_state[0].filestore_cnt = 0;
+ tx_de_state->dir_state[0].flags = 0;
+
+ tx_de_state->dir_state[1].cnt = 0;
+ tx_de_state->dir_state[1].filestore_cnt = 0;
+ tx_de_state->dir_state[1].flags = 0;
+ }
+ }
+ }
+}
+
+/*********Unittests*********/
+
+#ifdef UNITTESTS
+#include "flow-util.h"
+
+static int DeStateTest01(void)
+{
+ SCLogDebug("sizeof(DetectEngineState)\t\t%"PRIuMAX,
+ (uintmax_t)sizeof(DetectEngineState));
+ SCLogDebug("sizeof(DeStateStore)\t\t\t%"PRIuMAX,
+ (uintmax_t)sizeof(DeStateStore));
+ SCLogDebug("sizeof(DeStateStoreItem)\t\t%"PRIuMAX"",
+ (uintmax_t)sizeof(DeStateStoreItem));
+
+ return 1;
+}
+
+static int DeStateTest02(void)
+{
+ int result = 0;
+
+ DetectEngineState *state = DetectEngineStateAlloc();
+ if (state == NULL) {
+ printf("d == NULL: ");
+ goto end;
+ }
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ uint8_t direction = STREAM_TOSERVER;
+
+ s.num = 0;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 33;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 44;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 55;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 66;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 77;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 88;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 99;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 100;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 111;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 122;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 133;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 144;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 155;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 166;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 11) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[0].sid != 155) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[1].sid != 166) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (state != NULL) {
+ DetectEngineStateFree(state);
+ }
+ return result;
+}
+
+static int DeStateTest03(void)
+{
+ int result = 0;
+
+ DetectEngineState *state = DetectEngineStateAlloc();
+ if (state == NULL) {
+ printf("d == NULL: ");
+ goto end;
+ }
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ uint8_t direction = STREAM_TOSERVER;
+
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, DE_STATE_FLAG_URI_INSPECT, direction);
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].sid != 11) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].flags & DE_STATE_FLAG_URI_INSPECT) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 22) {
+ goto end;
+ }
+
+ if (!(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].flags & DE_STATE_FLAG_URI_INSPECT)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (state != NULL) {
+ DetectEngineStateFree(state);
+ }
+ return result;
+}
+
+static int DeStateSigTest01(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\nContent-Length: 10\r\n\r\n";
+ uint8_t httpbuf4[] = "Http Body!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy\"; http_cookie; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (http_state != NULL) {
+ HTPStateFree(http_state);
+ }
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multiple pipelined http transactions */
+static int DeStateSigTest02(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Http Body!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nHttp Body!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"/\"; http_uri; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"body\"; nocase; http_client_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted too early: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, f.alstate, 0);
+ if (tx == NULL) {
+ printf("no http tx: ");
+ goto end;
+ }
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(IPPROTO_TCP, ALPROTO_HTTP, tx);
+ if (tx_de_state == NULL || tx_de_state->dir_state[0].cnt != 1 ||
+ tx_de_state->dir_state[0].head->store[0].flags != 0x00000001) {
+ printf("de_state not present or has unexpected content: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int DeStateSigTest03(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_STORE)) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int DeStateSigTest04(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (file->flags & FILE_STORE) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int DeStateSigTest05(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"nomatch\"; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_NOSTORE)) {
+ printf("file is not set to \"no store\": ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int DeStateSigTest06(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"nomatch\"; filestore; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_NOSTORE)) {
+ printf("file is not set to \"no store\": ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+static int DeStateSigTest07(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (file->flags & FILE_STORE) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#endif
+
+void DeStateRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DeStateTest01", DeStateTest01, 1);
+ UtRegisterTest("DeStateTest02", DeStateTest02, 1);
+ UtRegisterTest("DeStateTest03", DeStateTest03, 1);
+ UtRegisterTest("DeStateSigTest01", DeStateSigTest01, 1);
+ UtRegisterTest("DeStateSigTest02", DeStateSigTest02, 1);
+ UtRegisterTest("DeStateSigTest03", DeStateSigTest03, 1);
+ UtRegisterTest("DeStateSigTest04", DeStateSigTest04, 1);
+ UtRegisterTest("DeStateSigTest05", DeStateSigTest05, 1);
+ UtRegisterTest("DeStateSigTest06", DeStateSigTest06, 1);
+ UtRegisterTest("DeStateSigTest07", DeStateSigTest07, 1);
+#endif
+
+ return;
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-state.h b/framework/src/suricata/src/detect-engine-state.h
new file mode 100644
index 00000000..51f67448
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-state.h
@@ -0,0 +1,243 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup sigstate
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \brief Data structures and function prototypes for keeping
+ * state for the detection engine.
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+/* On DeState and locking.
+ *
+ * The DeState is part of a flow, but it can't be protected by the flow lock.
+ * Reason is we need to lock the DeState data for an entire detection run,
+ * as we're looping through on "continued" detection and rely on only a single
+ * detection instance setting it up on first run. We can't keep the entire flow
+ * locked during detection for performance reasons, it would slow us down too
+ * much.
+ *
+ * So a new lock was introduced. The only part of the process where we need
+ * the flow lock is obviously when we're getting/setting the de_state ptr from
+ * to the flow.
+ */
+
+#ifndef __DETECT_ENGINE_STATE_H__
+#define __DETECT_ENGINE_STATE_H__
+
+#define DETECT_ENGINE_INSPECT_SIG_NO_MATCH 0
+#define DETECT_ENGINE_INSPECT_SIG_MATCH 1
+#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH 2
+#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE 3
+
+/** number of DeStateStoreItem's in one DeStateStore object */
+#define DE_STATE_CHUNK_SIZE 15
+
+/* per sig flags */
+#define DE_STATE_FLAG_URI_INSPECT (1)
+#define DE_STATE_FLAG_HRUD_INSPECT (1 << 1)
+#define DE_STATE_FLAG_HCBD_INSPECT (1 << 2)
+#define DE_STATE_FLAG_HSBD_INSPECT (1 << 3)
+#define DE_STATE_FLAG_HHD_INSPECT (1 << 4)
+#define DE_STATE_FLAG_HRHD_INSPECT (1 << 5)
+#define DE_STATE_FLAG_HHHD_INSPECT (1 << 6)
+#define DE_STATE_FLAG_HRHHD_INSPECT (1 << 7)
+#define DE_STATE_FLAG_HUAD_INSPECT (1 << 8)
+#define DE_STATE_FLAG_HMD_INSPECT (1 << 9)
+#define DE_STATE_FLAG_HCD_INSPECT (1 << 10)
+#define DE_STATE_FLAG_HSMD_INSPECT (1 << 11)
+#define DE_STATE_FLAG_HSCD_INSPECT (1 << 12)
+#define DE_STATE_FLAG_FILE_TC_INSPECT (1 << 13)
+#define DE_STATE_FLAG_FILE_TS_INSPECT (1 << 14)
+#define DE_STATE_FLAG_FULL_INSPECT (1 << 15)
+#define DE_STATE_FLAG_SIG_CANT_MATCH (1 << 16)
+#define DE_STATE_FLAG_DNSQUERYNAME_INSPECT (1 << 17)
+#define DE_STATE_FLAG_APP_EVENT_INSPECT (1 << 18)
+#define DE_STATE_FLAG_MODBUS_INSPECT (1 << 19)
+#define DE_STATE_FLAG_HRL_INSPECT (1 << 20)
+#define DE_STATE_FLAG_FD_SMTP_INSPECT (1 << 21)
+#define DE_STATE_FLAG_DNSREQUEST_INSPECT (1 << 22)
+#define DE_STATE_FLAG_DNSRESPONSE_INSPECT (1 << 23)
+
+/* state flags */
+#define DETECT_ENGINE_STATE_FLAG_FILE_STORE_DISABLED 0x0001
+#define DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW 0x0002
+#define DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW 0x0004
+
+/* We have 2 possible state values to be used by ContinueDetection() while
+ * trying to figure if we have fresh state to install or not.
+ *
+ * For tx based alprotos, we don't need to indicate the below values on a
+ * per sig basis, but for non-tx based alprotos we do, since we might have
+ * new alstate coming in, and some sigs might have already matchced in
+ * de_state and ContinueDetection needs to inform the detection filter that
+ * it no longer needs to inspect this sig, since ContinueDetection would
+ * handle it.
+ *
+ * Wrt tx based alprotos, if we have a new tx available apart from the one
+ * currently being inspected(and also added to de_state), we continue with
+ * the HAS_NEW_STATE flag, while if we don't have a new tx, we set
+ * NO_NEW_STATE, to avoid getting the sig reinspected for the already
+ * inspected tx. */
+#define DE_STATE_MATCH_HAS_NEW_STATE 0x00
+#define DE_STATE_MATCH_NO_NEW_STATE 0x80
+
+/* TX BASED (inspect engines) */
+
+typedef struct DeStateStoreItem_ {
+ uint32_t flags;
+ SigIntId sid;
+} DeStateStoreItem;
+
+typedef struct DeStateStore_ {
+ DeStateStoreItem store[DE_STATE_CHUNK_SIZE];
+ struct DeStateStore_ *next;
+} DeStateStore;
+
+typedef struct DetectEngineStateDirection_ {
+ DeStateStore *head;
+ DeStateStore *tail;
+ SigIntId cnt;
+ uint16_t filestore_cnt;
+ uint8_t flags;
+} DetectEngineStateDirection;
+
+typedef struct DetectEngineState_ {
+ DetectEngineStateDirection dir_state[2];
+} DetectEngineState;
+
+/* FLOW BASED (AMATCH) */
+
+typedef struct DeStateStoreFlowRule_ {
+ SigMatch *nm;
+ uint32_t flags;
+ SigIntId sid;
+} DeStateStoreFlowRule;
+
+typedef struct DeStateStoreFlowRules_ {
+ DeStateStoreFlowRule store[DE_STATE_CHUNK_SIZE];
+ struct DeStateStoreFlowRules_ *next;
+} DeStateStoreFlowRules;
+
+typedef struct DetectEngineStateDirectionFlow_ {
+ DeStateStoreFlowRules *head;
+ DeStateStoreFlowRules *tail;
+ SigIntId cnt;
+ uint8_t flags;
+} DetectEngineStateDirectionFlow;
+
+typedef struct DetectEngineStateFlow_ {
+ DetectEngineStateDirectionFlow dir_state[2];
+} DetectEngineStateFlow;
+
+/**
+ * \brief Alloc a DetectEngineState object.
+ *
+ * \retval Alloc'd instance of DetectEngineState.
+ */
+DetectEngineState *DetectEngineStateAlloc(void);
+
+/**
+ * \brief Frees a DetectEngineState object.
+ *
+ * \param state DetectEngineState instance to free.
+ */
+void DetectEngineStateFree(DetectEngineState *state);
+void DetectEngineStateFlowFree(DetectEngineStateFlow *state);
+
+/**
+ * \brief Check if a flow already contains(newly updated as well) de state.
+ *
+ * \param f Pointer to the flow.
+ * \param alversino The alversion to check against de_state's.
+ * \param direction Direction to check. 0 - ts, 1 - tc.
+ *
+ * \retval 1 Has state.
+ * \retval 0 Has no state.
+ */
+int DeStateFlowHasInspectableState(Flow *f, AppProto alproto, uint8_t alversion, uint8_t flags);
+
+/**
+ * \brief Match app layer sig list against app state and store relevant match
+ * information.
+ *
+ * \param tv Pointer to the threadvars.
+ * \param de_ctx DetectEngineCtx instance.
+ * \param det_ctx DetectEngineThreadCtx instance.
+ * \param s Pointer to the signature.
+ * \param f Pointer to the flow.
+ * \param flags Flags.
+ * \param alproto App protocol.
+ * \param alversion Current app layer version.
+ *
+ * \retval >= 0 An integer value indicating the no of matches.
+ */
+int DeStateDetectStartDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, uint8_t alversion);
+
+/**
+ * \brief Continue DeState detection of the signatures stored in the state.
+ *
+ * \param tv Pointer to the threadvars.
+ * \param de_ctx DetectEngineCtx instance.
+ * \param det_ctx DetectEngineThreadCtx instance.
+ * \param f Pointer to the flow.
+ * \param flags Flags.
+ * \param alproto App protocol.
+ * \param alversion Current app layer version.
+ */
+void DeStateDetectContinueDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, uint8_t alversion);
+
+/**
+ * \brief Update the inspect id.
+ *
+ * \param f unlocked flow
+ * \param flags direction and disruption flags
+ */
+void DeStateUpdateInspectTransactionId(Flow *f, const uint8_t flags);
+
+/**
+ * \brief Reset a DetectEngineState state.
+ *
+ * \param state Pointer to the state(LOCKED).
+ * \param direction Direction flags - STREAM_TOSERVER or STREAM_TOCLIENT.
+ */
+void DetectEngineStateReset(DetectEngineStateFlow *state, uint8_t direction);
+
+void DetectEngineStateResetTxs(Flow *f);
+
+void DeStateRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_STATE_H__ */
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-tag.c b/framework/src/suricata/src/detect-engine-tag.c
new file mode 100644
index 00000000..7c8caabb
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-tag.c
@@ -0,0 +1,1519 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-engine-tag.c
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements a global context to store data related to hosts flagged
+ * tag keyword
+ */
+
+#include "suricata-common.h"
+#include "detect-engine.h"
+#include "util-hash.h"
+#include "util-atomic.h"
+#include "util-time.h"
+#include "util-hashlist.h"
+#include "detect-engine-tag.h"
+#include "detect-tag.h"
+#include "host.h"
+#include "host-storage.h"
+#include "flow-storage.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "flow-util.h"
+#include "stream-tcp-private.h"
+
+SC_ATOMIC_DECLARE(unsigned int, num_tags); /**< Atomic counter, to know if we
+ have tagged hosts/sessions,
+ to avoid locking */
+static int host_tag_id = -1; /**< Host storage id for tags */
+static int flow_tag_id = -1; /**< Flow storage id for tags */
+
+void TagInitCtx(void)
+{
+ SC_ATOMIC_INIT(num_tags);
+
+ host_tag_id = HostStorageRegister("tag", sizeof(void *), NULL, DetectTagDataListFree);
+ if (host_tag_id == -1) {
+ SCLogError(SC_ERR_HOST_INIT, "Can't initiate host storage for tag");
+ exit(EXIT_FAILURE);
+ }
+ flow_tag_id = FlowStorageRegister("tag", sizeof(void *), NULL, DetectTagDataListFree);
+ if (flow_tag_id == -1) {
+ SCLogError(SC_ERR_FLOW_INIT, "Can't initiate flow storage for tag");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/**
+ * \brief Destroy tag context hash tables
+ *
+ * \param tag_ctx Tag Context
+ *
+ */
+void TagDestroyCtx(void)
+{
+#ifdef DEBUG
+ BUG_ON(SC_ATOMIC_GET(num_tags) != 0);
+#endif
+ SC_ATOMIC_DESTROY(num_tags);
+}
+
+/** \brief Reset the tagging engine context
+ */
+void TagRestartCtx()
+{
+ TagDestroyCtx();
+ TagInitCtx();
+}
+
+int TagHostHasTag(Host *host)
+{
+ return HostGetStorageById(host, host_tag_id) ? 1 : 0;
+}
+
+static DetectTagDataEntry *DetectTagDataCopy(DetectTagDataEntry *dtd)
+{
+ DetectTagDataEntry *tde = SCMalloc(sizeof(DetectTagDataEntry));
+ if (unlikely(tde == NULL)) {
+ return NULL;
+ }
+ memset(tde, 0, sizeof(DetectTagDataEntry));
+
+ tde->sid = dtd->sid;
+ tde->gid = dtd->gid;
+ tde->flags = dtd->flags;
+ tde->metric = dtd->metric;
+ tde->count = dtd->count;
+
+ tde->first_ts = dtd->first_ts;
+ tde->last_ts = dtd->last_ts;
+ return tde;
+}
+
+/**
+ * \brief This function is used to add a tag to a session (type session)
+ * or update it if it's already installed. The number of times to
+ * allow an update is limited by DETECT_TAG_MATCH_LIMIT. This way
+ * repetitive matches to the same rule are limited of setting tags,
+ * to avoid DOS attacks
+ *
+ * \param p pointer to the current packet
+ * \param tde pointer to the new DetectTagDataEntry
+ *
+ * \retval 0 if the tde was added succesfuly
+ * \retval 1 if an entry of this sid/gid already exist and was updated
+ */
+int TagFlowAdd(Packet *p, DetectTagDataEntry *tde)
+{
+ uint8_t updated = 0;
+ uint16_t tag_cnt = 0;
+ DetectTagDataEntry *iter = NULL;
+
+ if (p->flow == NULL)
+ return 1;
+
+ FLOWLOCK_WRLOCK(p->flow);
+ iter = FlowGetStorageById(p->flow, flow_tag_id);
+ if (iter != NULL) {
+ /* First iterate installed entries searching a duplicated sid/gid */
+ for (; iter != NULL; iter = iter->next) {
+ tag_cnt++;
+
+ if (iter->sid == tde->sid && iter->gid == tde->gid) {
+ iter->cnt_match++;
+
+ /* If so, update data, unless the maximum MATCH limit is
+ * reached. This prevents possible DOS attacks */
+ if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) {
+ /* Reset time and counters */
+ iter->first_ts = iter->last_ts = tde->first_ts;
+ iter->packets = 0;
+ iter->bytes = 0;
+ }
+ updated = 1;
+ break;
+ }
+ }
+ }
+
+ /* If there was no entry of this rule, prepend the new tde */
+ if (updated == 0 && tag_cnt < DETECT_TAG_MAX_TAGS) {
+ DetectTagDataEntry *new_tde = DetectTagDataCopy(tde);
+ if (new_tde != NULL) {
+ new_tde->next = FlowGetStorageById(p->flow, flow_tag_id);
+ FlowSetStorageById(p->flow, flow_tag_id, new_tde);
+ SCLogDebug("adding tag with first_ts %u", new_tde->first_ts);
+ (void) SC_ATOMIC_ADD(num_tags, 1);
+ }
+ } else if (tag_cnt == DETECT_TAG_MAX_TAGS) {
+ SCLogDebug("Max tags for sessions reached (%"PRIu16")", tag_cnt);
+ }
+
+ FLOWLOCK_UNLOCK(p->flow);
+ return updated;
+}
+
+/**
+ * \brief Add a tag entry for a host. If it already exist, update it.
+ *
+ * \param tag_ctx Tag context for hosts
+ * \param tde Tag data
+ * \param p packet
+ *
+ * \retval 0 if it was added, 1 if it was updated
+ */
+int TagHashAddTag(DetectTagDataEntry *tde, Packet *p)
+{
+ SCEnter();
+
+ uint8_t updated = 0;
+ uint16_t num_tags = 0;
+ Host *host = NULL;
+
+ /* Lookup host in the hash. If it doesn't exist yet it's
+ * created. */
+ if (tde->flags & TAG_ENTRY_FLAG_DIR_SRC) {
+ host = HostGetHostFromHash(&p->src);
+ } else if (tde->flags & TAG_ENTRY_FLAG_DIR_DST) {
+ host = HostGetHostFromHash(&p->dst);
+ }
+ /* no host for us */
+ if (host == NULL) {
+ SCLogDebug("host tag not added: no host");
+ return -1;
+ }
+
+ void *tag = HostGetStorageById(host, host_tag_id);
+ if (tag == NULL) {
+ /* get a new tde as the one we have is on the stack */
+ DetectTagDataEntry *new_tde = DetectTagDataCopy(tde);
+ if (new_tde != NULL) {
+ HostSetStorageById(host, host_tag_id, new_tde);
+ (void) SC_ATOMIC_ADD(num_tags, 1);
+ SCLogDebug("host tag added");
+ }
+ } else {
+ /* Append the tag to the list of this host */
+ SCLogDebug("updating existing host");
+
+ /* First iterate installed entries searching a duplicated sid/gid */
+ DetectTagDataEntry *iter = NULL;
+
+ for (iter = tag; iter != NULL; iter = iter->next) {
+ num_tags++;
+ if (iter->sid == tde->sid && iter->gid == tde->gid) {
+ iter->cnt_match++;
+ /* If so, update data, unless the maximum MATCH limit is
+ * reached. This prevents possible DOS attacks */
+ if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) {
+ /* Reset time and counters */
+ iter->first_ts = iter->last_ts = tde->first_ts;
+ iter->packets = 0;
+ iter->bytes = 0;
+ }
+ updated = 1;
+ break;
+ }
+ }
+
+ /* If there was no entry of this rule, append the new tde */
+ if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) {
+ /* get a new tde as the one we have is on the stack */
+ DetectTagDataEntry *new_tde = DetectTagDataCopy(tde);
+ if (new_tde != NULL) {
+ (void) SC_ATOMIC_ADD(num_tags, 1);
+
+ new_tde->next = tag;
+ HostSetStorageById(host, host_tag_id, new_tde);
+ }
+ } else if (num_tags == DETECT_TAG_MAX_TAGS) {
+ SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags);
+ }
+ }
+
+ HostRelease(host);
+ SCReturnInt(updated);
+}
+
+static void TagHandlePacketFlow(Flow *f, Packet *p)
+{
+ if (FlowGetStorageById(f, flow_tag_id) == NULL)
+ return;
+
+ DetectTagDataEntry *tde = NULL;
+ DetectTagDataEntry *prev = NULL;
+ DetectTagDataEntry *iter = FlowGetStorageById(f, flow_tag_id);
+ uint8_t flag_added = 0;
+
+ while (iter != NULL) {
+ /* update counters */
+ iter->last_ts = p->ts.tv_sec;
+ switch (iter->metric) {
+ case DETECT_TAG_METRIC_PACKET:
+ iter->packets++;
+ break;
+ case DETECT_TAG_METRIC_BYTES:
+ iter->bytes += GET_PKT_LEN(p);
+ break;
+ }
+
+ /* If this packet triggered the rule with tag, we dont need
+ * to log it (the alert will log it) */
+ if (!(iter->flags & TAG_ENTRY_FLAG_SKIPPED_FIRST)) {
+ iter->flags |= TAG_ENTRY_FLAG_SKIPPED_FIRST;
+ } else {
+ /* Update metrics; remove if tag expired; and set alerts */
+ switch (iter->metric) {
+ case DETECT_TAG_METRIC_PACKET:
+ if (iter->packets > iter->count) {
+ SCLogDebug("flow tag expired: packets %u > %u",
+ iter->packets, iter->count);
+ /* tag expired */
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ FlowSetStorageById(p->flow, flow_tag_id, iter->next);
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ case DETECT_TAG_METRIC_BYTES:
+ if (iter->bytes > iter->count) {
+ /* tag expired */
+ SCLogDebug("flow tag expired: bytes %u > %u",
+ iter->bytes, iter->count);
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ FlowSetStorageById(p->flow, flow_tag_id, iter->next);
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ case DETECT_TAG_METRIC_SECONDS:
+ /* last_ts handles this metric, but also a generic time based
+ * expiration to prevent dead sessions/hosts */
+ if (iter->last_ts - iter->first_ts > iter->count) {
+ SCLogDebug("flow tag expired: %u - %u = %u > %u",
+ iter->last_ts, iter->first_ts,
+ (iter->last_ts - iter->first_ts), iter->count);
+ /* tag expired */
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ FlowSetStorageById(p->flow, flow_tag_id, iter->next);
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ }
+
+ }
+
+ prev = iter;
+ iter = iter->next;
+ }
+}
+
+void TagHandlePacketHost(Host *host, Packet *p)
+{
+ DetectTagDataEntry *tde = NULL;
+ DetectTagDataEntry *prev = NULL;
+ DetectTagDataEntry *iter;
+ uint8_t flag_added = 0;
+
+ iter = HostGetStorageById(host, host_tag_id);
+ prev = NULL;
+ while (iter != NULL) {
+ /* update counters */
+ iter->last_ts = p->ts.tv_sec;
+ switch (iter->metric) {
+ case DETECT_TAG_METRIC_PACKET:
+ iter->packets++;
+ break;
+ case DETECT_TAG_METRIC_BYTES:
+ iter->bytes += GET_PKT_LEN(p);
+ break;
+ }
+
+ /* If this packet triggered the rule with tag, we dont need
+ * to log it (the alert will log it) */
+ if (!(iter->flags & TAG_ENTRY_FLAG_SKIPPED_FIRST)) {
+ iter->flags |= TAG_ENTRY_FLAG_SKIPPED_FIRST;
+ } else {
+ /* Update metrics; remove if tag expired; and set alerts */
+ switch (iter->metric) {
+ case DETECT_TAG_METRIC_PACKET:
+ if (iter->packets > iter->count) {
+ SCLogDebug("host tag expired: packets %u > %u", iter->packets, iter->count);
+ /* tag expired */
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ HostSetStorageById(host, host_tag_id, iter);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ case DETECT_TAG_METRIC_BYTES:
+ if (iter->bytes > iter->count) {
+ SCLogDebug("host tag expired: bytes %u > %u", iter->bytes, iter->count);
+ /* tag expired */
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ HostSetStorageById(host, host_tag_id, iter);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ case DETECT_TAG_METRIC_SECONDS:
+ /* last_ts handles this metric, but also a generic time based
+ * expiration to prevent dead sessions/hosts */
+ if (iter->last_ts - iter->first_ts > iter->count) {
+ SCLogDebug("host tag expired: %u - %u = %u > %u",
+ iter->last_ts, iter->first_ts,
+ (iter->last_ts - iter->first_ts), iter->count);
+ /* tag expired */
+ if (prev != NULL) {
+ tde = iter;
+ prev->next = iter->next;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ continue;
+ } else {
+ tde = iter;
+ iter = iter->next;
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ HostSetStorageById(host, host_tag_id, iter);
+ continue;
+ }
+ } else if (flag_added == 0) {
+ /* It's matching the tag. Add it to be logged and
+ * update "flag_added" to add the packet once. */
+ p->flags |= PKT_HAS_TAG;
+ flag_added++;
+ }
+ break;
+ }
+
+ }
+
+ prev = iter;
+ iter = iter->next;
+ }
+}
+
+/**
+ * \brief Search tags for src and dst. Update entries of the tag, remove if necessary
+ *
+ * \param de_ctx Detect context
+ * \param det_ctx Detect thread context
+ * \param p packet
+ *
+ */
+void TagHandlePacket(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+ SCEnter();
+
+ /* If there's no tag, get out of here */
+ unsigned int current_tags = SC_ATOMIC_GET(num_tags);
+ if (current_tags == 0)
+ SCReturn;
+
+ /* First update and get session tags */
+ if (p->flow != NULL) {
+ FLOWLOCK_WRLOCK(p->flow);
+ TagHandlePacketFlow(p->flow, p);
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+
+ Host *src = HostLookupHostFromHash(&p->src);
+ if (src) {
+ if (TagHostHasTag(src)) {
+ TagHandlePacketHost(src,p);
+ }
+ HostRelease(src);
+ }
+ Host *dst = HostLookupHostFromHash(&p->dst);
+ if (dst) {
+ if (TagHostHasTag(dst)) {
+ TagHandlePacketHost(dst,p);
+ }
+ HostRelease(dst);
+ }
+ SCReturn;
+}
+
+/**
+ * \brief Removes the entries exceding the max timeout value
+ *
+ * \param tag_ctx Tag context
+ * \param ts the current time
+ *
+ * \retval 1 no tags or tags removed -- host is free to go (from tag perspective)
+ * \retval 0 still active tags
+ */
+int TagTimeoutCheck(Host *host, struct timeval *tv)
+{
+ DetectTagDataEntry *tde = NULL;
+ DetectTagDataEntry *tmp = NULL;
+ DetectTagDataEntry *prev = NULL;
+ int retval = 1;
+
+ tmp = HostGetStorageById(host, host_tag_id);
+ if (tmp == NULL)
+ return 1;
+
+ prev = NULL;
+ while (tmp != NULL) {
+ if ((tv->tv_sec - tmp->last_ts) <= TAG_MAX_LAST_TIME_SEEN) {
+ prev = tmp;
+ tmp = tmp->next;
+ retval = 0;
+ continue;
+ }
+
+ /* timed out */
+
+ if (prev != NULL) {
+ prev->next = tmp->next;
+
+ tde = tmp;
+ tmp = tde->next;
+
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ } else {
+ HostSetStorageById(host, host_tag_id, tmp->next);
+
+ tde = tmp;
+ tmp = tde->next;
+
+ SCFree(tde);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ }
+ }
+ return retval;
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test host tagging: packets
+ */
+int DetectTagTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.9",
+ 41424, 80);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.11",
+ 41424, 80);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.11",
+ 41424, 80);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host,3,packets,src; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host,4,packets,dst; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+
+ int32_t results[7][5] = {
+ {1, 1, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+
+ SCLogDebug("running tests");
+ result = UTHGenericTest(p, 7, sigs, sid, (uint32_t *) results, 5);
+ SCLogDebug("running tests done");
+
+ Host *src = HostLookupHostFromHash(&p[1]->src);
+ if (src) {
+ void *tag = HostGetStorageById(src, host_tag_id);
+ if (tag != NULL) {
+ printf("tag should have been expired: ");
+ result = 0;
+ }
+
+ HostRelease(src);
+ }
+ Host *dst = HostLookupHostFromHash(&p[1]->dst);
+ if (dst) {
+ void *tag = HostGetStorageById(dst, host_tag_id);
+ BUG_ON(tag == NULL);
+
+ DetectTagDataEntry *iter = tag;
+
+ /* check internal state */
+ if (!(iter->gid == 1 && iter->sid == 2 && iter->packets == 4 && iter->count == 4)) {
+ printf("gid %u sid %u packets %u count %u: ", iter->gid, iter->sid, iter->packets, iter->count);
+ result = 0;
+ }
+
+ HostRelease(dst);
+ }
+ BUG_ON(src == NULL || dst == NULL);
+
+ UTHFreePackets(p, 7);
+
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test host tagging: seconds
+ */
+int DetectTagTestPacket02 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.9",
+ 41424, 80);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.11",
+ 41424, 80);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.11",
+ 41424, 80);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host,3,seconds,src; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host,8,seconds,dst; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ //de_ctx->flags |= DE_QUIET;
+
+ int32_t results[7][5] = {
+ {1, 1, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ SCLogDebug("packet %d", i);
+ TimeGet(&p[i]->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ TimeSetIncrementTime(2);
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 2 || i == 3 || i == 5 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+end:
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test host tagging: bytes
+ */
+static int DetectTagTestPacket03 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.9",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.9",
+ 41424, 80);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.11",
+ 41424, 80);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.11",
+ 41424, 80);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host, 150, bytes, src; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host, 150, bytes, dst; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ int32_t results[7][5] = {
+ {1, 1, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 3 || i == 5 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+end:
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test session tagging: packets
+ */
+static int DetectTagTestPacket04 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ Flow *f = NULL;
+ TcpSession ssn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+ FlowInitConfig(1);
+
+ f = FlowAlloc();
+ BUG_ON(f == NULL);
+ FLOW_INITIALIZE(f);
+ f->protoctx = (void *)&ssn;
+ f->flags |= FLOW_IPV4;
+ if (inet_pton(AF_INET, "192.168.1.5", f->src.addr_data32) != 1)
+ goto end;
+ if (inet_pton(AF_INET, "192.168.1.1", f->dst.addr_data32) != 1)
+ goto end;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 80, 41424);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:session,4,packets; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"blahblah\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ int32_t results[7][5] = {
+ {1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ p[i]->flow = f;
+ p[i]->flow->protoctx = &ssn;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 4 || i == 5 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ /* clean up flow */
+ uint8_t proto_map = FlowGetProtoMapping(f->proto);
+ FlowClearMemory(f, proto_map);
+ FLOW_DESTROY(f);
+end:
+ FlowShutdown();
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test session tagging: seconds
+ */
+static int DetectTagTestPacket05 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ Flow *f = NULL;
+ TcpSession ssn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+ FlowInitConfig(1);
+
+ f = FlowAlloc();
+ BUG_ON(f == NULL);
+ FLOW_INITIALIZE(f);
+ f->protoctx = (void *)&ssn;
+ f->flags |= FLOW_IPV4;
+ if (inet_pton(AF_INET, "192.168.1.5", f->src.addr_data32) != 1)
+ goto end;
+ if (inet_pton(AF_INET, "192.168.1.1", f->dst.addr_data32) != 1)
+ goto end;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 80, 41424);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:session,8,seconds; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"blahblah\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ int32_t results[7][5] = {
+ {1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ p[i]->flow = f;
+ p[i]->flow->protoctx = &ssn;
+
+ SCLogDebug("packet %d", i);
+ TimeGet(&p[i]->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ TimeSetIncrementTime(2);
+
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 5 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ /* clean up flow */
+ uint8_t proto_map = FlowGetProtoMapping(f->proto);
+ FlowClearMemory(f, proto_map);
+ FLOW_DESTROY(f);
+end:
+ FlowShutdown();
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test session tagging: bytes
+ */
+static int DetectTagTestPacket06 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ Flow *f = NULL;
+ TcpSession ssn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+ FlowInitConfig(1);
+
+ f = FlowAlloc();
+ BUG_ON(f == NULL);
+ FLOW_INITIALIZE(f);
+ f->protoctx = (void *)&ssn;
+ f->flags |= FLOW_IPV4;
+ if (inet_pton(AF_INET, "192.168.1.5", f->src.addr_data32) != 1)
+ goto end;
+ if (inet_pton(AF_INET, "192.168.1.1", f->dst.addr_data32) != 1)
+ goto end;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 80, 41424);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:session,150,bytes; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"blahblah\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ int32_t results[7][5] = {
+ {1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ p[i]->flow = f;
+ p[i]->flow->protoctx = &ssn;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 3 || i == 4 || i == 5 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ /* clean up flow */
+ uint8_t proto_map = FlowGetProtoMapping(f->proto);
+ FlowClearMemory(f, proto_map);
+ FLOW_DESTROY(f);
+end:
+ FlowShutdown();
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+/**
+ * \test session tagging: bytes, where a 2nd match makes us tag more
+ */
+static int DetectTagTestPacket07 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint8_t *buf2 = (uint8_t *)"lalala!";
+ uint16_t buf_len = strlen((char *)buf);
+ uint16_t buf_len2 = strlen((char *)buf2);
+
+ Flow *f = NULL;
+ TcpSession ssn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ StorageInit();
+ TagInitCtx();
+ StorageFinalize();
+ HostInitConfig(1);
+ FlowInitConfig(1);
+
+ f = FlowAlloc();
+ BUG_ON(f == NULL);
+ FLOW_INITIALIZE(f);
+ f->protoctx = (void *)&ssn;
+ f->flags |= FLOW_IPV4;
+ if (inet_pton(AF_INET, "192.168.1.5", f->src.addr_data32) != 1)
+ goto end;
+ if (inet_pton(AF_INET, "192.168.1.1", f->dst.addr_data32) != 1)
+ goto end;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ Packet *p[7];
+ p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[3] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 80, 41424);
+
+ char *sigs[5];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:session,150,bytes; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"blahblah\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)";
+ sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:4;)";
+ sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:5;)";
+
+ /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */
+ uint32_t sid[5] = {1,2,3,4,5};
+ int numsigs = 5;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ int32_t results[7][5] = {
+ {1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+ };
+
+ int num_packets = 7;
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ p[i]->flow = f;
+ p[i]->flow->protoctx = &ssn;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0)
+ goto cleanup;
+
+ SCLogDebug("packet %d flag %s", i, p[i]->flags & PKT_HAS_TAG ? "true" : "false");
+#if 1
+ /* see if the PKT_HAS_TAG is set on the packet if needed */
+ int expect;
+ if (i == 0 || i == 6)
+ expect = FALSE;
+ else
+ expect = TRUE;
+ if (((p[i]->flags & PKT_HAS_TAG) ? TRUE : FALSE) != expect)
+ goto cleanup;
+#endif
+ }
+
+ result = 1;
+
+cleanup:
+ UTHFreePackets(p, 7);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ /* clean up flow */
+ uint8_t proto_map = FlowGetProtoMapping(f->proto);
+ FlowClearMemory(f, proto_map);
+ FLOW_DESTROY(f);
+end:
+ FlowShutdown();
+ HostShutdown();
+ TagDestroyCtx();
+ StorageCleanup();
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectTag
+ */
+void DetectEngineTagRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectTagTestPacket01", DetectTagTestPacket01, 1);
+ UtRegisterTest("DetectTagTestPacket02", DetectTagTestPacket02, 1);
+ UtRegisterTest("DetectTagTestPacket03", DetectTagTestPacket03, 1);
+ UtRegisterTest("DetectTagTestPacket04", DetectTagTestPacket04, 1);
+ UtRegisterTest("DetectTagTestPacket05", DetectTagTestPacket05, 1);
+ UtRegisterTest("DetectTagTestPacket06", DetectTagTestPacket06, 1);
+ UtRegisterTest("DetectTagTestPacket07", DetectTagTestPacket07, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-engine-tag.h b/framework/src/suricata/src/detect-engine-tag.h
new file mode 100644
index 00000000..93243cef
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-tag.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-engine-tag.h
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements a global context to store data related to hosts flagged
+ * tag keyword
+ */
+
+#ifndef __DETECT_ENGINE_TAG_H__
+#define __DETECT_ENGINE_TAG_H__
+
+#include "host.h"
+#include "detect.h"
+
+/* This limit should be overwriten/predefined at the config file
+ * to limit the options to prevent possible DOS situations. We should also
+ * create a limit for bytes and a limit for number of packets */
+#define TAG_MAX_LAST_TIME_SEEN 600
+
+#define TAG_TIMEOUT_CHECK_INTERVAL 60
+
+/* Used for tagged data (sid and gid of the packets that
+ * follow the one that triggered the rule with tag option) */
+#define TAG_SIG_GEN 2
+#define TAG_SIG_ID 1
+
+int TagHashAddTag(DetectTagDataEntry *, Packet *);
+int TagFlowAdd(Packet *, DetectTagDataEntry *);
+
+void TagContextDestroy(void);
+void TagHandlePacket(DetectEngineCtx *, DetectEngineThreadCtx *, Packet *);
+
+void TagInitCtx(void);
+void TagDestroyCtx(void);
+void TagRestartCtx(void);
+
+int TagTimeoutCheck(Host *, struct timeval *);
+
+int TagHostHasTag(Host *host);
+
+void DetectEngineTagRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_TAG_H__ */
+
+
diff --git a/framework/src/suricata/src/detect-engine-threshold.c b/framework/src/suricata/src/detect-engine-threshold.c
new file mode 100644
index 00000000..6c6d1371
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-threshold.c
@@ -0,0 +1,689 @@
+/* Copyright (C) 2007-2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup threshold Thresholding
+ *
+ * This feature is used to reduce the number of logged alerts for noisy rules.
+ * This can be tuned to significantly reduce false alarms, and it can also be
+ * used to write a newer breed of rules. Thresholding commands limit the number
+ * of times a particular event is logged during a specified time interval.
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Threshold part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+
+#include "host.h"
+#include "host-storage.h"
+
+#include "detect-parse.h"
+#include "detect-engine-sigorder.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-iponly.h"
+
+#include "detect-engine.h"
+#include "detect-engine-threshold.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "util-hash.h"
+#include "util-time.h"
+#include "util-error.h"
+#include "util-debug.h"
+
+#include "util-var-name.h"
+#include "tm-threads.h"
+
+static int threshold_id = -1; /**< host storage id for thresholds */
+
+int ThresholdHostStorageId(void)
+{
+ return threshold_id;
+}
+
+void ThresholdInit(void)
+{
+ threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
+ if (threshold_id == -1) {
+ SCLogError(SC_ERR_HOST_INIT, "Can't initiate host storage for thresholding");
+ exit(EXIT_FAILURE);
+ }
+}
+
+int ThresholdHostHasThreshold(Host *host)
+{
+ return HostGetStorageById(host, threshold_id) ? 1 : 0;
+}
+
+/**
+ * \brief Return next DetectThresholdData for signature
+ *
+ * \param sig Signature pointer
+ * \param p Packet structure
+ * \param sm Pointer to a Signature Match pointer
+ *
+ * \retval tsh Return the threshold data from signature or NULL if not found
+ *
+ *
+ */
+DetectThresholdData *SigGetThresholdTypeIter(Signature *sig, Packet *p, SigMatch **psm, int list)
+{
+ SigMatch *sm = NULL;
+ DetectThresholdData *tsh = NULL;
+
+ if (sig == NULL)
+ return NULL;
+
+ if (*psm == NULL) {
+ sm = sig->sm_lists_tail[list];
+ } else {
+ /* Iteration in progress, using provided value */
+ sm = *psm;
+ }
+
+ if (p == NULL)
+ return NULL;
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_THRESHOLD || sm->type == DETECT_DETECTION_FILTER) {
+ tsh = (DetectThresholdData *)sm->ctx;
+ *psm = sm->prev;
+ return tsh;
+ }
+
+ sm = sm->prev;
+ }
+ *psm = NULL;
+
+ return NULL;
+}
+
+/**
+ * \brief Remove timeout threshold hash elements
+ *
+ * \param de_ctx Dectection Context
+ *
+ */
+
+int ThresholdTimeoutCheck(Host *host, struct timeval *tv)
+{
+ DetectThresholdEntry *tde = NULL;
+ DetectThresholdEntry *tmp = NULL;
+ DetectThresholdEntry *prev = NULL;
+ int retval = 1;
+
+ tmp = HostGetStorageById(host, threshold_id);
+ if (tmp == NULL)
+ return 1;
+
+ prev = NULL;
+ while (tmp != NULL) {
+ if ((tv->tv_sec - tmp->tv_sec1) <= tmp->seconds) {
+ prev = tmp;
+ tmp = tmp->next;
+ retval = 0;
+ continue;
+ }
+
+ /* timed out */
+
+ if (prev != NULL) {
+ prev->next = tmp->next;
+
+ tde = tmp;
+ tmp = tde->next;
+
+ SCFree(tde);
+ } else {
+ HostSetStorageById(host, threshold_id, tmp->next);
+ tde = tmp;
+ tmp = tde->next;
+
+ SCFree(tde);
+ }
+ }
+
+ return retval;
+}
+
+static inline DetectThresholdEntry *DetectThresholdEntryAlloc(DetectThresholdData *td, Packet *p, uint32_t sid, uint32_t gid)
+{
+ SCEnter();
+
+ DetectThresholdEntry *ste = SCMalloc(sizeof(DetectThresholdEntry));
+ if (unlikely(ste == NULL)) {
+ SCReturnPtr(NULL, "DetectThresholdEntry");
+ }
+
+ ste->sid = sid;
+ ste->gid = gid;
+
+ ste->track = td->track;
+ ste->seconds = td->seconds;
+ ste->tv_timeout = 0;
+
+ SCReturnPtr(ste, "DetectThresholdEntry");
+}
+
+static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h, uint32_t sid, uint32_t gid)
+{
+ DetectThresholdEntry *e;
+
+ for (e = HostGetStorageById(h, threshold_id); e != NULL; e = e->next) {
+ if (e->sid == sid && e->gid == gid)
+ break;
+ }
+
+ return e;
+}
+
+int ThresholdHandlePacketSuppress(Packet *p, DetectThresholdData *td, uint32_t sid, uint32_t gid)
+{
+ int ret = 0;
+ DetectAddress *m = NULL;
+ switch (td->track) {
+ case TRACK_DST:
+ m = DetectAddressLookupInHead(&td->addrs, &p->dst);
+ SCLogInfo("TRACK_DST");
+ break;
+ case TRACK_SRC:
+ m = DetectAddressLookupInHead(&td->addrs, &p->src);
+ SCLogInfo("TRACK_SRC");
+ break;
+ /* suppress if either src or dst is a match on the suppress
+ * address list */
+ case TRACK_EITHER:
+ m = DetectAddressLookupInHead(&td->addrs, &p->src);
+ if (m == NULL) {
+ m = DetectAddressLookupInHead(&td->addrs, &p->dst);
+ }
+ break;
+ case TRACK_RULE:
+ default:
+ SCLogError(SC_ERR_INVALID_VALUE,
+ "track mode %d is not supported", td->track);
+ break;
+ }
+ if (m == NULL)
+ ret = 1;
+ else
+ ret = 2; /* suppressed but still need actions */
+
+ return ret;
+}
+
+/**
+ * \retval 2 silent match (no alert but apply actions)
+ * \retval 1 normal match
+ * \retval 0 no match
+ */
+int ThresholdHandlePacketHost(Host *h, Packet *p, DetectThresholdData *td, uint32_t sid, uint32_t gid)
+{
+ int ret = 0;
+
+ DetectThresholdEntry *lookup_tsh = ThresholdHostLookupEntry(h, sid, gid);
+ SCLogDebug("lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid);
+
+ switch(td->type) {
+ case TYPE_LIMIT:
+ {
+ SCLogDebug("limit");
+
+ if (lookup_tsh != NULL) {
+ if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) {
+ lookup_tsh->current_count++;
+
+ if (lookup_tsh->current_count <= td->count) {
+ ret = 1;
+ } else {
+ ret = 2;
+ }
+ } else {
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->current_count = 1;
+
+ ret = 1;
+ }
+ } else {
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
+ if (e == NULL) {
+ break;
+ }
+
+ e->tv_sec1 = p->ts.tv_sec;
+ e->current_count = 1;
+
+ ret = 1;
+
+ e->next = HostGetStorageById(h, threshold_id);
+ HostSetStorageById(h, threshold_id, e);
+ }
+ break;
+ }
+ case TYPE_THRESHOLD:
+ {
+ SCLogDebug("threshold");
+
+ if (lookup_tsh != NULL) {
+ if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) {
+ lookup_tsh->current_count++;
+
+ if (lookup_tsh->current_count >= td->count) {
+ ret = 1;
+ lookup_tsh->current_count = 0;
+ }
+ } else {
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->current_count = 1;
+ }
+ } else {
+ if (td->count == 1) {
+ ret = 1;
+ } else {
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
+ if (e == NULL) {
+ break;
+ }
+
+ e->current_count = 1;
+ e->tv_sec1 = p->ts.tv_sec;
+
+ e->next = HostGetStorageById(h, threshold_id);
+ HostSetStorageById(h, threshold_id, e);
+ }
+ }
+ break;
+ }
+ case TYPE_BOTH:
+ {
+ SCLogDebug("both");
+
+ if (lookup_tsh != NULL) {
+ if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) {
+ /* within time limit */
+
+ lookup_tsh->current_count++;
+ if (lookup_tsh->current_count == td->count) {
+ ret = 1;
+ } else if (lookup_tsh->current_count > td->count) {
+ /* silent match */
+ ret = 2;
+ }
+ } else {
+ /* expired, so reset */
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->current_count = 1;
+
+ /* if we have a limit of 1, this is a match */
+ if (lookup_tsh->current_count == td->count) {
+ ret = 1;
+ }
+ }
+ } else {
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
+ if (e == NULL) {
+ break;
+ }
+
+ e->current_count = 1;
+ e->tv_sec1 = p->ts.tv_sec;
+
+ e->next = HostGetStorageById(h, threshold_id);
+ HostSetStorageById(h, threshold_id, e);
+
+ /* for the first match we return 1 to
+ * indicate we should alert */
+ if (td->count == 1) {
+ ret = 1;
+ }
+ }
+ break;
+ }
+ /* detection_filter */
+ case TYPE_DETECTION:
+ {
+ SCLogDebug("detection_filter");
+
+ if (lookup_tsh != NULL) {
+ long double time_diff = ((p->ts.tv_sec + p->ts.tv_usec/1000000.0) -
+ (lookup_tsh->tv_sec1 + lookup_tsh->tv_usec1/1000000.0));
+
+ if (time_diff < td->seconds) {
+ /* within timeout */
+
+ lookup_tsh->current_count++;
+ if (lookup_tsh->current_count > td->count) {
+ ret = 1;
+ }
+ } else {
+ /* expired, reset */
+
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->tv_usec1 = p->ts.tv_usec;
+ lookup_tsh->current_count = 1;
+ }
+ } else {
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
+ if (e == NULL) {
+ break;
+ }
+
+ e->current_count = 1;
+ e->tv_sec1 = p->ts.tv_sec;
+ e->tv_usec1 = p->ts.tv_usec;
+
+ e->next = HostGetStorageById(h, threshold_id);
+ HostSetStorageById(h, threshold_id, e);
+ }
+ break;
+ }
+ /* rate_filter */
+ case TYPE_RATE:
+ {
+ SCLogDebug("rate_filter");
+
+ ret = 1;
+
+ if (lookup_tsh != NULL) {
+ /* Check if we have a timeout enabled, if so,
+ * we still matching (and enabling the new_action) */
+ if (lookup_tsh->tv_timeout != 0) {
+ if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) {
+ /* Ok, we are done, timeout reached */
+ lookup_tsh->tv_timeout = 0;
+ } else {
+ /* Already matching */
+ /* Take the action to perform */
+ switch (td->new_action) {
+ case TH_ACTION_ALERT:
+ PACKET_ALERT(p);
+ break;
+ case TH_ACTION_DROP:
+ PACKET_DROP(p);
+ break;
+ case TH_ACTION_REJECT:
+ PACKET_REJECT(p);
+ break;
+ case TH_ACTION_PASS:
+ PACKET_PASS(p);
+ break;
+ default:
+ /* Weird, leave the default action */
+ break;
+ }
+ ret = 1;
+ } /* else - if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) */
+
+ } else {
+ /* Update the matching state with the timeout interval */
+ if ( (p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) {
+ lookup_tsh->current_count++;
+ if (lookup_tsh->current_count > td->count) {
+ /* Then we must enable the new action by setting a
+ * timeout */
+ lookup_tsh->tv_timeout = p->ts.tv_sec;
+ /* Take the action to perform */
+ switch (td->new_action) {
+ case TH_ACTION_ALERT:
+ PACKET_ALERT(p);
+ break;
+ case TH_ACTION_DROP:
+ PACKET_DROP(p);
+ break;
+ case TH_ACTION_REJECT:
+ PACKET_REJECT(p);
+ break;
+ case TH_ACTION_PASS:
+ PACKET_PASS(p);
+ break;
+ default:
+ /* Weird, leave the default action */
+ break;
+ }
+ ret = 1;
+ }
+ } else {
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->current_count = 1;
+ }
+ } /* else - if (lookup_tsh->tv_timeout != 0) */
+ } else {
+ if (td->count == 1) {
+ ret = 1;
+ }
+
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
+ if (e == NULL) {
+ break;
+ }
+
+ e->current_count = 1;
+ e->tv_sec1 = p->ts.tv_sec;
+ e->tv_timeout = 0;
+
+ e->next = HostGetStorageById(h, threshold_id);
+ HostSetStorageById(h, threshold_id, e);
+ }
+ break;
+ }
+ /* case TYPE_SUPPRESS: is not handled here */
+ default:
+ SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type);
+ }
+
+ return ret;
+}
+
+static int ThresholdHandlePacketRule(DetectEngineCtx *de_ctx, Packet *p, DetectThresholdData *td, Signature *s)
+{
+ int ret = 0;
+
+ if (td->type != TYPE_RATE)
+ return 1;
+
+ DetectThresholdEntry* lookup_tsh = (DetectThresholdEntry *)de_ctx->ths_ctx.th_entry[s->num];
+ if (lookup_tsh != NULL) {
+ /* Check if we have a timeout enabled, if so,
+ * we still matching (and enabling the new_action) */
+ if ( (p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) {
+ /* Ok, we are done, timeout reached */
+ td->timeout = 0;
+ } else {
+ /* Already matching */
+ /* Take the action to perform */
+ switch (td->new_action) {
+ case TH_ACTION_ALERT:
+ PACKET_ALERT(p);
+ break;
+ case TH_ACTION_DROP:
+ PACKET_DROP(p);
+ break;
+ case TH_ACTION_REJECT:
+ PACKET_REJECT(p);
+ break;
+ case TH_ACTION_PASS:
+ PACKET_PASS(p);
+ break;
+ default:
+ /* Weird, leave the default action */
+ break;
+ }
+ ret = 1;
+ }
+
+ /* Update the matching state with the timeout interval */
+ if ( (p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) {
+ lookup_tsh->current_count++;
+ if (lookup_tsh->current_count >= td->count) {
+ /* Then we must enable the new action by setting a
+ * timeout */
+ lookup_tsh->tv_timeout = p->ts.tv_sec;
+ /* Take the action to perform */
+ switch (td->new_action) {
+ case TH_ACTION_ALERT:
+ PACKET_ALERT(p);
+ break;
+ case TH_ACTION_DROP:
+ PACKET_DROP(p);
+ break;
+ case TH_ACTION_REJECT:
+ PACKET_REJECT(p);
+ break;
+ case TH_ACTION_PASS:
+ PACKET_PASS(p);
+ break;
+ default:
+ /* Weird, leave the default action */
+ break;
+ }
+ ret = 1;
+ }
+ } else {
+ lookup_tsh->tv_sec1 = p->ts.tv_sec;
+ lookup_tsh->current_count = 1;
+ }
+ } else {
+ if (td->count == 1) {
+ ret = 1;
+ }
+
+ DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, s->id, s->gid);
+ if (e != NULL) {
+ e->current_count = 1;
+ e->tv_sec1 = p->ts.tv_sec;
+ e->tv_timeout = 0;
+
+ de_ctx->ths_ctx.th_entry[s->num] = e;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Make the threshold logic for signatures
+ *
+ * \param de_ctx Dectection Context
+ * \param tsh_ptr Threshold element
+ * \param p Packet structure
+ * \param s Signature structure
+ *
+ * \retval 2 silent match (no alert but apply actions)
+ * \retval 1 alert on this event
+ * \retval 0 do not alert on this event
+ */
+int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ DetectThresholdData *td, Packet *p, Signature *s)
+{
+ SCEnter();
+
+ int ret = 0;
+ if (td == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (td->type == TYPE_SUPPRESS) {
+ ret = ThresholdHandlePacketSuppress(p,td,s->id,s->gid);
+ } else if (td->track == TRACK_SRC) {
+ Host *src = HostGetHostFromHash(&p->src);
+ if (src) {
+ ret = ThresholdHandlePacketHost(src,p,td,s->id,s->gid);
+ HostRelease(src);
+ }
+ } else if (td->track == TRACK_DST) {
+ Host *dst = HostGetHostFromHash(&p->dst);
+ if (dst) {
+ ret = ThresholdHandlePacketHost(dst,p,td,s->id,s->gid);
+ HostRelease(dst);
+ }
+ } else if (td->track == TRACK_RULE) {
+ SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock);
+ ret = ThresholdHandlePacketRule(de_ctx,p,td,s);
+ SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock);
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Init threshold context hash tables
+ *
+ * \param de_ctx Dectection Context
+ *
+ */
+void ThresholdHashInit(DetectEngineCtx *de_ctx)
+{
+ if (SCMutexInit(&de_ctx->ths_ctx.threshold_table_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Threshold: Failed to initialize hash table mutex.");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/**
+ * \brief Destroy threshold context hash tables
+ *
+ * \param de_ctx Dectection Context
+ *
+ */
+void ThresholdContextDestroy(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->ths_ctx.th_entry != NULL)
+ SCFree(de_ctx->ths_ctx.th_entry);
+ SCMutexDestroy(&de_ctx->ths_ctx.threshold_table_lock);
+}
+
+/**
+ * \brief this function will free all the entries of a list
+ * DetectTagDataEntry
+ *
+ * \param td pointer to DetectTagDataEntryList
+ */
+void ThresholdListFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectThresholdEntry *entry = ptr;
+
+ while (entry != NULL) {
+ DetectThresholdEntry *next_entry = entry->next;
+ SCFree(entry);
+ entry = next_entry;
+ }
+ }
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-engine-threshold.h b/framework/src/suricata/src/detect-engine-threshold.h
new file mode 100644
index 00000000..b874ecb2
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-threshold.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_THRESHOLD_H__
+#define __DETECT_ENGINE_THRESHOLD_H__
+
+#include "detect.h"
+#include "host.h"
+
+void ThresholdInit(void);
+
+int ThresholdHostStorageId(void);
+int ThresholdHostHasThreshold(Host *);
+
+DetectThresholdData *SigGetThresholdTypeIter(Signature *, Packet *, SigMatch **, int list);
+int PacketAlertThreshold(DetectEngineCtx *, DetectEngineThreadCtx *,
+ DetectThresholdData *, Packet *, Signature *);
+
+void ThresholdHashInit(DetectEngineCtx *);
+void ThresholdContextDestroy(DetectEngineCtx *);
+
+int ThresholdTimeoutCheck(Host *, struct timeval *);
+void ThresholdListFree(void *ptr);
+
+#endif /* __DETECT_ENGINE_THRESHOLD_H__ */
diff --git a/framework/src/suricata/src/detect-engine-uri.c b/framework/src/suricata/src/detect-engine-uri.c
new file mode 100644
index 00000000..cd737157
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-uri.c
@@ -0,0 +1,4222 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Based on detect-engine-uri.c
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+#include "detect-engine-content-inspection.h"
+
+#include "flow-util.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "flow.h"
+
+#include "stream-tcp.h"
+
+#include "app-layer-parser.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "app-layer-protos.h"
+
+/**
+ * \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param sm SigMatch to inspect
+ * \param f Flow
+ * \param flags app layer flags
+ * \param state App layer state
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ * \retval 2 Sig can't match.
+ */
+int DetectEngineInspectPacketUris(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *txv, uint64_t tx_id)
+{
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(txv);
+
+ if (tx_ud == NULL || tx_ud->request_uri_normalized == NULL) {
+ if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_REQUEST_LINE)
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ else
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+ }
+
+ det_ctx->discontinue_matching = 0;
+ det_ctx->buffer_offset = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+#if 0
+ PrintRawDataFp(stdout, (uint8_t *)bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+#endif
+
+ /* Inspect all the uricontents fetched on each
+ * transaction at the app layer */
+ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_UMATCH],
+ f,
+ bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized),
+ 0,
+ DETECT_ENGINE_CONTENT_INSPECTION_MODE_URI, NULL);
+ if (r == 1) {
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ } else {
+ return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+ }
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+/** \test Test a simple uricontent option */
+static int UriTestSig01(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig02(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /on HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/one/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted with payload2, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig03(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/blah/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the urilen option */
+static int UriTestSig04(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the urilen option */
+static int UriTestSig05(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>4; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option */
+static int UriTestSig06(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option\"; "
+ "pcre:/(oneself)+/U; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert on payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig07(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen \"; "
+ "pcre:/(one){2,}(self)?/U; urilen:3<>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig08(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen\"; "
+ "pcre:/(blabla){2,}(self)?/U; urilen:3<>20; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the pcre /U option in combination with urilen */
+static int UriTestSig09(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U option with urilen \"; "
+ "pcre:/(one){2,}(self)?/U; urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test the uricontent option in combination with urilen */
+static int UriTestSig10(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent with urilen option\"; "
+ "uricontent:\"one\"; urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test content, uricontent, urilen, pcre /U options */
+static int UriTestSig11(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test content, uricontent, pcre /U and urilen options\"; "
+ "content:\"one\"; uricontent:\"one\"; pcre:/(one){2,}(self)?/U;"
+ "urilen:<2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, urilen, pcre /U options */
+static int UriTestSig12(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /oneoneoneone HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneoneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test pcre /U, uricontent and urilen option\"; "
+ "uricontent:\"one\"; "
+ "pcre:/(one)+self/U; urilen:>2; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, urilen */
+static int UriTestSig13(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test urilen option\"; "
+ "urilen:>2; uricontent:\"one\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test uricontent, pcre /U */
+static int UriTestSig14(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; pcre:/one(self)?/U;sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test pcre /U with anchored regex (bug 155) */
+static int UriTestSig15(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /oneself HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Test uricontent option\"; "
+ "uricontent:\"one\"; pcre:/^\\/one(self)?$/U;sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didnt alert with payload2, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Test pcre /U with anchored regex (bug 155) */
+static int UriTestSig16(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /search?q=123&aq=7123abcee HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0/\r\n"
+ "Host: 1.2.3.4\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] = "POST /search?q=123&aq=7123abcee HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf2) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any any (msg:\"ET TROJAN Downadup/Conficker A or B Worm reporting\"; flow:to_server,established; uricontent:\"/search?q=\"; pcre:\"/^\\/search\\?q=[0-9]{1,3}(&aq=7(\\?[0-9a-f]{8})?)?/U\"; pcre:\"/\\x0d\\x0aHost\\: \\d+\\.\\d+\\.\\d+\\.\\d+\\x0d\\x0a/\"; sid:2009024; rev:9;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 2009024)) {
+ printf("sig 1 didnt alert with pkt, but it should: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ DetectEngineStateReset(f.de_state, STREAM_TOSERVER | STREAM_TOCLIENT);
+ p->payload = http_buf2;
+ p->payload_len = http_buf2_len;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 2009024)) {
+ printf("sig 1 alerted, but it should not (host should not match): ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig17(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /now_this_is_is_big_big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"this\"; uricontent:\"is\"; within:6; "
+ "uricontent:\"big\"; within:8; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig18(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /now_this_is_is_is_big_big_big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"this\"; uricontent:\"is\"; within:9; "
+ "uricontent:\"big\"; within:12; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents
+ */
+static int UriTestSig19(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_this_now_is_is_____big_string_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"now\"; uricontent:\"this\"; "
+ "uricontent:\"is\"; within:12; "
+ "uricontent:\"big\"; within:8; "
+ "uricontent:\"string\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with offset
+ */
+static int UriTestSig20(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /_________thus_thus_is_a_big HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"thus\"; offset:8; "
+ "uricontent:\"is\"; within:6; "
+ "uricontent:\"big\"; within:8; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig21(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix\"; uricontent:\"this\"; within:6; "
+ "uricontent:!\"and\"; distance:0; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test relative pcre.
+ */
+static int UriTestSig22(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_is_a_super_duper_"
+ "nova_in_super_nova_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "pcre:/super/U; uricontent:\"nova\"; within:7; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig23(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:!\"fix_this_now\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig24(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"we_need_to\"; uricontent:!\"fix_this_now\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test normalized uricontents.
+ */
+static int UriTestSig25(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "pcre:/normalized/U; uricontent:\"normalized uri\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig26(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix_this\"; isdataat:4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test Test multiple relative contents with a negated content.
+ */
+static int UriTestSig27(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /we_need_to_fix_this_and_yes_fix_this_now HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "uricontent:\"fix_this\"; isdataat:!10,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int UriTestSig28(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"ring\"; distance:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig29(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"ring\"; distance:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig30(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"_b5ig\"; offset:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig31(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"his\"; depth:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig32(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /this_b5ig_string_now_in_http HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"dummy\"; "
+ "uricontent:\"this\"; "
+ "byte_extract:1,2,one,string,dec,relative; "
+ "uricontent:\"g_st\"; within:one; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig33(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:15; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig34(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:15, norm; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig35(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:16; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig36(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:16, norm; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig37(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:17, raw; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int UriTestSig38(void)
+{
+ int result = 0;
+ uint8_t *http_buf = (uint8_t *)"POST /normalized%20uri "
+ "HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n";
+ uint32_t http_buf_len = strlen((char *)http_buf);
+ Flow f;
+ TcpSession ssn;
+ HtpState *http_state = NULL;
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(http_buf, http_buf_len, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test multiple relative uricontents\"; "
+ "urilen:18, raw; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted, but it shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void UriRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("UriTestSig01", UriTestSig01, 1);
+ UtRegisterTest("UriTestSig02", UriTestSig02, 1);
+ UtRegisterTest("UriTestSig03", UriTestSig03, 1);
+ UtRegisterTest("UriTestSig04", UriTestSig04, 1);
+ UtRegisterTest("UriTestSig05", UriTestSig05, 1);
+ UtRegisterTest("UriTestSig06", UriTestSig06, 1);
+ UtRegisterTest("UriTestSig07", UriTestSig07, 1);
+ UtRegisterTest("UriTestSig08", UriTestSig08, 1);
+ UtRegisterTest("UriTestSig09", UriTestSig09, 1);
+ UtRegisterTest("UriTestSig10", UriTestSig10, 1);
+ UtRegisterTest("UriTestSig11", UriTestSig11, 1);
+ UtRegisterTest("UriTestSig12", UriTestSig12, 1);
+ UtRegisterTest("UriTestSig13", UriTestSig13, 1);
+ UtRegisterTest("UriTestSig14", UriTestSig14, 1);
+ UtRegisterTest("UriTestSig15", UriTestSig15, 1);
+ UtRegisterTest("UriTestSig16", UriTestSig16, 1);
+ UtRegisterTest("UriTestSig17", UriTestSig17, 1);
+ UtRegisterTest("UriTestSig18", UriTestSig18, 1);
+ UtRegisterTest("UriTestSig19", UriTestSig19, 1);
+ UtRegisterTest("UriTestSig20", UriTestSig20, 1);
+ UtRegisterTest("UriTestSig21", UriTestSig21, 1);
+ UtRegisterTest("UriTestSig22", UriTestSig22, 1);
+ UtRegisterTest("UriTestSig23", UriTestSig23, 1);
+ UtRegisterTest("UriTestSig24", UriTestSig24, 1);
+ UtRegisterTest("UriTestSig25", UriTestSig25, 1);
+ UtRegisterTest("UriTestSig26", UriTestSig26, 1);
+ UtRegisterTest("UriTestSig27", UriTestSig27, 1);
+
+ UtRegisterTest("UriTestSig28", UriTestSig28, 1);
+ UtRegisterTest("UriTestSig29", UriTestSig29, 1);
+ UtRegisterTest("UriTestSig30", UriTestSig30, 1);
+ UtRegisterTest("UriTestSig31", UriTestSig31, 1);
+ UtRegisterTest("UriTestSig32", UriTestSig32, 1);
+ UtRegisterTest("UriTestSig33", UriTestSig33, 1);
+ UtRegisterTest("UriTestSig34", UriTestSig34, 1);
+ UtRegisterTest("UriTestSig35", UriTestSig35, 1);
+ UtRegisterTest("UriTestSig36", UriTestSig36, 1);
+ UtRegisterTest("UriTestSig37", UriTestSig37, 1);
+ UtRegisterTest("UriTestSig38", UriTestSig38, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine-uri.h b/framework/src/suricata/src/detect-engine-uri.h
new file mode 100644
index 00000000..92b22cd3
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-uri.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_URICONTENT_H__
+#define __DETECT_ENGINE_URICONTENT_H__
+
+int DetectEngineInspectPacketUris(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Flow *f, uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id);
+void UriRegisterTests(void);
+
+#endif /* __DETECT_ENGINE_URICONTENT_H__ */
+
diff --git a/framework/src/suricata/src/detect-engine.c b/framework/src/suricata/src/detect-engine.c
new file mode 100644
index 00000000..7de04969
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine.c
@@ -0,0 +1,3225 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "flow-private.h"
+#include "flow-util.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "app-layer-htp.h"
+
+#include "detect-parse.h"
+#include "detect-engine-sigorder.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-hcbd.h"
+#include "detect-engine-iponly.h"
+#include "detect-engine-tag.h"
+
+#include "detect-engine-uri.h"
+#include "detect-engine-hcbd.h"
+#include "detect-engine-hsbd.h"
+#include "detect-engine-hhd.h"
+#include "detect-engine-hrhd.h"
+#include "detect-engine-hmd.h"
+#include "detect-engine-hcd.h"
+#include "detect-engine-hrud.h"
+#include "detect-engine-hrl.h"
+#include "detect-engine-hsmd.h"
+#include "detect-engine-hscd.h"
+#include "detect-engine-hua.h"
+#include "detect-engine-hhhd.h"
+#include "detect-engine-hrhhd.h"
+#include "detect-engine-file.h"
+#include "detect-engine-dns.h"
+#include "detect-engine-modbus.h"
+#include "detect-engine-filedata-smtp.h"
+
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+
+#include "detect-byte-extract.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-engine-threshold.h"
+
+#include "detect-engine-loader.h"
+
+#include "util-classification-config.h"
+#include "util-reference-config.h"
+#include "util-threshold-config.h"
+#include "util-error.h"
+#include "util-hash.h"
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-action.h"
+#include "util-magic.h"
+#include "util-signal.h"
+
+#include "util-var-name.h"
+
+#include "tm-threads.h"
+#include "runmodes.h"
+
+#ifdef PROFILING
+#include "util-profiling.h"
+#endif
+
+#include "reputation.h"
+
+#define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000
+
+static uint32_t detect_engine_ctx_id = 1;
+
+static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
+ ThreadVars *tv, DetectEngineCtx *new_de_ctx);
+
+static uint8_t DetectEngineCtxLoadConf(DetectEngineCtx *);
+
+static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER, 0, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL,};
+
+static DetectEngineThreadCtx *DetectEngineThreadCtxInitForMT(ThreadVars *tv);
+
+/* 2 - for each direction */
+DetectEngineAppInspectionEngine *app_inspection_engine[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2];
+
+#if 0
+
+static void DetectEnginePrintAppInspectionEngines(DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2])
+{
+ printf("\n");
+
+ AppProto alproto = ALPROTO_UNKNOWN + 1;
+ for ( ; alproto < ALPROTO_MAX; alproto++) {
+ printf("alproto - %d\n", alproto);
+ int dir = 0;
+ for ( ; dir < 2; dir++) {
+ printf(" direction - %d\n", dir);
+ DetectEngineAppInspectionEngine *engine = list[alproto][dir];
+ while (engine != NULL) {
+ printf(" engine->alproto - %"PRIu16"\n", engine->alproto);
+ printf(" engine->dir - %"PRIu16"\n", engine->dir);
+ printf(" engine->sm_list - %d\n", engine->sm_list);
+ printf(" engine->inspect_flags - %"PRIu32"\n", engine->inspect_flags);
+ printf(" engine->match_flags - %"PRIu32"\n", engine->match_flags);
+ printf("\n");
+
+ engine = engine->next;
+ }
+ } /* for ( ; dir < 2; dir++) */
+ } /* for ( ; alproto < ALPROTO_MAX; alproto++) */
+
+ return;
+}
+
+#endif
+
+void DetectEngineRegisterAppInspectionEngines(void)
+{
+ struct tmp_t {
+ uint8_t ipproto;
+ AppProto alproto;
+ int32_t sm_list;
+ uint32_t inspect_flags;
+ uint16_t dir;
+ int (*Callback)(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig, Flow *f,
+ uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id);
+
+ };
+
+ struct tmp_t data_toserver[] = {
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_UMATCH,
+ DE_STATE_FLAG_URI_INSPECT,
+ 0,
+ DetectEngineInspectPacketUris },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HRLMATCH,
+ DE_STATE_FLAG_HRL_INSPECT,
+ 0,
+ DetectEngineInspectHttpRequestLine },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HCBDMATCH,
+ DE_STATE_FLAG_HCBD_INSPECT,
+ 0,
+ DetectEngineInspectHttpClientBody },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HHDMATCH,
+ DE_STATE_FLAG_HHD_INSPECT,
+ 0,
+ DetectEngineInspectHttpHeader },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HRHDMATCH,
+ DE_STATE_FLAG_HRHD_INSPECT,
+ 0,
+ DetectEngineInspectHttpRawHeader },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HMDMATCH,
+ DE_STATE_FLAG_HMD_INSPECT,
+ 0,
+ DetectEngineInspectHttpMethod },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HCDMATCH,
+ DE_STATE_FLAG_HCD_INSPECT,
+ 0,
+ DetectEngineInspectHttpCookie },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HRUDMATCH,
+ DE_STATE_FLAG_HRUD_INSPECT,
+ 0,
+ DetectEngineInspectHttpRawUri },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_FILEMATCH,
+ DE_STATE_FLAG_FILE_TS_INSPECT,
+ 0,
+ DetectFileInspectHttp },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HUADMATCH,
+ DE_STATE_FLAG_HUAD_INSPECT,
+ 0,
+ DetectEngineInspectHttpUA },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HHHDMATCH,
+ DE_STATE_FLAG_HHHD_INSPECT,
+ 0,
+ DetectEngineInspectHttpHH },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HRHHDMATCH,
+ DE_STATE_FLAG_HRHHD_INSPECT,
+ 0,
+ DetectEngineInspectHttpHRH },
+ /* DNS */
+ { IPPROTO_TCP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSQUERYNAME_MATCH,
+ DE_STATE_FLAG_DNSQUERYNAME_INSPECT,
+ 0,
+ DetectEngineInspectDnsQueryName },
+ /* specifically for UDP, register again
+ * allows us to use the alproto w/o translation
+ * in the detection engine */
+ { IPPROTO_UDP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSQUERYNAME_MATCH,
+ DE_STATE_FLAG_DNSQUERYNAME_INSPECT,
+ 0,
+ DetectEngineInspectDnsQueryName },
+ { IPPROTO_TCP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSREQUEST_MATCH,
+ DE_STATE_FLAG_DNSREQUEST_INSPECT,
+ 0,
+ DetectEngineInspectDnsRequest },
+ /* specifically for UDP, register again
+ * allows us to use the alproto w/o translation
+ * in the detection engine */
+ { IPPROTO_UDP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSREQUEST_MATCH,
+ DE_STATE_FLAG_DNSREQUEST_INSPECT,
+ 0,
+ DetectEngineInspectDnsRequest },
+ /* SMTP */
+ { IPPROTO_TCP,
+ ALPROTO_SMTP,
+ DETECT_SM_LIST_FILEMATCH,
+ DE_STATE_FLAG_FILE_TS_INSPECT,
+ 0,
+ DetectFileInspectSmtp },
+ /* Modbus */
+ { IPPROTO_TCP,
+ ALPROTO_MODBUS,
+ DETECT_SM_LIST_MODBUS_MATCH,
+ DE_STATE_FLAG_MODBUS_INSPECT,
+ 0,
+ DetectEngineInspectModbus },
+ /* file_data smtp */
+ { IPPROTO_TCP,
+ ALPROTO_SMTP,
+ DETECT_SM_LIST_FILEDATA,
+ DE_STATE_FLAG_FD_SMTP_INSPECT,
+ 0,
+ DetectEngineInspectSMTPFiledata },
+ };
+
+ struct tmp_t data_toclient[] = {
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_FILEDATA,
+ DE_STATE_FLAG_HSBD_INSPECT,
+ 1,
+ DetectEngineInspectHttpServerBody },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HHDMATCH,
+ DE_STATE_FLAG_HHD_INSPECT,
+ 1,
+ DetectEngineInspectHttpHeader },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HRHDMATCH,
+ DE_STATE_FLAG_HRHD_INSPECT,
+ 1,
+ DetectEngineInspectHttpRawHeader },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HCDMATCH,
+ DE_STATE_FLAG_HCD_INSPECT,
+ 1,
+ DetectEngineInspectHttpCookie },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_FILEMATCH,
+ DE_STATE_FLAG_FILE_TC_INSPECT,
+ 1,
+ DetectFileInspectHttp },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HSMDMATCH,
+ DE_STATE_FLAG_HSMD_INSPECT,
+ 1,
+ DetectEngineInspectHttpStatMsg },
+ { IPPROTO_TCP,
+ ALPROTO_HTTP,
+ DETECT_SM_LIST_HSCDMATCH,
+ DE_STATE_FLAG_HSCD_INSPECT,
+ 1,
+ DetectEngineInspectHttpStatCode },
+ /* Modbus */
+ { IPPROTO_TCP,
+ ALPROTO_MODBUS,
+ DETECT_SM_LIST_MODBUS_MATCH,
+ DE_STATE_FLAG_MODBUS_INSPECT,
+ 0,
+ DetectEngineInspectModbus },
+ { IPPROTO_TCP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSRESPONSE_MATCH,
+ DE_STATE_FLAG_DNSRESPONSE_INSPECT,
+ 1,
+ DetectEngineInspectDnsResponse },
+ /* specifically for UDP, register again
+ * allows us to use the alproto w/o translation
+ * in the detection engine */
+ { IPPROTO_UDP,
+ ALPROTO_DNS,
+ DETECT_SM_LIST_DNSRESPONSE_MATCH,
+ DE_STATE_FLAG_DNSRESPONSE_INSPECT,
+ 1,
+ DetectEngineInspectDnsResponse },
+ };
+
+ size_t i;
+ for (i = 0 ; i < sizeof(data_toserver) / sizeof(struct tmp_t); i++) {
+ DetectEngineRegisterAppInspectionEngine(data_toserver[i].ipproto,
+ data_toserver[i].alproto,
+ data_toserver[i].dir,
+ data_toserver[i].sm_list,
+ data_toserver[i].inspect_flags,
+ data_toserver[i].Callback,
+ app_inspection_engine);
+ }
+
+ for (i = 0 ; i < sizeof(data_toclient) / sizeof(struct tmp_t); i++) {
+ DetectEngineRegisterAppInspectionEngine(data_toclient[i].ipproto,
+ data_toclient[i].alproto,
+ data_toclient[i].dir,
+ data_toclient[i].sm_list,
+ data_toclient[i].inspect_flags,
+ data_toclient[i].Callback,
+ app_inspection_engine);
+ }
+
+#if 0
+ DetectEnginePrintAppInspectionEngines(app_inspection_engine);
+#endif
+
+ return;
+}
+
+static void AppendAppInspectionEngine(DetectEngineAppInspectionEngine *engine,
+ DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2])
+{
+ /* append to the list */
+ DetectEngineAppInspectionEngine *tmp = list[FlowGetProtoMapping(engine->ipproto)][engine->alproto][engine->dir];
+ DetectEngineAppInspectionEngine *insert = NULL;
+ while (tmp != NULL) {
+ if (tmp->dir == engine->dir &&
+ (tmp->sm_list == engine->sm_list ||
+ tmp->inspect_flags == engine->inspect_flags
+ )) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "App Inspection Engine already "
+ "registered for this direction(%"PRIu16") ||"
+ "sm_list(%d) || "
+ "[inspect(%"PRIu32")]_flags",
+ tmp->dir, tmp->sm_list, tmp->inspect_flags);
+ exit(EXIT_FAILURE);
+ }
+ insert = tmp;
+ tmp = tmp->next;
+ }
+ if (insert == NULL)
+ list[FlowGetProtoMapping(engine->ipproto)][engine->alproto][engine->dir] = engine;
+ else
+ insert->next = engine;
+
+ return;
+}
+
+void DetectEngineRegisterAppInspectionEngine(uint8_t ipproto,
+ AppProto alproto,
+ uint16_t dir,
+ int32_t sm_list,
+ uint32_t inspect_flags,
+ int (*Callback)(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig, Flow *f,
+ uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id),
+ DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2])
+{
+ if ((list == NULL) ||
+ (alproto <= ALPROTO_UNKNOWN || alproto >= ALPROTO_FAILED) ||
+ (dir > 1) ||
+ (sm_list < DETECT_SM_LIST_MATCH || sm_list >= DETECT_SM_LIST_MAX) ||
+ (Callback == NULL))
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
+ exit(EXIT_FAILURE);
+ }
+
+ DetectEngineAppInspectionEngine *tmp = list[FlowGetProtoMapping(ipproto)][alproto][dir];
+ while (tmp != NULL) {
+ if (tmp->sm_list == sm_list && tmp->Callback == Callback) {
+ return;
+ }
+ tmp = tmp->next;
+ }
+
+ DetectEngineAppInspectionEngine *new_engine = SCMalloc(sizeof(DetectEngineAppInspectionEngine));
+ if (unlikely(new_engine == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(new_engine, 0, sizeof(*new_engine));
+ new_engine->ipproto = ipproto;
+ new_engine->alproto = alproto;
+ new_engine->dir = dir;
+ new_engine->sm_list = sm_list;
+ new_engine->inspect_flags = inspect_flags;
+ new_engine->Callback = Callback;
+
+ AppendAppInspectionEngine(new_engine, list);
+
+ return;
+}
+
+/* code to control the main thread to do a reload */
+
+enum DetectEngineSyncState {
+ IDLE, /**< ready to start a reload */
+ RELOAD, /**< command main thread to do the reload */
+ DONE, /**< main thread telling us reload is done */
+};
+
+
+typedef struct DetectEngineSyncer_ {
+ SCMutex m;
+ enum DetectEngineSyncState state;
+} DetectEngineSyncer;
+
+static DetectEngineSyncer detect_sync = { SCMUTEX_INITIALIZER, IDLE };
+
+/* tell main to start reloading */
+int DetectEngineReloadStart(void)
+{
+ int r = 0;
+ SCMutexLock(&detect_sync.m);
+ if (detect_sync.state == IDLE) {
+ detect_sync.state = RELOAD;
+ } else {
+ r = -1;
+ }
+ SCMutexUnlock(&detect_sync.m);
+ return r;
+}
+
+/* main thread checks this to see if it should start */
+int DetectEngineReloadIsStart(void)
+{
+ int r = 0;
+ SCMutexLock(&detect_sync.m);
+ if (detect_sync.state == RELOAD) {
+ r = 1;
+ }
+ SCMutexUnlock(&detect_sync.m);
+ return r;
+}
+
+/* main thread sets done when it's done */
+void DetectEngineReloadSetDone(void)
+{
+ SCMutexLock(&detect_sync.m);
+ detect_sync.state = DONE;
+ SCMutexUnlock(&detect_sync.m);
+}
+
+/* caller loops this until it returns 1 */
+int DetectEngineReloadIsDone(void)
+{
+ int r = 0;
+ SCMutexLock(&detect_sync.m);
+ if (detect_sync.state == DONE) {
+ r = 1;
+ detect_sync.state = IDLE;
+ }
+ SCMutexUnlock(&detect_sync.m);
+ return r;
+}
+
+/** \internal
+ * \brief Update detect threads with new detect engine
+ *
+ * Atomically update each detect thread with a new thread context
+ * that is associated to the new detection engine(s).
+ *
+ * If called in unix socket mode, it's possible that we don't have
+ * detect threads yet.
+ *
+ * \retval -1 error
+ * \retval 0 no detection threads
+ * \retval 1 successful reload
+ */
+static int DetectEngineReloadThreads(DetectEngineCtx *new_de_ctx)
+{
+ SCEnter();
+
+ int i = 0;
+ int no_of_detect_tvs = 0;
+ ThreadVars *tv = NULL;
+
+ /* count detect threads in use */
+ SCMutexLock(&tv_root_lock);
+ tv = tv_root[TVT_PPT];
+ while (tv) {
+ /* obtain the slots for this TV */
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+
+ if (suricata_ctl_flags != 0) {
+ SCLogInfo("rule reload interupted by engine shutdown");
+ SCMutexUnlock(&tv_root_lock);
+ return -1;
+ }
+
+ if (!(tm->flags & TM_FLAG_DETECT_TM)) {
+ slots = slots->slot_next;
+ continue;
+ }
+ no_of_detect_tvs++;
+ break;
+ }
+
+ tv = tv->next;
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ /* can be zero in unix socket mode */
+ if (no_of_detect_tvs == 0) {
+ return 0;
+ }
+
+ SCLogNotice("rule reload starting");
+
+ /* prepare swap structures */
+ DetectEngineThreadCtx *old_det_ctx[no_of_detect_tvs];
+ DetectEngineThreadCtx *new_det_ctx[no_of_detect_tvs];
+ ThreadVars *detect_tvs[no_of_detect_tvs];
+ memset(old_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
+ memset(new_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
+ memset(detect_tvs, 0x00, (no_of_detect_tvs * sizeof(ThreadVars *)));
+
+ /* start the process of swapping detect threads ctxs */
+
+ /* get reference to tv's and setup new_det_ctx array */
+ SCMutexLock(&tv_root_lock);
+ tv = tv_root[TVT_PPT];
+ while (tv) {
+ /* obtain the slots for this TV */
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+
+ if (suricata_ctl_flags != 0) {
+ SCMutexUnlock(&tv_root_lock);
+ goto error;
+ }
+
+ if (!(tm->flags & TM_FLAG_DETECT_TM)) {
+ slots = slots->slot_next;
+ continue;
+ }
+
+ old_det_ctx[i] = SC_ATOMIC_GET(slots->slot_data);
+ detect_tvs[i] = tv;
+ if (new_de_ctx != NULL)
+ new_det_ctx[i] = DetectEngineThreadCtxInitForReload(tv, new_de_ctx);
+ else
+ new_det_ctx[i] = DetectEngineThreadCtxInitForMT(tv);
+ if (new_det_ctx[i] == NULL) {
+ SCLogError(SC_ERR_LIVE_RULE_SWAP, "Detect engine thread init "
+ "failure in live rule swap. Let's get out of here");
+ SCMutexUnlock(&tv_root_lock);
+ goto error;
+ }
+ SCLogDebug("live rule swap created new det_ctx - %p and de_ctx "
+ "- %p\n", new_det_ctx[i], new_de_ctx);
+ i++;
+ break;
+ }
+
+ tv = tv->next;
+ }
+ BUG_ON(i != no_of_detect_tvs);
+
+ /* atomicly replace the det_ctx data */
+ i = 0;
+ tv = tv_root[TVT_PPT];
+ while (tv) {
+ /* find the correct slot */
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ if (suricata_ctl_flags != 0) {
+ return -1;
+ }
+
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+ if (!(tm->flags & TM_FLAG_DETECT_TM)) {
+ slots = slots->slot_next;
+ continue;
+ }
+ SCLogDebug("swapping new det_ctx - %p with older one - %p",
+ new_det_ctx[i], SC_ATOMIC_GET(slots->slot_data));
+ (void)SC_ATOMIC_SET(slots->slot_data, new_det_ctx[i++]);
+ break;
+ }
+ tv = tv->next;
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ /* threads now all have new data, however they may not have started using
+ * it and may still use the old data */
+
+ SCLogInfo("Live rule swap has swapped %d old det_ctx's with new ones, "
+ "along with the new de_ctx", no_of_detect_tvs);
+
+ /* inject a fake packet if the detect thread isn't using the new ctx yet,
+ * this speeds up the process */
+ for (i = 0; i < no_of_detect_tvs; i++) {
+ int break_out = 0;
+ int pseudo_pkt_inserted = 0;
+ usleep(1000);
+ while (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
+ if (suricata_ctl_flags != 0) {
+ break_out = 1;
+ break;
+ }
+
+ if (pseudo_pkt_inserted == 0) {
+ pseudo_pkt_inserted = 1;
+ if (detect_tvs[i]->inq != NULL) {
+ Packet *p = PacketGetFromAlloc();
+ if (p != NULL) {
+ p->flags |= PKT_PSEUDO_STREAM_END;
+ PacketQueue *q = &trans_q[detect_tvs[i]->inq->id];
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+ }
+ }
+ }
+ usleep(1000);
+ }
+ if (break_out)
+ break;
+ SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx[i]);
+ }
+
+ /* this is to make sure that if someone initiated shutdown during a live
+ * rule swap, the live rule swap won't clean up the old det_ctx and
+ * de_ctx, till all detect threads have stopped working and sitting
+ * silently after setting RUNNING_DONE flag and while waiting for
+ * THV_DEINIT flag */
+ if (i != no_of_detect_tvs) { // not all threads we swapped
+ ThreadVars *tv = tv_root[TVT_PPT];
+ while (tv) {
+ /* obtain the slots for this TV */
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+ if (!(tm->flags & TM_FLAG_DETECT_TM)) {
+ slots = slots->slot_next;
+ continue;
+ }
+
+ while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
+ usleep(100);
+ }
+
+ slots = slots->slot_next;
+ }
+
+ tv = tv->next;
+ }
+ }
+
+ /* free all the ctxs */
+ for (i = 0; i < no_of_detect_tvs; i++) {
+ SCLogDebug("Freeing old_det_ctx - %p used by detect",
+ old_det_ctx[i]);
+ DetectEngineThreadCtxDeinit(NULL, old_det_ctx[i]);
+ }
+
+ SRepReloadComplete();
+
+ SCLogNotice("rule reload complete");
+ return 1;
+
+ error:
+ for (i = 0; i < no_of_detect_tvs; i++) {
+ if (new_det_ctx[i] != NULL)
+ DetectEngineThreadCtxDeinit(NULL, new_det_ctx[i]);
+ }
+ return -1;
+}
+
+static DetectEngineCtx *DetectEngineCtxInitReal(int minimal, const char *prefix)
+{
+ DetectEngineCtx *de_ctx;
+
+ ConfNode *seq_node = NULL;
+ ConfNode *insp_recursion_limit_node = NULL;
+ ConfNode *de_engine_node = NULL;
+ char *insp_recursion_limit = NULL;
+
+ de_ctx = SCMalloc(sizeof(DetectEngineCtx));
+ if (unlikely(de_ctx == NULL))
+ goto error;
+
+ memset(de_ctx,0,sizeof(DetectEngineCtx));
+
+ if (minimal) {
+ de_ctx->minimal = 1;
+ de_ctx->id = detect_engine_ctx_id++;
+ return de_ctx;
+ }
+
+ if (prefix != NULL) {
+ strlcpy(de_ctx->config_prefix, prefix, sizeof(de_ctx->config_prefix));
+ }
+
+ if (ConfGetBool("engine.init-failure-fatal", (int *)&(de_ctx->failure_fatal)) != 1) {
+ SCLogDebug("ConfGetBool could not load the value.");
+ }
+
+ de_engine_node = ConfGetNode("detect-engine");
+ if (de_engine_node != NULL) {
+ TAILQ_FOREACH(seq_node, &de_engine_node->head, next) {
+ if (strcmp(seq_node->val, "inspection-recursion-limit") != 0)
+ continue;
+
+ insp_recursion_limit_node = ConfNodeLookupChild(seq_node, seq_node->val);
+ if (insp_recursion_limit_node == NULL) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Error retrieving conf "
+ "entry for detect-engine:inspection-recursion-limit");
+ break;
+ }
+ insp_recursion_limit = insp_recursion_limit_node->val;
+ SCLogDebug("Found detect-engine:inspection-recursion-limit - %s:%s",
+ insp_recursion_limit_node->name, insp_recursion_limit_node->val);
+
+ break;
+ }
+ }
+
+ if (insp_recursion_limit != NULL) {
+ de_ctx->inspection_recursion_limit = atoi(insp_recursion_limit);
+ } else {
+ de_ctx->inspection_recursion_limit =
+ DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
+ }
+
+ if (de_ctx->inspection_recursion_limit == 0)
+ de_ctx->inspection_recursion_limit = -1;
+
+ SCLogDebug("de_ctx->inspection_recursion_limit: %d",
+ de_ctx->inspection_recursion_limit);
+
+ de_ctx->mpm_matcher = PatternMatchDefaultMatcher();
+ DetectEngineCtxLoadConf(de_ctx);
+
+ SigGroupHeadHashInit(de_ctx);
+ SigGroupHeadMpmHashInit(de_ctx);
+ SigGroupHeadMpmUriHashInit(de_ctx);
+ SigGroupHeadSPortHashInit(de_ctx);
+ SigGroupHeadDPortHashInit(de_ctx);
+ DetectPortSpHashInit(de_ctx);
+ DetectPortDpHashInit(de_ctx);
+ ThresholdHashInit(de_ctx);
+ VariableNameInitHash(de_ctx);
+ DetectParseDupSigHashInit(de_ctx);
+
+ de_ctx->mpm_pattern_id_store = MpmPatternIdTableInitHash();
+ if (de_ctx->mpm_pattern_id_store == NULL) {
+ goto error;
+ }
+
+ /* init iprep... ignore errors for now */
+ (void)SRepInit(de_ctx);
+
+#ifdef PROFILING
+ SCProfilingKeywordInitCounters(de_ctx);
+#endif
+
+ SCClassConfLoadClassficationConfigFile(de_ctx, NULL);
+ SCRConfLoadReferenceConfigFile(de_ctx, NULL);
+
+ if (ActionInitConfig() < 0) {
+ goto error;
+ }
+
+ de_ctx->id = detect_engine_ctx_id++;
+ return de_ctx;
+error:
+ return NULL;
+
+}
+
+DetectEngineCtx *DetectEngineCtxInitMinimal(void)
+{
+ return DetectEngineCtxInitReal(1, NULL);
+}
+
+DetectEngineCtx *DetectEngineCtxInit(void)
+{
+ return DetectEngineCtxInitReal(0, NULL);
+}
+
+DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix)
+{
+ if (prefix == NULL || strlen(prefix) == 0)
+ return DetectEngineCtxInit();
+ else
+ return DetectEngineCtxInitReal(0, prefix);
+}
+
+static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx *de_ctx)
+{
+ DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
+ while (item) {
+ DetectEngineThreadKeywordCtxItem *next = item->next;
+ SCFree(item);
+ item = next;
+ }
+ de_ctx->keyword_list = NULL;
+}
+
+/**
+ * \brief Free a DetectEngineCtx::
+ *
+ * \param de_ctx DetectEngineCtx:: to be freed
+ */
+void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
+{
+
+ if (de_ctx == NULL)
+ return;
+
+#ifdef PROFILING
+ if (de_ctx->profile_ctx != NULL) {
+ SCProfilingRuleDestroyCtx(de_ctx->profile_ctx);
+ de_ctx->profile_ctx = NULL;
+ }
+ if (de_ctx->profile_keyword_ctx != NULL) {
+ SCProfilingKeywordDestroyCtx(de_ctx);//->profile_keyword_ctx);
+// de_ctx->profile_keyword_ctx = NULL;
+ }
+#endif
+
+ /* Normally the hashes are freed elsewhere, but
+ * to be sure look at them again here.
+ */
+ MpmPatternIdTableFreeHash(de_ctx->mpm_pattern_id_store); /* normally cleaned up in SigGroupBuild */
+
+ SigGroupHeadHashFree(de_ctx);
+ SigGroupHeadMpmHashFree(de_ctx);
+ SigGroupHeadMpmUriHashFree(de_ctx);
+ SigGroupHeadSPortHashFree(de_ctx);
+ SigGroupHeadDPortHashFree(de_ctx);
+ DetectParseDupSigHashFree(de_ctx);
+ SCSigSignatureOrderingModuleCleanup(de_ctx);
+ DetectPortSpHashFree(de_ctx);
+ DetectPortDpHashFree(de_ctx);
+ ThresholdContextDestroy(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ VariableNameFreeHash(de_ctx);
+ if (de_ctx->sig_array)
+ SCFree(de_ctx->sig_array);
+
+ SCClassConfDeInitContext(de_ctx);
+ SCRConfDeInitContext(de_ctx);
+
+ SigGroupCleanup(de_ctx);
+
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx);
+ }
+
+ DetectEngineCtxFreeThreadKeywordData(de_ctx);
+ SRepDestroy(de_ctx);
+
+ /* if we have a config prefix, remove the config from the tree */
+ if (strlen(de_ctx->config_prefix) > 0) {
+ /* remove config */
+ ConfNode *node = ConfGetNode(de_ctx->config_prefix);
+ if (node != NULL) {
+ ConfNodeRemove(node); /* frees node */
+ }
+#if 0
+ ConfDump();
+#endif
+ }
+
+ SCFree(de_ctx);
+ //DetectAddressGroupPrintMemory();
+ //DetectSigGroupPrintMemory();
+ //DetectPortPrintMemory();
+}
+
+/** \brief Function that load DetectEngineCtx config for grouping sigs
+ * used by the engine
+ * \retval 0 if no config provided, 1 if config was provided
+ * and loaded successfuly
+ */
+static uint8_t DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx)
+{
+ uint8_t profile = ENGINE_PROFILE_UNKNOWN;
+ char *de_ctx_profile = NULL;
+
+ const char *max_uniq_toclient_src_groups_str = NULL;
+ const char *max_uniq_toclient_dst_groups_str = NULL;
+ const char *max_uniq_toclient_sp_groups_str = NULL;
+ const char *max_uniq_toclient_dp_groups_str = NULL;
+
+ const char *max_uniq_toserver_src_groups_str = NULL;
+ const char *max_uniq_toserver_dst_groups_str = NULL;
+ const char *max_uniq_toserver_sp_groups_str = NULL;
+ const char *max_uniq_toserver_dp_groups_str = NULL;
+
+ char *sgh_mpm_context = NULL;
+
+ ConfNode *de_ctx_custom = ConfGetNode("detect-engine");
+ ConfNode *opt = NULL;
+
+ if (de_ctx_custom != NULL) {
+ TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
+ if (strcmp(opt->val, "profile") == 0) {
+ de_ctx_profile = opt->head.tqh_first->val;
+ } else if (strcmp(opt->val, "sgh-mpm-context") == 0) {
+ sgh_mpm_context = opt->head.tqh_first->val;
+ }
+ }
+ }
+
+ if (de_ctx_profile != NULL) {
+ if (strcmp(de_ctx_profile, "low") == 0) {
+ profile = ENGINE_PROFILE_LOW;
+ } else if (strcmp(de_ctx_profile, "medium") == 0) {
+ profile = ENGINE_PROFILE_MEDIUM;
+ } else if (strcmp(de_ctx_profile, "high") == 0) {
+ profile = ENGINE_PROFILE_HIGH;
+ } else if (strcmp(de_ctx_profile, "custom") == 0) {
+ profile = ENGINE_PROFILE_CUSTOM;
+ }
+
+ SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile);
+ } else {
+ SCLogDebug("Profile for detection engine groups not provided "
+ "at suricata.yaml. Using default (\"medium\").");
+ }
+
+ /* detect-engine.sgh-mpm-context option parsing */
+ if (sgh_mpm_context == NULL || strcmp(sgh_mpm_context, "auto") == 0) {
+ /* for now, since we still haven't implemented any intelligence into
+ * understanding the patterns and distributing mpm_ctx across sgh */
+ if (de_ctx->mpm_matcher == DEFAULT_MPM || de_ctx->mpm_matcher == MPM_AC_GFBS ||
+#ifdef __SC_CUDA_SUPPORT__
+ de_ctx->mpm_matcher == MPM_AC_BS || de_ctx->mpm_matcher == MPM_AC_CUDA) {
+#else
+ de_ctx->mpm_matcher == MPM_AC_BS) {
+#endif
+ de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
+ } else {
+ de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
+ }
+ } else {
+ if (strcmp(sgh_mpm_context, "single") == 0) {
+ de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
+ } else if (strcmp(sgh_mpm_context, "full") == 0) {
+#ifdef __SC_CUDA_SUPPORT__
+ if (de_ctx->mpm_matcher == MPM_AC_CUDA) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You can't use "
+ "the cuda version of our mpm ac, i.e. \"ac-cuda\" "
+ "along with \"full\" \"sgh-mpm-context\". "
+ "Allowed values are \"single\" and \"auto\".");
+ exit(EXIT_FAILURE);
+ }
+#endif
+ de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You have supplied an "
+ "invalid conf value for detect-engine.sgh-mpm-context-"
+ "%s", sgh_mpm_context);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (run_mode == RUNMODE_UNITTEST) {
+ de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
+ }
+
+ opt = NULL;
+ switch (profile) {
+ case ENGINE_PROFILE_LOW:
+ de_ctx->max_uniq_toclient_src_groups = 2;
+ de_ctx->max_uniq_toclient_dst_groups = 2;
+ de_ctx->max_uniq_toclient_sp_groups = 2;
+ de_ctx->max_uniq_toclient_dp_groups = 3;
+ de_ctx->max_uniq_toserver_src_groups = 2;
+ de_ctx->max_uniq_toserver_dst_groups = 2;
+ de_ctx->max_uniq_toserver_sp_groups = 2;
+ de_ctx->max_uniq_toserver_dp_groups = 3;
+ break;
+
+ case ENGINE_PROFILE_HIGH:
+ de_ctx->max_uniq_toclient_src_groups = 15;
+ de_ctx->max_uniq_toclient_dst_groups = 15;
+ de_ctx->max_uniq_toclient_sp_groups = 15;
+ de_ctx->max_uniq_toclient_dp_groups = 20;
+ de_ctx->max_uniq_toserver_src_groups = 15;
+ de_ctx->max_uniq_toserver_dst_groups = 15;
+ de_ctx->max_uniq_toserver_sp_groups = 15;
+ de_ctx->max_uniq_toserver_dp_groups = 40;
+ break;
+
+ case ENGINE_PROFILE_CUSTOM:
+ TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
+ if (strcmp(opt->val, "custom-values") == 0) {
+ max_uniq_toclient_src_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toclient-src-groups");
+ max_uniq_toclient_dst_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toclient-dst-groups");
+ max_uniq_toclient_sp_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toclient-sp-groups");
+ max_uniq_toclient_dp_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toclient-dp-groups");
+ max_uniq_toserver_src_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toserver-src-groups");
+ max_uniq_toserver_dst_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toserver-dst-groups");
+ max_uniq_toserver_sp_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toserver-sp-groups");
+ max_uniq_toserver_dp_groups_str = ConfNodeLookupChildValue
+ (opt->head.tqh_first, "toserver-dp-groups");
+ }
+ }
+ if (max_uniq_toclient_src_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_src_groups, 10,
+ strlen(max_uniq_toclient_src_groups_str),
+ (const char *)max_uniq_toclient_src_groups_str) <= 0) {
+ de_ctx->max_uniq_toclient_src_groups = 4;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toclient-src-groups failed, using %u",
+ max_uniq_toclient_src_groups_str,
+ de_ctx->max_uniq_toclient_src_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toclient_src_groups = 4;
+ }
+ if (max_uniq_toclient_dst_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_dst_groups, 10,
+ strlen(max_uniq_toclient_dst_groups_str),
+ (const char *)max_uniq_toclient_dst_groups_str) <= 0) {
+ de_ctx->max_uniq_toclient_dst_groups = 4;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toclient-dst-groups failed, using %u",
+ max_uniq_toclient_dst_groups_str,
+ de_ctx->max_uniq_toclient_dst_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toclient_dst_groups = 4;
+ }
+ if (max_uniq_toclient_sp_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_sp_groups, 10,
+ strlen(max_uniq_toclient_sp_groups_str),
+ (const char *)max_uniq_toclient_sp_groups_str) <= 0) {
+ de_ctx->max_uniq_toclient_sp_groups = 4;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toclient-sp-groups failed, using %u",
+ max_uniq_toclient_sp_groups_str,
+ de_ctx->max_uniq_toclient_sp_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toclient_sp_groups = 4;
+ }
+ if (max_uniq_toclient_dp_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_dp_groups, 10,
+ strlen(max_uniq_toclient_dp_groups_str),
+ (const char *)max_uniq_toclient_dp_groups_str) <= 0) {
+ de_ctx->max_uniq_toclient_dp_groups = 6;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toclient-dp-groups failed, using %u",
+ max_uniq_toclient_dp_groups_str,
+ de_ctx->max_uniq_toclient_dp_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toclient_dp_groups = 6;
+ }
+ if (max_uniq_toserver_src_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_src_groups, 10,
+ strlen(max_uniq_toserver_src_groups_str),
+ (const char *)max_uniq_toserver_src_groups_str) <= 0) {
+ de_ctx->max_uniq_toserver_src_groups = 4;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toserver-src-groups failed, using %u",
+ max_uniq_toserver_src_groups_str,
+ de_ctx->max_uniq_toserver_src_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toserver_src_groups = 4;
+ }
+ if (max_uniq_toserver_dst_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_dst_groups, 10,
+ strlen(max_uniq_toserver_dst_groups_str),
+ (const char *)max_uniq_toserver_dst_groups_str) <= 0) {
+ de_ctx->max_uniq_toserver_dst_groups = 8;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toserver-dst-groups failed, using %u",
+ max_uniq_toserver_dst_groups_str,
+ de_ctx->max_uniq_toserver_dst_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toserver_dst_groups = 8;
+ }
+ if (max_uniq_toserver_sp_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_sp_groups, 10,
+ strlen(max_uniq_toserver_sp_groups_str),
+ (const char *)max_uniq_toserver_sp_groups_str) <= 0) {
+ de_ctx->max_uniq_toserver_sp_groups = 4;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toserver-sp-groups failed, using %u",
+ max_uniq_toserver_sp_groups_str,
+ de_ctx->max_uniq_toserver_sp_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toserver_sp_groups = 4;
+ }
+ if (max_uniq_toserver_dp_groups_str != NULL) {
+ if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_dp_groups, 10,
+ strlen(max_uniq_toserver_dp_groups_str),
+ (const char *)max_uniq_toserver_dp_groups_str) <= 0) {
+ de_ctx->max_uniq_toserver_dp_groups = 30;
+ SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
+ "toserver-dp-groups failed, using %u",
+ max_uniq_toserver_dp_groups_str,
+ de_ctx->max_uniq_toserver_dp_groups);
+ }
+ } else {
+ de_ctx->max_uniq_toserver_dp_groups = 30;
+ }
+ break;
+
+ /* Default (or no config provided) is profile medium */
+ case ENGINE_PROFILE_MEDIUM:
+ case ENGINE_PROFILE_UNKNOWN:
+ default:
+ de_ctx->max_uniq_toclient_src_groups = 4;
+ de_ctx->max_uniq_toclient_dst_groups = 4;
+ de_ctx->max_uniq_toclient_sp_groups = 4;
+ de_ctx->max_uniq_toclient_dp_groups = 6;
+
+ de_ctx->max_uniq_toserver_src_groups = 4;
+ de_ctx->max_uniq_toserver_dst_groups = 8;
+ de_ctx->max_uniq_toserver_sp_groups = 4;
+ de_ctx->max_uniq_toserver_dp_groups = 30;
+ break;
+ }
+
+ if (profile == ENGINE_PROFILE_UNKNOWN)
+ return 0;
+ return 1;
+}
+
+/*
+ * getting & (re)setting the internal sig i
+ */
+
+//inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx)
+//{
+// return de_ctx->signum;
+//}
+
+void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx)
+{
+ de_ctx->signum = 0;
+}
+
+static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (de_ctx->keyword_id > 0) {
+ det_ctx->keyword_ctxs_array = SCMalloc(de_ctx->keyword_id * sizeof(void *));
+ if (det_ctx->keyword_ctxs_array == NULL) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx");
+ return TM_ECODE_FAILED;
+ }
+
+ memset(det_ctx->keyword_ctxs_array, 0x00, de_ctx->keyword_id * sizeof(void *));
+
+ det_ctx->keyword_ctxs_size = de_ctx->keyword_id;
+
+ DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
+ while (item) {
+ det_ctx->keyword_ctxs_array[item->id] = item->InitFunc(item->data);
+ if (det_ctx->keyword_ctxs_array[item->id] == NULL) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx "
+ "for keyword \"%s\" failed", item->name);
+ return TM_ECODE_FAILED;
+ }
+ item = item->next;
+ }
+ }
+ return TM_ECODE_OK;
+}
+
+static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (de_ctx->keyword_id > 0) {
+ DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
+ while (item) {
+ if (det_ctx->keyword_ctxs_array[item->id] != NULL)
+ item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
+
+ item = item->next;
+ }
+ det_ctx->keyword_ctxs_size = 0;
+ SCFree(det_ctx->keyword_ctxs_array);
+ det_ctx->keyword_ctxs_array = NULL;
+ }
+}
+
+/** \internal
+ * \brief Helper for DetectThread setup functions
+ */
+static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
+{
+ int i;
+
+ /** \todo we still depend on the global mpm_ctx here
+ *
+ * Initialize the thread pattern match ctx with the max size
+ * of the content and uricontent id's so our match lookup
+ * table is always big enough
+ */
+ PatternMatchThreadPrepare(&det_ctx->mtc, de_ctx->mpm_matcher, DetectContentMaxId(de_ctx));
+ PatternMatchThreadPrepare(&det_ctx->mtcs, de_ctx->mpm_matcher, DetectContentMaxId(de_ctx));
+ PatternMatchThreadPrepare(&det_ctx->mtcu, de_ctx->mpm_matcher, DetectUricontentMaxId(de_ctx));
+
+ PmqSetup(&det_ctx->pmq, de_ctx->max_fp_id);
+ for (i = 0; i < DETECT_SMSG_PMQ_NUM; i++) {
+ PmqSetup(&det_ctx->smsg_pmq[i], de_ctx->max_fp_id);
+ }
+
+ /* sized to the max of our sgh settings. A max setting of 0 implies that all
+ * sgh's have: sgh->non_mpm_store_cnt == 0 */
+ if (de_ctx->non_mpm_store_cnt_max > 0) {
+ det_ctx->non_mpm_id_array = SCCalloc(de_ctx->non_mpm_store_cnt_max, sizeof(SigIntId));
+ BUG_ON(det_ctx->non_mpm_id_array == NULL);
+ }
+
+ /* IP-ONLY */
+ DetectEngineIPOnlyThreadInit(de_ctx,&det_ctx->io_ctx);
+
+ /* DeState */
+ if (de_ctx->sig_array_len > 0) {
+ det_ctx->de_state_sig_array_len = de_ctx->sig_array_len;
+ det_ctx->de_state_sig_array = SCMalloc(det_ctx->de_state_sig_array_len * sizeof(uint8_t));
+ if (det_ctx->de_state_sig_array == NULL) {
+ return TM_ECODE_FAILED;
+ }
+ memset(det_ctx->de_state_sig_array, 0,
+ det_ctx->de_state_sig_array_len * sizeof(uint8_t));
+
+ det_ctx->match_array_len = de_ctx->sig_array_len;
+ det_ctx->match_array = SCMalloc(det_ctx->match_array_len * sizeof(Signature *));
+ if (det_ctx->match_array == NULL) {
+ return TM_ECODE_FAILED;
+ }
+ memset(det_ctx->match_array, 0,
+ det_ctx->match_array_len * sizeof(Signature *));
+ }
+
+ /* byte_extract storage */
+ det_ctx->bj_values = SCMalloc(sizeof(*det_ctx->bj_values) *
+ (de_ctx->byte_extract_max_local_id + 1));
+ if (det_ctx->bj_values == NULL) {
+ return TM_ECODE_FAILED;
+ }
+
+ DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx);
+#ifdef PROFILING
+ SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx);
+ SCProfilingKeywordThreadSetup(de_ctx->profile_keyword_ctx, det_ctx);
+#endif
+ SC_ATOMIC_INIT(det_ctx->so_far_used_by_detect);
+
+ return TM_ECODE_OK;
+}
+
+/** \brief initialize thread specific detection engine context
+ *
+ * \note there is a special case when using delayed detect. In this case the
+ * function is called twice per thread. The first time the rules are not
+ * yet loaded. de_ctx->delayed_detect_initialized will be 0. The 2nd
+ * time they will be loaded. de_ctx->delayed_detect_initialized will be 1.
+ * This is needed to do the per thread counter registration before the
+ * packet runtime starts. In delayed detect mode, the first call will
+ * return a NULL ptr through the data ptr.
+ *
+ * \param tv ThreadVars for this thread
+ * \param initdata pointer to de_ctx
+ * \param data[out] pointer to store our thread detection ctx
+ *
+ * \retval TM_ECODE_OK if all went well
+ * \retval TM_ECODE_FAILED on serious erro
+ */
+TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
+{
+ if (DetectEngineMultiTenantEnabled()) {
+ DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForMT(tv);
+ *data = (void *)mt_det_ctx;
+ return (mt_det_ctx == NULL) ? TM_ECODE_FAILED : TM_ECODE_OK;
+ }
+
+ /* first register the counter. In delayed detect mode we exit right after if the
+ * rules haven't been loaded yet. */
+ uint16_t counter_alerts = StatsRegisterCounter("detect.alert", tv);
+#ifdef PROFILING
+ uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
+ uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
+ uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
+ uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
+#endif
+ DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
+ if (unlikely(det_ctx == NULL))
+ return TM_ECODE_FAILED;
+ memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
+
+ det_ctx->tv = tv;
+ det_ctx->de_ctx = DetectEngineGetCurrent();
+ if (det_ctx->de_ctx == NULL) {
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ det_ctx->de_ctx = (DetectEngineCtx *)initdata;
+ } else {
+ DetectEngineThreadCtxDeinit(tv, det_ctx);
+ return TM_ECODE_FAILED;
+ }
+#else
+ DetectEngineThreadCtxDeinit(tv, det_ctx);
+ return TM_ECODE_FAILED;
+#endif
+ }
+
+ if (det_ctx->de_ctx->minimal == 0) {
+ if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
+ DetectEngineThreadCtxDeinit(tv, det_ctx);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ /** alert counter setup */
+ det_ctx->counter_alerts = counter_alerts;
+#ifdef PROFILING
+ det_ctx->counter_mpm_list = counter_mpm_list;
+ det_ctx->counter_nonmpm_list = counter_nonmpm_list;
+ det_ctx->counter_fnonmpm_list = counter_fnonmpm_list;
+ det_ctx->counter_match_list = counter_match_list;
+#endif
+
+ /* pass thread data back to caller */
+ *data = (void *)det_ctx;
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \internal
+ * \brief initialize a det_ctx for reload cases
+ * \param new_de_ctx the new detection engine
+ * \retval det_ctx detection engine thread ctx or NULL in case of error
+ */
+static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
+ ThreadVars *tv, DetectEngineCtx *new_de_ctx)
+{
+ DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
+ if (unlikely(det_ctx == NULL))
+ return NULL;
+ memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
+
+ det_ctx->tenant_id = new_de_ctx->tenant_id;
+ det_ctx->tv = tv;
+ det_ctx->de_ctx = DetectEngineReference(new_de_ctx);
+ if (det_ctx->de_ctx == NULL) {
+ SCFree(det_ctx);
+ return NULL;
+ }
+
+ /* most of the init happens here */
+ if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
+ DetectEngineDeReference(&det_ctx->de_ctx);
+ SCFree(det_ctx);
+ return NULL;
+ }
+
+ /** alert counter setup */
+ det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
+#ifdef PROFILING
+ uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
+ uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
+ uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
+ uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
+ det_ctx->counter_mpm_list = counter_mpm_list;
+ det_ctx->counter_nonmpm_list = counter_nonmpm_list;
+ det_ctx->counter_fnonmpm_list = counter_fnonmpm_list;
+ det_ctx->counter_match_list = counter_match_list;
+#endif
+
+ return det_ctx;
+}
+
+void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->tenant_array != NULL) {
+ SCFree(det_ctx->tenant_array);
+ det_ctx->tenant_array = NULL;
+ }
+
+#ifdef PROFILING
+ SCProfilingRuleThreadCleanup(det_ctx);
+ SCProfilingKeywordThreadCleanup(det_ctx);
+#endif
+
+ DetectEngineIPOnlyThreadDeinit(&det_ctx->io_ctx);
+
+ /** \todo get rid of this static */
+ if (det_ctx->de_ctx != NULL) {
+ PatternMatchThreadDestroy(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
+ PatternMatchThreadDestroy(&det_ctx->mtcs, det_ctx->de_ctx->mpm_matcher);
+ PatternMatchThreadDestroy(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
+ }
+
+ PmqFree(&det_ctx->pmq);
+ int i;
+ for (i = 0; i < DETECT_SMSG_PMQ_NUM; i++) {
+ PmqFree(&det_ctx->smsg_pmq[i]);
+ }
+
+ if (det_ctx->non_mpm_id_array != NULL)
+ SCFree(det_ctx->non_mpm_id_array);
+
+ if (det_ctx->de_state_sig_array != NULL)
+ SCFree(det_ctx->de_state_sig_array);
+ if (det_ctx->match_array != NULL)
+ SCFree(det_ctx->match_array);
+
+ if (det_ctx->bj_values != NULL)
+ SCFree(det_ctx->bj_values);
+
+ /* HHD temp storage */
+ for (i = 0; i < det_ctx->hhd_buffers_size; i++) {
+ if (det_ctx->hhd_buffers[i] != NULL)
+ SCFree(det_ctx->hhd_buffers[i]);
+ }
+ if (det_ctx->hhd_buffers)
+ SCFree(det_ctx->hhd_buffers);
+ det_ctx->hhd_buffers = NULL;
+ if (det_ctx->hhd_buffers_len)
+ SCFree(det_ctx->hhd_buffers_len);
+ det_ctx->hhd_buffers_len = NULL;
+
+ /* HSBD */
+ if (det_ctx->hsbd != NULL) {
+ SCLogDebug("det_ctx hsbd %u", det_ctx->hsbd_buffers_size);
+ for (i = 0; i < det_ctx->hsbd_buffers_size; i++) {
+ if (det_ctx->hsbd[i].buffer != NULL) {
+ HTPFree(det_ctx->hsbd[i].buffer, det_ctx->hsbd[i].buffer_size);
+ }
+ }
+ SCFree(det_ctx->hsbd);
+ }
+
+ /* HSCB */
+ if (det_ctx->hcbd != NULL) {
+ SCLogDebug("det_ctx hcbd %u", det_ctx->hcbd_buffers_size);
+ for (i = 0; i < det_ctx->hcbd_buffers_size; i++) {
+ if (det_ctx->hcbd[i].buffer != NULL)
+ SCFree(det_ctx->hcbd[i].buffer);
+ SCLogDebug("det_ctx->hcbd[i].buffer_size %u", det_ctx->hcbd[i].buffer_size);
+ }
+ SCFree(det_ctx->hcbd);
+ }
+
+ if (det_ctx->de_ctx != NULL) {
+ DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx);
+#ifdef UNITTESTS
+ if (!RunmodeIsUnittests() || det_ctx->de_ctx->ref_cnt > 0)
+ DetectEngineDeReference(&det_ctx->de_ctx);
+#else
+ DetectEngineDeReference(&det_ctx->de_ctx);
+#endif
+ }
+ SCFree(det_ctx);
+}
+
+TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
+{
+ DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
+
+ if (det_ctx == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "argument \"data\" NULL");
+ return TM_ECODE_OK;
+ }
+
+ if (det_ctx->mt_det_ctxs_hash != NULL) {
+ HashTableFree(det_ctx->mt_det_ctxs_hash);
+ det_ctx->mt_det_ctxs_hash = NULL;
+ }
+ DetectEngineThreadCtxFree(det_ctx);
+
+ return TM_ECODE_OK;
+}
+
+void DetectEngineThreadCtxInfo(ThreadVars *t, DetectEngineThreadCtx *det_ctx)
+{
+ /* XXX */
+ PatternMatchThreadPrint(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
+ PatternMatchThreadPrint(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
+}
+
+/** \brief Register Thread keyword context Funcs
+ *
+ * \param de_ctx detection engine to register in
+ * \param name keyword name for error printing
+ * \param InitFunc function ptr
+ * \param data keyword init data to pass to Func
+ * \param FreeFunc function ptr
+ * \param mode 0 normal (ctx per keyword instance) 1 shared (one ctx per det_ct)
+ *
+ * \retval id for retrieval of ctx at runtime
+ * \retval -1 on error
+ *
+ * \note make sure "data" remains valid and it free'd elsewhere. It's
+ * recommended to store it in the keywords global ctx so that
+ * it's freed when the de_ctx is freed.
+ */
+int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int mode)
+{
+ BUG_ON(de_ctx == NULL || InitFunc == NULL || FreeFunc == NULL || data == NULL);
+
+ if (mode) {
+ DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
+ while (item != NULL) {
+ if (strcmp(name, item->name) == 0) {
+ return item->id;
+ }
+
+ item = item->next;
+ }
+ }
+
+ DetectEngineThreadKeywordCtxItem *item = SCMalloc(sizeof(DetectEngineThreadKeywordCtxItem));
+ if (unlikely(item == NULL))
+ return -1;
+ memset(item, 0x00, sizeof(DetectEngineThreadKeywordCtxItem));
+
+ item->InitFunc = InitFunc;
+ item->FreeFunc = FreeFunc;
+ item->data = data;
+ item->name = name;
+
+ item->next = de_ctx->keyword_list;
+ de_ctx->keyword_list = item;
+ item->id = de_ctx->keyword_id++;
+
+ return item->id;
+}
+
+/** \brief Retrieve thread local keyword ctx by id
+ *
+ * \param det_ctx detection engine thread ctx to retrieve the ctx from
+ * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
+ * keyword init.
+ *
+ * \retval ctx or NULL on error
+ */
+void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
+{
+ if (id < 0 || id > det_ctx->keyword_ctxs_size || det_ctx->keyword_ctxs_array == NULL)
+ return NULL;
+
+ return det_ctx->keyword_ctxs_array[id];
+}
+
+/** \brief Check if detection is enabled
+ * \retval bool true or false */
+int DetectEngineEnabled(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (master->list == NULL) {
+ SCMutexUnlock(&master->lock);
+ return 0;
+ }
+
+ SCMutexUnlock(&master->lock);
+ return 1;
+}
+
+DetectEngineCtx *DetectEngineGetCurrent(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (master->list == NULL) {
+ SCMutexUnlock(&master->lock);
+ return NULL;
+ }
+
+ master->list->ref_cnt++;
+ SCLogDebug("master->list %p ref_cnt %u", master->list, master->list->ref_cnt);
+ SCMutexUnlock(&master->lock);
+ return master->list;
+}
+
+DetectEngineCtx *DetectEngineReference(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx == NULL)
+ return NULL;
+ de_ctx->ref_cnt++;
+ return de_ctx;
+}
+
+/** TODO locking? Not needed if this is a one time setting at startup */
+int DetectEngineMultiTenantEnabled(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ return (master->multi_tenant_enabled);
+}
+
+/** \internal
+ * \brief load a tenant from a yaml file
+ *
+ * \param tenant_id the tenant id by which the config is known
+ * \param filename full path of a yaml file
+ * \param loader_id id of loader thread or -1
+ *
+ * \retval 0 ok
+ * \retval -1 failed
+ */
+static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id, const char *filename, int loader_id)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ char prefix[64];
+
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
+
+#ifdef OS_WIN32
+ struct _stat st;
+ if(_stat(filename, &st) != 0) {
+#else
+ struct stat st;
+ if(stat(filename, &st) != 0) {
+#endif /* OS_WIN32 */
+ SCLogError(SC_ERR_FOPEN, "failed to stat file %s", filename);
+ goto error;
+ }
+
+ de_ctx = DetectEngineGetByTenantId(tenant_id);
+ if (de_ctx != NULL) {
+ SCLogError(SC_ERR_MT_DUPLICATE_TENANT, "tenant %u already registered",
+ tenant_id);
+ DetectEngineDeReference(&de_ctx);
+ goto error;
+ }
+
+ ConfNode *node = ConfGetNode(prefix);
+ if (node == NULL) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
+ goto error;
+ }
+
+ de_ctx = DetectEngineCtxInitWithPrefix(prefix);
+ if (de_ctx == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
+ "context failed.");
+ goto error;
+ }
+ SCLogDebug("de_ctx %p with prefix %s", de_ctx, de_ctx->config_prefix);
+
+ de_ctx->tenant_id = tenant_id;
+ de_ctx->loader_id = loader_id;
+
+ if (SigLoadSignatures(de_ctx, NULL, 0) < 0) {
+ SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
+ goto error;
+ }
+
+ DetectEngineAddToMaster(de_ctx);
+
+ return 0;
+
+error:
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return -1;
+}
+
+static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id, const char *filename, int reload_cnt)
+{
+ DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
+ if (old_de_ctx == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "tenant detect engine not found");
+ return -1;
+ }
+
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt);
+ reload_cnt++;
+ SCLogInfo("prefix %s", prefix);
+
+ if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
+ SCLogError(SC_ERR_INITIALIZATION,"failed to load yaml");
+ goto error;
+ }
+
+ ConfNode *node = ConfGetNode(prefix);
+ if (node == NULL) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
+ goto error;
+ }
+
+ DetectEngineCtx *new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
+ if (new_de_ctx == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
+ "context failed.");
+ goto error;
+ }
+ SCLogDebug("de_ctx %p with prefix %s", new_de_ctx, new_de_ctx->config_prefix);
+
+ new_de_ctx->tenant_id = tenant_id;
+ new_de_ctx->loader_id = old_de_ctx->loader_id;
+
+ if (SigLoadSignatures(new_de_ctx, NULL, 0) < 0) {
+ SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
+ goto error;
+ }
+
+ DetectEngineAddToMaster(new_de_ctx);
+
+ /* move to free list */
+ DetectEngineMoveToFreeList(old_de_ctx);
+ DetectEngineDeReference(&old_de_ctx);
+ return 0;
+
+error:
+ DetectEngineDeReference(&old_de_ctx);
+ return -1;
+}
+
+
+typedef struct TenantLoaderCtx_ {
+ uint32_t tenant_id;
+ int reload_cnt; /**< used by reload */
+ const char *yaml;
+} TenantLoaderCtx;
+
+static int DetectLoaderFuncLoadTenant(void *vctx, int loader_id)
+{
+ TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
+
+ SCLogInfo("loader %d", loader_id);
+ if (DetectEngineMultiTenantLoadTenant(ctx->tenant_id, ctx->yaml, loader_id) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int DetectLoaderSetupLoadTenant(uint32_t tenant_id, const char *yaml)
+{
+ TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->tenant_id = tenant_id;
+ t->yaml = yaml;
+
+ return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant, t);
+}
+
+static int DetectLoaderFuncReloadTenant(void *vctx, int loader_id)
+{
+ TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
+
+ SCLogDebug("loader_id %d", loader_id);
+
+ if (DetectEngineMultiTenantReloadTenant(ctx->tenant_id, ctx->yaml, ctx->reload_cnt) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int DetectLoaderSetupReloadTenant(uint32_t tenant_id, const char *yaml, int reload_cnt)
+{
+ DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
+ if (old_de_ctx == NULL)
+ return -ENOENT;
+ int loader_id = old_de_ctx->loader_id;
+ DetectEngineDeReference(&old_de_ctx);
+
+ TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
+ if (t == NULL)
+ return -ENOMEM;
+
+ t->tenant_id = tenant_id;
+ t->yaml = yaml;
+ t->reload_cnt = reload_cnt;
+
+ SCLogDebug("loader_id %d", loader_id);
+
+ return DetectLoaderQueueTask(loader_id, DetectLoaderFuncReloadTenant, t);
+}
+
+/** \brief Load a tenant and wait for loading to complete
+ */
+int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml)
+{
+ int r = DetectLoaderSetupLoadTenant(tenant_id, yaml);
+ if (r < 0)
+ return r;
+
+ if (DetectLoadersSync() != 0)
+ return -1;
+
+ return 0;
+}
+
+/** \brief Reload a tenant and wait for loading to complete
+ */
+int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt)
+{
+ int r = DetectLoaderSetupReloadTenant(tenant_id, yaml, reload_cnt);
+ if (r < 0)
+ return r;
+
+ if (DetectLoadersSync() != 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * \brief setup multi-detect / multi-tenancy
+ *
+ * See if MT is enabled. If so, setup the selector, tenants and mappings.
+ * Tenants and mappings are optional, and can also dynamically be added
+ * and removed from the unix socket.
+ */
+void DetectEngineMultiTenantSetup(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+
+ int failure_fatal = 0;
+ (void)ConfGetBool("engine.init-failure-fatal", &failure_fatal);
+
+ int enabled = 0;
+ (void)ConfGetBool("multi-detect.enabled", &enabled);
+ if (enabled == 1) {
+ DetectLoadersInit();
+ TmModuleDetectLoaderRegister();
+ DetectLoaderThreadSpawn();
+ TmThreadContinueDetectLoaderThreads();
+
+ SCMutexLock(&master->lock);
+ master->multi_tenant_enabled = 1;
+
+ char *handler = NULL;
+ if (ConfGet("multi-detect.selector", &handler) == 1) {
+ SCLogInfo("selector %s", handler);
+
+ if (strcmp(handler, "vlan") == 0) {
+ master->tenant_selector = TENANT_SELECTOR_VLAN;
+ } else if (strcmp(handler, "direct") == 0) {
+ master->tenant_selector = TENANT_SELECTOR_DIRECT;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "unknown value %s "
+ "multi-detect.selector", handler);
+ SCMutexUnlock(&master->lock);
+ goto error;
+ }
+ }
+ SCMutexUnlock(&master->lock);
+ SCLogInfo("multi-detect is enabled (multi tenancy). Selector: %s", handler);
+
+ /* traffic -- tenant mappings */
+ ConfNode *mappings_root_node = ConfGetNode("multi-detect.mappings");
+ ConfNode *mapping_node = NULL;
+
+ if (mappings_root_node != NULL) {
+ TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
+ if (strcmp(mapping_node->val, "vlan") == 0) {
+ ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id");
+ if (tenant_id_node == NULL)
+ goto bad_mapping;
+ ConfNode *vlan_id_node = ConfNodeLookupChild(mapping_node, "vlan-id");
+ if (vlan_id_node == NULL)
+ goto bad_mapping;
+
+ SCLogInfo("vlan %s %s", tenant_id_node->val, vlan_id_node->val);
+
+ uint32_t tenant_id = 0;
+ if (ByteExtractStringUint32(&tenant_id, 10, strlen(tenant_id_node->val),
+ tenant_id_node->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id "
+ "of %s is invalid", tenant_id_node->val);
+ goto bad_mapping;
+ }
+
+ uint16_t vlan_id = 0;
+ if (ByteExtractStringUint16(&vlan_id, 10, strlen(vlan_id_node->val),
+ vlan_id_node->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id "
+ "of %s is invalid", vlan_id_node->val);
+ goto bad_mapping;
+ }
+
+ if (DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)vlan_id) != 0) {
+ goto error;
+ }
+ } else {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "multi-detect.mappings expects a list of 'vlan's. Not %s", mapping_node->val);
+ goto bad_mapping;
+ }
+ continue;
+
+ bad_mapping:
+ if (failure_fatal)
+ goto error;
+ }
+ }
+
+ /* tenants */
+ ConfNode *tenants_root_node = ConfGetNode("multi-detect.tenants");
+ ConfNode *tenant_node = NULL;
+
+ if (tenants_root_node != NULL) {
+ TAILQ_FOREACH(tenant_node, &tenants_root_node->head, next) {
+ if (strcmp(tenant_node->val, "tenant") != 0) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "multi-detect.tenants expects a list of 'tenant's. Not %s", tenant_node->val);
+ goto bad_tenant;
+ }
+ ConfNode *id_node = ConfNodeLookupChild(tenant_node, "id");
+ if (id_node == NULL)
+ goto bad_tenant;
+ ConfNode *yaml_node = ConfNodeLookupChild(tenant_node, "yaml");
+ if (yaml_node == NULL)
+ goto bad_tenant;
+
+ uint32_t tenant_id = 0;
+ if (ByteExtractStringUint32(&tenant_id, 10, strlen(id_node->val),
+ id_node->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant_id "
+ "of %s is invalid", id_node->val);
+ goto bad_tenant;
+ }
+ SCLogInfo("tenant id: %u, %s", tenant_id, yaml_node->val);
+
+ /* setup the yaml in this loop so that it's not done by the loader
+ * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
+ if (ConfYamlLoadFileWithPrefix(yaml_node->val, prefix) != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", yaml_node->val);
+ goto bad_tenant;
+ }
+
+ if (DetectLoaderSetupLoadTenant(tenant_id, yaml_node->val) != 0) {
+ /* error logged already */
+ goto bad_tenant;
+ }
+ continue;
+
+ bad_tenant:
+ if (failure_fatal)
+ goto error;
+ }
+ }
+
+ /* wait for our loaders to complete their tasks */
+ if (DetectLoadersSync() != 0)
+ goto error;
+
+ if (DetectEngineMTApply() < 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ goto error;
+ }
+
+ } else {
+ SCLogDebug("multi-detect not enabled (multi tenancy)");
+ }
+error:
+ return;
+}
+
+uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p)
+{
+ const DetectEngineThreadCtx *det_ctx = ctx;
+ uint32_t x = 0;
+ uint32_t vlan_id = 0;
+
+ if (p->vlan_idx == 0)
+ return 0;
+
+ vlan_id = p->vlan_id[0];
+
+ if (det_ctx == NULL || det_ctx->tenant_array == NULL || det_ctx->tenant_array_size == 0)
+ return 0;
+
+ /* not very efficient, but for now we're targeting only limited amounts.
+ * Can use hash/tree approach later. */
+ for (x = 0; x < det_ctx->tenant_array_size; x++) {
+ if (det_ctx->tenant_array[x].traffic_id == vlan_id)
+ return det_ctx->tenant_array[x].tenant_id;
+ }
+
+ return 0;
+}
+
+static int DetectEngineTentantRegisterSelector(enum DetectEngineTenantSelectors selector,
+ uint32_t tenant_id, uint32_t traffic_id)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (!(master->tenant_selector == TENANT_SELECTOR_UNKNOWN || master->tenant_selector == selector)) {
+ SCLogInfo("conflicting selector already set");
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+
+ DetectEngineTenantMapping *m = master->tenant_mapping_list;
+ while (m) {
+ if (m->traffic_id == traffic_id) {
+ SCLogInfo("traffic id already registered");
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+ m = m->next;
+ }
+
+ DetectEngineTenantMapping *map = SCCalloc(1, sizeof(*map));
+ if (map == NULL) {
+ SCLogInfo("memory fail");
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+ map->traffic_id = traffic_id;
+ map->tenant_id = tenant_id;
+
+ map->next = master->tenant_mapping_list;
+ master->tenant_mapping_list = map;
+
+ master->tenant_selector = selector;
+
+ SCLogInfo("tenant handler %u %u %u registered", selector, tenant_id, traffic_id);
+ SCMutexUnlock(&master->lock);
+ return 0;
+}
+
+static int DetectEngineTentantUnregisterSelector(enum DetectEngineTenantSelectors selector,
+ uint32_t tenant_id, uint32_t traffic_id)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (master->tenant_mapping_list == NULL) {
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+
+ DetectEngineTenantMapping *prev = NULL;
+ DetectEngineTenantMapping *map = master->tenant_mapping_list;
+ while (map) {
+ if (map->traffic_id == traffic_id &&
+ map->tenant_id == tenant_id)
+ {
+ if (prev != NULL)
+ prev->next = map->next;
+ else
+ master->tenant_mapping_list = map->next;
+
+ map->next = NULL;
+ SCFree(map);
+ SCLogInfo("tenant handler %u %u %u unregistered", selector, tenant_id, traffic_id);
+ SCMutexUnlock(&master->lock);
+ return 0;
+ }
+ prev = map;
+ map = map->next;
+ }
+
+ SCMutexUnlock(&master->lock);
+ return -1;
+}
+
+int DetectEngineTentantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
+{
+ return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
+}
+
+int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
+{
+ return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
+}
+
+int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id)
+{
+ SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
+ return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
+}
+
+int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id)
+{
+ SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
+ return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
+}
+
+uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p)
+{
+ return p->pcap_v.tenant_id;
+}
+
+DetectEngineCtx *DetectEngineGetByTenantId(int tenant_id)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (master->list == NULL) {
+ SCMutexUnlock(&master->lock);
+ return NULL;
+ }
+
+ DetectEngineCtx *de_ctx = master->list;
+ while (de_ctx) {
+ if (de_ctx->tenant_id == tenant_id) {
+ de_ctx->ref_cnt++;
+ break;
+ }
+
+ de_ctx = de_ctx->next;
+ }
+
+ SCMutexUnlock(&master->lock);
+ return de_ctx;
+}
+
+void DetectEngineDeReference(DetectEngineCtx **de_ctx)
+{
+ BUG_ON((*de_ctx)->ref_cnt == 0);
+ (*de_ctx)->ref_cnt--;
+ *de_ctx = NULL;
+}
+
+static int DetectEngineAddToList(DetectEngineCtx *instance)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+
+ if (instance == NULL)
+ return -1;
+
+ if (master->list == NULL) {
+ master->list = instance;
+ } else {
+ instance->next = master->list;
+ master->list = instance;
+ }
+
+ return 0;
+}
+
+int DetectEngineAddToMaster(DetectEngineCtx *de_ctx)
+{
+ int r;
+
+ if (de_ctx == NULL)
+ return -1;
+
+ SCLogDebug("adding de_ctx %p to master", de_ctx);
+
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+ r = DetectEngineAddToList(de_ctx);
+ SCMutexUnlock(&master->lock);
+ return r;
+}
+
+int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+
+ SCMutexLock(&master->lock);
+ DetectEngineCtx *instance = master->list;
+ if (instance == NULL) {
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+
+ /* remove from active list */
+ if (instance == de_ctx) {
+ master->list = instance->next;
+ } else {
+ DetectEngineCtx *prev = instance;
+ instance = instance->next; /* already checked first element */
+
+ while (instance) {
+ DetectEngineCtx *next = instance->next;
+
+ if (instance == de_ctx) {
+ prev->next = instance->next;
+ break;
+ }
+
+ prev = instance;
+ instance = next;
+ }
+ if (instance == NULL) {
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+ }
+
+ /* instance is now detached from list */
+ instance->next = NULL;
+
+ /* add to free list */
+ if (master->free_list == NULL) {
+ master->free_list = instance;
+ } else {
+ instance->next = master->free_list;
+ master->free_list = instance;
+ }
+ SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx, de_ctx->ref_cnt);
+
+ SCMutexUnlock(&master->lock);
+ return 0;
+}
+
+void DetectEnginePruneFreeList(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ DetectEngineCtx *prev = NULL;
+ DetectEngineCtx *instance = master->free_list;
+ while (instance) {
+ DetectEngineCtx *next = instance->next;
+
+ SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt);
+
+ if (instance->ref_cnt == 0) {
+ if (prev == NULL) {
+ master->free_list = next;
+ } else {
+ prev->next = next;
+ }
+
+ SCLogDebug("freeing detect engine %p", instance);
+ DetectEngineCtxFree(instance);
+ instance = NULL;
+ }
+
+ prev = instance;
+ instance = next;
+ }
+ SCMutexUnlock(&master->lock);
+}
+
+static int reloads = 0;
+
+/** \brief Reload the detection engine
+ *
+ * \param filename YAML file to load for the detect config
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int DetectEngineReload(const char *filename)
+{
+ DetectEngineCtx *new_de_ctx = NULL;
+ DetectEngineCtx *old_de_ctx = NULL;
+
+ char prefix[128] = "";
+ if (filename != NULL) {
+ snprintf(prefix, sizeof(prefix), "detect-engine-reloads.%d", reloads++);
+ if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", filename);
+ return -1;
+ }
+
+ ConfNode *node = ConfGetNode(prefix);
+ if (node == NULL) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
+ return -1;
+ }
+#if 0
+ ConfDump();
+#endif
+ }
+
+ /* get a reference to the current de_ctx */
+ old_de_ctx = DetectEngineGetCurrent();
+ if (old_de_ctx == NULL)
+ return -1;
+ SCLogDebug("get ref to old_de_ctx %p", old_de_ctx);
+
+ /* get new detection engine */
+ new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
+ if (new_de_ctx == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
+ "context failed.");
+ DetectEngineDeReference(&old_de_ctx);
+ return -1;
+ }
+ if (SigLoadSignatures(new_de_ctx, NULL, 0) != 0) {
+ DetectEngineCtxFree(new_de_ctx);
+ DetectEngineDeReference(&old_de_ctx);
+ return -1;
+ }
+ SCThresholdConfInitContext(new_de_ctx, NULL);
+ SCLogDebug("set up new_de_ctx %p", new_de_ctx);
+
+ /* add to master */
+ DetectEngineAddToMaster(new_de_ctx);
+
+ /* move to old free list */
+ DetectEngineMoveToFreeList(old_de_ctx);
+ DetectEngineDeReference(&old_de_ctx);
+
+ SCLogDebug("going to reload the threads to use new_de_ctx %p", new_de_ctx);
+ /* update the threads */
+ DetectEngineReloadThreads(new_de_ctx);
+ SCLogDebug("threads now run new_de_ctx %p", new_de_ctx);
+
+ /* walk free list, freeing the old_de_ctx */
+ DetectEnginePruneFreeList();
+
+ SCLogDebug("old_de_ctx should have been freed");
+ return 0;
+}
+
+static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len)
+{
+ DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
+ return det_ctx->tenant_id % h->array_size;
+}
+
+static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len)
+{
+ DetectEngineThreadCtx *det1 = (DetectEngineThreadCtx *)d1;
+ DetectEngineThreadCtx *det2 = (DetectEngineThreadCtx *)d2;
+ return (det1->tenant_id == det2->tenant_id);
+}
+
+static void TenantIdFree(void *d)
+{
+ DetectEngineThreadCtxFree(d);
+}
+
+/** NOTE: master MUST be locked before calling this */
+static DetectEngineThreadCtx *DetectEngineThreadCtxInitForMT(ThreadVars *tv)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ DetectEngineTenantMapping *map_array = NULL;
+ uint32_t map_array_size = 0;
+ uint32_t map_cnt = 0;
+ int max_tenant_id = 0;
+ DetectEngineCtx *list = master->list;
+ HashTable *mt_det_ctxs_hash = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
+ SCLogError(SC_ERR_MT_NO_SELECTOR, "no tenant selector set: "
+ "set using multi-detect.selector");
+ return NULL;
+ }
+
+ uint32_t tcnt = 0;
+ while (list) {
+ if (list->tenant_id > max_tenant_id)
+ max_tenant_id = list->tenant_id;
+
+ list = list->next;
+ tcnt++;
+ }
+
+ mt_det_ctxs_hash = HashTableInit(tcnt * 2, TenantIdHash, TenantIdCompare, TenantIdFree);
+ if (mt_det_ctxs_hash == NULL) {
+ goto error;
+ }
+
+ if (max_tenant_id == 0) {
+ SCLogInfo("no tenants left, or none registered yet");
+ } else {
+ max_tenant_id++;
+
+ DetectEngineTenantMapping *map = master->tenant_mapping_list;
+ while (map) {
+ map_cnt++;
+ map = map->next;
+ }
+
+ if (map_cnt > 0) {
+ map_array_size = map_cnt + 1;
+
+ map_array = SCCalloc(map_array_size, sizeof(*map_array));
+ if (map_array == NULL)
+ goto error;
+
+ /* fill the array */
+ map_cnt = 0;
+ map = master->tenant_mapping_list;
+ while (map) {
+ BUG_ON(map_cnt > map_array_size);
+ map_array[map_cnt].traffic_id = map->traffic_id;
+ map_array[map_cnt].tenant_id = map->tenant_id;
+ map_cnt++;
+ map = map->next;
+ }
+
+ }
+
+ /* set up hash for tenant lookup */
+ list = master->list;
+ while (list) {
+ if (list->tenant_id != 0) {
+ DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForReload(tv, list);
+ if (mt_det_ctx == NULL)
+ goto error;
+ BUG_ON(HashTableAdd(mt_det_ctxs_hash, mt_det_ctx, 0) != 0);
+ }
+ list = list->next;
+ }
+ }
+
+ det_ctx = SCCalloc(1, sizeof(DetectEngineThreadCtx));
+ if (det_ctx == NULL) {
+ goto error;
+ }
+ det_ctx->mt_det_ctxs_hash = mt_det_ctxs_hash;
+ mt_det_ctxs_hash = NULL;
+
+ /* first register the counter. In delayed detect mode we exit right after if the
+ * rules haven't been loaded yet. */
+ uint16_t counter_alerts = StatsRegisterCounter("detect.alert", tv);
+#ifdef PROFILING
+ uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
+ uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
+ uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
+ uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
+#endif
+ /** alert counter setup */
+ det_ctx->counter_alerts = counter_alerts;
+#ifdef PROFILING
+ det_ctx->counter_mpm_list = counter_mpm_list;
+ det_ctx->counter_nonmpm_list = counter_nonmpm_list;
+ det_ctx->counter_fnonmpm_list = counter_fnonmpm_list;
+ det_ctx->counter_match_list = counter_match_list;
+#endif
+ det_ctx->mt_det_ctxs_cnt = max_tenant_id;
+
+ det_ctx->tenant_array = map_array;
+ det_ctx->tenant_array_size = map_array_size;
+
+ switch (master->tenant_selector) {
+ case TENANT_SELECTOR_UNKNOWN:
+ SCLogDebug("TENANT_SELECTOR_UNKNOWN");
+ break;
+ case TENANT_SELECTOR_VLAN:
+ det_ctx->TenantGetId = DetectEngineTentantGetIdFromVlanId;
+ SCLogDebug("TENANT_SELECTOR_VLAN");
+ break;
+ case TENANT_SELECTOR_DIRECT:
+ det_ctx->TenantGetId = DetectEngineTentantGetIdFromPcap;
+ SCLogDebug("TENANT_SELECTOR_DIRECT");
+ break;
+ }
+
+ return det_ctx;
+error:
+ if (map_array != NULL)
+ SCFree(map_array);
+ if (mt_det_ctxs_hash != NULL)
+ HashTableFree(mt_det_ctxs_hash);
+
+ return NULL;
+}
+
+int DetectEngineMTApply(void)
+{
+ DetectEngineMasterCtx *master = &g_master_de_ctx;
+ SCMutexLock(&master->lock);
+
+ if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
+ SCLogInfo("error, no tenant selector");
+ SCMutexUnlock(&master->lock);
+ return -1;
+ }
+
+ DetectEngineCtx *minimal_de_ctx = NULL;
+ /* if we have no tenants, we need a minimal on */
+ if (master->list == NULL) {
+ minimal_de_ctx = master->list = DetectEngineCtxInitMinimal();
+ SCLogDebug("no tenants, using minimal %p", minimal_de_ctx);
+ } else if (master->list->next == NULL && master->list->tenant_id == 0) {
+ minimal_de_ctx = master->list;
+ SCLogDebug("no tenants, using original %p", minimal_de_ctx);
+ }
+
+ /* update the threads */
+ SCLogDebug("MT reload starting");
+ DetectEngineReloadThreads(minimal_de_ctx);
+ SCLogDebug("MT reload done");
+
+ SCMutexUnlock(&master->lock);
+
+ /* walk free list, freeing the old_de_ctx */
+ DetectEnginePruneFreeList();
+
+ SCLogDebug("old_de_ctx should have been freed");
+ return 0;
+}
+
+const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
+{
+ switch (type) {
+ case DETECT_SM_LIST_MATCH:
+ return "packet";
+ case DETECT_SM_LIST_PMATCH:
+ return "packet/stream payload";
+
+ case DETECT_SM_LIST_UMATCH:
+ return "http uri";
+ case DETECT_SM_LIST_HRUDMATCH:
+ return "http raw uri";
+ case DETECT_SM_LIST_HCBDMATCH:
+ return "http client body";
+ case DETECT_SM_LIST_FILEDATA:
+ return "http server body";
+ case DETECT_SM_LIST_HHDMATCH:
+ return "http headers";
+ case DETECT_SM_LIST_HRHDMATCH:
+ return "http raw headers";
+ case DETECT_SM_LIST_HSMDMATCH:
+ return "http stat msg";
+ case DETECT_SM_LIST_HSCDMATCH:
+ return "http stat code";
+ case DETECT_SM_LIST_HHHDMATCH:
+ return "http host";
+ case DETECT_SM_LIST_HRHHDMATCH:
+ return "http raw host header";
+ case DETECT_SM_LIST_HMDMATCH:
+ return "http method";
+ case DETECT_SM_LIST_HCDMATCH:
+ return "http cookie";
+ case DETECT_SM_LIST_HUADMATCH:
+ return "http user-agent";
+ case DETECT_SM_LIST_HRLMATCH:
+ return "http request line";
+ case DETECT_SM_LIST_APP_EVENT:
+ return "app layer events";
+
+ case DETECT_SM_LIST_AMATCH:
+ return "generic app layer";
+ case DETECT_SM_LIST_DMATCH:
+ return "dcerpc";
+ case DETECT_SM_LIST_TMATCH:
+ return "tag";
+
+ case DETECT_SM_LIST_FILEMATCH:
+ return "file";
+
+ case DETECT_SM_LIST_DNSQUERYNAME_MATCH:
+ return "dns query name";
+ case DETECT_SM_LIST_DNSREQUEST_MATCH:
+ return "dns request";
+ case DETECT_SM_LIST_DNSRESPONSE_MATCH:
+ return "dns response";
+
+ case DETECT_SM_LIST_MODBUS_MATCH:
+ return "modbus";
+
+ case DETECT_SM_LIST_POSTMATCH:
+ return "post-match";
+
+ case DETECT_SM_LIST_SUPPRESS:
+ return "suppress";
+ case DETECT_SM_LIST_THRESHOLD:
+ return "threshold";
+
+ case DETECT_SM_LIST_MAX:
+ return "max (internal)";
+ case DETECT_SM_LIST_NOTSET:
+ return "not set (internal)";
+ }
+ return "error";
+}
+
+
+/*************************************Unittest*********************************/
+
+#ifdef UNITTESTS
+
+static int DetectEngineInitYamlConf(char *conf)
+{
+ ConfCreateContextBackup();
+ ConfInit();
+ return ConfYamlLoadString(conf, strlen(conf));
+}
+
+static void DetectEngineDeInitYamlConf(void)
+{
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return;
+}
+
+static int DetectEngineTest01(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: medium\n"
+ " - custom-values:\n"
+ " toclient_src_groups: 2\n"
+ " toclient_dst_groups: 2\n"
+ " toclient_sp_groups: 2\n"
+ " toclient_dp_groups: 3\n"
+ " toserver_src_groups: 2\n"
+ " toserver_dst_groups: 4\n"
+ " toserver_sp_groups: 2\n"
+ " toserver_dp_groups: 25\n"
+ " - inspection-recursion-limit: 0\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ result = (de_ctx->inspection_recursion_limit == -1);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+static int DetectEngineTest02(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: medium\n"
+ " - custom-values:\n"
+ " toclient_src_groups: 2\n"
+ " toclient_dst_groups: 2\n"
+ " toclient_sp_groups: 2\n"
+ " toclient_dp_groups: 3\n"
+ " toserver_src_groups: 2\n"
+ " toserver_dst_groups: 4\n"
+ " toserver_sp_groups: 2\n"
+ " toserver_dp_groups: 25\n"
+ " - inspection-recursion-limit:\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ result = (de_ctx->inspection_recursion_limit == -1);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+static int DetectEngineTest03(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: medium\n"
+ " - custom-values:\n"
+ " toclient_src_groups: 2\n"
+ " toclient_dst_groups: 2\n"
+ " toclient_sp_groups: 2\n"
+ " toclient_dp_groups: 3\n"
+ " toserver_src_groups: 2\n"
+ " toserver_dst_groups: 4\n"
+ " toserver_sp_groups: 2\n"
+ " toserver_dp_groups: 25\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ result = (de_ctx->inspection_recursion_limit ==
+ DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+static int DetectEngineTest04(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: medium\n"
+ " - custom-values:\n"
+ " toclient_src_groups: 2\n"
+ " toclient_dst_groups: 2\n"
+ " toclient_sp_groups: 2\n"
+ " toclient_dp_groups: 3\n"
+ " toserver_src_groups: 2\n"
+ " toserver_dst_groups: 4\n"
+ " toserver_sp_groups: 2\n"
+ " toserver_dp_groups: 25\n"
+ " - inspection-recursion-limit: 10\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ result = (de_ctx->inspection_recursion_limit == 10);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+int DummyTestAppInspectionEngine01(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig,
+ Flow *f,
+ uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ return 0;
+}
+
+int DummyTestAppInspectionEngine02(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig,
+ Flow *f,
+ uint8_t flags,
+ void *alstate,
+ void *tx, uint64_t tx_id)
+{
+ return 0;
+}
+
+int DetectEngineTest05(void)
+{
+ int result = 0;
+ int ip = 0;
+
+ DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2];
+ memset(engine_list, 0, sizeof(engine_list));
+
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ ALPROTO_HTTP,
+ 0 /* STREAM_TOSERVER */,
+ DETECT_SM_LIST_UMATCH,
+ DE_STATE_FLAG_URI_INSPECT,
+ DummyTestAppInspectionEngine01,
+ engine_list);
+
+ int alproto = ALPROTO_UNKNOWN + 1;
+ for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) {
+ for ( ; alproto < ALPROTO_FAILED; alproto++) {
+ int dir = 0;
+ for ( ; dir < 2; dir++) {
+ if (alproto == ALPROTO_HTTP && dir == 0) {
+ if (engine_list[ip][alproto][dir]->next != NULL) {
+ printf("more than one entry found\n");
+ goto end;
+ }
+
+ DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir];
+
+ if (engine->alproto != alproto ||
+ engine->dir != dir ||
+ engine->sm_list != DETECT_SM_LIST_UMATCH ||
+ engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT ||
+ engine->Callback != DummyTestAppInspectionEngine01) {
+ printf("failed for http and dir(0-toserver)\n");
+ goto end;
+ }
+ } /* if (alproto == ALPROTO_HTTP && dir == 0) */
+
+ if (alproto == ALPROTO_HTTP && dir == 1) {
+ if (engine_list[ip][alproto][dir] != NULL) {
+ printf("failed for http and dir(1-toclient)\n");
+ goto end;
+ }
+ }
+
+ if (alproto != ALPROTO_HTTP &&
+ engine_list[ip][alproto][0] != NULL &&
+ engine_list[ip][alproto][1] != NULL) {
+ printf("failed for protocol %d\n", alproto);
+ goto end;
+ }
+ } /* for ( ; dir < 2 ..)*/
+ } /* for ( ; alproto < ALPROTO_FAILED; ..) */
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+int DetectEngineTest06(void)
+{
+ int result = 0;
+ int ip = 0;
+
+ DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2];
+ memset(engine_list, 0, sizeof(engine_list));
+
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ ALPROTO_HTTP,
+ 0 /* STREAM_TOSERVER */,
+ DETECT_SM_LIST_UMATCH,
+ DE_STATE_FLAG_URI_INSPECT,
+ DummyTestAppInspectionEngine01,
+ engine_list);
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ ALPROTO_HTTP,
+ 1 /* STREAM_TOCLIENT */,
+ DETECT_SM_LIST_UMATCH,
+ DE_STATE_FLAG_URI_INSPECT,
+ DummyTestAppInspectionEngine02,
+ engine_list);
+
+ int alproto = ALPROTO_UNKNOWN + 1;
+ for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) {
+ for ( ; alproto < ALPROTO_FAILED; alproto++) {
+ int dir = 0;
+ for ( ; dir < 2; dir++) {
+ if (alproto == ALPROTO_HTTP && dir == 0) {
+ if (engine_list[ip][alproto][dir]->next != NULL) {
+ printf("more than one entry found\n");
+ goto end;
+ }
+
+ DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir];
+
+ if (engine->alproto != alproto ||
+ engine->dir != dir ||
+ engine->sm_list != DETECT_SM_LIST_UMATCH ||
+ engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT ||
+ engine->Callback != DummyTestAppInspectionEngine01) {
+ printf("failed for http and dir(0-toserver)\n");
+ goto end;
+ }
+ } /* if (alproto == ALPROTO_HTTP && dir == 0) */
+
+ if (alproto == ALPROTO_HTTP && dir == 1) {
+ if (engine_list[ip][alproto][dir]->next != NULL) {
+ printf("more than one entry found\n");
+ goto end;
+ }
+
+ DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir];
+
+ if (engine->alproto != alproto ||
+ engine->dir != dir ||
+ engine->sm_list != DETECT_SM_LIST_UMATCH ||
+ engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT ||
+ engine->Callback != DummyTestAppInspectionEngine02) {
+ printf("failed for http and dir(0-toclient)\n");
+ goto end;
+ }
+ } /* if (alproto == ALPROTO_HTTP && dir == 1) */
+
+ if (alproto != ALPROTO_HTTP &&
+ engine_list[ip][alproto][0] != NULL &&
+ engine_list[ip][alproto][1] != NULL) {
+ printf("failed for protocol %d\n", alproto);
+ goto end;
+ }
+ } /* for ( ; dir < 2 ..)*/
+ } /* for ( ; alproto < ALPROTO_FAILED; ..) */
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+int DetectEngineTest07(void)
+{
+ int result = 0;
+ int ip = 0;
+
+ DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2];
+ memset(engine_list, 0, sizeof(engine_list));
+
+ struct test_data_t {
+ int32_t sm_list;
+ uint32_t inspect_flags;
+ uint16_t dir;
+ int (*Callback)(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig, Flow *f,
+ uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id);
+
+ };
+
+ struct test_data_t data[] = {
+ { DETECT_SM_LIST_UMATCH,
+ DE_STATE_FLAG_URI_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HCBDMATCH,
+ DE_STATE_FLAG_HCBD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine02 },
+ { DETECT_SM_LIST_FILEDATA,
+ DE_STATE_FLAG_HSBD_INSPECT,
+ 1,
+ DummyTestAppInspectionEngine02 },
+ { DETECT_SM_LIST_HHDMATCH,
+ DE_STATE_FLAG_HHD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HRHDMATCH,
+ DE_STATE_FLAG_HRHD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HMDMATCH,
+ DE_STATE_FLAG_HMD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine02 },
+ { DETECT_SM_LIST_HCDMATCH,
+ DE_STATE_FLAG_HCD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HRUDMATCH,
+ DE_STATE_FLAG_HRUD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_FILEMATCH,
+ DE_STATE_FLAG_FILE_TS_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine02 },
+ { DETECT_SM_LIST_FILEMATCH,
+ DE_STATE_FLAG_FILE_TC_INSPECT,
+ 1,
+ DummyTestAppInspectionEngine02 },
+ { DETECT_SM_LIST_HSMDMATCH,
+ DE_STATE_FLAG_HSMD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HSCDMATCH,
+ DE_STATE_FLAG_HSCD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine01 },
+ { DETECT_SM_LIST_HUADMATCH,
+ DE_STATE_FLAG_HUAD_INSPECT,
+ 0,
+ DummyTestAppInspectionEngine02 },
+ };
+
+ size_t i = 0;
+ for ( ; i < sizeof(data) / sizeof(struct test_data_t); i++) {
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ ALPROTO_HTTP,
+ data[i].dir /* STREAM_TOCLIENT */,
+ data[i].sm_list,
+ data[i].inspect_flags,
+ data[i].Callback,
+ engine_list);
+ }
+
+#if 0
+ DetectEnginePrintAppInspectionEngines(engine_list);
+#endif
+
+ int alproto = ALPROTO_UNKNOWN + 1;
+ for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) {
+ for ( ; alproto < ALPROTO_FAILED; alproto++) {
+ int dir = 0;
+ for ( ; dir < 2; dir++) {
+ if (alproto == ALPROTO_HTTP) {
+ DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir];
+
+ size_t i = 0;
+ for ( ; i < (sizeof(data) / sizeof(struct test_data_t)); i++) {
+ if (data[i].dir != dir)
+ continue;
+
+ if (engine->alproto != ALPROTO_HTTP ||
+ engine->dir != data[i].dir ||
+ engine->sm_list != data[i].sm_list ||
+ engine->inspect_flags != data[i].inspect_flags ||
+ engine->Callback != data[i].Callback) {
+ printf("failed for http\n");
+ goto end;
+ }
+ engine = engine->next;
+ }
+ } else {
+ if (engine_list[ip][alproto][0] != NULL &&
+ engine_list[ip][alproto][1] != NULL) {
+ printf("failed for protocol %d\n", alproto);
+ goto end;
+ }
+ } /* else */
+ } /* for ( ; dir < 2; dir++) */
+ } /* for ( ; alproto < ALPROTO_FAILED; ..) */
+ }
+
+ result = 1;
+ end:
+ return result;
+}
+
+static int DetectEngineTest08(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: custom\n"
+ " - custom-values:\n"
+ " toclient-src-groups: 20\n"
+ " toclient-dst-groups: 21\n"
+ " toclient-sp-groups: 22\n"
+ " toclient-dp-groups: 23\n"
+ " toserver-src-groups: 24\n"
+ " toserver-dst-groups: 25\n"
+ " toserver-sp-groups: 26\n"
+ " toserver-dp-groups: 27\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (de_ctx->max_uniq_toclient_src_groups == 20 &&
+ de_ctx->max_uniq_toclient_dst_groups == 21 &&
+ de_ctx->max_uniq_toclient_sp_groups == 22 &&
+ de_ctx->max_uniq_toclient_dp_groups == 23 &&
+ de_ctx->max_uniq_toserver_src_groups == 24 &&
+ de_ctx->max_uniq_toserver_dst_groups == 25 &&
+ de_ctx->max_uniq_toserver_sp_groups == 26 &&
+ de_ctx->max_uniq_toserver_dp_groups == 27)
+ result = 1;
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+/** \test bug 892 bad values */
+static int DetectEngineTest09(void)
+{
+ char *conf =
+ "%YAML 1.1\n"
+ "---\n"
+ "detect-engine:\n"
+ " - profile: custom\n"
+ " - custom-values:\n"
+ " toclient-src-groups: BA\n"
+ " toclient-dst-groups: BA\n"
+ " toclient-sp-groups: BA\n"
+ " toclient-dp-groups: BA\n"
+ " toserver-src-groups: BA\n"
+ " toserver-dst-groups: BA\n"
+ " toserver-sp-groups: BA\n"
+ " toserver-dp-groups: BA\n"
+ " - inspection-recursion-limit: 10\n";
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if (DetectEngineInitYamlConf(conf) == -1)
+ return 0;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (de_ctx->max_uniq_toclient_src_groups == 4 &&
+ de_ctx->max_uniq_toclient_dst_groups == 4 &&
+ de_ctx->max_uniq_toclient_sp_groups == 4 &&
+ de_ctx->max_uniq_toclient_dp_groups == 6 &&
+ de_ctx->max_uniq_toserver_src_groups == 4 &&
+ de_ctx->max_uniq_toserver_dst_groups == 8 &&
+ de_ctx->max_uniq_toserver_sp_groups == 4 &&
+ de_ctx->max_uniq_toserver_dp_groups == 30)
+ result = 1;
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ DetectEngineDeInitYamlConf();
+
+ return result;
+}
+
+#endif
+
+void DetectEngineRegisterTests()
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectEngineTest01", DetectEngineTest01, 1);
+ UtRegisterTest("DetectEngineTest02", DetectEngineTest02, 1);
+ UtRegisterTest("DetectEngineTest03", DetectEngineTest03, 1);
+ UtRegisterTest("DetectEngineTest04", DetectEngineTest04, 1);
+ UtRegisterTest("DetectEngineTest05", DetectEngineTest05, 1);
+ UtRegisterTest("DetectEngineTest06", DetectEngineTest06, 1);
+ UtRegisterTest("DetectEngineTest07", DetectEngineTest07, 1);
+ UtRegisterTest("DetectEngineTest08", DetectEngineTest08, 1);
+ UtRegisterTest("DetectEngineTest09", DetectEngineTest09, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-engine.h b/framework/src/suricata/src/detect-engine.h
new file mode 100644
index 00000000..ce486213
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_ENGINE_H__
+#define __DETECT_ENGINE_H__
+
+#include "detect.h"
+#include "tm-threads.h"
+#include "flow-private.h"
+
+typedef struct DetectEngineAppInspectionEngine_ {
+ uint8_t ipproto;
+ AppProto alproto;
+ uint16_t dir;
+
+ int32_t sm_list;
+ uint32_t inspect_flags;
+
+ /* \retval 0 No match. Don't discontinue matching yet. We need more data.
+ * 1 Match.
+ * 2 Sig can't match.
+ * 3 Special value used by filestore sigs to indicate disabling
+ * filestore for the tx.
+ */
+ int (*Callback)(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ Signature *sig, Flow *f, uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id);
+
+ struct DetectEngineAppInspectionEngine_ *next;
+} DetectEngineAppInspectionEngine;
+
+extern DetectEngineAppInspectionEngine *app_inspection_engine[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2];
+
+/* prototypes */
+void DetectEngineRegisterAppInspectionEngines(void);
+DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix);
+DetectEngineCtx *DetectEngineCtxInit(void);
+DetectEngineCtx *DetectEngineCtxInitMinimal(void);
+void DetectEngineCtxFree(DetectEngineCtx *);
+
+TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **);
+TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *);
+//inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *);
+/* faster as a macro than a inline function on my box -- VJ */
+#define DetectEngineGetMaxSigId(de_ctx) ((de_ctx)->signum)
+void DetectEngineResetMaxSigId(DetectEngineCtx *);
+void DetectEngineRegisterTests(void);
+const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type);
+
+int DetectEngineAddToMaster(DetectEngineCtx *de_ctx);
+DetectEngineCtx *DetectEngineGetCurrent(void);
+DetectEngineCtx *DetectEngineGetByTenantId(int tenant_id);
+void DetectEnginePruneFreeList(void);
+int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx);
+DetectEngineCtx *DetectEngineReference(DetectEngineCtx *);
+void DetectEngineDeReference(DetectEngineCtx **de_ctx);
+int DetectEngineReload(const char *filename);
+int DetectEngineEnabled(void);
+int DetectEngineMTApply(void);
+int DetectEngineMultiTenantEnabled(void);
+void DetectEngineMultiTenantSetup(void);
+
+int DetectEngineReloadStart(void);
+int DetectEngineReloadIsStart(void);
+void DetectEngineReloadSetDone(void);
+int DetectEngineReloadIsDone(void);
+
+int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml);
+int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt);
+
+int DetectEngineTentantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id);
+int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id);
+int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id);
+int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id);
+
+/**
+ * \brief Registers an app inspection engine.
+ *
+ * \param alproto App layer protocol for which we will register the engine.
+ * \param direction The direction for the engine. 0 - toserver; 1- toclient.
+ * \param sm_list The SigMatch list against which the engine works.
+ * \param inspect_flags The inspection flags to be used by de_state
+ * against the engine.
+ * \param match_flags The match flags to be used by de_state in tandem with
+ * the inpsect_flags.
+ * \param Callback The engine callback.
+ */
+void DetectEngineRegisterAppInspectionEngine(uint8_t ipproto,
+ AppProto alproto,
+ uint16_t direction,
+ int32_t sm_list,
+ uint32_t inspect_flags,
+ int (*Callback)(ThreadVars *tv,
+ DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *sig, Flow *f,
+ uint8_t flags, void *alstate,
+ void *tx, uint64_t tx_id),
+ DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2]);
+#endif /* __DETECT_ENGINE_H__ */
diff --git a/framework/src/suricata/src/detect-fast-pattern.c b/framework/src/suricata/src/detect-fast-pattern.c
new file mode 100644
index 00000000..01b8f398
--- /dev/null
+++ b/framework/src/suricata/src/detect-fast-pattern.c
@@ -0,0 +1,20156 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the fast_pattern keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "flow.h"
+#include "detect-content.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-fast-pattern.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#define DETECT_FAST_PATTERN_REGEX "^(\\s*only\\s*)|\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"
+
+static pcre *parse_regex = NULL;
+static pcre_extra *parse_regex_study = NULL;
+
+static int DetectFastPatternSetup(DetectEngineCtx *, Signature *, char *);
+void DetectFastPatternRegisterTests(void);
+
+/* holds the list of sm match lists that need to be searched for a keyword
+ * that has fp support */
+SCFPSupportSMList *sm_fp_support_smlist_list = NULL;
+
+/**
+ * \brief Lets one add a sm list id to be searched for potential fp supported
+ * keywords later.
+ *
+ * \param list_id SM list id.
+ * \param priority Priority for this list.
+ */
+static void SupportFastPatternForSigMatchList(int list_id, int priority)
+{
+ if (sm_fp_support_smlist_list == NULL) {
+ SCFPSupportSMList *new = SCMalloc(sizeof(SCFPSupportSMList));
+ if (unlikely(new == NULL))
+ exit(EXIT_FAILURE);
+ memset(new, 0, sizeof(SCFPSupportSMList));
+ new->list_id = list_id;
+ new->priority = priority;
+
+ sm_fp_support_smlist_list = new;
+
+ return;
+ }
+
+ /* insertion point - ip */
+ SCFPSupportSMList *ip = NULL;
+ for (SCFPSupportSMList *tmp = sm_fp_support_smlist_list; tmp != NULL; tmp = tmp->next) {
+ if (list_id == tmp->list_id) {
+ SCLogError(SC_ERR_FATAL, "SM list already registered.");
+ exit(EXIT_FAILURE);
+ }
+
+ if (priority <= tmp->priority)
+ break;
+
+ ip = tmp;
+ }
+
+ SCFPSupportSMList *new = SCMalloc(sizeof(SCFPSupportSMList));
+ if (unlikely(new == NULL))
+ exit(EXIT_FAILURE);
+ memset(new, 0, sizeof(SCFPSupportSMList));
+ new->list_id = list_id;
+ new->priority = priority;
+ if (ip == NULL) {
+ new->next = sm_fp_support_smlist_list;
+ sm_fp_support_smlist_list = new;
+ } else {
+ new->next = ip->next;
+ ip->next = new;
+ }
+
+ for (SCFPSupportSMList *tmp = new->next; tmp != NULL; tmp = tmp->next) {
+ if (list_id == tmp->list_id) {
+ SCLogError(SC_ERR_FATAL, "SM list already registered.");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Registers the keywords(SMs) that should be given fp support.
+ */
+void SupportFastPatternForSigMatchTypes(void)
+{
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HCBDMATCH, 2);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_FILEDATA, 2);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HHDMATCH, 2);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HRHDMATCH, 2);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_UMATCH, 2);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HRUDMATCH, 2);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HHHDMATCH, 2);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HRHHDMATCH, 2);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HCDMATCH, 2);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HUADMATCH, 2);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_PMATCH, 3);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HMDMATCH, 3);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HSCDMATCH, 3);
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_HSMDMATCH, 3);
+
+ SupportFastPatternForSigMatchList(DETECT_SM_LIST_DNSQUERYNAME_MATCH, 2);
+
+#if 0
+ SCFPSupportSMList *tmp = sm_fp_support_smlist_list;
+ while (tmp != NULL) {
+ printf("%d - %d\n", tmp->list_id, tmp->priority);
+
+ tmp = tmp->next;
+ }
+#endif
+
+ return;
+}
+
+/**
+ * \brief Registration function for fast_pattern keyword
+ */
+void DetectFastPatternRegister(void)
+{
+ sigmatch_table[DETECT_FAST_PATTERN].name = "fast_pattern";
+ sigmatch_table[DETECT_FAST_PATTERN].desc = "force using preceding content in the multi pattern matcher";
+ sigmatch_table[DETECT_FAST_PATTERN].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#fast_pattern";
+ sigmatch_table[DETECT_FAST_PATTERN].Match = NULL;
+ sigmatch_table[DETECT_FAST_PATTERN].Setup = DetectFastPatternSetup;
+ sigmatch_table[DETECT_FAST_PATTERN].Free = NULL;
+ sigmatch_table[DETECT_FAST_PATTERN].RegisterTests = DetectFastPatternRegisterTests;
+
+ sigmatch_table[DETECT_FAST_PATTERN].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_FAST_PATTERN].flags |= SIGMATCH_PAYLOAD;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(DETECT_FAST_PATTERN_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", DETECT_FAST_PATTERN_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+ error:
+ /* get some way to return an error code! */
+ return;
+}
+
+//static int DetectFastPatternParseArg(
+
+/**
+ * \brief Configures the previous content context for a fast_pattern modifier
+ * keyword used in the rule.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Signature to which the current keyword belongs.
+ * \param null_str Should hold an empty string always.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char arg_substr[128] = "";
+ DetectContentData *cd = NULL;
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH] == NULL &&
+ s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH] == NULL) {
+ SCLogWarning(SC_WARN_COMPATIBILITY, "fast_pattern found inside the "
+ "rule, without a preceding content based keyword. "
+ "Currently we provide fast_pattern support for content, "
+ "uricontent, http_client_body, http_server_body, http_header, "
+ "http_raw_header, http_method, http_cookie, "
+ "http_raw_uri, http_stat_msg, http_stat_code, "
+ "http_user_agent, http_host, http_raw_host or "
+ "dns_query option");
+ return -1;
+ }
+
+ SigMatch *pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH]);
+ if (pm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern found inside "
+ "the rule, without a content context. Please use a "
+ "content based keyword before using fast_pattern");
+ return -1;
+ }
+
+ cd = (DetectContentData *)pm->ctx;
+ if ((cd->flags & DETECT_CONTENT_NEGATED) &&
+ ((cd->flags & DETECT_CONTENT_DISTANCE) ||
+ (cd->flags & DETECT_CONTENT_WITHIN) ||
+ (cd->flags & DETECT_CONTENT_OFFSET) ||
+ (cd->flags & DETECT_CONTENT_DEPTH))) {
+
+ /* we can't have any of these if we are having "only" */
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern; cannot be "
+ "used with negated content, along with relative modifiers");
+ goto error;
+ }
+
+ if (arg == NULL|| strcmp(arg, "") == 0) {
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple fast_pattern "
+ "options for the same content");
+ goto error;
+ }
+ else { /*allow only one content to have fast_pattern modifier*/
+ int list_id = 0;
+ for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+ SigMatch *sm = NULL;
+ for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ DetectContentData *tmp_cd = (DetectContentData *)sm->ctx;
+ if (tmp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern "
+ "can be used on only one content in a rule");
+ goto error;
+ }
+ }
+ } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+ }
+ }
+ cd->flags |= DETECT_CONTENT_FAST_PATTERN;
+ return 0;
+ }
+
+ /* Execute the regex and populate args with captures. */
+ ret = pcre_exec(parse_regex, parse_regex_study, arg,
+ strlen(arg), 0, 0, ov, MAX_SUBSTRINGS);
+ /* fast pattern only */
+ if (ret == 2) {
+ if ((cd->flags & DETECT_CONTENT_NEGATED) ||
+ (cd->flags & DETECT_CONTENT_DISTANCE) ||
+ (cd->flags & DETECT_CONTENT_WITHIN) ||
+ (cd->flags & DETECT_CONTENT_OFFSET) ||
+ (cd->flags & DETECT_CONTENT_DEPTH)) {
+
+ /* we can't have any of these if we are having "only" */
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern: only; cannot be "
+ "used with negated content or with any of the relative "
+ "modifiers like distance, within, offset, depth");
+ goto error;
+ }
+ cd->flags |= DETECT_CONTENT_FAST_PATTERN_ONLY;
+
+ /* fast pattern chop */
+ } else if (ret == 4) {
+ res = pcre_copy_substring((char *)arg, ov, MAX_SUBSTRINGS,
+ 2, arg_substr, sizeof(arg_substr));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed "
+ "for fast_pattern offset");
+ goto error;
+ }
+ int offset = atoi(arg_substr);
+ if (offset > 65535) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Fast pattern offset exceeds "
+ "limit");
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)arg, ov, MAX_SUBSTRINGS,
+ 3, arg_substr, sizeof(arg_substr));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed "
+ "for fast_pattern offset");
+ goto error;
+ }
+ int length = atoi(arg_substr);
+ if (length > 65535) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Fast pattern length exceeds "
+ "limit");
+ goto error;
+ }
+
+ if (offset + length > 65535) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Fast pattern (length + offset) "
+ "exceeds limit pattern length limit");
+ goto error;
+ }
+
+ if (offset + length > cd->content_len) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Fast pattern (length + "
+ "offset (%u)) exceeds pattern length (%u)",
+ offset + length, cd->content_len);
+ goto error;
+ }
+
+ cd->fp_chop_offset = offset;
+ cd->fp_chop_len = length;
+ cd->flags |= DETECT_CONTENT_FAST_PATTERN_CHOP;
+
+ } else {
+ SCLogError(SC_ERR_PCRE_PARSE, "parse error, ret %" PRId32
+ ", string %s", ret, arg);
+ goto error;
+ }
+
+ //int args;
+ //args = 0;
+ //printf("ret-%d\n", ret);
+ //for (args = 0; args < ret; args++) {
+ // res = pcre_get_substring((char *)arg, ov, MAX_SUBSTRINGS,
+ // args, &arg_substr);
+ // if (res < 0) {
+ // SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed "
+ // "for arg 1");
+ // goto error;
+ // }
+ // printf("%d-%s\n", args, arg_substr);
+ //}
+
+ cd->flags |= DETECT_CONTENT_FAST_PATTERN;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/*----------------------------------Unittests---------------------------------*/
+
+#ifdef UNITTESTS
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature
+ */
+int DetectFastPatternTest01(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; tcpv4-csum:valid; fast_pattern; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature
+ */
+int DetectFastPatternTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern; "
+ "content:\"boo\"; fast_pattern; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that we have no fast_pattern registerd for a Signature when the
+ * Signature doesn't contain a fast_pattern
+ */
+int DetectFastPatternTest03(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( !(((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN)) {
+ result = 1;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a fast_pattern is not registered in a Signature, when we
+ * supply a fast_pattern with an argument
+ */
+int DetectFastPatternTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:boo; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a fast_pattern is used in the mpm phase.
+ */
+int DetectFastPatternTest05(void)
+{
+ uint8_t *buf = (uint8_t *) "Oh strin1. But what "
+ "strin2. This is strings3. We strins_str4. we "
+ "have strins_string5";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; fast_pattern; "
+ "content:\"strings_str4\"; content:\"strings_string5\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) != 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a fast_pattern is used in the mpm phase.
+ */
+int DetectFastPatternTest06(void)
+{
+ uint8_t *buf = (uint8_t *) "Oh this is a string1. But what is this with "
+ "string2. This is strings3. We have strings_str4. We also have "
+ "strings_string5";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; fast_pattern; "
+ "content:\"strings_str4\"; content:\"strings_string5\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) != 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a fast_pattern is used in the mpm phase, when the payload
+ * doesn't contain the fast_pattern string within it.
+ */
+int DetectFastPatternTest07(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. now here comes our "
+ "dark knight strings_string5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; fast_pattern; "
+ "content:\"strings_str4\"; content:\"strings_string5\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) == 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a fast_pattern is used in the mpm phase and that we get
+ * exactly 1 match for the mpm phase.
+ */
+int DetectFastPatternTest08(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. now here comes our "
+ "dark knight strings3. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx init: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; fast_pattern; "
+ "content:\"strings_str4\"; content:\"strings_string5\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = PacketPatternSearchWithStreamCtx(det_ctx, p);
+ if (r != 1) {
+ printf("expected 1, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePackets(&p, 1);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/**
+ * \test Checks that a fast_pattern is used in the mpm phase, when the payload
+ * doesn't contain the fast_pattern string within it.
+ */
+int DetectFastPatternTest09(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. no_strings4 _imp now here "
+ "comes our dark knight strings3. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; "
+ "content:\"strings4_imp\"; fast_pattern; "
+ "content:\"strings_string5\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) == 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a the SigInit chooses the fast_pattern with better pattern
+ * strength, when we have multiple fast_patterns in the Signature. Also
+ * checks that we get a match for the fast_pattern from the mpm phase.
+ */
+int DetectFastPatternTest10(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. strings4_imp now here "
+ "comes our dark knight strings5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx init: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; "
+ "content:\"strings4_imp\"; fast_pattern; "
+ "content:\"strings_string5\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = PacketPatternSearchWithStreamCtx(det_ctx, p);
+ if (r != 1) {
+ printf("expected 1, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePackets(&p, 1);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a the SigInit chooses the fast_pattern with better pattern
+ * strength, when we have multiple fast_patterns in the Signature. Also
+ * checks that we get no matches for the fast_pattern from the mpm phase.
+ */
+int DetectFastPatternTest11(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. strings5_imp now here "
+ "comes our dark knight strings5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; "
+ "content:\"strings4_imp\"; fast_pattern; "
+ "content:\"strings_string5\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) == 0)
+ result = 1;
+
+
+end:
+ UTHFreePackets(&p, 1);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+
+/**
+ * \test Checks that we don't get a match for the mpm phase.
+ */
+int DetectFastPatternTest12(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. strings5_imp now here "
+ "comes our dark knight strings5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; "
+ "content:\"strings4_imp\"; "
+ "content:\"strings_string5\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (PacketPatternSearchWithStreamCtx(det_ctx, p) == 0)
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks that a the SigInit chooses the fast_pattern with a better
+ * strength from the available patterns, when we don't specify a
+ * fast_pattern. We also check that we get a match from the mpm
+ * phase.
+ */
+int DetectFastPatternTest13(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. strings5_imp now here "
+ "comes our dark knight strings_string5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx init: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"string1\"; "
+ "content:\"string2\"; content:\"strings3\"; "
+ "content:\"strings4_imp\"; "
+ "content:\"strings_string5\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* start the search phase */
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ uint32_t r = PacketPatternSearchWithStreamCtx(det_ctx, p);
+ if (r != 1) {
+ printf("expected 1 result, got %"PRIu32": ", r);
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePackets(&p, 1);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks to make sure that other sigs work that should when fast_pattern is inspecting on the same payload
+ *
+ */
+int DetectFastPatternTest14(void)
+{
+ uint8_t *buf = (uint8_t *) "Dummy is our name. Oh yes. From right here "
+ "right now, all the way to hangover. right. strings5_imp now here "
+ "comes our dark knight strings_string5. Yes here is our dark knight";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int alertcnt = 0;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf,buflen,IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ de_ctx->mpm_matcher = MPM_B3G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"fast_pattern test\"; content:\"strings_string5\"; content:\"knight\"; fast_pattern; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"test different content\"; content:\"Dummy is our name\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)){
+ alertcnt++;
+ }else{
+ SCLogInfo("could not match on sig 1 with when fast_pattern is inspecting payload");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)){
+ result = 1;
+ }else{
+ SCLogInfo("match on sig 1 fast_pattern no match sig 2 inspecting same payload");
+ }
+end:
+ UTHFreePackets(&p, 1);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature
+ */
+int DetectFastPatternTest15(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature
+ */
+int DetectFastPatternTest16(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest17(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest18(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest19(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:only; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; distance:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:only; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; within:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:only; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; offset:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:only; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; depth:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; content:\"two\"; distance:30; content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; within:30; content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; offset:30; content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; depth:30; content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; content:\"two\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_NEGATED &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ cd->fp_chop_offset == 0 &&
+ cd->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; fast_pattern; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; fast_pattern; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; fast_pattern; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; fast_pattern; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest37(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; content:\"oneonetwo\"; fast_pattern:3,4; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest38(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"twotwotwo\"; fast_pattern:3,4; content:\"three\"; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest39(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"twotwotwo\"; fast_pattern:3,4; content:\"three\"; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest40(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"twotwotwo\"; fast_pattern:3,4; content:\"three\"; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest41(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"twotwotwo\"; fast_pattern:3,4; content:\"three\"; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest42(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; distance:10; content:\"threethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest43(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; within:10; content:\"threethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest44(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; offset:10; content:\"threethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest45(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; depth:10; content:\"threethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest46(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:65977,4; content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest47(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"twooneone\"; fast_pattern:3,65977; content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest48(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; fast_pattern:65534,4; content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest49(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN &&
+ cd->flags & DETECT_CONTENT_NEGATED &&
+ !(cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ cd->fp_chop_offset == 3 &&
+ cd->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest50(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; distance:10; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest51(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; within:10; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest52(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; offset:10; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest53(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; depth:10; content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* content fast_pattern tests ^ */
+/* uricontent fast_pattern tests v */
+
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest54(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"/one/\"; fast_pattern:only; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest55(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"oneoneone\"; fast_pattern:3,4; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest56(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest57(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"oneoneone\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest58(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:only; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest59(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; distance:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest60(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:only; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest61(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; within:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest62(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:only; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest63(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; offset:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest64(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:only; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest65(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; depth:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest66(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest67(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent: \"one\"; uricontent:\"two\"; distance:30; uricontent:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest68(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; within:30; uricontent:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest69(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; offset:30; uricontent:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest70(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; depth:30; uricontent:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest71(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:!\"one\"; fast_pattern; uricontent:\"two\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest72(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; uricontent:!\"one\"; fast_pattern; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest73(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; uricontent:!\"one\"; fast_pattern; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest74(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; uricontent:!\"one\"; fast_pattern; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest75(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; uricontent:!\"one\"; fast_pattern; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest76(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest77(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest78(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest79(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest80(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest81(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; distance:10; uricontent:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest82(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; within:10; uricontent:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest83(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; offset:10; uricontent:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest84(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; depth:10; uricontent:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest85(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:65977,4; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest86(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"oneonetwo\"; fast_pattern:3,65977; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest87(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; fast_pattern:65534,4; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest88(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"oneonetwo\"; fast_pattern:3,4; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest89(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"oneonetwo\"; fast_pattern:3,4; distance:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest90(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"oneonetwo\"; fast_pattern:3,4; within:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest91(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"oneonetwo\"; fast_pattern:3,4; offset:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest92(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:!\"oneonetwo\"; fast_pattern:3,4; depth:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* uricontent fast_pattern tests ^ */
+/* http_uri fast_pattern tests v */
+
+
+int DetectFastPatternTest93(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest94(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_uri; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest95(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_uri; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ break;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ sm = sm->next;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest96(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest97(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (sm->type == DETECT_CONTENT) {
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest98(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:only; http_uri; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest99(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; distance:10; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest100(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:only; http_uri; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest101(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; within:10; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest102(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:only; http_uri; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest103(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; offset:10; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest104(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:only; http_uri; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest105(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; depth:10; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest106(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"two\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest107(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent: \"one\"; uricontent:\"two\"; distance:30; content:\"two\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest108(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; within:30; content:\"two\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest109(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; offset:30; content:\"two\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest110(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; depth:30; content:\"two\"; fast_pattern:only; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest111(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_uri; uricontent:\"two\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest112(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; content:!\"one\"; fast_pattern; http_uri; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest113(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; content:!\"one\"; fast_pattern; http_uri; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest114(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; content:!\"one\"; fast_pattern; http_uri; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest115(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"two\"; content:!\"one\"; fast_pattern; http_uri; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest116(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest117(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest118(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest119(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest120(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest121(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest122(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest123(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest124(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; uricontent:\"two\"; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest125(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:65977,4; http_uri; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest126(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"oneonetwo\"; fast_pattern:3,65977; http_uri; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest127(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:\"two\"; fast_pattern:65534,4; http_uri; uricontent:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest128(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest129(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; distance:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest130(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; within:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest131(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"twooneone\"; fast_pattern:3,4; http_uri; offset:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest132(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; depth:10; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest133(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; content:!\"oneonetwo\"; fast_pattern:3,4; http_uri; uricontent:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_uri fast_pattern tests ^ */
+/* http_client_body fast_pattern tests v */
+
+
+int DetectFastPatternTest134(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest135(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_client_body; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest136(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_client_body; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest137(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest138(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest139(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:only; http_client_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest140(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; distance:10; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest141(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:only; http_client_body; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest142(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; within:10; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest143(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:only; http_client_body; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest144(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; offset:10; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest145(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:only; http_client_body; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest146(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; depth:10; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest147(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"two\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest148(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; http_client_body; content:\"two\"; http_client_body; distance:30; content:\"two\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest149(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; within:30; content:\"two\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest150(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; offset:30; content:\"two\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest151(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; depth:30; content:\"two\"; fast_pattern:only; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest152(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_client_body; content:\"two\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest153(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_client_body; content:!\"one\"; fast_pattern; http_client_body; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest154(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_client_body; content:!\"one\"; fast_pattern; http_client_body; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest155(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_client_body; content:!\"one\"; fast_pattern; http_client_body; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest156(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_client_body; content:!\"one\"; fast_pattern; http_client_body; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest157(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest158(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest159(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest160(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest161(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest162(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest163(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest164(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest165(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; http_client_body; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest166(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:65977,4; http_client_body; content:\"three\"; http_client_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest167(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"oneonetwo\"; fast_pattern:3,65977; http_client_body; content:\"three\"; distance:10; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest168(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:\"two\"; fast_pattern:65534,4; http_client_body; content:\"three\"; http_client_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest169(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest170(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; distance:10; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest171(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; within:10; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest172(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"twooneone\"; fast_pattern:3,4; http_client_body; offset:10; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest173(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; depth:10; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest174(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; content:!\"oneonetwo\"; fast_pattern:3,4; http_client_body; content:\"three\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_client_body fast_pattern tests ^ */
+/* content fast_pattern tests v */
+
+
+int DetectFastPatternTest175(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; distance:20; fast_pattern; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest176(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; within:20; fast_pattern; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest177(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; offset:20; fast_pattern; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest178(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; content:!\"one\"; depth:20; fast_pattern; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/* content fast_pattern tests ^ */
+/* http_header fast_pattern tests v */
+
+
+int DetectFastPatternTest179(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_header; "
+ "content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest180(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_header; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest181(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_header; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest182(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest183(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest184(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:only; http_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest185(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; distance:10; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest186(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:only; http_header; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest187(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; within:10; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest188(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:only; http_header; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest189(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; offset:10; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest190(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:only; http_header; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest191(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; depth:10; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest192(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"two\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest193(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; http_header; content:\"two\"; http_header; distance:30; content:\"two\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest194(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; within:30; content:\"two\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest195(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; offset:30; content:\"two\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest196(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; depth:30; content:\"two\"; fast_pattern:only; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest197(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_header; content:\"two\"; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest198(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_header; content:!\"one\"; fast_pattern; http_header; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest199(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_header; content:!\"one\"; fast_pattern; http_header; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest200(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_header; content:!\"one\"; fast_pattern; http_header; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest201(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_header; content:!\"one\"; fast_pattern; http_header; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest202(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest203(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest204(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest205(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest206(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest207(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest208(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest209(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest210(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; http_header; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest211(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:65977,4; http_header; content:\"three\"; http_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest212(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"oneonetwo\"; fast_pattern:3,65977; http_header; content:\"three\"; distance:10; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest213(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:\"two\"; fast_pattern:65534,4; http_header; content:\"three\"; http_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest214(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest215(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; distance:10; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest216(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; within:10; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest217(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; offset:10; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest218(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; depth:10; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest219(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_header; content:\"three\"; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_header fast_pattern tests ^ */
+/* http_raw_header fast_pattern tests v */
+
+
+int DetectFastPatternTest220(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; "
+ "content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest221(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"/one/\"; fast_pattern:only; http_raw_header; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest222(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"oneoneone\"; fast_pattern:3,4; http_raw_header; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest223(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest224(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"oneoneone\"; fast_pattern:3,4; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest225(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:only; http_raw_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest226(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; distance:10; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest227(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:only; http_raw_header; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest228(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; within:10; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest229(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:only; http_raw_header; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest230(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; offset:10; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest231(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:only; http_raw_header; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest232(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; depth:10; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest233(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"two\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest234(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content: \"one\"; http_raw_header; content:\"two\"; http_raw_header; distance:30; content:\"two\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest235(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; within:30; content:\"two\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest236(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; offset:30; content:\"two\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest237(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; depth:30; content:\"two\"; fast_pattern:only; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest238(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:!\"one\"; fast_pattern; http_raw_header; content:\"two\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest239(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"two\"; http_raw_header; content:!\"one\"; fast_pattern; http_raw_header; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest240(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"two\"; http_raw_header; content:!\"one\"; fast_pattern; http_raw_header; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest241(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"two\"; http_raw_header; content:!\"one\"; fast_pattern; http_raw_header; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest242(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"two\"; http_raw_header; content:!\"one\"; fast_pattern; http_raw_header; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest243(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest244(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest245(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest246(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest247(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest248(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest249(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest250(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest251(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; http_raw_header; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest252(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:65977,4; http_raw_header; content:\"three\"; http_raw_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest253(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"oneonetwo\"; fast_pattern:3,65977; http_raw_header; content:\"three\"; distance:10; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest254(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:\"two\"; fast_pattern:65534,4; http_raw_header; content:\"three\"; http_raw_header; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest255(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest256(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; distance:10; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest257(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; within:10; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest258(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; offset:10; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest259(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; depth:10; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest260(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_header; content:\"three\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_raw_header fast_pattern tests ^ */
+/* http_method fast_pattern tests v */
+
+
+int DetectFastPatternTest261(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_method; "
+ "content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest262(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_method; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest263(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_method; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest264(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest265(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest266(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:only; http_method; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest267(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; distance:10; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest268(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:only; http_method; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest269(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; within:10; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest270(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:only; http_method; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest271(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; offset:10; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest272(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:only; http_method; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest273(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; depth:10; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest274(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"two\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest275(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; http_method; content:\"two\"; http_method; distance:30; content:\"two\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest276(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; within:30; content:\"two\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest277(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; offset:30; content:\"two\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest278(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; depth:30; content:\"two\"; fast_pattern:only; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest279(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_method; content:\"two\"; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest280(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_method; content:!\"one\"; fast_pattern; http_method; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest281(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_method; content:!\"one\"; fast_pattern; http_method; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest282(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_method; content:!\"one\"; fast_pattern; http_method; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest283(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_method; content:!\"one\"; fast_pattern; http_method; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest284(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest285(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest286(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest287(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest288(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest289(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest290(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest291(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest292(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; http_method; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest293(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:65977,4; http_method; content:\"three\"; http_method; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest294(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"oneonetwo\"; fast_pattern:3,65977; http_method; content:\"three\"; distance:10; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest295(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:\"two\"; fast_pattern:65534,4; http_method; content:\"three\"; http_method; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest296(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest297(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; distance:10; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest298(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; within:10; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest299(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; offset:10; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest300(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; depth:10; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest301(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_method; content:!\"oneonetwo\"; fast_pattern:3,4; http_method; content:\"three\"; http_method; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_method fast_pattern tests ^ */
+/* http_cookie fast_pattern tests v */
+
+
+int DetectFastPatternTest302(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; "
+ "content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest303(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_cookie; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest304(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_cookie; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest305(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest306(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest307(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:only; http_cookie; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest308(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; distance:10; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest309(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:only; http_cookie; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest310(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; within:10; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest311(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:only; http_cookie; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest312(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; offset:10; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest313(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:only; http_cookie; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest314(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; depth:10; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest315(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"two\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest316(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; http_cookie; content:\"two\"; http_cookie; distance:30; content:\"two\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest317(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; within:30; content:\"two\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest318(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; offset:30; content:\"two\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest319(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; depth:30; content:\"two\"; fast_pattern:only; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest320(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_cookie; content:\"two\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest321(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_cookie; content:!\"one\"; fast_pattern; http_cookie; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest322(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_cookie; content:!\"one\"; fast_pattern; http_cookie; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest323(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_cookie; content:!\"one\"; fast_pattern; http_cookie; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest324(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_cookie; content:!\"one\"; fast_pattern; http_cookie; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest325(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest326(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest327(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest328(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest329(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest330(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; distance:10; content:\"oneonethree\"; fast_pattern:3,4; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest331(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; within:10; content:\"oneonethree\"; fast_pattern:3,4; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest332(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; offset:10; content:\"oneonethree\"; fast_pattern:3,4; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest333(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; http_cookie; depth:10; content:\"oneonethree\"; fast_pattern:3,4; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest334(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:65977,4; http_cookie; content:\"three\"; http_cookie; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest335(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"oneonetwo\"; fast_pattern:3,65977; http_cookie; content:\"three\"; distance:10; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest336(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:\"two\"; fast_pattern:65534,4; http_cookie; content:\"three\"; http_cookie; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest337(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest338(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; distance:10; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest339(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; within:10; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest340(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; offset:10; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest341(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; depth:10; content:\"three2\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest342(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_cookie; content:!\"oneonetwo\"; fast_pattern:3,4; http_cookie; content:\"three\"; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_cookie fast_pattern tests ^ */
+/* http_raw_uri fast_pattern tests v */
+
+
+int DetectFastPatternTest343(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest344(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"/one/\"; fast_pattern:only; http_raw_uri; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest345(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_raw_uri; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest346(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest347(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH];
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest348(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest349(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest350(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest351(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; within:10; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest352(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest353(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest354(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest355(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest356(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"two\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest357(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content: \"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest358(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; within:30; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest359(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest360(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest361(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest362(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_uri; "
+ "content:!\"one\"; fast_pattern; http_raw_uri; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest363(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_uri; "
+ "content:!\"one\"; fast_pattern; http_raw_uri; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest364(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_uri; "
+ "content:!\"one\"; fast_pattern; http_raw_uri; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest365(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_uri; "
+ "content:!\"one\"; fast_pattern; http_raw_uri; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest366(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest367(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest368(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest369(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest370(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest371(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest372(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest373(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest374(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest375(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:65977,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest376(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_raw_uri; "
+ "content:\"three\"; distance:10; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest377(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; fast_pattern:65534,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest378(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest379(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; distance:10; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest380(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; within:10; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest381(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; offset:10; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest382(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; depth:10; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest383(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_raw_uri fast_pattern tests ^ */
+/* http_stat_msg fast_pattern tests v */
+
+
+int DetectFastPatternTest384(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest385(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_stat_msg; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest386(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_stat_msg; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest387(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest388(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest389(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest390(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest391(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest392(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; within:10; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest393(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest394(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest395(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest396(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest397(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"two\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest398(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest399(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; within:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest400(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest401(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest402(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest403(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_msg; "
+ "content:!\"one\"; fast_pattern; http_stat_msg; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest404(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_msg; "
+ "content:!\"one\"; fast_pattern; http_stat_msg; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest405(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_msg; "
+ "content:!\"one\"; fast_pattern; http_stat_msg; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest406(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_msg; "
+ "content:!\"one\"; fast_pattern; http_stat_msg; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest407(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest408(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest409(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest410(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest411(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest412(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest413(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest414(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest415(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; http_stat_msg; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest416(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:65977,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest417(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_stat_msg; "
+ "content:\"three\"; distance:10; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest418(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:\"two\"; fast_pattern:65534,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest419(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest420(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; distance:10; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest421(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; within:10; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest422(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; offset:10; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest423(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; depth:10; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest424(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_msg; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_msg; "
+ "content:\"three\"; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_stat_msg fast_pattern tests ^ */
+/* http_stat_code fast_pattern tests v */
+
+
+int DetectFastPatternTest425(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest426(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_stat_code; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest427(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_stat_code; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest428(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest429(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest430(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest431(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest432(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest433(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; within:10; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest434(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest435(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest436(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest437(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest438(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"two\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest439(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest440(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; within:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest441(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest442(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest443(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_stat_code; "
+ "content:\"two\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest444(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_code; "
+ "content:!\"one\"; fast_pattern; http_stat_code; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest445(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_code; "
+ "content:!\"one\"; fast_pattern; http_stat_code; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest446(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_code; "
+ "content:!\"one\"; fast_pattern; http_stat_code; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest447(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_stat_code; "
+ "content:!\"one\"; fast_pattern; http_stat_code; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest448(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest449(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest450(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest451(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest452(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest453(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest454(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest455(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest456(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; http_stat_code; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest457(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:65977,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest458(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_stat_code; "
+ "content:\"three\"; distance:10; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest459(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:\"two\"; fast_pattern:65534,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest460(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest461(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; distance:10; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest462(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; within:10; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest463(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; offset:10; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest464(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; depth:10; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest465(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_stat_code; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_stat_code; "
+ "content:\"three\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_stat_code fast_pattern tests ^ */
+/* http_server_body fast_pattern tests v */
+
+
+int DetectFastPatternTest466(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest467(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_server_body; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest468(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_server_body; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest469(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest470(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest471(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:only; http_server_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest472(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest473(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:only; http_server_body; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest474(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; within:10; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest475(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:only; http_server_body; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest476(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest477(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:only; http_server_body; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest478(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest479(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"two\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest480(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_server_body; "
+ "content:\"two\"; http_server_body; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest481(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; within:30; "
+ "content:\"two\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest482(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest483(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest484(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_server_body; "
+ "content:\"two\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest485(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_server_body; "
+ "content:!\"one\"; fast_pattern; http_server_body; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest486(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_server_body; "
+ "content:!\"one\"; fast_pattern; http_server_body; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest487(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_server_body; "
+ "content:!\"one\"; fast_pattern; http_server_body; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest488(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_server_body; "
+ "content:!\"one\"; fast_pattern; http_server_body; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest489(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest490(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest491(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest492(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest493(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest494(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest495(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest496(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest497(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; http_server_body; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest498(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:65977,4; http_server_body; "
+ "content:\"three\"; http_server_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest499(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_server_body; "
+ "content:\"three\"; distance:10; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest500(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; fast_pattern:65534,4; http_server_body; "
+ "content:\"three\"; http_server_body; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest501(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest502(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; distance:10; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest503(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; within:10; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest504(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; offset:10; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest505(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; depth:10; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest506(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_server_body; "
+ "content:\"three\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_server_body fast_pattern tests ^ */
+/* file_data fast_pattern tests v */
+
+
+int DetectFastPatternTest507(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest508(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; fast_pattern:only; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest509(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"oneoneone\"; fast_pattern:3,4; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest510(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest511(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"oneoneone\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest512(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:only; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest513(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; distance:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest514(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:only; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest515(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; within:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest516(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:only; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest517(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; offset:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest518(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:only; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest519(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; depth:10; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest520(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest521(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\" one\"; "
+ "content:\"two\"; distance:30; "
+ "content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest522(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; within:30; "
+ "content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest523(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; offset:30; "
+ "content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest524(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; depth:30; "
+ "content:\"two\"; fast_pattern:only; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest525(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:!\"one\"; fast_pattern; "
+ "content:\"two\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest526(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"two\"; "
+ "content:!\"one\"; fast_pattern; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest527(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"two\"; "
+ "content:!\"one\"; fast_pattern; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest528(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"two\"; "
+ "content:!\"one\"; fast_pattern; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest529(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"two\"; "
+ "content:!\"one\"; fast_pattern; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest530(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest531(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest532(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest533(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest534(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest535(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest536(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest537(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest538(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest539(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:65977,4; "
+ "content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest540(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; "
+ "content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest541(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:\"two\"; fast_pattern:65534,4; "
+ "content:\"three\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest542(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest543(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; distance:10; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest544(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; within:10; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest545(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; offset:10; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest546(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; depth:10; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest547(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(file_data; content:\"one\"; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; "
+ "content:\"three\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* file_data fast_pattern tests ^ */
+/* http_user_agent fast_pattern tests v */
+
+
+int DetectFastPatternTest548(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest549(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_user_agent; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest550(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_user_agent; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH];
+ if (sm != NULL) {
+ if ( ((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest551(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest552(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest553(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest554(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest555(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest556(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; within:10; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest557(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest558(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest559(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest560(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest561(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"two\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest562(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest563(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; within:30; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest564(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest565(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest566(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_user_agent; "
+ "content:\"two\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest567(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_user_agent; "
+ "content:!\"one\"; fast_pattern; http_user_agent; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest568(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_user_agent; "
+ "content:!\"one\"; fast_pattern; http_user_agent; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest569(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_user_agent; "
+ "content:!\"one\"; fast_pattern; http_user_agent; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest570(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_user_agent; "
+ "content:!\"one\"; fast_pattern; http_user_agent; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest571(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest572(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest573(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest574(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest575(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest576(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest577(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest578(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest579(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; http_user_agent; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest580(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:65977,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest581(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_user_agent; "
+ "content:\"three\"; distance:10; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest582(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; fast_pattern:65534,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest583(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest584(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; distance:10; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest585(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; within:10; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest586(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; offset:10; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest587(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; depth:10; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest588(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_user_agent; "
+ "content:\"three\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_user_agent fast_pattern tests ^ */
+/* http_host fast_pattern tests v */
+
+
+int DetectFastPatternTest589(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest590(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_host; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH];
+ if (sm != NULL) {
+ if ( (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN)) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest591(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_host; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH];
+ if (sm != NULL) {
+ if ( (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN)) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest592(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest593(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest594(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:only; http_host; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest595(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest596(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:only; http_host; within:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest597(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; within:10; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest598(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:only; http_host; offset:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest599(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest600(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:only; http_host; depth:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest601(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest602(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"two\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest603(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_host; "
+ "content:\"two\"; http_host; distance:30; "
+ "content:\"two\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest604(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; within:30; "
+ "content:\"two\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest605(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; offset:30; "
+ "content:\"two\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest606(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; depth:30; "
+ "content:\"two\"; fast_pattern:only; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest607(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_host; "
+ "content:\"two\"; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest608(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_host; "
+ "content:!\"one\"; fast_pattern; http_host; distance:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest609(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_host; "
+ "content:!\"one\"; fast_pattern; http_host; within:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest610(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_host; "
+ "content:!\"one\"; fast_pattern; http_host; offset:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest611(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_host; "
+ "content:!\"one\"; fast_pattern; http_host; depth:20; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest612(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest613(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; distance:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest614(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; within:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest615(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; offset:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest616(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; depth:30; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest617(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; distance:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest618(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; within:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest619(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; offset:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest620(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; http_host; depth:10; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest621(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:65977,4; http_host; "
+ "content:\"three\"; http_host; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest622(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_host; "
+ "content:\"three\"; distance:10; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest623(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; fast_pattern:65534,4; http_host; "
+ "content:\"three\"; http_host; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest624(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest625(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; distance:10; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest626(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; within:10; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest627(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; offset:10; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest628(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; depth:10; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest629(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_host; "
+ "content:\"three\"; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/* http_host fast_pattern tests ^ */
+/* http_rawhost fast_pattern tests v */
+
+
+int DetectFastPatternTest630(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest631(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_raw_host; nocase; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH];
+ if (sm != NULL) {
+ if ( (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) &&
+ (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_NOCASE)) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a fast_pattern is registered in a Signature for uricontent.
+ */
+int DetectFastPatternTest632(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "msg:\"Testing fast_pattern\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH];
+ if (sm != NULL) {
+ if ( (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN) &&
+ (((DetectContentData *)sm->ctx)->flags &
+ DETECT_CONTENT_NOCASE)) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest633(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest634(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"oneoneone\"; fast_pattern:3,4; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest635(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; distance:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest636(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; distance:10; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest637(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; within:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest638(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; within:10; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest639(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; offset:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest640(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; offset:10; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest641(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; depth:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest642(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; depth:10; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest643(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"two\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest644(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\" one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; distance:30; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest645(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; within:30; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest646(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; offset:30; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest647(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; depth:30; nocase; "
+ "content:\"two\"; fast_pattern:only; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest648(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:!\"one\"; fast_pattern; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) &&
+ ud->fp_chop_offset == 0 &&
+ ud->fp_chop_len == 0) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest649(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_host; nocase; "
+ "content:!\"one\"; fast_pattern; http_raw_host; distance:20; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest650(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_host; nocase; "
+ "content:!\"one\"; fast_pattern; http_raw_host; within:20; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest651(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_host; nocase; "
+ "content:!\"one\"; fast_pattern; http_raw_host; offset:20; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest652(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_raw_host; nocase; "
+ "content:!\"one\"; fast_pattern; http_raw_host; depth:20; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest653(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest654(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; distance:30; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest655(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; within:30; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest656(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; offset:30; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest657(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; depth:30; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest658(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; distance:10; nocase; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest659(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; within:10; nocase; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest660(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; offset:10; nocase; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest661(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; http_raw_host; depth:10; nocase; "
+ "content:\"oneonethree\"; fast_pattern:3,4; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest662(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:65977,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; distance:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest663(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"oneonetwo\"; fast_pattern:3,65977; http_raw_host; nocase; "
+ "content:\"three\"; distance:10; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest664(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:\"two\"; fast_pattern:65534,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; distance:10; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest665(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest666(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; distance:10; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest667(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; within:10; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest668(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; offset:10; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest669(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; depth:10; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectFastPatternTest670(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; "
+ "content:!\"oneonetwo\"; fast_pattern:3,4; http_raw_host; nocase; "
+ "content:\"three\"; http_raw_host; nocase; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ DetectContentData *ud = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ if (ud->flags & DETECT_CONTENT_FAST_PATTERN &&
+ ud->flags & DETECT_CONTENT_NOCASE &&
+ ud->flags & DETECT_CONTENT_NEGATED &&
+ !(ud->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) &&
+ ud->flags & DETECT_CONTENT_FAST_PATTERN_CHOP &&
+ ud->fp_chop_offset == 3 &&
+ ud->fp_chop_len == 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/**
+ * Unittest to check
+ * - if we assign different content_ids to duplicate patterns, but one of the
+ * patterns has a fast_pattern chop set.
+ * - if 2 unique patterns get unique ids.
+ * - if 2 duplicate patterns, with no chop set get unique ids.
+ */
+int DetectFastPatternTest671(void)
+{
+ int no_of_sigs = 6;
+ int result = 0;
+ char *sigs[no_of_sigs];
+ Signature *s[no_of_sigs];
+ Signature *sig = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectContentData *cd = NULL;
+ SigMatch *sm = NULL;
+ int i = 0;
+
+ sigs[0] = "alert tcp any any -> any any "
+ "(content:\"onetwothreefour\"; sid:1;)";
+ sigs[1] = "alert tcp any any -> any any "
+ "(content:\"onetwothreefour\"; sid:2;)";
+ sigs[2] = "alert tcp any any -> any any "
+ "(content:\"uniquepattern\"; sid:3;)";
+ sigs[3] = "alert tcp any any -> any any "
+ "(content:\"onetwothreefour\"; fast_pattern:3,5; sid:4;)";
+ sigs[4] = "alert tcp any any -> any any "
+ "(content:\"twoth\"; sid:5;)";
+ sigs[5] = "alert tcp any any -> any any "
+ "(content:\"onetwothreefour\"; fast_pattern:0,15; sid:6;)";
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("DetectEngineCtxInit() failure\n");
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ i = 0;
+ s[i] = SigInit(de_ctx, sigs[i]);
+ de_ctx->sig_list = sig = s[i];
+ if (sig == NULL) {
+ printf("SigInit(de_ctx, sig1) failure\n");
+ goto end;
+ }
+ i++;
+ for ( ; i < no_of_sigs; i++) {
+ s[i] = SigInit(de_ctx, sigs[i]);
+ sig->next = s[i];
+ sig = sig->next;
+ if (sig == NULL) {
+ printf("SigInit(de_ctx, sig[%d]) failure\n", i);
+ goto end;
+ }
+ }
+
+ SigGroupBuild(de_ctx);
+
+ sm = s[0]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 0) {
+ printf("sm = s[0]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ sm = s[1]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 0) {
+ printf("sm = s[1]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ sm = s[2]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 1) {
+ printf("sm = s[2]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ sm = s[3]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 2) {
+ printf("sm = s[3]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ sm = s[4]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 2) {
+ printf("sm = s[4]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ sm = s[5]->sm_lists[DETECT_SM_LIST_PMATCH];
+ cd = (DetectContentData *)sm->ctx;
+ if (cd->id != 0) {
+ printf("sm = s[5]->sm_lists[DETECT_SM_LIST_PMATCH] failure\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ return result;
+}
+
+#endif
+
+void DetectFastPatternRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFastPatternTest01", DetectFastPatternTest01, 1);
+ UtRegisterTest("DetectFastPatternTest02", DetectFastPatternTest02, 1);
+ UtRegisterTest("DetectFastPatternTest03", DetectFastPatternTest03, 1);
+ UtRegisterTest("DetectFastPatternTest04", DetectFastPatternTest04, 1);
+ UtRegisterTest("DetectFastPatternTest05", DetectFastPatternTest05, 1);
+ UtRegisterTest("DetectFastPatternTest06", DetectFastPatternTest06, 1);
+ UtRegisterTest("DetectFastPatternTest07", DetectFastPatternTest07, 1);
+ UtRegisterTest("DetectFastPatternTest08", DetectFastPatternTest08, 1);
+ UtRegisterTest("DetectFastPatternTest09", DetectFastPatternTest09, 1);
+ UtRegisterTest("DetectFastPatternTest10", DetectFastPatternTest10, 1);
+ UtRegisterTest("DetectFastPatternTest11", DetectFastPatternTest11, 1);
+ UtRegisterTest("DetectFastPatternTest12", DetectFastPatternTest12, 1);
+ UtRegisterTest("DetectFastPatternTest13", DetectFastPatternTest13, 1);
+ UtRegisterTest("DetectFastPatternTest14", DetectFastPatternTest14, 1);
+ UtRegisterTest("DetectFastPatternTest15", DetectFastPatternTest15, 1);
+ UtRegisterTest("DetectFastPatternTest16", DetectFastPatternTest16, 1);
+ UtRegisterTest("DetectFastPatternTest17", DetectFastPatternTest17, 1);
+ UtRegisterTest("DetectFastPatternTest18", DetectFastPatternTest18, 1);
+ UtRegisterTest("DetectFastPatternTest19", DetectFastPatternTest19, 1);
+ UtRegisterTest("DetectFastPatternTest20", DetectFastPatternTest20, 1);
+ UtRegisterTest("DetectFastPatternTest21", DetectFastPatternTest21, 1);
+ UtRegisterTest("DetectFastPatternTest22", DetectFastPatternTest22, 1);
+ UtRegisterTest("DetectFastPatternTest23", DetectFastPatternTest23, 1);
+ UtRegisterTest("DetectFastPatternTest24", DetectFastPatternTest24, 1);
+ UtRegisterTest("DetectFastPatternTest25", DetectFastPatternTest25, 1);
+ UtRegisterTest("DetectFastPatternTest26", DetectFastPatternTest26, 1);
+ UtRegisterTest("DetectFastPatternTest27", DetectFastPatternTest27, 1);
+ UtRegisterTest("DetectFastPatternTest28", DetectFastPatternTest28, 1);
+ UtRegisterTest("DetectFastPatternTest29", DetectFastPatternTest29, 1);
+ UtRegisterTest("DetectFastPatternTest30", DetectFastPatternTest30, 1);
+ UtRegisterTest("DetectFastPatternTest31", DetectFastPatternTest31, 1);
+ UtRegisterTest("DetectFastPatternTest32", DetectFastPatternTest32, 1);
+ UtRegisterTest("DetectFastPatternTest33", DetectFastPatternTest33, 1);
+ UtRegisterTest("DetectFastPatternTest34", DetectFastPatternTest34, 1);
+ UtRegisterTest("DetectFastPatternTest35", DetectFastPatternTest35, 1);
+ UtRegisterTest("DetectFastPatternTest36", DetectFastPatternTest36, 1);
+ UtRegisterTest("DetectFastPatternTest37", DetectFastPatternTest37, 1);
+ UtRegisterTest("DetectFastPatternTest38", DetectFastPatternTest38, 1);
+ UtRegisterTest("DetectFastPatternTest39", DetectFastPatternTest39, 1);
+ UtRegisterTest("DetectFastPatternTest40", DetectFastPatternTest40, 1);
+ UtRegisterTest("DetectFastPatternTest41", DetectFastPatternTest41, 1);
+ UtRegisterTest("DetectFastPatternTest42", DetectFastPatternTest42, 1);
+ UtRegisterTest("DetectFastPatternTest43", DetectFastPatternTest43, 1);
+ UtRegisterTest("DetectFastPatternTest44", DetectFastPatternTest44, 1);
+ UtRegisterTest("DetectFastPatternTest45", DetectFastPatternTest45, 1);
+ UtRegisterTest("DetectFastPatternTest46", DetectFastPatternTest46, 1);
+ UtRegisterTest("DetectFastPatternTest47", DetectFastPatternTest47, 1);
+ UtRegisterTest("DetectFastPatternTest48", DetectFastPatternTest48, 1);
+ UtRegisterTest("DetectFastPatternTest49", DetectFastPatternTest49, 1);
+ UtRegisterTest("DetectFastPatternTest50", DetectFastPatternTest50, 1);
+ UtRegisterTest("DetectFastPatternTest51", DetectFastPatternTest51, 1);
+ UtRegisterTest("DetectFastPatternTest52", DetectFastPatternTest52, 1);
+ UtRegisterTest("DetectFastPatternTest53", DetectFastPatternTest53, 1);
+ /* content fast_pattern tests ^ */
+ /* uricontent fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest54", DetectFastPatternTest54, 1);
+ UtRegisterTest("DetectFastPatternTest55", DetectFastPatternTest55, 1);
+ UtRegisterTest("DetectFastPatternTest56", DetectFastPatternTest56, 1);
+ UtRegisterTest("DetectFastPatternTest57", DetectFastPatternTest57, 1);
+ UtRegisterTest("DetectFastPatternTest58", DetectFastPatternTest58, 1);
+ UtRegisterTest("DetectFastPatternTest59", DetectFastPatternTest59, 1);
+ UtRegisterTest("DetectFastPatternTest60", DetectFastPatternTest60, 1);
+ UtRegisterTest("DetectFastPatternTest61", DetectFastPatternTest61, 1);
+ UtRegisterTest("DetectFastPatternTest62", DetectFastPatternTest62, 1);
+ UtRegisterTest("DetectFastPatternTest63", DetectFastPatternTest63, 1);
+ UtRegisterTest("DetectFastPatternTest64", DetectFastPatternTest64, 1);
+ UtRegisterTest("DetectFastPatternTest65", DetectFastPatternTest65, 1);
+ UtRegisterTest("DetectFastPatternTest66", DetectFastPatternTest66, 1);
+ UtRegisterTest("DetectFastPatternTest67", DetectFastPatternTest67, 1);
+ UtRegisterTest("DetectFastPatternTest68", DetectFastPatternTest68, 1);
+ UtRegisterTest("DetectFastPatternTest69", DetectFastPatternTest69, 1);
+ UtRegisterTest("DetectFastPatternTest70", DetectFastPatternTest70, 1);
+ UtRegisterTest("DetectFastPatternTest71", DetectFastPatternTest71, 1);
+ UtRegisterTest("DetectFastPatternTest72", DetectFastPatternTest72, 1);
+ UtRegisterTest("DetectFastPatternTest73", DetectFastPatternTest73, 1);
+ UtRegisterTest("DetectFastPatternTest74", DetectFastPatternTest74, 1);
+ UtRegisterTest("DetectFastPatternTest75", DetectFastPatternTest75, 1);
+ UtRegisterTest("DetectFastPatternTest76", DetectFastPatternTest76, 1);
+ UtRegisterTest("DetectFastPatternTest77", DetectFastPatternTest77, 1);
+ UtRegisterTest("DetectFastPatternTest78", DetectFastPatternTest78, 1);
+ UtRegisterTest("DetectFastPatternTest79", DetectFastPatternTest79, 1);
+ UtRegisterTest("DetectFastPatternTest80", DetectFastPatternTest80, 1);
+ UtRegisterTest("DetectFastPatternTest81", DetectFastPatternTest81, 1);
+ UtRegisterTest("DetectFastPatternTest82", DetectFastPatternTest82, 1);
+ UtRegisterTest("DetectFastPatternTest83", DetectFastPatternTest83, 1);
+ UtRegisterTest("DetectFastPatternTest84", DetectFastPatternTest84, 1);
+ UtRegisterTest("DetectFastPatternTest85", DetectFastPatternTest85, 1);
+ UtRegisterTest("DetectFastPatternTest86", DetectFastPatternTest86, 1);
+ UtRegisterTest("DetectFastPatternTest87", DetectFastPatternTest87, 1);
+ UtRegisterTest("DetectFastPatternTest88", DetectFastPatternTest88, 1);
+ UtRegisterTest("DetectFastPatternTest89", DetectFastPatternTest89, 1);
+ UtRegisterTest("DetectFastPatternTest90", DetectFastPatternTest90, 1);
+ UtRegisterTest("DetectFastPatternTest91", DetectFastPatternTest91, 1);
+ UtRegisterTest("DetectFastPatternTest92", DetectFastPatternTest92, 1);
+ /* uricontent fast_pattern tests ^ */
+ /* http_uri fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest93", DetectFastPatternTest93, 1);
+ UtRegisterTest("DetectFastPatternTest94", DetectFastPatternTest94, 1);
+ UtRegisterTest("DetectFastPatternTest95", DetectFastPatternTest95, 1);
+ UtRegisterTest("DetectFastPatternTest96", DetectFastPatternTest96, 1);
+ UtRegisterTest("DetectFastPatternTest97", DetectFastPatternTest97, 1);
+ UtRegisterTest("DetectFastPatternTest98", DetectFastPatternTest98, 1);
+ UtRegisterTest("DetectFastPatternTest99", DetectFastPatternTest99, 1);
+ UtRegisterTest("DetectFastPatternTest100", DetectFastPatternTest100, 1);
+ UtRegisterTest("DetectFastPatternTest101", DetectFastPatternTest101, 1);
+ UtRegisterTest("DetectFastPatternTest102", DetectFastPatternTest102, 1);
+ UtRegisterTest("DetectFastPatternTest103", DetectFastPatternTest103, 1);
+ UtRegisterTest("DetectFastPatternTest104", DetectFastPatternTest104, 1);
+ UtRegisterTest("DetectFastPatternTest105", DetectFastPatternTest105, 1);
+ UtRegisterTest("DetectFastPatternTest106", DetectFastPatternTest106, 1);
+ UtRegisterTest("DetectFastPatternTest107", DetectFastPatternTest107, 1);
+ UtRegisterTest("DetectFastPatternTest108", DetectFastPatternTest108, 1);
+ UtRegisterTest("DetectFastPatternTest109", DetectFastPatternTest109, 1);
+ UtRegisterTest("DetectFastPatternTest110", DetectFastPatternTest110, 1);
+ UtRegisterTest("DetectFastPatternTest111", DetectFastPatternTest111, 1);
+ UtRegisterTest("DetectFastPatternTest112", DetectFastPatternTest112, 1);
+ UtRegisterTest("DetectFastPatternTest113", DetectFastPatternTest113, 1);
+ UtRegisterTest("DetectFastPatternTest114", DetectFastPatternTest114, 1);
+ UtRegisterTest("DetectFastPatternTest115", DetectFastPatternTest115, 1);
+ UtRegisterTest("DetectFastPatternTest116", DetectFastPatternTest116, 1);
+ UtRegisterTest("DetectFastPatternTest117", DetectFastPatternTest117, 1);
+ UtRegisterTest("DetectFastPatternTest118", DetectFastPatternTest118, 1);
+ UtRegisterTest("DetectFastPatternTest119", DetectFastPatternTest119, 1);
+ UtRegisterTest("DetectFastPatternTest120", DetectFastPatternTest120, 1);
+ UtRegisterTest("DetectFastPatternTest121", DetectFastPatternTest121, 1);
+ UtRegisterTest("DetectFastPatternTest122", DetectFastPatternTest122, 1);
+ UtRegisterTest("DetectFastPatternTest123", DetectFastPatternTest123, 1);
+ UtRegisterTest("DetectFastPatternTest124", DetectFastPatternTest124, 1);
+ UtRegisterTest("DetectFastPatternTest125", DetectFastPatternTest125, 1);
+ UtRegisterTest("DetectFastPatternTest126", DetectFastPatternTest126, 1);
+ UtRegisterTest("DetectFastPatternTest127", DetectFastPatternTest127, 1);
+ UtRegisterTest("DetectFastPatternTest128", DetectFastPatternTest128, 1);
+ UtRegisterTest("DetectFastPatternTest129", DetectFastPatternTest129, 1);
+ UtRegisterTest("DetectFastPatternTest130", DetectFastPatternTest130, 1);
+ UtRegisterTest("DetectFastPatternTest131", DetectFastPatternTest131, 1);
+ UtRegisterTest("DetectFastPatternTest132", DetectFastPatternTest132, 1);
+ UtRegisterTest("DetectFastPatternTest133", DetectFastPatternTest133, 1);
+ /* http_uri fast_pattern tests ^ */
+ /* http_client_body fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest134", DetectFastPatternTest134, 1);
+ UtRegisterTest("DetectFastPatternTest135", DetectFastPatternTest135, 1);
+ UtRegisterTest("DetectFastPatternTest136", DetectFastPatternTest136, 1);
+ UtRegisterTest("DetectFastPatternTest137", DetectFastPatternTest137, 1);
+ UtRegisterTest("DetectFastPatternTest138", DetectFastPatternTest138, 1);
+ UtRegisterTest("DetectFastPatternTest139", DetectFastPatternTest139, 1);
+ UtRegisterTest("DetectFastPatternTest140", DetectFastPatternTest140, 1);
+ UtRegisterTest("DetectFastPatternTest141", DetectFastPatternTest141, 1);
+ UtRegisterTest("DetectFastPatternTest142", DetectFastPatternTest142, 1);
+ UtRegisterTest("DetectFastPatternTest143", DetectFastPatternTest143, 1);
+ UtRegisterTest("DetectFastPatternTest144", DetectFastPatternTest144, 1);
+ UtRegisterTest("DetectFastPatternTest145", DetectFastPatternTest145, 1);
+ UtRegisterTest("DetectFastPatternTest146", DetectFastPatternTest146, 1);
+ UtRegisterTest("DetectFastPatternTest147", DetectFastPatternTest147, 1);
+ UtRegisterTest("DetectFastPatternTest148", DetectFastPatternTest148, 1);
+ UtRegisterTest("DetectFastPatternTest149", DetectFastPatternTest149, 1);
+ UtRegisterTest("DetectFastPatternTest150", DetectFastPatternTest150, 1);
+ UtRegisterTest("DetectFastPatternTest151", DetectFastPatternTest151, 1);
+ UtRegisterTest("DetectFastPatternTest152", DetectFastPatternTest152, 1);
+ UtRegisterTest("DetectFastPatternTest153", DetectFastPatternTest153, 1);
+ UtRegisterTest("DetectFastPatternTest154", DetectFastPatternTest154, 1);
+ UtRegisterTest("DetectFastPatternTest155", DetectFastPatternTest155, 1);
+ UtRegisterTest("DetectFastPatternTest156", DetectFastPatternTest156, 1);
+ UtRegisterTest("DetectFastPatternTest157", DetectFastPatternTest157, 1);
+ UtRegisterTest("DetectFastPatternTest158", DetectFastPatternTest158, 1);
+ UtRegisterTest("DetectFastPatternTest159", DetectFastPatternTest159, 1);
+ UtRegisterTest("DetectFastPatternTest160", DetectFastPatternTest160, 1);
+ UtRegisterTest("DetectFastPatternTest161", DetectFastPatternTest161, 1);
+ UtRegisterTest("DetectFastPatternTest162", DetectFastPatternTest162, 1);
+ UtRegisterTest("DetectFastPatternTest163", DetectFastPatternTest163, 1);
+ UtRegisterTest("DetectFastPatternTest164", DetectFastPatternTest164, 1);
+ UtRegisterTest("DetectFastPatternTest165", DetectFastPatternTest165, 1);
+ UtRegisterTest("DetectFastPatternTest166", DetectFastPatternTest166, 1);
+ UtRegisterTest("DetectFastPatternTest167", DetectFastPatternTest167, 1);
+ UtRegisterTest("DetectFastPatternTest168", DetectFastPatternTest168, 1);
+ UtRegisterTest("DetectFastPatternTest169", DetectFastPatternTest169, 1);
+ UtRegisterTest("DetectFastPatternTest170", DetectFastPatternTest170, 1);
+ UtRegisterTest("DetectFastPatternTest171", DetectFastPatternTest171, 1);
+ UtRegisterTest("DetectFastPatternTest172", DetectFastPatternTest172, 1);
+ UtRegisterTest("DetectFastPatternTest173", DetectFastPatternTest173, 1);
+ UtRegisterTest("DetectFastPatternTest174", DetectFastPatternTest174, 1);
+ /* http_client_body fast_pattern tests ^ */
+ /* content fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest175", DetectFastPatternTest175, 1);
+ UtRegisterTest("DetectFastPatternTest176", DetectFastPatternTest176, 1);
+ UtRegisterTest("DetectFastPatternTest177", DetectFastPatternTest177, 1);
+ UtRegisterTest("DetectFastPatternTest178", DetectFastPatternTest178, 1);
+ /* content fast_pattern tests ^ */
+ /* http_header fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest179", DetectFastPatternTest179, 1);
+ UtRegisterTest("DetectFastPatternTest180", DetectFastPatternTest180, 1);
+ UtRegisterTest("DetectFastPatternTest181", DetectFastPatternTest181, 1);
+ UtRegisterTest("DetectFastPatternTest182", DetectFastPatternTest182, 1);
+ UtRegisterTest("DetectFastPatternTest183", DetectFastPatternTest183, 1);
+ UtRegisterTest("DetectFastPatternTest184", DetectFastPatternTest184, 1);
+ UtRegisterTest("DetectFastPatternTest185", DetectFastPatternTest185, 1);
+ UtRegisterTest("DetectFastPatternTest186", DetectFastPatternTest186, 1);
+ UtRegisterTest("DetectFastPatternTest187", DetectFastPatternTest187, 1);
+ UtRegisterTest("DetectFastPatternTest188", DetectFastPatternTest188, 1);
+ UtRegisterTest("DetectFastPatternTest189", DetectFastPatternTest189, 1);
+ UtRegisterTest("DetectFastPatternTest190", DetectFastPatternTest190, 1);
+ UtRegisterTest("DetectFastPatternTest191", DetectFastPatternTest191, 1);
+ UtRegisterTest("DetectFastPatternTest192", DetectFastPatternTest192, 1);
+ UtRegisterTest("DetectFastPatternTest193", DetectFastPatternTest193, 1);
+ UtRegisterTest("DetectFastPatternTest194", DetectFastPatternTest194, 1);
+ UtRegisterTest("DetectFastPatternTest195", DetectFastPatternTest195, 1);
+ UtRegisterTest("DetectFastPatternTest196", DetectFastPatternTest196, 1);
+ UtRegisterTest("DetectFastPatternTest197", DetectFastPatternTest197, 1);
+ UtRegisterTest("DetectFastPatternTest198", DetectFastPatternTest198, 1);
+ UtRegisterTest("DetectFastPatternTest199", DetectFastPatternTest199, 1);
+ UtRegisterTest("DetectFastPatternTest200", DetectFastPatternTest200, 1);
+ UtRegisterTest("DetectFastPatternTest201", DetectFastPatternTest201, 1);
+ UtRegisterTest("DetectFastPatternTest202", DetectFastPatternTest202, 1);
+ UtRegisterTest("DetectFastPatternTest203", DetectFastPatternTest203, 1);
+ UtRegisterTest("DetectFastPatternTest204", DetectFastPatternTest204, 1);
+ UtRegisterTest("DetectFastPatternTest205", DetectFastPatternTest205, 1);
+ UtRegisterTest("DetectFastPatternTest206", DetectFastPatternTest206, 1);
+ UtRegisterTest("DetectFastPatternTest207", DetectFastPatternTest207, 1);
+ UtRegisterTest("DetectFastPatternTest208", DetectFastPatternTest208, 1);
+ UtRegisterTest("DetectFastPatternTest209", DetectFastPatternTest209, 1);
+ UtRegisterTest("DetectFastPatternTest210", DetectFastPatternTest210, 1);
+ UtRegisterTest("DetectFastPatternTest211", DetectFastPatternTest211, 1);
+ UtRegisterTest("DetectFastPatternTest212", DetectFastPatternTest212, 1);
+ UtRegisterTest("DetectFastPatternTest213", DetectFastPatternTest213, 1);
+ UtRegisterTest("DetectFastPatternTest214", DetectFastPatternTest214, 1);
+ UtRegisterTest("DetectFastPatternTest215", DetectFastPatternTest215, 1);
+ UtRegisterTest("DetectFastPatternTest216", DetectFastPatternTest216, 1);
+ UtRegisterTest("DetectFastPatternTest217", DetectFastPatternTest217, 1);
+ UtRegisterTest("DetectFastPatternTest218", DetectFastPatternTest218, 1);
+ UtRegisterTest("DetectFastPatternTest219", DetectFastPatternTest219, 1);
+ /* http_header fast_pattern tests ^ */
+ /* http_raw_header fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest220", DetectFastPatternTest220, 1);
+ UtRegisterTest("DetectFastPatternTest221", DetectFastPatternTest221, 1);
+ UtRegisterTest("DetectFastPatternTest222", DetectFastPatternTest222, 1);
+ UtRegisterTest("DetectFastPatternTest223", DetectFastPatternTest223, 1);
+ UtRegisterTest("DetectFastPatternTest224", DetectFastPatternTest224, 1);
+ UtRegisterTest("DetectFastPatternTest225", DetectFastPatternTest225, 1);
+ UtRegisterTest("DetectFastPatternTest226", DetectFastPatternTest226, 1);
+ UtRegisterTest("DetectFastPatternTest227", DetectFastPatternTest227, 1);
+ UtRegisterTest("DetectFastPatternTest228", DetectFastPatternTest228, 1);
+ UtRegisterTest("DetectFastPatternTest229", DetectFastPatternTest229, 1);
+ UtRegisterTest("DetectFastPatternTest230", DetectFastPatternTest230, 1);
+ UtRegisterTest("DetectFastPatternTest231", DetectFastPatternTest231, 1);
+ UtRegisterTest("DetectFastPatternTest232", DetectFastPatternTest232, 1);
+ UtRegisterTest("DetectFastPatternTest233", DetectFastPatternTest233, 1);
+ UtRegisterTest("DetectFastPatternTest234", DetectFastPatternTest234, 1);
+ UtRegisterTest("DetectFastPatternTest235", DetectFastPatternTest235, 1);
+ UtRegisterTest("DetectFastPatternTest236", DetectFastPatternTest236, 1);
+ UtRegisterTest("DetectFastPatternTest237", DetectFastPatternTest237, 1);
+ UtRegisterTest("DetectFastPatternTest238", DetectFastPatternTest238, 1);
+ UtRegisterTest("DetectFastPatternTest239", DetectFastPatternTest239, 1);
+ UtRegisterTest("DetectFastPatternTest240", DetectFastPatternTest240, 1);
+ UtRegisterTest("DetectFastPatternTest241", DetectFastPatternTest241, 1);
+ UtRegisterTest("DetectFastPatternTest242", DetectFastPatternTest242, 1);
+ UtRegisterTest("DetectFastPatternTest243", DetectFastPatternTest243, 1);
+ UtRegisterTest("DetectFastPatternTest244", DetectFastPatternTest244, 1);
+ UtRegisterTest("DetectFastPatternTest245", DetectFastPatternTest245, 1);
+ UtRegisterTest("DetectFastPatternTest246", DetectFastPatternTest246, 1);
+ UtRegisterTest("DetectFastPatternTest247", DetectFastPatternTest247, 1);
+ UtRegisterTest("DetectFastPatternTest248", DetectFastPatternTest248, 1);
+ UtRegisterTest("DetectFastPatternTest249", DetectFastPatternTest249, 1);
+ UtRegisterTest("DetectFastPatternTest250", DetectFastPatternTest250, 1);
+ UtRegisterTest("DetectFastPatternTest251", DetectFastPatternTest251, 1);
+ UtRegisterTest("DetectFastPatternTest252", DetectFastPatternTest252, 1);
+ UtRegisterTest("DetectFastPatternTest253", DetectFastPatternTest253, 1);
+ UtRegisterTest("DetectFastPatternTest254", DetectFastPatternTest254, 1);
+ UtRegisterTest("DetectFastPatternTest255", DetectFastPatternTest255, 1);
+ UtRegisterTest("DetectFastPatternTest256", DetectFastPatternTest256, 1);
+ UtRegisterTest("DetectFastPatternTest257", DetectFastPatternTest257, 1);
+ UtRegisterTest("DetectFastPatternTest258", DetectFastPatternTest258, 1);
+ UtRegisterTest("DetectFastPatternTest259", DetectFastPatternTest259, 1);
+ UtRegisterTest("DetectFastPatternTest260", DetectFastPatternTest260, 1);
+ /* http_raw_header fast_pattern tests ^ */
+ /* http_method fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest261", DetectFastPatternTest261, 1);
+ UtRegisterTest("DetectFastPatternTest262", DetectFastPatternTest262, 1);
+ UtRegisterTest("DetectFastPatternTest263", DetectFastPatternTest263, 1);
+ UtRegisterTest("DetectFastPatternTest264", DetectFastPatternTest264, 1);
+ UtRegisterTest("DetectFastPatternTest265", DetectFastPatternTest265, 1);
+ UtRegisterTest("DetectFastPatternTest266", DetectFastPatternTest266, 1);
+ UtRegisterTest("DetectFastPatternTest267", DetectFastPatternTest267, 1);
+ UtRegisterTest("DetectFastPatternTest268", DetectFastPatternTest268, 1);
+ UtRegisterTest("DetectFastPatternTest269", DetectFastPatternTest269, 1);
+ UtRegisterTest("DetectFastPatternTest270", DetectFastPatternTest270, 1);
+ UtRegisterTest("DetectFastPatternTest271", DetectFastPatternTest271, 1);
+ UtRegisterTest("DetectFastPatternTest272", DetectFastPatternTest272, 1);
+ UtRegisterTest("DetectFastPatternTest273", DetectFastPatternTest273, 1);
+ UtRegisterTest("DetectFastPatternTest274", DetectFastPatternTest274, 1);
+ UtRegisterTest("DetectFastPatternTest275", DetectFastPatternTest275, 1);
+ UtRegisterTest("DetectFastPatternTest276", DetectFastPatternTest276, 1);
+ UtRegisterTest("DetectFastPatternTest277", DetectFastPatternTest277, 1);
+ UtRegisterTest("DetectFastPatternTest278", DetectFastPatternTest278, 1);
+ UtRegisterTest("DetectFastPatternTest279", DetectFastPatternTest279, 1);
+ UtRegisterTest("DetectFastPatternTest280", DetectFastPatternTest280, 1);
+ UtRegisterTest("DetectFastPatternTest281", DetectFastPatternTest281, 1);
+ UtRegisterTest("DetectFastPatternTest282", DetectFastPatternTest282, 1);
+ UtRegisterTest("DetectFastPatternTest283", DetectFastPatternTest283, 1);
+ UtRegisterTest("DetectFastPatternTest284", DetectFastPatternTest284, 1);
+ UtRegisterTest("DetectFastPatternTest285", DetectFastPatternTest285, 1);
+ UtRegisterTest("DetectFastPatternTest286", DetectFastPatternTest286, 1);
+ UtRegisterTest("DetectFastPatternTest287", DetectFastPatternTest287, 1);
+ UtRegisterTest("DetectFastPatternTest288", DetectFastPatternTest288, 1);
+ UtRegisterTest("DetectFastPatternTest289", DetectFastPatternTest289, 1);
+ UtRegisterTest("DetectFastPatternTest290", DetectFastPatternTest290, 1);
+ UtRegisterTest("DetectFastPatternTest291", DetectFastPatternTest291, 1);
+ UtRegisterTest("DetectFastPatternTest292", DetectFastPatternTest292, 1);
+ UtRegisterTest("DetectFastPatternTest293", DetectFastPatternTest293, 1);
+ UtRegisterTest("DetectFastPatternTest294", DetectFastPatternTest294, 1);
+ UtRegisterTest("DetectFastPatternTest295", DetectFastPatternTest295, 1);
+ UtRegisterTest("DetectFastPatternTest296", DetectFastPatternTest296, 1);
+ UtRegisterTest("DetectFastPatternTest297", DetectFastPatternTest297, 1);
+ UtRegisterTest("DetectFastPatternTest298", DetectFastPatternTest298, 1);
+ UtRegisterTest("DetectFastPatternTest299", DetectFastPatternTest299, 1);
+ UtRegisterTest("DetectFastPatternTest300", DetectFastPatternTest300, 1);
+ UtRegisterTest("DetectFastPatternTest301", DetectFastPatternTest301, 1);
+ /* http_method fast_pattern tests ^ */
+ /* http_cookie fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest302", DetectFastPatternTest302, 1);
+ UtRegisterTest("DetectFastPatternTest303", DetectFastPatternTest303, 1);
+ UtRegisterTest("DetectFastPatternTest304", DetectFastPatternTest304, 1);
+ UtRegisterTest("DetectFastPatternTest305", DetectFastPatternTest305, 1);
+ UtRegisterTest("DetectFastPatternTest306", DetectFastPatternTest306, 1);
+ UtRegisterTest("DetectFastPatternTest307", DetectFastPatternTest307, 1);
+ UtRegisterTest("DetectFastPatternTest308", DetectFastPatternTest308, 1);
+ UtRegisterTest("DetectFastPatternTest309", DetectFastPatternTest309, 1);
+ UtRegisterTest("DetectFastPatternTest310", DetectFastPatternTest310, 1);
+ UtRegisterTest("DetectFastPatternTest311", DetectFastPatternTest311, 1);
+ UtRegisterTest("DetectFastPatternTest312", DetectFastPatternTest312, 1);
+ UtRegisterTest("DetectFastPatternTest313", DetectFastPatternTest313, 1);
+ UtRegisterTest("DetectFastPatternTest314", DetectFastPatternTest314, 1);
+ UtRegisterTest("DetectFastPatternTest315", DetectFastPatternTest315, 1);
+ UtRegisterTest("DetectFastPatternTest316", DetectFastPatternTest316, 1);
+ UtRegisterTest("DetectFastPatternTest317", DetectFastPatternTest317, 1);
+ UtRegisterTest("DetectFastPatternTest318", DetectFastPatternTest318, 1);
+ UtRegisterTest("DetectFastPatternTest319", DetectFastPatternTest319, 1);
+ UtRegisterTest("DetectFastPatternTest320", DetectFastPatternTest320, 1);
+ UtRegisterTest("DetectFastPatternTest321", DetectFastPatternTest321, 1);
+ UtRegisterTest("DetectFastPatternTest322", DetectFastPatternTest322, 1);
+ UtRegisterTest("DetectFastPatternTest323", DetectFastPatternTest323, 1);
+ UtRegisterTest("DetectFastPatternTest324", DetectFastPatternTest324, 1);
+ UtRegisterTest("DetectFastPatternTest325", DetectFastPatternTest325, 1);
+ UtRegisterTest("DetectFastPatternTest326", DetectFastPatternTest326, 1);
+ UtRegisterTest("DetectFastPatternTest327", DetectFastPatternTest327, 1);
+ UtRegisterTest("DetectFastPatternTest328", DetectFastPatternTest328, 1);
+ UtRegisterTest("DetectFastPatternTest329", DetectFastPatternTest329, 1);
+ UtRegisterTest("DetectFastPatternTest330", DetectFastPatternTest330, 1);
+ UtRegisterTest("DetectFastPatternTest331", DetectFastPatternTest331, 1);
+ UtRegisterTest("DetectFastPatternTest332", DetectFastPatternTest332, 1);
+ UtRegisterTest("DetectFastPatternTest333", DetectFastPatternTest333, 1);
+ UtRegisterTest("DetectFastPatternTest334", DetectFastPatternTest334, 1);
+ UtRegisterTest("DetectFastPatternTest335", DetectFastPatternTest335, 1);
+ UtRegisterTest("DetectFastPatternTest336", DetectFastPatternTest336, 1);
+ UtRegisterTest("DetectFastPatternTest337", DetectFastPatternTest337, 1);
+ UtRegisterTest("DetectFastPatternTest338", DetectFastPatternTest338, 1);
+ UtRegisterTest("DetectFastPatternTest339", DetectFastPatternTest339, 1);
+ UtRegisterTest("DetectFastPatternTest340", DetectFastPatternTest340, 1);
+ UtRegisterTest("DetectFastPatternTest341", DetectFastPatternTest341, 1);
+ UtRegisterTest("DetectFastPatternTest342", DetectFastPatternTest342, 1);
+ /* http_cookie fast_pattern tests ^ */
+ /* http_raw_uri fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest343", DetectFastPatternTest343, 1);
+ UtRegisterTest("DetectFastPatternTest344", DetectFastPatternTest344, 1);
+ UtRegisterTest("DetectFastPatternTest345", DetectFastPatternTest345, 1);
+ UtRegisterTest("DetectFastPatternTest346", DetectFastPatternTest346, 1);
+ UtRegisterTest("DetectFastPatternTest347", DetectFastPatternTest347, 1);
+ UtRegisterTest("DetectFastPatternTest348", DetectFastPatternTest348, 1);
+ UtRegisterTest("DetectFastPatternTest349", DetectFastPatternTest349, 1);
+ UtRegisterTest("DetectFastPatternTest350", DetectFastPatternTest350, 1);
+ UtRegisterTest("DetectFastPatternTest351", DetectFastPatternTest351, 1);
+ UtRegisterTest("DetectFastPatternTest352", DetectFastPatternTest352, 1);
+ UtRegisterTest("DetectFastPatternTest353", DetectFastPatternTest353, 1);
+ UtRegisterTest("DetectFastPatternTest354", DetectFastPatternTest354, 1);
+ UtRegisterTest("DetectFastPatternTest355", DetectFastPatternTest355, 1);
+ UtRegisterTest("DetectFastPatternTest356", DetectFastPatternTest356, 1);
+ UtRegisterTest("DetectFastPatternTest357", DetectFastPatternTest357, 1);
+ UtRegisterTest("DetectFastPatternTest358", DetectFastPatternTest358, 1);
+ UtRegisterTest("DetectFastPatternTest359", DetectFastPatternTest359, 1);
+ UtRegisterTest("DetectFastPatternTest360", DetectFastPatternTest360, 1);
+ UtRegisterTest("DetectFastPatternTest361", DetectFastPatternTest361, 1);
+ UtRegisterTest("DetectFastPatternTest362", DetectFastPatternTest362, 1);
+ UtRegisterTest("DetectFastPatternTest363", DetectFastPatternTest363, 1);
+ UtRegisterTest("DetectFastPatternTest364", DetectFastPatternTest364, 1);
+ UtRegisterTest("DetectFastPatternTest365", DetectFastPatternTest365, 1);
+ UtRegisterTest("DetectFastPatternTest366", DetectFastPatternTest366, 1);
+ UtRegisterTest("DetectFastPatternTest367", DetectFastPatternTest367, 1);
+ UtRegisterTest("DetectFastPatternTest368", DetectFastPatternTest368, 1);
+ UtRegisterTest("DetectFastPatternTest369", DetectFastPatternTest369, 1);
+ UtRegisterTest("DetectFastPatternTest370", DetectFastPatternTest370, 1);
+ UtRegisterTest("DetectFastPatternTest371", DetectFastPatternTest371, 1);
+ UtRegisterTest("DetectFastPatternTest372", DetectFastPatternTest372, 1);
+ UtRegisterTest("DetectFastPatternTest373", DetectFastPatternTest373, 1);
+ UtRegisterTest("DetectFastPatternTest374", DetectFastPatternTest374, 1);
+ UtRegisterTest("DetectFastPatternTest375", DetectFastPatternTest375, 1);
+ UtRegisterTest("DetectFastPatternTest376", DetectFastPatternTest376, 1);
+ UtRegisterTest("DetectFastPatternTest377", DetectFastPatternTest377, 1);
+ UtRegisterTest("DetectFastPatternTest378", DetectFastPatternTest378, 1);
+ UtRegisterTest("DetectFastPatternTest379", DetectFastPatternTest379, 1);
+ UtRegisterTest("DetectFastPatternTest380", DetectFastPatternTest380, 1);
+ UtRegisterTest("DetectFastPatternTest381", DetectFastPatternTest381, 1);
+ UtRegisterTest("DetectFastPatternTest382", DetectFastPatternTest382, 1);
+ UtRegisterTest("DetectFastPatternTest383", DetectFastPatternTest383, 1);
+ /* http_raw_uri fast_pattern tests ^ */
+ /* http_stat_msg fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest384", DetectFastPatternTest384, 1);
+ UtRegisterTest("DetectFastPatternTest385", DetectFastPatternTest385, 1);
+ UtRegisterTest("DetectFastPatternTest386", DetectFastPatternTest386, 1);
+ UtRegisterTest("DetectFastPatternTest387", DetectFastPatternTest387, 1);
+ UtRegisterTest("DetectFastPatternTest388", DetectFastPatternTest388, 1);
+ UtRegisterTest("DetectFastPatternTest389", DetectFastPatternTest389, 1);
+ UtRegisterTest("DetectFastPatternTest390", DetectFastPatternTest390, 1);
+ UtRegisterTest("DetectFastPatternTest391", DetectFastPatternTest391, 1);
+ UtRegisterTest("DetectFastPatternTest392", DetectFastPatternTest392, 1);
+ UtRegisterTest("DetectFastPatternTest393", DetectFastPatternTest393, 1);
+ UtRegisterTest("DetectFastPatternTest394", DetectFastPatternTest394, 1);
+ UtRegisterTest("DetectFastPatternTest395", DetectFastPatternTest395, 1);
+ UtRegisterTest("DetectFastPatternTest396", DetectFastPatternTest396, 1);
+ UtRegisterTest("DetectFastPatternTest397", DetectFastPatternTest397, 1);
+ UtRegisterTest("DetectFastPatternTest398", DetectFastPatternTest398, 1);
+ UtRegisterTest("DetectFastPatternTest399", DetectFastPatternTest399, 1);
+ UtRegisterTest("DetectFastPatternTest400", DetectFastPatternTest400, 1);
+ UtRegisterTest("DetectFastPatternTest401", DetectFastPatternTest401, 1);
+ UtRegisterTest("DetectFastPatternTest402", DetectFastPatternTest402, 1);
+ UtRegisterTest("DetectFastPatternTest403", DetectFastPatternTest403, 1);
+ UtRegisterTest("DetectFastPatternTest404", DetectFastPatternTest404, 1);
+ UtRegisterTest("DetectFastPatternTest405", DetectFastPatternTest405, 1);
+ UtRegisterTest("DetectFastPatternTest406", DetectFastPatternTest406, 1);
+ UtRegisterTest("DetectFastPatternTest407", DetectFastPatternTest407, 1);
+ UtRegisterTest("DetectFastPatternTest408", DetectFastPatternTest408, 1);
+ UtRegisterTest("DetectFastPatternTest409", DetectFastPatternTest409, 1);
+ UtRegisterTest("DetectFastPatternTest410", DetectFastPatternTest410, 1);
+ UtRegisterTest("DetectFastPatternTest411", DetectFastPatternTest411, 1);
+ UtRegisterTest("DetectFastPatternTest412", DetectFastPatternTest412, 1);
+ UtRegisterTest("DetectFastPatternTest413", DetectFastPatternTest413, 1);
+ UtRegisterTest("DetectFastPatternTest414", DetectFastPatternTest414, 1);
+ UtRegisterTest("DetectFastPatternTest415", DetectFastPatternTest415, 1);
+ UtRegisterTest("DetectFastPatternTest416", DetectFastPatternTest415, 1);
+ UtRegisterTest("DetectFastPatternTest417", DetectFastPatternTest417, 1);
+ UtRegisterTest("DetectFastPatternTest418", DetectFastPatternTest418, 1);
+ UtRegisterTest("DetectFastPatternTest419", DetectFastPatternTest419, 1);
+ UtRegisterTest("DetectFastPatternTest420", DetectFastPatternTest420, 1);
+ UtRegisterTest("DetectFastPatternTest421", DetectFastPatternTest421, 1);
+ UtRegisterTest("DetectFastPatternTest422", DetectFastPatternTest422, 1);
+ UtRegisterTest("DetectFastPatternTest423", DetectFastPatternTest423, 1);
+ UtRegisterTest("DetectFastPatternTest424", DetectFastPatternTest424, 1);
+ /* http_stat_msg fast_pattern tests ^ */
+ /* http_stat_code fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest425", DetectFastPatternTest425, 1);
+ UtRegisterTest("DetectFastPatternTest426", DetectFastPatternTest426, 1);
+ UtRegisterTest("DetectFastPatternTest427", DetectFastPatternTest427, 1);
+ UtRegisterTest("DetectFastPatternTest428", DetectFastPatternTest428, 1);
+ UtRegisterTest("DetectFastPatternTest429", DetectFastPatternTest429, 1);
+ UtRegisterTest("DetectFastPatternTest430", DetectFastPatternTest430, 1);
+ UtRegisterTest("DetectFastPatternTest431", DetectFastPatternTest431, 1);
+ UtRegisterTest("DetectFastPatternTest432", DetectFastPatternTest432, 1);
+ UtRegisterTest("DetectFastPatternTest433", DetectFastPatternTest433, 1);
+ UtRegisterTest("DetectFastPatternTest434", DetectFastPatternTest434, 1);
+ UtRegisterTest("DetectFastPatternTest435", DetectFastPatternTest435, 1);
+ UtRegisterTest("DetectFastPatternTest436", DetectFastPatternTest436, 1);
+ UtRegisterTest("DetectFastPatternTest437", DetectFastPatternTest437, 1);
+ UtRegisterTest("DetectFastPatternTest438", DetectFastPatternTest438, 1);
+ UtRegisterTest("DetectFastPatternTest439", DetectFastPatternTest439, 1);
+ UtRegisterTest("DetectFastPatternTest440", DetectFastPatternTest440, 1);
+ UtRegisterTest("DetectFastPatternTest441", DetectFastPatternTest441, 1);
+ UtRegisterTest("DetectFastPatternTest442", DetectFastPatternTest442, 1);
+ UtRegisterTest("DetectFastPatternTest443", DetectFastPatternTest443, 1);
+ UtRegisterTest("DetectFastPatternTest444", DetectFastPatternTest444, 1);
+ UtRegisterTest("DetectFastPatternTest445", DetectFastPatternTest445, 1);
+ UtRegisterTest("DetectFastPatternTest446", DetectFastPatternTest446, 1);
+ UtRegisterTest("DetectFastPatternTest447", DetectFastPatternTest447, 1);
+ UtRegisterTest("DetectFastPatternTest448", DetectFastPatternTest448, 1);
+ UtRegisterTest("DetectFastPatternTest449", DetectFastPatternTest449, 1);
+ UtRegisterTest("DetectFastPatternTest450", DetectFastPatternTest450, 1);
+ UtRegisterTest("DetectFastPatternTest451", DetectFastPatternTest451, 1);
+ UtRegisterTest("DetectFastPatternTest452", DetectFastPatternTest452, 1);
+ UtRegisterTest("DetectFastPatternTest453", DetectFastPatternTest453, 1);
+ UtRegisterTest("DetectFastPatternTest454", DetectFastPatternTest454, 1);
+ UtRegisterTest("DetectFastPatternTest455", DetectFastPatternTest455, 1);
+ UtRegisterTest("DetectFastPatternTest456", DetectFastPatternTest456, 1);
+ UtRegisterTest("DetectFastPatternTest457", DetectFastPatternTest457, 1);
+ UtRegisterTest("DetectFastPatternTest458", DetectFastPatternTest458, 1);
+ UtRegisterTest("DetectFastPatternTest459", DetectFastPatternTest459, 1);
+ UtRegisterTest("DetectFastPatternTest460", DetectFastPatternTest460, 1);
+ UtRegisterTest("DetectFastPatternTest461", DetectFastPatternTest461, 1);
+ UtRegisterTest("DetectFastPatternTest462", DetectFastPatternTest462, 1);
+ UtRegisterTest("DetectFastPatternTest463", DetectFastPatternTest463, 1);
+ UtRegisterTest("DetectFastPatternTest464", DetectFastPatternTest464, 1);
+ UtRegisterTest("DetectFastPatternTest465", DetectFastPatternTest465, 1);
+ /* http_stat_code fast_pattern tests ^ */
+ /* http_server_body fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest466", DetectFastPatternTest466, 1);
+ UtRegisterTest("DetectFastPatternTest467", DetectFastPatternTest467, 1);
+ UtRegisterTest("DetectFastPatternTest468", DetectFastPatternTest468, 1);
+ UtRegisterTest("DetectFastPatternTest469", DetectFastPatternTest469, 1);
+ UtRegisterTest("DetectFastPatternTest470", DetectFastPatternTest470, 1);
+ UtRegisterTest("DetectFastPatternTest471", DetectFastPatternTest471, 1);
+ UtRegisterTest("DetectFastPatternTest472", DetectFastPatternTest472, 1);
+ UtRegisterTest("DetectFastPatternTest473", DetectFastPatternTest473, 1);
+ UtRegisterTest("DetectFastPatternTest474", DetectFastPatternTest474, 1);
+ UtRegisterTest("DetectFastPatternTest475", DetectFastPatternTest475, 1);
+ UtRegisterTest("DetectFastPatternTest476", DetectFastPatternTest476, 1);
+ UtRegisterTest("DetectFastPatternTest477", DetectFastPatternTest477, 1);
+ UtRegisterTest("DetectFastPatternTest478", DetectFastPatternTest478, 1);
+ UtRegisterTest("DetectFastPatternTest479", DetectFastPatternTest479, 1);
+ UtRegisterTest("DetectFastPatternTest480", DetectFastPatternTest480, 1);
+ UtRegisterTest("DetectFastPatternTest481", DetectFastPatternTest481, 1);
+ UtRegisterTest("DetectFastPatternTest482", DetectFastPatternTest482, 1);
+ UtRegisterTest("DetectFastPatternTest483", DetectFastPatternTest483, 1);
+ UtRegisterTest("DetectFastPatternTest484", DetectFastPatternTest484, 1);
+ UtRegisterTest("DetectFastPatternTest485", DetectFastPatternTest485, 1);
+ UtRegisterTest("DetectFastPatternTest486", DetectFastPatternTest486, 1);
+ UtRegisterTest("DetectFastPatternTest487", DetectFastPatternTest487, 1);
+ UtRegisterTest("DetectFastPatternTest488", DetectFastPatternTest488, 1);
+ UtRegisterTest("DetectFastPatternTest489", DetectFastPatternTest489, 1);
+ UtRegisterTest("DetectFastPatternTest490", DetectFastPatternTest490, 1);
+ UtRegisterTest("DetectFastPatternTest491", DetectFastPatternTest491, 1);
+ UtRegisterTest("DetectFastPatternTest492", DetectFastPatternTest492, 1);
+ UtRegisterTest("DetectFastPatternTest493", DetectFastPatternTest493, 1);
+ UtRegisterTest("DetectFastPatternTest494", DetectFastPatternTest494, 1);
+ UtRegisterTest("DetectFastPatternTest495", DetectFastPatternTest495, 1);
+ UtRegisterTest("DetectFastPatternTest496", DetectFastPatternTest496, 1);
+ UtRegisterTest("DetectFastPatternTest497", DetectFastPatternTest497, 1);
+ UtRegisterTest("DetectFastPatternTest498", DetectFastPatternTest498, 1);
+ UtRegisterTest("DetectFastPatternTest499", DetectFastPatternTest499, 1);
+ UtRegisterTest("DetectFastPatternTest500", DetectFastPatternTest500, 1);
+ UtRegisterTest("DetectFastPatternTest501", DetectFastPatternTest501, 1);
+ UtRegisterTest("DetectFastPatternTest502", DetectFastPatternTest502, 1);
+ UtRegisterTest("DetectFastPatternTest503", DetectFastPatternTest503, 1);
+ UtRegisterTest("DetectFastPatternTest504", DetectFastPatternTest504, 1);
+ UtRegisterTest("DetectFastPatternTest505", DetectFastPatternTest505, 1);
+ UtRegisterTest("DetectFastPatternTest506", DetectFastPatternTest506, 1);
+ /* http_server_body fast_pattern tests ^ */
+ /* file_data fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest507", DetectFastPatternTest507, 1);
+ UtRegisterTest("DetectFastPatternTest508", DetectFastPatternTest508, 1);
+ UtRegisterTest("DetectFastPatternTest509", DetectFastPatternTest509, 1);
+ UtRegisterTest("DetectFastPatternTest510", DetectFastPatternTest510, 1);
+ UtRegisterTest("DetectFastPatternTest511", DetectFastPatternTest511, 1);
+ UtRegisterTest("DetectFastPatternTest512", DetectFastPatternTest512, 1);
+ UtRegisterTest("DetectFastPatternTest513", DetectFastPatternTest513, 1);
+ UtRegisterTest("DetectFastPatternTest514", DetectFastPatternTest514, 1);
+ UtRegisterTest("DetectFastPatternTest515", DetectFastPatternTest515, 1);
+ UtRegisterTest("DetectFastPatternTest516", DetectFastPatternTest516, 1);
+ UtRegisterTest("DetectFastPatternTest517", DetectFastPatternTest517, 1);
+ UtRegisterTest("DetectFastPatternTest518", DetectFastPatternTest518, 1);
+ UtRegisterTest("DetectFastPatternTest519", DetectFastPatternTest519, 1);
+ UtRegisterTest("DetectFastPatternTest520", DetectFastPatternTest520, 1);
+ UtRegisterTest("DetectFastPatternTest521", DetectFastPatternTest521, 1);
+ UtRegisterTest("DetectFastPatternTest522", DetectFastPatternTest522, 1);
+ UtRegisterTest("DetectFastPatternTest523", DetectFastPatternTest523, 1);
+ UtRegisterTest("DetectFastPatternTest524", DetectFastPatternTest524, 1);
+ UtRegisterTest("DetectFastPatternTest525", DetectFastPatternTest525, 1);
+ UtRegisterTest("DetectFastPatternTest526", DetectFastPatternTest526, 1);
+ UtRegisterTest("DetectFastPatternTest527", DetectFastPatternTest527, 1);
+ UtRegisterTest("DetectFastPatternTest528", DetectFastPatternTest528, 1);
+ UtRegisterTest("DetectFastPatternTest529", DetectFastPatternTest529, 1);
+ UtRegisterTest("DetectFastPatternTest530", DetectFastPatternTest530, 1);
+ UtRegisterTest("DetectFastPatternTest531", DetectFastPatternTest531, 1);
+ UtRegisterTest("DetectFastPatternTest532", DetectFastPatternTest532, 1);
+ UtRegisterTest("DetectFastPatternTest533", DetectFastPatternTest533, 1);
+ UtRegisterTest("DetectFastPatternTest534", DetectFastPatternTest534, 1);
+ UtRegisterTest("DetectFastPatternTest535", DetectFastPatternTest535, 1);
+ UtRegisterTest("DetectFastPatternTest536", DetectFastPatternTest536, 1);
+ UtRegisterTest("DetectFastPatternTest537", DetectFastPatternTest537, 1);
+ UtRegisterTest("DetectFastPatternTest538", DetectFastPatternTest538, 1);
+ UtRegisterTest("DetectFastPatternTest539", DetectFastPatternTest539, 1);
+ UtRegisterTest("DetectFastPatternTest540", DetectFastPatternTest540, 1);
+ UtRegisterTest("DetectFastPatternTest541", DetectFastPatternTest541, 1);
+ UtRegisterTest("DetectFastPatternTest542", DetectFastPatternTest542, 1);
+ UtRegisterTest("DetectFastPatternTest543", DetectFastPatternTest543, 1);
+ UtRegisterTest("DetectFastPatternTest544", DetectFastPatternTest544, 1);
+ UtRegisterTest("DetectFastPatternTest545", DetectFastPatternTest545, 1);
+ UtRegisterTest("DetectFastPatternTest546", DetectFastPatternTest546, 1);
+ UtRegisterTest("DetectFastPatternTest547", DetectFastPatternTest547, 1);
+ /* file_data fast_pattern tests ^ */
+ /* http_user_agent fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest548", DetectFastPatternTest548, 1);
+ UtRegisterTest("DetectFastPatternTest549", DetectFastPatternTest549, 1);
+ UtRegisterTest("DetectFastPatternTest550", DetectFastPatternTest550, 1);
+ UtRegisterTest("DetectFastPatternTest551", DetectFastPatternTest551, 1);
+ UtRegisterTest("DetectFastPatternTest552", DetectFastPatternTest552, 1);
+ UtRegisterTest("DetectFastPatternTest553", DetectFastPatternTest553, 1);
+ UtRegisterTest("DetectFastPatternTest554", DetectFastPatternTest554, 1);
+ UtRegisterTest("DetectFastPatternTest555", DetectFastPatternTest555, 1);
+ UtRegisterTest("DetectFastPatternTest556", DetectFastPatternTest556, 1);
+ UtRegisterTest("DetectFastPatternTest557", DetectFastPatternTest557, 1);
+ UtRegisterTest("DetectFastPatternTest558", DetectFastPatternTest558, 1);
+ UtRegisterTest("DetectFastPatternTest559", DetectFastPatternTest559, 1);
+ UtRegisterTest("DetectFastPatternTest560", DetectFastPatternTest560, 1);
+ UtRegisterTest("DetectFastPatternTest561", DetectFastPatternTest561, 1);
+ UtRegisterTest("DetectFastPatternTest562", DetectFastPatternTest562, 1);
+ UtRegisterTest("DetectFastPatternTest563", DetectFastPatternTest563, 1);
+ UtRegisterTest("DetectFastPatternTest564", DetectFastPatternTest564, 1);
+ UtRegisterTest("DetectFastPatternTest565", DetectFastPatternTest565, 1);
+ UtRegisterTest("DetectFastPatternTest566", DetectFastPatternTest566, 1);
+ UtRegisterTest("DetectFastPatternTest567", DetectFastPatternTest567, 1);
+ UtRegisterTest("DetectFastPatternTest568", DetectFastPatternTest568, 1);
+ UtRegisterTest("DetectFastPatternTest569", DetectFastPatternTest569, 1);
+ UtRegisterTest("DetectFastPatternTest570", DetectFastPatternTest570, 1);
+ UtRegisterTest("DetectFastPatternTest571", DetectFastPatternTest571, 1);
+ UtRegisterTest("DetectFastPatternTest572", DetectFastPatternTest572, 1);
+ UtRegisterTest("DetectFastPatternTest573", DetectFastPatternTest573, 1);
+ UtRegisterTest("DetectFastPatternTest574", DetectFastPatternTest574, 1);
+ UtRegisterTest("DetectFastPatternTest575", DetectFastPatternTest575, 1);
+ UtRegisterTest("DetectFastPatternTest576", DetectFastPatternTest576, 1);
+ UtRegisterTest("DetectFastPatternTest577", DetectFastPatternTest577, 1);
+ UtRegisterTest("DetectFastPatternTest578", DetectFastPatternTest578, 1);
+ UtRegisterTest("DetectFastPatternTest579", DetectFastPatternTest579, 1);
+ UtRegisterTest("DetectFastPatternTest580", DetectFastPatternTest580, 1);
+ UtRegisterTest("DetectFastPatternTest581", DetectFastPatternTest581, 1);
+ UtRegisterTest("DetectFastPatternTest582", DetectFastPatternTest582, 1);
+ UtRegisterTest("DetectFastPatternTest583", DetectFastPatternTest583, 1);
+ UtRegisterTest("DetectFastPatternTest584", DetectFastPatternTest584, 1);
+ UtRegisterTest("DetectFastPatternTest585", DetectFastPatternTest585, 1);
+ UtRegisterTest("DetectFastPatternTest586", DetectFastPatternTest586, 1);
+ UtRegisterTest("DetectFastPatternTest587", DetectFastPatternTest587, 1);
+ UtRegisterTest("DetectFastPatternTest588", DetectFastPatternTest588, 1);
+ /* http_user_agent fast_pattern tests ^ */
+ /* http_host fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest589", DetectFastPatternTest589, 1);
+ UtRegisterTest("DetectFastPatternTest590", DetectFastPatternTest590, 1);
+ UtRegisterTest("DetectFastPatternTest591", DetectFastPatternTest591, 1);
+ UtRegisterTest("DetectFastPatternTest592", DetectFastPatternTest592, 1);
+ UtRegisterTest("DetectFastPatternTest593", DetectFastPatternTest593, 1);
+ UtRegisterTest("DetectFastPatternTest594", DetectFastPatternTest594, 1);
+ UtRegisterTest("DetectFastPatternTest595", DetectFastPatternTest595, 1);
+ UtRegisterTest("DetectFastPatternTest596", DetectFastPatternTest596, 1);
+ UtRegisterTest("DetectFastPatternTest597", DetectFastPatternTest597, 1);
+ UtRegisterTest("DetectFastPatternTest598", DetectFastPatternTest598, 1);
+ UtRegisterTest("DetectFastPatternTest599", DetectFastPatternTest599, 1);
+ UtRegisterTest("DetectFastPatternTest600", DetectFastPatternTest600, 1);
+ UtRegisterTest("DetectFastPatternTest601", DetectFastPatternTest601, 1);
+ UtRegisterTest("DetectFastPatternTest602", DetectFastPatternTest602, 1);
+ UtRegisterTest("DetectFastPatternTest603", DetectFastPatternTest603, 1);
+ UtRegisterTest("DetectFastPatternTest604", DetectFastPatternTest604, 1);
+ UtRegisterTest("DetectFastPatternTest605", DetectFastPatternTest605, 1);
+ UtRegisterTest("DetectFastPatternTest606", DetectFastPatternTest606, 1);
+ UtRegisterTest("DetectFastPatternTest607", DetectFastPatternTest607, 1);
+ UtRegisterTest("DetectFastPatternTest608", DetectFastPatternTest608, 1);
+ UtRegisterTest("DetectFastPatternTest609", DetectFastPatternTest609, 1);
+ UtRegisterTest("DetectFastPatternTest610", DetectFastPatternTest610, 1);
+ UtRegisterTest("DetectFastPatternTest611", DetectFastPatternTest611, 1);
+ UtRegisterTest("DetectFastPatternTest612", DetectFastPatternTest612, 1);
+ UtRegisterTest("DetectFastPatternTest613", DetectFastPatternTest613, 1);
+ UtRegisterTest("DetectFastPatternTest614", DetectFastPatternTest614, 1);
+ UtRegisterTest("DetectFastPatternTest615", DetectFastPatternTest615, 1);
+ UtRegisterTest("DetectFastPatternTest616", DetectFastPatternTest616, 1);
+ UtRegisterTest("DetectFastPatternTest617", DetectFastPatternTest617, 1);
+ UtRegisterTest("DetectFastPatternTest618", DetectFastPatternTest618, 1);
+ UtRegisterTest("DetectFastPatternTest619", DetectFastPatternTest619, 1);
+ UtRegisterTest("DetectFastPatternTest620", DetectFastPatternTest620, 1);
+ UtRegisterTest("DetectFastPatternTest621", DetectFastPatternTest621, 1);
+ UtRegisterTest("DetectFastPatternTest622", DetectFastPatternTest622, 1);
+ UtRegisterTest("DetectFastPatternTest623", DetectFastPatternTest623, 1);
+ UtRegisterTest("DetectFastPatternTest624", DetectFastPatternTest624, 1);
+ UtRegisterTest("DetectFastPatternTest625", DetectFastPatternTest625, 1);
+ UtRegisterTest("DetectFastPatternTest626", DetectFastPatternTest626, 1);
+ UtRegisterTest("DetectFastPatternTest627", DetectFastPatternTest627, 1);
+ UtRegisterTest("DetectFastPatternTest628", DetectFastPatternTest628, 1);
+ UtRegisterTest("DetectFastPatternTest629", DetectFastPatternTest629, 1);
+ /* http_host fast_pattern tests ^ */
+ /* http_rawhost fast_pattern tests v */
+ UtRegisterTest("DetectFastPatternTest630", DetectFastPatternTest630, 1);
+ UtRegisterTest("DetectFastPatternTest631", DetectFastPatternTest631, 1);
+ UtRegisterTest("DetectFastPatternTest632", DetectFastPatternTest632, 1);
+ UtRegisterTest("DetectFastPatternTest633", DetectFastPatternTest633, 1);
+ UtRegisterTest("DetectFastPatternTest634", DetectFastPatternTest634, 1);
+ UtRegisterTest("DetectFastPatternTest635", DetectFastPatternTest635, 1);
+ UtRegisterTest("DetectFastPatternTest636", DetectFastPatternTest636, 1);
+ UtRegisterTest("DetectFastPatternTest637", DetectFastPatternTest637, 1);
+ UtRegisterTest("DetectFastPatternTest638", DetectFastPatternTest638, 1);
+ UtRegisterTest("DetectFastPatternTest639", DetectFastPatternTest639, 1);
+ UtRegisterTest("DetectFastPatternTest640", DetectFastPatternTest640, 1);
+ UtRegisterTest("DetectFastPatternTest641", DetectFastPatternTest641, 1);
+ UtRegisterTest("DetectFastPatternTest642", DetectFastPatternTest642, 1);
+ UtRegisterTest("DetectFastPatternTest643", DetectFastPatternTest643, 1);
+ UtRegisterTest("DetectFastPatternTest644", DetectFastPatternTest644, 1);
+ UtRegisterTest("DetectFastPatternTest645", DetectFastPatternTest645, 1);
+ UtRegisterTest("DetectFastPatternTest646", DetectFastPatternTest646, 1);
+ UtRegisterTest("DetectFastPatternTest647", DetectFastPatternTest647, 1);
+ UtRegisterTest("DetectFastPatternTest648", DetectFastPatternTest648, 1);
+ UtRegisterTest("DetectFastPatternTest649", DetectFastPatternTest649, 1);
+ UtRegisterTest("DetectFastPatternTest650", DetectFastPatternTest650, 1);
+ UtRegisterTest("DetectFastPatternTest651", DetectFastPatternTest651, 1);
+ UtRegisterTest("DetectFastPatternTest652", DetectFastPatternTest652, 1);
+ UtRegisterTest("DetectFastPatternTest653", DetectFastPatternTest653, 1);
+ UtRegisterTest("DetectFastPatternTest654", DetectFastPatternTest654, 1);
+ UtRegisterTest("DetectFastPatternTest655", DetectFastPatternTest655, 1);
+ UtRegisterTest("DetectFastPatternTest656", DetectFastPatternTest656, 1);
+ UtRegisterTest("DetectFastPatternTest657", DetectFastPatternTest657, 1);
+ UtRegisterTest("DetectFastPatternTest658", DetectFastPatternTest658, 1);
+ UtRegisterTest("DetectFastPatternTest659", DetectFastPatternTest659, 1);
+ UtRegisterTest("DetectFastPatternTest660", DetectFastPatternTest660, 1);
+ UtRegisterTest("DetectFastPatternTest661", DetectFastPatternTest661, 1);
+ UtRegisterTest("DetectFastPatternTest662", DetectFastPatternTest662, 1);
+ UtRegisterTest("DetectFastPatternTest663", DetectFastPatternTest663, 1);
+ UtRegisterTest("DetectFastPatternTest664", DetectFastPatternTest664, 1);
+ UtRegisterTest("DetectFastPatternTest665", DetectFastPatternTest665, 1);
+ UtRegisterTest("DetectFastPatternTest666", DetectFastPatternTest666, 1);
+ UtRegisterTest("DetectFastPatternTest667", DetectFastPatternTest667, 1);
+ UtRegisterTest("DetectFastPatternTest668", DetectFastPatternTest668, 1);
+ UtRegisterTest("DetectFastPatternTest669", DetectFastPatternTest669, 1);
+ UtRegisterTest("DetectFastPatternTest670", DetectFastPatternTest670, 1);
+
+ /* Unittest to check
+ * - if we assign different content_ids to duplicate patterns, but one of the
+ * patterns has a fast_pattern chop set.
+ * - if 2 unique patterns get unique ids.
+ * - if 2 duplicate patterns, with no chop set get unique ids.
+ */
+ UtRegisterTest("DetectFastPatternTest671", DetectFastPatternTest671, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-fast-pattern.h b/framework/src/suricata/src/detect-fast-pattern.h
new file mode 100644
index 00000000..8298e7bb
--- /dev/null
+++ b/framework/src/suricata/src/detect-fast-pattern.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_FAST_PATTERN_H__
+#define __DETECT_FAST_PATTERN_H__
+
+typedef struct SCFPSupportSMList_ {
+ /* the list id. Have a look at Signature->sm_lists[] */
+ int list_id;
+ int priority;
+
+ struct SCFPSupportSMList_ *next;
+} SCFPSupportSMList;
+
+extern SCFPSupportSMList *sm_fp_support_smlist_list;
+
+/**
+ * \brief Checks if a particular list(Signature->sm_lists[]) is in the list
+ * of lists that need to be searched for a keyword that has fp support.
+ *
+ * \param list_id The list id.
+ *
+ * \retval 1 If supported.
+ * \retval 0 If not.
+ */
+static inline int FastPatternSupportEnabledForSigMatchList(int list_id)
+{
+ if (sm_fp_support_smlist_list == NULL)
+ return 0;
+
+ SCFPSupportSMList *tmp_smlist_fp = sm_fp_support_smlist_list;
+ while (tmp_smlist_fp != NULL) {
+ if (tmp_smlist_fp->list_id == list_id)
+ return 1;
+
+ tmp_smlist_fp = tmp_smlist_fp->next;
+ }
+
+ return 0;
+}
+
+void SupportFastPatternForSigMatchTypes(void);
+
+void DetectFastPatternRegister(void);
+
+#endif /* __DETECT_FAST_PATTERN_H__ */
+
diff --git a/framework/src/suricata/src/detect-file-data.c b/framework/src/suricata/src/detect-file-data.c
new file mode 100644
index 00000000..258a135c
--- /dev/null
+++ b/framework/src/suricata/src/detect-file-data.c
@@ -0,0 +1,280 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+static int DetectFiledataSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFiledataRegisterTests(void);
+/**
+ * \brief Registration function for keyword: file_data
+ */
+void DetectFiledataRegister(void)
+{
+ sigmatch_table[DETECT_FILE_DATA].name = "file_data";
+ sigmatch_table[DETECT_FILE_DATA].desc = "make content keywords match on HTTP response body";
+ sigmatch_table[DETECT_FILE_DATA].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#file_data";
+ sigmatch_table[DETECT_FILE_DATA].Match = NULL;
+ sigmatch_table[DETECT_FILE_DATA].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_FILE_DATA].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILE_DATA].Setup = DetectFiledataSetup;
+ sigmatch_table[DETECT_FILE_DATA].Free = NULL;
+ sigmatch_table[DETECT_FILE_DATA].RegisterTests = DetectFiledataRegisterTests;
+ sigmatch_table[DETECT_FILE_DATA].flags = SIGMATCH_NOOPT;
+}
+
+/**
+ * \brief this function is used to parse filedata options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filestore" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFiledataSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SCEnter();
+
+ if (!DetectProtoContainsProto(&s->proto, IPPROTO_TCP) &&
+ s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP &&
+ s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ return -1;
+ }
+
+ if (s->alproto == ALPROTO_HTTP && (s->init_flags & SIG_FLAG_INIT_FLOW) &&
+ (s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_TOCLIENT)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Can't use file_data with flow:to_server or from_client with http.");
+ return -1;
+ }
+
+ if (s->alproto == ALPROTO_SMTP && (s->init_flags & SIG_FLAG_INIT_FLOW) &&
+ !(s->flags & SIG_FLAG_TOSERVER) && (s->flags & SIG_FLAG_TOCLIENT)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Can't use file_data with flow:to_client or from_server with smtp.");
+ return -1;
+ }
+
+ s->list = DETECT_SM_LIST_FILEDATA;
+
+ return 0;
+}
+
+#ifdef UNITTESTS
+static int DetectFiledataParseTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert smtp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content is still in FILEDATA list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectFiledataParseTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content is still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectFiledataParseTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any 25 "
+ "(msg:\"test\"; flow:to_server,established; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("content is still in PMATCH list: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("content not in FILEDATA list: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectFiledataParseTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert smtp any any -> any any "
+ "(msg:\"test\"; flow:to_client,established; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectFiledataParseTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert http any any -> any any "
+ "(msg:\"test\"; flow:to_server,established; file_data; content:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+#endif
+
+void DetectFiledataRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFiledataParseTest01", DetectFiledataParseTest01, 1);
+ UtRegisterTest("DetectFiledataParseTest02", DetectFiledataParseTest02, 1);
+ UtRegisterTest("DetectFiledataParseTest03", DetectFiledataParseTest03, 1);
+ UtRegisterTest("DetectFiledataParseTest04", DetectFiledataParseTest04, 0);
+ UtRegisterTest("DetectFiledataParseTest05", DetectFiledataParseTest05, 0);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-file-data.h b/framework/src/suricata/src/detect-file-data.h
new file mode 100644
index 00000000..41cdd734
--- /dev/null
+++ b/framework/src/suricata/src/detect-file-data.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FILEDATA_H__
+#define __DETECT_FILEDATA_H__
+
+/* prototypes */
+void DetectFiledataRegister (void);
+
+#endif /* __DETECT_FILEDATA_H__ */
diff --git a/framework/src/suricata/src/detect-fileext.c b/framework/src/suricata/src/detect-fileext.c
new file mode 100644
index 00000000..7b5e9103
--- /dev/null
+++ b/framework/src/suricata/src/detect-fileext.c
@@ -0,0 +1,315 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm-bm.h"
+#include "util-print.h"
+#include "util-memcmp.h"
+
+#include "app-layer.h"
+
+#include "stream-tcp.h"
+#include "detect-fileext.h"
+
+static int DetectFileextMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, File *, Signature *, SigMatch *);
+static int DetectFileextSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFileextRegisterTests(void);
+static void DetectFileextFree(void *);
+
+/**
+ * \brief Registration function for keyword: fileext
+ */
+void DetectFileextRegister(void)
+{
+ sigmatch_table[DETECT_FILEEXT].name = "fileext";
+ sigmatch_table[DETECT_FILEEXT].desc = "match on the extension of a file name";
+ sigmatch_table[DETECT_FILEEXT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#fileext";
+ sigmatch_table[DETECT_FILEEXT].FileMatch = DetectFileextMatch;
+ sigmatch_table[DETECT_FILEEXT].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILEEXT].Setup = DetectFileextSetup;
+ sigmatch_table[DETECT_FILEEXT].Free = DetectFileextFree;
+ sigmatch_table[DETECT_FILEEXT].RegisterTests = DetectFileextRegisterTests;
+
+ SCLogDebug("registering fileext rule option");
+ return;
+}
+
+/**
+ * \brief match the specified file extension
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFileextData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFileextMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ SCEnter();
+ int ret = 0;
+
+ DetectFileextData *fileext = (DetectFileextData *)m->ctx;
+
+ if (file->name == NULL)
+ SCReturnInt(0);
+
+ if (file->txid < det_ctx->tx_id)
+ SCReturnInt(0);
+
+ if (file->txid > det_ctx->tx_id)
+ SCReturnInt(0);
+
+ if (file->name_len <= fileext->len)
+ SCReturnInt(0);
+
+ int offset = file->name_len - fileext->len;
+
+ /* fileext->ext is already in lowercase, as SCMemcmpLowercase requires */
+ if (file->name[offset - 1] == '.' &&
+ SCMemcmpLowercase(fileext->ext, file->name + offset, fileext->len) == 0)
+ {
+ if (!(fileext->flags & DETECT_CONTENT_NEGATED)) {
+ ret = 1;
+ SCLogDebug("File ext found");
+ }
+ }
+
+ if (ret == 0 && (fileext->flags & DETECT_CONTENT_NEGATED)) {
+ SCLogDebug("negated match");
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse fileet
+ *
+ * \param str Pointer to the fileext value string
+ *
+ * \retval pointer to DetectFileextData on success
+ * \retval NULL on failure
+ */
+static DetectFileextData *DetectFileextParse (char *str)
+{
+ DetectFileextData *fileext = NULL;
+
+ /* We have a correct filename option */
+ fileext = SCMalloc(sizeof(DetectFileextData));
+ if (unlikely(fileext == NULL))
+ goto error;
+
+ memset(fileext, 0x00, sizeof(DetectFileextData));
+
+ if (DetectContentDataParse("fileext", str, &fileext->ext, &fileext->len, &fileext->flags) == -1) {
+ goto error;
+ }
+ uint16_t u;
+ for (u = 0; u < fileext->len; u++)
+ fileext->ext[u] = tolower(fileext->ext[u]);
+
+ SCLogDebug("flags %02X", fileext->flags);
+ if (fileext->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("negated fileext");
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ char *ext = SCMalloc(fileext->len + 1);
+ if (ext != NULL) {
+ memcpy(ext, fileext->ext, fileext->len);
+ ext[fileext->len] = '\0';
+ SCLogDebug("will look for fileext %s", ext);
+ }
+ }
+#endif
+
+ return fileext;
+
+error:
+ if (fileext != NULL)
+ DetectFileextFree(fileext);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFileextSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectFileextData *fileext= NULL;
+ SigMatch *sm = NULL;
+
+ fileext = DetectFileextParse(str);
+ if (fileext == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILEEXT;
+ sm->ctx = (void *)fileext;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_FILENAME);
+ return 0;
+
+error:
+ if (fileext != NULL)
+ DetectFileextFree(fileext);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectFileextData
+ *
+ * \param fileext pointer to DetectFileextData
+ */
+static void DetectFileextFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectFileextData *fileext = (DetectFileextData *)ptr;
+ if (fileext->ext != NULL)
+ SCFree(fileext->ext);
+ SCFree(fileext);
+ }
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectFileextTestParse01
+ */
+int DetectFileextTestParse01 (void)
+{
+ DetectFileextData *dfd = DetectFileextParse("\"doc\"");
+ if (dfd != NULL) {
+ DetectFileextFree(dfd);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFileextTestParse02
+ */
+int DetectFileextTestParse02 (void)
+{
+ int result = 0;
+
+ DetectFileextData *dfd = DetectFileextParse("\"tar.gz\"");
+ if (dfd != NULL) {
+ if (dfd->len == 6 && memcmp(dfd->ext, "tar.gz", 6) == 0) {
+ result = 1;
+ }
+
+ DetectFileextFree(dfd);
+ return result;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFileextTestParse03
+ */
+int DetectFileextTestParse03 (void)
+{
+ int result = 0;
+
+ DetectFileextData *dfd = DetectFileextParse("\"pdf\"");
+ if (dfd != NULL) {
+ if (dfd->len == 3 && memcmp(dfd->ext, "pdf", 3) == 0) {
+ result = 1;
+ }
+
+ DetectFileextFree(dfd);
+ return result;
+ }
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFileext
+ */
+void DetectFileextRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectFileextTestParse01", DetectFileextTestParse01, 1);
+ UtRegisterTest("DetectFileextTestParse02", DetectFileextTestParse02, 1);
+ UtRegisterTest("DetectFileextTestParse03", DetectFileextTestParse03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-fileext.h b/framework/src/suricata/src/detect-fileext.h
new file mode 100644
index 00000000..4981763b
--- /dev/null
+++ b/framework/src/suricata/src/detect-fileext.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_FILEEXT_H__
+#define __DETECT_FILEEXT_H__
+
+#include "util-spm-bm.h"
+
+typedef struct DetectFileextData_ {
+ uint8_t *ext; /** file extension to match */
+ uint16_t len; /** length of the file */
+ uint32_t flags;
+} DetectFileextData;
+
+/* prototypes */
+void DetectFileextRegister (void);
+
+#endif /* __DETECT_FILEEXT_H__ */
diff --git a/framework/src/suricata/src/detect-filemagic.c b/framework/src/suricata/src/detect-filemagic.c
new file mode 100644
index 00000000..5dd28ec8
--- /dev/null
+++ b/framework/src/suricata/src/detect-filemagic.c
@@ -0,0 +1,498 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-magic.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+
+#include "stream-tcp.h"
+
+#include "detect-filemagic.h"
+
+#include "conf.h"
+#include "util-magic.h"
+
+static int DetectFilemagicMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, File *, Signature *, SigMatch *);
+static int DetectFilemagicSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFilemagicRegisterTests(void);
+static void DetectFilemagicFree(void *);
+
+/**
+ * \brief Registration function for keyword: filemagic
+ */
+void DetectFilemagicRegister(void)
+{
+ sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
+ sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
+ sigmatch_table[DETECT_FILEMAGIC].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filemagic";
+ sigmatch_table[DETECT_FILEMAGIC].FileMatch = DetectFilemagicMatch;
+ sigmatch_table[DETECT_FILEMAGIC].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetup;
+ sigmatch_table[DETECT_FILEMAGIC].Free = DetectFilemagicFree;
+ sigmatch_table[DETECT_FILEMAGIC].RegisterTests = DetectFilemagicRegisterTests;
+
+ SCLogDebug("registering filemagic rule option");
+ return;
+}
+
+#define FILEMAGIC_MIN_SIZE 512
+
+/**
+ * \brief run the magic check
+ *
+ * \param file the file
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int FilemagicGlobalLookup(File *file)
+{
+ if (file == NULL || file->chunks_head == NULL) {
+ SCReturnInt(-1);
+ }
+
+ /* initial chunk already matching our requirement */
+ if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
+ file->magic = MagicGlobalLookup(file->chunks_head->data, FILEMAGIC_MIN_SIZE);
+ } else {
+ uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
+ uint32_t size = 0;
+
+ if (likely(buf != NULL)) {
+ FileData *ffd = file->chunks_head;
+
+ for ( ; ffd != NULL; ffd = ffd->next) {
+ uint32_t copy_len = ffd->len;
+ if (size + ffd->len > FILEMAGIC_MIN_SIZE)
+ copy_len = FILEMAGIC_MIN_SIZE - size;
+
+ memcpy(buf + size, ffd->data, copy_len);
+ size += copy_len;
+
+ if (size >= FILEMAGIC_MIN_SIZE) {
+ file->magic = MagicGlobalLookup(buf, size);
+ break;
+ }
+ /* file is done but smaller than FILEMAGIC_MIN_SIZE */
+ if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
+ file->magic = MagicGlobalLookup(buf, size);
+ break;
+ }
+ }
+
+ SCFree(buf);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief run the magic check
+ *
+ * \param file the file
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int FilemagicThreadLookup(magic_t *ctx, File *file)
+{
+ if (ctx == NULL || file == NULL || file->chunks_head == NULL) {
+ SCReturnInt(-1);
+ }
+
+ /* initial chunk already matching our requirement */
+ if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
+ file->magic = MagicThreadLookup(ctx, file->chunks_head->data, FILEMAGIC_MIN_SIZE);
+ } else {
+ uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
+ uint32_t size = 0;
+
+ if (likely(buf != NULL)) {
+ FileData *ffd = file->chunks_head;
+
+ for ( ; ffd != NULL; ffd = ffd->next) {
+ uint32_t copy_len = ffd->len;
+ if (size + ffd->len > FILEMAGIC_MIN_SIZE)
+ copy_len = FILEMAGIC_MIN_SIZE - size;
+
+ memcpy(buf + size, ffd->data, copy_len);
+ size += copy_len;
+
+ if (size >= FILEMAGIC_MIN_SIZE) {
+ file->magic = MagicThreadLookup(ctx, buf, size);
+ break;
+ }
+ /* file is done but smaller than FILEMAGIC_MIN_SIZE */
+ if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
+ file->magic = MagicThreadLookup(ctx, buf, size);
+ break;
+ }
+ }
+
+ SCFree(buf);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief match the specified filemagic
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFilemagicData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFilemagicMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ SCEnter();
+ int ret = 0;
+ DetectFilemagicData *filemagic = (DetectFilemagicData *)m->ctx;
+
+ if (file->txid < det_ctx->tx_id)
+ SCReturnInt(0);
+
+ if (file->txid > det_ctx->tx_id)
+ SCReturnInt(0);
+
+ DetectFilemagicThreadData *tfilemagic = (DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, filemagic->thread_ctx_id);
+ if (tfilemagic == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (file->magic == NULL) {
+ FilemagicThreadLookup(&tfilemagic->ctx, file);
+ }
+
+ if (file->magic != NULL) {
+ SCLogDebug("magic %s", file->magic);
+
+ /* we include the \0 in the inspection, so patterns can match on the
+ * end of the string. */
+ if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic,
+ strlen(file->magic) + 1, filemagic->bm_ctx) != NULL)
+ {
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ char *name = SCMalloc(filemagic->len + 1);
+ if (name != NULL) {
+ memcpy(name, filemagic->name, filemagic->len);
+ name[filemagic->len] = '\0';
+ SCLogDebug("will look for filemagic %s", name);
+ }
+ }
+#endif
+
+ if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) {
+ ret = 1;
+ }
+ } else if (filemagic->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("negated match");
+ ret = 1;
+ }
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Parse the filemagic keyword
+ *
+ * \param idstr Pointer to the user provided option
+ *
+ * \retval filemagic pointer to DetectFilemagicData on success
+ * \retval NULL on failure
+ */
+static DetectFilemagicData *DetectFilemagicParse (char *str)
+{
+ DetectFilemagicData *filemagic = NULL;
+
+ /* We have a correct filemagic option */
+ filemagic = SCMalloc(sizeof(DetectFilemagicData));
+ if (unlikely(filemagic == NULL))
+ goto error;
+
+ memset(filemagic, 0x00, sizeof(DetectFilemagicData));
+
+ if (DetectContentDataParse ("filemagic", str, &filemagic->name, &filemagic->len, &filemagic->flags) == -1) {
+ goto error;
+ }
+
+ filemagic->bm_ctx = BoyerMooreNocaseCtxInit(filemagic->name, filemagic->len);
+ if (filemagic->bm_ctx == NULL) {
+ goto error;
+ }
+
+ SCLogDebug("flags %02X", filemagic->flags);
+ if (filemagic->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("negated filemagic");
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ char *name = SCMalloc(filemagic->len + 1);
+ if (name != NULL) {
+ memcpy(name, filemagic->name, filemagic->len);
+ name[filemagic->len] = '\0';
+ SCLogDebug("will look for filemagic %s", name);
+ }
+ }
+#endif
+
+ return filemagic;
+
+error:
+ if (filemagic != NULL)
+ DetectFilemagicFree(filemagic);
+ return NULL;
+}
+
+static void *DetectFilemagicThreadInit(void *data)
+{
+ char *filename = NULL;
+ FILE *fd = NULL;
+ DetectFilemagicData *filemagic = (DetectFilemagicData *)data;
+ BUG_ON(filemagic == NULL);
+
+ DetectFilemagicThreadData *t = SCMalloc(sizeof(DetectFilemagicThreadData));
+ if (unlikely(t == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "couldn't alloc ctx memory");
+ return NULL;
+ }
+ memset(t, 0x00, sizeof(DetectFilemagicThreadData));
+
+ t->ctx = magic_open(0);
+ if (t->ctx == NULL) {
+ SCLogError(SC_ERR_MAGIC_OPEN, "magic_open failed: %s", magic_error(t->ctx));
+ goto error;
+ }
+
+ (void)ConfGet("magic-file", &filename);
+ if (filename != NULL) {
+ SCLogInfo("using magic-file %s", filename);
+
+ if ( (fd = fopen(filename, "r")) == NULL) {
+ SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
+ goto error;
+ }
+ fclose(fd);
+ }
+
+ if (magic_load(t->ctx, filename) != 0) {
+ SCLogError(SC_ERR_MAGIC_LOAD, "magic_load failed: %s", magic_error(t->ctx));
+ goto error;
+ }
+
+ return (void *)t;
+
+error:
+ if (t->ctx)
+ magic_close(t->ctx);
+ SCFree(t);
+ return NULL;
+}
+
+static void DetectFilemagicThreadFree(void *ctx)
+{
+ if (ctx != NULL) {
+ DetectFilemagicThreadData *t = (DetectFilemagicThreadData *)ctx;
+ if (t->ctx)
+ magic_close(t->ctx);
+ SCFree(t);
+ }
+}
+
+/**
+ * \brief this function is used to parse filemagic options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filemagic" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFilemagicSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectFilemagicData *filemagic = NULL;
+ SigMatch *sm = NULL;
+
+ filemagic = DetectFilemagicParse(str);
+ if (filemagic == NULL)
+ goto error;
+
+ filemagic->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "filemagic",
+ DetectFilemagicThreadInit, (void *)filemagic,
+ DetectFilemagicThreadFree, 1);
+ if (filemagic->thread_ctx_id == -1)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILEMAGIC;
+ sm->ctx = (void *)filemagic;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_MAGIC);
+ return 0;
+
+error:
+ if (filemagic != NULL)
+ DetectFilemagicFree(filemagic);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectFilemagicData
+ *
+ * \param filemagic pointer to DetectFilemagicData
+ */
+static void DetectFilemagicFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectFilemagicData *filemagic = (DetectFilemagicData *)ptr;
+ if (filemagic->bm_ctx != NULL) {
+ BoyerMooreCtxDeInit(filemagic->bm_ctx);
+ }
+ if (filemagic->name != NULL)
+ SCFree(filemagic->name);
+ SCFree(filemagic);
+ }
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectFilemagicTestParse01
+ */
+int DetectFilemagicTestParse01 (void)
+{
+ DetectFilemagicData *dnd = DetectFilemagicParse("\"secret.pdf\"");
+ if (dnd != NULL) {
+ DetectFilemagicFree(dnd);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFilemagicTestParse02
+ */
+int DetectFilemagicTestParse02 (void)
+{
+ int result = 0;
+
+ DetectFilemagicData *dnd = DetectFilemagicParse("\"backup.tar.gz\"");
+ if (dnd != NULL) {
+ if (dnd->len == 13 && memcmp(dnd->name, "backup.tar.gz", 13) == 0) {
+ result = 1;
+ }
+
+ DetectFilemagicFree(dnd);
+ return result;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFilemagicTestParse03
+ */
+int DetectFilemagicTestParse03 (void)
+{
+ int result = 0;
+
+ DetectFilemagicData *dnd = DetectFilemagicParse("\"cmd.exe\"");
+ if (dnd != NULL) {
+ if (dnd->len == 7 && memcmp(dnd->name, "cmd.exe", 7) == 0) {
+ result = 1;
+ }
+
+ DetectFilemagicFree(dnd);
+ return result;
+ }
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFilemagic
+ */
+void DetectFilemagicRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectFilemagicTestParse01", DetectFilemagicTestParse01, 1);
+ UtRegisterTest("DetectFilemagicTestParse02", DetectFilemagicTestParse02, 1);
+ UtRegisterTest("DetectFilemagicTestParse03", DetectFilemagicTestParse03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-filemagic.h b/framework/src/suricata/src/detect-filemagic.h
new file mode 100644
index 00000000..97cd7954
--- /dev/null
+++ b/framework/src/suricata/src/detect-filemagic.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FILEMAGIC_H__
+#define __DETECT_FILEMAGIC_H__
+
+#include "util-spm-bm.h"
+#include <magic.h>
+
+typedef struct DetectFilemagicThreadData {
+ magic_t ctx;
+} DetectFilemagicThreadData;
+
+typedef struct DetectFilemagicData {
+ int thread_ctx_id;
+ uint8_t *name; /** name of the file to match */
+ BmCtx *bm_ctx; /** BM context */
+ uint16_t len; /** name length */
+ uint32_t flags;
+} DetectFilemagicData;
+
+/* prototypes */
+void DetectFilemagicRegister (void);
+int FilemagicGlobalLookup(File *file);
+
+#endif /* __DETECT_FILEMAGIC_H__ */
diff --git a/framework/src/suricata/src/detect-filemd5.c b/framework/src/suricata/src/detect-filemd5.c
new file mode 100644
index 00000000..87f3f35e
--- /dev/null
+++ b/framework/src/suricata/src/detect-filemd5.c
@@ -0,0 +1,428 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+
+#include "stream-tcp.h"
+
+#include "detect-filemd5.h"
+
+#include "queue.h"
+#include "util-rohash.h"
+
+#ifndef HAVE_NSS
+
+static int DetectFileMd5SetupNoSupport (DetectEngineCtx *a, Signature *b, char *c)
+{
+ SCLogError(SC_ERR_NO_MD5_SUPPORT, "no MD5 calculation support built in, needed for filemd5 keyword");
+ return -1;
+}
+
+/**
+ * \brief Registration function for keyword: filemd5
+ */
+void DetectFileMd5Register(void)
+{
+ sigmatch_table[DETECT_FILEMD5].name = "filemd5";
+ sigmatch_table[DETECT_FILEMD5].FileMatch = NULL;
+ sigmatch_table[DETECT_FILEMD5].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILEMD5].Setup = DetectFileMd5SetupNoSupport;
+ sigmatch_table[DETECT_FILEMD5].Free = NULL;
+ sigmatch_table[DETECT_FILEMD5].RegisterTests = NULL;
+ sigmatch_table[DETECT_FILEMD5].flags = SIGMATCH_NOT_BUILT;
+
+ SCLogDebug("registering filemd5 rule option");
+ return;
+}
+
+#else /* HAVE_NSS */
+
+static int DetectFileMd5Match (ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t, File *, Signature *, SigMatch *);
+static int DetectFileMd5Setup (DetectEngineCtx *, Signature *, char *);
+static void DetectFileMd5RegisterTests(void);
+static void DetectFileMd5Free(void *);
+
+/**
+ * \brief Registration function for keyword: filemd5
+ */
+void DetectFileMd5Register(void)
+{
+ sigmatch_table[DETECT_FILEMD5].name = "filemd5";
+ sigmatch_table[DETECT_FILEMD5].desc = "match file MD5 against list of MD5 checksums";
+ sigmatch_table[DETECT_FILEMD5].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filemd5";
+ sigmatch_table[DETECT_FILEMD5].FileMatch = DetectFileMd5Match;
+ sigmatch_table[DETECT_FILEMD5].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILEMD5].Setup = DetectFileMd5Setup;
+ sigmatch_table[DETECT_FILEMD5].Free = DetectFileMd5Free;
+ sigmatch_table[DETECT_FILEMD5].RegisterTests = DetectFileMd5RegisterTests;
+
+ SCLogDebug("registering filemd5 rule option");
+ return;
+}
+
+static int Md5ReadString(uint8_t *md5, char *str, char *filename, int line_no)
+{
+ if (strlen(str) != 32) {
+ SCLogError(SC_ERR_INVALID_MD5, "%s:%d md5 string not 32 bytes",
+ filename, line_no);
+ return -1;
+ }
+
+ int i, x;
+ for (x = 0, i = 0; i < 32; i+=2, x++) {
+ char buf[3] = { 0, 0, 0};
+ buf[0] = str[i];
+ buf[1] = str[i+1];
+
+ long value = strtol(buf, NULL, 16);
+ if (value >= 0 && value <= 255)
+ md5[x] = (uint8_t)value;
+ else {
+ SCLogError(SC_ERR_INVALID_MD5, "%s:%d md5 byte out of range %ld",
+ filename, line_no, value);
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+static int MD5LoadHash(ROHashTable *hash, char *string, char *filename, int line_no)
+{
+ uint8_t md5[16];
+
+ if (Md5ReadString(md5, string, filename, line_no) == 1) {
+ if (ROHashInitQueueValue(hash, &md5, (uint16_t)sizeof(md5)) != 1)
+ return -1;
+ }
+
+ return 1;
+}
+
+static int MD5MatchLookupBuffer(ROHashTable *hash, uint8_t *buf, size_t buflen)
+{
+ void *ptr = ROHashLookup(hash, buf, (uint16_t)buflen);
+ if (ptr == NULL)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * \brief match the specified filemd5
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFileMd5Data
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFileMd5Match (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ SCEnter();
+ int ret = 0;
+ DetectFileMd5Data *filemd5 = (DetectFileMd5Data *)m->ctx;
+
+ if (file->txid < det_ctx->tx_id) {
+ SCReturnInt(0);
+ }
+
+ if (file->txid > det_ctx->tx_id) {
+ SCReturnInt(0);
+ }
+
+ if (file->state != FILE_STATE_CLOSED) {
+ SCReturnInt(0);
+ }
+
+ if (file->flags & FILE_MD5) {
+ if (MD5MatchLookupBuffer(filemd5->hash, file->md5, sizeof(file->md5)) == 1) {
+ if (filemd5->negated == 0)
+ ret = 1;
+ else
+ ret = 0;
+ } else {
+ if (filemd5->negated == 0)
+ ret = 0;
+ else
+ ret = 1;
+ }
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Parse the filemd5 keyword
+ *
+ * \param idstr Pointer to the user provided option
+ *
+ * \retval filemd5 pointer to DetectFileMd5Data on success
+ * \retval NULL on failure
+ */
+static DetectFileMd5Data *DetectFileMd5Parse (const DetectEngineCtx *de_ctx, char *str)
+{
+ DetectFileMd5Data *filemd5 = NULL;
+ FILE *fp = NULL;
+ char *filename = NULL;
+
+ /* We have a correct filemd5 option */
+ filemd5 = SCMalloc(sizeof(DetectFileMd5Data));
+ if (unlikely(filemd5 == NULL))
+ goto error;
+
+ memset(filemd5, 0x00, sizeof(DetectFileMd5Data));
+
+ if (strlen(str) && str[0] == '!') {
+ filemd5->negated = 1;
+ str++;
+ }
+
+ filemd5->hash = ROHashInit(18, 16);
+ if (filemd5->hash == NULL) {
+ goto error;
+ }
+
+ /* get full filename */
+ filename = DetectLoadCompleteSigPath(de_ctx, str);
+ if (filename == NULL) {
+ goto error;
+ }
+
+ char line[8192] = "";
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ SCLogError(SC_ERR_OPENING_RULE_FILE, "opening md5 file %s: %s", filename, strerror(errno));
+ goto error;
+ }
+
+ int line_no = 0;
+ while(fgets(line, (int)sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ line_no++;
+
+ /* ignore comments and empty lines */
+ if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
+ continue;
+
+ while (isspace(line[--len]));
+
+ /* Check if we have a trailing newline, and remove it */
+ len = strlen(line);
+ if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
+ line[len - 1] = '\0';
+ }
+
+ /* cut off longer lines */
+ if (strlen(line) > 32)
+ line[32] = 0x00;
+
+ if (MD5LoadHash(filemd5->hash, line, filename, line_no) != 1) {
+ goto error;
+ }
+ }
+ fclose(fp);
+ fp = NULL;
+
+ if (ROHashInitFinalize(filemd5->hash) != 1) {
+ goto error;
+ }
+ SCLogInfo("MD5 hash size %u bytes%s", ROHashMemorySize(filemd5->hash), filemd5->negated ? ", negated match" : "");
+
+ SCFree(filename);
+ return filemd5;
+
+error:
+ if (filemd5 != NULL)
+ DetectFileMd5Free(filemd5);
+ if (fp != NULL)
+ fclose(fp);
+ if (filename != NULL)
+ SCFree(filename);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to parse filemd5 options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filemd5" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFileMd5Setup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectFileMd5Data *filemd5 = NULL;
+ SigMatch *sm = NULL;
+
+ filemd5 = DetectFileMd5Parse(de_ctx, str);
+ if (filemd5 == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILEMD5;
+ sm->ctx = (void *)filemd5;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_MD5);
+ return 0;
+
+error:
+ if (filemd5 != NULL)
+ DetectFileMd5Free(filemd5);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectFileMd5Data
+ *
+ * \param filemd5 pointer to DetectFileMd5Data
+ */
+static void DetectFileMd5Free(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectFileMd5Data *filemd5 = (DetectFileMd5Data *)ptr;
+ if (filemd5->hash != NULL)
+ ROHashFree(filemd5->hash);
+ SCFree(filemd5);
+ }
+}
+
+#ifdef UNITTESTS
+static int MD5MatchLookupString(ROHashTable *hash, char *string)
+{
+ uint8_t md5[16];
+ if (Md5ReadString(md5, string, "file", 88) == 1) {
+ void *ptr = ROHashLookup(hash, &md5, (uint16_t)sizeof(md5));
+ if (ptr == NULL)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int MD5MatchTest01(void)
+{
+ ROHashTable *hash = ROHashInit(4, 16);
+ if (hash == NULL) {
+ return 0;
+ }
+ if (MD5LoadHash(hash, "d80f93a93dc5f3ee945704754d6e0a36", "file", 1) != 1)
+ return 0;
+ if (MD5LoadHash(hash, "92a49985b384f0d993a36e4c2d45e206", "file", 2) != 1)
+ return 0;
+ if (MD5LoadHash(hash, "11adeaacc8c309815f7bc3e33888f281", "file", 3) != 1)
+ return 0;
+ if (MD5LoadHash(hash, "22e10a8fe02344ade0bea8836a1714af", "file", 4) != 1)
+ return 0;
+ if (MD5LoadHash(hash, "c3db2cbf02c68f073afcaee5634677bc", "file", 5) != 1)
+ return 0;
+ if (MD5LoadHash(hash, "7ed095da259638f42402fb9e74287a17", "file", 6) != 1)
+ return 0;
+
+ if (ROHashInitFinalize(hash) != 1) {
+ return 0;
+ }
+
+ if (MD5MatchLookupString(hash, "d80f93a93dc5f3ee945704754d6e0a36") != 1)
+ return 0;
+ if (MD5MatchLookupString(hash, "92a49985b384f0d993a36e4c2d45e206") != 1)
+ return 0;
+ if (MD5MatchLookupString(hash, "11adeaacc8c309815f7bc3e33888f281") != 1)
+ return 0;
+ if (MD5MatchLookupString(hash, "22e10a8fe02344ade0bea8836a1714af") != 1)
+ return 0;
+ if (MD5MatchLookupString(hash, "c3db2cbf02c68f073afcaee5634677bc") != 1)
+ return 0;
+ if (MD5MatchLookupString(hash, "7ed095da259638f42402fb9e74287a17") != 1)
+ return 0;
+ /* shouldnt match */
+ if (MD5MatchLookupString(hash, "33333333333333333333333333333333") == 1)
+ return 0;
+
+ ROHashFree(hash);
+ return 1;
+}
+#endif
+
+void DetectFileMd5RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("MD5MatchTest01", MD5MatchTest01, 1);
+#endif
+}
+
+#endif /* HAVE_NSS */
+
diff --git a/framework/src/suricata/src/detect-filemd5.h b/framework/src/suricata/src/detect-filemd5.h
new file mode 100644
index 00000000..486812f7
--- /dev/null
+++ b/framework/src/suricata/src/detect-filemd5.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FILEMD5_H__
+#define __DETECT_FILEMD5_H__
+
+#include "util-rohash.h"
+
+typedef struct DetectFileMd5Data {
+ ROHashTable *hash;
+ int negated;
+} DetectFileMd5Data;
+
+/* prototypes */
+void DetectFileMd5Register (void);
+
+#endif /* __DETECT_FILEMD5_H__ */
diff --git a/framework/src/suricata/src/detect-filename.c b/framework/src/suricata/src/detect-filename.c
new file mode 100644
index 00000000..8e5ddc1c
--- /dev/null
+++ b/framework/src/suricata/src/detect-filename.c
@@ -0,0 +1,321 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+
+#include "stream-tcp.h"
+
+#include "detect-filename.h"
+
+static int DetectFilenameMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, File *, Signature *, SigMatch *);
+static int DetectFilenameSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFilenameRegisterTests(void);
+static void DetectFilenameFree(void *);
+
+/**
+ * \brief Registration function for keyword: filename
+ */
+void DetectFilenameRegister(void)
+{
+ sigmatch_table[DETECT_FILENAME].name = "filename";
+ sigmatch_table[DETECT_FILENAME].desc = "match on the file name";
+ sigmatch_table[DETECT_FILENAME].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filename";
+ sigmatch_table[DETECT_FILENAME].FileMatch = DetectFilenameMatch;
+ sigmatch_table[DETECT_FILENAME].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILENAME].Setup = DetectFilenameSetup;
+ sigmatch_table[DETECT_FILENAME].Free = DetectFilenameFree;
+ sigmatch_table[DETECT_FILENAME].RegisterTests = DetectFilenameRegisterTests;
+
+ SCLogDebug("registering filename rule option");
+ return;
+}
+
+/**
+ * \brief match the specified filename
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFilenameData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFilenameMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ SCEnter();
+ int ret = 0;
+
+ DetectFilenameData *filename = (DetectFilenameData *)m->ctx;
+
+ if (file->name == NULL)
+ SCReturnInt(0);
+
+ if (file->txid < det_ctx->tx_id)
+ SCReturnInt(0);
+
+ if (file->txid > det_ctx->tx_id)
+ SCReturnInt(0);
+
+ if (BoyerMooreNocase(filename->name, filename->len, file->name,
+ file->name_len, filename->bm_ctx) != NULL)
+ {
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ char *name = SCMalloc(filename->len + 1);
+ if (name != NULL) {
+ memcpy(name, filename->name, filename->len);
+ name[filename->len] = '\0';
+ SCLogDebug("will look for filename %s", name);
+ }
+ }
+#endif
+
+ if (!(filename->flags & DETECT_CONTENT_NEGATED)) {
+ ret = 1;
+ }
+ }
+
+ if (ret == 0 && (filename->flags & DETECT_CONTENT_NEGATED)) {
+ SCLogDebug("negated match");
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief Parse the filename keyword
+ *
+ * \param idstr Pointer to the user provided option
+ *
+ * \retval filename pointer to DetectFilenameData on success
+ * \retval NULL on failure
+ */
+static DetectFilenameData *DetectFilenameParse (char *str)
+{
+ DetectFilenameData *filename = NULL;
+
+ /* We have a correct filename option */
+ filename = SCMalloc(sizeof(DetectFilenameData));
+ if (unlikely(filename == NULL))
+ goto error;
+
+ memset(filename, 0x00, sizeof(DetectFilenameData));
+
+ if (DetectContentDataParse ("filename", str, &filename->name, &filename->len, &filename->flags) == -1) {
+ goto error;
+ }
+
+ filename->bm_ctx = BoyerMooreNocaseCtxInit(filename->name, filename->len);
+ if (filename->bm_ctx == NULL) {
+ goto error;
+ }
+
+ SCLogDebug("flags %02X", filename->flags);
+ if (filename->flags & DETECT_CONTENT_NEGATED) {
+ SCLogDebug("negated filename");
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ char *name = SCMalloc(filename->len + 1);
+ if (name != NULL) {
+ memcpy(name, filename->name, filename->len);
+ name[filename->len] = '\0';
+ SCLogDebug("will look for filename %s", name);
+ }
+ }
+#endif
+
+ return filename;
+
+error:
+ if (filename != NULL)
+ DetectFilenameFree(filename);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to parse filename options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filename" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFilenameSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectFilenameData *filename = NULL;
+ SigMatch *sm = NULL;
+
+ filename = DetectFilenameParse(str);
+ if (filename == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILENAME;
+ sm->ctx = (void *)filename;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_FILENAME);
+ return 0;
+
+error:
+ if (filename != NULL)
+ DetectFilenameFree(filename);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectFilenameData
+ *
+ * \param filename pointer to DetectFilenameData
+ */
+static void DetectFilenameFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectFilenameData *filename = (DetectFilenameData *)ptr;
+ if (filename->bm_ctx != NULL) {
+ BoyerMooreCtxDeInit(filename->bm_ctx);
+ }
+ if (filename->name != NULL)
+ SCFree(filename->name);
+ SCFree(filename);
+ }
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectFilenameTestParse01
+ */
+int DetectFilenameTestParse01 (void)
+{
+ DetectFilenameData *dnd = DetectFilenameParse("\"secret.pdf\"");
+ if (dnd != NULL) {
+ DetectFilenameFree(dnd);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFilenameTestParse02
+ */
+int DetectFilenameTestParse02 (void)
+{
+ int result = 0;
+
+ DetectFilenameData *dnd = DetectFilenameParse("\"backup.tar.gz\"");
+ if (dnd != NULL) {
+ if (dnd->len == 13 && memcmp(dnd->name, "backup.tar.gz", 13) == 0) {
+ result = 1;
+ }
+
+ DetectFilenameFree(dnd);
+ return result;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFilenameTestParse03
+ */
+int DetectFilenameTestParse03 (void)
+{
+ int result = 0;
+
+ DetectFilenameData *dnd = DetectFilenameParse("\"cmd.exe\"");
+ if (dnd != NULL) {
+ if (dnd->len == 7 && memcmp(dnd->name, "cmd.exe", 7) == 0) {
+ result = 1;
+ }
+
+ DetectFilenameFree(dnd);
+ return result;
+ }
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFilename
+ */
+void DetectFilenameRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectFilenameTestParse01", DetectFilenameTestParse01, 1);
+ UtRegisterTest("DetectFilenameTestParse02", DetectFilenameTestParse02, 1);
+ UtRegisterTest("DetectFilenameTestParse03", DetectFilenameTestParse03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-filename.h b/framework/src/suricata/src/detect-filename.h
new file mode 100644
index 00000000..7204f36d
--- /dev/null
+++ b/framework/src/suricata/src/detect-filename.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_FILENAME_H__
+#define __DETECT_FILENAME_H__
+
+#include "util-spm-bm.h"
+
+typedef struct DetectFilenameData {
+ uint8_t *name; /** name of the file to match */
+ BmCtx *bm_ctx; /** BM context */
+ uint16_t len; /** name length */
+ uint32_t flags;
+} DetectFilenameData;
+
+/* prototypes */
+void DetectFilenameRegister (void);
+
+#endif /* __DETECT_FILENAME_H__ */
diff --git a/framework/src/suricata/src/detect-filesize.c b/framework/src/suricata/src/detect-filesize.c
new file mode 100644
index 00000000..17a01216
--- /dev/null
+++ b/framework/src/suricata/src/detect-filesize.c
@@ -0,0 +1,532 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the filesize keyword
+ */
+
+#include "suricata-common.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+
+#include "detect-filesize.h"
+#include "util-debug.h"
+#include "util-byte.h"
+#include "flow-util.h"
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing our filesize
+ */
+#define PARSE_REGEX "^(?:\\s*)(<|>)?(?:\\s*)([0-9]{1,23})(?:\\s*)(?:(<>)(?:\\s*)([0-9]{1,23}))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+/*prototypes*/
+static int DetectFilesizeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, File *file, Signature *s, SigMatch *m);
+static int DetectFilesizeSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFilesizeFree (void *);
+static void DetectFilesizeRegisterTests (void);
+
+/**
+ * \brief Registration function for filesize: keyword
+ */
+
+void DetectFilesizeRegister(void)
+{
+ sigmatch_table[DETECT_FILESIZE].name = "filesize";
+ sigmatch_table[DETECT_FILESIZE].desc = "match on the size of the file as it is being transferred";
+ sigmatch_table[DETECT_FILESIZE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filesize";
+ sigmatch_table[DETECT_FILESIZE].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILESIZE].FileMatch = DetectFilesizeMatch;
+ sigmatch_table[DETECT_FILESIZE].Setup = DetectFilesizeSetup;
+ sigmatch_table[DETECT_FILESIZE].Free = DetectFilesizeFree;
+ sigmatch_table[DETECT_FILESIZE].RegisterTests = DetectFilesizeRegisterTests;
+ sigmatch_table[DETECT_FILESIZE].flags |= SIGMATCH_PAYLOAD; /** XXX necessary? */
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogDebug("pcre compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogDebug("pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ if (parse_regex != NULL)
+ SCFree(parse_regex);
+ if (parse_regex_study != NULL)
+ SCFree(parse_regex_study);
+ return;
+}
+
+/**
+ * \brief This function is used to match filesize rule option.
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFilesizeData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFilesizeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectFilesizeData *fsd = (DetectFilesizeData *)m->ctx;
+ int ret = 0;
+ SCLogDebug("file size %"PRIu64", check %"PRIu64, file->size, fsd->size1);
+
+ if (file->state == FILE_STATE_CLOSED) {
+ switch (fsd->mode) {
+ case DETECT_FILESIZE_EQ:
+ if (file->size == fsd->size1)
+ ret = 1;
+ break;
+ case DETECT_FILESIZE_LT:
+ if (file->size < fsd->size1)
+ ret = 1;
+ break;
+ case DETECT_FILESIZE_GT:
+ if (file->size > fsd->size1)
+ ret = 1;
+ break;
+ case DETECT_FILESIZE_RA:
+ if (file->size > fsd->size1 && file->size < fsd->size2)
+ ret = 1;
+ break;
+ }
+ /* truncated, error: only see if what we have meets the GT condition */
+ } else if (file->state > FILE_STATE_CLOSED) {
+ if (fsd->mode == DETECT_FILESIZE_GT && file->size > fsd->size1)
+ ret = 1;
+ }
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief parse filesize options
+ *
+ * \param str pointer to the user provided filesize
+ *
+ * \retval fsd pointer to DetectFilesizeData on success
+ * \retval NULL on failure
+ */
+static DetectFilesizeData *DetectFilesizeParse (char *str)
+{
+
+ DetectFilesizeData *fsd = NULL;
+ char *arg1 = NULL;
+ char *arg2 = NULL;
+ char *arg3 = NULL;
+ char *arg4 = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 5) {
+ SCLogError(SC_ERR_PCRE_PARSE, "filesize option pcre parse error: \"%s\"", str);
+ goto error;
+ }
+ const char *str_ptr;
+
+ SCLogDebug("ret %d", ret);
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg1 = (char *) str_ptr;
+ SCLogDebug("Arg1 \"%s\"", arg1);
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg2 = (char *) str_ptr;
+ SCLogDebug("Arg2 \"%s\"", arg2);
+
+ if (ret > 3) {
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg3 = (char *) str_ptr;
+ SCLogDebug("Arg3 \"%s\"", arg3);
+
+ if (ret > 4) {
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg4 = (char *) str_ptr;
+ SCLogDebug("Arg4 \"%s\"", arg4);
+ }
+ }
+
+ fsd = SCMalloc(sizeof (DetectFilesizeData));
+ if (unlikely(fsd == NULL))
+ goto error;
+ memset(fsd, 0, sizeof(DetectFilesizeData));
+
+ if (arg1[0] == '<')
+ fsd->mode = DETECT_FILESIZE_LT;
+ else if (arg1[0] == '>')
+ fsd->mode = DETECT_FILESIZE_GT;
+ else
+ fsd->mode = DETECT_FILESIZE_EQ;
+
+ if (arg3 != NULL && strcmp("<>", arg3) == 0) {
+ if (strlen(arg1) != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Range specified but mode also set");
+ goto error;
+ }
+ fsd->mode = DETECT_FILESIZE_RA;
+ }
+
+ /** set the first value */
+ if (ByteExtractStringUint64(&fsd->size1,10,strlen(arg2),arg2) <= 0){
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Invalid size :\"%s\"",arg2);
+ goto error;
+ }
+
+ /** set the second value if specified */
+ if (arg4 != NULL && strlen(arg4) > 0) {
+ if (fsd->mode != DETECT_FILESIZE_RA) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Multiple filesize values specified"
+ " but mode is not range");
+ goto error;
+ }
+
+ if(ByteExtractStringUint64(&fsd->size2,10,strlen(arg4),arg4) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Invalid size :\"%s\"",arg4);
+ goto error;
+ }
+
+ if (fsd->size2 <= fsd->size1){
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"filesize2:%"PRIu64" <= filesize:"
+ "%"PRIu64"",fsd->size2,fsd->size1);
+ goto error;
+ }
+ }
+
+ pcre_free_substring(arg1);
+ pcre_free_substring(arg2);
+ if (arg3 != NULL)
+ pcre_free_substring(arg3);
+ if (arg4 != NULL)
+ pcre_free_substring(arg4);
+ return fsd;
+
+error:
+ if (fsd)
+ SCFree(fsd);
+ if (arg1 != NULL)
+ SCFree(arg1);
+ if (arg2 != NULL)
+ SCFree(arg2);
+ if (arg3 != NULL)
+ SCFree(arg3);
+ if (arg4 != NULL)
+ SCFree(arg4);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to parse filesize data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFilesizeSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SCEnter();
+ DetectFilesizeData *fsd = NULL;
+ SigMatch *sm = NULL;
+
+ fsd = DetectFilesizeParse(str);
+ if (fsd == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILESIZE;
+ sm->ctx = (SigMatchCtx *)fsd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_SIZE);
+ SCReturnInt(0);
+
+error:
+ if (fsd != NULL)
+ DetectFilesizeFree(fsd);
+ if (sm != NULL)
+ SCFree(sm);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief this function will free memory associated with DetectFilesizeData
+ *
+ * \param ptr pointer to DetectFilesizeData
+ */
+static void DetectFilesizeFree(void *ptr)
+{
+ DetectFilesizeData *fsd = (DetectFilesizeData *)ptr;
+ SCFree(fsd);
+}
+
+#ifdef UNITTESTS
+
+#include "stream.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "app-layer-parser.h"
+
+/** \test Test the Filesize keyword setup */
+static int DetectFilesizeParseTest01(void)
+{
+ int ret = 0;
+ DetectFilesizeData *fsd = NULL;
+
+ fsd = DetectFilesizeParse("10");
+ if (fsd != NULL) {
+ if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_EQ)
+ ret = 1;
+
+ DetectFilesizeFree(fsd);
+ }
+ return ret;
+}
+
+/** \test Test the Filesize keyword setup */
+static int DetectFilesizeParseTest02(void)
+{
+ int ret = 0;
+ DetectFilesizeData *fsd = NULL;
+
+ fsd = DetectFilesizeParse(" < 10 ");
+ if (fsd != NULL) {
+ if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_LT)
+ ret = 1;
+
+ DetectFilesizeFree(fsd);
+ }
+ return ret;
+}
+
+/** \test Test the Filesize keyword setup */
+static int DetectFilesizeParseTest03(void)
+{
+ int ret = 0;
+ DetectFilesizeData *fsd = NULL;
+
+ fsd = DetectFilesizeParse(" > 10 ");
+ if (fsd != NULL) {
+ if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_GT)
+ ret = 1;
+
+ DetectFilesizeFree(fsd);
+ }
+ return ret;
+}
+
+/** \test Test the Filesize keyword setup */
+static int DetectFilesizeParseTest04(void)
+{
+ int ret = 0;
+ DetectFilesizeData *fsd = NULL;
+
+ fsd = DetectFilesizeParse(" 5 <> 10 ");
+ if (fsd != NULL) {
+ if (fsd->size1 == 5 && fsd->size2 == 10 &&
+ fsd->mode == DETECT_FILESIZE_RA)
+ ret = 1;
+
+ DetectFilesizeFree(fsd);
+ }
+ return ret;
+}
+
+/** \test Test the Filesize keyword setup */
+static int DetectFilesizeParseTest05(void)
+{
+ int ret = 0;
+ DetectFilesizeData *fsd = NULL;
+
+ fsd = DetectFilesizeParse("5<>10");
+ if (fsd != NULL) {
+ if (fsd->size1 == 5 && fsd->size2 == 10 &&
+ fsd->mode == DETECT_FILESIZE_RA)
+ ret = 1;
+
+ DetectFilesizeFree(fsd);
+ }
+ return ret;
+}
+
+/**
+ * \brief this function is used to initialize the detection engine context and
+ * setup the signature with passed values.
+ *
+ */
+
+static int DetectFilesizeInitTest(DetectEngineCtx **de_ctx, Signature **sig,
+ DetectFilesizeData **fsd, char *str)
+{
+ char fullstr[1024];
+ int result = 0;
+
+ *de_ctx = NULL;
+ *sig = NULL;
+
+ if (snprintf(fullstr, 1024, "alert http any any -> any any (msg:\"Filesize "
+ "test\"; filesize:%s; sid:1;)", str) >= 1024) {
+ goto end;
+ }
+
+ *de_ctx = DetectEngineCtxInit();
+ if (*de_ctx == NULL) {
+ goto end;
+ }
+
+ (*de_ctx)->flags |= DE_QUIET;
+
+ (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
+ if ((*de_ctx)->sig_list == NULL) {
+ goto end;
+ }
+
+ *sig = (*de_ctx)->sig_list;
+
+ *fsd = DetectFilesizeParse(str);
+
+ result = 1;
+
+end:
+ return result;
+}
+
+/**
+ * \test DetectFilesizeSetpTest01 is a test for setting up an valid filesize values
+ * with valid "<>" operator and include spaces arround the given values.
+ * In the test the values are setup with initializing the detection engine
+ * context and setting up the signature itself.
+ */
+
+static int DetectFilesizeSetpTest01(void)
+{
+
+ DetectFilesizeData *fsd = NULL;
+ uint8_t res = 0;
+ Signature *sig = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+
+ res = DetectFilesizeInitTest(&de_ctx, &sig, &fsd, "1 <> 2 ");
+ if (res == 0) {
+ goto end;
+ }
+
+ if(fsd == NULL)
+ goto cleanup;
+
+ if (fsd != NULL) {
+ if (fsd->size1 == 1 && fsd->size2 == 2 &&
+ fsd->mode == DETECT_FILESIZE_RA)
+ res = 1;
+ }
+
+cleanup:
+ if (fsd)
+ SCFree(fsd);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ return res;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFilesize
+ */
+void DetectFilesizeRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFilesizeParseTest01", DetectFilesizeParseTest01, 1);
+ UtRegisterTest("DetectFilesizeParseTest02", DetectFilesizeParseTest02, 1);
+ UtRegisterTest("DetectFilesizeParseTest03", DetectFilesizeParseTest03, 1);
+ UtRegisterTest("DetectFilesizeParseTest04", DetectFilesizeParseTest04, 1);
+ UtRegisterTest("DetectFilesizeParseTest05", DetectFilesizeParseTest05, 1);
+ UtRegisterTest("DetectFilesizeSetpTest01", DetectFilesizeSetpTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-filesize.h b/framework/src/suricata/src/detect-filesize.h
new file mode 100644
index 00000000..cecb29f0
--- /dev/null
+++ b/framework/src/suricata/src/detect-filesize.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FILESIZE_H__
+#define __DETECT_FILESIZE_H__
+
+#define DETECT_FILESIZE_LT 0 /**< "less than" operator */
+#define DETECT_FILESIZE_GT 1 /**< "greater than" operator */
+#define DETECT_FILESIZE_RA 2 /**< range operator */
+#define DETECT_FILESIZE_EQ 3 /**< equal operator */
+
+typedef struct DetectFilesizeData_ {
+ uint64_t size1; /**< 1st value in the signature*/
+ uint64_t size2; /**< 2nd value in the signature*/
+ uint8_t mode; /**< operator used in the signature */
+} DetectFilesizeData;
+
+//int DetectFilesizeMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+// uint8_t, void *, Signature *, SigMatch *);
+void DetectFilesizeRegister(void);
+
+#endif /* _DETECT_URILEN_H */
diff --git a/framework/src/suricata/src/detect-filestore.c b/framework/src/suricata/src/detect-filestore.c
new file mode 100644
index 00000000..00e8aacc
--- /dev/null
+++ b/framework/src/suricata/src/detect-filestore.c
@@ -0,0 +1,443 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the filestore keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "stream-tcp.h"
+
+#include "detect-filestore.h"
+
+/**
+ * \brief Regex for parsing our flow options
+ */
+#define PARSE_REGEX "^\\s*([A-z_]+)\\s*(?:,\\s*([A-z_]+))?\\s*(?:,\\s*([A-z_]+))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectFilestoreMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t, File *, Signature *, SigMatch *);
+static int DetectFilestoreSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFilestoreFree(void *);
+
+/**
+ * \brief Registration function for keyword: filestore
+ */
+void DetectFilestoreRegister(void)
+{
+ sigmatch_table[DETECT_FILESTORE].name = "filestore";
+ sigmatch_table[DETECT_FILESTORE].desc = "stores files to disk if the rule matched";
+ sigmatch_table[DETECT_FILESTORE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filestore";
+ sigmatch_table[DETECT_FILESTORE].FileMatch = DetectFilestoreMatch;
+ sigmatch_table[DETECT_FILESTORE].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_FILESTORE].Setup = DetectFilestoreSetup;
+ sigmatch_table[DETECT_FILESTORE].Free = DetectFilestoreFree;
+ sigmatch_table[DETECT_FILESTORE].RegisterTests = NULL;
+ sigmatch_table[DETECT_FILESTORE].flags = SIGMATCH_OPTIONAL_OPT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ SCLogDebug("registering filestore rule option");
+ return;
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \brief apply the post match filestore with options
+ */
+static int FilestorePostMatchWithOptions(Packet *p, Flow *f, DetectFilestoreData *filestore, FileContainer *fc,
+ uint16_t file_id, uint16_t tx_id)
+{
+ if (filestore == NULL) {
+ SCReturnInt(0);
+ }
+
+ int this_file = 0;
+ int this_tx = 0;
+ int this_flow = 0;
+ int rule_dir = 0;
+ int toserver_dir = 0;
+ int toclient_dir = 0;
+
+ switch (filestore->direction) {
+ case FILESTORE_DIR_DEFAULT:
+ rule_dir = 1;
+ break;
+ case FILESTORE_DIR_BOTH:
+ toserver_dir = 1;
+ toclient_dir = 1;
+ break;
+ case FILESTORE_DIR_TOSERVER:
+ toserver_dir = 1;
+ break;
+ case FILESTORE_DIR_TOCLIENT:
+ toclient_dir = 1;
+ break;
+ }
+
+ switch (filestore->scope) {
+ case FILESTORE_SCOPE_DEFAULT:
+ if (rule_dir) {
+ this_file = 1;
+ } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && toclient_dir) {
+ this_file = 1;
+ } else if ((p->flowflags & FLOW_PKT_TOSERVER) && toserver_dir) {
+ this_file = 1;
+ }
+ break;
+ case FILESTORE_SCOPE_TX:
+ this_tx = 1;
+ break;
+ case FILESTORE_SCOPE_SSN:
+ this_flow = 1;
+ break;
+ }
+
+ if (this_file) {
+ FileStoreFileById(fc, file_id);
+ } else if (this_tx) {
+ /* flag tx all files will be stored */
+ if (f->alproto == ALPROTO_HTTP && f->alstate != NULL) {
+ HtpState *htp_state = f->alstate;
+ if (toserver_dir) {
+ htp_state->flags |= HTP_FLAG_STORE_FILES_TX_TS;
+ FileStoreAllFilesForTx(htp_state->files_ts, tx_id);
+ }
+ if (toclient_dir) {
+ htp_state->flags |= HTP_FLAG_STORE_FILES_TX_TC;
+ FileStoreAllFilesForTx(htp_state->files_tc, tx_id);
+ }
+ htp_state->store_tx_id = tx_id;
+ }
+ } else if (this_flow) {
+ /* flag flow all files will be stored */
+ if (f->alproto == ALPROTO_HTTP && f->alstate != NULL) {
+ HtpState *htp_state = f->alstate;
+ if (toserver_dir) {
+ htp_state->flags |= HTP_FLAG_STORE_FILES_TS;
+ FileStoreAllFiles(htp_state->files_ts);
+ }
+ if (toclient_dir) {
+ htp_state->flags |= HTP_FLAG_STORE_FILES_TC;
+ FileStoreAllFiles(htp_state->files_tc);
+ }
+ }
+ } else {
+ FileStoreFileById(fc, file_id);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief post-match function for filestore
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param p packet
+ *
+ * The match function for filestore records store candidates in the det_ctx.
+ * When we are sure all parts of the signature matched, we run this function
+ * to finalize the filestore.
+ */
+int DetectFilestorePostMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s)
+{
+ uint8_t flags = 0;
+
+ SCEnter();
+
+ if (det_ctx->filestore_cnt == 0) {
+ SCReturnInt(0);
+ }
+
+ if (s->filestore_sm == NULL || p->flow == NULL) {
+#ifndef DEBUG
+ SCReturnInt(0);
+#else
+ BUG_ON(1);
+#endif
+ }
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT)
+ flags |= STREAM_TOCLIENT;
+ else
+ flags |= STREAM_TOSERVER;
+
+ if (det_ctx->flow_locked == 0)
+ FLOWLOCK_WRLOCK(p->flow);
+
+ FileContainer *ffc = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, flags);
+
+ /* filestore for single files only */
+ if (s->filestore_sm->ctx == NULL) {
+ uint16_t u;
+ for (u = 0; u < det_ctx->filestore_cnt; u++) {
+ FileStoreFileById(ffc, det_ctx->filestore[u].file_id);
+ }
+ } else {
+ DetectFilestoreData *filestore = (DetectFilestoreData *)s->filestore_sm->ctx;
+ uint16_t u;
+
+ for (u = 0; u < det_ctx->filestore_cnt; u++) {
+ FilestorePostMatchWithOptions(p, p->flow, filestore, ffc,
+ det_ctx->filestore[u].file_id, det_ctx->filestore[u].tx_id);
+ }
+ }
+
+ if (det_ctx->flow_locked == 0)
+ FLOWLOCK_UNLOCK(p->flow);
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief match the specified filestore
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param f *LOCKED* flow
+ * \param flags direction flags
+ * \param file file being inspected
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectFilestoreData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ *
+ * \todo when we start supporting more protocols, the logic in this function
+ * needs to be put behind a api.
+ */
+static int DetectFilestoreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, File *file, Signature *s, SigMatch *m)
+{
+ uint16_t file_id = 0;
+
+ SCEnter();
+
+ if (det_ctx->filestore_cnt >= DETECT_FILESTORE_MAX) {
+ SCReturnInt(1);
+ }
+
+ /* file can be NULL when a rule with filestore scope > file
+ * matches. */
+ if (file != NULL) {
+ file_id = file->file_id;
+ }
+
+ det_ctx->filestore[det_ctx->filestore_cnt].file_id = file_id;
+ det_ctx->filestore[det_ctx->filestore_cnt].tx_id = det_ctx->tx_id;
+
+ SCLogDebug("%u, file %u, tx %"PRIu64, det_ctx->filestore_cnt,
+ det_ctx->filestore[det_ctx->filestore_cnt].file_id,
+ det_ctx->filestore[det_ctx->filestore_cnt].tx_id);
+
+ det_ctx->filestore_cnt++;
+ SCReturnInt(1);
+}
+
+/**
+ * \brief this function is used to parse filestore options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filestore" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFilestoreSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SCEnter();
+
+ DetectFilestoreData *fd = NULL;
+ SigMatch *sm = NULL;
+ char *args[3] = {NULL,NULL,NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FILESTORE;
+
+ if (str != NULL && strlen(str) > 0) {
+ SCLogDebug("str %s", str);
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, str);
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr;
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[0] = (char *)str_ptr;
+
+ if (ret > 2) {
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[1] = (char *)str_ptr;
+ }
+ if (ret > 3) {
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[2] = (char *)str_ptr;
+ }
+ }
+
+ fd = SCMalloc(sizeof(DetectFilestoreData));
+ if (unlikely(fd == NULL))
+ goto error;
+ memset(fd, 0x00, sizeof(DetectFilestoreData));
+
+ if (args[0] != NULL) {
+ SCLogDebug("first arg %s", args[0]);
+
+ if (strcasecmp(args[0], "request") == 0 ||
+ strcasecmp(args[0], "to_server") == 0)
+ {
+ fd->direction = FILESTORE_DIR_TOSERVER;
+ fd->scope = FILESTORE_SCOPE_TX;
+ }
+ else if (strcasecmp(args[0], "response") == 0 ||
+ strcasecmp(args[0], "to_client") == 0)
+ {
+ fd->direction = FILESTORE_DIR_TOCLIENT;
+ fd->scope = FILESTORE_SCOPE_TX;
+ }
+ else if (strcasecmp(args[0], "both") == 0)
+ {
+ fd->direction = FILESTORE_DIR_BOTH;
+ fd->scope = FILESTORE_SCOPE_TX;
+ }
+ } else {
+ fd->direction = FILESTORE_DIR_DEFAULT;
+ }
+
+ if (args[1] != NULL) {
+ SCLogDebug("second arg %s", args[1]);
+
+ if (strcasecmp(args[1], "file") == 0)
+ {
+ fd->scope = FILESTORE_SCOPE_DEFAULT;
+ } else if (strcasecmp(args[1], "tx") == 0)
+ {
+ fd->scope = FILESTORE_SCOPE_TX;
+ } else if (strcasecmp(args[1], "ssn") == 0 ||
+ strcasecmp(args[1], "flow") == 0)
+ {
+ fd->scope = FILESTORE_SCOPE_SSN;
+ }
+ } else {
+ if (fd->scope == 0)
+ fd->scope = FILESTORE_SCOPE_DEFAULT;
+ }
+
+ sm->ctx = (SigMatchCtx*)fd;
+ } else {
+ sm->ctx = (SigMatchCtx*)NULL;
+ }
+
+ if (s->alproto != ALPROTO_HTTP && s->alproto != ALPROTO_SMTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if (s->alproto == ALPROTO_HTTP) {
+ AppLayerHtpNeedFileInspection();
+ }
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
+ s->filestore_sm = sm;
+
+ s->flags |= SIG_FLAG_FILESTORE;
+ return 0;
+
+error:
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+static void DetectFilestoreFree(void *ptr)
+{
+ if (ptr != NULL) {
+ SCFree(ptr);
+ }
+}
diff --git a/framework/src/suricata/src/detect-filestore.h b/framework/src/suricata/src/detect-filestore.h
new file mode 100644
index 00000000..1879b875
--- /dev/null
+++ b/framework/src/suricata/src/detect-filestore.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FILESTORE_H__
+#define __DETECT_FILESTORE_H__
+
+#define FILESTORE_DIR_DEFAULT 0 /* rule dir */
+#define FILESTORE_DIR_TOSERVER 1
+#define FILESTORE_DIR_TOCLIENT 2
+#define FILESTORE_DIR_BOTH 3
+
+#define FILESTORE_SCOPE_DEFAULT 0 /* per file */
+#define FILESTORE_SCOPE_TX 1 /* per transaction */
+#define FILESTORE_SCOPE_SSN 2 /* per flow/ssn */
+
+typedef struct DetectFilestoreData_ {
+ int16_t direction;
+ int16_t scope;
+} DetectFilestoreData;
+
+/* prototypes */
+void DetectFilestoreRegister (void);
+
+int DetectFilestorePostMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *);
+#endif /* __DETECT_FILESTORE_H__ */
diff --git a/framework/src/suricata/src/detect-flags.c b/framework/src/suricata/src/detect-flags.c
new file mode 100644
index 00000000..b5b1840a
--- /dev/null
+++ b/framework/src/suricata/src/detect-flags.c
@@ -0,0 +1,1341 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the flags keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "decode-events.h"
+
+#include "detect-flags.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+/**
+ * Regex (by Brian Rectanus)
+ * flags: [!+*](SAPRFU120)[,SAPRFU12]
+ */
+#define PARSE_REGEX "^\\s*(?:([\\+\\*!]))?\\s*([SAPRFU120CE\\+\\*!]+)(?:\\s*,\\s*([SAPRFU12CE]+))?\\s*$"
+
+/**
+ * Flags args[0] *(3) +(2) !(1)
+ *
+ */
+
+#define MODIFIER_NOT 1
+#define MODIFIER_PLUS 2
+#define MODIFIER_ANY 3
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectFlagsMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFlagsSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFlagsFree(void *);
+
+/**
+ * \brief Registration function for flags: keyword
+ */
+
+void DetectFlagsRegister (void)
+{
+ sigmatch_table[DETECT_FLAGS].name = "flags";
+ sigmatch_table[DETECT_FLAGS].Match = DetectFlagsMatch;
+ sigmatch_table[DETECT_FLAGS].Setup = DetectFlagsSetup;
+ sigmatch_table[DETECT_FLAGS].Free = DetectFlagsFree;
+ sigmatch_table[DETECT_FLAGS].RegisterTests = FlagsRegisterTests;
+
+ const char *eb;
+ int opts = 0;
+ int eo;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+
+}
+
+/**
+ * \internal
+ * \brief This function is used to match flags on a packet with those passed via flags:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the Signature
+ * \param m pointer to the sigmatch
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFlagsMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ uint8_t flags = 0;
+ const DetectFlagsData *de = (const DetectFlagsData *)ctx;
+
+ if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
+ SCReturnInt(0);
+ }
+
+ flags = p->tcph->th_flags;
+
+ if (!de->flags && flags) {
+ if(de->modifier == MODIFIER_NOT) {
+ SCReturnInt(1);
+ }
+
+ SCReturnInt(0);
+ }
+
+ flags &= de->ignored_flags;
+
+ switch (de->modifier) {
+ case MODIFIER_ANY:
+ if ((flags & de->flags) > 0) {
+ SCReturnInt(1);
+ }
+ SCReturnInt(0);
+
+ case MODIFIER_PLUS:
+ if (((flags & de->flags) == de->flags)) {
+ SCReturnInt(1);
+ }
+ SCReturnInt(0);
+
+ case MODIFIER_NOT:
+ if ((flags & de->flags) != de->flags) {
+ SCReturnInt(1);
+ }
+ SCReturnInt(0);
+
+ default:
+ SCLogDebug("flags %"PRIu8" and de->flags %"PRIu8"",flags,de->flags);
+ if (flags == de->flags) {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse flags options passed via flags: keyword
+ *
+ * \param rawstr Pointer to the user provided flags options
+ *
+ * \retval de pointer to DetectFlagsData on success
+ * \retval NULL on failure
+ */
+static DetectFlagsData *DetectFlagsParse (char *rawstr)
+{
+ SCEnter();
+
+ DetectFlagsData *de = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, found = 0, ignore = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+ char *args[3] = { NULL, NULL, NULL };
+ char *ptr;
+ int i;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre match failed");
+ goto error;
+ }
+
+ for (i = 0; i < (ret - 1); i++) {
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS,i + 1,
+ &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ args[i] = (char *)str_ptr;
+ }
+
+ if(args[1] == NULL) {
+ SCLogDebug("args[1] == NULL");
+ goto error;
+ }
+
+ de = SCMalloc(sizeof(DetectFlagsData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ memset(de,0,sizeof(DetectFlagsData));
+
+ de->ignored_flags = 0xff;
+
+ /** First parse args[0] */
+
+ if(args[0]) {
+
+ ptr = args[0];
+
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case 'S':
+ case 's':
+ de->flags |= TH_SYN;
+ found++;
+ break;
+ case 'A':
+ case 'a':
+ de->flags |= TH_ACK;
+ found++;
+ break;
+ case 'F':
+ case 'f':
+ de->flags |= TH_FIN;
+ found++;
+ break;
+ case 'R':
+ case 'r':
+ de->flags |= TH_RST;
+ found++;
+ break;
+ case 'P':
+ case 'p':
+ de->flags |= TH_PUSH;
+ found++;
+ break;
+ case 'U':
+ case 'u':
+ de->flags |= TH_URG;
+ found++;
+ break;
+ case '1':
+ de->flags |= TH_CWR;
+ found++;
+ break;
+ case '2':
+ de->flags |= TH_ECN;
+ found++;
+ break;
+ case 'C':
+ case 'c':
+ de->flags |= TH_CWR;
+ found++;
+ break;
+ case 'E':
+ case 'e':
+ de->flags |= TH_ECN;
+ found++;
+ break;
+ case '0':
+ de->flags = 0;
+ found++;
+ break;
+
+ case '!':
+ de->modifier = MODIFIER_NOT;
+ break;
+ case '+':
+ de->modifier = MODIFIER_PLUS;
+ break;
+ case '*':
+ de->modifier = MODIFIER_ANY;
+ break;
+ }
+ ptr++;
+ }
+
+ }
+
+ /** Second parse first set of flags */
+
+ ptr = args[1];
+
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case 'S':
+ case 's':
+ de->flags |= TH_SYN;
+ found++;
+ break;
+ case 'A':
+ case 'a':
+ de->flags |= TH_ACK;
+ found++;
+ break;
+ case 'F':
+ case 'f':
+ de->flags |= TH_FIN;
+ found++;
+ break;
+ case 'R':
+ case 'r':
+ de->flags |= TH_RST;
+ found++;
+ break;
+ case 'P':
+ case 'p':
+ de->flags |= TH_PUSH;
+ found++;
+ break;
+ case 'U':
+ case 'u':
+ de->flags |= TH_URG;
+ found++;
+ break;
+ case '1':
+ de->flags |= TH_CWR;
+ found++;
+ break;
+ case '2':
+ de->flags |= TH_ECN;
+ found++;
+ break;
+ case 'C':
+ case 'c':
+ de->flags |= TH_CWR;
+ found++;
+ break;
+ case 'E':
+ case 'e':
+ de->flags |= TH_ECN;
+ found++;
+ break;
+ case '0':
+ de->flags = 0;
+ found++;
+ break;
+
+ case '!':
+ if (de->modifier != 0) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "\"flags\" supports only"
+ " one modifier at a time");
+ goto error;
+ }
+ de->modifier = MODIFIER_NOT;
+ SCLogDebug("NOT modifier is set");
+ break;
+ case '+':
+ if (de->modifier != 0) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "\"flags\" supports only"
+ " one modifier at a time");
+ goto error;
+ }
+ de->modifier = MODIFIER_PLUS;
+ SCLogDebug("PLUS modifier is set");
+ break;
+ case '*':
+ if (de->modifier != 0) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "\"flags\" supports only"
+ " one modifier at a time");
+ goto error;
+ }
+ de->modifier = MODIFIER_ANY;
+ SCLogDebug("ANY modifier is set");
+ break;
+ default:
+ break;
+ }
+ ptr++;
+ }
+
+ if(found == 0)
+ goto error;
+
+ /** Finally parse ignored flags */
+
+ if(args[2]) {
+
+ ptr = args[2];
+
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case 'S':
+ case 's':
+ de->ignored_flags &= ~TH_SYN;
+ ignore++;
+ break;
+ case 'A':
+ case 'a':
+ de->ignored_flags &= ~TH_ACK;
+ ignore++;
+ break;
+ case 'F':
+ case 'f':
+ de->ignored_flags &= ~TH_FIN;
+ ignore++;
+ break;
+ case 'R':
+ case 'r':
+ de->ignored_flags &= ~TH_RST;
+ ignore++;
+ break;
+ case 'P':
+ case 'p':
+ de->ignored_flags &= ~TH_PUSH;
+ ignore++;
+ break;
+ case 'U':
+ case 'u':
+ de->ignored_flags &= ~TH_URG;
+ ignore++;
+ break;
+ case '1':
+ de->ignored_flags &= ~TH_CWR;
+ ignore++;
+ break;
+ case '2':
+ de->ignored_flags &= ~TH_ECN;
+ ignore++;
+ break;
+ case 'C':
+ case 'c':
+ de->ignored_flags &= ~TH_CWR;
+ ignore++;
+ break;
+ case 'E':
+ case 'e':
+ de->ignored_flags &= ~TH_ECN;
+ ignore++;
+ break;
+ case '0':
+ break;
+ default:
+ break;
+ }
+ ptr++;
+ }
+
+ if(ignore == 0) {
+ SCLogDebug("ignore == 0");
+ goto error;
+ }
+ }
+
+ for (i = 0; i < (ret - 1); i++){
+ SCLogDebug("args[%"PRId32"] = %s",i, args[i]);
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+
+ SCLogDebug("found %"PRId32" ignore %"PRId32"", found, ignore);
+ SCReturnPtr(de, "DetectFlagsData");
+
+error:
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+ if (de) SCFree(de);
+ SCReturnPtr(NULL, "DetectFlagsData");
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed flags into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param rawstr pointer to the user provided flags options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFlagsSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+
+ de = DetectFlagsParse(rawstr);
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectFlagsData
+ *
+ * \param de pointer to DetectFlagsData
+ */
+static void DetectFlagsFree(void *de_ptr)
+{
+ DetectFlagsData *de = (DetectFlagsData *)de_ptr;
+ if(de) SCFree(de);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+/**
+ * \test FlagsTestParse01 is a test for a valid flags value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FlagsTestParse01 (void)
+{
+ DetectFlagsData *de = NULL;
+ de = DetectFlagsParse("S");
+ if (de && (de->flags == TH_SYN) ) {
+ DetectFlagsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse02 is a test for an invalid flags value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FlagsTestParse02 (void)
+{
+ DetectFlagsData *de = NULL;
+ de = DetectFlagsParse("G");
+ if (de) {
+ DetectFlagsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse03 test if ACK and PUSH are set. Must return success
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse03 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ACK|TH_PUSH|TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("AP+");
+
+ if (de == NULL || (de->flags != (TH_ACK|TH_PUSH)) )
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse04 check if ACK bit is set. Must fails.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FlagsTestParse04 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN;
+
+ de = DetectFlagsParse("A");
+
+ if (de == NULL || de->flags != TH_ACK)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse05 test if ACK+PUSH and more flags are set. Ignore SYN and RST bits.
+ * Must fails.
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse05 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ACK|TH_PUSH|TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("+AP,SR");
+
+ if (de == NULL || (de->modifier != MODIFIER_PLUS) || (de->flags != (TH_ACK|TH_PUSH)) || (de->ignored_flags != (TH_SYN|TH_RST)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse06 test if ACK+PUSH and more flags are set. Ignore URG and RST bits.
+ * Must return success.
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse06 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ACK|TH_PUSH|TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("+AP,UR");
+
+ if (de == NULL || (de->modifier != MODIFIER_PLUS) || (de->flags != (TH_ACK|TH_PUSH)) || ((0xff - de->ignored_flags) != (TH_URG|TH_RST)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse07 test if SYN or RST are set. Must fails.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse07 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("*AP");
+
+ if (de == NULL || (de->modifier != MODIFIER_ANY) || (de->flags != (TH_ACK|TH_PUSH)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse08 test if SYN or RST are set. Must return success.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse08 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("*SA");
+
+ if (de == NULL || (de->modifier != MODIFIER_ANY) || (de->flags != (TH_ACK|TH_SYN)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse09 test if SYN and RST are not set. Must fails.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse09 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("!PA");
+
+ if (de == NULL || (de->modifier != MODIFIER_NOT) || (de->flags != (TH_ACK|TH_PUSH)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse10 test if ACK and PUSH are not set. Must return success.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse10 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN|TH_RST;
+
+ de = DetectFlagsParse("!AP");
+
+ if (de == NULL || (de->modifier != MODIFIER_NOT) || (de->flags != (TH_ACK|TH_PUSH)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse11 test if ACK or PUSH are set. Ignore SYN and RST. Must fails.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FlagsTestParse11 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN|TH_RST|TH_URG;
+
+ de = DetectFlagsParse("*AP,SR");
+
+ if (de == NULL || (de->modifier != MODIFIER_ANY) || (de->flags != (TH_ACK|TH_PUSH)) || ((0xff - de->ignored_flags) != (TH_SYN|TH_RST)))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FlagsTestParse12 check if no flags are set. Must fails.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FlagsTestParse12 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_SYN;
+
+ de = DetectFlagsParse("0");
+
+ if (de == NULL || de->flags != 0) {
+ printf("de setup: ");
+ goto error;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test test for a valid flags value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FlagsTestParse13 (void)
+{
+ DetectFlagsData *de = NULL;
+ de = DetectFlagsParse("+S*");
+ if (de != NULL) {
+ DetectFlagsFree(de);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test Parse 'C' and 'E' flags.
+ *
+ * \retval 1 on success.
+ * \retval 0 on failure.
+ */
+static int FlagsTestParse14(void)
+{
+ DetectFlagsData *de = DetectFlagsParse("CE");
+ if (de != NULL && (de->flags == (TH_CWR | TH_ECN)) ) {
+ DetectFlagsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int FlagsTestParse15(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ECN | TH_CWR | TH_SYN | TH_RST;
+
+ de = DetectFlagsParse("EC+");
+
+ if (de == NULL || (de->flags != (TH_ECN | TH_CWR)) )
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if (ret) {
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+static int FlagsTestParse16(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ECN | TH_SYN | TH_RST;
+
+ de = DetectFlagsParse("EC*");
+
+ if (de == NULL || (de->flags != (TH_ECN | TH_CWR)) )
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if (ret) {
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test Negative test.
+ */
+static int FlagsTestParse17(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectFlagsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ipv4h;
+ TCPHdr tcph;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ p->ip4h = &ipv4h;
+ p->tcph = &tcph;
+ p->tcph->th_flags = TH_ECN | TH_SYN | TH_RST;
+
+ de = DetectFlagsParse("EC+");
+
+ if (de == NULL || (de->flags != (TH_ECN | TH_CWR)) )
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLAGS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFlagsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if (ret == 0) {
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de)
+ SCFree(de);
+ if (sm)
+ SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for Flags
+ */
+void FlagsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlagsTestParse01", FlagsTestParse01, 1);
+ UtRegisterTest("FlagsTestParse02", FlagsTestParse02, 0);
+ UtRegisterTest("FlagsTestParse03", FlagsTestParse03, 1);
+ UtRegisterTest("FlagsTestParse04", FlagsTestParse04, 0);
+ UtRegisterTest("FlagsTestParse05", FlagsTestParse05, 0);
+ UtRegisterTest("FlagsTestParse06", FlagsTestParse06, 1);
+ UtRegisterTest("FlagsTestParse07", FlagsTestParse07, 0);
+ UtRegisterTest("FlagsTestParse08", FlagsTestParse08, 1);
+ UtRegisterTest("FlagsTestParse09", FlagsTestParse09, 1);
+ UtRegisterTest("FlagsTestParse10", FlagsTestParse10, 1);
+ UtRegisterTest("FlagsTestParse11", FlagsTestParse11, 0);
+ UtRegisterTest("FlagsTestParse12", FlagsTestParse12, 0);
+ UtRegisterTest("FlagsTestParse13", FlagsTestParse13, 1);
+ UtRegisterTest("FlagsTestParse14", FlagsTestParse14, 1);
+ UtRegisterTest("FlagsTestParse15", FlagsTestParse15, 1);
+ UtRegisterTest("FlagsTestParse16", FlagsTestParse16, 1);
+ UtRegisterTest("FlagsTestParse17", FlagsTestParse17, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-flags.h b/framework/src/suricata/src/detect-flags.h
new file mode 100644
index 00000000..0eaaa282
--- /dev/null
+++ b/framework/src/suricata/src/detect-flags.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_FLAGS_H__
+#define __DETECT_FLAGS_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+/**
+ * \struct DetectFlagsData_
+ * DetectFlagsData_ is used to store flags: input value
+ */
+
+/**
+ * \typedef DetectFlagsData
+ * A typedef for DetectFlagsData_
+ */
+
+typedef struct DetectFlagsData_ {
+ uint8_t flags; /**< TCP flags */
+ uint8_t modifier; /**< !(1) +(2) *(3) modifiers */
+ uint8_t ignored_flags; /**< Ignored TCP flags defined by modifer , */
+} DetectFlagsData;
+
+/**
+ * Registration function for flags: keyword
+ */
+
+void DetectFlagsRegister (void);
+
+/**
+ * This function registers unit tests for Flags
+ */
+
+void FlagsRegisterTests(void);
+
+#endif /*__DETECT_FLAGS_H__ */
diff --git a/framework/src/suricata/src/detect-flow.c b/framework/src/suricata/src/detect-flow.c
new file mode 100644
index 00000000..9c2db944
--- /dev/null
+++ b/framework/src/suricata/src/detect-flow.c
@@ -0,0 +1,1127 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * FLOW part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "flow.h"
+#include "flow-var.h"
+
+#include "detect-flow.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/**
+ * \brief Regex for parsing our flow options
+ */
+#define PARSE_REGEX "^\\s*([A-z_]+)\\s*(?:,\\s*([A-z_]+))?\\s*(?:,\\s*([A-z_]+))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectFlowMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFlowSetup (DetectEngineCtx *, Signature *, char *);
+void DetectFlowRegisterTests(void);
+void DetectFlowFree(void *);
+
+/**
+ * \brief Registration function for flow: keyword
+ */
+void DetectFlowRegister (void)
+{
+ sigmatch_table[DETECT_FLOW].name = "flow";
+ sigmatch_table[DETECT_FLOW].desc = "match on direction and state of the flow";
+ sigmatch_table[DETECT_FLOW].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flow-keywords#Flow";
+ sigmatch_table[DETECT_FLOW].Match = DetectFlowMatch;
+ sigmatch_table[DETECT_FLOW].Setup = DetectFlowSetup;
+ sigmatch_table[DETECT_FLOW].Free = DetectFlowFree;
+ sigmatch_table[DETECT_FLOW].RegisterTests = DetectFlowRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+/**
+ * \brief This function is used to match flow flags set on a packet with those passed via flow:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectFlowData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectFlowMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ SCLogDebug("pkt %p", p);
+
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ SCLogDebug("FLOW_PKT_TOSERVER");
+ } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ SCLogDebug("FLOW_PKT_TOCLIENT");
+ }
+
+ if (p->flowflags & FLOW_PKT_ESTABLISHED) {
+ SCLogDebug("FLOW_PKT_ESTABLISHED");
+ }
+
+ uint8_t cnt = 0;
+ const DetectFlowData *fd = (const DetectFlowData *)ctx;
+
+ if ((fd->flags & DETECT_FLOW_FLAG_TOSERVER) && (p->flowflags & FLOW_PKT_TOSERVER)) {
+ cnt++;
+ } else if ((fd->flags & DETECT_FLOW_FLAG_TOCLIENT) && (p->flowflags & FLOW_PKT_TOCLIENT)) {
+ cnt++;
+ }
+
+ if ((fd->flags & DETECT_FLOW_FLAG_ESTABLISHED) && (p->flowflags & FLOW_PKT_ESTABLISHED)) {
+ cnt++;
+ } else if (fd->flags & DETECT_FLOW_FLAG_STATELESS) {
+ cnt++;
+ }
+
+ if (det_ctx->flags & DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH) {
+ if (fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM)
+ cnt++;
+ } else {
+ if (fd->flags & DETECT_FLOW_FLAG_NOSTREAM)
+ cnt++;
+ }
+
+ int ret = (fd->match_cnt == cnt) ? 1 : 0;
+ SCLogDebug("returning %" PRId32 " cnt %" PRIu8 " fd->match_cnt %" PRId32 " fd->flags 0x%02X p->flowflags 0x%02X",
+ ret, cnt, fd->match_cnt, fd->flags, p->flowflags);
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse flow options passed via flow: keyword
+ *
+ * \param flowstr Pointer to the user provided flow options
+ *
+ * \retval fd pointer to DetectFlowData on success
+ * \retval NULL on failure
+ */
+DetectFlowData *DetectFlowParse (char *flowstr)
+{
+ DetectFlowData *fd = NULL;
+ char *args[3] = {NULL,NULL,NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char str1[16] = "", str2[16] = "", str3[16] = "";
+
+ ret = pcre_exec(parse_regex, parse_regex_study, flowstr, strlen(flowstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, flowstr);
+ goto error;
+ }
+
+ if (ret > 1) {
+ res = pcre_copy_substring((char *)flowstr, ov, MAX_SUBSTRINGS, 1, str1, sizeof(str1));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ args[0] = (char *)str1;
+
+ if (ret > 2) {
+ res = pcre_copy_substring((char *)flowstr, ov, MAX_SUBSTRINGS, 2, str2, sizeof(str2));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ args[1] = (char *)str2;
+ }
+ if (ret > 3) {
+ res = pcre_copy_substring((char *)flowstr, ov, MAX_SUBSTRINGS, 3, str3, sizeof(str3));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ args[2] = (char *)str3;
+ }
+ }
+
+ fd = SCMalloc(sizeof(DetectFlowData));
+ if (unlikely(fd == NULL))
+ goto error;
+ fd->flags = 0;
+ fd->match_cnt = 0;
+
+ int i;
+ for (i = 0; i < (ret - 1); i++) {
+ if (args[i]) {
+ /* inspect our options and set the flags */
+ if (strcasecmp(args[i], "established") == 0) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "DETECT_FLOW_FLAG_ESTABLISHED flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_STATELESS) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "DETECT_FLOW_FLAG_STATELESS already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_ESTABLISHED;
+ } else if (strcasecmp(args[i], "stateless") == 0) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "DETECT_FLOW_FLAG_STATELESS flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set DETECT_FLOW_FLAG_STATELESS, DETECT_FLOW_FLAG_ESTABLISHED already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_STATELESS;
+ } else if (strcasecmp(args[i], "to_client") == 0 || strcasecmp(args[i], "from_server") == 0) {
+ if (fd->flags & DETECT_FLOW_FLAG_TOCLIENT) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set DETECT_FLOW_FLAG_TOCLIENT flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_TOSERVER) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set to_client, DETECT_FLOW_FLAG_TOSERVER already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_TOCLIENT;
+ } else if (strcasecmp(args[i], "to_server") == 0 || strcasecmp(args[i], "from_client") == 0){
+ if (fd->flags & DETECT_FLOW_FLAG_TOSERVER) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set DETECT_FLOW_FLAG_TOSERVER flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_TOCLIENT) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set to_server, DETECT_FLOW_FLAG_TO_CLIENT flag already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_TOSERVER;
+ } else if (strcasecmp(args[i], "only_stream") == 0) {
+ if (fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set only_stream flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_NOSTREAM) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set only_stream flag, DETECT_FLOW_FLAG_NOSTREAM already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_ONLYSTREAM;
+ } else if (strcasecmp(args[i], "no_stream") == 0) {
+ if (fd->flags & DETECT_FLOW_FLAG_NOSTREAM) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set no_stream flag is already set");
+ goto error;
+ } else if (fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM) {
+ SCLogError(SC_ERR_FLAGS_MODIFIER, "cannot set no_stream flag, DETECT_FLOW_FLAG_ONLYSTREAM already set");
+ goto error;
+ }
+ fd->flags |= DETECT_FLOW_FLAG_NOSTREAM;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "invalid flow option \"%s\"", args[i]);
+ goto error;
+ }
+
+ fd->match_cnt++;
+ //printf("args[%" PRId32 "]: %s match_cnt: %" PRId32 " flags: 0x%02X\n", i, args[i], fd->match_cnt, fd->flags);
+ }
+ }
+ return fd;
+
+error:
+ if (fd != NULL)
+ DetectFlowFree(fd);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed flowdata into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param flowstr pointer to the user provided flow options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectFlowSetup (DetectEngineCtx *de_ctx, Signature *s, char *flowstr)
+{
+ DetectFlowData *fd = NULL;
+ SigMatch *sm = NULL;
+
+ fd = DetectFlowParse(flowstr);
+ if (fd == NULL)
+ goto error;
+
+ /*ensure only one flow option*/
+ if (s->init_flags & SIG_FLAG_INIT_FLOW) {
+ SCLogError (SC_ERR_INVALID_SIGNATURE, "A signature may have only one flow option.");
+ goto error;
+ }
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLOW;
+ sm->ctx = (SigMatchCtx *)fd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ /* set the signature direction flags */
+ if (fd->flags & DETECT_FLOW_FLAG_TOSERVER) {
+ s->flags |= SIG_FLAG_TOSERVER;
+ } else if (fd->flags & DETECT_FLOW_FLAG_TOCLIENT) {
+ s->flags |= SIG_FLAG_TOCLIENT;
+ } else {
+ s->flags |= SIG_FLAG_TOSERVER;
+ s->flags |= SIG_FLAG_TOCLIENT;
+ }
+ if (fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM) {
+ s->flags |= SIG_FLAG_REQUIRE_STREAM;
+ }
+ if (fd->flags & DETECT_FLOW_FLAG_NOSTREAM) {
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+ } else {
+ s->init_flags |= SIG_FLAG_INIT_FLOW;
+ }
+
+ return 0;
+
+error:
+ if (fd != NULL)
+ DetectFlowFree(fd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectFlowData
+ *
+ * \param fd pointer to DetectFlowData
+ */
+void DetectFlowFree(void *ptr)
+{
+ DetectFlowData *fd = (DetectFlowData *)ptr;
+ SCFree(fd);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectFlowTestParse01 is a test to make sure that we return "something"
+ * when given valid flow opt
+ */
+int DetectFlowTestParse01 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("established");
+ if (fd != NULL) {
+ DetectFlowFree(fd);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse02 is a test for setting the established flow opt
+ */
+int DetectFlowTestParse02 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("established");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_ESTABLISHED && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse03 is a test for setting the stateless flow opt
+ */
+int DetectFlowTestParse03 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("stateless");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_STATELESS && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse04 is a test for setting the to_client flow opt
+ */
+int DetectFlowTestParse04 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("to_client");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOCLIENT, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse05 is a test for setting the to_server flow opt
+ */
+int DetectFlowTestParse05 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("to_server");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOSERVER && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOSERVER, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse06 is a test for setting the from_server flow opt
+ */
+int DetectFlowTestParse06 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOCLIENT, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse07 is a test for setting the from_client flow opt
+ */
+int DetectFlowTestParse07 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_client");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOSERVER && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOSERVER, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse08 is a test for setting the established,to_client flow opts
+ */
+int DetectFlowTestParse08 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("established,to_client");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2) {
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse09 is a test for setting the to_client,stateless flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParse09 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("to_client,stateless");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2) {
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse10 is a test for setting the from_server,stateless flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParse10 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server,stateless");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2){
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse11 is a test for setting the from_server,stateless flow opts with spaces all around
+ */
+int DetectFlowTestParse11 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse(" from_server , stateless ");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2){
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase01 is a test to make sure that we return "something"
+ * when given valid flow opt
+ */
+int DetectFlowTestParseNocase01 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("ESTABLISHED");
+ if (fd != NULL) {
+ DetectFlowFree(fd);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase02 is a test for setting the established flow opt
+ */
+int DetectFlowTestParseNocase02 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("ESTABLISHED");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_ESTABLISHED && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase03 is a test for setting the stateless flow opt
+ */
+int DetectFlowTestParseNocase03 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("STATELESS");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_STATELESS && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase04 is a test for setting the to_client flow opt
+ */
+int DetectFlowTestParseNocase04 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("TO_CLIENT");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOCLIENT, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase05 is a test for setting the to_server flow opt
+ */
+int DetectFlowTestParseNocase05 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("TO_SERVER");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOSERVER && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOSERVER, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase06 is a test for setting the from_server flow opt
+ */
+int DetectFlowTestParseNocase06 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("FROM_SERVER");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOCLIENT, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase07 is a test for setting the from_client flow opt
+ */
+int DetectFlowTestParseNocase07 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("FROM_CLIENT");
+ if (fd != NULL) {
+ if (fd->flags == DETECT_FLOW_FLAG_TOSERVER && fd->match_cnt == 1) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_TOSERVER, 1, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase08 is a test for setting the established,to_client flow opts
+ */
+int DetectFlowTestParseNocase08 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("ESTABLISHED,TO_CLIENT");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2) {
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase09 is a test for setting the to_client,stateless flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParseNocase09 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("TO_CLIENT,STATELESS");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2) {
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase10 is a test for setting the from_server,stateless flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParseNocase10 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("FROM_SERVER,STATELESS");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2){
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase11 is a test for setting the from_server,stateless flow opts with spaces all around
+ */
+int DetectFlowTestParseNocase11 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse(" FROM_SERVER , STATELESS ");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_STATELESS && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->match_cnt == 2){
+ result = 1;
+ } else {
+ printf("expected: 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_STATELESS + DETECT_FLOW_FLAG_TOCLIENT, 2, fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+
+/**
+ * \test DetectFlowTestParse12 is a test for setting an invalid seperator :
+ */
+int DetectFlowTestParse12 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server:stateless");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse13 is a test for an invalid option
+ */
+int DetectFlowTestParse13 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("invalidoptiontest");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+/**
+ * \test DetectFlowTestParse14 is a test for a empty option
+ */
+int DetectFlowTestParse14 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse15 is a test for an invalid combo of options established,stateless
+ */
+int DetectFlowTestParse15 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("established,stateless");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse16 is a test for an invalid combo of options to_client,to_server
+ */
+int DetectFlowTestParse16 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("to_client,to_server");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse16 is a test for an invalid combo of options to_client,from_server
+ * flowbit flags are the same
+ */
+int DetectFlowTestParse17 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("to_client,from_server");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse18 is a test for setting the from_server,stateless,only_stream flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParse18 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server,established,only_stream");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM && fd->match_cnt == 3) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT + DETECT_FLOW_FLAG_ONLYSTREAM, 3,
+ fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParseNocase18 is a test for setting the from_server,stateless,only_stream flow opts (order of state,dir reversed)
+ */
+int DetectFlowTestParseNocase18 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("FROM_SERVER,ESTABLISHED,ONLY_STREAM");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->flags & DETECT_FLOW_FLAG_ONLYSTREAM && fd->match_cnt == 3) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT + DETECT_FLOW_FLAG_ONLYSTREAM, 3,
+ fd->flags, fd->match_cnt);
+ }
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+
+/**
+ * \test DetectFlowTestParse19 is a test for one to many options passed to DetectFlowParse
+ */
+int DetectFlowTestParse19 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server,established,only_stream,a");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse20 is a test for setting from_server, established, no_stream
+ */
+int DetectFlowTestParse20 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server,established,no_stream");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->flags & DETECT_FLOW_FLAG_NOSTREAM && fd->match_cnt == 3) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT + DETECT_FLOW_FLAG_NOSTREAM, 3,
+ fd->flags, fd->match_cnt);
+ }
+
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse20 is a test for setting from_server, established, no_stream
+ */
+int DetectFlowTestParseNocase20 (void)
+{
+ int result = 0;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("FROM_SERVER,ESTABLISHED,NO_STREAM");
+ if (fd != NULL) {
+ if (fd->flags & DETECT_FLOW_FLAG_ESTABLISHED && fd->flags & DETECT_FLOW_FLAG_TOCLIENT && fd->flags & DETECT_FLOW_FLAG_NOSTREAM && fd->match_cnt == 3) {
+ result = 1;
+ } else {
+ printf("expected 0x%02X cnt %" PRId32 " got 0x%02X cnt %" PRId32 ": ", DETECT_FLOW_FLAG_ESTABLISHED + DETECT_FLOW_FLAG_TOCLIENT + DETECT_FLOW_FLAG_NOSTREAM, 3,
+ fd->flags, fd->match_cnt);
+ }
+
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectFlowTestParse21 is a test for an invalid opt between to valid opts
+ */
+int DetectFlowTestParse21 (void)
+{
+ int result = 1;
+ DetectFlowData *fd = NULL;
+ fd = DetectFlowParse("from_server,a,no_stream");
+ if (fd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId32 ": ",fd->flags, fd->match_cnt);
+ result = 0;
+ DetectFlowFree(fd);
+ }
+
+ return result;
+}
+
+static int DetectFlowSigTest01(void)
+{
+ int result = 0;
+ ThreadVars th_v;
+ DecodeThreadVars dtv;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ uint8_t *buf = (uint8_t *)"supernovaduper";
+ uint16_t buflen = strlen((char *)buf);
+
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ if (p->flow != NULL) {
+ printf("packet has flow set\n");
+ goto end;
+ }
+
+ char *sig1 = "alert tcp any any -> any any (msg:\"dummy\"; "
+ "content:\"nova\"; flow:no_stream; sid:1;)";
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx == NULL: ");
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig1);
+ if (de_ctx->sig_list == NULL) {
+ printf("signature == NULL: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if (p != NULL)
+ UTHFreePacket(p);
+
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFlow
+ */
+void DetectFlowRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFlowTestParse01", DetectFlowTestParse01, 1);
+ UtRegisterTest("DetectFlowTestParse02", DetectFlowTestParse02, 1);
+ UtRegisterTest("DetectFlowTestParse03", DetectFlowTestParse03, 1);
+ UtRegisterTest("DetectFlowTestParse04", DetectFlowTestParse04, 1);
+ UtRegisterTest("DetectFlowTestParse05", DetectFlowTestParse05, 1);
+ UtRegisterTest("DetectFlowTestParse06", DetectFlowTestParse06, 1);
+ UtRegisterTest("DetectFlowTestParse07", DetectFlowTestParse07, 1);
+ UtRegisterTest("DetectFlowTestParse08", DetectFlowTestParse08, 1);
+ UtRegisterTest("DetectFlowTestParse09", DetectFlowTestParse09, 1);
+ UtRegisterTest("DetectFlowTestParse10", DetectFlowTestParse10, 1);
+ UtRegisterTest("DetectFlowTestParse11", DetectFlowTestParse11, 1);
+ UtRegisterTest("DetectFlowTestParseNocase01", DetectFlowTestParseNocase01, 1);
+ UtRegisterTest("DetectFlowTestParseNocase02", DetectFlowTestParseNocase02, 1);
+ UtRegisterTest("DetectFlowTestParseNocase03", DetectFlowTestParseNocase03, 1);
+ UtRegisterTest("DetectFlowTestParseNocase04", DetectFlowTestParseNocase04, 1);
+ UtRegisterTest("DetectFlowTestParseNocase05", DetectFlowTestParseNocase05, 1);
+ UtRegisterTest("DetectFlowTestParseNocase06", DetectFlowTestParseNocase06, 1);
+ UtRegisterTest("DetectFlowTestParseNocase07", DetectFlowTestParseNocase07, 1);
+ UtRegisterTest("DetectFlowTestParseNocase08", DetectFlowTestParseNocase08, 1);
+ UtRegisterTest("DetectFlowTestParseNocase09", DetectFlowTestParseNocase09, 1);
+ UtRegisterTest("DetectFlowTestParseNocase10", DetectFlowTestParseNocase10, 1);
+ UtRegisterTest("DetectFlowTestParseNocase11", DetectFlowTestParseNocase11, 1);
+ UtRegisterTest("DetectFlowTestParse12", DetectFlowTestParse12, 1);
+ UtRegisterTest("DetectFlowTestParse13", DetectFlowTestParse13, 1);
+ UtRegisterTest("DetectFlowTestParse14", DetectFlowTestParse14, 1);
+ UtRegisterTest("DetectFlowTestParse15", DetectFlowTestParse15, 1);
+ UtRegisterTest("DetectFlowTestParse16", DetectFlowTestParse16, 1);
+ UtRegisterTest("DetectFlowTestParse17", DetectFlowTestParse17, 1);
+ UtRegisterTest("DetectFlowTestParse18", DetectFlowTestParse18, 1);
+ UtRegisterTest("DetectFlowTestParseNocase18", DetectFlowTestParseNocase18, 1);
+ UtRegisterTest("DetectFlowTestParse19", DetectFlowTestParse19, 1);
+ UtRegisterTest("DetectFlowTestParse20", DetectFlowTestParse20, 1);
+ UtRegisterTest("DetectFlowTestParseNocase20", DetectFlowTestParseNocase20, 1);
+ UtRegisterTest("DetectFlowTestParse21", DetectFlowTestParse21, 1);
+
+ UtRegisterTest("DetectFlowSigTest01", DetectFlowSigTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-flow.h b/framework/src/suricata/src/detect-flow.h
new file mode 100644
index 00000000..b3774c29
--- /dev/null
+++ b/framework/src/suricata/src/detect-flow.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FLOW_H__
+#define __DETECT_FLOW_H__
+
+#define DETECT_FLOW_FLAG_TOSERVER 0x01
+#define DETECT_FLOW_FLAG_TOCLIENT 0x02
+#define DETECT_FLOW_FLAG_ESTABLISHED 0x04
+#define DETECT_FLOW_FLAG_STATELESS 0x08
+#define DETECT_FLOW_FLAG_ONLYSTREAM 0x10
+#define DETECT_FLOW_FLAG_NOSTREAM 0x20
+
+typedef struct DetectFlowData_ {
+ uint8_t flags; /* flags to match */
+ uint8_t match_cnt; /* number of matches we need */
+} DetectFlowData;
+
+/* prototypes */
+void DetectFlowRegister (void);
+
+#endif /* __DETECT_FLOW_H__ */
+
diff --git a/framework/src/suricata/src/detect-flowbits.c b/framework/src/suricata/src/detect-flowbits.c
new file mode 100644
index 00000000..2c10f499
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowbits.c
@@ -0,0 +1,1156 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the flowbits keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-bit.h"
+#include "flow-util.h"
+#include "detect-flowbits.h"
+#include "util-spm.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow-bit.h"
+#include "util-var-name.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "([a-z]+)(?:,\\s*([^\\s]*))?"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectFlowbitMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFlowbitSetup (DetectEngineCtx *, Signature *, char *);
+void DetectFlowbitFree (void *);
+void FlowBitsRegisterTests(void);
+
+void DetectFlowbitsRegister (void)
+{
+ sigmatch_table[DETECT_FLOWBITS].name = "flowbits";
+ sigmatch_table[DETECT_FLOWBITS].desc = "operate on flow flag";
+ sigmatch_table[DETECT_FLOWBITS].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flow-keywords#Flowbits";
+ sigmatch_table[DETECT_FLOWBITS].Match = DetectFlowbitMatch;
+ sigmatch_table[DETECT_FLOWBITS].Setup = DetectFlowbitSetup;
+ sigmatch_table[DETECT_FLOWBITS].Free = DetectFlowbitFree;
+ sigmatch_table[DETECT_FLOWBITS].RegisterTests = FlowBitsRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_FLOWBITS].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+
+static int DetectFlowbitMatchToggle (Packet *p, const DetectFlowbitsData *fd, const int flow_locked)
+{
+ if (p->flow == NULL)
+ return 0;
+
+ if (flow_locked)
+ FlowBitToggleNoLock(p->flow,fd->idx);
+ else
+ FlowBitToggle(p->flow,fd->idx);
+ return 1;
+}
+
+static int DetectFlowbitMatchUnset (Packet *p, const DetectFlowbitsData *fd, const int flow_locked)
+{
+ if (p->flow == NULL)
+ return 0;
+
+ if (flow_locked)
+ FlowBitUnsetNoLock(p->flow,fd->idx);
+ else
+ FlowBitUnset(p->flow,fd->idx);
+ return 1;
+}
+
+static int DetectFlowbitMatchSet (Packet *p, const DetectFlowbitsData *fd, const int flow_locked)
+{
+ if (p->flow == NULL)
+ return 0;
+
+ if (flow_locked)
+ FlowBitSetNoLock(p->flow,fd->idx);
+ else
+ FlowBitSet(p->flow,fd->idx);
+ return 1;
+}
+
+static int DetectFlowbitMatchIsset (Packet *p, const DetectFlowbitsData *fd)
+{
+ if (p->flow == NULL)
+ return 0;
+
+ return FlowBitIsset(p->flow,fd->idx);
+}
+
+static int DetectFlowbitMatchIsnotset (Packet *p, const DetectFlowbitsData *fd)
+{
+ if (p->flow == NULL)
+ return 0;
+
+ return FlowBitIsnotset(p->flow,fd->idx);
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+int DetectFlowbitMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectFlowbitsData *fd = (const DetectFlowbitsData *)ctx;
+ if (fd == NULL)
+ return 0;
+ const int flow_locked = det_ctx->flow_locked;
+
+ switch (fd->cmd) {
+ case DETECT_FLOWBITS_CMD_ISSET:
+ return DetectFlowbitMatchIsset(p,fd);
+ case DETECT_FLOWBITS_CMD_ISNOTSET:
+ return DetectFlowbitMatchIsnotset(p,fd);
+ case DETECT_FLOWBITS_CMD_SET:
+ return DetectFlowbitMatchSet(p,fd,flow_locked);
+ case DETECT_FLOWBITS_CMD_UNSET:
+ return DetectFlowbitMatchUnset(p,fd,flow_locked);
+ case DETECT_FLOWBITS_CMD_TOGGLE:
+ return DetectFlowbitMatchToggle(p,fd,flow_locked);
+ default:
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown cmd %" PRIu32 "", fd->cmd);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int DetectFlowbitParse(char *str, char *cmd, int cmd_len, char *name,
+ int name_len)
+{
+ const int max_substrings = 30;
+ int count, rc;
+ int ov[max_substrings];
+
+ count = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, max_substrings);
+ if (count != 2 && count != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "\"%s\" is not a valid setting for flowbits.", str);
+ return 0;
+ }
+
+ rc = pcre_copy_substring((char *)str, ov, max_substrings, 1, cmd, cmd_len);
+ if (rc < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return 0;
+ }
+
+ if (count == 3) {
+ rc = pcre_copy_substring((char *)str, ov, max_substrings, 2, name,
+ name_len);
+ if (rc < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int DetectFlowbitSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectFlowbitsData *cd = NULL;
+ SigMatch *sm = NULL;
+ uint8_t fb_cmd = 0;
+ char fb_cmd_str[16] = "", fb_name[256] = "";
+
+ if (!DetectFlowbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str), fb_name,
+ sizeof(fb_name))) {
+ return -1;
+ }
+
+ if (strcmp(fb_cmd_str,"noalert") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_NOALERT;
+ } else if (strcmp(fb_cmd_str,"isset") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_ISSET;
+ } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_ISNOTSET;
+ } else if (strcmp(fb_cmd_str,"set") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_SET;
+ } else if (strcmp(fb_cmd_str,"unset") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_UNSET;
+ } else if (strcmp(fb_cmd_str,"toggle") == 0) {
+ fb_cmd = DETECT_FLOWBITS_CMD_TOGGLE;
+ } else {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
+ goto error;
+ }
+
+ switch (fb_cmd) {
+ case DETECT_FLOWBITS_CMD_NOALERT:
+ if (strlen(fb_name) != 0)
+ goto error;
+ s->flags |= SIG_FLAG_NOALERT;
+ return 0;
+ case DETECT_FLOWBITS_CMD_ISNOTSET:
+ case DETECT_FLOWBITS_CMD_ISSET:
+ case DETECT_FLOWBITS_CMD_SET:
+ case DETECT_FLOWBITS_CMD_UNSET:
+ case DETECT_FLOWBITS_CMD_TOGGLE:
+ default:
+ if (strlen(fb_name) == 0)
+ goto error;
+ break;
+ }
+
+ cd = SCMalloc(sizeof(DetectFlowbitsData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ cd->idx = VariableNameGetIdx(de_ctx, fb_name, VAR_TYPE_FLOW_BIT);
+ cd->cmd = fb_cmd;
+
+ SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
+ cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLOWBITS;
+ sm->ctx = (SigMatchCtx *)cd;
+
+ switch (fb_cmd) {
+ /* case DETECT_FLOWBITS_CMD_NOALERT can't happen here */
+
+ case DETECT_FLOWBITS_CMD_ISNOTSET:
+ case DETECT_FLOWBITS_CMD_ISSET:
+ /* checks, so packet list */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ break;
+
+ case DETECT_FLOWBITS_CMD_SET:
+ case DETECT_FLOWBITS_CMD_UNSET:
+ case DETECT_FLOWBITS_CMD_TOGGLE:
+ /* modifiers, only run when entire sig has matched */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
+ break;
+ }
+
+ return 0;
+
+error:
+ if (cd != NULL)
+ SCFree(cd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectFlowbitFree (void *ptr)
+{
+ DetectFlowbitsData *fd = (DetectFlowbitsData *)ptr;
+
+ if (fd == NULL)
+ return;
+
+ SCFree(fd);
+}
+
+#ifdef UNITTESTS
+
+static int FlowBitsTestParse01(void)
+{
+ int ret = 0;
+ char command[16] = "", name[16] = "";
+
+ /* Single argument version. */
+ if (!DetectFlowbitParse("noalert", command, sizeof(command), name,
+ sizeof(name))) {
+ goto end;
+ }
+ if (strcmp(command, "noalert") != 0) {
+ goto end;
+ }
+
+ /* No leading or trailing spaces. */
+ if (!DetectFlowbitParse("set,flowbit", command, sizeof(command), name,
+ sizeof(name))) {
+ goto end;
+ }
+ if (strcmp(command, "set") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "flowbit") != 0) {
+ goto end;
+ }
+
+ /* Leading space. */
+ if (!DetectFlowbitParse("set, flowbit", command, sizeof(command), name,
+ sizeof(name))) {
+ goto end;
+ }
+ if (strcmp(command, "set") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "flowbit") != 0) {
+ goto end;
+ }
+
+ /* Trailing space. */
+ if (!DetectFlowbitParse("set,flowbit ", command, sizeof(command), name,
+ sizeof(name))) {
+ goto end;
+ }
+ if (strcmp(command, "set") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "flowbit") != 0) {
+ goto end;
+ }
+
+ /* Leading and trailing space. */
+ if (!DetectFlowbitParse("set, flowbit ", command, sizeof(command), name,
+ sizeof(name))) {
+ goto end;
+ }
+ if (strcmp(command, "set") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "flowbit") != 0) {
+ goto end;
+ }
+
+ ret = 1;
+end:
+ return ret;
+}
+
+/**
+ * \test FlowBitsTestSig01 is a test for a valid noalert flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig01(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert,wrongusage; content:\"GET \"; sid:1;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig02 is a test for a valid isset,set,isnotset,unset,toggle flowbits options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig02(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int error_count = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset rule need an option\"; flowbits:isset; content:\"GET \"; sid:1;)");
+
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isnotset rule need an option\"; flowbits:isnotset; content:\"GET \"; sid:2;)");
+
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"set rule need an option\"; flowbits:set; content:\"GET \"; sid:3;)");
+
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"unset rule need an option\"; flowbits:unset; content:\"GET \"; sid:4;)");
+
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"toggle rule need an option\"; flowbits:toggle; content:\"GET \"; sid:5;)");
+
+ if (s == NULL) {
+ error_count++;
+ }
+
+ if(error_count == 5)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p, 3)) {
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p, 4)) {
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p, 5)) {
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig03 is a test for a invalid flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig03(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Unknown cmd\"; flowbits:wrongcmd; content:\"GET \"; sid:1;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig04 is a test check idx value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig04(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; flowbits:isset,fbt; content:\"GET \"; sid:1;)");
+
+ idx = VariableNameGetIdx(de_ctx, "fbt", VAR_TYPE_FLOW_BIT);
+
+ if (s == NULL || idx != 1) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p);
+ return result;
+
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig05 is a test check noalert flag
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig05(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert; content:\"GET \"; sid:1;)");
+
+ if (s == NULL || ((s->flags & SIG_FLAG_NOALERT) != SIG_FLAG_NOALERT)) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig06 is a test set flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig06(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ GenericVar flowvar, *gv = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ p->flow->flowvar = &flowvar;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->flags |= PKT_HAS_FLOW;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ idx = VariableNameGetIdx(de_ctx, "myflow", VAR_TYPE_FLOW_BIT);
+
+ gv = p->flow->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig07 is a test unset flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig07(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ GenericVar flowvar, *gv = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ p->flow->flowvar = &flowvar;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ idx = VariableNameGetIdx(de_ctx, "myflow", VAR_TYPE_FLOW_BIT);
+
+ gv = p->flow->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test FlowBitsTestSig08 is a test toogle flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int FlowBitsTestSig08(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ GenericVar flowvar, *gv = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ p->flow->flowvar = &flowvar;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:toggle,myflow2; sid:11;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ idx = VariableNameGetIdx(de_ctx, "myflow", VAR_TYPE_FLOW_BIT);
+
+ gv = p->flow->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for FlowBits
+ */
+void FlowBitsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlowBitsTestParse01", FlowBitsTestParse01, 1);
+ UtRegisterTest("FlowBitsTestSig01", FlowBitsTestSig01, 0);
+ UtRegisterTest("FlowBitsTestSig02", FlowBitsTestSig02, 0);
+ UtRegisterTest("FlowBitsTestSig03", FlowBitsTestSig03, 0);
+ UtRegisterTest("FlowBitsTestSig04", FlowBitsTestSig04, 1);
+ UtRegisterTest("FlowBitsTestSig05", FlowBitsTestSig05, 1);
+ UtRegisterTest("FlowBitsTestSig06", FlowBitsTestSig06, 1);
+ UtRegisterTest("FlowBitsTestSig07", FlowBitsTestSig07, 0);
+ UtRegisterTest("FlowBitsTestSig08", FlowBitsTestSig08, 0);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-flowbits.h b/framework/src/suricata/src/detect-flowbits.h
new file mode 100644
index 00000000..8961da3f
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowbits.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_FLOWBITS_H__
+#define __DETECT_FLOWBITS_H__
+
+#define DETECT_FLOWBITS_CMD_SET 0
+#define DETECT_FLOWBITS_CMD_TOGGLE 1
+#define DETECT_FLOWBITS_CMD_UNSET 2
+#define DETECT_FLOWBITS_CMD_ISNOTSET 3
+#define DETECT_FLOWBITS_CMD_ISSET 4
+#define DETECT_FLOWBITS_CMD_NOALERT 5
+#define DETECT_FLOWBITS_CMD_MAX 6
+
+typedef struct DetectFlowbitsData_ {
+ uint16_t idx;
+ uint8_t cmd;
+} DetectFlowbitsData;
+
+/* prototypes */
+void DetectFlowbitsRegister (void);
+
+#endif /* __DETECT_FLOWBITS_H__ */
+
diff --git a/framework/src/suricata/src/detect-flowint.c b/framework/src/suricata/src/detect-flowint.c
new file mode 100644
index 00000000..54303cd3
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowint.c
@@ -0,0 +1,2178 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Flowvar management for integer types, part of the detection engine
+ * Keyword: flowint
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-var.h"
+#include "detect-flowint.h"
+#include "util-spm.h"
+#include "util-var-name.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+/* name modifiers value */
+#define PARSE_REGEX "^\\s*([a-zA-Z][\\w\\d_.]+)\\s*,\\s*([+=-]{1}|==|!=|<|<=|>|>=|isset|notset)\\s*,?\\s*([a-zA-Z][\\w\\d]+|[\\d]{1,10})?\\s*$"
+/* Varnames must begin with a letter */
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectFlowintMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectFlowintSetup(DetectEngineCtx *, Signature *, char *);
+void DetectFlowintFree(void *);
+void DetectFlowintRegisterTests(void);
+
+void DetectFlowintRegister(void)
+{
+ sigmatch_table[DETECT_FLOWINT].name = "flowint";
+ sigmatch_table[DETECT_FLOWINT].desc = "operate on a per-flow integer";
+ sigmatch_table[DETECT_FLOWINT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flowint";
+ sigmatch_table[DETECT_FLOWINT].Match = DetectFlowintMatch;
+ sigmatch_table[DETECT_FLOWINT].Setup = DetectFlowintSetup;
+ sigmatch_table[DETECT_FLOWINT].Free = DetectFlowintFree;
+ sigmatch_table[DETECT_FLOWINT].RegisterTests = DetectFlowintRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+error:
+ SCLogInfo("Error registering flowint detection plugin");
+ return;
+}
+
+/**
+ * \brief This function is used to create a flowint, add/substract values,
+ * compare it with other flowints, etc
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the current Signature
+ * \param m pointer to the sigmatch that we will cast into DetectFlowintData
+ *
+ * \retval 0 no match, when a var doesn't exist
+ * \retval 1 match, when a var is initialized well, add/substracted, or a true
+ * condition
+ */
+int DetectFlowintMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const int flow_locked = det_ctx->flow_locked;
+ const DetectFlowintData *sfd = (const DetectFlowintData *)ctx;
+ FlowVar *fv;
+ FlowVar *fvt;
+ uint32_t targetval;
+ int ret = 0;
+
+ if (flow_locked == 0)
+ FLOWLOCK_WRLOCK(p->flow);
+
+ /** ATM If we are going to compare the current var with another
+ * that doesn't exist, the default value will be zero;
+ * if you don't want this behaviour, you can use the keyword
+ * "isset" to make it match or not before using the default
+ * value of zero;
+ * But it is mandatory that the current var exist, otherwise, it will
+ * return zero(not match).
+ */
+ if (sfd->targettype == FLOWINT_TARGET_VAR) {
+ uint16_t tvar_idx = VariableNameGetIdx(det_ctx->de_ctx, sfd->target.tvar.name, VAR_TYPE_FLOW_INT);
+
+ fvt = FlowVarGet(p->flow, tvar_idx);
+ /* We don't have that variable initialized yet */
+ if (fvt == NULL)
+ targetval = 0;
+ else
+ targetval = fvt->data.fv_int.value;
+ } else {
+ targetval = sfd->target.value;
+ }
+
+ SCLogDebug("Our var %s is at idx: %"PRIu16"", sfd->name, sfd->idx);
+
+ if (sfd->modifier == FLOWINT_MODIFIER_SET) {
+ FlowVarAddIntNoLock(p->flow, sfd->idx, targetval);
+ SCLogDebug("Setting %s = %u", sfd->name, targetval);
+ ret = 1;
+ goto end;
+ }
+
+ fv = FlowVarGet(p->flow, sfd->idx);
+
+ if (sfd->modifier == FLOWINT_MODIFIER_ISSET) {
+ SCLogDebug(" Isset %s? = %u", sfd->name,(fv) ? 1 : 0);
+ if (fv != NULL)
+ ret = 1;
+ goto end;
+ }
+
+ if (sfd->modifier == FLOWINT_MODIFIER_NOTSET) {
+ SCLogDebug(" Not set %s? = %u", sfd->name,(fv) ? 0 : 1);
+ if (fv == NULL)
+ ret = 1;
+ goto end;
+ }
+
+ if (fv != NULL && fv->datatype == FLOWVAR_TYPE_INT) {
+ if (sfd->modifier == FLOWINT_MODIFIER_ADD) {
+ SCLogDebug("Adding %u to %s", targetval, sfd->name);
+ FlowVarAddIntNoLock(p->flow, sfd->idx, fv->data.fv_int.value +
+ targetval);
+ ret = 1;
+ goto end;
+ }
+
+ if (sfd->modifier == FLOWINT_MODIFIER_SUB) {
+ SCLogDebug("Substracting %u to %s", targetval, sfd->name);
+ FlowVarAddIntNoLock(p->flow, sfd->idx, fv->data.fv_int.value -
+ targetval);
+ ret = 1;
+ goto end;
+ }
+
+ switch(sfd->modifier) {
+ case FLOWINT_MODIFIER_EQ:
+ SCLogDebug("( %u EQ %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value == targetval);
+ break;
+ case FLOWINT_MODIFIER_NE:
+ SCLogDebug("( %u NE %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value != targetval);
+ break;
+ case FLOWINT_MODIFIER_LT:
+ SCLogDebug("( %u LT %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value < targetval);
+ break;
+ case FLOWINT_MODIFIER_LE:
+ SCLogDebug("( %u LE %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value <= targetval);
+ break;
+ case FLOWINT_MODIFIER_GT:
+ SCLogDebug("( %u GT %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value > targetval);
+ break;
+ case FLOWINT_MODIFIER_GE:
+ SCLogDebug("( %u GE %u )", fv->data.fv_int.value, targetval);
+ ret = (fv->data.fv_int.value >= targetval);
+ break;
+ default:
+ SCLogDebug("Unknown Modifier!");
+#ifdef DEBUG
+ BUG_ON(1);
+#endif
+ }
+ } else {
+ /* allow a add on a non-existing var, it will init to the "add" value,
+ * so implying a 0 set. */
+ if (sfd->modifier == FLOWINT_MODIFIER_ADD) {
+ SCLogDebug("Adding %u to %s (new var)", targetval, sfd->name);
+ FlowVarAddIntNoLock(p->flow, sfd->idx, targetval);
+ ret = 1;
+ } else {
+ SCLogDebug("Var not found!");
+ /* It doesn't exist because it wasn't set
+ * or it is a string var, that we don't compare here
+ */
+ ret = 0;
+ }
+ }
+
+end:
+ if (flow_locked == 0)
+ FLOWLOCK_UNLOCK(p->flow);
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse a flowint option
+ *
+ * \param de_ctx pointer to the engine context
+ * \param rawstr pointer to the string holding the options
+ *
+ * \retval NULL if invalid option
+ * \retval DetectFlowintData pointer with the flowint parsed
+ */
+DetectFlowintData *DetectFlowintParse(DetectEngineCtx *de_ctx, char *rawstr)
+{
+ DetectFlowintData *sfd = NULL;
+ char *varname = NULL;
+ char *varval = NULL;
+ char *modstr = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ uint8_t modifier = FLOWINT_MODIFIER_UNKNOWN;
+ unsigned long long value_long = 0;
+ const char *str_ptr;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for flowint(ret = %d).", rawstr, ret);
+ return NULL;
+ }
+
+ /* Get our flowint varname */
+ res = pcre_get_substring((char *) rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ varname = (char *)str_ptr;
+
+ res = pcre_get_substring((char *) rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ modstr = (char *)str_ptr;
+
+ /* Get the modifier */
+ if (strcmp("=", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_SET;
+ if (strcmp("+", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_ADD;
+ if (strcmp("-", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_SUB;
+
+ if (strcmp("<", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_LT;
+ if (strcmp("<=", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_LE;
+ if (strcmp("!=", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_NE;
+ if (strcmp("==", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_EQ;
+ if (strcmp(">=", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_GE;
+ if (strcmp(">", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_GT;
+ if (strcmp("isset", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_ISSET;
+ if (strcmp("notset", modstr) == 0)
+ modifier = FLOWINT_MODIFIER_NOTSET;
+
+ if (modifier == FLOWINT_MODIFIER_UNKNOWN) {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "Unknown modifier");
+ goto error;
+ }
+
+ sfd = SCMalloc(sizeof(DetectFlowintData));
+ if (unlikely(sfd == NULL))
+ goto error;
+
+ /* If we need another arg, check it out(isset doesn't need another arg) */
+ if (modifier != FLOWINT_MODIFIER_ISSET && modifier != FLOWINT_MODIFIER_NOTSET) {
+ if (ret < 4)
+ goto error;
+
+ res = pcre_get_substring((char *) rawstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ varval = (char *)str_ptr;
+ if (res < 0 || varval == NULL || strcmp(varval, "") == 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (varval[0] >= '0' && varval[0] <= '9') { /* is digit, look at the regexp */
+ sfd->targettype = FLOWINT_TARGET_VAL;
+ value_long = atoll(varval);
+ if (value_long > UINT32_MAX) {
+ SCLogDebug("DetectFlowintParse: Cannot load this value."
+ " Values should be between 0 and %"PRIu32, UINT32_MAX);
+ goto error;
+ }
+ sfd->target.value = (uint32_t) value_long;
+ } else {
+ sfd->targettype = FLOWINT_TARGET_VAR;
+ sfd->target.tvar.name = SCStrdup(varval);
+ if (unlikely(sfd->target.tvar.name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc from strdup failed");
+ goto error;
+ }
+ }
+ } else {
+ sfd->targettype = FLOWINT_TARGET_SELF;
+ }
+
+ /* Set the name of the origin var to modify/compared with the target */
+ sfd->name = SCStrdup(varname);
+ if (unlikely(sfd->name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc from strdup failed");
+ goto error;
+ }
+ if (de_ctx != NULL)
+ sfd->idx = VariableNameGetIdx(de_ctx, varname, VAR_TYPE_FLOW_INT);
+ sfd->modifier = modifier;
+
+ pcre_free_substring(varname);
+ pcre_free_substring(modstr);
+ if (varval)
+ pcre_free_substring(varval);
+ return sfd;
+error:
+ if (varname)
+ pcre_free_substring(varname);
+ if (varval)
+ pcre_free_substring(varval);
+ if (modstr)
+ pcre_free_substring(modstr);
+ if (sfd != NULL)
+ SCFree(sfd);
+ return NULL;
+}
+
+/**
+ * \brief This function is used to set up the SigMatch holding the flowint opt
+ *
+ * \param de_ctx pointer to the engine context
+ * \param s pointer to the current Signature
+ * \param rawstr pointer to the string holding the options
+ *
+ * \retval 0 if all is ok
+ * \retval -1 if we find any problem
+ */
+static int DetectFlowintSetup(DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectFlowintData *sfd = NULL;
+ SigMatch *sm = NULL;
+
+ sfd = DetectFlowintParse(de_ctx, rawstr);
+ if (sfd == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FLOWINT;
+ sm->ctx = (SigMatchCtx *)sfd;
+
+ switch (sfd->modifier) {
+ case FLOWINT_MODIFIER_SET:
+ case FLOWINT_MODIFIER_ADD:
+ case FLOWINT_MODIFIER_SUB:
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
+ break;
+
+ case FLOWINT_MODIFIER_LT:
+ case FLOWINT_MODIFIER_LE:
+ case FLOWINT_MODIFIER_NE:
+ case FLOWINT_MODIFIER_EQ:
+ case FLOWINT_MODIFIER_GE:
+ case FLOWINT_MODIFIER_GT:
+ case FLOWINT_MODIFIER_ISSET:
+ case FLOWINT_MODIFIER_NOTSET:
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ break;
+ default:
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (sfd)
+ DetectFlowintFree(sfd);
+ if (sm)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief This function is used to free the data of DetectFlowintData
+ */
+void DetectFlowintFree(void *tmp)
+{
+ DetectFlowintData *sfd =(DetectFlowintData*) tmp;
+ if (sfd != NULL) {
+ if (sfd->name != NULL)
+ SCFree(sfd->name);
+ if (sfd->targettype == FLOWINT_TARGET_VAR)
+ if (sfd->target.tvar.name != NULL)
+ SCFree(sfd->target.tvar.name);
+ SCFree(sfd);
+ }
+}
+
+/**
+ * \brief This is a helper funtion used for debugging purposes
+ */
+void DetectFlowintPrintData(DetectFlowintData *sfd)
+{
+ if (sfd == NULL) {
+ SCLogDebug("DetectFlowintPrintData: Error, DetectFlowintData == NULL!");
+ return;
+ }
+
+ SCLogDebug("Varname: %s, modifier: %"PRIu8", idx: %"PRIu16" Target: ",
+ sfd->name, sfd->modifier, sfd->idx);
+ switch(sfd->targettype) {
+ case FLOWINT_TARGET_VAR:
+ SCLogDebug("target_var: %s",
+ sfd->target.tvar.name);
+ break;
+ case FLOWINT_TARGET_VAL:
+ SCLogDebug("Value: %"PRIu32"; ", sfd->target.value);
+ break;
+ default :
+ SCLogDebug("DetectFlowintPrintData: Error, Targettype not known!");
+ }
+}
+
+#ifdef UNITTESTS
+/**
+ * \test DetectFlowintTestParseVal01 is a test to make sure that we set the
+ * DetectFlowint correctly for setting a valid target value
+ */
+int DetectFlowintTestParseVal01(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,=,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_SET) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar01 is a test to make sure that we set the
+ * DetectFlowint correctly for setting a valid target variable
+ */
+int DetectFlowintTestParseVar01(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,=,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_SET) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal02 is a test to make sure that we set the
+ * DetectFlowint correctly for adding a valid target value
+ */
+int DetectFlowintTestParseVal02(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,+,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_ADD) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar02 is a test to make sure that we set the
+ * DetectFlowint correctly for adding a valid target variable
+ */
+int DetectFlowintTestParseVar02(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,+,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_ADD) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal03 is a test to make sure that we set the
+ * DetectFlowint correctly for substract a valid target value
+ */
+int DetectFlowintTestParseVal03(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,-,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_SUB) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar03 is a test to make sure that we set the
+ * DetectFlowint correctly for substract a valid target variable
+ */
+int DetectFlowintTestParseVar03(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,-,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_SUB) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+
+/**
+ * \test DetectFlowintTestParseVal04 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if equal to a valid target value
+ */
+int DetectFlowintTestParseVal04(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,==,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_EQ) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar04 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if equal to a valid target variable
+ */
+int DetectFlowintTestParseVar04(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,==,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_EQ) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal05 is a test to make sure that we set the
+ * DetectFlowint correctly for cheking if not equal to a valid target value
+ */
+int DetectFlowintTestParseVal05(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,!=,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_NE) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar05 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if not equal to a valid target variable
+ */
+int DetectFlowintTestParseVar05(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,!=,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_NE) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal06 is a test to make sure that we set the
+ * DetectFlowint correctly for cheking if greater than a valid target value
+ */
+int DetectFlowintTestParseVal06(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, >,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_GT) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar06 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if greater than a valid target variable
+ */
+int DetectFlowintTestParseVar06(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, >,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_GT) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal07 is a test to make sure that we set the
+ * DetectFlowint correctly for cheking if greater or equal than a valid target value
+ */
+int DetectFlowintTestParseVal07(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, >= ,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_GE) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar07 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if greater or equal than a valid target variable
+ */
+int DetectFlowintTestParseVar07(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, >= ,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_GE) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal08 is a test to make sure that we set the
+ * DetectFlowint correctly for cheking if lower or equal than a valid target value
+ */
+int DetectFlowintTestParseVal08(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, <= ,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_LE) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar08 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if lower or equal than a valid target variable
+ */
+int DetectFlowintTestParseVar08(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, <= ,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_LE) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVal09 is a test to make sure that we set the
+ * DetectFlowint correctly for cheking if lower than a valid target value
+ */
+int DetectFlowintTestParseVal09(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, < ,35");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && sfd->target.value == 35 && !strcmp(sfd->name, "myvar")
+ && sfd->modifier == FLOWINT_MODIFIER_LT) {
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar09 is a test to make sure that we set the
+ * DetectFlowint correctly for checking if lower than a valid target variable
+ */
+int DetectFlowintTestParseVar09(void)
+{
+ int result = 0;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, < ,targetvar");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_VAR
+ && sfd->target.tvar.name != NULL
+ && !strcmp(sfd->target.tvar.name, "targetvar")
+ && sfd->modifier == FLOWINT_MODIFIER_LT) {
+
+ result = 1;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseVar09 is a test to make sure that handle the
+ * isset keyword correctly
+ */
+int DetectFlowintTestParseIsset10(void)
+{
+ int result = 1;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar, isset");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_SELF
+ && sfd->modifier == FLOWINT_MODIFIER_ISSET) {
+
+ result &= 1;
+ } else {
+ result = 0;
+ }
+
+ if (sfd) DetectFlowintFree(sfd);
+ sfd = DetectFlowintParse(de_ctx, "myvar, notset");
+ DetectFlowintPrintData(sfd);
+ if (sfd != NULL && !strcmp(sfd->name, "myvar")
+ && sfd->targettype == FLOWINT_TARGET_SELF
+ && sfd->modifier == FLOWINT_MODIFIER_NOTSET) {
+
+ result &= 1;
+ } else {
+ result = 0;
+ }
+
+ if (sfd) DetectFlowintFree(sfd);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestParseInvalidSyntaxis01 is a test to make sure that we dont set the
+ * DetectFlowint for a invalid input option
+ */
+int DetectFlowintTestParseInvalidSyntaxis01(void)
+{
+ int result = 1;
+ DetectFlowintData *sfd = NULL;
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto error;
+ de_ctx->flags |= DE_QUIET;
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,=,9999999999");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,=,9532458716234857");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,=,45targetvar");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,=,45targetvar ");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "657myvar,=,targetvar");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at 657myvar,=,targetvar ");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,=<,targetvar");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,=<,targetvar ");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,===,targetvar");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,===,targetvar ");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,==");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,==");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar,");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar,");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ sfd = DetectFlowintParse(de_ctx, "myvar");
+ if (sfd != NULL) {
+ SCLogDebug("DetectFlowintTestParseInvalidSyntaxis01: ERROR: invalid option at myvar");
+ result = 0;
+ }
+ if (sfd) DetectFlowintFree(sfd);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+error:
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test DetectFlowintTestPacket01Real
+ * \brief Set a counter when we see a content:"GET"
+ * and increment it by 2 if we match a "Unauthorized"
+ * When it reach 3(with the last +2), another counter starts
+ * and when that counter reach 6 packets.
+ *
+ * All the Signatures generate an alert(its for testing)
+ * but the ignature that increment the second counter +1, that has
+ * a "noalert", so we can do all increments
+ * silently until we reach 6 next packets counted
+ */
+int DetectFlowintTestPacket01Real()
+{
+ int result = 1;
+
+ uint8_t pkt1[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0xc2, 0x26, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x67, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb5, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0x16, 0xd0, 0xe8, 0xb0, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x72,
+ 0x40, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt2[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06,
+ 0xb6, 0x8e, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xdd, 0x17, 0x51, 0x82, 0xb6, 0xa0, 0x12,
+ 0x16, 0x80, 0x17, 0x8a, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xac, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x29,
+ 0x23, 0x63, 0x01, 0x72, 0x40, 0x93, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt3[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x27, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6e, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x10,
+ 0x00, 0x2e, 0x5c, 0xa0, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63
+ };
+
+ uint8_t pkt4[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x12, 0xc2, 0x28, 0x40, 0x00, 0x40, 0x06,
+ 0xf3, 0x8f, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x18,
+ 0x00, 0x2e, 0x24, 0x39, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e,
+ 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61,
+ 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x63, 0x73, 0x73, 0x2c, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x73, 0x67, 0x6d, 0x6c, 0x2c,
+ 0x20, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30,
+ 0x2e, 0x30, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f,
+ 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a,
+ 0x69, 0x70, 0x2c, 0x20, 0x62, 0x7a, 0x69, 0x70,
+ 0x32, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70,
+ 0x74, 0x2d, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61,
+ 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x0d, 0x0a,
+ 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x3a, 0x20, 0x4c, 0x79, 0x6e, 0x78,
+ 0x2f, 0x32, 0x2e, 0x38, 0x2e, 0x36, 0x72, 0x65,
+ 0x6c, 0x2e, 0x34, 0x20, 0x6c, 0x69, 0x62, 0x77,
+ 0x77, 0x77, 0x2d, 0x46, 0x4d, 0x2f, 0x32, 0x2e,
+ 0x31, 0x34, 0x20, 0x53, 0x53, 0x4c, 0x2d, 0x4d,
+ 0x4d, 0x2f, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x20,
+ 0x47, 0x4e, 0x55, 0x54, 0x4c, 0x53, 0x2f, 0x32,
+ 0x2e, 0x30, 0x2e, 0x34, 0x0d, 0x0a, 0x0d, 0x0a
+ };
+
+ uint8_t pkt5[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbd, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd9, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x10,
+ 0x00, 0x2d, 0x5b, 0xc3, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x63, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt6[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0xe4, 0xa8, 0xbe, 0x40, 0x00, 0x40, 0x06,
+ 0x0c, 0x28, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x18,
+ 0x00, 0x2d, 0x1b, 0x84, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,
+ 0x2e, 0x31, 0x20, 0x34, 0x30, 0x31, 0x20, 0x55,
+ 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x0d, 0x0a, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x3a, 0x20, 0x6d, 0x69, 0x63,
+ 0x72, 0x6f, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64,
+ 0x0d, 0x0a, 0x43, 0x61, 0x63, 0x68, 0x65, 0x2d,
+ 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3a,
+ 0x20, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68,
+ 0x65, 0x0d, 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a,
+ 0x20, 0x57, 0x65, 0x64, 0x2c, 0x20, 0x31, 0x34,
+ 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x30,
+ 0x39, 0x20, 0x31, 0x33, 0x3a, 0x34, 0x39, 0x3a,
+ 0x35, 0x33, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x57, 0x57, 0x57, 0x2d, 0x41, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x3a, 0x20, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20,
+ 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x3d, 0x22, 0x44,
+ 0x53, 0x4c, 0x20, 0x52, 0x6f, 0x75, 0x74, 0x65,
+ 0x72, 0x22, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
+ 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
+ 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x43, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a,
+ 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x3c, 0x48, 0x54, 0x4d, 0x4c, 0x3e,
+ 0x3c, 0x48, 0x45, 0x41, 0x44, 0x3e, 0x3c, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x34, 0x30, 0x31,
+ 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x64, 0x3c, 0x2f, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x3c, 0x2f, 0x48,
+ 0x45, 0x41, 0x44, 0x3e, 0x0a, 0x3c, 0x42, 0x4f,
+ 0x44, 0x59, 0x20, 0x42, 0x47, 0x43, 0x4f, 0x4c,
+ 0x4f, 0x52, 0x3d, 0x22, 0x23, 0x63, 0x63, 0x39,
+ 0x39, 0x39, 0x39, 0x22, 0x3e, 0x3c, 0x48, 0x34,
+ 0x3e, 0x34, 0x30, 0x31, 0x20, 0x55, 0x6e, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
+ 0x64, 0x3c, 0x2f, 0x48, 0x34, 0x3e, 0x0a, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x3c,
+ 0x48, 0x52, 0x3e, 0x0a, 0x3c, 0x41, 0x44, 0x44,
+ 0x52, 0x45, 0x53, 0x53, 0x3e, 0x3c, 0x41, 0x20,
+ 0x48, 0x52, 0x45, 0x46, 0x3d, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61,
+ 0x72, 0x65, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
+ 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64, 0x2f, 0x22,
+ 0x3e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x5f, 0x68,
+ 0x74, 0x74, 0x70, 0x64, 0x3c, 0x2f, 0x41, 0x3e,
+ 0x3c, 0x2f, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53,
+ 0x53, 0x3e, 0x0a, 0x3c, 0x2f, 0x42, 0x4f, 0x44,
+ 0x59, 0x3e, 0x3c, 0x2f, 0x48, 0x54, 0x4d, 0x4c,
+ 0x3e, 0x0a
+ };
+
+ uint8_t pkt7[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x29, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6c, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8e, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xfa, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x9c, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt8[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbf, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8e, 0x17, 0x51, 0x83, 0x94, 0x80, 0x11,
+ 0x00, 0x2d, 0x5a, 0x0b, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt9[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2a, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6b, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xef, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0xa6, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt10[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2b, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6a, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x11,
+ 0x00, 0x36, 0x57, 0x0a, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x43, 0x8a, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt11[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0x10, 0xaf, 0x40, 0x00, 0x40, 0x06,
+ 0xa5, 0xe7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8f, 0x17, 0x51, 0x83, 0x95, 0x80, 0x10,
+ 0x00, 0x2d, 0x54, 0xbb, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x25, 0xc2, 0x01, 0x72,
+ 0x43, 0x8a
+ };
+
+ uint8_t *pkts[] = {
+ pkt1, pkt2, pkt3, pkt4, pkt5, pkt6, pkt7, pkt8,
+ pkt9, pkt10, pkt11
+ };
+
+ uint16_t pktssizes[] = {
+ sizeof(pkt1), sizeof(pkt2), sizeof(pkt3), sizeof(pkt4), sizeof(pkt5),
+ sizeof(pkt6), sizeof(pkt7), sizeof(pkt8), sizeof(pkt9), sizeof(pkt10),
+ sizeof(pkt11)
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ /* Now that we have the array of packets for the flow, prepare the signatures */
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Setting a flowint counter\"; content:\"GET\"; flowint:myvar,=,1; flowint:maxvar,=,6; sid:101;)");
+
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Adding to flowint counter\"; content:\"Unauthorized\"; flowint: myvar,+,2; sid:102;)");
+
+ de_ctx->sig_list->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"if the flowint counter is 3 create a new counter\"; content:\"Unauthorized\"; flowint: myvar,==,3; flowint: cntpackets, =, 0; sid:103;)");
+
+ de_ctx->sig_list->next->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"and count the rest of the packets received without generating alerts!!!\"; flowint: myvar,==,3; flowint: cntpackets, +, 1; noalert;sid:104;)");
+
+ /* comparation of myvar with maxvar */
+ de_ctx->sig_list->next->next->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\" and fire this when it reach 6\"; flowint: cntpackets, ==, maxvar; sid:105;)");
+
+ /* I know it's a bit ugly, */
+ de_ctx->sig_list->next->next->next->next->next = NULL;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx);
+
+ /* Decode the packets, and test the matches*/
+ int i;
+ for (i = 0;i < 11;i++) {
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+ DecodeEthernet(&th_v, &dtv, p, pkts[i], pktssizes[i], NULL);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ switch(i) {
+ case 3:
+ if (PacketAlertCheck(p, 101) == 0) {
+ SCLogDebug("Not declared/initialized!");
+ result = 0;
+ }
+ break;
+ case 5:
+ if (PacketAlertCheck(p, 102) == 0) {
+ SCLogDebug("Not incremented!");
+ result = 0;
+ }
+
+ if (PacketAlertCheck(p, 103) == 0) {
+ SCLogDebug("myvar is not 3 or bad cmp!!");
+ result = 0;
+ }
+ break;
+ case 10:
+ if (PacketAlertCheck(p, 105) == 0) {
+ SCLogDebug("Not declared/initialized/or well incremented the"
+ " second var!");
+ result = 0;
+ }
+ break;
+ }
+ SCLogDebug("Raw Packet %d has %u alerts ", i, p->alerts.cnt);
+ PACKET_RECYCLE(p);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestPacket02Real
+ * \brief like DetectFlowintTestPacket01Real but using isset/notset keywords
+ */
+int DetectFlowintTestPacket02Real()
+{
+ int result = 1;
+
+ uint8_t pkt1[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0xc2, 0x26, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x67, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb5, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0x16, 0xd0, 0xe8, 0xb0, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x72,
+ 0x40, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt2[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06,
+ 0xb6, 0x8e, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xdd, 0x17, 0x51, 0x82, 0xb6, 0xa0, 0x12,
+ 0x16, 0x80, 0x17, 0x8a, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xac, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x29,
+ 0x23, 0x63, 0x01, 0x72, 0x40, 0x93, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt3[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x27, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6e, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x10,
+ 0x00, 0x2e, 0x5c, 0xa0, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63
+ };
+
+ uint8_t pkt4[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x12, 0xc2, 0x28, 0x40, 0x00, 0x40, 0x06,
+ 0xf3, 0x8f, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x18,
+ 0x00, 0x2e, 0x24, 0x39, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e,
+ 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61,
+ 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x63, 0x73, 0x73, 0x2c, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x73, 0x67, 0x6d, 0x6c, 0x2c,
+ 0x20, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30,
+ 0x2e, 0x30, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f,
+ 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a,
+ 0x69, 0x70, 0x2c, 0x20, 0x62, 0x7a, 0x69, 0x70,
+ 0x32, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70,
+ 0x74, 0x2d, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61,
+ 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x0d, 0x0a,
+ 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x3a, 0x20, 0x4c, 0x79, 0x6e, 0x78,
+ 0x2f, 0x32, 0x2e, 0x38, 0x2e, 0x36, 0x72, 0x65,
+ 0x6c, 0x2e, 0x34, 0x20, 0x6c, 0x69, 0x62, 0x77,
+ 0x77, 0x77, 0x2d, 0x46, 0x4d, 0x2f, 0x32, 0x2e,
+ 0x31, 0x34, 0x20, 0x53, 0x53, 0x4c, 0x2d, 0x4d,
+ 0x4d, 0x2f, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x20,
+ 0x47, 0x4e, 0x55, 0x54, 0x4c, 0x53, 0x2f, 0x32,
+ 0x2e, 0x30, 0x2e, 0x34, 0x0d, 0x0a, 0x0d, 0x0a
+ };
+
+ uint8_t pkt5[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbd, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd9, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x10,
+ 0x00, 0x2d, 0x5b, 0xc3, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x63, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt6[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0xe4, 0xa8, 0xbe, 0x40, 0x00, 0x40, 0x06,
+ 0x0c, 0x28, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x18,
+ 0x00, 0x2d, 0x1b, 0x84, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,
+ 0x2e, 0x31, 0x20, 0x34, 0x30, 0x31, 0x20, 0x55,
+ 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x0d, 0x0a, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x3a, 0x20, 0x6d, 0x69, 0x63,
+ 0x72, 0x6f, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64,
+ 0x0d, 0x0a, 0x43, 0x61, 0x63, 0x68, 0x65, 0x2d,
+ 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3a,
+ 0x20, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68,
+ 0x65, 0x0d, 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a,
+ 0x20, 0x57, 0x65, 0x64, 0x2c, 0x20, 0x31, 0x34,
+ 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x30,
+ 0x39, 0x20, 0x31, 0x33, 0x3a, 0x34, 0x39, 0x3a,
+ 0x35, 0x33, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x57, 0x57, 0x57, 0x2d, 0x41, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x3a, 0x20, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20,
+ 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x3d, 0x22, 0x44,
+ 0x53, 0x4c, 0x20, 0x52, 0x6f, 0x75, 0x74, 0x65,
+ 0x72, 0x22, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
+ 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
+ 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x43, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a,
+ 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x3c, 0x48, 0x54, 0x4d, 0x4c, 0x3e,
+ 0x3c, 0x48, 0x45, 0x41, 0x44, 0x3e, 0x3c, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x34, 0x30, 0x31,
+ 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x64, 0x3c, 0x2f, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x3c, 0x2f, 0x48,
+ 0x45, 0x41, 0x44, 0x3e, 0x0a, 0x3c, 0x42, 0x4f,
+ 0x44, 0x59, 0x20, 0x42, 0x47, 0x43, 0x4f, 0x4c,
+ 0x4f, 0x52, 0x3d, 0x22, 0x23, 0x63, 0x63, 0x39,
+ 0x39, 0x39, 0x39, 0x22, 0x3e, 0x3c, 0x48, 0x34,
+ 0x3e, 0x34, 0x30, 0x31, 0x20, 0x55, 0x6e, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
+ 0x64, 0x3c, 0x2f, 0x48, 0x34, 0x3e, 0x0a, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x3c,
+ 0x48, 0x52, 0x3e, 0x0a, 0x3c, 0x41, 0x44, 0x44,
+ 0x52, 0x45, 0x53, 0x53, 0x3e, 0x3c, 0x41, 0x20,
+ 0x48, 0x52, 0x45, 0x46, 0x3d, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61,
+ 0x72, 0x65, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
+ 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64, 0x2f, 0x22,
+ 0x3e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x5f, 0x68,
+ 0x74, 0x74, 0x70, 0x64, 0x3c, 0x2f, 0x41, 0x3e,
+ 0x3c, 0x2f, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53,
+ 0x53, 0x3e, 0x0a, 0x3c, 0x2f, 0x42, 0x4f, 0x44,
+ 0x59, 0x3e, 0x3c, 0x2f, 0x48, 0x54, 0x4d, 0x4c,
+ 0x3e, 0x0a
+ };
+
+ uint8_t pkt7[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x29, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6c, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8e, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xfa, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x9c, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt8[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbf, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8e, 0x17, 0x51, 0x83, 0x94, 0x80, 0x11,
+ 0x00, 0x2d, 0x5a, 0x0b, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt9[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2a, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6b, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xef, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0xa6, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt10[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2b, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6a, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x11,
+ 0x00, 0x36, 0x57, 0x0a, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x43, 0x8a, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt11[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0x10, 0xaf, 0x40, 0x00, 0x40, 0x06,
+ 0xa5, 0xe7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8f, 0x17, 0x51, 0x83, 0x95, 0x80, 0x10,
+ 0x00, 0x2d, 0x54, 0xbb, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x25, 0xc2, 0x01, 0x72,
+ 0x43, 0x8a
+ };
+
+ uint8_t *pkts[] = {
+ pkt1, pkt2, pkt3, pkt4, pkt5, pkt6, pkt7, pkt8,
+ pkt9, pkt10, pkt11
+ };
+
+ uint16_t pktssizes[] = {
+ sizeof(pkt1), sizeof(pkt2), sizeof(pkt3), sizeof(pkt4), sizeof(pkt5),
+ sizeof(pkt6), sizeof(pkt7), sizeof(pkt8), sizeof(pkt9), sizeof(pkt10),
+ sizeof(pkt11)
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ /* Now that we have the array of packets for the flow, prepare the signatures */
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Setting a flowint counter\"; content:\"GET\"; flowint: myvar, notset; flowint:maxvar,notset; flowint: myvar,=,1; flowint: maxvar,=,6; sid:101;)");
+
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Adding to flowint counter\"; content:\"Unauthorized\"; flowint:myvar,isset; flowint: myvar,+,2; sid:102;)");
+
+ de_ctx->sig_list->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"if the flowint counter is 3 create a new counter\"; content:\"Unauthorized\"; flowint: myvar, isset; flowint: myvar,==,3; flowint:cntpackets,notset; flowint: cntpackets, =, 0; sid:103;)");
+
+ de_ctx->sig_list->next->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"and count the rest of the packets received without generating alerts!!!\"; flowint: cntpackets,isset; flowint: cntpackets, +, 1; noalert;sid:104;)");
+
+ /* comparation of myvar with maxvar */
+ de_ctx->sig_list->next->next->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\" and fire this when it reach 6\"; flowint: cntpackets, isset; flowint: maxvar,isset; flowint: cntpackets, ==, maxvar; sid:105;)");
+
+ /* I know it's a bit ugly, */
+ de_ctx->sig_list->next->next->next->next->next = NULL;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx);
+
+ int i;
+
+ /* Decode the packets, and test the matches*/
+ for (i = 0;i < 11;i++) {
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+ DecodeEthernet(&th_v, &dtv, p, pkts[i], pktssizes[i], NULL);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ switch(i) {
+ case 3:
+ if (PacketAlertCheck(p, 101) == 0) {
+ SCLogDebug("Not declared/initialized!");
+ result = 0;
+ }
+ break;
+ case 5:
+ if (PacketAlertCheck(p, 102) == 0) {
+ SCLogDebug("Not incremented!");
+ result = 0;
+ }
+
+ if (PacketAlertCheck(p, 103) == 0) {
+ SCLogDebug("myvar is not 3 or bad cmp!!");
+ result = 0;
+ }
+ break;
+ case 10:
+ if (PacketAlertCheck(p, 105) == 0) {
+ SCLogDebug("Not declared/initialized/or well incremented the"
+ " second var!");
+ result = 0;
+ }
+ break;
+ }
+ SCLogDebug("Raw Packet %d has %u alerts ", i, p->alerts.cnt);
+ PACKET_RECYCLE(p);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectFlowintTestPacket03Real
+ * \brief Check the behaviour of isset/notset
+ */
+int DetectFlowintTestPacket03Real()
+{
+ int result = 1;
+
+ uint8_t pkt1[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0xc2, 0x26, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x67, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb5, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0x16, 0xd0, 0xe8, 0xb0, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x72,
+ 0x40, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt2[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06,
+ 0xb6, 0x8e, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xdd, 0x17, 0x51, 0x82, 0xb6, 0xa0, 0x12,
+ 0x16, 0x80, 0x17, 0x8a, 0x00, 0x00, 0x02, 0x04,
+ 0x05, 0xac, 0x04, 0x02, 0x08, 0x0a, 0x01, 0x29,
+ 0x23, 0x63, 0x01, 0x72, 0x40, 0x93, 0x01, 0x03,
+ 0x03, 0x07
+ };
+
+ uint8_t pkt3[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x27, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6e, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x10,
+ 0x00, 0x2e, 0x5c, 0xa0, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63
+ };
+
+ uint8_t pkt4[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0x12, 0xc2, 0x28, 0x40, 0x00, 0x40, 0x06,
+ 0xf3, 0x8f, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x82, 0xb6, 0x21, 0x04, 0x8b, 0xde, 0x80, 0x18,
+ 0x00, 0x2e, 0x24, 0x39, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x93, 0x01, 0x29,
+ 0x23, 0x63, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e,
+ 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, 0x78,
+ 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x20,
+ 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61,
+ 0x69, 0x6e, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x63, 0x73, 0x73, 0x2c, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x73, 0x67, 0x6d, 0x6c, 0x2c,
+ 0x20, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30,
+ 0x2e, 0x30, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63,
+ 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f,
+ 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a,
+ 0x69, 0x70, 0x2c, 0x20, 0x62, 0x7a, 0x69, 0x70,
+ 0x32, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70,
+ 0x74, 0x2d, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61,
+ 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x0d, 0x0a,
+ 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x3a, 0x20, 0x4c, 0x79, 0x6e, 0x78,
+ 0x2f, 0x32, 0x2e, 0x38, 0x2e, 0x36, 0x72, 0x65,
+ 0x6c, 0x2e, 0x34, 0x20, 0x6c, 0x69, 0x62, 0x77,
+ 0x77, 0x77, 0x2d, 0x46, 0x4d, 0x2f, 0x32, 0x2e,
+ 0x31, 0x34, 0x20, 0x53, 0x53, 0x4c, 0x2d, 0x4d,
+ 0x4d, 0x2f, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x20,
+ 0x47, 0x4e, 0x55, 0x54, 0x4c, 0x53, 0x2f, 0x32,
+ 0x2e, 0x30, 0x2e, 0x34, 0x0d, 0x0a, 0x0d, 0x0a
+ };
+
+ uint8_t pkt5[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbd, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd9, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x10,
+ 0x00, 0x2d, 0x5b, 0xc3, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x63, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt6[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x01, 0xe4, 0xa8, 0xbe, 0x40, 0x00, 0x40, 0x06,
+ 0x0c, 0x28, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8b, 0xde, 0x17, 0x51, 0x83, 0x94, 0x80, 0x18,
+ 0x00, 0x2d, 0x1b, 0x84, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,
+ 0x2e, 0x31, 0x20, 0x34, 0x30, 0x31, 0x20, 0x55,
+ 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x0d, 0x0a, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x3a, 0x20, 0x6d, 0x69, 0x63,
+ 0x72, 0x6f, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64,
+ 0x0d, 0x0a, 0x43, 0x61, 0x63, 0x68, 0x65, 0x2d,
+ 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3a,
+ 0x20, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68,
+ 0x65, 0x0d, 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a,
+ 0x20, 0x57, 0x65, 0x64, 0x2c, 0x20, 0x31, 0x34,
+ 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x30,
+ 0x39, 0x20, 0x31, 0x33, 0x3a, 0x34, 0x39, 0x3a,
+ 0x35, 0x33, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x57, 0x57, 0x57, 0x2d, 0x41, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x3a, 0x20, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20,
+ 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x3d, 0x22, 0x44,
+ 0x53, 0x4c, 0x20, 0x52, 0x6f, 0x75, 0x74, 0x65,
+ 0x72, 0x22, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
+ 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
+ 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x43, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a,
+ 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x3c, 0x48, 0x54, 0x4d, 0x4c, 0x3e,
+ 0x3c, 0x48, 0x45, 0x41, 0x44, 0x3e, 0x3c, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x34, 0x30, 0x31,
+ 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x64, 0x3c, 0x2f, 0x54,
+ 0x49, 0x54, 0x4c, 0x45, 0x3e, 0x3c, 0x2f, 0x48,
+ 0x45, 0x41, 0x44, 0x3e, 0x0a, 0x3c, 0x42, 0x4f,
+ 0x44, 0x59, 0x20, 0x42, 0x47, 0x43, 0x4f, 0x4c,
+ 0x4f, 0x52, 0x3d, 0x22, 0x23, 0x63, 0x63, 0x39,
+ 0x39, 0x39, 0x39, 0x22, 0x3e, 0x3c, 0x48, 0x34,
+ 0x3e, 0x34, 0x30, 0x31, 0x20, 0x55, 0x6e, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
+ 0x64, 0x3c, 0x2f, 0x48, 0x34, 0x3e, 0x0a, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x3c,
+ 0x48, 0x52, 0x3e, 0x0a, 0x3c, 0x41, 0x44, 0x44,
+ 0x52, 0x45, 0x53, 0x53, 0x3e, 0x3c, 0x41, 0x20,
+ 0x48, 0x52, 0x45, 0x46, 0x3d, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61,
+ 0x72, 0x65, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
+ 0x5f, 0x68, 0x74, 0x74, 0x70, 0x64, 0x2f, 0x22,
+ 0x3e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x5f, 0x68,
+ 0x74, 0x74, 0x70, 0x64, 0x3c, 0x2f, 0x41, 0x3e,
+ 0x3c, 0x2f, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53,
+ 0x53, 0x3e, 0x0a, 0x3c, 0x2f, 0x42, 0x4f, 0x44,
+ 0x59, 0x3e, 0x3c, 0x2f, 0x48, 0x54, 0x4d, 0x4c,
+ 0x3e, 0x0a
+ };
+
+ uint8_t pkt7[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x29, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6c, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8e, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xfa, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0x9c, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt8[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xa8, 0xbf, 0x40, 0x00, 0x40, 0x06,
+ 0x0d, 0xd7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8e, 0x17, 0x51, 0x83, 0x94, 0x80, 0x11,
+ 0x00, 0x2d, 0x5a, 0x0b, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x23, 0x6a, 0x01, 0x72,
+ 0x40, 0x93
+ };
+
+ uint8_t pkt9[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2a, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6b, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x10,
+ 0x00, 0x36, 0x59, 0xef, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x40, 0xa6, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt10[] = {
+ 0x00, 0x1a, 0x2b, 0x19, 0x52, 0xa8, 0x00, 0x13,
+ 0x20, 0x65, 0x1a, 0x9e, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0xc2, 0x2b, 0x40, 0x00, 0x40, 0x06,
+ 0xf4, 0x6a, 0xc0, 0xa8, 0x01, 0xdc, 0xc0, 0xa8,
+ 0x01, 0x01, 0xe7, 0xf5, 0x00, 0x50, 0x17, 0x51,
+ 0x83, 0x94, 0x21, 0x04, 0x8d, 0x8f, 0x80, 0x11,
+ 0x00, 0x36, 0x57, 0x0a, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x72, 0x43, 0x8a, 0x01, 0x29,
+ 0x23, 0x6a
+ };
+
+ uint8_t pkt11[] = {
+ 0x00, 0x13, 0x20, 0x65, 0x1a, 0x9e, 0x00, 0x1a,
+ 0x2b, 0x19, 0x52, 0xa8, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x34, 0x10, 0xaf, 0x40, 0x00, 0x40, 0x06,
+ 0xa5, 0xe7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8,
+ 0x01, 0xdc, 0x00, 0x50, 0xe7, 0xf5, 0x21, 0x04,
+ 0x8d, 0x8f, 0x17, 0x51, 0x83, 0x95, 0x80, 0x10,
+ 0x00, 0x2d, 0x54, 0xbb, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x01, 0x29, 0x25, 0xc2, 0x01, 0x72,
+ 0x43, 0x8a
+ };
+
+ uint8_t *pkts[] = {
+ pkt1, pkt2, pkt3, pkt4, pkt5, pkt6, pkt7, pkt8,
+ pkt9, pkt10, pkt11
+ };
+
+ uint16_t pktssizes[] = {
+ sizeof(pkt1), sizeof(pkt2), sizeof(pkt3), sizeof(pkt4), sizeof(pkt5),
+ sizeof(pkt6), sizeof(pkt7), sizeof(pkt8), sizeof(pkt9), sizeof(pkt10),
+ sizeof(pkt11)
+ };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ /* Now that we have the array of packets for the flow, prepare the signatures */
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"check notset\"; content:\"GET\"; flowint: myvar, notset; flowint: myvar,=,0; flowint: other,=,10; sid:101;)");
+
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"check isset\"; content:\"Unauthorized\"; flowint:myvar,isset; flowint: other,isset; sid:102;)");
+
+ de_ctx->sig_list->next->next = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"check notset\"; content:\"Unauthorized\"; flowint:lala,isset; sid:103;)");
+
+ de_ctx->sig_list->next->next->next = NULL;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx);
+
+ int i;
+
+ /* Decode the packets, and test the matches*/
+ for (i = 0;i < 11;i++) {
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+ DecodeEthernet(&th_v, &dtv, p, pkts[i], pktssizes[i], NULL);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ switch(i) {
+ case 3:
+ if (PacketAlertCheck(p, 101) == 0) {
+ SCLogDebug("Not declared/initialized but match!");
+ result = 0;
+ }
+ if (PacketAlertCheck(p, 103) != 0) {
+ SCLogDebug(" var lala is never set, it should NOT match!!");
+ result = 0;
+ }
+ break;
+ case 5:
+ if (PacketAlertCheck(p, 102) == 0) {
+ SCLogDebug("Not incremented!");
+ result = 0;
+ }
+
+ if (PacketAlertCheck(p, 103) != 0) {
+ SCLogDebug(" var lala is never set, it should NOT match!!");
+ result = 0;
+ }
+ break;
+ }
+ SCLogDebug("Raw Packet %d has %u alerts ", i, p->alerts.cnt);
+ PACKET_RECYCLE(p);
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v,(void *) det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+ SCFree(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFlowint
+ */
+void DetectFlowintRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectFlowintTestParseVal01",
+ DetectFlowintTestParseVal01, 1);
+ UtRegisterTest("DetectFlowintTestParseVar01",
+ DetectFlowintTestParseVar01, 1);
+ UtRegisterTest("DetectFlowintTestParseVal02",
+ DetectFlowintTestParseVal02, 1);
+ UtRegisterTest("DetectFlowintTestParseVar02",
+ DetectFlowintTestParseVar02, 1);
+ UtRegisterTest("DetectFlowintTestParseVal03",
+ DetectFlowintTestParseVal03, 1);
+ UtRegisterTest("DetectFlowintTestParseVar03",
+ DetectFlowintTestParseVar03, 1);
+ UtRegisterTest("DetectFlowintTestParseVal04",
+ DetectFlowintTestParseVal04, 1);
+ UtRegisterTest("DetectFlowintTestParseVar04",
+ DetectFlowintTestParseVar04, 1);
+ UtRegisterTest("DetectFlowintTestParseVal05",
+ DetectFlowintTestParseVal05, 1);
+ UtRegisterTest("DetectFlowintTestParseVar05",
+ DetectFlowintTestParseVar05, 1);
+ UtRegisterTest("DetectFlowintTestParseVal06",
+ DetectFlowintTestParseVal06, 1);
+ UtRegisterTest("DetectFlowintTestParseVar06",
+ DetectFlowintTestParseVar06, 1);
+ UtRegisterTest("DetectFlowintTestParseVal07",
+ DetectFlowintTestParseVal07, 1);
+ UtRegisterTest("DetectFlowintTestParseVar07",
+ DetectFlowintTestParseVar07, 1);
+ UtRegisterTest("DetectFlowintTestParseVal08",
+ DetectFlowintTestParseVal08, 1);
+ UtRegisterTest("DetectFlowintTestParseVar08",
+ DetectFlowintTestParseVar08, 1);
+ UtRegisterTest("DetectFlowintTestParseVal09",
+ DetectFlowintTestParseVal09, 1);
+ UtRegisterTest("DetectFlowintTestParseVar09",
+ DetectFlowintTestParseVar09, 1);
+ UtRegisterTest("DetectFlowintTestParseIsset10",
+ DetectFlowintTestParseIsset10, 1);
+ UtRegisterTest("DetectFlowintTestParseInvalidSyntaxis01",
+ DetectFlowintTestParseInvalidSyntaxis01, 1);
+ UtRegisterTest("DetectFlowintTestPacket01Real",
+ DetectFlowintTestPacket01Real, 1);
+ UtRegisterTest("DetectFlowintTestPacket02Real",
+ DetectFlowintTestPacket02Real, 1);
+ UtRegisterTest("DetectFlowintTestPacket03Real",
+ DetectFlowintTestPacket03Real, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-flowint.h b/framework/src/suricata/src/detect-flowint.h
new file mode 100644
index 00000000..9a18efd5
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowint.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_FLOWINT_H__
+#define __DETECT_FLOWINT_H__
+
+/** Flowint operations allowed */
+enum {
+ /** Changing integer values */
+ FLOWINT_MODIFIER_SET,
+ FLOWINT_MODIFIER_ADD,
+ FLOWINT_MODIFIER_SUB,
+
+ /** Comparing integer values */
+ FLOWINT_MODIFIER_LT,
+ FLOWINT_MODIFIER_LE,
+ FLOWINT_MODIFIER_EQ,
+ FLOWINT_MODIFIER_NE,
+ FLOWINT_MODIFIER_GE,
+ FLOWINT_MODIFIER_GT,
+ /** Checking if a var is set (keyword isset/notset)*/
+ FLOWINT_MODIFIER_ISSET,
+ FLOWINT_MODIFIER_NOTSET,
+
+ FLOWINT_MODIFIER_UNKNOWN
+};
+
+/** The target can be a value, or another variable arleady declared */
+enum {
+ FLOWINT_TARGET_VAL,
+ FLOWINT_TARGET_VAR,
+ FLOWINT_TARGET_SELF,
+ FLOWINT_TARGET_UNKNOWN
+};
+
+/** If the target is another var, get the name and the idx */
+typedef struct TargetVar_ {
+ char *name;
+} TargetVar;
+
+/** Context data for flowint vars */
+typedef struct DetectFlowintData_ {
+ /* This is the main var we are going to use
+ * against the target */
+ char *name;
+ /* Internal id of the var */
+ uint16_t idx;
+
+ /* The modifier/operation/condition we are
+ * going to execute */
+ uint8_t modifier;
+ uint8_t targettype;
+
+ union {
+ /* the target value */
+ uint32_t value;
+ /* or the target var */
+ TargetVar tvar;
+ } target;
+} DetectFlowintData;
+
+/* prototypes */
+void DetectFlowintRegister (void);
+
+#endif /* __DETECT_FLOWINT_H__ */
+
diff --git a/framework/src/suricata/src/detect-flowvar.c b/framework/src/suricata/src/detect-flowvar.c
new file mode 100644
index 00000000..f6ff82d6
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowvar.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Simple flowvar content match part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-content.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-var.h"
+#include "detect-flowvar.h"
+
+#include "util-spm.h"
+#include "util-var-name.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+#define PARSE_REGEX "(.*),(.*)"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectFlowvarMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFlowvarSetup (DetectEngineCtx *, Signature *, char *);
+static int DetectFlowvarPostMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx);
+static void DetectFlowvarDataFree(void *ptr);
+
+void DetectFlowvarRegister (void)
+{
+ sigmatch_table[DETECT_FLOWVAR].name = "flowvar";
+ sigmatch_table[DETECT_FLOWVAR].Match = DetectFlowvarMatch;
+ sigmatch_table[DETECT_FLOWVAR].Setup = DetectFlowvarSetup;
+ sigmatch_table[DETECT_FLOWVAR].Free = DetectFlowvarDataFree;
+ sigmatch_table[DETECT_FLOWVAR].RegisterTests = NULL;
+
+ /* post-match for flowvar storage */
+ sigmatch_table[DETECT_FLOWVAR_POSTMATCH].name = "__flowvar__postmatch__";
+ sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Match = DetectFlowvarPostMatch;
+ sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Setup = NULL;
+ sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Free = DetectFlowvarDataFree;
+ sigmatch_table[DETECT_FLOWVAR_POSTMATCH].RegisterTests = NULL;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief this function will SCFree memory associated with DetectFlowvarData
+ *
+ * \param cd pointer to DetectCotentData
+ */
+static void DetectFlowvarDataFree(void *ptr)
+{
+ if (ptr == NULL)
+ SCReturn;
+
+ DetectFlowvarData *fd = (DetectFlowvarData *)ptr;
+
+ if (fd->name)
+ SCFree(fd->name);
+ if (fd->content)
+ SCFree(fd->content);
+
+ SCFree(fd);
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+int DetectFlowvarMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ DetectFlowvarData *fd = (DetectFlowvarData *)ctx;
+
+ /* we need a lock */
+ FLOWLOCK_RDLOCK(p->flow);
+
+ FlowVar *fv = FlowVarGet(p->flow, fd->idx);
+ if (fv != NULL) {
+ uint8_t *ptr = SpmSearch(fv->data.fv_str.value,
+ fv->data.fv_str.value_len,
+ fd->content, fd->content_len);
+ if (ptr != NULL)
+ ret = 1;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ return ret;
+}
+
+static int DetectFlowvarSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectFlowvarData *fd = NULL;
+ SigMatch *sm = NULL;
+ char *varname = NULL, *varcontent = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr;
+ uint8_t *content = NULL;
+ uint16_t contentlen = 0;
+ uint32_t contentflags = 0;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for flowvar.", rawstr);
+ return -1;
+ }
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return -1;
+ }
+ varname = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return -1;
+ }
+ varcontent = (char *)str_ptr;
+
+ res = DetectContentDataParse("flowvar", varcontent, &content, &contentlen, &contentflags);
+ if (res == -1)
+ goto error;
+
+ fd = SCMalloc(sizeof(DetectFlowvarData));
+ if (unlikely(fd == NULL))
+ goto error;
+ memset(fd, 0x00, sizeof(*fd));
+
+ fd->content = SCMalloc(contentlen);
+ if (unlikely(fd->content == NULL))
+ goto error;
+
+ memcpy(fd->content, content, contentlen);
+ fd->content_len = contentlen;
+ fd->flags = contentflags;
+
+ fd->name = SCStrdup(varname);
+ if (unlikely(fd->name == NULL))
+ goto error;
+ fd->idx = VariableNameGetIdx(de_ctx, varname, VAR_TYPE_FLOW_VAR);
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (unlikely(sm == NULL))
+ goto error;
+
+ sm->type = DETECT_FLOWVAR;
+ sm->ctx = (SigMatchCtx *)fd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ SCFree(content);
+ return 0;
+
+error:
+ if (fd != NULL)
+ DetectFlowvarDataFree(fd);
+ if (sm != NULL)
+ SCFree(sm);
+ if (content != NULL)
+ SCFree(content);
+ return -1;
+}
+
+
+/** \brief Store flowvar in det_ctx so we can exec it post-match */
+int DetectFlowvarStoreMatch(DetectEngineThreadCtx *det_ctx, uint16_t idx,
+ uint8_t *buffer, uint16_t len, int type)
+{
+ DetectFlowvarList *fs = det_ctx->flowvarlist;
+
+ /* first check if we have had a previous match for this idx */
+ for ( ; fs != NULL; fs = fs->next) {
+ if (fs->idx == idx) {
+ /* we're replacing the older store */
+ SCFree(fs->buffer);
+ fs->buffer = NULL;
+ break;
+ }
+ }
+
+ if (fs == NULL) {
+ fs = SCMalloc(sizeof(*fs));
+ if (unlikely(fs == NULL))
+ return -1;
+
+ fs->idx = idx;
+
+ fs->next = det_ctx->flowvarlist;
+ det_ctx->flowvarlist = fs;
+ }
+
+ fs->len = len;
+ fs->type = type;
+ fs->buffer = buffer;
+ return 0;
+}
+
+/** \brief Setup a post-match for flowvar storage
+ * We're piggyback riding the DetectFlowvarData struct
+ */
+int DetectFlowvarPostMatchSetup(Signature *s, uint16_t idx)
+{
+ SigMatch *sm = NULL;
+ DetectFlowvarData *fv = NULL;
+
+ fv = SCMalloc(sizeof(DetectFlowvarData));
+ if (unlikely(fv == NULL))
+ goto error;
+ memset(fv, 0x00, sizeof(*fv));
+
+ /* we only need the idx */
+ fv->idx = idx;
+
+ sm = SigMatchAlloc();
+ if (unlikely(sm == NULL))
+ goto error;
+
+ sm->type = DETECT_FLOWVAR_POSTMATCH;
+ sm->ctx = (SigMatchCtx *)fv;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
+ return 0;
+error:
+ if (fv != NULL)
+ DetectFlowvarDataFree(fv);
+ return -1;
+}
+
+/** \internal
+ * \brief post-match func to store flowvars in the flow
+ * \param sm sigmatch containing the idx to store
+ * \retval 1 or -1 in case of error
+ */
+static int DetectFlowvarPostMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ DetectFlowvarList *fs, *prev;
+ const DetectFlowvarData *fd;
+ const int flow_locked = det_ctx->flow_locked;
+
+ if (det_ctx->flowvarlist == NULL || p->flow == NULL)
+ return 1;
+
+ fd = (const DetectFlowvarData *)ctx;
+
+ prev = NULL;
+ fs = det_ctx->flowvarlist;
+ while (fs != NULL) {
+ if (fd->idx == fs->idx) {
+ SCLogDebug("adding to the flow %u:", fs->idx);
+ //PrintRawDataFp(stdout, fs->buffer, fs->len);
+
+ if (flow_locked)
+ FlowVarAddStrNoLock(p->flow, fs->idx, fs->buffer, fs->len);
+ else
+ FlowVarAddStr(p->flow, fs->idx, fs->buffer, fs->len);
+ /* memory at fs->buffer is now the responsibility of
+ * the flowvar code. */
+
+ if (fs == det_ctx->flowvarlist) {
+ det_ctx->flowvarlist = fs->next;
+ SCFree(fs);
+ fs = det_ctx->flowvarlist;
+ } else {
+ prev->next = fs->next;
+ SCFree(fs);
+ fs = prev->next;
+ }
+ } else {
+ prev = fs;
+ fs = fs->next;
+ }
+ }
+ return 1;
+}
+
+/** \brief Handle flowvar candidate list in det_ctx:
+ * - clean up the list
+ * - enforce storage for type ALWAYS (luajit)
+ * Only called from DetectFlowvarProcessList() when flowvarlist is not NULL .
+ */
+void DetectFlowvarProcessListInternal(DetectFlowvarList *fs, Flow *f, const int flow_locked)
+{
+ DetectFlowvarList *next;
+
+ do {
+ next = fs->next;
+
+ if (fs->type == DETECT_FLOWVAR_TYPE_ALWAYS) {
+ BUG_ON(f == NULL);
+ SCLogDebug("adding to the flow %u:", fs->idx);
+ //PrintRawDataFp(stdout, fs->buffer, fs->len);
+
+ if (flow_locked)
+ FlowVarAddStrNoLock(f, fs->idx, fs->buffer, fs->len);
+ else
+ FlowVarAddStr(f, fs->idx, fs->buffer, fs->len);
+ /* memory at fs->buffer is now the responsibility of
+ * the flowvar code. */
+ } else
+ SCFree(fs->buffer);
+ SCFree(fs);
+ fs = next;
+ } while (fs != NULL);
+}
diff --git a/framework/src/suricata/src/detect-flowvar.h b/framework/src/suricata/src/detect-flowvar.h
new file mode 100644
index 00000000..9e72a5bc
--- /dev/null
+++ b/framework/src/suricata/src/detect-flowvar.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_FLOWVAR_H__
+#define __DETECT_FLOWVAR_H__
+
+typedef struct DetectFlowvarData_ {
+ char *name;
+ uint16_t idx;
+ uint8_t *content;
+ uint8_t content_len;
+ uint8_t flags;
+} DetectFlowvarData;
+
+/* prototypes */
+void DetectFlowvarRegister (void);
+
+int DetectFlowvarPostMatchSetup(Signature *s, uint16_t idx);
+int DetectFlowvarStoreMatch(DetectEngineThreadCtx *, uint16_t, uint8_t *, uint16_t, int);
+
+/* For use only by DetectFlowvarProcessList() */
+void DetectFlowvarProcessListInternal(DetectFlowvarList *fs, Flow *f, const int flow_locked);
+static inline void DetectFlowvarProcessList(DetectEngineThreadCtx *det_ctx, Flow *f)
+{
+ DetectFlowvarList *fs = det_ctx->flowvarlist;
+ const int flow_locked = det_ctx->flow_locked;
+
+ SCLogDebug("det_ctx->flowvarlist %p", fs);
+
+ if (fs != NULL) {
+ det_ctx->flowvarlist = NULL;
+ DetectFlowvarProcessListInternal(fs, f, flow_locked);
+ }
+}
+
+#endif /* __DETECT_FLOWVAR_H__ */
diff --git a/framework/src/suricata/src/detect-fragbits.c b/framework/src/suricata/src/detect-fragbits.c
new file mode 100644
index 00000000..7a564ba0
--- /dev/null
+++ b/framework/src/suricata/src/detect-fragbits.c
@@ -0,0 +1,581 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements fragbits keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "decode-events.h"
+#include "app-layer.h"
+#include "app-layer-detect-proto.h"
+
+#include "detect-fragbits.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+/**
+ * Regex
+ * fragbits: [!+*](MDR)
+ */
+#define PARSE_REGEX "^\\s*(?:([\\+\\*!]))?\\s*([MDR]+)"
+
+/**
+ * FragBits args[0] *(3) +(2) !(1)
+ *
+ */
+
+#define MODIFIER_NOT 1
+#define MODIFIER_PLUS 2
+#define MODIFIER_ANY 3
+
+#define FRAGBITS_HAVE_MF 0x01
+#define FRAGBITS_HAVE_DF 0x02
+#define FRAGBITS_HAVE_RF 0x04
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectFragBitsMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFragBitsSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectFragBitsFree(void *);
+
+/**
+ * \brief Registration function for fragbits: keyword
+ */
+
+void DetectFragBitsRegister (void)
+{
+ sigmatch_table[DETECT_FRAGBITS].name = "fragbits";
+ sigmatch_table[DETECT_FRAGBITS].desc = "check if the fragmentation and reserved bits are set in the IP header";
+ sigmatch_table[DETECT_FRAGBITS].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#Fragbits";
+ sigmatch_table[DETECT_FRAGBITS].Match = DetectFragBitsMatch;
+ sigmatch_table[DETECT_FRAGBITS].Setup = DetectFragBitsSetup;
+ sigmatch_table[DETECT_FRAGBITS].Free = DetectFragBitsFree;
+ sigmatch_table[DETECT_FRAGBITS].RegisterTests = FragBitsRegisterTests;
+
+ const char *eb;
+ int opts = 0;
+ int eo;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+
+}
+
+/**
+ * \internal
+ * \brief This function is used to match fragbits on a packet with those passed via fragbits:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the Signature
+ * \param m pointer to the sigmatch
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectFragBitsMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ uint16_t fragbits = 0;
+ const DetectFragBitsData *de = (const DetectFragBitsData *)ctx;
+
+ if (!de || !PKT_IS_IPV4(p) || !p || PKT_IS_PSEUDOPKT(p))
+ return ret;
+
+ if(IPV4_GET_MF(p))
+ fragbits |= FRAGBITS_HAVE_MF;
+ if(IPV4_GET_DF(p))
+ fragbits |= FRAGBITS_HAVE_DF;
+ if(IPV4_GET_RF(p))
+ fragbits |= FRAGBITS_HAVE_RF;
+
+ switch(de->modifier) {
+ case MODIFIER_ANY:
+ if((fragbits & de->fragbits) > 0)
+ return 1;
+ return ret;
+ case MODIFIER_PLUS:
+ if(((fragbits & de->fragbits) == de->fragbits) && (((fragbits - de->fragbits) > 0)))
+ return 1;
+ return ret;
+ case MODIFIER_NOT:
+ if((fragbits & de->fragbits) != de->fragbits)
+ return 1;
+ return ret;
+ default:
+ if(fragbits == de->fragbits)
+ return 1;
+ }
+
+ return ret;
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse fragbits options passed via fragbits: keyword
+ *
+ * \param rawstr Pointer to the user provided fragbits options
+ *
+ * \retval de pointer to DetectFragBitsData on success
+ * \retval NULL on failure
+ */
+static DetectFragBitsData *DetectFragBitsParse (char *rawstr)
+{
+ DetectFragBitsData *de = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, found = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+ char *args[2] = { NULL, NULL};
+ char *ptr;
+ int i;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ for (i = 0; i < (ret - 1); i++) {
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS,i + 1, &str_ptr);
+
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ args[i] = (char *)str_ptr;
+ }
+
+ if(args[1] == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "invalid value");
+ goto error;
+ }
+
+ de = SCMalloc(sizeof(DetectFragBitsData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ memset(de,0,sizeof(DetectFragBitsData));
+
+ /** First parse args[0] */
+
+ if(args[0]) {
+
+ ptr = args[0];
+
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case '!':
+ de->modifier = MODIFIER_NOT;
+ break;
+ case '+':
+ de->modifier = MODIFIER_PLUS;
+ break;
+ case '*':
+ de->modifier = MODIFIER_ANY;
+ break;
+ }
+ ptr++;
+ }
+
+ }
+
+ /** Second parse first set of fragbits */
+
+ ptr = args[1];
+
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case 'M':
+ case 'm':
+ de->fragbits |= FRAGBITS_HAVE_MF;
+ found++;
+ break;
+ case 'D':
+ case 'd':
+ de->fragbits |= FRAGBITS_HAVE_DF;
+ found++;
+ break;
+ case 'R':
+ case 'r':
+ de->fragbits |= FRAGBITS_HAVE_RF;
+ found++;
+ break;
+ default:
+ found = 0;
+ break;
+ }
+ ptr++;
+ }
+
+ if(found == 0)
+ goto error;
+
+ for (i = 0; i < 2; i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ return de;
+
+error:
+ for (i = 0; i < 2; i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (de != NULL)
+ SCFree(de);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed fragbits into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param rawstr pointer to the user provided fragbits options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectFragBitsData *de = NULL;
+ SigMatch *sm = NULL;
+
+ de = DetectFragBitsParse(rawstr);
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FRAGBITS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectFragBitsData
+ *
+ * \param de pointer to DetectFragBitsData
+ */
+static void DetectFragBitsFree(void *de_ptr)
+{
+ DetectFragBitsData *de = (DetectFragBitsData *)de_ptr;
+ if(de) SCFree(de);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+/**
+ * \test FragBitsTestParse01 is a test for a valid fragbits value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FragBitsTestParse01 (void)
+{
+ DetectFragBitsData *de = NULL;
+ de = DetectFragBitsParse("M");
+ if (de && (de->fragbits == FRAGBITS_HAVE_MF) ) {
+ DetectFragBitsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test FragBitsTestParse02 is a test for an invalid fragbits value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int FragBitsTestParse02 (void)
+{
+ DetectFragBitsData *de = NULL;
+ de = DetectFragBitsParse("G");
+ if (de) {
+ DetectFragBitsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test FragBitsTestParse03 test if DONT FRAG is set. Must return success
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FragBitsTestParse03 (void)
+{
+ uint8_t raw_eth[] = {
+ 0x00 ,0x40 ,0x33 ,0xd9 ,0x7c ,0xfd ,0x00 ,0x00,
+ 0x39 ,0xcf ,0xd9 ,0xcd ,0x08 ,0x00 ,0x45 ,0x00,
+ 0x01 ,0x13 ,0x9c ,0x5d ,0x40 ,0x00 ,0xf6 ,0x11,
+ 0x44 ,0xca ,0x97 ,0xa4 ,0x01 ,0x08 ,0x0a ,0x00,
+ 0x00 ,0x06 ,0x00 ,0x35 ,0x04 ,0x0b ,0x00 ,0xff,
+ 0x3c ,0x87 ,0x7d ,0x9e ,0x85 ,0x80 ,0x00 ,0x01,
+ 0x00 ,0x01 ,0x00 ,0x05 ,0x00 ,0x05 ,0x06 ,0x70,
+ 0x69 ,0x63 ,0x61 ,0x72 ,0x64 ,0x07 ,0x75 ,0x74,
+ 0x68 ,0x73 ,0x63 ,0x73 ,0x61 ,0x03 ,0x65 ,0x64,
+ 0x75 ,0x00 ,0x00 ,0x01 ,0x00 ,0x01 ,0xc0 ,0x0c,
+ 0x00 ,0x01 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10,
+ 0x00 ,0x04 ,0x81 ,0x6f ,0x1e ,0x1b ,0x07 ,0x75,
+ 0x74 ,0x68 ,0x73 ,0x63 ,0x73 ,0x61 ,0x03 ,0x65,
+ 0x64 ,0x75 ,0x00 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x09 ,0x06 ,0x6b ,0x65,
+ 0x6e ,0x6f ,0x62 ,0x69 ,0xc0 ,0x34 ,0xc0 ,0x34,
+ 0x00 ,0x02 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10,
+ 0x00 ,0x07 ,0x04 ,0x6a ,0x69 ,0x6e ,0x6e ,0xc0,
+ 0x34 ,0xc0 ,0x34 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x0c ,0x04 ,0x64 ,0x6e,
+ 0x73 ,0x31 ,0x04 ,0x6e ,0x6a ,0x69 ,0x74 ,0xc0,
+ 0x3c ,0xc0 ,0x34 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x08 ,0x05 ,0x65 ,0x6c,
+ 0x7a ,0x69 ,0x70 ,0xc0 ,0x34 ,0xc0 ,0x34 ,0x00,
+ 0x02 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10 ,0x00,
+ 0x08 ,0x05 ,0x61 ,0x72 ,0x77 ,0x65 ,0x6e ,0xc0,
+ 0x34 ,0xc0 ,0x4b ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x1a,
+ 0x06 ,0xc0 ,0x60 ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x1a,
+ 0x07 ,0xc0 ,0x73 ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x01 ,0x03 ,0x82 ,0x00 ,0x04 ,0x80 ,0xeb ,0xfb,
+ 0x0a ,0xc0 ,0x8b ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x01,
+ 0x0b ,0xc0 ,0x9f ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x0b,
+ 0x51};
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+ DetectFragBitsData *de = NULL;
+ SigMatch *sm = NULL;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ dtv.app_tctx = AppLayerGetCtxThread(&tv);
+
+ p->ip4h = &ipv4h;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+ de = DetectFragBitsParse("D");
+
+ if (de == NULL || (de->fragbits != FRAGBITS_HAVE_DF))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FRAGBITS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFragBitsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ FlowShutdown();
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test FragBitsTestParse04 test if DONT FRAG is not set. Must fails.
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int FragBitsTestParse04 (void)
+{
+ uint8_t raw_eth[] = {
+ 0x00 ,0x40 ,0x33 ,0xd9 ,0x7c ,0xfd ,0x00 ,0x00,
+ 0x39 ,0xcf ,0xd9 ,0xcd ,0x08 ,0x00 ,0x45 ,0x00,
+ 0x01 ,0x13 ,0x9c ,0x5d ,0x40 ,0x00 ,0xf6 ,0x11,
+ 0x44 ,0xca ,0x97 ,0xa4 ,0x01 ,0x08 ,0x0a ,0x00,
+ 0x00 ,0x06 ,0x00 ,0x35 ,0x04 ,0x0b ,0x00 ,0xff,
+ 0x3c ,0x87 ,0x7d ,0x9e ,0x85 ,0x80 ,0x00 ,0x01,
+ 0x00 ,0x01 ,0x00 ,0x05 ,0x00 ,0x05 ,0x06 ,0x70,
+ 0x69 ,0x63 ,0x61 ,0x72 ,0x64 ,0x07 ,0x75 ,0x74,
+ 0x68 ,0x73 ,0x63 ,0x73 ,0x61 ,0x03 ,0x65 ,0x64,
+ 0x75 ,0x00 ,0x00 ,0x01 ,0x00 ,0x01 ,0xc0 ,0x0c,
+ 0x00 ,0x01 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10,
+ 0x00 ,0x04 ,0x81 ,0x6f ,0x1e ,0x1b ,0x07 ,0x75,
+ 0x74 ,0x68 ,0x73 ,0x63 ,0x73 ,0x61 ,0x03 ,0x65,
+ 0x64 ,0x75 ,0x00 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x09 ,0x06 ,0x6b ,0x65,
+ 0x6e ,0x6f ,0x62 ,0x69 ,0xc0 ,0x34 ,0xc0 ,0x34,
+ 0x00 ,0x02 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10,
+ 0x00 ,0x07 ,0x04 ,0x6a ,0x69 ,0x6e ,0x6e ,0xc0,
+ 0x34 ,0xc0 ,0x34 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x0c ,0x04 ,0x64 ,0x6e,
+ 0x73 ,0x31 ,0x04 ,0x6e ,0x6a ,0x69 ,0x74 ,0xc0,
+ 0x3c ,0xc0 ,0x34 ,0x00 ,0x02 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x08 ,0x05 ,0x65 ,0x6c,
+ 0x7a ,0x69 ,0x70 ,0xc0 ,0x34 ,0xc0 ,0x34 ,0x00,
+ 0x02 ,0x00 ,0x01 ,0x00 ,0x00 ,0x0e ,0x10 ,0x00,
+ 0x08 ,0x05 ,0x61 ,0x72 ,0x77 ,0x65 ,0x6e ,0xc0,
+ 0x34 ,0xc0 ,0x4b ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x1a,
+ 0x06 ,0xc0 ,0x60 ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x1a,
+ 0x07 ,0xc0 ,0x73 ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x01 ,0x03 ,0x82 ,0x00 ,0x04 ,0x80 ,0xeb ,0xfb,
+ 0x0a ,0xc0 ,0x8b ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x01,
+ 0x0b ,0xc0 ,0x9f ,0x00 ,0x01 ,0x00 ,0x01 ,0x00,
+ 0x00 ,0x0e ,0x10 ,0x00 ,0x04 ,0x81 ,0x6f ,0x0b,
+ 0x51};
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+ DetectFragBitsData *de = NULL;
+ SigMatch *sm = NULL;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&ipv4h, 0, sizeof(IPV4Hdr));
+ dtv.app_tctx = AppLayerGetCtxThread(&tv);
+
+ p->ip4h = &ipv4h;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+
+ de = DetectFragBitsParse("!D");
+
+ if (de == NULL || (de->fragbits != FRAGBITS_HAVE_DF) || (de->modifier != MODIFIER_NOT))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_FRAGBITS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectFragBitsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+ return 0;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for FragBits
+ */
+void FragBitsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FragBitsTestParse01", FragBitsTestParse01, 1);
+ UtRegisterTest("FragBitsTestParse02", FragBitsTestParse02, 0);
+ UtRegisterTest("FragBitsTestParse03", FragBitsTestParse03, 1);
+ UtRegisterTest("FragBitsTestParse04", FragBitsTestParse04, 0);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-fragbits.h b/framework/src/suricata/src/detect-fragbits.h
new file mode 100644
index 00000000..2fdf0323
--- /dev/null
+++ b/framework/src/suricata/src/detect-fragbits.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_FRAGBITS_H__
+#define __DETECT_FRAGBITS_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+/**
+ * \struct DetectFragBitsData_
+ * DetectFragBitsData_ is used to store fragbits: input value
+ */
+
+/**
+ * \typedef DetectFragBitsData
+ * A typedef for DetectFragBitsData_
+ */
+
+typedef struct DetectFragBitsData_ {
+ uint16_t fragbits; /**< TCP fragbits */
+ uint8_t modifier; /**< !(1) +(2) *(3) modifiers */
+} DetectFragBitsData;
+
+/**
+ * Registration function for fragbits: keyword
+ */
+
+void DetectFragBitsRegister (void);
+
+/**
+ * This function registers unit tests for FragBits
+ */
+
+void FragBitsRegisterTests(void);
+
+#endif /*__DETECT_FRAGBITS_H__ */
diff --git a/framework/src/suricata/src/detect-fragoffset.c b/framework/src/suricata/src/detect-fragoffset.c
new file mode 100644
index 00000000..c887b7b9
--- /dev/null
+++ b/framework/src/suricata/src/detect-fragoffset.c
@@ -0,0 +1,396 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements fragoffset keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "decode-ipv4.h"
+#include "decode-ipv6.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-fragoffset.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "^\\s*(?:(<|>))?\\s*([0-9]+)"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectFragOffsetMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectFragOffsetSetup(DetectEngineCtx *, Signature *, char *);
+void DetectFragOffsetRegisterTests(void);
+void DetectFragOffsetFree(void *);
+
+/**
+ * \brief Registration function for fragoffset
+ */
+void DetectFragOffsetRegister (void)
+{
+ sigmatch_table[DETECT_FRAGOFFSET].name = "fragoffset";
+ sigmatch_table[DETECT_FRAGOFFSET].desc = "match on specific decimal values of the IP fragment offset field";
+ sigmatch_table[DETECT_FRAGOFFSET].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#Fragoffset";
+ sigmatch_table[DETECT_FRAGOFFSET].Match = DetectFragOffsetMatch;
+ sigmatch_table[DETECT_FRAGOFFSET].Setup = DetectFragOffsetSetup;
+ sigmatch_table[DETECT_FRAGOFFSET].Free = DetectFragOffsetFree;
+ sigmatch_table[DETECT_FRAGOFFSET].RegisterTests = DetectFragOffsetRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE,"pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY,"pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match fragoffset rule option set on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectFragOffsetData
+ *
+ * \retval 0 no match or frag is not set
+ * \retval 1 match
+ *
+ */
+int DetectFragOffsetMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ uint16_t frag = 0;
+ const DetectFragOffsetData *fragoff = (const DetectFragOffsetData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_IPV4(p)) {
+ frag = IPV4_GET_IPOFFSET(p);
+ } else if (PKT_IS_IPV6(p)) {
+ if(IPV6_EXTHDR_FH(p)) {
+ frag = IPV6_EXTHDR_GET_FH_OFFSET(p);
+ } else {
+ return 0;
+ }
+ } else {
+ SCLogDebug("No IPv4 or IPv6 packet");
+ return 0;
+ }
+
+ switch (fragoff->mode) {
+ case FRAG_LESS:
+ if (frag < fragoff->frag_off) return 1;
+ break;
+ case FRAG_MORE:
+ if (frag > fragoff->frag_off) return 1;
+ break;
+ default:
+ if (frag == fragoff->frag_off) return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse fragoffset option passed via fragoffset: keyword
+ *
+ * \param fragoffsetstr Pointer to the user provided fragoffset options
+ *
+ * \retval fragoff pointer to DetectFragOffsetData on success
+ * \retval NULL on failure
+ */
+DetectFragOffsetData *DetectFragOffsetParse (char *fragoffsetstr)
+{
+ DetectFragOffsetData *fragoff = NULL;
+ char *substr[3] = {NULL, NULL, NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i;
+ const char *str_ptr;
+ char *mode = NULL;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, fragoffsetstr, strlen(fragoffsetstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH,"Parse error %s", fragoffsetstr);
+ goto error;
+ }
+
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *)fragoffsetstr, ov, MAX_SUBSTRINGS, i, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_get_substring failed");
+ goto error;
+ }
+ substr[i-1] = (char *)str_ptr;
+ }
+
+ fragoff = SCMalloc(sizeof(DetectFragOffsetData));
+ if (unlikely(fragoff == NULL))
+ goto error;
+
+ fragoff->frag_off = 0;
+ fragoff->mode = 0;
+
+ mode = substr[0];
+
+ if(mode != NULL) {
+
+ while(*mode != '\0') {
+ switch(*mode) {
+ case '>':
+ fragoff->mode = FRAG_MORE;
+ break;
+ case '<':
+ fragoff->mode = FRAG_LESS;
+ break;
+ }
+ mode++;
+ }
+ }
+
+ if (ByteExtractStringUint16(&fragoff->frag_off, 10, 0, substr[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified frag offset %s is not "
+ "valid", substr[1]);
+ goto error;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+
+ return fragoff;
+
+error:
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+ if (fragoff != NULL) DetectFragOffsetFree(fragoff);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed fragoffset data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param fragoffsetstr pointer to the user provided fragoffset option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectFragOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, char *fragoffsetstr)
+{
+ DetectFragOffsetData *fragoff = NULL;
+ SigMatch *sm = NULL;
+
+ fragoff = DetectFragOffsetParse(fragoffsetstr);
+ if (fragoff == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) goto error;
+
+ sm->type = DETECT_FRAGOFFSET;
+ sm->ctx = (SigMatchCtx *)fragoff;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (fragoff != NULL) DetectFragOffsetFree(fragoff);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectFragOffsetData
+ *
+ * \param ptr pointer to DetectFragOffsetData
+ */
+void DetectFragOffsetFree (void *ptr)
+{
+ DetectFragOffsetData *fragoff = (DetectFragOffsetData *)ptr;
+ SCFree(fragoff);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectFragOffsetParseTest01 is a test for setting a valid fragoffset value
+ */
+int DetectFragOffsetParseTest01 (void)
+{
+ DetectFragOffsetData *fragoff = NULL;
+ fragoff = DetectFragOffsetParse("300");
+ if (fragoff != NULL && fragoff->frag_off == 300) {
+ DetectFragOffsetFree(fragoff);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFragOffsetParseTest02 is a test for setting a valid fragoffset value
+ * with spaces all around
+ */
+int DetectFragOffsetParseTest02 (void)
+{
+ DetectFragOffsetData *fragoff = NULL;
+ fragoff = DetectFragOffsetParse(">300");
+ if (fragoff != NULL && fragoff->frag_off == 300 && fragoff->mode == FRAG_MORE) {
+ DetectFragOffsetFree(fragoff);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFragOffsetParseTest03 is a test for setting an invalid fragoffset value
+ */
+int DetectFragOffsetParseTest03 (void)
+{
+ DetectFragOffsetData *fragoff = NULL;
+ fragoff = DetectFragOffsetParse("badc");
+ if (fragoff != NULL) {
+ DetectFragOffsetFree(fragoff);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectFragOffsetMatchTest01 is a test for checking the working of
+ * fragoffset keyword by creating 2 rules and matching a crafted packet
+ * against them. Only the first one shall trigger.
+ */
+int DetectFragOffsetMatchTest01 (void)
+{
+ int result = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ IPV4Hdr ip4h;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = 0x01020304;
+ p->dst.addr_data32[0] = 0x04030201;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ ip4h.ip_off = 0x2222;
+ p->ip4h = &ip4h;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert ip any any -> any any (fragoffset:546; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx, "alert ip any any -> any any (fragoffset:5000; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+end:
+ SCFree(p);
+ return result;
+
+}
+#endif /* UNITTESTS */
+
+void DetectFragOffsetRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFragOffsetParseTest01", DetectFragOffsetParseTest01, 1);
+ UtRegisterTest("DetectFragOffsetParseTest02", DetectFragOffsetParseTest02, 1);
+ UtRegisterTest("DetectFragOffsetParseTest03", DetectFragOffsetParseTest03, 0);
+ UtRegisterTest("DetectFragOffsetMatchTest01", DetectFragOffsetMatchTest01, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-fragoffset.h b/framework/src/suricata/src/detect-fragoffset.h
new file mode 100644
index 00000000..5d22e920
--- /dev/null
+++ b/framework/src/suricata/src/detect-fragoffset.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_FRAGOFFSET_H__
+#define __DETECT_FRAGOFFSET_H__
+
+#define FRAG_LESS 1
+#define FRAG_MORE 2
+
+typedef struct DetectFragOffsetData_ {
+ uint16_t frag_off;
+ uint8_t mode;
+} DetectFragOffsetData;
+
+/* prototypes */
+void DetectFragOffsetRegister(void);
+
+#endif /* __DETECT_FRAGOFFSET__ */
diff --git a/framework/src/suricata/src/detect-ftpbounce.c b/framework/src/suricata/src/detect-ftpbounce.c
new file mode 100644
index 00000000..39e1c5a2
--- /dev/null
+++ b/framework/src/suricata/src/detect-ftpbounce.c
@@ -0,0 +1,560 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * ftpbounce keyword, part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-ftp.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+#include "threads.h"
+#include "detect-ftpbounce.h"
+#include "stream-tcp.h"
+#include "util-byte.h"
+
+int DetectFtpbounceMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, SigMatch *);
+int DetectFtpbounceALMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, void *, Signature *, SigMatch *);
+static int DetectFtpbounceSetup(DetectEngineCtx *, Signature *, char *);
+int DetectFtpbounceMatchArgs(uint8_t *payload, uint16_t payload_len,
+ uint32_t ip_orig, uint16_t offset);
+void DetectFtpbounceRegisterTests(void);
+void DetectFtpbounceFree(void *);
+
+/**
+ * \brief Registration function for ftpbounce: keyword
+ * \todo add support for no_stream and stream_only
+ */
+void DetectFtpbounceRegister(void)
+{
+ sigmatch_table[DETECT_FTPBOUNCE].name = "ftpbounce";
+ sigmatch_table[DETECT_FTPBOUNCE].Setup = DetectFtpbounceSetup;
+ sigmatch_table[DETECT_FTPBOUNCE].Match = NULL;
+ sigmatch_table[DETECT_FTPBOUNCE].AppLayerMatch = DetectFtpbounceALMatch;
+ sigmatch_table[DETECT_FTPBOUNCE].alproto = ALPROTO_FTP;
+ sigmatch_table[DETECT_FTPBOUNCE].Free = NULL;
+ sigmatch_table[DETECT_FTPBOUNCE].RegisterTests = DetectFtpbounceRegisterTests;
+ sigmatch_table[DETECT_FTPBOUNCE].flags = SIGMATCH_NOOPT;
+ return;
+}
+
+/**
+ * \brief This function is used to match ftpbounce attacks
+ *
+ * \param payload Payload of the PORT command
+ * \param payload_len Length of the payload
+ * \param ip_orig IP source to check the ftpbounce condition
+ * \param offset offset to the arguments of the PORT command
+ *
+ * \retval 1 if ftpbounce detected, 0 if not
+ */
+int DetectFtpbounceMatchArgs(uint8_t *payload, uint16_t payload_len,
+ uint32_t ip_orig, uint16_t offset)
+{
+ SCEnter();
+ SCLogDebug("Checking ftpbounce condition");
+ char *c = NULL;
+ uint16_t i = 0;
+ int octet = 0;
+ int octet_ascii_len = 0;
+ int noctet = 0;
+ uint32_t ip = 0;
+ /* PrintRawDataFp(stdout, payload, payload_len); */
+
+ if (payload_len < 7) {
+ /* we need at least a differet ip address
+ * in the format 1,2,3,4,x,y where x,y is the port
+ * in two byte representation so let's look at
+ * least for the IP octets in comma separated */
+ return 0;
+ }
+
+ if (offset + 7 >= payload_len)
+ return 0;
+
+ c =(char*) payload;
+ if (c == NULL) {
+ SCLogDebug("No payload to check");
+ return 0;
+ }
+
+ i = offset;
+ /* Search for the first IP octect(Skips "PORT ") */
+ while (i < payload_len && !isdigit((unsigned char)c[i])) i++;
+
+ for (;i < payload_len && octet_ascii_len < 4 ;i++) {
+ if (isdigit((unsigned char)c[i])) {
+ octet =(c[i] - '0') + octet * 10;
+ octet_ascii_len++;
+ } else {
+ if (octet > 256) {
+ SCLogDebug("Octet not in ip format");
+ return 0;
+ }
+
+ if (isspace((unsigned char)c[i]))
+ while (i < payload_len && isspace((unsigned char)c[i]) ) i++;
+
+ if (i < payload_len && c[i] == ',') { /* we have an octet */
+ noctet++;
+ octet_ascii_len = 0;
+ ip =(ip << 8) + octet;
+ octet = 0;
+ } else {
+ SCLogDebug("Unrecognized character '%c'", c[i]);
+ return 0;
+ }
+ if (noctet == 4) {
+ /* Different IP than src, ftp bounce scan */
+ ip = SCByteSwap32(ip);
+
+ if (ip != ip_orig) {
+ SCLogDebug("Different ip, so Matched ip:%d <-> ip_orig:%d",
+ ip, ip_orig);
+ return 1;
+ }
+ SCLogDebug("Same ip, so no match here");
+ return 0;
+ }
+ }
+ }
+ SCLogDebug("No match");
+ return 0;
+}
+
+/**
+ * \brief This function is used to check matches from the FTP App Layer Parser
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch but we don't use it since ftpbounce
+ * has no options
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectFtpbounceALMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state, Signature *s,
+ SigMatch *m)
+{
+ SCEnter();
+ FtpState *ftp_state =(FtpState *)state;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+
+ if (ftp_state->command == FTP_COMMAND_PORT) {
+ ret = DetectFtpbounceMatchArgs(ftp_state->port_line,
+ ftp_state->port_line_len, f->src.address.address_un_data32[0],
+ ftp_state->arg_offset);
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief this function is used to add the parsed ftpbounce
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param ftpbouncestr pointer to the user provided ftpbounce options
+ * currently there are no options.
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectFtpbounceSetup(DetectEngineCtx *de_ctx, Signature *s, char *ftpbouncestr)
+{
+ SCEnter();
+
+ SigMatch *sm = NULL;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ goto error;;
+ }
+
+ sm->type = DETECT_FTPBOUNCE;
+
+ /* We don't need to allocate any data for ftpbounce here.
+ *
+ * TODO: As a suggestion, maybe we can add a flag in the flow
+ * to set the stream as "bounce detected" for fast Match.
+ * When you do a ftp bounce attack you usually use the same
+ * communication control stream to "setup" various destinations
+ * whithout breaking the connection, so I guess we can make it a bit faster
+ * with a flow flag set lookup in the Match function.
+ */
+ sm->ctx = NULL;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_FTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ s->alproto = ALPROTO_FTP;
+ s->flags |= SIG_FLAG_APPLAYER;
+ SCReturnInt(0);
+
+error:
+ if (sm != NULL) {
+ SigMatchFree(sm);
+ }
+ SCReturnInt(-1);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectFtpbounceTestSetup01 is a test for the Setup ftpbounce
+ */
+int DetectFtpbounceTestSetup01(void)
+{
+ int res = 0;
+ DetectEngineCtx *de_ctx = NULL;
+ Signature *s = SigAlloc();
+ if (s == NULL)
+ return 0;
+
+ /* ftpbounce doesn't accept options so the str is NULL */
+ res = !DetectFtpbounceSetup(de_ctx, s, NULL);
+ res &= s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL && s->sm_lists[DETECT_SM_LIST_AMATCH]->type & DETECT_FTPBOUNCE;
+
+ SigFree(s);
+ return res;
+}
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Check the ftpbounce match, send a get request in three chunks
+ * + more data.
+ * \brief This test tests the ftpbounce condition match, based on the
+ * ftp layer parser
+ */
+static int DetectFtpbounceTestALMatch02(void)
+{
+ int result = 0;
+
+ uint8_t ftpbuf1[] = { 'P','O' };
+ uint32_t ftplen1 = sizeof(ftpbuf1);
+ uint8_t ftpbuf2[] = { 'R', 'T' };
+ uint32_t ftplen2 = sizeof(ftpbuf2);
+ uint8_t ftpbuf3[] = { ' ', '8','0',',','5' };
+ uint32_t ftplen3 = sizeof(ftpbuf3);
+ uint8_t ftpbuf4[] = "8,0,33,10,20\r\n";
+ uint32_t ftplen4 = sizeof(ftpbuf4);
+
+ TcpSession ssn;
+ Flow f;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacketSrcDst(NULL, 0, IPPROTO_TCP, "1.2.3.4", "5.6.7.8");
+
+ FLOW_INITIALIZE(&f);
+ f.src.address.address_un_data32[0]=0x01020304;
+ f.protoctx =(void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_FTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"Ftp Bounce\"; ftpbounce; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v,(void *)de_ctx,(void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf1, ftplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf3, ftplen3);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf4, ftplen4);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command port not detected");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v,(void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Check the ftpbounce match
+ * \brief This test tests the ftpbounce condition match, based on
+ * the ftp layer parser
+ */
+static int DetectFtpbounceTestALMatch03(void)
+{
+ int result = 0;
+
+ uint8_t ftpbuf1[] = { 'P','O' };
+ uint32_t ftplen1 = sizeof(ftpbuf1);
+ uint8_t ftpbuf2[] = { 'R', 'T' };
+ uint32_t ftplen2 = sizeof(ftpbuf2);
+ uint8_t ftpbuf3[] = { ' ', '1',',','2',',' };
+ uint32_t ftplen3 = sizeof(ftpbuf3);
+ uint8_t ftpbuf4[] = "3,4,10,20\r\n";
+ uint32_t ftplen4 = sizeof(ftpbuf4);
+
+ TcpSession ssn;
+ Flow f;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->src.addr_data32[0] = 0x04030201;
+ p->payload = NULL;
+ p->payload_len = 0;
+ p->proto = IPPROTO_TCP;
+
+ FLOW_INITIALIZE(&f);
+ f.src.address.address_un_data32[0]=0x04030201;
+ f.protoctx =(void *)&ssn;
+ f.proto = IPPROTO_TCP;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_FTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any "
+ "(msg:\"Ftp Bounce\"; ftpbounce; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v,(void *)de_ctx,(void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf1, ftplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf3, ftplen3);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf4, ftplen4);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ FtpState *ftp_state = f.alstate;
+ if (ftp_state == NULL) {
+ SCLogDebug("no ftp state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (ftp_state->command != FTP_COMMAND_PORT) {
+ SCLogDebug("expected command port not detected");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* It should not match */
+ if (!(PacketAlertCheck(p, 1))) {
+ result = 1;
+ } else {
+ SCLogDebug("It should not match here!");
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v,(void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ SCFree(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectFtpbounce
+ */
+void DetectFtpbounceRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectFtpbounceTestSetup01", DetectFtpbounceTestSetup01, 1);
+ UtRegisterTest("DetectFtpbounceTestALMatch02",
+ DetectFtpbounceTestALMatch02, 1);
+ UtRegisterTest("DetectFtpbounceTestALMatch03",
+ DetectFtpbounceTestALMatch03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-ftpbounce.h b/framework/src/suricata/src/detect-ftpbounce.h
new file mode 100644
index 00000000..f9cdfe39
--- /dev/null
+++ b/framework/src/suricata/src/detect-ftpbounce.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_FTPBOUNCE_H__
+#define __DETECT_FTPBOUNCE_H__
+
+/* prototypes */
+void DetectFtpbounceRegister (void);
+
+#endif /* __DETECT_FTPBOUNCE_H__ */
+
diff --git a/framework/src/suricata/src/detect-geoip.c b/framework/src/suricata/src/detect-geoip.c
new file mode 100644
index 00000000..53c950f8
--- /dev/null
+++ b/framework/src/suricata/src/detect-geoip.c
@@ -0,0 +1,618 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ *
+ * Implements the geoip keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-geoip.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#ifndef HAVE_GEOIP
+
+static int DetectGeoipSetupNoSupport (DetectEngineCtx *a, Signature *b, char *c)
+{
+ SCLogError(SC_ERR_NO_GEOIP_SUPPORT, "no GeoIP support built in, needed for geoip keyword");
+ return -1;
+}
+
+/**
+ * \brief Registration function for geoip keyword (no libgeoip support)
+ * \todo add support for src_only and dst_only
+ */
+void DetectGeoipRegister(void)
+{
+ sigmatch_table[DETECT_GEOIP].name = "geoip";
+ sigmatch_table[DETECT_GEOIP].Setup = DetectGeoipSetupNoSupport;
+ sigmatch_table[DETECT_GEOIP].Free = NULL;
+ sigmatch_table[DETECT_GEOIP].RegisterTests = NULL;
+}
+
+#else /* HAVE_GEOIP */
+
+#include <GeoIP.h>
+
+
+static int DetectGeoipMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectGeoipSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectGeoipRegisterTests(void);
+static void DetectGeoipDataFree(void *);
+
+/**
+ * \brief Registration function for geoip keyword
+ * \todo add support for src_only and dst_only
+ */
+void DetectGeoipRegister(void)
+{
+ sigmatch_table[DETECT_GEOIP].name = "geoip";
+ sigmatch_table[DETECT_GEOIP].Match = DetectGeoipMatch;
+ sigmatch_table[DETECT_GEOIP].Setup = DetectGeoipSetup;
+ sigmatch_table[DETECT_GEOIP].Free = DetectGeoipDataFree;
+ sigmatch_table[DETECT_GEOIP].RegisterTests = DetectGeoipRegisterTests;
+}
+
+/**
+ * \internal
+ * \brief This function is used to initialize the geolocation MaxMind engine
+ *
+ * \retval NULL if the engine couldn't be initialized
+ * \retval (GeoIP *) to the geolocation engine
+ */
+static GeoIP *InitGeolocationEngine(void)
+{
+ return GeoIP_new(GEOIP_MEMORY_CACHE);
+}
+
+/**
+ * \internal
+ * \brief This function is used to geolocate the IP using the MaxMind libraries
+ *
+ * \param ip IP to geolocate (uint32_t ip)
+ *
+ * \retval NULL if it couldn't be geolocated
+ * \retval ptr (const char *) to the country code string
+ */
+static const char *GeolocateIPv4(GeoIP *geoengine, uint32_t ip)
+{
+ if (geoengine != NULL)
+ return GeoIP_country_code_by_ipnum(geoengine, ntohl(ip));
+ return NULL;
+}
+
+/* Match-on conditions supported */
+#define GEOIP_MATCH_SRC_STR "src"
+#define GEOIP_MATCH_DST_STR "dst"
+#define GEOIP_MATCH_BOTH_STR "both"
+#define GEOIP_MATCH_ANY_STR "any"
+
+#define GEOIP_MATCH_NO_FLAG 0
+#define GEOIP_MATCH_SRC_FLAG 1
+#define GEOIP_MATCH_DST_FLAG 2
+#define GEOIP_MATCH_ANY_FLAG 3 /* default src and dst*/
+#define GEOIP_MATCH_BOTH_FLAG 4
+#define GEOIP_MATCH_NEGATED 8
+
+/**
+ * \internal
+ * \brief This function is used to geolocate the IP using the MaxMind libraries
+ *
+ * \param ip IP to geolocate (uint32_t ip)
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int CheckGeoMatchIPv4(const DetectGeoipData *geoipdata, uint32_t ip)
+{
+ const char *country;
+ int i;
+ country = GeolocateIPv4(geoipdata->geoengine, ip);
+ /* Check if NOT NEGATED match-on condition */
+ if ((geoipdata->flags & GEOIP_MATCH_NEGATED) == 0)
+ {
+ for (i = 0; i < geoipdata->nlocations; i++)
+ if (country != NULL && strcmp(country, (char *)geoipdata->location[i])==0)
+ return 1;
+ } else {
+ /* Check if NEGATED match-on condition */
+ for (i = 0; i < geoipdata->nlocations; i++)
+ if (country != NULL && strcmp(country, (char *)geoipdata->location[i])==0)
+ return 0; /* if one matches, rule does NOT match (negated) */
+ return 1; /* returns 1 if no location matches (negated) */
+ }
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief This function is used to match packets with a IPs in an specified country
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectGeoipData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectGeoipMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectGeoipData *geoipdata = (const DetectGeoipData *)ctx;
+ int matches = 0;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_IPV4(p))
+ {
+ if (geoipdata->flags & ( GEOIP_MATCH_SRC_FLAG | GEOIP_MATCH_BOTH_FLAG ))
+ {
+ if (CheckGeoMatchIPv4(geoipdata, GET_IPV4_SRC_ADDR_U32(p)))
+ {
+ if (geoipdata->flags & GEOIP_MATCH_BOTH_FLAG)
+ matches++;
+ else
+ return 1;
+ }
+ }
+ if (geoipdata->flags & ( GEOIP_MATCH_DST_FLAG | GEOIP_MATCH_BOTH_FLAG ))
+ {
+ if (CheckGeoMatchIPv4(geoipdata, GET_IPV4_DST_ADDR_U32(p)))
+ {
+ if (geoipdata->flags & GEOIP_MATCH_BOTH_FLAG)
+ matches++;
+ else
+ return 1;
+ }
+ }
+ /* if matches == 2 is because match-on is "both" */
+ if (matches == 2)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse geoipdata
+ *
+ * \param str Pointer to the geoipdata value string
+ *
+ * \retval pointer to DetectGeoipData on success
+ * \retval NULL on failure
+ */
+static DetectGeoipData *DetectGeoipDataParse (char *str)
+{
+ DetectGeoipData *geoipdata = NULL;
+ uint16_t pos = 0;
+ uint16_t prevpos = 0;
+ uint16_t slen = 0;
+ int skiplocationparsing = 0;
+
+ slen = strlen(str);
+ if (slen == 0)
+ goto error;
+
+ /* We have a correct geoip options string */
+ geoipdata = SCMalloc(sizeof(DetectGeoipData));
+ if (unlikely(geoipdata == NULL))
+ goto error;
+
+ memset(geoipdata, 0x00, sizeof(DetectGeoipData));
+
+ /* Parse the geoip option string */
+ while (pos <= slen)
+ {
+ /* search for ',' or end of string */
+ if (str[pos] == ',' || pos == slen)
+ {
+ if (geoipdata->flags == GEOIP_MATCH_NO_FLAG)
+ {
+ /* Parse match-on condition */
+ if (pos == slen) /* if end of option str then there are no match-on cond. */
+ {
+ /* There was NO match-on condition! we default to ANY*/
+ skiplocationparsing = 0;
+ geoipdata->flags |= GEOIP_MATCH_ANY_FLAG;
+ } else {
+ skiplocationparsing = 1;
+ if (strncmp(&str[prevpos], GEOIP_MATCH_SRC_STR, pos-prevpos) == 0)
+ geoipdata->flags |= GEOIP_MATCH_SRC_FLAG;
+ else if (strncmp(&str[prevpos], GEOIP_MATCH_DST_STR, pos-prevpos) == 0)
+ geoipdata->flags |= GEOIP_MATCH_DST_FLAG;
+ else if (strncmp(&str[prevpos], GEOIP_MATCH_BOTH_STR, pos-prevpos) == 0)
+ geoipdata->flags |= GEOIP_MATCH_BOTH_FLAG;
+ else if (strncmp(&str[prevpos], GEOIP_MATCH_ANY_STR, pos-prevpos) == 0)
+ geoipdata->flags |= GEOIP_MATCH_ANY_FLAG;
+ else {
+ /* There was NO match-on condition! we default to ANY*/
+ skiplocationparsing = 0;
+ geoipdata->flags |= GEOIP_MATCH_ANY_FLAG;
+ }
+ }
+ }
+ if (geoipdata->flags != GEOIP_MATCH_NO_FLAG && skiplocationparsing == 0)
+ {
+ /* Parse location string: for now just the country code(s) */
+ if (str[prevpos] == '!')
+ {
+ geoipdata->flags |= GEOIP_MATCH_NEGATED;
+ prevpos++; /* dot not copy the ! */
+ }
+
+ if (geoipdata->nlocations >= GEOOPTION_MAXLOCATIONS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "too many arguements for geoip keyword");
+ goto error;
+ }
+
+ if (pos-prevpos > GEOOPTION_MAXSIZE)
+ strlcpy((char *)geoipdata->location[geoipdata->nlocations], &str[prevpos],
+ GEOOPTION_MAXSIZE);
+ else
+ strlcpy((char *)geoipdata->location[geoipdata->nlocations], &str[prevpos],
+ pos-prevpos+1);
+
+ if (geoipdata->nlocations < GEOOPTION_MAXLOCATIONS)
+ geoipdata->nlocations++;
+ }
+ prevpos = pos+1;
+ skiplocationparsing = 0; /* match-on condition for sure has been parsed already */
+ }
+ pos++;
+ }
+
+ SCLogDebug("GeoIP: %"PRIu32" countries loaded", geoipdata->nlocations);
+ for (int i=0; i<geoipdata->nlocations; i++)
+ SCLogDebug("GeoIP country code: %s", geoipdata->location[i]);
+
+ SCLogDebug("flags %02X", geoipdata->flags);
+ if (geoipdata->flags & GEOIP_MATCH_NEGATED) {
+ SCLogDebug("negated geoip");
+ }
+
+ /* Initialize the geolocation engine */
+ geoipdata->geoengine = InitGeolocationEngine();
+ if (geoipdata->geoengine == NULL)
+ goto error;
+
+ return geoipdata;
+
+error:
+ if (geoipdata != NULL)
+ DetectGeoipDataFree(geoipdata);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the geoip option into the signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectGeoipSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ DetectGeoipData *geoipdata = NULL;
+ SigMatch *sm = NULL;
+
+ geoipdata = DetectGeoipDataParse(optstr);
+ if (geoipdata == NULL)
+ goto error;
+
+ /* Get this into a SigMatch and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_GEOIP;
+ sm->ctx = (SigMatchCtx *)geoipdata;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (geoipdata != NULL)
+ DetectGeoipDataFree(geoipdata);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectGeoipData
+ *
+ * \param geoipdata pointer to DetectGeoipData
+ */
+static void DetectGeoipDataFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectGeoipData *geoipdata = (DetectGeoipData *)ptr;
+ SCFree(geoipdata);
+ }
+}
+
+#ifdef UNITTESTS
+
+static int GeoipParseTest(char *rule, int ncountries, char **countries, uint32_t flags)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectGeoipData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_MATCH] == NULL) {
+ printf("empty server body list: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_MATCH]->type != DETECT_GEOIP) {
+ printf("last sm not geoip: ");
+ goto end;
+ }
+
+ data = (DetectGeoipData *)s->sm_lists_tail[DETECT_SM_LIST_MATCH]->ctx;
+ if (data->flags != flags) {
+ printf("flags not right: (flags=%d)",data->flags);
+ goto end;
+ }
+
+ if (data->nlocations!=ncountries)
+ {
+ printf("wrong number of parsed countries: ");
+ goto end;
+ }
+
+ for (int i=0; i<ncountries; i++)
+ {
+ if (strcmp((char *)data->location[i],countries[i])!=0)
+ {
+ printf("wrong parsed country code: ");
+ goto end;
+ }
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int GeoipParseTest01(void)
+{
+ char *ccodes[1] = {"US"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:US;sid:1;)", 1, ccodes,
+ GEOIP_MATCH_ANY_FLAG);
+}
+
+static int GeoipParseTest02(void)
+{
+ char *ccodes[1] = {"US"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:!US;sid:1;)", 1, ccodes,
+ GEOIP_MATCH_ANY_FLAG | GEOIP_MATCH_NEGATED);
+}
+
+static int GeoipParseTest03(void)
+{
+ char *ccodes[1] = {"US"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:!US;sid:1;)", 1, ccodes,
+ GEOIP_MATCH_ANY_FLAG | GEOIP_MATCH_NEGATED);
+}
+
+static int GeoipParseTest04(void)
+{
+ char *ccodes[1] = {"US"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:src,US;sid:1;)", 1, ccodes,
+ GEOIP_MATCH_SRC_FLAG);
+}
+
+static int GeoipParseTest05(void)
+{
+ char *ccodes[1] = {"US"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:dst,!US;sid:1;)", 1, ccodes,
+ GEOIP_MATCH_DST_FLAG | GEOIP_MATCH_NEGATED);
+}
+
+static int GeoipParseTest06(void)
+{
+ char *ccodes[3] = {"US", "ES", "UK"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:US,ES,UK;sid:1;)", 3, ccodes,
+ GEOIP_MATCH_ANY_FLAG);
+}
+
+static int GeoipParseTest07(void)
+{
+ char *ccodes[3] = {"US", "ES", "UK"};
+ return GeoipParseTest("alert tcp any any -> any any (geoip:both,!US,ES,UK;sid:1;)", 3, ccodes,
+ GEOIP_MATCH_BOTH_FLAG | GEOIP_MATCH_NEGATED);
+}
+
+/**
+ * \internal
+ * \brief This test tests geoip success and failure.
+ */
+static int GeoipMatchTest(char *rule, char *srcip, char *dstip)
+{
+ uint8_t *buf = (uint8_t *) "GET / HTTP/1.0\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p1 = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p1 = UTHBuildPacketSrcDst(buf, buflen, IPPROTO_TCP, srcip, dstip);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ result = 2;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1) == 0) {
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+static int GeoipMatchTest01(void)
+{
+ /* Tests with IP of google DNS as US for both src and dst IPs */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:US;sid:1;)", "8.8.8.8", "8.8.8.8");
+ /* Expected result 1 = match */
+}
+
+static int GeoipMatchTest02(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:JP;sid:1;)", "8.8.8.8",
+ "202.12.27.33");
+ /* Expected result 1 = match */
+}
+
+static int GeoipMatchTest03(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:dst,JP;sid:1;)",
+ "8.8.8.8", "202.12.27.33");
+ /* Expected result 1 = match */
+}
+
+static int GeoipMatchTest04(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:src,JP;sid:1;)",
+ "8.8.8.8", "202.12.27.33");
+ /* Expected result 2 = NO match */
+}
+
+static int GeoipMatchTest05(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:src,JP,US;sid:1;)",
+ "8.8.8.8", "202.12.27.33");
+ /* Expected result 1 = match */
+}
+
+static int GeoipMatchTest06(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:src,ES,JP,US,UK,PT;sid:1;)",
+ "8.8.8.8", "202.12.27.33");
+ /* Expected result 1 = match */
+}
+
+static int GeoipMatchTest07(void)
+{
+ /* Tests with IP of google DNS as US, and m.root-servers.net as japan */
+ return GeoipMatchTest("alert tcp any any -> any any (geoip:src,!ES,JP,US,UK,PT;sid:1;)",
+ "8.8.8.8", "202.12.27.33");
+ /* Expected result 2 = NO match */
+}
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \internal
+ * \brief This function registers unit tests for DetectGeoip
+ */
+static void DetectGeoipRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("GeoipParseTest01", GeoipParseTest01, 1);
+ UtRegisterTest("GeoipParseTest02", GeoipParseTest02, 1);
+ UtRegisterTest("GeoipParseTest03", GeoipParseTest03, 1);
+ UtRegisterTest("GeoipParseTest04", GeoipParseTest04, 1);
+ UtRegisterTest("GeoipParseTest05", GeoipParseTest05, 1);
+ UtRegisterTest("GeoipParseTest06", GeoipParseTest06, 1);
+ UtRegisterTest("GeoipParseTest07", GeoipParseTest07, 1);
+
+ UtRegisterTest("GeoipMatchTest01", GeoipMatchTest01, 1);
+ UtRegisterTest("GeoipMatchTest02", GeoipMatchTest02, 1);
+ UtRegisterTest("GeoipMatchTest03", GeoipMatchTest03, 1);
+ UtRegisterTest("GeoipMatchTest04", GeoipMatchTest04, 2);
+ UtRegisterTest("GeoipMatchTest05", GeoipMatchTest05, 1);
+ UtRegisterTest("GeoipMatchTest06", GeoipMatchTest06, 1);
+ UtRegisterTest("GeoipMatchTest07", GeoipMatchTest07, 2);
+#endif /* UNITTESTS */
+}
+
+#endif /* HAVE_GEOIP */
diff --git a/framework/src/suricata/src/detect-geoip.h b/framework/src/suricata/src/detect-geoip.h
new file mode 100644
index 00000000..1bfc7aff
--- /dev/null
+++ b/framework/src/suricata/src/detect-geoip.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ */
+
+#ifndef __DETECT_GEOIP_H__
+#define __DETECT_GEOIP_H__
+
+#ifdef HAVE_GEOIP
+
+#include <GeoIP.h>
+#include "util-spm-bm.h"
+
+#define GEOOPTION_MAXSIZE 3 /* Country Code (2 chars) + NULL */
+#define GEOOPTION_MAXLOCATIONS 64
+
+typedef struct DetectGeoipData_ {
+ uint8_t location[GEOOPTION_MAXLOCATIONS][GEOOPTION_MAXSIZE]; /** country code for now, null term.*/
+ int nlocations; /** number of location strings parsed */
+ uint32_t flags;
+ GeoIP *geoengine;
+} DetectGeoipData;
+
+#endif
+
+void DetectGeoipRegister(void);
+
+#endif
diff --git a/framework/src/suricata/src/detect-gid.c b/framework/src/suricata/src/detect-gid.c
new file mode 100644
index 00000000..f63ea28a
--- /dev/null
+++ b/framework/src/suricata/src/detect-gid.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the gid keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "flow-var.h"
+#include "decode-events.h"
+
+#include "detect-gid.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "[0-9]+"
+
+static int DetectGidSetup (DetectEngineCtx *, Signature *, char *);
+
+/**
+ * \brief Registration function for gid: keyword
+ */
+
+void DetectGidRegister (void)
+{
+ sigmatch_table[DETECT_GID].name = "gid";
+ sigmatch_table[DETECT_GID].desc = "give different groups of signatures another id value";
+ sigmatch_table[DETECT_GID].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Gid-group-id";
+ sigmatch_table[DETECT_GID].Match = NULL;
+ sigmatch_table[DETECT_GID].Setup = DetectGidSetup;
+ sigmatch_table[DETECT_GID].Free = NULL;
+ sigmatch_table[DETECT_GID].RegisterTests = GidRegisterTests;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed gid into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided gid options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectGidSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ char *str = rawstr;
+ char duped = 0;
+
+ /* Strip leading and trailing "s. */
+ if (rawstr[0] == '\"') {
+ str = SCStrdup(rawstr + 1);
+ if (unlikely(str == NULL)) {
+ return -1;
+ }
+ if (strlen(str) && str[strlen(str) - 1] == '\"') {
+ str[strlen(str) - 1] = '\"';
+ }
+ duped = 1;
+ }
+
+ unsigned long gid = 0;
+ char *endptr = NULL;
+ gid = strtoul(rawstr, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0') {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
+ "to gid keyword");
+ goto error;
+ }
+ if (gid >= UINT_MAX) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "gid value to high, max %u", UINT_MAX);
+ goto error;
+ }
+
+ s->gid = (uint32_t)gid;
+
+ if (duped)
+ SCFree(str);
+ return 0;
+
+ error:
+ if (duped)
+ SCFree(str);
+ return -1;
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+/**
+ * \test GidTestParse01 is a test for a valid gid value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int GidTestParse01 (void)
+{
+ int result = 0;
+ Signature *s = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 any -> any any (sid:1; gid:1;)");
+ if (s == NULL || s->gid != 1)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test GidTestParse02 is a test for an invalid gid value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int GidTestParse02 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 any -> any any (sid:1; gid:a;)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Test a gid consisting of a single quote.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int GidTestParse03 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx,
+ "alert tcp any any -> any any (content:\"ABC\"; gid:\";)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for Gid
+ */
+void GidRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("GidTestParse01", GidTestParse01, 1);
+ UtRegisterTest("GidTestParse02", GidTestParse02, 1);
+ UtRegisterTest("GidTestParse03", GidTestParse03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-gid.h b/framework/src/suricata/src/detect-gid.h
new file mode 100644
index 00000000..d98d6edc
--- /dev/null
+++ b/framework/src/suricata/src/detect-gid.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the gid keyword
+ */
+
+#ifndef __DETECT_GID_H__
+#define __DETECT_GID_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+/**
+ * Registration function for gid: keyword
+ */
+
+void DetectGidRegister (void);
+
+/**
+ * This function registers unit tests for Gid
+ */
+
+void GidRegisterTests(void);
+
+#endif /*__DETECT_GID_H__ */
diff --git a/framework/src/suricata/src/detect-hostbits.c b/framework/src/suricata/src/detect-hostbits.c
new file mode 100644
index 00000000..ad1fce57
--- /dev/null
+++ b/framework/src/suricata/src/detect-hostbits.c
@@ -0,0 +1,1473 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the hostbits keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "detect-hostbits.h"
+#include "util-spm.h"
+
+#include "detect-engine-sigorder.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow-bit.h"
+#include "host-bit.h"
+#include "util-var-name.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/*
+ hostbits:isset,bitname;
+ hostbits:set,bitname;
+
+ hostbits:set,bitname,src;
+ hostbits:set,bitname,dst;
+TODO:
+ hostbits:set,bitname,both;
+
+ hostbits:set,bitname,src,3600;
+ hostbits:set,bitname,dst,60;
+ hostbits:set,bitname,both,120;
+ */
+
+#define PARSE_REGEX "([a-z]+)(?:\\s*,\\s*([^\\s,]+))?(?:\\s*,\\s*([^,\\s]+))?"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectHostbitMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectHostbitSetup (DetectEngineCtx *, Signature *, char *);
+void DetectHostbitFree (void *);
+void HostBitsRegisterTests(void);
+
+void DetectHostbitsRegister (void)
+{
+ sigmatch_table[DETECT_HOSTBITS].name = "hostbits";
+ sigmatch_table[DETECT_HOSTBITS].desc = "operate on host flag";
+// sigmatch_table[DETECT_HOSTBITS].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flow-keywords#Flowbits";
+ sigmatch_table[DETECT_HOSTBITS].Match = DetectHostbitMatch;
+ sigmatch_table[DETECT_HOSTBITS].Setup = DetectHostbitSetup;
+ sigmatch_table[DETECT_HOSTBITS].Free = DetectHostbitFree;
+ sigmatch_table[DETECT_HOSTBITS].RegisterTests = HostBitsRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_HOSTBITS].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+static int DetectHostbitMatchToggle (Packet *p, const DetectXbitsData *fd)
+{
+ switch (fd->tracker) {
+ case DETECT_XBITS_TRACK_IPSRC:
+ if (p->host_src == NULL) {
+ p->host_src = HostGetHostFromHash(&p->src);
+ if (p->host_src == NULL)
+ return 0;
+ }
+ else
+ HostLock(p->host_src);
+
+ HostBitToggle(p->host_src,fd->idx,p->ts.tv_sec + fd->expire);
+ HostUnlock(p->host_src);
+ break;
+ case DETECT_XBITS_TRACK_IPDST:
+ if (p->host_dst == NULL) {
+ p->host_dst = HostGetHostFromHash(&p->dst);
+ if (p->host_dst == NULL)
+ return 0;
+ }
+ else
+ HostLock(p->host_dst);
+
+ HostBitToggle(p->host_dst,fd->idx,p->ts.tv_sec + fd->expire);
+ HostUnlock(p->host_dst);
+ break;
+ }
+ return 1;
+}
+
+/* return true even if bit not found */
+static int DetectHostbitMatchUnset (Packet *p, const DetectXbitsData *fd)
+{
+ switch (fd->tracker) {
+ case DETECT_XBITS_TRACK_IPSRC:
+ if (p->host_src == NULL) {
+ p->host_src = HostLookupHostFromHash(&p->src);
+ if (p->host_src == NULL)
+ return 1;
+ } else
+ HostLock(p->host_src);
+
+ HostBitUnset(p->host_src,fd->idx);
+ HostUnlock(p->host_src);
+ break;
+ case DETECT_XBITS_TRACK_IPDST:
+ if (p->host_dst == NULL) {
+ p->host_dst = HostLookupHostFromHash(&p->dst);
+ if (p->host_dst == NULL)
+ return 1;
+ } else
+ HostLock(p->host_dst);
+
+ HostBitUnset(p->host_dst,fd->idx);
+ HostUnlock(p->host_dst);
+ break;
+ }
+ return 1;
+}
+
+static int DetectHostbitMatchSet (Packet *p, const DetectXbitsData *fd)
+{
+ switch (fd->tracker) {
+ case DETECT_XBITS_TRACK_IPSRC:
+ if (p->host_src == NULL) {
+ p->host_src = HostGetHostFromHash(&p->src);
+ if (p->host_src == NULL)
+ return 0;
+ } else
+ HostLock(p->host_src);
+
+ HostBitSet(p->host_src,fd->idx,p->ts.tv_sec + fd->expire);
+ HostUnlock(p->host_src);
+ break;
+ case DETECT_XBITS_TRACK_IPDST:
+ if (p->host_dst == NULL) {
+ p->host_dst = HostGetHostFromHash(&p->dst);
+ if (p->host_dst == NULL)
+ return 0;
+ } else
+ HostLock(p->host_dst);
+
+ HostBitSet(p->host_dst,fd->idx, p->ts.tv_sec + fd->expire);
+ HostUnlock(p->host_dst);
+ break;
+ }
+ return 1;
+}
+
+static int DetectHostbitMatchIsset (Packet *p, const DetectXbitsData *fd)
+{
+ int r = 0;
+ switch (fd->tracker) {
+ case DETECT_XBITS_TRACK_IPSRC:
+ if (p->host_src == NULL) {
+ p->host_src = HostLookupHostFromHash(&p->src);
+ if (p->host_src == NULL)
+ return 0;
+ } else
+ HostLock(p->host_src);
+
+ r = HostBitIsset(p->host_src,fd->idx, p->ts.tv_sec);
+ HostUnlock(p->host_src);
+ return r;
+ case DETECT_XBITS_TRACK_IPDST:
+ if (p->host_dst == NULL) {
+ p->host_dst = HostLookupHostFromHash(&p->dst);
+ if (p->host_dst == NULL)
+ return 0;
+ } else
+ HostLock(p->host_dst);
+
+ r = HostBitIsset(p->host_dst,fd->idx, p->ts.tv_sec);
+ HostUnlock(p->host_dst);
+ return r;
+ }
+ return 0;
+}
+
+static int DetectHostbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
+{
+ int r = 0;
+ switch (fd->tracker) {
+ case DETECT_XBITS_TRACK_IPSRC:
+ if (p->host_src == NULL) {
+ p->host_src = HostLookupHostFromHash(&p->src);
+ if (p->host_src == NULL)
+ return 1;
+ } else
+ HostLock(p->host_src);
+
+ r = HostBitIsnotset(p->host_src,fd->idx, p->ts.tv_sec);
+ HostUnlock(p->host_src);
+ return r;
+ case DETECT_XBITS_TRACK_IPDST:
+ if (p->host_dst == NULL) {
+ p->host_dst = HostLookupHostFromHash(&p->dst);
+ if (p->host_dst == NULL)
+ return 1;
+ } else
+ HostLock(p->host_dst);
+
+ r = HostBitIsnotset(p->host_dst,fd->idx, p->ts.tv_sec);
+ HostUnlock(p->host_dst);
+ return r;
+ }
+ return 0;
+}
+
+int DetectXbitMatchHost(Packet *p, const DetectXbitsData *xd)
+{
+ switch (xd->cmd) {
+ case DETECT_XBITS_CMD_ISSET:
+ return DetectHostbitMatchIsset(p,xd);
+ case DETECT_XBITS_CMD_ISNOTSET:
+ return DetectHostbitMatchIsnotset(p,xd);
+ case DETECT_XBITS_CMD_SET:
+ return DetectHostbitMatchSet(p,xd);
+ case DETECT_XBITS_CMD_UNSET:
+ return DetectHostbitMatchUnset(p,xd);
+ case DETECT_XBITS_CMD_TOGGLE:
+ return DetectHostbitMatchToggle(p,xd);
+ default:
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown cmd %" PRIu32 "", xd->cmd);
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+int DetectHostbitMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
+ if (xd == NULL)
+ return 0;
+
+ return DetectXbitMatchHost(p, xd);
+}
+
+static int DetectHostbitParse(const char *str, char *cmd, int cmd_len,
+ char *name, int name_len, char *dir, int dir_len)
+{
+ const int max_substrings = 30;
+ int count, rc;
+ int ov[max_substrings];
+
+ count = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, max_substrings);
+ if (count != 2 && count != 3 && count != 4) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "\"%s\" is not a valid setting for hostbits.", str);
+ return 0;
+ }
+
+ rc = pcre_copy_substring((char *)str, ov, max_substrings, 1, cmd, cmd_len);
+ if (rc < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return 0;
+ }
+
+ if (count >= 3) {
+ rc = pcre_copy_substring((char *)str, ov, max_substrings, 2, name,
+ name_len);
+ if (rc < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return 0;
+ }
+ if (count >= 4) {
+ rc = pcre_copy_substring((char *)str, ov, max_substrings, 3, dir,
+ dir_len);
+ if (rc < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
+ "pcre_copy_substring failed");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int DetectHostbitSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectXbitsData *cd = NULL;
+ SigMatch *sm = NULL;
+ uint8_t fb_cmd = 0;
+ uint8_t hb_dir = 0;
+ char fb_cmd_str[16] = "", fb_name[256] = "";
+ char hb_dir_str[16] = "";
+
+ if (!DetectHostbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str),
+ fb_name, sizeof(fb_name), hb_dir_str, sizeof(hb_dir_str))) {
+ return -1;
+ }
+
+ if (strlen(hb_dir_str) > 0) {
+ if (strcmp(hb_dir_str, "src") == 0)
+ hb_dir = DETECT_XBITS_TRACK_IPSRC;
+ else if (strcmp(hb_dir_str, "dst") == 0)
+ hb_dir = DETECT_XBITS_TRACK_IPDST;
+ else if (strcmp(hb_dir_str, "both") == 0) {
+ //hb_dir = DETECT_XBITS_TRACK_IPBOTH;
+ SCLogError(SC_ERR_UNIMPLEMENTED, "'both' not implemented");
+ goto error;
+ } else {
+ // TODO
+ goto error;
+ }
+ }
+
+ if (strcmp(fb_cmd_str,"noalert") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_NOALERT;
+ } else if (strcmp(fb_cmd_str,"isset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_ISSET;
+ } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_ISNOTSET;
+ } else if (strcmp(fb_cmd_str,"set") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_SET;
+ } else if (strcmp(fb_cmd_str,"unset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_UNSET;
+ } else if (strcmp(fb_cmd_str,"toggle") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_TOGGLE;
+ } else {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
+ goto error;
+ }
+
+ switch (fb_cmd) {
+ case DETECT_XBITS_CMD_NOALERT:
+ if (strlen(fb_name) != 0)
+ goto error;
+ s->flags |= SIG_FLAG_NOALERT;
+ return 0;
+ case DETECT_XBITS_CMD_ISNOTSET:
+ case DETECT_XBITS_CMD_ISSET:
+ case DETECT_XBITS_CMD_SET:
+ case DETECT_XBITS_CMD_UNSET:
+ case DETECT_XBITS_CMD_TOGGLE:
+ default:
+ if (strlen(fb_name) == 0)
+ goto error;
+ break;
+ }
+
+ cd = SCMalloc(sizeof(DetectXbitsData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ cd->idx = VariableNameGetIdx(de_ctx, fb_name, VAR_TYPE_HOST_BIT);
+ cd->cmd = fb_cmd;
+ cd->tracker = hb_dir;
+ cd->type = VAR_TYPE_HOST_BIT;
+ cd->expire = 300;
+
+ SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
+ cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_HOSTBITS;
+ sm->ctx = (void *)cd;
+
+ switch (fb_cmd) {
+ /* case DETECT_XBITS_CMD_NOALERT can't happen here */
+
+ case DETECT_XBITS_CMD_ISNOTSET:
+ case DETECT_XBITS_CMD_ISSET:
+ /* checks, so packet list */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ break;
+
+ case DETECT_XBITS_CMD_SET:
+ case DETECT_XBITS_CMD_UNSET:
+ case DETECT_XBITS_CMD_TOGGLE:
+ /* modifiers, only run when entire sig has matched */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
+ break;
+ }
+
+ return 0;
+
+error:
+ if (cd != NULL)
+ SCFree(cd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectHostbitFree (void *ptr)
+{
+ DetectXbitsData *fd = (DetectXbitsData *)ptr;
+
+ if (fd == NULL)
+ return;
+
+ SCFree(fd);
+}
+
+#ifdef UNITTESTS
+
+static void HostBitsTestSetup(void)
+{
+ StorageInit();
+ HostBitInitCtx();
+ StorageFinalize();
+ HostInitConfig(TRUE);
+}
+
+static void HostBitsTestShutdown(void)
+{
+ HostCleanup();
+ StorageCleanup();
+}
+
+static int HostBitsTestParse01(void)
+{
+ int ret = 0;
+ char cmd[16] = "", name[256] = "", dir[16] = "";
+
+ /* No direction. */
+ if (!DetectHostbitParse("isset,name", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+ if (strlen(dir)) {
+ goto end;
+ }
+
+ /* No direction, leading space. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset, name", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+
+ /* No direction, trailing space. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset,name ", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+
+ /* No direction, leading and trailing space. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset, name ", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+
+ /* With direction. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset,name,src", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+ if (strcmp(dir, "src") != 0) {
+ goto end;
+ }
+
+ /* With direction - leading and trailing spaces on name. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset, name ,src", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+ if (strcmp(dir, "src") != 0) {
+ goto end;
+ }
+
+ /* With direction - space around direction. */
+ *cmd = '\0';
+ *name = '\0';
+ *dir = '\0';
+ if (!DetectHostbitParse("isset, name , src ", cmd, sizeof(cmd), name,
+ sizeof(name), dir, sizeof(dir))) {
+ goto end;
+ }
+ if (strcmp(cmd, "isset") != 0) {
+ goto end;
+ }
+ if (strcmp(name, "name") != 0) {
+ goto end;
+ }
+ if (strcmp(dir, "src") != 0) {
+ goto end;
+ }
+
+ ret = 1;
+end:
+ return ret;
+}
+
+/**
+ * \test HostBitsTestSig01 is a test for a valid noalert flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig01(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ HostBitsTestSetup();
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ printf("bad de_ctx: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (hostbits:set,abc; content:\"GET \"; sid:1;)");
+
+ if (s == NULL) {
+ printf("bad sig: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test various options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig02(void)
+{
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int error_count = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:isset,abc,src; content:\"GET \"; sid:1;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:isnotset,abc,dst; content:\"GET \"; sid:2;)");
+ if (s == NULL) {
+ error_count++;
+ }
+/* TODO reenable after both is supported
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:set,abc,both; content:\"GET \"; sid:3;)");
+ if (s == NULL) {
+ error_count++;
+ }
+*/
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:unset,abc,src; content:\"GET \"; sid:4;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:toggle,abc,dst; content:\"GET \"; sid:5;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ if (error_count != 0)
+ goto end;
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ return result;
+}
+
+#if 0
+/**
+ * \test HostBitsTestSig03 is a test for a invalid flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig03(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Unknown cmd\"; flowbits:wrongcmd; content:\"GET \"; sid:1;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+
+ SCFree(p);
+ return result;
+}
+#endif
+
+/**
+ * \test HostBitsTestSig04 is a test check idx value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig04(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ HostBitsTestSetup();
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; hostbits:isset,fbt; content:\"GET \"; sid:1;)");
+
+ idx = VariableNameGetIdx(de_ctx, "fbt", VAR_TYPE_HOST_BIT);
+
+ if (s == NULL || idx != 1) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test HostBitsTestSig05 is a test check noalert flag
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig05(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ HostBitsTestSetup();
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any (hostbits:noalert; content:\"GET \"; sid:1;)");
+
+ if (s == NULL || ((s->flags & SIG_FLAG_NOALERT) != SIG_FLAG_NOALERT)) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ result = 1;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+#if 0
+/**
+ * \test HostBitsTestSig06 is a test set flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig06(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ GenericVar flowvar, *gv = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ p->flow->flowvar = &flowvar;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->flags |= PKT_HAS_FLOW;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ idx = VariableNameGetIdx(de_ctx, "myflow", VAR_TYPE_HOST_BIT);
+
+ gv = p->flow->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_HOSTBITS && gv->idx == idx) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test HostBitsTestSig07 is a test unset flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int HostBitsTestSig07(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ GenericVar flowvar, *gv = NULL;
+ int result = 0;
+ int idx = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+ memset(&flowvar, 0, sizeof(GenericVar));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ p->flow->flowvar = &flowvar;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ idx = VariableNameGetIdx(de_ctx, "myflow", VAR_TYPE_HOST_BIT);
+
+ gv = p->flow->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_HOSTBITS && gv->idx == idx) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if(gv) GenericVarFree(gv);
+ FLOW_DESTROY(&f);
+
+ SCFree(p);
+ return result;
+}
+#endif
+
+/**
+ * \test set / isset
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int HostBitsTestSig07(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ int result = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+
+ HostBitsTestSetup();
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any (hostbits:set,myflow2; sid:10;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,
+ "alert ip any any -> any any (hostbits:isset,myflow2; sid:11;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ SCLogInfo("p->host_src %p", p->host_src);
+
+ if (HostHasHostBits(p->host_src) == 1) {
+ if (PacketAlertCheck(p, 11)) {
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+
+ HostBitsTestShutdown();
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ FLOW_DESTROY(&f);
+
+ HostBitsTestShutdown();
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test set / toggle / toggle / isset
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int HostBitsTestSig08(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ Flow f;
+ int result = 0;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(Flow));
+
+ HostBitsTestSetup();
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:set,myflow2; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:toggle,myflow2; sid:11;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:toggle,myflow2; sid:12;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (hostbits:isset,myflow2; sid:13;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+ SCSigSignatureOrderingModuleCleanup(de_ctx);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ SCLogInfo("p->host_src %p", p->host_src);
+
+ if (HostHasHostBits(p->host_src) == 1) {
+ if (PacketAlertCheck(p, 10)) {
+ SCLogInfo("sid 10 matched");
+ }
+ if (PacketAlertCheck(p, 11)) {
+ SCLogInfo("sid 11 matched");
+ }
+ if (PacketAlertCheck(p, 12)) {
+ SCLogInfo("sid 12 matched");
+ }
+ if (PacketAlertCheck(p, 13)) {
+ SCLogInfo("sid 13 matched");
+ result = 1;
+ }
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ FLOW_DESTROY(&f);
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+end:
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ FLOW_DESTROY(&f);
+
+ HostBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for HostBits
+ */
+void HostBitsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HostBitsTestParse01", HostBitsTestParse01, 1);
+ UtRegisterTest("HostBitsTestSig01", HostBitsTestSig01, 1);
+ UtRegisterTest("HostBitsTestSig02", HostBitsTestSig02, 1);
+#if 0
+ UtRegisterTest("HostBitsTestSig03", HostBitsTestSig03, 0);
+#endif
+ UtRegisterTest("HostBitsTestSig04", HostBitsTestSig04, 1);
+ UtRegisterTest("HostBitsTestSig05", HostBitsTestSig05, 1);
+#if 0
+ UtRegisterTest("HostBitsTestSig06", HostBitsTestSig06, 1);
+#endif
+ UtRegisterTest("HostBitsTestSig07", HostBitsTestSig07, 1);
+ UtRegisterTest("HostBitsTestSig08", HostBitsTestSig08, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-hostbits.h b/framework/src/suricata/src/detect-hostbits.h
new file mode 100644
index 00000000..6e9c7f5f
--- /dev/null
+++ b/framework/src/suricata/src/detect-hostbits.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_HOSTBITS_H__
+#define __DETECT_HOSTBITS_H__
+
+#include "detect-xbits.h"
+
+int DetectXbitMatchHost(Packet *p, const DetectXbitsData *xd);
+
+/* prototypes */
+void DetectHostbitsRegister (void);
+
+#endif /* __DETECT_HOSTBITS_H__ */
diff --git a/framework/src/suricata/src/detect-http-client-body.c b/framework/src/suricata/src/detect-http-client-body.c
new file mode 100644
index 00000000..e6c902aa
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-client-body.c
@@ -0,0 +1,2407 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements support for the http_client_body keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "detect-http-client-body.h"
+#include "stream-tcp.h"
+
+int DetectHttpClientBodySetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpClientBodyRegisterTests(void);
+void DetectHttpClientBodyFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_client_body" keyword.
+ */
+void DetectHttpClientBodyRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].name = "http_client_body";
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].desc = "content modifier to match only on HTTP request-body";
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_client_body";
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].Setup = DetectHttpClientBodySetup;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].Free = DetectHttpClientBodyFree;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].RegisterTests = DetectHttpClientBodyRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].flags |= SIGMATCH_NOOPT ;
+ sigmatch_table[DETECT_AL_HTTP_CLIENT_BODY].flags |= SIGMATCH_PAYLOAD ;
+}
+
+static void DetectHttpClientBodySetupCallback(Signature *s)
+{
+ AppLayerHtpEnableRequestBodyCallback();
+ return;
+}
+
+/**
+ * \brief The setup function for the http_client_body keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param m Pointer to the head of the SigMatchs for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int DetectHttpClientBodySetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_CLIENT_BODY,
+ DETECT_SM_LIST_HCBDMATCH,
+ ALPROTO_HTTP,
+ DetectHttpClientBodySetupCallback);
+}
+
+/**
+ * \brief The function to free the http_client_body data.
+ *
+ * \param ptr Pointer to the http_client_body.
+ */
+void DetectHttpClientBodyFree(void *ptr)
+{
+ SCEnter();
+ DetectContentData *hcbd = (DetectContentData *)ptr;
+ if (hcbd == NULL)
+ SCReturn;
+
+ if (hcbd->content != NULL)
+ SCFree(hcbd->content);
+
+ BoyerMooreCtxDeInit(hcbd->bm_ctx);
+ SCFree(hcbd);
+
+ SCReturn;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_client_body is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpClientBodyTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ SigMatch *sm = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_client_body\"; "
+ "content:\"one\"; http_client_body; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_MATCH];
+ if (sm != NULL) {
+ result &= (sm->type == DETECT_CONTENT);
+ result &= (sm->next == NULL);
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_client_body entry is
+ * parsed.
+ */
+static int DetectHttpClientBodyTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_client_body\"; "
+ "content:\"one\"; http_client_body:; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a http_client_body
+ * is invalidated.
+ */
+static int DetectHttpClientBodyTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_client_body\"; "
+ "http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_client_body is invalidated.
+ */
+static int DetectHttpClientBodyTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_client_body\"; "
+ "content:\"one\"; rawbytes; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_client_body is invalidated.
+ */
+static int DetectHttpClientBodyTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_client_body\"; "
+ "content:\"one\"; http_client_body; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpClientBodyTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"message\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpClientBodyTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 54\r\n"
+ "\r\n"
+ "This is dummy message body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"message\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpClientBodyTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"message\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpClientBodyTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"body1This\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpClientBodyTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy bodY1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"body1This\"; http_client_body; nocase;"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_client_body content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpClientBodyTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:!\"message1\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_client_body content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpClientBodyTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:!\"message\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_client_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpClientBodyTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 55\r\n"
+ "\r\n"
+ "longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"abcdefghijklmnopqrstuvwxyz0123456789\"; http_client_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test multiple http transactions and body chunks of request handling */
+static int DetectHttpClientBodyTest14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Body one!!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multiple http transactions and body chunks of request handling */
+static int DetectHttpClientBodyTest15(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Body one!!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* hardcoded check of the transactions and it's client body chunks */
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ htp_tx_t *t1 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+ htp_tx_t *t2 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 1);
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1);
+
+ HtpBodyChunk *cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+ htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
+
+ cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+int DetectHttpClientBodyTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; http_client_body; "
+ "content:\"three\"; distance:10; http_client_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (cd1->flags != 0 || memcmp(cd1->content, "one", cd1->content_len) != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "two", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd1) ||
+ !DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_client_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; pcre:/two/; "
+ "content:\"three\"; distance:10; within:15; http_client_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_client_body; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != DETECT_PCRE_RELATIVE_NEXT ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; offset:10; http_client_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_client_body; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != (DETECT_CONTENT_RELATIVE_NEXT | DETECT_CONTENT_OFFSET) ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ printf ("failed: http_client_body incorrect flags");
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; offset:10; http_client_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_client_body; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; pcre:/two/; "
+ "content:\"three\"; http_client_body; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hcbd1->flags != 0 ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_DEPTH ||
+ memcmp(hcbd2->content, "three", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ !DETECT_CONTENT_IS_SINGLE(hcbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hcbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; "
+ "content:\"two\"; distance:0; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hcbd2->content, "two", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; "
+ "content:\"two\"; within:5; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "one", hcbd1->content_len) != 0 ||
+ hcbd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hcbd2->content, "two", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_client_body; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list != NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/P; "
+ "content:\"two\"; within:5; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hcbd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hcbd2->content, "two", hcbd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_client_body; "
+ "pcre:/one/PR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hcbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ hcbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hcbd1->content, "two", hcbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpClientBodyTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/P; "
+ "content:\"two\"; distance:5; http_client_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCBDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->prev->ctx;
+ DetectContentData *hcbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hcbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hcbd2->content, "two", hcbd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpClientBodyRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpClientBodyTest01", DetectHttpClientBodyTest01, 1);
+ UtRegisterTest("DetectHttpClientBodyTest02", DetectHttpClientBodyTest02, 1);
+ UtRegisterTest("DetectHttpClientBodyTest03", DetectHttpClientBodyTest03, 1);
+ UtRegisterTest("DetectHttpClientBodyTest04", DetectHttpClientBodyTest04, 1);
+ UtRegisterTest("DetectHttpClientBodyTest05", DetectHttpClientBodyTest05, 1);
+ UtRegisterTest("DetectHttpClientBodyTest06", DetectHttpClientBodyTest06, 1);
+ UtRegisterTest("DetectHttpClientBodyTest07", DetectHttpClientBodyTest07, 1);
+ UtRegisterTest("DetectHttpClientBodyTest08", DetectHttpClientBodyTest08, 1);
+ UtRegisterTest("DetectHttpClientBodyTest09", DetectHttpClientBodyTest09, 1);
+ UtRegisterTest("DetectHttpClientBodyTest10", DetectHttpClientBodyTest10, 1);
+ UtRegisterTest("DetectHttpClientBodyTest11", DetectHttpClientBodyTest11, 1);
+ UtRegisterTest("DetectHttpClientBodyTest12", DetectHttpClientBodyTest12, 1);
+ UtRegisterTest("DetectHttpClientBodyTest13", DetectHttpClientBodyTest13, 1);
+ UtRegisterTest("DetectHttpClientBodyTest14", DetectHttpClientBodyTest14, 1);
+ UtRegisterTest("DetectHttpClientBodyTest15", DetectHttpClientBodyTest15, 1);
+
+ UtRegisterTest("DetectHttpClientBodyTest22", DetectHttpClientBodyTest22, 1);
+ UtRegisterTest("DetectHttpClientBodyTest23", DetectHttpClientBodyTest23, 1);
+ UtRegisterTest("DetectHttpClientBodyTest24", DetectHttpClientBodyTest24, 1);
+ UtRegisterTest("DetectHttpClientBodyTest25", DetectHttpClientBodyTest25, 1);
+ UtRegisterTest("DetectHttpClientBodyTest26", DetectHttpClientBodyTest26, 1);
+ UtRegisterTest("DetectHttpClientBodyTest27", DetectHttpClientBodyTest27, 1);
+ UtRegisterTest("DetectHttpClientBodyTest28", DetectHttpClientBodyTest28, 1);
+ UtRegisterTest("DetectHttpClientBodyTest29", DetectHttpClientBodyTest29, 1);
+ UtRegisterTest("DetectHttpClientBodyTest30", DetectHttpClientBodyTest30, 1);
+ UtRegisterTest("DetectHttpClientBodyTest31", DetectHttpClientBodyTest31, 1);
+ UtRegisterTest("DetectHttpClientBodyTest32", DetectHttpClientBodyTest32, 1);
+ UtRegisterTest("DetectHttpClientBodyTest33", DetectHttpClientBodyTest33, 1);
+ UtRegisterTest("DetectHttpClientBodyTest34", DetectHttpClientBodyTest34, 1);
+ UtRegisterTest("DetectHttpClientBodyTest35", DetectHttpClientBodyTest35, 1);
+ UtRegisterTest("DetectHttpClientBodyTest36", DetectHttpClientBodyTest36, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-client-body.h b/framework/src/suricata/src/detect-http-client-body.h
new file mode 100644
index 00000000..7a2249c2
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-client-body.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_CLIENT_BODY_H__
+#define __DETECT_HTTP_CLIENT_BODY_H__
+
+void DetectHttpClientBodyRegister(void);
+
+#endif /* __DETECT_HTTP_CLIENT_BODY_H__ */
diff --git a/framework/src/suricata/src/detect-http-cookie.c b/framework/src/suricata/src/detect-http-cookie.c
new file mode 100644
index 00000000..94f76765
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-cookie.c
@@ -0,0 +1,1277 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Implements the http_cookie keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-cookie.h"
+#include "stream-tcp.h"
+
+static int DetectHttpCookieSetup (DetectEngineCtx *, Signature *, char *);
+void DetectHttpCookieRegisterTests(void);
+void DetectHttpCookieFree(void *);
+
+/**
+ * \brief Registration function for keyword: http_cookie
+ */
+void DetectHttpCookieRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].name = "http_cookie";
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].desc = "content modifier to match only on the HTTP cookie-buffer";
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_cookie";
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].Setup = DetectHttpCookieSetup;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].Free = DetectHttpCookieFree;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].RegisterTests = DetectHttpCookieRegisterTests;
+
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_COOKIE].flags |= SIGMATCH_PAYLOAD;
+}
+
+/**
+ * \brief this function clears the memory of http_cookie modifier keyword
+ *
+ * \param ptr Pointer to the Detection Cookie data
+ */
+void DetectHttpCookieFree(void *ptr)
+{
+ DetectContentData *hcd = (DetectContentData *)ptr;
+ if (hcd == NULL)
+ return;
+ if (hcd->content != NULL)
+ SCFree(hcd->content);
+ SCFree(hcd);
+}
+
+/**
+ * \brief this function setups the http_cookie modifier keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+static int DetectHttpCookieSetup(DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, str,
+ DETECT_AL_HTTP_COOKIE,
+ DETECT_SM_LIST_HCDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/******************************** UNITESTS **********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature, if content is not
+ * specified in the signature
+ */
+int DetectHttpCookieTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; http_cookie;sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature, if some parameter
+ * is specified with http_cookie in the signature
+ */
+int DetectHttpCookieTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; content:\"me\"; "
+ "http_cookie:woo; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature
+ */
+int DetectHttpCookieTest03(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; content:\"one\"; "
+ "http_cookie; content:\"two\"; http_cookie; "
+ "content:\"two\"; http_cookie; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HCDMATCH];
+ if (sm == NULL) {
+ printf("no sigmatch(es): ");
+ goto end;
+ }
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ printf("expected DETECT_CONTENT for http_cookie, got %d: ", sm->type);
+ goto end;
+ }
+ sm = sm->next;
+ }
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature, when fast_pattern
+ * is also specified in the signature (now it should)
+ */
+int DetectHttpCookieTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; content:\"one\"; "
+ "fast_pattern; http_cookie; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature, when rawbytes is
+ * also specified in the signature
+ */
+int DetectHttpCookieTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; content:\"one\"; "
+ "rawbytes; http_cookie; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_cookie is registered in a Signature, when rawbytes is
+ * also specified in the signature
+ */
+int DetectHttpCookieTest06(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_cookie\"; content:\"one\"; "
+ "http_cookie; uricontent:\"abc\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ Signature *s = de_ctx->sig_list;
+
+ BUG_ON(s->sm_lists[DETECT_SM_LIST_HCDMATCH] == NULL);
+
+ if (s->sm_lists[DETECT_SM_LIST_HCDMATCH]->type != DETECT_CONTENT)
+ goto end;
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("expected another SigMatch, got NULL: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH]->type != DETECT_CONTENT) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is matched . */
+static int DetectHttpCookieSigTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatchme\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:\"me\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "cookie\"; content:\"go\"; http_cookie; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 matched but shouldn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:\"me\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:\"boo\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest04(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:!\"boo\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest05(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: DuMmY\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:\"dummy\"; nocase; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest06(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: DuMmY\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:\"dummy\"; "
+ "http_cookie; nocase; sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is not present */
+static int DetectHttpCookieSigTest07(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; content:!\"dummy\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Check the signature working to alert against set-cookie
+ */
+static int DetectHttpCookieSigTest08(void)
+{
+ int result = 0;
+ Flow f;
+
+ uint8_t httpbuf_request[] =
+ "GET / HTTP/1.1\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "\r\n";
+ uint32_t httpbuf_request_len = sizeof(httpbuf_request) - 1; /* minus the \0 */
+
+ uint8_t httpbuf_response[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Set-Cookie: response_user_agent\r\n"
+ "\r\n";
+ uint32_t httpbuf_response_len = sizeof(httpbuf_response) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ Packet *p1 = NULL, *p2 = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(flow:to_client; content:\"response_user_agent\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request */
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER,
+ httpbuf_request, httpbuf_request_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ goto end;
+ }
+
+ /* response */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT,
+ httpbuf_response, httpbuf_response_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!PacketAlertCheck(p2, 1)) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ * \test Check the signature working to alert against cookie/set-cookie
+ */
+static int DetectHttpCookieSigTest09(void)
+{
+ int result = 0;
+ Flow f;
+
+ uint8_t httpbuf_request[] =
+ "GET / HTTP/1.1\r\n"
+ "Cookie: request_user_agent\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "\r\n";
+ uint32_t httpbuf_request_len = sizeof(httpbuf_request) - 1; /* minus the \0 */
+
+ uint8_t httpbuf_response[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Set-Cookie: response_user_agent\r\n"
+ "\r\n";
+ uint32_t httpbuf_response_len = sizeof(httpbuf_response) - 1; /* minus the \0 */
+
+ TcpSession ssn;
+ Packet *p1 = NULL, *p2 = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(flow:to_server; content:\"request_user_agent\"; "
+ "http_cookie; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = de_ctx->sig_list->next = SigInit(de_ctx,"alert http any any -> any any "
+ "(flow:to_client; content:\"response_user_agent\"; "
+ "http_cookie; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* request */
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER,
+ httpbuf_request, httpbuf_request_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!PacketAlertCheck(p1, 1) || PacketAlertCheck(p1, 2)) {
+ goto end;
+ }
+
+ /* response */
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT,
+ httpbuf_response, httpbuf_response_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 1) || !PacketAlertCheck(p2, 2)) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the UNITTESTS for the http_cookie keyword
+ */
+void DetectHttpCookieRegisterTests (void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectHttpCookieTest01", DetectHttpCookieTest01, 1);
+ UtRegisterTest("DetectHttpCookieTest02", DetectHttpCookieTest02, 1);
+ UtRegisterTest("DetectHttpCookieTest03", DetectHttpCookieTest03, 1);
+ UtRegisterTest("DetectHttpCookieTest04", DetectHttpCookieTest04, 1);
+ UtRegisterTest("DetectHttpCookieTest05", DetectHttpCookieTest05, 1);
+ UtRegisterTest("DetectHttpCookieTest06", DetectHttpCookieTest06, 1);
+ UtRegisterTest("DetectHttpCookieSigTest01", DetectHttpCookieSigTest01, 1);
+ UtRegisterTest("DetectHttpCookieSigTest02", DetectHttpCookieSigTest02, 1);
+ UtRegisterTest("DetectHttpCookieSigTest03", DetectHttpCookieSigTest03, 1);
+ UtRegisterTest("DetectHttpCookieSigTest04", DetectHttpCookieSigTest04, 1);
+ UtRegisterTest("DetectHttpCookieSigTest05", DetectHttpCookieSigTest05, 1);
+ UtRegisterTest("DetectHttpCookieSigTest06", DetectHttpCookieSigTest06, 1);
+ UtRegisterTest("DetectHttpCookieSigTest07", DetectHttpCookieSigTest07, 1);
+ UtRegisterTest("DetectHttpCookieSigTest08", DetectHttpCookieSigTest08, 1);
+ UtRegisterTest("DetectHttpCookieSigTest09", DetectHttpCookieSigTest09, 1);
+#endif /* UNITTESTS */
+
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-cookie.h b/framework/src/suricata/src/detect-http-cookie.h
new file mode 100644
index 00000000..63128e0c
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-cookie.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_HTTP_COOKIE_H
+#define _DETECT_HTTP_COOKIE_H
+
+/* prototypes */
+void DetectHttpCookieRegister (void);
+int DetectHttpCookieDoMatch(DetectEngineThreadCtx *, Signature *, SigMatch *,
+ Flow *, uint8_t, void *);
+
+#endif /* _DETECT_HTTP_COOKIE_H */
+
diff --git a/framework/src/suricata/src/detect-http-header.c b/framework/src/suricata/src/detect-http-header.c
new file mode 100644
index 00000000..6c9fea9a
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-header.c
@@ -0,0 +1,1822 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements support for http_header keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-header.h"
+#include "stream-tcp.h"
+
+int DetectHttpHeaderSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpHeaderRegisterTests(void);
+void DetectHttpHeaderFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_header" keyword.
+ */
+void DetectHttpHeaderRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_HEADER].name = "http_header";
+ sigmatch_table[DETECT_AL_HTTP_HEADER].desc = "content modifier to match only on the HTTP header-buffer";
+ sigmatch_table[DETECT_AL_HTTP_HEADER].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_header";
+ sigmatch_table[DETECT_AL_HTTP_HEADER].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].Setup = DetectHttpHeaderSetup;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].Free = DetectHttpHeaderFree;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].RegisterTests = DetectHttpHeaderRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_NOOPT ;
+ sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_PAYLOAD ;
+
+ return;
+}
+
+/**
+ * \brief this function clears the memory of http_header modifier keyword
+ *
+ * \param ptr Pointer to the Detection Header Data
+ */
+void DetectHttpHeaderFree(void *ptr)
+{
+ DetectContentData *hhd = (DetectContentData *)ptr;
+ if (hhd == NULL)
+ return;
+
+ if (hhd->content != NULL)
+ SCFree(hhd->content);
+ SCFree(hhd);
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_header keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param m Pointer to the head of the SigMatchs for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectHttpHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_HEADER,
+ DETECT_SM_LIST_HHDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_header is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpHeaderTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ SigMatch *sm = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; "
+ "content:\"one\"; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("Error parsing signature: ");
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH];
+ if (sm != NULL) {
+ result &= (sm->type == DETECT_CONTENT);
+ result &= (sm->next == NULL);
+ } else {
+ result = 0;
+ printf("Error updating content pattern to http_header pattern: ");
+ }
+
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_header entry is
+ * parsed.
+ */
+static int DetectHttpHeaderTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; "
+ "content:\"one\"; http_header:; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a http_header
+ * is invalidated.
+ */
+static int DetectHttpHeaderTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; "
+ "http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_header is invalidated.
+ */
+static int DetectHttpHeaderTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; "
+ "content:\"one\"; rawbytes; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_header is invalidated.
+ */
+static int DetectHttpHeaderTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; "
+ "content:\"one\"; nocase; http_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHeaderTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Content-Type: text/html\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHeaderTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozi";
+ uint8_t http2_buf[] =
+ "lla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\nContent-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy message body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Mozilla\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ( (PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHeaderTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n";
+ uint8_t http2_buf[] =
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Gecko/20091221 Firefox/3.5.7\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpHeaderTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n";
+ uint8_t http2_buf[] =
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy body\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Firefox/3.5.7|0D 0A|Content\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpHeaderTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n";
+ uint8_t http2_buf[] =
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy body";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"firefox/3.5.7|0D 0A|content\"; nocase; http_header;"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_header content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpHeaderTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"lalalalala\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_header content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpHeaderTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:!\"User-Agent: Mozilla/5.0 \"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHeaderTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 100\r\n"
+ "\r\n"
+ "longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; "
+ "content:\"Host: www.openinfosecfoundation.org\"; http_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+int DetectHttpHeaderTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; "
+ "content:\"two\"; distance:0; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (hhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hhd1->content, "one", hhd1->content_len) != 0 ||
+ hhd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hhd2->content, "two", hhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; "
+ "content:\"two\"; within:5; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (hhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hhd1->content, "one", hhd1->content_len) != 0 ||
+ hhd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hhd2->content, "two", hhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_header; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/H; "
+ "content:\"two\"; within:5; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hhd2->content, "two", hhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_header; "
+ "pcre:/one/HR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ hhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hhd1->content, "two", hhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHeaderTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/H; "
+ "content:\"two\"; distance:5; http_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hhd2->content, "two", hhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test app-layer-event:http.host_header_ambiguous should not be set
+ * \bug 640*/
+static int DetectHttpHeaderTest28(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "POST http://xxx.intranet.local:8000/xxx HTTP/1.1\r\n"
+ "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_29\r\n"
+ "Host: xxx.intranet.local:8000\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(app-layer-event:http.host_header_ambiguous; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldnt have: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test app-layer-event:http.host_header_ambiguous should be set
+ * \bug 640*/
+static int DetectHttpHeaderTest29(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "POST http://xxx.intranet.local:8001/xxx HTTP/1.1\r\n"
+ "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_29\r\n"
+ "Host: xxx.intranet.local:8000\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(app-layer-event:http.host_header_ambiguous; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test app-layer-event:http.host_header_ambiguous should be set
+ * \bug 640*/
+static int DetectHttpHeaderTest30(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "POST http://xxx.intranet.local:8000/xxx HTTP/1.1\r\n"
+ "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_29\r\n"
+ "Host: xyz.intranet.local:8000\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(app-layer-event:http.host_header_ambiguous; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpHeaderRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpHeaderTest01", DetectHttpHeaderTest01, 1);
+ UtRegisterTest("DetectHttpHeaderTest02", DetectHttpHeaderTest02, 1);
+ UtRegisterTest("DetectHttpHeaderTest03", DetectHttpHeaderTest03, 1);
+ UtRegisterTest("DetectHttpHeaderTest04", DetectHttpHeaderTest04, 1);
+ UtRegisterTest("DetectHttpHeaderTest05", DetectHttpHeaderTest05, 1);
+ UtRegisterTest("DetectHttpHeaderTest06", DetectHttpHeaderTest06, 1);
+ UtRegisterTest("DetectHttpHeaderTest07", DetectHttpHeaderTest07, 1);
+ UtRegisterTest("DetectHttpHeaderTest08", DetectHttpHeaderTest08, 1);
+ UtRegisterTest("DetectHttpHeaderTest09", DetectHttpHeaderTest09, 1);
+ UtRegisterTest("DetectHttpHeaderTest10", DetectHttpHeaderTest10, 1);
+ UtRegisterTest("DetectHttpHeaderTest11", DetectHttpHeaderTest11, 1);
+ UtRegisterTest("DetectHttpHeaderTest12", DetectHttpHeaderTest12, 1);
+ UtRegisterTest("DetectHttpHeaderTest13", DetectHttpHeaderTest13, 1);
+ UtRegisterTest("DetectHttpHeaderTest20", DetectHttpHeaderTest20, 1);
+ UtRegisterTest("DetectHttpHeaderTest21", DetectHttpHeaderTest21, 1);
+ UtRegisterTest("DetectHttpHeaderTest22", DetectHttpHeaderTest22, 1);
+ UtRegisterTest("DetectHttpHeaderTest23", DetectHttpHeaderTest23, 1);
+ UtRegisterTest("DetectHttpHeaderTest24", DetectHttpHeaderTest24, 1);
+ UtRegisterTest("DetectHttpHeaderTest25", DetectHttpHeaderTest25, 1);
+ UtRegisterTest("DetectHttpHeaderTest26", DetectHttpHeaderTest26, 1);
+ UtRegisterTest("DetectHttpHeaderTest27", DetectHttpHeaderTest27, 1);
+ UtRegisterTest("DetectHttpHeaderTest28", DetectHttpHeaderTest28, 1);
+ UtRegisterTest("DetectHttpHeaderTest29", DetectHttpHeaderTest29, 1);
+ UtRegisterTest("DetectHttpHeaderTest30", DetectHttpHeaderTest30, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-header.h b/framework/src/suricata/src/detect-http-header.h
new file mode 100644
index 00000000..5327b5b8
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-header.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_HEADER_H__
+#define __DETECT_HTTP_HEADER_H__
+
+void DetectHttpHeaderRegister(void);
+void DetectHttpRawHeaderRegister(void);
+
+#endif /* __DETECT_HTTP_HEADER_H__ */
diff --git a/framework/src/suricata/src/detect-http-hh.c b/framework/src/suricata/src/detect-http-hh.c
new file mode 100644
index 00000000..19e87e16
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-hh.c
@@ -0,0 +1,2106 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements support for the http_host keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "stream-tcp.h"
+#include "detect-http-hh.h"
+
+int DetectHttpHHSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpHHRegisterTests(void);
+void DetectHttpHHFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_host" keyword.
+ */
+void DetectHttpHHRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_HOST].name = "http_host";
+ sigmatch_table[DETECT_AL_HTTP_HOST].desc = "content modifier to match only on the HTTP hostname";
+ sigmatch_table[DETECT_AL_HTTP_HOST].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_HOST].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_HOST].Setup = DetectHttpHHSetup;
+ sigmatch_table[DETECT_AL_HTTP_HOST].Free = DetectHttpHHFree;
+ sigmatch_table[DETECT_AL_HTTP_HOST].RegisterTests = DetectHttpHHRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_HOST].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_HOST].flags |= SIGMATCH_NOOPT ;
+ sigmatch_table[DETECT_AL_HTTP_HOST].flags |= SIGMATCH_PAYLOAD ;
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_host keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to the signature for the current Signature being
+ * parsed from the rules.
+ * \param m Pointer to the head of the SigMatch for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int DetectHttpHHSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_HOST,
+ DETECT_SM_LIST_HHHDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/**
+ * \brief The function to free the http_host data.
+ *
+ * \param ptr Pointer to the http_host.
+ */
+void DetectHttpHHFree(void *ptr)
+{
+ DetectContentData *hhhd = (DetectContentData *)ptr;
+ if (hhhd == NULL)
+ return;
+
+ if (hhhd->content != NULL)
+ SCFree(hhhd->content);
+
+ BoyerMooreCtxDeInit(hhhd->bm_ctx);
+ SCFree(hhhd);
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_host is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpHHTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_host\"; "
+ "content:\"one\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_host entry is
+ * parsed.
+ */
+static int DetectHttpHHTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_host\"; "
+ "content:\"one\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a
+ * http_host is invalidated.
+ */
+static int DetectHttpHHTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_host\"; "
+ "http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_host is invalidated.
+ */
+static int DetectHttpHHTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_host\"; "
+ "content:\"one\"; rawbytes; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a http_host with nocase is parsed.
+ */
+static int DetectHttpHHTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_host\"; "
+ "content:\"one\"; http_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHHTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHHTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message";
+ uint8_t http2_buf[] =
+ "body1\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHHTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "host: This is dummy mess";
+ uint8_t http2_buf[] =
+ "age body\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_host content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpHHTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"body1this\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_host content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpHHTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy bodY1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy bodY1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"body1this\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_host content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpHHTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:!\"message\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_host content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpHHTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy body\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:!\"message\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHHTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"abcdefghijklmnopqrstuvwxyz0123456789\"; http_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test multiple http transactions and body chunks of request handling
+ */
+static int DetectHttpHHTest14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "Cookie: dummy1\r\n";
+ uint8_t httpbuf3[] = "Host: Body one!!\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf5[] = "Cookie: dummy2\r\n";
+ uint8_t httpbuf6[] = "Host: Body two\r\n\r\n";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy1\"; http_cookie; content:\"body one\"; http_host; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"dummy2\"; http_cookie; content:\"body two\"; http_host; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2)) {
+ printf("sig 1 alerted (4): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || !(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match or sig 1 matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+int DetectHttpHHTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; content:\"two\"; http_host; "
+ "content:\"three\"; distance:10; http_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (cd1->flags != 0 || memcmp(cd1->content, "one", cd1->content_len) != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "two", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd1) ||
+ !DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; pcre:/two/; "
+ "content:\"three\"; distance:10; within:15; http_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_host; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != DETECT_PCRE_RELATIVE_NEXT ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_host; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT | DETECT_CONTENT_OFFSET) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ printf ("failed: http_host incorrect flags");
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_host; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; pcre:/two/; "
+ "content:\"three\"; http_host; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hhhd1->flags != (0) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DEPTH) ||
+ memcmp(hhhd2->content, "three", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ !DETECT_CONTENT_IS_SINGLE(hhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; distance:0; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hhhd2->content, "two", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; "
+ "content:\"two\"; within:5; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "one", hhhd1->content_len) != 0 ||
+ hhhd2->flags != (DETECT_CONTENT_WITHIN) ||
+ memcmp(hhhd2->content, "two", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_host; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list != NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/W; "
+ "content:\"two\"; within:5; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhhd2->flags != (DETECT_CONTENT_WITHIN) ||
+ memcmp(hhhd2->content, "two", hhhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"two\"; http_host; "
+ "pcre:/one/WR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ hhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hhhd1->content, "two", hhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHHTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/W; "
+ "content:\"two\"; distance:5; http_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->prev->ctx;
+ DetectContentData *hhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hhhd2->content, "two", hhhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpHHRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpHHTest01", DetectHttpHHTest01, 1);
+ UtRegisterTest("DetectHttpHHTest02", DetectHttpHHTest02, 1);
+ UtRegisterTest("DetectHttpHHTest03", DetectHttpHHTest03, 1);
+ UtRegisterTest("DetectHttpHHTest04", DetectHttpHHTest04, 1);
+ UtRegisterTest("DetectHttpHHTest05", DetectHttpHHTest05, 1);
+ UtRegisterTest("DetectHttpHHTest06", DetectHttpHHTest06, 1);
+ UtRegisterTest("DetectHttpHHTest07", DetectHttpHHTest07, 1);
+ UtRegisterTest("DetectHttpHHTest08", DetectHttpHHTest08, 1);
+ UtRegisterTest("DetectHttpHHTest09", DetectHttpHHTest09, 1);
+ UtRegisterTest("DetectHttpHHTest10", DetectHttpHHTest10, 1);
+ UtRegisterTest("DetectHttpHHTest11", DetectHttpHHTest11, 1);
+ UtRegisterTest("DetectHttpHHTest12", DetectHttpHHTest12, 1);
+ UtRegisterTest("DetectHttpHHTest13", DetectHttpHHTest13, 1);
+ UtRegisterTest("DetectHttpHHTest14", DetectHttpHHTest14, 1);
+
+ UtRegisterTest("DetectHttpHHTest22", DetectHttpHHTest22, 1);
+ UtRegisterTest("DetectHttpHHTest23", DetectHttpHHTest23, 1);
+ UtRegisterTest("DetectHttpHHTest24", DetectHttpHHTest24, 1);
+ UtRegisterTest("DetectHttpHHTest25", DetectHttpHHTest25, 1);
+ UtRegisterTest("DetectHttpHHTest26", DetectHttpHHTest26, 1);
+ UtRegisterTest("DetectHttpHHTest27", DetectHttpHHTest27, 1);
+ UtRegisterTest("DetectHttpHHTest28", DetectHttpHHTest28, 1);
+ UtRegisterTest("DetectHttpHHTest29", DetectHttpHHTest29, 1);
+ UtRegisterTest("DetectHttpHHTest30", DetectHttpHHTest30, 1);
+ UtRegisterTest("DetectHttpHHTest31", DetectHttpHHTest31, 1);
+ UtRegisterTest("DetectHttpHHTest32", DetectHttpHHTest32, 1);
+ UtRegisterTest("DetectHttpHHTest33", DetectHttpHHTest33, 1);
+ UtRegisterTest("DetectHttpHHTest34", DetectHttpHHTest34, 1);
+ UtRegisterTest("DetectHttpHHTest35", DetectHttpHHTest35, 1);
+ UtRegisterTest("DetectHttpHHTest36", DetectHttpHHTest36, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-hh.h b/framework/src/suricata/src/detect-http-hh.h
new file mode 100644
index 00000000..bb6fd3da
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-hh.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_HH_H__
+#define __DETECT_HTTP_HH_H__
+
+void DetectHttpHHRegister(void);
+
+#endif /* __DETECT_HTTP_HH_H__ */
diff --git a/framework/src/suricata/src/detect-http-hrh.c b/framework/src/suricata/src/detect-http-hrh.c
new file mode 100644
index 00000000..a6a56b10
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-hrh.c
@@ -0,0 +1,2233 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements support for the http_raw_host keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "stream-tcp.h"
+#include "detect-http-hrh.h"
+
+int DetectHttpHRHSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpHRHRegisterTests(void);
+void DetectHttpHRHFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_raw_host" keyword.
+ */
+void DetectHttpHRHRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].name = "http_raw_host";
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].desc = "content modifier to match only on the HTTP host header or the raw hostname from the HTTP uri";
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].Setup = DetectHttpHRHSetup;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].Free = DetectHttpHRHFree;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].RegisterTests = DetectHttpHRHRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].flags |= SIGMATCH_NOOPT ;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HOST].flags |= SIGMATCH_PAYLOAD ;
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_raw_host keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to the signature for the current Signature being
+ * parsed from the rules.
+ * \param m Pointer to the head of the SigMatch for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int DetectHttpHRHSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_RAW_HOST,
+ DETECT_SM_LIST_HRHHDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/**
+ * \brief The function to free the http_raw_host data.
+ *
+ * \param ptr Pointer to the http_raw_host.
+ */
+void DetectHttpHRHFree(void *ptr)
+{
+ DetectContentData *hrhhd = (DetectContentData *)ptr;
+ if (hrhhd == NULL)
+ return;
+
+ if (hrhhd->content != NULL)
+ SCFree(hrhhd->content);
+
+ BoyerMooreCtxDeInit(hrhhd->bm_ctx);
+ SCFree(hrhhd);
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_raw_host is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpHRHTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_host\"; "
+ "content:\"one\"; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_raw_host entry is
+ * parsed.
+ */
+static int DetectHttpHRHTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_host\"; "
+ "content:\"one\"; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a
+ * http_raw_host is invalidated.
+ */
+static int DetectHttpHRHTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_host\"; "
+ "http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_raw_host is invalidated.
+ */
+static int DetectHttpHRHTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_host\"; "
+ "content:\"one\"; rawbytes; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a http_raw_host with nocase is parsed.
+ */
+static int DetectHttpHRHTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_host\"; "
+ "content:\"one\"; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHRHTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHRHTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message";
+ uint8_t http2_buf[] =
+ "body1\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHRHTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "host: This is dummy mess";
+ uint8_t http2_buf[] =
+ "age body\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"message\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpHRHTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"body1This\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpHRHTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy bodY1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy bodY1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"bodY1This\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_raw_host content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpHRHTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:!\"message\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_raw_host content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpHRHTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy body\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:!\"message\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_raw_host content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpHRHTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"abcdefghijklmnopqrstuvwxyz0123456789\"; http_raw_host; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test multiple http transactions and body chunks of request handling
+ */
+static int DetectHttpHRHTest14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "Cookie: dummy1\r\n";
+ uint8_t httpbuf3[] = "Host: Body one!!\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf5[] = "Cookie: dummy2\r\n";
+ uint8_t httpbuf6[] = "Host: Body two\r\n\r\n";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy1\"; http_cookie; content:\"Body one\"; http_raw_host; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"dummy2\"; http_cookie; content:\"Body two\"; http_raw_host; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2)) {
+ printf("sig 1 alerted (4): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || !(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match or sig 1 matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+int DetectHttpHRHTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; content:\"two\"; http_raw_host; "
+ "content:\"three\"; distance:10; http_raw_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (cd1->flags != 0 || memcmp(cd1->content, "one", cd1->content_len) != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "two", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd1) ||
+ !DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_raw_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; pcre:/two/; "
+ "content:\"three\"; distance:10; within:15; http_raw_host; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_raw_host; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != DETECT_PCRE_RELATIVE_NEXT ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_raw_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_raw_host; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT | DETECT_CONTENT_OFFSET) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ printf ("failed: http_raw_host incorrect flags");
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_raw_host; pcre:/two/; "
+ "content:\"three\"; distance:10; http_raw_host; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; nocase; pcre:/two/; "
+ "content:\"three\"; http_raw_host; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hrhhd1->flags != (DETECT_CONTENT_NOCASE) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DEPTH) ||
+ memcmp(hrhhd2->content, "three", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ !DETECT_CONTENT_IS_SINGLE(hrhhd1) ||
+ DETECT_CONTENT_IS_SINGLE(hrhhd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; "
+ "content:\"two\"; distance:0; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hrhhd2->content, "two", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; "
+ "content:\"two\"; within:5; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "one", hrhhd1->content_len) != 0 ||
+ hrhhd2->flags != (DETECT_CONTENT_WITHIN) ||
+ memcmp(hrhhd2->content, "two", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_raw_host; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list != NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/Zi; "
+ "content:\"two\"; within:5; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT | DETECT_PCRE_CASELESS) ||
+ hrhhd2->flags != (DETECT_CONTENT_WITHIN) ||
+ memcmp(hrhhd2->content, "two", hrhhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"two\"; http_raw_host; "
+ "pcre:/one/ZRi; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hrhhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE | DETECT_PCRE_CASELESS) ||
+ hrhhd1->flags != (DETECT_CONTENT_RELATIVE_NEXT) ||
+ memcmp(hrhhd1->content, "two", hrhhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpHRHTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/Zi; "
+ "content:\"two\"; distance:5; http_raw_host; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->prev->ctx;
+ DetectContentData *hrhhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT | DETECT_PCRE_CASELESS) ||
+ hrhhd2->flags != (DETECT_CONTENT_DISTANCE) ||
+ memcmp(hrhhd2->content, "two", hrhhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ *\test Test that the http_raw_host content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpHRHTest37(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "User-Agent: www.openinfosecfoundation.org\r\n"
+ "Host: This is dummy bodY1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy bodY1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http host test\"; "
+ "content:\"body1this\"; http_raw_host; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpHRHRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpHRHTest01", DetectHttpHRHTest01, 1);
+ UtRegisterTest("DetectHttpHRHTest02", DetectHttpHRHTest02, 1);
+ UtRegisterTest("DetectHttpHRHTest03", DetectHttpHRHTest03, 1);
+ UtRegisterTest("DetectHttpHRHTest04", DetectHttpHRHTest04, 1);
+ UtRegisterTest("DetectHttpHRHTest05", DetectHttpHRHTest05, 1);
+ UtRegisterTest("DetectHttpHRHTest06", DetectHttpHRHTest06, 1);
+ UtRegisterTest("DetectHttpHRHTest07", DetectHttpHRHTest07, 1);
+ UtRegisterTest("DetectHttpHRHTest08", DetectHttpHRHTest08, 1);
+ UtRegisterTest("DetectHttpHRHTest09", DetectHttpHRHTest09, 1);
+ UtRegisterTest("DetectHttpHRHTest10", DetectHttpHRHTest10, 1);
+ UtRegisterTest("DetectHttpHRHTest11", DetectHttpHRHTest11, 1);
+ UtRegisterTest("DetectHttpHRHTest12", DetectHttpHRHTest12, 1);
+ UtRegisterTest("DetectHttpHRHTest13", DetectHttpHRHTest13, 1);
+ UtRegisterTest("DetectHttpHRHTest14", DetectHttpHRHTest14, 1);
+
+ UtRegisterTest("DetectHttpHRHTest22", DetectHttpHRHTest22, 1);
+ UtRegisterTest("DetectHttpHRHTest23", DetectHttpHRHTest23, 1);
+ UtRegisterTest("DetectHttpHRHTest24", DetectHttpHRHTest24, 1);
+ UtRegisterTest("DetectHttpHRHTest25", DetectHttpHRHTest25, 1);
+ UtRegisterTest("DetectHttpHRHTest26", DetectHttpHRHTest26, 1);
+ UtRegisterTest("DetectHttpHRHTest27", DetectHttpHRHTest27, 1);
+ UtRegisterTest("DetectHttpHRHTest28", DetectHttpHRHTest28, 1);
+ UtRegisterTest("DetectHttpHRHTest29", DetectHttpHRHTest29, 1);
+ UtRegisterTest("DetectHttpHRHTest30", DetectHttpHRHTest30, 1);
+ UtRegisterTest("DetectHttpHRHTest31", DetectHttpHRHTest31, 1);
+ UtRegisterTest("DetectHttpHRHTest32", DetectHttpHRHTest32, 1);
+ UtRegisterTest("DetectHttpHRHTest33", DetectHttpHRHTest33, 1);
+ UtRegisterTest("DetectHttpHRHTest34", DetectHttpHRHTest34, 1);
+ UtRegisterTest("DetectHttpHRHTest35", DetectHttpHRHTest35, 1);
+ UtRegisterTest("DetectHttpHRHTest36", DetectHttpHRHTest36, 1);
+ UtRegisterTest("DetectHttpHRHTest37", DetectHttpHRHTest37, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-hrh.h b/framework/src/suricata/src/detect-http-hrh.h
new file mode 100644
index 00000000..0255f167
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-hrh.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_HRH_H__
+#define __DETECT_HTTP_HRH_H__
+
+void DetectHttpHRHRegister(void);
+
+#endif /* __DETECT_HTTP_HRH_H__ */
diff --git a/framework/src/suricata/src/detect-http-method.c b/framework/src/suricata/src/detect-http-method.c
new file mode 100644
index 00000000..4cc8a787
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-method.c
@@ -0,0 +1,833 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements the http_method keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-method.h"
+#include "stream-tcp.h"
+
+
+static int DetectHttpMethodSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpMethodRegisterTests(void);
+void DetectHttpMethodFree(void *);
+
+/**
+ * \brief Registration function for keyword: http_method
+ */
+void DetectHttpMethodRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_METHOD].name = "http_method";
+ sigmatch_table[DETECT_AL_HTTP_METHOD].desc = "content modifier to match only on the HTTP method-buffer";
+ sigmatch_table[DETECT_AL_HTTP_METHOD].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_method";
+ sigmatch_table[DETECT_AL_HTTP_METHOD].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].Setup = DetectHttpMethodSetup;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].Free = DetectHttpMethodFree;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].RegisterTests = DetectHttpMethodRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_METHOD].flags |= SIGMATCH_PAYLOAD;
+
+ SCLogDebug("registering http_method rule option");
+}
+
+/**
+ * \brief This function is used to add the parsed "http_method" option
+ * into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param str Pointer to the user provided option string.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectHttpMethodSetup(DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, str,
+ DETECT_AL_HTTP_METHOD,
+ DETECT_SM_LIST_HMDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/**
+ * \brief this function will free memory associated with DetectContentData
+ *
+ * \param id_d pointer to DetectContentData
+ */
+void DetectHttpMethodFree(void *ptr)
+{
+ DetectContentData *data = (DetectContentData *)ptr;
+
+ if (data->content != NULL)
+ SCFree(data->content);
+ SCFree(data);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Check a signature with content */
+int DetectHttpMethodTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"GET\"; "
+ "http_method; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature without content (fail) */
+int DetectHttpMethodTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "http_method; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with parameter (fail) */
+int DetectHttpMethodTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"foobar\"; "
+ "http_method:\"GET\"; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with fast_pattern (should work) */
+int DetectHttpMethodTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"GET\"; "
+ "fast_pattern; "
+ "http_method; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with rawbytes (fail) */
+int DetectHttpMethodTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"GET\"; "
+ "rawbytes; "
+ "http_method; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test setting the nocase flag */
+static int DetectHttpMethodTest12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ if (DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
+ "(content:\"one\"; http_method; nocase; sid:1;)") == NULL) {
+ printf("DetectEngineAppend == NULL: ");
+ goto end;
+ }
+ if (DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
+ "(content:\"one\"; nocase; http_method; sid:2;)") == NULL) {
+ printf("DetectEngineAppend == NULL: ");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HMDMATCH] == NULL: ");
+ goto end;
+ }
+
+ DetectContentData *hmd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ DetectContentData *hmd2 = (DetectContentData *)de_ctx->sig_list->next->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+
+ if (!(hmd1->flags & DETECT_CONTENT_NOCASE)) {
+ printf("nocase flag not set on sig 1: ");
+ goto end;
+ }
+
+ if (!(hmd2->flags & DETECT_CONTENT_NOCASE)) {
+ printf("nocase flag not set on sig 2: ");
+ goto end;
+ }
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with method + within and pcre with /M (should work) */
+int DetectHttpMethodTest13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "pcre:\"/HE/M\"; "
+ "content:\"AD\"; "
+ "within:2; http_method; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with method + within and pcre without /M (should fail) */
+int DetectHttpMethodTest14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "pcre:\"/HE/\"; "
+ "content:\"AD\"; "
+ "http_method; within:2; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with method + within and pcre with /M (should work) */
+int DetectHttpMethodTest15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "pcre:\"/HE/M\"; "
+ "content:\"AD\"; "
+ "http_method; within:2; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/** \test Check a signature with an known request method */
+static int DetectHttpMethodSigTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\n"
+ "Host: foo.bar.tld\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"GET\"; "
+ "http_method; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"POST\"; "
+ "http_method; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ SCLogDebug("no http state: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check a signature with an unknown request method */
+static int DetectHttpMethodSigTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "FOO / HTTP/1.0\r\n"
+ "Host: foo.bar.tld\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"FOO\"; "
+ "http_method; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\"BAR\"; "
+ "http_method; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ SCLogDebug("no http state: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, (void *) det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check a signature against an unparsable request */
+static int DetectHttpMethodSigTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = " ";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "content:\" \"; "
+ "http_method; sid:1;)");
+ if (s == NULL) {
+ SCLogDebug("Bad signature");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ SCLogDebug("no http state: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check a signature with an request method and negation of the same */
+static int DetectHttpMethodSigTest04(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\n"
+ "Host: foo.bar.tld\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"Testing http_method\"; "
+ "content:\"GET\"; http_method; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,
+ "alert tcp any any -> any any (msg:\"Testing http_method\"; "
+ "content:!\"GET\"; http_method; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ SCLogDebug("no http state: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 matched but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *) det_ctx);
+ }
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectHttpMethod
+ */
+void DetectHttpMethodRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ SCLogDebug("Registering tests for DetectHttpMethod...");
+ UtRegisterTest("DetectHttpMethodTest01", DetectHttpMethodTest01, 1);
+ UtRegisterTest("DetectHttpMethodTest02", DetectHttpMethodTest02, 1);
+ UtRegisterTest("DetectHttpMethodTest03", DetectHttpMethodTest03, 1);
+ UtRegisterTest("DetectHttpMethodTest04", DetectHttpMethodTest04, 1);
+ UtRegisterTest("DetectHttpMethodTest05", DetectHttpMethodTest05, 1);
+ UtRegisterTest("DetectHttpMethodTest12 -- nocase flag", DetectHttpMethodTest12, 1);
+ UtRegisterTest("DetectHttpMethodTest13", DetectHttpMethodTest13, 1);
+ UtRegisterTest("DetectHttpMethodTest14", DetectHttpMethodTest14, 1);
+ UtRegisterTest("DetectHttpMethodTest15", DetectHttpMethodTest15, 1);
+ UtRegisterTest("DetectHttpMethodSigTest01", DetectHttpMethodSigTest01, 1);
+ UtRegisterTest("DetectHttpMethodSigTest02", DetectHttpMethodSigTest02, 1);
+ UtRegisterTest("DetectHttpMethodSigTest03", DetectHttpMethodSigTest03, 1);
+ UtRegisterTest("DetectHttpMethodSigTest04", DetectHttpMethodSigTest04, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-method.h b/framework/src/suricata/src/detect-http-method.h
new file mode 100644
index 00000000..9e6dc4dd
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-method.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_METHOD_H__
+#define __DETECT_HTTP_METHOD_H__
+
+/* prototypes */
+void DetectHttpMethodRegister(void);
+int DetectHttpMethodDoMatch(DetectEngineThreadCtx *, Signature *, SigMatch *,
+ Flow *, uint8_t, void *);
+
+#endif /* __DETECT_HTTP_METHOD_H__ */
+
diff --git a/framework/src/suricata/src/detect-http-raw-header.c b/framework/src/suricata/src/detect-http-raw-header.c
new file mode 100644
index 00000000..1a5f0eb2
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-raw-header.c
@@ -0,0 +1,1557 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements support for http_raw_header keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-raw-header.h"
+#include "stream-tcp.h"
+
+int DetectHttpRawHeaderSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpRawHeaderRegisterTests(void);
+void DetectHttpRawHeaderFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_raw_header" keyword.
+ */
+void DetectHttpRawHeaderRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].name = "http_raw_header";
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].Setup = DetectHttpRawHeaderSetup;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].Free = DetectHttpRawHeaderFree;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].RegisterTests = DetectHttpRawHeaderRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_RAW_HEADER].flags |= SIGMATCH_PAYLOAD;
+
+ return;
+}
+
+
+/**
+ * \brief this function clears the memory of http_raw_header modifier keyword
+ *
+ * \param ptr Pointer to the Detection Header Data
+ */
+void DetectHttpRawHeaderFree(void *ptr)
+{
+ DetectContentData *cd = (DetectContentData *)ptr;
+ if (cd == NULL)
+ return;
+
+ if (cd->content != NULL)
+ SCFree(cd->content);
+ SCFree(cd);
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_raw_header keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param m Pointer to the head of the SigMatchs for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectHttpRawHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_RAW_HEADER,
+ DETECT_SM_LIST_HRHDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_header is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpRawHeaderTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ SigMatch *sm = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; flow:to_server; "
+ "content:\"one\"; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("Error parsing signature: ");
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH];
+ if (sm != NULL) {
+ result &= (sm->type == DETECT_CONTENT);
+ result &= (sm->next == NULL);
+ } else {
+ result = 0;
+ printf("Error updating content pattern to http_header pattern: ");
+ }
+
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_header entry is
+ * parsed.
+ */
+static int DetectHttpRawHeaderTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; flow:to_server; "
+ "content:\"one\"; http_raw_header:; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a http_header
+ * is invalidated.
+ */
+static int DetectHttpRawHeaderTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; flow:to_server; "
+ "http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_header is invalidated.
+ */
+static int DetectHttpRawHeaderTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; flow:to_server; "
+ "content:\"one\"; rawbytes; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_header is invalidated.
+ */
+static int DetectHttpRawHeaderTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_header\"; flow:to_server; "
+ "content:\"one\"; nocase; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+ else
+ printf("Error parsing signature: ");
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpRawHeaderTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"Content-Type: text/html\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpRawHeaderTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozi";
+ uint8_t http2_buf[] =
+ "lla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\nContent-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy message body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"Mozilla\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ( (PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpRawHeaderTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n";
+ uint8_t http2_buf[] =
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"Gecko/20091221 Firefox/3.5.7\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpRawHeaderTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n";
+ uint8_t http2_buf[] =
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy body\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"Firefox/3.5.7|0D 0A|Content\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpRawHeaderTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n";
+ uint8_t http2_buf[] =
+ "Content-Type: text/html\r\n"
+ "Content-Length: 67\r\n"
+ "\r\n"
+ "This is dummy body";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"firefox/3.5.7|0D 0A|content\"; nocase; http_raw_header;"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_header content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpRawHeaderTest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"lalalalala\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_header content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpRawHeaderTest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 26\r\n"
+ "\r\n"
+ "This is dummy message body\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:!\"User-Agent: Mozilla/5.0 \"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_header content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpRawHeaderTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 100\r\n"
+ "\r\n"
+ "longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http header test\"; flow:to_server; "
+ "content:\"Host: www.openinfosecfoundation.org\"; http_raw_header; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+int DetectHttpRawHeaderTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; "
+ "content:\"two\"; distance:0; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hrhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ DetectContentData *hrhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (hrhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hrhd1->content, "one", hrhd1->content_len) != 0 ||
+ hrhd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hrhd2->content, "two", hrhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; "
+ "content:\"two\"; within:5; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hrhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ DetectContentData *hrhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (hrhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hrhd1->content, "one", hrhd1->content_len) != 0 ||
+ hrhd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hrhd2->content, "two", hrhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; within:5; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; http_raw_header; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; pcre:/one/D; "
+ "content:\"two\"; within:5; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hhd2->content, "two", hhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; content:\"two\"; http_raw_header; "
+ "pcre:/one/DR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hhd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ hhd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hhd1->content, "two", hhd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawHeaderTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
+ "(flow:to_server; pcre:/one/D; "
+ "content:\"two\"; distance:5; http_raw_header; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRHDMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->prev->ctx;
+ DetectContentData *hhd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hhd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hhd2->content, "two", hhd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpRawHeaderRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpRawHeaderTest01", DetectHttpRawHeaderTest01, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest02", DetectHttpRawHeaderTest02, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest03", DetectHttpRawHeaderTest03, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest04", DetectHttpRawHeaderTest04, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest05", DetectHttpRawHeaderTest05, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest06", DetectHttpRawHeaderTest06, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest07", DetectHttpRawHeaderTest07, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest08", DetectHttpRawHeaderTest08, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest09", DetectHttpRawHeaderTest09, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest10", DetectHttpRawHeaderTest10, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest11", DetectHttpRawHeaderTest11, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest12", DetectHttpRawHeaderTest12, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest13", DetectHttpRawHeaderTest13, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest20", DetectHttpRawHeaderTest20, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest21", DetectHttpRawHeaderTest21, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest22", DetectHttpRawHeaderTest22, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest23", DetectHttpRawHeaderTest23, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest24", DetectHttpRawHeaderTest24, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest25", DetectHttpRawHeaderTest25, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest26", DetectHttpRawHeaderTest26, 1);
+ UtRegisterTest("DetectHttpRawHeaderTest27", DetectHttpRawHeaderTest27, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-raw-header.h b/framework/src/suricata/src/detect-http-raw-header.h
new file mode 100644
index 00000000..f6b480b2
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-raw-header.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_RAW_HEADER_H__
+#define __DETECT_HTTP_RAW_HEADER_H__
+
+void DetectHttpRawHeaderRegister(void);
+
+#endif /* __DETECT_HTTP_RAW_HEADER_H__ */
diff --git a/framework/src/suricata/src/detect-http-raw-uri.c b/framework/src/suricata/src/detect-http-raw-uri.c
new file mode 100644
index 00000000..e269686e
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-raw-uri.c
@@ -0,0 +1,566 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-raw-uri.h"
+#include "stream-tcp.h"
+
+static int DetectHttpRawUriSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectHttpRawUriRegisterTests(void);
+
+/**
+ * \brief Registration function for keyword http_raw_uri.
+ */
+void DetectHttpRawUriRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].name = "http_raw_uri";
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].desc = "content modifier to match on HTTP uri";
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_uri-and-http_raw_uri";
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].Setup = DetectHttpRawUriSetup;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].Free = NULL;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].RegisterTests = DetectHttpRawUriRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_RAW_URI].flags |= SIGMATCH_PAYLOAD;
+
+ return;
+}
+
+/**
+ * \brief Sets up the http_raw_uri modifier keyword.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Signature to which the current keyword belongs.
+ * \param arg Should hold an empty string always.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int DetectHttpRawUriSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_RAW_URI,
+ DETECT_SM_LIST_HRUDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+
+/******************************** UNITESTS **********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Checks if a http_raw_uri is registered in a Signature, if content is not
+ * specified in the signature.
+ */
+int DetectHttpRawUriTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_raw_uri is registered in a Signature, if some parameter
+ * is specified with http_raw_uri in the signature.
+ */
+int DetectHttpRawUriTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; content:\"one\"; "
+ "http_raw_uri:wrong; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_raw_uri is registered in a Signature.
+ */
+int DetectHttpRawUriTest03(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; "
+ "content:\"one\"; http_raw_uri; "
+ "content:\"two\"; http_raw_uri; "
+ "content:\"three\"; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH];
+ if (sm == NULL) {
+ printf("no sigmatch(es): ");
+ goto end;
+ }
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ printf("expected DETECT_CONTENT for http_raw_uri(%d), got %d: ",
+ DETECT_CONTENT, sm->type);
+ goto end;
+ }
+ sm = sm->next;
+ }
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_raw_uri is registered in a Signature, when rawbytes is
+ * also specified in the signature.
+ */
+int DetectHttpRawUriTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; "
+ "content:\"one\"; rawbytes; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_raw_uri is successfully converted to a rawuricontent.
+ *
+ */
+int DetectHttpRawUriTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ Signature *s = NULL;
+ int result = 0;
+
+ if ((de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; "
+ "content:\"we are testing http_raw_uri keyword\"; http_raw_uri; "
+ "sid:1;)");
+ if (s == NULL) {
+ printf("sig failed to parse\n");
+ goto end;
+ }
+ if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL)
+ goto end;
+ if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH]->type != DETECT_CONTENT) {
+ printf("wrong type\n");
+ goto end;
+ }
+
+ char *str = "we are testing http_raw_uri keyword";
+ int uricomp = memcmp((const char *)
+ ((DetectContentData*)s->sm_lists[DETECT_SM_LIST_HRUDMATCH]->ctx)->content,
+ str,
+ strlen(str) - 1);
+ int urilen = ((DetectContentData*)s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx)->content_len;
+ if (uricomp != 0 ||
+ urilen != strlen("we are testing http_raw_uri keyword")) {
+ printf("sig failed to parse, content not setup properly\n");
+ goto end;
+ }
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; distance:0; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ DetectContentData *ud2 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ /* inside body */
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; within:5; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ DetectContentData *ud2 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ /* inside the body */
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest16(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest17(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; distance:0; http_raw_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ DetectContentData *ud2 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ /* inside body */
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpRawUriTest18(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_raw_uri; "
+ "content:\"two\"; within:5; http_raw_uri; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HRUDMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->prev->ctx;
+ DetectContentData *ud2 =
+ (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ /* inside body */
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the UNITTESTS for the http_uri keyword
+ */
+static void DetectHttpRawUriRegisterTests (void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectHttpRawUriTest01", DetectHttpRawUriTest01, 1);
+ UtRegisterTest("DetectHttpRawUriTest02", DetectHttpRawUriTest02, 1);
+ UtRegisterTest("DetectHttpRawUriTest03", DetectHttpRawUriTest03, 1);
+ UtRegisterTest("DetectHttpRawUriTest04", DetectHttpRawUriTest04, 1);
+ UtRegisterTest("DetectHttpRawUriTest05", DetectHttpRawUriTest05, 1);
+ UtRegisterTest("DetectHttpRawUriTest12", DetectHttpRawUriTest12, 1);
+ UtRegisterTest("DetectHttpRawUriTest13", DetectHttpRawUriTest13, 1);
+ UtRegisterTest("DetectHttpRawUriTest14", DetectHttpRawUriTest14, 1);
+ UtRegisterTest("DetectHttpRawUriTest15", DetectHttpRawUriTest15, 1);
+ UtRegisterTest("DetectHttpRawUriTest16", DetectHttpRawUriTest16, 1);
+ UtRegisterTest("DetectHttpRawUriTest17", DetectHttpRawUriTest17, 1);
+ UtRegisterTest("DetectHttpRawUriTest18", DetectHttpRawUriTest18, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-raw-uri.h b/framework/src/suricata/src/detect-http-raw-uri.h
new file mode 100644
index 00000000..805adfbe
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-raw-uri.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ */
+
+#ifndef __DETECT_HTTP_URI_H__
+#define __DETECT_HTTP_URI_H__
+
+void DetectHttpRawUriRegister(void);
+
+#endif /* __DETECT_HTTP_URI_H__ */
diff --git a/framework/src/suricata/src/detect-http-server-body.c b/framework/src/suricata/src/detect-http-server-body.c
new file mode 100644
index 00000000..a5463b66
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-server-body.c
@@ -0,0 +1,3873 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements support for the http_server_body keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-server-body.h"
+#include "stream-tcp.h"
+
+int DetectHttpServerBodySetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpServerBodyRegisterTests(void);
+void DetectHttpServerBodyFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_server_body" keyword.
+ */
+void DetectHttpServerBodyRegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].name = "http_server_body";
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].desc = "content modifier to match only on the HTTP response-body";
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_server_body";
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].Setup = DetectHttpServerBodySetup;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].Free = DetectHttpServerBodyFree;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].RegisterTests = DetectHttpServerBodyRegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_SERVER_BODY].flags |= SIGMATCH_PAYLOAD ;
+}
+
+static void DetectHttpServerBodySetupCallback(Signature *s)
+{
+ s->flags |= SIG_FLAG_APPLAYER;
+ AppLayerHtpEnableResponseBodyCallback();
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_server_body keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules.
+ * \param m Pointer to the head of the SigMatchs for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int DetectHttpServerBodySetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_SERVER_BODY,
+ DETECT_SM_LIST_FILEDATA,
+ ALPROTO_HTTP,
+ DetectHttpServerBodySetupCallback);
+}
+
+/**
+ * \brief The function to free the http_server_body data.
+ *
+ * \param ptr Pointer to the http_server_body.
+ */
+void DetectHttpServerBodyFree(void *ptr)
+{
+ SCEnter();
+ DetectContentData *hsbd = (DetectContentData *)ptr;
+ if (hsbd == NULL)
+ SCReturn;
+
+ if (hsbd->content != NULL)
+ SCFree(hsbd->content);
+
+ BoyerMooreCtxDeInit(hsbd->bm_ctx);
+ SCFree(hsbd);
+
+ SCReturn;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_server_body is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpServerBodyTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ SigMatch *sm = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_server_body\"; "
+ "content:\"one\"; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ /* sm should not be in the MATCH list */
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_MATCH];
+ if (sm != NULL) {
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ goto end;
+ }
+
+ if (sm->type != DETECT_CONTENT) {
+ printf("sm type not DETECT_AL_HTTP_SERVER_BODY: ");
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_server_body entry is
+ * parsed.
+ */
+static int DetectHttpServerBodyTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_server_body\"; "
+ "content:\"one\"; http_server_body:; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a http_server_body
+ * is invalidated.
+ */
+static int DetectHttpServerBodyTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_server_body\"; "
+ "http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_server_body is invalidated.
+ */
+static int DetectHttpServerBodyTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_server_body\"; "
+ "content:\"one\"; rawbytes; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_server_body is invalidated.
+ */
+static int DetectHttpServerBodyTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_server_body\"; "
+ "content:\"one\"; http_server_body; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyTest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"message\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START|STREAM_EOF, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "message";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"message\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched on chunk2 but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 (chunk3) but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyTest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"message\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyTest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sag";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ uint8_t http_buf4[] =
+ "e4u!!";
+ uint32_t http_len4 = sizeof(http_buf4) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"message\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf4, http_len4);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Case insensitve.
+ */
+static int DetectHttpServerBodyTest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sag";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ uint8_t http_buf4[] =
+ "e4u!!";
+ uint32_t http_len4 = sizeof(http_buf4) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:\"MeSSaGE\"; http_server_body; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf4, http_len4);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Negated match.
+ */
+static int DetectHttpServerBodyTest11(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "bigmessage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:!\"MaSSaGE\"; http_server_body; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have (p1): ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have (p2): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Negated match.
+ */
+static int DetectHttpServerBodyTest12(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "bigmessage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "content:!\"MeSSaGE\"; http_server_body; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have (p1): ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have (p2): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectHttpServerBodyTest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 55\r\n"
+ "\r\n"
+ "longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "content:\"longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\"; http_server_body; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START|STREAM_EOF, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test multiple http transactions and body chunks of request handling */
+static int DetectHttpServerBodyTest14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "GET /index1.html HTTP/1.1\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "one";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "GET /index2.html HTTP/1.1\r\n"
+ "User-Agent: Firefox/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy2\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "two";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; content:\"one\"; http_server_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; content:\"two\"; http_server_body; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCLogDebug("add chunk 1");
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ SCLogDebug("add chunk 2");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCLogDebug("inspect chunk 1");
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert (tx 1): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("add chunk 3");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ SCLogDebug("add chunk 4");
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SCLogDebug("inspect chunk 4");
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted (tx 2): ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sig 2 didn't alert (tx 2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int DetectHttpServerBodyTest15(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "GET /index1.html HTTP/1.1\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "one";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "GET /index2.html HTTP/1.1\r\n"
+ "User-Agent: Firefox/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy2\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "two";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; content:\"one\"; http_server_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; content:\"two\"; http_server_body; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert (tx 1): ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 2 alerted (tx 1): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted (tx 2): ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sig 2 didn't alert (tx 2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+int DetectHttpServerBodyTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; content:\"two\"; http_server_body; "
+ "content:\"three\"; distance:10; http_server_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (cd1->flags != 0 || memcmp(cd1->content, "one", cd1->content_len) != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "two", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd1) ||
+ !DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_server_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; pcre:/two/; "
+ "content:\"three\"; distance:10; within:15; http_server_body; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_server_body; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != DETECT_PCRE_RELATIVE_NEXT ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; offset:10; http_server_body; pcre:/two/; "
+ "content:\"three\"; distance:10; http_server_body; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != (DETECT_CONTENT_RELATIVE_NEXT | DETECT_CONTENT_OFFSET) ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test invalid combination for content: distance, depth, http_server_body */
+int DetectHttpServerBodyTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; offset:10; http_server_body; pcre:/two/; distance:10; "
+ "content:\"three\"; distance:10; http_server_body; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ printf("de_ctx->sig_list != NULL: ");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; pcre:/two/; "
+ "content:\"three\"; http_server_body; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ hsbd1->flags != 0 ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_DEPTH ||
+ memcmp(hsbd2->content, "three", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ !DETECT_CONTENT_IS_SINGLE(hsbd1) ||
+ DETECT_CONTENT_IS_SINGLE(hsbd2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; distance:0; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hsbd2->content, "two", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; "
+ "content:\"two\"; within:5; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "one", hsbd1->content_len) != 0 ||
+ hsbd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hsbd2->content, "two", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_server_body; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/Q; "
+ "content:\"two\"; within:5; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hsbd2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(hsbd2->content, "two", hsbd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"two\"; http_server_body; "
+ "pcre:/one/QR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *hsbd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ hsbd1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(hsbd1->content, "two", hsbd1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpServerBodyTest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(pcre:/one/Q; "
+ "content:\"two\"; distance:5; http_server_body; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->prev->ctx;
+ DetectContentData *hsbd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ hsbd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(hsbd2->content, "two", hsbd2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyFileDataTest01(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 7\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "file_data; content:\"message\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START|STREAM_EOF, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyFileDataTest02(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "message";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "message";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "file_data; content:\"message\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyFileDataTest03(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "file_data; content:\"message\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpServerBodyFileDataTest04(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sag";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ uint8_t http_buf4[] =
+ "e4u!!";
+ uint32_t http_len4 = sizeof(http_buf4) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "file_data; content:\"message\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf4, http_len4);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Case insensitve.
+ */
+static int DetectHttpServerBodyFileDataTest05(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n"
+ "bigmes";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "sag";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ uint8_t http_buf4[] =
+ "e4u!!";
+ uint32_t http_len4 = sizeof(http_buf4) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http client body test\"; "
+ "file_data; content:\"MeSSaGE\"; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 matched but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf4, http_len4);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Negated match.
+ */
+static int DetectHttpServerBodyFileDataTest06(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "bigmessage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http file_data test\"; "
+ "file_data; content:!\"MaSSaGE\"; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have (p1): ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have (p2): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_server_body content matches against a http request
+ * which holds the content. Negated match.
+ */
+static int DetectHttpServerBodyFileDataTest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf1[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len1 = sizeof(http_buf1) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 14\r\n"
+ "\r\n";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ uint8_t http_buf3[] =
+ "bigmessage4u!!";
+ uint32_t http_len3 = sizeof(http_buf3) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOCLIENT;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOCLIENT;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http file_data test\"; "
+ "file_data; content:!\"MeSSaGE\"; nocase; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, http_buf1, http_len1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched but shouldn't have (p1): ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, http_buf3, http_len3);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sid 1 matched but shouldn't have (p2): ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+static int DetectHttpServerBodyFileDataTest08(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ uint8_t http_buf2[] =
+ "HTTP/1.0 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 55\r\n"
+ "\r\n"
+ "longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend";
+ uint32_t http_len2 = sizeof(http_buf2) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http server body test\"; "
+ "file_data; content:\"longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START|STREAM_EOF, http_buf2, http_len2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test multiple http transactions and body chunks of request handling */
+static int DetectHttpServerBodyFileDataTest09(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "GET /index1.html HTTP/1.1\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "one";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "GET /index2.html HTTP/1.1\r\n"
+ "User-Agent: Firefox/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy2\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "two";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; file_data; content:\"one\"; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; file_data; content:\"two\"; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert (tx 1): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted (tx 2): ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sig 2 didn't alert (tx 2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+static int DetectHttpServerBodyFileDataTest10(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "GET /index1.html HTTP/1.1\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "one";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "GET /index2.html HTTP/1.1\r\n"
+ "User-Agent: Firefox/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "Connection: keep-alive\r\n"
+ "Cookie: dummy2\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "HTTP/1.1 200 ok\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n"
+ "two";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; file_data; content:\"one\"; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:established,to_client; file_data; content:\"two\"; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert (tx 1): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted (tx 2): ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sig 2 didn't alert (tx 2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpServerBodyRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpServerBodyTest01", DetectHttpServerBodyTest01, 1);
+ UtRegisterTest("DetectHttpServerBodyTest02", DetectHttpServerBodyTest02, 1);
+ UtRegisterTest("DetectHttpServerBodyTest03", DetectHttpServerBodyTest03, 1);
+ UtRegisterTest("DetectHttpServerBodyTest04", DetectHttpServerBodyTest04, 1);
+ UtRegisterTest("DetectHttpServerBodyTest05", DetectHttpServerBodyTest05, 1);
+ UtRegisterTest("DetectHttpServerBodyTest06", DetectHttpServerBodyTest06, 1);
+ UtRegisterTest("DetectHttpServerBodyTest07", DetectHttpServerBodyTest07, 1);
+ UtRegisterTest("DetectHttpServerBodyTest08", DetectHttpServerBodyTest08, 1);
+ UtRegisterTest("DetectHttpServerBodyTest09", DetectHttpServerBodyTest09, 1);
+ UtRegisterTest("DetectHttpServerBodyTest10", DetectHttpServerBodyTest10, 1);
+ UtRegisterTest("DetectHttpServerBodyTest11", DetectHttpServerBodyTest11, 1);
+ UtRegisterTest("DetectHttpServerBodyTest12", DetectHttpServerBodyTest12, 1);
+ UtRegisterTest("DetectHttpServerBodyTest13", DetectHttpServerBodyTest13, 1);
+ UtRegisterTest("DetectHttpServerBodyTest14", DetectHttpServerBodyTest14, 1);
+ UtRegisterTest("DetectHttpServerBodyTest15", DetectHttpServerBodyTest15, 1);
+ UtRegisterTest("DetectHttpServerBodyTest22", DetectHttpServerBodyTest22, 1);
+ UtRegisterTest("DetectHttpServerBodyTest23", DetectHttpServerBodyTest23, 1);
+ UtRegisterTest("DetectHttpServerBodyTest24", DetectHttpServerBodyTest24, 1);
+ UtRegisterTest("DetectHttpServerBodyTest25", DetectHttpServerBodyTest25, 1);
+ UtRegisterTest("DetectHttpServerBodyTest26", DetectHttpServerBodyTest26, 1);
+ UtRegisterTest("DetectHttpServerBodyTest27", DetectHttpServerBodyTest27, 1);
+ UtRegisterTest("DetectHttpServerBodyTest28", DetectHttpServerBodyTest28, 1);
+ UtRegisterTest("DetectHttpServerBodyTest29", DetectHttpServerBodyTest29, 1);
+ UtRegisterTest("DetectHttpServerBodyTest30", DetectHttpServerBodyTest30, 1);
+ UtRegisterTest("DetectHttpServerBodyTest31", DetectHttpServerBodyTest31, 1);
+ UtRegisterTest("DetectHttpServerBodyTest32", DetectHttpServerBodyTest32, 1);
+ UtRegisterTest("DetectHttpServerBodyTest33", DetectHttpServerBodyTest33, 1);
+ UtRegisterTest("DetectHttpServerBodyTest34", DetectHttpServerBodyTest34, 1);
+ UtRegisterTest("DetectHttpServerBodyTest35", DetectHttpServerBodyTest35, 1);
+ UtRegisterTest("DetectHttpServerBodyTest36", DetectHttpServerBodyTest36, 1);
+
+ UtRegisterTest("DetectHttpServerBodyFileDataTest01", DetectHttpServerBodyFileDataTest01, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest02", DetectHttpServerBodyFileDataTest02, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest03", DetectHttpServerBodyFileDataTest03, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest04", DetectHttpServerBodyFileDataTest04, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest05", DetectHttpServerBodyFileDataTest05, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest06", DetectHttpServerBodyFileDataTest06, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest07", DetectHttpServerBodyFileDataTest07, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest08", DetectHttpServerBodyFileDataTest08, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest09", DetectHttpServerBodyFileDataTest09, 1);
+ UtRegisterTest("DetectHttpServerBodyFileDataTest10", DetectHttpServerBodyFileDataTest10, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-server-body.h b/framework/src/suricata/src/detect-http-server-body.h
new file mode 100644
index 00000000..7f20dc54
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-server-body.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_SERVER_BODY_H__
+#define __DETECT_HTTP_SERVER_BODY_H__
+
+void DetectHttpServerBodyRegister(void);
+
+#endif /* __DETECT_HTTP_SERVER_BODY_H__ */
diff --git a/framework/src/suricata/src/detect-http-stat-code.c b/framework/src/suricata/src/detect-http-stat-code.c
new file mode 100644
index 00000000..0237009f
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-stat-code.c
@@ -0,0 +1,688 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the http_stat_code keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-engine-mpm.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-stat-code.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp.h"
+
+int DetectHttpStatCodeMatch(ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t , void *, Signature *,
+ SigMatch *);
+static int DetectHttpStatCodeSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpStatCodeRegisterTests(void);
+void DetectHttpStatCodeFree(void *);
+
+/**
+ * \brief Registration function for keyword: http_stat_code
+ */
+void DetectHttpStatCodeRegister (void)
+{
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].name = "http_stat_code";
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].desc = "content modifier to match only on HTTP stat-code-buffer";
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_stat_code";
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].Setup = DetectHttpStatCodeSetup;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].Free = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].RegisterTests = DetectHttpStatCodeRegisterTests;
+
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_STAT_CODE].flags |= SIGMATCH_PAYLOAD;
+}
+
+/**
+ * \brief this function setups the http_stat_code modifier keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+static int DetectHttpStatCodeSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_STAT_CODE,
+ DETECT_SM_LIST_HSCDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test Checks if a http_stat_code is registered in a Signature, if content is not
+ * specified in the signature or rawbyes is specified or fast_pattern is
+ * provided in the signature.
+ */
+int DetectHttpStatCodeTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ((de_ctx = DetectEngineCtxInit()) == NULL) {
+ printf("DetectEngineCtxInit failed: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_code\"; http_stat_code; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ printf("sid 1 parse failed to error out: ");
+ goto end;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_code\"; content:\"|FF F1|\";"
+ " rawbytes; http_stat_code; sid:2;)");
+ if (de_ctx->sig_list != NULL) {
+ printf("sid 2 parse failed to error out: ");
+ goto end;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_code\"; content:\"100\";"
+ "fast_pattern; http_stat_code; sid:3;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sid 3 parse failed: ");
+ goto end;
+ }
+ if (!(((DetectContentData *)de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH]->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_stat_code is registered in a Signature and also checks
+ * the nocase
+ */
+int DetectHttpStatCodeTest02(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_code\"; content:\"one\"; "
+ "http_stat_code; content:\"200\"; http_stat_code; "
+ "content:\"two hundred\"; nocase; http_stat_code; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSCDMATCH];
+ if (sm == NULL) {
+ printf("no sigmatch(es): ");
+ goto end;
+ }
+
+ SigMatch *prev = NULL;
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ printf("expected DETECT_CONTENT for http_stat_code, got %d: ", sm->type);
+ goto end;
+ }
+ prev = sm;
+ sm = sm->next;
+ }
+
+ if (! (((DetectContentData *)prev->ctx)->flags &
+ DETECT_CONTENT_NOCASE))
+ {
+ result = 0;
+ }
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_code is matched . */
+static int DetectHttpStatCodeSigTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("DetectEngineCtxInit failed: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status code\"; content:\"200\"; http_stat_code; sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_code is not matched . */
+static int DetectHttpStatCodeSigTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status code\"; content:\"no\"; "
+ "http_stat_code; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "Status code\"; content:\"100\";"
+ "http_stat_code; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't: ");
+ goto end;
+ }
+ if ((PacketAlertCheck(p, 2))) {
+ printf("sid 2 match but shouldn't have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_code is matched for
+ * for nocase or not */
+static int DetectHttpStatCodeSigTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 FAIL OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status code\"; content:\"FAIL\"; "
+ "http_stat_code; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "Status code nocase\"; content:\"fail\"; nocase; "
+ "http_stat_code; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_code is matched for
+ * for negatoin or not */
+static int DetectHttpStatCodeSigTest04(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status code\"; content:\"200\"; "
+ "http_stat_code; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "Status code negation\"; content:!\"100\"; nocase; "
+ "http_stat_code; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the UNITTESTS for the http_stat_code keyword
+ */
+void DetectHttpStatCodeRegisterTests (void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+
+ UtRegisterTest("DetectHttpStatCodeTest01", DetectHttpStatCodeTest01, 1);
+ UtRegisterTest("DetectHttpStatCodeTest02", DetectHttpStatCodeTest02, 1);
+ UtRegisterTest("DetectHttpStatCodeSigTest01", DetectHttpStatCodeSigTest01, 1);
+ UtRegisterTest("DetectHttpStatCodeSigTest02", DetectHttpStatCodeSigTest02, 1);
+ UtRegisterTest("DetectHttpStatCodeSigTest03", DetectHttpStatCodeSigTest03, 1);
+ UtRegisterTest("DetectHttpStatCodeSigTest04", DetectHttpStatCodeSigTest04, 1);
+
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-stat-code.h b/framework/src/suricata/src/detect-http-stat-code.h
new file mode 100644
index 00000000..811c6951
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-stat-code.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_HTTP_STAT_CODE_H
+#define _DETECT_HTTP_STAT_CODE_H
+
+/* prototypes */
+int DetectHttpStatCodeMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t , void *, Signature *,
+ SigMatch *);
+void DetectHttpStatCodeRegister(void);
+
+#endif /* _DETECT_HTTP_STAT_CODE_H */
+
diff --git a/framework/src/suricata/src/detect-http-stat-msg.c b/framework/src/suricata/src/detect-http-stat-msg.c
new file mode 100644
index 00000000..d2d7685e
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-stat-msg.c
@@ -0,0 +1,564 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the http_stat_msg keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-engine-mpm.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-stat-msg.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp.h"
+
+int DetectHttpStatMsgMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t , void *, Signature *,
+ SigMatch *);
+static int DetectHttpStatMsgSetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpStatMsgRegisterTests(void);
+void DetectHttpStatMsgFree(void *);
+
+/**
+ * \brief Registration function for keyword: http_stat_msg
+ */
+void DetectHttpStatMsgRegister (void)
+{
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].name = "http_stat_msg";
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].desc = "content modifier to match on HTTP stat-msg-buffer";
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_stat_msg";
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].Setup = DetectHttpStatMsgSetup;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].Free = NULL;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].RegisterTests = DetectHttpStatMsgRegisterTests;
+
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_STAT_MSG].flags |= SIGMATCH_PAYLOAD;
+}
+
+/**
+ * \brief this function setups the http_stat_msg modifier keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+static int DetectHttpStatMsgSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_STAT_MSG,
+ DETECT_SM_LIST_HSMDMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test Checks if a http_stat_msg is registered in a Signature, if content is not
+ * specified in the signature or rawbyes is specified or fast_pattern is
+ * provided in the signature.
+ */
+int DetectHttpStatMsgTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_msg\"; http_stat_msg;sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_msg\"; content:\"|FF F1|\";"
+ " rawbytes; http_stat_msg;sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_msg\"; content:\"one\";"
+ "fast_pattern; http_stat_msg; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ if (!(((DetectContentData *)de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH]->ctx)->flags &
+ DETECT_CONTENT_FAST_PATTERN))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_stat_msg is registered in a Signature and also checks
+ * the nocase
+ */
+int DetectHttpStatMsgTest02(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_stat_msg\"; content:\"one\"; "
+ "http_stat_msg; content:\"two\"; http_stat_msg; "
+ "content:\"two\"; nocase; http_stat_msg; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ result = 0;
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HSMDMATCH];
+ if (sm == NULL) {
+ printf("no sigmatch(es): ");
+ goto end;
+ }
+
+ SigMatch *prev = NULL;
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ printf("expected DETECT_CONTENT for http_stat_msg, got %d: ", sm->type);
+ goto end;
+ }
+ prev = sm;
+ sm = sm->next;
+ }
+
+ if (! (((DetectContentData *)prev->ctx)->flags &
+ DETECT_CONTENT_NOCASE))
+ {
+ result = 0;
+ }
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_msg is matched . */
+static int DetectHttpStatMsgSigTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status message\"; content:\"OK\"; "
+ "http_stat_msg; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "Status message nocase\"; content:\"ok\"; nocase; "
+ "http_stat_msg; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("sid 2 didn't match but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_msg is not matched . */
+static int DetectHttpStatMsgSigTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status message\"; content:\"no\"; "
+ "http_stat_msg; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_stat_msg is used with
+ * negated content . */
+static int DetectHttpStatMsgSigTest03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP status message\"; content:\"ok\"; "
+ "nocase; http_stat_msg; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"HTTP "
+ "Status message nocase\"; content:!\"Not\"; "
+ "http_stat_msg; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (! PacketAlertCheck(p, 1)) {
+ printf("sid 1 didn't matched but should have: ");
+ goto end;
+ }
+ if (! PacketAlertCheck(p, 2)) {
+ printf("sid 2 didn't matched but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the UNITTESTS for the http_stat_msg keyword
+ */
+void DetectHttpStatMsgRegisterTests (void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+
+ UtRegisterTest("DetectHttpStatMsgTest01", DetectHttpStatMsgTest01, 1);
+ UtRegisterTest("DetectHttpStatMsgTest02", DetectHttpStatMsgTest02, 1);
+ UtRegisterTest("DetectHttpStatMsgSigTest01", DetectHttpStatMsgSigTest01, 1);
+ UtRegisterTest("DetectHttpStatMsgSigTest02", DetectHttpStatMsgSigTest02, 1);
+ UtRegisterTest("DetectHttpStatMsgSigTest03", DetectHttpStatMsgSigTest03, 1);
+
+#endif /* UNITTESTS */
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-stat-msg.h b/framework/src/suricata/src/detect-http-stat-msg.h
new file mode 100644
index 00000000..baa05718
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-stat-msg.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_HTTP_STAT_MSG_H
+#define _DETECT_HTTP_STAT_MSG_H
+
+/* prototypes */
+int DetectHttpStatMsgMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t , void *, Signature *, SigMatch *);
+void DetectHttpStatMsgRegister(void);
+
+#endif /* _DETECT_HTTP_STAT_MSG_H */
+
diff --git a/framework/src/suricata/src/detect-http-ua.c b/framework/src/suricata/src/detect-http-ua.c
new file mode 100644
index 00000000..cae5e9fa
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-ua.c
@@ -0,0 +1,2110 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements support for the http_user_agent keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "stream-tcp.h"
+#include "detect-http-ua.h"
+
+int DetectHttpUASetup(DetectEngineCtx *, Signature *, char *);
+void DetectHttpUARegisterTests(void);
+void DetectHttpUAFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "http_user_agent" keyword.
+ */
+void DetectHttpUARegister(void)
+{
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].name = "http_user_agent";
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].desc = "content modifier to match only on the HTTP User-Agent header";
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_user_agent";
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].Setup = DetectHttpUASetup;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].Free = DetectHttpUAFree;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].RegisterTests = DetectHttpUARegisterTests;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_USER_AGENT].flags |= SIGMATCH_PAYLOAD ;
+
+ return;
+}
+
+/**
+ * \brief The setup function for the http_user_agent keyword for a signature.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param s Pointer to the signature for the current Signature being
+ * parsed from the rules.
+ * \param m Pointer to the head of the SigMatch for the current rule
+ * being parsed.
+ * \param arg Pointer to the string holding the keyword value.
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int DetectHttpUASetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
+ DETECT_AL_HTTP_USER_AGENT,
+ DETECT_SM_LIST_HUADMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+/**
+ * \brief The function to free the http_user_agent data.
+ *
+ * \param ptr Pointer to the http_user_agent.
+ */
+void DetectHttpUAFree(void *ptr)
+{
+ DetectContentData *huad = (DetectContentData *)ptr;
+ if (huad == NULL)
+ return;
+
+ if (huad->content != NULL)
+ SCFree(huad->content);
+
+ BoyerMooreCtxDeInit(huad->bm_ctx);
+ SCFree(huad);
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Test that a signature containting a http_user_agent is correctly parsed
+ * and the keyword is registered.
+ */
+static int DetectHttpUATest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_user_agent\"; "
+ "content:\"one\"; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a signature containing an valid http_user_agent entry is
+ * parsed.
+ */
+static int DetectHttpUATest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_user_agent\"; "
+ "content:\"one\"; http_user_agent:; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing no content but a
+ * http_user_agent is invalidated.
+ */
+static int DetectHttpUATest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_user_agent\"; "
+ "http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that an invalid signature containing a rawbytes along with a
+ * http_user_agent is invalidated.
+ */
+static int DetectHttpUATest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_user_agent\"; "
+ "content:\"one\"; rawbytes; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test that a http_user_agent with nocase is parsed.
+ */
+static int DetectHttpUATest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_user_agent\"; "
+ "content:\"one\"; http_user_agent; nocase; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ *\test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpUATest06(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"message\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpUATest07(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy message";
+ uint8_t http2_buf[] =
+ "body1\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"message\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but shouldn't have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpUATest08(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy mess";
+ uint8_t http2_buf[] =
+ "age body\r\n\r\n";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"message\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_user_agent content matches against a http request
+ * which holds the content, against a cross boundary present pattern.
+ */
+static int DetectHttpUATest09(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy body1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy body1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"body1This\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the http_user_agent content matches against a http request
+ * against a case insensitive pattern.
+ */
+static int DetectHttpUATest10(void)
+{
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http1_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy bodY1";
+ uint8_t http2_buf[] =
+ "This is dummy message body2\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 46\r\n"
+ "\r\n"
+ "This is dummy bodY1";
+ uint32_t http1_len = sizeof(http1_buf) - 1;
+ uint32_t http2_len = sizeof(http2_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"body1this\"; http_user_agent; nocase;"
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http1_buf, http1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: \n");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match but should have\n");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http2_buf, http2_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: \n", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/**
+ *\test Test that the negated http_user_agent content matches against a
+ * http request which doesn't hold the content.
+ */
+static int DetectHttpUATest11(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy message body\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:!\"message\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 matched but shouldn't have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ *\test Negative test that the negated http_user_agent content matches against a
+ * http request which holds hold the content.
+ */
+static int DetectHttpUATest12(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: This is dummy body\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:!\"message\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test that the http_user_agent content matches against a http request
+ * which holds the content.
+ */
+static int DetectHttpUATest13(void)
+{
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ Flow f;
+ uint8_t http_buf[] =
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: www.openinfosecfoundation.org\r\n"
+ "User-Agent: longbufferabcdefghijklmnopqrstuvwxyz0123456789bufferend\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";
+ uint32_t http_len = sizeof(http_buf) - 1;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any "
+ "(msg:\"http user agent test\"; "
+ "content:\"abcdefghijklmnopqrstuvwxyz0123456789\"; http_user_agent; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf, http_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test multiple http transactions and body chunks of request handling
+ */
+static int DetectHttpUATest14(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "Cookie: dummy1\r\n";
+ uint8_t httpbuf3[] = "User-Agent: Body one!!\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf5[] = "Cookie: dummy2\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Body two\r\n\r\n";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy1\"; http_cookie; content:\"Body one\"; http_user_agent; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"dummy2\"; http_cookie; content:\"Body two\"; http_user_agent; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || PacketAlertCheck(p, 2)) {
+ printf("sig 1 alerted (4): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) || !(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match or sig 1 matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+int DetectHttpUATest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; content:\"two\"; http_user_agent; "
+ "content:\"three\"; distance:10; http_user_agent; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (cd1->flags != 0 || memcmp(cd1->content, "one", cd1->content_len) != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "two", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd1) ||
+ !DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; pcre:/two/; "
+ "content:\"three\"; distance:10; http_user_agent; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; pcre:/two/; "
+ "content:\"three\"; distance:10; within:15; http_user_agent; content:\"four\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != 0 ||
+ cd2->flags != 0 || memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ if (!DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; pcre:/two/; "
+ "content:\"three\"; distance:10; http_user_agent; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != DETECT_PCRE_RELATIVE_NEXT ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_user_agent; pcre:/two/; "
+ "content:\"three\"; distance:10; http_user_agent; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != (DETECT_CONTENT_RELATIVE_NEXT | DETECT_CONTENT_OFFSET) ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ printf ("failed: http_user_agent incorrect flags");
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; offset:10; http_user_agent; pcre:/two/; "
+ "content:\"three\"; distance:10; http_user_agent; within:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; pcre:/two/; "
+ "content:\"three\"; http_user_agent; depth:10; "
+ "content:\"four\"; distance:10; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->prev->ctx;
+ DetectContentData *cd2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ cd2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(cd2->content, "four", cd2->content_len) != 0 ||
+ huad1->flags != 0 ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_DEPTH ||
+ memcmp(huad2->content, "three", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ if (DETECT_CONTENT_IS_SINGLE(cd2) ||
+ !DETECT_CONTENT_IS_SINGLE(huad1) ||
+ DETECT_CONTENT_IS_SINGLE(huad2)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; distance:0; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(huad2->content, "two", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; "
+ "content:\"two\"; within:5; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "one", huad1->content_len) != 0 ||
+ huad2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(huad2->content, "two", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; http_user_agent; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list != NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/V; "
+ "content:\"two\"; within:5; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ huad2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(huad2->content, "two", huad2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"two\"; http_user_agent; "
+ "pcre:/one/VR; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->type != DETECT_PCRE ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->type != DETECT_CONTENT) {
+
+ goto end;
+ }
+
+ DetectContentData *huad1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectPcreData *pd2 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd2->flags != (DETECT_PCRE_RELATIVE) ||
+ huad1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(huad1->content, "two", huad1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUATest36(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(pcre:/one/V; "
+ "content:\"two\"; distance:5; http_user_agent; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_HUADMATCH] == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->type != DETECT_CONTENT ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev == NULL ||
+ de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->type != DETECT_PCRE) {
+
+ goto end;
+ }
+
+ DetectPcreData *pd1 = (DetectPcreData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->prev->ctx;
+ DetectContentData *huad2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_HUADMATCH]->ctx;
+ if (pd1->flags != (DETECT_PCRE_RELATIVE_NEXT) ||
+ huad2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(huad2->content, "two", huad2->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectHttpUARegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectHttpUATest01", DetectHttpUATest01, 1);
+ UtRegisterTest("DetectHttpUATest02", DetectHttpUATest02, 1);
+ UtRegisterTest("DetectHttpUATest03", DetectHttpUATest03, 1);
+ UtRegisterTest("DetectHttpUATest04", DetectHttpUATest04, 1);
+ UtRegisterTest("DetectHttpUATest05", DetectHttpUATest05, 1);
+ UtRegisterTest("DetectHttpUATest06", DetectHttpUATest06, 1);
+ UtRegisterTest("DetectHttpUATest07", DetectHttpUATest07, 1);
+ UtRegisterTest("DetectHttpUATest08", DetectHttpUATest08, 1);
+ UtRegisterTest("DetectHttpUATest09", DetectHttpUATest09, 1);
+ UtRegisterTest("DetectHttpUATest10", DetectHttpUATest10, 1);
+ UtRegisterTest("DetectHttpUATest11", DetectHttpUATest11, 1);
+ UtRegisterTest("DetectHttpUATest12", DetectHttpUATest12, 1);
+ UtRegisterTest("DetectHttpUATest13", DetectHttpUATest13, 1);
+ UtRegisterTest("DetectHttpUATest14", DetectHttpUATest14, 1);
+
+ UtRegisterTest("DetectHttpUATest22", DetectHttpUATest22, 1);
+ UtRegisterTest("DetectHttpUATest23", DetectHttpUATest23, 1);
+ UtRegisterTest("DetectHttpUATest24", DetectHttpUATest24, 1);
+ UtRegisterTest("DetectHttpUATest25", DetectHttpUATest25, 1);
+ UtRegisterTest("DetectHttpUATest26", DetectHttpUATest26, 1);
+ UtRegisterTest("DetectHttpUATest27", DetectHttpUATest27, 1);
+ UtRegisterTest("DetectHttpUATest28", DetectHttpUATest28, 1);
+ UtRegisterTest("DetectHttpUATest29", DetectHttpUATest29, 1);
+ UtRegisterTest("DetectHttpUATest30", DetectHttpUATest30, 1);
+ UtRegisterTest("DetectHttpUATest31", DetectHttpUATest31, 1);
+ UtRegisterTest("DetectHttpUATest32", DetectHttpUATest32, 1);
+ UtRegisterTest("DetectHttpUATest33", DetectHttpUATest33, 1);
+ UtRegisterTest("DetectHttpUATest34", DetectHttpUATest34, 1);
+ UtRegisterTest("DetectHttpUATest35", DetectHttpUATest35, 1);
+ UtRegisterTest("DetectHttpUATest36", DetectHttpUATest36, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-ua.h b/framework/src/suricata/src/detect-http-ua.h
new file mode 100644
index 00000000..2b04245e
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-ua.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_HTTP_UA_H__
+#define __DETECT_HTTP_UA_H__
+
+void DetectHttpUARegister(void);
+
+#endif /* __DETECT_HTTP_UA_H__ */
diff --git a/framework/src/suricata/src/detect-http-uri.c b/framework/src/suricata/src/detect-http-uri.c
new file mode 100644
index 00000000..92c4803e
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-uri.c
@@ -0,0 +1,556 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "flow.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-uri.h"
+#include "detect-uricontent.h"
+#include "stream-tcp.h"
+
+int DetectHttpUriSetup (DetectEngineCtx *, Signature *, char *);
+void DetectHttpUriRegisterTests(void);
+
+/**
+ * \brief Registration function for keyword: http_uri
+ */
+void DetectHttpUriRegister (void)
+{
+ sigmatch_table[DETECT_AL_HTTP_URI].name = "http_uri";
+ sigmatch_table[DETECT_AL_HTTP_URI].desc = "content modifier to match specifically and only on the HTTP uri-buffer";
+ sigmatch_table[DETECT_AL_HTTP_URI].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#http_uri-and-http_raw_uri";
+ sigmatch_table[DETECT_AL_HTTP_URI].Match = NULL;
+ sigmatch_table[DETECT_AL_HTTP_URI].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_HTTP_URI].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_HTTP_URI].Setup = DetectHttpUriSetup;
+ sigmatch_table[DETECT_AL_HTTP_URI].Free = NULL;
+ sigmatch_table[DETECT_AL_HTTP_URI].RegisterTests = DetectHttpUriRegisterTests;
+
+ sigmatch_table[DETECT_AL_HTTP_URI].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_HTTP_URI].flags |= SIGMATCH_PAYLOAD;
+}
+
+
+/**
+ * \brief this function setups the http_uri modifier keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+int DetectHttpUriSetup(DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ return DetectEngineContentModifierBufferSetup(de_ctx, s, str,
+ DETECT_AL_HTTP_URI,
+ DETECT_SM_LIST_UMATCH,
+ ALPROTO_HTTP,
+ NULL);
+}
+
+
+/******************************** UNITESTS **********************************/
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/**
+ * \test Checks if a http_uri is registered in a Signature, if content is not
+ * specified in the signature
+ */
+int DetectHttpUriTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_uri\"; http_uri;sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_uri is registered in a Signature, if some parameter
+ * is specified with http_uri in the signature
+ */
+int DetectHttpUriTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_uri\"; content:\"one\"; "
+ "http_cookie:wrong; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_uri is registered in a Signature
+ */
+int DetectHttpUriTest03(void)
+{
+ SigMatch *sm = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_uri\"; content:\"one\"; "
+ "http_uri; content:\"two\"; http_uri; "
+ "content:\"three\"; http_uri; "
+ "sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm == NULL) {
+ printf("no sigmatch(es): ");
+ goto end;
+ }
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ printf("expected DETECT_AL_HTTP_URI, got %d: ", sm->type);
+ goto end;
+ }
+ sm = sm->next;
+ }
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_uri is registered in a Signature, when rawbytes is
+ * also specified in the signature
+ */
+int DetectHttpUriTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_uri\"; content:\"one\"; "
+ "rawbytes; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ result = 1;
+
+ end:
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Checks if a http_uri is successfully converted to a uricontent
+ *
+ */
+int DetectHttpUriTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ Signature *s = NULL;
+ int result = 0;
+
+ if ((de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ s = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing http_uri\"; "
+ "content:\"we are testing http_uri keyword\"; "
+ "http_uri; sid:1;)");
+ if (s == NULL) {
+ printf("sig failed to parse\n");
+ goto end;
+ }
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL)
+ goto end;
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH]->type != DETECT_CONTENT) {
+ printf("wrong type\n");
+ goto end;
+ }
+
+ char *str = "we are testing http_uri keyword";
+ int uricomp = memcmp((const char *)((DetectContentData*) s->sm_lists[DETECT_SM_LIST_UMATCH]->ctx)->content, str, strlen(str)-1);
+ int urilen = ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx)->content_len;
+ if (uricomp != 0 ||
+ urilen != strlen("we are testing http_uri keyword")) {
+ printf("sig failed to parse, content not setup properly\n");
+ goto end;
+ }
+ result = 1;
+
+end:
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_uri; "
+ "content:\"two\"; distance:0; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ DetectContentData *ud2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_uri; "
+ "content:\"two\"; within:5; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ DetectContentData *ud2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; http_uri; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *cd = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (memcmp(cd->content, "one", cd->content_len) != 0 ||
+ cd->flags != DETECT_CONTENT_WITHIN) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest16(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(content:\"one\"; within:5; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest17(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; "
+ "content:\"two\"; distance:0; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ DetectContentData *ud2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_DISTANCE ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int DetectHttpUriTest18(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(uricontent:\"one\"; "
+ "content:\"two\"; within:5; http_uri; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH] != NULL\n");
+ goto end;
+ }
+
+ if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL) {
+ printf("de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL\n");
+ goto end;
+ }
+
+ DetectContentData *ud1 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->prev->ctx;
+ DetectContentData *ud2 = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if (ud1->flags != DETECT_CONTENT_RELATIVE_NEXT ||
+ memcmp(ud1->content, "one", ud1->content_len) != 0 ||
+ ud2->flags != DETECT_CONTENT_WITHIN ||
+ memcmp(ud2->content, "two", ud1->content_len) != 0) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Register the UNITTESTS for the http_uri keyword
+ */
+void DetectHttpUriRegisterTests (void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectHttpUriTest01", DetectHttpUriTest01, 1);
+ UtRegisterTest("DetectHttpUriTest02", DetectHttpUriTest02, 1);
+ UtRegisterTest("DetectHttpUriTest03", DetectHttpUriTest03, 1);
+ UtRegisterTest("DetectHttpUriTest04", DetectHttpUriTest04, 1);
+ UtRegisterTest("DetectHttpUriTest05", DetectHttpUriTest05, 1);
+ UtRegisterTest("DetectHttpUriTest12", DetectHttpUriTest12, 1);
+ UtRegisterTest("DetectHttpUriTest13", DetectHttpUriTest13, 1);
+ UtRegisterTest("DetectHttpUriTest14", DetectHttpUriTest14, 1);
+ UtRegisterTest("DetectHttpUriTest15", DetectHttpUriTest15, 1);
+ UtRegisterTest("DetectHttpUriTest16", DetectHttpUriTest16, 1);
+ UtRegisterTest("DetectHttpUriTest17", DetectHttpUriTest17, 1);
+ UtRegisterTest("DetectHttpUriTest18", DetectHttpUriTest18, 1);
+#endif /* UNITTESTS */
+
+}
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-http-uri.h b/framework/src/suricata/src/detect-http-uri.h
new file mode 100644
index 00000000..cb327804
--- /dev/null
+++ b/framework/src/suricata/src/detect-http-uri.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias Galvan <iglesiasg@gmail.com>
+ */
+
+#ifndef _DETECT_HTTP_URI_H
+#define _DETECT_HTTP_URI_H
+
+/* prototypes */
+void DetectHttpUriRegister (void);
+
+int DetectHttpUriSetup(DetectEngineCtx *de_ctx, Signature *s, char *str);
+int DetectHttpUriDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s,
+ SigMatch *sm, Flow *f, uint8_t flags, void *state);
+
+#endif /* _DETECT_HTTP_URI_H */
diff --git a/framework/src/suricata/src/detect-icmp-id.c b/framework/src/suricata/src/detect-icmp-id.c
new file mode 100644
index 00000000..5d6f7f26
--- /dev/null
+++ b/framework/src/suricata/src/detect-icmp-id.c
@@ -0,0 +1,500 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias Galvan <iglesiasg@gmail.com>
+ *
+ * Implements the icmp_id keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-icmp-id.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "^\\s*(\"\\s*)?([0-9]+)(\\s*\")?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIcmpIdMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectIcmpIdSetup(DetectEngineCtx *, Signature *, char *);
+void DetectIcmpIdRegisterTests(void);
+void DetectIcmpIdFree(void *);
+
+/**
+ * \brief Registration function for icode: icmp_id
+ */
+void DetectIcmpIdRegister (void)
+{
+ sigmatch_table[DETECT_ICMP_ID].name = "icmp_id";
+ sigmatch_table[DETECT_ICMP_ID].desc = "check for a ICMP id";
+ sigmatch_table[DETECT_ICMP_ID].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#icmp_id";
+ sigmatch_table[DETECT_ICMP_ID].Match = DetectIcmpIdMatch;
+ sigmatch_table[DETECT_ICMP_ID].Setup = DetectIcmpIdSetup;
+ sigmatch_table[DETECT_ICMP_ID].Free = DetectIcmpIdFree;
+ sigmatch_table[DETECT_ICMP_ID].RegisterTests = DetectIcmpIdRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match icmp_id rule option set on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectIcmpIdData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectIcmpIdMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ uint16_t pid;
+ const DetectIcmpIdData *iid = (const DetectIcmpIdData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_ICMPV4(p)) {
+ switch (ICMPV4_GET_TYPE(p)){
+ case ICMP_ECHOREPLY:
+ case ICMP_ECHO:
+ case ICMP_TIMESTAMP:
+ case ICMP_TIMESTAMPREPLY:
+ case ICMP_INFO_REQUEST:
+ case ICMP_INFO_REPLY:
+ case ICMP_ADDRESS:
+ case ICMP_ADDRESSREPLY:
+ SCLogDebug("ICMPV4_GET_ID(p) %"PRIu16" (network byte order), "
+ "%"PRIu16" (host byte order)", ICMPV4_GET_ID(p),
+ ntohs(ICMPV4_GET_ID(p)));
+
+ pid = ICMPV4_GET_ID(p);
+ break;
+ default:
+ SCLogDebug("Packet has no id field");
+ return 0;
+ }
+ } else if (PKT_IS_ICMPV6(p)) {
+ switch (ICMPV6_GET_TYPE(p)) {
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY:
+ SCLogDebug("ICMPV6_GET_ID(p) %"PRIu16" (network byte order), "
+ "%"PRIu16" (host byte order)", ICMPV6_GET_ID(p),
+ ntohs(ICMPV6_GET_ID(p)));
+
+ pid = ICMPV6_GET_ID(p);
+ break;
+ default:
+ SCLogDebug("Packet has no id field");
+ return 0;
+ }
+ } else {
+ SCLogDebug("Packet not ICMPV4 nor ICMPV6");
+ return 0;
+ }
+
+ if (pid == iid->id)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse icmp_id option passed via icmp_id: keyword
+ *
+ * \param icmpidstr Pointer to the user provided icmp_id options
+ *
+ * \retval iid pointer to DetectIcmpIdData on success
+ * \retval NULL on failure
+ */
+DetectIcmpIdData *DetectIcmpIdParse (char *icmpidstr)
+{
+ DetectIcmpIdData *iid = NULL;
+ char *substr[3] = {NULL, NULL, NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, icmpidstr, strlen(icmpidstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Parse error %s", icmpidstr);
+ goto error;
+ }
+
+ int i;
+ const char *str_ptr;
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *)icmpidstr, ov, MAX_SUBSTRINGS, i, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ substr[i-1] = (char *)str_ptr;
+ }
+
+ iid = SCMalloc(sizeof(DetectIcmpIdData));
+ if (unlikely(iid == NULL))
+ goto error;
+ iid->id = 0;
+
+ if (substr[0]!= NULL && strlen(substr[0]) != 0) {
+ if (substr[2] == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Missing close quote in input");
+ goto error;
+ }
+ } else {
+ if (substr[2] != NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Missing open quote in input");
+ goto error;
+ }
+ }
+
+ /** \todo can ByteExtractStringUint16 do this? */
+ uint16_t id = 0;
+ if (ByteExtractStringUint16(&id, 10, 0, substr[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp id %s is not "
+ "valid", substr[1]);
+ goto error;
+ }
+ iid->id = htons(id);
+
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+ return iid;
+
+error:
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+ if (iid != NULL) DetectIcmpIdFree(iid);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed icmp_id data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param icmpidstr pointer to the user provided icmp_id option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectIcmpIdSetup (DetectEngineCtx *de_ctx, Signature *s, char *icmpidstr)
+{
+ DetectIcmpIdData *iid = NULL;
+ SigMatch *sm = NULL;
+
+ iid = DetectIcmpIdParse(icmpidstr);
+ if (iid == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) goto error;
+
+ sm->type = DETECT_ICMP_ID;
+ sm->ctx = (SigMatchCtx *)iid;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (iid != NULL) DetectIcmpIdFree(iid);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectIcmpIdData
+ *
+ * \param ptr pointer to DetectIcmpIdData
+ */
+void DetectIcmpIdFree (void *ptr)
+{
+ DetectIcmpIdData *iid = (DetectIcmpIdData *)ptr;
+ SCFree(iid);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectIcmpIdParseTest01 is a test for setting a valid icmp_id value
+ */
+int DetectIcmpIdParseTest01 (void)
+{
+ DetectIcmpIdData *iid = NULL;
+ iid = DetectIcmpIdParse("300");
+ if (iid != NULL && iid->id == htons(300)) {
+ DetectIcmpIdFree(iid);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpIdParseTest02 is a test for setting a valid icmp_id value
+ * with spaces all around
+ */
+int DetectIcmpIdParseTest02 (void)
+{
+ DetectIcmpIdData *iid = NULL;
+ iid = DetectIcmpIdParse(" 300 ");
+ if (iid != NULL && iid->id == htons(300)) {
+ DetectIcmpIdFree(iid);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpIdParseTest03 is a test for setting a valid icmp_id value
+ * with quotation marks
+ */
+int DetectIcmpIdParseTest03 (void)
+{
+ DetectIcmpIdData *iid = NULL;
+ iid = DetectIcmpIdParse("\"300\"");
+ if (iid != NULL && iid->id == htons(300)) {
+ DetectIcmpIdFree(iid);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpIdParseTest04 is a test for setting a valid icmp_id value
+ * with quotation marks and spaces all around
+ */
+int DetectIcmpIdParseTest04 (void)
+{
+ DetectIcmpIdData *iid = NULL;
+ iid = DetectIcmpIdParse(" \" 300 \"");
+ if (iid != NULL && iid->id == htons(300)) {
+ DetectIcmpIdFree(iid);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpIdParseTest05 is a test for setting an invalid icmp_id
+ * value with missing quotation marks
+ */
+int DetectIcmpIdParseTest05 (void)
+{
+ DetectIcmpIdData *iid = NULL;
+ iid = DetectIcmpIdParse("\"300");
+ if (iid == NULL) {
+ DetectIcmpIdFree(iid);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpIdMatchTest01 is a test for checking the working of
+ * icmp_id keyword by creating 2 rules and matching a crafted packet
+ * against them. Only the first one shall trigger.
+ */
+int DetectIcmpIdMatchTest01 (void)
+{
+ int result = 0;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
+ p->icmpv4vars.id = htons(21781);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any (icmp_id:21781; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx, "alert icmp any any -> any any (icmp_id:21782; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+
+}
+
+/**
+ * \test DetectIcmpIdMatchTest02 is a test for checking the working of
+ * icmp_id keyword by creating 1 rule and matching a crafted packet
+ * against them. The packet is an ICMP packet with no "id" field,
+ * therefore the rule should not trigger.
+ */
+int DetectIcmpIdMatchTest02 (void)
+{
+ int result = 0;
+
+ uint8_t raw_icmpv4[] = {
+ 0x0b, 0x00, 0x8a, 0xdf, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x14, 0x25, 0x0c, 0x00, 0x00,
+ 0xff, 0x11, 0x00, 0x00, 0x85, 0x64, 0xea, 0x5b,
+ 0x51, 0xa6, 0xbb, 0x35, 0x59, 0x8a, 0x5a, 0xe2,
+ 0x00, 0x14, 0x00, 0x00 };
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ IPV4Hdr ip4h;
+
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(ThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ p->src.addr_data32[0] = 0x01020304;
+ p->dst.addr_data32[0] = 0x04030201;
+
+ ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
+ ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h = &ip4h;
+
+ DecodeICMPV4(&th_v, &dtv, p, raw_icmpv4, sizeof(raw_icmpv4), NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any (icmp_id:0; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+end:
+ SCFree(p);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void DetectIcmpIdRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectIcmpIdParseTest01", DetectIcmpIdParseTest01, 1);
+ UtRegisterTest("DetectIcmpIdParseTest02", DetectIcmpIdParseTest02, 1);
+ UtRegisterTest("DetectIcmpIdParseTest03", DetectIcmpIdParseTest03, 1);
+ UtRegisterTest("DetectIcmpIdParseTest04", DetectIcmpIdParseTest04, 1);
+ UtRegisterTest("DetectIcmpIdParseTest05", DetectIcmpIdParseTest05, 1);
+ UtRegisterTest("DetectIcmpIdMatchTest01", DetectIcmpIdMatchTest01, 1);
+ UtRegisterTest("DetectIcmpIdMatchTest02", DetectIcmpIdMatchTest02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-icmp-id.h b/framework/src/suricata/src/detect-icmp-id.h
new file mode 100644
index 00000000..a3b20f3c
--- /dev/null
+++ b/framework/src/suricata/src/detect-icmp-id.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias Galvan <iglesiasg@gmail.com>
+ */
+
+#ifndef __DETECT_ICMP_ID_H__
+#define __DETECT_ICMP_ID_H__
+
+typedef struct DetectIcmpIdData_ {
+ uint16_t id; /**< id in network byte error */
+} DetectIcmpIdData;
+
+/* prototypes */
+void DetectIcmpIdRegister(void);
+
+#endif /* __DETECT_ICMP_ID__ */
diff --git a/framework/src/suricata/src/detect-icmp-seq.c b/framework/src/suricata/src/detect-icmp-seq.c
new file mode 100644
index 00000000..4e3fe1ac
--- /dev/null
+++ b/framework/src/suricata/src/detect-icmp-seq.c
@@ -0,0 +1,390 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the icmp_seq keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-icmp-seq.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "^\\s*(\"\\s*)?([0-9]+)(\\s*\")?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIcmpSeqMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectIcmpSeqSetup(DetectEngineCtx *, Signature *, char *);
+void DetectIcmpSeqRegisterTests(void);
+void DetectIcmpSeqFree(void *);
+
+/**
+ * \brief Registration function for icmp_seq
+ */
+void DetectIcmpSeqRegister (void)
+{
+ sigmatch_table[DETECT_ICMP_SEQ].name = "icmp_seq";
+ sigmatch_table[DETECT_ICMP_SEQ].desc = "check for a ICMP sequence number";
+ sigmatch_table[DETECT_ICMP_SEQ].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#icmp_seq";
+ sigmatch_table[DETECT_ICMP_SEQ].Match = DetectIcmpSeqMatch;
+ sigmatch_table[DETECT_ICMP_SEQ].Setup = DetectIcmpSeqSetup;
+ sigmatch_table[DETECT_ICMP_SEQ].Free = DetectIcmpSeqFree;
+ sigmatch_table[DETECT_ICMP_SEQ].RegisterTests = DetectIcmpSeqRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE,"pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY,"pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match icmp_seq rule option set on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectIcmpSeqData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectIcmpSeqMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ uint16_t seqn;
+ const DetectIcmpSeqData *iseq = (const DetectIcmpSeqData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_ICMPV4(p)) {
+ switch (ICMPV4_GET_TYPE(p)){
+ case ICMP_ECHOREPLY:
+ case ICMP_ECHO:
+ case ICMP_TIMESTAMP:
+ case ICMP_TIMESTAMPREPLY:
+ case ICMP_INFO_REQUEST:
+ case ICMP_INFO_REPLY:
+ case ICMP_ADDRESS:
+ case ICMP_ADDRESSREPLY:
+ SCLogDebug("ICMPV4_GET_SEQ(p) %"PRIu16" (network byte order), "
+ "%"PRIu16" (host byte order)", ICMPV4_GET_SEQ(p),
+ ntohs(ICMPV4_GET_SEQ(p)));
+
+ seqn = ICMPV4_GET_SEQ(p);
+ break;
+ default:
+ SCLogDebug("Packet has no seq field");
+ return 0;
+ }
+ } else if (PKT_IS_ICMPV6(p)) {
+
+ switch (ICMPV6_GET_TYPE(p)) {
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY:
+ SCLogDebug("ICMPV6_GET_SEQ(p) %"PRIu16" (network byte order), "
+ "%"PRIu16" (host byte order)", ICMPV6_GET_SEQ(p),
+ ntohs(ICMPV6_GET_SEQ(p)));
+
+ seqn = ICMPV6_GET_SEQ(p);
+ break;
+ default:
+ SCLogDebug("Packet has no seq field");
+ return 0;
+ }
+ } else {
+ SCLogDebug("Packet not ICMPV4 nor ICMPV6");
+ return 0;
+ }
+
+ if (seqn == iseq->seq)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse icmp_seq option passed via icmp_seq: keyword
+ *
+ * \param icmpseqstr Pointer to the user provided icmp_seq options
+ *
+ * \retval iseq pointer to DetectIcmpSeqData on success
+ * \retval NULL on failure
+ */
+DetectIcmpSeqData *DetectIcmpSeqParse (char *icmpseqstr)
+{
+ DetectIcmpSeqData *iseq = NULL;
+ char *substr[3] = {NULL, NULL, NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i;
+ const char *str_ptr;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, icmpseqstr, strlen(icmpseqstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH,"Parse error %s", icmpseqstr);
+ goto error;
+ }
+
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *)icmpseqstr, ov, MAX_SUBSTRINGS, i, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING,"pcre_get_substring failed");
+ goto error;
+ }
+ substr[i-1] = (char *)str_ptr;
+ }
+
+ iseq = SCMalloc(sizeof(DetectIcmpSeqData));
+ if (unlikely(iseq == NULL))
+ goto error;
+
+ iseq->seq = 0;
+
+ if (substr[0] != NULL && strlen(substr[0]) != 0) {
+ if (substr[2] == NULL) {
+ SCLogError(SC_ERR_MISSING_QUOTE,"Missing quote in input");
+ goto error;
+ }
+ } else {
+ if (substr[2] != NULL) {
+ SCLogError(SC_ERR_MISSING_QUOTE,"Missing quote in input");
+ goto error;
+ }
+ }
+
+ uint16_t seq = 0;
+ if (ByteExtractStringUint16(&seq, 10, 0, substr[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp seq %s is not "
+ "valid", substr[1]);
+ goto error;
+ }
+ iseq->seq = htons(seq);
+
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+
+ return iseq;
+
+error:
+ for (i = 0; i < 3; i++) {
+ if (substr[i] != NULL) SCFree(substr[i]);
+ }
+ if (iseq != NULL) DetectIcmpSeqFree(iseq);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed icmp_seq data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param icmpseqstr pointer to the user provided icmp_seq option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectIcmpSeqSetup (DetectEngineCtx *de_ctx, Signature *s, char *icmpseqstr)
+{
+ DetectIcmpSeqData *iseq = NULL;
+ SigMatch *sm = NULL;
+
+ iseq = DetectIcmpSeqParse(icmpseqstr);
+ if (iseq == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) goto error;
+
+ sm->type = DETECT_ICMP_SEQ;
+ sm->ctx = (SigMatchCtx *)iseq;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (iseq != NULL) DetectIcmpSeqFree(iseq);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectIcmpSeqData
+ *
+ * \param ptr pointer to DetectIcmpSeqData
+ */
+void DetectIcmpSeqFree (void *ptr)
+{
+ DetectIcmpSeqData *iseq = (DetectIcmpSeqData *)ptr;
+ SCFree(iseq);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectIcmpSeqParseTest01 is a test for setting a valid icmp_seq value
+ */
+int DetectIcmpSeqParseTest01 (void)
+{
+ DetectIcmpSeqData *iseq = NULL;
+ iseq = DetectIcmpSeqParse("300");
+ if (iseq != NULL && htons(iseq->seq) == 300) {
+ DetectIcmpSeqFree(iseq);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpSeqParseTest02 is a test for setting a valid icmp_seq value
+ * with spaces all around
+ */
+int DetectIcmpSeqParseTest02 (void)
+{
+ DetectIcmpSeqData *iseq = NULL;
+ iseq = DetectIcmpSeqParse(" 300 ");
+ if (iseq != NULL && htons(iseq->seq) == 300) {
+ DetectIcmpSeqFree(iseq);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpSeqParseTest03 is a test for setting an invalid icmp_seq value
+ */
+int DetectIcmpSeqParseTest03 (void)
+{
+ DetectIcmpSeqData *iseq = NULL;
+ iseq = DetectIcmpSeqParse("badc");
+ if (iseq != NULL) {
+ DetectIcmpSeqFree(iseq);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \test DetectIcmpSeqMatchTest01 is a test for checking the working of
+ * icmp_seq keyword by creating 2 rules and matching a crafted packet
+ * against them. Only the first one shall trigger.
+ */
+int DetectIcmpSeqMatchTest01 (void)
+{
+ int result = 0;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
+ p->icmpv4vars.seq = htons(2216);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any (icmp_seq:2216; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx, "alert icmp any any -> any any (icmp_seq:5000; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+
+}
+#endif /* UNITTESTS */
+
+void DetectIcmpSeqRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectIcmpSeqParseTest01", DetectIcmpSeqParseTest01, 1);
+ UtRegisterTest("DetectIcmpSeqParseTest02", DetectIcmpSeqParseTest02, 1);
+ UtRegisterTest("DetectIcmpSeqParseTest03", DetectIcmpSeqParseTest03, 0);
+ UtRegisterTest("DetectIcmpSeqMatchTest01", DetectIcmpSeqMatchTest01, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-icmp-seq.h b/framework/src/suricata/src/detect-icmp-seq.h
new file mode 100644
index 00000000..5c41f1d8
--- /dev/null
+++ b/framework/src/suricata/src/detect-icmp-seq.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+*/
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_ICMP_SEQ_H__
+#define __DETECT_ICMP_SEQ_H__
+
+typedef struct DetectIcmpSeqData_ {
+ uint16_t seq; /**< sequence value in network byte order */
+} DetectIcmpSeqData;
+
+/* prototypes */
+void DetectIcmpSeqRegister(void);
+
+#endif /* __DETECT_ICMP_SEQ__ */
+
diff --git a/framework/src/suricata/src/detect-icode.c b/framework/src/suricata/src/detect-icode.c
new file mode 100644
index 00000000..05c35a72
--- /dev/null
+++ b/framework/src/suricata/src/detect-icode.c
@@ -0,0 +1,528 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ *
+ * Implements icode keyword support
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-icode.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/**
+ *\brief Regex for parsing our icode options
+ */
+#define PARSE_REGEX "^\\s*(<|>)?\\s*([0-9]+)\\s*(?:<>\\s*([0-9]+))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectICodeMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectICodeSetup(DetectEngineCtx *, Signature *, char *);
+void DetectICodeRegisterTests(void);
+void DetectICodeFree(void *);
+
+
+/**
+ * \brief Registration function for icode: keyword
+ */
+void DetectICodeRegister (void)
+{
+ sigmatch_table[DETECT_ICODE].name = "icode";
+ sigmatch_table[DETECT_ICODE].desc = "match on specific ICMP id-value";
+ sigmatch_table[DETECT_ICODE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#icode";
+ sigmatch_table[DETECT_ICODE].Match = DetectICodeMatch;
+ sigmatch_table[DETECT_ICODE].Setup = DetectICodeSetup;
+ sigmatch_table[DETECT_ICODE].Free = DetectICodeFree;
+ sigmatch_table[DETECT_ICODE].RegisterTests = DetectICodeRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match icode rule option set on a packet with those passed via icode:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectICodeData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectICodeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ uint8_t picode;
+ const DetectICodeData *icd = (const DetectICodeData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_ICMPV4(p)) {
+ picode = ICMPV4_GET_CODE(p);
+ } else if (PKT_IS_ICMPV6(p)) {
+ picode = ICMPV6_GET_CODE(p);
+ } else {
+ /* Packet not ICMPv4 nor ICMPv6 */
+ return ret;
+ }
+
+ switch(icd->mode) {
+ case DETECT_ICODE_EQ:
+ ret = (picode == icd->code1) ? 1 : 0;
+ break;
+ case DETECT_ICODE_LT:
+ ret = (picode < icd->code1) ? 1 : 0;
+ break;
+ case DETECT_ICODE_GT:
+ ret = (picode > icd->code1) ? 1 : 0;
+ break;
+ case DETECT_ICODE_RN:
+ ret = (picode >= icd->code1 && picode <= icd->code2) ? 1 : 0;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse icode options passed via icode: keyword
+ *
+ * \param icodestr Pointer to the user provided icode options
+ *
+ * \retval icd pointer to DetectICodeData on success
+ * \retval NULL on failure
+ */
+DetectICodeData *DetectICodeParse(char *icodestr)
+{
+ DetectICodeData *icd = NULL;
+ char *args[3] = {NULL, NULL, NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, icodestr, strlen(icodestr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, icodestr);
+ goto error;
+ }
+
+ int i;
+ const char *str_ptr;
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *)icodestr, ov, MAX_SUBSTRINGS, i, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[i-1] = (char *)str_ptr;
+ }
+
+ icd = SCMalloc(sizeof(DetectICodeData));
+ if (unlikely(icd == NULL))
+ goto error;
+ icd->code1 = 0;
+ icd->code2 = 0;
+ icd->mode = 0;
+
+ /* we have either "<" or ">" */
+ if (args[0] != NULL && strlen(args[0]) != 0) {
+ /* we have a third part ("<> y"), therefore it's invalid */
+ if (args[2] != NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "icode: invalid value");
+ goto error;
+ }
+ /* we have only a comparison ("<", ">") */
+ if (ByteExtractStringUint8(&icd->code1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp code %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ if ((strcmp(args[0], ">")) == 0) icd->mode = DETECT_ICODE_GT;
+ else icd->mode = DETECT_ICODE_LT;
+ } else { /* no "<", ">" */
+ /* we have a range ("<>") */
+ if (args[2] != NULL) {
+ icd->mode = (uint8_t) DETECT_ICODE_RN;
+ if (ByteExtractStringUint8(&icd->code1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp code %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ if (ByteExtractStringUint8(&icd->code2, 10, 0, args[2]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp code %s is not "
+ "valid", args[2]);
+ goto error;
+ }
+ /* we check that the first given value in the range is less than
+ the second, otherwise we swap them */
+ if (icd->code1 > icd->code2) {
+ uint8_t temp = icd->code1;
+ icd->code1 = icd->code2;
+ icd->code2 = temp;
+ }
+ } else { /* we have an equality */
+ icd->mode = DETECT_ICODE_EQ;
+ if (ByteExtractStringUint8(&icd->code1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp code %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ }
+ }
+
+ for (i = 0; i < (ret-1); i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ return icd;
+
+error:
+ for (i = 0; i < (ret-1) && i < 3; i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (icd != NULL)
+ DetectICodeFree(icd);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed icode data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param icodestr pointer to the user provided icode options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectICodeSetup(DetectEngineCtx *de_ctx, Signature *s, char *icodestr)
+{
+
+ DetectICodeData *icd = NULL;
+ SigMatch *sm = NULL;
+
+ icd = DetectICodeParse(icodestr);
+ if (icd == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) goto error;
+
+ sm->type = DETECT_ICODE;
+ sm->ctx = (SigMatchCtx *)icd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (icd != NULL) DetectICodeFree(icd);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectICodeData
+ *
+ * \param ptr pointer to DetectICodeData
+ */
+void DetectICodeFree(void *ptr)
+{
+ DetectICodeData *icd = (DetectICodeData *)ptr;
+ SCFree(icd);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectICodeParseTest01 is a test for setting a valid icode value
+ */
+int DetectICodeParseTest01(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse("8");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->mode == DETECT_ICODE_EQ)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest02 is a test for setting a valid icode value
+ * with ">" operator
+ */
+int DetectICodeParseTest02(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse(">8");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->mode == DETECT_ICODE_GT)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest03 is a test for setting a valid icode value
+ * with "<" operator
+ */
+int DetectICodeParseTest03(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse("<8");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->mode == DETECT_ICODE_LT)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest04 is a test for setting a valid icode value
+ * with "<>" operator
+ */
+int DetectICodeParseTest04(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse("8<>20");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->code2 == 20 && icd->mode == DETECT_ICODE_RN)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest05 is a test for setting a valid icode value
+ * with spaces all around
+ */
+int DetectICodeParseTest05(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse(" 8 ");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->mode == DETECT_ICODE_EQ)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest06 is a test for setting a valid icode value
+ * with ">" operator and spaces all around
+ */
+int DetectICodeParseTest06(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse(" > 8 ");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->mode == DETECT_ICODE_GT)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest07 is a test for setting a valid icode value
+ * with "<>" operator and spaces all around
+ */
+int DetectICodeParseTest07(void)
+{
+ DetectICodeData *icd = NULL;
+ int result = 0;
+ icd = DetectICodeParse(" 8 <> 20 ");
+ if (icd != NULL) {
+ if (icd->code1 == 8 && icd->code2 == 20 && icd->mode == DETECT_ICODE_RN)
+ result = 1;
+ DetectICodeFree(icd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectICodeParseTest08 is a test for setting an invalid icode value
+ */
+int DetectICodeParseTest08(void)
+{
+ DetectICodeData *icd = NULL;
+ icd = DetectICodeParse("> 8 <> 20");
+ if (icd == NULL)
+ return 1;
+ DetectICodeFree(icd);
+ return 0;
+}
+
+/**
+ * \test DetectICodeMatchTest01 is a test for checking the working of icode
+ * keyword by creating 5 rules and matching a crafted packet against
+ * them. 4 out of 5 rules shall trigger.
+ */
+int DetectICodeMatchTest01(void)
+{
+
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
+
+ p->icmpv4h->code = 10;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert icmp any any -> any any (icode:10; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (icode:<15; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (icode:>20; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (icode:8<>20; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (icode:20<>8; sid:5;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ SCLogDebug("sid 1 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ SCLogDebug("sid 2 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3)) {
+ SCLogDebug("sid 3 alerted, but should not have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ SCLogDebug("sid 4 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 5) == 0) {
+ SCLogDebug("sid 5 did not alert, but should have");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectICode
+ */
+void DetectICodeRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectICodeParseTest01", DetectICodeParseTest01, 1);
+ UtRegisterTest("DetectICodeParseTest02", DetectICodeParseTest02, 1);
+ UtRegisterTest("DetectICodeParseTest03", DetectICodeParseTest03, 1);
+ UtRegisterTest("DetectICodeParseTest04", DetectICodeParseTest04, 1);
+ UtRegisterTest("DetectICodeParseTest05", DetectICodeParseTest05, 1);
+ UtRegisterTest("DetectICodeParseTest06", DetectICodeParseTest06, 1);
+ UtRegisterTest("DetectICodeParseTest07", DetectICodeParseTest07, 1);
+ UtRegisterTest("DetectICodeParseTest08", DetectICodeParseTest08, 1);
+ UtRegisterTest("DetectICodeMatchTest01", DetectICodeMatchTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-icode.h b/framework/src/suricata/src/detect-icode.h
new file mode 100644
index 00000000..88a4d481
--- /dev/null
+++ b/framework/src/suricata/src/detect-icode.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \file detect-icode.c
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ */
+
+#ifndef __DETECT_ICODE_H__
+#define __DETECT_ICODE_H__
+
+#define DETECT_ICODE_EQ 0 /**< "equal" operator */
+#define DETECT_ICODE_LT 1 /**< "less than" operator */
+#define DETECT_ICODE_GT 2 /**< "greater than" operator */
+#define DETECT_ICODE_RN 3 /**< "range" operator */
+
+typedef struct DetectICodeData_ {
+ uint8_t code1;
+ uint8_t code2;
+
+ uint8_t mode;
+}DetectICodeData;
+
+/* prototypes */
+void DetectICodeRegister(void);
+
+#endif /* __DETECT_ICODE_H__ */
diff --git a/framework/src/suricata/src/detect-id.c b/framework/src/suricata/src/detect-id.c
new file mode 100644
index 00000000..5df2a6d1
--- /dev/null
+++ b/framework/src/suricata/src/detect-id.c
@@ -0,0 +1,384 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements the id keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-id.h"
+#include "flow.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/**
+ * \brief Regex for parsing "id" option, matching number or "number"
+ */
+#define PARSE_REGEX "^\\s*([0-9]{1,5}|\"[0-9]{1,5}\")\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIdMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectIdSetup (DetectEngineCtx *, Signature *, char *);
+void DetectIdRegisterTests(void);
+void DetectIdFree(void *);
+
+/**
+ * \brief Registration function for keyword: id
+ */
+void DetectIdRegister (void)
+{
+ sigmatch_table[DETECT_ID].name = "id";
+ sigmatch_table[DETECT_ID].desc = "match on a specific IP ID value";
+ sigmatch_table[DETECT_ID].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#Id";
+ sigmatch_table[DETECT_ID].Match = DetectIdMatch;
+ sigmatch_table[DETECT_ID].Setup = DetectIdSetup;
+ sigmatch_table[DETECT_ID].Free = DetectIdFree;
+ sigmatch_table[DETECT_ID].RegisterTests = DetectIdRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering id rule option");
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match the specified id on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectIdData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectIdMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectIdData *id_d = (const DetectIdData *)ctx;
+
+ /**
+ * To match a ipv4 packet with a "id" rule
+ */
+ if (!PKT_IS_IPV4(p) || PKT_IS_PSEUDOPKT(p)) {
+ return 0;
+ }
+
+ if (id_d->id == IPV4_GET_IPID(p)) {
+ SCLogDebug("IPV4 Proto and matched with ip_id: %u.\n",
+ id_d->id);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectIdData on success
+ * \retval NULL on failure
+ */
+DetectIdData *DetectIdParse (char *idstr)
+{
+ uint32_t temp;
+ DetectIdData *id_d = NULL;
+ #define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, idstr, strlen(idstr), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret < 1 || ret > 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid id option. The id option value must be"
+ " in the range %u - %u",
+ DETECT_IPID_MIN, DETECT_IPID_MAX);
+ goto error;
+ }
+
+
+ if (ret > 1) {
+ char copy_str[128] = "";
+ char *tmp_str;
+ res = pcre_copy_substring((char *)idstr, ov, MAX_SUBSTRINGS, 1,
+ copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ tmp_str = copy_str;
+
+ /* Let's see if we need to scape "'s */
+ if (tmp_str[0] == '"')
+ {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+ /* ok, fill the id data */
+ temp = atoi((char *)tmp_str);
+
+ if (temp > DETECT_IPID_MAX) {
+ SCLogError(SC_ERR_INVALID_VALUE, "\"id\" option must be in "
+ "the range %u - %u",
+ DETECT_IPID_MIN, DETECT_IPID_MAX);
+ goto error;
+ }
+
+ /* We have a correct id option */
+ id_d = SCMalloc(sizeof(DetectIdData));
+ if (unlikely(id_d == NULL))
+ goto error;
+
+ id_d->id = temp;
+
+ SCLogDebug("detect-id: will look for ip_id: %u\n", id_d->id);
+ }
+
+ return id_d;
+
+error:
+ if (id_d != NULL)
+ DetectIdFree(id_d);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectIdSetup (DetectEngineCtx *de_ctx, Signature *s, char *idstr)
+{
+ DetectIdData *id_d = NULL;
+ SigMatch *sm = NULL;
+
+ id_d = DetectIdParse(idstr);
+ if (id_d == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ID;
+ sm->ctx = (SigMatchCtx *)id_d;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (id_d != NULL) DetectIdFree(id_d);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectIdData
+ *
+ * \param id_d pointer to DetectIdData
+ */
+void DetectIdFree(void *ptr)
+{
+ DetectIdData *id_d = (DetectIdData *)ptr;
+ SCFree(id_d);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectIdTestParse01 is a test to make sure that we parse the "id"
+ * option correctly when given valid id option
+ */
+int DetectIdTestParse01 (void)
+{
+ DetectIdData *id_d = NULL;
+ id_d = DetectIdParse(" 35402 ");
+ if (id_d != NULL &&id_d->id==35402) {
+ DetectIdFree(id_d);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectIdTestParse02 is a test to make sure that we parse the "id"
+ * option correctly when given an invalid id option
+ * it should return id_d = NULL
+ */
+int DetectIdTestParse02 (void)
+{
+ DetectIdData *id_d = NULL;
+ id_d = DetectIdParse("65537");
+ if (id_d == NULL) {
+ DetectIdFree(id_d);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectIdTestParse03 is a test to make sure that we parse the "id"
+ * option correctly when given an invalid id option
+ * it should return id_d = NULL
+ */
+int DetectIdTestParse03 (void)
+{
+ DetectIdData *id_d = NULL;
+ id_d = DetectIdParse("12what?");
+ if (id_d == NULL) {
+ DetectIdFree(id_d);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectIdTestParse04 is a test to make sure that we parse the "id"
+ * option correctly when given valid id option but wrapped with "'s
+ */
+int DetectIdTestParse04 (void)
+{
+ DetectIdData *id_d = NULL;
+ /* yep, look if we trim blank spaces correctly and ignore "'s */
+ id_d = DetectIdParse(" \"35402\" ");
+ if (id_d != NULL &&id_d->id==35402) {
+ DetectIdFree(id_d);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectIdTestSig01
+ * \brief Test to check "id" keyword with constructed packets
+ */
+int DetectIdTestMatch01(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_UDP);
+ p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ /* TCP IP id = 1234 */
+ p[0]->ip4h->ip_id = htons(1234);
+
+ /* UDP IP id = 5678 */
+ p[1]->ip4h->ip_id = htons(5678);
+
+ /* UDP IP id = 91011 */
+ p[2]->ip4h->ip_id = htons(5101);
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; id:1234; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; id:5678; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; id:5101; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ /* packet 0 match sid 1 but should not match sid 2 */
+ {1, 0, 0},
+ /* packet 1 should not match */
+ {0, 1, 0},
+ /* packet 2 should not match */
+ {0, 0, 1} };
+
+ result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 3);
+
+ UTHFreePackets(p, 3);
+end:
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectId
+ */
+void DetectIdRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectIdTestParse01", DetectIdTestParse01, 1);
+ UtRegisterTest("DetectIdTestParse02", DetectIdTestParse02, 1);
+ UtRegisterTest("DetectIdTestParse03", DetectIdTestParse03, 1);
+ UtRegisterTest("DetectIdTestParse04", DetectIdTestParse04, 1);
+ UtRegisterTest("DetectIdTestMatch01", DetectIdTestMatch01, 1);
+
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-id.h b/framework/src/suricata/src/detect-id.h
new file mode 100644
index 00000000..3198c9c3
--- /dev/null
+++ b/framework/src/suricata/src/detect-id.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_ID_H__
+#define __DETECT_ID_H__
+
+
+#define DETECT_IPID_MIN 0
+#define DETECT_IPID_MAX 65536
+
+typedef struct DetectIdData_ {
+ uint16_t id; /** ip->id to match */
+} DetectIdData;
+
+/* prototypes */
+void DetectIdRegister (void);
+
+#endif /* __DETECT_ID_H__ */
+
diff --git a/framework/src/suricata/src/detect-ipopts.c b/framework/src/suricata/src/detect-ipopts.c
new file mode 100644
index 00000000..159578ee
--- /dev/null
+++ b/framework/src/suricata/src/detect-ipopts.c
@@ -0,0 +1,386 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the ipopts keyword
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "decode-events.h"
+
+#include "util-debug.h"
+
+/* Need to get the DIpOpts[] array */
+#define DETECT_EVENTS
+
+#include "detect-ipopts.h"
+#include "util-unittest.h"
+
+#define PARSE_REGEX "\\S[A-z]"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIpOptsMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectIpOptsSetup (DetectEngineCtx *, Signature *, char *);
+void IpOptsRegisterTests(void);
+void DetectIpOptsFree(void *);
+
+/**
+ * \brief Registration function for ipopts: keyword
+ */
+void DetectIpOptsRegister (void)
+{
+ sigmatch_table[DETECT_IPOPTS].name = "ipopts";
+ sigmatch_table[DETECT_IPOPTS].desc = "check if a specific IP option is set";
+ sigmatch_table[DETECT_IPOPTS].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#Ipopts";
+ sigmatch_table[DETECT_IPOPTS].Match = DetectIpOptsMatch;
+ sigmatch_table[DETECT_IPOPTS].Setup = DetectIpOptsSetup;
+ sigmatch_table[DETECT_IPOPTS].Free = DetectIpOptsFree;
+ sigmatch_table[DETECT_IPOPTS].RegisterTests = IpOptsRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+
+}
+
+/**
+ * \internal
+ * \brief This function is used to match ip option on a packet with those passed via ipopts:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param s pointer to the Signature
+ * \param m pointer to the sigmatch
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectIpOptsMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ int ipopt = 0;
+ const DetectIpOptsData *de = (const DetectIpOptsData *)ctx;
+
+ if (!de || !PKT_IS_IPV4(p) || PKT_IS_PSEUDOPKT(p))
+ return ret;
+
+ /* IPV4_OPT_ANY matches on any options */
+
+ if (p->IPV4_OPTS_CNT && (de->ipopt == IPV4_OPT_ANY)) {
+ return 1;
+ }
+
+ /* Loop through instead of using o_xxx direct access fields so that
+ * future options do not require any modification here.
+ */
+
+ while(ipopt < p->IPV4_OPTS_CNT) {
+ if (p->IPV4_OPTS[ipopt].type == de->ipopt) {
+ return 1;
+ }
+ ipopt++;
+ }
+
+ return ret;
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse ipopts options passed via ipopts: keyword
+ *
+ * \param rawstr Pointer to the user provided ipopts options
+ *
+ * \retval de pointer to DetectIpOptsData on success
+ * \retval NULL on failure
+ */
+DetectIpOptsData *DetectIpOptsParse (char *rawstr)
+{
+ int i;
+ DetectIpOptsData *de = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, found = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ for(i = 0; DIpOpts[i].ipopt_name != NULL; i++) {
+ if((strcasecmp(DIpOpts[i].ipopt_name,rawstr)) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if(found == 0)
+ goto error;
+
+ de = SCMalloc(sizeof(DetectIpOptsData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ de->ipopt = DIpOpts[i].code;
+
+ return de;
+
+error:
+ if (de) SCFree(de);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed ipopts into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided ipopts options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectIpOptsSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectIpOptsData *de = NULL;
+ SigMatch *sm = NULL;
+
+ de = DetectIpOptsParse(rawstr);
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_IPOPTS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectIpOptsData
+ *
+ * \param de pointer to DetectIpOptsData
+ */
+void DetectIpOptsFree(void *de_ptr)
+{
+ DetectIpOptsData *de = (DetectIpOptsData *)de_ptr;
+ if(de) SCFree(de);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+/**
+ * \test IpOptsTestParse01 is a test for a valid ipopts value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int IpOptsTestParse01 (void)
+{
+ DetectIpOptsData *de = NULL;
+ de = DetectIpOptsParse("lsrr");
+ if (de) {
+ DetectIpOptsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test IpOptsTestParse02 is a test for an invalid ipopts value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int IpOptsTestParse02 (void)
+{
+ DetectIpOptsData *de = NULL;
+ de = DetectIpOptsParse("invalidopt");
+ if (de) {
+ DetectIpOptsFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test IpOptsTestParse03 test the match function on a packet that needs to match
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int IpOptsTestParse03 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectIpOptsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ip4h;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->ip4h = &ip4h;
+ p->IPV4_OPTS[0].type = IPV4_OPT_RR;
+
+ p->IPV4_OPTS_CNT++;
+
+ de = DetectIpOptsParse("rr");
+
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_IPOPTS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectIpOptsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test IpOptsTestParse04 test the match function on a packet that needs to not match
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int IpOptsTestParse04 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ int ret = 0;
+ DetectIpOptsData *de = NULL;
+ SigMatch *sm = NULL;
+ IPV4Hdr ip4h;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ p->ip4h = &ip4h;
+ p->IPV4_OPTS[0].type = IPV4_OPT_RR;
+
+ p->IPV4_OPTS_CNT++;
+
+ de = DetectIpOptsParse("lsrr");
+
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_IPOPTS;
+ sm->ctx = (SigMatchCtx *)de;
+
+ ret = DetectIpOptsMatch(&tv, NULL, p, NULL, sm->ctx);
+
+ if(ret) {
+ SCFree(p);
+ return 1;
+ }
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ SCFree(p);
+ return 0;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for IpOpts
+ */
+void IpOptsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IpOptsTestParse01", IpOptsTestParse01, 1);
+ UtRegisterTest("IpOptsTestParse02", IpOptsTestParse02, 0);
+ UtRegisterTest("IpOptsTestParse03", IpOptsTestParse03, 1);
+ UtRegisterTest("IpOptsTestParse04", IpOptsTestParse04, 0);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-ipopts.h b/framework/src/suricata/src/detect-ipopts.h
new file mode 100644
index 00000000..bd346256
--- /dev/null
+++ b/framework/src/suricata/src/detect-ipopts.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_IPOPTS_H__
+#define __DETECT_IPOPTS_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+
+/**
+ * \struct DetectIpOptsData_
+ * DetectIpOptsData_ is used to store ipopts: input value
+ */
+
+/**
+ * \typedef DetectIpOptsData
+ * A typedef for DetectIpOptsData_
+ */
+
+typedef struct DetectIpOptsData_ {
+ uint8_t ipopt; /**< Ip option */
+} DetectIpOptsData;
+
+/**
+ * Registration function for ipopts: keyword
+ */
+
+void DetectIpOptsRegister (void);
+
+#ifdef DETECT_EVENTS
+
+/**
+ * Used to check ipopts:any
+ */
+
+#define IPV4_OPT_ANY 0xff
+
+/**
+ * \struct DetectIpOptss_
+ * DetectIpOptss_ is used to store supported iptops values
+ */
+
+struct DetectIpOptss_ {
+ char *ipopt_name; /**< Ip option name */
+ uint8_t code; /**< Ip option value */
+} DIpOpts[] = {
+ { "rr", IPV4_OPT_RR, },
+ { "lsrr", IPV4_OPT_LSRR, },
+ { "eol", IPV4_OPT_EOL, },
+ { "nop", IPV4_OPT_NOP, },
+ { "ts", IPV4_OPT_TS, },
+ { "sec", IPV4_OPT_SEC, },
+ { "ssrr", IPV4_OPT_SSRR, },
+ { "satid", IPV4_OPT_SID, },
+ { "any", IPV4_OPT_ANY, },
+ { NULL, 0 },
+};
+#endif /* DETECT_EVENTS */
+#endif /*__DETECT_IPOPTS_H__ */
+
diff --git a/framework/src/suricata/src/detect-ipproto.c b/framework/src/suricata/src/detect-ipproto.c
new file mode 100644
index 00000000..d8ffc44b
--- /dev/null
+++ b/framework/src/suricata/src/detect-ipproto.c
@@ -0,0 +1,9609 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements the ip_proto keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-ipproto.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "util-debug.h"
+
+/**
+ * \brief Regex for parsing our options
+ */
+#define PARSE_REGEX "^\\s*" \
+ "([!<>]?)" \
+ "\\s*([^\\s]+)" \
+ "\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectIPProtoSetup(DetectEngineCtx *, Signature *, char *);
+static DetectIPProtoData *DetectIPProtoParse(const char *);
+static void DetectIPProtoRegisterTests(void);
+static void DetectIPProtoFree(void *);
+
+void DetectIPProtoRegister(void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_IPPROTO].name = "ip_proto";
+ sigmatch_table[DETECT_IPPROTO].desc = "match on the IP protocol in the packet-header";
+ sigmatch_table[DETECT_IPPROTO].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#ip_proto";
+ sigmatch_table[DETECT_IPPROTO].Match = NULL;
+ sigmatch_table[DETECT_IPPROTO].Setup = DetectIPProtoSetup;
+ sigmatch_table[DETECT_IPPROTO].Free = DetectIPProtoFree;
+ sigmatch_table[DETECT_IPPROTO].RegisterTests = DetectIPProtoRegisterTests;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ if (parse_regex)
+ pcre_free(parse_regex);
+ if (parse_regex_study)
+ pcre_free_study(parse_regex_study);
+ return;
+}
+
+/**
+ * \internal
+ * \brief Parse ip_proto options string.
+ *
+ * \param optstr Options string to parse
+ *
+ * \return New ip_proto data structure
+ */
+static DetectIPProtoData *DetectIPProtoParse(const char *optstr)
+{
+ DetectIPProtoData *data = NULL;
+ char *args[2] = { NULL, NULL };
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i;
+ const char *str_ptr;
+
+ /* Execute the regex and populate args with captures. */
+ ret = pcre_exec(parse_regex, parse_regex_study, optstr,
+ strlen(optstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret"
+ "%" PRId32 ", string %s", ret, optstr);
+ goto error;
+ }
+
+ for (i = 0; i < (ret - 1); i++) {
+ res = pcre_get_substring((char *)optstr, ov, MAX_SUBSTRINGS,
+ i + 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[i] = (char *)str_ptr;
+ }
+
+ /* Initialize the data */
+ data = SCMalloc(sizeof(DetectIPProtoData));
+ if (unlikely(data == NULL))
+ goto error;
+ data->op = DETECT_IPPROTO_OP_EQ;
+ data->proto = 0;
+
+ /* Operator */
+ if (*(args[0]) != '\0') {
+ data->op = *(args[0]);
+ }
+
+ /* Protocol name/number */
+ if (!isdigit((unsigned char)*(args[1]))) {
+ struct protoent *pent = getprotobyname(args[1]);
+ if (pent == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed protocol name: %s",
+ str_ptr);
+ goto error;
+ }
+ data->proto = (uint8_t)pent->p_proto;
+ }
+ else {
+ if (ByteExtractStringUint8(&data->proto, 10, 0, args[1]) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Malformed protocol number: %s",
+ str_ptr);
+ goto error;
+ }
+ }
+
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+
+ return data;
+
+error:
+ for (i = 0; i < (ret - 1) && i < 2; i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (data != NULL)
+ SCFree(data);
+
+ return NULL;
+}
+
+static int DetectIPProtoTypePresentForOP(Signature *s, uint8_t op)
+{
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ DetectIPProtoData *data;
+
+ while (sm != NULL) {
+ if (sm->type == DETECT_IPPROTO) {
+ data = (DetectIPProtoData *)sm->ctx;
+ if (data->op == op)
+ return 1;
+ }
+ sm = sm->next;
+ }
+
+ return 0;
+}
+
+/* Updated by AS. Please do not remove this unused code.
+ * Need it as we redo this code once we solve ipproto
+ * multiple uses */
+#if 0
+static int DetectIPProtoQSortCompare(const void *a, const void *b)
+{
+ const uint8_t *one = a;
+ const uint8_t *two = b;
+
+ return ((int)*one - *two);
+}
+#endif
+
+/**
+ * \internal
+ * \brief Setup ip_proto keyword.
+ *
+ * \param de_ctx Detection engine context
+ * \param s Signature
+ * \param optstr Options string
+ *
+ * \return Non-zero on error
+ */
+static int DetectIPProtoSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ SigMatch *sm = NULL;
+ DetectIPProtoData *data = NULL;
+ int i;
+
+ data = DetectIPProtoParse((const char *)optstr);
+ if (data == NULL) {
+ goto error;
+ }
+
+ /* Reset our "any" (or "ip") state: for ipv4, ipv6 and ip cases, the bitfield
+ * s->proto.proto have all bit set to 1 to be able to match any protocols. ipproto
+ * will refined the protocol list and thus it needs to reset the bitfield to zero
+ * before setting the value specified by the ip_proto keyword.
+ */
+ if (s->proto.flags & (DETECT_PROTO_ANY | DETECT_PROTO_IPV6 | DETECT_PROTO_IPV4)) {
+ s->proto.flags &= ~DETECT_PROTO_ANY;
+ memset(s->proto.proto, 0x00, sizeof(s->proto.proto));
+ s->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ } else {
+ /* The ipproto engine has a relationship with the protocol that is
+ * set after the action and also the app protocol(that can also be
+ * set through the app-layer-protocol.
+ * An ip_proto keyword can be used only with alert ip, which if
+ * not true we error out on the sig. And hence the init_flag to
+ * indicate this. */
+ if (!(s->init_flags & SIG_FLAG_INIT_FIRST_IPPROTO_SEEN)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Signature can use "
+ "ip_proto keyword only when we use alert ip, "
+ "in which case the _ANY flag is set on the sig "
+ "and the if condition should match.");
+ goto error;
+ }
+ }
+
+ int eq_set = DetectIPProtoTypePresentForOP(s, DETECT_IPPROTO_OP_EQ);
+ int gt_set = DetectIPProtoTypePresentForOP(s, DETECT_IPPROTO_OP_GT);
+ int lt_set = DetectIPProtoTypePresentForOP(s, DETECT_IPPROTO_OP_LT);
+ int not_set = DetectIPProtoTypePresentForOP(s, DETECT_IPPROTO_OP_NOT);
+
+ switch (data->op) {
+ case DETECT_IPPROTO_OP_EQ:
+ if (eq_set || gt_set || lt_set || not_set) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a eq "
+ "ipproto without any operators attached to "
+ "them in the same sig");
+ goto error;
+ }
+ s->proto.proto[data->proto / 8] |= 1 << (data->proto % 8);
+ break;
+
+ case DETECT_IPPROTO_OP_GT:
+ if (eq_set || gt_set) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a eq or gt "
+ "ipproto along with a greater than ipproto in the "
+ "same sig ");
+ goto error;
+ }
+ if (!lt_set && !not_set) {
+ s->proto.proto[data->proto / 8] = 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ } else if (lt_set && !not_set) {
+ SigMatch *temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO) {
+ break;
+ }
+ temp_sm = temp_sm->next;
+ }
+ if (temp_sm != NULL) {
+ DetectIPProtoData *data_temp = (DetectIPProtoData *)temp_sm->ctx;
+ if (data_temp->proto <= data->proto) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have "
+ "both gt and lt ipprotos, with the lt being "
+ "lower than gt value");
+ goto error;
+ /* Updated by AS. Please do not remove this unused code. Need it
+ * as we redo this code once we solve ipproto multiple uses */
+#if 0
+ s->proto.proto[data->proto / 8] |= 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+#endif
+ } else {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0;
+ }
+ s->proto.proto[data->proto / 8] &= 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ }
+ }
+ } else if (!lt_set && not_set) {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0;
+ }
+ s->proto.proto[data->proto / 8] &= 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ } else {
+ DetectIPProtoData *data_temp;
+ SigMatch *temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO &&
+ ((DetectIPProtoData *)temp_sm->ctx)->op == DETECT_IPPROTO_OP_LT) {
+ break;
+ }
+ temp_sm = temp_sm->next;
+ }
+ if (temp_sm != NULL) {
+ data_temp = (DetectIPProtoData *)temp_sm->ctx;
+ if (data_temp->proto <= data->proto) {
+ /* Updated by AS. Please do not remove this unused code.
+ * Need it as we redo this code once we solve ipproto
+ * multiple uses */
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have "
+ "both gt and lt ipprotos, with the lt being "
+ "lower than gt value");
+ goto error;
+#if 0
+ s->proto.proto[data->proto / 8] |= 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ uint8_t *not_protos = NULL;
+ int not_protos_len = 0;
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO &&
+ ((DetectIPProtoData *)temp_sm->ctx)->op == DETECT_IPPROTO_OP_NOT) {
+ DetectIPProtoData *data_temp = temp_sm->ctx;
+ not_protos = SCRealloc(not_protos,
+ (not_protos_len + 1) * sizeof(uint8_t));
+ if (not_protos == NULL)
+ goto error;
+ not_protos[not_protos_len] = data_temp->proto;
+ not_protos_len++;
+ }
+ temp_sm = temp_sm->next;
+ }
+ qsort(not_protos, not_protos_len, sizeof(uint8_t),
+ DetectIPProtoQSortCompare);
+ int j = 0;
+ while (j < not_protos_len) {
+ if (not_protos[j] < data->proto) {
+ ;
+ } else {
+ s->proto.proto[not_protos[j] / 8] &= ~(1 << (not_protos[j] % 8));
+ }
+ j++;
+ }
+#endif
+ } else {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0;
+ }
+ s->proto.proto[data->proto / 8] &= 0xfe << (data->proto % 8);
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ }
+ }
+ }
+ break;
+
+ case DETECT_IPPROTO_OP_LT:
+ if (eq_set || lt_set) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a eq or lt "
+ "ipproto along with a less than ipproto in the "
+ "same sig ");
+ goto error;
+ }
+ if (!gt_set && !not_set) {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ s->proto.proto[data->proto / 8] = ~(0xff << (data->proto % 8));
+ } else if (gt_set && !not_set) {
+ SigMatch *temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO) {
+ break;
+ }
+ temp_sm = temp_sm->next;
+ }
+ if (temp_sm != NULL) {
+ DetectIPProtoData *data_temp = (DetectIPProtoData *)temp_sm->ctx;
+ if (data_temp->proto >= data->proto) {
+ /* Updated by AS. Please do not remove this unused code.
+ * Need it as we redo this code once we solve ipproto
+ * multiple uses */
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a have "
+ "both gt and lt ipprotos, with the lt being "
+ "lower than gt value");
+ goto error;
+#if 0
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ s->proto.proto[data->proto / 8] |= ~(0xff << (data->proto % 8));;
+#endif
+ } else {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ s->proto.proto[data->proto / 8] &= ~(0xff << (data->proto % 8));
+ for (i = (data->proto / 8) + 1; i < 256 / 8; i++) {
+ s->proto.proto[i] = 0;
+ }
+ }
+ }
+ } else if (!gt_set && not_set) {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] &= 0xFF;
+ }
+ s->proto.proto[data->proto / 8] &= ~(0xff << (data->proto % 8));
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0;
+ }
+ } else {
+ DetectIPProtoData *data_temp;
+ SigMatch *temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO &&
+ ((DetectIPProtoData *)temp_sm->ctx)->op == DETECT_IPPROTO_OP_GT) {
+ break;
+ }
+ temp_sm = temp_sm->next;
+ }
+ if (temp_sm != NULL) {
+ data_temp = (DetectIPProtoData *)temp_sm->ctx;
+ if (data_temp->proto >= data->proto) {
+ /* Updated by AS. Please do not remove this unused code.
+ * Need it as we redo this code once we solve ipproto
+ * multiple uses */
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have "
+ "both gt and lt ipprotos, with the lt being "
+ "lower than gt value");
+ goto error;
+#if 0
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ s->proto.proto[data->proto / 8] |= ~(0xff << (data->proto % 8));
+ temp_sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ uint8_t *not_protos = NULL;
+ int not_protos_len = 0;
+ while (temp_sm != NULL) {
+ if (temp_sm->type == DETECT_IPPROTO &&
+ ((DetectIPProtoData *)temp_sm->ctx)->op == DETECT_IPPROTO_OP_NOT) {
+ DetectIPProtoData *data_temp = temp_sm->ctx;
+ not_protos = SCRealloc(not_protos,
+ (not_protos_len + 1) * sizeof(uint8_t));
+ if (not_protos == NULL)
+ goto error;
+ not_protos[not_protos_len] = data_temp->proto;
+ not_protos_len++;
+ }
+ temp_sm = temp_sm->next;
+ }
+ qsort(not_protos, not_protos_len, sizeof(uint8_t),
+ DetectIPProtoQSortCompare);
+ int j = 0;
+ while (j < not_protos_len) {
+ if (not_protos[j] < data->proto) {
+ s->proto.proto[not_protos[j] / 8] &= ~(1 << (not_protos[j] % 8));
+ } else {
+ ;
+ }
+ j++;
+ }
+#endif
+ } else {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] &= 0xFF;
+ }
+ s->proto.proto[data->proto / 8] &= ~(0xff << (data->proto % 8));
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0;
+ }
+ }
+ }
+ }
+ break;
+
+ case DETECT_IPPROTO_OP_NOT:
+ if (eq_set) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a eq "
+ "ipproto along with a not ipproto in the "
+ "same sig ");
+ goto error;
+ }
+ if (!gt_set && !lt_set && !not_set) {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ s->proto.proto[data->proto / 8] = ~(1 << (data->proto % 8));
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] = 0xff;
+ }
+ } else {
+ for (i = 0; i < (data->proto / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ s->proto.proto[data->proto / 8] &= ~(1 << (data->proto % 8));
+ for (i = (data->proto / 8) + 1; i < (256 / 8); i++) {
+ s->proto.proto[i] &= 0xff;
+ }
+ }
+ break;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_IPPROTO;
+ sm->ctx = (void *)data;
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+ error:
+
+ return -1;
+}
+
+
+void DetectIPProtoRemoveAllSMs(Signature *s)
+{
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+
+ while (sm != NULL) {
+ if (sm->type != DETECT_IPPROTO) {
+ sm = sm->next;
+ continue;
+ }
+ SigMatch *tmp_sm = sm->next;
+ SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_MATCH);
+ SigMatchFree(sm);
+ sm = tmp_sm;
+ }
+
+ return;
+}
+
+static void DetectIPProtoFree(void *ptr)
+{
+ DetectIPProtoData *data = (DetectIPProtoData *)ptr;
+ if (data) {
+ SCFree(data);
+ }
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+#include "detect-engine.h"
+#include "detect-parse.h"
+
+/**
+ * \test DetectIPProtoTestParse01 is a test for an invalid proto number
+ */
+static int DetectIPProtoTestParse01(void)
+{
+ int result = 0;
+ DetectIPProtoData *data = NULL;
+ data = DetectIPProtoParse("999");
+ if (data == NULL) {
+ result = 1;
+ }
+
+ if (data)
+ SCFree(data);
+
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestParse02 is a test for an invalid proto name
+ */
+static int DetectIPProtoTestParse02(void)
+{
+ int result = 0;
+ DetectIPProtoData *data = NULL;
+ data = DetectIPProtoParse("foobarbooeek");
+ if (data == NULL) {
+ result = 1;
+ }
+
+ if (data)
+ SCFree(data);
+
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestSetup01 is a test for a protocol number
+ */
+static int DetectIPProtoTestSetup01(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value_str = "14";
+ int value = atoi(value_str);
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ DetectIPProtoSetup(NULL, sig, value_str);
+ for (i = 0; i < (value / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value / 8] != 0x40) {
+ goto end;
+ }
+ for (i = (value / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestSetup02 is a test for a protocol name
+ */
+static int DetectIPProtoTestSetup02(void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ char *value_str = "tcp";
+ struct protoent *pent = getprotobyname(value_str);
+ if (pent == NULL) {
+ goto end;
+ }
+ uint8_t value = (uint8_t)pent->p_proto;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ DetectIPProtoSetup(NULL, sig, value_str);
+ for (i = 0; i < (value / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value / 8] != 0x40) {
+ goto end;
+ }
+ for (i = (value / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ if (sig != NULL)
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestSetup03 is a test for a < operator
+ */
+static int DetectIPProtoTestSetup03(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value_str = "<14";
+ int value = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ DetectIPProtoSetup(NULL, sig, value_str);
+ for (i = 0; i < (value / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value / 8] != 0x3F) {
+ goto end;
+ }
+ for (i = (value / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestSetup04 is a test for a > operator
+ */
+static int DetectIPProtoTestSetup04(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value_str = ">14";
+ int value = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ DetectIPProtoSetup(NULL, sig, value_str);
+ for (i = 0; i < (value / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test DetectIPProtoTestSetup05 is a test for a ! operator
+ */
+static int DetectIPProtoTestSetup05(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value_str = "!14";
+ int value = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ DetectIPProtoSetup(NULL, sig, value_str);
+ for (i = 0; i < (value / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value / 8] != 0xBF) {
+ goto end;
+ }
+ for (i = (value / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup06(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "14";
+ char *value2_str = "15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup07(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "14";
+ char *value2_str = "<15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup08(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "14";
+ char *value2_str = ">15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup09(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "14";
+ char *value2_str = "!15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup10(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">14";
+ char *value2_str = "15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup11(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ char *value2_str = "15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup12(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!14";
+ char *value2_str = "15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+/**
+ * \test Negative test.
+ */
+static int DetectIPProtoTestSetup13(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">14";
+ char *value2_str = ">15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup14(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ char *value2_str = "<15";
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup15(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ int value1 = 14;
+ char *value2_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x3F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ int value1 = 14;
+ char *value2_str = ">34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x3F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup16(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ char *value2_str = ">34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<14";
+ int value1 = 14;
+ char *value2_str = ">34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x3F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup17(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = ">13";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = ">13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xC7) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xC7) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup18(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = ">13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xC0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = ">13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xC7) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xC7) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup19(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup20(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup21(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup22(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup23(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup24(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!13";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup25(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup26(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup27(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup28(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup29(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup30(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!18";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup31(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup32(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup33(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup34(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup35(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup36(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<11";
+ int value1 = 11;
+ char *value2_str = "!34";
+ char *value3_str = ">36";
+ int value3 = 36;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup37(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup38(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value3_str = ">14";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup39(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup40(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup41(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup42(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!12";
+ char *value3_str = ">14";
+ int value3 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x83) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup43(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup44(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup45(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup46(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup47(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup48(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup49(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup50(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup51(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup52(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value3_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup53(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup54(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value2_str = "<13";
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!11";
+ int value1 = 11;
+ char *value2_str = "<13";
+ int value2 = 13;
+ char *value3_str = ">34";
+ int value3 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x17) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < value3 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup55(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup56(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ char *value3_str = "!37";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup57(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup58(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup59(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ char *value3_str = "!37";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup60(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!37";
+ int value3 = 37;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup61(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup62(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ char *value3_str = "!44";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup63(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup64(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup65(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ char *value3_str = "!44";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup66(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+
+#if 0
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<13";
+ int value1 = 13;
+ char *value2_str = ">34";
+ int value2 = 34;
+ char *value3_str = "!44";
+ int value3 = 44;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1F) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < value2 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0xEF) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+#endif
+}
+
+static int DetectIPProtoTestSetup67(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">14";
+ int value1 = 14;
+ char *value2_str = "<34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup68(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">14";
+ int value1 = 14;
+ char *value2_str = "<34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x80) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup69(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<14";
+ int value2 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x38) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup70(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<14";
+ int value2 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x38) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup71(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!14";
+ int value2 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xB8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup72(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!14";
+ int value2 = 14;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xB8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup73(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup74(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!34";
+ int value2 = 34;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup75(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!8";
+ char *value2_str = ">10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup76(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!8";
+ char *value2_str = ">10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup77(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup78(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup79(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup80(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ int value1 = 4;
+ char *value2_str = "<10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[value1 / 8] != 0xEF) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup81(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ int value1 = 9;
+ char *value2_str = "<13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1D) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup82(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ int value1 = 9;
+ char *value2_str = "<13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x1D) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (256 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup83(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup84(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup85(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!35";
+ int value2 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup86(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!35";
+ int value2 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup87(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup88(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup89(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup90(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup91(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup92(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">10";
+ int value2 = 10;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x07) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup93(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup94(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup95(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup96(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup97(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup98(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!9";
+ char *value2_str = ">12";
+ int value2 = 12;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xE0) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup99(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup100(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup101(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup102(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup103(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup104(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!13";
+ int value2 = 13;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xD8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup105(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup106(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup107(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup108(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup109(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup110(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!18";
+ int value2 = 18;
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xFB) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup111(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup112(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup113(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup114(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup115(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup116(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!33";
+ char *value3_str = "<35";
+ int value3 = 35;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value3 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value3 / 8] != 0x05) {
+ goto end;
+ }
+ for (i = (value3 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup117(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup118(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup119(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup120(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup121(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup122(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!38";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup123(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup124(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup125(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup126(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup127(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup128(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "<34";
+ int value2 = 34;
+ char *value3_str = "!45";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup129(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = ">10";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (DetectIPProtoSetup(NULL, sig, value2_str) == 0)
+ goto end;
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup130(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ char *value2_str = ">10";
+ int value2 = 10;
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) == 0)
+ goto end;
+ for (i = 0; i < (value2 / 8); i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+ if (sig->proto.proto[value2 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value2 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup131(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!10";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup132(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "<10";
+ int value1 = 10;
+ char *value2_str = "!10";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0x03) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup133(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!10";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+
+static int DetectIPProtoTestSetup134(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = ">10";
+ int value1 = 10;
+ char *value2_str = "!10";
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ for (i = 0; i < (value1 / 8); i++) {
+ if (sig->proto.proto[i] != 0x0)
+ goto end;
+ }
+ if (sig->proto.proto[value1 / 8] != 0xF8) {
+ goto end;
+ }
+ for (i = (value1 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0xFF)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup135(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup136(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup137(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup138(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup139(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup140(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup141(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup142(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!27";
+ char *value4_str = "!29";
+ char *value5_str = "!30";
+ char *value6_str = "!34";
+ char *value7_str = "<36";
+ char *value8_str = "!38";
+ int value8 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xFE) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value8 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup143(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!10";
+ char *value4_str = "!14";
+ char *value5_str = "!27";
+ char *value6_str = "!29";
+ char *value7_str = "!30";
+ char *value8_str = "!34";
+ char *value9_str = "<36";
+ char *value10_str = "!38";
+ int value10 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value9_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value10_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xBA) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value10 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup144(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!10";
+ char *value4_str = "!14";
+ char *value5_str = "!27";
+ char *value6_str = "!29";
+ char *value7_str = "!30";
+ char *value8_str = "!34";
+ char *value9_str = "<36";
+ char *value10_str = "!38";
+ int value10 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value10_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value9_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xBA) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value10 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSetup145(void)
+{
+ int result = 0;
+ Signature *sig;
+ char *value1_str = "!4";
+ char *value2_str = ">8";
+ char *value3_str = "!10";
+ char *value4_str = "!14";
+ char *value5_str = "!27";
+ char *value6_str = "!29";
+ char *value7_str = "!30";
+ char *value8_str = "!34";
+ char *value9_str = "<36";
+ char *value10_str = "!38";
+ int value10 = 38;
+
+ int i;
+
+ if ((sig = SigAlloc()) == NULL)
+ goto end;
+
+ sig->init_flags |= SIG_FLAG_INIT_FIRST_IPPROTO_SEEN;
+ sig->proto.flags |= DETECT_PROTO_ANY;
+ if (DetectIPProtoSetup(NULL, sig, value5_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value8_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value2_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value10_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value1_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value6_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value9_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value4_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value3_str) != 0)
+ goto end;
+ if (DetectIPProtoSetup(NULL, sig, value7_str) != 0)
+ goto end;
+ if (sig->proto.proto[0] != 0) {
+ goto end;
+ }
+ if (sig->proto.proto[1] != 0xBA) {
+ goto end;
+ }
+ if (sig->proto.proto[2] != 0xFF) {
+ goto end;
+ }
+ if (sig->proto.proto[3] != 0x97) {
+ goto end;
+ }
+ if (sig->proto.proto[4] != 0x0B) {
+ goto end;
+ }
+ for (i = (value10 / 8) + 1; i < 256 / 8; i++) {
+ if (sig->proto.proto[i] != 0)
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigFree(sig);
+ return result;
+}
+
+static int DetectIPProtoTestSig1(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ char *sigs[4];
+ sigs[0] = "alert ip any any -> any any "
+ "(msg:\"Not tcp\"; ip_proto:!tcp; content:\"GET \"; sid:1;)";
+ sigs[1] = "alert ip any any -> any any "
+ "(msg:\"Less than 7\"; content:\"GET \"; ip_proto:<7; sid:2;)";
+ sigs[2] = "alert ip any any -> any any "
+ "(msg:\"Greater than 5\"; content:\"GET \"; ip_proto:>5; sid:3;)";
+ sigs[3] = "alert ip any any -> any any "
+ "(msg:\"Equals tcp\"; content:\"GET \"; ip_proto:tcp; sid:4;)";
+
+ /* sids to match */
+ uint32_t sid[4] = {1, 2, 3, 4};
+ /* expected matches for each sid within this packet we are testing */
+ uint32_t results[4] = {0, 1, 1, 1};
+
+ /* remember that UTHGenericTest expect the first parameter
+ * as an array of packet pointers. And also a bidimensional array of results
+ * For example:
+ * results[numpacket][position] should hold the number of times
+ * that the sid at sid[position] matched that packet (should be always 1..)
+ * But here we built it as unidimensional array
+ */
+ result = UTHGenericTest(&p, 1, sigs, sid, results, 4);
+
+ UTHFreePacket(p);
+end:
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ return result;
+}
+
+static int DetectIPProtoTestSig2(void)
+{
+ int result = 0;
+
+ uint8_t raw_eth[] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x0d, 0x00, 0x26,
+ 0x88, 0x61, 0x3a, 0x80, 0x08, 0x00, 0x45, 0xc0,
+ 0x00, 0x36, 0xe4, 0xcd, 0x00, 0x00, 0x01, 0x67,
+ 0xc7, 0xab, 0xac, 0x1c, 0x7f, 0xfe, 0xe0, 0x00,
+ 0x00, 0x0d, 0x20, 0x00, 0x90, 0x20, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x69, 0x00, 0x02, 0x00, 0x04,
+ 0x81, 0xf4, 0x07, 0xd0, 0x00, 0x13, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x04,
+ 0x4a, 0xea, 0x7a, 0x8e,
+ };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ p->proto = 0;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any (msg:\"Check ipproto usage\"; "
+ "ip_proto:!103; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ result = 1;
+ goto end;
+ } else {
+ result = 0;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+ SCFree(p);
+
+ return result;
+}
+
+static int DetectIPProtoTestSig3(void)
+{
+ int result = 0;
+
+ uint8_t raw_eth[] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x0d, 0x00, 0x26,
+ 0x88, 0x61, 0x3a, 0x80, 0x08, 0x00, 0x45, 0xc0,
+ 0x00, 0x36, 0xe4, 0xcd, 0x00, 0x00, 0x01, 0x67,
+ 0xc7, 0xab, 0xac, 0x1c, 0x7f, 0xfe, 0xe0, 0x00,
+ 0x00, 0x0d, 0x20, 0x00, 0x90, 0x20, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x69, 0x00, 0x02, 0x00, 0x04,
+ 0x81, 0xf4, 0x07, 0xd0, 0x00, 0x13, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x04,
+ 0x4a, 0xea, 0x7a, 0x8e,
+ };
+
+ Packet *p = UTHBuildPacket((uint8_t *)"boom", 4, IPPROTO_TCP);
+ if (p == NULL)
+ return 0;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ p->proto = 0;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = DEFAULT_MPM;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any (msg:\"Check ipproto usage\"; "
+ "ip_proto:103; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!PacketAlertCheck(p, 1)) {
+ result = 0;
+ goto end;
+ } else {
+ result = 1;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ if (de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ FlowShutdown();
+ SCFree(p);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \internal
+ * \brief Register ip_proto tests.
+ */
+static void DetectIPProtoRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectIPProtoTestParse01", DetectIPProtoTestParse01, 1);
+ UtRegisterTest("DetectIPProtoTestParse02", DetectIPProtoTestParse02, 1);
+ UtRegisterTest("DetectIPProtoTestSetup01", DetectIPProtoTestSetup01, 1);
+ UtRegisterTest("DetectIPProtoTestSetup02", DetectIPProtoTestSetup02, 1);
+ UtRegisterTest("DetectIPProtoTestSetup03", DetectIPProtoTestSetup03, 1);
+ UtRegisterTest("DetectIPProtoTestSetup04", DetectIPProtoTestSetup04, 1);
+ UtRegisterTest("DetectIPProtoTestSetup05", DetectIPProtoTestSetup05, 1);
+ UtRegisterTest("DetectIPProtoTestSetup06", DetectIPProtoTestSetup06, 1);
+ UtRegisterTest("DetectIPProtoTestSetup07", DetectIPProtoTestSetup07, 1);
+ UtRegisterTest("DetectIPProtoTestSetup08", DetectIPProtoTestSetup08, 1);
+ UtRegisterTest("DetectIPProtoTestSetup09", DetectIPProtoTestSetup09, 1);
+ UtRegisterTest("DetectIPProtoTestSetup10", DetectIPProtoTestSetup10, 1);
+ UtRegisterTest("DetectIPProtoTestSetup11", DetectIPProtoTestSetup11, 1);
+ UtRegisterTest("DetectIPProtoTestSetup12", DetectIPProtoTestSetup12, 1);
+ UtRegisterTest("DetectIPProtoTestSetup13", DetectIPProtoTestSetup13, 1);
+ UtRegisterTest("DetectIPProtoTestSetup14", DetectIPProtoTestSetup14, 1);
+ UtRegisterTest("DetectIPProtoTestSetup15", DetectIPProtoTestSetup15, 1);
+ UtRegisterTest("DetectIPProtoTestSetup16", DetectIPProtoTestSetup16, 1);
+ UtRegisterTest("DetectIPProtoTestSetup17", DetectIPProtoTestSetup17, 1);
+ UtRegisterTest("DetectIPProtoTestSetup18", DetectIPProtoTestSetup18, 1);
+ UtRegisterTest("DetectIPProtoTestSetup19", DetectIPProtoTestSetup19, 1);
+ UtRegisterTest("DetectIPProtoTestSetup20", DetectIPProtoTestSetup20, 1);
+ UtRegisterTest("DetectIPProtoTestSetup21", DetectIPProtoTestSetup21, 1);
+ UtRegisterTest("DetectIPProtoTestSetup22", DetectIPProtoTestSetup22, 1);
+ UtRegisterTest("DetectIPProtoTestSetup23", DetectIPProtoTestSetup23, 1);
+ UtRegisterTest("DetectIPProtoTestSetup24", DetectIPProtoTestSetup24, 1);
+ UtRegisterTest("DetectIPProtoTestSetup25", DetectIPProtoTestSetup25, 1);
+ UtRegisterTest("DetectIPProtoTestSetup26", DetectIPProtoTestSetup26, 1);
+ UtRegisterTest("DetectIPProtoTestSetup27", DetectIPProtoTestSetup27, 1);
+ UtRegisterTest("DetectIPProtoTestSetup28", DetectIPProtoTestSetup28, 1);
+ UtRegisterTest("DetectIPProtoTestSetup29", DetectIPProtoTestSetup29, 1);
+ UtRegisterTest("DetectIPProtoTestSetup30", DetectIPProtoTestSetup30, 1);
+ UtRegisterTest("DetectIPProtoTestSetup31", DetectIPProtoTestSetup31, 1);
+ UtRegisterTest("DetectIPProtoTestSetup32", DetectIPProtoTestSetup32, 1);
+ UtRegisterTest("DetectIPProtoTestSetup33", DetectIPProtoTestSetup33, 1);
+ UtRegisterTest("DetectIPProtoTestSetup34", DetectIPProtoTestSetup34, 1);
+ UtRegisterTest("DetectIPProtoTestSetup35", DetectIPProtoTestSetup35, 1);
+ UtRegisterTest("DetectIPProtoTestSetup36", DetectIPProtoTestSetup36, 1);
+ UtRegisterTest("DetectIPProtoTestSetup37", DetectIPProtoTestSetup37, 1);
+ UtRegisterTest("DetectIPProtoTestSetup38", DetectIPProtoTestSetup38, 1);
+ UtRegisterTest("DetectIPProtoTestSetup39", DetectIPProtoTestSetup39, 1);
+ UtRegisterTest("DetectIPProtoTestSetup40", DetectIPProtoTestSetup40, 1);
+ UtRegisterTest("DetectIPProtoTestSetup41", DetectIPProtoTestSetup41, 1);
+ UtRegisterTest("DetectIPProtoTestSetup42", DetectIPProtoTestSetup42, 1);
+ UtRegisterTest("DetectIPProtoTestSetup43", DetectIPProtoTestSetup43, 1);
+ UtRegisterTest("DetectIPProtoTestSetup44", DetectIPProtoTestSetup44, 1);
+ UtRegisterTest("DetectIPProtoTestSetup45", DetectIPProtoTestSetup45, 1);
+ UtRegisterTest("DetectIPProtoTestSetup46", DetectIPProtoTestSetup46, 1);
+ UtRegisterTest("DetectIPProtoTestSetup47", DetectIPProtoTestSetup47, 1);
+ UtRegisterTest("DetectIPProtoTestSetup48", DetectIPProtoTestSetup48, 1);
+ UtRegisterTest("DetectIPProtoTestSetup49", DetectIPProtoTestSetup49, 1);
+ UtRegisterTest("DetectIPProtoTestSetup50", DetectIPProtoTestSetup50, 1);
+ UtRegisterTest("DetectIPProtoTestSetup51", DetectIPProtoTestSetup51, 1);
+ UtRegisterTest("DetectIPProtoTestSetup52", DetectIPProtoTestSetup52, 1);
+ UtRegisterTest("DetectIPProtoTestSetup53", DetectIPProtoTestSetup53, 1);
+ UtRegisterTest("DetectIPProtoTestSetup54", DetectIPProtoTestSetup54, 1);
+ UtRegisterTest("DetectIPProtoTestSetup55", DetectIPProtoTestSetup55, 1);
+ UtRegisterTest("DetectIPProtoTestSetup56", DetectIPProtoTestSetup56, 1);
+ UtRegisterTest("DetectIPProtoTestSetup57", DetectIPProtoTestSetup57, 1);
+ UtRegisterTest("DetectIPProtoTestSetup58", DetectIPProtoTestSetup58, 1);
+ UtRegisterTest("DetectIPProtoTestSetup59", DetectIPProtoTestSetup59, 1);
+ UtRegisterTest("DetectIPProtoTestSetup60", DetectIPProtoTestSetup60, 1);
+ UtRegisterTest("DetectIPProtoTestSetup61", DetectIPProtoTestSetup61, 1);
+ UtRegisterTest("DetectIPProtoTestSetup62", DetectIPProtoTestSetup62, 1);
+ UtRegisterTest("DetectIPProtoTestSetup63", DetectIPProtoTestSetup63, 1);
+ UtRegisterTest("DetectIPProtoTestSetup64", DetectIPProtoTestSetup64, 1);
+ UtRegisterTest("DetectIPProtoTestSetup65", DetectIPProtoTestSetup65, 1);
+ UtRegisterTest("DetectIPProtoTestSetup66", DetectIPProtoTestSetup66, 1);
+ UtRegisterTest("DetectIPProtoTestSetup67", DetectIPProtoTestSetup67, 1);
+ UtRegisterTest("DetectIPProtoTestSetup68", DetectIPProtoTestSetup68, 1);
+ UtRegisterTest("DetectIPProtoTestSetup69", DetectIPProtoTestSetup69, 1);
+ UtRegisterTest("DetectIPProtoTestSetup70", DetectIPProtoTestSetup70, 1);
+ UtRegisterTest("DetectIPProtoTestSetup71", DetectIPProtoTestSetup71, 1);
+ UtRegisterTest("DetectIPProtoTestSetup72", DetectIPProtoTestSetup72, 1);
+ UtRegisterTest("DetectIPProtoTestSetup73", DetectIPProtoTestSetup73, 1);
+ UtRegisterTest("DetectIPProtoTestSetup74", DetectIPProtoTestSetup74, 1);
+ UtRegisterTest("DetectIPProtoTestSetup75", DetectIPProtoTestSetup75, 1);
+ UtRegisterTest("DetectIPProtoTestSetup76", DetectIPProtoTestSetup76, 1);
+ UtRegisterTest("DetectIPProtoTestSetup77", DetectIPProtoTestSetup77, 1);
+ UtRegisterTest("DetectIPProtoTestSetup78", DetectIPProtoTestSetup78, 1);
+ UtRegisterTest("DetectIPProtoTestSetup79", DetectIPProtoTestSetup79, 1);
+ UtRegisterTest("DetectIPProtoTestSetup80", DetectIPProtoTestSetup80, 1);
+ UtRegisterTest("DetectIPProtoTestSetup81", DetectIPProtoTestSetup81, 1);
+ UtRegisterTest("DetectIPProtoTestSetup82", DetectIPProtoTestSetup82, 1);
+ UtRegisterTest("DetectIPProtoTestSetup83", DetectIPProtoTestSetup83, 1);
+ UtRegisterTest("DetectIPProtoTestSetup84", DetectIPProtoTestSetup84, 1);
+ UtRegisterTest("DetectIPProtoTestSetup85", DetectIPProtoTestSetup85, 1);
+ UtRegisterTest("DetectIPProtoTestSetup86", DetectIPProtoTestSetup86, 1);
+ UtRegisterTest("DetectIPProtoTestSetup87", DetectIPProtoTestSetup87, 1);
+ UtRegisterTest("DetectIPProtoTestSetup88", DetectIPProtoTestSetup88, 1);
+ UtRegisterTest("DetectIPProtoTestSetup89", DetectIPProtoTestSetup89, 1);
+ UtRegisterTest("DetectIPProtoTestSetup90", DetectIPProtoTestSetup90, 1);
+ UtRegisterTest("DetectIPProtoTestSetup91", DetectIPProtoTestSetup91, 1);
+ UtRegisterTest("DetectIPProtoTestSetup92", DetectIPProtoTestSetup92, 1);
+ UtRegisterTest("DetectIPProtoTestSetup93", DetectIPProtoTestSetup93, 1);
+ UtRegisterTest("DetectIPProtoTestSetup94", DetectIPProtoTestSetup94, 1);
+ UtRegisterTest("DetectIPProtoTestSetup95", DetectIPProtoTestSetup95, 1);
+ UtRegisterTest("DetectIPProtoTestSetup96", DetectIPProtoTestSetup96, 1);
+ UtRegisterTest("DetectIPProtoTestSetup97", DetectIPProtoTestSetup97, 1);
+ UtRegisterTest("DetectIPProtoTestSetup98", DetectIPProtoTestSetup98, 1);
+ UtRegisterTest("DetectIPProtoTestSetup99", DetectIPProtoTestSetup99, 1);
+ UtRegisterTest("DetectIPProtoTestSetup100", DetectIPProtoTestSetup100, 1);
+ UtRegisterTest("DetectIPProtoTestSetup101", DetectIPProtoTestSetup101, 1);
+ UtRegisterTest("DetectIPProtoTestSetup102", DetectIPProtoTestSetup102, 1);
+ UtRegisterTest("DetectIPProtoTestSetup103", DetectIPProtoTestSetup103, 1);
+ UtRegisterTest("DetectIPProtoTestSetup104", DetectIPProtoTestSetup104, 1);
+ UtRegisterTest("DetectIPProtoTestSetup105", DetectIPProtoTestSetup105, 1);
+ UtRegisterTest("DetectIPProtoTestSetup106", DetectIPProtoTestSetup106, 1);
+ UtRegisterTest("DetectIPProtoTestSetup107", DetectIPProtoTestSetup107, 1);
+ UtRegisterTest("DetectIPProtoTestSetup108", DetectIPProtoTestSetup108, 1);
+ UtRegisterTest("DetectIPProtoTestSetup109", DetectIPProtoTestSetup109, 1);
+ UtRegisterTest("DetectIPProtoTestSetup110", DetectIPProtoTestSetup110, 1);
+ UtRegisterTest("DetectIPProtoTestSetup111", DetectIPProtoTestSetup111, 1);
+ UtRegisterTest("DetectIPProtoTestSetup112", DetectIPProtoTestSetup112, 1);
+ UtRegisterTest("DetectIPProtoTestSetup113", DetectIPProtoTestSetup113, 1);
+ UtRegisterTest("DetectIPProtoTestSetup114", DetectIPProtoTestSetup114, 1);
+ UtRegisterTest("DetectIPProtoTestSetup115", DetectIPProtoTestSetup115, 1);
+ UtRegisterTest("DetectIPProtoTestSetup116", DetectIPProtoTestSetup116, 1);
+ UtRegisterTest("DetectIPProtoTestSetup117", DetectIPProtoTestSetup117, 1);
+ UtRegisterTest("DetectIPProtoTestSetup118", DetectIPProtoTestSetup118, 1);
+ UtRegisterTest("DetectIPProtoTestSetup119", DetectIPProtoTestSetup119, 1);
+ UtRegisterTest("DetectIPProtoTestSetup120", DetectIPProtoTestSetup120, 1);
+ UtRegisterTest("DetectIPProtoTestSetup121", DetectIPProtoTestSetup121, 1);
+ UtRegisterTest("DetectIPProtoTestSetup122", DetectIPProtoTestSetup122, 1);
+ UtRegisterTest("DetectIPProtoTestSetup123", DetectIPProtoTestSetup123, 1);
+ UtRegisterTest("DetectIPProtoTestSetup124", DetectIPProtoTestSetup124, 1);
+ UtRegisterTest("DetectIPProtoTestSetup125", DetectIPProtoTestSetup125, 1);
+ UtRegisterTest("DetectIPProtoTestSetup126", DetectIPProtoTestSetup126, 1);
+ UtRegisterTest("DetectIPProtoTestSetup127", DetectIPProtoTestSetup127, 1);
+ UtRegisterTest("DetectIPProtoTestSetup128", DetectIPProtoTestSetup128, 1);
+ UtRegisterTest("DetectIPProtoTestSetup129", DetectIPProtoTestSetup129, 1);
+ UtRegisterTest("DetectIPProtoTestSetup130", DetectIPProtoTestSetup130, 1);
+ UtRegisterTest("DetectIPProtoTestSetup131", DetectIPProtoTestSetup131, 1);
+ UtRegisterTest("DetectIPProtoTestSetup132", DetectIPProtoTestSetup132, 1);
+ UtRegisterTest("DetectIPProtoTestSetup133", DetectIPProtoTestSetup133, 1);
+ UtRegisterTest("DetectIPProtoTestSetup134", DetectIPProtoTestSetup134, 1);
+ UtRegisterTest("DetectIPProtoTestSetup135", DetectIPProtoTestSetup135, 1);
+ UtRegisterTest("DetectIPProtoTestSetup136", DetectIPProtoTestSetup136, 1);
+ UtRegisterTest("DetectIPProtoTestSetup137", DetectIPProtoTestSetup137, 1);
+ UtRegisterTest("DetectIPProtoTestSetup138", DetectIPProtoTestSetup138, 1);
+ UtRegisterTest("DetectIPProtoTestSetup139", DetectIPProtoTestSetup139, 1);
+ UtRegisterTest("DetectIPProtoTestSetup140", DetectIPProtoTestSetup140, 1);
+ UtRegisterTest("DetectIPProtoTestSetup141", DetectIPProtoTestSetup141, 1);
+ UtRegisterTest("DetectIPProtoTestSetup142", DetectIPProtoTestSetup142, 1);
+ UtRegisterTest("DetectIPProtoTestSetup143", DetectIPProtoTestSetup143, 1);
+ UtRegisterTest("DetectIPProtoTestSetup144", DetectIPProtoTestSetup144, 1);
+ UtRegisterTest("DetectIPProtoTestSetup145", DetectIPProtoTestSetup145, 1);
+
+ UtRegisterTest("DetectIPProtoTestSig1", DetectIPProtoTestSig1, 1);
+ UtRegisterTest("DetectIPProtoTestSig2", DetectIPProtoTestSig2, 1);
+ UtRegisterTest("DetectIPProtoTestSig3", DetectIPProtoTestSig3, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-ipproto.h b/framework/src/suricata/src/detect-ipproto.h
new file mode 100644
index 00000000..e586f303
--- /dev/null
+++ b/framework/src/suricata/src/detect-ipproto.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_IPPROTO_H__
+#define __DETECT_IPPROTO_H__
+
+/** IPProto Operators */
+#define DETECT_IPPROTO_OP_EQ '=' /**< "equals" operator (default) */
+#define DETECT_IPPROTO_OP_NOT '!' /**< "not" operator */
+#define DETECT_IPPROTO_OP_LT '<' /**< "less than" operator */
+#define DETECT_IPPROTO_OP_GT '>' /**< "greater than" operator */
+
+/** ip_proto data */
+typedef struct DetectIPProtoData_ {
+ uint8_t op; /**< Operator used to compare */
+ uint8_t proto; /**< Protocol used to compare */
+} DetectIPProtoData;
+
+/* prototypes */
+
+/**
+ * \brief Registration function for ip_proto keyword.
+ */
+void DetectIPProtoRegister (void);
+void DetectIPProtoRemoveAllSMs(Signature *);
+
+#endif /* __DETECT_IPPROTO_H__ */
+
diff --git a/framework/src/suricata/src/detect-iprep.c b/framework/src/suricata/src/detect-iprep.c
new file mode 100644
index 00000000..4e47acdc
--- /dev/null
+++ b/framework/src/suricata/src/detect-iprep.c
@@ -0,0 +1,1030 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the iprep keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-bit.h"
+#include "flow-util.h"
+#include "detect-iprep.h"
+#include "util-spm.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-fmemopen.h"
+
+#include "reputation.h"
+#include "host.h"
+
+#define PARSE_REGEX "\\s*(any|src|dst|both)\\s*,\\s*([A-Za-z0-9\\-\\_]+)\\s*,\\s*(\\<|\\>|\\=)\\s*,\\s*([0-9]+)\\s*"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIPRepMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectIPRepSetup (DetectEngineCtx *, Signature *, char *);
+void DetectIPRepFree (void *);
+void IPRepRegisterTests(void);
+
+void DetectIPRepRegister (void)
+{
+ sigmatch_table[DETECT_IPREP].name = "iprep";
+ sigmatch_table[DETECT_IPREP].Match = DetectIPRepMatch;
+ sigmatch_table[DETECT_IPREP].Setup = DetectIPRepSetup;
+ sigmatch_table[DETECT_IPREP].Free = DetectIPRepFree;
+ sigmatch_table[DETECT_IPREP].RegisterTests = IPRepRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_IPREP].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+static uint8_t GetHostRepSrc(Packet *p, uint8_t cat, uint32_t version)
+{
+ uint8_t val = 0;
+ Host *h = NULL;
+
+ if (p->flags & PKT_HOST_SRC_LOOKED_UP && p->host_src == NULL) {
+ return 0;
+ } else if (p->host_src != NULL) {
+ h = (Host *)p->host_src;
+ HostLock(h);
+ } else {
+ h = HostLookupHostFromHash(&(p->src));
+
+ p->flags |= PKT_HOST_SRC_LOOKED_UP;
+
+ if (h == NULL)
+ return 0;
+
+ HostReference(&p->host_src, h);
+ }
+
+ if (h->iprep == NULL) {
+ HostRelease(h);
+ return 0;
+ }
+
+ SReputation *r = (SReputation *)h->iprep;
+
+ /* allow higher versions as this happens during
+ * rule reload */
+ if (r->version >= version)
+ val = r->rep[cat];
+ else
+ SCLogDebug("version mismatch %u != %u", r->version, version);
+
+ HostRelease(h);
+ return val;
+}
+
+static uint8_t GetHostRepDst(Packet *p, uint8_t cat, uint32_t version)
+{
+ uint8_t val = 0;
+ Host *h = NULL;
+
+ if (p->flags & PKT_HOST_DST_LOOKED_UP && p->host_dst == NULL) {
+ return 0;
+ } else if (p->host_dst != NULL) {
+ h = (Host *)p->host_dst;
+ HostLock(h);
+ } else {
+ h = HostLookupHostFromHash(&(p->dst));
+
+ p->flags |= PKT_HOST_DST_LOOKED_UP;
+
+ if (h == NULL) {
+ return 0;
+ }
+
+ HostReference(&p->host_dst, h);
+ }
+
+ if (h->iprep == NULL) {
+ HostRelease(h);
+ return 0;
+ }
+
+ SReputation *r = (SReputation *)h->iprep;
+
+ /* allow higher versions as this happens during
+ * rule reload */
+ if (r->version >= version)
+ val = r->rep[cat];
+ else
+ SCLogDebug("version mismatch %u != %u", r->version, version);
+
+ HostRelease(h);
+ return val;
+}
+
+static inline int RepMatch(uint8_t op, uint8_t val1, uint8_t val2)
+{
+ if (op == DETECT_IPREP_OP_GT && val1 > val2) {
+ return 1;
+ } else if (op == DETECT_IPREP_OP_LT && val1 < val2) {
+ return 1;
+ } else if (op == DETECT_IPREP_OP_EQ && val1 == val2) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+int DetectIPRepMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectIPRepData *rd = (const DetectIPRepData *)ctx;
+ if (rd == NULL)
+ return 0;
+
+ uint32_t version = det_ctx->de_ctx->srep_version;
+ uint8_t val = 0;
+
+ SCLogDebug("rd->cmd %u", rd->cmd);
+ switch(rd->cmd) {
+ case DETECT_IPREP_CMD_ANY:
+ val = GetHostRepSrc(p, rd->cat, version);
+ if (val == 0)
+ val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val > 0) {
+ if (RepMatch(rd->op, val, rd->val) == 1)
+ return 1;
+ }
+ val = GetHostRepDst(p, rd->cat, version);
+ if (val == 0)
+ val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val > 0) {
+ return RepMatch(rd->op, val, rd->val);
+ }
+ break;
+
+ case DETECT_IPREP_CMD_SRC:
+ val = GetHostRepSrc(p, rd->cat, version);
+ SCLogDebug("checking src -- val %u (looking for cat %u, val %u)", val, rd->cat, rd->val);
+ if (val == 0)
+ val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val > 0) {
+ return RepMatch(rd->op, val, rd->val);
+ }
+ break;
+
+ case DETECT_IPREP_CMD_DST:
+ SCLogDebug("checking dst");
+ val = GetHostRepDst(p, rd->cat, version);
+ if (val == 0)
+ val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val > 0) {
+ return RepMatch(rd->op, val, rd->val);
+ }
+ break;
+
+ case DETECT_IPREP_CMD_BOTH:
+ val = GetHostRepSrc(p, rd->cat, version);
+ if (val == 0)
+ val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val == 0 || RepMatch(rd->op, val, rd->val) == 0)
+ return 0;
+ val = GetHostRepDst(p, rd->cat, version);
+ if (val == 0)
+ val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+ if (val > 0) {
+ return RepMatch(rd->op, val, rd->val);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int DetectIPRepSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectIPRepData *cd = NULL;
+ SigMatch *sm = NULL;
+ char *cmd_str = NULL, *name = NULL, *op_str = NULL, *value = NULL;
+ uint8_t cmd = 0;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 5) {
+ SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for iprep", rawstr);
+ return -1;
+ }
+
+ const char *str_ptr;
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return -1;
+ }
+ cmd_str = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ name = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ op_str = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 4, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ value = (char *)str_ptr;
+
+ if (strcmp(cmd_str,"any") == 0) {
+ cmd = DETECT_IPREP_CMD_ANY;
+ } else if (strcmp(cmd_str,"both") == 0) {
+ cmd = DETECT_IPREP_CMD_BOTH;
+ } else if (strcmp(cmd_str,"src") == 0) {
+ cmd = DETECT_IPREP_CMD_SRC;
+ } else if (strcmp(cmd_str,"dst") == 0) {
+ cmd = DETECT_IPREP_CMD_DST;
+ } else {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "ERROR: iprep \"%s\" is not supported.", cmd_str);
+ goto error;
+ }
+
+ //SCLogInfo("category %s", name);
+ uint8_t cat = SRepCatGetByShortname(name);
+ if (cat == 0) {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown iprep category \"%s\"", name);
+ goto error;
+ }
+
+ uint8_t op = 0;
+ uint8_t val = 0;
+
+ if (op_str == NULL || strlen(op_str) != 1) {
+ goto error;
+ }
+
+ switch(op_str[0]) {
+ case '<':
+ op = DETECT_IPREP_OP_LT;
+ break;
+ case '>':
+ op = DETECT_IPREP_OP_GT;
+ break;
+ case '=':
+ op = DETECT_IPREP_OP_EQ;
+ break;
+ default:
+ goto error;
+ break;
+ }
+
+ if (value != NULL && strlen(value) > 0) {
+ int ival = atoi(value);
+ if (ival < 0 || ival > 127)
+ goto error;
+ val = (uint8_t)ival;
+ }
+
+ cd = SCMalloc(sizeof(DetectIPRepData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ cd->cmd = cmd;
+ cd->cat = cat;
+ cd->op = op;
+ cd->val = val;
+ SCLogDebug("cmd %u, cat %u, op %u, val %u", cd->cmd, cd->cat, cd->op, cd->val);
+
+ pcre_free_substring(name);
+ name = NULL;
+ pcre_free_substring(cmd_str);
+ cmd_str = NULL;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_IPREP;
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (name != NULL)
+ pcre_free_substring(name);
+ if (cmd_str != NULL)
+ pcre_free_substring(cmd_str);
+ if (cd != NULL)
+ SCFree(cd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectIPRepFree (void *ptr)
+{
+ DetectIPRepData *fd = (DetectIPRepData *)ptr;
+
+ if (fd == NULL)
+ return;
+
+ SCFree(fd);
+}
+
+#ifdef UNITTESTS
+FILE *DetectIPRepGenerateCategoriesDummy()
+{
+ FILE *fd = NULL;
+ const char *buffer = "1,BadHosts,Know bad hosts";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen()");
+
+ return fd;
+}
+
+FILE *DetectIPRepGenerateCategoriesDummy2()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "1,BadHosts,Know bad hosts\n"
+ "2,GoodHosts,Know good hosts\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen()");
+
+ return fd;
+}
+
+FILE *DetectIPRepGenerateNetworksDummy()
+{
+ FILE *fd = NULL;
+ const char *buffer = "10.0.0.0/24,1,20";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen()");
+
+ return fd;
+}
+
+FILE *DetectIPRepGenerateNetworksDummy2()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "0.0.0.0/0,1,10\n"
+ "192.168.0.0/16,2,127";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen()");
+
+ return fd;
+}
+
+static int DetectIPRepTest01(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("10.0.0.1");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:any,BadHosts,>,1; sid:1;rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest02(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("10.0.0.1");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:src,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest03(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->dst.addr_data32[0] = UTHSetIPv4Address("10.0.0.2");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:dst,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest04(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("10.0.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("10.0.0.2");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:both,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest05(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("1.0.0.1");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:any,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest06(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("1.0.0.1");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:any,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest07(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.0.0.2");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:any,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest08(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("1.0.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.0.0.2");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"IPREP High value badhost\"; iprep:any,BadHosts,>,1; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+static int DetectIPRepTest09(void)
+{
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *sig = NULL;
+ FILE *fd = NULL;
+ int result = 0, r = 0;
+ Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ HostInitConfig(HOST_QUIET);
+ memset(&th_v, 0, sizeof(th_v));
+
+ if (de_ctx == NULL || p == NULL)
+ goto end;
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("192.168.0.2");
+ de_ctx->flags |= DE_QUIET;
+
+ SRepInit(de_ctx);
+ SRepResetVersion();
+
+ fd = DetectIPRepGenerateCategoriesDummy2();
+ r = SRepLoadCatFileFromFD(fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ fd = DetectIPRepGenerateNetworksDummy2();
+ r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+ if (r < 0) {
+ goto end;
+ }
+
+ sig = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"test\"; iprep:src,BadHosts,>,9; sid:1; rev:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for IPRep
+ */
+void IPRepRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectIPRepTest01", DetectIPRepTest01, 1);
+ UtRegisterTest("DetectIPRepTest02", DetectIPRepTest02, 1);
+ UtRegisterTest("DetectIPRepTest03", DetectIPRepTest03, 1);
+ UtRegisterTest("DetectIPRepTest04", DetectIPRepTest04, 1);
+ UtRegisterTest("DetectIPRepTest05", DetectIPRepTest05, 0);
+ UtRegisterTest("DetectIPRepTest06", DetectIPRepTest06, 0);
+ UtRegisterTest("DetectIPRepTest07", DetectIPRepTest07, 0);
+ UtRegisterTest("DetectIPRepTest08", DetectIPRepTest08, 0);
+ UtRegisterTest("DetectIPRepTest09", DetectIPRepTest09, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-iprep.h b/framework/src/suricata/src/detect-iprep.h
new file mode 100644
index 00000000..129851b9
--- /dev/null
+++ b/framework/src/suricata/src/detect-iprep.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_IPREP_H__
+#define __DETECT_IPREP_H__
+
+#define DETECT_IPREP_CMD_ANY 0
+#define DETECT_IPREP_CMD_BOTH 1
+#define DETECT_IPREP_CMD_SRC 2
+#define DETECT_IPREP_CMD_DST 3
+
+#define DETECT_IPREP_OP_LT 0
+#define DETECT_IPREP_OP_GT 1
+#define DETECT_IPREP_OP_EQ 2
+
+typedef struct DetectIPRepData_ {
+ uint8_t cmd;
+ int8_t cat;
+ int8_t op;
+ uint8_t val;
+} DetectIPRepData;
+
+/* prototypes */
+void DetectIPRepRegister (void);
+
+#endif /* __DETECT_IPREP_H__ */
diff --git a/framework/src/suricata/src/detect-isdataat.c b/framework/src/suricata/src/detect-isdataat.c
new file mode 100644
index 00000000..e6a16f40
--- /dev/null
+++ b/framework/src/suricata/src/detect-isdataat.c
@@ -0,0 +1,1249 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements isdataat keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "app-layer.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect-isdataat.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+
+#include "flow.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-byte.h"
+#include "detect-pcre.h"
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+
+/**
+ * \brief Regex for parsing our isdataat options
+ */
+#define PARSE_REGEX "^\\s*!?([^\\s,]+)\\s*(,\\s*relative)?\\s*(,\\s*rawbytes\\s*)?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectIsdataatMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+int DetectIsdataatSetup (DetectEngineCtx *, Signature *, char *);
+void DetectIsdataatRegisterTests(void);
+void DetectIsdataatFree(void *);
+
+/**
+ * \brief Registration function for isdataat: keyword
+ */
+void DetectIsdataatRegister(void)
+{
+ sigmatch_table[DETECT_ISDATAAT].name = "isdataat";
+ sigmatch_table[DETECT_ISDATAAT].desc = "check if there is still data at a specific part of the payload";
+ sigmatch_table[DETECT_ISDATAAT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Isadataat";
+ sigmatch_table[DETECT_ISDATAAT].Match = DetectIsdataatMatch;
+ sigmatch_table[DETECT_ISDATAAT].Setup = DetectIsdataatSetup;
+ sigmatch_table[DETECT_ISDATAAT].Free = DetectIsdataatFree;
+ sigmatch_table[DETECT_ISDATAAT].RegisterTests = DetectIsdataatRegisterTests;
+
+ sigmatch_table[DETECT_ISDATAAT].flags |= SIGMATCH_PAYLOAD;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \brief This function is used to match isdataat on a packet
+ * \todo We need to add support for rawbytes
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectIsdataatData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectIsdataatMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectIsdataatData *idad = (const DetectIsdataatData *)ctx;
+
+ SCLogDebug("payload_len: %u , dataat? %u ; relative? %u...", p->payload_len,idad->dataat,idad->flags &ISDATAAT_RELATIVE);
+
+ /* Relative to the last matched content is not performed here, returning match (content should take care of this)*/
+ if (idad->flags & ISDATAAT_RELATIVE)
+ return 1;
+
+ /* its not relative and we have more data in the packet than the offset of isdataat */
+ if (p->payload_len >= idad->dataat) {
+ SCLogDebug("matched with payload_len: %u , dataat? %u ; relative? %u...", p->payload_len,idad->dataat,idad->flags &ISDATAAT_RELATIVE);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse isdataat options passed via isdataat: keyword
+ *
+ * \param isdataatstr Pointer to the user provided isdataat options
+ *
+ * \retval idad pointer to DetectIsdataatData on success
+ * \retval NULL on failure
+ */
+DetectIsdataatData *DetectIsdataatParse (char *isdataatstr, char **offset)
+{
+ DetectIsdataatData *idad = NULL;
+ char *args[3] = {NULL,NULL,NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ int i=0;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, isdataatstr, strlen(isdataatstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, isdataatstr);
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr;
+ res = pcre_get_substring((char *)isdataatstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[0] = (char *)str_ptr;
+
+
+ if (ret > 2) {
+ res = pcre_get_substring((char *)isdataatstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[1] = (char *)str_ptr;
+ }
+ if (ret > 3) {
+ res = pcre_get_substring((char *)isdataatstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[2] = (char *)str_ptr;
+ }
+
+ idad = SCMalloc(sizeof(DetectIsdataatData));
+ if (unlikely(idad == NULL))
+ goto error;
+
+ idad->flags = 0;
+ idad->dataat = 0;
+
+ if (args[0][0] != '-' && isalpha((unsigned char)args[0][0])) {
+ if (offset == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "isdataat supplied with "
+ "var name for offset. \"offset\" argument supplied to "
+ "this function has to be non-NULL");
+ goto error;
+ }
+ *offset = SCStrdup(args[0]);
+ if (*offset == NULL)
+ goto error;
+ } else {
+ if (ByteExtractStringUint16(&idad->dataat, 10,
+ strlen(args[0]), args[0]) < 0 ) {
+ SCLogError(SC_ERR_INVALID_VALUE, "isdataat out of range");
+ SCFree(idad);
+ idad = NULL;
+ goto error;
+ }
+ }
+
+ if (args[1] !=NULL) {
+ idad->flags |= ISDATAAT_RELATIVE;
+
+ if(args[2] !=NULL)
+ idad->flags |= ISDATAAT_RAWBYTES;
+ }
+
+ if (isdataatstr[0] == '!') {
+ idad->flags |= ISDATAAT_NEGATED;
+ }
+
+ for (i = 0; i < (ret -1); i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+
+ return idad;
+
+ }
+
+error:
+ for (i = 0; i < (ret -1) && i < 3; i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+
+ if (idad != NULL)
+ DetectIsdataatFree(idad);
+ return NULL;
+
+}
+
+/**
+ * \brief This function is used to add the parsed isdataatdata into the current
+ * signature.
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param isdataatstr pointer to the user provided isdataat options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectIsdataatSetup (DetectEngineCtx *de_ctx, Signature *s, char *isdataatstr)
+{
+ SigMatch *sm = NULL;
+ SigMatch *prev_pm = NULL;
+ DetectIsdataatData *idad = NULL;
+ char *offset = NULL;
+ int ret = -1;
+
+ idad = DetectIsdataatParse(isdataatstr, &offset);
+ if (idad == NULL)
+ return -1;
+
+ int sm_list;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA) {
+ AppLayerHtpEnableResponseBodyCallback();
+ s->alproto = ALPROTO_HTTP;
+ }
+ sm_list = s->list;
+ s->flags |= SIG_FLAG_APPLAYER;
+ if (idad->flags & ISDATAAT_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, s->sm_lists_tail[sm_list],
+ DETECT_PCRE, s->sm_lists_tail[sm_list]);
+ }
+ } else if (idad->flags & ISDATAAT_RELATIVE) {
+ prev_pm = SigMatchGetLastSMFromLists(s, 168,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ if (prev_pm == NULL)
+ sm_list = DETECT_SM_LIST_PMATCH;
+ else {
+ sm_list = SigMatchListSMBelongsTo(s, prev_pm);
+ if (sm_list < 0)
+ goto end;
+ }
+ } else {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ }
+
+ if (offset != NULL) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(offset, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unknown byte_extract var "
+ "seen in isdataat - %s\n", offset);
+ goto end;
+ }
+ idad->dataat = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ idad->flags |= ISDATAAT_OFFSET_BE;
+ SCFree(offset);
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto end;
+ sm->type = DETECT_ISDATAAT;
+ sm->ctx = (SigMatchCtx *)idad;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ if (!(idad->flags & ISDATAAT_RELATIVE)) {
+ ret = 0;
+ goto end;
+ }
+
+ if (prev_pm == NULL) {
+ ret = 0;
+ goto end;
+ }
+
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ ret = 0;
+
+end:
+ if (ret != 0)
+ DetectIsdataatFree(idad);
+ return ret;
+}
+
+/**
+ * \brief this function will free memory associated with DetectIsdataatData
+ *
+ * \param idad pointer to DetectIsdataatData
+ */
+void DetectIsdataatFree(void *ptr)
+{
+ DetectIsdataatData *idad = (DetectIsdataatData *)ptr;
+ SCFree(idad);
+}
+
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectIsdataatTestParse01 is a test to make sure that we return a correct IsdataatData structure
+ * when given valid isdataat opt
+ */
+int DetectIsdataatTestParse01 (void)
+{
+ int result = 0;
+ DetectIsdataatData *idad = NULL;
+ idad = DetectIsdataatParse("30 ", NULL);
+ if (idad != NULL) {
+ DetectIsdataatFree(idad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectIsdataatTestParse02 is a test to make sure that we return a correct IsdataatData structure
+ * when given valid isdataat opt
+ */
+int DetectIsdataatTestParse02 (void)
+{
+ int result = 0;
+ DetectIsdataatData *idad = NULL;
+ idad = DetectIsdataatParse("30 , relative", NULL);
+ if (idad != NULL && idad->flags & ISDATAAT_RELATIVE && !(idad->flags & ISDATAAT_RAWBYTES)) {
+ DetectIsdataatFree(idad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectIsdataatTestParse03 is a test to make sure that we return a correct IsdataatData structure
+ * when given valid isdataat opt
+ */
+int DetectIsdataatTestParse03 (void)
+{
+ int result = 0;
+ DetectIsdataatData *idad = NULL;
+ idad = DetectIsdataatParse("30,relative, rawbytes ", NULL);
+ if (idad != NULL && idad->flags & ISDATAAT_RELATIVE && idad->flags & ISDATAAT_RAWBYTES) {
+ DetectIsdataatFree(idad);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test Test isdataat option for dce sig.
+ */
+int DetectIsdataatTestParse04(void)
+{
+ Signature *s = SigAlloc();
+ int result = 1;
+
+ s->alproto = ALPROTO_DCERPC;
+
+ result &= (DetectIsdataatSetup(NULL, s, "30") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+ SigFree(s);
+
+ s = SigAlloc();
+ s->alproto = ALPROTO_DCERPC;
+ /* failure since we have no preceding content/pcre/bytejump */
+ result &= (DetectIsdataatSetup(NULL, s, "30,relative") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ SigFree(s);
+
+ return result;
+}
+
+/**
+ * \test Test isdataat option for dce sig.
+ */
+int DetectIsdataatTestParse05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "isdataat:4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "isdataat:4,relative; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "isdataat:4,relative,rawbytes; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ !(data->flags & ISDATAAT_RAWBYTES) ) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; isdataat:4,relative,rawbytes; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse06(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse07(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "uricontent:\"one\"; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse08(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; http_uri; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse09(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; http_client_body; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse10(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; http_header; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse11(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "flow:to_server; content:\"one\"; http_raw_header; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; http_method; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+int DetectIsdataatTestParse13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; http_cookie; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH] == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->type == DETECT_ISDATAAT);
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int DetectIsdataatTestParse14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing file_data and isdataat\"; "
+ "file_data; content:\"one\"; "
+ "isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("server body list empty: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_ISDATAAT) {
+ printf("last server body sm not isdataat: ");
+ goto end;
+ }
+
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test file_data with isdataat relative to it
+ */
+static int DetectIsdataatTestParse15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing file_data and isdataat\"; "
+ "file_data; isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("server body list empty: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_ISDATAAT) {
+ printf("last server body sm not isdataat: ");
+ goto end;
+ }
+
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test dns_query with isdataat relative to it
+ */
+static int DetectIsdataatTestParse16(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectIsdataatData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dns_query and isdataat\"; "
+ "dns_query; isdataat:!4,relative; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH] == NULL) {
+ printf("dns_query list empty: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH]->type != DETECT_ISDATAAT) {
+ printf("last dns_query body sm not isdataat: ");
+ goto end;
+ }
+
+ data = (DetectIsdataatData *)s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH]->ctx;
+ if ( !(data->flags & ISDATAAT_RELATIVE) ||
+ (data->flags & ISDATAAT_RAWBYTES) ||
+ !(data->flags & ISDATAAT_NEGATED) ) {
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test DetectIsdataatTestPacket01 is a test to check matches of
+ * isdataat, and isdataat relative
+ */
+int DetectIsdataatTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_UDP);
+ p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[5];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing window 1\"; isdataat:6; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing window 2\"; content:\"all\"; isdataat:1, relative; isdataat:6; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing window 3\"; isdataat:8; sid:3;)";
+ sigs[3]= "alert ip any any -> any any (msg:\"Testing window 4\"; content:\"Hi\"; isdataat:5, relative; sid:4;)";
+ sigs[4]= "alert ip any any -> any any (msg:\"Testing window 4\"; content:\"Hi\"; isdataat:6, relative; sid:5;)";
+
+ uint32_t sid[5] = {1, 2, 3, 4, 5};
+
+ uint32_t results[3][5] = {
+ /* packet 0 match sid 1 but should not match sid 2 */
+ {1, 1, 0, 1, 0},
+ /* packet 1 should not match */
+ {1, 1, 0, 1, 0},
+ /* packet 2 should not match */
+ {1, 1, 0, 1, 0} };
+
+ result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 5);
+
+ UTHFreePackets(p, 3);
+end:
+ return result;
+}
+
+/**
+ * \test DetectIsdataatTestPacket02 is a test to check matches of
+ * isdataat, and isdataat relative works if the previous keyword is pcre
+ * (bug 144)
+ */
+int DetectIsdataatTestPacket02 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with"
+ " isdataat + relative\"; pcre:\"/A(ll|pp)WorkAndNoPlayMakesWillA"
+ "DullBoy/\"; isdataat:96,relative; sid:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+/**
+ * \test DetectIsdataatTestPacket03 is a test to check matches of
+ * isdataat, and isdataat relative works if the previous keyword is byte_jump
+ * (bug 146)
+ */
+int DetectIsdataatTestPacket03 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"byte_jump match = 0 "
+ "with distance content HTTP/1. relative against HTTP/1.0\"; byte_jump:1,"
+ "46,string,dec; isdataat:87,relative; sid:109; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+#endif
+
+/**
+ * \brief this function registers unit tests for DetectIsdataat
+ */
+void DetectIsdataatRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectIsdataatTestParse01", DetectIsdataatTestParse01, 1);
+ UtRegisterTest("DetectIsdataatTestParse02", DetectIsdataatTestParse02, 1);
+ UtRegisterTest("DetectIsdataatTestParse03", DetectIsdataatTestParse03, 1);
+ UtRegisterTest("DetectIsdataatTestParse04", DetectIsdataatTestParse04, 1);
+ UtRegisterTest("DetectIsdataatTestParse05", DetectIsdataatTestParse05, 1);
+ UtRegisterTest("DetectIsdataatTestParse06", DetectIsdataatTestParse06, 1);
+ UtRegisterTest("DetectIsdataatTestParse07", DetectIsdataatTestParse07, 1);
+ UtRegisterTest("DetectIsdataatTestParse08", DetectIsdataatTestParse08, 1);
+ UtRegisterTest("DetectIsdataatTestParse09", DetectIsdataatTestParse09, 1);
+ UtRegisterTest("DetectIsdataatTestParse10", DetectIsdataatTestParse10, 1);
+ UtRegisterTest("DetectIsdataatTestParse11", DetectIsdataatTestParse11, 1);
+ UtRegisterTest("DetectIsdataatTestParse12", DetectIsdataatTestParse12, 1);
+ UtRegisterTest("DetectIsdataatTestParse13", DetectIsdataatTestParse13, 1);
+ UtRegisterTest("DetectIsdataatTestParse14", DetectIsdataatTestParse14, 1);
+ UtRegisterTest("DetectIsdataatTestParse15", DetectIsdataatTestParse15, 1);
+ UtRegisterTest("DetectIsdataatTestParse16", DetectIsdataatTestParse16, 1);
+
+ UtRegisterTest("DetectIsdataatTestPacket01", DetectIsdataatTestPacket01, 1);
+ UtRegisterTest("DetectIsdataatTestPacket02", DetectIsdataatTestPacket02, 1);
+ UtRegisterTest("DetectIsdataatTestPacket03", DetectIsdataatTestPacket03, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/detect-isdataat.h b/framework/src/suricata/src/detect-isdataat.h
new file mode 100644
index 00000000..f264f36d
--- /dev/null
+++ b/framework/src/suricata/src/detect-isdataat.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_ISDATAAT_H__
+#define __DETECT_ISDATAAT_H__
+
+#define ISDATAAT_RELATIVE 0x01
+#define ISDATAAT_RAWBYTES 0x02
+#define ISDATAAT_NEGATED 0x04
+#define ISDATAAT_OFFSET_BE 0x08
+
+#define ISDATAAT_MIN 0
+#define ISDATAAT_MAX 65535
+
+typedef struct DetectIsdataatData_ {
+ uint16_t dataat; /* data offset to match */
+ uint8_t flags; /* isdataat options*/
+} DetectIsdataatData;
+
+/* prototypes */
+void DetectIsdataatRegister (void);
+
+#endif /* __DETECT_ISDATAAT_H__ */
+
diff --git a/framework/src/suricata/src/detect-itype.c b/framework/src/suricata/src/detect-itype.c
new file mode 100644
index 00000000..1be07647
--- /dev/null
+++ b/framework/src/suricata/src/detect-itype.c
@@ -0,0 +1,529 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ *
+ * Implements itype keyword support
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-itype.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/**
+ *\brief Regex for parsing our itype options
+ */
+#define PARSE_REGEX "^\\s*(<|>)?\\s*([0-9]+)\\s*(?:<>\\s*([0-9]+))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectITypeMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectITypeSetup(DetectEngineCtx *, Signature *, char *);
+void DetectITypeRegisterTests(void);
+void DetectITypeFree(void *);
+
+
+/**
+ * \brief Registration function for itype: keyword
+ */
+void DetectITypeRegister (void)
+{
+ sigmatch_table[DETECT_ITYPE].name = "itype";
+ sigmatch_table[DETECT_ITYPE].desc = "matching on a specific ICMP type";
+ sigmatch_table[DETECT_ITYPE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#itype";
+ sigmatch_table[DETECT_ITYPE].Match = DetectITypeMatch;
+ sigmatch_table[DETECT_ITYPE].Setup = DetectITypeSetup;
+ sigmatch_table[DETECT_ITYPE].Free = DetectITypeFree;
+ sigmatch_table[DETECT_ITYPE].RegisterTests = DetectITypeRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief This function is used to match itype rule option set on a packet with those passed via itype:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectITypeData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectITypeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ uint8_t pitype;
+ const DetectITypeData *itd = (const DetectITypeData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_ICMPV4(p)) {
+ pitype = ICMPV4_GET_TYPE(p);
+ } else if (PKT_IS_ICMPV6(p)) {
+ pitype = ICMPV6_GET_TYPE(p);
+ } else {
+ /* Packet not ICMPv4 nor ICMPv6 */
+ return ret;
+ }
+
+ switch(itd->mode) {
+ case DETECT_ITYPE_EQ:
+ ret = (pitype == itd->type1) ? 1 : 0;
+ break;
+ case DETECT_ITYPE_LT:
+ ret = (pitype < itd->type1) ? 1 : 0;
+ break;
+ case DETECT_ITYPE_GT:
+ ret = (pitype > itd->type1) ? 1 : 0;
+ break;
+ case DETECT_ITYPE_RN:
+ ret = (pitype > itd->type1 && pitype < itd->type2) ? 1 : 0;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse itype options passed via itype: keyword
+ *
+ * \param itypestr Pointer to the user provided itype options
+ *
+ * \retval itd pointer to DetectITypeData on success
+ * \retval NULL on failure
+ */
+DetectITypeData *DetectITypeParse(char *itypestr)
+{
+ DetectITypeData *itd = NULL;
+ char *args[3] = {NULL, NULL, NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, itypestr, strlen(itypestr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, itypestr);
+ goto error;
+ }
+
+ int i;
+ const char *str_ptr;
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *)itypestr, ov, MAX_SUBSTRINGS, i, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[i-1] = (char *)str_ptr;
+ }
+
+ itd = SCMalloc(sizeof(DetectITypeData));
+ if (unlikely(itd == NULL))
+ goto error;
+ itd->type1 = 0;
+ itd->type2 = 0;
+ itd->mode = 0;
+
+ /* we have either "<" or ">" */
+ if (args[0] != NULL && strlen(args[0]) != 0) {
+ /* we have a third part ("<> y"), therefore it's invalid */
+ if (args[2] != NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "itype: invalid value");
+ goto error;
+ }
+ /* we have only a comparison ("<", ">") */
+ if (ByteExtractStringUint8(&itd->type1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp type %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ if ((strcmp(args[0], ">")) == 0) itd->mode = DETECT_ITYPE_GT;
+ else itd->mode = DETECT_ITYPE_LT;
+ } else { /* no "<", ">" */
+ /* we have a range ("<>") */
+ if (args[2] != NULL) {
+ itd->mode = (uint8_t) DETECT_ITYPE_RN;
+ if (ByteExtractStringUint8(&itd->type1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp type %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ if (ByteExtractStringUint8(&itd->type2, 10, 0, args[2]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp type %s is not "
+ "valid", args[2]);
+ goto error;
+ }
+ /* we check that the first given value in the range is less than
+ the second, otherwise we swap them */
+ if (itd->type1 > itd->type2) {
+ uint8_t temp = itd->type1;
+ itd->type1 = itd->type2;
+ itd->type2 = temp;
+ }
+ } else { /* we have an equality */
+ itd->mode = DETECT_ITYPE_EQ;
+ if (ByteExtractStringUint8(&itd->type1, 10, 0, args[1]) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified icmp type %s is not "
+ "valid", args[1]);
+ goto error;
+ }
+ }
+ }
+
+ for (i = 0; i < (ret-1); i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ return itd;
+
+error:
+ for (i = 0; i < (ret-1) && i < 3; i++) {
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (itd != NULL)
+ DetectITypeFree(itd);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed itype data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param itypestr pointer to the user provided itype options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectITypeSetup(DetectEngineCtx *de_ctx, Signature *s, char *itypestr)
+{
+
+ DetectITypeData *itd = NULL;
+ SigMatch *sm = NULL;
+
+ itd = DetectITypeParse(itypestr);
+ if (itd == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) goto error;
+
+ sm->type = DETECT_ITYPE;
+ sm->ctx = (SigMatchCtx *)itd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (itd != NULL) DetectITypeFree(itd);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectITypeData
+ *
+ * \param ptr pointer to DetectITypeData
+ */
+void DetectITypeFree(void *ptr)
+{
+ DetectITypeData *itd = (DetectITypeData *)ptr;
+ SCFree(itd);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectITypeParseTest01 is a test for setting a valid itype value
+ */
+int DetectITypeParseTest01(void)
+{
+ DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse("8");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->mode == DETECT_ITYPE_EQ)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest02 is a test for setting a valid itype value
+ * with ">" operator
+ */
+int DetectITypeParseTest02(void)
+{
+DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse(">8");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->mode == DETECT_ITYPE_GT)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest03 is a test for setting a valid itype value
+ * with "<" operator
+ */
+int DetectITypeParseTest03(void)
+{
+ DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse("<8");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->mode == DETECT_ITYPE_LT)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest04 is a test for setting a valid itype value
+ * with "<>" operator
+ */
+int DetectITypeParseTest04(void)
+{
+DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse("8<>20");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->type2 == 20 && itd->mode == DETECT_ITYPE_RN)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest05 is a test for setting a valid itype value
+ * with spaces all around
+ */
+int DetectITypeParseTest05(void)
+{
+DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse(" 8 ");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->mode == DETECT_ITYPE_EQ)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest06 is a test for setting a valid itype value
+ * with ">" operator and spaces all around
+ */
+int DetectITypeParseTest06(void)
+{
+DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse(" > 8 ");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->mode == DETECT_ITYPE_GT)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest07 is a test for setting a valid itype value
+ * with "<>" operator and spaces all around
+ */
+int DetectITypeParseTest07(void)
+{
+DetectITypeData *itd = NULL;
+ int result = 0;
+ itd = DetectITypeParse(" 8 <> 20 ");
+ if (itd != NULL) {
+ if (itd->type1 == 8 && itd->type2 == 20 && itd->mode == DETECT_ITYPE_RN)
+ result = 1;
+ DetectITypeFree(itd);
+ }
+ return result;
+}
+
+/**
+ * \test DetectITypeParseTest08 is a test for setting an invalid itype value
+ */
+int DetectITypeParseTest08(void)
+{
+ DetectITypeData *itd = NULL;
+ itd = DetectITypeParse("> 8 <> 20");
+ if (itd == NULL)
+ return 1;
+ DetectITypeFree(itd);
+ return 0;
+}
+
+/**
+ * \test DetectITypeMatchTest01 is a test for checking the working of itype
+ * keyword by creating 5 rules and matching a crafted packet against
+ * them. 4 out of 5 rules shall trigger.
+ */
+int DetectITypeMatchTest01(void)
+{
+
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
+ p->icmpv4h->type = 10;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert icmp any any -> any any (itype:10; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (itype:<15; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (itype:>20; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (itype:8<>20; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert icmp any any -> any any (itype:20<>8; sid:5;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ SCLogDebug("sid 1 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ SCLogDebug("sid 2 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3)) {
+ SCLogDebug("sid 3 alerted, but should not have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ SCLogDebug("sid 4 did not alert, but should have");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 5) == 0) {
+ SCLogDebug("sid 5 did not alert, but should have");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+}
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectIType
+ */
+void DetectITypeRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectITypeParseTest01", DetectITypeParseTest01, 1);
+ UtRegisterTest("DetectITypeParseTest02", DetectITypeParseTest02, 1);
+ UtRegisterTest("DetectITypeParseTest03", DetectITypeParseTest03, 1);
+ UtRegisterTest("DetectITypeParseTest04", DetectITypeParseTest04, 1);
+ UtRegisterTest("DetectITypeParseTest05", DetectITypeParseTest05, 1);
+ UtRegisterTest("DetectITypeParseTest06", DetectITypeParseTest06, 1);
+ UtRegisterTest("DetectITypeParseTest07", DetectITypeParseTest07, 1);
+ UtRegisterTest("DetectITypeParseTest08", DetectITypeParseTest08, 1);
+ UtRegisterTest("DetectITypeMatchTest01", DetectITypeMatchTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-itype.h b/framework/src/suricata/src/detect-itype.h
new file mode 100644
index 00000000..168e0e7b
--- /dev/null
+++ b/framework/src/suricata/src/detect-itype.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias <iglesiasg@gmail.com>
+ */
+
+#ifndef __DETECT_ITYPE_H__
+#define __DETECT_ITYPE_H__
+
+#define DETECT_ITYPE_EQ 0 /**< "equal" operator */
+#define DETECT_ITYPE_LT 1 /**< "less than" operator */
+#define DETECT_ITYPE_GT 2 /**< "greater than" operator */
+#define DETECT_ITYPE_RN 3 /**< "range" operator */
+
+typedef struct DetectITypeData_ {
+ uint8_t type1;
+ uint8_t type2;
+
+ uint8_t mode;
+} DetectITypeData;
+
+/* prototypes */
+void DetectITypeRegister(void);
+
+#endif /* __DETECT_ITYPE_H__ */
diff --git a/framework/src/suricata/src/detect-l3proto.c b/framework/src/suricata/src/detect-l3proto.c
new file mode 100644
index 00000000..c67b47c8
--- /dev/null
+++ b/framework/src/suricata/src/detect-l3proto.c
@@ -0,0 +1,391 @@
+/* Copyright (C) 2012-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ *
+ * Implements the l3_proto keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-ipproto.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "util-debug.h"
+
+static int DetectL3ProtoSetup(DetectEngineCtx *, Signature *, char *);
+
+void DetectL3protoRegisterTests(void);
+
+void DetectL3ProtoRegister(void)
+{
+ sigmatch_table[DETECT_L3PROTO].name = "l3_proto";
+ sigmatch_table[DETECT_L3PROTO].Match = NULL;
+ sigmatch_table[DETECT_L3PROTO].Setup = DetectL3ProtoSetup;
+ sigmatch_table[DETECT_L3PROTO].Free = NULL;
+ sigmatch_table[DETECT_L3PROTO].RegisterTests = DetectL3protoRegisterTests;
+
+ return;
+}
+/**
+ * \internal
+ * \brief Setup l3_proto keyword.
+ *
+ * \param de_ctx Detection engine context
+ * \param s Signature
+ * \param optstr Options string
+ *
+ * \return Non-zero on error
+ */
+static int DetectL3ProtoSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ char *str = optstr;
+ char dubbed = 0;
+
+ /* strip "'s */
+ if (optstr[0] == '\"' && optstr[strlen(optstr) - 1] == '\"') {
+ str = SCStrdup(optstr + 1);
+ if (unlikely(str == NULL))
+ goto error;
+ str[strlen(optstr) - 2] = '\0';
+ dubbed = 1;
+ }
+
+ /* reset possible any value */
+ if (s->proto.flags & DETECT_PROTO_ANY) {
+ s->proto.flags &= ~DETECT_PROTO_ANY;
+ }
+
+ /* authorized value, ip, any, ip4, ipv4, ip6, ipv6 */
+ if (strcasecmp(str,"ipv4") == 0 ||
+ strcasecmp(str,"ip4") == 0 ) {
+ if (s->proto.flags & DETECT_PROTO_IPV6) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Conflicting l3 proto specified");
+ goto error;
+ }
+ s->proto.flags |= DETECT_PROTO_IPV4;
+ SCLogDebug("IPv4 protocol detected");
+ } else if (strcasecmp(str,"ipv6") == 0 ||
+ strcasecmp(str,"ip6") == 0 ) {
+ if (s->proto.flags & DETECT_PROTO_IPV6) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Conflicting l3 proto specified");
+ goto error;
+ }
+ s->proto.flags |= DETECT_PROTO_IPV6;
+ SCLogDebug("IPv6 protocol detected");
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid l3 proto: \"%s\"", str);
+ goto error;
+ }
+
+ if (dubbed)
+ SCFree(str);
+ return 0;
+error:
+ if (dubbed)
+ SCFree(str);
+ return -1;
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \test DetectL3protoTestSig01 is a test for checking the working of ttl keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ */
+
+static int DetectL3protoTestSig1(void)
+{
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ IPV4Hdr ip4h;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->ip4h = &ip4h;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv4\"; l3_proto:ipv4; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv6\"; l3_proto:ipv6; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ip4\"; l3_proto:ip4; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ip6\"; l3_proto:ip6; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3) == 0) {
+ printf("sid 3 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4)) {
+ printf("sid 4 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectL3protoTestSig02 is a test for checking the working of l3proto keyword
+ * by setting up the signature and later testing its working by matching
+ * the received IPv6 packet against the sig.
+ */
+
+static int DetectL3protoTestSig2(void)
+{
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ IPV6Hdr ip6h;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p->src.family = AF_INET6;
+ p->dst.family = AF_INET6;
+ p->proto = IPPROTO_TCP;
+ p->ip6h = &ip6h;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv4\"; l3_proto:ipv4; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv6\"; l3_proto:ipv6; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ip4\"; l3_proto:ip4; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ip6\"; l3_proto:ip6; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ printf("sid 2 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3)) {
+ printf("sid 3 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ printf("sid 4 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectL3protoTestSig03 is a test for checking the working of l3proto keyword
+ * in conjonction with ip_proto keyword.
+ */
+
+static int DetectL3protoTestSig3(void)
+{
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ IPV6Hdr ip6h;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p->src.family = AF_INET6;
+ p->dst.family = AF_INET6;
+ p->proto = IPPROTO_TCP;
+ p->ip6h = &ip6h;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv4 and ip_proto udp\"; l3_proto:ipv4; ip_proto:17; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv6 and ip_proto udp\"; l3_proto:ipv6; ip_proto:17; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ip4 and ip_proto tcp\"; l3_proto:ipv4; ip_proto:6; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"l3proto ipv6 and ip_proto tcp\"; l3_proto:ipv6; ip_proto:6; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3)) {
+ printf("sid 3 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ printf("sid 4 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p);
+ return result;
+}
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectL3proto
+ */
+void DetectL3protoRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectL3protoTestSig1", DetectL3protoTestSig1, 1);
+ UtRegisterTest("DetectL3protoTestSig2", DetectL3protoTestSig2, 1);
+ UtRegisterTest("DetectL3protoTestSig3", DetectL3protoTestSig3, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-l3proto.h b/framework/src/suricata/src/detect-l3proto.h
new file mode 100644
index 00000000..447cd95d
--- /dev/null
+++ b/framework/src/suricata/src/detect-l3proto.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ */
+
+#ifndef __DETECT_L3PROTO_H__
+#define __DETECT_L3PROTO_H__
+
+/**
+ * \brief Registration function for ip_proto keyword.
+ */
+void DetectL3ProtoRegister (void);
+
+#endif /* __DETECT_L3PROTO_H__ */
diff --git a/framework/src/suricata/src/detect-lua-extensions.c b/framework/src/suricata/src/detect-lua-extensions.c
new file mode 100644
index 00000000..020c886d
--- /dev/null
+++ b/framework/src/suricata/src/detect-lua-extensions.c
@@ -0,0 +1,625 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Functions to expose to the lua scripts.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-flowvar.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+
+#include "stream-tcp.h"
+
+#include "detect-lua.h"
+
+#include "queue.h"
+#include "util-cpu.h"
+
+#include "app-layer-parser.h"
+
+#ifdef HAVE_LUA
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+#include "util-lua-http.h"
+#include "util-lua-dns.h"
+#include "util-lua-tls.h"
+
+static const char luaext_key_ld[] = "suricata:luajitdata";
+static const char luaext_key_det_ctx[] = "suricata:det_ctx";
+
+static int LuaGetFlowvar(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ FlowVar *fv;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowvar idx */
+ if (!lua_isnumber(luastate, 1)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowvar id out of range");
+ return 2;
+ }
+ idx = ld->flowvar[id];
+ if (idx == 0) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowvar id uninitialized");
+ return 2;
+ }
+
+ /* lookup var */
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_RDLOCK(f);
+
+ fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow var");
+ return 2;
+ }
+
+ LuaPushStringBuffer(luastate, (const uint8_t *)fv->data.fv_str.value,
+ (size_t)fv->data.fv_str.value_len);
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ return 1;
+
+}
+
+int LuaSetFlowvar(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ const char *str;
+ int len;
+ uint8_t *buffer;
+ DetectEngineThreadCtx *det_ctx;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need det_ctx */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_det_ctx);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ det_ctx = lua_touserdata(luastate, -1);
+ SCLogDebug("det_ctx %p", det_ctx);
+ if (det_ctx == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no det_ctx");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowvar idx */
+ if (!lua_isnumber(luastate, 1)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowvar id out of range");
+ return 2;
+ }
+
+ if (!lua_isstring(luastate, 2)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "2nd arg not a string");
+ return 2;
+ }
+ str = lua_tostring(luastate, 2);
+ if (str == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "null string");
+ return 2;
+ }
+
+ if (!lua_isnumber(luastate, 3)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "3rd arg not a number");
+ return 2;
+ }
+ len = lua_tonumber(luastate, 3);
+ if (len < 0 || len > 0xffff) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "len out of range: max 64k");
+ return 2;
+ }
+
+ idx = ld->flowvar[id];
+ if (idx == 0) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowvar id uninitialized");
+ return 2;
+ }
+
+ buffer = SCMalloc(len+1);
+ if (unlikely(buffer == NULL)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "out of memory");
+ return 2;
+ }
+ memcpy(buffer, str, len);
+ buffer[len] = '\0';
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FlowVarAddStr(f, idx, buffer, len);
+ else
+ FlowVarAddStrNoLock(f, idx, buffer, len);
+
+ //SCLogInfo("stored:");
+ //PrintRawDataFp(stdout,buffer,len);
+ return 0;
+}
+
+static int LuaGetFlowint(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ FlowVar *fv;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+ uint32_t number;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowint idx */
+ if (!lua_isnumber(luastate, 1)) {
+ SCLogDebug("1st arg not a number");
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) {
+ SCLogDebug("id %d", id);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id out of range");
+ return 2;
+ }
+ idx = ld->flowint[id];
+ if (idx == 0) {
+ SCLogDebug("idx %u", idx);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id uninitialized");
+ return 2;
+ }
+
+ /* lookup var */
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_RDLOCK(f);
+
+ fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ SCLogDebug("fv NULL");
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow var");
+ return 2;
+ }
+ number = fv->data.fv_int.value;
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ /* return value through luastate, as a luanumber */
+ lua_pushnumber(luastate, (lua_Number)number);
+ SCLogDebug("retrieved flow:%p idx:%u value:%u", f, idx, number);
+
+ return 1;
+
+}
+
+int LuaSetFlowint(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ DetectEngineThreadCtx *det_ctx;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+ uint32_t number;
+ lua_Number luanumber;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need det_ctx */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_det_ctx);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ det_ctx = lua_touserdata(luastate, -1);
+ SCLogDebug("det_ctx %p", det_ctx);
+ if (det_ctx == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no det_ctx");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowint idx */
+ if (!lua_isnumber(luastate, 1)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id out of range");
+ return 2;
+ }
+
+ if (!lua_isnumber(luastate, 2)) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "2nd arg not a number");
+ return 2;
+ }
+ luanumber = lua_tonumber(luastate, 2);
+ if (luanumber < 0 || id > (double)UINT_MAX) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "value out of range, value must be unsigned 32bit int");
+ return 2;
+ }
+ number = (uint32_t)luanumber;
+
+ idx = ld->flowint[id];
+ if (idx == 0) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id uninitialized");
+ return 2;
+ }
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FlowVarAddInt(f, idx, number);
+ else
+ FlowVarAddIntNoLock(f, idx, number);
+
+ SCLogDebug("stored flow:%p idx:%u value:%u", f, idx, number);
+ return 0;
+}
+
+static int LuaIncrFlowint(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ FlowVar *fv;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+ uint32_t number;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowint idx */
+ if (!lua_isnumber(luastate, 1)) {
+ SCLogDebug("1st arg not a number");
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) {
+ SCLogDebug("id %d", id);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id out of range");
+ return 2;
+ }
+ idx = ld->flowint[id];
+ if (idx == 0) {
+ SCLogDebug("idx %u", idx);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id uninitialized");
+ return 2;
+ }
+
+ /* lookup var */
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_RDLOCK(f);
+
+ fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ number = 1;
+ } else {
+ number = fv->data.fv_int.value;
+ if (number < UINT_MAX)
+ number++;
+ }
+ FlowVarAddIntNoLock(f, idx, number);
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ /* return value through luastate, as a luanumber */
+ lua_pushnumber(luastate, (lua_Number)number);
+ SCLogDebug("incremented flow:%p idx:%u value:%u", f, idx, number);
+
+ return 1;
+
+}
+
+static int LuaDecrFlowint(lua_State *luastate)
+{
+ uint16_t idx;
+ int id;
+ Flow *f;
+ FlowVar *fv;
+ DetectLuaData *ld;
+ int flow_lock = 0;
+ uint32_t number;
+
+ /* need luajit data for id -> idx conversion */
+ lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ ld = lua_touserdata(luastate, -1);
+ SCLogDebug("ld %p", ld);
+ if (ld == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "internal error: no ld");
+ return 2;
+ }
+
+ /* need flow and lock hint */
+ f = LuaStateGetFlow(luastate, &flow_lock);
+ if (f == NULL) {
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "no flow");
+ return 2;
+ }
+
+ /* need flowint idx */
+ if (!lua_isnumber(luastate, 1)) {
+ SCLogDebug("1st arg not a number");
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "1st arg not a number");
+ return 2;
+ }
+ id = lua_tonumber(luastate, 1);
+ if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) {
+ SCLogDebug("id %d", id);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id out of range");
+ return 2;
+ }
+ idx = ld->flowint[id];
+ if (idx == 0) {
+ SCLogDebug("idx %u", idx);
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, "flowint id uninitialized");
+ return 2;
+ }
+
+ /* lookup var */
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_RDLOCK(f);
+
+ fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ number = 0;
+ } else {
+ number = fv->data.fv_int.value;
+ if (number > 0)
+ number--;
+ }
+ FlowVarAddIntNoLock(f, idx, number);
+
+ if (flow_lock == LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ FLOWLOCK_UNLOCK(f);
+
+ /* return value through luastate, as a luanumber */
+ lua_pushnumber(luastate, (lua_Number)number);
+ SCLogDebug("decremented flow:%p idx:%u value:%u", f, idx, number);
+
+ return 1;
+
+}
+
+void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld, DetectEngineThreadCtx *det_ctx,
+ Flow *f, int flow_locked, Packet *p, uint8_t flags)
+{
+ SCLogDebug("det_ctx %p, f %p", det_ctx, f);
+
+ /* luajit keyword data */
+ lua_pushlightuserdata(lua_state, (void *)&luaext_key_ld);
+ lua_pushlightuserdata(lua_state, (void *)ld);
+ lua_settable(lua_state, LUA_REGISTRYINDEX);
+
+ /* detection engine thread ctx */
+ lua_pushlightuserdata(lua_state, (void *)&luaext_key_det_ctx);
+ lua_pushlightuserdata(lua_state, (void *)det_ctx);
+ lua_settable(lua_state, LUA_REGISTRYINDEX);
+
+ LuaStateSetFlow(lua_state, f, flow_locked);
+
+ if (det_ctx->tx_id_set && flow_locked == LUA_FLOW_LOCKED_BY_PARENT) {
+ if (f && f->alstate) {
+ void *txptr = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id);
+ if (txptr) {
+ LuaStateSetTX(lua_state, txptr);
+ }
+ }
+ }
+
+ if (p != NULL)
+ LuaStateSetPacket(lua_state, p);
+
+ LuaStateSetDirection(lua_state, (flags & STREAM_TOSERVER));
+}
+
+/**
+ * \brief Register Suricata Lua functions
+ */
+int LuaRegisterExtensions(lua_State *lua_state)
+{
+ lua_pushcfunction(lua_state, LuaGetFlowvar);
+ lua_setglobal(lua_state, "ScFlowvarGet");
+
+ lua_pushcfunction(lua_state, LuaSetFlowvar);
+ lua_setglobal(lua_state, "ScFlowvarSet");
+
+ lua_pushcfunction(lua_state, LuaGetFlowint);
+ lua_setglobal(lua_state, "ScFlowintGet");
+
+ lua_pushcfunction(lua_state, LuaSetFlowint);
+ lua_setglobal(lua_state, "ScFlowintSet");
+
+ lua_pushcfunction(lua_state, LuaIncrFlowint);
+ lua_setglobal(lua_state, "ScFlowintIncr");
+
+ lua_pushcfunction(lua_state, LuaDecrFlowint);
+ lua_setglobal(lua_state, "ScFlowintDecr");
+
+ LuaRegisterFunctions(lua_state);
+ LuaRegisterHttpFunctions(lua_state);
+ LuaRegisterDnsFunctions(lua_state);
+ LuaRegisterTlsFunctions(lua_state);
+ return 0;
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/detect-lua-extensions.h b/framework/src/suricata/src/detect-lua-extensions.h
new file mode 100644
index 00000000..b8aa2736
--- /dev/null
+++ b/framework/src/suricata/src/detect-lua-extensions.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_LUA_EXT_H__
+#define __DETECT_LUA_EXT_H__
+
+#ifdef HAVE_LUA
+int LuaRegisterExtensions(lua_State *);
+
+void LuaExtensionsMatchSetup(lua_State *lua_state,
+ DetectLuaData *, DetectEngineThreadCtx *det_ctx,
+ Flow *f, int flow_locked, Packet *p, uint8_t flags);
+
+#endif /* HAVE_LUA */
+#endif
diff --git a/framework/src/suricata/src/detect-lua.c b/framework/src/suricata/src/detect-lua.c
new file mode 100644
index 00000000..d1f2cd00
--- /dev/null
+++ b/framework/src/suricata/src/detect-lua.c
@@ -0,0 +1,2072 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "stream-tcp.h"
+
+#include "detect-lua.h"
+#include "detect-lua-extensions.h"
+
+#include "queue.h"
+#include "util-cpu.h"
+#include "util-var-name.h"
+
+#ifndef HAVE_LUA
+
+static int DetectLuaSetupNoSupport (DetectEngineCtx *a, Signature *b, char *c)
+{
+ SCLogError(SC_ERR_NO_LUA_SUPPORT, "no Lua support built in, needed for lua/luajit keyword");
+ return -1;
+}
+
+/**
+ * \brief Registration function for keyword: luajit
+ */
+void DetectLuaRegister(void)
+{
+ sigmatch_table[DETECT_LUA].name = "lua";
+ sigmatch_table[DETECT_LUA].alias = "luajit";
+ sigmatch_table[DETECT_LUA].Setup = DetectLuaSetupNoSupport;
+ sigmatch_table[DETECT_LUA].Free = NULL;
+ sigmatch_table[DETECT_LUA].RegisterTests = NULL;
+ sigmatch_table[DETECT_LUA].flags = SIGMATCH_NOT_BUILT;
+
+ SCLogDebug("registering lua rule option");
+ return;
+}
+
+#else /* HAVE_LUA */
+
+#ifdef HAVE_LUAJIT
+#include "util-pool.h"
+
+/** \brief lua_State pool
+ *
+ * Lua requires states to be alloc'd in memory <2GB. For this reason we
+ * prealloc the states early during engine startup so we have a better chance
+ * of getting the states. We protect the pool with a lock as the detect
+ * threads access it during their init and cleanup.
+ *
+ * Pool size is automagically determined based on number of keyword occurences,
+ * cpus/cores and rule reloads being enabled or not.
+ *
+ * Alternatively, the "detect-engine.luajit-states" var can be set.
+ */
+static Pool *luajit_states = NULL;
+static pthread_mutex_t luajit_states_lock = SCMUTEX_INITIALIZER;
+
+#endif /* HAVE_LUAJIT */
+
+#include "util-lua.h"
+
+static int DetectLuaMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, const SigMatchCtx *);
+static int DetectLuaAppMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m);
+static int DetectLuaAppTxMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags,
+ void *state, void *txv, const Signature *s,
+ const SigMatchCtx *ctx);
+static int DetectLuaSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectLuaRegisterTests(void);
+static void DetectLuaFree(void *);
+
+/**
+ * \brief Registration function for keyword: luajit
+ */
+void DetectLuaRegister(void)
+{
+ sigmatch_table[DETECT_LUA].name = "lua";
+ sigmatch_table[DETECT_LUA].alias = "luajit";
+ sigmatch_table[DETECT_LUA].desc = "match via a luajit script";
+ sigmatch_table[DETECT_LUA].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Lua_scripting";
+ sigmatch_table[DETECT_LUA].Match = DetectLuaMatch;
+ sigmatch_table[DETECT_LUA].AppLayerMatch = DetectLuaAppMatch;
+ sigmatch_table[DETECT_LUA].AppLayerTxMatch = DetectLuaAppTxMatch;
+ sigmatch_table[DETECT_LUA].Setup = DetectLuaSetup;
+ sigmatch_table[DETECT_LUA].Free = DetectLuaFree;
+ sigmatch_table[DETECT_LUA].RegisterTests = DetectLuaRegisterTests;
+
+ SCLogDebug("registering luajit rule option");
+ return;
+}
+
+#define DATATYPE_PACKET (1<<0)
+#define DATATYPE_PAYLOAD (1<<1)
+#define DATATYPE_STREAM (1<<2)
+
+#define DATATYPE_HTTP_URI (1<<3)
+#define DATATYPE_HTTP_URI_RAW (1<<4)
+
+#define DATATYPE_HTTP_REQUEST_HEADERS (1<<5)
+#define DATATYPE_HTTP_REQUEST_HEADERS_RAW (1<<6)
+#define DATATYPE_HTTP_REQUEST_COOKIE (1<<7)
+#define DATATYPE_HTTP_REQUEST_UA (1<<8)
+
+#define DATATYPE_HTTP_REQUEST_LINE (1<<9)
+#define DATATYPE_HTTP_REQUEST_BODY (1<<10)
+
+#define DATATYPE_HTTP_RESPONSE_COOKIE (1<<11)
+#define DATATYPE_HTTP_RESPONSE_BODY (1<<12)
+
+#define DATATYPE_HTTP_RESPONSE_HEADERS (1<<13)
+#define DATATYPE_HTTP_RESPONSE_HEADERS_RAW (1<<14)
+
+#define DATATYPE_DNS_RRNAME (1<<15)
+#define DATATYPE_DNS_REQUEST (1<<16)
+#define DATATYPE_DNS_RESPONSE (1<<17)
+
+#define DATATYPE_TLS (1<<18)
+
+#ifdef HAVE_LUAJIT
+static void *LuaStatePoolAlloc(void)
+{
+ return luaL_newstate();
+}
+
+static void LuaStatePoolFree(void *d)
+{
+ lua_State *s = (lua_State *)d;
+ if (s != NULL)
+ lua_close(s);
+}
+
+/** \brief Populate lua states pool
+ *
+ * \param num keyword instances
+ * \param reloads bool indicating we have rule reloads enabled
+ */
+int DetectLuajitSetupStatesPool(int num, int reloads)
+{
+ int retval = 0;
+ pthread_mutex_lock(&luajit_states_lock);
+
+ if (luajit_states == NULL) {
+ int cnt = 0;
+ char *conf_val = NULL;
+
+ if ((ConfGet("detect-engine.luajit-states", &conf_val)) == 1) {
+ cnt = (int)atoi(conf_val);
+ } else {
+ int cpus = UtilCpuGetNumProcessorsOnline();
+ if (cpus == 0) {
+ cpus = 10;
+ }
+ cnt = num * cpus;
+ cnt *= 3; /* assume 3 threads per core */
+
+ /* alloc a bunch extra so reload can add new rules/instances */
+ if (reloads)
+ cnt *= 5;
+ }
+
+ luajit_states = PoolInit(0, cnt, 0, LuaStatePoolAlloc, NULL, NULL, NULL, LuaStatePoolFree);
+ if (luajit_states == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "luastate pool init failed, lua/luajit keywords won't work");
+ retval = -1;
+ }
+ }
+
+ pthread_mutex_unlock(&luajit_states_lock);
+ return retval;
+}
+#endif /* HAVE_LUAJIT */
+
+static lua_State *DetectLuaGetState(void)
+{
+
+ lua_State *s = NULL;
+#ifdef HAVE_LUAJIT
+ pthread_mutex_lock(&luajit_states_lock);
+ if (luajit_states != NULL)
+ s = (lua_State *)PoolGet(luajit_states);
+ pthread_mutex_unlock(&luajit_states_lock);
+#else
+ s = luaL_newstate();
+#endif
+ return s;
+}
+
+static void DetectLuaReturnState(lua_State *s)
+{
+ if (s != NULL) {
+#ifdef HAVE_LUAJIT
+ pthread_mutex_lock(&luajit_states_lock);
+ PoolReturn(luajit_states, (void *)s);
+ pthread_mutex_unlock(&luajit_states_lock);
+#else
+ lua_close(s);
+#endif
+ }
+}
+
+/** \brief dump stack from lua state to screen */
+void LuaDumpStack(lua_State *state)
+{
+ int size = lua_gettop(state);
+ int i;
+
+ for (i = 1; i <= size; i++) {
+ int type = lua_type(state, i);
+ printf("Stack size=%d, level=%d, type=%d, ", size, i, type);
+
+ switch (type) {
+ case LUA_TFUNCTION:
+ printf("function %s", lua_tostring(state, i) ? "true" : "false");
+ break;
+ case LUA_TBOOLEAN:
+ printf("bool %s", lua_toboolean(state, i) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf("number %g", lua_tonumber(state, i));
+ break;
+ case LUA_TSTRING:
+ printf("string `%s'", lua_tostring(state, i));
+ break;
+ case LUA_TTABLE:
+ printf("table `%s'", lua_tostring(state, i));
+ break;
+ default:
+ printf("other %s", lua_typename(state, type));
+ break;
+
+ }
+ printf("\n");
+ }
+}
+
+int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm,
+ uint8_t *buffer, uint32_t buffer_len, uint32_t offset,
+ Flow *f, int flow_lock)
+{
+ SCEnter();
+ int ret = 0;
+
+ if (buffer == NULL || buffer_len == 0)
+ SCReturnInt(0);
+
+ DetectLuaData *luajit = (DetectLuaData *)sm->ctx;
+ if (luajit == NULL)
+ SCReturnInt(0);
+
+ DetectLuaThreadData *tluajit = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, luajit->thread_ctx_id);
+ if (tluajit == NULL)
+ SCReturnInt(0);
+
+ /* setup extension data for use in lua c functions */
+ LuaExtensionsMatchSetup(tluajit->luastate, luajit, det_ctx,
+ f, flow_lock, /* no packet in the ctx */NULL, 0);
+
+ /* prepare data to pass to script */
+ lua_getglobal(tluajit->luastate, "match");
+ lua_newtable(tluajit->luastate); /* stack at -1 */
+
+ lua_pushliteral (tluajit->luastate, "offset"); /* stack at -2 */
+ lua_pushnumber (tluajit->luastate, (int)(offset + 1));
+ lua_settable(tluajit->luastate, -3);
+
+ lua_pushstring (tluajit->luastate, luajit->buffername); /* stack at -2 */
+ LuaPushStringBuffer(tluajit->luastate, (const uint8_t *)buffer, (size_t)buffer_len);
+ lua_settable(tluajit->luastate, -3);
+
+ int retval = lua_pcall(tluajit->luastate, 1, 1, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(tluajit->luastate, -1));
+ }
+
+ /* process returns from script */
+ if (lua_gettop(tluajit->luastate) > 0) {
+ /* script returns a number (return 1 or return 0) */
+ if (lua_type(tluajit->luastate, 1) == LUA_TNUMBER) {
+ double script_ret = lua_tonumber(tluajit->luastate, 1);
+ SCLogDebug("script_ret %f", script_ret);
+ lua_pop(tluajit->luastate, 1);
+
+ if (script_ret == 1.0)
+ ret = 1;
+
+ /* script returns a table */
+ } else if (lua_type(tluajit->luastate, 1) == LUA_TTABLE) {
+ lua_pushnil(tluajit->luastate);
+ const char *k, *v;
+ while (lua_next(tluajit->luastate, -2)) {
+ v = lua_tostring(tluajit->luastate, -1);
+ lua_pop(tluajit->luastate, 1);
+ k = lua_tostring(tluajit->luastate, -1);
+
+ if (!k || !v)
+ continue;
+
+ SCLogDebug("k='%s', v='%s'", k, v);
+
+ if (strcmp(k, "retval") == 0) {
+ if (atoi(v) == 1)
+ ret = 1;
+ } else {
+ /* set flow var? */
+ }
+ }
+
+ /* pop the table */
+ lua_pop(tluajit->luastate, 1);
+ }
+ } else {
+ SCLogDebug("no stack");
+ }
+
+ /* clear the stack */
+ while (lua_gettop(tluajit->luastate) > 0) {
+ lua_pop(tluajit->luastate, 1);
+ }
+
+ if (luajit->negated) {
+ if (ret == 1)
+ ret = 0;
+ else
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+
+}
+
+/**
+ * \brief match the specified luajit
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param p packet
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectLuaData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectLuaMatch (ThreadVars *tv, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+ int ret = 0;
+ DetectLuaData *luajit = (DetectLuaData *)ctx;
+ if (luajit == NULL)
+ SCReturnInt(0);
+
+ DetectLuaThreadData *tluajit = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, luajit->thread_ctx_id);
+ if (tluajit == NULL)
+ SCReturnInt(0);
+
+ /* setup extension data for use in lua c functions */
+ uint8_t flags = 0;
+ if (p->flowflags & FLOW_PKT_TOSERVER)
+ flags = STREAM_TOSERVER;
+ else if (p->flowflags & FLOW_PKT_TOCLIENT)
+ flags = STREAM_TOCLIENT;
+
+ LuaExtensionsMatchSetup(tluajit->luastate, luajit, det_ctx,
+ p->flow, /* flow not locked */LUA_FLOW_NOT_LOCKED_BY_PARENT, p, flags);
+
+ if ((tluajit->flags & DATATYPE_PAYLOAD) && p->payload_len == 0)
+ SCReturnInt(0);
+ if ((tluajit->flags & DATATYPE_PACKET) && GET_PKT_LEN(p) == 0)
+ SCReturnInt(0);
+ if (tluajit->alproto != ALPROTO_UNKNOWN) {
+ if (p->flow == NULL)
+ SCReturnInt(0);
+
+ FLOWLOCK_RDLOCK(p->flow);
+ int alproto = p->flow->alproto;
+ FLOWLOCK_UNLOCK(p->flow);
+
+ if (tluajit->alproto != alproto)
+ SCReturnInt(0);
+ }
+
+ lua_getglobal(tluajit->luastate, "match");
+ lua_newtable(tluajit->luastate); /* stack at -1 */
+
+ if ((tluajit->flags & DATATYPE_PAYLOAD) && p->payload_len) {
+ lua_pushliteral(tluajit->luastate, "payload"); /* stack at -2 */
+ LuaPushStringBuffer (tluajit->luastate, (const uint8_t *)p->payload, (size_t)p->payload_len); /* stack at -3 */
+ lua_settable(tluajit->luastate, -3);
+ }
+ if ((tluajit->flags & DATATYPE_PACKET) && GET_PKT_LEN(p)) {
+ lua_pushliteral(tluajit->luastate, "packet"); /* stack at -2 */
+ LuaPushStringBuffer (tluajit->luastate, (const uint8_t *)GET_PKT_DATA(p), (size_t)GET_PKT_LEN(p)); /* stack at -3 */
+ lua_settable(tluajit->luastate, -3);
+ }
+ if (tluajit->alproto == ALPROTO_HTTP) {
+ FLOWLOCK_RDLOCK(p->flow);
+ HtpState *htp_state = p->flow->alstate;
+ if (htp_state != NULL && htp_state->connp != NULL) {
+ htp_tx_t *tx = NULL;
+ uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser,
+ STREAM_TOSERVER);
+ uint64_t total_txs= AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state);
+ for ( ; idx < total_txs; idx++) {
+ tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, idx);
+ if (tx == NULL)
+ continue;
+
+ if ((tluajit->flags & DATATYPE_HTTP_REQUEST_LINE) && tx->request_line != NULL &&
+ bstr_len(tx->request_line) > 0) {
+ lua_pushliteral(tluajit->luastate, "http.request_line"); /* stack at -2 */
+ LuaPushStringBuffer(tluajit->luastate,
+ (const uint8_t *)bstr_ptr(tx->request_line),
+ bstr_len(tx->request_line));
+ lua_settable(tluajit->luastate, -3);
+ }
+ }
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+
+ int retval = lua_pcall(tluajit->luastate, 1, 1, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(tluajit->luastate, -1));
+ }
+
+ /* process returns from script */
+ if (lua_gettop(tluajit->luastate) > 0) {
+
+ /* script returns a number (return 1 or return 0) */
+ if (lua_type(tluajit->luastate, 1) == LUA_TNUMBER) {
+ double script_ret = lua_tonumber(tluajit->luastate, 1);
+ SCLogDebug("script_ret %f", script_ret);
+ lua_pop(tluajit->luastate, 1);
+
+ if (script_ret == 1.0)
+ ret = 1;
+
+ /* script returns a table */
+ } else if (lua_type(tluajit->luastate, 1) == LUA_TTABLE) {
+ lua_pushnil(tluajit->luastate);
+ const char *k, *v;
+ while (lua_next(tluajit->luastate, -2)) {
+ v = lua_tostring(tluajit->luastate, -1);
+ lua_pop(tluajit->luastate, 1);
+ k = lua_tostring(tluajit->luastate, -1);
+
+ if (!k || !v)
+ continue;
+
+ SCLogDebug("k='%s', v='%s'", k, v);
+
+ if (strcmp(k, "retval") == 0) {
+ if (atoi(v) == 1)
+ ret = 1;
+ } else {
+ /* set flow var? */
+ }
+ }
+
+ /* pop the table */
+ lua_pop(tluajit->luastate, 1);
+ }
+ }
+ while (lua_gettop(tluajit->luastate) > 0) {
+ lua_pop(tluajit->luastate, 1);
+ }
+
+ if (luajit->negated) {
+ if (ret == 1)
+ ret = 0;
+ else
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+}
+
+static int DetectLuaAppMatchCommon (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state,
+ const Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+ int ret = 0;
+ DetectLuaData *luajit = (DetectLuaData *)ctx;
+ if (luajit == NULL)
+ SCReturnInt(0);
+
+ DetectLuaThreadData *tluajit = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, luajit->thread_ctx_id);
+ if (tluajit == NULL)
+ SCReturnInt(0);
+
+ /* setup extension data for use in lua c functions */
+ LuaExtensionsMatchSetup(tluajit->luastate, luajit, det_ctx,
+ f, /* flow is locked */LUA_FLOW_LOCKED_BY_PARENT,
+ NULL, flags);
+
+ if (tluajit->alproto != ALPROTO_UNKNOWN) {
+ int alproto = f->alproto;
+ if (tluajit->alproto != alproto)
+ SCReturnInt(0);
+ }
+
+ lua_getglobal(tluajit->luastate, "match");
+ lua_newtable(tluajit->luastate); /* stack at -1 */
+
+ if (tluajit->alproto == ALPROTO_HTTP) {
+ HtpState *htp_state = state;
+ if (htp_state != NULL && htp_state->connp != NULL) {
+ htp_tx_t *tx = NULL;
+ tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, det_ctx->tx_id);
+ if (tx != NULL) {
+ if ((tluajit->flags & DATATYPE_HTTP_REQUEST_LINE) && tx->request_line != NULL &&
+ bstr_len(tx->request_line) > 0) {
+ lua_pushliteral(tluajit->luastate, "http.request_line"); /* stack at -2 */
+ LuaPushStringBuffer(tluajit->luastate,
+ (const uint8_t *)bstr_ptr(tx->request_line),
+ bstr_len(tx->request_line));
+ lua_settable(tluajit->luastate, -3);
+ }
+ }
+ }
+ }
+
+ int retval = lua_pcall(tluajit->luastate, 1, 1, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(tluajit->luastate, -1));
+ }
+
+ /* process returns from script */
+ if (lua_gettop(tluajit->luastate) > 0) {
+
+ /* script returns a number (return 1 or return 0) */
+ if (lua_type(tluajit->luastate, 1) == LUA_TNUMBER) {
+ double script_ret = lua_tonumber(tluajit->luastate, 1);
+ SCLogDebug("script_ret %f", script_ret);
+ lua_pop(tluajit->luastate, 1);
+
+ if (script_ret == 1.0)
+ ret = 1;
+
+ /* script returns a table */
+ } else if (lua_type(tluajit->luastate, 1) == LUA_TTABLE) {
+ lua_pushnil(tluajit->luastate);
+ const char *k, *v;
+ while (lua_next(tluajit->luastate, -2)) {
+ v = lua_tostring(tluajit->luastate, -1);
+ lua_pop(tluajit->luastate, 1);
+ k = lua_tostring(tluajit->luastate, -1);
+
+ if (!k || !v)
+ continue;
+
+ SCLogDebug("k='%s', v='%s'", k, v);
+
+ if (strcmp(k, "retval") == 0) {
+ if (atoi(v) == 1)
+ ret = 1;
+ } else {
+ /* set flow var? */
+ }
+ }
+
+ /* pop the table */
+ lua_pop(tluajit->luastate, 1);
+ }
+ }
+ while (lua_gettop(tluajit->luastate) > 0) {
+ lua_pop(tluajit->luastate, 1);
+ }
+
+ if (luajit->negated) {
+ if (ret == 1)
+ ret = 0;
+ else
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief match the specified lua script in AMATCH
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectLuaData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectLuaAppMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ return DetectLuaAppMatchCommon(t, det_ctx, f, flags, state, s, m->ctx);
+}
+
+/**
+ * \brief match the specified lua script in a list with a tx
+ *
+ * \param t thread local vars
+ * \param det_ctx pattern matcher thread local data
+ * \param s signature being inspected
+ * \param m sigmatch that we will cast into DetectLuaData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectLuaAppTxMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags,
+ void *state, void *txv, const Signature *s,
+ const SigMatchCtx *ctx)
+{
+ return DetectLuaAppMatchCommon(t, det_ctx, f, flags, state, s, ctx);
+}
+
+#ifdef UNITTESTS
+/* if this ptr is set the luajit setup functions will use this buffer as the
+ * lua script instead of calling luaL_loadfile on the filename supplied. */
+static const char *ut_script = NULL;
+#endif
+
+static void *DetectLuaThreadInit(void *data)
+{
+ int status;
+ DetectLuaData *luajit = (DetectLuaData *)data;
+ BUG_ON(luajit == NULL);
+
+ DetectLuaThreadData *t = SCMalloc(sizeof(DetectLuaThreadData));
+ if (unlikely(t == NULL)) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't alloc ctx memory");
+ return NULL;
+ }
+ memset(t, 0x00, sizeof(DetectLuaThreadData));
+
+ t->alproto = luajit->alproto;
+ t->flags = luajit->flags;
+
+ t->luastate = DetectLuaGetState();
+ if (t->luastate == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "luastate pool depleted");
+ goto error;
+ }
+
+ luaL_openlibs(t->luastate);
+
+ LuaRegisterExtensions(t->luastate);
+
+ lua_pushinteger(t->luastate, (lua_Integer)(luajit->sid));
+ lua_setglobal(t->luastate, "SCRuleSid");
+ lua_pushinteger(t->luastate, (lua_Integer)(luajit->rev));
+ lua_setglobal(t->luastate, "SCRuleRev");
+ lua_pushinteger(t->luastate, (lua_Integer)(luajit->gid));
+ lua_setglobal(t->luastate, "SCRuleGid");
+
+ /* hackish, needed to allow unittests to pass buffers as scripts instead of files */
+#ifdef UNITTESTS
+ if (ut_script != NULL) {
+ status = luaL_loadbuffer(t->luastate, ut_script, strlen(ut_script), "unittest");
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(t->luastate, -1));
+ goto error;
+ }
+ } else {
+#endif
+ status = luaL_loadfile(t->luastate, luajit->filename);
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(t->luastate, -1));
+ goto error;
+ }
+#ifdef UNITTESTS
+ }
+#endif
+
+ /* prime the script (or something) */
+ if (lua_pcall(t->luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't prime file: %s", lua_tostring(t->luastate, -1));
+ goto error;
+ }
+
+ return (void *)t;
+
+error:
+ if (t->luastate != NULL)
+ DetectLuaReturnState(t->luastate);
+ SCFree(t);
+ return NULL;
+}
+
+static void DetectLuaThreadFree(void *ctx)
+{
+ if (ctx != NULL) {
+ DetectLuaThreadData *t = (DetectLuaThreadData *)ctx;
+ if (t->luastate != NULL)
+ DetectLuaReturnState(t->luastate);
+ SCFree(t);
+ }
+}
+
+/**
+ * \brief Parse the luajit keyword
+ *
+ * \param str Pointer to the user provided option
+ *
+ * \retval luajit pointer to DetectLuaData on success
+ * \retval NULL on failure
+ */
+static DetectLuaData *DetectLuaParse (const DetectEngineCtx *de_ctx, char *str)
+{
+ DetectLuaData *luajit = NULL;
+
+ /* We have a correct luajit option */
+ luajit = SCMalloc(sizeof(DetectLuaData));
+ if (unlikely(luajit == NULL))
+ goto error;
+
+ memset(luajit, 0x00, sizeof(DetectLuaData));
+
+ if (strlen(str) && str[0] == '!') {
+ luajit->negated = 1;
+ str++;
+ }
+
+ /* get full filename */
+ luajit->filename = DetectLoadCompleteSigPath(de_ctx, str);
+ if (luajit->filename == NULL) {
+ goto error;
+ }
+
+ return luajit;
+
+error:
+ if (luajit != NULL)
+ DetectLuaFree(luajit);
+ return NULL;
+}
+
+static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld)
+{
+ int status;
+
+ lua_State *luastate = luaL_newstate();
+ if (luastate == NULL)
+ return -1;
+ luaL_openlibs(luastate);
+
+ /* hackish, needed to allow unittests to pass buffers as scripts instead of files */
+#ifdef UNITTESTS
+ if (ut_script != NULL) {
+ status = luaL_loadbuffer(luastate, ut_script, strlen(ut_script), "unittest");
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+ } else {
+#endif
+ status = luaL_loadfile(luastate, ld->filename);
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+#ifdef UNITTESTS
+ }
+#endif
+
+ /* prime the script (or something) */
+ if (lua_pcall(luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't prime file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ lua_getglobal(luastate, "init");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no init function in script");
+ goto error;
+ }
+
+ lua_newtable(luastate); /* stack at -1 */
+ if (lua_gettop(luastate) == 0 || lua_type(luastate, 2) != LUA_TTABLE) {
+ SCLogError(SC_ERR_LUA_ERROR, "no table setup");
+ goto error;
+ }
+
+ lua_pushliteral(luastate, "script_api_ver"); /* stack at -2 */
+ lua_pushnumber (luastate, 1); /* stack at -3 */
+ lua_settable(luastate, -3);
+
+ if (lua_pcall(luastate, 1, 1, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't run script 'init' function: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ /* process returns from script */
+ if (lua_gettop(luastate) == 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "init function in script should return table, nothing returned");
+ goto error;
+ }
+ if (lua_type(luastate, 1) != LUA_TTABLE) {
+ SCLogError(SC_ERR_LUA_ERROR, "init function in script should return table, returned is not table");
+ goto error;
+ }
+
+ lua_pushnil(luastate);
+ const char *k, *v;
+ while (lua_next(luastate, -2)) {
+ k = lua_tostring(luastate, -2);
+ if (k == NULL)
+ continue;
+
+ /* handle flowvar separately as it has a table as value */
+ if (strcmp(k, "flowvar") == 0) {
+ if (lua_istable(luastate, -1)) {
+ lua_pushnil(luastate);
+ while (lua_next(luastate, -2) != 0) {
+ /* value at -1, key is at -2 which we ignore */
+ const char *value = lua_tostring(luastate, -1);
+ SCLogDebug("value %s", value);
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(luastate, 1);
+
+ if (ld->flowvars == DETECT_LUAJIT_MAX_FLOWVARS) {
+ SCLogError(SC_ERR_LUA_ERROR, "too many flowvars registered");
+ goto error;
+ }
+
+ uint16_t idx = VariableNameGetIdx(de_ctx, (char *)value, VAR_TYPE_FLOW_VAR);
+ ld->flowvar[ld->flowvars++] = idx;
+ SCLogDebug("script uses flowvar %u with script id %u", idx, ld->flowvars - 1);
+ }
+ }
+ lua_pop(luastate, 1);
+ continue;
+ } else if (strcmp(k, "flowint") == 0) {
+ if (lua_istable(luastate, -1)) {
+ lua_pushnil(luastate);
+ while (lua_next(luastate, -2) != 0) {
+ /* value at -1, key is at -2 which we ignore */
+ const char *value = lua_tostring(luastate, -1);
+ SCLogDebug("value %s", value);
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(luastate, 1);
+
+ if (ld->flowints == DETECT_LUAJIT_MAX_FLOWINTS) {
+ SCLogError(SC_ERR_LUA_ERROR, "too many flowints registered");
+ goto error;
+ }
+
+ uint16_t idx = VariableNameGetIdx(de_ctx, (char *)value, VAR_TYPE_FLOW_INT);
+ ld->flowint[ld->flowints++] = idx;
+ SCLogDebug("script uses flowint %u with script id %u", idx, ld->flowints - 1);
+ }
+ }
+ lua_pop(luastate, 1);
+ continue;
+ }
+
+ v = lua_tostring(luastate, -1);
+ lua_pop(luastate, 1);
+ if (v == NULL)
+ continue;
+
+ SCLogDebug("k='%s', v='%s'", k, v);
+ if (strcmp(k, "packet") == 0 && strcmp(v, "true") == 0) {
+ ld->flags |= DATATYPE_PACKET;
+ } else if (strcmp(k, "payload") == 0 && strcmp(v, "true") == 0) {
+ ld->flags |= DATATYPE_PAYLOAD;
+ } else if (strcmp(k, "stream") == 0 && strcmp(v, "true") == 0) {
+ ld->flags |= DATATYPE_STREAM;
+
+ ld->buffername = SCStrdup("stream");
+ if (ld->buffername == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "alloc error");
+ goto error;
+ }
+
+ } else if (strncmp(k, "http", 4) == 0 && strcmp(v, "true") == 0) {
+ if (ld->alproto != ALPROTO_UNKNOWN && ld->alproto != ALPROTO_HTTP) {
+ SCLogError(SC_ERR_LUA_ERROR, "can just inspect script against one app layer proto like HTTP at a time");
+ goto error;
+ }
+ if (ld->flags != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "when inspecting HTTP buffers only a single buffer can be inspected");
+ goto error;
+ }
+
+ /* http types */
+ ld->alproto = ALPROTO_HTTP;
+
+ if (strcmp(k, "http.uri") == 0)
+ ld->flags |= DATATYPE_HTTP_URI;
+
+ else if (strcmp(k, "http.uri.raw") == 0)
+ ld->flags |= DATATYPE_HTTP_URI_RAW;
+
+ else if (strcmp(k, "http.request_line") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_LINE;
+
+ else if (strcmp(k, "http.request_headers") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_HEADERS;
+
+ else if (strcmp(k, "http.request_headers.raw") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_HEADERS_RAW;
+
+ else if (strcmp(k, "http.request_cookie") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_COOKIE;
+
+ else if (strcmp(k, "http.request_user_agent") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_UA;
+
+ else if (strcmp(k, "http.request_body") == 0)
+ ld->flags |= DATATYPE_HTTP_REQUEST_BODY;
+
+ else if (strcmp(k, "http.response_body") == 0)
+ ld->flags |= DATATYPE_HTTP_RESPONSE_BODY;
+
+ else if (strcmp(k, "http.response_cookie") == 0)
+ ld->flags |= DATATYPE_HTTP_RESPONSE_COOKIE;
+
+ else if (strcmp(k, "http.response_headers") == 0)
+ ld->flags |= DATATYPE_HTTP_RESPONSE_HEADERS;
+
+ else if (strcmp(k, "http.response_headers.raw") == 0)
+ ld->flags |= DATATYPE_HTTP_RESPONSE_HEADERS_RAW;
+
+ else {
+ SCLogError(SC_ERR_LUA_ERROR, "unsupported http data type %s", k);
+ goto error;
+ }
+
+ ld->buffername = SCStrdup(k);
+ if (ld->buffername == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "alloc error");
+ goto error;
+ }
+ } else if (strncmp(k, "dns", 3) == 0 && strcmp(v, "true") == 0) {
+
+ ld->alproto = ALPROTO_DNS;
+
+ if (strcmp(k, "dns.rrname") == 0)
+ ld->flags |= DATATYPE_DNS_RRNAME;
+ else if (strcmp(k, "dns.request") == 0)
+ ld->flags |= DATATYPE_DNS_REQUEST;
+ else if (strcmp(k, "dns.response") == 0)
+ ld->flags |= DATATYPE_DNS_RESPONSE;
+
+ else {
+ SCLogError(SC_ERR_LUA_ERROR, "unsupported dns data type %s", k);
+ goto error;
+ }
+ ld->buffername = SCStrdup(k);
+ if (ld->buffername == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "alloc error");
+ goto error;
+ }
+ } else if (strncmp(k, "tls", 3) == 0 && strcmp(v, "true") == 0) {
+
+ ld->alproto = ALPROTO_TLS;
+
+ ld->flags |= DATATYPE_TLS;
+
+ } else {
+ SCLogError(SC_ERR_LUA_ERROR, "unsupported data type %s", k);
+ goto error;
+ }
+ }
+
+ /* pop the table */
+ lua_pop(luastate, 1);
+ lua_close(luastate);
+ return 0;
+error:
+ lua_close(luastate);
+ return -1;
+}
+
+/**
+ * \brief this function is used to parse luajit options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "luajit" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectLuaSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectLuaData *luajit = NULL;
+ SigMatch *sm = NULL;
+
+ luajit = DetectLuaParse(de_ctx, str);
+ if (luajit == NULL)
+ goto error;
+
+ if (DetectLuaSetupPrime(de_ctx, luajit) == -1) {
+ goto error;
+ }
+
+ luajit->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "luajit",
+ DetectLuaThreadInit, (void *)luajit,
+ DetectLuaThreadFree, 0);
+ if (luajit->thread_ctx_id == -1)
+ goto error;
+
+ if (luajit->alproto != ALPROTO_UNKNOWN) {
+ if (s->alproto != ALPROTO_UNKNOWN && luajit->alproto != s->alproto) {
+ goto error;
+ }
+ s->alproto = luajit->alproto;
+ }
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_LUA;
+ sm->ctx = (SigMatchCtx *)luajit;
+
+ if (luajit->alproto == ALPROTO_UNKNOWN) {
+ if (luajit->flags & DATATYPE_STREAM)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_PMATCH);
+ else
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ } else if (luajit->alproto == ALPROTO_HTTP) {
+ if (luajit->flags & DATATYPE_HTTP_RESPONSE_BODY)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEDATA);
+ else if (luajit->flags & DATATYPE_HTTP_REQUEST_BODY)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCBDMATCH);
+ else if (luajit->flags & DATATYPE_HTTP_URI)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_UMATCH);
+ else if (luajit->flags & DATATYPE_HTTP_URI_RAW)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRUDMATCH);
+ else if (luajit->flags & DATATYPE_HTTP_REQUEST_COOKIE)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCDMATCH);
+ else if (luajit->flags & DATATYPE_HTTP_REQUEST_UA)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HUADMATCH);
+ else if (luajit->flags & (DATATYPE_HTTP_REQUEST_HEADERS|DATATYPE_HTTP_RESPONSE_HEADERS))
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HHDMATCH);
+ else if (luajit->flags & (DATATYPE_HTTP_REQUEST_HEADERS_RAW|DATATYPE_HTTP_RESPONSE_HEADERS_RAW))
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRHDMATCH);
+ else if (luajit->flags & DATATYPE_HTTP_RESPONSE_COOKIE)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCDMATCH);
+ else
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRLMATCH);
+ } else if (luajit->alproto == ALPROTO_DNS) {
+ if (luajit->flags & DATATYPE_DNS_RRNAME) {
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSQUERYNAME_MATCH);
+ } else if (luajit->flags & DATATYPE_DNS_REQUEST) {
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSREQUEST_MATCH);
+ } else if (luajit->flags & DATATYPE_DNS_RESPONSE) {
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSRESPONSE_MATCH);
+ }
+ } else if (luajit->alproto == ALPROTO_TLS) {
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+ } else {
+ SCLogError(SC_ERR_LUA_ERROR, "luajit can't be used with protocol %s",
+ AppLayerGetProtoName(luajit->alproto));
+ goto error;
+ }
+
+ de_ctx->detect_luajit_instances++;
+ return 0;
+
+error:
+ if (luajit != NULL)
+ DetectLuaFree(luajit);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/** \brief post-sig parse function to set the sid,rev,gid into the
+ * ctx, as this isn't available yet during parsing.
+ */
+void DetectLuaPostSetup(Signature *s)
+{
+ int i;
+ SigMatch *sm;
+
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ for (sm = s->sm_lists[i]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_LUA)
+ continue;
+
+ DetectLuaData *ld = (DetectLuaData *)sm->ctx;
+ ld->sid = s->id;
+ ld->rev = s->rev;
+ ld->gid = s->gid;
+ }
+ }
+}
+
+/**
+ * \brief this function will free memory associated with DetectLuaData
+ *
+ * \param luajit pointer to DetectLuaData
+ */
+static void DetectLuaFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectLuaData *luajit = (DetectLuaData *)ptr;
+
+ if (luajit->buffername)
+ SCFree(luajit->buffername);
+ if (luajit->filename)
+ SCFree(luajit->filename);
+
+ SCFree(luajit);
+ }
+}
+
+#ifdef UNITTESTS
+/** \test http buffer */
+static int LuaMatchTest01(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"http.request_headers\"] = tostring(true)\n"
+ " needs[\"flowvar\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " a = ScFlowvarGet(0)\n"
+ " if a then\n"
+ " a = tostring(tonumber(a)+1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " else\n"
+ " a = tostring(1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " end\n"
+ " \n"
+ " print (\"pre check: \" .. (a))\n"
+ " if tonumber(a) == 2 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert http any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SCLogDebug("inspecting p1");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect for p2 */
+ SCLogDebug("inspecting p2");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_str.value_len != 1) {
+ printf("%u != %u: ", fv->data.fv_str.value_len, 1);
+ goto end;
+ }
+
+ if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+
+ printf("buffer mismatch: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test payload buffer */
+static int LuaMatchTest02(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"payload\"] = tostring(true)\n"
+ " needs[\"flowvar\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " a = ScFlowvarGet(0)\n"
+ " if a then\n"
+ " a = tostring(tonumber(a)+1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " else\n"
+ " a = tostring(1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " end\n"
+ " \n"
+ " print (\"pre check: \" .. (a))\n"
+ " if tonumber(a) == 2 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert tcp any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+ p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ /* do detect for p2 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_str.value_len != 1) {
+ printf("%u != %u: ", fv->data.fv_str.value_len, 1);
+ goto end;
+ }
+
+ if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+
+ printf("buffer mismatch: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test packet buffer */
+static int LuaMatchTest03(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"packet\"] = tostring(true)\n"
+ " needs[\"flowvar\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " a = ScFlowvarGet(0)\n"
+ " if a then\n"
+ " a = tostring(tonumber(a)+1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " else\n"
+ " a = tostring(1)\n"
+ " print (a)\n"
+ " ScFlowvarSet(0, a, #a)\n"
+ " end\n"
+ " \n"
+ " print (\"pre check: \" .. (a))\n"
+ " if tonumber(a) == 2 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert tcp any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+ p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if ((PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ /* do detect for p2 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_str.value_len != 1) {
+ printf("%u != %u: ", fv->data.fv_str.value_len, 1);
+ goto end;
+ }
+
+ if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+
+ printf("buffer mismatch: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test http buffer, flowints */
+static int LuaMatchTest04(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"http.request_headers\"] = tostring(true)\n"
+ " needs[\"flowint\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " print \"inspecting\""
+ " a = ScFlowintGet(0)\n"
+ " if a then\n"
+ " ScFlowintSet(0, a + 1)\n"
+ " else\n"
+ " ScFlowintSet(0, 1)\n"
+ " end\n"
+ " \n"
+ " a = ScFlowintGet(0)\n"
+ " if a == 2 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert http any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SCLogInfo("p1");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect for p2 */
+ SCLogInfo("p2");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_int.value != 2) {
+ printf("%u != %u: ", fv->data.fv_int.value, 2);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test http buffer, flowints */
+static int LuaMatchTest05(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"http.request_headers\"] = tostring(true)\n"
+ " needs[\"flowint\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " print \"inspecting\""
+ " a = ScFlowintIncr(0)\n"
+ " if a == 2 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert http any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SCLogInfo("p1");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect for p2 */
+ SCLogInfo("p2");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_int.value != 2) {
+ printf("%u != %u: ", fv->data.fv_int.value, 2);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+/** \test http buffer, flowints */
+static int LuaMatchTest06(void)
+{
+ const char script[] =
+ "function init (args)\n"
+ " local needs = {}\n"
+ " needs[\"http.request_headers\"] = tostring(true)\n"
+ " needs[\"flowint\"] = {\"cnt\"}\n"
+ " return needs\n"
+ "end\n"
+ "\n"
+ "function match(args)\n"
+ " print \"inspecting\""
+ " a = ScFlowintGet(0)\n"
+ " if a == nil then\n"
+ " print \"new var set to 2\""
+ " ScFlowintSet(0, 2)\n"
+ " end\n"
+ " a = ScFlowintDecr(0)\n"
+ " if a == 0 then\n"
+ " print \"match\"\n"
+ " return 1\n"
+ " end\n"
+ " return 0\n"
+ "end\n"
+ "return 0\n";
+ char sig[] = "alert http any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n\r\n";
+ uint8_t httpbuf2[] =
+ "POST / HTTP/1.1\r\n"
+ "Host: www.openinfosecfoundation.org\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ ut_script = script;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, sig);
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SCLogInfo("p1");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but should not have: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect for p2 */
+ SCLogInfo("p2");
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 didn't match on p2 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, 1);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_int.value != 0) {
+ printf("%u != %u: ", fv->data.fv_int.value, 0);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+#endif
+
+void DetectLuaRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("LuaMatchTest01", LuaMatchTest01, 1);
+ UtRegisterTest("LuaMatchTest02", LuaMatchTest02, 1);
+ UtRegisterTest("LuaMatchTest03", LuaMatchTest03, 1);
+ UtRegisterTest("LuaMatchTest04", LuaMatchTest04, 1);
+ UtRegisterTest("LuaMatchTest05", LuaMatchTest05, 1);
+ UtRegisterTest("LuaMatchTest06", LuaMatchTest06, 1);
+#endif
+}
+
+#endif /* HAVE_LUAJIT */
diff --git a/framework/src/suricata/src/detect-lua.h b/framework/src/suricata/src/detect-lua.h
new file mode 100644
index 00000000..f7dc5de4
--- /dev/null
+++ b/framework/src/suricata/src/detect-lua.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_LUAJIT_H__
+#define __DETECT_LUAJIT_H__
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+typedef struct DetectLuaThreadData {
+ lua_State *luastate;
+ uint32_t flags;
+ int alproto;
+} DetectLuaThreadData;
+
+#define DETECT_LUAJIT_MAX_FLOWVARS 15
+#define DETECT_LUAJIT_MAX_FLOWINTS 15
+
+typedef struct DetectLuaData {
+ int thread_ctx_id;
+ int negated;
+ char *filename;
+ uint32_t flags;
+ int alproto;
+ char *buffername; /* buffer name in case of a single buffer */
+ uint16_t flowint[DETECT_LUAJIT_MAX_FLOWINTS];
+ uint16_t flowints;
+ uint16_t flowvar[DETECT_LUAJIT_MAX_FLOWVARS];
+ uint16_t flowvars;
+ uint32_t sid;
+ uint32_t rev;
+ uint32_t gid;
+} DetectLuaData;
+
+#endif /* HAVE_LUA */
+
+/* prototypes */
+void DetectLuaRegister (void);
+int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm,
+ uint8_t *buffer, uint32_t buffer_len, uint32_t offset,
+ Flow *f, int flow_lock);
+
+#ifdef HAVE_LUAJIT
+int DetectLuajitSetupStatesPool(int num, int reloads);
+#endif /* HAVE_LUAJIT */
+
+void DetectLuaPostSetup(Signature *s);
+
+#endif /* __DETECT_FILELUAJIT_H__ */
diff --git a/framework/src/suricata/src/detect-mark.c b/framework/src/suricata/src/detect-mark.c
new file mode 100644
index 00000000..695a7b41
--- /dev/null
+++ b/framework/src/suricata/src/detect-mark.c
@@ -0,0 +1,353 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Implements the mark keyword. Based on detect-gid
+ * by Breno Silva <breno.silva@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "detect.h"
+#include "flow-var.h"
+#include "decode-events.h"
+
+#include "detect-mark.h"
+#include "detect-parse.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "([0x]*[0-9a-f]+)/([0x]*[0-9a-f]+)"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectMarkSetup (DetectEngineCtx *, Signature *, char *);
+int DetectMarkPacket(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx);
+void DetectMarkDataFree(void *ptr);
+
+/**
+ * \brief Registration function for nfq_set_mark: keyword
+ */
+
+void DetectMarkRegister (void)
+{
+ sigmatch_table[DETECT_MARK].name = "nfq_set_mark";
+ sigmatch_table[DETECT_MARK].Match = DetectMarkPacket;
+ sigmatch_table[DETECT_MARK].Setup = DetectMarkSetup;
+ sigmatch_table[DETECT_MARK].Free = DetectMarkDataFree;
+ sigmatch_table[DETECT_MARK].RegisterTests = MarkRegisterTests;
+
+ const char *eb;
+ int opts = 0;
+ int eo;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+
+}
+
+#ifdef NFQ
+/**
+ * \internal
+ * \brief This function is used to parse mark options passed via mark: keyword
+ *
+ * \param rawstr Pointer to the user provided mark options
+ *
+ * \retval 0 on success
+ * \retval < 0 on failure
+ */
+static void * DetectMarkParse (char *rawstr)
+{
+ int ret = 0, res = 0;
+#define MAX_SUBSTRINGS 30
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+ char *ptr = NULL;
+ char *endptr = NULL;
+ uint32_t mark;
+ uint32_t mask;
+ DetectMarkData *data;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ return NULL;
+ }
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return NULL;
+ }
+
+ ptr = (char *)str_ptr;
+
+ if (ptr == NULL)
+ return NULL;
+
+ errno = 0;
+ mark = strtoul(ptr, &endptr, 0);
+ if (errno == ERANGE) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
+ SCFree(ptr);
+ return NULL;
+ } /* If there is no numeric value in the given string then strtoull(), makes
+ endptr equals to ptr and return 0 as result */
+ else if (endptr == ptr && mark == 0) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "No numeric value");
+ SCFree(ptr);
+ return NULL;
+ } else if (endptr == ptr) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
+ SCFree(ptr);
+ return NULL;
+ }
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return NULL;
+ }
+
+ SCFree(ptr);
+ ptr = (char *)str_ptr;
+
+ if (ptr == NULL) {
+ data = SCMalloc(sizeof(DetectMarkData));
+ if (unlikely(data == NULL)) {
+ return NULL;
+ }
+ data->mark = mark;
+ data->mask = 0xffff;
+ return data;
+ }
+
+ errno = 0;
+ mask = strtoul(ptr, &endptr, 0);
+ if (errno == ERANGE) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
+ SCFree(ptr);
+ return NULL;
+ } /* If there is no numeric value in the given string then strtoull(), makes
+ endptr equals to ptr and return 0 as result */
+ else if (endptr == ptr && mask == 0) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "No numeric value");
+ SCFree(ptr);
+ return NULL;
+ }
+ else if (endptr == ptr) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
+ SCFree(ptr);
+ return NULL;
+ }
+
+ SCLogDebug("Rule will set mark 0x%x with mask 0x%x", mark, mask);
+ SCFree(ptr);
+
+ data = SCMalloc(sizeof(DetectMarkData));
+ if (unlikely(data == NULL)) {
+ return NULL;
+ }
+ data->mark = mark;
+ data->mask = mask;
+ return data;
+}
+
+#endif /* NFQ */
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed mark into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided mark options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectMarkSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+#ifdef NFQ
+ DetectMarkData *data = NULL;
+ SigMatch *sm = NULL;
+
+ data = DetectMarkParse(rawstr);
+
+ if (data == NULL) {
+ return -1;
+ } else {
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ DetectMarkDataFree(data);
+ return -1;
+ }
+
+ sm->type = DETECT_MARK;
+ sm->ctx = (SigMatchCtx *)data;
+
+ /* Append it to the list of tags */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_TMATCH);
+ return 0;
+ }
+#else
+ return 0;
+#endif
+}
+
+void DetectMarkDataFree(void *ptr)
+{
+ DetectMarkData *data = (DetectMarkData *)ptr;
+ SCFree(data);
+}
+
+
+int DetectMarkPacket(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+#ifdef NFQ
+ const DetectMarkData *nf_data = (const DetectMarkData *)ctx;
+ if (nf_data->mask) {
+ p->nfq_v.mark = (nf_data->mark & nf_data->mask)
+ | (p->nfq_v.mark & ~(nf_data->mask));
+ p->flags |= PKT_MARK_MODIFIED;
+ }
+#endif
+ return 1;
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#if defined UNITTESTS && defined NFQ
+/**
+ * \test MarkTestParse01 is a test for a valid mark value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int MarkTestParse01 (void)
+{
+ DetectMarkData *data;
+
+ data = DetectMarkParse("1/1");
+
+ if (data == NULL) {
+ return 0;
+ }
+
+ DetectMarkDataFree(data);
+ return 1;
+}
+
+/**
+ * \test MarkTestParse02 is a test for an invalid mark value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int MarkTestParse02 (void)
+{
+ DetectMarkData *data;
+
+ data = DetectMarkParse("4");
+
+ if (data == NULL) {
+ return 0;
+ }
+
+ DetectMarkDataFree(data);
+ return 1;
+}
+
+/**
+ * \test MarkTestParse03 is a test for a valid mark value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int MarkTestParse03 (void)
+{
+ DetectMarkData *data;
+
+ data = DetectMarkParse("0x10/0xff");
+
+ if (data == NULL) {
+ return 0;
+ }
+
+ DetectMarkDataFree(data);
+ return 1;
+}
+
+/**
+ * \test MarkTestParse04 is a test for a invalid mark value
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int MarkTestParse04 (void)
+{
+ DetectMarkData *data;
+
+ data = DetectMarkParse("0x1g/0xff");
+
+ if (data == NULL) {
+ return 0;
+ }
+
+ DetectMarkDataFree(data);
+ return 1;
+}
+
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for Mark
+ */
+void MarkRegisterTests(void)
+{
+#if defined UNITTESTS && defined NFQ
+ UtRegisterTest("MarkTestParse01", MarkTestParse01, 1);
+ UtRegisterTest("MarkTestParse02", MarkTestParse02, 0);
+ UtRegisterTest("MarkTestParse03", MarkTestParse03, 1);
+ UtRegisterTest("MarkTestParse04", MarkTestParse04, 0);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-mark.h b/framework/src/suricata/src/detect-mark.h
new file mode 100644
index 00000000..3c3b8593
--- /dev/null
+++ b/framework/src/suricata/src/detect-mark.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Based on detect-mark.h by Breno Silva <breno.silva@gmail.com>
+ *
+ * Implements the nfq_set_mark keyword
+ */
+
+#ifndef __DETECT_MARK_H__
+#define __DETECT_MARK_H__
+
+#include "decode.h"
+#include "detect.h"
+
+/**
+ * \struct DetectMarkData_
+ * DetectMarkData_ is used to store nfq_set_mark: input value
+ */
+
+/**
+ * \typedef DetectMarkData
+ * A typedef for DetectMarkData_
+ */
+
+typedef struct DetectMarkData_ {
+ uint32_t mark; /**< Rule mark */
+ uint32_t mask; /**< Rule mask */
+} DetectMarkData;
+
+/**
+ * Registration function for nfq_set_mark: keyword
+ */
+
+void DetectMarkRegister (void);
+
+/**
+ * This function registers unit tests for Mark
+ */
+
+void MarkRegisterTests(void);
+
+#endif /*__DETECT_MARK_H__ */
diff --git a/framework/src/suricata/src/detect-metadata.c b/framework/src/suricata/src/detect-metadata.c
new file mode 100644
index 00000000..3055ec78
--- /dev/null
+++ b/framework/src/suricata/src/detect-metadata.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements metadata keyword support
+ *
+ * \todo Do we need to do anything more this is used in snort host attribute table
+ * It is also used for rule managment.
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+
+static int DetectMetadataSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectMetadataRegister (void)
+{
+ sigmatch_table[DETECT_METADATA].name = "metadata";
+ sigmatch_table[DETECT_METADATA].desc = "ignored by suricata";
+ sigmatch_table[DETECT_METADATA].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Metadata";
+ sigmatch_table[DETECT_METADATA].Match = NULL;
+ sigmatch_table[DETECT_METADATA].Setup = DetectMetadataSetup;
+ sigmatch_table[DETECT_METADATA].Free = NULL;
+ sigmatch_table[DETECT_METADATA].RegisterTests = NULL;
+}
+
+static int DetectMetadataSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ return 0;
+}
+
diff --git a/framework/src/suricata/src/detect-metadata.h b/framework/src/suricata/src/detect-metadata.h
new file mode 100644
index 00000000..bcb4c01c
--- /dev/null
+++ b/framework/src/suricata/src/detect-metadata.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_METADATA_H__
+#define __DETECT_METADATA_H__
+
+/* prototypes */
+void DetectMetadataRegister (void);
+
+#endif /* __DETECT_METADATA_H__ */
+
diff --git a/framework/src/suricata/src/detect-modbus.c b/framework/src/suricata/src/detect-modbus.c
new file mode 100644
index 00000000..d1e4ec50
--- /dev/null
+++ b/framework/src/suricata/src/detect-modbus.c
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ *
+ * Implements the Modbus function and access keywords
+ * You can specify a:
+ * - concrete function like Modbus:
+ * function 8, subfunction 4 (diagnostic: Force Listen Only Mode)
+ * - data (in primary table) register access (r/w) like Modbus:
+ * access read coils, address 1000 (.i.e Read coils: at address 1000)
+ * - write data value at specific address Modbus:
+ * access write, address 1500<>2000, value >2000 (Write multiple coils/register:
+ * at address between 1500 and 2000 value greater than 2000)
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-modbus.h"
+
+#include "util-debug.h"
+
+#include "app-layer-modbus.h"
+
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing the Modbus function string
+ */
+#define PARSE_REGEX_FUNCTION "^\\s*\"?\\s*function\\s*(!?[A-z0-9]+)(,\\s*subfunction\\s+(\\d+))?\\s*\"?\\s*$"
+static pcre *function_parse_regex;
+static pcre_extra *function_parse_regex_study;
+
+/**
+ * \brief Regex for parsing the Modbus access string
+ */
+#define PARSE_REGEX_ACCESS "^\\s*\"?\\s*access\\s*(read|write)\\s*(discretes|coils|input|holding)?(,\\s*address\\s+([<>]?\\d+)(<>\\d+)?(,\\s*value\\s+([<>]?\\d+)(<>\\d+)?)?)?\\s*\"?\\s*$"
+static pcre *access_parse_regex;
+static pcre_extra *access_parse_regex_study;
+
+#define MAX_SUBSTRINGS 30
+
+void DetectModbusRegisterTests(void);
+
+/** \internal
+ *
+ * \brief this function will free memory associated with DetectModbus
+ *
+ * \param ptr pointer to DetectModbus
+ */
+static void DetectModbusFree(void *ptr) {
+ SCEnter();
+ DetectModbus *modbus = (DetectModbus *) ptr;
+
+ if(modbus) {
+ if (modbus->subfunction)
+ SCFree(modbus->subfunction);
+
+ if (modbus->address)
+ SCFree(modbus->address);
+
+ if (modbus->data)
+ SCFree(modbus->data);
+
+ SCFree(modbus);
+ }
+}
+
+/** \internal
+ *
+ * \brief This function is used to parse Modbus parameters in access mode
+ *
+ * \param str Pointer to the user provided id option
+ *
+ * \retval Pointer to DetectModbusData on success or NULL on failure
+ */
+static DetectModbus *DetectModbusAccessParse(char *str)
+{
+ SCEnter();
+ DetectModbus *modbus = NULL;
+
+ char arg[MAX_SUBSTRINGS];
+ int ov[MAX_SUBSTRINGS], ret, res;
+
+ ret = pcre_exec(access_parse_regex, access_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
+
+ if (ret < 1)
+ goto error;
+
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct Modbus option */
+ modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
+ if (unlikely(modbus == NULL))
+ goto error;
+
+ if (strcmp(arg, "read") == 0)
+ modbus->type = MODBUS_TYP_READ;
+ else if (strcmp(arg, "write") == 0)
+ modbus->type = MODBUS_TYP_WRITE;
+ else
+ goto error;
+
+ if (ret > 2) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (*arg != '\0') {
+ if (strcmp(arg, "discretes") == 0) {
+ if (modbus->type == MODBUS_TYP_WRITE)
+ /* Discrete access is only read access. */
+ goto error;
+
+ modbus->type |= MODBUS_TYP_DISCRETES;
+ }
+ else if (strcmp(arg, "coils") == 0) {
+ modbus->type |= MODBUS_TYP_COILS;
+ }
+ else if (strcmp(arg, "input") == 0) {
+ if (modbus->type == MODBUS_TYP_WRITE) {
+ /* Input access is only read access. */
+ goto error;
+ }
+
+ modbus->type |= MODBUS_TYP_INPUT;
+ }
+ else if (strcmp(arg, "holding") == 0) {
+ modbus->type |= MODBUS_TYP_HOLDING;
+ }
+ else
+ goto error;
+ }
+
+ if (ret > 4) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 4, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct address option */
+ modbus->address = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
+ if (unlikely(modbus->address == NULL))
+ goto error;
+
+ if (arg[0] == '>') {
+ modbus->address->min = atoi((const char*) (arg+1));
+ modbus->address->mode = DETECT_MODBUS_GT;
+ } else if (arg[0] == '<') {
+ modbus->address->min = atoi((const char*) (arg+1));
+ modbus->address->mode = DETECT_MODBUS_LT;
+ } else {
+ modbus->address->min = atoi((const char*) arg);
+ }
+ SCLogDebug("and min/equal address %d", modbus->address->min);
+
+ if (ret > 5) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 5, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (*arg != '\0') {
+ modbus->address->max = atoi((const char*) (arg+2));
+ modbus->address->mode = DETECT_MODBUS_RA;
+ SCLogDebug("and max address %d", modbus->address->max);
+ }
+
+ if (ret > 7) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 7, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (modbus->address->mode != DETECT_MODBUS_EQ) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords (address range and value).");
+ goto error;
+ }
+
+ /* We have a correct address option */
+ modbus->data = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
+ if (unlikely(modbus->data == NULL))
+ goto error;
+
+ if (arg[0] == '>') {
+ modbus->data->min = atoi((const char*) (arg+1));
+ modbus->data->mode = DETECT_MODBUS_GT;
+ } else if (arg[0] == '<') {
+ modbus->data->min = atoi((const char*) (arg+1));
+ modbus->data->mode = DETECT_MODBUS_LT;
+ } else {
+ modbus->data->min = atoi((const char*) arg);
+ }
+ SCLogDebug("and min/equal value %d", modbus->data->min);
+
+ if (ret > 8) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 8, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (*arg != '\0') {
+ modbus->data->max = atoi((const char*) (arg+2));
+ modbus->data->mode = DETECT_MODBUS_RA;
+ SCLogDebug("and max value %d", modbus->data->max);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ SCReturnPtr(modbus, "DetectModbusAccess");
+
+error:
+ if (modbus != NULL)
+ DetectModbusFree(modbus);
+
+ SCReturnPtr(NULL, "DetectModbus");
+}
+
+/** \internal
+ *
+ * \brief This function is used to parse Modbus parameters in function mode
+ *
+ * \param str Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectModbusData on success
+ * \retval NULL on failure
+ */
+static DetectModbus *DetectModbusFunctionParse(char *str)
+{
+ SCEnter();
+ DetectModbus *modbus = NULL;
+
+ char arg[MAX_SUBSTRINGS], *ptr = arg;
+ int ov[MAX_SUBSTRINGS], res, ret;
+
+ ret = pcre_exec(function_parse_regex, function_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
+
+ if (ret < 1)
+ goto error;
+
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct Modbus function option */
+ modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
+ if (unlikely(modbus == NULL))
+ goto error;
+
+ if (isdigit((unsigned char)ptr[0])) {
+ modbus->function = atoi((const char*) ptr);
+ SCLogDebug("will look for modbus function %d", modbus->function);
+
+ if (ret > 2) {
+ res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 3, arg, MAX_SUBSTRINGS);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct address option */
+ modbus->subfunction =(uint16_t *) SCCalloc(1, sizeof(uint16_t));
+ if (modbus->subfunction == NULL)
+ goto error;
+
+ *(modbus->subfunction) = atoi((const char*) arg);
+ SCLogDebug("and subfunction %d", *(modbus->subfunction));
+ }
+ } else {
+ uint8_t neg = 0;
+
+ if (ptr[0] == '!') {
+ neg = 1;
+ ptr++;
+ }
+
+ if (strcmp("assigned", ptr) == 0)
+ modbus->category = MODBUS_CAT_PUBLIC_ASSIGNED;
+ else if (strcmp("unassigned", ptr) == 0)
+ modbus->category = MODBUS_CAT_PUBLIC_UNASSIGNED;
+ else if (strcmp("public", ptr) == 0)
+ modbus->category = MODBUS_CAT_PUBLIC_ASSIGNED | MODBUS_CAT_PUBLIC_UNASSIGNED;
+ else if (strcmp("user", ptr) == 0)
+ modbus->category = MODBUS_CAT_USER_DEFINED;
+ else if (strcmp("reserved", ptr) == 0)
+ modbus->category = MODBUS_CAT_RESERVED;
+ else if (strcmp("all", ptr) == 0)
+ modbus->category = MODBUS_CAT_ALL;
+
+ if (neg)
+ modbus->category = ~modbus->category;
+ SCLogDebug("will look for modbus category function %d", modbus->category);
+ }
+
+ SCReturnPtr(modbus, "DetectModbusFunction");
+
+error:
+ if (modbus != NULL)
+ DetectModbusFree(modbus);
+
+ SCReturnPtr(NULL, "DetectModbus");
+}
+
+/** \internal
+ *
+ * \brief this function is used to add the parsed "id" option into the current signature
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Current Signature
+ * \param str Pointer to the user provided "id" option
+ *
+ * \retval 0 on Success or -1 on Failure
+ */
+static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SCEnter();
+ DetectModbus *modbus = NULL;
+ SigMatch *sm = NULL;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_MODBUS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ if ((modbus = DetectModbusFunctionParse(str)) == NULL) {
+ if ((modbus = DetectModbusAccessParse(str)) == NULL) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
+ goto error;
+ }
+ }
+
+ /* Okay so far so good, lets get this into a SigMatch and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_MODBUS;
+ sm->ctx = (void *) modbus;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MODBUS_MATCH);
+ s->alproto = ALPROTO_MODBUS;
+
+ SCReturnInt(0);
+
+error:
+ if (modbus != NULL)
+ DetectModbusFree(modbus);
+ if (sm != NULL)
+ SCFree(sm);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Registration function for Modbus keyword
+ */
+void DetectModbusRegister(void)
+{
+ SCEnter();
+ sigmatch_table[DETECT_AL_MODBUS].name = "modbus";
+ sigmatch_table[DETECT_AL_MODBUS].Match = NULL;
+ sigmatch_table[DETECT_AL_MODBUS].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_AL_MODBUS].alproto = ALPROTO_MODBUS;
+ sigmatch_table[DETECT_AL_MODBUS].Setup = DetectModbusSetup;
+ sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree;
+ sigmatch_table[DETECT_AL_MODBUS].RegisterTests = DetectModbusRegisterTests;
+
+ const char *eb;
+ int eo, opts = 0;
+
+ SCLogDebug("registering modbus rule option");
+
+ /* Function PARSE_REGEX */
+ function_parse_regex = pcre_compile(PARSE_REGEX_FUNCTION, opts, &eb, &eo, NULL);
+ if (function_parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX_FUNCTION, eo, eb);
+ goto error;
+ }
+
+ function_parse_regex_study = pcre_study(function_parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ /* Access PARSE_REGEX */
+ access_parse_regex = pcre_compile(PARSE_REGEX_ACCESS, opts, &eb, &eo, NULL);
+ if (access_parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX_ACCESS, eo, eb);
+ goto error;
+ }
+
+ access_parse_regex_study = pcre_study(access_parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ SCReturn;
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+#include "detect-engine.h"
+
+#include "util-unittest.h"
+
+/** \test Signature containing a function. */
+static int DetectModbusTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus function\"; "
+ "modbus: function 1; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if (modbus->function != 1) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 1, modbus->function);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a function and a subfunction. */
+static int DetectModbusTest02(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus function and subfunction\"; "
+ "modbus: function 8, subfunction 4; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if ((modbus->function != 8) || (*modbus->subfunction != 4)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", 1, modbus->function);
+ printf("expected subfunction %" PRIu8 ", got %" PRIu16 ": ", 4, *modbus->subfunction);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a function category. */
+static int DetectModbusTest03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.function\"; "
+ "modbus: function reserved; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if (modbus->category != MODBUS_CAT_RESERVED) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", MODBUS_CAT_RESERVED, modbus->category);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a negative function category. */
+static int DetectModbusTest04(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED;
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus function\"; "
+ "modbus: function !assigned; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if (modbus->category != category) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", ~MODBUS_CAT_PUBLIC_ASSIGNED, modbus->category);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a access type. */
+static int DetectModbusTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.access\"; "
+ "modbus: access read; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if (modbus->type != MODBUS_TYP_READ) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", MODBUS_TYP_READ, modbus->type);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a access function. */
+static int DetectModbusTest06(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+
+ uint8_t type = (MODBUS_TYP_READ | MODBUS_TYP_DISCRETES);
+
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.access\"; "
+ "modbus: access read discretes; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if (modbus->type != type) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a read access at an address. */
+static int DetectModbusTest07(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+ DetectModbusMode mode = DETECT_MODBUS_EQ;
+
+ uint8_t type = MODBUS_TYP_READ;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.access\"; "
+ "modbus: access read, address 1000; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if ((modbus->type != type) ||
+ ((*modbus->address).mode != mode) ||
+ ((*modbus->address).min != 1000)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type);
+ printf("expected mode %" PRIu8 ", got %" PRIu16 ": ", mode, (*modbus->address).mode);
+ printf("expected address %" PRIu8 ", got %" PRIu16 ": ", 1000, (*modbus->address).min);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a write access at a range of address. */
+static int DetectModbusTest08(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+ DetectModbusMode mode = DETECT_MODBUS_GT;
+
+ uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS);
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.access\"; "
+ "modbus: access write coils, address >500; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if ((modbus->type != type) ||
+ ((*modbus->address).mode != mode) ||
+ ((*modbus->address).min != 500)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type);
+ printf("expected mode %" PRIu8 ", got %" PRIu16 ": ", mode, (*modbus->address).mode);
+ printf("expected address %" PRIu8 ", got %" PRIu16 ": ", 500, (*modbus->address).min);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Signature containing a write access at a address a range of value. */
+static int DetectModbusTest09(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectModbus *modbus = NULL;
+ DetectModbusMode addressMode = DETECT_MODBUS_EQ;
+ DetectModbusMode valueMode = DETECT_MODBUS_RA;
+
+ uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_HOLDING);
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
+ "(msg:\"Testing modbus.access\"; "
+ "modbus: access write holding, address 100, value 500<>1000; sid:1;)");
+
+ if (de_ctx->sig_list == NULL)
+ goto end;
+
+ if ((de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH] == NULL) ||
+ (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx == NULL)) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_MODBUS_MATCH]->ctx;
+
+ if ((modbus->type != type) ||
+ ((*modbus->address).mode != addressMode) ||
+ ((*modbus->address).min != 100) ||
+ ((*modbus->data).mode != valueMode) ||
+ ((*modbus->data).min != 500) ||
+ ((*modbus->data).max != 1000)) {
+ printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type);
+ printf("expected address mode %" PRIu8 ", got %" PRIu16 ": ", addressMode, (*modbus->address).mode);
+ printf("expected address %" PRIu8 ", got %" PRIu16 ": ", 500, (*modbus->address).min);
+ printf("expected value mode %" PRIu8 ", got %" PRIu16 ": ", valueMode, (*modbus->data).mode);
+ printf("expected min value %" PRIu8 ", got %" PRIu16 ": ", 500, (*modbus->data).min);
+ printf("expected max value %" PRIu8 ", got %" PRIu16 ": ", 1000, (*modbus->data).max);
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectModbus
+ */
+void DetectModbusRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectModbusTest01 - Testing function", DetectModbusTest01, 1);
+ UtRegisterTest("DetectModbusTest02 - Testing function and subfunction", DetectModbusTest02, 1);
+ UtRegisterTest("DetectModbusTest03 - Testing category function", DetectModbusTest03, 1);
+ UtRegisterTest("DetectModbusTest04 - Testing category function in negative", DetectModbusTest04, 1);
+ UtRegisterTest("DetectModbusTest05 - Testing access type", DetectModbusTest05, 1);
+ UtRegisterTest("DetectModbusTest06 - Testing access function", DetectModbusTest06, 1);
+ UtRegisterTest("DetectModbusTest07 - Testing access at address", DetectModbusTest07, 1);
+ UtRegisterTest("DetectModbusTest08 - Testing a range of address", DetectModbusTest08, 1);
+ UtRegisterTest("DetectModbusTest09 - Testing write a range of value", DetectModbusTest09, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-modbus.h b/framework/src/suricata/src/detect-modbus.h
new file mode 100644
index 00000000..408fb7af
--- /dev/null
+++ b/framework/src/suricata/src/detect-modbus.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author David DIALLO <diallo@et.esiea.fr>
+ */
+
+#ifndef __DETECT_MODBUS_H__
+#define __DETECT_MODBUS_H__
+
+#include "app-layer-modbus.h"
+
+typedef enum {
+ DETECT_MODBUS_EQ = 0, /** < EQual operator */
+ DETECT_MODBUS_LT, /** < "Less Than" operator */
+ DETECT_MODBUS_GT, /** < "Greater Than" operator */
+ DETECT_MODBUS_RA, /** < RAnge operator */
+} DetectModbusMode;
+
+typedef struct DetectModbusValue_ {
+ uint16_t min; /** < Modbus minimum [range] or equal value to match */
+ uint16_t max; /** < Modbus maximum value [range] to match */
+ DetectModbusMode mode; /** < Modbus operator used in the address/data signature */
+} DetectModbusValue;
+
+typedef struct DetectModbus_ {
+ uint8_t category; /** < Modbus function code category to match */
+ uint8_t function; /** < Modbus function code to match */
+ uint16_t *subfunction; /** < Modbus subfunction to match */
+ uint8_t type; /** < Modbus access type to match */
+ DetectModbusValue *address; /** < Modbus address to match */
+ DetectModbusValue *data; /** < Modbus data to match */
+} DetectModbus;
+
+/* prototypes */
+void DetectModbusRegister(void);
+
+#endif /* __DETECT_MODBUS_H__ */
diff --git a/framework/src/suricata/src/detect-msg.c b/framework/src/suricata/src/detect-msg.c
new file mode 100644
index 00000000..e8b87529
--- /dev/null
+++ b/framework/src/suricata/src/detect-msg.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the msg keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "util-classification-config.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+static int DetectMsgSetup (DetectEngineCtx *, Signature *, char *);
+void DetectMsgRegisterTests(void);
+
+void DetectMsgRegister (void)
+{
+ sigmatch_table[DETECT_MSG].name = "msg";
+ sigmatch_table[DETECT_MSG].desc = "information about the rule and the possible alert";
+ sigmatch_table[DETECT_MSG].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#msg-message";
+ sigmatch_table[DETECT_MSG].Match = NULL;
+ sigmatch_table[DETECT_MSG].Setup = DetectMsgSetup;
+ sigmatch_table[DETECT_MSG].Free = NULL;
+ sigmatch_table[DETECT_MSG].RegisterTests = DetectMsgRegisterTests;
+}
+
+static int DetectMsgSetup (DetectEngineCtx *de_ctx, Signature *s, char *msgstr)
+{
+ char *str = NULL;
+ uint16_t len;
+
+ if (strlen(msgstr) == 0)
+ goto error;
+
+ /* strip "'s */
+ if (msgstr[0] == '\"' && msgstr[strlen(msgstr)-1] == '\"') {
+ str = SCStrdup(msgstr+1);
+ if (unlikely(str == NULL))
+ goto error;
+ str[strlen(msgstr)-2] = '\0';
+ } else if (msgstr[1] == '\"' && msgstr[strlen(msgstr)-1] == '\"') {
+ /* XXX do this parsing in a better way */
+ str = SCStrdup(msgstr+2);
+ if (unlikely(str == NULL))
+ goto error;
+ str[strlen(msgstr)-3] = '\0';
+ //printf("DetectMsgSetup: format hack applied: \'%s\'\n", str);
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "format error \'%s\'", msgstr);
+ goto error;
+ }
+
+ len = strlen(str);
+ if (len == 0)
+ goto error;
+
+ char converted = 0;
+
+ {
+ uint16_t i, x;
+ uint8_t escape = 0;
+
+ /* it doesn't matter if we need to escape or not we remove the extra "\" to mimic snort */
+ for (i = 0, x = 0; i < len; i++) {
+ //printf("str[%02u]: %c\n", i, str[i]);
+ if(!escape && str[i] == '\\') {
+ escape = 1;
+ } else if (escape) {
+ if (str[i] != ':' &&
+ str[i] != ';' &&
+ str[i] != '\\' &&
+ str[i] != '\"')
+ {
+ SCLogDebug("character \"%c\" does not need to be escaped but is" ,str[i]);
+ }
+ escape = 0;
+ converted = 1;
+
+ str[x] = str[i];
+ x++;
+ }else{
+ str[x] = str[i];
+ x++;
+ }
+
+ }
+#if 0 //def DEBUG
+ if (SCLogDebugEnabled()) {
+ for (i = 0; i < x; i++) {
+ printf("%c", str[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (converted) {
+ len = x;
+ str[len] = '\0';
+ }
+ }
+
+ s->msg = SCMalloc(len + 1);
+ if (s->msg == NULL)
+ goto error;
+
+ strlcpy(s->msg, str, len + 1);
+
+ SCFree(str);
+ return 0;
+
+error:
+ SCFree(str);
+ return -1;
+}
+
+/* -------------------------------------Unittests-----------------------------*/
+
+#ifdef UNITTESTS
+static int DetectMsgParseTest01(void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ char *teststringparsed = "flow stateless to_server";
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"flow stateless to_server\"; flow:stateless,to_server; content:\"flowstatelesscheck\"; classtype:bad-unknown; sid: 40000002; rev: 1;)");
+ if(sig == NULL)
+ goto end;
+
+ if (strcmp(sig->msg, teststringparsed) != 0) {
+ printf("got \"%s\", expected: \"%s\": ", sig->msg, teststringparsed);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (sig != NULL)
+ SigFree(sig);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int DetectMsgParseTest02(void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ char *teststringparsed = "msg escape tests wxy'\"\\;:";
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"msg escape tests \\w\\x\\y\\'\\\"\\\\;\\:\"; flow:to_server,established; content:\"blah\"; uricontent:\"/blah/\"; sid: 100;)");
+ if(sig == NULL)
+ goto end;
+
+ if (strcmp(sig->msg, teststringparsed) != 0) {
+ printf("got \"%s\", expected: \"%s\": ",sig->msg, teststringparsed);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (sig != NULL)
+ SigFree(sig);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectMsg
+ */
+void DetectMsgRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectMsgParseTest01", DetectMsgParseTest01, 1);
+ UtRegisterTest("DetectMsgParseTest02", DetectMsgParseTest02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-msg.h b/framework/src/suricata/src/detect-msg.h
new file mode 100644
index 00000000..a21d5110
--- /dev/null
+++ b/framework/src/suricata/src/detect-msg.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_MSG_H__
+#define __DETECT_MSG_H__
+
+/* prototypes */
+void DetectMsgRegister (void);
+
+#endif /* __DETECT_MSG_H__ */
+
diff --git a/framework/src/suricata/src/detect-noalert.c b/framework/src/suricata/src/detect-noalert.c
new file mode 100644
index 00000000..b4f69af4
--- /dev/null
+++ b/framework/src/suricata/src/detect-noalert.c
@@ -0,0 +1,53 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the noalert keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "util-debug.h"
+
+static int DetectNoalertSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectNoalertRegister (void)
+{
+ sigmatch_table[DETECT_NOALERT].name = "noalert";
+ sigmatch_table[DETECT_NOALERT].Match = NULL;
+ sigmatch_table[DETECT_NOALERT].Setup = DetectNoalertSetup;
+ sigmatch_table[DETECT_NOALERT].Free = NULL;
+ sigmatch_table[DETECT_NOALERT].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_NOALERT].flags |= SIGMATCH_NOOPT;
+}
+
+static int DetectNoalertSetup (DetectEngineCtx *de_ctx, Signature *s, char *nullstr)
+{
+ if (nullstr != NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "nocase has no value");
+ return -1;
+ }
+
+ s->flags |= SIG_FLAG_NOALERT;
+ return 0;
+}
+
diff --git a/framework/src/suricata/src/detect-noalert.h b/framework/src/suricata/src/detect-noalert.h
new file mode 100644
index 00000000..abe39efc
--- /dev/null
+++ b/framework/src/suricata/src/detect-noalert.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_NOALERT_H__
+#define __DETECT_NOALERT_H__
+
+/* prototypes */
+void DetectNoalertRegister (void);
+
+#endif /* __DETECT_NOALERT_H__ */
+
diff --git a/framework/src/suricata/src/detect-nocase.c b/framework/src/suricata/src/detect-nocase.c
new file mode 100644
index 00000000..9968e3c4
--- /dev/null
+++ b/framework/src/suricata/src/detect-nocase.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the nocase keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-pcre.h"
+#include "detect-http-client-body.h"
+#include "detect-http-cookie.h"
+#include "detect-http-header.h"
+#include "detect-http-method.h"
+#include "detect-http-uri.h"
+
+#include "util-debug.h"
+
+static int DetectNocaseSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectNocaseRegister(void)
+{
+ sigmatch_table[DETECT_NOCASE].name = "nocase";
+ sigmatch_table[DETECT_NOCASE].desc = "modify content match to be case insensitive";
+ sigmatch_table[DETECT_NOCASE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Nocase";
+ sigmatch_table[DETECT_NOCASE].Match = NULL;
+ sigmatch_table[DETECT_NOCASE].Setup = DetectNocaseSetup;
+ sigmatch_table[DETECT_NOCASE].Free = NULL;
+ sigmatch_table[DETECT_NOCASE].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_NOCASE].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_NOCASE].flags |= SIGMATCH_PAYLOAD;
+}
+
+/**
+ * \internal
+ * \brief Apply the nocase keyword to the last pattern match, either content or uricontent
+ * \param det_ctx detection engine ctx
+ * \param s signature
+ * \param nullstr should be null
+ * \retval 0 ok
+ * \retval -1 failure
+ */
+static int DetectNocaseSetup (DetectEngineCtx *de_ctx, Signature *s, char *nullstr)
+{
+ SCEnter();
+
+ SigMatch *pm = NULL;
+ int ret = -1;
+
+ if (nullstr != NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "nocase has value");
+ goto end;
+ }
+
+ /* retrive the sm to apply the depth against */
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ pm = SigMatchGetLastSMFromLists(s, 2, DETECT_CONTENT, s->sm_lists_tail[s->list]);
+ } else {
+ pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ }
+ if (pm == NULL) {
+ SCLogError(SC_ERR_NOCASE_MISSING_PATTERN, "nocase needs "
+ "preceding content, uricontent option, http_client_body, "
+ "http_server_body, http_header option, http_raw_header option, "
+ "http_method option, http_cookie, http_raw_uri, "
+ "http_stat_msg, http_stat_code, http_user_agent or "
+ "file_data/dce_stub_data sticky buffer options");
+ goto end;
+ }
+
+
+ /* verify other conditions. */
+ DetectContentData *cd = (DetectContentData *)pm->ctx;;
+
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple nocase modifiers with the same content");
+ goto end;
+ }
+ cd->flags |= DETECT_CONTENT_NOCASE;
+ /* Recreate the context with nocase chars */
+ BoyerMooreCtxToNocase(cd->bm_ctx, cd->content, cd->content_len);
+
+ ret = 0;
+ end:
+ SCReturnInt(ret);
+}
diff --git a/framework/src/suricata/src/detect-nocase.h b/framework/src/suricata/src/detect-nocase.h
new file mode 100644
index 00000000..4ba6723e
--- /dev/null
+++ b/framework/src/suricata/src/detect-nocase.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_NOCASE_H__
+#define __DETECT_NOCASE_H__
+
+/* prototypes */
+void DetectNocaseRegister (void);
+
+#endif /* __DETECT_NOCASE_H__ */
+
diff --git a/framework/src/suricata/src/detect-offset.c b/framework/src/suricata/src/detect-offset.c
new file mode 100644
index 00000000..65372ec0
--- /dev/null
+++ b/framework/src/suricata/src/detect-offset.c
@@ -0,0 +1,158 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the offset keyword.
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-byte-extract.h"
+#include "app-layer.h"
+
+#include "flow-var.h"
+
+#include "util-debug.h"
+
+static int DetectOffsetSetup(DetectEngineCtx *, Signature *, char *);
+
+void DetectOffsetRegister (void)
+{
+ sigmatch_table[DETECT_OFFSET].name = "offset";
+ sigmatch_table[DETECT_OFFSET].desc = "designate from which byte in the payload will be checked to find a match";
+ sigmatch_table[DETECT_OFFSET].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Offset";
+ sigmatch_table[DETECT_OFFSET].Match = NULL;
+ sigmatch_table[DETECT_OFFSET].Setup = DetectOffsetSetup;
+ sigmatch_table[DETECT_OFFSET].Free = NULL;
+ sigmatch_table[DETECT_OFFSET].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_OFFSET].flags |= SIGMATCH_PAYLOAD;
+}
+
+int DetectOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, char *offsetstr)
+{
+ char *str = offsetstr;
+ char dubbed = 0;
+ SigMatch *pm = NULL;
+ int ret = -1;
+
+ /* strip "'s */
+ if (offsetstr[0] == '\"' && offsetstr[strlen(offsetstr)-1] == '\"') {
+ str = SCStrdup(offsetstr+1);
+ if (unlikely(str == NULL))
+ goto end;
+ str[strlen(offsetstr) - 2] = '\0';
+ dubbed = 1;
+ }
+
+ /* retrive the sm to apply the depth against */
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ pm = SigMatchGetLastSMFromLists(s, 2, DETECT_CONTENT, s->sm_lists_tail[s->list]);
+ } else {
+ pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ }
+ if (pm == NULL) {
+ SCLogError(SC_ERR_OFFSET_MISSING_CONTENT, "offset needs "
+ "preceding content, uricontent option, http_client_body, "
+ "http_server_body, http_header option, http_raw_header option, "
+ "http_method option, http_cookie, http_raw_uri, "
+ "http_stat_msg, http_stat_code, http_user_agent or "
+ "file_data/dce_stub_data sticky buffer options");
+ goto end;
+ }
+
+
+ /* verify other conditions */
+ DetectContentData *cd = (DetectContentData *)pm->ctx;
+
+ if (cd->flags & DETECT_CONTENT_OFFSET) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple offsets for the same content. ");
+ goto end;
+ }
+ if ((cd->flags & DETECT_CONTENT_WITHIN) || (cd->flags & DETECT_CONTENT_DISTANCE)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative "
+ "keyword like within/distance with a absolute "
+ "relative keyword like depth/offset for the same "
+ "content." );
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "negated keyword set along with a fast_pattern");
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "keyword set along with a fast_pattern:only;");
+ goto end;
+ }
+ if (str[0] != '-' && isalpha((unsigned char)str[0])) {
+ SigMatch *bed_sm =
+ DetectByteExtractRetrieveSMVar(str, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "unknown byte_extract var "
+ "seen in offset - %s\n", str);
+ goto end;
+ }
+ cd->offset = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ cd->flags |= DETECT_CONTENT_OFFSET_BE;
+ } else {
+ cd->offset = (uint32_t)atoi(str);
+ if (cd->depth != 0) {
+ if (cd->depth < cd->content_len) {
+ SCLogDebug("depth increased to %"PRIu32" to match pattern len",
+ cd->content_len);
+ cd->depth = cd->content_len;
+ }
+ /* Updating the depth as is relative to the offset */
+ cd->depth += cd->offset;
+ }
+ }
+ cd->flags |= DETECT_CONTENT_OFFSET;
+
+ ret = 0;
+ end:
+ if (dubbed)
+ SCFree(str);
+ return ret;
+}
+
diff --git a/framework/src/suricata/src/detect-offset.h b/framework/src/suricata/src/detect-offset.h
new file mode 100644
index 00000000..76befe17
--- /dev/null
+++ b/framework/src/suricata/src/detect-offset.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_OFFSET_H__
+#define __DETECT_OFFSET_H__
+
+/* prototypes */
+void DetectOffsetRegister (void);
+
+#endif /* __DETECT_OFFSET_H__ */
+
diff --git a/framework/src/suricata/src/detect-parse.c b/framework/src/suricata/src/detect-parse.c
new file mode 100644
index 00000000..d077791e
--- /dev/null
+++ b/framework/src/suricata/src/detect-parse.c
@@ -0,0 +1,3476 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * signature parser
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-address.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-uricontent.h"
+#include "detect-reference.h"
+#include "detect-ipproto.h"
+#include "detect-flow.h"
+#include "detect-app-layer-protocol.h"
+#include "detect-engine-apt-event.h"
+#include "detect-lua.h"
+#include "detect-app-layer-event.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+#include "decode.h"
+
+#include "flow.h"
+
+#include "util-rule-vars.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+
+#include "util-classification-config.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "string.h"
+#include "detect-parse.h"
+#include "detect-engine-iponly.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer.h"
+
+extern int sc_set_caps;
+
+static pcre *config_pcre = NULL;
+static pcre *option_pcre = NULL;
+static pcre_extra *config_pcre_extra = NULL;
+static pcre_extra *option_pcre_extra = NULL;
+
+static uint32_t dbg_srcportany_cnt = 0;
+static uint32_t dbg_dstportany_cnt = 0;
+
+/**
+ * \brief We use this as data to the hash table DetectEngineCtx->dup_sig_hash_table.
+ */
+typedef struct SigDuplWrapper_ {
+ /* the signature we want to wrap */
+ Signature *s;
+ /* the signature right before the above signatue in the det_ctx->sig_list */
+ Signature *s_prev;
+} SigDuplWrapper;
+
+#define CONFIG_PARTS 8
+
+#define CONFIG_ACTION 0
+#define CONFIG_PROTO 1
+#define CONFIG_SRC 2
+#define CONFIG_SP 3
+#define CONFIG_DIREC 4
+#define CONFIG_DST 5
+#define CONFIG_DP 6
+#define CONFIG_OPTS 7
+
+/* if enclosed in [], spaces are allowed */
+#define CONFIG_PCRE_SRCDST "(" \
+ "[\\[\\]A-z0-9\\.\\:_\\$\\!\\-,\\/]+" \
+ "|" \
+ "\\[[\\[\\]A-z0-9\\.\\:_\\$\\!\\-,\\/\\s]+\\]"\
+ ")"
+
+/* if enclosed in [], spaces are allowed */
+#define CONFIG_PCRE_PORT "(" \
+ "[\\:A-z0-9_\\$\\!,]+"\
+ "|"\
+ "\\[[\\:A-z0-9_\\$\\!,\\s]+\\]"\
+ ")"
+
+/* format: action space(s) protocol spaces(s) src space(s) sp spaces(s) dir spaces(s) dst spaces(s) dp spaces(s) options */
+#define CONFIG_PCRE "^([A-z]+)\\s+([A-z0-9\\-]+)\\s+" \
+ CONFIG_PCRE_SRCDST \
+ "\\s+"\
+ CONFIG_PCRE_PORT \
+ "\\s+(-\\>|\\<\\>|\\<\\-)\\s+" \
+ CONFIG_PCRE_SRCDST \
+ "\\s+" \
+ CONFIG_PCRE_PORT \
+ "(?:\\s+\\((.*)?(?:\\s*)\\))?(?:(?:\\s*)\\n)?\\s*$"
+#define OPTION_PARTS 3
+#define OPTION_PCRE "^\\s*([A-z_0-9-\\.]+)(?:\\s*\\:\\s*(.*)(?<!\\\\))?\\s*;\\s*(?:\\s*(.*))?\\s*$"
+
+/** helper structure for sig parsing */
+typedef struct SignatureParser_ {
+ char action[DETECT_MAX_RULE_SIZE];
+ char protocol[DETECT_MAX_RULE_SIZE];
+ char direction[DETECT_MAX_RULE_SIZE];
+ char src[DETECT_MAX_RULE_SIZE];
+ char dst[DETECT_MAX_RULE_SIZE];
+ char sp[DETECT_MAX_RULE_SIZE];
+ char dp[DETECT_MAX_RULE_SIZE];
+ char opts[DETECT_MAX_RULE_SIZE];
+} SignatureParser;
+
+int DetectEngineContentModifierBufferSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg,
+ uint8_t sm_type, uint8_t sm_list,
+ AppProto alproto, void (*CustomCallback)(Signature *s))
+{
+ SigMatch *sm = NULL;
+ int ret = -1;
+
+ if (arg != NULL && strcmp(arg, "") != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "%s shouldn't be supplied "
+ "with an argument", sigmatch_table[sm_type].name);
+ goto end;
+ }
+
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "\"%s\" keyword seen "
+ "with a sticky buffer still set. Reset sticky buffer "
+ "with pkt_data before using the modifier.",
+ sigmatch_table[sm_type].name);
+ goto end;
+ }
+ /* for now let's hardcode it as http */
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != alproto) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting "
+ "alprotos set");
+ goto end;
+ }
+
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "\"%s\" keyword "
+ "found inside the rule without a content context. "
+ "Please use a \"content\" keyword before using the "
+ "\"%s\" keyword", sigmatch_table[sm_type].name,
+ sigmatch_table[sm_type].name);
+ goto end;
+ }
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "%s rule can not "
+ "be used with the rawbytes rule keyword",
+ sigmatch_table[sm_type].name);
+ goto end;
+ }
+ if (cd->flags & (DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) {
+ SigMatch *pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, sm->prev,
+ DETECT_PCRE, sm->prev);
+ if (pm != NULL) {
+ if (pm->type == DETECT_CONTENT) {
+ DetectContentData *tmp_cd = (DetectContentData *)pm->ctx;
+ tmp_cd->flags &= ~DETECT_CONTENT_RELATIVE_NEXT;
+ } else {
+ DetectPcreData *tmp_pd = (DetectPcreData *)pm->ctx;
+ tmp_pd->flags &= ~DETECT_PCRE_RELATIVE_NEXT;
+ }
+ }
+
+ pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, s->sm_lists_tail[sm_list],
+ DETECT_PCRE, s->sm_lists_tail[sm_list]);
+ if (pm != NULL) {
+ if (pm->type == DETECT_CONTENT) {
+ DetectContentData *tmp_cd = (DetectContentData *)pm->ctx;
+ tmp_cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else {
+ DetectPcreData *tmp_pd = (DetectPcreData *)pm->ctx;
+ tmp_pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+ }
+ }
+ if (CustomCallback != NULL)
+ CustomCallback(s);
+ s->alproto = alproto;
+ s->flags |= SIG_FLAG_APPLAYER;
+
+ /* transfer the sm from the pmatch list to hcbdmatch list */
+ SigMatchTransferSigMatchAcrossLists(sm,
+ &s->sm_lists[DETECT_SM_LIST_PMATCH],
+ &s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ &s->sm_lists[sm_list],
+ &s->sm_lists_tail[sm_list]);
+
+ ret = 0;
+ end:
+ return ret;
+}
+
+uint32_t DbgGetSrcPortAnyCnt(void)
+{
+ return dbg_srcportany_cnt;
+}
+
+uint32_t DbgGetDstPortAnyCnt(void)
+{
+ return dbg_dstportany_cnt;
+}
+
+SigMatch *SigMatchAlloc(void)
+{
+ SigMatch *sm = SCMalloc(sizeof(SigMatch));
+ if (unlikely(sm == NULL))
+ return NULL;
+
+ memset(sm, 0, sizeof(SigMatch));
+ sm->prev = NULL;
+ sm->next = NULL;
+ return sm;
+}
+
+/** \brief free a SigMatch
+ * \param sm SigMatch to free.
+ */
+void SigMatchFree(SigMatch *sm)
+{
+ if (sm == NULL)
+ return;
+
+ /** free the ctx, for that we call the Free func */
+ if (sm->ctx != NULL) {
+ if (sigmatch_table[sm->type].Free != NULL) {
+ sigmatch_table[sm->type].Free(sm->ctx);
+ }
+ }
+ SCFree(sm);
+}
+
+/* Get the detection module by name */
+SigTableElmt *SigTableGet(char *name)
+{
+ SigTableElmt *st = NULL;
+ int i = 0;
+
+ for (i = 0; i < DETECT_TBLSIZE; i++) {
+ st = &sigmatch_table[i];
+
+ if (st->name != NULL) {
+ if (strcasecmp(name,st->name) == 0)
+ return st;
+ if (st->alias != NULL && strcasecmp(name,st->alias) == 0)
+ return st;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Append a SigMatch to the list type.
+ *
+ * \param s Signature.
+ * \param new The sig match to append.
+ * \param list The list to append to.
+ */
+void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
+{
+ if (s->sm_lists[list] == NULL) {
+ s->sm_lists[list] = new;
+ s->sm_lists_tail[list] = new;
+ new->next = NULL;
+ new->prev = NULL;
+ } else {
+ SigMatch *cur = s->sm_lists_tail[list];
+ cur->next = new;
+ new->prev = cur;
+ new->next = NULL;
+ s->sm_lists_tail[list] = new;
+ }
+
+ new->idx = s->sm_cnt;
+ s->sm_cnt++;
+}
+
+void SigMatchRemoveSMFromList(Signature *s, SigMatch *sm, int sm_list)
+{
+ if (sm == s->sm_lists[sm_list]) {
+ s->sm_lists[sm_list] = sm->next;
+ }
+ if (sm == s->sm_lists_tail[sm_list]) {
+ s->sm_lists_tail[sm_list] = sm->prev;
+ }
+ if (sm->prev != NULL)
+ sm->prev->next = sm->next;
+ if (sm->next != NULL)
+ sm->next->prev = sm->prev;
+
+ return;
+}
+
+/**
+ * \brief Returns a pointer to the last SigMatch instance of a particular type
+ * in a Signature of the payload list.
+ *
+ * \param s Pointer to the tail of the sigmatch list
+ * \param type SigMatch type which has to be searched for in the Signature.
+ *
+ * \retval match Pointer to the last SigMatch instance of type 'type'.
+ */
+static inline SigMatch *SigMatchGetLastSM(SigMatch *sm, uint8_t type)
+{
+ while (sm != NULL) {
+ if (sm->type == type) {
+ return sm;
+ }
+ sm = sm->prev;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Returns the sm with the largest index (added latest) from all the lists.
+ *
+ * \retval Pointer to Last sm.
+ */
+SigMatch *SigMatchGetLastSMFromLists(Signature *s, int args, ...)
+{
+ if (args == 0 || args % 2 != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "You need to send an even no of args "
+ "(non zero as well) to this function, since we need a "
+ "SigMatch list for every SigMatch type(send a map of sm_type "
+ "and sm_list) sent");
+ /* as this is a bug we should abort to ease debugging */
+ BUG_ON(1);
+ }
+
+ SigMatch *sm_last = NULL;
+ SigMatch *sm_new;
+ int i;
+
+ va_list ap;
+ va_start(ap, args);
+
+ for (i = 0; i < args; i += 2) {
+ int sm_type = va_arg(ap, int);
+ SigMatch *sm_list = va_arg(ap, SigMatch *);
+ sm_new = SigMatchGetLastSM(sm_list, sm_type);
+ if (sm_new == NULL)
+ continue;
+ if (sm_last == NULL || sm_new->idx > sm_last->idx)
+ sm_last = sm_new;
+ }
+
+ va_end(ap);
+
+ return sm_last;
+}
+
+void SigMatchTransferSigMatchAcrossLists(SigMatch *sm,
+ SigMatch **src_sm_list, SigMatch **src_sm_list_tail,
+ SigMatch **dst_sm_list, SigMatch **dst_sm_list_tail)
+{
+ /* we won't do any checks for args */
+
+ if (sm->prev != NULL)
+ sm->prev->next = sm->next;
+ if (sm->next != NULL)
+ sm->next->prev = sm->prev;
+
+ if (sm == *src_sm_list)
+ *src_sm_list = sm->next;
+ if (sm == *src_sm_list_tail)
+ *src_sm_list_tail = sm->prev;
+
+ if (*dst_sm_list == NULL) {
+ *dst_sm_list = sm;
+ *dst_sm_list_tail = sm;
+ sm->next = NULL;
+ sm->prev = NULL;
+ } else {
+ SigMatch *cur = *dst_sm_list_tail;
+ cur->next = sm;
+ sm->prev = cur;
+ sm->next = NULL;
+ *dst_sm_list_tail = sm;
+ }
+
+ return;
+}
+
+int SigMatchListSMBelongsTo(Signature *s, SigMatch *key_sm)
+{
+ int list = 0;
+
+ for (list = 0; list < DETECT_SM_LIST_MAX; list++) {
+ SigMatch *sm = s->sm_lists[list];
+ while (sm != NULL) {
+ if (sm == key_sm)
+ return list;
+ sm = sm->next;
+ }
+ }
+
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unable to find the sm in any of the "
+ "sm lists");
+ return -1;
+}
+
+void SigParsePrepare(void)
+{
+ char *regexstr = CONFIG_PCRE;
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ opts |= PCRE_UNGREEDY;
+ config_pcre = pcre_compile(regexstr, opts, &eb, &eo, NULL);
+ if(config_pcre == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", regexstr, eo, eb);
+ exit(1);
+ }
+
+ config_pcre_extra = pcre_study(config_pcre, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ exit(1);
+ }
+
+ regexstr = OPTION_PCRE;
+ opts |= PCRE_UNGREEDY;
+
+ option_pcre = pcre_compile(regexstr, opts, &eb, &eo, NULL);
+ if(option_pcre == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", regexstr, eo, eb);
+ exit(1);
+ }
+
+ option_pcre_extra = pcre_study(option_pcre, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ exit(1);
+ }
+}
+
+static int SigParseOptions(DetectEngineCtx *de_ctx, Signature *s, char *optstr, char *output, size_t output_size)
+{
+#define MAX_SUBSTRINGS 30
+ int ov[MAX_SUBSTRINGS];
+ int ret = 0;
+ SigTableElmt *st = NULL;
+ char optname[64];
+ char optvalue[DETECT_MAX_RULE_SIZE] = "";
+
+ ret = pcre_exec(option_pcre, option_pcre_extra, optstr, strlen(optstr), 0, 0, ov, MAX_SUBSTRINGS);
+ /* if successful, we either have:
+ * 2: keyword w/o value
+ * 3: keyword w value, final opt OR keyword w/o value, more options coming
+ * 4: keyword w value, more options coming
+ */
+ if (ret != 2 && ret != 3 && ret != 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec failed: ret %" PRId32 ", optstr \"%s\"", ret, optstr);
+ goto error;
+ }
+
+ /* extract the substrings */
+ if (pcre_copy_substring(optstr, ov, MAX_SUBSTRINGS, 1, optname, sizeof(optname)) < 0) {
+ goto error;
+ }
+
+ /* Call option parsing */
+ st = SigTableGet(optname);
+ if (st == NULL) {
+ SCLogError(SC_ERR_RULE_KEYWORD_UNKNOWN, "unknown rule keyword '%s'.", optname);
+ goto error;
+ }
+
+ if (ret == 3) {
+ if (st->flags & SIGMATCH_NOOPT) {
+ if (pcre_copy_substring(optstr, ov, MAX_SUBSTRINGS, 2, output, output_size) < 0) {
+ goto error;
+ }
+ } else {
+ if (pcre_copy_substring(optstr, ov, MAX_SUBSTRINGS, 2, optvalue, sizeof(optvalue)) < 0) {
+ goto error;
+ }
+ }
+ } else if (ret == 4) {
+ if (pcre_copy_substring(optstr, ov, MAX_SUBSTRINGS, 2, optvalue, sizeof(optvalue)) < 0) {
+ goto error;
+ }
+ if (pcre_copy_substring(optstr, ov, MAX_SUBSTRINGS, 3, output, output_size) < 0) {
+ goto error;
+ }
+ }
+
+ if (!(st->flags & (SIGMATCH_NOOPT|SIGMATCH_OPTIONAL_OPT))) {
+ if (strlen(optvalue) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid formatting or malformed option to %s keyword: \'%s\'",
+ optname, optstr);
+ goto error;
+ }
+ }
+
+ /* setup may or may not add a new SigMatch to the list */
+ if (st->Setup(de_ctx, s, strlen(optvalue) ? optvalue : NULL) < 0) {
+ SCLogDebug("\"%s\" failed to setup", st->name);
+ goto error;
+ }
+
+ if (ret == 4) {
+ return 1;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/** \brief Parse address string and update signature
+ *
+ * \retval 0 ok, -1 error
+ */
+int SigParseAddress(const DetectEngineCtx *de_ctx,
+ Signature *s, const char *addrstr, char flag)
+{
+ SCLogDebug("Address Group \"%s\" to be parsed now", addrstr);
+
+ /* pass on to the address(list) parser */
+ if (flag == 0) {
+ if (strcasecmp(addrstr, "any") == 0)
+ s->flags |= SIG_FLAG_SRC_ANY;
+
+ if (DetectAddressParse(de_ctx, &s->src, (char *)addrstr) < 0)
+ goto error;
+ } else {
+ if (strcasecmp(addrstr, "any") == 0)
+ s->flags |= SIG_FLAG_DST_ANY;
+
+ if (DetectAddressParse(de_ctx, &s->dst, (char *)addrstr) < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Parses the protocol supplied by the Signature.
+ *
+ * http://www.iana.org/assignments/protocol-numbers
+ *
+ * \param s Pointer to the Signature instance to which the parsed
+ * protocol has to be added.
+ * \param protostr Pointer to the character string containing the protocol name.
+ *
+ * \retval 0 On successfully parsing the protocl sent as the argument.
+ * \retval -1 On failure
+ */
+int SigParseProto(Signature *s, const char *protostr)
+{
+ SCEnter();
+
+ int r = DetectProtoParse(&s->proto, (char *)protostr);
+ if (r < 0) {
+ s->alproto = AppLayerGetProtoByName((char *)protostr);
+ /* indicate that the signature is app-layer */
+ if (s->alproto != ALPROTO_UNKNOWN)
+ s->flags |= SIG_FLAG_APPLAYER;
+ else {
+ SCLogError(SC_ERR_UNKNOWN_PROTOCOL, "protocol \"%s\" cannot be used "
+ "in a signature. Either detection for this protocol "
+ "supported yet OR detection has been disabled for "
+ "protocol through the yaml option "
+ "app-layer.protocols.%s.detection-enabled", protostr,
+ protostr);
+ SCReturnInt(-1);
+ }
+ }
+
+ /* if any of these flags are set they are set in a mutually exclusive
+ * manner */
+ if (s->proto.flags & DETECT_PROTO_ONLY_PKT) {
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+ } else if (s->proto.flags & DETECT_PROTO_ONLY_STREAM) {
+ s->flags |= SIG_FLAG_REQUIRE_STREAM;
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Parses the port(source or destination) field, from a Signature.
+ *
+ * \param s Pointer to the signature which has to be updated with the
+ * port information.
+ * \param portstr Pointer to the character string containing the port info.
+ * \param Flag which indicates if the portstr received is src or dst
+ * port. For src port: flag = 0, dst port: flag = 1.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SigParsePort(const DetectEngineCtx *de_ctx,
+ Signature *s, const char *portstr, char flag)
+{
+ int r = 0;
+
+ /* XXX VJ exclude handling this for none UDP/TCP proto's */
+
+ SCLogDebug("Port group \"%s\" to be parsed", portstr);
+
+ if (flag == 0) {
+ if (strcasecmp(portstr, "any") == 0)
+ s->flags |= SIG_FLAG_SP_ANY;
+
+ r = DetectPortParse(de_ctx, &s->sp, (char *)portstr);
+ } else if (flag == 1) {
+ if (strcasecmp(portstr, "any") == 0)
+ s->flags |= SIG_FLAG_DP_ANY;
+
+ r = DetectPortParse(de_ctx, &s->dp, (char *)portstr);
+ }
+
+ if (r < 0)
+ return -1;
+
+ return 0;
+}
+
+/** \retval 1 valid
+ * \retval 0 invalid
+ */
+static int SigParseActionRejectValidate(const char *action)
+{
+#ifdef HAVE_LIBNET11
+#ifdef HAVE_LIBCAP_NG
+ if (sc_set_caps == TRUE) {
+ SCLogError(SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG, "Libnet 1.1 is "
+ "incompatible with POSIX based capabilities with privs dropping. "
+ "For rejects to work, run as root/super user.");
+ return 0;
+ }
+#endif
+#else /* no libnet 1.1 */
+ SCLogError(SC_ERR_LIBNET_REQUIRED_FOR_ACTION, "Libnet 1.1.x is "
+ "required for action \"%s\" but is not compiled into Suricata",
+ action);
+ return 0;
+#endif
+ return 1;
+}
+
+/**
+ * \brief Parses the action that has been used by the Signature and allots it
+ * to its Signature instance.
+ *
+ * \param s Pointer to the Signature instance to which the action belongs.
+ * \param action Pointer to the action string used by the Signature.
+ *
+ * \retval 0 On successfully parsing the action string and adding it to the
+ * Signature.
+ * \retval -1 On failure.
+ */
+int SigParseAction(Signature *s, const char *action)
+{
+ if (strcasecmp(action, "alert") == 0) {
+ s->action = ACTION_ALERT;
+ return 0;
+ } else if (strcasecmp(action, "drop") == 0) {
+ s->action = ACTION_DROP;
+ return 0;
+ } else if (strcasecmp(action, "pass") == 0) {
+ s->action = ACTION_PASS;
+ return 0;
+ } else if (strcasecmp(action, "reject") == 0) {
+ if (!(SigParseActionRejectValidate(action)))
+ return -1;
+ s->action = ACTION_REJECT|ACTION_DROP;
+ return 0;
+ } else if (strcasecmp(action, "rejectsrc") == 0) {
+ if (!(SigParseActionRejectValidate(action)))
+ return -1;
+ s->action = ACTION_REJECT|ACTION_DROP;
+ return 0;
+ } else if (strcasecmp(action, "rejectdst") == 0) {
+ if (!(SigParseActionRejectValidate(action)))
+ return -1;
+ s->action = ACTION_REJECT_DST|ACTION_DROP;
+ return 0;
+ } else if (strcasecmp(action, "rejectboth") == 0) {
+ if (!(SigParseActionRejectValidate(action)))
+ return -1;
+ s->action = ACTION_REJECT_BOTH|ACTION_DROP;
+ return 0;
+ } else {
+ SCLogError(SC_ERR_INVALID_ACTION,"An invalid action \"%s\" was given",action);
+ return -1;
+ }
+}
+
+/**
+ * \internal
+ * \brief split a signature string into a few blocks for further parsing
+ */
+static int SigParseBasics(const DetectEngineCtx *de_ctx,
+ Signature *s, const char *sigstr, SignatureParser *parser, uint8_t addrs_direction)
+{
+#define MAX_SUBSTRINGS 30
+ int ov[MAX_SUBSTRINGS];
+ int ret = 0;
+
+ ret = pcre_exec(config_pcre, config_pcre_extra, sigstr, strlen(sigstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 8 && ret != 9) {
+ SCLogDebug("pcre_exec failed: ret %" PRId32 ", sigstr \"%s\"", ret, sigstr);
+ goto error;
+ }
+
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 1, parser->action, sizeof(parser->action)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 2, parser->protocol, sizeof(parser->protocol)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 3, parser->src, sizeof(parser->src)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 4, parser->sp, sizeof(parser->sp)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 5, parser->direction, sizeof(parser->direction)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 6, parser->dst, sizeof(parser->dst)) < 0)
+ goto error;
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 7, parser->dp, sizeof(parser->dp)) < 0)
+ goto error;
+ if (ret == 9) {
+ if (pcre_copy_substring(sigstr, ov, MAX_SUBSTRINGS, 8, parser->opts, sizeof(parser->opts)) < 0)
+ goto error;
+ }
+
+ /* Parse Action */
+ if (SigParseAction(s, parser->action) < 0)
+ goto error;
+
+ if (SigParseProto(s, parser->protocol) < 0)
+ goto error;
+
+ if (strcmp(parser->direction, "<-") == 0) {
+ SCLogError(SC_ERR_INVALID_DIRECTION, "\"<-\" is not a valid direction modifier, \"->\" and \"<>\" are supported.");
+ goto error;
+ }
+ /* Check if it is bidirectional */
+ if (strcmp(parser->direction, "<>") == 0)
+ s->init_flags |= SIG_FLAG_INIT_BIDIREC;
+
+ /* Parse Address & Ports */
+ if (SigParseAddress(de_ctx, s, parser->src, SIG_DIREC_SRC ^ addrs_direction) < 0)
+ goto error;
+
+ if (SigParseAddress(de_ctx, s, parser->dst, SIG_DIREC_DST ^ addrs_direction) < 0)
+ goto error;
+
+ /* For IPOnly */
+ if (IPOnlySigParseAddress(de_ctx, s, parser->src, SIG_DIREC_SRC ^ addrs_direction) < 0)
+ goto error;
+
+ if (IPOnlySigParseAddress(de_ctx, s, parser->dst, SIG_DIREC_DST ^ addrs_direction) < 0)
+ goto error;
+
+ /* By AWS - Traditionally we should be doing this only for tcp/udp/sctp,
+ * but we do it for regardless of ip proto, since the dns/dnstcp/dnsudp
+ * changes that we made sees to it that at this point of time we don't
+ * set the ip proto for the sig. We do it a bit later. */
+ if (SigParsePort(de_ctx, s, parser->sp, SIG_DIREC_SRC ^ addrs_direction) < 0)
+ goto error;
+ if (SigParsePort(de_ctx, s, parser->dp, SIG_DIREC_DST ^ addrs_direction) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief parse a signature
+ *
+ * \param de_ctx detection engine ctx to add it to
+ * \param s memory structure to store the signature in
+ * \param sigstr the raw signature as a null terminated string
+ * \param addrs_direction direction (for bi-directional sigs)
+ *
+ * \param -1 parse error
+ * \param 0 ok
+ */
+int SigParse(DetectEngineCtx *de_ctx, Signature *s, char *sigstr, uint8_t addrs_direction)
+{
+ SCEnter();
+
+ SignatureParser parser;
+ memset(&parser, 0x00, sizeof(parser));
+
+ s->sig_str = sigstr;
+
+ int ret = SigParseBasics(de_ctx, s, sigstr, &parser, addrs_direction);
+ if (ret < 0) {
+ SCLogDebug("SigParseBasics failed");
+ SCReturnInt(-1);
+ }
+
+ /* we can have no options, so make sure we have them */
+ if (strlen(parser.opts) > 0) {
+ size_t buffer_size = strlen(parser.opts) + 1;
+ char input[buffer_size];
+ char output[buffer_size];
+ memset(input, 0x00, buffer_size);
+ memcpy(input, parser.opts, strlen(parser.opts)+1);
+
+ /* loop the option parsing. Each run processes one option
+ * and returns the rest of the option string through the
+ * output variable. */
+ do {
+ memset(output, 0x00, buffer_size);
+ ret = SigParseOptions(de_ctx, s, input, output, buffer_size);
+ if (ret == 1) {
+ memcpy(input, output, buffer_size);
+ }
+
+ } while (ret == 1);
+ }
+
+ s->sig_str = NULL;
+
+ DetectIPProtoRemoveAllSMs(s);
+
+ SCReturnInt(ret);
+}
+
+Signature *SigAlloc (void)
+{
+ Signature *sig = SCMalloc(sizeof(Signature));
+ if (unlikely(sig == NULL))
+ return NULL;
+
+ memset(sig, 0, sizeof(Signature));
+
+ /* assign it to -1, so that we can later check if the value has been
+ * overwritten after the Signature has been parsed, and if it hasn't been
+ * overwritten, we can then assign the default value of 3 */
+ sig->prio = -1;
+
+ sig->list = DETECT_SM_LIST_NOTSET;
+ return sig;
+}
+
+/**
+ * \internal
+ * \brief Free Reference list
+ *
+ * \param s Pointer to the signature
+ */
+static void SigRefFree (Signature *s)
+{
+ SCEnter();
+
+ DetectReference *ref = NULL;
+ DetectReference *next_ref = NULL;
+
+ if (s == NULL) {
+ SCReturn;
+ }
+
+ SCLogDebug("s %p, s->references %p", s, s->references);
+
+ for (ref = s->references; ref != NULL;) {
+ next_ref = ref->next;
+ DetectReferenceFree(ref);
+ ref = next_ref;
+ }
+
+ s->references = NULL;
+
+ SCReturn;
+}
+
+static void SigMatchFreeArrays(Signature *s)
+{
+ if (s != NULL) {
+ int type;
+ for (type = 0; type < DETECT_SM_LIST_MAX; type++) {
+ if (s->sm_arrays[type] != NULL)
+ SCFree(s->sm_arrays[type]);
+ }
+ }
+}
+
+void SigFree(Signature *s)
+{
+ if (s == NULL)
+ return;
+
+ if (s->CidrDst != NULL)
+ IPOnlyCIDRListFree(s->CidrDst);
+
+ if (s->CidrSrc != NULL)
+ IPOnlyCIDRListFree(s->CidrSrc);
+
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ SigMatch *sm = s->sm_lists[i], *nsm;
+ while (sm != NULL) {
+ nsm = sm->next;
+ SigMatchFree(sm);
+ sm = nsm;
+ }
+ }
+ SigMatchFreeArrays(s);
+
+ DetectAddressHeadCleanup(&s->src);
+ DetectAddressHeadCleanup(&s->dst);
+
+ if (s->sp != NULL) {
+ DetectPortCleanupList(s->sp);
+ }
+ if (s->dp != NULL) {
+ DetectPortCleanupList(s->dp);
+ }
+
+ if (s->msg != NULL)
+ SCFree(s->msg);
+
+ if (s->addr_src_match4 != NULL) {
+ SCFree(s->addr_src_match4);
+ }
+ if (s->addr_dst_match4 != NULL) {
+ SCFree(s->addr_dst_match4);
+ }
+ if (s->addr_src_match6 != NULL) {
+ SCFree(s->addr_src_match6);
+ }
+ if (s->addr_dst_match6 != NULL) {
+ SCFree(s->addr_dst_match6);
+ }
+
+ SigRefFree(s);
+
+ SCFree(s);
+}
+
+/**
+ * \internal
+ * \brief build address match array for cache efficient matching
+ *
+ * \param s the signature
+ */
+static void SigBuildAddressMatchArray(Signature *s)
+{
+ /* source addresses */
+ uint16_t cnt = 0;
+ uint16_t idx = 0;
+ DetectAddress *da = s->src.ipv4_head;
+ for ( ; da != NULL; da = da->next) {
+ cnt++;
+ }
+ if (cnt > 0) {
+ s->addr_src_match4 = SCMalloc(cnt * sizeof(DetectMatchAddressIPv4));
+ if (s->addr_src_match4 == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (da = s->src.ipv4_head; da != NULL; da = da->next) {
+ s->addr_src_match4[idx].ip = ntohl(da->ip.addr_data32[0]);
+ s->addr_src_match4[idx].ip2 = ntohl(da->ip2.addr_data32[0]);
+ idx++;
+ }
+ s->addr_src_match4_cnt = cnt;
+ }
+
+ /* destination addresses */
+ cnt = 0;
+ idx = 0;
+ da = s->dst.ipv4_head;
+ for ( ; da != NULL; da = da->next) {
+ cnt++;
+ }
+ if (cnt > 0) {
+ s->addr_dst_match4 = SCMalloc(cnt * sizeof(DetectMatchAddressIPv4));
+ if (s->addr_dst_match4 == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (da = s->dst.ipv4_head; da != NULL; da = da->next) {
+ s->addr_dst_match4[idx].ip = ntohl(da->ip.addr_data32[0]);
+ s->addr_dst_match4[idx].ip2 = ntohl(da->ip2.addr_data32[0]);
+ idx++;
+ }
+ s->addr_dst_match4_cnt = cnt;
+ }
+
+ /* source addresses IPv6 */
+ cnt = 0;
+ idx = 0;
+ da = s->src.ipv6_head;
+ for ( ; da != NULL; da = da->next) {
+ cnt++;
+ }
+ if (cnt > 0) {
+ s->addr_src_match6 = SCMalloc(cnt * sizeof(DetectMatchAddressIPv6));
+ if (s->addr_src_match6 == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (da = s->src.ipv6_head; da != NULL; da = da->next) {
+ s->addr_src_match6[idx].ip[0] = ntohl(da->ip.addr_data32[0]);
+ s->addr_src_match6[idx].ip[1] = ntohl(da->ip.addr_data32[1]);
+ s->addr_src_match6[idx].ip[2] = ntohl(da->ip.addr_data32[2]);
+ s->addr_src_match6[idx].ip[3] = ntohl(da->ip.addr_data32[3]);
+ s->addr_src_match6[idx].ip2[0] = ntohl(da->ip2.addr_data32[0]);
+ s->addr_src_match6[idx].ip2[1] = ntohl(da->ip2.addr_data32[1]);
+ s->addr_src_match6[idx].ip2[2] = ntohl(da->ip2.addr_data32[2]);
+ s->addr_src_match6[idx].ip2[3] = ntohl(da->ip2.addr_data32[3]);
+ idx++;
+ }
+ s->addr_src_match6_cnt = cnt;
+ }
+
+ /* destination addresses IPv6 */
+ cnt = 0;
+ idx = 0;
+ da = s->dst.ipv6_head;
+ for ( ; da != NULL; da = da->next) {
+ cnt++;
+ }
+ if (cnt > 0) {
+ s->addr_dst_match6 = SCMalloc(cnt * sizeof(DetectMatchAddressIPv6));
+ if (s->addr_dst_match6 == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (da = s->dst.ipv6_head; da != NULL; da = da->next) {
+ s->addr_dst_match6[idx].ip[0] = ntohl(da->ip.addr_data32[0]);
+ s->addr_dst_match6[idx].ip[1] = ntohl(da->ip.addr_data32[1]);
+ s->addr_dst_match6[idx].ip[2] = ntohl(da->ip.addr_data32[2]);
+ s->addr_dst_match6[idx].ip[3] = ntohl(da->ip.addr_data32[3]);
+ s->addr_dst_match6[idx].ip2[0] = ntohl(da->ip2.addr_data32[0]);
+ s->addr_dst_match6[idx].ip2[1] = ntohl(da->ip2.addr_data32[1]);
+ s->addr_dst_match6[idx].ip2[2] = ntohl(da->ip2.addr_data32[2]);
+ s->addr_dst_match6[idx].ip2[3] = ntohl(da->ip2.addr_data32[3]);
+ idx++;
+ }
+ s->addr_dst_match6_cnt = cnt;
+ }
+}
+
+/**
+ * \internal
+ * \brief validate a just parsed signature for internal inconsistencies
+ *
+ * \param s just parsed signature
+ *
+ * \retval 0 invalid
+ * \retval 1 valid
+ */
+int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
+{
+ uint32_t u = 0;
+ uint32_t sig_flags = 0;
+ SigMatch *sm, *pm;
+
+ SCEnter();
+
+ if ((s->flags & SIG_FLAG_REQUIRE_PACKET) &&
+ (s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't mix packet keywords with "
+ "tcp-stream or flow:only_stream. Invalidating signature.");
+ SCReturnInt(0);
+ }
+
+ for (sm = s->sm_lists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_FLOW) {
+ DetectFlowData *fd = (DetectFlowData *)sm->ctx;
+ if (fd == NULL)
+ continue;
+
+ if (fd->flags & FLOW_PKT_TOCLIENT) {
+ /* check for uricontent + from_server/to_client */
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use uricontent "
+ "/http_uri , raw_uri, http_client_body, "
+ "http_method, http_user_agent keywords "
+ "with flow:to_client or flow:from_server");
+ SCReturnInt(0);
+ }
+ } else if (fd->flags & FLOW_PKT_TOSERVER) {
+ /* check for uricontent + from_server/to_client */
+ if (/*s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL ||*/
+ s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use http_"
+ "server_body, http_stat_msg, http_stat_code "
+ "with flow:to_server or flow:from_client");
+ SCReturnInt(0);
+ }
+ }
+ }
+ }
+
+ if ((s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL && s->alproto == ALPROTO_SMTP) ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL) {
+ sig_flags |= SIG_FLAG_TOSERVER;
+ s->flags |= SIG_FLAG_TOSERVER;
+ s->flags &= ~SIG_FLAG_TOCLIENT;
+ }
+ if ((s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL && s->alproto == ALPROTO_HTTP) ||
+ s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL) {
+ sig_flags |= SIG_FLAG_TOCLIENT;
+ s->flags |= SIG_FLAG_TOCLIENT;
+ s->flags &= ~SIG_FLAG_TOSERVER;
+ }
+ if ((sig_flags & (SIG_FLAG_TOCLIENT | SIG_FLAG_TOSERVER)) == (SIG_FLAG_TOCLIENT | SIG_FLAG_TOSERVER)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE,"You seem to have mixed keywords "
+ "that require inspection in both directions. Atm we only "
+ "support keywords in one direction within a rule.");
+ SCReturnInt(0);
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL) {
+ if ((s->flags & (SIG_FLAG_TOCLIENT|SIG_FLAG_TOSERVER)) == (SIG_FLAG_TOCLIENT|SIG_FLAG_TOSERVER)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE,"http_raw_header signature "
+ "without a flow direction. Use flow:to_server for "
+ "inspecting request headers or flow:to_client for "
+ "inspecting response headers.");
+ SCReturnInt(0);
+ }
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL) {
+ for (sm = s->sm_lists[DETECT_SM_LIST_HHHDMATCH];
+ sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ SCLogWarning(SC_ERR_INVALID_SIGNATURE, "http_host keyword "
+ "specified along with \"nocase\". "
+ "Since the hostname buffer we match against "
+ "is actually lowercase. So having a "
+ "nocase is redundant.");
+ } else {
+ for (u = 0; u < cd->content_len; u++) {
+ if (isupper(cd->content[u]))
+ break;
+ }
+ if (u != cd->content_len) {
+ SCLogWarning(SC_ERR_INVALID_SIGNATURE, "A pattern with "
+ "uppercase chars detected for http_host. "
+ "Since the hostname buffer we match against "
+ "is lowercase only, please specify a "
+ "lowercase pattern.");
+ SCReturnInt(0);
+ }
+ }
+ }
+ }
+ }
+
+ //if (s->alproto != ALPROTO_UNKNOWN) {
+ // if (s->flags & SIG_FLAG_STATE_MATCH) {
+ // if (s->alproto == ALPROTO_DNS) {
+ // if (al_proto_table[ALPROTO_DNS_UDP].to_server == 0 ||
+ // al_proto_table[ALPROTO_DNS_UDP].to_client == 0 ||
+ // al_proto_table[ALPROTO_DNS_TCP].to_server == 0 ||
+ // al_proto_table[ALPROTO_DNS_TCP].to_client == 0) {
+ // SCLogInfo("Signature uses options that need the app layer "
+ // "parser for dns, but the parser's disabled "
+ // "for the protocol. Please check if you have "
+ // "disabled it through the option "
+ // "\"app-layer.protocols.dcerpc[udp|tcp].enabled\""
+ // "or internally the parser has been disabled in "
+ // "the code. Invalidating signature.");
+ // SCReturnInt(0);
+ // }
+ // } else {
+ // if (al_proto_table[s->alproto].to_server == 0 ||
+ // al_proto_table[s->alproto].to_client == 0) {
+ // const char *proto_name = AppProtoToString(s->alproto);
+ // SCLogInfo("Signature uses options that need the app layer "
+ // "parser for \"%s\", but the parser's disabled "
+ // "for the protocol. Please check if you have "
+ // "disabled it through the option "
+ // "\"app-layer.protocols.%s.enabled\" or internally "
+ // "there the parser has been disabled in the code. "
+ // "Invalidating signature.", proto_name, proto_name);
+ // SCReturnInt(0);
+ // }
+ // }
+ // }
+ //
+ //
+ //
+ //
+ //
+ //}
+
+ if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
+ pm = SigMatchGetLastSMFromLists(s, 24,
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_REPLACE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ if (pm != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Signature has"
+ " replace keyword linked with a modified content"
+ " keyword (http_*, dce_*). It only supports content on"
+ " raw payload");
+ SCReturnInt(0);
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] ||
+ s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH])
+ {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Signature combines packet "
+ "specific matches (like dsize, flags, ttl) with stream / "
+ "state matching by matching on app layer proto (like using "
+ "http_* keywords).");
+ SCReturnInt(0);
+ }
+ }
+
+ for (sm = s->sm_lists[DETECT_SM_LIST_AMATCH]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_AL_APP_LAYER_PROTOCOL)
+ continue;
+ if (((DetectAppLayerProtocolData *)sm->ctx)->negated)
+ break;
+ }
+ if (sm != NULL && s->alproto != ALPROTO_UNKNOWN) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "We can't have "
+ "the rule match on a fixed alproto and at the same time"
+ "have an app-layer-protocol keyword set.");
+ SCReturnInt(0);
+ }
+
+ /* TCP: pkt vs stream vs depth/offset */
+ if (s->proto.proto[IPPROTO_TCP / 8] & (1 << (IPPROTO_TCP % 8))) {
+ if (!(s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM))) {
+ s->flags |= SIG_FLAG_REQUIRE_STREAM;
+ sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ while (sm != NULL) {
+ if (sm->type == DETECT_CONTENT &&
+ (((DetectContentData *)(sm->ctx))->flags &
+ (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET))) {
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+ break;
+ }
+ sm = sm->next;
+ }
+ }
+ }
+
+#ifdef HAVE_LUA
+ DetectLuaPostSetup(s);
+#endif
+
+#ifdef DEBUG
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ if (s->sm_lists[i] != NULL) {
+ for (sm = s->sm_lists[i]; sm != NULL; sm = sm->next) {
+ BUG_ON(sm == sm->prev);
+ BUG_ON(sm == sm->next);
+ }
+ }
+ }
+#endif
+
+ SCReturnInt(1);
+}
+
+/**
+ * \internal
+ * \brief Helper function for SigInit().
+ */
+static Signature *SigInitHelper(DetectEngineCtx *de_ctx, char *sigstr,
+ uint8_t dir)
+{
+ SigMatch *sm;
+ Signature *sig = SigAlloc();
+ if (sig == NULL)
+ goto error;
+
+ /* default gid to 1 */
+ sig->gid = 1;
+
+ if (SigParse(de_ctx, sig, sigstr, dir) < 0)
+ goto error;
+
+ /* signature priority hasn't been overwritten. Using default priority */
+ if (sig->prio == -1)
+ sig->prio = 3;
+
+ sig->num = de_ctx->signum;
+ de_ctx->signum++;
+
+ if (sig->alproto != ALPROTO_UNKNOWN) {
+ int override_needed = 0;
+ if (sig->proto.flags & DETECT_PROTO_ANY) {
+ sig->proto.flags &= ~DETECT_PROTO_ANY;
+ memset(sig->proto.proto, 0x00, sizeof(sig->proto.proto));
+ override_needed = 1;
+ } else {
+ override_needed = 1;
+ size_t s = 0;
+ for (s = 0; s < sizeof(sig->proto.proto); s++) {
+ if (sig->proto.proto[s] != 0x00) {
+ override_needed = 0;
+ break;
+ }
+ }
+ }
+
+ /* at this point if we had alert ip and the ip proto was not
+ * overridden, we use the ip proto that has been configured
+ * against the app proto in use. */
+ if (override_needed)
+ AppLayerProtoDetectSupportedIpprotos(sig->alproto, sig->proto.proto);
+ }
+
+ if (DetectAppLayerEventPrepare(sig) < 0)
+ goto error;
+
+ /* set mpm_content_len */
+
+ /* determine the length of the longest pattern in the sig */
+ if (sig->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ sig->mpm_content_maxlen = 0;
+
+ for (sm = sig->sm_lists[DETECT_SM_LIST_PMATCH]; sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd == NULL)
+ continue;
+
+ if (sig->mpm_content_maxlen == 0)
+ sig->mpm_content_maxlen = cd->content_len;
+ if (sig->mpm_content_maxlen < cd->content_len)
+ sig->mpm_content_maxlen = cd->content_len;
+ }
+ }
+ }
+ if (sig->sm_lists[DETECT_SM_LIST_UMATCH] != NULL) {
+ sig->mpm_uricontent_maxlen = 0;
+
+ for (sm = sig->sm_lists[DETECT_SM_LIST_UMATCH]; sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_CONTENT) {
+ DetectContentData *ud = (DetectContentData *)sm->ctx;
+ if (ud == NULL)
+ continue;
+
+ if (sig->mpm_uricontent_maxlen == 0)
+ sig->mpm_uricontent_maxlen = ud->content_len;
+ if (sig->mpm_uricontent_maxlen < ud->content_len)
+ sig->mpm_uricontent_maxlen = ud->content_len;
+ }
+ }
+ }
+
+ /* set the packet and app layer flags, but only if the
+ * app layer flag wasn't already set in which case we
+ * only consider the app layer */
+ if (!(sig->flags & SIG_FLAG_APPLAYER)) {
+ if (sig->sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ SigMatch *sm = sig->sm_lists[DETECT_SM_LIST_MATCH];
+ for ( ; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].Match != NULL)
+ sig->init_flags |= SIG_FLAG_INIT_PACKET;
+ }
+ } else {
+ sig->init_flags |= SIG_FLAG_INIT_PACKET;
+ }
+ }
+
+ if (sig->sm_lists[DETECT_SM_LIST_AMATCH] != NULL)
+ sig->flags |= SIG_FLAG_APPLAYER;
+
+ if (sig->sm_lists[DETECT_SM_LIST_UMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_DMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_AMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HRLMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HCBDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_FILEDATA])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HHDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HRHDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HMDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HCDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HRUDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_FILEMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HSMDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HSCDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HUADMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HHHDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_HRHHDMATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+
+ /* DNS */
+ if (sig->sm_lists[DETECT_SM_LIST_DNSQUERYNAME_MATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_DNSREQUEST_MATCH]) {
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ }
+ if (sig->sm_lists[DETECT_SM_LIST_DNSRESPONSE_MATCH]) {
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ }
+
+ if (sig->sm_lists[DETECT_SM_LIST_MODBUS_MATCH])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+ if (sig->sm_lists[DETECT_SM_LIST_APP_EVENT])
+ sig->flags |= SIG_FLAG_STATE_MATCH;
+
+ if (!(sig->init_flags & SIG_FLAG_INIT_FLOW)) {
+ sig->flags |= SIG_FLAG_TOSERVER;
+ sig->flags |= SIG_FLAG_TOCLIENT;
+ }
+
+ SCLogDebug("sig %"PRIu32" SIG_FLAG_APPLAYER: %s, SIG_FLAG_PACKET: %s",
+ sig->id, sig->flags & SIG_FLAG_APPLAYER ? "set" : "not set",
+ sig->init_flags & SIG_FLAG_INIT_PACKET ? "set" : "not set");
+
+ SigBuildAddressMatchArray(sig);
+
+ if (sig->sm_lists[DETECT_SM_LIST_APP_EVENT] != NULL) {
+ if (AppLayerParserProtocolIsTxEventAware(IPPROTO_TCP, sig->alproto)) {
+ if (sig->flags & SIG_FLAG_TOSERVER) {
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ sig->alproto,
+ 0,
+ DETECT_SM_LIST_APP_EVENT,
+ DE_STATE_FLAG_APP_EVENT_INSPECT,
+ DetectEngineAptEventInspect,
+ app_inspection_engine);
+ }
+ if (sig->flags & SIG_FLAG_TOCLIENT) {
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP,
+ sig->alproto,
+ 1,
+ DETECT_SM_LIST_APP_EVENT,
+ DE_STATE_FLAG_APP_EVENT_INSPECT,
+ DetectEngineAptEventInspect,
+ app_inspection_engine);
+ }
+ }
+ if (AppLayerParserProtocolIsTxEventAware(IPPROTO_UDP, sig->alproto)) {
+ if (sig->flags & SIG_FLAG_TOSERVER) {
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_UDP,
+ sig->alproto,
+ 0,
+ DETECT_SM_LIST_APP_EVENT,
+ DE_STATE_FLAG_APP_EVENT_INSPECT,
+ DetectEngineAptEventInspect,
+ app_inspection_engine);
+ }
+ if (sig->flags & SIG_FLAG_TOCLIENT) {
+ DetectEngineRegisterAppInspectionEngine(IPPROTO_UDP,
+ sig->alproto,
+ 1,
+ DETECT_SM_LIST_APP_EVENT,
+ DE_STATE_FLAG_APP_EVENT_INSPECT,
+ DetectEngineAptEventInspect,
+ app_inspection_engine);
+ }
+ }
+ }
+
+ /* validate signature, SigValidate will report the error reason */
+ if (SigValidate(de_ctx, sig) == 0) {
+ goto error;
+ }
+
+ return sig;
+
+error:
+ if (sig != NULL) {
+ SigFree(sig);
+ }
+ return NULL;
+}
+
+/**
+ * \brief Parses a signature and adds it to the Detection Engine Context.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param sigstr Pointer to a character string containing the signature to be
+ * parsed.
+ *
+ * \retval Pointer to the Signature instance on success; NULL on failure.
+ */
+Signature *SigInit(DetectEngineCtx *de_ctx, char *sigstr)
+{
+ SCEnter();
+
+ uint32_t oldsignum = de_ctx->signum;
+
+ Signature *sig;
+
+ if ((sig = SigInitHelper(de_ctx, sigstr, SIG_DIREC_NORMAL)) == NULL) {
+ goto error;
+ }
+
+ if (sig->init_flags & SIG_FLAG_INIT_BIDIREC) {
+ sig->next = SigInitHelper(de_ctx, sigstr, SIG_DIREC_SWITCHED);
+ if (sig->next == NULL) {
+ goto error;
+ }
+ }
+
+ SCReturnPtr(sig, "Signature");
+
+error:
+ if (sig != NULL) {
+ SigFree(sig);
+ }
+ /* if something failed, restore the old signum count
+ * since we didn't install it */
+ de_ctx->signum = oldsignum;
+
+ SCReturnPtr(NULL, "Signature");
+}
+
+/**
+ * \brief The hash free function to be the used by the hash table -
+ * DetectEngineCtx->dup_sig_hash_table.
+ *
+ * \param data Pointer to the data, in our case SigDuplWrapper to be freed.
+ */
+void DetectParseDupSigFreeFunc(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+/**
+ * \brief The hash function to be the used by the hash table -
+ * DetectEngineCtx->dup_sig_hash_table.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the data, in our case SigDuplWrapper.
+ * \param datalen Not used in our case.
+ *
+ * \retval sw->s->id The generated hash value.
+ */
+uint32_t DetectParseDupSigHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ SigDuplWrapper *sw = (SigDuplWrapper *)data;
+
+ return (sw->s->id % ht->array_size);
+}
+
+/**
+ * \brief The Compare function to be used by the hash table -
+ * DetectEngineCtx->dup_sig_hash_table.
+ *
+ * \param data1 Pointer to the first SigDuplWrapper.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second SigDuplWrapper.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 SigDuplWrappers sent as args match.
+ * \retval 0 If the 2 SigDuplWrappers sent as args do not match.
+ */
+char DetectParseDupSigCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ SigDuplWrapper *sw1 = (SigDuplWrapper *)data1;
+ SigDuplWrapper *sw2 = (SigDuplWrapper *)data2;
+
+ if (sw1 == NULL || sw2 == NULL ||
+ sw1->s == NULL || sw2->s == NULL)
+ return 0;
+
+ /* sid and gid match required */
+ if (sw1->s->id == sw2->s->id && sw1->s->gid == sw2->s->gid) return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Initializes the hash table that is used to cull duplicate sigs.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectParseDupSigHashInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->dup_sig_hash_table = HashListTableInit(15000,
+ DetectParseDupSigHashFunc,
+ DetectParseDupSigCompareFunc,
+ DetectParseDupSigFreeFunc);
+ if (de_ctx->dup_sig_hash_table == NULL)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * \brief Frees the hash table that is used to cull duplicate sigs.
+ *
+ * \param de_ctx Pointer to the detection engine context that holds this table.
+ */
+void DetectParseDupSigHashFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->dup_sig_hash_table != NULL)
+ HashListTableFree(de_ctx->dup_sig_hash_table);
+
+ de_ctx->dup_sig_hash_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Check if a signature is a duplicate.
+ *
+ * There are 3 types of return values for this function.
+ *
+ * - 0, which indicates that the Signature is not a duplicate
+ * and has to be added to the detection engine list.
+ * - 1, Signature is duplicate, and the existing signature in
+ * the list shouldn't be replaced with this duplicate.
+ * - 2, Signature is duplicate, and the existing signature in
+ * the list should be replaced with this duplicate.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sig Pointer to the Signature that has to be checked.
+ *
+ * \retval 2 If Signature is duplicate and the existing signature in
+ * the list should be chucked out and replaced with this.
+ * \retval 1 If Signature is duplicate, and should be chucked out.
+ * \retval 0 If Signature is not a duplicate.
+ */
+static inline int DetectEngineSignatureIsDuplicate(DetectEngineCtx *de_ctx,
+ Signature *sig)
+{
+ /* we won't do any NULL checks on the args */
+
+ /* return value */
+ int ret = 0;
+
+ SigDuplWrapper *sw_dup = NULL;
+ SigDuplWrapper *sw = NULL;
+
+ /* used for making a duplicate_sig_hash_table entry */
+ sw = SCMalloc(sizeof(SigDuplWrapper));
+ if (unlikely(sw == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(sw, 0, sizeof(SigDuplWrapper));
+ sw->s = sig;
+
+ /* check if we have a duplicate entry for this signature */
+ sw_dup = HashListTableLookup(de_ctx->dup_sig_hash_table, (void *)sw, 0);
+ /* we don't have a duplicate entry for this sig */
+ if (sw_dup == NULL) {
+ /* add it to the hash table */
+ HashListTableAdd(de_ctx->dup_sig_hash_table, (void *)sw, 0);
+
+ /* add the s_prev entry for the previously loaded sw in the hash_table */
+ if (de_ctx->sig_list != NULL) {
+ SigDuplWrapper *sw_old = NULL;
+ SigDuplWrapper sw_tmp;
+ memset(&sw_tmp, 0, sizeof(SigDuplWrapper));
+
+ /* the topmost sig would be the last loaded sig */
+ sw_tmp.s = de_ctx->sig_list;
+ sw_old = HashListTableLookup(de_ctx->dup_sig_hash_table,
+ (void *)&sw_tmp, 0);
+ /* sw_old == NULL case is impossible */
+ sw_old->s_prev = sig;
+ }
+
+ ret = 0;
+ goto end;
+ }
+
+ /* if we have reached here we have a duplicate entry for this signature.
+ * Check the signature revision. Store the signature with the latest rev
+ * and discard the other one */
+ if (sw->s->rev <= sw_dup->s->rev) {
+ ret = 1;
+ goto end;
+ }
+
+ /* the new sig is of a newer revision than the one that is already in the
+ * list. Remove the old sig from the list */
+ if (sw_dup->s_prev == NULL) {
+ SigDuplWrapper sw_temp;
+ memset(&sw_temp, 0, sizeof(SigDuplWrapper));
+ if (sw_dup->s->init_flags & SIG_FLAG_INIT_BIDIREC) {
+ sw_temp.s = sw_dup->s->next->next;
+ de_ctx->sig_list = sw_dup->s->next->next;
+ SigFree(sw_dup->s->next);
+ } else {
+ sw_temp.s = sw_dup->s->next;
+ de_ctx->sig_list = sw_dup->s->next;
+ }
+ SigDuplWrapper *sw_next = NULL;
+ if (sw_temp.s != NULL) {
+ sw_next = HashListTableLookup(de_ctx->dup_sig_hash_table,
+ (void *)&sw_temp, 0);
+ sw_next->s_prev = sw_dup->s_prev;
+ }
+ SigFree(sw_dup->s);
+ } else {
+ SigDuplWrapper sw_temp;
+ memset(&sw_temp, 0, sizeof(SigDuplWrapper));
+ if (sw_dup->s->init_flags & SIG_FLAG_INIT_BIDIREC) {
+ sw_temp.s = sw_dup->s->next->next;
+ sw_dup->s_prev->next = sw_dup->s->next->next;
+ SigFree(sw_dup->s->next);
+ } else {
+ sw_temp.s = sw_dup->s->next;
+ sw_dup->s_prev->next = sw_dup->s->next;
+ }
+ SigDuplWrapper *sw_next = NULL;
+ if (sw_temp.s != NULL) {
+ sw_next = HashListTableLookup(de_ctx->dup_sig_hash_table,
+ (void *)&sw_temp, 0);
+ sw_next->s_prev = sw_dup->s_prev;;
+ }
+ SigFree(sw_dup->s);
+ }
+
+ /* make changes to the entry to reflect the presence of the new sig */
+ sw_dup->s = sig;
+ sw_dup->s_prev = NULL;
+
+ /* this is duplicate, but a duplicate that replaced the existing sig entry */
+ ret = 2;
+
+ SCFree(sw);
+
+end:
+ return ret;
+}
+
+/**
+ * \brief Parse and append a Signature into the Detection Engine Context
+ * signature list.
+ *
+ * If the signature is bidirectional it should append two signatures
+ * (with the addresses switched) into the list. Also handle duplicate
+ * signatures. In case of duplicate sigs, use the ones that have the
+ * latest revision. We use the sid and the msg to identifiy duplicate
+ * sigs. If 2 sigs have the same sid and gid, they are duplicates.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param sigstr Pointer to a character string containing the signature to be
+ * parsed.
+ * \param sig_file Pointer to a character string containing the filename from
+ * which signature is read
+ * \param lineno Line number from where signature is read
+ *
+ * \retval Pointer to the head Signature in the detection engine ctx sig_list
+ * on success; NULL on failure.
+ */
+Signature *DetectEngineAppendSig(DetectEngineCtx *de_ctx, char *sigstr)
+{
+ Signature *sig = SigInit(de_ctx, sigstr);
+ if (sig == NULL) {
+ return NULL;
+ }
+
+ /* checking for the status of duplicate signature */
+ int dup_sig = DetectEngineSignatureIsDuplicate(de_ctx, sig);
+ /* a duplicate signature that should be chucked out. Check the previously
+ * called function details to understand the different return values */
+ if (dup_sig == 1) {
+ SCLogError(SC_ERR_DUPLICATE_SIG, "Duplicate signature \"%s\"", sigstr);
+ goto error;
+ } else if (dup_sig == 2) {
+ SCLogWarning(SC_ERR_DUPLICATE_SIG, "Signature with newer revision,"
+ " so the older sig replaced by this new signature \"%s\"",
+ sigstr);
+ }
+
+ if (sig->init_flags & SIG_FLAG_INIT_BIDIREC) {
+ if (sig->next != NULL) {
+ sig->next->next = de_ctx->sig_list;
+ } else {
+ goto error;
+ }
+ } else {
+ /* if this sig is the first one, sig_list should be null */
+ sig->next = de_ctx->sig_list;
+ }
+
+ de_ctx->sig_list = sig;
+
+ /**
+ * In DetectEngineAppendSig(), the signatures are prepended and we always return the first one
+ * so if the signature is bidirectional, the returned sig will point through "next" ptr
+ * to the cloned signatures with the switched addresses
+ */
+ return (dup_sig == 0 || dup_sig == 2) ? sig : NULL;
+
+error:
+ if (sig != NULL)
+ SigFree(sig);
+ return NULL;
+}
+
+/*
+ * TESTS
+ */
+
+#ifdef UNITTESTS
+int SigParseTest01 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp 1.2.3.4 any -> !1.2.3.4 any (msg:\"SigParseTest01\"; sid:1;)");
+ if (sig == NULL)
+ result = 0;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int SigParseTest02 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ DetectPort *port = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL)
+ goto end;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"ET MALWARE Suspicious 220 Banner on Local Port\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:2003055; rev:4;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ int r = DetectPortParse(de_ctx, &port, "0:20");
+ if (r < 0)
+ goto end;
+
+ if (DetectPortCmp(sig->sp, port) == PORT_EQ) {
+ result = 1;
+ } else {
+ DetectPortPrint(port); printf(" != "); DetectPortPrint(sig->sp); printf(": ");
+ }
+
+end:
+ if (port != NULL) DetectPortCleanupList(port);
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test SigParseTest03 test for invalid direction operator in rule
+ */
+int SigParseTest03 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp 1.2.3.4 any <- !1.2.3.4 any (msg:\"SigParseTest03\"; sid:1;)");
+ if (sig != NULL) {
+ result = 0;
+ printf("expected NULL got sig ptr %p: ",sig);
+ }
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+int SigParseTest04 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp 1.2.3.4 1024: -> !1.2.3.4 1024: (msg:\"SigParseTest04\"; sid:1;)");
+ if (sig == NULL)
+ result = 0;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Port validation */
+int SigParseTest05 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp 1.2.3.4 1024:65536 -> !1.2.3.4 any (msg:\"SigParseTest05\"; sid:1;)");
+ if (sig == NULL) {
+ result = 1;
+ } else {
+ printf("signature didn't fail to parse as we expected: ");
+ }
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Parsing bug debugging at 2010-03-18 */
+int SigParseTest06 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (flow:to_server; content:\"GET\"; nocase; http_method; uricontent:\"/uri/\"; nocase; content:\"Host|3A| abc\"; nocase; sid:1; rev:1;)");
+ if (sig != NULL) {
+ result = 1;
+ } else {
+ printf("signature failed to parse: ");
+ }
+
+end:
+ if (sig != NULL)
+ SigFree(sig);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Parsing duplicate sigs.
+ */
+int SigParseTest07(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:1;)");
+
+ result = (de_ctx->sig_list != NULL && de_ctx->sig_list->next == NULL);
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Parsing duplicate sigs.
+ */
+int SigParseTest08(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:2;)");
+
+ result = (de_ctx->sig_list != NULL && de_ctx->sig_list->next == NULL &&
+ de_ctx->sig_list->rev == 2);
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Parsing duplicate sigs.
+ */
+int SigParseTest09(void)
+{
+ int result = 1;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:2;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:6;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:4;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:2;)");
+ result &= (de_ctx->sig_list != NULL && de_ctx->sig_list->id == 2 &&
+ de_ctx->sig_list->rev == 2);
+ if (result == 0)
+ goto end;
+ result &= (de_ctx->sig_list->next != NULL && de_ctx->sig_list->next->id == 1 &&
+ de_ctx->sig_list->next->rev == 6);
+ if (result == 0)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:1;)");
+ result &= (de_ctx->sig_list != NULL && de_ctx->sig_list->id == 2 &&
+ de_ctx->sig_list->rev == 2);
+ if (result == 0)
+ goto end;
+ result &= (de_ctx->sig_list->next != NULL && de_ctx->sig_list->next->id == 1 &&
+ de_ctx->sig_list->next->rev == 6);
+ if (result == 0)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:4;)");
+ result &= (de_ctx->sig_list != NULL && de_ctx->sig_list->id == 2 &&
+ de_ctx->sig_list->rev == 4);
+ if (result == 0)
+ goto end;
+ result &= (de_ctx->sig_list->next != NULL && de_ctx->sig_list->next->id == 1 &&
+ de_ctx->sig_list->next->rev == 6);
+ if (result == 0)
+ goto end;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Parsing duplicate sigs.
+ */
+int SigParseTest10(void)
+{
+ int result = 1;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:1; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:3; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:4; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:5; rev:1;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:3; rev:2;)");
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:2;)");
+
+ result &= ((de_ctx->sig_list->id == 2) &&
+ (de_ctx->sig_list->next->id == 3) &&
+ (de_ctx->sig_list->next->next->id == 5) &&
+ (de_ctx->sig_list->next->next->next->id == 4) &&
+ (de_ctx->sig_list->next->next->next->next->id == 1));
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Parsing sig with trailing space(s) as reported by
+ * Morgan Cox on oisf-users.
+ */
+int SigParseTest11(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "drop tcp any any -> any 80 (msg:\"Snort_Inline is blocking the http link\";) ");
+ if (s == NULL) {
+ printf("sig 1 didn't parse: ");
+ goto end;
+ }
+
+ s = DetectEngineAppendSig(de_ctx, "drop tcp any any -> any 80 (msg:\"Snort_Inline is blocking the http link\"; sid:1;) ");
+ if (s == NULL) {
+ printf("sig 2 didn't parse: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test file_data with rawbytes
+ */
+static int SigParseTest12(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (file_data; content:\"abc\"; rawbytes; sid:1;)");
+ if (s != NULL) {
+ printf("sig 1 should have given an error: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test packet/stream sig
+ */
+static int SigParseTest13(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"abc\"; sid:1;)");
+ if (s == NULL) {
+ printf("sig 1 invalidated: failure");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ printf("sig doesn't have stream flag set\n");
+ goto end;
+ }
+
+ if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
+ printf("sig has packet flag set\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test packet/stream sig
+ */
+static int SigParseTest14(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"abc\"; dsize:>0; sid:1;)");
+ if (s == NULL) {
+ printf("sig 1 invalidated: failure");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ printf("sig doesn't have packet flag set\n");
+ goto end;
+ }
+
+ if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
+ printf("sig has stream flag set\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test packet/stream sig
+ */
+static int SigParseTest15(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"abc\"; offset:5; sid:1;)");
+ if (s == NULL) {
+ printf("sig 1 invalidated: failure");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ printf("sig doesn't have packet flag set\n");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ printf("sig doesn't have stream flag set\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test packet/stream sig
+ */
+static int SigParseTest16(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"abc\"; depth:5; sid:1;)");
+ if (s == NULL) {
+ printf("sig 1 invalidated: failure");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ printf("sig doesn't have packet flag set\n");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ printf("sig doesn't have stream flag set\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test packet/stream sig
+ */
+static int SigParseTest17(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *s = NULL;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"abc\"; offset:1; depth:5; sid:1;)");
+ if (s == NULL) {
+ printf("sig 1 invalidated: failure");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ printf("sig doesn't have packet flag set\n");
+ goto end;
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ printf("sig doesn't have stream flag set\n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test sid value too large. Bug #779 */
+static int SigParseTest18 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 any -> !1.2.3.4 any (msg:\"SigParseTest01\"; sid:99999999999999999999;)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test gid value too large. Related to bug #779 */
+static int SigParseTest19 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 any -> !1.2.3.4 any (msg:\"SigParseTest01\"; sid:1; gid:99999999999999999999;)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test rev value too large. Related to bug #779 */
+static int SigParseTest20 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 any -> !1.2.3.4 any (msg:\"SigParseTest01\"; sid:1; rev:99999999999999999999;)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test address parsing */
+static int SigParseTest21 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp [1.2.3.4, 1.2.3.5] any -> !1.2.3.4 any (sid:1;)") == NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test address parsing */
+static int SigParseTest22 (void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx, "alert tcp [10.10.10.0/24, !10.10.10.247] any -> [10.10.10.0/24, !10.10.10.247] any (sid:1;)") == NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest06 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any - 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest07 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any <- 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest08 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any < 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest09 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any > 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest10 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any -< 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest11 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any >- 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (invalid) */
+int SigParseBidirecTest12 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any >< 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig == NULL)
+ result = 1;
+
+end:
+ if (sig != NULL) SigFree(sig);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (valid) */
+int SigParseBidirecTest13 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any <> 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig != NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Direction operator validation (valid) */
+int SigParseBidirecTest14 (void)
+{
+ int result = 1;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any -> 192.168.1.5 any (msg:\"SigParseBidirecTest05\"; sid:1;)");
+ if (sig != NULL)
+ result = 1;
+
+end:
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Ensure that we don't set bidirectional in a
+ * normal (one direction) Signature
+ */
+int SigTestBidirec01 (void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 1024:65535 -> !1.2.3.4 any (msg:\"SigTestBidirec01\"; sid:1;)");
+ if (sig == NULL)
+ goto end;
+ if (sig->next != NULL)
+ goto end;
+ if (sig->init_flags & SIG_FLAG_INIT_BIDIREC)
+ goto end;
+ if (de_ctx->signum != 1)
+ goto end;
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL) {
+ SigCleanSignatures(de_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+
+/** \test Ensure that we set a bidirectional Signature correctly */
+int SigTestBidirec02 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ Signature *copy = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 1.2.3.4 1024:65535 <> !1.2.3.4 any (msg:\"SigTestBidirec02\"; sid:1;)");
+ if (sig == NULL)
+ goto end;
+ if (de_ctx->sig_list != sig)
+ goto end;
+ if (!(sig->init_flags & SIG_FLAG_INIT_BIDIREC))
+ goto end;
+ if (sig->next == NULL)
+ goto end;
+ if (de_ctx->signum != 2)
+ goto end;
+ copy = sig->next;
+ if (copy->next != NULL)
+ goto end;
+ if (!(copy->init_flags & SIG_FLAG_INIT_BIDIREC))
+ goto end;
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL) {
+ SigCleanSignatures(de_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ return result;
+}
+
+/** \test Ensure that we set a bidirectional Signature correctly
+* and we install it with the rest of the signatures, checking
+* also that it match with the correct addr directions
+*/
+int SigTestBidirec03 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ Packet *p = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ char *sigs[3];
+ sigs[0] = "alert tcp any any -> 192.168.1.1 any (msg:\"SigTestBidirec03 sid 1\"; sid:1;)";
+ sigs[1] = "alert tcp any any <> 192.168.1.1 any (msg:\"SigTestBidirec03 sid 2 bidirectional\"; sid:2;)";
+ sigs[2] = "alert tcp any any -> 192.168.1.1 any (msg:\"SigTestBidirec03 sid 3\"; sid:3;)";
+ UTHAppendSigs(de_ctx, sigs, 3);
+
+ /* Checking that bidirectional rules are set correctly */
+ sig = de_ctx->sig_list;
+ if (sig == NULL)
+ goto end;
+ if (sig->next == NULL)
+ goto end;
+ if (sig->next->next == NULL)
+ goto end;
+ if (sig->next->next->next == NULL)
+ goto end;
+ if (sig->next->next->next->next != NULL)
+ goto end;
+ if (de_ctx->signum != 4)
+ goto end;
+
+ uint8_t rawpkt1_ether[] = {
+ 0x00,0x50,0x56,0xea,0x00,0xbd,0x00,0x0c,
+ 0x29,0x40,0xc8,0xb5,0x08,0x00,0x45,0x00,
+ 0x01,0xa8,0xb9,0xbb,0x40,0x00,0x40,0x06,
+ 0xe0,0xbf,0xc0,0xa8,0x1c,0x83,0xc0,0xa8,
+ 0x01,0x01,0xb9,0x0a,0x00,0x50,0x6f,0xa2,
+ 0x92,0xed,0x7b,0xc1,0xd3,0x4d,0x50,0x18,
+ 0x16,0xd0,0xa0,0x6f,0x00,0x00,0x47,0x45,
+ 0x54,0x20,0x2f,0x20,0x48,0x54,0x54,0x50,
+ 0x2f,0x31,0x2e,0x31,0x0d,0x0a,0x48,0x6f,
+ 0x73,0x74,0x3a,0x20,0x31,0x39,0x32,0x2e,
+ 0x31,0x36,0x38,0x2e,0x31,0x2e,0x31,0x0d,
+ 0x0a,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,
+ 0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,
+ 0x69,0x6c,0x6c,0x61,0x2f,0x35,0x2e,0x30,
+ 0x20,0x28,0x58,0x31,0x31,0x3b,0x20,0x55,
+ 0x3b,0x20,0x4c,0x69,0x6e,0x75,0x78,0x20,
+ 0x78,0x38,0x36,0x5f,0x36,0x34,0x3b,0x20,
+ 0x65,0x6e,0x2d,0x55,0x53,0x3b,0x20,0x72,
+ 0x76,0x3a,0x31,0x2e,0x39,0x2e,0x30,0x2e,
+ 0x31,0x34,0x29,0x20,0x47,0x65,0x63,0x6b,
+ 0x6f,0x2f,0x32,0x30,0x30,0x39,0x30,0x39,
+ 0x30,0x32,0x31,0x37,0x20,0x55,0x62,0x75,
+ 0x6e,0x74,0x75,0x2f,0x39,0x2e,0x30,0x34,
+ 0x20,0x28,0x6a,0x61,0x75,0x6e,0x74,0x79,
+ 0x29,0x20,0x46,0x69,0x72,0x65,0x66,0x6f,
+ 0x78,0x2f,0x33,0x2e,0x30,0x2e,0x31,0x34,
+ 0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,0x74,
+ 0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,
+ 0x74,0x6d,0x6c,0x2c,0x61,0x70,0x70,0x6c,
+ 0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x2f,
+ 0x78,0x68,0x74,0x6d,0x6c,0x2b,0x78,0x6d,
+ 0x6c,0x2c,0x61,0x70,0x70,0x6c,0x69,0x63,
+ 0x61,0x74,0x69,0x6f,0x6e,0x2f,0x78,0x6d,
+ 0x6c,0x3b,0x71,0x3d,0x30,0x2e,0x39,0x2c,
+ 0x2a,0x2f,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x38,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x4c,0x61,0x6e,0x67,0x75,0x61,
+ 0x67,0x65,0x3a,0x20,0x65,0x6e,0x2d,0x75,
+ 0x73,0x2c,0x65,0x6e,0x3b,0x71,0x3d,0x30,
+ 0x2e,0x35,0x0d,0x0a,0x41,0x63,0x63,0x65,
+ 0x70,0x74,0x2d,0x45,0x6e,0x63,0x6f,0x64,
+ 0x69,0x6e,0x67,0x3a,0x20,0x67,0x7a,0x69,
+ 0x70,0x2c,0x64,0x65,0x66,0x6c,0x61,0x74,
+ 0x65,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x43,0x68,0x61,0x72,0x73,0x65,
+ 0x74,0x3a,0x20,0x49,0x53,0x4f,0x2d,0x38,
+ 0x38,0x35,0x39,0x2d,0x31,0x2c,0x75,0x74,
+ 0x66,0x2d,0x38,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x2c,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x0d,0x0a,0x4b,0x65,0x65,0x70,0x2d,
+ 0x41,0x6c,0x69,0x76,0x65,0x3a,0x20,0x33,
+ 0x30,0x30,0x0d,0x0a,0x43,0x6f,0x6e,0x6e,
+ 0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,
+ 0x6b,0x65,0x65,0x70,0x2d,0x61,0x6c,0x69,
+ 0x76,0x65,0x0d,0x0a,0x0d,0x0a }; /* end rawpkt1_ether */
+
+ FlowInitConfig(FLOW_QUIET);
+ p = UTHBuildPacketFromEth(rawpkt1_ether, sizeof(rawpkt1_ether));
+ if (p == NULL) {
+ SCLogDebug("Error building packet");
+ goto end;
+ }
+ UTHMatchPackets(de_ctx, &p, 1);
+
+ uint32_t sids[3] = {1, 2, 3};
+ uint32_t results[3] = {1, 1, 1};
+ result = UTHCheckPacketMatchResults(p, sids, results, 1);
+
+end:
+ if (p != NULL) {
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ }
+ if (de_ctx != NULL) {
+ SigCleanSignatures(de_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ FlowShutdown();
+
+ return result;
+}
+
+/** \test Ensure that we set a bidirectional Signature correctly
+* and we install it with the rest of the signatures, checking
+* also that it match with the correct addr directions
+*/
+int SigTestBidirec04 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+ Packet *p = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any -> any any (msg:\"SigTestBidirec03 sid 1\"; sid:1;)");
+ if (sig == NULL)
+ goto end;
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any <> any any (msg:\"SigTestBidirec03 sid 2 bidirectional\"; sid:2;)");
+ if (sig == NULL)
+ goto end;
+ if ( !(sig->init_flags & SIG_FLAG_INIT_BIDIREC))
+ goto end;
+ if (sig->next == NULL)
+ goto end;
+ if (sig->next->next == NULL)
+ goto end;
+ if (sig->next->next->next != NULL)
+ goto end;
+ if (de_ctx->signum != 3)
+ goto end;
+
+ sig = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.1.1 any -> any any (msg:\"SigTestBidirec03 sid 3\"; sid:3;)");
+ if (sig == NULL)
+ goto end;
+ if (sig->next == NULL)
+ goto end;
+ if (sig->next->next == NULL)
+ goto end;
+ if (sig->next->next->next == NULL)
+ goto end;
+ if (sig->next->next->next->next != NULL)
+ goto end;
+ if (de_ctx->signum != 4)
+ goto end;
+
+ uint8_t rawpkt1_ether[] = {
+ 0x00,0x50,0x56,0xea,0x00,0xbd,0x00,0x0c,
+ 0x29,0x40,0xc8,0xb5,0x08,0x00,0x45,0x00,
+ 0x01,0xa8,0xb9,0xbb,0x40,0x00,0x40,0x06,
+ 0xe0,0xbf,0xc0,0xa8,0x1c,0x83,0xc0,0xa8,
+ 0x01,0x01,0xb9,0x0a,0x00,0x50,0x6f,0xa2,
+ 0x92,0xed,0x7b,0xc1,0xd3,0x4d,0x50,0x18,
+ 0x16,0xd0,0xa0,0x6f,0x00,0x00,0x47,0x45,
+ 0x54,0x20,0x2f,0x20,0x48,0x54,0x54,0x50,
+ 0x2f,0x31,0x2e,0x31,0x0d,0x0a,0x48,0x6f,
+ 0x73,0x74,0x3a,0x20,0x31,0x39,0x32,0x2e,
+ 0x31,0x36,0x38,0x2e,0x31,0x2e,0x31,0x0d,
+ 0x0a,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,
+ 0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,
+ 0x69,0x6c,0x6c,0x61,0x2f,0x35,0x2e,0x30,
+ 0x20,0x28,0x58,0x31,0x31,0x3b,0x20,0x55,
+ 0x3b,0x20,0x4c,0x69,0x6e,0x75,0x78,0x20,
+ 0x78,0x38,0x36,0x5f,0x36,0x34,0x3b,0x20,
+ 0x65,0x6e,0x2d,0x55,0x53,0x3b,0x20,0x72,
+ 0x76,0x3a,0x31,0x2e,0x39,0x2e,0x30,0x2e,
+ 0x31,0x34,0x29,0x20,0x47,0x65,0x63,0x6b,
+ 0x6f,0x2f,0x32,0x30,0x30,0x39,0x30,0x39,
+ 0x30,0x32,0x31,0x37,0x20,0x55,0x62,0x75,
+ 0x6e,0x74,0x75,0x2f,0x39,0x2e,0x30,0x34,
+ 0x20,0x28,0x6a,0x61,0x75,0x6e,0x74,0x79,
+ 0x29,0x20,0x46,0x69,0x72,0x65,0x66,0x6f,
+ 0x78,0x2f,0x33,0x2e,0x30,0x2e,0x31,0x34,
+ 0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,0x74,
+ 0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,
+ 0x74,0x6d,0x6c,0x2c,0x61,0x70,0x70,0x6c,
+ 0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x2f,
+ 0x78,0x68,0x74,0x6d,0x6c,0x2b,0x78,0x6d,
+ 0x6c,0x2c,0x61,0x70,0x70,0x6c,0x69,0x63,
+ 0x61,0x74,0x69,0x6f,0x6e,0x2f,0x78,0x6d,
+ 0x6c,0x3b,0x71,0x3d,0x30,0x2e,0x39,0x2c,
+ 0x2a,0x2f,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x38,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x4c,0x61,0x6e,0x67,0x75,0x61,
+ 0x67,0x65,0x3a,0x20,0x65,0x6e,0x2d,0x75,
+ 0x73,0x2c,0x65,0x6e,0x3b,0x71,0x3d,0x30,
+ 0x2e,0x35,0x0d,0x0a,0x41,0x63,0x63,0x65,
+ 0x70,0x74,0x2d,0x45,0x6e,0x63,0x6f,0x64,
+ 0x69,0x6e,0x67,0x3a,0x20,0x67,0x7a,0x69,
+ 0x70,0x2c,0x64,0x65,0x66,0x6c,0x61,0x74,
+ 0x65,0x0d,0x0a,0x41,0x63,0x63,0x65,0x70,
+ 0x74,0x2d,0x43,0x68,0x61,0x72,0x73,0x65,
+ 0x74,0x3a,0x20,0x49,0x53,0x4f,0x2d,0x38,
+ 0x38,0x35,0x39,0x2d,0x31,0x2c,0x75,0x74,
+ 0x66,0x2d,0x38,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x2c,0x2a,0x3b,0x71,0x3d,0x30,0x2e,
+ 0x37,0x0d,0x0a,0x4b,0x65,0x65,0x70,0x2d,
+ 0x41,0x6c,0x69,0x76,0x65,0x3a,0x20,0x33,
+ 0x30,0x30,0x0d,0x0a,0x43,0x6f,0x6e,0x6e,
+ 0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,
+ 0x6b,0x65,0x65,0x70,0x2d,0x61,0x6c,0x69,
+ 0x76,0x65,0x0d,0x0a,0x0d,0x0a }; /* end rawpkt1_ether */
+
+ p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, rawpkt1_ether, sizeof(rawpkt1_ether), NULL);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* At this point we have a list of 4 signatures. The last one
+ is a copy of the second one. If we receive a packet
+ with source 192.168.1.1 80, all the sids should match */
+
+ SigGroupBuild(de_ctx);
+ //PatternMatchPrepare(mpm_ctx, MPM_B2G);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* only sid 2 should match with a packet going to 192.168.1.1 port 80 */
+ if (PacketAlertCheck(p, 1) <= 0 && PacketAlertCheck(p, 3) <= 0 &&
+ PacketAlertCheck(p, 2) == 1) {
+ result = 1;
+ }
+
+ if (p != NULL) {
+ PACKET_RECYCLE(p);
+ }
+ FlowShutdown();
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+end:
+ if (de_ctx != NULL) {
+ SigCleanSignatures(de_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ if (p != NULL)
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation01 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp !any any -> any any (msg:\"SigTest41-01 src address is !any \"; classtype:misc-activity; sid:410001; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation02 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any !any -> any any (msg:\"SigTest41-02 src ip is !any \"; classtype:misc-activity; sid:410002; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation03 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> any [80:!80] (msg:\"SigTest41-03 dst port [80:!80] \"; classtype:misc-activity; sid:410003; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation04 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> any [80,!80] (msg:\"SigTest41-03 dst port [80:!80] \"; classtype:misc-activity; sid:410003; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation05 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> [192.168.0.2,!192.168.0.2] any (msg:\"SigTest41-04 dst ip [192.168.0.2,!192.168.0.2] \"; classtype:misc-activity; sid:410004; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation06 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> any [100:1000,!1:20000] (msg:\"SigTest41-05 dst port [100:1000,!1:20000] \"; classtype:misc-activity; sid:410005; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test check that we don't allow invalid negation options
+ */
+static int SigParseTestNegation07 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> [192.168.0.2,!192.168.0.0/24] any (msg:\"SigTest41-06 dst ip [192.168.0.2,!192.168.0.0/24] \"; classtype:misc-activity; sid:410006; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test check valid negation bug 1079
+ */
+static int SigParseTestNegation08 (void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tcp any any -> [192.168.0.0/16,!192.168.0.0/24] any (sid:410006; rev:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test mpm
+ */
+int SigParseTestMpm01 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"mpm test\"; content:\"abcd\"; sid:1;)");
+ if (sig == NULL) {
+ printf("sig failed to init: ");
+ goto end;
+ }
+
+ if (sig->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("sig doesn't have content list: ");
+ goto end;
+ }
+
+ if (sig->mpm_content_maxlen != 4) {
+ printf("mpm content max len %"PRIu16", expected 4: ", sig->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sig->mpm_uricontent_maxlen != 0) {
+ printf("mpm uricontent max len %"PRIu16", expected 0: ", sig->mpm_uricontent_maxlen);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (sig != NULL)
+ SigFree(sig);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test mpm
+ */
+int SigParseTestMpm02 (void)
+{
+ int result = 0;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"mpm test\"; content:\"abcd\"; content:\"abcdef\"; sid:1;)");
+ if (sig == NULL) {
+ printf("sig failed to init: ");
+ goto end;
+ }
+
+ if (sig->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("sig doesn't have content list: ");
+ goto end;
+ }
+
+ if (sig->mpm_content_maxlen != 6) {
+ printf("mpm content max len %"PRIu16", expected 6: ", sig->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sig->mpm_uricontent_maxlen != 0) {
+ printf("mpm uricontent max len %"PRIu16", expected 0: ", sig->mpm_uricontent_maxlen);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (sig != NULL)
+ SigFree(sig);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test test tls (app layer) rule
+ */
+static int SigParseTestAppLayerTLS01(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS01 \"; sid:410006; rev:1;)");
+ if (s == NULL) {
+ printf("parsing sig failed: ");
+ goto end;
+ }
+
+ if (s->alproto == 0) {
+ printf("alproto not set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (s != NULL)
+ SigFree(s);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test test tls (app layer) rule
+ */
+static int SigParseTestAppLayerTLS02(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS02 \"; tls.version:1.0; sid:410006; rev:1;)");
+ if (s == NULL) {
+ printf("parsing sig failed: ");
+ goto end;
+ }
+
+ if (s->alproto == 0) {
+ printf("alproto not set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (s != NULL)
+ SigFree(s);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test test tls (app layer) rule
+ */
+static int SigParseTestAppLayerTLS03(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx;
+ Signature *s=NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS03 \"; tls.version:2.5; sid:410006; rev:1;)");
+ if (s != NULL) {
+ SigFree(s);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SigParseRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SigParseTest01", SigParseTest01, 1);
+ UtRegisterTest("SigParseTest02", SigParseTest02, 1);
+ UtRegisterTest("SigParseTest03", SigParseTest03, 1);
+ UtRegisterTest("SigParseTest04", SigParseTest04, 1);
+ UtRegisterTest("SigParseTest05", SigParseTest05, 1);
+ UtRegisterTest("SigParseTest06", SigParseTest06, 1);
+ UtRegisterTest("SigParseTest07", SigParseTest07, 1);
+ UtRegisterTest("SigParseTest08", SigParseTest08, 1);
+ UtRegisterTest("SigParseTest09", SigParseTest09, 1);
+ UtRegisterTest("SigParseTest10", SigParseTest10, 1);
+ UtRegisterTest("SigParseTest11", SigParseTest11, 1);
+ UtRegisterTest("SigParseTest12", SigParseTest12, 1);
+ UtRegisterTest("SigParseTest13", SigParseTest13, 1);
+ UtRegisterTest("SigParseTest14", SigParseTest14, 1);
+ UtRegisterTest("SigParseTest15", SigParseTest15, 1);
+ UtRegisterTest("SigParseTest16", SigParseTest16, 1);
+ UtRegisterTest("SigParseTest17", SigParseTest17, 1);
+ UtRegisterTest("SigParseTest18", SigParseTest18, 1);
+ UtRegisterTest("SigParseTest19", SigParseTest19, 1);
+ UtRegisterTest("SigParseTest20", SigParseTest20, 1);
+ UtRegisterTest("SigParseTest21 -- address with space", SigParseTest21, 1);
+ UtRegisterTest("SigParseTest22 -- address with space", SigParseTest22, 1);
+
+ UtRegisterTest("SigParseBidirecTest06", SigParseBidirecTest06, 1);
+ UtRegisterTest("SigParseBidirecTest07", SigParseBidirecTest07, 1);
+ UtRegisterTest("SigParseBidirecTest08", SigParseBidirecTest08, 1);
+ UtRegisterTest("SigParseBidirecTest09", SigParseBidirecTest09, 1);
+ UtRegisterTest("SigParseBidirecTest10", SigParseBidirecTest10, 1);
+ UtRegisterTest("SigParseBidirecTest11", SigParseBidirecTest11, 1);
+ UtRegisterTest("SigParseBidirecTest12", SigParseBidirecTest12, 1);
+ UtRegisterTest("SigParseBidirecTest13", SigParseBidirecTest13, 1);
+ UtRegisterTest("SigParseBidirecTest14", SigParseBidirecTest14, 1);
+ UtRegisterTest("SigTestBidirec01", SigTestBidirec01, 1);
+ UtRegisterTest("SigTestBidirec02", SigTestBidirec02, 1);
+ UtRegisterTest("SigTestBidirec03", SigTestBidirec03, 1);
+ UtRegisterTest("SigTestBidirec04", SigTestBidirec04, 1);
+ UtRegisterTest("SigParseTestNegation01", SigParseTestNegation01, 1);
+ UtRegisterTest("SigParseTestNegation02", SigParseTestNegation02, 1);
+ UtRegisterTest("SigParseTestNegation03", SigParseTestNegation03, 1);
+ UtRegisterTest("SigParseTestNegation04", SigParseTestNegation04, 1);
+ UtRegisterTest("SigParseTestNegation05", SigParseTestNegation05, 1);
+ UtRegisterTest("SigParseTestNegation06", SigParseTestNegation06, 1);
+ UtRegisterTest("SigParseTestNegation07", SigParseTestNegation07, 1);
+ UtRegisterTest("SigParseTestNegation08", SigParseTestNegation08, 1);
+ UtRegisterTest("SigParseTestMpm01", SigParseTestMpm01, 1);
+ UtRegisterTest("SigParseTestMpm02", SigParseTestMpm02, 1);
+ UtRegisterTest("SigParseTestAppLayerTLS01", SigParseTestAppLayerTLS01, 1);
+ UtRegisterTest("SigParseTestAppLayerTLS02", SigParseTestAppLayerTLS02, 1);
+ UtRegisterTest("SigParseTestAppLayerTLS03", SigParseTestAppLayerTLS03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-parse.h b/framework/src/suricata/src/detect-parse.h
new file mode 100644
index 00000000..c90560a9
--- /dev/null
+++ b/framework/src/suricata/src/detect-parse.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PARSE_H__
+#define __DETECT_PARSE_H__
+
+/** Flags to indicate if the Signature parsing must be done
+* switching the source and dest (for ip addresses and ports)
+* or otherwise as normal */
+enum {
+ SIG_DIREC_NORMAL,
+ SIG_DIREC_SWITCHED
+};
+
+/** Flags to indicate if are referencing the source of the Signature
+* or the destination (for ip addresses and ports)*/
+enum {
+ SIG_DIREC_SRC,
+ SIG_DIREC_DST
+};
+
+/* prototypes */
+int SigParse(DetectEngineCtx *,Signature *, char *, uint8_t);
+Signature *SigAlloc(void);
+void SigFree(Signature *s);
+Signature *SigInit(DetectEngineCtx *,char *sigstr);
+Signature *SigInitReal(DetectEngineCtx *, char *);
+SigMatch *SigMatchGetLastSMFromLists(Signature *, int, ...);
+void SigMatchTransferSigMatchAcrossLists(SigMatch *sm,
+ SigMatch **, SigMatch **s,
+ SigMatch **, SigMatch **);
+void SigParsePrepare(void);
+void SigParseRegisterTests(void);
+Signature *DetectEngineAppendSig(DetectEngineCtx *, char *);
+
+void SigMatchAppendSMToList(Signature *, SigMatch *, int);
+void SigMatchRemoveSMFromList(Signature *, SigMatch *, int);
+int SigMatchListSMBelongsTo(Signature *, SigMatch *);
+
+int DetectParseDupSigHashInit(DetectEngineCtx *);
+void DetectParseDupSigHashFree(DetectEngineCtx *);
+
+int DetectEngineContentModifierBufferSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg,
+ uint8_t sm_type, uint8_t sm_list,
+ AppProto alproto, void (*CustomCallback)(Signature *s));
+
+#endif /* __DETECT_PARSE_H__ */
+
diff --git a/framework/src/suricata/src/detect-pcre.c b/framework/src/suricata/src/detect-pcre.c
new file mode 100644
index 00000000..86f3d169
--- /dev/null
+++ b/framework/src/suricata/src/detect-pcre.c
@@ -0,0 +1,4126 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the pcre keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "pkt-var.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "detect-pcre.h"
+#include "detect-flowvar.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-sigorder.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "util-var-name.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-pool.h"
+
+#include "conf.h"
+#include "app-layer.h"
+#include "app-layer-htp.h"
+#include "stream.h"
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+
+#include "stream.h"
+
+
+#define PARSE_CAPTURE_REGEX "\\(\\?P\\<([A-z]+)\\_([A-z0-9_]+)\\>"
+#define PARSE_REGEX "(?<!\\\\)/(.*(?<!(?<!\\\\)\\\\))/([^\"]*)"
+
+#define SC_MATCH_LIMIT_DEFAULT 3500
+#define SC_MATCH_LIMIT_RECURSION_DEFAULT 1500
+
+static int pcre_match_limit = 0;
+static int pcre_match_limit_recursion = 0;
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+static pcre *parse_capture_regex;
+static pcre_extra *parse_capture_regex_study;
+
+int DetectPcreMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *);
+int DetectPcreALMatchCookie(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m);
+int DetectPcreALMatchMethod(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m);
+static int DetectPcreSetup (DetectEngineCtx *, Signature *, char *);
+void DetectPcreFree(void *);
+void DetectPcreRegisterTests(void);
+
+void DetectPcreRegister (void)
+{
+ sigmatch_table[DETECT_PCRE].name = "pcre";
+ sigmatch_table[DETECT_PCRE].desc = "match on regular expression";
+ sigmatch_table[DETECT_PCRE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#Pcre-Perl-Compatible-Regular-Expressions";
+ sigmatch_table[DETECT_PCRE].Match = NULL;
+ sigmatch_table[DETECT_PCRE].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_PCRE].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_PCRE].Setup = DetectPcreSetup;
+ sigmatch_table[DETECT_PCRE].Free = DetectPcreFree;
+ sigmatch_table[DETECT_PCRE].RegisterTests = DetectPcreRegisterTests;
+
+ sigmatch_table[DETECT_PCRE].flags |= SIGMATCH_PAYLOAD;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+ intmax_t val = 0;
+
+ if (!ConfGetInt("pcre.match-limit", &val)) {
+ pcre_match_limit = SC_MATCH_LIMIT_DEFAULT;
+ SCLogDebug("Using PCRE match-limit setting of: %i", pcre_match_limit);
+ }
+ else {
+ pcre_match_limit = val;
+ if (pcre_match_limit != SC_MATCH_LIMIT_DEFAULT) {
+ SCLogInfo("Using PCRE match-limit setting of: %i", pcre_match_limit);
+ } else {
+ SCLogDebug("Using PCRE match-limit setting of: %i", pcre_match_limit);
+ }
+ }
+
+ val = 0;
+
+ if (!ConfGetInt("pcre.match-limit-recursion", &val)) {
+ pcre_match_limit_recursion = SC_MATCH_LIMIT_RECURSION_DEFAULT;
+ SCLogDebug("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion);
+ }
+ else {
+ pcre_match_limit_recursion = val;
+ if (pcre_match_limit_recursion != SC_MATCH_LIMIT_RECURSION_DEFAULT) {
+ SCLogInfo("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion);
+ } else {
+ SCLogDebug("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion);
+ }
+ }
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ opts |= PCRE_UNGREEDY; /* pkt_http_ua should be pkt, http_ua, for this reason the UNGREEDY */
+ parse_capture_regex = pcre_compile(PARSE_CAPTURE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_capture_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_CAPTURE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_capture_regex_study = pcre_study(parse_capture_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \brief Match a regex on a single payload.
+ *
+ * \param det_ctx Thread detection ctx.
+ * \param s Signature.
+ * \param sm Sig match to match against.
+ * \param p Packet to set PktVars if any.
+ * \param f Flow to set FlowVars if any.
+ * \param payload Payload to inspect.
+ * \param payload_len Length of the payload.
+ *
+ * \retval 1 Match.
+ * \retval 0 No match.
+ */
+int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, Signature *s,
+ SigMatch *sm, Packet *p, Flow *f, uint8_t *payload,
+ uint32_t payload_len)
+{
+ SCEnter();
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+ uint8_t *ptr = NULL;
+ uint16_t len = 0;
+ uint16_t capture_len = 0;
+
+ DetectPcreData *pe = (DetectPcreData *)sm->ctx;
+
+ if (pe->flags & DETECT_PCRE_RELATIVE) {
+ ptr = payload + det_ctx->buffer_offset;
+ len = payload_len - det_ctx->buffer_offset;
+ } else {
+ ptr = payload;
+ len = payload_len;
+ }
+
+ int start_offset = 0;
+ if (det_ctx->pcre_match_start_offset != 0) {
+ start_offset = (payload + det_ctx->pcre_match_start_offset - ptr);
+ }
+
+ /* run the actual pcre detection */
+ ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, start_offset, 0, ov, MAX_SUBSTRINGS);
+ SCLogDebug("ret %d (negating %s)", ret, (pe->flags & DETECT_PCRE_NEGATE) ? "set" : "not set");
+
+ if (ret == PCRE_ERROR_NOMATCH) {
+ if (pe->flags & DETECT_PCRE_NEGATE) {
+ /* regex didn't match with negate option means we
+ * consider it a match */
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ } else if (ret >= 0) {
+ if (pe->flags & DETECT_PCRE_NEGATE) {
+ /* regex matched but we're negated, so not
+ * considering it a match */
+ ret = 0;
+ } else {
+ /* regex matched and we're not negated,
+ * considering it a match */
+
+ SCLogDebug("ret %d capidx %u", ret, pe->capidx);
+
+ /* see if we need to do substring capturing. */
+ if (ret > 1 && pe->capidx != 0) {
+ SCLogDebug("capturing");
+ const char *str_ptr;
+ ret = pcre_get_substring((char *)ptr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (ret) {
+ if (pe->flags & DETECT_PCRE_CAPTURE_PKT) {
+ if (p != NULL) {
+ PktVarAdd(p, pe->capname, (uint8_t *)str_ptr, ret);
+ }
+ } else if (pe->flags & DETECT_PCRE_CAPTURE_FLOW) {
+ if (f != NULL) {
+ /* store max 64k. Errors are ignored */
+ capture_len = (ret < 0xffff) ? (uint16_t)ret : 0xffff;
+ (void)DetectFlowvarStoreMatch(det_ctx, pe->capidx,
+ (uint8_t *)str_ptr, capture_len,
+ DETECT_FLOWVAR_TYPE_POSTMATCH);
+ }
+ }
+ }
+ }
+
+ /* update offset for pcre RELATIVE */
+ det_ctx->buffer_offset = (ptr + ov[1]) - payload;
+ det_ctx->pcre_match_start_offset = (ptr + ov[0] + 1) - payload;
+
+ ret = 1;
+ }
+
+ } else {
+ SCLogDebug("pcre had matching error");
+ ret = 0;
+ }
+ SCReturnInt(ret);
+}
+
+static int DetectPcreSetList(int list, int set)
+{
+ if (list != DETECT_SM_LIST_NOTSET) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "only one pcre option to specify a buffer type is allowed");
+ return -1;
+ }
+ return set;
+}
+
+static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, char *regexstr, int *sm_list)
+{
+ int ec;
+ const char *eb;
+ int eo;
+ int opts = 0;
+ DetectPcreData *pd = NULL;
+ char *op = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ size_t slen = strlen(regexstr);
+ char re[slen], op_str[64] = "";
+ uint16_t pos = 0;
+ uint8_t negate = 0;
+ uint16_t re_len = 0;
+ uint32_t u = 0;
+
+ while (pos < slen && isspace((unsigned char)regexstr[pos])) {
+ pos++;
+ }
+
+ if (regexstr[pos] == '!') {
+ negate = 1;
+ pos++;
+ }
+
+ ret = pcre_exec(parse_regex, parse_regex_study, regexstr + pos, slen-pos,
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret <= 0) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre parse error: %s", regexstr);
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)regexstr + pos, ov, MAX_SUBSTRINGS,
+ 1, re, slen);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return NULL;
+ }
+
+ if (ret > 2) {
+ res = pcre_copy_substring((char *)regexstr + pos, ov, MAX_SUBSTRINGS,
+ 2, op_str, sizeof(op_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return NULL;
+ }
+ op = op_str;
+ }
+ //printf("ret %" PRId32 " re \'%s\', op \'%s\'\n", ret, re, op);
+
+ pd = SCMalloc(sizeof(DetectPcreData));
+ if (unlikely(pd == NULL))
+ goto error;
+ memset(pd, 0, sizeof(DetectPcreData));
+
+ if (negate)
+ pd->flags |= DETECT_PCRE_NEGATE;
+
+ if (op != NULL) {
+ while (*op) {
+ SCLogDebug("regex option %c", *op);
+
+ switch (*op) {
+ case 'A':
+ opts |= PCRE_ANCHORED;
+ break;
+ case 'E':
+ opts |= PCRE_DOLLAR_ENDONLY;
+ break;
+ case 'G':
+ opts |= PCRE_UNGREEDY;
+ break;
+
+ case 'i':
+ opts |= PCRE_CASELESS;
+ pd->flags |= DETECT_PCRE_CASELESS;
+ break;
+ case 'm':
+ opts |= PCRE_MULTILINE;
+ break;
+ case 's':
+ opts |= PCRE_DOTALL;
+ break;
+ case 'x':
+ opts |= PCRE_EXTENDED;
+ break;
+
+ case 'O':
+ pd->flags |= DETECT_PCRE_MATCH_LIMIT;
+ break;
+
+ case 'B': /* snort's option */
+ if (*sm_list != DETECT_SM_LIST_NOTSET) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'B' inconsistent with chosen buffer");
+ goto error;
+ }
+ pd->flags |= DETECT_PCRE_RAWBYTES;
+ break;
+ case 'R': /* snort's option */
+ pd->flags |= DETECT_PCRE_RELATIVE;
+ break;
+
+ /* buffer selection */
+
+ case 'U': /* snort's option */
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'U' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_UMATCH);
+ break;
+ case 'V':
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'V' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HUADMATCH);
+ break;
+ case 'W':
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'W' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HHHDMATCH);
+ break;
+ case 'Z':
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'Z' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HRHHDMATCH);
+ break;
+ case 'H': /* snort's option */
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'H' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HHDMATCH);
+ break;
+ case 'I': /* snort's option */
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'I' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HRUDMATCH);
+ break;
+ case 'D': /* snort's option */
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HRHDMATCH);
+ break;
+ case 'M': /* snort's option */
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'M' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HMDMATCH);
+ break;
+ case 'C': /* snort's option */
+ if (pd->flags & DETECT_PCRE_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "regex modifier 'C' inconsistent with 'B'");
+ goto error;
+ }
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HCDMATCH);
+ break;
+ case 'P':
+ /* snort's option (http request body inspection) */
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HCBDMATCH);
+ break;
+ case 'Q':
+ /* suricata extension (http response body inspection) */
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_FILEDATA);
+ break;
+ case 'Y':
+ /* snort's option */
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HSMDMATCH);
+ break;
+ case 'S':
+ /* snort's option */
+ *sm_list = DetectPcreSetList(*sm_list, DETECT_SM_LIST_HSCDMATCH);
+ break;
+ default:
+ SCLogError(SC_ERR_UNKNOWN_REGEX_MOD, "unknown regex modifier '%c'", *op);
+ goto error;
+ }
+ op++;
+ }
+ }
+ if (*sm_list == -1)
+ goto error;
+
+ SCLogDebug("DetectPcreParse: \"%s\"", re);
+
+ /* host header */
+ if (*sm_list == DETECT_SM_LIST_HHHDMATCH) {
+ if (pd->flags & DETECT_PCRE_CASELESS) {
+ SCLogWarning(SC_ERR_INVALID_SIGNATURE, "http host pcre(\"W\") "
+ "specified along with \"i(caseless)\" modifier. "
+ "Since the hostname buffer we match against "
+ "is actually lowercase, having a "
+ "nocase is redundant.");
+ } else {
+ re_len = strlen(re);
+ for (u = 0; u < re_len; u++) {
+ if (isupper((unsigned char)re[u])) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "pcre host(\"W\") "
+ "specified has an uppercase char. "
+ "Since the hostname buffer we match against "
+ "is actually lowercase, please specify an "
+ "all lowercase based pcre.");
+ goto error;
+ }
+ }
+ }
+ }
+
+ /* Try to compile as if all (...) groups had been meant as (?:...),
+ * which is the common case in most rules.
+ * If we fail because a capture group is later referenced (e.g., \1),
+ * PCRE will let us know.
+ */
+ pd->re = pcre_compile2(re, opts | PCRE_NO_AUTO_CAPTURE, &ec, &eb, &eo, NULL);
+ if (pd->re == NULL && ec == 15) { // reference to non-existent subpattern
+ pd->re = pcre_compile(re, opts, &eb, &eo, NULL);
+ }
+
+ if(pd->re == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", regexstr, eo, eb);
+ goto error;
+ }
+#ifdef PCRE_HAVE_JIT
+ pd->sd = pcre_study(pd->re, PCRE_STUDY_JIT_COMPILE, &eb);
+ if(eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed : %s", eb);
+ goto error;
+ }
+
+ int jit = 0;
+ ret = pcre_fullinfo(pd->re, pd->sd, PCRE_INFO_JIT, &jit);
+ if (ret != 0 || jit != 1) {
+ /* warning, so we won't print the sig after this. Adding
+ * file and line to the message so the admin can figure
+ * out what sig this is about */
+ SCLogDebug("PCRE JIT compiler does not support: %s. "
+ "Falling back to regular PCRE handling (%s:%d)",
+ regexstr, de_ctx->rule_file, de_ctx->rule_line);
+ }
+#else
+ pd->sd = pcre_study(pd->re, 0, &eb);
+ if(eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed : %s", eb);
+ goto error;
+ }
+#endif /*PCRE_HAVE_JIT*/
+
+ if (pd->sd == NULL)
+ pd->sd = (pcre_extra *) SCCalloc(1,sizeof(pcre_extra));
+
+ if (pd->sd) {
+ if(pd->flags & DETECT_PCRE_MATCH_LIMIT) {
+ if(pcre_match_limit >= -1) {
+ pd->sd->match_limit = pcre_match_limit;
+ pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT;
+ }
+#ifndef NO_PCRE_MATCH_RLIMIT
+ if(pcre_match_limit_recursion >= -1) {
+ pd->sd->match_limit_recursion = pcre_match_limit_recursion;
+ pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+ }
+#endif /* NO_PCRE_MATCH_RLIMIT */
+ } else {
+ pd->sd->match_limit = SC_MATCH_LIMIT_DEFAULT;
+ pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT;
+#ifndef NO_PCRE_MATCH_RLIMIT
+ pd->sd->match_limit_recursion = SC_MATCH_LIMIT_RECURSION_DEFAULT;
+ pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+#endif /* NO_PCRE_MATCH_RLIMIT */
+ }
+ } else {
+ goto error;
+ }
+
+ return pd;
+
+error:
+ if (pd != NULL && pd->re != NULL)
+ pcre_free(pd->re);
+ if (pd != NULL && pd->sd != NULL)
+ pcre_free(pd->sd);
+ if (pd)
+ SCFree(pd);
+ return NULL;
+}
+
+/** \internal
+ * \brief check if we need to extract capture settings and set them up if needed
+ */
+static int DetectPcreParseCapture(char *regexstr, DetectEngineCtx *de_ctx, DetectPcreData *pd)
+{
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char type_str[16] = "";
+ size_t cap_buffer_len = strlen(regexstr);
+ char capture_str[cap_buffer_len];
+ memset(capture_str, 0x00, cap_buffer_len);
+
+ if (de_ctx == NULL)
+ goto error;
+
+ SCLogDebug("\'%s\'", regexstr);
+
+ ret = pcre_exec(parse_capture_regex, parse_capture_regex_study, regexstr, strlen(regexstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3) {
+ return 0;
+ }
+
+ res = pcre_copy_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 1, type_str, sizeof(type_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ res = pcre_copy_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 2, capture_str, cap_buffer_len);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ if (strlen(capture_str) == 0 || strlen(type_str) == 0) {
+ goto error;
+ }
+
+ SCLogDebug("type \'%s\'", type_str);
+ SCLogDebug("capture \'%s\'", capture_str);
+
+ pd->capname = SCStrdup(capture_str);
+ if (unlikely(pd->capname == NULL))
+ goto error;
+
+ if (strcmp(type_str, "pkt") == 0) {
+ pd->flags |= DETECT_PCRE_CAPTURE_PKT;
+ } else if (strcmp(type_str, "flow") == 0) {
+ pd->flags |= DETECT_PCRE_CAPTURE_FLOW;
+ SCLogDebug("flow capture");
+ }
+ if (pd->capname != NULL) {
+ if (pd->flags & DETECT_PCRE_CAPTURE_PKT)
+ pd->capidx = VariableNameGetIdx(de_ctx, (char *)pd->capname, VAR_TYPE_PKT_VAR);
+ else if (pd->flags & DETECT_PCRE_CAPTURE_FLOW)
+ pd->capidx = VariableNameGetIdx(de_ctx, (char *)pd->capname, VAR_TYPE_FLOW_VAR);
+ }
+
+ SCLogDebug("pd->capname %s", pd->capname);
+ return 0;
+
+error:
+ if (pd->capname != NULL) {
+ SCFree(pd->capname);
+ pd->capname = NULL;
+ }
+ return -1;
+}
+
+static int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, char *regexstr)
+{
+ SCEnter();
+ DetectPcreData *pd = NULL;
+ SigMatch *sm = NULL;
+ int ret = -1;
+ int parsed_sm_list = DETECT_SM_LIST_NOTSET;
+
+ pd = DetectPcreParse(de_ctx, regexstr, &parsed_sm_list);
+ if (pd == NULL)
+ goto error;
+ if (DetectPcreParseCapture(regexstr, de_ctx, pd) < 0)
+ goto error;
+
+ if (parsed_sm_list == DETECT_SM_LIST_UMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HRUDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HCBDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_FILEDATA ||
+ parsed_sm_list == DETECT_SM_LIST_HHDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HRHDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HSMDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HSCDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HHHDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HRHHDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HMDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HCDMATCH ||
+ parsed_sm_list == DETECT_SM_LIST_HUADMATCH)
+ {
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. "
+ "Conflicting alprotos detected for this rule. Http "
+ "pcre modifier found along with a different protocol "
+ "for the rule.");
+ goto error;
+ }
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "pcre found with http "
+ "modifier set, with file_data/dce_stub_data sticky "
+ "option set.");
+ goto error;
+ }
+ }
+
+ int sm_list = -1;
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ if (s->list == DETECT_SM_LIST_FILEDATA) {
+ SCLogDebug("adding to http server body list because of file data");
+ AppLayerHtpEnableResponseBodyCallback();
+ } else if (s->list == DETECT_SM_LIST_DMATCH) {
+ SCLogDebug("adding to dmatch list because of dce_stub_data");
+ } else if (s->list == DETECT_SM_LIST_DNSQUERYNAME_MATCH) {
+ SCLogDebug("adding to DETECT_SM_LIST_DNSQUERYNAME_MATCH list because of dns_query");
+ }
+ s->flags |= SIG_FLAG_APPLAYER;
+ sm_list = s->list;
+ } else {
+ switch(parsed_sm_list) {
+ case DETECT_SM_LIST_HCBDMATCH:
+ AppLayerHtpEnableRequestBodyCallback();
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_HTTP;
+ sm_list = parsed_sm_list;
+ break;
+
+ case DETECT_SM_LIST_FILEDATA:
+ AppLayerHtpEnableResponseBodyCallback();
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_HTTP;
+ sm_list = parsed_sm_list;
+ break;
+
+ case DETECT_SM_LIST_UMATCH:
+ case DETECT_SM_LIST_HRUDMATCH:
+ case DETECT_SM_LIST_HHDMATCH:
+ case DETECT_SM_LIST_HRHDMATCH:
+ case DETECT_SM_LIST_HHHDMATCH:
+ case DETECT_SM_LIST_HRHHDMATCH:
+ case DETECT_SM_LIST_HSMDMATCH:
+ case DETECT_SM_LIST_HSCDMATCH:
+ case DETECT_SM_LIST_HCDMATCH:
+ case DETECT_SM_LIST_HMDMATCH:
+ case DETECT_SM_LIST_HUADMATCH:
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_HTTP;
+ sm_list = parsed_sm_list;
+ break;
+ case DETECT_SM_LIST_NOTSET:
+ sm_list = DETECT_SM_LIST_PMATCH;
+ break;
+ }
+ }
+ if (sm_list == -1)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_PCRE;
+ sm->ctx = (void *)pd;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ if (pd->capidx != 0) {
+ if (DetectFlowvarPostMatchSetup(s, pd->capidx) < 0)
+ goto error_nofree;
+ }
+
+ if (!(pd->flags & DETECT_PCRE_RELATIVE))
+ goto okay;
+
+ /* errors below shouldn't free pd */
+
+ SigMatch *prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, sm->prev,
+ DETECT_PCRE, sm->prev);
+ if (s->list == DETECT_SM_LIST_NOTSET && prev_pm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "pcre with /R (relative) needs "
+ "preceeding match in the same buffer");
+ goto error_nofree;
+ /* null is allowed when we use a sticky buffer */
+ } else if (prev_pm == NULL)
+ goto okay;
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *tmp = (DetectPcreData *)prev_pm->ctx;
+ tmp->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ okay:
+ ret = 0;
+ SCReturnInt(ret);
+ error:
+ DetectPcreFree(pd);
+ error_nofree:
+ SCReturnInt(ret);
+}
+
+void DetectPcreFree(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ DetectPcreData *pd = (DetectPcreData *)ptr;
+
+ if (pd->capname != NULL)
+ SCFree(pd->capname);
+ if (pd->re != NULL)
+ pcre_free(pd->re);
+ if (pd->sd != NULL)
+ pcre_free(pd->sd);
+
+ SCFree(pd);
+ return;
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectPcreParseTest01 make sure we don't allow invalid opts 7.
+ */
+static int DetectPcreParseTest01 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/blah/7";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd != NULL) {
+ printf("expected NULL: got %p", pd);
+ result = 0;
+ DetectPcreFree(pd);
+ }
+
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest02 make sure we don't allow invalid opts Ui$.
+ */
+static int DetectPcreParseTest02 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/blah/Ui$";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd != NULL) {
+ printf("expected NULL: got %p", pd);
+ result = 0;
+ DetectPcreFree(pd);
+ }
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest03 make sure we don't allow invalid opts UZi.
+ */
+static int DetectPcreParseTest03 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/blah/UNi";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd != NULL) {
+ printf("expected NULL: got %p", pd);
+ result = 0;
+ DetectPcreFree(pd);
+ }
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest04 make sure we allow escaped "
+ */
+static int DetectPcreParseTest04 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/b\\\"lah/i";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest05 make sure we parse pcre with no opts
+ */
+static int DetectPcreParseTest05 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/b(l|a)h/";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest06 make sure we parse pcre with smi opts
+ */
+static int DetectPcreParseTest06 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/b(l|a)h/smi";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest07 make sure we parse pcre with /Ui opts
+ */
+static int DetectPcreParseTest07 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/blah/Ui";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest08 make sure we parse pcre with O opts
+ */
+static int DetectPcreParseTest08 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/b(l|a)h/O";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test DetectPcreParseTest09 make sure we parse pcre with a content
+ * that has slashes
+ */
+static int DetectPcreParseTest09 (void)
+{
+ int result = 1;
+ DetectPcreData *pd = NULL;
+ char *teststring = "/lala\\\\/";
+ int list = DETECT_SM_LIST_NOTSET;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return 0;
+
+ pd = DetectPcreParse(de_ctx, teststring, &list);
+ if (pd == NULL) {
+ printf("expected %p: got NULL", pd);
+ result = 0;
+ }
+
+ DetectPcreFree(pd);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Test pcre option for dce sig(yeah I'm bored of writing test titles).
+ */
+int DetectPcreParseTest10(void)
+{
+ Signature *s = SigAlloc();
+ int result = 1;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ s->alproto = ALPROTO_DCERPC;
+
+ result &= (DetectPcreSetup(de_ctx, s, "/bamboo/") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ SigFree(s);
+
+ s = SigAlloc();
+ /* failure since we have no preceding content/pcre/bytejump */
+ result &= (DetectPcreSetup(de_ctx, s, "/bamboo/") == 0);
+ result &= (s->sm_lists[DETECT_SM_LIST_DMATCH] == NULL && s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL);
+
+ end:
+ SigFree(s);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test pcre option for dce sig.
+ */
+int DetectPcreParseTest11(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+ Signature *s = NULL;
+ DetectPcreData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "pcre:/bamboo/R; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_PCRE);
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_PCRE_RAWBYTES ||
+ !(data->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "pcre:/bamboo/R; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_PCRE);
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (data->flags & DETECT_PCRE_RAWBYTES ||
+ !(data->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "pcre:/bamboo/RB; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] == NULL) {
+ result = 0;
+ goto end;
+ }
+ result &= (s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->type == DETECT_PCRE);
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_DMATCH]->ctx;
+ if (!(data->flags & DETECT_PCRE_RAWBYTES) ||
+ !(data->flags & DETECT_PCRE_RELATIVE)) {
+ result = 0;
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing bytejump_body\"; "
+ "content:\"one\"; pcre:/bamboo/; sid:1;)");
+ if (s->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ s = s->next;
+ if (s->sm_lists_tail[DETECT_SM_LIST_DMATCH] != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test pcre option with file data. pcre is relative to file_data,
+ * so relative flag should be unset.
+ */
+static int DetectPcreParseTest12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectPcreData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; pcre:/abc/R; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("empty server body list: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_PCRE) {
+ printf("last sm not pcre: ");
+ goto end;
+ }
+
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (data->flags & DETECT_PCRE_RAWBYTES ||
+ !(data->flags & DETECT_PCRE_RELATIVE)) {
+ printf("flags not right: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test pcre option with file data.
+ */
+static int DetectPcreParseTest13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectPcreData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; content:\"abc\"; pcre:/def/R; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("empty server body list: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_PCRE) {
+ printf("last sm not pcre: ");
+ goto end;
+ }
+
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (data->flags & DETECT_PCRE_RAWBYTES ||
+ !(data->flags & DETECT_PCRE_RELATIVE)) {
+ printf("flags not right: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test pcre option with file data.
+ */
+static int DetectPcreParseTest14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Signature *s = NULL;
+ DetectPcreData *data = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; pcre:/def/; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ s = de_ctx->sig_list;
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA] == NULL) {
+ printf("empty server body list: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->type != DETECT_PCRE) {
+ printf("last sm not pcre: ");
+ goto end;
+ }
+
+ data = (DetectPcreData *)s->sm_lists_tail[DETECT_SM_LIST_FILEDATA]->ctx;
+ if (data->flags & DETECT_PCRE_RAWBYTES ||
+ data->flags & DETECT_PCRE_RELATIVE) {
+ printf("flags not right: ");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/** \test Check a signature with pcre relative method */
+int DetectPcreParseTest15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative http_method\"; "
+ "content:\"GET\"; "
+ "http_method; pcre:\"/abc/RM\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+
+/** \test Check a signature with pcre relative cookie */
+int DetectPcreParseTest16(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative http_cookie\"; "
+ "content:\"test\"; "
+ "http_cookie; pcre:\"/abc/RC\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative raw header */
+int DetectPcreParseTest17(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative http_raw_header\"; "
+ "flow:to_server; content:\"test\"; "
+ "http_raw_header; pcre:\"/abc/RD\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative header */
+int DetectPcreParseTest18(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative http_header\"; "
+ "content:\"test\"; "
+ "http_header; pcre:\"/abc/RH\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative client-body */
+int DetectPcreParseTest19(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relativie http_client_body\"; "
+ "content:\"test\"; "
+ "http_client_body; pcre:\"/abc/RP\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative raw uri */
+int DetectPcreParseTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_raw_uri\"; "
+ "content:\"test\"; "
+ "http_raw_uri; pcre:\"/abc/RI\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative uricontent */
+int DetectPcreParseTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative uricontent\"; "
+ "uricontent:\"test\"; "
+ "pcre:\"/abc/RU\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with pcre relative http_uri */
+int DetectPcreParseTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing pcre relative http_uri\"; "
+ "content:\"test\"; "
+ "http_uri; pcre:\"/abc/RU\"; sid:1;)");
+
+ if (de_ctx->sig_list != NULL) {
+ result = 1;
+ } else {
+ printf("sig parse failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with inconsistent pcre relative */
+int DetectPcreParseTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing inconsistent pcre relative\"; "
+ "content:\"GET\"; "
+ "http_cookie; pcre:\"/abc/RM\"; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ } else {
+ printf("sig parse shouldn't have failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with inconsistent pcre modifiers */
+int DetectPcreParseTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing inconsistent pcre modifiers\"; "
+ "pcre:\"/abc/UI\"; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ } else {
+ printf("sig parse should have failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with inconsistent pcre modifiers */
+int DetectPcreParseTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing inconsistent pcre modifiers\"; "
+ "pcre:\"/abc/DH\"; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ } else {
+ printf("sig parse should have failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check a signature with inconsistent pcre modifiers */
+static int DetectPcreParseTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert http any any -> any any "
+ "(msg:\"Testing inconsistent pcre modifiers\"; "
+ "pcre:\"/abc/F\"; sid:1;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ } else {
+ printf("sig parse should have failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Bug 1098 */
+static int DetectPcreParseTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any 80 "
+ "(content:\"baduricontent\"; http_raw_uri; "
+ "pcre:\"/^[a-z]{5}\\.html/R\"; sid:2; rev:2;)");
+
+ if (de_ctx->sig_list == NULL) {
+ result = 1;
+ } else {
+ printf("sig parse should have failed: ");
+ }
+
+ end:
+ if (de_ctx != NULL)
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int DetectPcreTestSig01Real(int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ TcpSession ssn;
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+ Flow f;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; pcre:\"/^gEt/i\"; pcre:\"/\\/two\\//U; pcre:\"/GET \\/two\\//\"; pcre:\"/\\s+HTTP/R\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 1) {
+ result = 1;
+ }
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int DetectPcreTestSig01B2g (void)
+{
+ return DetectPcreTestSig01Real(MPM_B2G);
+}
+static int DetectPcreTestSig01B3g (void)
+{
+ return DetectPcreTestSig01Real(MPM_B3G);
+}
+static int DetectPcreTestSig01Wm (void)
+{
+ return DetectPcreTestSig01Real(MPM_WUMANBER);
+}
+
+static int DetectPcreTestSig02Real(int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ Flow f;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+
+ FLOW_INITIALIZE(&f);
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ pcre_match_limit = 100;
+ pcre_match_limit_recursion = 100;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; pcre:\"/two/O\"; sid:2;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 2) == 1) {
+ result = 1;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ FLOW_DESTROY(&f);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int DetectPcreTestSig02B2g (void)
+{
+ return DetectPcreTestSig02Real(MPM_B2G);
+}
+static int DetectPcreTestSig02B3g (void)
+{
+ return DetectPcreTestSig02Real(MPM_B3G);
+}
+static int DetectPcreTestSig02Wm (void)
+{
+ return DetectPcreTestSig02Real(MPM_WUMANBER);
+}
+
+/**
+ * \test DetectPcreTestSig03Real negation test ! outside of "" this sig should not match
+ */
+static int DetectPcreTestSig03Real(int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 1;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"GET\"; pcre:!\"/two/\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)){
+ printf("sid 1 matched even though it shouldn't have:");
+ result = 0;
+ }
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectPcreTestSig03B2g (void)
+{
+ return DetectPcreTestSig03Real(MPM_B2G);
+}
+static int DetectPcreTestSig03B3g (void)
+{
+ return DetectPcreTestSig03Real(MPM_B3G);
+}
+static int DetectPcreTestSig03Wm (void)
+{
+ return DetectPcreTestSig03Real(MPM_WUMANBER);
+}
+
+/**
+ * \test Check the signature with pcre modifier P (match with L7 to http body data)
+ */
+static int DetectPcreModifPTest04(void)
+{
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"
+ "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Server: Apache\r\n"
+ "X-Powered-By: PHP/5.2.5\r\n"
+ "P3P: CP=\"NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM\"\r\n"
+ "Expires: Mon, 1 Jan 2001 00:00:00 GMT\r\n"
+ "Last-Modified: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n"
+ "Pragma: no-cache\r\n"
+ "Keep-Alive: timeout=15, max=100\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "15"
+ "\r\n"
+ "<!DOCTYPE html PUBLIC\r\n"
+ "0\r\n\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"Pcre modifier P\"; pcre:\"/DOCTYPE/P\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\""
+ "Pcre modifier P (no match)\"; pcre:\"/blah/P\"; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sid 1 didn't match but should have: ");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2)) {
+ printf("sid 2 matched but shouldn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Check the signature with pcre modifier P (match with L7 to http body data)
+ * over fragmented chunks (DOCTYPE fragmented)
+ */
+static int DetectPcreModifPTest05(void)
+{
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"
+ "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Server: Apache\r\n"
+ "X-Powered-By: PHP/5.2.5\r\n"
+ "P3P: CP=\"NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM\"\r\n"
+ "Expires: Mon, 1 Jan 2001 00:00:00 GMT\r\n"
+ "Last-Modified: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n"
+ "Pragma: no-cache\r\n"
+ "Keep-Alive: timeout=15, max=100\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "15"
+ "\r\n"
+ "<!DOC";
+
+ uint8_t httpbuf2[] = "<!DOCTYPE html PUBLIC\r\n0\r\n\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"Pcre modifier P\"; pcre:\"/DOC/P\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\""
+ "Pcre modifier P (no match)\"; pcre:\"/DOCTYPE/P\"; sid:2;)");
+ if (s->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p1, 2)) {
+ printf("sid 2 did match on p1 but shouldn't have: ");
+ /* It's a partial match over 2 chunks*/
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect for p2 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+ if (!(PacketAlertCheck(p2, 1))) {
+ printf("sid 1 did match on p2 but should have: ");
+ goto end;
+ }
+
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sid 2 didn't match on p2 but should have: ");
+ /* It's a partial match over 2 chunks*/
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ return result;
+}
+
+int DetectPcreTestSig06()
+{
+ uint8_t *buf = (uint8_t *)
+ "lalala lalala\\ lala\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; pcre:\"/ lalala\\\\/\"; sid:1;)";
+ if (UTHPacketMatchSig(p, sig) == 0) {
+ result = 0;
+ goto end;
+ }
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test anchored pcre */
+int DetectPcreTestSig07()
+{
+ uint8_t *buf = (uint8_t *)
+ "lalala\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; pcre:\"/^(la)+$/\"; sid:1;)";
+ if (UTHPacketMatchSig(p, sig) == 0) {
+ result = 0;
+ goto end;
+ }
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test anchored pcre */
+int DetectPcreTestSig08()
+{
+ /* test it also without ending in a newline "\n" */
+ uint8_t *buf = (uint8_t *)
+ "lalala";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; pcre:\"/^(la)+$/\"; sid:1;)";
+ if (UTHPacketMatchSig(p, sig) == 0) {
+ result = 0;
+ goto end;
+ }
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test Check the signature working to alert when cookie modifier is
+ * passed to pcre
+ */
+static int DetectPcreTestSig09(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; pcre:\"/dummy/C\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when cookie modifier is
+ * passed to a negated pcre
+ */
+static int DetectPcreTestSig10(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummoOOooooO\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP cookie\"; pcre:!\"/dummy/C\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when method modifier is
+ * passed to pcre
+ */
+static int DetectPcreTestSig11(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP method\"; pcre:\"/POST/M\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when method modifier is
+ * passed to a negated pcre
+ */
+static int DetectPcreTestSig12(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummoOOooooO\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP method\"; pcre:!\"/POST/M\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when header modifier is
+ * passed to pcre
+ */
+static int DetectPcreTestSig13(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP header\"; pcre:\"/User[-_]Agent[:]?\\sMozilla/H\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when header modifier is
+ * passed to a negated pcre
+ */
+static int DetectPcreTestSig14(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nUser-Agent: IEXPLORER/1.0\r\n"
+ "Cookie: dummoOOooooO\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"HTTP header\"; pcre:!\"/User-Agent[:]?\\s+Mozilla/H\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when cookie and relative modifiers are
+ * passed to pcre
+ */
+static int DetectPcreTestSig15(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy 1234\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"pcre relative HTTP cookie\"; content:\"dummy\";"
+ " http_cookie; pcre:\"/1234/RC\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the signature working to alert when method and relative modifiers are
+ * passed to pcre
+ */
+static int DetectPcreTestSig16(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n"
+ "Cookie: dummy 1234\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\"pcre relative HTTP method\"; content:\"PO\";"
+ " http_method; pcre:\"/ST/RM\"; "
+ " sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 failed to match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Test tracking of body chunks per transactions (on requests)
+ */
+static int DetectPcreTxBodyChunksTest01(void)
+{
+ int result = 0;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "GET / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Body one!!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ AppLayerHtpEnableRequestBodyCallback();
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ /* Now we should have 2 transactions, each with it's own list
+ * of request body chunks (let's test it) */
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* hardcoded check of the transactions and it's client body chunks */
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ htp_tx_t *t1 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+ htp_tx_t *t2 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 1);
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1);
+ if (htud == NULL) {
+ printf("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ HtpBodyChunk *cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+ htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
+
+ cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SCMutexUnlock(&f.m);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test test pcre P modifier with multiple pipelined http transactions */
+static int DetectPcreTxBodyChunksTest02(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Body one!!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ /* hardcoded check of the transactions and it's client body chunks */
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ htp_tx_t *t1 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+ htp_tx_t *t2 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 1);
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1);
+
+ HtpBodyChunk *cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+ htud = (HtpTxUserData *) htp_tx_get_user_data(t2);
+
+ cur = htud->request_body.first;
+ if (htud->request_body.first == NULL) {
+ SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): ");
+ goto end;
+ }
+
+ if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) {
+ SCLogDebug("Body data in t1 is not correctly set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multiple http transactions and body chunks of request handling */
+static int DetectPcreTxBodyChunksTest03(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n";
+ uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n";
+ uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n";
+ uint8_t httpbuf4[] = "Body one!!";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n";
+ uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n";
+ uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+ uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
+ uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, htp_state) != 2) {
+ printf("The http app layer doesn't have 2 transactions, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/**
+ * \test flowvar capture on http buffer
+ */
+static int DetectPcreFlowvarCapture01(void)
+{
+ int result = 0;
+ uint8_t uabuf1[] =
+ "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13";
+ uint32_t ualen1 = sizeof(uabuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf1[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"
+ "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Server: Apache\r\n"
+ "\r\n"
+ "<!DOCTYPE html PUBLIC\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"User-Agent: \"; http_header; pcre:\"/(?P<flow_ua>.*)\\r\\n/HR\"; sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->type != DETECT_PCRE) {
+ goto end;
+ }
+ DetectPcreData *pd = (DetectPcreData *)s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->ctx;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, pd->capidx);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_str.value_len != ualen1) {
+ printf("%u != %u: ", fv->data.fv_str.value_len, ualen1);
+ goto end;
+ }
+
+ if (memcmp(fv->data.fv_str.value, uabuf1, ualen1) != 0) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+ PrintRawDataFp(stdout, uabuf1, ualen1);
+
+ printf("buffer mismatch: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/**
+ * \test flowvar capture on http buffer, capture overwrite
+ */
+static int DetectPcreFlowvarCapture02(void)
+{
+ int result = 0;
+ uint8_t uabuf1[] =
+ "Apache";
+ uint32_t ualen1 = sizeof(uabuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf1[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"
+ "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Server: Apache\r\n"
+ "\r\n"
+ "<!DOCTYPE html PUBLIC\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"User-Agent: \"; http_header; pcre:\"/(?P<flow_ua>.*)\\r\\n/HR\"; priority:1; sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->type != DETECT_PCRE) {
+ goto end;
+ }
+ DetectPcreData *pd1 = (DetectPcreData *)s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->ctx;
+
+ s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"Server: \"; http_header; pcre:\"/(?P<flow_ua>.*)\\r\\n/HR\"; priority:3; sid:2;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->type != DETECT_PCRE) {
+ goto end;
+ }
+ DetectPcreData *pd2 = (DetectPcreData *)s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->ctx;
+
+ if (pd1->capidx != pd2->capidx) {
+ printf("capidx mismatch, %u != %u: ", pd1->capidx, pd2->capidx);
+ goto end;
+ }
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+ SCSigSignatureOrderingModuleCleanup(de_ctx);
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1 but should have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, pd1->capidx);
+ if (fv == NULL) {
+ printf("no flowvar: ");
+ goto end;
+ }
+
+ if (fv->data.fv_str.value_len != ualen1) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+ PrintRawDataFp(stdout, uabuf1, ualen1);
+ printf("%u != %u: ", fv->data.fv_str.value_len, ualen1);
+ goto end;
+ }
+
+ if (memcmp(fv->data.fv_str.value, uabuf1, ualen1) != 0) {
+ PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
+ PrintRawDataFp(stdout, uabuf1, ualen1);
+
+ printf("buffer mismatch: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/**
+ * \test flowvar capture on http buffer, capture overwrite + no matching sigs, so flowvars should not be set.
+ */
+static int DetectPcreFlowvarCapture03(void)
+{
+ int result = 0;
+ uint8_t httpbuf1[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: www.emergingthreats.net\r\n"
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"
+ "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+ "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+ "Server: Apache\r\n"
+ "\r\n"
+ "<!DOCTYPE html PUBLIC\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Flow f;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"User-Agent: \"; http_header; pcre:\"/(?P<flow_ua>.*)\\r\\n/HR\"; content:\"xyz\"; http_header; priority:1; sid:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->type != DETECT_PCRE) {
+ goto end;
+ }
+ DetectPcreData *pd1 = (DetectPcreData *)s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->ctx;
+
+ s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"Server: \"; http_header; pcre:\"/(?P<flow_ua>.*)\\r\\n/HR\"; content:\"xyz\"; http_header; priority:3; sid:2;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next == NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->type != DETECT_PCRE) {
+ goto end;
+ }
+ DetectPcreData *pd2 = (DetectPcreData *)s->sm_lists[DETECT_SM_LIST_HHDMATCH]->next->ctx;
+
+ if (pd1->capidx != pd2->capidx) {
+ printf("capidx mismatch, %u != %u: ", pd1->capidx, pd2->capidx);
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ HtpState *http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect for p1 */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 matched on p1 but shouldn't have: ");
+ goto end;
+ }
+
+ FlowVar *fv = FlowVarGet(&f, pd1->capidx);
+ if (fv != NULL) {
+ printf("flowvar, shouldn't have one: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectPcre
+ */
+void DetectPcreRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectPcreParseTest01", DetectPcreParseTest01, 1);
+ UtRegisterTest("DetectPcreParseTest02", DetectPcreParseTest02, 1);
+ UtRegisterTest("DetectPcreParseTest03", DetectPcreParseTest03, 1);
+ UtRegisterTest("DetectPcreParseTest04", DetectPcreParseTest04, 1);
+ UtRegisterTest("DetectPcreParseTest05", DetectPcreParseTest05, 1);
+ UtRegisterTest("DetectPcreParseTest06", DetectPcreParseTest06, 1);
+ UtRegisterTest("DetectPcreParseTest07", DetectPcreParseTest07, 1);
+ UtRegisterTest("DetectPcreParseTest08", DetectPcreParseTest08, 1);
+ UtRegisterTest("DetectPcreParseTest09", DetectPcreParseTest09, 1);
+ UtRegisterTest("DetectPcreParseTest10", DetectPcreParseTest10, 1);
+ UtRegisterTest("DetectPcreParseTest11", DetectPcreParseTest11, 1);
+ UtRegisterTest("DetectPcreParseTest12", DetectPcreParseTest12, 1);
+ UtRegisterTest("DetectPcreParseTest13", DetectPcreParseTest13, 1);
+ UtRegisterTest("DetectPcreParseTest14", DetectPcreParseTest14, 1);
+ UtRegisterTest("DetectPcreParseTest15", DetectPcreParseTest15, 1);
+ UtRegisterTest("DetectPcreParseTest16", DetectPcreParseTest16, 1);
+ UtRegisterTest("DetectPcreParseTest17", DetectPcreParseTest17, 1);
+ UtRegisterTest("DetectPcreParseTest18", DetectPcreParseTest18, 1);
+ UtRegisterTest("DetectPcreParseTest19", DetectPcreParseTest19, 1);
+ UtRegisterTest("DetectPcreParseTest20", DetectPcreParseTest20, 1);
+ UtRegisterTest("DetectPcreParseTest21", DetectPcreParseTest21, 1);
+ UtRegisterTest("DetectPcreParseTest22", DetectPcreParseTest22, 1);
+ UtRegisterTest("DetectPcreParseTest23", DetectPcreParseTest23, 1);
+ UtRegisterTest("DetectPcreParseTest24", DetectPcreParseTest24, 1);
+ UtRegisterTest("DetectPcreParseTest25", DetectPcreParseTest25, 1);
+ UtRegisterTest("DetectPcreParseTest26", DetectPcreParseTest26, 1);
+ UtRegisterTest("DetectPcreParseTest27", DetectPcreParseTest27, 1);
+
+ UtRegisterTest("DetectPcreTestSig01B2g -- pcre test", DetectPcreTestSig01B2g, 1);
+ UtRegisterTest("DetectPcreTestSig01B3g -- pcre test", DetectPcreTestSig01B3g, 1);
+ UtRegisterTest("DetectPcreTestSig01Wm -- pcre test", DetectPcreTestSig01Wm, 1);
+ UtRegisterTest("DetectPcreTestSig02B2g -- pcre test", DetectPcreTestSig02B2g, 1);
+ UtRegisterTest("DetectPcreTestSig02B3g -- pcre test", DetectPcreTestSig02B3g, 1);
+ UtRegisterTest("DetectPcreTestSig02Wm -- pcre test", DetectPcreTestSig02Wm, 1);
+ UtRegisterTest("DetectPcreTestSig03B2g -- negated pcre test", DetectPcreTestSig03B2g, 1);
+ UtRegisterTest("DetectPcreTestSig03B3g -- negated pcre test", DetectPcreTestSig03B3g, 1);
+ UtRegisterTest("DetectPcreTestSig03Wm -- negated pcre test", DetectPcreTestSig03Wm, 1);
+
+ UtRegisterTest("DetectPcreModifPTest04 -- Modifier P", DetectPcreModifPTest04, 1);
+ UtRegisterTest("DetectPcreModifPTest05 -- Modifier P fragmented", DetectPcreModifPTest05, 1);
+ UtRegisterTest("DetectPcreTestSig06", DetectPcreTestSig06, 1);
+ UtRegisterTest("DetectPcreTestSig07 -- anchored pcre", DetectPcreTestSig07, 1);
+ UtRegisterTest("DetectPcreTestSig08 -- anchored pcre", DetectPcreTestSig08, 1);
+ UtRegisterTest("DetectPcreTestSig09 -- Cookie modifier", DetectPcreTestSig09, 1);
+ UtRegisterTest("DetectPcreTestSig10 -- negated Cookie modifier", DetectPcreTestSig10, 1);
+ UtRegisterTest("DetectPcreTestSig11 -- Method modifier", DetectPcreTestSig11, 1);
+ UtRegisterTest("DetectPcreTestSig12 -- negated Method modifier", DetectPcreTestSig12, 1);
+ UtRegisterTest("DetectPcreTestSig13 -- Header modifier", DetectPcreTestSig13, 1);
+ UtRegisterTest("DetectPcreTestSig14 -- negated Header modifier", DetectPcreTestSig14, 1);
+ UtRegisterTest("DetectPcreTestSig15 -- relative Cookie modifier", DetectPcreTestSig15, 1);
+ UtRegisterTest("DetectPcreTestSig16 -- relative Method modifier", DetectPcreTestSig16, 1);
+
+ UtRegisterTest("DetectPcreTxBodyChunksTest01", DetectPcreTxBodyChunksTest01, 1);
+ UtRegisterTest("DetectPcreTxBodyChunksTest02 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest02, 1);
+ UtRegisterTest("DetectPcreTxBodyChunksTest03 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest03, 1);
+
+ UtRegisterTest("DetectPcreFlowvarCapture01 -- capture for http_header", DetectPcreFlowvarCapture01, 1);
+ UtRegisterTest("DetectPcreFlowvarCapture02 -- capture for http_header", DetectPcreFlowvarCapture02, 1);
+ UtRegisterTest("DetectPcreFlowvarCapture03 -- capture for http_header", DetectPcreFlowvarCapture03, 1);
+
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-pcre.h b/framework/src/suricata/src/detect-pcre.h
new file mode 100644
index 00000000..e0098cb1
--- /dev/null
+++ b/framework/src/suricata/src/detect-pcre.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PCRE_H__
+#define __DETECT_PCRE_H__
+
+#define DETECT_PCRE_RELATIVE 0x00001
+#define DETECT_PCRE_RAWBYTES 0x00002
+#define DETECT_PCRE_CASELESS 0x00004
+#define DETECT_PCRE_CAPTURE_PKT 0x00008
+#define DETECT_PCRE_CAPTURE_FLOW 0x00010
+#define DETECT_PCRE_MATCH_LIMIT 0x00020
+#define DETECT_PCRE_RELATIVE_NEXT 0x00040
+#define DETECT_PCRE_NEGATE 0x00080
+
+typedef struct DetectPcreData_ {
+ /* pcre options */
+ pcre *re;
+ pcre_extra *sd;
+ int opts;
+ uint16_t flags;
+ uint16_t capidx;
+ char *capname;
+} DetectPcreData;
+
+/* prototypes */
+int DetectPcrePayloadMatch(DetectEngineThreadCtx *, Signature *, SigMatch *, Packet *, Flow *, uint8_t *, uint32_t);
+int DetectPcrePacketPayloadMatch(DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *);
+int DetectPcrePayloadDoMatch(DetectEngineThreadCtx *, Signature *, SigMatch *,
+ Packet *, uint8_t *, uint16_t);
+void DetectPcreRegister (void);
+
+#endif /* __DETECT_PCRE_H__ */
+
diff --git a/framework/src/suricata/src/detect-pkt-data.c b/framework/src/suricata/src/detect-pkt-data.c
new file mode 100644
index 00000000..df65444a
--- /dev/null
+++ b/framework/src/suricata/src/detect-pkt-data.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Xavier Lange <xrlange@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+static int DetectPktDataSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectPktDataTestRegister(void);
+
+/**
+ * \brief Registration function for keyword: file_data
+ */
+void DetectPktDataRegister(void)
+{
+ sigmatch_table[DETECT_PKT_DATA].name = "pkt_data";
+ sigmatch_table[DETECT_PKT_DATA].Match = NULL;
+ sigmatch_table[DETECT_PKT_DATA].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_PKT_DATA].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_PKT_DATA].Setup = DetectPktDataSetup;
+ sigmatch_table[DETECT_PKT_DATA].Free = NULL;
+ sigmatch_table[DETECT_PKT_DATA].RegisterTests = DetectPktDataTestRegister;
+ sigmatch_table[DETECT_PKT_DATA].flags = SIGMATCH_NOOPT;
+}
+
+/**
+ * \brief this function is used to parse pkt_data options
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param str pointer to the user provided "filestore" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectPktDataSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SCEnter();
+ s->list = DETECT_SM_LIST_NOTSET;
+
+ return 0;
+}
+
+#ifdef UNITTESTS
+
+/************************************Unittests*********************************/
+
+static int DetectPktDataTest01(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ SigMatch *sm = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(file_data; content:\"in file data\";"
+ " pkt_data; content:\"in pkt data\";)");
+ de_ctx->sig_list = sig;
+ if (de_ctx->sig_list == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE,"could not load test signature");
+ goto end;
+ }
+
+ /* sm should be in the MATCH list */
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+ if (sm == NULL) {
+ printf("sm not in DETECT_SM_LIST_FILEDATA: ");
+ goto end;
+ }
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm == NULL) {
+ printf("sm not in DETECT_SM_LIST_PMATCH: ");
+ goto end;
+ }
+
+ if (sm->type != DETECT_CONTENT) {
+ printf("sm type not DETECT_AL_HTTP_SERVER_BODY: ");
+ goto end;
+ }
+
+ if (sm->next != NULL) {
+ goto end;
+ }
+
+
+ if (sig->list != DETECT_SM_LIST_NOTSET) {
+ printf("sticky buffer set: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+#endif
+
+static void DetectPktDataTestRegister(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectPktDataTest01", DetectPktDataTest01, 1);
+#endif
+}
+
diff --git a/framework/src/suricata/src/detect-pkt-data.h b/framework/src/suricata/src/detect-pkt-data.h
new file mode 100644
index 00000000..fbaf8b98
--- /dev/null
+++ b/framework/src/suricata/src/detect-pkt-data.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PKTDATA_H__
+#define __DETECT_PKTDATA_H__
+
+/* prototypes */
+void DetectPktDataRegister (void);
+
+#endif /* __DETECT_PKTDATA_H__ */
diff --git a/framework/src/suricata/src/detect-pktvar.c b/framework/src/suricata/src/detect-pktvar.c
new file mode 100644
index 00000000..c841f69d
--- /dev/null
+++ b/framework/src/suricata/src/detect-pktvar.c
@@ -0,0 +1,259 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the pktvar keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "threads.h"
+#include "pkt-var.h"
+#include "detect-pktvar.h"
+#include "util-spm.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "(.*),(.*)"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectPktvarMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectPktvarSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectPktvarRegister (void)
+{
+ sigmatch_table[DETECT_PKTVAR].name = "pktvar";
+ sigmatch_table[DETECT_PKTVAR].Match = DetectPktvarMatch;
+ sigmatch_table[DETECT_PKTVAR].Setup = DetectPktvarSetup;
+ sigmatch_table[DETECT_PKTVAR].Free = NULL;
+ sigmatch_table[DETECT_PKTVAR].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_PKTVAR].flags |= SIGMATCH_PAYLOAD;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+int DetectPktvarMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ const DetectPktvarData *pd = (const DetectPktvarData *)ctx;
+
+ PktVar *pv = PktVarGet(p, pd->name);
+ if (pv != NULL) {
+ uint8_t *ptr = SpmSearch(pv->value, pv->value_len, pd->content, pd->content_len);
+ if (ptr != NULL)
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int DetectPktvarSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectPktvarData *cd = NULL;
+ SigMatch *sm = NULL;
+ char *str = rawstr;
+ char dubbed = 0;
+ uint16_t len;
+ char *varname = NULL, *varcontent = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for pktvar.", rawstr);
+ return -1;
+
+ }
+
+ const char *str_ptr;
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return -1;
+ }
+ varname = (char *)str_ptr;
+
+ if (ret > 2) {
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ return -1;
+ }
+ varcontent = (char *)str_ptr;
+ }
+
+ SCLogDebug("varname %s, varcontent %s", varname, varcontent);
+
+ if (varcontent[0] == '\"' && varcontent[strlen(varcontent)-1] == '\"') {
+ str = SCStrdup(varcontent+1);
+ if (unlikely(str == NULL)) {
+ return -1;
+ }
+ str[strlen(varcontent)-2] = '\0';
+ dubbed = 1;
+ }
+
+ len = strlen(str);
+ if (len == 0) {
+ if (dubbed) SCFree(str);
+ return -1;
+ }
+
+ cd = SCMalloc(sizeof(DetectPktvarData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ char converted = 0;
+
+ {
+ uint16_t i, x;
+ uint8_t bin = 0, binstr[3] = "", binpos = 0;
+ for (i = 0, x = 0; i < len; i++) {
+ // printf("str[%02u]: %c\n", i, str[i]);
+ if (str[i] == '|') {
+ if (bin) {
+ bin = 0;
+ } else {
+ bin = 1;
+ }
+ } else {
+ if (bin) {
+ if (isdigit((unsigned char)str[i]) ||
+ str[i] == 'A' || str[i] == 'a' ||
+ str[i] == 'B' || str[i] == 'b' ||
+ str[i] == 'C' || str[i] == 'c' ||
+ str[i] == 'D' || str[i] == 'd' ||
+ str[i] == 'E' || str[i] == 'e' ||
+ str[i] == 'F' || str[i] == 'f') {
+ // printf("part of binary: %c\n", str[i]);
+
+ binstr[binpos] = (char)str[i];
+ binpos++;
+
+ if (binpos == 2) {
+ uint8_t c = strtol((char *)binstr, (char **) NULL, 16) & 0xFF;
+ binpos = 0;
+ str[x] = c;
+ x++;
+ converted = 1;
+ }
+ } else if (str[i] == ' ') {
+ // printf("space as part of binary string\n");
+ }
+ } else {
+ str[x] = str[i];
+ x++;
+ }
+ }
+ }
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ for (i = 0; i < x; i++) {
+ if (isprint((unsigned char)str[i])) printf("%c", str[i]);
+ else printf("\\x%02u", str[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (converted)
+ len = x;
+ }
+
+ cd->content = SCMalloc(len);
+ if (cd->content == NULL) {
+ SCFree(cd);
+ if (dubbed) SCFree(str);
+ return -1;
+ }
+
+ cd->name = SCStrdup(varname);
+ if (cd->name == NULL) {
+ SCFree(cd);
+ if (dubbed) SCFree(str);
+ return -1;
+ }
+
+ memcpy(cd->content, str, len);
+ cd->content_len = len;
+ cd->flags = 0;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_PKTVAR;
+ sm->ctx = (SigMatchCtx *)cd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ if (dubbed) SCFree(str);
+ return 0;
+
+error:
+ if (dubbed)
+ SCFree(str);
+ if (cd) {
+ if (cd->name)
+ SCFree(cd->name);
+ SCFree(cd);
+ }
+ if (sm)
+ SCFree(sm);
+ return -1;
+}
+
+
diff --git a/framework/src/suricata/src/detect-pktvar.h b/framework/src/suricata/src/detect-pktvar.h
new file mode 100644
index 00000000..e5d1d3a6
--- /dev/null
+++ b/framework/src/suricata/src/detect-pktvar.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_PKTVAR_H__
+#define __DETECT_PKTVAR_H__
+
+typedef struct DetectPktvarData_ {
+ char *name;
+ uint8_t *content;
+ uint8_t content_len;
+ uint8_t flags;
+} DetectPktvarData;
+
+/* prototypes */
+void DetectPktvarRegister (void);
+
+#endif /* __DETECT_PKTVAR_H__ */
+
diff --git a/framework/src/suricata/src/detect-priority.c b/framework/src/suricata/src/detect-priority.c
new file mode 100644
index 00000000..5bb0359f
--- /dev/null
+++ b/framework/src/suricata/src/detect-priority.c
@@ -0,0 +1,221 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the priority keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#define DETECT_PRIORITY_REGEX "^\\s*(\\d+|\"\\d+\")\\s*$"
+
+static pcre *regex = NULL;
+static pcre_extra *regex_study = NULL;
+
+static int DetectPrioritySetup (DetectEngineCtx *, Signature *, char *);
+void SCPriorityRegisterTests(void);
+
+/**
+ * \brief Registers the handler functions for the "priority" keyword
+ */
+void DetectPriorityRegister (void)
+{
+ const char *eb = NULL;
+ int eo;
+ int opts = 0;
+
+ sigmatch_table[DETECT_PRIORITY].name = "priority";
+ sigmatch_table[DETECT_PRIORITY].desc = "rules with a higher priority will be examined first";
+ sigmatch_table[DETECT_PRIORITY].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Priority";
+ sigmatch_table[DETECT_PRIORITY].Match = NULL;
+ sigmatch_table[DETECT_PRIORITY].Setup = DetectPrioritySetup;
+ sigmatch_table[DETECT_PRIORITY].Free = NULL;
+ sigmatch_table[DETECT_PRIORITY].RegisterTests = SCPriorityRegisterTests;
+
+ regex = pcre_compile(DETECT_PRIORITY_REGEX, opts, &eb, &eo, NULL);
+ if (regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_PRIORITY_REGEX, eo, eb);
+ goto end;
+ }
+
+ regex_study = pcre_study(regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto end;
+ }
+
+ end:
+ return;
+}
+
+static int DetectPrioritySetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ char copy_str[128] = "";
+
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Invalid Priority in Signature "
+ "- %s", rawstr);
+ return -1;
+ }
+
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, copy_str, sizeof(copy_str));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return -1;
+ }
+
+ long prio = 0;
+ char *endptr = NULL;
+ prio = strtol(copy_str, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0') {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Saw an invalid character as arg "
+ "to priority keyword");
+ return -1;
+ }
+ /* if we have reached here, we have had a valid priority. Assign it */
+ s->prio = prio;
+
+ return 0;
+}
+
+/*------------------------------Unittests-------------------------------------*/
+
+#ifdef UNITTESTS
+
+int DetectPriorityTest01()
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:2; sid:1;)");
+ if (de_ctx->sig_list != NULL)
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+int DetectPriorityTest02()
+{
+ int result = 0;
+ Signature *last = NULL;
+ Signature *sig = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:1; sid:1;)");
+ de_ctx->sig_list = last = sig;
+ if (sig == NULL) {
+ result = 0;
+ } else {
+ result = 1;
+ result &= (sig->prio == 1);
+ }
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:boo; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:10boo; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:b10oo; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:boo10; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; priority:-1; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ result &= (sig == NULL);
+
+ sig = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Priority test\"; sid:1;)");
+ if (last != NULL)
+ last->next = sig;
+ if (sig == NULL) {
+ result &= 0;
+ } else {
+ result &= (sig->prio == 3);
+ }
+
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for Classification Config API.
+ */
+void SCPriorityRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("DetectPriorityTest01", DetectPriorityTest01, 1);
+ UtRegisterTest("DetectPriorityTest02", DetectPriorityTest02, 1);
+
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/detect-priority.h b/framework/src/suricata/src/detect-priority.h
new file mode 100644
index 00000000..0ec877e5
--- /dev/null
+++ b/framework/src/suricata/src/detect-priority.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the priority keyword
+ */
+
+#ifndef __DETECT_PRIORITY_H__
+#define __DETECT_PRIORITY_H__
+
+/* prototypes */
+void DetectPriorityRegister (void);
+
+#endif /* __DETECT_PRIORITY_H__ */
+
diff --git a/framework/src/suricata/src/detect-rawbytes.c b/framework/src/suricata/src/detect-rawbytes.c
new file mode 100644
index 00000000..872a71a0
--- /dev/null
+++ b/framework/src/suricata/src/detect-rawbytes.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements rawbytes keyword support
+ *
+ * \todo Provide un-normalized telnet dce/rpc buffers to match on
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "flow-var.h"
+
+#include "detect-content.h"
+#include "detect-pcre.h"
+
+#include "util-debug.h"
+
+static int DetectRawbytesSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectRawbytesRegister (void)
+{
+ sigmatch_table[DETECT_RAWBYTES].name = "rawbytes";
+ sigmatch_table[DETECT_RAWBYTES].Match = NULL;
+ sigmatch_table[DETECT_RAWBYTES].Setup = DetectRawbytesSetup;
+ sigmatch_table[DETECT_RAWBYTES].Free = NULL;
+ sigmatch_table[DETECT_RAWBYTES].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_RAWBYTES].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_RAWBYTES].flags |= SIGMATCH_PAYLOAD;
+}
+
+static int DetectRawbytesSetup (DetectEngineCtx *de_ctx, Signature *s, char *nullstr)
+{
+ SCEnter();
+
+ if (nullstr != NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "rawbytes has no value");
+ return -1;
+ }
+
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ SCLogError(SC_ERR_RAWBYTES_FILE_DATA, "\"rawbytes\" cannot be combined with \"file_data\"");
+ SCReturnInt(-1);
+ }
+
+ SigMatch *pm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (pm == NULL) {
+ SCLogError(SC_ERR_RAWBYTES_MISSING_CONTENT, "\"rawbytes\" needs a preceding content option");
+ SCReturnInt(-1);
+ }
+
+ switch (pm->type) {
+ case DETECT_CONTENT:
+ {
+ DetectContentData *cd = (DetectContentData *)pm->ctx;
+ if (cd->flags & DETECT_CONTENT_RAWBYTES) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple rawbytes modifiers for the same content. ");
+ SCReturnInt(-1);
+ }
+ cd->flags |= DETECT_CONTENT_RAWBYTES;
+ break;
+ }
+ default:
+ SCLogError(SC_ERR_RAWBYTES_MISSING_CONTENT, "\"rawbytes\" needs a preceding content option");
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
diff --git a/framework/src/suricata/src/detect-rawbytes.h b/framework/src/suricata/src/detect-rawbytes.h
new file mode 100644
index 00000000..8716e56a
--- /dev/null
+++ b/framework/src/suricata/src/detect-rawbytes.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_RAWBYTES_H__
+#define __DETECT_RAWBYTES_H__
+
+/* prototypes */
+void DetectRawbytesRegister (void);
+
+#endif /* __DETECT_RAWBYTES_H__ */
+
diff --git a/framework/src/suricata/src/detect-reference.c b/framework/src/suricata/src/detect-reference.c
new file mode 100644
index 00000000..a672f5c2
--- /dev/null
+++ b/framework/src/suricata/src/detect-reference.c
@@ -0,0 +1,376 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the reference keyword support
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "decode.h"
+#include "flow-var.h"
+#include "decode-events.h"
+#include "stream-tcp.h"
+
+#include "util-reference-config.h"
+#include "detect-reference.h"
+
+#include "util-unittest.h"
+#include "util-byte.h"
+#include "util-debug.h"
+
+#define PARSE_REGEX "^\\s*([A-Za-z0-9]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z0-9\\-_\\.\\/\\?\\=]+)\"?\\s*\"?"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectReferenceSetup(DetectEngineCtx *, Signature *s, char *str);
+
+/**
+ * \brief Registration function for the reference: keyword
+ */
+void DetectReferenceRegister(void)
+{
+ sigmatch_table[DETECT_REFERENCE].name = "reference";
+ sigmatch_table[DETECT_REFERENCE].desc = "direct to places where information about the rule can be found";
+ sigmatch_table[DETECT_REFERENCE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Reference";
+ sigmatch_table[DETECT_REFERENCE].Match = NULL;
+ sigmatch_table[DETECT_REFERENCE].Setup = DetectReferenceSetup;
+ sigmatch_table[DETECT_REFERENCE].Free = NULL;
+ sigmatch_table[DETECT_REFERENCE].RegisterTests = ReferenceRegisterTests;
+
+ const char *eb;
+ int opts = 0;
+ int eo;
+
+ opts |= PCRE_CASELESS;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+}
+
+/**
+ * \brief Free a Reference object
+ */
+void DetectReferenceFree(DetectReference *ref)
+{
+ SCEnter();
+
+ if (ref->reference != NULL) {
+ SCFree(ref->reference);
+ }
+ SCFree(ref);
+
+ SCReturn;
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse reference options passed via reference: keyword
+ *
+ * \param rawstr Pointer to the user provided reference options.
+ *
+ * \retval ref Pointer to signature reference on success.
+ * \retval NULL On failure.
+ */
+static DetectReference *DetectReferenceParse(char *rawstr, DetectEngineCtx *de_ctx)
+{
+ SCEnter();
+
+ DetectReference *ref = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char key[64] = "";
+ char content[1024] = "";
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 2) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Unable to parse \"reference\" "
+ "keyword argument - \"%s\". Invalid argument.", rawstr);
+ goto error;
+ }
+
+ ref = SCMalloc(sizeof(DetectReference));
+ if (unlikely(ref == NULL)) {
+ goto error;
+ }
+ memset(ref, 0, sizeof(DetectReference));
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, key, sizeof(key));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, content, sizeof(content));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (strlen(key) == 0 || strlen(content) == 0)
+ goto error;
+
+ SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx);
+ if (lookup_ref_conf != NULL) {
+ ref->key = lookup_ref_conf->url;
+ } else {
+ SCLogError(SC_ERR_REFERENCE_UNKNOWN, "unknown reference key \"%s\". "
+ "Supported keys are defined in reference.config file. Please "
+ "have a look at the conf param \"reference-config-file\"", key);
+ goto error;
+ }
+
+ /* make a copy so we can free pcre's substring */
+ ref->reference = SCStrdup((char *)content);
+ if (ref->reference == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "strdup failed: %s", strerror(errno));
+ goto error;
+ }
+
+ /* free the substrings */
+ SCReturnPtr(ref, "Reference");
+
+error:
+ if (ref != NULL)
+ DetectReferenceFree(ref);
+
+ SCReturnPtr(NULL, "Reference");
+}
+
+/**
+ * \internal
+ * \brief Used to add the parsed reference into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param m Pointer to the Current SigMatch.
+ * \param rawstr Pointer to the user provided reference options.
+ *
+ * \retval 0 On Success.
+ * \retval -1 On Failure.
+ */
+static int DetectReferenceSetup(DetectEngineCtx *de_ctx, Signature *s,
+ char *rawstr)
+{
+ SCEnter();
+
+ DetectReference *ref = NULL;
+ DetectReference *sig_refs = NULL;
+
+ ref = DetectReferenceParse(rawstr, de_ctx);
+ if (ref == NULL)
+ goto error;
+
+ SCLogDebug("ref %s %s", ref->key, ref->reference);
+
+ if (s->references == NULL) {
+ s->references = ref;
+ } else {
+ sig_refs = s->references;
+ while (sig_refs->next != NULL) {
+ sig_refs = sig_refs->next;
+ }
+ sig_refs->next = ref;
+ ref->next = NULL;
+ }
+
+ SCReturnInt(0);
+
+error:
+ SCReturnInt(-1);
+}
+
+/***************************************Unittests******************************/
+
+#ifdef UNITTESTS
+
+/**
+ * \test one valid reference.
+ *
+ * \retval 1 on succces.
+ * \retval 0 on failure.
+ */
+static int DetectReferenceParseTest01(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectReference *ref = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto cleanup;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCRConfGenerateValidDummyReferenceConfigFD01();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(msg:\"One reference\"; reference:one,001-2010; sid:2;)");
+ if (s == NULL) {
+ goto cleanup;
+ }
+
+ if (s->references == NULL) {
+ goto cleanup;
+ }
+
+ ref = s->references;
+ if (strcmp(ref->key, "http://www.one.com") != 0 ||
+ strcmp(ref->reference, "001-2010") != 0) {
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+
+}
+
+/**
+ * \test for two valid references.
+ *
+ * \retval 1 on succces.
+ * \retval 0 on failure.
+ */
+static int DetectReferenceParseTest02(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto cleanup;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCRConfGenerateValidDummyReferenceConfigFD01();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(msg:\"Two references\"; "
+ "reference:one,openinfosecdoundation.txt; "
+ "reference:two,001-2010; sid:2;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto cleanup;
+ }
+
+ if (s->references == NULL || s->references->next == NULL) {
+ printf("no ref or not enough refs: ");
+ goto cleanup;
+ }
+
+ if (strcmp(s->references->key, "http://www.one.com") != 0 ||
+ strcmp(s->references->reference, "openinfosecdoundation.txt") != 0) {
+ printf("first ref failed: ");
+ goto cleanup;
+ }
+
+ if (strcmp(s->references->next->key, "http://www.two.com") != 0 ||
+ strcmp(s->references->next->reference, "001-2010") != 0) {
+ printf("second ref failed: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+
+/**
+ * \test parsing: invalid reference.
+ *
+ * \retval 1 on succces.
+ * \retval 0 on failure.
+ */
+static int DetectReferenceParseTest03(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto cleanup;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd =SCRConfGenerateValidDummyReferenceConfigFD01();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any "
+ "(msg:\"invalid ref\"; "
+ "reference:unknownkey,001-2010; sid:2;)");
+ if (s != NULL) {
+ printf("sig parsed even though it's invalid: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void ReferenceRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectReferenceParseTest01", DetectReferenceParseTest01, 1);
+ UtRegisterTest("DetectReferenceParseTest02", DetectReferenceParseTest02, 1);
+ UtRegisterTest("DetectReferenceParseTest03", DetectReferenceParseTest03, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-reference.h b/framework/src/suricata/src/detect-reference.h
new file mode 100644
index 00000000..6c513fe1
--- /dev/null
+++ b/framework/src/suricata/src/detect-reference.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_REFERENCE_H__
+#define __DETECT_REFERENCE_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+/**
+ * \brief Signature reference list.
+ */
+typedef struct DetectReference_ {
+ /* pointer to key */
+ char *key;
+ /* reference data */
+ char *reference;
+ /* next reference in the signature */
+ struct DetectReference_ *next;
+} DetectReference;
+
+/**
+ * Registration function for Reference keyword
+ */
+void DetectReferenceRegister(void);
+
+/**
+ * This function registers unit tests for Reference keyword.
+ */
+void ReferenceRegisterTests(void);
+
+/**
+ * Free function for a Reference object
+ */
+void DetectReferenceFree(DetectReference *);
+
+#endif /*__DETECT_REFERENCE_H__ */
diff --git a/framework/src/suricata/src/detect-replace.c b/framework/src/suricata/src/detect-replace.c
new file mode 100644
index 00000000..57e06e7a
--- /dev/null
+++ b/framework/src/suricata/src/detect-replace.c
@@ -0,0 +1,845 @@
+/* Copyright (C) 2011-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Replace part of the detection engine.
+ *
+ * If previous filter is of content type, replace can be used to change
+ * the matched part to a new value.
+ */
+
+#include "suricata-common.h"
+
+#include "runmodes.h"
+
+extern int run_mode;
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-byte-extract.h"
+#include "detect-replace.h"
+#include "app-layer.h"
+
+#include "detect-engine-mpm.h"
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+
+#include "util-checksum.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "flow-var.h"
+
+#include "util-debug.h"
+
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+
+static int DetectReplaceSetup(DetectEngineCtx *, Signature *, char *);
+void DetectReplaceRegisterTests(void);
+
+void DetectReplaceRegister (void)
+{
+ sigmatch_table[DETECT_REPLACE].name = "replace";
+ sigmatch_table[DETECT_REPLACE].Match = NULL;
+ sigmatch_table[DETECT_REPLACE].Setup = DetectReplaceSetup;
+ sigmatch_table[DETECT_REPLACE].Free = NULL;
+ sigmatch_table[DETECT_REPLACE].RegisterTests = DetectReplaceRegisterTests;
+
+ sigmatch_table[DETECT_REPLACE].flags |= SIGMATCH_PAYLOAD;
+}
+
+int DetectReplaceSetup(DetectEngineCtx *de_ctx, Signature *s, char *replacestr)
+{
+ uint8_t *content = NULL;
+ uint16_t len = 0;
+ uint32_t flags = 0;
+ SigMatch *pm = NULL;
+ DetectContentData *ud = NULL;
+
+ int ret = DetectContentDataParse("replace", replacestr, &content, &len, &flags);
+ if (ret == -1)
+ goto error;
+
+ if (flags & DETECT_CONTENT_NEGATED) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Can't negate replacement string: %s",
+ replacestr);
+ goto error;
+ }
+
+ switch (run_mode) {
+ case RUNMODE_NFQ:
+ case RUNMODE_IPFW:
+ break;
+ default:
+ SCLogWarning(SC_ERR_RUNMODE,
+ "Can't use 'replace' keyword in non IPS mode: %s",
+ s->sig_str);
+ /* this is a success, having the alert is interesting */
+ return 0;
+ }
+
+ /* add to the latest "content" keyword from either dmatch or pmatch */
+ pm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH]);
+ if (pm == NULL) {
+ SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "replace needs"
+ "preceding content option for raw sig");
+ SCFree(content);
+ return -1;
+ }
+
+ /* we can remove this switch now with the unified structure */
+ ud = (DetectContentData *)pm->ctx;
+ if (ud == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument");
+ SCFree(content);
+ return -1;
+ }
+ if (ud->flags & DETECT_CONTENT_NEGATED) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "negated keyword set along with a replacement");
+ goto error;
+ }
+ if (ud->content_len != len) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a content "
+ "length different from replace length");
+ goto error;
+ }
+
+ ud->replace = SCMalloc(len);
+ if (ud->replace == NULL) {
+ goto error;
+ }
+ memcpy(ud->replace, content, len);
+ ud->replace_len = len;
+ ud->flags |= DETECT_CONTENT_REPLACE;
+ /* want packet matching only won't be able to replace data with
+ * a flow.
+ */
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+ SCFree(content);
+
+ return 0;
+
+error:
+ SCFree(content);
+ return -1;
+}
+
+/* Add to the head of the replace-list.
+ *
+ * The first to add to the replace-list has the highest priority. So,
+ * adding the the head of the list results in the newest modifications
+ * of content being applied first, so later changes can over ride
+ * earlier changes. Thus the highest priority modifications should be
+ * applied last.
+ */
+DetectReplaceList *DetectReplaceAddToList(DetectReplaceList *replist,
+ uint8_t *found,
+ DetectContentData *cd)
+{
+ DetectReplaceList *newlist;
+
+ if (cd->content_len != cd->replace_len)
+ return NULL;
+ SCLogDebug("replace: Adding match");
+
+ newlist = SCMalloc(sizeof(DetectReplaceList));
+ if (unlikely(newlist == NULL))
+ return replist;
+ newlist->found = found;
+ newlist->cd = cd;
+ /* Push new value onto the front of the list. */
+ newlist->next = replist;
+
+ return newlist;
+}
+
+
+void DetectReplaceExecuteInternal(Packet *p, DetectReplaceList *replist)
+{
+ DetectReplaceList *tlist = NULL;
+
+ SCLogDebug("replace: Executing match");
+ while (replist) {
+ memcpy(replist->found, replist->cd->replace, replist->cd->replace_len);
+ SCLogDebug("replace: injecting '%s'", replist->cd->replace);
+ p->flags |= PKT_STREAM_MODIFIED;
+ ReCalculateChecksum(p);
+ tlist = replist;
+ replist = replist->next;
+ SCFree(tlist);
+ }
+}
+
+
+void DetectReplaceFreeInternal(DetectReplaceList *replist)
+{
+ DetectReplaceList *tlist = NULL;
+ while (replist) {
+ SCLogDebug("replace: Freeing match");
+ tlist = replist;
+ replist = replist->next;
+ SCFree(tlist);
+ }
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test Test packet Matches
+ * \param raw_eth_pkt pointer to the ethernet packet
+ * \param pktsize size of the packet
+ * \param sig pointer to the signature to test
+ * \param sid sid number of the signature
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+static
+int DetectReplaceLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize,
+ char *sig, uint32_t sid, uint8_t *pp,
+ uint16_t *len)
+{
+ int result = 0;
+
+ Packet *p = NULL;
+ p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ if (pp == NULL) {
+ SCLogDebug("replace: looks like a second run");
+ }
+
+ PacketCopyData(p, raw_eth_pkt, pktsize);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+ dtv.app_tctx = AppLayerGetCtxThread(&th_v);
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, GET_PKT_DATA(p), pktsize, NULL);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+ de_ctx->sig_list->next = NULL;
+
+ if (de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) {
+ DetectContentData *co = (DetectContentData *)de_ctx->sig_list->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) {
+ printf("relative next flag set on final match which is content: ");
+ goto end;
+ }
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineAddToMaster(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, NULL, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ DetectEngineMoveToFreeList(de_ctx);
+
+ if (PacketAlertCheck(p, sid) != 1) {
+ SCLogDebug("replace: no alert on sig %d", sid);
+ goto end;
+ }
+
+ if (pp) {
+ memcpy(pp, GET_PKT_DATA(p), GET_PKT_LEN(p));
+ *len = pktsize;
+ SCLogDebug("replace: copying %d on %p", *len, pp);
+ }
+
+
+ result = 1;
+end:
+ if (dtv.app_tctx != NULL)
+ AppLayerDestroyCtxThread(dtv.app_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEnginePruneFreeList();
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+ SCFree(p);
+
+
+ return result;
+}
+
+
+/**
+ * \brief Wrapper for DetectContentLongPatternMatchTest
+ */
+int DetectReplaceLongPatternMatchTestWrp(char *sig, uint32_t sid, char *sig_rep, uint32_t sid_rep)
+{
+ int ret;
+ /** Real packet with the following tcp data:
+ * "Hi, this is a big test to check content matches of splitted"
+ * "patterns between multiple chunks!"
+ * (without quotes! :) )
+ */
+ uint8_t raw_eth_pkt[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00,
+ 0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06,
+ 0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00,
+ 0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,
+ 0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66,
+ 0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65,
+ 0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+ 0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65,
+ 0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69,
+ 0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e,
+ 0x6b,0x73,0x21 }; /* end raw_eth_pkt */
+ uint8_t p[sizeof(raw_eth_pkt)];
+ uint16_t psize = sizeof(raw_eth_pkt);
+
+ /* would be unittest */
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+ ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
+ sig, sid, p, &psize);
+ if (ret == 1) {
+ SCLogDebug("replace: test1 phase1");
+ ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
+ }
+ run_mode = run_mode_backup;
+ return ret;
+}
+
+
+/**
+ * \brief Wrapper for DetectContentLongPatternMatchTest
+ */
+int DetectReplaceLongPatternMatchTestUDPWrp(char *sig, uint32_t sid, char *sig_rep, uint32_t sid_rep)
+{
+ int ret;
+ /** Real UDP DNS packet with a request A to a1.twimg.com
+ */
+ uint8_t raw_eth_pkt[] = {
+ 0x8c, 0xa9, 0x82, 0x75, 0x5d, 0x62, 0xb4, 0x07,
+ 0xf9, 0xf3, 0xc7, 0x0a, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x3a, 0x92, 0x4f, 0x40, 0x00, 0x40, 0x11,
+ 0x31, 0x1a, 0xc0, 0xa8, 0x00, 0x02, 0xc1, 0xbd,
+ 0xf4, 0xe1, 0x3b, 0x7e, 0x00, 0x35, 0x00, 0x26,
+ 0xcb, 0x81, 0x37, 0x62, 0x01, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61,
+ 0x31, 0x05, 0x74, 0x77, 0x69, 0x6d, 0x67, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 };
+
+ uint8_t p[sizeof(raw_eth_pkt)];
+ uint16_t psize = sizeof(raw_eth_pkt);
+
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+ ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
+ sig, sid, p, &psize);
+ if (ret == 1) {
+ SCLogDebug("replace: test1 phase1 ok: %" PRIuMAX" vs %d",(uintmax_t)sizeof(raw_eth_pkt),psize);
+ ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
+ }
+ run_mode = run_mode_backup;
+ return ret;
+}
+
+/**
+ * \test Check if replace is working
+ */
+static int DetectReplaceMatchTest01(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; replace:\"pig\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"this is a pig test\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with offset
+ */
+static int DetectReplaceMatchTest02(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"th\"; offset: 4; replace:\"TH\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"THis\"; offset:4; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with offset and keyword inversion
+ */
+static int DetectReplaceMatchTest03(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"th\"; replace:\"TH\"; offset: 4; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"THis\"; offset:4; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with second content
+ */
+static int DetectReplaceMatchTest04(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"th\"; replace:\"TH\"; content:\"patter\"; replace:\"matter\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"THis\"; content:\"matterns\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is not done when second content don't match
+ */
+static int DetectReplaceMatchTest05(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"th\"; replace:\"TH\"; content:\"nutella\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"TH\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is not done when second content match and not
+ * first
+ */
+static int DetectReplaceMatchTest06(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"nutella\"; replace:\"commode\"; content:\"this is\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"commode\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working when nocase used
+ */
+static int DetectReplaceMatchTest07(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"BiG\"; nocase; replace:\"pig\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"this is a pig test\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working when depth is used
+ */
+static int DetectReplaceMatchTest08(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; depth:17; replace:\"pig\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"this is a pig test\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working when depth block match used
+ */
+static int DetectReplaceMatchTest09(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; depth:16; replace:\"pig\"; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"this is a pig test\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working when depth block match used
+ */
+static int DetectReplaceMatchTest10(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; depth:17; replace:\"pig\"; offset: 14; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"pig\"; depth:17; offset:14; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with within
+ */
+static int DetectReplaceMatchTest11(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 11; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"pig\"; depth:17; offset:14; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with within
+ */
+static int DetectReplaceMatchTest12(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 4; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"pig\"; depth:17; offset:14; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with within
+ */
+static int DetectReplaceMatchTest13(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 1; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"pig\"; depth:17; offset:14; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with within
+ */
+static int DetectReplaceMatchTest14(void)
+{
+ char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 2; sid:1;)";
+ char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
+ " content:\"pig\"; depth:17; offset:14; sid:2;)";
+ return DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2);
+}
+
+/**
+ * \test Check if replace is working with within
+ */
+static int DetectReplaceMatchTest15(void)
+{
+ char *sig = "alert udp any any -> any any (msg:\"Nothing..\";"
+ " content:\"com\"; replace:\"org\"; sid:1;)";
+ char *sig_rep = "alert udp any any -> any any (msg:\"replace worked\";"
+ " content:\"twimg|03|org\"; sid:2;)";
+ return DetectReplaceLongPatternMatchTestUDPWrp(sig, 1, sig_rep, 2);
+}
+
+
+/**
+ * \test Parsing test
+ */
+static int DetectReplaceParseTest01(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"doh\"; replace:\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: non valid because of http protocol
+ */
+static int DetectReplaceParseTest02(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert http any any -> any any "
+ "(msg:\"test\"; content:\"doh\"; replace:\"bon\"; sid:238012;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test: non valid because of http_header on same content
+ * as replace keyword
+ */
+static int DetectReplaceParseTest03(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; content:\"doh\"; replace:\"don\"; http_header; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test no content
+ */
+static int DetectReplaceParseTest04(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; replace:\"don\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test content after replace
+ */
+static int DetectReplaceParseTest05(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; replace:\"don\"; content:\"doh\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test content and replace length differ
+ */
+static int DetectReplaceParseTest06(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; content:\"don\"; replace:\"donut\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test content and replace length differ
+ */
+static int DetectReplaceParseTest07(void)
+{
+ int run_mode_backup = run_mode;
+ run_mode = RUNMODE_NFQ;
+
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; content:\"don\"; replace:\"dou\"; content:\"jpg\"; http_header; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ run_mode = run_mode_backup;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectContent
+ */
+void DetectReplaceRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+/* matching */
+ UtRegisterTest("DetectReplaceMatchTest01", DetectReplaceMatchTest01, 1);
+ UtRegisterTest("DetectReplaceMatchTest02", DetectReplaceMatchTest02, 1);
+ UtRegisterTest("DetectReplaceMatchTest03", DetectReplaceMatchTest03, 1);
+ UtRegisterTest("DetectReplaceMatchTest04", DetectReplaceMatchTest04, 1);
+ UtRegisterTest("DetectReplaceMatchTest05", DetectReplaceMatchTest05, 0);
+ UtRegisterTest("DetectReplaceMatchTest06", DetectReplaceMatchTest06, 0);
+ UtRegisterTest("DetectReplaceMatchTest07", DetectReplaceMatchTest07, 1);
+ UtRegisterTest("DetectReplaceMatchTest08", DetectReplaceMatchTest08, 1);
+ UtRegisterTest("DetectReplaceMatchTest09", DetectReplaceMatchTest09, 0);
+ UtRegisterTest("DetectReplaceMatchTest10", DetectReplaceMatchTest10, 1);
+ UtRegisterTest("DetectReplaceMatchTest11", DetectReplaceMatchTest11, 1);
+ UtRegisterTest("DetectReplaceMatchTest12", DetectReplaceMatchTest12, 0);
+ UtRegisterTest("DetectReplaceMatchTest13", DetectReplaceMatchTest13, 1);
+ UtRegisterTest("DetectReplaceMatchTest14", DetectReplaceMatchTest14, 0);
+ UtRegisterTest("DetectReplaceMatchTest15", DetectReplaceMatchTest15, 1);
+/* parsing */
+ UtRegisterTest("DetectReplaceParseTest01", DetectReplaceParseTest01, 1);
+ UtRegisterTest("DetectReplaceParseTest02", DetectReplaceParseTest02, 1);
+ UtRegisterTest("DetectReplaceParseTest03", DetectReplaceParseTest03, 1);
+ UtRegisterTest("DetectReplaceParseTest04", DetectReplaceParseTest04, 1);
+ UtRegisterTest("DetectReplaceParseTest05", DetectReplaceParseTest05, 1);
+ UtRegisterTest("DetectReplaceParseTest06", DetectReplaceParseTest06, 1);
+ UtRegisterTest("DetectReplaceParseTest07", DetectReplaceParseTest07, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-replace.h b/framework/src/suricata/src/detect-replace.h
new file mode 100644
index 00000000..020f73c4
--- /dev/null
+++ b/framework/src/suricata/src/detect-replace.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2011-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __DETECT_REPLACE_H__
+#define __DETECT_REPLACE_H__
+
+DetectReplaceList * DetectReplaceAddToList(DetectReplaceList *replist, uint8_t *found, DetectContentData *cd);
+
+/* Internal functions are only called via the inline functions below. */
+void DetectReplaceExecuteInternal(Packet *p, DetectReplaceList *replist);
+void DetectReplaceFreeInternal(DetectReplaceList *replist);
+
+static inline void DetectReplaceExecute(Packet *p, DetectEngineThreadCtx *det_ctx)
+{
+ if (p == NULL || det_ctx->replist == NULL)
+ return;
+ DetectReplaceExecuteInternal(p, det_ctx->replist);
+ det_ctx->replist = NULL;
+}
+
+static inline void DetectReplaceFree(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx->replist) {
+ DetectReplaceFreeInternal(det_ctx->replist);
+ det_ctx->replist = NULL;
+ }
+}
+
+void DetectReplaceRegister (void);
+
+#endif
diff --git a/framework/src/suricata/src/detect-rev.c b/framework/src/suricata/src/detect-rev.c
new file mode 100644
index 00000000..6306f6cc
--- /dev/null
+++ b/framework/src/suricata/src/detect-rev.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the rev keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "util-debug.h"
+#include "util-error.h"
+
+static int DetectRevSetup (DetectEngineCtx *, Signature *, char *);
+
+void DetectRevRegister (void)
+{
+ sigmatch_table[DETECT_REV].name = "rev";
+ sigmatch_table[DETECT_REV].desc = "set version of the rule";
+ sigmatch_table[DETECT_REV].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Rev-Revision";
+ sigmatch_table[DETECT_REV].Match = NULL;
+ sigmatch_table[DETECT_REV].Setup = DetectRevSetup;
+ sigmatch_table[DETECT_REV].Free = NULL;
+ sigmatch_table[DETECT_REV].RegisterTests = NULL;
+}
+
+static int DetectRevSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ char *str = rawstr;
+ char dubbed = 0;
+
+ /* strip "'s */
+ if (rawstr[0] == '\"' && rawstr[strlen(rawstr)-1] == '\"') {
+ str = SCStrdup(rawstr+1);
+ if (unlikely(str == NULL))
+ return -1;
+
+ str[strlen(rawstr)-2] = '\0';
+ dubbed = 1;
+ }
+
+ unsigned long rev = 0;
+ char *endptr = NULL;
+ rev = strtoul(rawstr, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0') {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
+ "to rev keyword");
+ goto error;
+ }
+ if (rev >= UINT_MAX) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "rev value to high, max %u", UINT_MAX);
+ goto error;
+ }
+
+ s->rev = (uint32_t)rev;
+
+ if (dubbed)
+ SCFree(str);
+ return 0;
+
+ error:
+ if (dubbed)
+ SCFree(str);
+ return -1;
+}
+
diff --git a/framework/src/suricata/src/detect-rev.h b/framework/src/suricata/src/detect-rev.h
new file mode 100644
index 00000000..24ae202f
--- /dev/null
+++ b/framework/src/suricata/src/detect-rev.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_REV_H__
+#define __DETECT_REV_H__
+
+/* prototypes */
+void DetectRevRegister (void);
+
+#endif /* __DETECT_REV_H__ */
+
diff --git a/framework/src/suricata/src/detect-rpc.c b/framework/src/suricata/src/detect-rpc.c
new file mode 100644
index 00000000..86fc03c6
--- /dev/null
+++ b/framework/src/suricata/src/detect-rpc.c
@@ -0,0 +1,609 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements RPC keyword
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-rpc.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "util-byte.h"
+
+/**
+ * \brief Regex for parsing our rpc options
+ */
+#define PARSE_REGEX "^\\s*([0-9]{0,10})\\s*(?:,\\s*([0-9]{0,10}|[*])\\s*(?:,\\s*([0-9]{0,10}|[*]))?)?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectRpcMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+int DetectRpcSetup (DetectEngineCtx *, Signature *, char *);
+void DetectRpcRegisterTests(void);
+void DetectRpcFree(void *);
+
+/**
+ * \brief Registration function for rpc keyword
+ */
+void DetectRpcRegister (void)
+{
+ sigmatch_table[DETECT_RPC].name = "rpc";
+ sigmatch_table[DETECT_RPC].desc = "match RPC procedure numbers and RPC version";
+ sigmatch_table[DETECT_RPC].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#rpc";
+ sigmatch_table[DETECT_RPC].Match = DetectRpcMatch;
+ sigmatch_table[DETECT_RPC].Setup = DetectRpcSetup;
+ sigmatch_table[DETECT_RPC].Free = DetectRpcFree;
+ sigmatch_table[DETECT_RPC].RegisterTests = DetectRpcRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+/**
+ * \brief This function is used to match rpc request set on a packet with those passed via rpc
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectRpcData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectRpcMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ /* PrintRawDataFp(stdout, p->payload, p->payload_len); */
+ const DetectRpcData *rd = (const DetectRpcData *)ctx;
+ char *rpcmsg = (char *)p->payload;
+
+ if (PKT_IS_TCP(p)) {
+ /* if Rpc msg too small */
+ if (p->payload_len < 28) {
+ SCLogDebug("TCP packet to small for the rpc msg (%u)", p->payload_len);
+ return 0;
+ }
+ rpcmsg += 4;
+ } else if (PKT_IS_UDP(p)) {
+ /* if Rpc msg too small */
+ if (p->payload_len < 24) {
+ SCLogDebug("UDP packet to small for the rpc msg (%u)", p->payload_len);
+ return 0;
+ }
+ } else {
+ SCLogDebug("No valid proto for the rpc message");
+ return 0;
+ }
+
+ /* Point through the rpc msg structure. Use ntohl() to compare values */
+ RpcMsg *msg = (RpcMsg *)rpcmsg;
+
+ /* If its not a call, no match */
+ if (ntohl(msg->type) != 0) {
+ SCLogDebug("RPC message type is not a call");
+ return 0;
+ }
+
+ if (ntohl(msg->prog) != rd->program)
+ return 0;
+
+ if ((rd->flags & DETECT_RPC_CHECK_VERSION) && ntohl(msg->vers) != rd->program_version)
+ return 0;
+
+ if ((rd->flags & DETECT_RPC_CHECK_PROCEDURE) && ntohl(msg->proc) != rd->procedure)
+ return 0;
+
+ SCLogDebug("prog:%u pver:%u proc:%u matched", ntohl(msg->prog), ntohl(msg->vers), ntohl(msg->proc));
+ return 1;
+}
+
+/**
+ * \brief This function is used to parse rpc options passed via rpc keyword
+ *
+ * \param rpcstr Pointer to the user provided rpc options
+ *
+ * \retval rd pointer to DetectRpcData on success
+ * \retval NULL on failure
+ */
+DetectRpcData *DetectRpcParse (char *rpcstr)
+{
+ DetectRpcData *rd = NULL;
+ char *args[3] = {NULL,NULL,NULL};
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rpcstr, strlen(rpcstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, rpcstr);
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr;
+ res = pcre_get_substring((char *)rpcstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[0] = (char *)str_ptr;
+
+ if (ret > 2) {
+ res = pcre_get_substring((char *)rpcstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[1] = (char *)str_ptr;
+ }
+ if (ret > 3) {
+ res = pcre_get_substring((char *)rpcstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ args[2] = (char *)str_ptr;
+ }
+ }
+
+ rd = SCMalloc(sizeof(DetectRpcData));
+ if (unlikely(rd == NULL))
+ goto error;
+ rd->flags = 0;
+ rd->program = 0;
+ rd->program_version = 0;
+ rd->procedure = 0;
+
+ int i;
+ for (i = 0; i < (ret -1); i++) {
+ if (args[i]) {
+ switch (i) {
+ case 0:
+ if (ByteExtractStringUint32(&rd->program, 10, strlen(args[i]), args[i]) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid size specified for the rpc program:\"%s\"", args[i]);
+ goto error;
+ }
+ rd->flags |= DETECT_RPC_CHECK_PROGRAM;
+ break;
+ case 1:
+ if (args[i][0] != '*') {
+ if (ByteExtractStringUint32(&rd->program_version, 10, strlen(args[i]), args[i]) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid size specified for the rpc version:\"%s\"", args[i]);
+ goto error;
+ }
+ rd->flags |= DETECT_RPC_CHECK_VERSION;
+ }
+ break;
+ case 2:
+ if (args[i][0] != '*') {
+ if (ByteExtractStringUint32(&rd->procedure, 10, strlen(args[i]), args[i]) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid size specified for the rpc procedure:\"%s\"", args[i]);
+ goto error;
+ }
+ rd->flags |= DETECT_RPC_CHECK_PROCEDURE;
+ }
+ break;
+ }
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "invalid rpc option %s",args[i]);
+ goto error;
+ }
+ }
+ for (i = 0; i < (ret -1); i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ return rd;
+
+error:
+ for (i = 0; i < (ret -1) && i < 3; i++){
+ if (args[i] != NULL)
+ SCFree(args[i]);
+ }
+ if (rd != NULL)
+ DetectRpcFree(rd);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed rpcdata into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param m pointer to the Current SigMatch
+ * \param rpcstr pointer to the user provided rpc options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectRpcSetup (DetectEngineCtx *de_ctx, Signature *s, char *rpcstr)
+{
+ DetectRpcData *rd = NULL;
+ SigMatch *sm = NULL;
+
+ rd = DetectRpcParse(rpcstr);
+ if (rd == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_RPC;
+ sm->ctx = (SigMatchCtx *)rd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (rd != NULL) DetectRpcFree(rd);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectRpcData
+ *
+ * \param rd pointer to DetectRpcData
+ */
+void DetectRpcFree(void *ptr)
+{
+ SCEnter();
+
+ if (ptr == NULL) {
+ SCReturn;
+ }
+
+ DetectRpcData *rd = (DetectRpcData *)ptr;
+ SCFree(rd);
+
+ SCReturn;
+}
+
+#ifdef UNITTESTS
+/**
+ * \test DetectRpcTestParse01 is a test to make sure that we return "something"
+ * when given valid rpc opt
+ */
+int DetectRpcTestParse01 (void)
+{
+ int result = 0;
+ DetectRpcData *rd = NULL;
+ rd = DetectRpcParse("123,444,555");
+ if (rd != NULL) {
+ DetectRpcFree(rd);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectRpcTestParse02 is a test for setting the established rpc opt
+ */
+int DetectRpcTestParse02 (void)
+{
+ int result = 0;
+ DetectRpcData *rd = NULL;
+ rd = DetectRpcParse("111,222,333");
+ if (rd != NULL) {
+ if (rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ rd->flags & DETECT_RPC_CHECK_VERSION &&
+ rd->flags & DETECT_RPC_CHECK_PROCEDURE &&
+ rd->program == 111 && rd->program_version == 222 &&
+ rd->procedure == 333) {
+ result = 1;
+ } else {
+ SCLogDebug("Error: Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+ }
+ DetectRpcFree(rd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectRpcTestParse03 is a test for checking the wildcards
+ * and not specified fields
+ */
+int DetectRpcTestParse03 (void)
+{
+ int result = 1;
+ DetectRpcData *rd = NULL;
+ rd = DetectRpcParse("111,*,333");
+ if (rd == NULL)
+ return 0;
+
+ if ( !(rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ !(rd->flags & DETECT_RPC_CHECK_VERSION) &&
+ rd->flags & DETECT_RPC_CHECK_PROCEDURE &&
+ rd->program == 111 && rd->program_version == 0 &&
+ rd->procedure == 333))
+ result = 0;
+ SCLogDebug("rd1 Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+
+ DetectRpcFree(rd);
+
+ rd = DetectRpcParse("111,222,*");
+ if (rd == NULL)
+ return 0;
+
+ if ( !(rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ rd->flags & DETECT_RPC_CHECK_VERSION &&
+ !(rd->flags & DETECT_RPC_CHECK_PROCEDURE) &&
+ rd->program == 111 && rd->program_version == 222 &&
+ rd->procedure == 0))
+ result = 0;
+ SCLogDebug("rd2 Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+
+ DetectRpcFree(rd);
+
+ rd = DetectRpcParse("111,*,*");
+ if (rd == NULL)
+ return 0;
+
+ if ( !(rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ !(rd->flags & DETECT_RPC_CHECK_VERSION) &&
+ !(rd->flags & DETECT_RPC_CHECK_PROCEDURE) &&
+ rd->program == 111 && rd->program_version == 0 &&
+ rd->procedure == 0))
+ result = 0;
+ SCLogDebug("rd2 Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+
+ DetectRpcFree(rd);
+
+ rd = DetectRpcParse("111,222");
+ if (rd == NULL)
+ return 0;
+
+ if ( !(rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ rd->flags & DETECT_RPC_CHECK_VERSION &&
+ !(rd->flags & DETECT_RPC_CHECK_PROCEDURE) &&
+ rd->program == 111 && rd->program_version == 222 &&
+ rd->procedure == 0))
+ result = 0;
+ SCLogDebug("rd2 Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+
+ DetectRpcFree(rd);
+
+ rd = DetectRpcParse("111");
+ if (rd == NULL)
+ return 0;
+
+ if ( !(rd->flags & DETECT_RPC_CHECK_PROGRAM &&
+ !(rd->flags & DETECT_RPC_CHECK_VERSION) &&
+ !(rd->flags & DETECT_RPC_CHECK_PROCEDURE) &&
+ rd->program == 111 && rd->program_version == 0 &&
+ rd->procedure == 0))
+ result = 0;
+ SCLogDebug("rd2 Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+
+ DetectRpcFree(rd);
+ return result;
+}
+
+/**
+ * \test DetectRpcTestParse04 is a test for check the discarding of empty options
+ */
+int DetectRpcTestParse04 (void)
+{
+ int result = 0;
+ DetectRpcData *rd = NULL;
+ rd = DetectRpcParse("");
+ if (rd == NULL) {
+ result = 1;
+ } else {
+ SCLogDebug("Error: Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+ DetectRpcFree(rd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectRpcTestParse05 is a test for check invalid values
+ */
+int DetectRpcTestParse05 (void)
+{
+ int result = 0;
+ DetectRpcData *rd = NULL;
+ rd = DetectRpcParse("111,aaa,*");
+ if (rd == NULL) {
+ result = 1;
+ } else {
+ SCLogDebug("Error: Flags: %d; program: %u, version: %u, procedure: %u", rd->flags, rd->program, rd->program_version, rd->procedure);
+ DetectRpcFree(rd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectRpcTestParse05 is a test to check the match function
+ */
+static int DetectRpcTestSig01(void)
+{
+ /* RPC Call */
+ uint8_t buf[] = {
+ /* XID */
+ 0x64,0xb2,0xb3,0x75,
+ /* Message type: Call (0) */
+ 0x00,0x00,0x00,0x00,
+ /* RPC Version (2) */
+ 0x00,0x00,0x00,0x02,
+ /* Program portmap (100000) */
+ 0x00,0x01,0x86,0xa0,
+ /* Program version (2) */
+ 0x00,0x00,0x00,0x02,
+ /* Program procedure (3) = GETPORT */
+ 0x00,0x00,0x00,0x03,
+ /* AUTH_NULL */
+ 0x00,0x00,0x00,0x00,
+ /* Length 0 */
+ 0x00,0x00,0x00,0x00,
+ /* VERIFIER NULL */
+ 0x00,0x00,0x00,0x00,
+ /* Length 0 */
+ 0x00,0x00,0x00,0x00,
+ /* Program portmap */
+ 0x00,0x01,0x86,0xa2,
+ /* Version 2 */
+ 0x00,0x00,0x00,0x02,
+ /* Proto UDP */
+ 0x00,0x00,0x00,0x11,
+ /* Port 0 */
+ 0x00,0x00,0x00,0x00 };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_UDP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, 2, 3; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, 2, *; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, *, 3; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, *, *; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert udp any any -> any any (msg:\"RPC Get XXX Call.. no match\"; rpc:123456, *, 3; sid:5;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) == 0) {
+ printf("sid 1 didnt alert, but it should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ printf("sid 2 didnt alert, but it should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3) == 0) {
+ printf("sid 3 didnt alert, but it should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ printf("sid 4 didnt alert, but it should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 5) > 0) {
+ printf("sid 5 did alert, but should not: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ UTHFreePackets(&p, 1);
+end:
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectRpc
+ */
+void DetectRpcRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectRpcTestParse01", DetectRpcTestParse01, 1);
+ UtRegisterTest("DetectRpcTestParse02", DetectRpcTestParse02, 1);
+ UtRegisterTest("DetectRpcTestParse03", DetectRpcTestParse03, 1);
+ UtRegisterTest("DetectRpcTestParse04", DetectRpcTestParse04, 1);
+ UtRegisterTest("DetectRpcTestParse05", DetectRpcTestParse05, 1);
+ UtRegisterTest("DetectRpcTestSig01", DetectRpcTestSig01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-rpc.h b/framework/src/suricata/src/detect-rpc.h
new file mode 100644
index 00000000..6a5bc6c7
--- /dev/null
+++ b/framework/src/suricata/src/detect-rpc.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_RPC_H__
+#define __DETECT_RPC_H__
+
+/* At least we check the program, the version is optional,
+ * and the procedure is optional if we are checking the version.
+ * If we parse the wildcard "*" we will allow any value (no check) */
+#define DETECT_RPC_CHECK_PROGRAM 0x01
+#define DETECT_RPC_CHECK_VERSION 0x02
+#define DETECT_RPC_CHECK_PROCEDURE 0x04
+
+/** Simple struct for a rpc msg call */
+typedef struct RpcMsg_ {
+ uint32_t xid;
+ uint32_t type; /**< CALL = 0 (We only search for CALLS */
+ uint32_t rpcvers; /**< must be equal to two (2) */
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t proc;
+} RpcMsg;
+
+/* Extract uint32_t */
+#define EXT_GET_UINT32T(buf) ((long)ntohl((long)*(buf)++))
+
+typedef struct DetectRpcData_ {
+ uint32_t program;
+ uint32_t program_version;
+ uint32_t procedure;
+ uint8_t flags;
+} DetectRpcData;
+
+/* prototypes */
+void DetectRpcRegister (void);
+
+#endif /* __DETECT_RPC_H__ */
+
diff --git a/framework/src/suricata/src/detect-sameip.c b/framework/src/suricata/src/detect-sameip.c
new file mode 100644
index 00000000..a8e02908
--- /dev/null
+++ b/framework/src/suricata/src/detect-sameip.c
@@ -0,0 +1,222 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements the sameip keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-sameip.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+static int DetectSameipMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static int DetectSameipSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectSameipRegisterTests(void);
+
+/**
+ * \brief Registration function for sameip: keyword
+ * \todo add support for no_stream and stream_only
+ */
+void DetectSameipRegister(void)
+{
+ sigmatch_table[DETECT_SAMEIP].name = "sameip";
+ sigmatch_table[DETECT_SAMEIP].desc = "check if the IP address of the source is the same as the IP address of the destination";
+ sigmatch_table[DETECT_SAMEIP].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#sameip";
+ sigmatch_table[DETECT_SAMEIP].Match = DetectSameipMatch;
+ sigmatch_table[DETECT_SAMEIP].Setup = DetectSameipSetup;
+ sigmatch_table[DETECT_SAMEIP].Free = NULL;
+ sigmatch_table[DETECT_SAMEIP].RegisterTests = DetectSameipRegisterTests;
+ sigmatch_table[DETECT_SAMEIP].flags = SIGMATCH_NOOPT;
+}
+
+/**
+ * \internal
+ * \brief This function is used to match packets with same src/dst IPs
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectSameipData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectSameipMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ return CMP_ADDR(&p->src, &p->dst) ? 1 : 0;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the sameip option into the signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectSameipSetup(DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ SigMatch *sm = NULL;
+
+ /* Get this into a SigMatch and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_SAMEIP;
+ sm->ctx = NULL;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+#ifdef UNITTESTS
+
+/* NOTE: No parameters, so no parse tests */
+
+/**
+ * \internal
+ * \brief This test tests sameip success and failure.
+ */
+static int DetectSameipSigTest01Real(int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET / HTTP/1.0\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ /* First packet has same IPs */
+ p1 = UTHBuildPacketSrcDst(buf, buflen, IPPROTO_TCP, "1.2.3.4", "1.2.3.4");
+
+ /* Second packet does not have same IPs */
+ p2 = UTHBuildPacketSrcDst(buf, buflen, IPPROTO_TCP, "1.2.3.4", "4.3.2.1");
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing sameip\"; sameip; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1) == 0) {
+ printf("sid 2 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 1) != 0) {
+ printf("sid 2 alerted, but should not have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ return result;
+}
+
+/**
+ * \test DetectSameipSigTest01B2g tests sameip under B2g MPM
+ */
+static int DetectSameipSigTest01B2g(void)
+{
+ return DetectSameipSigTest01Real(MPM_B2G);
+}
+
+/**
+ * \test DetectSameipSigTest01B2g tests sameip under B3g MPM
+ */
+static int DetectSameipSigTest01B3g(void)
+{
+ return DetectSameipSigTest01Real(MPM_B3G);
+}
+
+/**
+ * \test DetectSameipSigTest01B2g tests sameip under WuManber MPM
+ */
+static int DetectSameipSigTest01Wm(void)
+{
+ return DetectSameipSigTest01Real(MPM_WUMANBER);
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \internal
+ * \brief This function registers unit tests for DetectSameip
+ */
+static void DetectSameipRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectSameipSigTest01B2g", DetectSameipSigTest01B2g, 1);
+ UtRegisterTest("DetectSameipSigTest01B3g", DetectSameipSigTest01B3g, 1);
+ UtRegisterTest("DetectSameipSigTest01Wm", DetectSameipSigTest01Wm, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-sameip.h b/framework/src/suricata/src/detect-sameip.h
new file mode 100644
index 00000000..ef6b54b6
--- /dev/null
+++ b/framework/src/suricata/src/detect-sameip.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_SAMEIP_H__
+#define __DETECT_SAMEIP_H__
+
+/* prototypes */
+void DetectSameipRegister(void);
+
+#endif /* __DETECT_SAMEIP_H__ */
diff --git a/framework/src/suricata/src/detect-seq.c b/framework/src/suricata/src/detect-seq.c
new file mode 100644
index 00000000..4d285ca3
--- /dev/null
+++ b/framework/src/suricata/src/detect-seq.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Implements the seq keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "detect-seq.h"
+
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+static int DetectSeqSetup(DetectEngineCtx *, Signature *, char *);
+static int DetectSeqMatch(ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, const SigMatchCtx *);
+static void DetectSeqRegisterTests(void);
+static void DetectSeqFree(void *);
+
+
+void DetectSeqRegister(void)
+{
+ sigmatch_table[DETECT_SEQ].name = "seq";
+ sigmatch_table[DETECT_SEQ].desc = "check for a specific TCP sequence number";
+ sigmatch_table[DETECT_SEQ].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#seq";
+ sigmatch_table[DETECT_SEQ].Match = DetectSeqMatch;
+ sigmatch_table[DETECT_SEQ].Setup = DetectSeqSetup;
+ sigmatch_table[DETECT_SEQ].Free = DetectSeqFree;
+ sigmatch_table[DETECT_SEQ].RegisterTests = DetectSeqRegisterTests;
+}
+
+/**
+ * \internal
+ * \brief This function is used to match packets with a given Seq number
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectSeqData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectSeqMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectSeqData *data = (const DetectSeqData *)ctx;
+
+ /* This is only needed on TCP packets */
+ if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
+ return 0;
+ }
+
+ return (data->seq == TCP_GET_SEQ(p)) ? 1 : 0;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the seq option into the signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param optstr pointer to the user provided options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectSeqSetup (DetectEngineCtx *de_ctx, Signature *s, char *optstr)
+{
+ DetectSeqData *data = NULL;
+ SigMatch *sm = NULL;
+
+ data = SCMalloc(sizeof(DetectSeqData));
+ if (unlikely(data == NULL))
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_SEQ;
+
+ if (-1 == ByteExtractStringUint32(&data->seq, 10, 0, optstr)) {
+ goto error;
+ }
+ sm->ctx = (SigMatchCtx*)data;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (data)
+ SCFree(data);
+ if (sm)
+ SigMatchFree(sm);
+ return -1;
+
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with seq option
+ *
+ * \param data pointer to seq configuration data
+ */
+static void DetectSeqFree(void *ptr)
+{
+ DetectSeqData *data = (DetectSeqData *)ptr;
+ SCFree(data);
+}
+
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectSeqSigTest01 tests parses
+ */
+static int DetectSeqSigTest01(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ /* These three are crammed in here as there is no Parse */
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing seq\";seq:foo;sid:1;)") != NULL)
+ {
+ printf("invalid seq accepted: ");
+ goto cleanup;
+ }
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing seq\";seq:9999999999;sid:1;)") != NULL)
+ {
+ printf("overflowing seq accepted: ");
+ goto cleanup;
+ }
+ if (SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing seq\";seq:-100;sid:1;)") != NULL)
+ {
+ printf("negative seq accepted: ");
+ goto cleanup;
+ }
+ result = 1;
+
+cleanup:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+end:
+ return result;
+}
+
+/**
+ * \test DetectSeqSigTest02 tests seq keyword
+ */
+static int DetectSeqSigTest02(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ /* TCP w/seq=42 */
+ p[0]->tcph->th_seq = htonl(42);
+
+ /* TCP w/seq=100 */
+ p[1]->tcph->th_seq = htonl(100);
+
+ char *sigs[2];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:41; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:42; sid:2;)";
+
+ uint32_t sid[2] = {1, 2};
+
+ uint32_t results[3][2] = {
+ /* packet 0 match sid 1 but should not match sid 2 */
+ {0, 1},
+ /* packet 1 should not match */
+ {0, 0},
+ /* packet 2 should not match */
+ {0, 0} };
+
+ result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 2);
+ UTHFreePackets(p, 3);
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \internal
+ * \brief This function registers unit tests for DetectSeq
+ */
+static void DetectSeqRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectSeqSigTest01", DetectSeqSigTest01, 1);
+ UtRegisterTest("DetectSeqSigTest02", DetectSeqSigTest02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-seq.h b/framework/src/suricata/src/detect-seq.h
new file mode 100644
index 00000000..813e8fab
--- /dev/null
+++ b/framework/src/suricata/src/detect-seq.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __DETECT_SEQ_H__
+#define __DETECT_SEQ_H__
+
+/**
+ * \brief seq data
+ */
+typedef struct DetectSeqData_ {
+ uint32_t seq; /**< seq to match */
+} DetectSeqData;
+
+/**
+ * \brief Registration function for ack: keyword
+ */
+void DetectSeqRegister(void);
+
+#endif /* __DETECT_SEQ_H__ */
+
diff --git a/framework/src/suricata/src/detect-sid.c b/framework/src/suricata/src/detect-sid.c
new file mode 100644
index 00000000..a3ed3403
--- /dev/null
+++ b/framework/src/suricata/src/detect-sid.c
@@ -0,0 +1,165 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the sid keyword
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-unittest.h"
+
+static int DetectSidSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectSidRegisterTests(void);
+
+void DetectSidRegister (void)
+{
+ sigmatch_table[DETECT_SID].name = "sid";
+ sigmatch_table[DETECT_SID].desc = "set rule id";
+ sigmatch_table[DETECT_SID].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Meta-settings#Sid-signature-id";
+ sigmatch_table[DETECT_SID].Match = NULL;
+ sigmatch_table[DETECT_SID].Setup = DetectSidSetup;
+ sigmatch_table[DETECT_SID].Free = NULL;
+ sigmatch_table[DETECT_SID].RegisterTests = DetectSidRegisterTests;
+}
+
+static int DetectSidSetup (DetectEngineCtx *de_ctx, Signature *s, char *sidstr)
+{
+ char *str = sidstr;
+ char duped = 0;
+
+ /* Strip leading and trailing "s. */
+ if (sidstr[0] == '\"') {
+ str = SCStrdup(sidstr + 1);
+ if (unlikely(str == NULL)) {
+ return -1;
+ }
+ if (strlen(str) && str[strlen(str) - 1] == '\"') {
+ str[strlen(str) - 1] = '\0';
+ }
+ duped = 1;
+ }
+
+ unsigned long id = 0;
+ char *endptr = NULL;
+ id = strtoul(sidstr, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0') {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
+ "to sid keyword");
+ goto error;
+ }
+ if (id >= UINT_MAX) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "sid value to high, max %u", UINT_MAX);
+ goto error;
+ }
+
+ s->id = (uint32_t)id;
+
+ if (duped)
+ SCFree(str);
+ return 0;
+
+ error:
+ if (duped)
+ SCFree(str);
+ return -1;
+}
+
+#ifdef UNITTESTS
+
+static int SidTestParse01(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert tcp 1.2.3.4 any -> any any (sid:1; gid:1;)");
+ if (s == NULL || s->id != 1)
+ goto end;
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SidTestParse02(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx,
+ "alert tcp 1.2.3.4 any -> any any (sid:a; gid:1;)") != NULL)
+ goto end;
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SidTestParse03(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ if (DetectEngineAppendSig(de_ctx,
+ "alert tcp any any -> any any (content:\"ABC\"; sid:\";)") != NULL)
+ goto end;
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif
+
+/**
+ * \brief Register DetectSid unit tests.
+ */
+static void DetectSidRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SidTestParse01", SidTestParse01, 1);
+ UtRegisterTest("SidTestParse02", SidTestParse02, 1);
+ UtRegisterTest("SidTestParse03", SidTestParse03, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-sid.h b/framework/src/suricata/src/detect-sid.h
new file mode 100644
index 00000000..f7389d10
--- /dev/null
+++ b/framework/src/suricata/src/detect-sid.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_SID_H__
+#define __DETECT_SID_H__
+
+/* prototypes */
+void DetectSidRegister (void);
+
+#endif /* __DETECT_SID_H__ */
+
diff --git a/framework/src/suricata/src/detect-ssh-proto-version.c b/framework/src/suricata/src/detect-ssh-proto-version.c
new file mode 100644
index 00000000..96a8b676
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssh-proto-version.c
@@ -0,0 +1,700 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements the ssh.protoversion keyword
+ * You can specify a concrete version like ssh.protoversion: 1.66
+ * or search for protoversion 2 compat (1.99 is considered as 2) like
+ * ssh.protoversion:2_compat
+ * or just the beginning of the string like ssh.protoversion:"1."
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-ssh.h"
+#include "detect-ssh-proto-version.h"
+
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing the protoversion string
+ */
+#define PARSE_REGEX "^\\s*\"?\\s*([0-9]+([\\.\\-0-9]+)?|2_compat)\\s*\"?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectSshVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectSshVersionSetup (DetectEngineCtx *, Signature *, char *);
+void DetectSshVersionRegisterTests(void);
+void DetectSshVersionFree(void *);
+
+/**
+ * \brief Registration function for keyword: ssh.protoversion
+ */
+void DetectSshVersionRegister(void)
+{
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].name = "ssh.protoversion";
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Match = NULL;
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].AppLayerMatch = DetectSshVersionMatch;
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].alproto = ALPROTO_SSH;
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Setup = DetectSshVersionSetup;
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Free = DetectSshVersionFree;
+ sigmatch_table[DETECT_AL_SSH_PROTOVERSION].RegisterTests = DetectSshVersionRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering ssh.protoversion rule option");
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief match the specified version on a ssh session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectSshVersionData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectSshVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectSshVersionData *ssh = (DetectSshVersionData *)m->ctx;
+ SshState *ssh_state = (SshState *)state;
+ if (ssh_state == NULL) {
+ SCLogDebug("no ssh state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+ if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
+ SCLogDebug("looking for ssh server protoversion 2 compat");
+ if (strncmp((char *) ssh_state->srv_hdr.proto_version, "2", 1) == 0 ||
+ strncmp((char *) ssh_state->srv_hdr.proto_version, "2.", 2) == 0 ||
+ strncmp((char *) ssh_state->srv_hdr.proto_version, "1.99", 4) == 0)
+ ret = 1;
+ } else {
+ SCLogDebug("looking for ssh server protoversion %s length %"PRIu16"", ssh->ver, ssh->len);
+ ret = (strncmp((char *) ssh_state->srv_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0;
+ }
+ } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
+ SCLogDebug("looking for client ssh client protoversion 2 compat");
+ if (strncmp((char *) ssh_state->cli_hdr.proto_version, "2", 1) == 0 ||
+ strncmp((char *) ssh_state->cli_hdr.proto_version, "2.", 2) == 0 ||
+ strncmp((char *) ssh_state->cli_hdr.proto_version, "1.99", 4) == 0)
+ ret = 1;
+ } else {
+ SCLogDebug("looking for ssh client protoversion %s length %"PRIu16"", ssh->ver, ssh->len);
+ ret = (strncmp((char *) ssh_state->cli_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0;
+ }
+ }
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectSshVersionData on success
+ * \retval NULL on failure
+ */
+DetectSshVersionData *DetectSshVersionParse (char *str)
+{
+ DetectSshVersionData *ssh = NULL;
+ #define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret < 1 || ret > 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid ssh.protoversion option");
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr;
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ ssh = SCMalloc(sizeof(DetectSshVersionData));
+ if (unlikely(ssh == NULL))
+ goto error;
+
+ memset(ssh, 0x00, sizeof(DetectSshVersionData));
+
+ /* If we expect a protocol version 2 or 1.99 (considered 2, we
+ * will compare it with both strings) */
+ if (strcmp("2_compat", str_ptr) == 0) {
+ ssh->flags |= SSH_FLAG_PROTOVERSION_2_COMPAT;
+ SCLogDebug("will look for ssh protocol version 2 (2, 2.0, 1.99 that's considered as 2");
+ return ssh;
+ }
+
+ ssh->ver = (uint8_t *)SCStrdup((char*)str_ptr);
+ if (ssh->ver == NULL) {
+ goto error;
+ }
+ ssh->len = strlen((char *) ssh->ver);
+
+ SCLogDebug("will look for ssh %s", ssh->ver);
+ }
+
+ return ssh;
+
+error:
+ if (ssh != NULL)
+ DetectSshVersionFree(ssh);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectSshVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectSshVersionData *ssh = NULL;
+ SigMatch *sm = NULL;
+
+ ssh = DetectSshVersionParse(str);
+ if (ssh == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_SSH) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_SSH_PROTOVERSION;
+ sm->ctx = (void *)ssh;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_SSH;
+ return 0;
+
+error:
+ if (ssh != NULL)
+ DetectSshVersionFree(ssh);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectSshVersionData
+ *
+ * \param id_d pointer to DetectSshVersionData
+ */
+void DetectSshVersionFree(void *ptr)
+{
+ DetectSshVersionData *id_d = (DetectSshVersionData *)ptr;
+ SCFree(id_d);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectSshVersionTestParse01 is a test to make sure that we parse
+ * a proto version correctly
+ */
+int DetectSshVersionTestParse01 (void)
+{
+ DetectSshVersionData *ssh = NULL;
+ ssh = DetectSshVersionParse("1.0");
+ if (ssh != NULL && strncmp((char *) ssh->ver, "1.0", 3) == 0) {
+ DetectSshVersionFree(ssh);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSshVersionTestParse02 is a test to make sure that we parse
+ * the proto version (compatible with proto version 2) correctly
+ */
+int DetectSshVersionTestParse02 (void)
+{
+ DetectSshVersionData *ssh = NULL;
+ ssh = DetectSshVersionParse("2_compat");
+ if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
+ DetectSshVersionFree(ssh);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSshVersionTestParse03 is a test to make sure that we
+ * don't return a ssh_data with an invalid value specified
+ */
+int DetectSshVersionTestParse03 (void)
+{
+ DetectSshVersionData *ssh = NULL;
+ ssh = DetectSshVersionParse("2_com");
+ if (ssh != NULL) {
+ DetectSshVersionFree(ssh);
+ return 0;
+ }
+ ssh = DetectSshVersionParse("");
+ if (ssh != NULL) {
+ DetectSshVersionFree(ssh);
+ return 0;
+ }
+ ssh = DetectSshVersionParse(".1");
+ if (ssh != NULL) {
+ DetectSshVersionFree(ssh);
+ return 0;
+ }
+ ssh = DetectSshVersionParse("lalala");
+ if (ssh != NULL) {
+ DetectSshVersionFree(ssh);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshVersionTestDetect01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "10-PuTTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ( !(PacketAlertCheck(p, 1))) {
+ printf("Error, the sig should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshVersionTestDetect02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "99-PuTTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ( !(PacketAlertCheck(p, 1))) {
+ printf("Error, the sig should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshVersionTestDetect03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "7-PuTTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("Error, 1.7 version is not 2 compat, so the sig should not match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectSshVersion
+ */
+void DetectSshVersionRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectSshVersionTestParse01", DetectSshVersionTestParse01, 1);
+ UtRegisterTest("DetectSshVersionTestParse02", DetectSshVersionTestParse02, 1);
+ UtRegisterTest("DetectSshVersionTestParse03", DetectSshVersionTestParse03, 1);
+ UtRegisterTest("DetectSshVersionTestDetect01", DetectSshVersionTestDetect01, 1);
+ UtRegisterTest("DetectSshVersionTestDetect02", DetectSshVersionTestDetect02, 1);
+ UtRegisterTest("DetectSshVersionTestDetect03", DetectSshVersionTestDetect03, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-ssh-proto-version.h b/framework/src/suricata/src/detect-ssh-proto-version.h
new file mode 100644
index 00000000..a4c8eddb
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssh-proto-version.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_SSH_VERSION_H__
+#define __DETECT_SSH_VERSION_H__
+
+/** proto version 1.99 is considered proto version 2 */
+#define SSH_FLAG_PROTOVERSION_2_COMPAT 0x01
+
+typedef struct DetectSshVersionData_ {
+ uint8_t *ver; /** ssh version to match */
+ uint16_t len; /** ssh version length to match */
+ uint8_t flags;
+} DetectSshVersionData;
+
+/* prototypes */
+void DetectSshVersionRegister (void);
+
+#endif /* __DETECT_SSH_VERSION_H__ */
+
diff --git a/framework/src/suricata/src/detect-ssh-software-version.c b/framework/src/suricata/src/detect-ssh-software-version.c
new file mode 100644
index 00000000..4197bfff
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssh-software-version.c
@@ -0,0 +1,673 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements the ssh.softwareversion keyword
+ * You can match over the software version string of ssh, and it will
+ * be compared from the beginning of the string so you can say for
+ * example ssh.softwareversion:"PuTTY" and it can match, or you can
+ * also specify the version, something like
+ * ssh.softwareversion:"PuTTY-Release-0.55"
+ * I find this useful to match over a known vulnerable server/client
+ * software version incombination to other checks, so you can know
+ * that the risk is higher
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-ssh.h"
+#include "detect-ssh-software-version.h"
+
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing the softwareversion string
+ */
+#define PARSE_REGEX "^\\s*\"?\\s*?([0-9a-zA-Z\\:\\.\\-\\_\\+\\s+]+)\\s*\"?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectSshSoftwareVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectSshSoftwareVersionSetup (DetectEngineCtx *, Signature *, char *);
+void DetectSshSoftwareVersionRegisterTests(void);
+void DetectSshSoftwareVersionFree(void *);
+void DetectSshSoftwareVersionRegister(void);
+
+/**
+ * \brief Registration function for keyword: ssh.softwareversion
+ */
+void DetectSshSoftwareVersionRegister(void)
+{
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].name = "ssh.softwareversion";
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Match = NULL;
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].AppLayerMatch = DetectSshSoftwareVersionMatch;
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].alproto = ALPROTO_SSH;
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Setup = DetectSshSoftwareVersionSetup;
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Free = DetectSshSoftwareVersionFree;
+ sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].RegisterTests = DetectSshSoftwareVersionRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering ssh.softwareversion rule option");
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief match the specified version on a ssh session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectSshSoftwareVersionData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectSshSoftwareVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)m->ctx;
+ SshState *ssh_state = (SshState *)state;
+ if (ssh_state == NULL) {
+ SCLogDebug("no ssh state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+ if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ SCLogDebug("looking for ssh server softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->srv_hdr.software_version);
+ ret = (strncmp((char *) ssh_state->srv_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0;
+ } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
+ SCLogDebug("looking for ssh client softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->cli_hdr.software_version);
+ ret = (strncmp((char *) ssh_state->cli_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0;
+ }
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectSshSoftwareVersionData on success
+ * \retval NULL on failure
+ */
+DetectSshSoftwareVersionData *DetectSshSoftwareVersionParse (char *str)
+{
+ DetectSshSoftwareVersionData *ssh = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret < 1 || ret > 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid ssh.softwareversion option");
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr = NULL;
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ ssh = SCMalloc(sizeof(DetectSshSoftwareVersionData));
+ if (unlikely(ssh == NULL))
+ goto error;
+
+ ssh->software_ver = (uint8_t *)SCStrdup((char *)str_ptr);
+ if (ssh->software_ver == NULL) {
+ goto error;
+ }
+ pcre_free_substring(str_ptr);
+
+ ssh->len = strlen((char *)ssh->software_ver);
+
+ SCLogDebug("will look for ssh %s", ssh->software_ver);
+ }
+
+ return ssh;
+
+error:
+ if (ssh != NULL)
+ DetectSshSoftwareVersionFree(ssh);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectSshSoftwareVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectSshSoftwareVersionData *ssh = NULL;
+ SigMatch *sm = NULL;
+
+ ssh = DetectSshSoftwareVersionParse(str);
+ if (ssh == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_SSH) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_SSH_SOFTWAREVERSION;
+ sm->ctx = (void *)ssh;
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_SSH;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (ssh != NULL)
+ DetectSshSoftwareVersionFree(ssh);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectSshSoftwareVersionData
+ *
+ * \param id_d pointer to DetectSshSoftwareVersionData
+ */
+void DetectSshSoftwareVersionFree(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)ptr;
+ if (ssh->software_ver != NULL)
+ SCFree(ssh->software_ver);
+ SCFree(ssh);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectSshSoftwareVersionTestParse01 is a test to make sure that we parse
+ * a software version correctly
+ */
+int DetectSshSoftwareVersionTestParse01 (void)
+{
+ DetectSshSoftwareVersionData *ssh = NULL;
+ ssh = DetectSshSoftwareVersionParse("PuTTY_1.0");
+ if (ssh != NULL && strncmp((char *) ssh->software_ver, "PuTTY_1.0", 9) == 0) {
+ DetectSshSoftwareVersionFree(ssh);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSshSoftwareVersionTestParse02 is a test to make sure that we parse
+ * the software version correctly
+ */
+int DetectSshSoftwareVersionTestParse02 (void)
+{
+ DetectSshSoftwareVersionData *ssh = NULL;
+ ssh = DetectSshSoftwareVersionParse("\"SecureCRT-4.0\"");
+ if (ssh != NULL && strncmp((char *) ssh->software_ver, "SecureCRT-4.0", 13) == 0) {
+ DetectSshSoftwareVersionFree(ssh);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSshSoftwareVersionTestParse03 is a test to make sure that we
+ * don't return a ssh_data with an empty value specified
+ */
+int DetectSshSoftwareVersionTestParse03 (void)
+{
+ DetectSshSoftwareVersionData *ssh = NULL;
+ ssh = DetectSshSoftwareVersionParse("");
+ if (ssh != NULL) {
+ DetectSshSoftwareVersionFree(ssh);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshSoftwareVersionTestDetect01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "10-PuTTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ( !(PacketAlertCheck(p, 1))) {
+ printf("Error, the sig should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshSoftwareVersionTestDetect02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.99-Pu";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "TTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ( !(PacketAlertCheck(p, 1))) {
+ printf("Error, the sig should match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSshSoftwareVersionTestDetect03(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sshbuf1[] = "SSH-1.";
+ uint32_t sshlen1 = sizeof(sshbuf1) - 1;
+ uint8_t sshbuf2[] = "7-PuTTY_2.123" ;
+ uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+ uint8_t sshbuf3[] = "\n";
+ uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+ uint8_t sshbuf4[] = "whatever...";
+ uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_SSH;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:lalala-3.1.4; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SshState *ssh_state = f.alstate;
+ if (ssh_state == NULL) {
+ printf("no ssh state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("Error, 1.7 version is not 2 compat, so the sig should not match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectSshSoftwareVersion
+ */
+void DetectSshSoftwareVersionRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectSshSoftwareVersionTestParse01", DetectSshSoftwareVersionTestParse01, 1);
+ UtRegisterTest("DetectSshSoftwareVersionTestParse02", DetectSshSoftwareVersionTestParse02, 1);
+ UtRegisterTest("DetectSshSoftwareVersionTestParse03", DetectSshSoftwareVersionTestParse03, 1);
+ UtRegisterTest("DetectSshSoftwareVersionTestDetect01", DetectSshSoftwareVersionTestDetect01, 1);
+ UtRegisterTest("DetectSshSoftwareVersionTestDetect02", DetectSshSoftwareVersionTestDetect02, 1);
+ UtRegisterTest("DetectSshSoftwareVersionTestDetect03", DetectSshSoftwareVersionTestDetect03, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-ssh-software-version.h b/framework/src/suricata/src/detect-ssh-software-version.h
new file mode 100644
index 00000000..70c37c74
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssh-software-version.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __DETECT_SSH_SOFTWARE_VERSION_H__
+#define __DETECT_SSH_SOFTWARE_VERSION_H__
+
+typedef struct DetectSshSoftwareVersionData_ {
+ uint8_t *software_ver; /** ssh version to match */
+ uint16_t len; /** ssh version length to match */
+} DetectSshSoftwareVersionData;
+
+/* prototypes */
+void DetectSshSoftwareVersionRegister(void);
+void DetectSshSoftwareVersionRegisterTests(void);
+
+#endif /* __DETECT_SSH_SOFTWARE_VERSION_H__ */
+
diff --git a/framework/src/suricata/src/detect-ssl-state.c b/framework/src/suricata/src/detect-ssl-state.c
new file mode 100644
index 00000000..fa296631
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssl-state.c
@@ -0,0 +1,913 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements support for ssl_state keyword.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "detect-ssl-state.h"
+
+#include "stream-tcp.h"
+#include "app-layer-ssl.h"
+
+#define PARSE_REGEX1 "^\\s*([_a-zA-Z0-9]+)(.*)$"
+static pcre *parse_regex1;
+static pcre_extra *parse_regex1_study;
+
+#define PARSE_REGEX2 "^(?:\\s*[|]\\s*([_a-zA-Z0-9]+))(.*)$"
+static pcre *parse_regex2;
+static pcre_extra *parse_regex2_study;
+
+int DetectSslStateMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, void *, Signature *, SigMatch *);
+int DetectSslStateSetup(DetectEngineCtx *, Signature *, char *);
+void DetectSslStateRegisterTests(void);
+void DetectSslStateFree(void *);
+
+/**
+ * \brief Registers the keyword handlers for the "ssl_state" keyword.
+ */
+void DetectSslStateRegister(void)
+{
+ sigmatch_table[DETECT_AL_SSL_STATE].name = "ssl_state";
+ sigmatch_table[DETECT_AL_SSL_STATE].Match = NULL;
+ sigmatch_table[DETECT_AL_SSL_STATE].AppLayerMatch = DetectSslStateMatch;
+ sigmatch_table[DETECT_AL_SSL_STATE].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_SSL_STATE].Setup = DetectSslStateSetup;
+ sigmatch_table[DETECT_AL_SSL_STATE].Free = DetectSslStateFree;
+ sigmatch_table[DETECT_AL_SSL_STATE].RegisterTests = DetectSslStateRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering ssl_state rule option");
+
+ /* PARSE_REGEX1 */
+ parse_regex1 = pcre_compile(PARSE_REGEX1, opts, &eb, &eo, NULL);
+ if (parse_regex1 == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX1, eo, eb);
+ goto error;
+ }
+
+ parse_regex1_study = pcre_study(parse_regex1, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ /* PARSE_REGEX2 */
+ parse_regex2 = pcre_compile(PARSE_REGEX2, opts, &eb, &eo, NULL);
+ if (parse_regex2 == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX2, eo, eb);
+ goto error;
+ }
+
+ parse_regex2_study = pcre_study(parse_regex2, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+}
+
+/**
+ * \brief App layer match function ssl_state keyword.
+ *
+ * \param tv Pointer to threadvars.
+ * \param det_ctx Pointer to the thread's detection context.
+ * \param f Pointer to the flow.
+ * \param flags Flags.
+ * \param state App layer state.
+ * \param s Sig we are currently inspecting.
+ * \param m SigMatch we are currently inspecting.
+ *
+ * \retval 1 Match.
+ * \retval 0 No match.
+ */
+int DetectSslStateMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *alstate, Signature *s,
+ SigMatch *m)
+{
+ int result = 1;
+ DetectSslStateData *ssd = (DetectSslStateData *)m->ctx;
+ SSLState *ssl_state = (SSLState *)alstate;
+ if (ssl_state == NULL) {
+ SCLogDebug("no app state, no match");
+ return 0;
+ }
+
+ if ((ssd->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
+ !(ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO)) {
+ result = 0;
+ goto end;
+ }
+ if ((ssd->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ !(ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO)) {
+ result = 0;
+ goto end;
+ }
+ if ((ssd->flags & SSL_AL_FLAG_STATE_CLIENT_KEYX) &&
+ !(ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_KEYX)) {
+ result = 0;
+ goto end;
+ }
+ if ((ssd->flags & SSL_AL_FLAG_STATE_SERVER_KEYX) &&
+ !(ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_KEYX)) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ return result;
+}
+
+/**
+ * \brief Parse the arg supplied with ssl_state and return it in a
+ * DetectSslStateData instance.
+ *
+ * \param arg Pointer to the string to be parsed.
+ *
+ * \retval ssd Pointer to DetectSslStateData on succese.
+ * \retval NULL On failure.
+ */
+DetectSslStateData *DetectSslStateParse(char *arg)
+{
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov1[MAX_SUBSTRINGS];
+ int ov2[MAX_SUBSTRINGS];
+ const char *str1;
+ const char *str2;
+ uint32_t flags = 0;
+ DetectSslStateData *ssd = NULL;
+
+ ret = pcre_exec(parse_regex1, parse_regex1_study, arg, strlen(arg), 0, 0,
+ ov1, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid arg \"%s\" supplied to "
+ "ssl_state keyword.", arg);
+ goto error;
+ }
+ res = pcre_get_substring((char *)arg, ov1, MAX_SUBSTRINGS, 1, &str1);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (strcmp("client_hello", str1) == 0) {
+ flags |= DETECT_SSL_STATE_CLIENT_HELLO;
+ } else if (strcmp("server_hello", str1) == 0) {
+ flags |= DETECT_SSL_STATE_SERVER_HELLO;
+ } else if (strcmp("client_keyx", str1) == 0) {
+ flags |= DETECT_SSL_STATE_CLIENT_KEYX;
+ } else if (strcmp("server_keyx", str1) == 0) {
+ flags |= DETECT_SSL_STATE_SERVER_KEYX;
+ } else if (strcmp("unknown", str1) == 0) {
+ flags |= DETECT_SSL_STATE_UNKNOWN;
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Found invalid option \"%s\" "
+ "in ssl_state keyword.", str1);
+ goto error;
+ }
+
+ pcre_free_substring(str1);
+
+ res = pcre_get_substring((char *)arg, ov1, MAX_SUBSTRINGS, 2, &str1);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ while (res > 0) {
+ ret = pcre_exec(parse_regex2, parse_regex2_study, str1, strlen(str1), 0, 0,
+ ov2, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid arg \"%s\" supplied to "
+ "ssl_state keyword.", arg);
+ goto error;
+ }
+ res = pcre_get_substring((char *)str1, ov2, MAX_SUBSTRINGS, 1, &str2);
+ if (res <= 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ if (strcmp("client_hello", str2) == 0) {
+ flags |= DETECT_SSL_STATE_CLIENT_HELLO;
+ } else if (strcmp("server_hello", str2) == 0) {
+ flags |= DETECT_SSL_STATE_SERVER_HELLO;
+ } else if (strcmp("client_keyx", str2) == 0) {
+ flags |= DETECT_SSL_STATE_CLIENT_KEYX;
+ } else if (strcmp("server_keyx", str2) == 0) {
+ flags |= DETECT_SSL_STATE_SERVER_KEYX;
+ } else if (strcmp("unknown", str2) == 0) {
+ flags |= DETECT_SSL_STATE_UNKNOWN;
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Found invalid option \"%s\" "
+ "in ssl_state keyword.", str2);
+ goto error;
+ }
+
+ res = pcre_get_substring((char *)str1, ov2, MAX_SUBSTRINGS, 2, &str2);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ pcre_free_substring(str1);
+ str1 = str2;
+ }
+
+ if ( (ssd = SCMalloc(sizeof(DetectSslStateData))) == NULL) {
+ goto error;
+ }
+ ssd->flags = flags;
+
+ return ssd;
+
+error:
+ if (ssd != NULL)
+ DetectSslStateFree(ssd);
+ return NULL;
+}
+
+ /**
+ * \internal
+ * \brief Setup function for ssl_state keyword.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature
+ * \param arg String holding the arg.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectSslStateSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ DetectSslStateData *ssd = NULL;
+ SigMatch *sm = NULL;
+
+ ssd = DetectSslStateParse(arg);
+ if (ssd == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_SSL_STATE;
+ sm->ctx = (SigMatchCtx*)ssd;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS,
+ "Rule contains conflicting keywords. Have non-tls alproto "
+ "set for a rule containing \"ssl_state\" keyword");
+ goto error;
+ }
+ s->alproto = ALPROTO_TLS;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (ssd != NULL)
+ DetectSslStateFree(ssd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief Free memory associated with DetectSslStateData.
+ *
+ * \param ptr pointer to the data to be freed.
+ */
+void DetectSslStateFree(void *ptr)
+{
+ if (ptr != NULL)
+ SCFree(ptr);
+
+ return;
+}
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+
+int DetectSslStateTest01(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("client_hello");
+ if (ssd == NULL) {
+ printf("ssd == NULL\n");
+ return 0;
+ }
+ if (ssd->flags == DETECT_SSL_STATE_CLIENT_HELLO) {
+ SCFree(ssd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectSslStateTest02(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("server_hello | client_hello");
+ if (ssd == NULL) {
+ printf("ssd == NULL\n");
+ return 0;
+ }
+ if (ssd->flags == (DETECT_SSL_STATE_SERVER_HELLO |
+ DETECT_SSL_STATE_CLIENT_HELLO)) {
+ SCFree(ssd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectSslStateTest03(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("server_hello | client_keyx | "
+ "client_hello");
+ if (ssd == NULL) {
+ printf("ssd == NULL\n");
+ return 0;
+ }
+ if (ssd->flags == (DETECT_SSL_STATE_SERVER_HELLO |
+ DETECT_SSL_STATE_CLIENT_KEYX |
+ DETECT_SSL_STATE_CLIENT_HELLO)) {
+ SCFree(ssd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectSslStateTest04(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("server_hello | client_keyx | "
+ "client_hello | server_keyx | "
+ "unknown");
+ if (ssd == NULL) {
+ printf("ssd == NULL\n");
+ return 0;
+ }
+ if (ssd->flags == (DETECT_SSL_STATE_SERVER_HELLO |
+ DETECT_SSL_STATE_CLIENT_KEYX |
+ DETECT_SSL_STATE_CLIENT_HELLO |
+ DETECT_SSL_STATE_SERVER_KEYX |
+ DETECT_SSL_STATE_UNKNOWN)) {
+ SCFree(ssd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectSslStateTest05(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("| server_hello | client_keyx | "
+ "client_hello | server_keyx | "
+ "unknown");
+
+ if (ssd != NULL) {
+ printf("ssd != NULL - failure\n");
+ SCFree(ssd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int DetectSslStateTest06(void)
+{
+ DetectSslStateData *ssd = DetectSslStateParse("server_hello | client_keyx | "
+ "client_hello | server_keyx | "
+ "unknown | ");
+ if (ssd != NULL) {
+ printf("ssd != NULL - failure\n");
+ SCFree(ssd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \test Test a valid dce_iface entry for a bind and bind_ack
+ */
+static int DetectSslStateTest07(void)
+{
+ uint8_t chello_buf[] = {
+ 0x80, 0x67, 0x01, 0x03, 0x00, 0x00, 0x4e, 0x00,
+ 0x00, 0x00, 0x10, 0x01, 0x00, 0x80, 0x03, 0x00,
+ 0x80, 0x07, 0x00, 0xc0, 0x06, 0x00, 0x40, 0x02,
+ 0x00, 0x80, 0x04, 0x00, 0x80, 0x00, 0x00, 0x39,
+ 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00,
+ 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x13, 0x00, 0xfe, 0xff, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00,
+ 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64,
+ 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x06, 0xa8, 0xb8, 0x93, 0xbb, 0x90, 0xe9, 0x2a,
+ 0xa2, 0x4d, 0x6d, 0xcc, 0x1c, 0xe7, 0x2a, 0x80,
+ 0x21
+ };
+ uint32_t chello_buf_len = sizeof(chello_buf);
+
+ uint8_t shello_buf[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x4a, 0x02,
+ 0x00, 0x00, 0x46, 0x03, 0x00, 0x44, 0x4c, 0x94,
+ 0x8f, 0xfe, 0x81, 0xed, 0x93, 0x65, 0x02, 0x88,
+ 0xa3, 0xf8, 0xeb, 0x63, 0x86, 0x0e, 0x2c, 0xf6,
+ 0x8d, 0xd0, 0x0f, 0x2c, 0x2a, 0xd6, 0x4f, 0xcd,
+ 0x2d, 0x3c, 0x16, 0xd7, 0xd6, 0x20, 0xa0, 0xfb,
+ 0x60, 0x86, 0x3d, 0x1e, 0x76, 0xf3, 0x30, 0xfe,
+ 0x0b, 0x01, 0xfd, 0x1a, 0x01, 0xed, 0x95, 0xf6,
+ 0x7b, 0x8e, 0xc0, 0xd4, 0x27, 0xbf, 0xf0, 0x6e,
+ 0xc7, 0x56, 0xb1, 0x47, 0xce, 0x98, 0x00, 0x35,
+ 0x00, 0x16, 0x03, 0x00, 0x03, 0x44, 0x0b, 0x00,
+ 0x03, 0x40, 0x00, 0x03, 0x3d, 0x00, 0x03, 0x3a,
+ 0x30, 0x82, 0x03, 0x36, 0x30, 0x82, 0x02, 0x9f,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x58, 0x59, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0c, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20,
+ 0x44, 0x65, 0x73, 0x65, 0x72, 0x74, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x0a, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20, 0x54,
+ 0x6f, 0x77, 0x6e, 0x31, 0x17, 0x30, 0x15, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x53, 0x6e,
+ 0x61, 0x6b, 0x65, 0x20, 0x4f, 0x69, 0x6c, 0x2c,
+ 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1e, 0x30, 0x1c,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x31, 0x15, 0x30, 0x13,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0c, 0x53,
+ 0x6e, 0x61, 0x6b, 0x65, 0x20, 0x4f, 0x69, 0x6c,
+ 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x0f, 0x63, 0x61, 0x40, 0x73,
+ 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69, 0x6c, 0x2e,
+ 0x64, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30,
+ 0x33, 0x30, 0x33, 0x30, 0x35, 0x31, 0x36, 0x34,
+ 0x37, 0x34, 0x35, 0x5a, 0x17, 0x0d, 0x30, 0x38,
+ 0x30, 0x33, 0x30, 0x33, 0x31, 0x36, 0x34, 0x37,
+ 0x34, 0x35, 0x5a, 0x30, 0x81, 0xa7, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x58, 0x59, 0x31, 0x15, 0x30, 0x13, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x0c, 0x53, 0x6e,
+ 0x61, 0x6b, 0x65, 0x20, 0x44, 0x65, 0x73, 0x65,
+ 0x72, 0x74, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x6e, 0x61,
+ 0x6b, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0e, 0x53, 0x6e, 0x61, 0x6b, 0x65, 0x20,
+ 0x4f, 0x69, 0x6c, 0x2c, 0x20, 0x4c, 0x74, 0x64,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0e, 0x57, 0x65, 0x62, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x54, 0x65, 0x61,
+ 0x6d, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69, 0x6c,
+ 0x2e, 0x64, 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x01, 0x16, 0x10, 0x77, 0x77, 0x77,
+ 0x40, 0x73, 0x6e, 0x61, 0x6b, 0x65, 0x6f, 0x69,
+ 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x30, 0x81, 0x9f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81,
+ 0x81, 0x00, 0xa4, 0x6e, 0x53, 0x14, 0x0a, 0xde,
+ 0x2c, 0xe3, 0x60, 0x55, 0x9a, 0xf2, 0x42, 0xa6,
+ 0xaf, 0x47, 0x12, 0x2f, 0x17, 0xce, 0xfa, 0xba,
+ 0xdc, 0x4e, 0x63, 0x56, 0x34, 0xb9, 0xba, 0x73,
+ 0x4b, 0x78, 0x44, 0x3d, 0xc6, 0x6c, 0x69, 0xa4,
+ 0x25, 0xb3, 0x61, 0x02, 0x9d, 0x09, 0x04, 0x3f,
+ 0x72, 0x3d, 0xd8, 0x27, 0xd3, 0xb0, 0x5a, 0x45,
+ 0x77, 0xb7, 0x36, 0xe4, 0x26, 0x23, 0xcc, 0x12,
+ 0xb8, 0xae, 0xde, 0xa7, 0xb6, 0x3a, 0x82, 0x3c,
+ 0x7c, 0x24, 0x59, 0x0a, 0xf8, 0x96, 0x43, 0x8b,
+ 0xa3, 0x29, 0x36, 0x3f, 0x91, 0x7f, 0x5d, 0xc7,
+ 0x23, 0x94, 0x29, 0x7f, 0x0a, 0xce, 0x0a, 0xbd,
+ 0x8d, 0x9b, 0x2f, 0x19, 0x17, 0xaa, 0xd5, 0x8e,
+ 0xec, 0x66, 0xa2, 0x37, 0xeb, 0x3f, 0x57, 0x53,
+ 0x3c, 0xf2, 0xaa, 0xbb, 0x79, 0x19, 0x4b, 0x90,
+ 0x7e, 0xa7, 0xa3, 0x99, 0xfe, 0x84, 0x4c, 0x89,
+ 0xf0, 0x3d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x6e, 0x30, 0x6c, 0x30, 0x1b, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x14, 0x30, 0x12, 0x81, 0x10,
+ 0x77, 0x77, 0x77, 0x40, 0x73, 0x6e, 0x61, 0x6b,
+ 0x65, 0x6f, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d,
+ 0x30, 0x3a, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x2d, 0x16,
+ 0x2b, 0x6d, 0x6f, 0x64, 0x5f, 0x73, 0x73, 0x6c,
+ 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f,
+ 0x6d, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x30, 0x11, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01,
+ 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0xae, 0x79, 0x79, 0x22, 0x90, 0x75,
+ 0xfd, 0xa6, 0xd5, 0xc4, 0xb8, 0xc4, 0x99, 0x4e,
+ 0x1c, 0x05, 0x7c, 0x91, 0x59, 0xbe, 0x89, 0x0d,
+ 0x3d, 0xc6, 0x8c, 0xa3, 0xcf, 0xf6, 0xba, 0x23,
+ 0xdf, 0xb8, 0xae, 0x44, 0x68, 0x8a, 0x8f, 0xb9,
+ 0x8b, 0xcb, 0x12, 0xda, 0xe6, 0xa2, 0xca, 0xa5,
+ 0xa6, 0x55, 0xd9, 0xd2, 0xa1, 0xad, 0xba, 0x9b,
+ 0x2c, 0x44, 0x95, 0x1d, 0x4a, 0x90, 0x59, 0x7f,
+ 0x83, 0xae, 0x81, 0x5e, 0x3f, 0x92, 0xe0, 0x14,
+ 0x41, 0x82, 0x4e, 0x7f, 0x53, 0xfd, 0x10, 0x23,
+ 0xeb, 0x8a, 0xeb, 0xe9, 0x92, 0xea, 0x61, 0xf2,
+ 0x8e, 0x19, 0xa1, 0xd3, 0x49, 0xc0, 0x84, 0x34,
+ 0x1e, 0x2e, 0x6e, 0xf6, 0x98, 0xe2, 0x87, 0x53,
+ 0xd6, 0x55, 0xd9, 0x1a, 0x8a, 0x92, 0x5c, 0xad,
+ 0xdc, 0x1e, 0x1c, 0x30, 0xa7, 0x65, 0x9d, 0xc2,
+ 0x4f, 0x60, 0xd2, 0x6f, 0xdb, 0xe0, 0x9f, 0x9e,
+ 0xbc, 0x41, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e,
+ 0x00, 0x00, 0x00
+ };
+ uint32_t shello_buf_len = sizeof(shello_buf);
+
+ uint8_t client_change_cipher_spec_buf[] = {
+ 0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00,
+ 0x80, 0x65, 0x51, 0x2d, 0xa6, 0xd4, 0xa7, 0x38,
+ 0xdf, 0xac, 0x79, 0x1f, 0x0b, 0xd9, 0xb2, 0x61,
+ 0x7d, 0x73, 0x88, 0x32, 0xd9, 0xf2, 0x62, 0x3a,
+ 0x8b, 0x11, 0x04, 0x75, 0xca, 0x42, 0xff, 0x4e,
+ 0xd9, 0xcc, 0xb9, 0xfa, 0x86, 0xf3, 0x16, 0x2f,
+ 0x09, 0x73, 0x51, 0x66, 0xaa, 0x29, 0xcd, 0x80,
+ 0x61, 0x0f, 0xe8, 0x13, 0xce, 0x5b, 0x8e, 0x0a,
+ 0x23, 0xf8, 0x91, 0x5e, 0x5f, 0x54, 0x70, 0x80,
+ 0x8e, 0x7b, 0x28, 0xef, 0xb6, 0x69, 0xb2, 0x59,
+ 0x85, 0x74, 0x98, 0xe2, 0x7e, 0xd8, 0xcc, 0x76,
+ 0x80, 0xe1, 0xb6, 0x45, 0x4d, 0xc7, 0xcd, 0x84,
+ 0xce, 0xb4, 0x52, 0x79, 0x74, 0xcd, 0xe6, 0xd7,
+ 0xd1, 0x9c, 0xad, 0xef, 0x63, 0x6c, 0x0f, 0xf7,
+ 0x05, 0xe4, 0x4d, 0x1a, 0xd3, 0xcb, 0x9c, 0xd2,
+ 0x51, 0xb5, 0x61, 0xcb, 0xff, 0x7c, 0xee, 0xc7,
+ 0xbc, 0x5e, 0x15, 0xa3, 0xf2, 0x52, 0x0f, 0xbb,
+ 0x32, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16,
+ 0x03, 0x00, 0x00, 0x40, 0xa9, 0xd8, 0xd7, 0x35,
+ 0xbc, 0x39, 0x56, 0x98, 0xad, 0x87, 0x61, 0x2a,
+ 0xc4, 0x8f, 0xcc, 0x03, 0xcb, 0x93, 0x80, 0x81,
+ 0xb0, 0x4a, 0xc4, 0xd2, 0x09, 0x71, 0x3e, 0x90,
+ 0x3c, 0x8d, 0xe0, 0x95, 0x44, 0xfe, 0x56, 0xd1,
+ 0x7e, 0x88, 0xe2, 0x48, 0xfd, 0x76, 0x70, 0x76,
+ 0xe2, 0xcd, 0x06, 0xd0, 0xf3, 0x9d, 0x13, 0x79,
+ 0x67, 0x1e, 0x37, 0xf6, 0x98, 0xbe, 0x59, 0x18,
+ 0x4c, 0xfc, 0x75, 0x56
+ };
+ uint32_t client_change_cipher_spec_buf_len =
+ sizeof(client_change_cipher_spec_buf);
+
+ uint8_t server_change_cipher_spec_buf[] = {
+ 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x00, 0x00, 0x40, 0xce, 0x7c, 0x92, 0x43, 0x59,
+ 0xcc, 0x3d, 0x90, 0x91, 0x9c, 0x58, 0xf0, 0x7a,
+ 0xce, 0xae, 0x0d, 0x08, 0xe0, 0x76, 0xb4, 0x86,
+ 0xb1, 0x15, 0x5b, 0x32, 0xb8, 0x77, 0x53, 0xe7,
+ 0xa6, 0xf9, 0xd0, 0x95, 0x5f, 0xaa, 0x07, 0xc3,
+ 0x96, 0x7c, 0xc9, 0x88, 0xc2, 0x7a, 0x20, 0x89,
+ 0x4f, 0xeb, 0xeb, 0xb6, 0x19, 0xef, 0xaa, 0x27,
+ 0x73, 0x9d, 0xa6, 0xb4, 0x9f, 0xeb, 0x34, 0xe2,
+ 0x4d, 0x9f, 0x6b
+ };
+ uint32_t server_change_cipher_spec_buf_len =
+ sizeof(server_change_cipher_spec_buf);
+
+ uint8_t toserver_app_data_buf[] = {
+ 0x17, 0x03, 0x00, 0x01, 0xb0, 0x4a, 0xc3, 0x3e,
+ 0x9d, 0x77, 0x78, 0x01, 0x2c, 0xb4, 0xbc, 0x4c,
+ 0x9a, 0x84, 0xd7, 0xb9, 0x90, 0x0c, 0x21, 0x10,
+ 0xf0, 0xfa, 0x00, 0x7c, 0x16, 0xbb, 0x77, 0xfb,
+ 0x72, 0x42, 0x4f, 0xad, 0x50, 0x4a, 0xd0, 0xaa,
+ 0x6f, 0xaa, 0x44, 0x6c, 0x62, 0x94, 0x1b, 0xc5,
+ 0xfe, 0xe9, 0x1c, 0x5e, 0xde, 0x85, 0x0b, 0x0e,
+ 0x05, 0xe4, 0x18, 0x6e, 0xd2, 0xd3, 0xb5, 0x20,
+ 0xab, 0x81, 0xfd, 0x18, 0x9a, 0x73, 0xb8, 0xd7,
+ 0xef, 0xc3, 0xdd, 0x74, 0xd7, 0x9c, 0x1e, 0x6f,
+ 0x21, 0x6d, 0xf8, 0x24, 0xca, 0x3c, 0x70, 0x78,
+ 0x36, 0x12, 0x7a, 0x8a, 0x9c, 0xac, 0x4e, 0x1c,
+ 0xa8, 0xfb, 0x27, 0x30, 0xba, 0x9a, 0xf4, 0x2f,
+ 0x0a, 0xab, 0x80, 0x6a, 0xa1, 0x60, 0x74, 0xf0,
+ 0xe3, 0x91, 0x84, 0xe7, 0x90, 0x88, 0xcc, 0xf0,
+ 0x95, 0x7b, 0x0a, 0x22, 0xf2, 0xf9, 0x27, 0xe0,
+ 0xdd, 0x38, 0x0c, 0xfd, 0xe9, 0x03, 0x71, 0xdc,
+ 0x70, 0xa4, 0x6e, 0xdf, 0xe3, 0x72, 0x9e, 0xa1,
+ 0xf0, 0xc9, 0x00, 0xd6, 0x03, 0x55, 0x6a, 0x67,
+ 0x5d, 0x9c, 0xb8, 0x75, 0x01, 0xb0, 0x01, 0x9f,
+ 0xe6, 0xd2, 0x44, 0x18, 0xbc, 0xca, 0x7a, 0x10,
+ 0x39, 0xa6, 0xcf, 0x15, 0xc7, 0xf5, 0x35, 0xd4,
+ 0xb3, 0x6d, 0x91, 0x23, 0x84, 0x99, 0xba, 0xb0,
+ 0x7e, 0xd0, 0xc9, 0x4c, 0xbf, 0x3f, 0x33, 0x68,
+ 0x37, 0xb7, 0x7d, 0x44, 0xb0, 0x0b, 0x2c, 0x0f,
+ 0xd0, 0x75, 0xa2, 0x6b, 0x5b, 0xe1, 0x9f, 0xd4,
+ 0x69, 0x9a, 0x14, 0xc8, 0x29, 0xb7, 0xd9, 0x10,
+ 0xbb, 0x99, 0x30, 0x9a, 0xfb, 0xcc, 0x13, 0x1f,
+ 0x76, 0x4e, 0xe6, 0xdf, 0x14, 0xaa, 0xd5, 0x60,
+ 0xbf, 0x91, 0x49, 0x0d, 0x64, 0x42, 0x29, 0xa8,
+ 0x64, 0x27, 0xd4, 0x5e, 0x1b, 0x18, 0x03, 0xa8,
+ 0x73, 0xd6, 0x05, 0x6e, 0xf7, 0x50, 0xb0, 0x09,
+ 0x6b, 0x69, 0x7a, 0x12, 0x28, 0x58, 0xef, 0x5a,
+ 0x86, 0x11, 0xde, 0x71, 0x71, 0x9f, 0xca, 0xbd,
+ 0x79, 0x2a, 0xc2, 0xe5, 0x9b, 0x5e, 0x32, 0xe7,
+ 0xcb, 0x97, 0x6e, 0xa0, 0xea, 0xa4, 0xa4, 0x6a,
+ 0x32, 0xf9, 0x37, 0x39, 0xd8, 0x37, 0x6d, 0x63,
+ 0xf3, 0x08, 0x1c, 0xdd, 0x06, 0xdd, 0x2c, 0x2b,
+ 0x9f, 0x04, 0x88, 0x5f, 0x36, 0x42, 0xc1, 0xb1,
+ 0xc7, 0xe8, 0x2d, 0x5d, 0xa4, 0x6c, 0xe5, 0x60,
+ 0x94, 0xae, 0xd0, 0x90, 0x1e, 0x88, 0xa0, 0x87,
+ 0x52, 0xfb, 0xed, 0x97, 0xa5, 0x25, 0x5a, 0xb7,
+ 0x55, 0xc5, 0x13, 0x07, 0x85, 0x27, 0x40, 0xed,
+ 0xb8, 0xa0, 0x26, 0x13, 0x44, 0x0c, 0xfc, 0xcc,
+ 0x5a, 0x09, 0xe5, 0x44, 0xb5, 0x63, 0xa1, 0x43,
+ 0x51, 0x23, 0x4f, 0x17, 0x21, 0x89, 0x2e, 0x58,
+ 0xfd, 0xf9, 0x63, 0x74, 0x04, 0x70, 0x1e, 0x7d,
+ 0xd0, 0x66, 0xba, 0x40, 0x5e, 0x45, 0xdc, 0x39,
+ 0x7c, 0x53, 0x0f, 0xa8, 0x38, 0xb2, 0x13, 0x99,
+ 0x27, 0xd9, 0x4a, 0x51, 0xe9, 0x9f, 0x2a, 0x92,
+ 0xbb, 0x9c, 0x90, 0xab, 0xfd, 0xf1, 0xb7, 0x40,
+ 0x05, 0xa9, 0x7a, 0x20, 0x63, 0x36, 0xc1, 0xef,
+ 0xb9, 0xad, 0xa2, 0xe0, 0x1d, 0x20, 0x4f, 0xb2,
+ 0x34, 0xbd, 0xea, 0x07, 0xac, 0x21, 0xce, 0xf6,
+ 0x8a, 0xa2, 0x9e, 0xcd, 0xfa
+ };
+ uint32_t toserver_app_data_buf_len = sizeof(toserver_app_data_buf);
+
+ int result = 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ Packet *p = NULL;
+ Flow f;
+ TcpSession ssn;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ SSLState *ssl_state = NULL;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_TLS;
+
+ StreamTcpInitConfig(TRUE);
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"ssl state\"; ssl_state:client_hello; "
+ "sid:1;)");
+ if (s == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"ssl state\"; "
+ "ssl_state:client_hello | server_hello; "
+ "sid:2;)");
+ if (s == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"ssl state\"; "
+ "ssl_state:client_hello | server_hello | "
+ "client_keyx; sid:3;)");
+ if (s == NULL)
+ goto end;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"ssl state\"; "
+ "ssl_state:client_hello | server_hello | "
+ "client_keyx | server_keyx; sid:4;)");
+ if (s == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER | STREAM_START, chello_buf,
+ chello_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no ssl state: ");
+ goto end;
+ }
+
+ /* do detect */
+ p->alerts.cnt = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1))
+ goto end;
+ if (PacketAlertCheck(p, 2))
+ goto end;
+ if (PacketAlertCheck(p, 3))
+ goto end;
+ if (PacketAlertCheck(p, 4))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, shello_buf,
+ shello_buf_len);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ p->alerts.cnt = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+ if (!PacketAlertCheck(p, 2))
+ goto end;
+ if (PacketAlertCheck(p, 3))
+ goto end;
+ if (PacketAlertCheck(p, 4))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, client_change_cipher_spec_buf,
+ client_change_cipher_spec_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ p->alerts.cnt = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+ if (PacketAlertCheck(p, 2))
+ goto end;
+ if (!PacketAlertCheck(p, 3))
+ goto end;
+ if (PacketAlertCheck(p, 4))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOCLIENT, server_change_cipher_spec_buf,
+ server_change_cipher_spec_buf_len);
+ if (r != 0) {
+ printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ p->alerts.cnt = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+ if (PacketAlertCheck(p, 2))
+ goto end;
+ if (PacketAlertCheck(p, 3))
+ goto end;
+ if (PacketAlertCheck(p, 4))
+ goto end;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, toserver_app_data_buf,
+ toserver_app_data_buf_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ p->alerts.cnt = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ goto end;
+ if (PacketAlertCheck(p, 2))
+ goto end;
+ if (PacketAlertCheck(p, 3))
+ goto end;
+ if (PacketAlertCheck(p, 4))
+ goto end;
+
+ result = 1;
+
+ end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectSslStateRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectSslStateTest01", DetectSslStateTest01, 1);
+ UtRegisterTest("DetectSslStateTest02", DetectSslStateTest02, 1);
+ UtRegisterTest("DetectSslStateTest03", DetectSslStateTest03, 1);
+ UtRegisterTest("DetectSslStateTest04", DetectSslStateTest04, 1);
+ UtRegisterTest("DetectSslStateTest05", DetectSslStateTest05, 1);
+ UtRegisterTest("DetectSslStateTest06", DetectSslStateTest06, 1);
+ UtRegisterTest("DetectSslStateTest07", DetectSslStateTest07, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-ssl-state.h b/framework/src/suricata/src/detect-ssl-state.h
new file mode 100644
index 00000000..a9c69a15
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssl-state.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef DETECT_SSL_STATE_H
+#define DETECT_SSL_STATE_H
+
+#include "app-layer-ssl.h"
+
+/* we pick these flags flags from the parser */
+#define DETECT_SSL_STATE_CLIENT_HELLO SSL_AL_FLAG_STATE_CLIENT_HELLO
+#define DETECT_SSL_STATE_SERVER_HELLO SSL_AL_FLAG_STATE_SERVER_HELLO
+#define DETECT_SSL_STATE_CLIENT_KEYX SSL_AL_FLAG_STATE_CLIENT_KEYX
+#define DETECT_SSL_STATE_SERVER_KEYX SSL_AL_FLAG_STATE_SERVER_KEYX
+#define DETECT_SSL_STATE_UNKNOWN SSL_AL_FLAG_STATE_UNKNOWN
+
+typedef struct DetectSslStateData_ {
+ uint32_t flags;
+} DetectSslStateData;
+
+void DetectSslStateRegister(void);
+
+#endif /* DETECT_SSL_STATE_H */
diff --git a/framework/src/suricata/src/detect-ssl-version.c b/framework/src/suricata/src/detect-ssl-version.c
new file mode 100644
index 00000000..599d26b8
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssl-version.c
@@ -0,0 +1,804 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-ssl-version.c
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Implements the ssl_version keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "detect-ssl-version.h"
+
+#include "stream-tcp.h"
+#include "app-layer-ssl.h"
+
+/**
+ * \brief Regex for parsing "id" option, matching number or "number"
+ */
+#define PARSE_REGEX "^\\s*(!?[A-z0-9.]+)\\s*,?\\s*(!?[A-z0-9.]+)?\\s*\\,?\\s*" \
+ "(!?[A-z0-9.]+)?\\s*,?\\s*(!?[A-z0-9.]+)?\\s*,?\\s*(!?[A-z0-9.]+)?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectSslVersionMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, void *, Signature *, SigMatch *);
+static int DetectSslVersionSetup(DetectEngineCtx *, Signature *, char *);
+void DetectSslVersionRegisterTests(void);
+void DetectSslVersionFree(void *);
+
+/**
+ * \brief Registration function for keyword: ssl_version
+ */
+void DetectSslVersionRegister(void)
+{
+ sigmatch_table[DETECT_AL_SSL_VERSION].name = "ssl_version";
+ sigmatch_table[DETECT_AL_SSL_VERSION].Match = NULL;
+ sigmatch_table[DETECT_AL_SSL_VERSION].AppLayerMatch = DetectSslVersionMatch;
+ sigmatch_table[DETECT_AL_SSL_VERSION].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_SSL_VERSION].Setup = DetectSslVersionSetup;
+ sigmatch_table[DETECT_AL_SSL_VERSION].Free = DetectSslVersionFree;
+ sigmatch_table[DETECT_AL_SSL_VERSION].RegisterTests = DetectSslVersionRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering ssl_version rule option");
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief match the specified version on a ssl session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectSslVersionData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectSslVersionMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
+ Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ int ret = 0;
+ uint16_t ver = 0;
+ uint8_t sig_ver = TLS_UNKNOWN;
+
+ DetectSslVersionData *ssl = (DetectSslVersionData *)m->ctx;
+ SSLState *app_state = (SSLState *)state;
+ if (app_state == NULL) {
+ SCLogDebug("no app state, no match");
+ SCReturnInt(0);
+ }
+
+ if (flags & STREAM_TOCLIENT) {
+ SCLogDebug("server (toclient) version is 0x%02X",
+ app_state->server_connp.version);
+ ver = app_state->server_connp.version;
+ } else if (flags & STREAM_TOSERVER) {
+ SCLogDebug("client (toserver) version is 0x%02X",
+ app_state->client_connp.version);
+ ver = app_state->client_connp.version;
+ }
+
+ switch (ver) {
+ case SSL_VERSION_2:
+ if (ver == ssl->data[SSLv2].ver)
+ ret = 1;
+ sig_ver = SSLv2;
+ break;
+ case SSL_VERSION_3:
+ if (ver == ssl->data[SSLv3].ver)
+ ret = 1;
+ sig_ver = SSLv3;
+ break;
+ case TLS_VERSION_10:
+ if (ver == ssl->data[TLS10].ver)
+ ret = 1;
+ sig_ver = TLS10;
+ break;
+ case TLS_VERSION_11:
+ if (ver == ssl->data[TLS11].ver)
+ ret = 1;
+ sig_ver = TLS11;
+ break;
+ case TLS_VERSION_12:
+ if (ver == ssl->data[TLS12].ver)
+ ret = 1;
+ sig_ver = TLS12;
+ break;
+ }
+
+ if (sig_ver == TLS_UNKNOWN)
+ SCReturnInt(0);
+
+ SCReturnInt(ret ^ ((ssl->data[sig_ver].flags & DETECT_SSL_VERSION_NEGATED) ? 1 : 0));
+}
+
+/**
+ * \brief This function is used to parse ssl_version data passed via
+ * keyword: "ssl_version"
+ *
+ * \param str Pointer to the user provided options
+ *
+ * \retval ssl pointer to DetectSslVersionData on success
+ * \retval NULL on failure
+ */
+DetectSslVersionData *DetectSslVersionParse(char *str)
+{
+ DetectSslVersionData *ssl = NULL;
+ #define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret < 1 || ret > 5) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid ssl_version option");
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr[5];
+ char *orig;
+ uint8_t found = 0, neg = 0;
+ char *tmp_str;
+
+ /* We have a correct ssl_version options */
+ ssl = SCCalloc(1, sizeof (DetectSslVersionData));
+ if (unlikely(ssl == NULL))
+ goto error;
+
+ int i;
+ for (i = 1; i < ret; i++) {
+ res = pcre_get_substring((char *) str, ov, MAX_SUBSTRINGS, i, &str_ptr[i]);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ if (found == 0)
+ goto error;
+ break;
+ }
+
+ orig = SCStrdup((char*) str_ptr[i]);
+ if (unlikely(orig == NULL)) {
+ goto error;
+ }
+ tmp_str = orig;
+
+ /* Let's see if we need to scape "'s */
+ if (tmp_str[0] == '"') {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+
+ if (tmp_str[0] == '!') {
+ neg = 1;
+ tmp_str++;
+ }
+
+ if (strncasecmp("sslv2", tmp_str, 5) == 0) {
+ ssl->data[SSLv2].ver = SSL_VERSION_2;
+ if (neg == 1)
+ ssl->data[SSLv2].flags |= DETECT_SSL_VERSION_NEGATED;
+ } else if (strncasecmp("sslv3", tmp_str, 5) == 0) {
+ ssl->data[SSLv3].ver = SSL_VERSION_3;
+ if (neg == 1)
+ ssl->data[SSLv3].flags |= DETECT_SSL_VERSION_NEGATED;
+ } else if (strncasecmp("tls1.0", tmp_str, 6) == 0) {
+ ssl->data[TLS10].ver = TLS_VERSION_10;
+ if (neg == 1)
+ ssl->data[TLS10].flags |= DETECT_SSL_VERSION_NEGATED;
+ } else if (strncasecmp("tls1.1", tmp_str, 6) == 0) {
+ ssl->data[TLS11].ver = TLS_VERSION_11;
+ if (neg == 1)
+ ssl->data[TLS11].flags |= DETECT_SSL_VERSION_NEGATED;
+ } else if (strncasecmp("tls1.2", tmp_str, 6) == 0) {
+ ssl->data[TLS12].ver = TLS_VERSION_12;
+ if (neg == 1)
+ ssl->data[TLS12].flags |= DETECT_SSL_VERSION_NEGATED;
+ } else if (strcmp(tmp_str, "") == 0) {
+ SCFree(orig);
+ if (found == 0)
+ goto error;
+ break;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid value");
+ SCFree(orig);
+ goto error;
+ }
+
+ found = 1;
+ neg = 0;
+ SCFree(orig);
+ }
+ }
+
+ return ssl;
+
+error:
+ if (ssl != NULL)
+ DetectSslVersionFree(ssl);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectSslVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectSslVersionData *ssl = NULL;
+ SigMatch *sm = NULL;
+
+ ssl = DetectSslVersionParse(str);
+ if (ssl == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_SSL_VERSION;
+ sm->ctx = (void *)ssl;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ s->alproto = ALPROTO_TLS;
+ return 0;
+
+error:
+ if (ssl != NULL)
+ DetectSslVersionFree(ssl);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectSslVersionData
+ *
+ * \param id_d pointer to DetectSslVersionData
+ */
+void DetectSslVersionFree(void *ptr)
+{
+ if (ptr != NULL)
+ SCFree(ptr);
+}
+
+/**********************************Unittests***********************************/
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectSslVersionTestParse01 is a test to make sure that we parse the
+ * "ssl_version" option correctly when given valid ssl_version option
+ */
+int DetectSslVersionTestParse01(void)
+{
+ DetectSslVersionData *ssl = NULL;
+ ssl = DetectSslVersionParse("SSlv3");
+ if (ssl != NULL && ssl->data[SSLv3].ver == SSL_VERSION_3) {
+ DetectSslVersionFree(ssl);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSslVersionTestParse02 is a test to make sure that we parse the
+ * "ssl_version" option correctly when given an invalid ssl_version option
+ * it should return ssl = NULL
+ */
+int DetectSslVersionTestParse02(void)
+{
+ DetectSslVersionData *ssl = NULL;
+ ssl = DetectSslVersionParse("2.5");
+ if (ssl == NULL) {
+ DetectSslVersionFree(ssl);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectSslVersionTestParse03 is a test to make sure that we parse the
+ * "ssl_version" options correctly when given valid ssl_version options
+ */
+int DetectSslVersionTestParse03(void)
+{
+ DetectSslVersionData *ssl = NULL;
+ ssl = DetectSslVersionParse("SSlv3,tls1.0, !tls1.2");
+ if (ssl != NULL && ssl->data[SSLv3].ver == SSL_VERSION_3 &&
+ ssl->data[TLS10].ver == TLS_VERSION_10 &&
+ ssl->data[TLS12].ver == TLS_VERSION_12 &&
+ ssl->data[TLS12].flags & DETECT_SSL_VERSION_NEGATED)
+ {
+ DetectSslVersionFree(ssl);
+ return 1;
+ }
+
+ return 0;
+}
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectSslVersionTestDetect01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sslbuf1[] = { 0x16 };
+ uint32_t ssllen1 = sizeof(sslbuf1);
+ uint8_t sslbuf2[] = { 0x03 };
+ uint32_t ssllen2 = sizeof(sslbuf2);
+ uint8_t sslbuf3[] = { 0x01 };
+ uint32_t ssllen3 = sizeof(sslbuf3);
+ uint8_t sslbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x01 };
+ uint32_t ssllen4 = sizeof(sslbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tls any any -> any any (msg:\"TLS\"; ssl_version:tls1.0; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf1, ssllen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf2, ssllen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf3, ssllen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf4, ssllen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ goto end;
+ }
+
+ if (app_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16, app_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (app_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, app_state->client_connp.version);
+ goto end;
+ }
+
+ SCLogDebug("app_state is at %p, app_state->server_connp.version 0x%02X app_state->client_connp.version 0x%02X",
+ app_state, app_state->server_connp.version, app_state->client_connp.version);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectSslVersionTestDetect02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t sslbuf1[] = { 0x16 };
+ uint32_t ssllen1 = sizeof(sslbuf1);
+ uint8_t sslbuf2[] = { 0x03 };
+ uint32_t ssllen2 = sizeof(sslbuf2);
+ uint8_t sslbuf3[] = { 0x01 };
+ uint32_t ssllen3 = sizeof(sslbuf3);
+ uint8_t sslbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x02 };
+ uint32_t ssllen4 = sizeof(sslbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tls any any -> any any (msg:\"TLS\"; ssl_version:tls1.0; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf1, ssllen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf2, ssllen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf3, ssllen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf4, ssllen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ goto end;
+ }
+
+ if (app_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16, app_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (app_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, app_state->client_connp.version);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("signature 1 didn't match while it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectSslVersionTestDetect03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Flow f;
+ uint8_t sslbuf1[] = { 0x16 };
+ uint32_t ssllen1 = sizeof(sslbuf1);
+ uint8_t sslbuf2[] = { 0x03 };
+ uint32_t ssllen2 = sizeof(sslbuf2);
+ uint8_t sslbuf3[] = { 0x01 };
+ uint32_t ssllen3 = sizeof(sslbuf3);
+ uint8_t sslbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x02 };
+ uint32_t ssllen4 = sizeof(sslbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->tcph->th_seq = htonl(1000);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+ f.proto = p->proto;
+
+ StreamTcpInitConfig(TRUE);
+
+ StreamMsg *stream_msg = StreamMsgGetFromPool();
+ if (stream_msg == NULL) {
+ printf("no stream_msg: ");
+ goto end;
+ }
+
+ memcpy(stream_msg->data, sslbuf4, ssllen4);
+ stream_msg->data_len = ssllen4;
+
+ ssn.toserver_smsg_head = stream_msg;
+ ssn.toserver_smsg_tail = stream_msg;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"TLS\"; ssl_version:tls1.0; content:\"|01 00 00 AD|\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf1, ssllen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf2, ssllen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf3, ssllen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, sslbuf4, ssllen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *app_state = f.alstate;
+ if (app_state == NULL) {
+ printf("no ssl state: ");
+ goto end;
+ }
+
+ if (app_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16, app_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (app_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, app_state->client_connp.version);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("signature 1 didn't match while it should have: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectSslVersion
+ */
+void DetectSslVersionRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectSslVersionTestParse01", DetectSslVersionTestParse01, 1);
+ UtRegisterTest("DetectSslVersionTestParse02", DetectSslVersionTestParse02, 1);
+ UtRegisterTest("DetectSslVersionTestParse03", DetectSslVersionTestParse03, 1);
+ UtRegisterTest("DetectSslVersionTestDetect01", DetectSslVersionTestDetect01, 1);
+ UtRegisterTest("DetectSslVersionTestDetect02", DetectSslVersionTestDetect02, 1);
+ UtRegisterTest("DetectSslVersionTestDetect03", DetectSslVersionTestDetect03, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-ssl-version.h b/framework/src/suricata/src/detect-ssl-version.h
new file mode 100644
index 00000000..b9a0f861
--- /dev/null
+++ b/framework/src/suricata/src/detect-ssl-version.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-ssl-version.h
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ */
+
+#ifndef DETECT_SSL_VERSION_H
+#define DETECT_SSL_VERSION_H
+
+#define DETECT_SSL_VERSION_NEGATED 0x01
+
+enum {
+ SSLv2 = 0,
+ SSLv3 = 1,
+ TLS10 = 2,
+ TLS11 = 3,
+ TLS12 = 4,
+
+ TLS_SIZE = 5,
+ TLS_UNKNOWN = 6,
+};
+
+typedef struct SSLVersionData_ {
+ uint16_t ver; /** ssl version to match */
+ uint8_t flags;
+} SSLVersionData;
+
+typedef struct DetectSslVersionData_ {
+ SSLVersionData data[TLS_SIZE];
+} DetectSslVersionData;
+
+/* prototypes */
+void DetectSslVersionRegister (void);
+
+#endif /* DETECT_SSL_VERSION_H */
diff --git a/framework/src/suricata/src/detect-stream_size.c b/framework/src/suricata/src/detect-stream_size.c
new file mode 100644
index 00000000..c3eaad90
--- /dev/null
+++ b/framework/src/suricata/src/detect-stream_size.c
@@ -0,0 +1,532 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Stream size for the engine.
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp.h"
+#include "util-unittest.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow.h"
+#include "detect-stream_size.h"
+#include "stream-tcp-private.h"
+#include "util-debug.h"
+
+/**
+ * \brief Regex for parsing our flow options
+ */
+#define PARSE_REGEX "^\\s*([A-z_]+)\\s*,\\s*([<=>!]+)\\s*,\\s*([0-9]+)\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+/*prototypes*/
+int DetectStreamSizeMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, char *);
+void DetectStreamSizeFree(void *);
+void DetectStreamSizeRegisterTests(void);
+
+/**
+ * \brief Registration function for stream_size: keyword
+ */
+
+void DetectStreamSizeRegister(void)
+{
+ sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size";
+ sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream";
+ sigmatch_table[DETECT_STREAM_SIZE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flow-keywords#stream_size";
+ sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch;
+ sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup;
+ sigmatch_table[DETECT_STREAM_SIZE].Free = DetectStreamSizeFree;
+ sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ if (parse_regex != NULL) SCFree(parse_regex);
+ if (parse_regex_study != NULL) SCFree(parse_regex_study);
+ return;
+}
+
+/**
+ * \brief Function to comapre the stream size against defined size in the user
+ * options.
+ *
+ * \param diff The stream size of server or client stream.
+ * \param stream_size User defined stream size
+ * \param mode The mode defined by user.
+ *
+ * \retval 1 on success and 0 on failure.
+ */
+
+static int DetectStreamSizeCompare (uint32_t diff, uint32_t stream_size, uint8_t mode) {
+
+ int ret = 0;
+ switch (mode) {
+ case DETECTSSIZE_LT:
+ if (diff < stream_size)
+ ret = 1;
+ break;
+ case DETECTSSIZE_LEQ:
+ if (diff <= stream_size)
+ ret = 1;
+ break;
+ case DETECTSSIZE_EQ:
+ if (diff == stream_size)
+ ret = 1;
+ break;
+ case DETECTSSIZE_NEQ:
+ if (diff != stream_size)
+ ret = 1;
+ break;
+ case DETECTSSIZE_GEQ:
+ if (diff >= stream_size)
+ ret = 1;
+ break;
+ case DETECTSSIZE_GT:
+ if (diff > stream_size)
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to match Stream size rule option on a packet with those passed via stream_size:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectStreamSizeData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectStreamSizeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+
+ int ret = 0;
+ const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx;
+
+ if (!(PKT_IS_TCP(p)))
+ return ret;
+
+ uint32_t csdiff = 0;
+ uint32_t ssdiff = 0;
+
+ if (p->flow == NULL)
+ return ret;
+
+ TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+ if (ssn == NULL)
+ return ret;
+
+ if (sd->flags & STREAM_SIZE_SERVER) {
+ /* get the server stream size */
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ ret = DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode);
+
+ } else if (sd->flags & STREAM_SIZE_CLIENT) {
+ /* get the client stream size */
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+ ret = DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode);
+
+ } else if (sd->flags & STREAM_SIZE_BOTH) {
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+ if (DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode) && DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode))
+ ret = 1;
+
+ } else if (sd->flags & STREAM_SIZE_EITHER) {
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+ if (DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode) || DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode))
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse stream options passed via stream_size: keyword
+ *
+ * \param streamstr Pointer to the user provided stream_size options
+ *
+ * \retval sd pointer to DetectStreamSizeData on success
+ * \retval NULL on failure
+ */
+
+DetectStreamSizeData *DetectStreamSizeParse (char *streamstr)
+{
+
+ DetectStreamSizeData *sd = NULL;
+ char *arg = NULL;
+ char *value = NULL;
+ char *mode = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, streamstr, strlen(streamstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, streamstr);
+ goto error;
+ }
+
+ const char *str_ptr;
+
+ res = pcre_get_substring((char *)streamstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)streamstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ mode = (char *)str_ptr;
+
+ res = pcre_get_substring((char *)streamstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ value = (char *)str_ptr;
+
+ sd = SCMalloc(sizeof(DetectStreamSizeData));
+ if (unlikely(sd == NULL))
+ goto error;
+ sd->ssize = 0;
+ sd->flags = 0;
+
+ if (strlen(mode) == 0)
+ goto error;
+
+ if (mode[0] == '=') {
+ sd->mode = DETECTSSIZE_EQ;
+ } else if (mode[0] == '<') {
+ sd->mode = DETECTSSIZE_LT;
+ if (strcmp("<=", mode) == 0)
+ sd->mode = DETECTSSIZE_LEQ;
+ } else if (mode[0] == '>') {
+ sd->mode = DETECTSSIZE_GT;
+ if (strcmp(">=", mode) == 0)
+ sd->mode = DETECTSSIZE_GEQ;
+ } else if (strcmp("!=", mode) == 0) {
+ sd->mode = DETECTSSIZE_NEQ;
+ } else {
+ SCLogError(SC_ERR_INVALID_OPERATOR, "Invalid operator");
+ goto error;
+ }
+
+ /* set the value */
+ sd->ssize = (uint32_t)atoi(value);
+
+ /* inspect our options and set the flags */
+ if (strcmp(arg, "server") == 0) {
+ sd->flags |= STREAM_SIZE_SERVER;
+ } else if (strcmp(arg, "client") == 0) {
+ sd->flags |= STREAM_SIZE_CLIENT;
+ } else if ((strcmp(arg, "both") == 0)) {
+ sd->flags |= STREAM_SIZE_BOTH;
+ } else if (strcmp(arg, "either") == 0) {
+ sd->flags |= STREAM_SIZE_EITHER;
+ } else {
+ goto error;
+ }
+
+ SCFree(mode);
+ SCFree(arg);
+ SCFree(value);
+ return sd;
+
+error:
+ if (mode != NULL)
+ SCFree(mode);
+ if (arg != NULL)
+ SCFree(arg);
+ if (value != NULL)
+ SCFree(value);
+ if (sd != NULL)
+ DetectStreamSizeFree(sd);
+
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed stream size data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param streamstr pointer to the user provided stream size options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, char *streamstr)
+{
+
+ DetectStreamSizeData *sd = NULL;
+ SigMatch *sm = NULL;
+
+ sd = DetectStreamSizeParse(streamstr);
+ if (sd == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_STREAM_SIZE;
+ sm->ctx = (SigMatchCtx *)sd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+
+ return 0;
+
+error:
+ if (sd != NULL) DetectStreamSizeFree(sd);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectStreamSizeData
+ *
+ * \param ptr pointer to DetectStreamSizeData
+ */
+void DetectStreamSizeFree(void *ptr)
+{
+ DetectStreamSizeData *sd = (DetectStreamSizeData *)ptr;
+ SCFree(sd);
+}
+
+#ifdef UNITTESTS
+/**
+ * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the
+ * user options correctly, when given valid stream_size options.
+ */
+
+static int DetectStreamSizeParseTest01 (void)
+{
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ sd = DetectStreamSizeParse("server,<,6");
+ if (sd != NULL) {
+ if (sd->flags & STREAM_SIZE_SERVER && sd->mode == DETECTSSIZE_LT && sd->ssize == 6)
+ result = 1;
+ DetectStreamSizeFree(sd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the
+ * invalid stream_size options.
+ */
+
+static int DetectStreamSizeParseTest02 (void)
+{
+ int result = 1;
+ DetectStreamSizeData *sd = NULL;
+ sd = DetectStreamSizeParse("invalidoption,<,6");
+ if (sd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRId16 ": ",sd->flags, sd->ssize);
+ result = 0;
+ DetectStreamSizeFree(sd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest03 is a test to make sure that we match the
+ * packet correctly provided valid stream size.
+ */
+
+static int DetectStreamSizeParseTest03 (void)
+{
+
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+ DetectEngineThreadCtx dtx;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature s;
+ SigMatch sm;
+ TcpStream client;
+ Flow f;
+ TCPHdr tcph;
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
+ memset(&s, 0, sizeof(Signature));
+ memset(&sm, 0, sizeof(SigMatch));
+ memset(&client, 0, sizeof(TcpStream));
+ memset(&f, 0, sizeof(Flow));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ sd = DetectStreamSizeParse("client,>,8");
+ if (sd != NULL) {
+ if (!(sd->flags & STREAM_SIZE_CLIENT)) {
+ printf("sd->flags not STREAM_SIZE_CLIENT: ");
+ DetectStreamSizeFree(sd);
+ SCFree(p);
+ return 0;
+ }
+
+ if (sd->mode != DETECTSSIZE_GT) {
+ printf("sd->mode not DETECTSSIZE_GT: ");
+ DetectStreamSizeFree(sd);
+ SCFree(p);
+ return 0;
+ }
+
+ if (sd->ssize != 8) {
+ printf("sd->ssize is %"PRIu32", not 8: ", sd->ssize);
+ DetectStreamSizeFree(sd);
+ SCFree(p);
+ return 0;
+ }
+ } else {
+ printf("sd == NULL: ");
+ SCFree(p);
+ return 0;
+ }
+
+ client.next_seq = 20;
+ client.isn = 10;
+ ssn.client = client;
+ f.protoctx = &ssn;
+ p->flow = &f;
+ p->tcph = &tcph;
+ sm.ctx = (SigMatchCtx*)sd;
+
+ result = DetectStreamSizeMatch(&tv, &dtx, p, &s, sm.ctx);
+ if (result == 0) {
+ printf("result 0 != 1: ");
+ }
+ DetectStreamSizeFree(sd);
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest04 is a test to make sure that we match the
+ * stream_size against invalid packet parameters.
+ */
+
+static int DetectStreamSizeParseTest04 (void)
+{
+
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+ DetectEngineThreadCtx dtx;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature s;
+ SigMatch sm;
+ TcpStream client;
+ Flow f;
+ IPV4Hdr ip4h;
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
+ memset(&s, 0, sizeof(Signature));
+ memset(&sm, 0, sizeof(SigMatch));
+ memset(&client, 0, sizeof(TcpStream));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ sd = DetectStreamSizeParse(" client , > , 8 ");
+ if (sd != NULL) {
+ if (!(sd->flags & STREAM_SIZE_CLIENT) && sd->mode != DETECTSSIZE_GT && sd->ssize != 8) {
+ SCFree(p);
+ return 0;
+ }
+ } else
+ {
+ SCFree(p);
+ return 0;
+ }
+
+ client.next_seq = 20;
+ client.isn = 12;
+ ssn.client = client;
+ f.protoctx = &ssn;
+ p->flow = &f;
+ p->ip4h = &ip4h;
+ sm.ctx = (SigMatchCtx*)sd;
+
+ if (!DetectStreamSizeMatch(&tv, &dtx, p, &s, sm.ctx))
+ result = 1;
+
+ SCFree(p);
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectStreamSize
+ */
+void DetectStreamSizeRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01, 1);
+ UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02, 1);
+ UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03, 1);
+ UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-stream_size.h b/framework/src/suricata/src/detect-stream_size.h
new file mode 100644
index 00000000..32f5c50b
--- /dev/null
+++ b/framework/src/suricata/src/detect-stream_size.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_STREAM_SIZE_H
+#define _DETECT_STREAM_SIZE_H
+
+#define DETECTSSIZE_LT 0
+#define DETECTSSIZE_LEQ 1
+#define DETECTSSIZE_EQ 2
+#define DETECTSSIZE_NEQ 3
+#define DETECTSSIZE_GT 4
+#define DETECTSSIZE_GEQ 5
+
+#define STREAM_SIZE_SERVER 0x01
+#define STREAM_SIZE_CLIENT 0x02
+#define STREAM_SIZE_BOTH 0x04
+#define STREAM_SIZE_EITHER 0x08
+
+typedef struct DetectStreamSizeData_ {
+ uint8_t flags;
+ uint8_t mode;
+ uint32_t ssize;
+}DetectStreamSizeData;
+
+void DetectStreamSizeRegister(void);
+
+#endif /* _DETECT_STREAM_SIZE_H */
+
diff --git a/framework/src/suricata/src/detect-tag.c b/framework/src/suricata/src/detect-tag.c
new file mode 100644
index 00000000..d011a0be
--- /dev/null
+++ b/framework/src/suricata/src/detect-tag.c
@@ -0,0 +1,488 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file detect-tag.c
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the tag keyword
+ *
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-tag.h"
+#include "detect-engine-tag.h"
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "app-layer-parser.h"
+
+#include "debug.h"
+#include "decode.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+#include "stream-tcp-private.h"
+
+#include "util-time.h"
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "threads.h"
+
+SC_ATOMIC_EXTERN(unsigned int, num_tags);
+
+/* format: tag: <type>, <count>, <metric>, [direction]; */
+#define PARSE_REGEX "^\\s*(host|session)\\s*(,\\s*(\\d+)\\s*,\\s*(packets|bytes|seconds)\\s*(,\\s*(src|dst))?\\s*)?$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectTagMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectTagSetup(DetectEngineCtx *, Signature *, char *);
+void DetectTagRegisterTests(void);
+void DetectTagDataFree(void *);
+
+/**
+ * \brief Registration function for keyword tag
+ */
+void DetectTagRegister(void)
+{
+ sigmatch_table[DETECT_TAG].name = "tag";
+ sigmatch_table[DETECT_TAG].Match = DetectTagMatch;
+ sigmatch_table[DETECT_TAG].Setup = DetectTagSetup;
+ sigmatch_table[DETECT_TAG].Free = DetectTagDataFree;
+ sigmatch_table[DETECT_TAG].RegisterTests = DetectTagRegisterTests;
+ sigmatch_table[DETECT_TAG].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \brief This function is used to setup a tag for session/host
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTagData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectTagMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectTagData *td = (const DetectTagData *)ctx;
+ DetectTagDataEntry tde;
+ memset(&tde, 0, sizeof(DetectTagDataEntry));
+
+ switch (td->type) {
+ case DETECT_TAG_TYPE_HOST:
+#ifdef DEBUG
+ BUG_ON(!(td->direction == DETECT_TAG_DIR_SRC || td->direction == DETECT_TAG_DIR_DST));
+#endif
+
+ tde.sid = s->id;
+ tde.gid = s->gid;
+ tde.last_ts = tde.first_ts = p->ts.tv_sec;
+ tde.metric = td->metric;
+ tde.count = td->count;
+ if (td->direction == DETECT_TAG_DIR_SRC)
+ tde.flags |= TAG_ENTRY_FLAG_DIR_SRC;
+ else if (td->direction == DETECT_TAG_DIR_DST)
+ tde.flags |= TAG_ENTRY_FLAG_DIR_DST;
+
+ SCLogDebug("Tagging Host with sid %"PRIu32":%"PRIu32"", s->id, s->gid);
+ TagHashAddTag(&tde, p);
+ break;
+ case DETECT_TAG_TYPE_SESSION:
+ if (p->flow != NULL) {
+ SCLogDebug("Setting up tag for flow");
+ /* If it already exists it will be updated */
+ tde.sid = s->id;
+ tde.gid = s->gid;
+ tde.last_ts = tde.first_ts = p->ts.tv_sec;
+ tde.metric = td->metric;
+ tde.count = td->count;
+
+ SCLogDebug("Adding to or updating flow; first_ts %u count %u",
+ tde.first_ts, tde.count);
+ TagFlowAdd(p, &tde);
+ } else {
+ SCLogDebug("No flow to append the session tag");
+ }
+ break;
+#ifdef DEBUG
+ default:
+ SCLogDebug("unknown type of a tag keyword (not session nor host)");
+ BUG_ON(1);
+ break;
+#endif
+ }
+
+ return 1;
+}
+
+/**
+ * \brief This function is used to parse tag options passed to tag keyword
+ *
+ * \param tagstr Pointer to the user provided tag options
+ *
+ * \retval td pointer to DetectTagData on success
+ * \retval NULL on failure
+ */
+DetectTagData *DetectTagParse(char *tagstr)
+{
+ DetectTagData td;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, tagstr, strlen(tagstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, tagstr);
+ goto error;
+ }
+
+ res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* Type */
+ if (strcasecmp("session", str_ptr) == 0) {
+ td.type = DETECT_TAG_TYPE_SESSION;
+ } else if (strcasecmp("host", str_ptr) == 0) {
+ td.type = DETECT_TAG_TYPE_HOST;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument type. Must be session or host (%s)", tagstr);
+ goto error;
+ }
+ pcre_free_substring(str_ptr);
+ str_ptr = NULL;
+
+ /* default tag is 256 packets from session or dst host */
+ td.count = DETECT_TAG_MAX_PKTS;
+ td.metric = DETECT_TAG_METRIC_PACKET;
+ td.direction = DETECT_TAG_DIR_DST;
+
+ if (ret > 4) {
+ res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* count */
+ if (ByteExtractStringUint32(&td.count, 10, strlen(str_ptr),
+ str_ptr) <= 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument for count. Must be a value in the range of 0 to %"PRIu32" (%s)", UINT32_MAX, tagstr);
+ goto error;
+ }
+
+ pcre_free_substring(str_ptr);
+ str_ptr = NULL;
+
+ res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 4, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* metric */
+ if (strcasecmp("packets", str_ptr) == 0) {
+ td.metric = DETECT_TAG_METRIC_PACKET;
+ if (DETECT_TAG_MAX_PKTS > 0 && td.count > DETECT_TAG_MAX_PKTS)
+ td.count = DETECT_TAG_MAX_PKTS;
+ /* TODO: load DETECT_TAG_MAX_PKTS from config */
+ } else if (strcasecmp("seconds", str_ptr) == 0) {
+ td.metric = DETECT_TAG_METRIC_SECONDS;
+ } else if (strcasecmp("bytes", str_ptr) == 0) {
+ td.metric = DETECT_TAG_METRIC_BYTES;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument metric. Must be one of \"seconds\", \"packets\" or \"bytes\" (%s)", tagstr);
+ goto error;
+ }
+
+ pcre_free_substring(str_ptr);
+ str_ptr = NULL;
+
+ /* if specified, overwrite it */
+ if (ret == 7) {
+ res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 6, &str_ptr);
+ if (res < 0 || str_ptr == NULL) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* metric */
+ if (strcasecmp("src", str_ptr) == 0) {
+ td.direction = DETECT_TAG_DIR_SRC;
+ } else if (strcasecmp("dst", str_ptr) == 0) {
+ td.direction = DETECT_TAG_DIR_DST;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument direction. Must be one of \"src\" or \"dst\" (only valid for tag host type, not sessions) (%s)", tagstr);
+ goto error;
+ }
+
+ if (td.type != DETECT_TAG_TYPE_HOST) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "Argument direction doesn't make sense for type \"session\" (%s [%"PRIu8"])", tagstr, td.type);
+ }
+
+ pcre_free_substring(str_ptr);
+ str_ptr = NULL;
+ }
+ }
+
+ DetectTagData *real_td = SCMalloc(sizeof(DetectTagData));
+ if (unlikely(real_td == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ goto error;
+ }
+
+ memcpy(real_td, &td, sizeof(DetectTagData));
+ return real_td;
+
+error:
+ if (str_ptr != NULL)
+ pcre_free_substring(str_ptr);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to add the parsed tag data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param tagstr pointer to the user provided tag options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectTagSetup(DetectEngineCtx *de_ctx, Signature *s, char *tagstr)
+{
+ DetectTagData *td = NULL;
+ SigMatch *sm = NULL;
+
+ td = DetectTagParse(tagstr);
+ if (td == NULL) goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TAG;
+ sm->ctx = (SigMatchCtx *)td;
+
+ /* Append it to the list of tags */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_TMATCH);
+
+ return 0;
+
+error:
+ if (td != NULL) DetectTagDataFree(td);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/** \internal
+ * \brief this function will free memory associated with
+ * DetectTagDataEntry
+ *
+ * \param td pointer to DetectTagDataEntry
+ */
+static void DetectTagDataEntryFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectTagDataEntry *dte = (DetectTagDataEntry *)ptr;
+ SCFree(dte);
+ }
+}
+
+
+/**
+ * \brief this function will free all the entries of a list
+ * DetectTagDataEntry
+ *
+ * \param td pointer to DetectTagDataEntryList
+ */
+void DetectTagDataListFree(void *ptr)
+{
+ if (ptr != NULL) {
+ DetectTagDataEntry *entry = ptr;
+
+ while (entry != NULL) {
+ DetectTagDataEntry *next_entry = entry->next;
+ DetectTagDataEntryFree(entry);
+ (void) SC_ATOMIC_SUB(num_tags, 1);
+ entry = next_entry;
+ }
+ }
+}
+
+/**
+ * \brief this function will free memory associated with DetectTagData
+ *
+ * \param td pointer to DetectTagData
+ */
+void DetectTagDataFree(void *ptr)
+{
+ DetectTagData *td = (DetectTagData *)ptr;
+ SCFree(td);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test DetectTagTestParse01 is a test to make sure that we return "something"
+ * when given valid tag opt
+ */
+static int DetectTagTestParse01(void)
+{
+ int result = 0;
+ DetectTagData *td = NULL;
+ td = DetectTagParse("session, 123, packets");
+ if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
+ && td->count == 123
+ && td->metric == DETECT_TAG_METRIC_PACKET) {
+ DetectTagDataFree(td);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectTagTestParse02 is a test to check that we parse tag correctly
+ */
+static int DetectTagTestParse02(void)
+{
+ int result = 0;
+ DetectTagData *td = NULL;
+ td = DetectTagParse("host, 200, bytes, src");
+ if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
+ && td->count == 200
+ && td->metric == DETECT_TAG_METRIC_BYTES
+ && td->direction == DETECT_TAG_DIR_SRC) {
+ result = 1;
+ DetectTagDataFree(td);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectTagTestParse03 is a test for setting the stateless tag opt
+ */
+static int DetectTagTestParse03(void)
+{
+ int result = 0;
+ DetectTagData *td = NULL;
+ td = DetectTagParse("host, 200, bytes, dst");
+ if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
+ && td->count == 200
+ && td->metric == DETECT_TAG_METRIC_BYTES
+ && td->direction == DETECT_TAG_DIR_DST) {
+ result = 1;
+ DetectTagDataFree(td);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectTagTestParse04 is a test for default opts
+ */
+static int DetectTagTestParse04(void)
+{
+ int result = 0;
+ DetectTagData *td = NULL;
+ td = DetectTagParse("session");
+ if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
+ && td->count == DETECT_TAG_MAX_PKTS
+ && td->metric == DETECT_TAG_METRIC_PACKET) {
+ result = 1;
+ DetectTagDataFree(td);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectTagTestParse05 is a test for default opts
+ */
+static int DetectTagTestParse05(void)
+{
+ int result = 0;
+ DetectTagData *td = NULL;
+ td = DetectTagParse("host");
+ if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
+ && td->count == DETECT_TAG_MAX_PKTS
+ && td->metric == DETECT_TAG_METRIC_PACKET
+ && td->direction == DETECT_TAG_DIR_DST) {
+ result = 1;
+ DetectTagDataFree(td);
+ }
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectTag
+ */
+void DetectTagRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectTagTestParse01", DetectTagTestParse01, 1);
+ UtRegisterTest("DetectTagTestParse02", DetectTagTestParse02, 1);
+ UtRegisterTest("DetectTagTestParse03", DetectTagTestParse03, 1);
+ UtRegisterTest("DetectTagTestParse04", DetectTagTestParse04, 1);
+ UtRegisterTest("DetectTagTestParse05", DetectTagTestParse05, 1);
+
+ DetectEngineTagRegisterTests();
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-tag.h b/framework/src/suricata/src/detect-tag.h
new file mode 100644
index 00000000..080d36a7
--- /dev/null
+++ b/framework/src/suricata/src/detect-tag.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_TAG_H__
+#define __DETECT_TAG_H__
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-time.h"
+
+/* Limit the number of times a session can be tagged by the
+ * same rule without finishing older tags */
+#define DETECT_TAG_MATCH_LIMIT 10
+
+/* Limit the number of tags that a session can have */
+#define DETECT_TAG_MAX_TAGS 50
+
+/* Limit the number of pkts to capture. Change this to
+ * zero to make it unlimited
+ * TODO: load it from config (var tagged_packet_limit) */
+#define DETECT_TAG_MAX_PKTS 256
+
+/* Type of tag: session or host */
+enum {
+ DETECT_TAG_TYPE_SESSION,
+ DETECT_TAG_TYPE_HOST,
+ DETECT_TAG_TYPE_MAX
+};
+
+enum {
+ DETECT_TAG_DIR_SRC,
+ DETECT_TAG_DIR_DST,
+ DETECT_TAG_DIR_MAX
+};
+
+enum {
+ DETECT_TAG_METRIC_PACKET,
+ DETECT_TAG_METRIC_SECONDS,
+ DETECT_TAG_METRIC_BYTES,
+ DETECT_TAG_METRIC_MAX
+};
+
+/** This will be the rule options/parameters */
+typedef struct DetectTagData_ {
+ uint8_t type; /**< tag type */
+ uint8_t direction; /**< host direction */
+ uint32_t count; /**< count */
+ uint32_t metric; /**< metric */
+} DetectTagData;
+
+/** This is the installed data at the session/global or host table */
+typedef struct DetectTagDataEntry_ {
+ uint8_t flags:3;
+ uint8_t metric:5;
+ uint8_t pad0;
+ uint16_t cnt_match; /**< number of times this tag was reset/updated */
+
+ uint32_t count; /**< count setting from rule */
+ uint32_t sid; /**< sid originating the tag */
+ uint32_t gid; /**< gid originating the tag */
+ union {
+ uint32_t packets; /**< number of packets (metric packets) */
+ uint32_t bytes; /**< number of bytes (metric bytes) */
+ };
+ uint32_t first_ts; /**< First time seen (for metric = seconds) */
+ uint32_t last_ts; /**< Last time seen (to prune old sessions) */
+#if __WORDSIZE == 64
+ uint32_t pad1;
+#endif
+ struct DetectTagDataEntry_ *next; /**< Pointer to the next tag of this
+ * session/src_host/dst_host (if any from other rule) */
+} DetectTagDataEntry;
+
+#define TAG_ENTRY_FLAG_DIR_SRC 0x01
+#define TAG_ENTRY_FLAG_DIR_DST 0x02
+#define TAG_ENTRY_FLAG_SKIPPED_FIRST 0x04
+
+/* prototypes */
+void DetectTagRegister(void);
+void DetectTagDataFree(void *ptr);
+void DetectTagDataListFree(void *ptr);
+
+#endif /* __DETECT_TAG_H__ */
+
diff --git a/framework/src/suricata/src/detect-template.c b/framework/src/suricata/src/detect-template.c
new file mode 100644
index 00000000..ecad7880
--- /dev/null
+++ b/framework/src/suricata/src/detect-template.c
@@ -0,0 +1,303 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author XXX Yourname <youremail@yourdomain>
+ *
+ * XXX Short description of the purpose of this keyword
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "detect-template.h"
+
+/**
+ * \brief Regex for parsing our keyword options
+ */
+#define PARSE_REGEX "^\\s*([0-9]+)?\\s*,s*([0-9]+)?\\s*$"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+/* Prototypes of functions registered in DetectTemplateRegister below */
+static int DetectTemplateMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Packet *, Signature *, const SigMatchCtx *);
+static int DetectTemplateSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectTemplateFree (void *);
+static void DetectTemplateRegisterTests (void);
+
+/**
+ * \brief Registration function for template: keyword
+ *
+ * This function is called once in the 'lifetime' of the engine.
+ */
+void DetectTemplateRegister(void) {
+ /* keyword name: this is how the keyword is used in a rule */
+ sigmatch_table[DETECT_TEMPLATE].name = "template";
+ /* description: listed in "suricata --list-keywords=all" */
+ sigmatch_table[DETECT_TEMPLATE].desc = "give an introduction into how a detection module works";
+ /* link to further documentation of the keyword. Normally on the Suricata redmine/wiki */
+ sigmatch_table[DETECT_TEMPLATE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Suricata_Developers_Guide";
+ /* match function is called when the signature is inspected on a packet */
+ sigmatch_table[DETECT_TEMPLATE].Match = DetectTemplateMatch;
+ /* setup function is called during signature parsing, when the template
+ * keyword is encountered in the rule */
+ sigmatch_table[DETECT_TEMPLATE].Setup = DetectTemplateSetup;
+ /* free function is called when the detect engine is freed. Normally at
+ * shutdown, but also during rule reloads. */
+ sigmatch_table[DETECT_TEMPLATE].Free = DetectTemplateFree;
+ /* registers unittests into the system */
+ sigmatch_table[DETECT_TEMPLATE].RegisterTests = DetectTemplateRegisterTests;
+
+ /* set up the PCRE for keyword parsing */
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ if (parse_regex != NULL)
+ SCFree(parse_regex);
+ if (parse_regex_study != NULL)
+ SCFree(parse_regex_study);
+ return;
+}
+
+/**
+ * \brief This function is used to match TEMPLATE rule option on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch with context that we will cast into DetectTemplateData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectTemplateMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s, const SigMatchCtx *ctx)
+{
+ int ret = 0;
+ const DetectTemplateData *templated = (const DetectTemplateData *) ctx;
+#if 0
+ if (PKT_IS_PSEUDOPKT(p)) {
+ /* fake pkt */
+ }
+
+ if (PKT_IS_IPV4(p)) {
+ /* ipv4 pkt */
+ } else if (PKT_IS_IPV6(p)) {
+ /* ipv6 pkt */
+ } else {
+ SCLogDebug("packet is of not IPv4 or IPv6");
+ return ret;
+ }
+#endif
+ /* packet payload access */
+ if (p->payload != NULL && p->payload_len > 0) {
+ if (templated->arg1 == p->payload[0] &&
+ templated->arg2 == p->payload[p->payload_len - 1])
+ {
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse template options passed via template: keyword
+ *
+ * \param templatestr Pointer to the user provided template options
+ *
+ * \retval templated pointer to DetectTemplateData on success
+ * \retval NULL on failure
+ */
+static DetectTemplateData *DetectTemplateParse (const char *templatestr)
+{
+ DetectTemplateData *templated = NULL;
+ char arg1[4] = "";
+ char arg2[4] = "";
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study,
+ templatestr, strlen(templatestr),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *) templatestr, ov, MAX_SUBSTRINGS, 1, arg1, sizeof(arg1));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("Arg1 \"%s\"", arg1);
+
+ if (ret >= 3) {
+ res = pcre_copy_substring((char *) templatestr, ov, MAX_SUBSTRINGS, 2, arg2, sizeof(arg2));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("Arg2 \"%s\"", arg2);
+
+ }
+
+ templated = SCMalloc(sizeof (DetectTemplateData));
+ if (unlikely(templated == NULL))
+ goto error;
+ templated->arg1 = (uint8_t)atoi(arg1);
+ templated->arg2 = (uint8_t)atoi(arg2);
+
+ return templated;
+
+error:
+ if (templated)
+ SCFree(templated);
+ return NULL;
+}
+
+/**
+ * \brief parse the options from the 'template' keyword in the rule into
+ * the Signature data structure.
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param templatestr pointer to the user provided template options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTemplateSetup (DetectEngineCtx *de_ctx, Signature *s, char *templatestr)
+{
+ DetectTemplateData *templated = NULL;
+ SigMatch *sm = NULL;
+
+ templated = DetectTemplateParse(templatestr);
+ if (templated == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TEMPLATE;
+ sm->ctx = (void *)templated;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (templated != NULL)
+ DetectTemplateFree(templated);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectTemplateData
+ *
+ * \param ptr pointer to DetectTemplateData
+ */
+static void DetectTemplateFree(void *ptr) {
+ DetectTemplateData *templated = (DetectTemplateData *)ptr;
+
+ /* do more specific cleanup here, if needed */
+
+ SCFree(templated);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test description of the test
+ */
+
+static int DetectTemplateParseTest01 (void) {
+ DetectTemplateData *templated = NULL;
+ uint8_t res = 0;
+
+ templated = DetectTemplateParse("1,10");
+ if (templated != NULL) {
+ if (templated->arg1 == 1 && templated->arg2 == 10)
+ res = 1;
+
+ DetectTemplateFree(templated);
+ }
+
+ return res;
+}
+
+static int DetectTemplateSignatureTest01 (void) {
+ uint8_t res = 0;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ Signature *sig = DetectEngineAppendSig(de_ctx, "alert ip any any -> any any (template:1,10; sid:1; rev:1;)");
+ if (sig == NULL) {
+ printf("parsing signature failed: ");
+ goto end;
+ }
+
+ /* if we get here, all conditions pass */
+ res = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return res;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectTemplate
+ */
+void DetectTemplateRegisterTests(void) {
+#ifdef UNITTESTS
+ UtRegisterTest("DetectTemplateParseTest01",
+ DetectTemplateParseTest01, 1);
+ UtRegisterTest("DetectTemplateSignatureTest01",
+ DetectTemplateSignatureTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-template.h b/framework/src/suricata/src/detect-template.h
new file mode 100644
index 00000000..d86c0083
--- /dev/null
+++ b/framework/src/suricata/src/detect-template.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author XXX Yourname <youremail@yourdomain>
+ */
+
+#ifndef __DETECT_TEMPLATE_H__
+#define __DETECT_TEMPLATE_H__
+
+/** Per keyword data. This is set up by the DetectTemplateSetup() function.
+ * Each signature will have an instance of DetectTemplateData per occurence
+ * of the keyword.
+ * The structure should be considered static/readonly after initialization.
+ */
+typedef struct DetectTemplateData_ {
+ uint8_t arg1;
+ uint8_t arg2;
+} DetectTemplateData;
+
+/** \brief registers the keyword into the engine. Called from
+ * detect.c::SigTableSetup() */
+void DetectTemplateRegister(void);
+
+#endif /* __DETECT_TEMPLATE_H__ */
diff --git a/framework/src/suricata/src/detect-threshold.c b/framework/src/suricata/src/detect-threshold.c
new file mode 100644
index 00000000..236b6bf6
--- /dev/null
+++ b/framework/src/suricata/src/detect-threshold.c
@@ -0,0 +1,1525 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup threshold
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the threshold keyword.
+ *
+ * The feature depends on what is provided
+ * by detect-engine-threshold.c and util-threshold-config.c
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+
+#include "host.h"
+#include "host-storage.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+#include "decode-events.h"
+#include "stream-tcp.h"
+
+#include "detect-threshold.h"
+#include "detect-engine-threshold.h"
+#include "detect-parse.h"
+#include "detect-engine-address.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+#include "util-debug.h"
+
+#ifdef UNITTESTS
+#include "util-cpu.h"
+#endif
+
+#define PARSE_REGEX "^\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|\\d+)\\s*"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectThresholdMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectThresholdSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectThresholdFree(void *);
+
+/**
+ * \brief Registration function for threshold: keyword
+ */
+
+void DetectThresholdRegister(void)
+{
+ sigmatch_table[DETECT_THRESHOLD].name = "threshold";
+ sigmatch_table[DETECT_THRESHOLD].desc = "control the rule's alert frequency";
+ sigmatch_table[DETECT_THRESHOLD].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Rule-Thresholding#threshold";
+ sigmatch_table[DETECT_THRESHOLD].Match = DetectThresholdMatch;
+ sigmatch_table[DETECT_THRESHOLD].Setup = DetectThresholdSetup;
+ sigmatch_table[DETECT_THRESHOLD].Free = DetectThresholdFree;
+ sigmatch_table[DETECT_THRESHOLD].RegisterTests = ThresholdRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_THRESHOLD].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int opts = 0;
+ int eo;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+error:
+ return;
+
+}
+
+static int DetectThresholdMatch(ThreadVars *thv, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief This function is used to parse threshold options passed via threshold: keyword
+ *
+ * \param rawstr Pointer to the user provided threshold options
+ *
+ * \retval de pointer to DetectThresholdData on success
+ * \retval NULL on failure
+ */
+static DetectThresholdData *DetectThresholdParse(char *rawstr)
+{
+ DetectThresholdData *de = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr = NULL;
+ char *args[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+ char *copy_str = NULL, *threshold_opt = NULL;
+ int second_found = 0, count_found = 0;
+ int type_found = 0, track_found = 0;
+ int second_pos = 0, count_pos = 0;
+ uint16_t pos = 0;
+ int i = 0;
+
+ copy_str = SCStrdup(rawstr);
+ if (unlikely(copy_str == NULL)) {
+ goto error;
+ }
+
+ char *saveptr = NULL;
+ for (pos = 0, threshold_opt = strtok_r(copy_str,",", &saveptr);
+ pos < strlen(copy_str) && threshold_opt != NULL;
+ pos++, threshold_opt = strtok_r(NULL,"," , &saveptr))
+ {
+ if(strstr(threshold_opt,"count"))
+ count_found++;
+ if(strstr(threshold_opt,"second"))
+ second_found++;
+ if(strstr(threshold_opt,"type"))
+ type_found++;
+ if(strstr(threshold_opt,"track"))
+ track_found++;
+ }
+ SCFree(copy_str);
+ copy_str = NULL;
+
+ if(count_found != 1 || second_found != 1 || type_found != 1 || track_found != 1)
+ goto error;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+
+ if (ret < 5) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ memset(de,0,sizeof(DetectThresholdData));
+
+ for (i = 0; i < (ret - 1); i++) {
+
+ res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS,i + 1, &str_ptr);
+
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ args[i] = (char *)str_ptr;
+
+ if (strncasecmp(args[i],"limit",strlen("limit")) == 0)
+ de->type = TYPE_LIMIT;
+ if (strncasecmp(args[i],"both",strlen("both")) == 0)
+ de->type = TYPE_BOTH;
+ if (strncasecmp(args[i],"threshold",strlen("threshold")) == 0)
+ de->type = TYPE_THRESHOLD;
+ if (strncasecmp(args[i],"by_dst",strlen("by_dst")) == 0)
+ de->track = TRACK_DST;
+ if (strncasecmp(args[i],"by_src",strlen("by_src")) == 0)
+ de->track = TRACK_SRC;
+ if (strncasecmp(args[i],"count",strlen("count")) == 0)
+ count_pos = i+1;
+ if (strncasecmp(args[i],"seconds",strlen("seconds")) == 0)
+ second_pos = i+1;
+ }
+
+ if (args[count_pos] == NULL || args[second_pos] == NULL) {
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&de->count, 10, strlen(args[count_pos]),
+ args[count_pos]) <= 0) {
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&de->seconds, 10, strlen(args[second_pos]),
+ args[second_pos]) <= 0) {
+ goto error;
+ }
+
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+ return de;
+
+error:
+ for (i = 0; i < (ret - 1); i++){
+ if (args[i] != NULL) SCFree(args[i]);
+ }
+ if (de != NULL)
+ SCFree(de);
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief this function is used to add the parsed threshold into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rawstr pointer to the user provided threshold options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectThresholdSetup(DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectThresholdData *de = NULL;
+ SigMatch *sm = NULL;
+ SigMatch *tmpm = NULL;
+
+ /* checks if there is a previous instance of detection_filter */
+ tmpm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_MATCH]);
+ if (tmpm != NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "\"detection_filter\" and \"threshold\" are not allowed in the same rule");
+ SCReturnInt(-1);
+ }
+
+ de = DetectThresholdParse(rawstr);
+ if (de == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (SigMatchCtx *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
+
+ return 0;
+
+error:
+ if (de) SCFree(de);
+ if (sm) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief this function will free memory associated with DetectThresholdData
+ *
+ * \param de pointer to DetectThresholdData
+ */
+static void DetectThresholdFree(void *de_ptr)
+{
+ DetectThresholdData *de = (DetectThresholdData *)de_ptr;
+ if (de) {
+ DetectAddressHeadCleanup(&de->addrs);
+ SCFree(de);
+ }
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-threshold.h"
+#include "util-time.h"
+#include "util-hashlist.h"
+
+/**
+ * \test ThresholdTestParse01 is a test for a valid threshold options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int ThresholdTestParse01(void)
+{
+ DetectThresholdData *de = NULL;
+ de = DetectThresholdParse("type limit,track by_dst,count 10,seconds 60");
+ if (de && (de->type == TYPE_LIMIT) && (de->track == TRACK_DST) && (de->count == 10) && (de->seconds == 60)) {
+ DetectThresholdFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test ThresholdTestParse02 is a test for a invalid threshold options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int ThresholdTestParse02(void)
+{
+ DetectThresholdData *de = NULL;
+ de = DetectThresholdParse("type any,track by_dst,count 10,seconds 60");
+ if (de && (de->type == TYPE_LIMIT) && (de->track == TRACK_DST) && (de->count == 10) && (de->seconds == 60)) {
+ DetectThresholdFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test ThresholdTestParse03 is a test for a valid threshold options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int ThresholdTestParse03(void)
+{
+ DetectThresholdData *de = NULL;
+ de = DetectThresholdParse("track by_dst, type limit, seconds 60, count 10");
+ if (de && (de->type == TYPE_LIMIT) && (de->track == TRACK_DST) && (de->count == 10) && (de->seconds == 60)) {
+ DetectThresholdFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * \test ThresholdTestParse04 is a test for an invalid threshold options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int ThresholdTestParse04(void)
+{
+ DetectThresholdData *de = NULL;
+ de = DetectThresholdParse("count 10, track by_dst, seconds 60, type both, count 10");
+ if (de && (de->type == TYPE_BOTH) && (de->track == TRACK_DST) && (de->count == 10) && (de->seconds == 60)) {
+ DetectThresholdFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test ThresholdTestParse05 is a test for a valid threshold options in any order
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int ThresholdTestParse05(void)
+{
+ DetectThresholdData *de = NULL;
+ de = DetectThresholdParse("count 10, track by_dst, seconds 60, type both");
+ if (de && (de->type == TYPE_BOTH) && (de->track == TRACK_DST) && (de->count == 10) && (de->seconds == 60)) {
+ DetectThresholdFree(de);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * \test DetectThresholdTestSig1 is a test for checking the working of limit keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectThresholdTestSig1(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; content:\"A\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+
+ if (s->flags & SIG_FLAG_IPONLY) {
+ printf("signature is ip-only: ");
+ goto end;
+ }
+
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 1);
+ if (alerts != 1) {
+ printf("alerts %"PRIi32", expected 1: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 2) {
+ printf("alerts %"PRIi32", expected 2: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 3) {
+ printf("alerts %"PRIi32", expected 3: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 4) {
+ printf("alerts %"PRIi32", expected 4: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 5) {
+ printf("alerts %"PRIi32", expected 5: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 5) {
+ printf("alerts %"PRIi32", expected 5: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 5) {
+ printf("alerts %"PRIi32", expected 5: ", alerts);
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ if (alerts != 5) {
+ printf("alerts %"PRIi32", expected 5: ", alerts);
+ }
+
+ if(alerts == 5)
+ result = 1;
+ else
+ printf("alerts %"PRIi32", expected 5: ", alerts);
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+
+ HostShutdown();
+end:
+ return result;
+}
+
+/**
+ * \test DetectThresholdTestSig2 is a test for checking the working of threshold keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectThresholdTestSig2(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold\"; threshold: type threshold, track by_dst, count 5, seconds 60; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+
+ if (alerts == 2)
+ result = 1;
+ else
+ goto cleanup;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test DetectThresholdTestSig3 is a test for checking the working of limit keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectThresholdTestSig3(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ struct timeval ts;
+ DetectThresholdEntry *lookup_tsh = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ Host *host = HostLookupHostFromHash(&p->dst);
+ if (host == NULL) {
+ printf("host not found: ");
+ goto cleanup;
+ }
+
+ if (!(ThresholdHostHasThreshold(host))) {
+ HostRelease(host);
+ printf("host has no threshold: ");
+ goto cleanup;
+ }
+ HostRelease(host);
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ host = HostLookupHostFromHash(&p->dst);
+ if (host == NULL) {
+ printf("host not found: ");
+ goto cleanup;
+ }
+ HostRelease(host);
+
+ lookup_tsh = HostGetStorageById(host, ThresholdHostStorageId());
+ if (lookup_tsh == NULL) {
+ HostRelease(host);
+ printf("lookup_tsh is NULL: ");
+ goto cleanup;
+ }
+
+ alerts = lookup_tsh->current_count;
+
+ if (alerts == 3)
+ result = 1;
+ else {
+ printf("alerts %u != 3: ", alerts);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test DetectThresholdTestSig4 is a test for checking the working of both keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectThresholdTestSig4(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold both\"; threshold: type both, track by_dst, count 2, seconds 60; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+
+ if (alerts == 2)
+ result = 1;
+ else
+ goto cleanup;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test DetectThresholdTestSig5 is a test for checking the working of limit keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int DetectThresholdTestSig5(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit sid 1\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit sid 1000\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+
+ if(alerts == 10)
+ result = 1;
+ else {
+ printf("alerts %d != 10: ", alerts);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+static int DetectThresholdTestSig6Ticks(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit sid 1\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit sid 1000\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+
+ ticks_start = UtilCpuGetTicks();
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 1);
+ alerts += PacketAlertCheck(p, 1000);
+ ticks_end = UtilCpuGetTicks();
+ printf("test run %"PRIu64"\n", (ticks_end - ticks_start));
+
+ if(alerts == 10)
+ result = 1;
+ else
+ goto cleanup;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig7(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type limit, track by_src, count 1, seconds 300; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 1 && drops == 6)
+ result = 1;
+ else {
+ if (alerts != 1)
+ printf("alerts: %d != 1: ", alerts);
+ if (drops != 6)
+ printf("drops: %d != 6: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig8(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type limit, track by_src, count 2, seconds 300; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 2 && drops == 6)
+ result = 1;
+ else {
+ if (alerts != 1)
+ printf("alerts: %d != 1: ", alerts);
+ if (drops != 6)
+ printf("drops: %d != 6: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig9(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type threshold, track by_src, count 3, seconds 100; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 2 && drops == 2)
+ result = 1;
+ else {
+ if (alerts != 2)
+ printf("alerts: %d != 2: ", alerts);
+ if (drops != 2)
+ printf("drops: %d != 2: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig10(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type threshold, track by_src, count 5, seconds 300; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 1 && drops == 1)
+ result = 1;
+ else {
+ if (alerts != 1)
+ printf("alerts: %d != 1: ", alerts);
+ if (drops != 1)
+ printf("drops: %d != 1: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig11(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type both, track by_src, count 3, seconds 300; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 1 && drops == 4)
+ result = 1;
+ else {
+ if (alerts != 1)
+ printf("alerts: %d != 1: ", alerts);
+ if (drops != 4)
+ printf("drops: %d != 4: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Test drop action being set even if thresholded
+ */
+static int DetectThresholdTestSig12(void)
+{
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ int alerts = 0;
+ int drops = 0;
+ struct timeval ts;
+
+ HostInitConfig(HOST_QUIET);
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any 80 (threshold: type both, track by_src, count 5, seconds 300; sid:10;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ TimeSetIncrementTime(200);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ drops += ((PACKET_TEST_ACTION(p, ACTION_DROP))?1:0);
+ p->action = 0;
+
+ if (alerts == 1 && drops == 2)
+ result = 1;
+ else {
+ if (alerts != 1)
+ printf("alerts: %d != 1: ", alerts);
+ if (drops != 2)
+ printf("drops: %d != 2: ", drops);
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void*)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ HostShutdown();
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void ThresholdRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ThresholdTestParse01", ThresholdTestParse01, 1);
+ UtRegisterTest("ThresholdTestParse02", ThresholdTestParse02, 0);
+ UtRegisterTest("ThresholdTestParse03", ThresholdTestParse03, 1);
+ UtRegisterTest("ThresholdTestParse04", ThresholdTestParse04, 0);
+ UtRegisterTest("ThresholdTestParse05", ThresholdTestParse05, 1);
+ UtRegisterTest("DetectThresholdTestSig1", DetectThresholdTestSig1, 1);
+ UtRegisterTest("DetectThresholdTestSig2", DetectThresholdTestSig2, 1);
+ UtRegisterTest("DetectThresholdTestSig3", DetectThresholdTestSig3, 1);
+ UtRegisterTest("DetectThresholdTestSig4", DetectThresholdTestSig4, 1);
+ UtRegisterTest("DetectThresholdTestSig5", DetectThresholdTestSig5, 1);
+ UtRegisterTest("DetectThresholdTestSig6Ticks", DetectThresholdTestSig6Ticks, 1);
+ UtRegisterTest("DetectThresholdTestSig7", DetectThresholdTestSig7, 1);
+ UtRegisterTest("DetectThresholdTestSig8", DetectThresholdTestSig8, 1);
+ UtRegisterTest("DetectThresholdTestSig9", DetectThresholdTestSig9, 1);
+ UtRegisterTest("DetectThresholdTestSig10", DetectThresholdTestSig10, 1);
+ UtRegisterTest("DetectThresholdTestSig11", DetectThresholdTestSig11, 1);
+ UtRegisterTest("DetectThresholdTestSig12", DetectThresholdTestSig12, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/detect-threshold.h b/framework/src/suricata/src/detect-threshold.h
new file mode 100644
index 00000000..50e1d270
--- /dev/null
+++ b/framework/src/suricata/src/detect-threshold.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva <breno.silva@gmail.com>
+ */
+
+#ifndef __DETECT_THRESHOLD_H__
+#define __DETECT_THRESHOLD_H__
+
+#include "decode-events.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+
+#define TYPE_LIMIT 1
+#define TYPE_BOTH 2
+#define TYPE_THRESHOLD 3
+#define TYPE_DETECTION 4
+#define TYPE_RATE 5
+#define TYPE_SUPPRESS 6
+
+#define TRACK_DST 1
+#define TRACK_SRC 2
+#define TRACK_RULE 3
+#define TRACK_EITHER 4 /**< either src or dst: only used by suppress */
+
+/* Get the new action to take */
+#define TH_ACTION_ALERT 0x01
+#define TH_ACTION_DROP 0x02
+#define TH_ACTION_PASS 0x04
+#define TH_ACTION_LOG 0x08
+#define TH_ACTION_SDROP 0x10
+#define TH_ACTION_REJECT 0x20
+
+/**
+ * \typedef DetectThresholdData
+ * A typedef for DetectThresholdData_
+ */
+
+typedef struct DetectThresholdData_ {
+ uint32_t count; /**< Event count */
+ uint32_t seconds; /**< Event seconds */
+ uint8_t type; /**< Threshold type : limit , threshold, both, detection_filter */
+ uint8_t track; /**< Track type: by_src, by_dst */
+ uint8_t new_action; /**< new_action alert|drop|pass|log|sdrop|reject */
+ uint32_t timeout; /**< timeout */
+ uint32_t flags; /**< flags used to set option */
+ DetectAddressHead addrs;
+} DetectThresholdData;
+
+typedef struct DetectThresholdEntry_ {
+ uint32_t sid; /**< Signature id */
+ uint32_t gid; /**< Signature group id */
+
+ uint32_t tv_timeout; /**< Timeout for new_action (for rate_filter)
+ its not "seconds", that define the time interval */
+ uint32_t seconds; /**< Event seconds */
+ uint32_t tv_sec1; /**< Var for time control */
+ uint32_t tv_usec1; /**< Var for time control */
+ uint32_t current_count; /**< Var for count control */
+ int track; /**< Track type: by_src, by_src */
+
+ struct DetectThresholdEntry_ *next;
+} DetectThresholdEntry;
+
+
+/**
+ * Registration function for threshold: keyword
+ */
+
+void DetectThresholdRegister(void);
+
+/**
+ * This function registers unit tests for Threshold
+ */
+
+void ThresholdRegisterTests(void);
+
+#endif /*__DETECT_THRESHOLD_H__ */
diff --git a/framework/src/suricata/src/detect-tls-version.c b/framework/src/suricata/src/detect-tls-version.c
new file mode 100644
index 00000000..51547260
--- /dev/null
+++ b/framework/src/suricata/src/detect-tls-version.c
@@ -0,0 +1,722 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the tls.version keyword
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-ssl.h"
+#include "detect-tls-version.h"
+
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing "id" option, matching number or "number"
+ */
+#define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9\\.]+\")\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectTlsVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectTlsVersionSetup (DetectEngineCtx *, Signature *, char *);
+void DetectTlsVersionRegisterTests(void);
+void DetectTlsVersionFree(void *);
+
+/**
+ * \brief Registration function for keyword: tls.version
+ */
+void DetectTlsVersionRegister (void)
+{
+ sigmatch_table[DETECT_AL_TLS_VERSION].name = "tls.version";
+ sigmatch_table[DETECT_AL_TLS_VERSION].desc = "match on TLS/SSL version";
+ sigmatch_table[DETECT_AL_TLS_VERSION].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/TLS-keywords#tlsversion";
+ sigmatch_table[DETECT_AL_TLS_VERSION].Match = NULL;
+ sigmatch_table[DETECT_AL_TLS_VERSION].AppLayerMatch = DetectTlsVersionMatch;
+ sigmatch_table[DETECT_AL_TLS_VERSION].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_TLS_VERSION].Setup = DetectTlsVersionSetup;
+ sigmatch_table[DETECT_AL_TLS_VERSION].Free = DetectTlsVersionFree;
+ sigmatch_table[DETECT_AL_TLS_VERSION].RegisterTests = DetectTlsVersionRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering tls.version rule option");
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief match the specified version on a tls session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTlsVersionData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectTlsVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectTlsVersionData *tls_data = (DetectTlsVersionData *)m->ctx;
+ SSLState *ssl_state = (SSLState *)state;
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+ SCLogDebug("looking for tls_data->ver 0x%02X (flags 0x%02X)", tls_data->ver, flags);
+
+ if (flags & STREAM_TOCLIENT) {
+ SCLogDebug("server (toclient) version is 0x%02X", ssl_state->server_connp.version);
+ if (tls_data->ver == ssl_state->server_connp.version)
+ ret = 1;
+ } else if (flags & STREAM_TOSERVER) {
+ SCLogDebug("client (toserver) version is 0x%02X", ssl_state->client_connp.version);
+ if (tls_data->ver == ssl_state->client_connp.version)
+ ret = 1;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectTlsVersionData on success
+ * \retval NULL on failure
+ */
+DetectTlsVersionData *DetectTlsVersionParse (char *str)
+{
+ uint16_t temp;
+ DetectTlsVersionData *tls = NULL;
+ #define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret < 1 || ret > 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.version option");
+ goto error;
+ }
+
+ if (ret > 1) {
+ const char *str_ptr;
+ char *orig;
+ char *tmp_str;
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ tls = SCMalloc(sizeof(DetectTlsVersionData));
+ if (unlikely(tls == NULL))
+ goto error;
+
+ orig = SCStrdup((char*)str_ptr);
+ if (unlikely(orig == NULL)) {
+ goto error;
+ }
+ tmp_str=orig;
+
+ /* Let's see if we need to scape "'s */
+ if (tmp_str[0] == '"')
+ {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+ if (strcmp("1.0", tmp_str) == 0) {
+ temp = TLS_VERSION_10;
+ } else if (strcmp("1.1", tmp_str) == 0) {
+ temp = TLS_VERSION_11;
+ } else if (strcmp("1.2", tmp_str) == 0) {
+ temp = TLS_VERSION_12;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid value");
+ goto error;
+ }
+
+ tls->ver = temp;
+
+ SCFree(orig);
+
+ SCLogDebug("will look for tls %"PRIu8"", tls->ver);
+ }
+
+ return tls;
+
+error:
+ if (tls != NULL)
+ DetectTlsVersionFree(tls);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTlsVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectTlsVersionData *tls = NULL;
+ SigMatch *sm = NULL;
+
+ tls = DetectTlsVersionParse(str);
+ if (tls == NULL) goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_TLS_VERSION;
+ sm->ctx = (void *)tls;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ s->alproto = ALPROTO_TLS;
+ return 0;
+
+error:
+ if (tls != NULL) DetectTlsVersionFree(tls);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectTlsVersionData
+ *
+ * \param id_d pointer to DetectTlsVersionData
+ */
+void DetectTlsVersionFree(void *ptr)
+{
+ DetectTlsVersionData *id_d = (DetectTlsVersionData *)ptr;
+ SCFree(id_d);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectTlsVersionTestParse01 is a test to make sure that we parse the "id"
+ * option correctly when given valid id option
+ */
+int DetectTlsVersionTestParse01 (void)
+{
+ DetectTlsVersionData *tls = NULL;
+ tls = DetectTlsVersionParse("1.0");
+ if (tls != NULL && tls->ver == TLS_VERSION_10) {
+ DetectTlsVersionFree(tls);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \test DetectTlsVersionTestParse02 is a test to make sure that we parse the "id"
+ * option correctly when given an invalid id option
+ * it should return id_d = NULL
+ */
+int DetectTlsVersionTestParse02 (void)
+{
+ DetectTlsVersionData *tls = NULL;
+ tls = DetectTlsVersionParse("2.5");
+ if (tls == NULL) {
+ DetectTlsVersionFree(tls);
+ return 1;
+ }
+
+ return 0;
+}
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Send a get request in three chunks + more data. */
+static int DetectTlsVersionTestDetect01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ uint8_t tlsbuf3[] = { 0x01 };
+ uint32_t tlslen3 = sizeof(tlsbuf3);
+ uint8_t tlsbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x01 };
+ uint32_t tlslen4 = sizeof(tlsbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tls any any -> any any (msg:\"TLS\"; tls.version:1.0; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf4, tlslen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ 0x16, ssl_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ goto end;
+ }
+
+ SCLogDebug("ssl_state is at %p, ssl_state->server_version 0x%02X "
+ "ssl_state->client_version 0x%02X",
+ ssl_state, ssl_state->server_connp.version,
+ ssl_state->client_connp.version);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectTlsVersionTestDetect02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ uint8_t tlsbuf3[] = { 0x01 };
+ uint32_t tlslen3 = sizeof(tlsbuf3);
+ uint8_t tlsbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x02 };
+ uint32_t tlslen4 = sizeof(tlsbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tls any any -> any any (msg:\"TLS\"; tls.version:1.0; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf4, tlslen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ 0x16, ssl_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("signature 1 didn't match while it should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+static int DetectTlsVersionTestDetect03(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Flow f;
+ uint8_t tlsbuf1[] = { 0x16 };
+ uint32_t tlslen1 = sizeof(tlsbuf1);
+ uint8_t tlsbuf2[] = { 0x03 };
+ uint32_t tlslen2 = sizeof(tlsbuf2);
+ uint8_t tlsbuf3[] = { 0x01 };
+ uint32_t tlslen3 = sizeof(tlsbuf3);
+ uint8_t tlsbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x02 };
+ uint32_t tlslen4 = sizeof(tlsbuf4);
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p->tcph->th_seq = htonl(1000);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_TLS;
+ f.proto = p->proto;
+
+ StreamTcpInitConfig(TRUE);
+
+ StreamMsg *stream_msg = StreamMsgGetFromPool();
+ if (stream_msg == NULL) {
+ printf("no stream_msg: ");
+ goto end;
+ }
+
+ memcpy(stream_msg->data, tlsbuf4, tlslen4);
+ stream_msg->data_len = tlslen4;
+
+ ssn.toserver_smsg_head = stream_msg;
+ ssn.toserver_smsg_tail = stream_msg;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"TLS\"; tls.version:1.0; content:\"|01 00 00 AD|\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf4, tlslen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SSLState *ssl_state = f.alstate;
+ if (ssl_state == NULL) {
+ printf("no tls state: ");
+ goto end;
+ }
+
+ if (ssl_state->client_connp.content_type != 0x16) {
+ printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ",
+ 0x16, ssl_state->client_connp.content_type);
+ goto end;
+ }
+
+ if (ssl_state->client_connp.version != TLS_VERSION_10) {
+ printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ",
+ TLS_VERSION_10, ssl_state->client_connp.version);
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("signature 1 didn't match while it should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectTlsVersion
+ */
+void DetectTlsVersionRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectTlsVersionTestParse01", DetectTlsVersionTestParse01, 1);
+ UtRegisterTest("DetectTlsVersionTestParse02", DetectTlsVersionTestParse02, 1);
+ UtRegisterTest("DetectTlsVersionTestDetect01", DetectTlsVersionTestDetect01, 1);
+ UtRegisterTest("DetectTlsVersionTestDetect02", DetectTlsVersionTestDetect02, 1);
+ UtRegisterTest("DetectTlsVersionTestDetect03", DetectTlsVersionTestDetect03, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect-tls-version.h b/framework/src/suricata/src/detect-tls-version.h
new file mode 100644
index 00000000..c4dd1692
--- /dev/null
+++ b/framework/src/suricata/src/detect-tls-version.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_TLS_VERSION_H__
+#define __DETECT_TLS_VERSION_H__
+
+typedef struct DetectTlsVersionData_ {
+ uint16_t ver; /** tls version to match */
+} DetectTlsVersionData;
+
+/* prototypes */
+void DetectTlsVersionRegister (void);
+
+#endif /* __DETECT_TLS_VERSION_H__ */
+
diff --git a/framework/src/suricata/src/detect-tls.c b/framework/src/suricata/src/detect-tls.c
new file mode 100644
index 00000000..9b1d237b
--- /dev/null
+++ b/framework/src/suricata/src/detect-tls.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ * Implements the tls.* keywords
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "app-layer.h"
+
+#include "app-layer-ssl.h"
+#include "detect-tls.h"
+
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing "id" option, matching number or "number"
+ */
+
+#define PARSE_REGEX "^\\s*(\\!*)\\s*([A-z0-9\\s\\-\\.=,\\*@]+|\"[A-z0-9\\s\\-\\.=,\\*@]+\")\\s*$"
+#define PARSE_REGEX_FINGERPRINT "^\\s*(\\!*)\\s*([A-z0-9\\:\\*]+|\"[A-z0-9\\:\\* ]+\")\\s*$"
+
+static pcre *subject_parse_regex;
+static pcre_extra *subject_parse_regex_study;
+static pcre *issuerdn_parse_regex;
+static pcre_extra *issuerdn_parse_regex_study;
+static pcre *fingerprint_parse_regex;
+static pcre_extra *fingerprint_parse_regex_study;
+
+static int DetectTlsSubjectMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectTlsSubjectSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectTlsSubjectRegisterTests(void);
+static void DetectTlsSubjectFree(void *);
+static int DetectTlsIssuerDNMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectTlsIssuerDNSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectTlsIssuerDNRegisterTests(void);
+static void DetectTlsIssuerDNFree(void *);
+static int DetectTlsFingerprintMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+static int DetectTlsFingerprintSetup (DetectEngineCtx *, Signature *, char *);
+static void DetectTlsFingerprintFree(void *);
+static int DetectTlsStoreSetup (DetectEngineCtx *, Signature *, char *);
+static int DetectTlsStoreMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
+
+/**
+ * \brief Registration function for keyword: tls.version
+ */
+void DetectTlsRegister (void)
+{
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].name = "tls.subject";
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].desc = "match TLS/SSL certificate Subject field";
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/TLS-keywords#tlssubject";
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].Match = NULL;
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].AppLayerMatch = DetectTlsSubjectMatch;
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].Setup = DetectTlsSubjectSetup;
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].Free = DetectTlsSubjectFree;
+ sigmatch_table[DETECT_AL_TLS_SUBJECT].RegisterTests = DetectTlsSubjectRegisterTests;
+
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].name = "tls.issuerdn";
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].desc = "match TLS/SSL certificate IssuerDN field";
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/TLS-keywords#tlsissuerdn";
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].Match = NULL;
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].AppLayerMatch = DetectTlsIssuerDNMatch;
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].Setup = DetectTlsIssuerDNSetup;
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].Free = DetectTlsIssuerDNFree;
+ sigmatch_table[DETECT_AL_TLS_ISSUERDN].RegisterTests = DetectTlsIssuerDNRegisterTests;
+
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].name = "tls.fingerprint";
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].desc = "match TLS/SSL certificate SHA1 fingerprint";
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/TLS-keywords#tlsfingerprint";
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Match = NULL;
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].AppLayerMatch = DetectTlsFingerprintMatch;
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Setup = DetectTlsFingerprintSetup;
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Free = DetectTlsFingerprintFree;
+ sigmatch_table[DETECT_AL_TLS_FINGERPRINT].RegisterTests = NULL;
+
+ sigmatch_table[DETECT_AL_TLS_STORE].name = "tls.store";
+ sigmatch_table[DETECT_AL_TLS_STORE].desc = "store TLS/SSL certificate on disk";
+ sigmatch_table[DETECT_AL_TLS_STORE].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/TLS-keywords#tlsstore";
+ sigmatch_table[DETECT_AL_TLS_STORE].Match = NULL;
+ sigmatch_table[DETECT_AL_TLS_STORE].AppLayerMatch = DetectTlsStoreMatch;
+ sigmatch_table[DETECT_AL_TLS_STORE].alproto = ALPROTO_TLS;
+ sigmatch_table[DETECT_AL_TLS_STORE].Setup = DetectTlsStoreSetup;
+ sigmatch_table[DETECT_AL_TLS_STORE].Free = NULL;
+ sigmatch_table[DETECT_AL_TLS_STORE].RegisterTests = NULL;
+ sigmatch_table[DETECT_AL_TLS_STORE].flags |= SIGMATCH_NOOPT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ SCLogDebug("registering tls.subject rule option");
+
+ subject_parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (subject_parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ subject_parse_regex_study = pcre_study(subject_parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ SCLogDebug("registering tls.issuerdn rule option");
+
+ issuerdn_parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (issuerdn_parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ issuerdn_parse_regex_study = pcre_study(issuerdn_parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ SCLogDebug("registering tls.fingerprint rule option");
+
+ fingerprint_parse_regex = pcre_compile(PARSE_REGEX_FINGERPRINT, opts, &eb, &eo, NULL);
+ if (fingerprint_parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX_FINGERPRINT, eo, eb);
+ goto error;
+ }
+
+ fingerprint_parse_regex_study = pcre_study(fingerprint_parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief match the specified Subject on a tls session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTlsData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectTlsSubjectMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectTlsData *tls_data = (DetectTlsData *)m->ctx;
+ SSLState *ssl_state = (SSLState *)state;
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+
+ SSLStateConnp *connp = NULL;
+ if (flags & STREAM_TOSERVER) {
+ connp = &ssl_state->client_connp;
+ } else {
+ connp = &ssl_state->server_connp;
+ }
+
+ if (connp->cert0_subject != NULL) {
+ SCLogDebug("TLS: Subject is [%s], looking for [%s]\n",
+ connp->cert0_subject, tls_data->subject);
+
+ if (strstr(connp->cert0_subject, tls_data->subject) != NULL) {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ } else {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ }
+ } else {
+ ret = 0;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectTlsData on success
+ * \retval NULL on failure
+ */
+static DetectTlsData *DetectTlsSubjectParse (char *str)
+{
+ DetectTlsData *tls = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr;
+ char *orig = NULL;
+ char *tmp_str;
+ uint32_t flag = 0;
+
+ ret = pcre_exec(subject_parse_regex, subject_parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.subject option");
+ goto error;
+ }
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ if (str_ptr[0] == '!')
+ flag = DETECT_CONTENT_NEGATED;
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ tls = SCMalloc(sizeof(DetectTlsData));
+ if (unlikely(tls == NULL))
+ goto error;
+ tls->subject = NULL;
+ tls->flags = flag;
+
+ orig = SCStrdup((char*)str_ptr);
+ if (unlikely(orig == NULL)) {
+ goto error;
+ }
+ tmp_str=orig;
+
+ /* Let's see if we need to escape "'s */
+ if (tmp_str[0] == '"') {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+ tls->subject = SCStrdup(tmp_str);
+ if (unlikely(tls->subject == NULL)) {
+ goto error;
+ }
+
+ SCFree(orig);
+
+ SCLogDebug("will look for TLS subject %s", tls->subject);
+
+ return tls;
+
+error:
+ if (orig != NULL)
+ SCFree(orig);
+ if (tls != NULL)
+ DetectTlsSubjectFree(tls);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTlsSubjectSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectTlsData *tls = NULL;
+ SigMatch *sm = NULL;
+
+ tls = DetectTlsSubjectParse(str);
+ if (tls == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_TLS_SUBJECT;
+ sm->ctx = (void *)tls;
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_TLS;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (tls != NULL)
+ DetectTlsSubjectFree(tls);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectTlsData
+ *
+ * \param id_d pointer to DetectTlsData
+ */
+static void DetectTlsSubjectFree(void *ptr)
+{
+ DetectTlsData *id_d = (DetectTlsData *)ptr;
+ if (ptr == NULL)
+ return;
+ if (id_d->subject != NULL)
+ SCFree(id_d->subject);
+ SCFree(id_d);
+}
+
+/**
+ * \brief this function registers unit tests for DetectTlsSubject
+ */
+static void DetectTlsSubjectRegisterTests(void)
+{
+}
+
+/**
+ * \brief match the specified IssuerDN on a tls session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTlsData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectTlsIssuerDNMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ DetectTlsData *tls_data = (DetectTlsData *)m->ctx;
+ SSLState *ssl_state = (SSLState *)state;
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+
+ SSLStateConnp *connp = NULL;
+ if (flags & STREAM_TOSERVER) {
+ connp = &ssl_state->client_connp;
+ } else {
+ connp = &ssl_state->server_connp;
+ }
+
+ if (connp->cert0_issuerdn != NULL) {
+ SCLogDebug("TLS: IssuerDN is [%s], looking for [%s]\n",
+ connp->cert0_issuerdn, tls_data->issuerdn);
+
+ if (strstr(connp->cert0_issuerdn, tls_data->issuerdn) != NULL) {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ } else {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ }
+ } else {
+ ret = 0;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
+ *
+ * \param idstr Pointer to the user provided id option
+ *
+ * \retval id_d pointer to DetectTlsData on success
+ * \retval NULL on failure
+ */
+static DetectTlsData *DetectTlsIssuerDNParse(char *str)
+{
+ DetectTlsData *tls = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr;
+ char *orig = NULL;
+ char *tmp_str;
+ uint32_t flag = 0;
+
+ ret = pcre_exec(issuerdn_parse_regex, issuerdn_parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.issuerdn option");
+ goto error;
+ }
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ if (str_ptr[0] == '!')
+ flag = DETECT_CONTENT_NEGATED;
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ tls = SCMalloc(sizeof(DetectTlsData));
+ if (unlikely(tls == NULL))
+ goto error;
+ tls->issuerdn = NULL;
+ tls->flags = flag;
+
+ orig = SCStrdup((char*)str_ptr);
+ if (unlikely(orig == NULL)) {
+ goto error;
+ }
+ tmp_str=orig;
+
+ /* Let's see if we need to escape "'s */
+ if (tmp_str[0] == '"')
+ {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+ tls->issuerdn = SCStrdup(tmp_str);
+ if (unlikely(tls->issuerdn == NULL)) {
+ goto error;
+ }
+
+ SCFree(orig);
+
+ SCLogDebug("Will look for TLS issuerdn %s", tls->issuerdn);
+
+ return tls;
+
+error:
+ if (orig != NULL)
+ SCFree(orig);
+ if (tls != NULL)
+ DetectTlsIssuerDNFree(tls);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed "id" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "id" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTlsIssuerDNSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectTlsData *tls = NULL;
+ SigMatch *sm = NULL;
+
+ tls = DetectTlsIssuerDNParse(str);
+ if (tls == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_TLS_ISSUERDN;
+ sm->ctx = (void *)tls;
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_TLS;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (tls != NULL)
+ DetectTlsIssuerDNFree(tls);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectTlsData
+ *
+ * \param id_d pointer to DetectTlsData
+ */
+static void DetectTlsIssuerDNFree(void *ptr)
+{
+ DetectTlsData *id_d = (DetectTlsData *)ptr;
+ SCFree(id_d->issuerdn);
+ SCFree(id_d);
+}
+
+/**
+ * \brief This function is used to parse fingerprint passed via keyword: "fingerprint"
+ *
+ * \param idstr Pointer to the user provided fingerprint option
+ *
+ * \retval pointer to DetectTlsData on success
+ * \retval NULL on failure
+ */
+static DetectTlsData *DetectTlsFingerprintParse (char *str)
+{
+ DetectTlsData *tls = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ const char *str_ptr;
+ char *orig;
+ char *tmp_str;
+ uint32_t flag = 0;
+
+ ret = pcre_exec(fingerprint_parse_regex, fingerprint_parse_regex_study, str, strlen(str), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret != 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.fingerprint option");
+ goto error;
+ }
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ if (str_ptr[0] == '!')
+ flag = DETECT_CONTENT_NEGATED;
+
+ res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* We have a correct id option */
+ tls = SCMalloc(sizeof(DetectTlsData));
+ if (unlikely(tls == NULL))
+ goto error;
+ tls->fingerprint = NULL;
+ tls->flags = flag;
+
+ orig = SCStrdup((char*)str_ptr);
+ if (unlikely(orig == NULL)) {
+ goto error;
+ }
+ tmp_str=orig;
+
+ /* Let's see if we need to escape "'s */
+ if (tmp_str[0] == '"')
+ {
+ tmp_str[strlen(tmp_str) - 1] = '\0';
+ tmp_str += 1;
+ }
+
+ tls->fingerprint = SCStrdup(tmp_str);
+ if (tls->fingerprint == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate fingerprint");
+ }
+
+ SCFree(orig);
+
+ SCLogDebug("will look for TLS fingerprint %s", tls->fingerprint);
+
+ return tls;
+
+error:
+ if (tls != NULL)
+ DetectTlsFingerprintFree(tls);
+ return NULL;
+
+}
+/**
+ * \brief match the specified fingerprint on a tls session
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTlsData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectTlsFingerprintMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+ DetectTlsData *tls_data = (DetectTlsData *)m->ctx;
+ SSLState *ssl_state = (SSLState *)state;
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, no match");
+ SCReturnInt(0);
+ }
+
+ int ret = 0;
+
+ if (ssl_state->server_connp.cert0_fingerprint != NULL) {
+ SCLogDebug("TLS: Fingerprint is [%s], looking for [%s]\n",
+ ssl_state->server_connp.cert0_fingerprint,
+ tls_data->fingerprint);
+
+ if (tls_data->fingerprint &&
+ (strstr(ssl_state->server_connp.cert0_fingerprint,
+ tls_data->fingerprint) != NULL)) {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 0;
+ } else {
+ ret = 1;
+
+ }
+ } else {
+ if (tls_data->flags & DETECT_CONTENT_NEGATED) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ }
+ } else {
+ ret = 0;
+ }
+
+ SCReturnInt(ret);
+}
+
+/**
+ * \brief this function is used to add the parsed "fingerprint" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param id pointer to the user provided "fingerprint" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTlsFingerprintSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ DetectTlsData *tls = NULL;
+ SigMatch *sm = NULL;
+
+ tls = DetectTlsFingerprintParse(str);
+ if (tls == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_TLS_FINGERPRINT;
+ sm->ctx = (void *)tls;
+
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_TLS;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (tls != NULL)
+ DetectTlsFingerprintFree(tls);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectTlsData
+ *
+ * \param pointer to DetectTlsData
+ */
+static void DetectTlsFingerprintFree(void *ptr)
+{
+ DetectTlsData *id_d = (DetectTlsData *)ptr;
+ if (id_d->fingerprint)
+ SCFree(id_d->fingerprint);
+ SCFree(id_d);
+}
+
+/**
+ * \brief this function is used to add the parsed "store" option
+ * \brief into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param idstr pointer to the user provided "store" option
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTlsStoreSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
+{
+ SigMatch *sm = NULL;
+
+ s->flags |= SIG_FLAG_TLSSTORE;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
+ goto error;
+ }
+
+ sm->type = DETECT_AL_TLS_STORE;
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_TLS;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
+
+ return 0;
+
+error:
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+
+}
+
+/** \warning modifies state */
+static int DetectTlsStoreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
+{
+ SCEnter();
+
+ SSLState *ssl_state = (SSLState *)state;
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, no match");
+ SCReturnInt(1);
+ }
+
+ if (s->flags & SIG_FLAG_TLSSTORE) {
+ ssl_state->server_connp.cert_log_flag |= SSL_TLS_LOG_PEM;
+ }
+
+ SCReturnInt(1);
+}
+
+
+/**
+ * \brief this function registers unit tests for DetectTlsIssuerDN
+ */
+static void DetectTlsIssuerDNRegisterTests(void)
+{
+}
diff --git a/framework/src/suricata/src/detect-tls.h b/framework/src/suricata/src/detect-tls.h
new file mode 100644
index 00000000..71652eb9
--- /dev/null
+++ b/framework/src/suricata/src/detect-tls.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ */
+
+#ifndef __DETECT_TLS_H__
+#define __DETECT_TLS_H__
+
+typedef struct DetectTlsData_ {
+ uint16_t ver; /** tls version to match */
+ uint32_t flags; /** flags containing match variant (Negation for example) */
+ char * subject; /** tls certificate subject substring to match */
+ char * issuerdn; /** tls certificate issuerDN substring to match */
+ char * fingerprint; /** tls fingerprint substring to match */
+} DetectTlsData;
+
+/* prototypes */
+void DetectTlsRegister (void);
+
+#endif /* __DETECT_TLS_H__ */
diff --git a/framework/src/suricata/src/detect-tos.c b/framework/src/suricata/src/detect-tos.c
new file mode 100644
index 00000000..40928261
--- /dev/null
+++ b/framework/src/suricata/src/detect-tos.c
@@ -0,0 +1,430 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-tos.h"
+
+#include "app-layer-protos.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#define PARSE_REGEX "^\\s*(!?\\s*[0-9]{1,3}|!?\\s*[xX][0-9a-fA-F]{1,2})\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static int DetectTosSetup(DetectEngineCtx *, Signature *, char *);
+static int DetectTosMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ Signature *, const SigMatchCtx *);
+static void DetectTosRegisterTests(void);
+static void DetectTosFree(void *);
+
+#define DETECT_IPTOS_MIN 0
+#define DETECT_IPTOS_MAX 255
+
+/**
+ * \brief Register Tos keyword.
+ */
+void DetectTosRegister(void)
+{
+ sigmatch_table[DETECT_TOS].name = "tos";
+ sigmatch_table[DETECT_TOS].Match = DetectTosMatch;
+ sigmatch_table[DETECT_TOS].Setup = DetectTosSetup;
+ sigmatch_table[DETECT_TOS].Free = DetectTosFree;
+ sigmatch_table[DETECT_TOS].RegisterTests = DetectTosRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at "
+ "offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+/**
+ * \brief Match function for tos keyword.
+ *
+ * \param tv ThreadVars instance.
+ * \param det_ctx Pointer to the detection thread ctx.
+ * \param p Pointer to the packet.
+ * \param m Pointer to the SigMatch containing the tos data.
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectTosMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectTosData *tosd = (const DetectTosData *)ctx;
+ int result = 0;
+
+ if (!PKT_IS_IPV4(p) || PKT_IS_PSEUDOPKT(p)) {
+ return 0;
+ }
+
+ if (tosd->tos == IPV4_GET_IPTOS(p)) {
+ SCLogDebug("tos match found for %d\n", tosd->tos);
+ result = 1;
+ }
+
+ return (tosd->negated ^ result);
+}
+
+DetectTosData *DetectTosParse(char *arg)
+{
+ DetectTosData *tosd = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, arg, strlen(arg), 0, 0,
+ ov, MAX_SUBSTRINGS);
+
+ if (ret != 2) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid tos option - %s. "
+ "The tos option value must be in the range "
+ "%u - %u", arg, DETECT_IPTOS_MIN, DETECT_IPTOS_MAX);
+ goto error;
+ }
+
+ const char *str_ptr;
+ res = pcre_get_substring((char *)arg, ov, MAX_SUBSTRINGS, 1,
+ &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ int64_t tos = 0;
+ int negated = 0;
+
+ if (*str_ptr == '!') {
+ str_ptr++;
+ negated = 1;
+ }
+
+ while (isspace((unsigned char)*str_ptr))
+ str_ptr++;
+
+ if (*str_ptr == 'x' || *str_ptr == 'X') {
+ int r = ByteExtractStringSigned(&tos, 16, 0, str_ptr + 1);
+ if (r < 0) {
+ goto error;
+ }
+ } else {
+ int r = ByteExtractStringSigned(&tos, 10, 0, str_ptr);
+ if (r < 0) {
+ goto error;
+ }
+ }
+ if (!(tos >= DETECT_IPTOS_MIN && tos <= DETECT_IPTOS_MAX)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid tos argument - "
+ "%s. The tos option value must be in the range "
+ "%u - %u", str_ptr, DETECT_IPTOS_MIN, DETECT_IPTOS_MAX);
+ goto error;
+ }
+
+ tosd = SCMalloc(sizeof(DetectTosData));
+ if (unlikely(tosd == NULL))
+ goto error;
+ tosd->tos = (uint8_t)tos;
+ tosd->negated = negated;
+
+ return tosd;
+
+error:
+ if (tosd != NULL)
+ DetectTosFree(tosd);
+ return NULL;
+}
+
+/**
+ * \brief Setup function for tos argument. Parse the argument and
+ * add it into the sig.
+ *
+ * \param de_ctx Detection Engine Context instance.
+ * \param s Pointer to the signature.
+ * \param arg Argument to be parsed.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+int DetectTosSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+ DetectTosData *tosd;
+ SigMatch *sm;
+
+ tosd = DetectTosParse(arg);
+ if (tosd == NULL)
+ goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TOS;
+ sm->ctx = (SigMatchCtx *)tosd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Free data allocated by the tos keyword.
+ *
+ * \param tosd Data to be freed.
+ */
+void DetectTosFree(void *tosd)
+{
+ SCFree(tosd);
+}
+
+/********************************Unittests***********************************/
+
+#ifdef UNITTESTS
+
+int DetectTosTest01(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("12");
+ if (tosd != NULL && tosd->tos == 12 && !tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest02(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("123");
+ if (tosd != NULL && tosd->tos == 123 && !tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest03(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse(" 12 ");
+ if (tosd != NULL && tosd->tos == 12 && !tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest04(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("256");
+ if (tosd != NULL) {
+ DetectTosFree(tosd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int DetectTosTest05(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("boom");
+ if (tosd != NULL) {
+ DetectTosFree(tosd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int DetectTosTest06(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("x12");
+ if (tosd != NULL && tosd->tos == 0x12 && !tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest07(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("X12");
+ if (tosd != NULL && tosd->tos == 0x12 && !tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest08(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("x121");
+ if (tosd != NULL) {
+ DetectTosFree(tosd);
+ return 0;
+ }
+
+ return 1;
+}
+
+int DetectTosTest09(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("!12");
+ if (tosd != NULL && tosd->tos == 12 && tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest10(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse("!x12");
+ if (tosd != NULL && tosd->tos == 0x12 && tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest11(void)
+{
+ DetectTosData *tosd = NULL;
+ tosd = DetectTosParse(" ! 12");
+ if (tosd != NULL && tosd->tos == 12 && tosd->negated) {
+ DetectTosFree(tosd);
+ return 1;
+ }
+
+ return 0;
+}
+
+int DetectTosTest12(void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ IPV4_SET_RAW_IPTOS(p->ip4h, 10);
+
+ char *sigs[4];
+ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; tos:10; sid:1;)";
+ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; tos:!10; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; tos:20; sid:3;)";
+ sigs[3]= "alert ip any any -> any any (msg:\"Testing id 3\"; tos:!20; sid:4;)";
+
+ uint32_t sid[4] = {1, 2, 3, 4};
+
+ uint32_t results[1][4] =
+ {
+ {1, 0, 0, 1},
+ };
+
+ result = UTHGenericTest(&p, 1, sigs, sid, (uint32_t *) results, 4);
+
+ UTHFreePackets(&p, 1);
+
+end:
+ return result;
+}
+
+#endif
+
+void DetectTosRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectTosTest01", DetectTosTest01, 1);
+ UtRegisterTest("DetectTosTest02", DetectTosTest02, 1);
+ UtRegisterTest("DetectTosTest03", DetectTosTest03, 1);
+ UtRegisterTest("DetectTosTest04", DetectTosTest04, 1);
+ UtRegisterTest("DetectTosTest05", DetectTosTest05, 1);
+ UtRegisterTest("DetectTosTest06", DetectTosTest06, 1);
+ UtRegisterTest("DetectTosTest07", DetectTosTest07, 1);
+ UtRegisterTest("DetectTosTest08", DetectTosTest08, 1);
+ UtRegisterTest("DetectTosTest09", DetectTosTest09, 1);
+ UtRegisterTest("DetectTosTest10", DetectTosTest10, 1);
+ UtRegisterTest("DetectTosTest11", DetectTosTest11, 1);
+ UtRegisterTest("DetectTosTest12", DetectTosTest12, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/detect-tos.h b/framework/src/suricata/src/detect-tos.h
new file mode 100644
index 00000000..ef6fea69
--- /dev/null
+++ b/framework/src/suricata/src/detect-tos.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DETECT_TOS_H__
+#define __DETECT_TOS_H__
+
+typedef struct DetectTosData_ {
+ uint8_t negated;
+ uint8_t tos;
+} DetectTosData;
+
+void DetectTosRegister(void);
+
+#endif /* __DETECT_TOS_H__ */
diff --git a/framework/src/suricata/src/detect-ttl.c b/framework/src/suricata/src/detect-ttl.c
new file mode 100644
index 00000000..80f39667
--- /dev/null
+++ b/framework/src/suricata/src/detect-ttl.c
@@ -0,0 +1,638 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersighdahiya@gmail.com>
+ *
+ * Implements the ttl keyword
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp.h"
+#include "util-unittest.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-ttl.h"
+#include "util-debug.h"
+
+/**
+ * \brief Regex for parsing our flow options
+ */
+#define PARSE_REGEX "^\\s*([0-9]*)?\\s*([<>=-]+)?\\s*([0-9]+)?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+/*prototypes*/
+int DetectTtlMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectTtlSetup (DetectEngineCtx *, Signature *, char *);
+void DetectTtlFree (void *);
+void DetectTtlRegisterTests (void);
+
+/**
+ * \brief Registration function for ttl: keyword
+ */
+
+void DetectTtlRegister(void)
+{
+ sigmatch_table[DETECT_TTL].name = "ttl";
+ sigmatch_table[DETECT_TTL].desc = "check for a specific IP time-to-live value";
+ sigmatch_table[DETECT_TTL].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#ttl";
+ sigmatch_table[DETECT_TTL].Match = DetectTtlMatch;
+ sigmatch_table[DETECT_TTL].Setup = DetectTtlSetup;
+ sigmatch_table[DETECT_TTL].Free = DetectTtlFree;
+ sigmatch_table[DETECT_TTL].RegisterTests = DetectTtlRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ if (parse_regex != NULL) SCFree(parse_regex);
+ if (parse_regex_study != NULL) SCFree(parse_regex_study);
+ return;
+}
+
+/**
+ * \brief This function is used to match TTL rule option on a packet with those passed via ttl:
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectTtlData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectTtlMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+
+ int ret = 0;
+ uint8_t pttl;
+ const DetectTtlData *ttld = (const DetectTtlData *)ctx;
+
+ if (PKT_IS_PSEUDOPKT(p))
+ return 0;
+
+ if (PKT_IS_IPV4(p)) {
+ pttl = IPV4_GET_IPTTL(p);
+ } else if (PKT_IS_IPV6(p)) {
+ pttl = IPV6_GET_HLIM(p);
+ } else {
+ SCLogDebug("Packet is of not IPv4 or IPv6");
+ return ret;
+ }
+
+ if (ttld->mode == DETECT_TTL_EQ && pttl == ttld->ttl1)
+ ret = 1;
+ else if (ttld->mode == DETECT_TTL_LT && pttl < ttld->ttl1)
+ ret = 1;
+ else if (ttld->mode == DETECT_TTL_GT && pttl > ttld->ttl1)
+ ret = 1;
+ else if (ttld->mode == DETECT_TTL_RA && (pttl > ttld->ttl1 && pttl < ttld->ttl2))
+ ret = 1;
+
+ return ret;
+}
+
+/**
+ * \brief This function is used to parse ttl options passed via ttl: keyword
+ *
+ * \param ttlstr Pointer to the user provided ttl options
+ *
+ * \retval ttld pointer to DetectTtlData on success
+ * \retval NULL on failure
+ */
+
+DetectTtlData *DetectTtlParse (char *ttlstr)
+{
+
+ DetectTtlData *ttld = NULL;
+ char *arg1 = NULL;
+ char *arg2 = NULL;
+ char *arg3 = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, ttlstr, strlen(ttlstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 2 || ret > 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
+ goto error;
+ }
+ const char *str_ptr;
+
+ res = pcre_get_substring((char *) ttlstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg1 = (char *) str_ptr;
+ SCLogDebug("Arg1 \"%s\"", arg1);
+
+ if (ret >= 3) {
+ res = pcre_get_substring((char *) ttlstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg2 = (char *) str_ptr;
+ SCLogDebug("Arg2 \"%s\"", arg2);
+
+ if (ret >= 4) {
+ res = pcre_get_substring((char *) ttlstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg3 = (char *) str_ptr;
+ SCLogDebug("Arg3 \"%s\"", arg3);
+ }
+ }
+
+ ttld = SCMalloc(sizeof (DetectTtlData));
+ if (unlikely(ttld == NULL))
+ goto error;
+ ttld->ttl1 = 0;
+ ttld->ttl2 = 0;
+
+ if (arg2 != NULL) {
+ /*set the values*/
+ switch(arg2[0]) {
+ case '<':
+ if (arg3 == NULL)
+ goto error;
+
+ ttld->mode = DETECT_TTL_LT;
+ ttld->ttl1 = (uint8_t) atoi(arg3);
+
+ SCLogDebug("ttl is %"PRIu8"",ttld->ttl1);
+ if (strlen(arg1) > 0)
+ goto error;
+
+ break;
+ case '>':
+ if (arg3 == NULL)
+ goto error;
+
+ ttld->mode = DETECT_TTL_GT;
+ ttld->ttl1 = (uint8_t) atoi(arg3);
+
+ SCLogDebug("ttl is %"PRIu8"",ttld->ttl1);
+ if (strlen(arg1) > 0)
+ goto error;
+
+ break;
+ case '-':
+ if (arg1 == NULL || strlen(arg1)== 0)
+ goto error;
+ if (arg3 == NULL || strlen(arg3)== 0)
+ goto error;
+
+ ttld->mode = DETECT_TTL_RA;
+ ttld->ttl1 = (uint8_t) atoi(arg1);
+
+ ttld->ttl2 = (uint8_t) atoi(arg3);
+ SCLogDebug("ttl is %"PRIu8" to %"PRIu8"",ttld->ttl1, ttld->ttl2);
+ if (ttld->ttl1 >= ttld->ttl2) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid ttl range. ");
+ goto error;
+ }
+ break;
+ default:
+ ttld->mode = DETECT_TTL_EQ;
+
+ if ((arg2 != NULL && strlen(arg2) > 0) || (arg3 != NULL && strlen(arg3) > 0) || (arg1 == NULL ||strlen(arg1) == 0))
+ goto error;
+
+ ttld->ttl1 = (uint8_t) atoi(arg1);
+ break;
+ }
+ } else {
+ ttld->mode = DETECT_TTL_EQ;
+
+ if ((arg2 != NULL && strlen(arg2) > 0) ||
+ (arg3 != NULL && strlen(arg3) > 0) ||
+ (arg1 == NULL ||strlen(arg1) == 0))
+ goto error;
+
+ ttld->ttl1 = (uint8_t) atoi(arg1);
+ }
+
+ SCFree(arg1);
+ SCFree(arg2);
+ SCFree(arg3);
+ return ttld;
+
+error:
+ if (ttld) SCFree(ttld);
+ if (arg1) SCFree(arg1);
+ if (arg2) SCFree(arg2);
+ if (arg3) SCFree(arg3);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to attld the parsed ttl data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param ttlstr pointer to the user provided ttl options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectTtlSetup (DetectEngineCtx *de_ctx, Signature *s, char *ttlstr)
+{
+
+ DetectTtlData *ttld = NULL;
+ SigMatch *sm = NULL;
+
+ ttld = DetectTtlParse(ttlstr);
+ if (ttld == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_TTL;
+ sm->ctx = (SigMatchCtx *)ttld;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (ttld != NULL) DetectTtlFree(ttld);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+}
+
+/**
+ * \brief this function will free memory associated with DetectTtlData
+ *
+ * \param ptr pointer to DetectTtlData
+ */
+void DetectTtlFree(void *ptr)
+{
+ DetectTtlData *ttld = (DetectTtlData *)ptr;
+ SCFree(ttld);
+}
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+/**
+ * \brief this function is used to initialize the detection engine context and
+ * setup the signature with passed values.
+ *
+ */
+
+static int DetectTtlInitTest(DetectEngineCtx **de_ctx, Signature **sig, DetectTtlData **ttld, char *str)
+{
+ char fullstr[1024];
+ int result = 0;
+
+ *de_ctx = NULL;
+ *sig = NULL;
+
+ if (snprintf(fullstr, 1024, "alert ip any any -> any any (msg:\"Ttl test\"; ttl:%s; sid:1;)", str) >= 1024) {
+ goto end;
+ }
+
+ *de_ctx = DetectEngineCtxInit();
+ if (*de_ctx == NULL) {
+ goto end;
+ }
+
+ (*de_ctx)->flags |= DE_QUIET;
+
+ (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
+ if ((*de_ctx)->sig_list == NULL) {
+ goto end;
+ }
+
+ *sig = (*de_ctx)->sig_list;
+
+ *ttld = DetectTtlParse(str);
+
+ result = 1;
+
+end:
+ return result;
+}
+
+/**
+ * \test DetectTtlParseTest01 is a test for setting up an valid ttl value.
+ */
+
+static int DetectTtlParseTest01 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+
+ ttld = DetectTtlParse("10");
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 10 && ttld->mode == DETECT_TTL_EQ)
+ res = 1;
+
+ DetectTtlFree(ttld);
+ }
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest02 is a test for setting up an valid ttl value with
+ * "<" operator.
+ */
+
+static int DetectTtlParseTest02 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+ ttld = DetectTtlParse("<10");
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 10 && ttld->mode == DETECT_TTL_LT)
+ res = 1;
+ DetectTtlFree(ttld);
+ }
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest03 is a test for setting up an valid ttl values with
+ * "-" operator.
+ */
+
+static int DetectTtlParseTest03 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+ ttld = DetectTtlParse("1-2");
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 1 && ttld->ttl2 == 2 && ttld->mode == DETECT_TTL_RA)
+ res = 1;
+ DetectTtlFree(ttld);
+ }
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest04 is a test for setting up an valid ttl value with
+ * ">" operator and include spaces arround the given values.
+ */
+
+static int DetectTtlParseTest04 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+
+ ttld = DetectTtlParse(" > 10 ");
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 10 && ttld->mode == DETECT_TTL_GT)
+ res = 1;
+
+ DetectTtlFree(ttld);
+ }
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest05 is a test for setting up an valid ttl values with
+ * "-" operator and include spaces arround the given values.
+ */
+
+static int DetectTtlParseTest05 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+
+ ttld = DetectTtlParse(" 1 - 2 ");
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 1 && ttld->ttl2 == 2 && ttld->mode == DETECT_TTL_RA)
+ res = 1;
+ DetectTtlFree(ttld);
+ }
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest06 is a test for setting up an valid ttl values with
+ * invalid "=" operator and include spaces arround the given values.
+ */
+
+static int DetectTtlParseTest06 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+
+ ttld = DetectTtlParse(" 1 = 2 ");
+ if (ttld == NULL)
+ res = 1;
+ if (ttld) SCFree(ttld);
+
+ return res;
+}
+
+/**
+ * \test DetectTtlParseTest07 is a test for setting up an valid ttl values with
+ * invalid "<>" operator and include spaces arround the given values.
+ */
+
+static int DetectTtlParseTest07 (void)
+{
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+
+ ttld = DetectTtlParse(" 1<>2 ");
+ if (ttld == NULL)
+ res = 1;
+
+ if (ttld) SCFree(ttld);
+
+ return res;
+}
+
+/**
+ * \test DetectTtlSetpTest01 is a test for setting up an valid ttl values with
+ * valid "-" operator and include spaces arround the given values. In the
+ * test the values are setup with initializing the detection engine context
+ * setting up the signature itself.
+ */
+
+static int DetectTtlSetpTest01(void)
+{
+
+ DetectTtlData *ttld = NULL;
+ uint8_t res = 0;
+ Signature *sig = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+
+ res = DetectTtlInitTest(&de_ctx, &sig, &ttld, "1 - 2 ");
+ if (res == 0) {
+ goto end;
+ }
+
+ if(ttld == NULL)
+ goto cleanup;
+
+ if (ttld != NULL) {
+ if (ttld->ttl1 == 1 && ttld->ttl2 == 2 && ttld->mode == DETECT_TTL_RA)
+ res = 1;
+ }
+
+cleanup:
+ if (ttld) SCFree(ttld);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ return res;
+}
+
+/**
+ * \test DetectTtlTestSig01 is a test for checking the working of ttl keyword
+ * by setting up the signature and later testing its working by matching
+ * the received packet against the sig.
+ */
+
+static int DetectTtlTestSig1(void)
+{
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ int result = 0;
+ IPV4Hdr ip4h;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ip4h, 0, sizeof(ip4h));
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ ip4h.ip_ttl = 15;
+ p->ip4h = &ip4h;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"with in ttl limit\"; ttl: >16; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Less than 17\"; ttl: <17; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Greater than 5\"; ttl:15; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Equals tcp\"; ttl: 1-30; sid:4;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sid 1 alerted, but should not have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 2) == 0) {
+ printf("sid 2 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 3) == 0) {
+ printf("sid 3 did not alert, but should have: ");
+ goto cleanup;
+ } else if (PacketAlertCheck(p, 4) == 0) {
+ printf("sid 4 did not alert, but should have: ");
+ goto cleanup;
+ }
+
+ result = 1;
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectTtl
+ */
+void DetectTtlRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectTtlParseTest01", DetectTtlParseTest01, 1);
+ UtRegisterTest("DetectTtlParseTest02", DetectTtlParseTest02, 1);
+ UtRegisterTest("DetectTtlParseTest03", DetectTtlParseTest03, 1);
+ UtRegisterTest("DetectTtlParseTest04", DetectTtlParseTest04, 1);
+ UtRegisterTest("DetectTtlParseTest05", DetectTtlParseTest05, 1);
+ UtRegisterTest("DetectTtlParseTest06", DetectTtlParseTest06, 1);
+ UtRegisterTest("DetectTtlParseTest07", DetectTtlParseTest07, 1);
+ UtRegisterTest("DetectTtlSetpTest01", DetectTtlSetpTest01, 1);
+ UtRegisterTest("DetectTtlTestSig1", DetectTtlTestSig1, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-ttl.h b/framework/src/suricata/src/detect-ttl.h
new file mode 100644
index 00000000..86505f76
--- /dev/null
+++ b/framework/src/suricata/src/detect-ttl.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersighdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_TTL_H
+#define _DETECT_TTL_H
+
+#define DETECT_TTL_LT 0 /**< "less than" operator */
+#define DETECT_TTL_EQ 1 /**< "equals" operator (default) */
+#define DETECT_TTL_GT 2 /**< "greater than" operator */
+#define DETECT_TTL_RA 3 /**< "range" operator */
+
+typedef struct DetectTtlData_ {
+ uint8_t ttl1; /**< first ttl value in the signature*/
+ uint8_t ttl2; /**< second ttl value in the signature, in case of range
+ operator*/
+ uint8_t mode; /**< operator used in the signature */
+}DetectTtlData;
+
+void DetectTtlRegister(void);
+
+#endif /* _DETECT_TTL_H */
+
diff --git a/framework/src/suricata/src/detect-uricontent.c b/framework/src/suricata/src/detect-uricontent.c
new file mode 100644
index 00000000..a22479b0
--- /dev/null
+++ b/framework/src/suricata/src/detect-uricontent.c
@@ -0,0 +1,1983 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Simple uricontent match part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-content.h"
+#include "detect-http-uri.h"
+#include "detect-uricontent.h"
+#include "detect-engine-mpm.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-state.h"
+#include "flow.h"
+#include "detect-flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+#include "threads.h"
+
+#include "stream-tcp.h"
+#include "stream.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+
+#include "util-mpm.h"
+#include "util-print.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-binsearch.h"
+#include "util-spm.h"
+#include "util-spm-bm.h"
+#include "conf.h"
+
+/* prototypes */
+static int DetectUricontentSetup (DetectEngineCtx *, Signature *, char *);
+void HttpUriRegisterTests(void);
+
+int DetectAppLayerUricontentMatch (ThreadVars *, DetectEngineThreadCtx *,
+ Flow *, uint8_t , void *,
+ Signature *, SigMatch *);
+void DetectUricontentFree(void *);
+
+/**
+ * \brief Registration function for uricontent: keyword
+ */
+void DetectUricontentRegister (void)
+{
+ sigmatch_table[DETECT_URICONTENT].name = "uricontent";
+ sigmatch_table[DETECT_URICONTENT].AppLayerMatch = NULL;
+ sigmatch_table[DETECT_URICONTENT].Match = NULL;
+ sigmatch_table[DETECT_URICONTENT].Setup = DetectUricontentSetup;
+ sigmatch_table[DETECT_URICONTENT].Free = DetectUricontentFree;
+ sigmatch_table[DETECT_URICONTENT].RegisterTests = HttpUriRegisterTests;
+ sigmatch_table[DETECT_URICONTENT].alproto = ALPROTO_HTTP;
+
+ sigmatch_table[DETECT_URICONTENT].flags |= SIGMATCH_PAYLOAD;
+}
+
+/**
+ * \brief pass on the uricontent_max_id
+ * \param de_ctx pointer to the detect egine context whose max id is asked
+ */
+uint32_t DetectUricontentMaxId(DetectEngineCtx *de_ctx)
+{
+ return MpmPatternIdStoreGetMaxId(de_ctx->mpm_pattern_id_store);
+}
+
+/**
+ * \brief this function will Free memory associated with DetectContentData
+ *
+ * \param cd pointer to DetectUricotentData
+ */
+void DetectUricontentFree(void *ptr)
+{
+ SCEnter();
+ DetectContentData *cd = (DetectContentData *)ptr;
+
+ if (cd == NULL)
+ SCReturn;
+
+ BoyerMooreCtxDeInit(cd->bm_ctx);
+ SCFree(cd);
+
+ SCReturn;
+}
+
+/**
+ * \brief Helper function to print a DetectContentData
+ */
+void DetectUricontentPrint(DetectContentData *cd)
+{
+ int i = 0;
+ if (cd == NULL) {
+ SCLogDebug("Detect UricontentData \"cd\" is NULL");
+ return;
+ }
+ char *tmpstr = SCMalloc(sizeof(char) * cd->content_len + 1);
+ if (unlikely(tmpstr == NULL))
+ return;
+
+ if (tmpstr != NULL) {
+ for (i = 0; i < cd->content_len; i++) {
+ if (isprint(cd->content[i]))
+ tmpstr[i] = cd->content[i];
+ else
+ tmpstr[i] = '.';
+ }
+ tmpstr[i] = '\0';
+ SCLogDebug("Uricontent: \"%s\"", tmpstr);
+ SCFree(tmpstr);
+ } else {
+ SCLogDebug("Uricontent: ");
+ for (i = 0; i < cd->content_len; i++)
+ SCLogDebug("%c", cd->content[i]);
+ }
+
+ SCLogDebug("Uricontent_id: %"PRIu32, cd->id);
+ SCLogDebug("Uricontent_len: %"PRIu16, cd->content_len);
+ SCLogDebug("Depth: %"PRIu16, cd->depth);
+ SCLogDebug("Offset: %"PRIu16, cd->offset);
+ SCLogDebug("Within: %"PRIi32, cd->within);
+ SCLogDebug("Distance: %"PRIi32, cd->distance);
+ SCLogDebug("flags: %u ", cd->flags);
+ SCLogDebug("negated: %s ",
+ cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false");
+ SCLogDebug("relative match next: %s ",
+ cd->flags & DETECT_CONTENT_RELATIVE_NEXT ? "true" : "false");
+ SCLogDebug("-----------");
+}
+
+/**
+ * \brief Creates a SigMatch for the uricontent keyword being sent as argument,
+ * and appends it to the Signature(s).
+ *
+ * \param de_ctx Pointer to the detection engine context
+ * \param s Pointer to signature for the current Signature being parsed
+ * from the rules
+ * \param contentstr Pointer to the string holding the keyword value
+ *
+ * \retval 0 on success, -1 on failure
+ */
+int DetectUricontentSetup(DetectEngineCtx *de_ctx, Signature *s, char *contentstr)
+{
+ SCEnter();
+
+ char *legacy = NULL;
+ if (ConfGet("legacy.uricontent", &legacy) == 1) {
+ if (strcasecmp("disabled", legacy) == 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "uriconent deprecated. To "
+ "use a rule with \"uricontent\", either set the "
+ "option - \"legacy.uricontent\" in the conf to "
+ "\"enabled\" OR replace uricontent with "
+ "\'content:%s; http_uri;\'.", contentstr);
+ goto error;
+ } else if (strcasecmp("enabled", legacy) == 0) {
+ ;
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid value found "
+ "for legacy.uriconent - \"%s\". Valid values are "
+ "\"enabled\" OR \"disabled\".", legacy);
+ goto error;
+ }
+ }
+
+ if (DetectContentSetup(de_ctx, s, contentstr) < 0)
+ goto error;
+
+ if (DetectHttpUriSetup(de_ctx, s, NULL) < 0)
+ goto error;
+
+ SCReturnInt(0);
+error:
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Checks if the content sent as the argument, has a uricontent which
+ * has been provided in the rule. This match function matches the
+ * normalized http uri against the given rule using multi pattern
+ * search algorithms.
+ *
+ * \param det_ctx Pointer to the detection engine thread context
+ * \param content Pointer to the uri content currently being matched
+ * \param content_len Content_len of the received uri content
+ *
+ * \retval 1 if the uri contents match; 0 no match
+ */
+static inline int DoDetectAppLayerUricontentMatch (DetectEngineThreadCtx *det_ctx,
+ uint8_t *uri, uint16_t uri_len, uint8_t flags)
+{
+ int ret = 0;
+ /* run the pattern matcher against the uri */
+ if (det_ctx->sgh->mpm_uricontent_maxlen > uri_len) {
+ SCLogDebug("not searching as pkt payload is smaller than the "
+ "largest uricontent length we need to match");
+ } else {
+ SCLogDebug("search: (%p, maxlen %" PRIu32 ", sgh->sig_cnt "
+ "%" PRIu32 ")", det_ctx->sgh, det_ctx->sgh->
+ mpm_uricontent_maxlen, det_ctx->sgh->sig_cnt);
+
+ ret += UriPatternSearch(det_ctx, uri, uri_len, flags);
+
+ SCLogDebug("post search: cnt %" PRIu32, ret);
+ }
+ return ret;
+}
+
+/**
+ * \brief Run the pattern matcher against the uri(s)
+ *
+ * We run against _all_ uri(s) we have as the pattern matcher will
+ * flag each sig that has a match. We need to do this for all uri(s)
+ * to not miss possible events.
+ *
+ * \param f locked flow
+ * \param htp_state initialized htp state
+ *
+ * \warning Make sure the flow/state is locked
+ * \todo what should we return? Just the fact that we matched?
+ */
+uint32_t DetectUricontentInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *txv, uint64_t idx)
+{
+ SCEnter();
+
+ htp_tx_t *tx = (htp_tx_t *)txv;
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ uint32_t cnt = 0;
+
+ if (tx_ud == NULL || tx_ud->request_uri_normalized == NULL)
+ goto end;
+ cnt = DoDetectAppLayerUricontentMatch(det_ctx, (uint8_t *)
+ bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized),
+ flags);
+
+end:
+ SCReturnUInt(cnt);
+}
+
+/*
+ * UNITTTESTS
+ */
+
+#ifdef UNITTESTS
+
+#include "stream-tcp-reassemble.h"
+
+/** \test Test case where path traversal has been sent as a path string in the
+ * HTTP URL and normalized path string is checked */
+static int HTTPUriTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "GET /../../images.gif HTTP/1.1\r\nHost: www.ExA"
+ "mPlE.cOM\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("AppLayerParse failed: r(%d) != 0: ", r);
+ goto end;
+ }
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+
+ if (tx->request_method_number != HTP_M_GET ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ goto end;
+ }
+
+ if ((tx->request_hostname == NULL) ||
+ (bstr_cmp_c(tx->request_hostname, "www.example.com") != 0))
+ {
+ goto end;
+ }
+
+ if ((tx->parsed_uri->path == NULL) ||
+ (bstr_cmp_c(tx->parsed_uri->path, "/images.gif") != 0))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Test case where path traversal has been sent in special characters in
+ * HEX encoding in the HTTP URL and normalized path string is checked */
+static int HTTPUriTest02(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *htp_state = NULL;
+ uint8_t httpbuf1[] = "GET /%2e%2e/images.gif HTTP/1.1\r\nHost: www.ExA"
+ "mPlE.cOM\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("AppLayerParse failed: r(%d) != 0: ", r);
+ goto end;
+ }
+
+ htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+
+ if (tx->request_method_number != HTP_M_GET ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ goto end;
+ }
+
+ if ((tx->request_hostname == NULL) ||
+ (bstr_cmp_c(tx->request_hostname, "www.example.com") != 0))
+ {
+ goto end;
+ }
+
+ if ((tx->parsed_uri->path == NULL) ||
+ (bstr_cmp_c(tx->parsed_uri->path, "/images.gif") != 0))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Test case where NULL character has been sent in HEX encoding in the
+ * HTTP URL and normalized path string is checked */
+static int HTTPUriTest03(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *htp_state = NULL;
+ uint8_t httpbuf1[] = "GET%00 /images.gif HTTP/1.1\r\nHost: www.ExA"
+ "mPlE.cOM\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("AppLayerParse failed: r(%d) != 0: ", r);
+ goto end;
+ }
+
+ htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+
+ if (tx->request_method_number != HTP_M_UNKNOWN ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ goto end;
+ }
+
+ if ((tx->request_hostname == NULL) ||
+ (bstr_cmp_c(tx->request_hostname, "www.example.com") != 0))
+ {
+ goto end;
+ }
+
+ if ((tx->parsed_uri->path == NULL) ||
+ (bstr_cmp_c(tx->parsed_uri->path, "/images.gif") != 0))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+
+/** \test Test case where self referencing directories request has been sent
+ * in the HTTP URL and normalized path string is checked */
+static int HTTPUriTest04(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *htp_state = NULL;
+ uint8_t httpbuf1[] = "GET /./././images.gif HTTP/1.1\r\nHost: www.ExA"
+ "mPlE.cOM\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ int r = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
+ STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("AppLayerParse failed: r(%d) != 0: ", r);
+ goto end;
+ }
+
+ htp_state = f.alstate;
+ if (htp_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, 0);
+
+ if (tx->request_method_number != HTP_M_GET ||
+ tx->request_protocol_number != HTP_PROTOCOL_1_1)
+ {
+ goto end;
+ }
+
+ if ((tx->request_hostname == NULL) ||
+ (bstr_cmp_c(tx->request_hostname, "www.example.com") != 0))
+ {
+ goto end;
+ }
+
+ if ((tx->parsed_uri->path == NULL) ||
+ (bstr_cmp_c(tx->parsed_uri->path, "/images.gif") != 0))
+ {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ StreamTcpFreeConfig(TRUE);
+ if (htp_state != NULL)
+ HTPStateFree(htp_state);
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Checks if a uricontent is registered in a Signature
+ */
+int DetectUriSigTest01(void)
+{
+ SigMatch *sm = NULL;
+ int result = 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Signature *s = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "content:\"me\"; uricontent:\"me\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ BUG_ON(de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH] == NULL);
+
+ sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_UMATCH];
+ if (sm->type == DETECT_CONTENT) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+
+ end:
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check the signature working to alert when http_cookie is matched . */
+static int DetectUriSigTest02(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST /one HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"foo\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"oisf\"; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig: 1 alerted, but it should not\n");
+ goto end;
+ } else if (!PacketAlertCheck(p, 2)) {
+ printf("sig: 2 did not alerted, but it should\n");
+ goto end;
+ } else if ((PacketAlertCheck(p, 3))) {
+ printf("sig: 3 alerted, but it should not\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ //if (http_state != NULL) HTPStateFree(http_state);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the working of search once per packet only in applayer
+ * match */
+static int DetectUriSigTest03(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t httpbuf1[] = "POST /one HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "POST /oneself HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"foo\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"self\"; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ } else if (!PacketAlertCheck(p, 2)) {
+ printf("sig 2 did not alert, but it should: ");
+ goto end;
+ } else if ((PacketAlertCheck(p, 3))) {
+ printf("sig 3 alerted, but it should not: ");
+ goto end;
+ }
+
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig 1 alerted, but it should not (chunk 2): ");
+ goto end;
+ } else if (!PacketAlertCheck(p, 2)) {
+ printf("sig 2 alerted, but it should not (chunk 2): ");
+ goto end;
+ } else if (!(PacketAlertCheck(p, 3))) {
+ printf("sig 3 did not alert, but it should (chunk 2): ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Check that modifiers of content apply only to content keywords
+ * and the same for uricontent modifiers
+ */
+static int DetectUriSigTest04(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"foo\"; sid:1;)");
+ if (s == NULL ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 1 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";sid:1;)");
+ if (s == NULL ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 2 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; sid:1;)");
+ if (s == NULL ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData *)s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData *)s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 3 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "content:\"foo\"; uricontent:\"bar\";"
+ " depth:10; offset: 5; sid:1;)");
+ if (s == NULL ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData *)s->sm_lists[DETECT_SM_LIST_UMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData *)s->sm_lists[DETECT_SM_LIST_UMATCH]->ctx)->offset != 5 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 4 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; within:3; sid:1;)");
+ if (s != NULL) {
+ printf("sig 5 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; distance:3; sid:1;)");
+ if (s != NULL) {
+ printf("sig 6 failed to parse: ");
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; content:"
+ "\"two_contents\"; within:30; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ } else if (s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx)->within != 30 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 7 failed to parse: ");
+ DetectContentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx);
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; uricontent:"
+ "\"two_uricontents\"; within:30; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ } else if (s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx)->within != 30 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 8 failed to parse: ");
+ DetectUricontentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx);
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; content:"
+ "\"two_contents\"; distance:30; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ } else if (
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx)->distance != 30 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 9 failed to parse: ");
+ DetectContentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx);
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; uricontent:"
+ "\"two_uricontents\"; distance:30; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ } else if (
+ s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL ||
+ s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx)->distance != 30 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL)
+ {
+ printf("sig 10 failed to parse: ");
+ DetectUricontentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx);
+ goto end;
+ }
+
+ s = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent and content\"; "
+ "uricontent:\"foo\"; content:\"bar\";"
+ " depth:10; offset: 5; uricontent:"
+ "\"two_uricontents\"; distance:30; "
+ "within:60; content:\"two_contents\";"
+ " within:70; distance:45; sid:1;)");
+ if (s == NULL) {
+ printf("sig 10 failed to parse: ");
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] == NULL || s->sm_lists[DETECT_SM_LIST_PMATCH] == NULL) {
+ printf("umatch %p or pmatch %p: ", s->sm_lists[DETECT_SM_LIST_UMATCH], s->sm_lists[DETECT_SM_LIST_PMATCH]);
+ goto end;
+ }
+
+ if ( ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->depth != 15 ||
+ ((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx)->offset != 5 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx)->distance != 30 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx)->within != 60 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx)->distance != 45 ||
+ ((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx)->within != 70 ||
+ s->sm_lists[DETECT_SM_LIST_MATCH] != NULL) {
+ printf("sig 10 failed to parse, content not setup properly: ");
+ DetectContentPrint((DetectContentData*) s->sm_lists[DETECT_SM_LIST_PMATCH]->ctx);
+ DetectUricontentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx);
+ DetectContentPrint((DetectContentData*) s->sm_lists_tail[DETECT_SM_LIST_PMATCH]->ctx);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test Check the modifiers for uricontent and content
+ * match
+ */
+static int DetectUriSigTest05(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+
+ p = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+ p->tcph->th_seq = htonl(1000);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+ f.proto = p->proto;
+
+ StreamTcpInitConfig(TRUE);
+
+ StreamMsg *stream_msg = StreamMsgGetFromPool();
+ if (stream_msg == NULL) {
+ printf("no stream_msg: ");
+ goto end;
+ }
+
+ memcpy(stream_msg->data, httpbuf1, httplen1);
+ stream_msg->data_len = httplen1;
+
+ ssn.toserver_smsg_head = stream_msg;
+ ssn.toserver_smsg_tail = stream_msg;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; uricontent:\"foo\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; uricontent:\"one\"; content:\"two\"; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; uricontent:\"one\"; offset:1; depth:10; "
+ "uricontent:\"two\"; distance:1; within: 4; uricontent:\"three\"; "
+ "distance:1; within: 6; sid:3;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig: 1 alerted, but it should not: ");
+ goto end;
+ } else if (! PacketAlertCheck(p, 2)) {
+ printf("sig: 2 did not alert, but it should: ");
+ goto end;
+ } else if (! (PacketAlertCheck(p, 3))) {
+ printf("sig: 3 did not alert, but it should: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the modifiers for uricontent and content
+ * match
+ */
+static int DetectUriSigTest06(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ TCPHdr tcp_hdr;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ memset(&tcp_hdr, 0, sizeof(tcp_hdr));
+
+
+ p = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+ p->tcph->th_seq = htonl(1000);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+ f.proto = p->proto;
+
+ StreamTcpInitConfig(TRUE);
+
+ StreamMsg *stream_msg = StreamMsgGetFromPool();
+ if (stream_msg == NULL) {
+ printf("no stream_msg: ");
+ goto end;
+ }
+
+ memcpy(stream_msg->data, httpbuf1, httplen1);
+ stream_msg->data_len = httplen1;
+
+ ssn.toserver_smsg_head = stream_msg;
+ ssn.toserver_smsg_tail = stream_msg;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"foo\"; content:\"bar\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; offset:1; depth:10; "
+ "content:\"one\"; offset:1; depth:10; "
+ "uricontent:\"two\"; distance:1; within: 4; "
+ "content:\"two\"; distance:1; within: 4; "
+ "uricontent:\"three\"; distance:1; within: 6; "
+ "content:\"/three\"; distance:0; within: 7; "
+ "sid:2;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; offset:1; depth:10; "
+ "uricontent:\"two\"; distance:1; within: 4; "
+ "uricontent:\"three\"; distance:1; within: 6; "
+ "sid:3;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sig: 1 alerted, but it should not:");
+ goto end;
+ } else if (! PacketAlertCheck(p, 2)) {
+ printf("sig: 2 did not alert, but it should:");
+ goto end;
+ } else if (! (PacketAlertCheck(p, 3))) {
+ printf("sig: 3 did not alert, but it should:");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test Check the modifiers for uricontent and content
+ * match
+ */
+static int DetectUriSigTest07(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:"
+ " hellocatch\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"foo\"; content:\"bar\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; offset:1; depth:10; "
+ "content:\"one\"; offset:1; depth:10; "
+ "uricontent:\"two\"; distance:3; within: 4; "
+ "content:\"two\"; distance:1; within: 4; "
+ "uricontent:\"three\"; distance:1; within: 6; "
+ "content:\"/three\"; distance:0; within: 7; "
+ "sid:2;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:"
+ "\" Test uricontent\"; "
+ "uricontent:\"one\"; offset:1; depth:10; "
+ "uricontent:\"two\"; distance:1; within: 4; "
+ "uricontent:\"six\"; distance:1; within: 6; "
+ "sid:3;)");
+
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig: 1 alerted, but it should not:");
+ goto end;
+ } else if (PacketAlertCheck(p, 2)) {
+ printf("sig: 2 alerted, but it should not:");
+ goto end;
+ } else if (PacketAlertCheck(p, 3)) {
+ printf("sig: 3 alerted, but it should not:");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectUriSigTest08(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectUriSigTest09(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectUriSigTest10(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"boo; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+int DetectUriSigTest11(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:boo\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriSigTest12(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectContentData *ud = 0;
+ Signature *s = NULL;
+ int result = 0;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent: !\"boo\"; sid:238012;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL: ");
+ goto end;
+ }
+
+ if (s->sm_lists_tail[DETECT_SM_LIST_UMATCH] == NULL || s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx == NULL) {
+ printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: ");
+ goto end;
+ }
+
+ ud = (DetectContentData *)s->sm_lists_tail[DETECT_SM_LIST_UMATCH]->ctx;
+ result = (strncmp("boo", (char *)ud->content, ud->content_len) == 0);
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest13(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest14(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest15(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest16(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest17(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"aast|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest18(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"aast|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest19(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"aast|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af|asdf\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af|af|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; uricontent:\"|af|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+int DetectUriContentParseTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; uricontent:\"\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void HttpUriRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HTTPUriTest01", HTTPUriTest01, 1);
+ UtRegisterTest("HTTPUriTest02", HTTPUriTest02, 1);
+ UtRegisterTest("HTTPUriTest03", HTTPUriTest03, 1);
+ UtRegisterTest("HTTPUriTest04", HTTPUriTest04, 1);
+
+ UtRegisterTest("DetectUriSigTest01", DetectUriSigTest01, 1);
+ UtRegisterTest("DetectUriSigTest02", DetectUriSigTest02, 1);
+ UtRegisterTest("DetectUriSigTest03", DetectUriSigTest03, 1);
+ UtRegisterTest("DetectUriSigTest04 - Modifiers", DetectUriSigTest04, 1);
+ UtRegisterTest("DetectUriSigTest05 - Inspection", DetectUriSigTest05, 1);
+ UtRegisterTest("DetectUriSigTest06 - Inspection", DetectUriSigTest06, 1);
+ UtRegisterTest("DetectUriSigTest07 - Inspection", DetectUriSigTest07, 1);
+ UtRegisterTest("DetectUriSigTest08", DetectUriSigTest08, 1);
+ UtRegisterTest("DetectUriSigTest09", DetectUriSigTest09, 1);
+ UtRegisterTest("DetectUriSigTest10", DetectUriSigTest10, 1);
+ UtRegisterTest("DetectUriSigTest11", DetectUriSigTest11, 1);
+ UtRegisterTest("DetectUriSigTest12", DetectUriSigTest12, 1);
+
+ UtRegisterTest("DetectUriContentParseTest13", DetectUriContentParseTest13, 1);
+ UtRegisterTest("DetectUriContentParseTest14", DetectUriContentParseTest14, 1);
+ UtRegisterTest("DetectUriContentParseTest15", DetectUriContentParseTest15, 1);
+ UtRegisterTest("DetectUriContentParseTest16", DetectUriContentParseTest16, 1);
+ UtRegisterTest("DetectUriContentParseTest17", DetectUriContentParseTest17, 1);
+ UtRegisterTest("DetectUriContentParseTest18", DetectUriContentParseTest18, 1);
+ UtRegisterTest("DetectUriContentParseTest19", DetectUriContentParseTest19, 1);
+ UtRegisterTest("DetectUriContentParseTest20", DetectUriContentParseTest20, 1);
+ UtRegisterTest("DetectUriContentParseTest21", DetectUriContentParseTest21, 1);
+ UtRegisterTest("DetectUriContentParseTest22", DetectUriContentParseTest22, 1);
+ UtRegisterTest("DetectUriContentParseTest23", DetectUriContentParseTest23, 1);
+ UtRegisterTest("DetectUriContentParseTest24", DetectUriContentParseTest24, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-uricontent.h b/framework/src/suricata/src/detect-uricontent.h
new file mode 100644
index 00000000..c32a923e
--- /dev/null
+++ b/framework/src/suricata/src/detect-uricontent.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef __DETECT_URICONTENT_H__
+#define __DETECT_URICONTENT_H__
+
+#include "detect-content.h"
+
+#include "util-spm-bm.h"
+#include "app-layer-htp.h"
+
+/* prototypes */
+void DetectUricontentRegister (void);
+uint32_t DetectUricontentMaxId(DetectEngineCtx *);
+void DetectUricontentPrint(DetectContentData *);
+
+uint32_t DetectUricontentInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f,
+ HtpState *htp_state, uint8_t flags,
+ void *tx, uint64_t idx);
+
+#endif /* __DETECT_URICONTENT_H__ */
diff --git a/framework/src/suricata/src/detect-urilen.c b/framework/src/suricata/src/detect-urilen.c
new file mode 100644
index 00000000..1355f559
--- /dev/null
+++ b/framework/src/suricata/src/detect-urilen.c
@@ -0,0 +1,700 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersighdahiya@gmail.com>
+ *
+ * Implements the urilen keyword
+ */
+
+#include "suricata-common.h"
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine-state.h"
+
+#include "detect-urilen.h"
+#include "util-debug.h"
+#include "util-byte.h"
+#include "flow-util.h"
+#include "stream-tcp.h"
+
+/**
+ * \brief Regex for parsing our urilen
+ */
+#define PARSE_REGEX "^(?:\\s*)(<|>)?(?:\\s*)([0-9]{1,5})(?:\\s*)(?:(<>)(?:\\s*)([0-9]{1,5}))?\\s*(?:,\\s*(norm|raw))?\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+/*prototypes*/
+static int DetectUrilenSetup (DetectEngineCtx *, Signature *, char *);
+void DetectUrilenFree (void *);
+void DetectUrilenRegisterTests (void);
+
+/**
+ * \brief Registration function for urilen: keyword
+ */
+
+void DetectUrilenRegister(void)
+{
+ sigmatch_table[DETECT_AL_URILEN].name = "urilen";
+ sigmatch_table[DETECT_AL_URILEN].desc = "match on the length of the HTTP uri";
+ sigmatch_table[DETECT_AL_URILEN].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/HTTP-keywords#Urilen";
+ sigmatch_table[DETECT_AL_URILEN].Match = NULL;
+ sigmatch_table[DETECT_AL_URILEN].alproto = ALPROTO_HTTP;
+ sigmatch_table[DETECT_AL_URILEN].AppLayerMatch = NULL /**< We handle this at detect-engine-uri.c now */;
+ sigmatch_table[DETECT_AL_URILEN].Setup = DetectUrilenSetup;
+ sigmatch_table[DETECT_AL_URILEN].Free = DetectUrilenFree;
+ sigmatch_table[DETECT_AL_URILEN].RegisterTests = DetectUrilenRegisterTests;
+ sigmatch_table[DETECT_AL_URILEN].flags |= SIGMATCH_PAYLOAD;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogDebug("pcre compile of \"%s\" failed at offset %" PRId32 ": %s",
+ PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogDebug("pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ if (parse_regex != NULL)
+ pcre_free(parse_regex);
+ if (parse_regex_study != NULL)
+ pcre_free_study(parse_regex_study);
+ return;
+}
+
+/**
+ * \brief This function is used to parse urilen options passed via urilen: keyword
+ *
+ * \param urilenstr Pointer to the user provided urilen options
+ *
+ * \retval urilend pointer to DetectUrilenData on success
+ * \retval NULL on failure
+ */
+
+DetectUrilenData *DetectUrilenParse (char *urilenstr)
+{
+
+ DetectUrilenData *urilend = NULL;
+ char *arg1 = NULL;
+ char *arg2 = NULL;
+ char *arg3 = NULL;
+ char *arg4 = NULL;
+ char *arg5 = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, urilenstr, strlen(urilenstr),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 6) {
+ SCLogError(SC_ERR_PCRE_PARSE, "urilen option pcre parse error: \"%s\"", urilenstr);
+ goto error;
+ }
+ const char *str_ptr;
+
+ SCLogDebug("ret %d", ret);
+
+ res = pcre_get_substring((char *)urilenstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg1 = (char *) str_ptr;
+ SCLogDebug("Arg1 \"%s\"", arg1);
+
+ res = pcre_get_substring((char *)urilenstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg2 = (char *) str_ptr;
+ SCLogDebug("Arg2 \"%s\"", arg2);
+
+ if (ret > 3) {
+ res = pcre_get_substring((char *)urilenstr, ov, MAX_SUBSTRINGS, 3, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg3 = (char *) str_ptr;
+ SCLogDebug("Arg3 \"%s\"", arg3);
+
+ if (ret > 4) {
+ res = pcre_get_substring((char *)urilenstr, ov, MAX_SUBSTRINGS, 4, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg4 = (char *) str_ptr;
+ SCLogDebug("Arg4 \"%s\"", arg4);
+ }
+ if (ret > 5) {
+ res = pcre_get_substring((char *)urilenstr, ov, MAX_SUBSTRINGS, 5, &str_ptr);
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ arg5 = (char *) str_ptr;
+ SCLogDebug("Arg5 \"%s\"", arg5);
+ }
+ }
+
+ urilend = SCMalloc(sizeof (DetectUrilenData));
+ if (unlikely(urilend == NULL))
+ goto error;
+ memset(urilend, 0, sizeof(DetectUrilenData));
+
+ if (arg1[0] == '<')
+ urilend->mode = DETECT_URILEN_LT;
+ else if (arg1[0] == '>')
+ urilend->mode = DETECT_URILEN_GT;
+ else
+ urilend->mode = DETECT_URILEN_EQ;
+
+ if (arg3 != NULL && strcmp("<>", arg3) == 0) {
+ if (strlen(arg1) != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Range specified but mode also set");
+ goto error;
+ }
+ urilend->mode = DETECT_URILEN_RA;
+ }
+
+ /** set the first urilen value */
+ if (ByteExtractStringUint16(&urilend->urilen1,10,strlen(arg2),arg2) <= 0){
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Invalid size :\"%s\"",arg2);
+ goto error;
+ }
+
+ /** set the second urilen value if specified */
+ if (arg4 != NULL && strlen(arg4) > 0) {
+ if (urilend->mode != DETECT_URILEN_RA) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Multiple urilen values specified"
+ " but mode is not range");
+ goto error;
+ }
+
+ if(ByteExtractStringUint16(&urilend->urilen2,10,strlen(arg4),arg4) <= 0)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Invalid size :\"%s\"",arg4);
+ goto error;
+ }
+
+ if (urilend->urilen2 <= urilend->urilen1){
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"urilen2:%"PRIu16" <= urilen:"
+ "%"PRIu16"",urilend->urilen2,urilend->urilen1);
+ goto error;
+ }
+ }
+
+ if (arg5 != NULL) {
+ if (strcasecmp("raw", arg5) == 0) {
+ urilend->raw_buffer = 1;
+ }
+ }
+
+ pcre_free_substring(arg1);
+ pcre_free_substring(arg2);
+ if (arg3 != NULL)
+ pcre_free_substring(arg3);
+ if (arg4 != NULL)
+ pcre_free_substring(arg4);
+ if (arg5 != NULL)
+ pcre_free_substring(arg5);
+ return urilend;
+
+error:
+ if (urilend)
+ SCFree(urilend);
+ if (arg1 != NULL)
+ SCFree(arg1);
+ if (arg2 != NULL)
+ SCFree(arg2);
+ if (arg3 != NULL)
+ SCFree(arg3);
+ if (arg4 != NULL)
+ SCFree(arg4);
+ return NULL;
+}
+
+/**
+ * \brief this function is used to parse urilen data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param urilenstr pointer to the user provided urilen options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectUrilenSetup (DetectEngineCtx *de_ctx, Signature *s, char *urilenstr)
+{
+ SCEnter();
+ DetectUrilenData *urilend = NULL;
+ SigMatch *sm = NULL;
+
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
+ SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains a non http "
+ "alproto set");
+ goto error;
+ }
+
+ urilend = DetectUrilenParse(urilenstr);
+ if (urilend == NULL)
+ goto error;
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->type = DETECT_AL_URILEN;
+ sm->ctx = (void *)urilend;
+
+ if (urilend->raw_buffer)
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRUDMATCH);
+ else
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_UMATCH);
+
+ /* Flagged the signature as to inspect the app layer data */
+ s->flags |= SIG_FLAG_APPLAYER;
+ s->alproto = ALPROTO_HTTP;
+
+ SCReturnInt(0);
+
+error:
+ DetectUrilenFree(urilend);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief this function will free memory associated with DetectUrilenData
+ *
+ * \param ptr pointer to DetectUrilenData
+ */
+void DetectUrilenFree(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ DetectUrilenData *urilend = (DetectUrilenData *)ptr;
+ SCFree(urilend);
+}
+
+#ifdef UNITTESTS
+
+#include "stream.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "app-layer-parser.h"
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest01(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse("10");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_EQ &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest02(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse(" < 10 ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_LT &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest03(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse(" > 10 ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_GT &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest04(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse(" 5 <> 10 ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 5 && urilend->urilen2 == 10 &&
+ urilend->mode == DETECT_URILEN_RA &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest05(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse("5<>10,norm");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 5 && urilend->urilen2 == 10 &&
+ urilend->mode == DETECT_URILEN_RA &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest06(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse("5<>10,raw");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 5 && urilend->urilen2 == 10 &&
+ urilend->mode == DETECT_URILEN_RA &&
+ urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest07(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse(">10, norm ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_GT &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest08(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse("<10, norm ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_LT &&
+ !urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest09(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse(">10, raw ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_GT &&
+ urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/** \test Test the Urilen keyword setup */
+static int DetectUrilenParseTest10(void)
+{
+ int ret = 0;
+ DetectUrilenData *urilend = NULL;
+
+ urilend = DetectUrilenParse("<10, raw ");
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 10 && urilend->mode == DETECT_URILEN_LT &&
+ urilend->raw_buffer)
+ ret = 1;
+
+ DetectUrilenFree(urilend);
+ }
+ return ret;
+}
+
+/**
+ * \brief this function is used to initialize the detection engine context and
+ * setup the signature with passed values.
+ *
+ */
+
+static int DetectUrilenInitTest(DetectEngineCtx **de_ctx, Signature **sig,
+ DetectUrilenData **urilend, char *str)
+{
+ char fullstr[1024];
+ int result = 0;
+
+ *de_ctx = NULL;
+ *sig = NULL;
+
+ if (snprintf(fullstr, 1024, "alert ip any any -> any any (msg:\"Urilen "
+ "test\"; urilen:%s; sid:1;)", str) >= 1024) {
+ goto end;
+ }
+
+ *de_ctx = DetectEngineCtxInit();
+ if (*de_ctx == NULL) {
+ goto end;
+ }
+
+ (*de_ctx)->flags |= DE_QUIET;
+
+ (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
+ if ((*de_ctx)->sig_list == NULL) {
+ goto end;
+ }
+
+ *sig = (*de_ctx)->sig_list;
+
+ *urilend = DetectUrilenParse(str);
+
+ result = 1;
+
+end:
+ return result;
+}
+
+/**
+ * \test DetectUrilenSetpTest01 is a test for setting up an valid urilen values
+ * with valid "<>" operator and include spaces arround the given values.
+ * In the test the values are setup with initializing the detection engine
+ * context and setting up the signature itself.
+ */
+
+static int DetectUrilenSetpTest01(void)
+{
+
+ DetectUrilenData *urilend = NULL;
+ uint8_t res = 0;
+ Signature *sig = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+
+ res = DetectUrilenInitTest(&de_ctx, &sig, &urilend, "1 <> 2 ");
+ if (res == 0) {
+ goto end;
+ }
+
+ if(urilend == NULL)
+ goto cleanup;
+
+ if (urilend != NULL) {
+ if (urilend->urilen1 == 1 && urilend->urilen2 == 2 &&
+ urilend->mode == DETECT_URILEN_RA)
+ res = 1;
+ }
+
+cleanup:
+ if (urilend) SCFree(urilend);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ return res;
+}
+
+/** \test Check a signature with gievn urilen */
+static int DetectUrilenSigTest01(void)
+{
+ int result = 0;
+ Flow f;
+ uint8_t httpbuf1[] = "POST /suricata HTTP/1.0\r\n"
+ "Host: foo.bar.tld\r\n"
+ "\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing urilen\"; "
+ "urilen: <5; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ s = s->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing http_method\"; "
+ "urilen: >5; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ HtpState *htp_state = f.alstate;
+ if (htp_state == NULL) {
+ SCLogDebug("no http state: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if ((PacketAlertCheck(p, 1))) {
+ printf("sid 1 alerted, but should not have: \n");
+ goto end;
+ }
+ if (!PacketAlertCheck(p, 2)) {
+ printf("sid 2 did not alerted, but should have: \n");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL) SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL) DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectUrilen
+ */
+void DetectUrilenRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DetectUrilenParseTest01", DetectUrilenParseTest01, 1);
+ UtRegisterTest("DetectUrilenParseTest02", DetectUrilenParseTest02, 1);
+ UtRegisterTest("DetectUrilenParseTest03", DetectUrilenParseTest03, 1);
+ UtRegisterTest("DetectUrilenParseTest04", DetectUrilenParseTest04, 1);
+ UtRegisterTest("DetectUrilenParseTest05", DetectUrilenParseTest05, 1);
+ UtRegisterTest("DetectUrilenParseTest06", DetectUrilenParseTest06, 1);
+ UtRegisterTest("DetectUrilenParseTest07", DetectUrilenParseTest07, 1);
+ UtRegisterTest("DetectUrilenParseTest08", DetectUrilenParseTest08, 1);
+ UtRegisterTest("DetectUrilenParseTest09", DetectUrilenParseTest09, 1);
+ UtRegisterTest("DetectUrilenParseTest10", DetectUrilenParseTest10, 1);
+ UtRegisterTest("DetectUrilenSetpTest01", DetectUrilenSetpTest01, 1);
+ UtRegisterTest("DetectUrilenSigTest01", DetectUrilenSigTest01, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-urilen.h b/framework/src/suricata/src/detect-urilen.h
new file mode 100644
index 00000000..c853011d
--- /dev/null
+++ b/framework/src/suricata/src/detect-urilen.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersighdahiya@gmail.com>
+ */
+
+#ifndef _DETECT_URILEN_H
+#define _DETECT_URILEN_H
+
+#define DETECT_URILEN_LT 0 /**< "less than" operator */
+#define DETECT_URILEN_GT 1 /**< "greater than" operator */
+#define DETECT_URILEN_RA 2 /**< range operator */
+#define DETECT_URILEN_EQ 3 /**< equal operator */
+
+typedef struct DetectUrilenData_ {
+ uint16_t urilen1; /**< 1st Uri Length value in the signature*/
+ uint16_t urilen2; /**< 2nd Uri Length value in the signature*/
+ uint8_t mode; /**< operator used in the signature */
+ uint8_t raw_buffer;
+}DetectUrilenData;
+
+int DetectUrilenMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t, void *, Signature *, SigMatch *);
+void DetectUrilenRegister(void);
+
+#endif /* _DETECT_URILEN_H */
+
diff --git a/framework/src/suricata/src/detect-window.c b/framework/src/suricata/src/detect-window.c
new file mode 100644
index 00000000..0fb1afb8
--- /dev/null
+++ b/framework/src/suricata/src/detect-window.c
@@ -0,0 +1,367 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements the window keyword.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-window.h"
+#include "flow.h"
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+
+/**
+ * \brief Regex for parsing our window option
+ */
+#define PARSE_REGEX "^\\s*([!])?\\s*([0-9]{1,9}+)\\s*$"
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectWindowMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+int DetectWindowSetup(DetectEngineCtx *, Signature *, char *);
+void DetectWindowRegisterTests(void);
+void DetectWindowFree(void *);
+
+/**
+ * \brief Registration function for window: keyword
+ */
+void DetectWindowRegister (void)
+{
+ sigmatch_table[DETECT_WINDOW].name = "window";
+ sigmatch_table[DETECT_WINDOW].desc = "check for a specific TCP window size";
+ sigmatch_table[DETECT_WINDOW].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Header_keywords#Window";
+ sigmatch_table[DETECT_WINDOW].Match = DetectWindowMatch;
+ sigmatch_table[DETECT_WINDOW].Setup = DetectWindowSetup;
+ sigmatch_table[DETECT_WINDOW].Free = DetectWindowFree;
+ sigmatch_table[DETECT_WINDOW].RegisterTests = DetectWindowRegisterTests;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ #ifdef WINDOW_DEBUG
+ printf("detect-window: Registering window rule option\n");
+ #endif
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+ return;
+
+error:
+ /* XXX */
+ return;
+}
+
+/**
+ * \brief This function is used to match the window size on a packet
+ *
+ * \param t pointer to thread vars
+ * \param det_ctx pointer to the pattern matcher thread
+ * \param p pointer to the current packet
+ * \param m pointer to the sigmatch that we will cast into DetectWindowData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+int DetectWindowMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectWindowData *wd = (const DetectWindowData *)ctx;
+
+ if ( !(PKT_IS_TCP(p)) || wd == NULL || PKT_IS_PSEUDOPKT(p)) {
+ return 0;
+ }
+
+ if ( (!wd->negated && wd->size == TCP_GET_WINDOW(p)) || (wd->negated && wd->size != TCP_GET_WINDOW(p))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief This function is used to parse window options passed via window: keyword
+ *
+ * \param windowstr Pointer to the user provided window options (negation! and size)
+ *
+ * \retval wd pointer to DetectWindowData on success
+ * \retval NULL on failure
+ */
+DetectWindowData *DetectWindowParse(char *windowstr)
+{
+ DetectWindowData *wd = NULL;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(parse_regex, parse_regex_study, windowstr, strlen(windowstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 1 || ret > 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, windowstr);
+ goto error;
+ }
+
+ wd = SCMalloc(sizeof(DetectWindowData));
+ if (unlikely(wd == NULL))
+ goto error;
+
+ if (ret > 1) {
+ char copy_str[128] = "";
+ res = pcre_copy_substring((char *)windowstr, ov, MAX_SUBSTRINGS, 1,
+ copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ /* Detect if it's negated */
+ if (copy_str[0] == '!')
+ wd->negated = 1;
+ else
+ wd->negated = 0;
+
+ if (ret > 2) {
+ res = pcre_copy_substring((char *)windowstr, ov, MAX_SUBSTRINGS, 2,
+ copy_str, sizeof(copy_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ /* Get the window size if it's a valid value (in packets, we
+ * should alert if this doesn't happend from decode) */
+ if (-1 == ByteExtractStringUint16(&wd->size, 10, 0, copy_str)) {
+ goto error;
+ }
+ }
+ }
+
+ return wd;
+
+error:
+ if (wd != NULL)
+ DetectWindowFree(wd);
+ return NULL;
+
+}
+
+/**
+ * \brief this function is used to add the parsed window sizedata into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param windowstr pointer to the user provided window options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+int DetectWindowSetup (DetectEngineCtx *de_ctx, Signature *s, char *windowstr)
+{
+ DetectWindowData *wd = NULL;
+ SigMatch *sm = NULL;
+
+ wd = DetectWindowParse(windowstr);
+ if (wd == NULL) goto error;
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_WINDOW;
+ sm->ctx = (SigMatchCtx *)wd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+ return 0;
+
+error:
+ if (wd != NULL) DetectWindowFree(wd);
+ if (sm != NULL) SCFree(sm);
+ return -1;
+
+}
+
+/**
+ * \brief this function will free memory associated with DetectWindowData
+ *
+ * \param wd pointer to DetectWindowData
+ */
+void DetectWindowFree(void *ptr)
+{
+ DetectWindowData *wd = (DetectWindowData *)ptr;
+ SCFree(wd);
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+
+/**
+ * \test DetectWindowTestParse01 is a test to make sure that we set the size correctly
+ * when given valid window opt
+ */
+int DetectWindowTestParse01 (void)
+{
+ int result = 0;
+ DetectWindowData *wd = NULL;
+ wd = DetectWindowParse("35402");
+ if (wd != NULL &&wd->size==35402) {
+ DetectWindowFree(wd);
+ result = 1;
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectWindowTestParse02 is a test for setting the window opt negated
+ */
+int DetectWindowTestParse02 (void)
+{
+ int result = 0;
+ DetectWindowData *wd = NULL;
+ wd = DetectWindowParse("!35402");
+ if (wd != NULL) {
+ if (wd->negated == 1 && wd->size==35402) {
+ result = 1;
+ } else {
+ printf("expected wd->negated=1 and wd->size=35402\n");
+ }
+ DetectWindowFree(wd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectWindowTestParse03 is a test to check for an empty value
+ */
+int DetectWindowTestParse03 (void)
+{
+ int result = 0;
+ DetectWindowData *wd = NULL;
+ wd = DetectWindowParse("");
+ if (wd == NULL) {
+ result = 1;
+ } else {
+ printf("expected a NULL pointer (It was an empty string)\n");
+ }
+ DetectWindowFree(wd);
+
+ return result;
+}
+
+/**
+ * \test DetectWindowTestParse03 is a test to check for a big value
+ */
+int DetectWindowTestParse04 (void)
+{
+ int result = 0;
+ DetectWindowData *wd = NULL;
+ wd = DetectWindowParse("1235402");
+ if (wd != NULL) {
+ printf("expected a NULL pointer (It was exceeding the MAX window size)\n");
+ DetectWindowFree(wd);
+ }else
+ result=1;
+
+ return result;
+}
+
+/**
+ * \test DetectWindowTestPacket01 is a test to check window with constructed packets
+ */
+int DetectWindowTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ /* TCP wwindow = 40 */
+ p[0]->tcph->th_win = htons(40);
+
+ /* TCP window = 41 */
+ p[1]->tcph->th_win = htons(41);
+
+ char *sigs[2];
+ sigs[0]= "alert tcp any any -> any any (msg:\"Testing window 1\"; window:40; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"Testing window 2\"; window:41; sid:2;)";
+
+ uint32_t sid[2] = {1, 2};
+
+ uint32_t results[3][2] = {
+ /* packet 0 match sid 1 but should not match sid 2 */
+ {1, 0},
+ /* packet 1 should not match */
+ {0, 1},
+ /* packet 2 should not match */
+ {0, 0} };
+ result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 2);
+
+ UTHFreePackets(p, 3);
+end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for DetectWindow
+ */
+void DetectWindowRegisterTests(void)
+{
+ #ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("DetectWindowTestParse01", DetectWindowTestParse01, 1);
+ UtRegisterTest("DetectWindowTestParse02", DetectWindowTestParse02, 1);
+ UtRegisterTest("DetectWindowTestParse03", DetectWindowTestParse03, 1);
+ UtRegisterTest("DetectWindowTestParse04", DetectWindowTestParse04, 1);
+ UtRegisterTest("DetectWindowTestPacket01" , DetectWindowTestPacket01 , 1);
+ #endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-window.h b/framework/src/suricata/src/detect-window.h
new file mode 100644
index 00000000..c51bbae3
--- /dev/null
+++ b/framework/src/suricata/src/detect-window.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __DETECT_WINDOW_H__
+#define __DETECT_WINDOW_H__
+
+#define MIN_WINDOW_VALUE 0
+#define MAX_WINDOW_VALUE 65535
+
+typedef struct DetectWindowData_ {
+ uint8_t negated; /** negated? 1=True : 0=False */
+ uint16_t size; /** window size to match */
+} DetectWindowData;
+
+/* prototypes */
+void DetectWindowRegister (void);
+
+#endif /* __DETECT_WINDOW_H__ */
+
diff --git a/framework/src/suricata/src/detect-within.c b/framework/src/suricata/src/detect-within.c
new file mode 100644
index 00000000..25c97238
--- /dev/null
+++ b/framework/src/suricata/src/detect-within.c
@@ -0,0 +1,292 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implements the within keyword
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-bytejump.h"
+#include "detect-byte-extract.h"
+#include "app-layer.h"
+#include "detect-parse.h"
+
+#include "flow-var.h"
+
+#include "util-debug.h"
+#include "detect-pcre.h"
+#include "util-unittest.h"
+
+static int DetectWithinSetup(DetectEngineCtx *, Signature *, char *);
+void DetectWithinRegisterTests(void);
+
+void DetectWithinRegister(void)
+{
+ sigmatch_table[DETECT_WITHIN].name = "within";
+ sigmatch_table[DETECT_WITHIN].desc = "indicate that this content match has to be within a certain distance of the previous content keyword match";
+ sigmatch_table[DETECT_WITHIN].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#Within";
+ sigmatch_table[DETECT_WITHIN].Match = NULL;
+ sigmatch_table[DETECT_WITHIN].Setup = DetectWithinSetup;
+ sigmatch_table[DETECT_WITHIN].Free = NULL;
+ sigmatch_table[DETECT_WITHIN].RegisterTests = DetectWithinRegisterTests;
+
+ sigmatch_table[DETECT_WITHIN].flags |= SIGMATCH_PAYLOAD;
+}
+
+/** \brief Setup within pattern (content/uricontent) modifier.
+ *
+ * \todo apply to uricontent
+ *
+ * \retval 0 ok
+ * \retval -1 error, sig needs to be invalidated
+ */
+static int DetectWithinSetup(DetectEngineCtx *de_ctx, Signature *s, char *withinstr)
+{
+ char *str = withinstr;
+ char dubbed = 0;
+ SigMatch *pm = NULL;
+ int ret = -1;
+
+ /* strip "'s */
+ if (withinstr[0] == '\"' && withinstr[strlen(withinstr)-1] == '\"') {
+ str = SCStrdup(withinstr+1);
+ if (unlikely(str == NULL))
+ goto end;
+ str[strlen(withinstr) - 2] = '\0';
+ dubbed = 1;
+ }
+
+ /* retrive the sm to apply the depth against */
+ if (s->list != DETECT_SM_LIST_NOTSET) {
+ pm = SigMatchGetLastSMFromLists(s, 2, DETECT_CONTENT, s->sm_lists_tail[s->list]);
+ } else {
+ pm = SigMatchGetLastSMFromLists(s, 28,
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+ DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+ }
+ if (pm == NULL) {
+ SCLogError(SC_ERR_OFFSET_MISSING_CONTENT, "within needs "
+ "preceding content, uricontent option, http_client_body, "
+ "http_server_body, http_header option, http_raw_header option, "
+ "http_method option, http_cookie, http_raw_uri, "
+ "http_stat_msg, http_stat_code, http_user_agent or "
+ "file_data/dce_stub_data sticky buffer option");
+ goto end;
+ }
+
+
+ /* verify other conditions */
+ DetectContentData *cd = (DetectContentData *)pm->ctx;
+ if (cd->flags & DETECT_CONTENT_WITHIN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple withins for the same content.");
+ goto end;
+ }
+ if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative "
+ "keyword like within/distance with a absolute "
+ "relative keyword like depth/offset for the same "
+ "content." );
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "negated keyword set along with a fast_pattern");
+ goto end;
+ }
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+ "keyword set along with a fast_pattern:only;");
+ goto end;
+ }
+ if (str[0] != '-' && isalpha((unsigned char)str[0])) {
+ SigMatch *bed_sm = DetectByteExtractRetrieveSMVar(str, s);
+ if (bed_sm == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "unknown byte_extract var "
+ "seen in within - %s\n", str);
+ goto end;
+ }
+ cd->within = ((DetectByteExtractData *)bed_sm->ctx)->local_id;
+ cd->flags |= DETECT_CONTENT_WITHIN_BE;
+ } else {
+ cd->within = strtol(str, NULL, 10);
+ if (cd->within < (int32_t)cd->content_len) {
+ SCLogError(SC_ERR_WITHIN_INVALID, "within argument \"%"PRIi32"\" is "
+ "less than the content length \"%"PRIu32"\" which is invalid, since "
+ "this will never match. Invalidating signature", cd->within,
+ cd->content_len);
+ goto end;
+ }
+ }
+ cd->flags |= DETECT_CONTENT_WITHIN;
+
+ /* these are the only ones against which we set a flag. We have other
+ * relative keywords like byttest, isdataat, bytejump, but we don't
+ * set a flag against them */
+ SigMatch *prev_pm = SigMatchGetLastSMFromLists(s, 4,
+ DETECT_CONTENT, pm->prev,
+ DETECT_PCRE, pm->prev);
+ if (prev_pm == NULL) {
+ ret = 0;
+ goto end;
+ }
+ if (prev_pm->type == DETECT_CONTENT) {
+ DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "previous keyword "
+ "has a fast_pattern:only; set. Can't "
+ "have relative keywords around a fast_pattern "
+ "only content");
+ goto end;
+ }
+ cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+ } else if (prev_pm->type == DETECT_PCRE) {
+ DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
+ pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+ }
+
+ ret = 0;
+ end:
+ if (dubbed)
+ SCFree(str);
+ return ret;
+}
+
+/***********************************Unittests**********************************/
+
+#ifdef UNITTESTS
+#include "util-unittest-helper.h"
+ /**
+ * \test DetectWithinTestPacket01 is a test to check matches of
+ * within, if the previous keyword is pcre (bug 145)
+ */
+int DetectWithinTestPacket01 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
+ "User-Agent: Wget/1.11.4"
+ "Accept: */*"
+ "Host: www.google.com"
+ "Connection: Keep-Alive"
+ "Date: Mon, 04 Jan 2010 17:29:39 GMT";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with within "
+ "modifier\"; pcre:\"/AllWorkAndNoPlayMakesWillADullBoy/\";"
+ " content:\"HTTP\"; within:5; sid:49; rev:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+
+int DetectWithinTestPacket02 (void)
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Zero Five Ten Fourteen";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ if (p == NULL)
+ goto end;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"pcre with within "
+ "modifier\"; content:\"Five\"; content:\"Ten\"; within:3; distance:1; sid:1;)";
+
+ result = UTHPacketMatchSig(p, sig);
+
+ UTHFreePacket(p);
+end:
+ return result;
+}
+
+static int DetectWithinTestVarSetup(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ char sig[] = "alert tcp any any -> any any ( "
+ "msg:\"test rule\"; "
+ "content:\"abc\"; "
+ "http_client_body; "
+ "byte_extract:2,0,somevar,relative; "
+ "content:\"def\"; "
+ "within:somevar; "
+ "http_client_body; "
+ "sid:4; rev:1;)";
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectWithinRegisterTests(void)
+{
+ #ifdef UNITTESTS
+ UtRegisterTest("DetectWithinTestPacket01", DetectWithinTestPacket01, 1);
+ UtRegisterTest("DetectWithinTestPacket02", DetectWithinTestPacket02, 1);
+ UtRegisterTest("DetectWithinTestVarSetup", DetectWithinTestVarSetup, 1);
+ #endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-within.h b/framework/src/suricata/src/detect-within.h
new file mode 100644
index 00000000..7ccaf264
--- /dev/null
+++ b/framework/src/suricata/src/detect-within.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_WITHIN_H__
+#define __DETECT_WITHIN_H__
+
+/* prototypes */
+void DetectWithinRegister (void);
+
+#endif /* __DETECT_WITHIN_H__ */
+
diff --git a/framework/src/suricata/src/detect-xbits.c b/framework/src/suricata/src/detect-xbits.c
new file mode 100644
index 00000000..876ddc36
--- /dev/null
+++ b/framework/src/suricata/src/detect-xbits.c
@@ -0,0 +1,545 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the xbits keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "detect-xbits.h"
+#include "detect-hostbits.h"
+#include "util-spm.h"
+
+#include "detect-engine-sigorder.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow-bit.h"
+#include "host-bit.h"
+#include "ippair-bit.h"
+#include "util-var-name.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/*
+ xbits:set,bitname,track ip_pair,expire 60
+ */
+
+#define PARSE_REGEX "([a-z]+)" "(?:,\\s*([^,]+))?" "(?:,\\s*(?:track\\s+([^,]+)))" "(?:,\\s*(?:expire\\s+([^,]+)))?"
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+int DetectXbitMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+static int DetectXbitSetup (DetectEngineCtx *, Signature *, char *);
+void DetectXbitFree (void *);
+void XBitsRegisterTests(void);
+
+void DetectXbitsRegister (void)
+{
+ sigmatch_table[DETECT_XBITS].name = "xbits";
+ sigmatch_table[DETECT_XBITS].desc = "operate on bits";
+// sigmatch_table[DETECT_XBITS].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Flow-keywords#Flowbits";
+ sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
+ sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
+ sigmatch_table[DETECT_XBITS].Free = DetectXbitFree;
+ sigmatch_table[DETECT_XBITS].RegisterTests = XBitsRegisterTests;
+ /* this is compatible to ip-only signatures */
+ sigmatch_table[DETECT_XBITS].flags |= SIGMATCH_IPONLY_COMPAT;
+
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ return;
+
+error:
+ return;
+}
+
+static int DetectIPPairbitMatchToggle (Packet *p, const DetectXbitsData *fd)
+{
+ IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
+ if (pair == NULL)
+ return 0;
+
+ IPPairBitToggle(pair,fd->idx,p->ts.tv_sec + fd->expire);
+ IPPairRelease(pair);
+ return 1;
+}
+
+/* return true even if bit not found */
+static int DetectIPPairbitMatchUnset (Packet *p, const DetectXbitsData *fd)
+{
+ IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
+ if (pair == NULL)
+ return 1;
+
+ IPPairBitUnset(pair,fd->idx);
+ IPPairRelease(pair);
+ return 1;
+}
+
+static int DetectIPPairbitMatchSet (Packet *p, const DetectXbitsData *fd)
+{
+ IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
+ if (pair == NULL)
+ return 0;
+
+ IPPairBitSet(pair, fd->idx, p->ts.tv_sec + fd->expire);
+ IPPairRelease(pair);
+ return 1;
+}
+
+static int DetectIPPairbitMatchIsset (Packet *p, const DetectXbitsData *fd)
+{
+ int r = 0;
+ IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
+ if (pair == NULL)
+ return 0;
+
+ r = IPPairBitIsset(pair,fd->idx,p->ts.tv_sec);
+ IPPairRelease(pair);
+ return r;
+}
+
+static int DetectIPPairbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
+{
+ int r = 0;
+ IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
+ if (pair == NULL)
+ return 1;
+
+ r = IPPairBitIsnotset(pair,fd->idx,p->ts.tv_sec);
+ IPPairRelease(pair);
+ return r;
+}
+
+static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd)
+{
+ switch (xd->cmd) {
+ case DETECT_XBITS_CMD_ISSET:
+ return DetectIPPairbitMatchIsset(p,xd);
+ case DETECT_XBITS_CMD_ISNOTSET:
+ return DetectIPPairbitMatchIsnotset(p,xd);
+ case DETECT_XBITS_CMD_SET:
+ return DetectIPPairbitMatchSet(p,xd);
+ case DETECT_XBITS_CMD_UNSET:
+ return DetectIPPairbitMatchUnset(p,xd);
+ case DETECT_XBITS_CMD_TOGGLE:
+ return DetectIPPairbitMatchToggle(p,xd);
+ default:
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown cmd %" PRIu32 "", xd->cmd);
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * returns 0: no match
+ * 1: match
+ * -1: error
+ */
+
+int DetectXbitMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectXbitsData *fd = (const DetectXbitsData *)ctx;
+ if (fd == NULL)
+ return 0;
+
+ switch (fd->type) {
+ case VAR_TYPE_HOST_BIT:
+ return DetectXbitMatchHost(p, (const DetectXbitsData *)fd);
+ break;
+ case VAR_TYPE_IPPAIR_BIT:
+ return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr)
+{
+ DetectXbitsData *cd = NULL;
+ SigMatch *sm = NULL;
+ uint8_t fb_cmd = 0;
+ uint8_t hb_dir = 0;
+#define MAX_SUBSTRINGS 30
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char fb_cmd_str[16] = "", fb_name[256] = "";
+ char hb_dir_str[16] = "";
+ enum VarTypes var_type = VAR_TYPE_NOT_SET;
+ int expire = 30;
+
+ ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret != 2 && ret != 3 && ret != 4 && ret != 5) {
+ SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for xbits.", rawstr);
+ return -1;
+ }
+ SCLogDebug("ret %d, %s", ret, rawstr);
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, fb_cmd_str, sizeof(fb_cmd_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ return -1;
+ }
+
+ if (ret >= 3) {
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, fb_name, sizeof(fb_name));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ if (ret >= 4) {
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 3, hb_dir_str, sizeof(hb_dir_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("hb_dir_str %s", hb_dir_str);
+ if (strlen(hb_dir_str) > 0) {
+ if (strcmp(hb_dir_str, "ip_src") == 0) {
+ hb_dir = DETECT_XBITS_TRACK_IPSRC;
+ var_type = VAR_TYPE_HOST_BIT;
+ } else if (strcmp(hb_dir_str, "ip_dst") == 0) {
+ hb_dir = DETECT_XBITS_TRACK_IPDST;
+ var_type = VAR_TYPE_HOST_BIT;
+ } else if (strcmp(hb_dir_str, "ip_pair") == 0) {
+ hb_dir = DETECT_XBITS_TRACK_IPPAIR;
+ var_type = VAR_TYPE_IPPAIR_BIT;
+ } else {
+ // TODO
+ goto error;
+ }
+ }
+
+ if (ret >= 5) {
+ char expire_str[16] = "";
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 4, expire_str, sizeof(expire_str));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+ SCLogDebug("expire_str %s", expire_str);
+ expire = atoi(expire_str);
+ SCLogDebug("expire %d", expire);
+ }
+ }
+ }
+
+ if (strcmp(fb_cmd_str,"noalert") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_NOALERT;
+ } else if (strcmp(fb_cmd_str,"isset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_ISSET;
+ } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_ISNOTSET;
+ } else if (strcmp(fb_cmd_str,"set") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_SET;
+ } else if (strcmp(fb_cmd_str,"unset") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_UNSET;
+ } else if (strcmp(fb_cmd_str,"toggle") == 0) {
+ fb_cmd = DETECT_XBITS_CMD_TOGGLE;
+ } else {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
+ goto error;
+ }
+
+ switch (fb_cmd) {
+ case DETECT_XBITS_CMD_NOALERT:
+ if (strlen(fb_name) != 0)
+ goto error;
+ s->flags |= SIG_FLAG_NOALERT;
+ return 0;
+ case DETECT_XBITS_CMD_ISNOTSET:
+ case DETECT_XBITS_CMD_ISSET:
+ case DETECT_XBITS_CMD_SET:
+ case DETECT_XBITS_CMD_UNSET:
+ case DETECT_XBITS_CMD_TOGGLE:
+ default:
+ if (strlen(fb_name) == 0)
+ goto error;
+ break;
+ }
+
+ cd = SCMalloc(sizeof(DetectXbitsData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ cd->idx = VariableNameGetIdx(de_ctx, fb_name, var_type);
+ cd->cmd = fb_cmd;
+ cd->tracker = hb_dir;
+ cd->type = var_type;
+ cd->expire = expire;
+
+ SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
+ cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_XBITS;
+ sm->ctx = (void *)cd;
+
+ switch (fb_cmd) {
+ /* case DETECT_XBITS_CMD_NOALERT can't happen here */
+
+ case DETECT_XBITS_CMD_ISNOTSET:
+ case DETECT_XBITS_CMD_ISSET:
+ /* checks, so packet list */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ break;
+
+ case DETECT_XBITS_CMD_SET:
+ case DETECT_XBITS_CMD_UNSET:
+ case DETECT_XBITS_CMD_TOGGLE:
+ /* modifiers, only run when entire sig has matched */
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
+ break;
+ }
+
+ return 0;
+
+error:
+ if (cd != NULL)
+ SCFree(cd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectXbitFree (void *ptr)
+{
+ DetectXbitsData *fd = (DetectXbitsData *)ptr;
+
+ if (fd == NULL)
+ return;
+
+ SCFree(fd);
+}
+
+#ifdef UNITTESTS
+
+static void XBitsTestSetup(void)
+{
+ StorageInit();
+ HostBitInitCtx();
+ IPPairBitInitCtx();
+ StorageFinalize();
+ HostInitConfig(TRUE);
+ IPPairInitConfig(TRUE);
+}
+
+static void XBitsTestShutdown(void)
+{
+ HostCleanup();
+ IPPairCleanup();
+ StorageCleanup();
+}
+
+/**
+ * \test HostBitsTestSig01 is a test for a valid noalert flowbits option
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int XBitsTestSig01(void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ XBitsTestSetup();
+
+ de_ctx = DetectEngineCtxInit();
+
+ if (de_ctx == NULL) {
+ printf("bad de_ctx: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:1;)");
+ if (s == NULL) {
+ printf("bad sig: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ result = 1;
+
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ XBitsTestShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test various options
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+
+static int XBitsTestSig02(void)
+{
+ Signature *s = NULL;
+ ThreadVars th_v;
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 0;
+ int error_count = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:isset,abc,track ip_src; content:\"GET \"; sid:1;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:isnotset,abc,track ip_dst; content:\"GET \"; sid:2;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:3;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:unset,abc,track ip_src; content:\"GET \"; sid:4;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert ip any any -> any any (xbits:toggle,abc,track ip_dst; content:\"GET \"; sid:5;)");
+ if (s == NULL) {
+ error_count++;
+ }
+
+ if (error_count != 0)
+ goto end;
+
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief this function registers unit tests for XBits
+ */
+void XBitsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("XBitsTestSig01", XBitsTestSig01, 1);
+ UtRegisterTest("XBitsTestSig02", XBitsTestSig02, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/detect-xbits.h b/framework/src/suricata/src/detect-xbits.h
new file mode 100644
index 00000000..477aab8c
--- /dev/null
+++ b/framework/src/suricata/src/detect-xbits.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_XBITS_H__
+#define __DETECT_XBITS_H__
+
+#define DETECT_XBITS_CMD_SET 0
+#define DETECT_XBITS_CMD_TOGGLE 1
+#define DETECT_XBITS_CMD_UNSET 2
+#define DETECT_XBITS_CMD_ISNOTSET 3
+#define DETECT_XBITS_CMD_ISSET 4
+#define DETECT_XBITS_CMD_NOALERT 5
+#define DETECT_XBITS_CMD_MAX 6
+
+#define DETECT_XBITS_TRACK_IPSRC 0
+#define DETECT_XBITS_TRACK_IPDST 1
+#define DETECT_XBITS_TRACK_IPPAIR 2
+#define DETECT_XBITS_TRACK_FLOW 3
+
+typedef struct DetectXbitsData_ {
+ uint16_t idx;
+ uint8_t cmd;
+ uint8_t tracker;
+ uint32_t expire;
+ /** data type: host/ippair/flow used for sig sorting in sigorder */
+ enum VarTypes type;
+} DetectXbitsData;
+
+/* prototypes */
+void DetectXbitsRegister (void);
+
+#endif /* __DETECT_XBITS_H__ */
diff --git a/framework/src/suricata/src/detect.c b/framework/src/suricata/src/detect.c
new file mode 100644
index 00000000..a1516200
--- /dev/null
+++ b/framework/src/suricata/src/detect.c
@@ -0,0 +1,12456 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Basic detection engine
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "flow-private.h"
+#include "flow-bit.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "detect-engine-alert.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-address.h"
+#include "detect-engine-proto.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-iponly.h"
+#include "detect-engine-threshold.h"
+
+#include "detect-engine-payload.h"
+#include "detect-engine-dcepayload.h"
+#include "detect-engine-uri.h"
+#include "detect-dns-query.h"
+#include "detect-engine-state.h"
+#include "detect-engine-analyzer.h"
+#include "detect-engine-filedata-smtp.h"
+
+#include "detect-http-cookie.h"
+#include "detect-http-method.h"
+#include "detect-http-ua.h"
+#include "detect-http-hh.h"
+#include "detect-http-hrh.h"
+
+#include "detect-engine-event.h"
+#include "decode.h"
+
+#include "detect-ipopts.h"
+#include "detect-flags.h"
+#include "detect-fragbits.h"
+#include "detect-fragoffset.h"
+#include "detect-gid.h"
+#include "detect-ack.h"
+#include "detect-seq.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-pcre.h"
+#include "detect-depth.h"
+#include "detect-nocase.h"
+#include "detect-rawbytes.h"
+#include "detect-bytetest.h"
+#include "detect-bytejump.h"
+#include "detect-sameip.h"
+#include "detect-l3proto.h"
+#include "detect-ipproto.h"
+#include "detect-within.h"
+#include "detect-distance.h"
+#include "detect-offset.h"
+#include "detect-sid.h"
+#include "detect-priority.h"
+#include "detect-classtype.h"
+#include "detect-reference.h"
+#include "detect-tag.h"
+#include "detect-threshold.h"
+#include "detect-metadata.h"
+#include "detect-msg.h"
+#include "detect-rev.h"
+#include "detect-flow.h"
+#include "detect-window.h"
+#include "detect-ftpbounce.h"
+#include "detect-isdataat.h"
+#include "detect-id.h"
+#include "detect-rpc.h"
+#include "detect-asn1.h"
+#include "detect-filename.h"
+#include "detect-fileext.h"
+#include "detect-filestore.h"
+#include "detect-filemagic.h"
+#include "detect-filemd5.h"
+#include "detect-filesize.h"
+#include "detect-dsize.h"
+#include "detect-flowvar.h"
+#include "detect-flowint.h"
+#include "detect-pktvar.h"
+#include "detect-noalert.h"
+#include "detect-flowbits.h"
+#include "detect-hostbits.h"
+#include "detect-xbits.h"
+#include "detect-csum.h"
+#include "detect-stream_size.h"
+#include "detect-engine-sigorder.h"
+#include "detect-ttl.h"
+#include "detect-fast-pattern.h"
+#include "detect-itype.h"
+#include "detect-icode.h"
+#include "detect-icmp-id.h"
+#include "detect-icmp-seq.h"
+#include "detect-dce-iface.h"
+#include "detect-dce-opnum.h"
+#include "detect-dce-stub-data.h"
+#include "detect-urilen.h"
+#include "detect-detection-filter.h"
+#include "detect-http-client-body.h"
+#include "detect-http-server-body.h"
+#include "detect-http-header.h"
+#include "detect-http-raw-header.h"
+#include "detect-http-uri.h"
+#include "detect-http-raw-uri.h"
+#include "detect-http-stat-msg.h"
+#include "detect-engine-hcbd.h"
+#include "detect-engine-hsbd.h"
+#include "detect-engine-hhd.h"
+#include "detect-engine-hrhd.h"
+#include "detect-engine-hmd.h"
+#include "detect-engine-hcd.h"
+#include "detect-engine-hrud.h"
+#include "detect-engine-hsmd.h"
+#include "detect-engine-hscd.h"
+#include "detect-engine-hua.h"
+#include "detect-engine-hhhd.h"
+#include "detect-engine-hrhhd.h"
+#include "detect-byte-extract.h"
+#include "detect-file-data.h"
+#include "detect-pkt-data.h"
+#include "detect-replace.h"
+#include "detect-tos.h"
+#include "detect-app-layer-event.h"
+#include "detect-lua.h"
+#include "detect-iprep.h"
+#include "detect-geoip.h"
+#include "detect-dns-query.h"
+#include "detect-app-layer-protocol.h"
+#include "detect-template.h"
+
+#include "util-rule-vars.h"
+
+#include "app-layer.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+#include "app-layer-smtp.h"
+#include "detect-tls.h"
+#include "detect-tls-version.h"
+#include "detect-ssh-proto-version.h"
+#include "detect-ssh-software-version.h"
+#include "detect-http-stat-code.h"
+#include "detect-ssl-version.h"
+#include "detect-ssl-state.h"
+#include "detect-modbus.h"
+
+#include "action-globals.h"
+#include "tm-threads.h"
+
+#include "pkt-var.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-inline.h"
+
+#include "util-var-name.h"
+#include "util-classification-config.h"
+#include "util-print.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+#include "util-hashlist.h"
+#include "util-cuda.h"
+#include "util-privs.h"
+#include "util-profiling.h"
+#include "util-validate.h"
+#include "util-optimize.h"
+#include "util-path.h"
+#include "util-mpm-ac.h"
+
+#include "runmodes.h"
+
+#include <glob.h>
+
+extern int rule_reload;
+
+extern int engine_analysis;
+static int fp_engine_analysis_set = 0;
+static int rule_engine_analysis_set = 0;
+
+SigMatch *SigMatchAlloc(void);
+void DetectExitPrintStats(ThreadVars *tv, void *data);
+
+void DbgPrintSigs(DetectEngineCtx *, SigGroupHead *);
+void DbgPrintSigs2(DetectEngineCtx *, SigGroupHead *);
+static void PacketCreateMask(Packet *, SignatureMask *, uint16_t, int, StreamMsg *, int);
+
+/* tm module api functions */
+TmEcode Detect(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode DetectThreadInit(ThreadVars *, void *, void **);
+TmEcode DetectThreadDeinit(ThreadVars *, void *);
+
+void TmModuleDetectRegister (void)
+{
+ tmm_modules[TMM_DETECT].name = "Detect";
+ tmm_modules[TMM_DETECT].ThreadInit = DetectThreadInit;
+ tmm_modules[TMM_DETECT].Func = Detect;
+ tmm_modules[TMM_DETECT].ThreadExitPrintStats = DetectExitPrintStats;
+ tmm_modules[TMM_DETECT].ThreadDeinit = DetectThreadDeinit;
+ tmm_modules[TMM_DETECT].RegisterTests = SigRegisterTests;
+ tmm_modules[TMM_DETECT].cap_flags = 0;
+ tmm_modules[TMM_DETECT].flags = TM_FLAG_DETECT_TM;
+
+ PacketAlertTagInit();
+}
+
+void DetectExitPrintStats(ThreadVars *tv, void *data)
+{
+ DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
+ if (det_ctx == NULL)
+ return;
+}
+
+/**
+ * \brief Create the path if default-rule-path was specified
+ * \param sig_file The name of the file
+ * \retval str Pointer to the string path + sig_file
+ */
+char *DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, char *sig_file)
+{
+ char *defaultpath = NULL;
+ char *path = NULL;
+ char varname[128] = "default-rule-path";
+
+ if (strlen(de_ctx->config_prefix) > 0) {
+ snprintf(varname, sizeof(varname), "%s.default-rule-path",
+ de_ctx->config_prefix);
+ }
+
+ /* Path not specified */
+ if (PathIsRelative(sig_file)) {
+ if (ConfGet(varname, &defaultpath) == 1) {
+ SCLogDebug("Default path: %s", defaultpath);
+ size_t path_len = sizeof(char) * (strlen(defaultpath) +
+ strlen(sig_file) + 2);
+ path = SCMalloc(path_len);
+ if (unlikely(path == NULL))
+ return NULL;
+ strlcpy(path, defaultpath, path_len);
+#if defined OS_WIN32 || defined __CYGWIN__
+ if (path[strlen(path) - 1] != '\\')
+ strlcat(path, "\\\\", path_len);
+#else
+ if (path[strlen(path) - 1] != '/')
+ strlcat(path, "/", path_len);
+#endif
+ strlcat(path, sig_file, path_len);
+ } else {
+ path = SCStrdup(sig_file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ } else {
+ path = SCStrdup(sig_file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ return path;
+}
+
+/**
+ * \brief Load a file with signatures
+ * \param de_ctx Pointer to the detection engine context
+ * \param sig_file Filename to load signatures from
+ * \param goodsigs_tot Will store number of valid signatures in the file
+ * \param badsigs_tot Will store number of invalid signatures in the file
+ * \retval 0 on success, -1 on error
+ */
+static int DetectLoadSigFile(DetectEngineCtx *de_ctx, char *sig_file,
+ int *goodsigs, int *badsigs)
+{
+ Signature *sig = NULL;
+ int good = 0, bad = 0;
+ char line[DETECT_MAX_RULE_SIZE] = "";
+ size_t offset = 0;
+ int lineno = 0, multiline = 0;
+
+ (*goodsigs) = 0;
+ (*badsigs) = 0;
+
+ FILE *fp = fopen(sig_file, "r");
+ if (fp == NULL) {
+ SCLogError(SC_ERR_OPENING_RULE_FILE, "opening rule file %s:"
+ " %s.", sig_file, strerror(errno));
+ return -1;
+ }
+
+ while(fgets(line + offset, (int)sizeof(line) - offset, fp) != NULL) {
+ lineno++;
+ size_t len = strlen(line);
+
+ /* ignore comments and empty lines */
+ if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
+ continue;
+
+ /* Check for multiline rules. */
+ while (len > 0 && isspace((unsigned char)line[--len]));
+ if (line[len] == '\\') {
+ multiline++;
+ offset = len;
+ if (offset < sizeof(line) - 1) {
+ /* We have room for more. */
+ continue;
+ }
+ /* No more room in line buffer, continue, rule will fail
+ * to parse. */
+ }
+
+ /* Check if we have a trailing newline, and remove it */
+ len = strlen(line);
+ if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
+ line[len - 1] = '\0';
+ }
+
+ /* Reset offset. */
+ offset = 0;
+
+ de_ctx->rule_file = sig_file;
+ de_ctx->rule_line = lineno - multiline;
+
+ sig = DetectEngineAppendSig(de_ctx, line);
+ if (sig != NULL) {
+ if (rule_engine_analysis_set || fp_engine_analysis_set) {
+ sig->mpm_sm = RetrieveFPForSigV2(sig);
+ if (fp_engine_analysis_set) {
+ EngineAnalysisFP(sig, line);
+ }
+ if (rule_engine_analysis_set) {
+ EngineAnalysisRules(sig, line);
+ }
+ }
+ SCLogDebug("signature %"PRIu32" loaded", sig->id);
+ good++;
+ } else {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "error parsing signature \"%s\" from "
+ "file %s at line %"PRId32"", line, sig_file, lineno - multiline);
+
+ if (rule_engine_analysis_set) {
+ EngineAnalysisRulesFailure(line, sig_file, lineno - multiline);
+ }
+ bad++;
+ }
+ multiline = 0;
+ }
+ fclose(fp);
+
+ *goodsigs = good;
+ *badsigs = bad;
+ return 0;
+}
+
+/**
+ * \brief Expands wildcards and reads signatures from each matching file
+ * \param de_ctx Pointer to the detection engine context
+ * \param sig_file Filename (or pattern) holding signatures
+ * \retval -1 on error
+ */
+static int ProcessSigFiles(DetectEngineCtx *de_ctx, char *pattern,
+ SigFileLoaderStat *st, int *good_sigs, int *bad_sigs)
+{
+ if (pattern == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "opening rule file null");
+ return -1;
+ }
+
+ glob_t files;
+ int r = glob(pattern, 0, NULL, &files);
+
+ if (r == GLOB_NOMATCH) {
+ SCLogWarning(SC_ERR_NO_RULES, "No rule files match the pattern %s", pattern);
+ ++(st->bad_files);
+ ++(st->total_files);
+ return -1;
+ } else if (r != 0) {
+ SCLogError(SC_ERR_OPENING_RULE_FILE, "error expanding template %s: %s",
+ pattern, strerror(errno));
+ return -1;
+ }
+
+ for (size_t i = 0; i < (size_t)files.gl_pathc; i++) {
+ char *fname = files.gl_pathv[i];
+ SCLogInfo("Loading rule file: %s", fname);
+ r = DetectLoadSigFile(de_ctx, fname, good_sigs, bad_sigs);
+ if (r < 0) {
+ ++(st->bad_files);
+ }
+
+ ++(st->total_files);
+
+ if (*good_sigs == 0) {
+ SCLogWarning(SC_ERR_NO_RULES,
+ "No rules loaded from %s", fname);
+ }
+
+ st->good_sigs_total += *good_sigs;
+ st->bad_sigs_total += *bad_sigs;
+ }
+
+ globfree(&files);
+ return r;
+}
+
+/**
+ * \brief Load signatures
+ * \param de_ctx Pointer to the detection engine context
+ * \param sig_file Filename (or pattern) holding signatures
+ * \param sig_file_exclusive File passed in 'sig_file' should be loaded exclusively.
+ * \retval -1 on error
+ */
+int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, int sig_file_exclusive)
+{
+ SCEnter();
+
+ ConfNode *rule_files;
+ ConfNode *file = NULL;
+ SigFileLoaderStat sig_stat;
+ int ret = 0;
+ char *sfile = NULL;
+ char varname[128] = "rule-files";
+ int good_sigs = 0;
+ int bad_sigs = 0;
+
+ memset(&sig_stat, 0, sizeof(SigFileLoaderStat));
+
+ if (strlen(de_ctx->config_prefix) > 0) {
+ snprintf(varname, sizeof(varname), "%s.rule-files",
+ de_ctx->config_prefix);
+ }
+
+ if (RunmodeGetCurrent() == RUNMODE_ENGINE_ANALYSIS) {
+ fp_engine_analysis_set = SetupFPAnalyzer();
+ rule_engine_analysis_set = SetupRuleAnalyzer();
+ }
+
+ /* ok, let's load signature files from the general config */
+ if (!(sig_file != NULL && sig_file_exclusive == TRUE)) {
+ rule_files = ConfGetNode(varname);
+ if (rule_files != NULL) {
+ if (!ConfNodeIsSequence(rule_files)) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT,
+ "Invalid rule-files configuration section: "
+ "expected a list of filenames.");
+ }
+ else {
+ TAILQ_FOREACH(file, &rule_files->head, next) {
+ sfile = DetectLoadCompleteSigPath(de_ctx, file->val);
+ good_sigs = bad_sigs = 0;
+ ret = ProcessSigFiles(de_ctx, sfile, &sig_stat, &good_sigs, &bad_sigs);
+ SCFree(sfile);
+
+ if (ret != 0 || good_sigs == 0) {
+ if (de_ctx->failure_fatal == 1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* If a Signature file is specified from commandline, parse it too */
+ if (sig_file != NULL) {
+ ret = ProcessSigFiles(de_ctx, sig_file, &sig_stat, &good_sigs, &bad_sigs);
+
+ if (ret != 0) {
+ if (de_ctx->failure_fatal == 1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (good_sigs == 0) {
+ SCLogError(SC_ERR_NO_RULES, "No rules loaded from %s", sig_file);
+
+ if (de_ctx->failure_fatal == 1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* now we should have signatures to work with */
+ if (sig_stat.good_sigs_total <= 0) {
+ if (sig_stat.total_files > 0) {
+ SCLogWarning(SC_ERR_NO_RULES_LOADED, "%d rule files specified, but no rule was loaded at all!", sig_stat.total_files);
+ } else {
+ SCLogInfo("No signatures supplied.");
+ goto end;
+ }
+ } else {
+ /* we report the total of files and rules successfully loaded and failed */
+ SCLogInfo("%" PRId32 " rule files processed. %" PRId32 " rules successfully loaded, %" PRId32 " rules failed",
+ sig_stat.total_files, sig_stat.good_sigs_total, sig_stat.bad_sigs_total);
+ }
+
+ if ((sig_stat.bad_sigs_total || sig_stat.bad_files) && de_ctx->failure_fatal) {
+ ret = -1;
+ goto end;
+ }
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+ SCSigSignatureOrderingModuleCleanup(de_ctx);
+
+ /* Setup the signature group lookup structure and pattern matchers */
+ if (SigGroupBuild(de_ctx) < 0)
+ goto end;
+
+ ret = 0;
+
+ end:
+ if (RunmodeGetCurrent() == RUNMODE_ENGINE_ANALYSIS) {
+ if (rule_engine_analysis_set) {
+ CleanupRuleAnalyzer();
+ }
+ if (fp_engine_analysis_set) {
+ CleanupFPAnalyzer();
+ }
+ }
+
+ DetectParseDupSigHashFree(de_ctx);
+ SCReturnInt(ret);
+}
+
+int SigMatchSignaturesRunPostMatch(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s)
+{
+ /* run the packet match functions */
+ if (s->sm_arrays[DETECT_SM_LIST_POSTMATCH] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
+
+ SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
+ SCLogDebug("running match functions, sm %p", smd);
+
+ if (smd != NULL) {
+ while (1) {
+ KEYWORD_PROFILING_START;
+ (void)sigmatch_table[smd->type].Match(tv, det_ctx, p, s, smd->ctx);
+ KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
+ if (smd->is_last)
+ break;
+ smd++;
+ }
+ }
+ }
+
+ DetectReplaceExecute(p, det_ctx);
+
+ if (s->flags & SIG_FLAG_FILESTORE)
+ DetectFilestorePostMatch(tv, det_ctx, p, s);
+
+ return 1;
+}
+
+/**
+ * \brief Get the SigGroupHead for a packet.
+ *
+ * \param de_ctx detection engine context
+ * \param det_ctx thread detection engine content
+ * \param p packet
+ *
+ * \retval sgh the SigGroupHead or NULL if non applies to the packet
+ */
+SigGroupHead *SigMatchSignaturesGetSgh(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+ SCEnter();
+
+ int f;
+ SigGroupHead *sgh = NULL;
+
+ /* if the packet proto is 0 (not set), we're inspecting it against
+ * the decoder events sgh we have. */
+ if (p->proto == 0 && p->events.cnt > 0) {
+ SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
+ }
+
+ /* select the flow_gh */
+ if (p->flowflags & FLOW_PKT_TOCLIENT)
+ f = 0;
+ else
+ f = 1;
+
+ SCLogDebug("f %d", f);
+ SCLogDebug("IP_GET_IPPROTO(p) %u", IP_GET_IPPROTO(p));
+
+ /* find the right mpm instance */
+ DetectAddress *ag = DetectAddressLookupInHead(de_ctx->flow_gh[f].src_gh[IP_GET_IPPROTO(p)], &p->src);
+ if (ag != NULL) {
+ /* source group found, lets try a dst group */
+ ag = DetectAddressLookupInHead(ag->dst_gh, &p->dst);
+ if (ag != NULL) {
+ if (ag->port == NULL) {
+ SCLogDebug("we don't have ports");
+ sgh = ag->sh;
+ } else {
+ SCLogDebug("we have ports");
+
+ DetectPort *sport = DetectPortLookupGroup(ag->port,p->sp);
+ if (sport != NULL) {
+ DetectPort *dport = DetectPortLookupGroup(sport->dst_ph,p->dp);
+ if (dport != NULL) {
+ sgh = dport->sh;
+ } else {
+ SCLogDebug("no dst port group found for the packet with dp %"PRIu16"", p->dp);
+ }
+ } else {
+ SCLogDebug("no src port group found for the packet with sp %"PRIu16"", p->sp);
+ }
+ }
+ } else {
+ SCLogDebug("no dst address group found for the packet");
+ }
+ } else {
+ SCLogDebug("no src address group found for the packet");
+ }
+
+ SCReturnPtr(sgh, "SigGroupHead");
+}
+
+/** \brief Get the smsgs relevant to this packet
+ *
+ * \param f LOCKED flow
+ * \param p packet
+ * \param flags stream flags
+ */
+static StreamMsg *SigMatchSignaturesGetSmsg(Flow *f, Packet *p, uint8_t flags)
+{
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ StreamMsg *smsg = NULL;
+
+ if (p->proto == IPPROTO_TCP && f->protoctx != NULL && (p->flags & PKT_STREAM_EST)) {
+ TcpSession *ssn = (TcpSession *)f->protoctx;
+
+ /* at stream eof, or in inline mode, inspect all smsg's */
+ if ((flags & STREAM_EOF) || StreamTcpInlineMode()) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ smsg = ssn->toserver_smsg_head;
+ /* deref from the ssn */
+ ssn->toserver_smsg_head = NULL;
+ ssn->toserver_smsg_tail = NULL;
+
+ SCLogDebug("to_server smsg %p at stream eof", smsg);
+ } else {
+ smsg = ssn->toclient_smsg_head;
+ /* deref from the ssn */
+ ssn->toclient_smsg_head = NULL;
+ ssn->toclient_smsg_tail = NULL;
+
+ SCLogDebug("to_client smsg %p at stream eof", smsg);
+ }
+ } else {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ StreamMsg *head = ssn->toserver_smsg_head;
+ if (unlikely(head == NULL)) {
+ SCLogDebug("no smsgs in to_server direction");
+ goto end;
+ }
+
+ /* if the smsg is bigger than the current packet, we will
+ * process the smsg in a later run */
+ if ((head->seq + head->data_len) > (TCP_GET_SEQ(p) + p->payload_len)) {
+ SCLogDebug("smsg ends beyond current packet, skipping for now %"PRIu32">%"PRIu32,
+ (head->seq + head->data_len), (TCP_GET_SEQ(p) + p->payload_len));
+ goto end;
+ }
+
+ smsg = head;
+ /* deref from the ssn */
+ ssn->toserver_smsg_head = NULL;
+ ssn->toserver_smsg_tail = NULL;
+
+ SCLogDebug("to_server smsg %p", smsg);
+ } else {
+ StreamMsg *head = ssn->toclient_smsg_head;
+ if (unlikely(head == NULL))
+ goto end;
+
+ /* if the smsg is bigger than the current packet, we will
+ * process the smsg in a later run */
+ if ((head->seq + head->data_len) > (TCP_GET_SEQ(p) + p->payload_len)) {
+ SCLogDebug("smsg ends beyond current packet, skipping for now %"PRIu32">%"PRIu32,
+ (head->seq + head->data_len), (TCP_GET_SEQ(p) + p->payload_len));
+ goto end;
+ }
+
+ smsg = head;
+ /* deref from the ssn */
+ ssn->toclient_smsg_head = NULL;
+ ssn->toclient_smsg_tail = NULL;
+
+ SCLogDebug("to_client smsg %p", smsg);
+ }
+ }
+ }
+
+end:
+ SCReturnPtr(smsg, "StreamMsg");
+}
+
+static inline void DetectPrefilterMergeSort(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx)
+// SigGroupHead *sgh)
+{
+ SigIntId mpm, nonmpm;
+ det_ctx->match_array_cnt = 0;
+ SigIntId *mpm_ptr = det_ctx->pmq.rule_id_array;
+ SigIntId *nonmpm_ptr = det_ctx->non_mpm_id_array;
+ //SigIntId *nonmpm_ptr = sgh->non_mpm_id_array;
+ uint32_t m_cnt = det_ctx->pmq.rule_id_array_cnt;
+ //uint32_t n_cnt = sgh->non_mpm_id_cnt;
+ uint32_t n_cnt = det_ctx->non_mpm_id_cnt;
+ SCLogDebug("PMQ rule id array count %d", det_ctx->pmq.rule_id_array_cnt);
+// SCLogDebug("SGH non-MPM id count %d", sgh->non_mpm_id_cnt);
+ SigIntId *final_ptr;
+ uint32_t final_cnt;
+ SigIntId id;
+ SigIntId previous_id = (SigIntId)-1;
+ Signature **sig_array = de_ctx->sig_array;
+ Signature **match_array = det_ctx->match_array;
+ Signature *s;
+
+ /* Load first values. */
+ if (likely(m_cnt)) {
+ mpm = *mpm_ptr;
+ } else {
+ /* mpm list is empty */
+ final_ptr = nonmpm_ptr;
+ final_cnt = n_cnt;
+ goto final;
+ }
+ if (likely(n_cnt)) {
+ nonmpm = *nonmpm_ptr;
+ } else {
+ /* non-mpm list is empty. */
+ final_ptr = mpm_ptr;
+ final_cnt = m_cnt;
+ goto final;
+ }
+ while (1) {
+ if (mpm <= nonmpm) {
+ /* Take from mpm list */
+ id = mpm;
+
+ s = sig_array[id];
+ /* As the mpm list can contain duplicates, check for that here. */
+ if (likely(id != previous_id)) {
+ *match_array++ = s;
+ previous_id = id;
+ }
+ if (unlikely(--m_cnt == 0)) {
+ /* mpm list is now empty */
+ final_ptr = nonmpm_ptr;
+ final_cnt = n_cnt;
+ goto final;
+ }
+ mpm_ptr++;
+ mpm = *mpm_ptr;
+ } else {
+ id = nonmpm;
+
+ s = sig_array[id];
+ /* As the mpm list can contain duplicates, check for that here. */
+ if (likely(id != previous_id)) {
+ *match_array++ = s;
+ previous_id = id;
+ }
+ if (unlikely(--n_cnt == 0)) {
+ final_ptr = mpm_ptr;
+ final_cnt = m_cnt;
+ goto final;
+ }
+ nonmpm_ptr++;
+ nonmpm = *nonmpm_ptr;
+ }
+ }
+
+ final: /* Only one list remaining. Just walk that list. */
+
+ while (final_cnt-- > 0) {
+ id = *final_ptr++;
+ s = sig_array[id];
+
+ /* As the mpm list can contain duplicates, check for that here. */
+ if (likely(id != previous_id)) {
+ *match_array++ = s;
+ previous_id = id;
+ }
+ }
+
+ det_ctx->match_array_cnt = match_array - det_ctx->match_array;
+
+ BUG_ON((det_ctx->pmq.rule_id_array_cnt + det_ctx->non_mpm_id_cnt) < det_ctx->match_array_cnt);
+}
+
+/* Return true is the list is sorted smallest to largest */
+static void QuickSortSigIntId(SigIntId *sids, uint32_t n)
+{
+ if (n < 2)
+ return;
+ SigIntId p = sids[n / 2];
+ SigIntId *l = sids;
+ SigIntId *r = sids + n - 1;
+ while (l <= r) {
+ if (*l < p)
+ l++;
+ else if (*r > p)
+ r--;
+ else {
+ SigIntId t = *l;
+ *l = *r;
+ *r = t;
+ l++;
+ r--;
+ }
+ }
+ QuickSortSigIntId(sids, r - sids + 1);
+ QuickSortSigIntId(l, sids + n - l);
+}
+
+#define SMS_USE_FLOW_SGH 0x01
+#define SMS_USED_PM 0x02
+#define SMS_USED_STREAM_PM 0x04
+
+/**
+ * \internal
+ * \brief Run mpm on packet, stream and other buffers based on
+ * alproto, sgh state.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param det_ctx Pointer to the detection engine thread context.
+ * \param smsg The stream segment to inspect for stream mpm.
+ * \param p Packet.
+ * \param flags Flags.
+ * \param alproto Flow alproto.
+ * \param has_state Bool indicating we have (al)state
+ * \param sms_runflags Used to store state by detection engine.
+ */
+static inline void DetectMpmPrefilter(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, StreamMsg *smsg, Packet *p,
+ const uint8_t flags, const AppProto alproto,
+ const int has_state, uint8_t *sms_runflags)
+{
+ /* have a look at the reassembled stream (if any) */
+ if (p->flowflags & FLOW_PKT_ESTABLISHED) {
+ SCLogDebug("p->flowflags & FLOW_PKT_ESTABLISHED");
+
+ /* all http based mpms */
+ if (has_state && alproto == ALPROTO_HTTP) {
+ FLOWLOCK_WRLOCK(p->flow);
+ void *alstate = FlowGetAppState(p->flow);
+ if (alstate == NULL) {
+ SCLogDebug("no alstate");
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ HtpState *htp_state = (HtpState *)alstate;
+ if (htp_state->connp == NULL) {
+ SCLogDebug("no HTTP connp");
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ int tx_progress = 0;
+ uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, alstate);
+ for (; idx < total_txs; idx++) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, idx);
+ if (tx == NULL)
+ continue;
+
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ tx_progress = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags);
+
+ if (tx_progress > HTP_REQUEST_LINE) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_URI) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_URI);
+ DetectUricontentInspectMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_URI);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HRUD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HRUD);
+ DetectEngineRunHttpRawUriMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HRUD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HMD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HMD);
+ DetectEngineRunHttpMethodMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HMD);
+ }
+ }
+
+ if (tx_progress >= HTP_REQUEST_HEADERS) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HHHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HHHD);
+ DetectEngineRunHttpHHMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HHHD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HRHHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HRHHD);
+ DetectEngineRunHttpHRHMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HRHHD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HCD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HCD);
+ DetectEngineRunHttpCookieMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HCD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HUAD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HUAD);
+ DetectEngineRunHttpUAMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HUAD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HHD);
+ DetectEngineRunHttpHeaderMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HHD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HRHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HRHD);
+ DetectEngineRunHttpRawHeaderMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HRHD);
+ }
+ }
+
+ if (tx_progress >= HTP_REQUEST_BODY) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HCBD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HCBD);
+ DetectEngineRunHttpClientBodyMpm(de_ctx, det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HCBD);
+ }
+ }
+ } else { /* implied FLOW_PKT_TOCLIENT */
+ tx_progress = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags);
+
+ if (tx_progress > HTP_RESPONSE_LINE) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HSMD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HSMD);
+ DetectEngineRunHttpStatMsgMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HSMD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HSCD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HSCD);
+ DetectEngineRunHttpStatCodeMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HSCD);
+ }
+ }
+
+ if (tx_progress >= HTP_RESPONSE_HEADERS) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HHD);
+ DetectEngineRunHttpHeaderMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HHD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HRHD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HRHD);
+ DetectEngineRunHttpRawHeaderMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HRHD);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HCD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HCD);
+ DetectEngineRunHttpCookieMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HCD);
+ }
+ }
+
+ if (tx_progress >= HTP_RESPONSE_BODY) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_HSBD) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_HSBD);
+ DetectEngineRunHttpServerBodyMpm(de_ctx, det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_HSBD);
+ }
+ }
+ }
+ } /* for */
+
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ /* all dns based mpms */
+ else if (alproto == ALPROTO_DNS && has_state) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_DNSQUERY) {
+ FLOWLOCK_RDLOCK(p->flow);
+ void *alstate = FlowGetAppState(p->flow);
+ if (alstate == NULL) {
+ SCLogDebug("no alstate");
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(p->flow->proto, alproto, alstate);
+ for (; idx < total_txs; idx++) {
+ void *tx = AppLayerParserGetTx(p->flow->proto, alproto, alstate, idx);
+ if (tx == NULL)
+ continue;
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_DNSQUERY);
+ DetectDnsQueryInspectMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_DNSQUERY);
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+ } else if (alproto == ALPROTO_SMTP && has_state) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_FD_SMTP) {
+ FLOWLOCK_RDLOCK(p->flow);
+ void *alstate = FlowGetAppState(p->flow);
+ if (alstate == NULL) {
+ SCLogDebug("no alstate");
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ SMTPState *smtp_state = (SMTPState *)alstate;
+ uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(p->flow->proto, alproto, alstate);
+ for (; idx < total_txs; idx++) {
+ void *tx = AppLayerParserGetTx(p->flow->proto, alproto, alstate, idx);
+ if (tx == NULL)
+ continue;
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_FD_SMTP);
+ DetectEngineRunSMTPMpm(de_ctx, det_ctx, p->flow, smtp_state, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_FD_SMTP);
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+ }
+
+ if (smsg != NULL && (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_STREAM)) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_STREAM);
+ StreamPatternSearch(det_ctx, p, smsg, flags);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_STREAM);
+
+ *sms_runflags |= SMS_USED_STREAM_PM;
+ } else {
+ SCLogDebug("smsg NULL or no stream mpm for this sgh");
+ }
+ } else {
+ SCLogDebug("NOT p->flowflags & FLOW_PKT_ESTABLISHED");
+ }
+
+ if (p->payload_len > 0 && (!(p->flags & PKT_NOPAYLOAD_INSPECTION))) {
+ if (!(p->flags & PKT_STREAM_ADD) && (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_STREAM)) {
+ *sms_runflags |= SMS_USED_PM;
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_PKT_STREAM);
+ PacketPatternSearchWithStreamCtx(det_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_PKT_STREAM);
+ }
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_PACKET) {
+ /* run the multi packet matcher against the payload of the packet */
+ SCLogDebug("search: (%p, maxlen %" PRIu32 ", sgh->sig_cnt %" PRIu32 ")",
+ det_ctx->sgh, det_ctx->sgh->mpm_content_maxlen, det_ctx->sgh->sig_cnt);
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_PACKET);
+ PacketPatternSearch(det_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_PACKET);
+
+ *sms_runflags |= SMS_USED_PM;
+ }
+ }
+
+ /* UDP DNS inspection is independent of est or not */
+ if (alproto == ALPROTO_DNS && has_state) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ SCLogDebug("mpm inspection");
+ if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_DNSQUERY) {
+ FLOWLOCK_RDLOCK(p->flow);
+ void *alstate = FlowGetAppState(p->flow);
+ if (alstate == NULL) {
+ SCLogDebug("no alstate");
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(p->flow->proto, alproto, alstate);
+ for (; idx < total_txs; idx++) {
+ void *tx = AppLayerParserGetTx(p->flow->proto, alproto, alstate, idx);
+ if (tx == NULL)
+ continue;
+ SCLogDebug("tx %p",tx);
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_DNSQUERY);
+ DetectDnsQueryInspectMpm(det_ctx, p->flow, alstate, flags, tx, idx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_DNSQUERY);
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+ }
+
+ /* Sort the rule list to lets look at pmq.
+ * NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
+ if (det_ctx->pmq.rule_id_array_cnt > 1) {
+ QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
+ }
+}
+
+#ifdef DEBUG
+static void DebugInspectIds(Packet *p, Flow *f, StreamMsg *smsg)
+{
+ SCLogDebug("pcap_cnt %02"PRIu64", %s, %12s, smsg %s",
+ p->pcap_cnt, p->flowflags & FLOW_PKT_TOSERVER ? "toserver" : "toclient",
+ p->flags & PKT_STREAM_EST ? "established" : "stateless",
+ smsg ? "yes" : "no");
+ AppLayerParserStatePrintDetails(f->alparser);
+}
+#endif
+
+static void AlertDebugLogModeSyncFlowbitsNamesToPacketStruct(Packet *p, DetectEngineCtx *de_ctx)
+{
+#define MALLOC_JUMP 5
+
+ int i = 0;
+
+ GenericVar *gv = p->flow->flowvar;
+
+ while (gv != NULL) {
+ i++;
+ gv = gv->next;
+ }
+ if (i == 0)
+ return;
+
+ p->debuglog_flowbits_names_len = i;
+
+ p->debuglog_flowbits_names = SCMalloc(sizeof(char *) *
+ p->debuglog_flowbits_names_len);
+ if (p->debuglog_flowbits_names == NULL) {
+ return;
+ }
+ memset(p->debuglog_flowbits_names, 0,
+ sizeof(char *) * p->debuglog_flowbits_names_len);
+
+ i = 0;
+ gv = p->flow->flowvar;
+ while (gv != NULL) {
+ if (gv->type != DETECT_FLOWBITS) {
+ gv = gv->next;
+ continue;
+ }
+
+ FlowBit *fb = (FlowBit *) gv;
+ char *name = VariableIdxGetName(de_ctx, fb->idx, VAR_TYPE_FLOW_BIT);
+ if (name != NULL) {
+ p->debuglog_flowbits_names[i] = SCStrdup(name);
+ if (p->debuglog_flowbits_names[i] == NULL) {
+ return;
+ }
+ i++;
+ }
+
+ if (i == p->debuglog_flowbits_names_len) {
+ p->debuglog_flowbits_names_len += MALLOC_JUMP;
+ const char **names = SCRealloc(p->debuglog_flowbits_names,
+ sizeof(char *) *
+ p->debuglog_flowbits_names_len);
+ if (names == NULL) {
+ SCFree(p->debuglog_flowbits_names);
+ p->debuglog_flowbits_names = NULL;
+ p->debuglog_flowbits_names_len = 0;
+ return;
+ }
+ p->debuglog_flowbits_names = names;
+ memset(p->debuglog_flowbits_names +
+ p->debuglog_flowbits_names_len - MALLOC_JUMP,
+ 0, sizeof(char *) * MALLOC_JUMP);
+ }
+
+ gv = gv->next;
+ }
+
+ return;
+}
+
+static inline void DetectPrefilterBuildNonMpmList(DetectEngineThreadCtx *det_ctx, SignatureMask mask)
+{
+ uint32_t x = 0;
+ for (x = 0; x < det_ctx->sgh->non_mpm_store_cnt; x++) {
+ /* only if the mask matches this rule can possibly match,
+ * so build the non_mpm array only for match candidates */
+ SignatureMask rule_mask = det_ctx->sgh->non_mpm_store_array[x].mask;
+ if ((rule_mask & mask) == rule_mask) {
+ det_ctx->non_mpm_id_array[det_ctx->non_mpm_id_cnt++] = det_ctx->sgh->non_mpm_store_array[x].id;
+ }
+ }
+}
+
+/**
+ * \brief Signature match function
+ *
+ * \retval 1 one or more signatures matched
+ * \retval 0 no matches were found
+ */
+int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+ uint8_t sms_runflags = 0; /* function flags */
+ uint8_t alert_flags = 0;
+ AppProto alproto = ALPROTO_UNKNOWN;
+#ifdef PROFILING
+ int smatch = 0; /* signature match: 1, no match: 0 */
+#endif
+ uint8_t flow_flags = 0; /* flow/state flags */
+ StreamMsg *smsg = NULL;
+ Signature *s = NULL;
+ Signature *next_s = NULL;
+ uint8_t alversion = 0;
+ int state_alert = 0;
+ int alerts = 0;
+ int app_decoder_events = 0;
+ int has_state = 0; /* do we have an alstate to work with? */
+
+ SCEnter();
+
+ SCLogDebug("pcap_cnt %"PRIu64, p->pcap_cnt);
+
+ p->alerts.cnt = 0;
+ det_ctx->filestore_cnt = 0;
+
+ /* No need to perform any detection on this packet, if the the given flag is set.*/
+ if (p->flags & PKT_NOPACKET_INSPECTION) {
+ SCReturnInt(0);
+ }
+
+ /* Load the Packet's flow early, even though it might not be needed.
+ * Mark as a constant pointer, although the flow can change.
+ */
+ Flow * const pflow = p->flow;
+
+ /* grab the protocol state we will detect on */
+ if (p->flags & PKT_HAS_FLOW) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flow_flags = STREAM_TOSERVER;
+ SCLogDebug("flag STREAM_TOSERVER set");
+ } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ flow_flags = STREAM_TOCLIENT;
+ SCLogDebug("flag STREAM_TOCLIENT set");
+ }
+ SCLogDebug("p->flowflags 0x%02x", p->flowflags);
+
+ if (p->flags & PKT_STREAM_EOF) {
+ flow_flags |= STREAM_EOF;
+ SCLogDebug("STREAM_EOF set");
+ }
+
+ FLOWLOCK_WRLOCK(pflow);
+ {
+ /* store tenant_id in the flow so that we can use it
+ * for creating pseudo packets */
+ if (p->tenant_id > 0 && pflow->tenant_id == 0) {
+ pflow->tenant_id = p->tenant_id;
+ }
+
+ /* live ruleswap check for flow updates */
+ if (pflow->de_ctx_id == 0) {
+ /* first time this flow is inspected, set id */
+ pflow->de_ctx_id = de_ctx->id;
+ } else if (pflow->de_ctx_id != de_ctx->id) {
+ /* first time we inspect flow with this de_ctx, reset */
+ pflow->flags &= ~FLOW_SGH_TOSERVER;
+ pflow->flags &= ~FLOW_SGH_TOCLIENT;
+ pflow->sgh_toserver = NULL;
+ pflow->sgh_toclient = NULL;
+
+ pflow->de_ctx_id = de_ctx->id;
+ GenericVarFree(pflow->flowvar);
+ pflow->flowvar = NULL;
+
+ DetectEngineStateReset(pflow->de_state,
+ (STREAM_TOSERVER|STREAM_TOCLIENT));
+ DetectEngineStateResetTxs(pflow);
+ }
+
+ /* set the iponly stuff */
+ if (pflow->flags & FLOW_TOCLIENT_IPONLY_SET)
+ p->flowflags |= FLOW_PKT_TOCLIENT_IPONLY_SET;
+ if (pflow->flags & FLOW_TOSERVER_IPONLY_SET)
+ p->flowflags |= FLOW_PKT_TOSERVER_IPONLY_SET;
+
+ /* Get the stored sgh from the flow (if any). Make sure we're not using
+ * the sgh for icmp error packets part of the same stream. */
+ if (IP_GET_IPPROTO(p) == pflow->proto) { /* filter out icmp */
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
+ if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
+ det_ctx->sgh = pflow->sgh_toserver;
+ sms_runflags |= SMS_USE_FLOW_SGH;
+ } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
+ det_ctx->sgh = pflow->sgh_toclient;
+ sms_runflags |= SMS_USE_FLOW_SGH;
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
+
+ smsg = SigMatchSignaturesGetSmsg(pflow, p, flow_flags);
+#if 0
+ StreamMsg *tmpsmsg = smsg;
+ while (tmpsmsg) {
+ printf("detect ---start---:\n");
+ PrintRawDataFp(stdout,tmpsmsg->data.data,tmpsmsg->data.data_len);
+ printf("detect ---end---:\n");
+ tmpsmsg = tmpsmsg->next;
+ }
+#endif
+ }
+
+ /* Retrieve the app layer state and protocol and the tcp reassembled
+ * stream chunks. */
+ if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
+ (p->proto == IPPROTO_UDP) ||
+ (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
+ {
+ /* update flow flags with knowledge on disruptions */
+ flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
+ has_state = (FlowGetAppState(pflow) != NULL);
+ alproto = FlowGetAppProtocol(pflow);
+ alversion = AppLayerParserGetStateVersion(pflow->alparser);
+ SCLogDebug("alstate %s, alproto %u", has_state ? "true" : "false", alproto);
+ } else {
+ SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
+ }
+
+ app_decoder_events = AppLayerParserHasDecoderEvents(pflow->proto,
+ pflow->alproto,
+ pflow->alstate,
+ pflow->alparser,
+ flow_flags);
+ }
+ FLOWLOCK_UNLOCK(pflow);
+
+ if (((p->flowflags & FLOW_PKT_TOSERVER) && !(p->flowflags & FLOW_PKT_TOSERVER_IPONLY_SET)) ||
+ ((p->flowflags & FLOW_PKT_TOCLIENT) && !(p->flowflags & FLOW_PKT_TOCLIENT_IPONLY_SET)))
+ {
+ SCLogDebug("testing against \"ip-only\" signatures");
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
+ IPOnlyMatchPacket(th_v, de_ctx, det_ctx, &de_ctx->io_ctx, &det_ctx->io_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
+
+ /* save in the flow that we scanned this direction... locking is
+ * done in the FlowSetIPOnlyFlag function. */
+ FlowSetIPOnlyFlag(pflow, p->flowflags & FLOW_PKT_TOSERVER ? 1 : 0);
+
+ } else if (((p->flowflags & FLOW_PKT_TOSERVER) &&
+ (pflow->flags & FLOW_TOSERVER_IPONLY_SET)) ||
+ ((p->flowflags & FLOW_PKT_TOCLIENT) &&
+ (pflow->flags & FLOW_TOCLIENT_IPONLY_SET)))
+ {
+ /* If we have a drop from IP only module,
+ * we will drop the rest of the flow packets
+ * This will apply only to inline/IPS */
+ if (pflow->flags & FLOW_ACTION_DROP)
+ {
+ alert_flags = PACKET_ALERT_FLAG_DROP_FLOW;
+ PACKET_DROP(p);
+ }
+ }
+
+ if (!(sms_runflags & SMS_USE_FLOW_SGH)) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
+ }
+
+#ifdef DEBUG
+ if (pflow) {
+ SCMutexLock(&pflow->m);
+ DebugInspectIds(p, pflow, smsg);
+ SCMutexUnlock(&pflow->m);
+ }
+#endif
+ } else { /* p->flags & PKT_HAS_FLOW */
+ /* no flow */
+
+ /* Even without flow we should match the packet src/dst */
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
+ IPOnlyMatchPacket(th_v, de_ctx, det_ctx, &de_ctx->io_ctx,
+ &det_ctx->io_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
+ det_ctx->sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
+ }
+
+ /* if we didn't get a sig group head, we
+ * have nothing to do.... */
+ if (det_ctx->sgh == NULL) {
+ SCLogDebug("no sgh for this packet, nothing to match against");
+ goto end;
+ }
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_STATEFUL);
+ /* stateful app layer detection */
+ if ((p->flags & PKT_HAS_FLOW) && has_state) {
+ memset(det_ctx->de_state_sig_array, 0x00, det_ctx->de_state_sig_array_len);
+ int has_inspectable_state = DeStateFlowHasInspectableState(pflow, alproto, alversion, flow_flags);
+ if (has_inspectable_state == 1) {
+ /* initialize to 0(DE_STATE_MATCH_HAS_NEW_STATE) */
+ DeStateDetectContinueDetection(th_v, de_ctx, det_ctx, p, pflow,
+ flow_flags, alproto, alversion);
+ } else if (has_inspectable_state == 2) {
+ /* no inspectable state, so pretend we don't have a state at all */
+ has_state = 0;
+ }
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_STATEFUL);
+
+ /* create our prefilter mask */
+ SignatureMask mask = 0;
+ PacketCreateMask(p, &mask, alproto, has_state, smsg, app_decoder_events);
+
+ /* build and prefilter non_mpm list against the mask of the packet */
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_NONMPMLIST);
+ det_ctx->non_mpm_id_cnt = 0;
+ if (likely(det_ctx->sgh->non_mpm_store_cnt > 0)) {
+ DetectPrefilterBuildNonMpmList(det_ctx, mask);
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_NONMPMLIST);
+
+ /* run the mpm for each type */
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM);
+ DetectMpmPrefilter(de_ctx, det_ctx, smsg, p, flow_flags, alproto, has_state, &sms_runflags);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM);
+#ifdef PROFILING
+ if (th_v) {
+ StatsAddUI64(th_v, det_ctx->counter_mpm_list,
+ (uint64_t)det_ctx->pmq.rule_id_array_cnt);
+ StatsAddUI64(th_v, det_ctx->counter_nonmpm_list,
+ (uint64_t)det_ctx->sgh->non_mpm_store_cnt);
+ /* non mpm sigs after mask prefilter */
+ StatsAddUI64(th_v, det_ctx->counter_fnonmpm_list,
+ (uint64_t)det_ctx->non_mpm_id_cnt);
+ }
+#endif
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PREFILTER);
+ DetectPrefilterMergeSort(de_ctx, det_ctx);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PREFILTER);
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
+ /* inspect the sigs against the packet */
+ /* Prefetch the next signature. */
+ SigIntId match_cnt = det_ctx->match_array_cnt;
+#ifdef PROFILING
+ if (th_v) {
+ StatsAddUI64(th_v, det_ctx->counter_match_list,
+ (uint64_t)match_cnt);
+ }
+#endif
+ Signature **match_array = det_ctx->match_array;
+
+ uint32_t sflags, next_sflags = 0;
+ if (match_cnt) {
+ next_s = *match_array++;
+ next_sflags = next_s->flags;
+ }
+
+ while (match_cnt--) {
+ RULE_PROFILING_START(p);
+ state_alert = 0;
+#ifdef PROFILING
+ smatch = 0;
+#endif
+
+ s = next_s;
+ sflags = next_sflags;
+ if (match_cnt) {
+ next_s = *match_array++;
+ next_sflags = next_s->flags;
+ }
+ uint8_t s_proto_flags = s->proto.flags;
+
+ SCLogDebug("inspecting signature id %"PRIu32"", s->id);
+
+ /* if the sig has alproto and the session as well they should match */
+ if (likely(sflags & SIG_FLAG_APPLAYER)) {
+ if (s->alproto != ALPROTO_UNKNOWN && s->alproto != alproto) {
+ if (s->alproto == ALPROTO_DCERPC) {
+ if (alproto != ALPROTO_SMB && alproto != ALPROTO_SMB2) {
+ SCLogDebug("DCERPC sig, alproto not SMB or SMB2");
+ goto next;
+ }
+ } else {
+ SCLogDebug("alproto mismatch");
+ goto next;
+ }
+ }
+ }
+
+ if (unlikely(sflags & SIG_FLAG_DSIZE)) {
+ if (likely(p->payload_len < s->dsize_low || p->payload_len > s->dsize_high)) {
+ SCLogDebug("kicked out as p->payload_len %u, dsize low %u, hi %u",
+ p->payload_len, s->dsize_low, s->dsize_high);
+ goto next;
+ }
+ }
+
+ /* check for a pattern match of the one pattern in this sig. */
+ if (likely(sflags & (SIG_FLAG_MPM_PACKET|SIG_FLAG_MPM_STREAM|SIG_FLAG_MPM_APPLAYER))) {
+ /* filter out sigs that want pattern matches, but
+ * have no matches */
+ if (!(det_ctx->pmq.pattern_id_bitarray[(s->mpm_pattern_id_div_8)] & s->mpm_pattern_id_mod_8)) {
+ if (sflags & SIG_FLAG_MPM_PACKET) {
+ if (!(sflags & SIG_FLAG_MPM_PACKET_NEG)) {
+ goto next;
+ }
+ } else if (sflags & SIG_FLAG_MPM_STREAM) {
+ /* filter out sigs that want pattern matches, but
+ * have no matches */
+ if (!(sflags & SIG_FLAG_MPM_STREAM_NEG)) {
+ goto next;
+ }
+ } else if (sflags & SIG_FLAG_MPM_APPLAYER) {
+ if (!(sflags & SIG_FLAG_MPM_APPLAYER_NEG)) {
+ goto next;
+ }
+ }
+ }
+ }
+ if (sflags & SIG_FLAG_STATE_MATCH) {
+ if (det_ctx->de_state_sig_array[s->num] & DE_STATE_MATCH_NO_NEW_STATE)
+ goto next;
+ }
+
+ /* check if this signature has a requirement for flowvars of some type
+ * and if so, if we actually have any in the flow. If not, the sig
+ * can't match and we skip it. */
+ if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) {
+ FLOWLOCK_RDLOCK(pflow);
+ int m = pflow->flowvar ? 1 : 0;
+ FLOWLOCK_UNLOCK(pflow);
+
+ /* no flowvars? skip this sig */
+ if (m == 0) {
+ SCLogDebug("skipping sig as the flow has no flowvars and sig "
+ "has SIG_FLAG_REQUIRE_FLOWVAR flag set.");
+ goto next;
+ }
+ }
+
+ if ((s_proto_flags & DETECT_PROTO_IPV4) && !PKT_IS_IPV4(p)) {
+ SCLogDebug("ip version didn't match");
+ goto next;
+ }
+ if ((s_proto_flags & DETECT_PROTO_IPV6) && !PKT_IS_IPV6(p)) {
+ SCLogDebug("ip version didn't match");
+ goto next;
+ }
+
+ if (DetectProtoContainsProto(&s->proto, IP_GET_IPPROTO(p)) == 0) {
+ SCLogDebug("proto didn't match");
+ goto next;
+ }
+
+ /* check the source & dst port in the sig */
+ if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
+ if (!(sflags & SIG_FLAG_DP_ANY)) {
+ if (p->flags & PKT_IS_FRAGMENT)
+ goto next;
+ DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp);
+ if (dport == NULL) {
+ SCLogDebug("dport didn't match.");
+ goto next;
+ }
+ }
+ if (!(sflags & SIG_FLAG_SP_ANY)) {
+ if (p->flags & PKT_IS_FRAGMENT)
+ goto next;
+ DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp);
+ if (sport == NULL) {
+ SCLogDebug("sport didn't match.");
+ goto next;
+ }
+ }
+ } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
+ SCLogDebug("port-less protocol and sig needs ports");
+ goto next;
+ }
+
+ /* check the destination address */
+ if (!(sflags & SIG_FLAG_DST_ANY)) {
+ if (PKT_IS_IPV4(p)) {
+ if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
+ goto next;
+ } else if (PKT_IS_IPV6(p)) {
+ if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
+ goto next;
+ }
+ }
+ /* check the source address */
+ if (!(sflags & SIG_FLAG_SRC_ANY)) {
+ if (PKT_IS_IPV4(p)) {
+ if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
+ goto next;
+ } else if (PKT_IS_IPV6(p)) {
+ if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0)
+ goto next;
+ }
+ }
+
+ /* Check the payload keywords. If we are a MPM sig and we've made
+ * to here, we've had at least one of the patterns match */
+ if (s->sm_arrays[DETECT_SM_LIST_PMATCH] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_PMATCH);
+ /* if we have stream msgs, inspect against those first,
+ * but not for a "dsize" signature */
+ if (sflags & SIG_FLAG_REQUIRE_STREAM) {
+ char pmatch = 0;
+ if (smsg != NULL) {
+ uint8_t pmq_idx = 0;
+ StreamMsg *smsg_inspect = smsg;
+ for ( ; smsg_inspect != NULL; smsg_inspect = smsg_inspect->next, pmq_idx++) {
+ /* filter out sigs that want pattern matches, but
+ * have no matches */
+ if ((sflags & SIG_FLAG_MPM_STREAM) && !(sflags & SIG_FLAG_MPM_STREAM_NEG) &&
+ !(det_ctx->smsg_pmq[pmq_idx].pattern_id_bitarray[(s->mpm_pattern_id_div_8)] & s->mpm_pattern_id_mod_8)) {
+ SCLogDebug("no match in this smsg");
+ continue;
+ }
+
+ if (DetectEngineInspectStreamPayload(de_ctx, det_ctx, s, pflow, smsg_inspect->data, smsg_inspect->data_len) == 1) {
+ SCLogDebug("match in smsg %p", smsg);
+ pmatch = 1;
+ det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH;
+ /* Tell the engine that this reassembled stream can drop the
+ * rest of the pkts with no further inspection */
+ if (s->action & ACTION_DROP)
+ alert_flags |= PACKET_ALERT_FLAG_DROP_FLOW;
+
+ alert_flags |= PACKET_ALERT_FLAG_STREAM_MATCH;
+ break;
+ }
+ }
+
+ } /* if (smsg != NULL) */
+
+ /* no match? then inspect packet payload */
+ if (pmatch == 0) {
+ SCLogDebug("no match in smsg, fall back to packet payload");
+
+ if (!(sflags & SIG_FLAG_REQUIRE_PACKET)) {
+ if (p->flags & PKT_STREAM_ADD)
+ goto next;
+ }
+
+ if (sms_runflags & SMS_USED_PM) {
+ if ((sflags & SIG_FLAG_MPM_PACKET) && !(sflags & SIG_FLAG_MPM_PACKET_NEG) &&
+ !(det_ctx->pmq.pattern_id_bitarray[(s->mpm_pattern_id_div_8)] &
+ s->mpm_pattern_id_mod_8)) {
+ goto next;
+ }
+ if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, pflow, p) != 1) {
+ goto next;
+ }
+ } else {
+ if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, pflow, p) != 1) {
+ goto next;
+ }
+ }
+ }
+ } else {
+ if (sms_runflags & SMS_USED_PM) {
+ if ((sflags & SIG_FLAG_MPM_PACKET) && !(sflags & SIG_FLAG_MPM_PACKET_NEG) &&
+ !(det_ctx->pmq.pattern_id_bitarray[(s->mpm_pattern_id_div_8)] &
+ s->mpm_pattern_id_mod_8)) {
+ goto next;
+ }
+ if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, pflow, p) != 1) {
+ goto next;
+ }
+ } else {
+ if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, pflow, p) != 1)
+ goto next;
+ }
+ }
+ }
+
+ /* run the packet match functions */
+ if (s->sm_arrays[DETECT_SM_LIST_MATCH] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH);
+ SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_MATCH];
+
+ SCLogDebug("running match functions, sm %p", smd);
+ if (smd != NULL) {
+ while (1) {
+ KEYWORD_PROFILING_START;
+ if (sigmatch_table[smd->type].Match(th_v, det_ctx, p, s, smd->ctx) <= 0) {
+ KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
+ goto next;
+ }
+ KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
+ if (smd->is_last)
+ break;
+ smd++;
+ }
+ }
+ }
+
+ SCLogDebug("s->sm_lists[DETECT_SM_LIST_AMATCH] %p, "
+ "s->sm_lists[DETECT_SM_LIST_UMATCH] %p, "
+ "s->sm_lists[DETECT_SM_LIST_DMATCH] %p, "
+ "s->sm_lists[DETECT_SM_LIST_HCDMATCH] %p",
+ s->sm_lists[DETECT_SM_LIST_AMATCH],
+ s->sm_lists[DETECT_SM_LIST_UMATCH],
+ s->sm_lists[DETECT_SM_LIST_DMATCH],
+ s->sm_lists[DETECT_SM_LIST_HCDMATCH]);
+
+ /* consider stateful sig matches */
+ if (sflags & SIG_FLAG_STATE_MATCH) {
+ if (has_state == 0) {
+ SCLogDebug("state matches but no state, we can't match");
+ goto next;
+ }
+
+ SCLogDebug("stateful app layer match inspection starting");
+
+ /* if DeStateDetectStartDetection matches, it's a full
+ * signature match. It will then call PacketAlertAppend
+ * itself, so we can skip it below. This is done so it
+ * can store the tx_id with the alert */
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_STATEFUL);
+ state_alert = DeStateDetectStartDetection(th_v, de_ctx, det_ctx, s,
+ p, pflow, flow_flags, alproto, alversion);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_STATEFUL);
+ if (state_alert == 0)
+ goto next;
+
+ /* match */
+ if (s->action & ACTION_DROP)
+ alert_flags |= PACKET_ALERT_FLAG_DROP_FLOW;
+
+ alert_flags |= PACKET_ALERT_FLAG_STATE_MATCH;
+ }
+
+#ifdef PROFILING
+ smatch = 1;
+#endif
+
+ SigMatchSignaturesRunPostMatch(th_v, de_ctx, det_ctx, p, s);
+
+ if (!(sflags & SIG_FLAG_NOALERT)) {
+ /* stateful sigs call PacketAlertAppend from DeStateDetectStartDetection */
+ if (!state_alert)
+ PacketAlertAppend(det_ctx, s, p, 0, alert_flags);
+ } else {
+ /* apply actions even if not alerting */
+ DetectSignatureApplyActions(p, s);
+ }
+ alerts++;
+next:
+ DetectFlowvarProcessList(det_ctx, pflow);
+ DetectReplaceFree(det_ctx);
+ RULE_PROFILING_END(det_ctx, s, smatch, p);
+
+ det_ctx->flags = 0;
+ continue;
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
+
+end:
+#ifdef __SC_CUDA_SUPPORT__
+ CudaReleasePacket(p);
+#endif
+
+ /* see if we need to increment the inspect_id and reset the de_state */
+ if (has_state && AppLayerParserProtocolSupportsTxs(p->proto, alproto)) {
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_STATEFUL);
+ DeStateUpdateInspectTransactionId(pflow, flow_flags);
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_STATEFUL);
+ }
+
+ /* so now let's iterate the alerts and remove the ones after a pass rule
+ * matched (if any). This is done inside PacketAlertFinalize() */
+ /* PR: installed "tag" keywords are handled after the threshold inspection */
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT);
+ PacketAlertFinalize(de_ctx, det_ctx, p);
+ if (p->alerts.cnt > 0) {
+ StatsAddUI64(th_v, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
+
+ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP);
+ /* cleanup pkt specific part of the patternmatcher */
+ PacketPatternCleanup(th_v, det_ctx);
+
+ DetectEngineCleanHCBDBuffers(det_ctx);
+ DetectEngineCleanHSBDBuffers(det_ctx);
+ DetectEngineCleanHHDBuffers(det_ctx);
+
+ /* store the found sgh (or NULL) in the flow to save us from looking it
+ * up again for the next packet. Also return any stream chunk we processed
+ * to the pool. */
+ if (p->flags & PKT_HAS_FLOW) {
+ if (sms_runflags & SMS_USED_STREAM_PM) {
+ StreamPatternCleanup(th_v, det_ctx, smsg);
+ }
+
+ FLOWLOCK_WRLOCK(pflow);
+ if (debuglog_enabled) {
+ if (p->alerts.cnt > 0) {
+ AlertDebugLogModeSyncFlowbitsNamesToPacketStruct(p, de_ctx);
+ }
+ }
+
+ if (!(sms_runflags & SMS_USE_FLOW_SGH)) {
+ if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) {
+ /* first time we see this toserver sgh, store it */
+ pflow->sgh_toserver = det_ctx->sgh;
+ pflow->flags |= FLOW_SGH_TOSERVER;
+
+ /* see if this sgh requires us to consider file storing */
+ if (pflow->sgh_toserver == NULL || pflow->sgh_toserver->filestore_cnt == 0) {
+ FileDisableStoring(pflow, STREAM_TOSERVER);
+ }
+
+ /* see if this sgh requires us to consider file magic */
+ if (!FileForceMagic() && (pflow->sgh_toserver == NULL ||
+ !(pflow->sgh_toserver->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)))
+ {
+ SCLogDebug("disabling magic for flow");
+ FileDisableMagic(pflow, STREAM_TOSERVER);
+ }
+
+ /* see if this sgh requires us to consider file md5 */
+ if (!FileForceMd5() && (pflow->sgh_toserver == NULL ||
+ !(pflow->sgh_toserver->flags & SIG_GROUP_HEAD_HAVEFILEMD5)))
+ {
+ SCLogDebug("disabling md5 for flow");
+ FileDisableMd5(pflow, STREAM_TOSERVER);
+ }
+
+ /* see if this sgh requires us to consider filesize */
+ if (pflow->sgh_toserver == NULL ||
+ !(pflow->sgh_toserver->flags & SIG_GROUP_HEAD_HAVEFILESIZE))
+ {
+ SCLogDebug("disabling filesize for flow");
+ FileDisableFilesize(pflow, STREAM_TOSERVER);
+ }
+ } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
+ pflow->sgh_toclient = det_ctx->sgh;
+ pflow->flags |= FLOW_SGH_TOCLIENT;
+
+ if (pflow->sgh_toclient == NULL || pflow->sgh_toclient->filestore_cnt == 0) {
+ FileDisableStoring(pflow, STREAM_TOCLIENT);
+ }
+
+ /* check if this flow needs magic, if not disable it */
+ if (!FileForceMagic() && (pflow->sgh_toclient == NULL ||
+ !(pflow->sgh_toclient->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)))
+ {
+ SCLogDebug("disabling magic for flow");
+ FileDisableMagic(pflow, STREAM_TOCLIENT);
+ }
+
+ /* check if this flow needs md5, if not disable it */
+ if (!FileForceMd5() && (pflow->sgh_toclient == NULL ||
+ !(pflow->sgh_toclient->flags & SIG_GROUP_HEAD_HAVEFILEMD5)))
+ {
+ SCLogDebug("disabling md5 for flow");
+ FileDisableMd5(pflow, STREAM_TOCLIENT);
+ }
+
+ /* see if this sgh requires us to consider filesize */
+ if (pflow->sgh_toclient == NULL ||
+ !(pflow->sgh_toclient->flags & SIG_GROUP_HEAD_HAVEFILESIZE))
+ {
+ SCLogDebug("disabling filesize for flow");
+ FileDisableFilesize(pflow, STREAM_TOCLIENT);
+ }
+ }
+ }
+
+ /* if we had no alerts that involved the smsgs,
+ * we can get rid of them now. */
+ StreamMsgReturnListToPool(smsg);
+
+ FLOWLOCK_UNLOCK(pflow);
+ }
+ PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
+
+ SCReturnInt((int)(alerts > 0));
+}
+
+/** \brief Apply action(s) and Set 'drop' sig info,
+ * if applicable */
+void DetectSignatureApplyActions(Packet *p, const Signature *s)
+{
+ PACKET_UPDATE_ACTION(p, s->action);
+
+ if (s->action & ACTION_DROP) {
+ if (p->alerts.drop.action == 0) {
+ p->alerts.drop.num = s->num;
+ p->alerts.drop.action = s->action;
+ p->alerts.drop.s = (Signature *)s;
+ }
+ }
+}
+
+/* tm module api functions */
+
+static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
+{
+ /* technically we need to pass a DetectEngineThreadCtx struct with the
+ * tentant_id member. But as that member is the first in the struct, we
+ * can use the id directly. */
+ return HashTableLookup(h, &id, 0);
+}
+
+/** \brief Detection engine thread wrapper.
+ * \param tv thread vars
+ * \param p packet to inspect
+ * \param data thread specific data
+ * \param pq packet queue
+ * \retval TM_ECODE_FAILED error
+ * \retval TM_ECODE_OK ok
+ */
+TmEcode Detect(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ DEBUG_VALIDATE_PACKET(p);
+
+ /* No need to perform any detection on this packet, if the the given flag is set.*/
+ if ((p->flags & PKT_NOPACKET_INSPECTION) ||
+ (PACKET_TEST_ACTION(p, ACTION_DROP)))
+ {
+ /* hack: if we are in pass the entire flow mode, we need to still
+ * update the inspect_id forward. So test for the condition here,
+ * and call the update code if necessary. */
+ if (p->flow) {
+ uint8_t flags = 0;
+ FLOWLOCK_RDLOCK(p->flow);
+ int pass = ((p->flow->flags & FLOW_NOPACKET_INSPECTION));
+ flags = FlowGetDisruptionFlags(p->flow, flags);
+ AppProto alproto = FlowGetAppProtocol(p->flow);
+ FLOWLOCK_UNLOCK(p->flow);
+ if (pass && AppLayerParserProtocolSupportsTxs(p->proto, alproto)) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flags |= STREAM_TOSERVER;
+ } else {
+ flags |= STREAM_TOCLIENT;
+ }
+ DeStateUpdateInspectTransactionId(p->flow, flags);
+ }
+ }
+ return 0;
+ }
+
+ DetectEngineCtx *de_ctx = NULL;
+ DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
+ if (det_ctx == NULL) {
+ printf("ERROR: Detect has no thread ctx\n");
+ goto error;
+ }
+
+ if (SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0) {
+ (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
+ SCLogDebug("Detect Engine using new det_ctx - %p",
+ det_ctx);
+ }
+
+ if (det_ctx->TenantGetId != NULL) {
+ /* in MT mode, but no tenants registered yet */
+ if (det_ctx->mt_det_ctxs_cnt == 0) {
+ return TM_ECODE_OK;
+ }
+
+ uint32_t tenant_id = p->tenant_id;
+ if (tenant_id == 0)
+ tenant_id = det_ctx->TenantGetId(det_ctx, p);
+ if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
+ p->tenant_id = tenant_id;
+ det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
+ if (det_ctx == NULL)
+ return TM_ECODE_OK;
+ de_ctx = det_ctx->de_ctx;
+ if (de_ctx == NULL)
+ return TM_ECODE_OK;
+
+ if (SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0) {
+ (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
+ SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
+ }
+ } else {
+ return TM_ECODE_OK;
+ }
+ } else {
+ de_ctx = det_ctx->de_ctx;
+ }
+
+ /* see if the packet matches one or more of the sigs */
+ int r = SigMatchSignatures(tv,de_ctx,det_ctx,p);
+ if (r >= 0) {
+ return TM_ECODE_OK;
+ }
+
+error:
+ return TM_ECODE_FAILED;
+}
+
+TmEcode DetectThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ return DetectEngineThreadCtxInit(t,initdata,data);
+}
+
+TmEcode DetectThreadDeinit(ThreadVars *t, void *data)
+{
+ return DetectEngineThreadCtxDeinit(t,data);
+}
+
+void SigCleanSignatures(DetectEngineCtx *de_ctx)
+{
+ Signature *s = NULL, *ns;
+
+ if (de_ctx == NULL)
+ return;
+
+ for (s = de_ctx->sig_list; s != NULL;) {
+ ns = s->next;
+ SigFree(s);
+ s = ns;
+ }
+
+ de_ctx->sig_list = NULL;
+
+ DetectEngineResetMaxSigId(de_ctx);
+ de_ctx->sig_list = NULL;
+}
+
+/** \brief Find a specific signature by sid and gid
+ * \param de_ctx detection engine ctx
+ * \param sid the signature id
+ * \param gid the signature group id
+ *
+ * \retval s sig found
+ * \retval NULL sig not found
+ */
+Signature *SigFindSignatureBySidGid(DetectEngineCtx *de_ctx, uint32_t sid, uint32_t gid)
+{
+ Signature *s = NULL;
+
+ if (de_ctx == NULL)
+ return NULL;
+
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->id == sid && s->gid == gid)
+ return s;
+ }
+
+ return NULL;
+}
+
+
+int SignatureIsAppLayer(DetectEngineCtx *de_ctx, Signature *s)
+{
+ if (s->alproto != 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Check if a signature contains the filestore keyword.
+ *
+ * \param s signature
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int SignatureIsFilestoring(Signature *s)
+{
+ if (s == NULL)
+ return 0;
+
+ if (s->flags & SIG_FLAG_FILESTORE)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Check if a signature contains the filemagic keyword.
+ *
+ * \param s signature
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int SignatureIsFilemagicInspecting(Signature *s)
+{
+ if (s == NULL)
+ return 0;
+
+ if (s->file_flags & FILE_SIG_NEED_MAGIC)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Check if a signature contains the filemd5 keyword.
+ *
+ * \param s signature
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int SignatureIsFileMd5Inspecting(Signature *s)
+{
+ if (s == NULL)
+ return 0;
+
+ if (s->file_flags & FILE_SIG_NEED_MD5)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Check if a signature contains the filesize keyword.
+ *
+ * \param s signature
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int SignatureIsFilesizeInspecting(Signature *s)
+{
+ if (s == NULL)
+ return 0;
+
+ if (s->file_flags & FILE_SIG_NEED_SIZE)
+ return 1;
+
+ return 0;
+}
+
+/** \brief Test is a initialized signature is IP only
+ * \param de_ctx detection engine ctx
+ * \param s the signature
+ * \retval 1 sig is ip only
+ * \retval 0 sig is not ip only
+ */
+int SignatureIsIPOnly(DetectEngineCtx *de_ctx, Signature *s)
+{
+ if (s->alproto != ALPROTO_UNKNOWN)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HCDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHHDMATCH] != NULL)
+ return 0;
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL)
+ return 0;
+
+ /* TMATCH list can be ignored, it contains TAGs and
+ * tags are compatible to IP-only. */
+
+ IPOnlyCIDRItem *cidr_item;
+ cidr_item = s->CidrSrc;
+ while (cidr_item != NULL) {
+ if (cidr_item->negated)
+ return 0;
+
+ cidr_item = cidr_item->next;
+ }
+ cidr_item = s->CidrDst;
+ while (cidr_item != NULL) {
+ if (cidr_item->negated)
+ return 0;
+
+ cidr_item = cidr_item->next;
+ }
+
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ if (sm == NULL)
+ goto iponly;
+
+ for ( ; sm != NULL; sm = sm->next) {
+ if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT))
+ return 0;
+ /* we have enabled flowbits to be compatible with ip only sigs, as long
+ * as the sig only has a "set" flowbits */
+ if (sm->type == DETECT_FLOWBITS &&
+ (((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) {
+ return 0;
+ }
+ }
+
+iponly:
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("IP-ONLY (%" PRIu32 "): source %s, dest %s", s->id,
+ s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET",
+ s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET");
+ }
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief Check if the initialized signature is inspecting the packet payload
+ * \param de_ctx detection engine ctx
+ * \param s the signature
+ * \retval 1 sig is inspecting the payload
+ * \retval 0 sig is not inspecting the payload
+ */
+static int SignatureIsInspectingPayload(DetectEngineCtx *de_ctx, Signature *s)
+{
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ return 1;
+ }
+#if 0
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ if (sm == NULL)
+ return 0;
+
+ for (; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].flags & SIGMATCH_PAYLOAD) {
+ if (!(de_ctx->flags & DE_QUIET))
+ SCLogDebug("Signature (%" PRIu32 "): is inspecting payload.", s->id);
+ return 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief check if a signature is decoder event matching only
+ * \param de_ctx detection engine
+ * \param s the signature to test
+ * \retval 0 not a DEOnly sig
+ * \retval 1 DEOnly sig
+ */
+static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, Signature *s)
+{
+ if (s->alproto != ALPROTO_UNKNOWN) {
+ SCReturnInt(0);
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HCDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL ||
+ s->sm_lists[DETECT_SM_LIST_HRHHDMATCH] != NULL)
+ {
+ SCReturnInt(0);
+ }
+
+ /* check for conflicting keywords */
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ for ( ;sm != NULL; sm = sm->next) {
+ if ( !(sigmatch_table[sm->type].flags & SIGMATCH_DEONLY_COMPAT))
+ SCReturnInt(0);
+ }
+
+ /* need at least one decode event keyword to be considered decode event. */
+ sm = s->sm_lists[DETECT_SM_LIST_MATCH];
+ for ( ;sm != NULL; sm = sm->next) {
+ if (sm->type == DETECT_DECODE_EVENT)
+ goto deonly;
+ if (sm->type == DETECT_ENGINE_EVENT)
+ goto deonly;
+ if (sm->type == DETECT_STREAM_EVENT)
+ goto deonly;
+ }
+
+ SCReturnInt(0);
+
+deonly:
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("DE-ONLY (%" PRIu32 "): source %s, dest %s", s->id,
+ s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET",
+ s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET");
+ }
+
+ SCReturnInt(1);
+}
+
+#define MASK_TCP_INITDEINIT_FLAGS (TH_SYN|TH_RST|TH_FIN)
+#define MASK_TCP_UNUSUAL_FLAGS (TH_URG|TH_ECN|TH_CWR)
+
+/* Create mask for this packet + it's flow if it has one
+ *
+ * Sets SIG_MASK_REQUIRE_PAYLOAD, SIG_MASK_REQUIRE_FLOW,
+ * SIG_MASK_REQUIRE_HTTP_STATE, SIG_MASK_REQUIRE_DCE_STATE
+ */
+static void
+PacketCreateMask(Packet *p, SignatureMask *mask, AppProto alproto, int has_state, StreamMsg *smsg,
+ int app_decoder_events)
+{
+ /* no payload inspect flag doesn't apply to smsg */
+ if (smsg != NULL || (!(p->flags & PKT_NOPAYLOAD_INSPECTION) && p->payload_len > 0)) {
+ SCLogDebug("packet has payload");
+ (*mask) |= SIG_MASK_REQUIRE_PAYLOAD;
+ } else {
+ SCLogDebug("packet has no payload");
+ (*mask) |= SIG_MASK_REQUIRE_NO_PAYLOAD;
+ }
+
+ if (p->events.cnt > 0 || app_decoder_events != 0 || p->app_layer_events != NULL) {
+ SCLogDebug("packet/flow has events set");
+ (*mask) |= SIG_MASK_REQUIRE_ENGINE_EVENT;
+ }
+
+ if (PKT_IS_TCP(p)) {
+ if ((p->tcph->th_flags & MASK_TCP_INITDEINIT_FLAGS) != 0) {
+ (*mask) |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
+ }
+ if ((p->tcph->th_flags & MASK_TCP_UNUSUAL_FLAGS) != 0) {
+ (*mask) |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
+ }
+ }
+
+ if (p->flags & PKT_HAS_FLOW) {
+ SCLogDebug("packet has flow");
+ (*mask) |= SIG_MASK_REQUIRE_FLOW;
+
+ if (has_state) {
+ switch(alproto) {
+ case ALPROTO_HTTP:
+ SCLogDebug("packet/flow has http state");
+ (*mask) |= SIG_MASK_REQUIRE_HTTP_STATE;
+ break;
+ case ALPROTO_SMB:
+ case ALPROTO_SMB2:
+ case ALPROTO_DCERPC:
+ SCLogDebug("packet/flow has dce state");
+ (*mask) |= SIG_MASK_REQUIRE_DCE_STATE;
+ break;
+ case ALPROTO_SSH:
+ SCLogDebug("packet/flow has ssh state");
+ (*mask) |= SIG_MASK_REQUIRE_SSH_STATE;
+ break;
+ case ALPROTO_TLS:
+ SCLogDebug("packet/flow has tls state");
+ (*mask) |= SIG_MASK_REQUIRE_TLS_STATE;
+ break;
+ case ALPROTO_DNS:
+ SCLogDebug("packet/flow has dns state");
+ (*mask) |= SIG_MASK_REQUIRE_DNS_STATE;
+ break;
+ case ALPROTO_FTP:
+ SCLogDebug("packet/flow has ftp state");
+ (*mask) |= SIG_MASK_REQUIRE_FTP_STATE;
+ break;
+ case ALPROTO_SMTP:
+ SCLogDebug("packet/flow has smtp state");
+ (*mask) |= SIG_MASK_REQUIRE_SMTP_STATE;
+ break;
+ default:
+ SCLogDebug("packet/flow has other state");
+ break;
+ }
+ } else {
+ SCLogDebug("no alstate");
+ }
+ }
+}
+
+static int SignatureCreateMask(Signature *s)
+{
+ SCEnter();
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
+ SCLogDebug("sig requires payload");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_DMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_DCE_STATE;
+ SCLogDebug("sig requires dce state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL) {
+ /* set the state depending from the protocol */
+ if (s->alproto == ALPROTO_HTTP)
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ else if (s->alproto == ALPROTO_SMTP)
+ s->mask |= SIG_MASK_REQUIRE_SMTP_STATE;
+
+ SCLogDebug("sig requires http or smtp app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HCDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_HRHHDMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires http app state");
+ }
+
+ SigMatch *sm;
+ for (sm = s->sm_lists[DETECT_SM_LIST_AMATCH] ; sm != NULL; sm = sm->next) {
+ switch(sm->type) {
+ case DETECT_AL_URILEN:
+ case DETECT_AL_HTTP_URI:
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig requires dce http state");
+ break;
+ case DETECT_AL_APP_LAYER_EVENT:
+ s->mask |= SIG_MASK_REQUIRE_ENGINE_EVENT;
+ break;
+ }
+ }
+
+ for (sm = s->sm_lists[DETECT_SM_LIST_APP_EVENT] ; sm != NULL; sm = sm->next) {
+ switch (sm->type) {
+ case DETECT_AL_APP_LAYER_EVENT:
+ {
+ DetectAppLayerEventData *aed = (DetectAppLayerEventData *)sm->ctx;
+ switch (aed->alproto) {
+ case ALPROTO_HTTP:
+ s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+ SCLogDebug("sig %u requires http app state (http event)", s->id);
+ break;
+ case ALPROTO_SMTP:
+ s->mask |= SIG_MASK_REQUIRE_SMTP_STATE;
+ SCLogDebug("sig %u requires smtp app state (smtp event)", s->id);
+ break;
+ case ALPROTO_DNS:
+ s->mask |= SIG_MASK_REQUIRE_DNS_STATE;
+ SCLogDebug("sig %u requires dns app state (dns event)", s->id);
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ for (sm = s->sm_lists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
+ switch(sm->type) {
+ case DETECT_FLOWBITS:
+ {
+ /* figure out what flowbit action */
+ DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
+ if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
+ /* not a mask flag, but still set it here */
+ s->flags |= SIG_FLAG_REQUIRE_FLOWVAR;
+
+ SCLogDebug("SIG_FLAG_REQUIRE_FLOWVAR set as sig has "
+ "flowbit isset option.");
+ }
+
+ /* flow is required for any flowbit manipulation */
+ s->mask |= SIG_MASK_REQUIRE_FLOW;
+ SCLogDebug("sig requires flow to be able to manipulate "
+ "flowbit(s)");
+ break;
+ }
+ case DETECT_FLAGS:
+ {
+ DetectFlagsData *fl = (DetectFlagsData *)sm->ctx;
+
+ if (fl->flags & TH_SYN) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_INITDEINIT");
+ }
+ if (fl->flags & TH_RST) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_INITDEINIT");
+ }
+ if (fl->flags & TH_FIN) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_INITDEINIT");
+ }
+ if (fl->flags & TH_URG) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_UNUSUAL");
+ }
+ if (fl->flags & TH_ECN) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_UNUSUAL");
+ }
+ if (fl->flags & TH_CWR) {
+ s->mask |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
+ SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_UNUSUAL");
+ }
+ break;
+ }
+ case DETECT_DSIZE:
+ {
+ DetectDsizeData *ds = (DetectDsizeData *)sm->ctx;
+ switch (ds->mode) {
+ case DETECTDSIZE_LT:
+ /* LT will include 0, so no payload.
+ * if GT is used in the same rule the
+ * flag will be set anyway. */
+ break;
+ case DETECTDSIZE_RA:
+ case DETECTDSIZE_GT:
+ s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
+ SCLogDebug("sig requires payload");
+ break;
+ case DETECTDSIZE_EQ:
+ if (ds->dsize > 0) {
+ s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
+ SCLogDebug("sig requires payload");
+ } else if (ds->dsize == 0) {
+ s->mask |= SIG_MASK_REQUIRE_NO_PAYLOAD;
+ SCLogDebug("sig requires no payload");
+ }
+ break;
+ }
+ break;
+ }
+ case DETECT_AL_APP_LAYER_EVENT:
+ s->mask |= SIG_MASK_REQUIRE_ENGINE_EVENT;
+ break;
+ case DETECT_ENGINE_EVENT:
+ s->mask |= SIG_MASK_REQUIRE_ENGINE_EVENT;
+ break;
+ }
+ }
+
+ if (s->alproto == ALPROTO_SSH) {
+ s->mask |= SIG_MASK_REQUIRE_SSH_STATE;
+ SCLogDebug("sig requires ssh state");
+ }
+ if (s->alproto == ALPROTO_TLS) {
+ s->mask |= SIG_MASK_REQUIRE_TLS_STATE;
+ SCLogDebug("sig requires tls state");
+ }
+ if (s->alproto == ALPROTO_DNS) {
+ s->mask |= SIG_MASK_REQUIRE_DNS_STATE;
+ SCLogDebug("sig requires dns state");
+ }
+ if (s->alproto == ALPROTO_FTP) {
+ s->mask |= SIG_MASK_REQUIRE_FTP_STATE;
+ SCLogDebug("sig requires ftp state");
+ }
+ if (s->alproto == ALPROTO_SMTP) {
+ s->mask |= SIG_MASK_REQUIRE_SMTP_STATE;
+ SCLogDebug("sig requires smtp state");
+ }
+
+ if ((s->mask & SIG_MASK_REQUIRE_DCE_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_HTTP_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_SSH_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_DNS_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_FTP_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_SMTP_STATE) ||
+ (s->mask & SIG_MASK_REQUIRE_TLS_STATE))
+ {
+ s->mask |= SIG_MASK_REQUIRE_FLOW;
+ SCLogDebug("sig requires flow");
+ }
+
+ if (s->init_flags & SIG_FLAG_INIT_FLOW) {
+ s->mask |= SIG_MASK_REQUIRE_FLOW;
+ SCLogDebug("sig requires flow");
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ s->mask |= SIG_MASK_REQUIRE_FLOW;
+ SCLogDebug("sig requires flow");
+ }
+
+ if (s->flags & SIG_FLAG_APPLAYER) {
+ s->mask |= SIG_MASK_REQUIRE_FLOW;
+ SCLogDebug("sig requires flow");
+ }
+
+ SCLogDebug("mask %02X", s->mask);
+ SCReturnInt(0);
+}
+
+static void SigInitStandardMpmFactoryContexts(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_mpm_context_proto_tcp_packet =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "packet_proto_tcp",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_proto_udp_packet =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "packet_proto_udp",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_proto_other_packet =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "packet_proto_other",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_uri =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "uri",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_stream =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "stream",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hcbd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hcbd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hsbd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hsbd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_smtp =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "smtp",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hhd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hhd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hrhd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hrhd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hmd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hmd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hcd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hcd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hrud =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hrud",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hsmd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hsmd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hscd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hscd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_huad =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "huad",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hhhd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hhhd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_hrhhd =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "hrhhd",
+ MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD);
+ de_ctx->sgh_mpm_context_app_proto_detect =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, "app_proto_detect", 0);
+
+ return;
+}
+
+/** \brief get max dsize "depth"
+ * \param s signature to get dsize value from
+ * \retval depth or negative value
+ */
+static int SigParseGetMaxDsize(Signature *s)
+{
+ if (s->flags & SIG_FLAG_DSIZE && s->dsize_sm != NULL) {
+ DetectDsizeData *dd = (DetectDsizeData *)s->dsize_sm->ctx;
+
+ switch (dd->mode) {
+ case DETECTDSIZE_LT:
+ case DETECTDSIZE_EQ:
+ return dd->dsize;
+ case DETECTDSIZE_RA:
+ return dd->dsize2;
+ case DETECTDSIZE_GT:
+ default:
+ SCReturnInt(-2);
+ }
+ }
+ SCReturnInt(-1);
+}
+
+/** \brief set prefilter dsize pair
+ * \param s signature to get dsize value from
+ */
+static void SigParseSetDsizePair(Signature *s)
+{
+ if (s->flags & SIG_FLAG_DSIZE && s->dsize_sm != NULL) {
+ DetectDsizeData *dd = (DetectDsizeData *)s->dsize_sm->ctx;
+
+ uint16_t low = 0;
+ uint16_t high = 65535;
+
+ switch (dd->mode) {
+ case DETECTDSIZE_LT:
+ low = 0;
+ high = dd->dsize;
+ break;
+ case DETECTDSIZE_EQ:
+ low = dd->dsize;
+ high = dd->dsize;
+ break;
+ case DETECTDSIZE_RA:
+ low = dd->dsize;
+ high = dd->dsize2;
+ break;
+ case DETECTDSIZE_GT:
+ low = dd->dsize;
+ high = 65535;
+ break;
+ }
+ s->dsize_low = low;
+ s->dsize_high = high;
+
+ SCLogDebug("low %u, high %u", low, high);
+ }
+}
+
+/**
+ * \brief Apply dsize as depth to content matches in the rule
+ * \param s signature to get dsize value from
+ */
+static void SigParseApplyDsizeToContent(Signature *s)
+{
+ SCEnter();
+
+ if (s->flags & SIG_FLAG_DSIZE) {
+ SigParseSetDsizePair(s);
+
+ int dsize = SigParseGetMaxDsize(s);
+ if (dsize < 0) {
+ /* nothing to do */
+ return;
+ }
+
+ SigMatch *sm = s->sm_lists[DETECT_SM_LIST_PMATCH];
+ for ( ; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT) {
+ continue;
+ }
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd == NULL) {
+ continue;
+ }
+
+ if (cd->depth == 0 || cd->depth >= dsize) {
+ cd->depth = (uint16_t)dsize;
+ SCLogDebug("updated %u, content %u to have depth %u "
+ "because of dsize.", s->id, cd->id, cd->depth);
+ }
+ }
+ }
+}
+
+/**
+ * \brief Preprocess signature, classify ip-only, etc, build sig array
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int SigAddressPrepareStage1(DetectEngineCtx *de_ctx)
+{
+ Signature *tmp_s = NULL;
+ uint32_t cnt_iponly = 0;
+ uint32_t cnt_payload = 0;
+ uint32_t cnt_applayer = 0;
+ uint32_t cnt_deonly = 0;
+
+ //DetectAddressPrintMemory();
+ //DetectSigGroupPrintMemory();
+ //DetectPortPrintMemory();
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("building signature grouping structure, stage 1: "
+ "preprocessing rules...");
+ }
+
+#ifdef HAVE_LUAJIT
+ /* run this before the mpm states are initialized */
+ if (DetectLuajitSetupStatesPool(de_ctx->detect_luajit_instances, TRUE) != 0) {
+ if (de_ctx->failure_fatal)
+ return -1;
+ }
+#endif
+
+ de_ctx->sig_array_len = DetectEngineGetMaxSigId(de_ctx);
+ de_ctx->sig_array_size = (de_ctx->sig_array_len * sizeof(Signature *));
+ de_ctx->sig_array = (Signature **)SCMalloc(de_ctx->sig_array_size);
+ if (de_ctx->sig_array == NULL)
+ goto error;
+ memset(de_ctx->sig_array,0,de_ctx->sig_array_size);
+
+ SCLogDebug("signature lookup array: %" PRIu32 " sigs, %" PRIu32 " bytes",
+ de_ctx->sig_array_len, de_ctx->sig_array_size);
+
+ /* now for every rule add the source group */
+ for (tmp_s = de_ctx->sig_list; tmp_s != NULL; tmp_s = tmp_s->next) {
+ de_ctx->sig_array[tmp_s->num] = tmp_s;
+
+ SCLogDebug("Signature %" PRIu32 ", internal id %" PRIu32 ", ptrs %p %p ", tmp_s->id, tmp_s->num, tmp_s, de_ctx->sig_array[tmp_s->num]);
+
+ /* see if the sig is ip only */
+ if (SignatureIsIPOnly(de_ctx, tmp_s) == 1) {
+ tmp_s->flags |= SIG_FLAG_IPONLY;
+ cnt_iponly++;
+
+ SCLogDebug("Signature %"PRIu32" is considered \"IP only\"", tmp_s->id);
+
+ /* see if any sig is inspecting the packet payload */
+ } else if (SignatureIsInspectingPayload(de_ctx, tmp_s) == 1) {
+ tmp_s->init_flags |= SIG_FLAG_INIT_PAYLOAD;
+ cnt_payload++;
+
+ SCLogDebug("Signature %"PRIu32" is considered \"Payload inspecting\"", tmp_s->id);
+ } else if (SignatureIsDEOnly(de_ctx, tmp_s) == 1) {
+ tmp_s->init_flags |= SIG_FLAG_INIT_DEONLY;
+ SCLogDebug("Signature %"PRIu32" is considered \"Decoder Event only\"", tmp_s->id);
+ cnt_deonly++;
+ }
+
+ if (tmp_s->flags & SIG_FLAG_APPLAYER) {
+ SCLogDebug("Signature %"PRIu32" is considered \"Applayer inspecting\"", tmp_s->id);
+ cnt_applayer++;
+ }
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ uint16_t colen = 0;
+ char copresent = 0;
+ SigMatch *sm;
+ DetectContentData *co;
+ for (sm = tmp_s->sm_lists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ copresent = 1;
+ co = (DetectContentData *)sm->ctx;
+ if (co->content_len > colen)
+ colen = co->content_len;
+ }
+
+ if (copresent && colen == 1) {
+ SCLogDebug("signature %8u content maxlen 1", tmp_s->id);
+ int proto;
+ for (proto = 0; proto < 256; proto++) {
+ if (tmp_s->proto.proto[(proto/8)] & (1<<(proto%8)))
+ SCLogDebug("=> proto %" PRId32 "", proto);
+ }
+ }
+ }
+#endif /* DEBUG */
+
+ SignatureCreateMask(tmp_s);
+ SigParseApplyDsizeToContent(tmp_s);
+
+ de_ctx->sig_cnt++;
+ }
+
+ //DetectAddressPrintMemory();
+ //DetectSigGroupPrintMemory();
+ //DetectPortPrintMemory();
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogInfo("%" PRIu32 " signatures processed. %" PRIu32 " are IP-only "
+ "rules, %" PRIu32 " are inspecting packet payload, %"PRIu32
+ " inspect application layer, %"PRIu32" are decoder event only",
+ de_ctx->sig_cnt, cnt_iponly, cnt_payload, cnt_applayer,
+ cnt_deonly);
+
+ SCLogInfo("building signature grouping structure, stage 1: "
+ "preprocessing rules... complete");
+ }
+ return 0;
+
+error:
+ return -1;
+}
+
+static int DetectEngineLookupBuildSourceAddressList(DetectEngineCtx *de_ctx, DetectEngineLookupFlow *flow_gh, Signature *s, int family)
+{
+ DetectAddress *gr = NULL, *lookup_gr = NULL, *head = NULL;
+ int proto;
+
+ if (family == AF_INET) {
+ head = s->src.ipv4_head;
+ } else if (family == AF_INET6) {
+ head = s->src.ipv6_head;
+ } else {
+ head = s->src.any_head;
+ }
+
+ /* for each source address group in the signature... */
+ for (gr = head; gr != NULL; gr = gr->next) {
+ BUG_ON(gr->ip.family == 0 && !(gr->flags & ADDRESS_FLAG_ANY));
+
+ /* ...and each protocol the signature matches on... */
+ for (proto = 0; proto < 256; proto++) {
+ if ((s->proto.proto[(proto/8)] & (1<<(proto%8))) || (s->proto.flags & DETECT_PROTO_ANY)) {
+ /* ...see if the group is in the tmp list, and if not add it. */
+ if (family == AF_INET) {
+ lookup_gr = DetectAddressLookupInList(flow_gh->tmp_gh[proto]->ipv4_head,gr);
+ } else if (family == AF_INET6) {
+ lookup_gr = DetectAddressLookupInList(flow_gh->tmp_gh[proto]->ipv6_head,gr);
+ } else {
+ lookup_gr = DetectAddressLookupInList(flow_gh->tmp_gh[proto]->any_head,gr);
+ }
+
+ if (lookup_gr == NULL) {
+ DetectAddress *grtmp = DetectAddressCopy(gr);
+ if (grtmp == NULL) {
+ goto error;
+ }
+ SigGroupHeadAppendSig(de_ctx, &grtmp->sh, s);
+
+ /* add to the lookup list */
+ if (family == AF_INET) {
+ DetectAddressAdd(&flow_gh->tmp_gh[proto]->ipv4_head, grtmp);
+ } else if (family == AF_INET6) {
+ DetectAddressAdd(&flow_gh->tmp_gh[proto]->ipv6_head, grtmp);
+ } else {
+ DetectAddressAdd(&flow_gh->tmp_gh[proto]->any_head, grtmp);
+ }
+ } else {
+ /* our group will only have one sig, this one. So add that. */
+ SigGroupHeadAppendSig(de_ctx, &lookup_gr->sh, s);
+ lookup_gr->cnt++;
+ }
+ }
+ }
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * \brief add signature to the right flow group(s)
+ */
+static int DetectEngineLookupFlowAddSig(DetectEngineCtx *de_ctx, Signature *s, int family)
+{
+ SCLogDebug("s->id %u", s->id);
+
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ SCLogDebug("s->id %u (toclient)", s->id);
+ DetectEngineLookupBuildSourceAddressList(de_ctx,
+ &de_ctx->flow_gh[0], s, family);
+ }
+
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ SCLogDebug("s->id %u (toserver)", s->id);
+ DetectEngineLookupBuildSourceAddressList(de_ctx,
+ &de_ctx->flow_gh[1], s, family);
+ }
+
+ return 0;
+}
+
+static DetectAddress *GetHeadPtr(DetectAddressHead *head, int family)
+{
+ DetectAddress *grhead;
+
+ if (head == NULL)
+ return NULL;
+
+ if (family == AF_INET) {
+ grhead = head->ipv4_head;
+ } else if (family == AF_INET6) {
+ grhead = head->ipv6_head;
+ } else {
+ grhead = head->any_head;
+ }
+
+ return grhead;
+}
+
+//#define SMALL_MPM(c) 0
+#define SMALL_MPM(c) ((c) == 1)
+// || (c) == 2)
+// || (c) == 3)
+
+int CreateGroupedAddrListCmpCnt(DetectAddress *a, DetectAddress *b)
+{
+ if (a->cnt > b->cnt)
+ return 1;
+ return 0;
+}
+
+int CreateGroupedAddrListCmpMpmMaxlen(DetectAddress *a, DetectAddress *b)
+{
+ if (a->sh == NULL || b->sh == NULL)
+ return 0;
+
+ if (SMALL_MPM(a->sh->mpm_content_maxlen))
+ return 1;
+
+ if (a->sh->mpm_content_maxlen < b->sh->mpm_content_maxlen)
+ return 1;
+ return 0;
+}
+
+/* set unique_groups to 0 for no grouping.
+ *
+ * srchead is a ordered "inserted" list w/o internal overlap
+ *
+ */
+int CreateGroupedAddrList(DetectEngineCtx *de_ctx, DetectAddress *srchead,
+ int family, DetectAddressHead *newhead,
+ uint32_t unique_groups,
+ int (*CompareFunc)(DetectAddress *, DetectAddress *),
+ uint32_t max_idx)
+{
+ DetectAddress *tmplist = NULL, *tmplist2 = NULL, *joingr = NULL;
+ char insert = 0;
+ DetectAddress *gr, *next_gr;
+ uint32_t groups = 0;
+
+ /* insert the addresses into the tmplist, where it will
+ * be sorted descending on 'cnt'. */
+ for (gr = srchead; gr != NULL; gr = gr->next) {
+ BUG_ON(gr->ip.family == 0 && !(gr->flags & ADDRESS_FLAG_ANY));
+
+ if (SMALL_MPM(gr->sh->mpm_content_maxlen) && unique_groups > 0)
+ unique_groups++;
+
+ groups++;
+
+ /* alloc a copy */
+ DetectAddress *newtmp = DetectAddressCopy(gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+ SigGroupHeadCopySigs(de_ctx, gr->sh,&newtmp->sh);
+
+ DetectPort *port = gr->port;
+ for ( ; port != NULL; port = port->next) {
+ DetectPortInsertCopy(de_ctx,&newtmp->port, port);
+ newtmp->flags |= ADDRESS_HAVEPORT;
+ }
+
+ /* insert it */
+ DetectAddress *tmpgr = tmplist, *prevtmpgr = NULL;
+ if (tmplist == NULL) {
+ /* empty list, set head */
+ tmplist = newtmp;
+ } else {
+ /* look for the place to insert */
+ for ( ; tmpgr != NULL&&!insert; tmpgr = tmpgr->next) {
+ if (CompareFunc(gr, tmpgr)) {
+ if (tmpgr == tmplist) {
+ newtmp->next = tmplist;
+ tmplist = newtmp;
+ } else {
+ newtmp->next = prevtmpgr->next;
+ prevtmpgr->next = newtmp;
+ }
+ insert = 1;
+ }
+ prevtmpgr = tmpgr;
+ }
+ if (insert == 0) {
+ newtmp->next = NULL;
+ prevtmpgr->next = newtmp;
+ }
+ insert = 0;
+ }
+ }
+
+ uint32_t i = unique_groups;
+ if (i == 0) i = groups;
+
+ for (gr = tmplist; gr != NULL; ) {
+ BUG_ON(gr->ip.family == 0 && !(gr->flags & ADDRESS_FLAG_ANY));
+
+ if (i == 0) {
+ if (joingr == NULL) {
+ joingr = DetectAddressCopy(gr);
+ if (joingr == NULL) {
+ goto error;
+ }
+
+ SigGroupHeadCopySigs(de_ctx,gr->sh,&joingr->sh);
+
+ DetectPort *port = gr->port;
+ for ( ; port != NULL; port = port->next) {
+ DetectPortInsertCopy(de_ctx,&joingr->port, port);
+ joingr->flags |= ADDRESS_HAVEPORT;
+ }
+ } else {
+ DetectAddressJoin(de_ctx, joingr, gr);
+ }
+ } else {
+ DetectAddress *newtmp = DetectAddressCopy(gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+
+ SigGroupHeadCopySigs(de_ctx,gr->sh,&newtmp->sh);
+
+ DetectPort *port = gr->port;
+ for ( ; port != NULL; port = port->next) {
+ DetectPortInsertCopy(de_ctx,&newtmp->port, port);
+ newtmp->flags |= ADDRESS_HAVEPORT;
+ }
+
+ if (tmplist2 == NULL) {
+ tmplist2 = newtmp;
+ } else {
+ newtmp->next = tmplist2;
+ tmplist2 = newtmp;
+ }
+ }
+ if (i)i--;
+
+ next_gr = gr->next;
+ DetectAddressFree(gr);
+ gr = next_gr;
+ }
+
+ /* we now have a tmplist2 containing the 'unique' groups and
+ * possibly a joingr that covers the rest. Now build the newhead
+ * that we will pass back to the caller.
+ *
+ * Start with inserting the unique groups */
+ for (gr = tmplist2; gr != NULL; ) {
+ BUG_ON(gr->ip.family == 0 && !(gr->flags & ADDRESS_FLAG_ANY));
+
+ DetectAddress *newtmp = DetectAddressCopy(gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+ SigGroupHeadCopySigs(de_ctx, gr->sh,&newtmp->sh);
+
+ DetectPort *port = gr->port;
+ for ( ; port != NULL; port = port->next) {
+ DetectPortInsertCopy(de_ctx, &newtmp->port, port);
+ newtmp->flags |= ADDRESS_HAVEPORT;
+ }
+
+ DetectAddressInsert(de_ctx, newhead, newtmp);
+
+ next_gr = gr->next;
+ DetectAddressFree(gr);
+ gr = next_gr;
+ }
+
+ /* if present, insert the joingr that covers the rest */
+ if (joingr != NULL) {
+ DetectAddressInsert(de_ctx, newhead, joingr);
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+int CreateGroupedPortListCmpCnt(DetectPort *a, DetectPort *b)
+{
+ if (a->cnt > b->cnt)
+ return 1;
+ return 0;
+}
+
+int CreateGroupedPortListCmpMpmMaxlen(DetectPort *a, DetectPort *b)
+{
+ if (a->sh == NULL || b->sh == NULL)
+ return 0;
+
+ if (SMALL_MPM(a->sh->mpm_content_maxlen))
+ return 1;
+
+ if (a->sh->mpm_content_maxlen < b->sh->mpm_content_maxlen)
+ return 1;
+
+ return 0;
+}
+
+static uint32_t g_groupportlist_maxgroups = 0;
+static uint32_t g_groupportlist_groupscnt = 0;
+static uint32_t g_groupportlist_totgroups = 0;
+
+int CreateGroupedPortList(DetectEngineCtx *de_ctx,HashListTable *port_hash, DetectPort **newhead, uint32_t unique_groups, int (*CompareFunc)(DetectPort *, DetectPort *), uint32_t max_idx)
+{
+ DetectPort *tmplist = NULL, *tmplist2 = NULL, *joingr = NULL;
+ char insert = 0;
+ DetectPort *gr, *next_gr;
+ uint32_t groups = 0;
+
+ HashListTableBucket *htb = HashListTableGetListHead(port_hash);
+
+ /* insert the addresses into the tmplist, where it will
+ * be sorted descending on 'cnt'. */
+ for ( ; htb != NULL; htb = HashListTableGetListNext(htb)) {
+ gr = (DetectPort *)HashListTableGetListData(htb);
+
+ SCLogDebug("hash list gr %p", gr);
+ DetectPortPrint(gr);
+
+ if (SMALL_MPM(gr->sh->mpm_content_maxlen) && unique_groups > 0)
+ unique_groups++;
+
+ groups++;
+
+ /* alloc a copy */
+ DetectPort *newtmp = DetectPortCopySingle(de_ctx, gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+
+ /* insert it */
+ DetectPort *tmpgr = tmplist, *prevtmpgr = NULL;
+ if (tmplist == NULL) {
+ /* empty list, set head */
+ tmplist = newtmp;
+ } else {
+ /* look for the place to insert */
+ for ( ; tmpgr != NULL&&!insert; tmpgr = tmpgr->next) {
+ if (CompareFunc(gr, tmpgr)) {
+ if (tmpgr == tmplist) {
+ newtmp->next = tmplist;
+ tmplist = newtmp;
+ } else {
+ newtmp->next = prevtmpgr->next;
+ prevtmpgr->next = newtmp;
+ }
+ insert = 1;
+ }
+ prevtmpgr = tmpgr;
+ }
+ if (insert == 0) {
+ newtmp->next = NULL;
+ prevtmpgr->next = newtmp;
+ }
+ insert = 0;
+ }
+ }
+
+ uint32_t i = unique_groups;
+ if (i == 0) i = groups;
+
+ if (unique_groups > g_groupportlist_maxgroups)
+ g_groupportlist_maxgroups = unique_groups;
+ g_groupportlist_groupscnt++;
+ g_groupportlist_totgroups += unique_groups;
+
+ for (gr = tmplist; gr != NULL; ) {
+ SCLogDebug("temp list gr %p", gr);
+ DetectPortPrint(gr);
+
+ if (i == 0) {
+ if (joingr == NULL) {
+ joingr = DetectPortCopySingle(de_ctx,gr);
+ if (joingr == NULL) {
+ goto error;
+ }
+ } else {
+ DetectPortJoin(de_ctx,joingr, gr);
+ }
+ } else {
+ DetectPort *newtmp = DetectPortCopySingle(de_ctx,gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+
+ if (tmplist2 == NULL) {
+ tmplist2 = newtmp;
+ } else {
+ newtmp->next = tmplist2;
+ tmplist2 = newtmp;
+ }
+ }
+ if (i)i--;
+
+ next_gr = gr->next;
+ gr->next = NULL;
+ DetectPortFree(gr);
+ gr = next_gr;
+ }
+
+ /* we now have a tmplist2 containing the 'unique' groups and
+ * possibly a joingr that covers the rest. Now build the newhead
+ * that we will pass back to the caller.
+ *
+ * Start with inserting the unique groups */
+ for (gr = tmplist2; gr != NULL; ) {
+ SCLogDebug("temp list2 gr %p", gr);
+ DetectPortPrint(gr);
+
+ DetectPort *newtmp = DetectPortCopySingle(de_ctx,gr);
+ if (newtmp == NULL) {
+ goto error;
+ }
+
+ int r = DetectPortInsert(de_ctx,newhead,newtmp);
+ BUG_ON(r == -1);
+
+ next_gr = gr->next;
+ gr->next = NULL;
+ DetectPortFree(gr);
+ gr = next_gr;
+ }
+
+ DetectPortPrintList(*newhead);
+
+ /* if present, insert the joingr that covers the rest */
+ if (joingr != NULL) {
+ SCLogDebug("inserting joingr %p", joingr);
+ DetectPortInsert(de_ctx,newhead,joingr);
+ } else {
+ SCLogDebug("no joingr");
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief add a decoder event signature to the detection engine ctx
+ */
+static void DetectEngineAddDecoderEventSig(DetectEngineCtx *de_ctx, Signature *s)
+{
+ SCLogDebug("adding signature %"PRIu32" to the decoder event sgh", s->id);
+ SigGroupHeadAppendSig(de_ctx, &de_ctx->decoder_event_sgh, s);
+}
+
+/**
+ * \brief Fill the global src group head, with the sigs included
+ *
+ * \param de_ctx Pointer to the Detection Engine Context whose Signatures have
+ * to be processed
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+int SigAddressPrepareStage2(DetectEngineCtx *de_ctx)
+{
+ Signature *tmp_s = NULL;
+ uint32_t sigs = 0;
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("building signature grouping structure, stage 2: "
+ "building source address lists...");
+ }
+
+ IPOnlyInit(de_ctx, &de_ctx->io_ctx);
+
+ int f, proto;
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (proto = 0; proto < 256; proto++) {
+ de_ctx->flow_gh[f].src_gh[proto] = DetectAddressHeadInit();
+ if (de_ctx->flow_gh[f].src_gh[proto] == NULL) {
+ goto error;
+ }
+ de_ctx->flow_gh[f].tmp_gh[proto] = DetectAddressHeadInit();
+ if (de_ctx->flow_gh[f].tmp_gh[proto] == NULL) {
+ goto error;
+ }
+ }
+ }
+
+ /* now for every rule add the source group to our temp lists */
+ for (tmp_s = de_ctx->sig_list; tmp_s != NULL; tmp_s = tmp_s->next) {
+ SCLogDebug("tmp_s->id %"PRIu32, tmp_s->id);
+ if (tmp_s->flags & SIG_FLAG_IPONLY) {
+ IPOnlyAddSignature(de_ctx, &de_ctx->io_ctx, tmp_s);
+ } else {
+ DetectEngineLookupFlowAddSig(de_ctx, tmp_s, AF_INET);
+ DetectEngineLookupFlowAddSig(de_ctx, tmp_s, AF_INET6);
+ DetectEngineLookupFlowAddSig(de_ctx, tmp_s, AF_UNSPEC);
+ }
+
+ if (tmp_s->init_flags & SIG_FLAG_INIT_DEONLY) {
+ DetectEngineAddDecoderEventSig(de_ctx, tmp_s);
+ }
+
+ sigs++;
+ }
+
+ /* create the final src addr list based on the tmplist. */
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (proto = 0; proto < 256; proto++) {
+ int groups = (f ? de_ctx->max_uniq_toserver_src_groups : de_ctx->max_uniq_toclient_src_groups);
+
+ CreateGroupedAddrList(de_ctx,
+ de_ctx->flow_gh[f].tmp_gh[proto]->ipv4_head, AF_INET,
+ de_ctx->flow_gh[f].src_gh[proto], groups,
+ CreateGroupedAddrListCmpMpmMaxlen, DetectEngineGetMaxSigId(de_ctx));
+
+ CreateGroupedAddrList(de_ctx,
+ de_ctx->flow_gh[f].tmp_gh[proto]->ipv6_head, AF_INET6,
+ de_ctx->flow_gh[f].src_gh[proto], groups,
+ CreateGroupedAddrListCmpMpmMaxlen, DetectEngineGetMaxSigId(de_ctx));
+ CreateGroupedAddrList(de_ctx,
+ de_ctx->flow_gh[f].tmp_gh[proto]->any_head, AF_UNSPEC,
+ de_ctx->flow_gh[f].src_gh[proto], groups,
+ CreateGroupedAddrListCmpMpmMaxlen, DetectEngineGetMaxSigId(de_ctx));
+
+ DetectAddressHeadFree(de_ctx->flow_gh[f].tmp_gh[proto]);
+ de_ctx->flow_gh[f].tmp_gh[proto] = NULL;
+ }
+ }
+ //DetectAddressPrintMemory();
+ //DetectSigGroupPrintMemory();
+
+ //printf("g_src_gh strt\n");
+ //DetectAddressPrintList(g_src_gh->ipv4_head);
+ //printf("g_src_gh end\n");
+
+ IPOnlyPrepare(de_ctx);
+ IPOnlyPrint(de_ctx, &de_ctx->io_ctx);
+#ifdef DEBUG
+ DetectAddress *gr = NULL;
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("%" PRIu32 " total signatures:", sigs);
+ }
+
+ /* TCP */
+ uint32_t cnt_any = 0, cnt_ipv4 = 0, cnt_ipv6 = 0;
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_TCP]->any_head; gr != NULL; gr = gr->next) {
+ cnt_any++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_TCP]->ipv4_head; gr != NULL; gr = gr->next) {
+ cnt_ipv4++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_TCP]->ipv6_head; gr != NULL; gr = gr->next) {
+ cnt_ipv6++;
+ }
+ }
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("TCP Source address blocks: any: %4u, ipv4: %4u, ipv6: %4u.", cnt_any, cnt_ipv4, cnt_ipv6);
+ }
+
+ cnt_any = 0, cnt_ipv4 = 0, cnt_ipv6 = 0;
+ /* UDP */
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_UDP]->any_head; gr != NULL; gr = gr->next) {
+ cnt_any++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_UDP]->ipv4_head; gr != NULL; gr = gr->next) {
+ cnt_ipv4++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_UDP]->ipv6_head; gr != NULL; gr = gr->next) {
+ cnt_ipv6++;
+ }
+ }
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("UDP Source address blocks: any: %4u, ipv4: %4u, ipv6: %4u.", cnt_any, cnt_ipv4, cnt_ipv6);
+ }
+
+ cnt_any = 0, cnt_ipv4 = 0, cnt_ipv6 = 0;
+ /* SCTP */
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP]->any_head; gr != NULL; gr = gr->next) {
+ cnt_any++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP]->ipv4_head; gr != NULL; gr = gr->next) {
+ cnt_ipv4++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP]->ipv6_head; gr != NULL; gr = gr->next) {
+ cnt_ipv6++;
+ }
+ }
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("SCTP Source address blocks: any: %4u, ipv4: %4u, ipv6: %4u.", cnt_any, cnt_ipv4, cnt_ipv6);
+ }
+
+ /* ICMP */
+ cnt_any = 0, cnt_ipv4 = 0, cnt_ipv6 = 0;
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[1]->any_head; gr != NULL; gr = gr->next) {
+ cnt_any++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[1]->ipv4_head; gr != NULL; gr = gr->next) {
+ cnt_ipv4++;
+ }
+ }
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (gr = de_ctx->flow_gh[f].src_gh[1]->ipv6_head; gr != NULL; gr = gr->next) {
+ cnt_ipv6++;
+ }
+ }
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("ICMP Source address blocks: any: %4u, ipv4: %4u, ipv6: %4u.", cnt_any, cnt_ipv4, cnt_ipv6);
+ }
+#endif /* DEBUG */
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogInfo("building signature grouping structure, stage 2: building source address list... complete");
+ }
+
+ return 0;
+error:
+ printf("SigAddressPrepareStage2 error\n");
+ return -1;
+}
+
+/**
+ * \brief Build the destination address portion of the match tree
+ */
+int BuildDestinationAddressHeads(DetectEngineCtx *de_ctx, DetectAddressHead *head, int family, int flow)
+{
+ Signature *tmp_s = NULL;
+ DetectAddress *gr = NULL, *sgr = NULL, *lookup_gr = NULL;
+ uint32_t max_idx = 0;
+
+ DetectAddress *grhead = NULL, *grdsthead = NULL, *grsighead = NULL;
+
+ /* based on the family, select the list we are using in the head */
+ grhead = GetHeadPtr(head, family);
+
+ /* loop through the global source address list */
+ for (gr = grhead; gr != NULL; gr = gr->next) {
+ //printf(" * Source group (BuildDestinationAddressHeads): "); DetectAddressPrint(gr); printf(" (%p)\n", gr);
+
+ /* initialize the destination group head */
+ gr->dst_gh = DetectAddressHeadInit();
+ if (gr->dst_gh == NULL) {
+ goto error;
+ }
+
+ /* use a tmp list for speeding up insertions */
+ DetectAddress *tmp_gr_list = NULL;
+
+ /* loop through all signatures in this source address group
+ * and build the temporary destination address list for it */
+ uint32_t sig;
+ for (sig = 0; sig < de_ctx->sig_array_len; sig++) {
+ if (!(gr->sh->init->sig_array[(sig/8)] & (1<<(sig%8))))
+ continue;
+
+ tmp_s = de_ctx->sig_array[sig];
+ if (tmp_s == NULL)
+ continue;
+
+ //printf(" * (tmp) Signature %u (num %u)\n", tmp_s->id, tmp_s->num);
+
+ max_idx = sig;
+
+ /* build the temp list */
+ grsighead = GetHeadPtr(&tmp_s->dst, family);
+ for (sgr = grsighead; sgr != NULL; sgr = sgr->next) {
+ //printf(" * (tmp) dst group: "); DetectAddressPrint(sgr); printf(" (%p)\n", sgr);
+
+ if ((lookup_gr = DetectAddressLookupInList(tmp_gr_list, sgr)) == NULL) {
+ DetectAddress *grtmp = DetectAddressCopy(sgr);
+ if (grtmp == NULL) {
+ goto error;
+ }
+ SigGroupHeadAppendSig(de_ctx,&grtmp->sh,tmp_s);
+
+ DetectAddressAdd(&tmp_gr_list,grtmp);
+ } else {
+ /* our group will only have one sig, this one. So add that. */
+ SigGroupHeadAppendSig(de_ctx, &lookup_gr->sh, tmp_s);
+ lookup_gr->cnt++;
+ }
+ }
+
+ }
+
+ /* Create the destination address list, keeping in
+ * mind the limits we use. */
+ int groups = (flow ? de_ctx->max_uniq_toserver_dst_groups : de_ctx->max_uniq_toclient_dst_groups);
+
+ CreateGroupedAddrList(de_ctx, tmp_gr_list, family, gr->dst_gh, groups, CreateGroupedAddrListCmpMpmMaxlen, max_idx);
+
+ /* see if the sig group head of each address group is the
+ * same as an earlier one. If it is, free our head and use
+ * a pointer to the earlier one. This saves _a lot_ of memory.
+ */
+ grdsthead = GetHeadPtr(gr->dst_gh, family);
+ for (sgr = grdsthead; sgr != NULL; sgr = sgr->next) {
+ //printf(" * Destination group: "); DetectAddressPrint(sgr); printf("\n");
+
+ /* Because a pattern matcher context uses quite some
+ * memory, we first check if we can reuse it from
+ * another group head. */
+ SigGroupHead *sgh = SigGroupHeadHashLookup(de_ctx, sgr->sh);
+ if (sgh == NULL) {
+ /* put the contents in our sig group head */
+ SigGroupHeadSetSigCnt(sgr->sh, max_idx);
+ SigGroupHeadBuildMatchArray(de_ctx, sgr->sh, max_idx);
+
+ /* init the pattern matcher, this will respect the copy
+ * setting */
+ if (PatternMatchPrepareGroup(de_ctx, sgr->sh) < 0) {
+ printf("PatternMatchPrepareGroup failed\n");
+ goto error;
+ }
+ if (sgr->sh->mpm_proto_tcp_ctx_ts != NULL) {
+ if (de_ctx->mpm_max_patcnt < sgr->sh->mpm_proto_tcp_ctx_ts->pattern_cnt)
+ de_ctx->mpm_max_patcnt = sgr->sh->mpm_proto_tcp_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += sgr->sh->mpm_proto_tcp_ctx_ts->pattern_cnt;
+ }
+ if (sgr->sh->mpm_proto_tcp_ctx_tc != NULL) {
+ if (de_ctx->mpm_max_patcnt < sgr->sh->mpm_proto_tcp_ctx_tc->pattern_cnt)
+ de_ctx->mpm_max_patcnt = sgr->sh->mpm_proto_tcp_ctx_tc->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += sgr->sh->mpm_proto_tcp_ctx_tc->pattern_cnt;
+ }
+ if (sgr->sh->mpm_proto_udp_ctx_ts != NULL) {
+ if (de_ctx->mpm_max_patcnt < sgr->sh->mpm_proto_udp_ctx_ts->pattern_cnt)
+ de_ctx->mpm_max_patcnt = sgr->sh->mpm_proto_udp_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += sgr->sh->mpm_proto_udp_ctx_ts->pattern_cnt;
+ }
+ if (sgr->sh->mpm_proto_udp_ctx_tc != NULL) {
+ if (de_ctx->mpm_max_patcnt < sgr->sh->mpm_proto_udp_ctx_tc->pattern_cnt)
+ de_ctx->mpm_max_patcnt = sgr->sh->mpm_proto_udp_ctx_tc->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += sgr->sh->mpm_proto_udp_ctx_tc->pattern_cnt;
+ }
+ if (sgr->sh->mpm_proto_other_ctx != NULL) {
+ if (de_ctx->mpm_max_patcnt < sgr->sh->mpm_proto_other_ctx->pattern_cnt)
+ de_ctx->mpm_max_patcnt = sgr->sh->mpm_proto_other_ctx->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += sgr->sh->mpm_proto_other_ctx->pattern_cnt;
+ }
+ if (sgr->sh->mpm_uri_ctx_ts != NULL) {
+ if (de_ctx->mpm_uri_max_patcnt < sgr->sh->mpm_uri_ctx_ts->pattern_cnt)
+ de_ctx->mpm_uri_max_patcnt = sgr->sh->mpm_uri_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_uri_tot_patcnt += sgr->sh->mpm_uri_ctx_ts->pattern_cnt;
+ }
+ /* dbg */
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && sgr->sh->mpm_proto_tcp_ctx_ts) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_proto_tcp_ctx_ts->memory_size;
+ }
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && sgr->sh->mpm_proto_tcp_ctx_tc) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_proto_tcp_ctx_tc->memory_size;
+ }
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && sgr->sh->mpm_proto_udp_ctx_ts) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_proto_udp_ctx_ts->memory_size;
+ }
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && sgr->sh->mpm_proto_udp_ctx_tc) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_proto_udp_ctx_tc->memory_size;
+ }
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && sgr->sh->mpm_proto_other_ctx) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_proto_other_ctx->memory_size;
+ }
+ if (!(sgr->sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY) && sgr->sh->mpm_uri_ctx_ts) {
+ de_ctx->mpm_memory_size += sgr->sh->mpm_uri_ctx_ts->memory_size;
+ }
+
+ SigGroupHeadHashAdd(de_ctx, sgr->sh);
+ SigGroupHeadStore(de_ctx, sgr->sh);
+ de_ctx->gh_unique++;
+ } else {
+ SCLogDebug("calling SigGroupHeadFree sgr %p, sgr->sh %p", sgr, sgr->sh);
+ SigGroupHeadFree(sgr->sh);
+ sgr->sh = sgh;
+
+ de_ctx->gh_reuse++;
+ sgr->flags |= ADDRESS_SIGGROUPHEAD_COPY;
+ sgr->sh->flags |= SIG_GROUP_HEAD_REFERENCED;
+ }
+ }
+
+ /* free the temp list */
+ DetectAddressCleanupList(tmp_gr_list);
+ /* clear now unneeded sig group head */
+ SCLogDebug("calling SigGroupHeadFree gr %p, gr->sh %p", gr, gr->sh);
+ SigGroupHeadFree(gr->sh);
+ gr->sh = NULL;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+//static
+int BuildDestinationAddressHeadsWithBothPorts(DetectEngineCtx *de_ctx, DetectAddressHead *head, int family, int flow)
+{
+ Signature *tmp_s = NULL;
+ DetectAddress *src_gr = NULL, *dst_gr = NULL, *sig_gr = NULL, *lookup_gr = NULL;
+ DetectAddress *src_gr_head = NULL, *dst_gr_head = NULL, *sig_gr_head = NULL;
+ uint32_t max_idx = 0;
+
+ /* loop through the global source address list */
+ src_gr_head = GetHeadPtr(head,family);
+ for (src_gr = src_gr_head; src_gr != NULL; src_gr = src_gr->next) {
+ //printf(" * Source group: "); DetectAddressPrint(src_gr); printf("\n");
+
+ /* initialize the destination group head */
+ src_gr->dst_gh = DetectAddressHeadInit();
+ if (src_gr->dst_gh == NULL) {
+ goto error;
+ }
+
+ /* use a tmp list for speeding up insertions */
+ DetectAddress *tmp_gr_list = NULL;
+
+ /* loop through all signatures in this source address group
+ * and build the temporary destination address list for it */
+ uint32_t sig;
+ for (sig = 0; sig < de_ctx->sig_array_len; sig++) {
+ if (!(src_gr->sh->init->sig_array[(sig/8)] & (1<<(sig%8))))
+ continue;
+
+ tmp_s = de_ctx->sig_array[sig];
+ if (tmp_s == NULL)
+ continue;
+
+ //printf(" * Source group: "); DetectAddressPrint(src_gr); printf("\n");
+
+ max_idx = sig;
+
+ /* build the temp list */
+ sig_gr_head = GetHeadPtr(&tmp_s->dst,family);
+ for (sig_gr = sig_gr_head; sig_gr != NULL; sig_gr = sig_gr->next) {
+ //printf(" * Sig dst addr: "); DetectAddressPrint(sig_gr); printf("\n");
+
+ if ((lookup_gr = DetectAddressLookupInList(tmp_gr_list, sig_gr)) == NULL) {
+ DetectAddress *grtmp = DetectAddressCopy(sig_gr);
+ if (grtmp == NULL) {
+ goto error;
+ }
+ SigGroupHeadAppendSig(de_ctx, &grtmp->sh, tmp_s);
+
+ DetectAddressAdd(&tmp_gr_list,grtmp);
+ } else {
+ /* our group will only have one sig, this one. So add that. */
+ SigGroupHeadAppendSig(de_ctx, &lookup_gr->sh, tmp_s);
+ lookup_gr->cnt++;
+ }
+
+ SCLogDebug("calling SigGroupHeadFree sig_gr %p, sig_gr->sh %p", sig_gr, sig_gr->sh);
+ SigGroupHeadFree(sig_gr->sh);
+ sig_gr->sh = NULL;
+ }
+ }
+
+ /* Create the destination address list, keeping in
+ * mind the limits we use. */
+ int groups = (flow ? de_ctx->max_uniq_toserver_dst_groups : de_ctx->max_uniq_toclient_dst_groups);
+
+ CreateGroupedAddrList(de_ctx, tmp_gr_list, family, src_gr->dst_gh, groups, CreateGroupedAddrListCmpMpmMaxlen, max_idx);
+
+ /* add the ports to the dst address groups and the sigs
+ * to the ports */
+ dst_gr_head = GetHeadPtr(src_gr->dst_gh,family);
+ for (dst_gr = dst_gr_head; dst_gr != NULL; dst_gr = dst_gr->next) {
+ //printf(" * Destination group: "); DetectAddressPrint(dst_gr); printf("\n");
+
+ dst_gr->flags |= ADDRESS_HAVEPORT;
+
+ if (dst_gr->sh == NULL)
+ continue;
+
+ /* we will reuse address sig group heads at this points,
+ * because if the sigs are the same, the ports will be
+ * the same. Saves memory and a lot of init time. */
+ SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, dst_gr->sh);
+ if (lookup_sgh == NULL) {
+ DetectPortSpHashReset(de_ctx);
+
+ uint32_t sig2;
+ for (sig2 = 0; sig2 < max_idx+1; sig2++) {
+ if (!(dst_gr->sh->init->sig_array[(sig2/8)] & (1<<(sig2%8))))
+ continue;
+
+ Signature *s = de_ctx->sig_array[sig2];
+ if (s == NULL)
+ continue;
+
+ //printf(" + Destination group (grouped): "); DetectAddressPrint(dst_gr); printf("\n");
+
+ DetectPort *sdp = s->sp;
+ for ( ; sdp != NULL; sdp = sdp->next) {
+ DetectPort *lookup_port = DetectPortSpHashLookup(de_ctx, sdp);
+ if (lookup_port == NULL) {
+ DetectPort *port = DetectPortCopySingle(de_ctx,sdp);
+ if (port == NULL)
+ goto error;
+
+ SigGroupHeadAppendSig(de_ctx, &port->sh, s);
+ DetectPortSpHashAdd(de_ctx, port);
+ port->cnt = 1;
+ } else {
+ SigGroupHeadAppendSig(de_ctx, &lookup_port->sh, s);
+ lookup_port->cnt++;
+ }
+ }
+ }
+
+ int spgroups = (flow ? de_ctx->max_uniq_toserver_sp_groups : de_ctx->max_uniq_toclient_sp_groups);
+
+ CreateGroupedPortList(de_ctx, de_ctx->sport_hash_table, &dst_gr->port, spgroups, CreateGroupedPortListCmpMpmMaxlen, max_idx);
+
+ SCLogDebug("adding sgh %p to the hash", dst_gr->sh);
+ SigGroupHeadHashAdd(de_ctx, dst_gr->sh);
+
+ dst_gr->sh->init->port = dst_gr->port;
+ /* mark this head for deletion once we no longer need
+ * the hash. We're only using the port ptr, so no problem
+ * when we remove this after initialization is done */
+ dst_gr->sh->flags |= SIG_GROUP_HEAD_FREE;
+
+ /* for each destination port we setup the siggrouphead here */
+ DetectPort *sp = dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ //printf(" * Src Port(range): "); DetectPortPrint(sp); printf("\n");
+
+ if (sp->sh == NULL)
+ continue;
+
+ /* we will reuse address sig group heads at this points,
+ * because if the sigs are the same, the ports will be
+ * the same. Saves memory and a lot of init time. */
+ SigGroupHead *lookup_sp_sgh = SigGroupHeadSPortHashLookup(de_ctx, sp->sh);
+ if (lookup_sp_sgh == NULL) {
+ DetectPortDpHashReset(de_ctx);
+ uint32_t sig2;
+ for (sig2 = 0; sig2 < max_idx+1; sig2++) {
+ if (!(sp->sh->init->sig_array[(sig2/8)] & (1<<(sig2%8))))
+ continue;
+
+ Signature *s = de_ctx->sig_array[sig2];
+ if (s == NULL)
+ continue;
+
+ DetectPort *sdp = s->dp;
+ for ( ; sdp != NULL; sdp = sdp->next) {
+ DetectPort *lookup_port = DetectPortDpHashLookup(de_ctx,sdp);
+ if (lookup_port == NULL) {
+ DetectPort *port = DetectPortCopySingle(de_ctx,sdp);
+ if (port == NULL)
+ goto error;
+
+ SigGroupHeadAppendSig(de_ctx, &port->sh, s);
+ DetectPortDpHashAdd(de_ctx,port);
+ port->cnt = 1;
+ } else {
+ SigGroupHeadAppendSig(de_ctx, &lookup_port->sh, s);
+ lookup_port->cnt++;
+ }
+ }
+ }
+
+ int dpgroups = (flow ? de_ctx->max_uniq_toserver_dp_groups : de_ctx->max_uniq_toclient_dp_groups);
+
+ CreateGroupedPortList(de_ctx, de_ctx->dport_hash_table,
+ &sp->dst_ph, dpgroups,
+ CreateGroupedPortListCmpMpmMaxlen, max_idx);
+
+ SigGroupHeadSPortHashAdd(de_ctx, sp->sh);
+
+ sp->sh->init->port = sp->dst_ph;
+ /* mark this head for deletion once we no longer need
+ * the hash. We're only using the port ptr, so no problem
+ * when we remove this after initialization is done */
+ sp->sh->flags |= SIG_GROUP_HEAD_FREE;
+
+ /* for each destination port we setup the siggrouphead here */
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ if (dp->sh == NULL)
+ continue;
+
+ /* Because a pattern matcher context uses quite some
+ * memory, we first check if we can reuse it from
+ * another group head. */
+ SigGroupHead *lookup_dp_sgh = SigGroupHeadDPortHashLookup(de_ctx, dp->sh);
+ if (lookup_dp_sgh == NULL) {
+ SCLogDebug("dp %p dp->sh %p is the original (sp %p, dst_gr %p, src_gr %p)", dp, dp->sh, sp, dst_gr, src_gr);
+
+ SigGroupHeadSetSigCnt(dp->sh, max_idx);
+ SigGroupHeadBuildMatchArray(de_ctx,dp->sh, max_idx);
+
+ /* init the pattern matcher, this will respect the copy
+ * setting */
+ if (PatternMatchPrepareGroup(de_ctx, dp->sh) < 0) {
+ printf("PatternMatchPrepareGroup failed\n");
+ goto error;
+ }
+ if (dp->sh->mpm_proto_tcp_ctx_ts != NULL) {
+ if (de_ctx->mpm_max_patcnt < dp->sh->mpm_proto_tcp_ctx_ts->pattern_cnt)
+ de_ctx->mpm_max_patcnt = dp->sh->mpm_proto_tcp_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += dp->sh->mpm_proto_tcp_ctx_ts->pattern_cnt;
+ }
+ if (dp->sh->mpm_proto_tcp_ctx_tc != NULL) {
+ if (de_ctx->mpm_max_patcnt < dp->sh->mpm_proto_tcp_ctx_tc->pattern_cnt)
+ de_ctx->mpm_max_patcnt = dp->sh->mpm_proto_tcp_ctx_tc->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += dp->sh->mpm_proto_tcp_ctx_tc->pattern_cnt;
+ }
+ if (dp->sh->mpm_proto_udp_ctx_ts != NULL) {
+ if (de_ctx->mpm_max_patcnt < dp->sh->mpm_proto_udp_ctx_ts->pattern_cnt)
+ de_ctx->mpm_max_patcnt = dp->sh->mpm_proto_udp_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += dp->sh->mpm_proto_udp_ctx_ts->pattern_cnt;
+ }
+ if (dp->sh->mpm_proto_udp_ctx_tc != NULL) {
+ if (de_ctx->mpm_max_patcnt < dp->sh->mpm_proto_udp_ctx_tc->pattern_cnt)
+ de_ctx->mpm_max_patcnt = dp->sh->mpm_proto_udp_ctx_tc->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += dp->sh->mpm_proto_udp_ctx_tc->pattern_cnt;
+ }
+ if (dp->sh->mpm_proto_other_ctx != NULL) {
+ if (de_ctx->mpm_max_patcnt < dp->sh->mpm_proto_other_ctx->pattern_cnt)
+ de_ctx->mpm_max_patcnt = dp->sh->mpm_proto_other_ctx->pattern_cnt;
+
+ de_ctx->mpm_tot_patcnt += dp->sh->mpm_proto_other_ctx->pattern_cnt;
+ }
+ if (dp->sh->mpm_uri_ctx_ts != NULL) {
+ if (de_ctx->mpm_uri_max_patcnt < dp->sh->mpm_uri_ctx_ts->pattern_cnt)
+ de_ctx->mpm_uri_max_patcnt = dp->sh->mpm_uri_ctx_ts->pattern_cnt;
+
+ de_ctx->mpm_uri_tot_patcnt += dp->sh->mpm_uri_ctx_ts->pattern_cnt;
+ }
+ /* dbg */
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && dp->sh->mpm_proto_tcp_ctx_ts) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_proto_tcp_ctx_ts->memory_size;
+ }
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && dp->sh->mpm_proto_tcp_ctx_tc) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_proto_tcp_ctx_tc->memory_size;
+ }
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && dp->sh->mpm_proto_udp_ctx_ts) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_proto_udp_ctx_ts->memory_size;
+ }
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && dp->sh->mpm_proto_udp_ctx_tc) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_proto_udp_ctx_tc->memory_size;
+ }
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_COPY) && dp->sh->mpm_proto_other_ctx) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_proto_other_ctx->memory_size;
+ }
+ if (!(dp->sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY) && dp->sh->mpm_uri_ctx_ts) {
+ de_ctx->mpm_memory_size += dp->sh->mpm_uri_ctx_ts->memory_size;
+ }
+
+ SigGroupHeadDPortHashAdd(de_ctx, dp->sh);
+ SigGroupHeadStore(de_ctx, dp->sh);
+ de_ctx->gh_unique++;
+ } else {
+ SCLogDebug("dp %p dp->sh %p is a copy", dp, dp->sh);
+
+ SigGroupHeadFree(dp->sh);
+ dp->sh = lookup_dp_sgh;
+ dp->flags |= PORT_SIGGROUPHEAD_COPY;
+ dp->sh->flags |= SIG_GROUP_HEAD_REFERENCED;
+
+ de_ctx->gh_reuse++;
+ }
+ }
+ /* sig group head found in hash, free it and use the hashed one */
+ } else {
+ SigGroupHeadFree(sp->sh);
+ sp->sh = lookup_sp_sgh;
+ sp->flags |= PORT_SIGGROUPHEAD_COPY;
+ sp->sh->flags |= SIG_GROUP_HEAD_REFERENCED;
+
+ SCLogDebug("replacing sp->dst_ph %p with lookup_sp_sgh->init->port %p", sp->dst_ph, lookup_sp_sgh->init->port);
+ DetectPortCleanupList(sp->dst_ph);
+ sp->dst_ph = lookup_sp_sgh->init->port;
+ sp->flags |= PORT_GROUP_PORTS_COPY;
+
+ de_ctx->gh_reuse++;
+ }
+ }
+ } else {
+ SigGroupHeadFree(dst_gr->sh);
+ dst_gr->sh = lookup_sgh;
+ dst_gr->flags |= ADDRESS_SIGGROUPHEAD_COPY;
+ dst_gr->sh->flags |= SIG_GROUP_HEAD_REFERENCED;
+
+ SCLogDebug("replacing dst_gr->port %p with lookup_sgh->init->port %p", dst_gr->port, lookup_sgh->init->port);
+ DetectPortCleanupList(dst_gr->port);
+ dst_gr->port = lookup_sgh->init->port;
+ dst_gr->flags |= ADDRESS_PORTS_COPY;
+
+ de_ctx->gh_reuse++;
+ }
+ }
+ /* free the temp list */
+ DetectAddressCleanupList(tmp_gr_list);
+ /* clear now unneeded sig group head */
+ SigGroupHeadFree(src_gr->sh);
+ src_gr->sh = NULL;
+
+ /* free dst addr sgh's */
+ dst_gr_head = GetHeadPtr(src_gr->dst_gh,family);
+ for (dst_gr = dst_gr_head; dst_gr != NULL; dst_gr = dst_gr->next) {
+ if (!(dst_gr->flags & ADDRESS_SIGGROUPHEAD_COPY)) {
+ if (!(dst_gr->sh->flags & SIG_GROUP_HEAD_REFERENCED)) {
+ SCLogDebug("removing sgh %p from hash", dst_gr->sh);
+
+ int r = SigGroupHeadHashRemove(de_ctx,dst_gr->sh);
+ BUG_ON(r == -1);
+ if (r == 0) {
+ SCLogDebug("removed sgh %p from hash", dst_gr->sh);
+ SigGroupHeadFree(dst_gr->sh);
+ dst_gr->sh = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+static void DetectEngineBuildDecoderEventSgh(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->decoder_event_sgh == NULL)
+ return;
+
+ uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx);
+ SigGroupHeadSetSigCnt(de_ctx->decoder_event_sgh, max_idx);
+ SigGroupHeadBuildMatchArray(de_ctx, de_ctx->decoder_event_sgh, max_idx);
+}
+
+int SigAddressPrepareStage3(DetectEngineCtx *de_ctx)
+{
+ int r;
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("building signature grouping structure, stage 3: "
+ "building destination address lists...");
+ }
+ //DetectAddressPrintMemory();
+ //DetectSigGroupPrintMemory();
+ //DetectPortPrintMemory();
+
+ int f = 0;
+ int proto;
+ for (f = 0; f < FLOW_STATES; f++) {
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_TCP],AF_INET,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[6],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_UDP],AF_INET,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[17],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP],AF_INET,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[IPPROTO_SCTP],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_TCP],AF_INET6,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[6],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_UDP],AF_INET6,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[17],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP],AF_INET6,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[IPPROTO_SCTP],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_TCP],AF_UNSPEC,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[6],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_UDP],AF_UNSPEC,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[17],AF_INET) failed\n");
+ goto error;
+ }
+ r = BuildDestinationAddressHeadsWithBothPorts(de_ctx, de_ctx->flow_gh[f].src_gh[IPPROTO_SCTP],AF_UNSPEC,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[IPPROTO_SCTP],AF_INET) failed\n");
+ goto error;
+ }
+ for (proto = 0; proto < 256; proto++) {
+ if (proto == IPPROTO_TCP || proto == IPPROTO_UDP || proto == IPPROTO_SCTP)
+ continue;
+
+ r = BuildDestinationAddressHeads(de_ctx, de_ctx->flow_gh[f].src_gh[proto],AF_INET,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[%" PRId32 "],AF_INET) failed\n", proto);
+ goto error;
+ }
+ r = BuildDestinationAddressHeads(de_ctx, de_ctx->flow_gh[f].src_gh[proto],AF_INET6,f);
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[%" PRId32 "],AF_INET6) failed\n", proto);
+ goto error;
+ }
+ r = BuildDestinationAddressHeads(de_ctx, de_ctx->flow_gh[f].src_gh[proto],AF_UNSPEC,f); /* for any */
+ if (r < 0) {
+ printf ("BuildDestinationAddressHeads(src_gh[%" PRId32 "],AF_UNSPEC) failed\n", proto);
+ goto error;
+ }
+ }
+ }
+
+ /* prepare the decoder event sgh */
+ DetectEngineBuildDecoderEventSgh(de_ctx);
+
+ /* cleanup group head (uri)content_array's */
+ SigGroupHeadFreeMpmArrays(de_ctx);
+ /* cleanup group head sig arrays */
+ SigGroupHeadFreeSigArrays(de_ctx);
+
+ /* cleanup the hashes now since we won't need them
+ * after the initialization phase. */
+ SigGroupHeadHashFree(de_ctx);
+ SigGroupHeadDPortHashFree(de_ctx);
+ SigGroupHeadSPortHashFree(de_ctx);
+ SigGroupHeadMpmHashFree(de_ctx);
+ SigGroupHeadMpmUriHashFree(de_ctx);
+ DetectPortDpHashFree(de_ctx);
+ DetectPortSpHashFree(de_ctx);
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("MPM memory %" PRIuMAX " (dynamic %" PRIu32 ", ctxs %" PRIuMAX ", avg per ctx %" PRIu32 ")",
+ de_ctx->mpm_memory_size + ((de_ctx->mpm_unique + de_ctx->mpm_uri_unique) * (uintmax_t)sizeof(MpmCtx)),
+ de_ctx->mpm_memory_size, ((de_ctx->mpm_unique + de_ctx->mpm_uri_unique) * (uintmax_t)sizeof(MpmCtx)),
+ de_ctx->mpm_unique ? de_ctx->mpm_memory_size / de_ctx->mpm_unique: 0);
+
+ SCLogDebug("max sig id %" PRIu32 ", array size %" PRIu32 "", DetectEngineGetMaxSigId(de_ctx), DetectEngineGetMaxSigId(de_ctx) / 8 + 1);
+ SCLogDebug("signature group heads: unique %" PRIu32 ", copies %" PRIu32 ".", de_ctx->gh_unique, de_ctx->gh_reuse);
+ SCLogDebug("MPM instances: %" PRIu32 " unique, copies %" PRIu32 " (none %" PRIu32 ").",
+ de_ctx->mpm_unique, de_ctx->mpm_reuse, de_ctx->mpm_none);
+ SCLogDebug("MPM (URI) instances: %" PRIu32 " unique, copies %" PRIu32 " (none %" PRIu32 ").",
+ de_ctx->mpm_uri_unique, de_ctx->mpm_uri_reuse, de_ctx->mpm_uri_none);
+ SCLogDebug("MPM max patcnt %" PRIu32 ", avg %" PRIu32 "", de_ctx->mpm_max_patcnt, de_ctx->mpm_unique?de_ctx->mpm_tot_patcnt/de_ctx->mpm_unique:0);
+ if (de_ctx->mpm_uri_tot_patcnt && de_ctx->mpm_uri_unique)
+ SCLogDebug("MPM (URI) max patcnt %" PRIu32 ", avg %" PRIu32 " (%" PRIu32 "/%" PRIu32 ")", de_ctx->mpm_uri_max_patcnt, de_ctx->mpm_uri_tot_patcnt/de_ctx->mpm_uri_unique, de_ctx->mpm_uri_tot_patcnt, de_ctx->mpm_uri_unique);
+ SCLogDebug("port maxgroups: %" PRIu32 ", avg %" PRIu32 ", tot %" PRIu32 "", g_groupportlist_maxgroups, g_groupportlist_groupscnt ? g_groupportlist_totgroups/g_groupportlist_groupscnt : 0, g_groupportlist_totgroups);
+
+ SCLogInfo("building signature grouping structure, stage 3: building destination address lists... complete");
+ }
+ return 0;
+error:
+ printf("SigAddressPrepareStage3 error\n");
+ return -1;
+}
+
+int SigAddressCleanupStage1(DetectEngineCtx *de_ctx)
+{
+ BUG_ON(de_ctx == NULL);
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogDebug("cleaning up signature grouping structure...");
+ }
+
+ int f, proto;
+ for (f = 0; f < FLOW_STATES; f++) {
+ for (proto = 0; proto < 256; proto++) {
+ /* XXX fix this */
+ DetectAddressHeadFree(de_ctx->flow_gh[f].src_gh[proto]);
+ de_ctx->flow_gh[f].src_gh[proto] = NULL;
+ }
+ }
+
+ if (de_ctx->decoder_event_sgh)
+ SigGroupHeadFree(de_ctx->decoder_event_sgh);
+ de_ctx->decoder_event_sgh = NULL;
+
+ IPOnlyDeinit(de_ctx, &de_ctx->io_ctx);
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ SCLogInfo("cleaning up signature grouping structure... complete");
+ }
+ return 0;
+}
+
+void DbgPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ if (sgh == NULL) {
+ printf("\n");
+ return;
+ }
+
+ uint32_t sig;
+ for (sig = 0; sig < sgh->sig_cnt; sig++) {
+ printf("%" PRIu32 " ", sgh->match_array[sig]->id);
+ }
+ printf("\n");
+}
+
+void DbgPrintSigs2(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ if (sgh == NULL || sgh->init == NULL) {
+ printf("\n");
+ return;
+ }
+
+ uint32_t sig;
+ for (sig = 0; sig < DetectEngineGetMaxSigId(de_ctx); sig++) {
+ if (sgh->init->sig_array[(sig/8)] & (1<<(sig%8))) {
+ printf("%" PRIu32 " ", de_ctx->sig_array[sig]->id);
+ }
+ }
+ printf("\n");
+}
+
+void DbgSghContainsSig(DetectEngineCtx *de_ctx, SigGroupHead *sgh, uint32_t sid)
+{
+ if (sgh == NULL || sgh->init == NULL) {
+ printf("\n");
+ return;
+ }
+
+ uint32_t sig;
+ for (sig = 0; sig < DetectEngineGetMaxSigId(de_ctx); sig++) {
+ if (!(sgh->init->sig_array[(sig/8)] & (1<<(sig%8))))
+ continue;
+
+ Signature *s = de_ctx->sig_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (sid == s->id) {
+ printf("%" PRIu32 " ", de_ctx->sig_array[sig]->id);
+ }
+ }
+ printf("\n");
+}
+
+/** \brief finalize preparing sgh's */
+int SigAddressPrepareStage4(DetectEngineCtx *de_ctx)
+{
+ SCEnter();
+
+ //SCLogInfo("sgh's %"PRIu32, de_ctx->sgh_array_cnt);
+
+ uint32_t idx = 0;
+
+ for (idx = 0; idx < de_ctx->sgh_array_cnt; idx++) {
+ SigGroupHead *sgh = de_ctx->sgh_array[idx];
+ if (sgh == NULL)
+ continue;
+ SigGroupHeadSetFilemagicFlag(de_ctx, sgh);
+ SigGroupHeadSetFileMd5Flag(de_ctx, sgh);
+ SigGroupHeadSetFilesizeFlag(de_ctx, sgh);
+ SigGroupHeadSetFilestoreCount(de_ctx, sgh);
+ SCLogDebug("filestore count %u", sgh->filestore_cnt);
+
+ SigGroupHeadBuildNonMpmArray(de_ctx, sgh);
+ }
+
+ if (de_ctx->decoder_event_sgh != NULL) {
+ /* no need to set filestore count here as that would make a
+ * signature not decode event only. */
+ }
+
+ SCFree(de_ctx->sgh_array);
+ de_ctx->sgh_array_cnt = 0;
+ de_ctx->sgh_array_size = 0;
+
+ SCReturnInt(0);
+}
+
+/* shortcut for debugging. If enabled Stage5 will
+ * print sigid's for all groups */
+#define PRINTSIGS
+
+/* just printing */
+int SigAddressPrepareStage5(DetectEngineCtx *de_ctx)
+{
+ DetectAddressHead *global_dst_gh = NULL;
+ DetectAddress *global_src_gr = NULL, *global_dst_gr = NULL;
+ uint32_t u;
+
+ printf("* Building signature grouping structure, stage 5: print...\n");
+
+ int f, proto;
+ printf("\n");
+ for (f = 0; f < FLOW_STATES; f++) {
+ printf("\n");
+ for (proto = 0; proto < 256; proto++) {
+ if (proto != IPPROTO_TCP)
+ continue;
+
+ for (global_src_gr = de_ctx->flow_gh[f].src_gh[proto]->ipv4_head; global_src_gr != NULL;
+ global_src_gr = global_src_gr->next)
+ {
+ printf("1 Src Addr: "); DetectAddressPrint(global_src_gr);
+ printf(" (sh %p)\n", global_src_gr->sh);
+ //printf("\n");
+
+#ifdef PRINTSIGS
+ SigGroupHeadPrintSigs(de_ctx, global_src_gr->sh);
+ if (global_src_gr->sh != NULL) {
+ printf(" - ");
+ for (u = 0; u < global_src_gr->sh->sig_cnt; u++) {
+ Signature *s = global_src_gr->sh->match_array[u];
+ printf("%" PRIu32 " ", s->id);
+ }
+ printf("\n");
+ }
+#endif
+
+ global_dst_gh = global_src_gr->dst_gh;
+ if (global_dst_gh == NULL)
+ continue;
+
+ for (global_dst_gr = global_dst_gh->ipv4_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" 2 Dst Addr: "); DetectAddressPrint(global_dst_gr);
+
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf(" (COPY): ");
+ } else {
+ printf(" (ORIGINAL): ");
+ }
+ } else {
+ printf(" ");
+ }
+
+#ifdef PRINTSIGS
+ if (global_dst_gr->sh != NULL) {
+ printf(" - ");
+ for (u = 0; u < global_dst_gr->sh->sig_cnt; u++) {
+ Signature *s = global_dst_gr->sh->match_array[u];
+ printf("%" PRIu32 " ", s->id);
+ }
+ printf("\n");
+ }
+#endif
+
+
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" 3 Src port(range): "); DetectPortPrint(sp);
+ //printf(" (sh %p)", sp->sh);
+ printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" 4 Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ", sgh %p, maxlen %" PRIu32 ")", dp->sh->sig_cnt, dp->sh, dp->sh->mpm_content_maxlen);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = dp->sh->match_array[u];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ for (global_dst_gr = global_dst_gh->any_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = dp->sh->match_array[u];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ }
+#if 0
+ for (global_src_gr = de_ctx->flow_gh[f].src_gh[proto]->ipv6_head; global_src_gr != NULL;
+ global_src_gr = global_src_gr->next)
+ {
+ printf("- "); DetectAddressPrint(global_src_gr);
+ //printf(" (sh %p)\n", global_src_gr->sh);
+
+ global_dst_gh = global_src_gr->dst_gh;
+ if (global_dst_gh == NULL)
+ continue;
+
+ for (global_dst_gr = global_dst_gh->ipv6_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = de_ctx->sig_array[dp->sh->match_array[u]];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ for (global_dst_gr = global_dst_gh->any_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = de_ctx->sig_array[dp->sh->match_array[u]];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ }
+
+ for (global_src_gr = de_ctx->flow_gh[f].src_gh[proto]->any_head; global_src_gr != NULL;
+ global_src_gr = global_src_gr->next)
+ {
+ printf("- "); DetectAddressPrint(global_src_gr);
+ //printf(" (sh %p)\n", global_src_gr->sh);
+
+ global_dst_gh = global_src_gr->dst_gh;
+ if (global_dst_gh == NULL)
+ continue;
+
+ for (global_dst_gr = global_dst_gh->any_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = de_ctx->sig_array[dp->sh->match_array[u]];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ for (global_dst_gr = global_dst_gh->ipv4_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = de_ctx->sig_array[dp->sh->match_array[u]];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ for (global_dst_gr = global_dst_gh->ipv6_head;
+ global_dst_gr != NULL;
+ global_dst_gr = global_dst_gr->next)
+ {
+ printf(" - "); DetectAddressPrint(global_dst_gr);
+ //printf(" (sh %p) ", global_dst_gr->sh);
+ if (global_dst_gr->sh) {
+ if (global_dst_gr->sh->flags & ADDRESS_SIGGROUPHEAD_COPY) {
+ printf("(COPY)\n");
+ } else {
+ printf("\n");
+ }
+ }
+ DetectPort *sp = global_dst_gr->port;
+ for ( ; sp != NULL; sp = sp->next) {
+ printf(" * Src port(range): "); DetectPortPrint(sp); printf("\n");
+ DetectPort *dp = sp->dst_ph;
+ for ( ; dp != NULL; dp = dp->next) {
+ printf(" * Dst port(range): "); DetectPortPrint(dp);
+ printf(" (sigs %" PRIu32 ")", dp->sh->sig_cnt);
+#ifdef PRINTSIGS
+ printf(" - ");
+ for (u = 0; u < dp->sh->sig_cnt; u++) {
+ Signature *s = de_ctx->sig_array[dp->sh->match_array[u]];
+ printf("%" PRIu32 " ", s->id);
+ }
+#endif
+ printf("\n");
+ }
+ }
+ }
+ }
+#endif
+ }
+ }
+
+ printf("* Building signature grouping structure, stage 5: print... done\n");
+ return 0;
+}
+
+static int SigMatchListLen(SigMatch *sm)
+{
+ int len = 0;
+ for (; sm != NULL; sm = sm->next)
+ len++;
+
+ return len;
+}
+
+static int SigMatchPrepare(DetectEngineCtx *de_ctx)
+{
+ SCEnter();
+
+ Signature *s = de_ctx->sig_list;
+ for (; s != NULL; s = s->next) {
+ int type;
+ for (type = 0; type < DETECT_SM_LIST_MAX; type++) {
+ SigMatch *sm = s->sm_lists[type];
+ int len = SigMatchListLen(sm);
+ if (len == 0)
+ s->sm_arrays[type] = NULL;
+ else {
+ SigMatchData *smd = (SigMatchData*)SCMalloc(len * sizeof(SigMatchData));
+ if (smd == NULL) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+ /* Copy sm type and Context into array */
+ s->sm_arrays[type] = smd;
+ for (; sm != NULL; sm = sm->next, smd++) {
+ smd->type = sm->type;
+ smd->ctx = sm->ctx;
+ smd->is_last = (sm->next == NULL);
+ }
+ }
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Convert the signature list into the runtime match structure.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context whose Signatures have
+ * to be processed
+ *
+ * \retval 0 On Success.
+ * \retval -1 On failure.
+ */
+int SigGroupBuild(DetectEngineCtx *de_ctx)
+{
+ Signature *s = de_ctx->sig_list;
+
+ /* Assign the unique order id of signatures after sorting,
+ * so the IP Only engine process them in order too. Also
+ * reset the old signums and assign new signums. We would
+ * have experienced Sig reordering by now, hence the new
+ * signums. */
+ de_ctx->signum = 0;
+ while (s != NULL) {
+ s->num = de_ctx->signum++;
+
+ s = s->next;
+ }
+
+ if (DetectSetFastPatternAndItsId(de_ctx) < 0)
+ return -1;
+
+ /* if we are using single sgh_mpm_context then let us init the standard mpm
+ * contexts using the mpm_ctx factory */
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ SigInitStandardMpmFactoryContexts(de_ctx);
+ }
+
+ if (SigAddressPrepareStage1(de_ctx) != 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+//exit(0);
+ if (SigAddressPrepareStage2(de_ctx) != 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (SigAddressPrepareStage3(de_ctx) != 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+ if (SigAddressPrepareStage4(de_ctx) != 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) {
+ MpmCtx *mpm_ctx = NULL;
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA) {
+ /* setting it to default. You've gotta remove it once you fix the state table thing */
+ SCACConstructBoth16and32StateTables();
+
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ CUcontext cuda_context = CudaHandlerModuleGetContext(MPM_AC_CUDA_MODULE_NAME, conf->device_id);
+ if (cuda_context == 0) {
+ SCLogError(SC_ERR_FATAL, "cuda context is NULL.");
+ exit(EXIT_FAILURE);
+ }
+ int r = SCCudaCtxPushCurrent(cuda_context);
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "context push failed.");
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("packet- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("packet- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_other_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("packet- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_uri, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_uri, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("uri- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcbd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcbd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hcbd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsbd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsbd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hsbd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_smtp, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_smtp, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("smtp- %d\n"; mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hhd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hrhd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hmd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hmd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hmd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hcd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrud, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrud, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hrud- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("stream- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsmd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hsmd- %d\n", mpm_ctx->pattern_cnt);
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsmd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hsmd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hscd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hscd- %d\n", mpm_ctx->pattern_cnt);
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hscd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hscd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_huad, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("huad- %d\n", mpm_ctx->pattern_cnt);
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_huad, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("huad- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhhd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hhhd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhhd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hhhd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhhd, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hrhhd- %d\n", mpm_ctx->pattern_cnt);
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhhd, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ //printf("hrhhd- %d\n", mpm_ctx->pattern_cnt);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA) {
+ int r = SCCudaCtxPopCurrent(NULL);
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "cuda context pop failure.");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* too late to call this either ways. Should be called post ac goto.
+ * \todo Support this. */
+ DetermineCudaStateTableSize(de_ctx);
+#endif
+
+ }
+
+// SigAddressPrepareStage5(de_ctx);
+// DetectAddressPrintMemory();
+// DetectSigGroupPrintMemory();
+// DetectPortPrintMemory();
+
+ if (SigMatchPrepare(de_ctx) != 0) {
+ SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed");
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef PROFILING
+ SCProfilingRuleInitCounters(de_ctx);
+#endif
+ return 0;
+}
+
+int SigGroupCleanup (DetectEngineCtx *de_ctx)
+{
+ SigAddressCleanupStage1(de_ctx);
+
+ return 0;
+}
+
+static inline void PrintFeatureList(int flags, char sep)
+{
+ int prev = 0;
+ if (flags & SIGMATCH_NOOPT) {
+ printf("No option");
+ prev = 1;
+ }
+ if (flags & SIGMATCH_IPONLY_COMPAT) {
+ if (prev == 1)
+ printf("%c", sep);
+ printf("compatible with IP only rule");
+ prev = 1;
+ }
+ if (flags & SIGMATCH_DEONLY_COMPAT) {
+ if (prev == 1)
+ printf("%c", sep);
+ printf("compatible with decoder event only rule");
+ prev = 1;
+ }
+ if (flags & SIGMATCH_PAYLOAD) {
+ if (prev == 1)
+ printf("%c", sep);
+ printf("payload inspecting keyword");
+ prev = 1;
+ }
+ if (prev == 0) {
+ printf("none");
+ }
+}
+
+static inline void SigMultilinePrint(int i, char *prefix)
+{
+ if (sigmatch_table[i].desc) {
+ printf("%sDescription: %s\n", prefix, sigmatch_table[i].desc);
+ }
+ printf("%sProtocol: %s\n", prefix,
+ AppLayerGetProtoName(sigmatch_table[i].alproto));
+ printf("%sFeatures: ", prefix);
+ PrintFeatureList(sigmatch_table[i].flags, ',');
+ if (sigmatch_table[i].url) {
+ printf("\n%sDocumentation: %s", prefix, sigmatch_table[i].url);
+ }
+ printf("\n");
+}
+
+void SigTableList(const char *keyword)
+{
+ size_t size = sizeof(sigmatch_table) / sizeof(SigTableElmt);
+ size_t i;
+ char *proto_name;
+
+ if (keyword == NULL) {
+ printf("=====Supported keywords=====\n");
+ for (i = 0; i < size; i++) {
+ if (sigmatch_table[i].name != NULL) {
+ if (sigmatch_table[i].flags & SIGMATCH_NOT_BUILT) {
+ printf("- %s (not built-in)\n", sigmatch_table[i].name);
+ } else {
+ printf("- %s\n", sigmatch_table[i].name);
+ }
+ }
+ }
+ } else if (!strcmp("csv", keyword)) {
+ printf("name;description;app layer;features;documentation\n");
+ for (i = 0; i < size; i++) {
+ if (sigmatch_table[i].name != NULL) {
+ if (sigmatch_table[i].flags & SIGMATCH_NOT_BUILT) {
+ continue;
+ }
+ printf("%s;", sigmatch_table[i].name);
+ if (sigmatch_table[i].desc) {
+ printf("%s", sigmatch_table[i].desc);
+ }
+ /* Build feature */
+ proto_name = AppLayerGetProtoName(sigmatch_table[i].alproto);
+ printf(";%s;", proto_name ? proto_name : "Unset");
+ PrintFeatureList(sigmatch_table[i].flags, ':');
+ printf(";");
+ if (sigmatch_table[i].url) {
+ printf("%s", sigmatch_table[i].url);
+ }
+ printf(";");
+ printf("\n");
+ }
+ }
+ } else if (!strcmp("all", keyword)) {
+ for (i = 0; i < size; i++) {
+ printf("%s:\n", sigmatch_table[i].name);
+ SigMultilinePrint(i, "\t");
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ if ((sigmatch_table[i].name != NULL) &&
+ !strcmp(sigmatch_table[i].name, keyword)) {
+ printf("= %s =\n", sigmatch_table[i].name);
+ if (sigmatch_table[i].flags & SIGMATCH_NOT_BUILT) {
+ printf("Not built-in\n");
+ return;
+ }
+ SigMultilinePrint(i, "");
+ return;
+ }
+ }
+ }
+ return;
+}
+
+void SigTableSetup(void)
+{
+ memset(sigmatch_table, 0, sizeof(sigmatch_table));
+
+ DetectSidRegister();
+ DetectPriorityRegister();
+ DetectRevRegister();
+ DetectClasstypeRegister();
+ DetectReferenceRegister();
+ DetectTagRegister();
+ DetectThresholdRegister();
+ DetectMetadataRegister();
+ DetectMsgRegister();
+ DetectAckRegister();
+ DetectSeqRegister();
+ DetectContentRegister();
+ DetectUricontentRegister();
+ DetectPcreRegister();
+ DetectDepthRegister();
+ DetectNocaseRegister();
+ DetectRawbytesRegister();
+ DetectBytetestRegister();
+ DetectBytejumpRegister();
+ DetectSameipRegister();
+ DetectGeoipRegister();
+ DetectL3ProtoRegister();
+ DetectIPProtoRegister();
+ DetectWithinRegister();
+ DetectDistanceRegister();
+ DetectOffsetRegister();
+ DetectReplaceRegister();
+ DetectFlowRegister();
+ DetectWindowRegister();
+ DetectRpcRegister();
+ DetectFtpbounceRegister();
+ DetectIsdataatRegister();
+ DetectIdRegister();
+ DetectDsizeRegister();
+ DetectFlowvarRegister();
+ DetectFlowintRegister();
+ DetectPktvarRegister();
+ DetectNoalertRegister();
+ DetectFlowbitsRegister();
+ DetectHostbitsRegister();
+ DetectXbitsRegister();
+ DetectEngineEventRegister();
+ DetectIpOptsRegister();
+ DetectFlagsRegister();
+ DetectFragBitsRegister();
+ DetectFragOffsetRegister();
+ DetectGidRegister();
+ DetectMarkRegister();
+ DetectCsumRegister();
+ DetectStreamSizeRegister();
+ DetectTtlRegister();
+ DetectTosRegister();
+ DetectFastPatternRegister();
+ DetectITypeRegister();
+ DetectICodeRegister();
+ DetectIcmpIdRegister();
+ DetectIcmpSeqRegister();
+ DetectDceIfaceRegister();
+ DetectDceOpnumRegister();
+ DetectDceStubDataRegister();
+ DetectHttpCookieRegister();
+ DetectHttpMethodRegister();
+ DetectHttpStatMsgRegister();
+ DetectTlsRegister();
+ DetectTlsVersionRegister();
+ DetectUrilenRegister();
+ DetectDetectionFilterRegister();
+ DetectHttpHeaderRegister();
+ DetectHttpRawHeaderRegister();
+ DetectHttpClientBodyRegister();
+ DetectHttpServerBodyRegister();
+ DetectHttpUriRegister();
+ DetectHttpRawUriRegister();
+ DetectAsn1Register();
+ DetectSshVersionRegister();
+ DetectSshSoftwareVersionRegister();
+ DetectSslStateRegister();
+ DetectHttpStatCodeRegister();
+ DetectSslVersionRegister();
+ DetectByteExtractRegister();
+ DetectFiledataRegister();
+ DetectPktDataRegister();
+ DetectFilenameRegister();
+ DetectFileextRegister();
+ DetectFilestoreRegister();
+ DetectFilemagicRegister();
+ DetectFileMd5Register();
+ DetectFilesizeRegister();
+ DetectAppLayerEventRegister();
+ DetectHttpUARegister();
+ DetectHttpHHRegister();
+ DetectHttpHRHRegister();
+ DetectLuaRegister();
+ DetectIPRepRegister();
+ DetectDnsQueryRegister();
+ DetectModbusRegister();
+ DetectAppLayerProtocolRegister();
+ DetectTemplateRegister();
+}
+
+void SigTableRegisterTests(void)
+{
+ /* register the tests */
+ int i = 0;
+ for (i = 0; i < DETECT_TBLSIZE; i++) {
+ g_ut_modules++;
+ if (sigmatch_table[i].RegisterTests != NULL) {
+ sigmatch_table[i].RegisterTests();
+ g_ut_covered++;
+ } else {
+ SCLogDebug("detection plugin %s has no unittest "
+ "registration function.", sigmatch_table[i].name);
+
+ if (coverage_unittests)
+ SCLogWarning(SC_WARN_NO_UNITTESTS, "detection plugin %s has no unittest "
+ "registration function.", sigmatch_table[i].name);
+ }
+ }
+}
+
+/*
+ * TESTS
+ */
+
+#ifdef UNITTESTS
+#include "flow-util.h"
+#include "stream-tcp-reassemble.h"
+#include "util-var-name.h"
+
+static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "default-log-dir: /var/log/suricata\n"
+ "\n"
+ "logging:\n"
+ "\n"
+ " default-log-level: debug\n"
+ "\n"
+ " default-format: \"<%t> - <%l>\"\n"
+ "\n"
+ " default-startup-message: Your IDS has started.\n"
+ "\n"
+ " default-output-filter:\n"
+ "\n"
+ " output:\n"
+ "\n"
+ " - interface: console\n"
+ " log-level: info\n"
+ "\n"
+ " - interface: file\n"
+ " filename: /var/log/suricata.log\n"
+ "\n"
+ " - interface: syslog\n"
+ " facility: local5\n"
+ " format: \"%l\"\n"
+ "\n"
+ "pfring:\n"
+ "\n"
+ " interface: eth0\n"
+ "\n"
+ " clusterid: 99\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[192.168.0.0/16,10.8.0.0/16,127.0.0.1,2001:888:"
+ "13c5:5AFE::/64,2001:888:13c5:CAFE::/64]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"[!192.168.0.0/16,2000::/3]\"\n"
+ "\n"
+ " HTTP_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " SMTP_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " SQL_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " DNS_SERVERS: any\n"
+ "\n"
+ " TELNET_SERVERS: any\n"
+ "\n"
+ " AIM_SERVERS: any\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n"
+ " SHELLCODE_PORTS: 80\n"
+ "\n"
+ " ORACLE_PORTS: 1521\n"
+ "\n"
+ " SSH_PORTS: 22\n"
+ "\n";
+
+static int SigTest01Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+
+ char sig[] = "alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, mpm_type) == 0) {
+ result = 0;
+ goto end;
+ }
+#if 0
+ //printf("URI0 \"%s\", len %" PRIu32 "\n", p.http_uri.raw[0], p.http_uri.raw_size[0]);
+ //printf("URI1 \"%s\", len %" PRIu32 "\n", p.http_uri.raw[1], p.http_uri.raw_size[1]);
+
+ if (p->http_uri.raw_size[0] == 5 &&
+ memcmp(p->http_uri.raw[0], "/one/", 5) == 0 &&
+ p->http_uri.raw_size[1] == 5 &&
+ memcmp(p->http_uri.raw[1], "/two/", 5) == 0)
+ {
+ result = 1;
+ }
+
+#endif
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int SigTest01B2g (void)
+{
+ return SigTest01Real(MPM_B2G);
+}
+static int SigTest01B3g (void)
+{
+ return SigTest01Real(MPM_B3G);
+}
+static int SigTest01Wm (void)
+{
+ return SigTest01Real(MPM_WUMANBER);
+}
+
+static int SigTest02Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ char sig[] = "alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"Host: one.example.org\"; offset:20; depth:41; sid:1;)";
+ int ret = UTHPacketMatchSigMpm(p, sig, mpm_type);
+ UTHFreePacket(p);
+ return ret;
+}
+
+static int SigTest02B2g (void)
+{
+ return SigTest02Real(MPM_B2G);
+}
+static int SigTest02B3g (void)
+{
+ return SigTest02Real(MPM_B3G);
+}
+static int SigTest02Wm (void)
+{
+ return SigTest02Real(MPM_WUMANBER);
+}
+
+
+static int SigTest03Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"Host: one.example.org\"; offset:20; depth:39; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ //PatternMatchPrepare(mpm_ctx, mpm_type);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest03B2g (void)
+{
+ return SigTest03Real(MPM_B2G);
+}
+static int SigTest03B3g (void)
+{
+ return SigTest03Real(MPM_B3G);
+}
+static int SigTest03Wm (void)
+{
+ return SigTest03Real(MPM_WUMANBER);
+}
+
+
+static int SigTest04Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n" /* 20*/
+ "Host: one.example.org\r\n" /* 23, post "Host:" 18 */
+ "\r\n\r\n" /* 4 */
+ "GET /two/ HTTP/1.1\r\n" /* 20 */
+ "Host: two.example.org\r\n" /* 23 */
+ "\r\n\r\n"; /* 4 */
+ uint16_t buflen = strlen((char *)buf);
+
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"Host:\"; offset:20; depth:25; content:\"Host:\"; distance:42; within:47; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest04B2g (void)
+{
+ return SigTest04Real(MPM_B2G);
+}
+static int SigTest04B3g (void)
+{
+ return SigTest04Real(MPM_B3G);
+}
+static int SigTest04Wm (void)
+{
+ return SigTest04Real(MPM_WUMANBER);
+}
+
+
+static int SigTest05Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.1\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"Host:\"; offset:20; depth:25; content:\"Host:\"; distance:48; within:52; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!PacketAlertCheck(p, 1)) {
+ result = 1;
+ } else {
+ printf("sig matched but shouldn't have: ");
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest05B2g (void)
+{
+ return SigTest05Real(MPM_B2G);
+}
+static int SigTest05B3g (void)
+{
+ return SigTest05Real(MPM_B3G);
+}
+static int SigTest05Wm (void)
+{
+ return SigTest05Real(MPM_WUMANBER);
+}
+
+
+static int SigTest06Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.1\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI test\"; uricontent:\"two\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 1;
+ else
+ printf("sid:1 %s, sid:2 %s: ",
+ PacketAlertCheck(p, 1) ? "OK" : "FAIL",
+ PacketAlertCheck(p, 2) ? "OK" : "FAIL");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest06B2g (void)
+{
+ return SigTest06Real(MPM_B2G);
+}
+static int SigTest06B3g (void)
+{
+ return SigTest06Real(MPM_B3G);
+}
+static int SigTest06Wm (void)
+{
+ return SigTest06Real(MPM_WUMANBER);
+}
+
+
+static int SigTest07Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.1\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI test\"; uricontent:\"three\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 0;
+ else
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FlowCleanupAppLayer(&f);
+ FLOW_DESTROY(&f);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+static int SigTest07B2g (void)
+{
+ return SigTest07Real(MPM_B2G);
+}
+static int SigTest07B3g (void)
+{
+ return SigTest07Real(MPM_B3G);
+}
+static int SigTest07Wm (void)
+{
+ return SigTest07Real(MPM_WUMANBER);
+}
+
+
+static int SigTest08Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.0\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.0\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(Flow));
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/1\\.0\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI test\"; uricontent:\"one\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 1;
+ else
+ printf("sid:1 %s, sid:2 %s: ",
+ PacketAlertCheck(p, 1) ? "OK" : "FAIL",
+ PacketAlertCheck(p, 2) ? "OK" : "FAIL");
+
+end:
+ FlowCleanupAppLayer(&f);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest08B2g (void)
+{
+ return SigTest08Real(MPM_B2G);
+}
+static int SigTest08B3g (void)
+{
+ return SigTest08Real(MPM_B3G);
+}
+static int SigTest08Wm (void)
+{
+ return SigTest08Real(MPM_WUMANBER);
+}
+
+
+static int SigTest09Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.0\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.0\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.flags |= FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/1\\.0\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI test\"; uricontent:\"two\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 1;
+ else
+ result = 0;
+
+end:
+ FlowCleanupAppLayer(&f);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest09B2g (void)
+{
+ return SigTest09Real(MPM_B2G);
+}
+static int SigTest09B3g (void)
+{
+ return SigTest09Real(MPM_B3G);
+}
+static int SigTest09Wm (void)
+{
+ return SigTest09Real(MPM_WUMANBER);
+}
+
+
+static int SigTest10Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "ABC";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ int result = 0;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Long content test (1)\"; content:\"ABCD\"; depth:4; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Long content test (2)\"; content:\"VWXYZ\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 0;
+ else
+ result = 1;
+
+ end:
+ FlowCleanupAppLayer(&f);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest10B2g (void)
+{
+ return SigTest10Real(MPM_B2G);
+}
+static int SigTest10B3g (void)
+{
+ return SigTest10Real(MPM_B3G);
+}
+static int SigTest10Wm (void)
+{
+ return SigTest10Real(MPM_WUMANBER);
+}
+
+
+static int SigTest11Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Flow f;
+ TcpSession ssn;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (content:\"ABCDEFGHIJ\"; content:\"klmnop\"; content:\"1234\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (content:\"VWXYZabcde\"; content:\"5678\"; content:\"89\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) && PacketAlertCheck(p, 2))
+ result = 1;
+
+ end:
+ FlowCleanupAppLayer(&f);
+ SigGroupCleanup(de_ctx);
+ if (det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ UTHFreePackets(&p, 1);
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest11B2g (void)
+{
+ return SigTest11Real(MPM_B2G);
+}
+static int SigTest11B3g (void)
+{
+ return SigTest11Real(MPM_B3G);
+}
+static int SigTest11Wm (void)
+{
+ return SigTest11Real(MPM_WUMANBER);
+}
+
+
+static int SigTest12Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FLOW_INITIALIZE(&f);
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Content order test\"; content:\"ABCDEFGHIJ\"; content:\"klmnop\"; content:\"1234\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+ else
+ result = 0;
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest12B2g (void)
+{
+ return SigTest12Real(MPM_B2G);
+}
+static int SigTest12B3g (void)
+{
+ return SigTest12Real(MPM_B3G);
+}
+static int SigTest12Wm (void)
+{
+ return SigTest12Real(MPM_WUMANBER);
+}
+
+
+static int SigTest13Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FLOW_INITIALIZE(&f);
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Content order test\"; content:\"ABCDEFGHIJ\"; content:\"1234\"; content:\"klmnop\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+ else
+ result = 0;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest13B2g (void)
+{
+ return SigTest13Real(MPM_B2G);
+}
+static int SigTest13B3g (void)
+{
+ return SigTest13Real(MPM_B3G);
+}
+static int SigTest13Wm (void)
+{
+ return SigTest13Real(MPM_WUMANBER);
+}
+
+
+static int SigTest14Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Content order test\"; content:\"ABCDEFGHIJ\"; content:\"1234\"; content:\"klmnop\"; distance:0; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 0;
+ else
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest14B2g (void)
+{
+ return SigTest14Real(MPM_B2G);
+}
+static int SigTest14B3g (void)
+{
+ return SigTest14Real(MPM_B3G);
+}
+static int SigTest14Wm (void)
+{
+ return SigTest14Real(MPM_WUMANBER);
+}
+
+
+static int SigTest15Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "CONNECT 213.92.8.7:31204 HTTP/1.1";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->dp = 80;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any !$HTTP_PORTS (msg:\"ET POLICY Inbound HTTP CONNECT Attempt on Off-Port\"; content:\"CONNECT \"; nocase; depth:8; content:\" HTTP/1.\"; nocase; within:1000; sid:2008284; rev:2;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 2008284))
+ result = 0;
+ else
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return result;
+}
+static int SigTest15B2g (void)
+{
+ return SigTest15Real(MPM_B2G);
+}
+static int SigTest15B3g (void)
+{
+ return SigTest15Real(MPM_B3G);
+}
+static int SigTest15Wm (void)
+{
+ return SigTest15Real(MPM_WUMANBER);
+}
+
+
+static int SigTest16Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "CONNECT 213.92.8.7:31204 HTTP/1.1";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&p, 0, sizeof(p));
+
+ p = UTHBuildPacketSrcDstPorts((uint8_t *)buf, buflen, IPPROTO_TCP, 12345, 1234);
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any !$HTTP_PORTS (msg:\"ET POLICY Inbound HTTP CONNECT Attempt on Off-Port\"; content:\"CONNECT \"; nocase; depth:8; content:\" HTTP/1.\"; nocase; within:1000; sid:2008284; rev:2;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 2008284))
+ result = 1;
+ else
+ printf("sid:2008284 %s: ", PacketAlertCheck(p, 2008284) ? "OK" : "FAIL");
+
+ SigGroupCleanup(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest16B2g (void)
+{
+ return SigTest16Real(MPM_B2G);
+}
+static int SigTest16B3g (void)
+{
+ return SigTest16Real(MPM_B3G);
+}
+static int SigTest16Wm (void)
+{
+ return SigTest16Real(MPM_WUMANBER);
+}
+
+
+static int SigTest17Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n" /* 20 */
+ "Host: one.example.org\r\n" /* 23, 43 */
+ "\r\n\r\n" /* 4, 47 */
+ "GET /two/ HTTP/1.1\r\n" /* 20, 67 */
+ "Host: two.example.org\r\n" /* 23, 90 */
+ "\r\n\r\n"; /* 4, 94 */
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacketSrcDstPorts((uint8_t *)buf, buflen, IPPROTO_TCP, 12345, 80);
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any $HTTP_PORTS (msg:\"HTTP host cap\"; content:\"Host:\"; pcre:\"/^Host: (?P<pkt_http_host>.*)\\r\\n/m\"; noalert; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ PktVar *pv_hn = PktVarGet(p, "http_host");
+ if (pv_hn != NULL) {
+ if (memcmp(pv_hn->value, "one.example.org", pv_hn->value_len < 15 ? pv_hn->value_len : 15) == 0)
+ result = 1;
+ else {
+ printf("\"");
+ PrintRawUriFp(stdout, pv_hn->value, pv_hn->value_len);
+ printf("\" != \"one.example.org\": ");
+ }
+ PktVarFree(pv_hn);
+ } else {
+ printf("Pkt var http_host not captured: ");
+ }
+
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTest17B2g (void)
+{
+ return SigTest17Real(MPM_B2G);
+}
+static int SigTest17B3g (void)
+{
+ return SigTest17Real(MPM_B3G);
+}
+static int SigTest17Wm (void)
+{
+ return SigTest17Real(MPM_WUMANBER);
+}
+
+static int SigTest18Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "220 (vsFTPd 2.0.5)\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->dp = 34260;
+ p->sp = 21;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any !21:902 -> any any (msg:\"ET MALWARE Suspicious 220 Banner on Local Port\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:2003055; rev:4;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!PacketAlertCheck(p, 2003055))
+ result = 1;
+ else
+ printf("signature shouldn't match, but did: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p);
+ return result;
+}
+static int SigTest18B2g (void)
+{
+ return SigTest18Real(MPM_B2G);
+}
+static int SigTest18B3g (void)
+{
+ return SigTest18Real(MPM_B3G);
+}
+static int SigTest18Wm (void)
+{
+ return SigTest18Real(MPM_WUMANBER);
+}
+
+int SigTest19Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "220 (vsFTPd 2.0.5)\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->dp = 34260;
+ p->sp = 21;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip $HOME_NET any -> 1.2.3.4 any (msg:\"IP-ONLY test (1)\"; sid:999; rev:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 999))
+ result = 1;
+ else
+ printf("signature didn't match, but should have: ");
+
+ SigGroupCleanup(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return result;
+}
+static int SigTest19B2g (void)
+{
+ return SigTest19Real(MPM_B2G);
+}
+static int SigTest19B3g (void)
+{
+ return SigTest19Real(MPM_B3G);
+}
+static int SigTest19Wm (void)
+{
+ return SigTest19Real(MPM_WUMANBER);
+}
+
+static int SigTest20Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)
+ "220 (vsFTPd 2.0.5)\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->dp = 34260;
+ p->sp = 21;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip $HOME_NET any -> [99.99.99.99,1.2.3.0/24,1.1.1.1,3.0.0.0/8] any (msg:\"IP-ONLY test (2)\"; sid:999; rev:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ //PatternMatchPrepare(mpm_ctx, mpm_type);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+ //DetectEngineIPOnlyThreadInit(de_ctx,&det_ctx->io_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 999))
+ result = 1;
+ else
+ printf("signature didn't match, but should have: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return result;
+}
+static int SigTest20B2g (void)
+{
+ return SigTest20Real(MPM_B2G);
+}
+static int SigTest20B3g (void)
+{
+ return SigTest20Real(MPM_B3G);
+}
+static int SigTest20Wm (void)
+{
+ return SigTest20Real(MPM_WUMANBER);
+}
+
+
+static int SigTest21Real (int mpm_type)
+{
+ ThreadVars th_v;
+ memset(&th_v, 0, sizeof(th_v));
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(f));
+ FLOW_INITIALIZE(&f);
+
+ /* packet 1 */
+ uint8_t *buf1 = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf1len = strlen((char *)buf1);
+ Packet *p1 = NULL;
+ /* packet 2 */
+ uint8_t *buf2 = (uint8_t *)"GET /two/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf2len = strlen((char *)buf2);
+ Packet *p2 = NULL;
+
+ p1 = UTHBuildPacket((uint8_t *)buf1, buf1len, IPPROTO_TCP);
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p2 = UTHBuildPacket((uint8_t *)buf2, buf2len, IPPROTO_TCP);
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT SET\"; content:\"/one/\"; flowbits:set,TEST.one; flowbits:noalert; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT TEST\"; content:\"/two/\"; flowbits:isset,TEST.one; sid:2;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 alerted, but shouldn't: ");
+ goto end;
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sid 2 didn't alert, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ }
+ DetectEngineCtxFree(de_ctx);
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest21B2g (void)
+{
+ return SigTest21Real(MPM_B2G);
+}
+static int SigTest21B3g (void)
+{
+ return SigTest21Real(MPM_B3G);
+}
+static int SigTest21Wm (void)
+{
+ return SigTest21Real(MPM_WUMANBER);
+}
+
+
+static int SigTest22Real (int mpm_type)
+{
+ ThreadVars th_v;
+ memset(&th_v, 0, sizeof(th_v));
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(f));
+ FLOW_INITIALIZE(&f);
+
+ /* packet 1 */
+ uint8_t *buf1 = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf1len = strlen((char *)buf1);
+ Packet *p1 = NULL;
+
+ p1 = UTHBuildPacket((uint8_t *)buf1, buf1len, IPPROTO_TCP);
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ /* packet 2 */
+ uint8_t *buf2 = (uint8_t *)"GET /two/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf2len = strlen((char *)buf2);
+ Packet *p2 = NULL;
+
+ p2 = UTHBuildPacket((uint8_t *)buf2, buf2len, IPPROTO_TCP);
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT SET\"; content:\"/one/\"; flowbits:set,TEST.one; flowbits:noalert; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT TEST\"; content:\"/two/\"; flowbits:isset,TEST.abc; sid:2;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ //PatternMatchPrepare(mpm_ctx, mpm_type);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 alerted, but shouldn't: ");
+ goto end;
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2)))
+ result = 1;
+ else
+ printf("sid 2 alerted, but shouldn't: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest22B2g (void)
+{
+ return SigTest22Real(MPM_B2G);
+}
+static int SigTest22B3g (void)
+{
+ return SigTest22Real(MPM_B3G);
+}
+static int SigTest22Wm (void)
+{
+ return SigTest22Real(MPM_WUMANBER);
+}
+
+static int SigTest23Real (int mpm_type)
+{
+ ThreadVars th_v;
+ memset(&th_v, 0, sizeof(th_v));
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(f));
+ FLOW_INITIALIZE(&f);
+
+ /* packet 1 */
+ uint8_t *buf1 = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf1len = strlen((char *)buf1);
+ Packet *p1 = NULL;
+
+ p1 = UTHBuildPacket((uint8_t *)buf1, buf1len, IPPROTO_TCP);
+ p1->flow = &f;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ /* packet 2 */
+ uint8_t *buf2 = (uint8_t *)"GET /two/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buf2len = strlen((char *)buf2);
+ Packet *p2 = NULL;
+
+ p2 = UTHBuildPacket((uint8_t *)buf2, buf2len, IPPROTO_TCP);
+ p2->flow = &f;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT SET\"; content:\"/one/\"; flowbits:toggle,TEST.one; flowbits:noalert; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"FLOWBIT TEST\"; content:\"/two/\"; flowbits:isset,TEST.one; sid:2;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sid 1 alerted, but shouldn't: ");
+ goto end;
+ }
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result = 1;
+ else
+ printf("sid 2 didn't alert, but should have: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+ FLOW_DESTROY(&f);
+ return result;
+}
+static int SigTest23B2g (void)
+{
+ return SigTest23Real(MPM_B2G);
+}
+static int SigTest23B3g (void)
+{
+ return SigTest23Real(MPM_B3G);
+}
+static int SigTest23Wm (void)
+{
+ return SigTest23Real(MPM_WUMANBER);
+}
+
+int SigTest24IPV4Keyword(void)
+{
+ uint8_t valid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t invalid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x06};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+ PACKET_RESET_CHECKSUMS(p1);
+ PACKET_RESET_CHECKSUMS(p2);
+
+ p1->ip4h = (IPV4Hdr *)valid_raw_ipv4;
+
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_TCP;
+
+ p2->ip4h = (IPV4Hdr *)invalid_raw_ipv4;
+
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"/one/\"; ipv4-csum:valid; "
+ "msg:\"ipv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig 1 parse: ");
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"/one/\"; ipv4-csum:invalid; "
+ "msg:\"ipv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ printf("sig 2 parse: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("signature 1 didn't match, but should have: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!((PacketAlertCheck(p2, 2)))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (det_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest25NegativeIPV4Keyword(void)
+{
+ uint8_t valid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t invalid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xb7, 0x52, 0xc0, 0xa8, 0x01, 0x03,
+ 0xc0, 0xa8, 0x01, 0x06};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+ PACKET_RESET_CHECKSUMS(p1);
+ PACKET_RESET_CHECKSUMS(p2);
+
+ p1->ip4h = (IPV4Hdr *)valid_raw_ipv4;
+
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_TCP;
+
+ p2->ip4h = (IPV4Hdr *)invalid_raw_ipv4;
+
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"/one/\"; ipv4-csum:invalid; "
+ "msg:\"ipv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"/one/\"; ipv4-csum:valid; "
+ "msg:\"ipv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest26TCPV4Keyword(void)
+{
+ uint8_t raw_ipv4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x8e, 0x7e, 0xb2,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t valid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0x4A, 0x04, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x02};
+
+ uint8_t invalid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x03};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PacketCopyData(p1, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p1, GET_PKT_LEN(p1), valid_raw_tcp, sizeof(valid_raw_tcp));
+
+ PacketCopyData(p2, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p2, GET_PKT_LEN(p2), invalid_raw_tcp, sizeof(invalid_raw_tcp));
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)GET_PKT_DATA(p1);
+ p1->tcph = (TCPHdr *)(GET_PKT_DATA(p1) + sizeof(raw_ipv4));
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = (uint8_t *)GET_PKT_DATA(p1) + sizeof(raw_ipv4) + 20;
+ p1->payload_len = 20;
+ p1->proto = IPPROTO_TCP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)GET_PKT_DATA(p2);
+ p2->tcph = (TCPHdr *)(GET_PKT_DATA(p2) + sizeof(raw_ipv4));
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = (uint8_t *)GET_PKT_DATA(p2) + sizeof(raw_ipv4) + 20;
+ p2->payload_len = 20;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:valid; dsize:20; "
+ "msg:\"tcpv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:invalid; "
+ "msg:\"tcpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sig 2 didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+/* Test SigTest26TCPV4Keyword but also check for invalid IPV4 checksum */
+static int SigTest26TCPV4AndNegativeIPV4Keyword(void)
+{
+ uint8_t raw_ipv4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x8e, 0x7e, 0xb2,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t valid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0x4A, 0x04, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x02};
+
+ uint8_t invalid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x03};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PacketCopyData(p1, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p1, GET_PKT_LEN(p1), valid_raw_tcp, sizeof(valid_raw_tcp));
+
+ PacketCopyData(p2, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p2, GET_PKT_LEN(p2), invalid_raw_tcp, sizeof(invalid_raw_tcp));
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)GET_PKT_DATA(p1);
+ p1->tcph = (TCPHdr *)(GET_PKT_DATA(p1) + sizeof(raw_ipv4));
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = (uint8_t *)GET_PKT_DATA(p1) + sizeof(raw_ipv4) + 20;
+ p1->payload_len = 20;
+ p1->proto = IPPROTO_TCP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)GET_PKT_DATA(p2);
+ p2->tcph = (TCPHdr *)(GET_PKT_DATA(p2) + sizeof(raw_ipv4));
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = (uint8_t *)GET_PKT_DATA(p2) + sizeof(raw_ipv4) + 20;
+ p2->payload_len = 20;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:valid; dsize:20; "
+ "ipv4-csum:invalid; "
+ "msg:\"tcpv4-csum and ipv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:invalid; "
+ "ipv4-csum:invalid; "
+ "msg:\"tcpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sig 2 didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+/* Similar to SigTest26, but with different packet */
+static int SigTest26TCPV4AndIPV4Keyword(void)
+{
+ /* IPV4: src:192.168.176.67 dst: 192.168.176.116
+ * TTL: 64 Flags: Don't Fragment
+ */
+ uint8_t raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x40, 0x9b, 0xa4, 0x40, 0x00,
+ 0x40, 0x06, 0xbd, 0x0a, 0xc0, 0xa8, 0xb0, 0x43,
+ 0xc0, 0xa8, 0xb0, 0x74};
+
+ /* TCP: sport: 49517 dport: 445 Flags: SYN
+ * Window size: 65535, checksum: 0x2009,
+ * MTU: 1460, Window scale: 4, TSACK permitted,
+ * 24 bytes of options, no payload.
+ */
+ uint8_t valid_raw_tcp[] = {
+ 0xc1, 0x6d, 0x01, 0xbd, 0x03, 0x10, 0xd3, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0xb0, 0x02, 0xff, 0xff,
+ 0x20, 0x09, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x01, 0x03, 0x03, 0x04, 0x01, 0x01, 0x08, 0x0a,
+ 0x19, 0x69, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x02, 0x00, 0x00};
+
+ uint8_t invalid_raw_tcp[] = {
+ 0xc1, 0x6d, 0x01, 0xbd, 0x03, 0x10, 0xd3, 0xc9,
+ 0x00, 0x00, 0x00, 0x00, 0xb0, 0x02, 0xff, 0xff,
+ 0x20, 0x09, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x01, 0x03, 0x03, 0x04, 0x01, 0x01, 0x08, 0x0a,
+ 0x19, 0x69, 0x81, 0x7e, 0xFF, 0xAA, 0x00, 0x00,
+ 0x04, 0x02, 0x00, 0x00};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PacketCopyData(p1, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p1, GET_PKT_LEN(p1), valid_raw_tcp, sizeof(valid_raw_tcp));
+
+ PacketCopyData(p2, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p2, GET_PKT_LEN(p2), invalid_raw_tcp, sizeof(invalid_raw_tcp));
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)GET_PKT_DATA(p1);
+ p1->tcph = (TCPHdr *)(GET_PKT_DATA(p1) + sizeof(raw_ipv4));
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = (uint8_t *)GET_PKT_DATA(p1) + sizeof(raw_ipv4) + 20 + 24;
+ p1->payload_len = 0;
+ p1->proto = IPPROTO_TCP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)GET_PKT_DATA(p2);
+ p2->tcph = (TCPHdr *)(GET_PKT_DATA(p2) + sizeof(raw_ipv4));
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = (uint8_t *)GET_PKT_DATA(p2) + sizeof(raw_ipv4) + 20 + 24;
+ p2->payload_len = 0;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(tcpv4-csum:valid; "
+ "ipv4-csum:valid; "
+ "msg:\"tcpv4-csum and ipv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert ip any any -> any any "
+ "(tcpv4-csum:invalid; "
+ "ipv4-csum:valid; "
+ "msg:\"tcpv4-csum and ipv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sig 2 didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+static int SigTest27NegativeTCPV4Keyword(void)
+{
+ uint8_t raw_ipv4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x8e, 0x7e, 0xb2,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t valid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x02};
+
+ uint8_t invalid_raw_tcp[] = {
+ 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c,
+ 0xcf, 0x0d, 0x21, 0x80, 0x50, 0x12, 0x16, 0xa0,
+ 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
+ 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73,
+ 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 0x03};
+
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PacketCopyData(p1, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p1, GET_PKT_LEN(p1), valid_raw_tcp, sizeof(valid_raw_tcp));
+
+ PacketCopyData(p2, raw_ipv4, sizeof(raw_ipv4));
+ PacketCopyDataOffset(p2, GET_PKT_LEN(p2), invalid_raw_tcp, sizeof(invalid_raw_tcp));
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)GET_PKT_DATA(p1);
+ p1->tcph = (TCPHdr *)(GET_PKT_DATA(p1) + sizeof(raw_ipv4));
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = (uint8_t *)GET_PKT_DATA(p1) + sizeof(raw_ipv4) + 20;
+ p1->payload_len = 20;
+ p1->proto = IPPROTO_TCP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)GET_PKT_DATA(p2);
+ p2->tcph = (TCPHdr *)(GET_PKT_DATA(p2) + sizeof(raw_ipv4));
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = (uint8_t *)GET_PKT_DATA(p2) + sizeof(raw_ipv4) + 20;
+ p2->payload_len = 20;
+ p2->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:invalid; dsize:20; "
+ "msg:\"tcpv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|DE 01 03|\"; tcpv4-csum:valid; dsize:20; "
+ "msg:\"tcpv4-csum keyword check(2)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!PacketAlertCheck(p1, 1)) {
+ printf("sig 1 didn't match on p1: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2)) {
+ printf("sig 2 matched on p2: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest28TCPV6Keyword(void)
+{
+ static uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd,
+
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40,
+ 0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda,
+ 0x3f, 0xfe, 0x05, 0x01, 0x04, 0x10, 0x00, 0x00,
+ 0x02, 0xc0, 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e,
+
+ 0x03, 0xfe, 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d,
+ 0x0c, 0x7a, 0x08, 0x77, 0x50, 0x10, 0x21, 0x5c,
+ 0xf2, 0xf1, 0x00, 0x00,
+
+ 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, 0xca, 0x5a,
+ 0x00, 0x01, 0x69, 0x27};
+
+ static uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd,
+
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40,
+ 0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda,
+ 0x3f, 0xfe, 0x05, 0x01, 0x04, 0x10, 0x00, 0x00,
+ 0x02, 0xc0, 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e,
+
+ 0x03, 0xfe, 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d,
+ 0x0c, 0x7a, 0x08, 0x77, 0x50, 0x10, 0x21, 0x5c,
+ 0xc2, 0xf1, 0x00, 0x00,
+
+ 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, 0xca, 0x5a,
+ 0x00, 0x01, 0x69, 0x28};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->tcph = (TCPHdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = valid_raw_ipv6 + 54 + 20;
+ p1->payload_len = 12;
+ p1->proto = IPPROTO_TCP;
+
+ if (TCP_GET_HLEN(p1) != 20) {
+ BUG_ON(1);
+ }
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->tcph = (TCPHdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = invalid_raw_ipv6 + 54 + 20;;
+ p2->payload_len = 12;
+ p2->proto = IPPROTO_TCP;
+
+ if (TCP_GET_HLEN(p2) != 20) {
+ BUG_ON(1);
+ }
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|00 01 69|\"; tcpv6-csum:valid; dsize:12; "
+ "msg:\"tcpv6-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|00 01 69|\"; tcpv6-csum:invalid; dsize:12; "
+ "msg:\"tcpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 1))) {
+ printf("sid 1 didn't match on p1: ");
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 2))) {
+ printf("sid 2 didn't match on p2: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest29NegativeTCPV6Keyword(void)
+{
+ static uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd,
+
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40,
+ 0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda,
+ 0x3f, 0xfe, 0x05, 0x01, 0x04, 0x10, 0x00, 0x00,
+ 0x02, 0xc0, 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e,
+
+ 0x03, 0xfe, 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d,
+ 0x0c, 0x7a, 0x08, 0x77, 0x50, 0x10, 0x21, 0x5c,
+ 0xf2, 0xf1, 0x00, 0x00,
+
+ 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, 0xca, 0x5a,
+ 0x00, 0x01, 0x69, 0x27};
+
+ static uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd,
+
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40,
+ 0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda,
+ 0x3f, 0xfe, 0x05, 0x01, 0x04, 0x10, 0x00, 0x00,
+ 0x02, 0xc0, 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e,
+
+ 0x03, 0xfe, 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d,
+ 0x0c, 0x7a, 0x08, 0x77, 0x50, 0x10, 0x21, 0x5c,
+ 0xc2, 0xf1, 0x00, 0x00,
+
+ 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, 0xca, 0x5a,
+ 0x00, 0x01, 0x69, 0x28};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->tcph = (TCPHdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = valid_raw_ipv6 + 54 + 20;
+ p1->payload_len = 12;
+ p1->proto = IPPROTO_TCP;
+
+ if (TCP_GET_HLEN(p1) != 20) {
+ BUG_ON(1);
+ }
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->tcph = (TCPHdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = invalid_raw_ipv6 + 54 + 20;;
+ p2->payload_len = 12;
+ p2->proto = IPPROTO_TCP;
+
+ if (TCP_GET_HLEN(p2) != 20) {
+ BUG_ON(1);
+ }
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|00 01 69|\"; tcpv6-csum:invalid; dsize:12; "
+ "msg:\"tcpv6-csum keyword check(1)\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"|00 01 69|\"; tcpv6-csum:valid; dsize:12; "
+ "msg:\"tcpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ goto end;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ goto end;
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest30UDPV4Keyword(void)
+{
+ uint8_t raw_ipv4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x11, 0x00, 0x00, 0xd0, 0x43, 0xdc, 0xdc,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t valid_raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x26};
+
+ uint8_t invalid_raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x27};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0yyyyyyyyyyyyyyyy\r\n"
+ "\r\n\r\nyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)raw_ipv4;
+ p1->udph = (UDPHdr *)valid_raw_udp;
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = sizeof(valid_raw_udp) - UDP_HEADER_LEN;
+ p1->proto = IPPROTO_UDP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)raw_ipv4;
+ p2->udph = (UDPHdr *)invalid_raw_udp;
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = sizeof(invalid_raw_udp) - UDP_HEADER_LEN;
+ p2->proto = IPPROTO_UDP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv4-csum:valid; "
+ "msg:\"udpv4-csum keyword check(1)\"; "
+ "sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv4-csum:invalid; "
+ "msg:\"udpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest31NegativeUDPV4Keyword(void)
+{
+ uint8_t raw_ipv4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xd0, 0x43, 0xdc, 0xdc,
+ 0xc0, 0xa8, 0x01, 0x03};
+
+ uint8_t valid_raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x26};
+
+ uint8_t invalid_raw_udp[] = {
+ 0x00, 0x35, 0xcf, 0x34, 0x00, 0x55, 0x6c, 0xe0,
+ 0x83, 0xfc, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67,
+ 0x65, 0x61, 0x64, 0x32, 0x11, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x73, 0x79, 0x6e, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x4b,
+ 0x50, 0x00, 0x12, 0x06, 0x70, 0x61, 0x67, 0x65,
+ 0x61, 0x64, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0xc0, 0x27};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0yyyyyyyyyyyyyyyy\r\n"
+ "\r\n\r\nyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)raw_ipv4;
+ p1->udph = (UDPHdr *)valid_raw_udp;
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = sizeof(valid_raw_udp) - UDP_HEADER_LEN;
+ p1->proto = IPPROTO_UDP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)raw_ipv4;
+ p2->udph = (UDPHdr *)invalid_raw_udp;
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = sizeof(invalid_raw_udp) - UDP_HEADER_LEN;
+ p2->proto = IPPROTO_UDP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv4-csum:invalid; "
+ "msg:\"udpv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv4-csum:valid; "
+ "msg:\"udpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2)) {
+ result &= 0;
+ }
+ else
+ result &= 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+
+int SigTest32UDPV6Keyword(void)
+{
+ static uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x00};
+
+ static uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x01};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP\r\n"
+ "\r\n\r\n";
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->udph = (UDPHdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = IPV6_GET_PLEN((p1)) - UDP_HEADER_LEN;
+ p1->proto = IPPROTO_UDP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->udph = (UDPHdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = IPV6_GET_PLEN((p2)) - UDP_HEADER_LEN;
+ p2->proto = IPPROTO_UDP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv6-csum:valid; "
+ "msg:\"udpv6-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv6-csum:invalid; "
+ "msg:\"udpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest33NegativeUDPV6Keyword(void)
+{
+ static uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x00};
+
+ static uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00,
+ 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x02, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0xa0, 0x00, 0x14, 0x1a, 0xc3, 0x06, 0x02,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0x57, 0xb0,
+ 0x09, 0x01};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP\r\n"
+ "\r\n\r\n";
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->udph = (UDPHdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = IPV6_GET_PLEN((p1)) - UDP_HEADER_LEN;
+ p1->proto = IPPROTO_UDP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->udph = (UDPHdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = IPV6_GET_PLEN((p2)) - UDP_HEADER_LEN;
+ p2->proto = IPPROTO_UDP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv6-csum:invalid; "
+ "msg:\"udpv6-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(content:\"/one/\"; udpv6-csum:valid; "
+ "msg:\"udpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest34ICMPV4Keyword(void)
+{
+ uint8_t valid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0x3c, 0xa7, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc3, 0x01,
+ 0x2b, 0x36, 0x00, 0x01, 0x3f, 0x16, 0x9a, 0x4a,
+ 0x41, 0x63, 0x04, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37};
+
+ uint8_t invalid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0x3c, 0xa7, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc3, 0x01,
+ 0x2b, 0x36, 0x00, 0x01, 0x3f, 0x16, 0x9a, 0x4a,
+ 0x41, 0x63, 0x04, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x38};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)(valid_raw_ipv4);
+ p1->ip4h->ip_verhl = 69;
+ p1->icmpv4h = (ICMPV4Hdr *) (valid_raw_ipv4 + IPV4_GET_RAW_HLEN(p1->ip4h) * 4);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_ICMP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)(invalid_raw_ipv4);
+ p2->ip4h->ip_verhl = 69;
+ p2->icmpv4h = (ICMPV4Hdr *) (invalid_raw_ipv4 + IPV4_GET_RAW_HLEN(p2->ip4h) * 4);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_ICMP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert icmp any any -> any any "
+ "(content:\"/one/\"; icmpv4-csum:valid; "
+ "msg:\"icmpv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert icmp any any -> any any "
+ "(content:\"/one/\"; icmpv4-csum:invalid; "
+ "msg:\"icmpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest35NegativeICMPV4Keyword(void)
+{
+ uint8_t valid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0x3c, 0xa7, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc3, 0x01,
+ 0x2b, 0x36, 0x00, 0x01, 0x3f, 0x16, 0x9a, 0x4a,
+ 0x41, 0x63, 0x04, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37};
+
+ uint8_t invalid_raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0x3c, 0xa7, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc3, 0x01,
+ 0x2b, 0x36, 0x00, 0x01, 0x3f, 0x16, 0x9a, 0x4a,
+ 0x41, 0x63, 0x04, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x38};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip4h = (IPV4Hdr *)(valid_raw_ipv4);
+ p1->ip4h->ip_verhl = 69;
+ p1->icmpv4h = (ICMPV4Hdr *) (valid_raw_ipv4 + IPV4_GET_RAW_HLEN(p1->ip4h) * 4);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_ICMP;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip4h = (IPV4Hdr *)(invalid_raw_ipv4);
+ p2->ip4h->ip_verhl = 69;
+ p2->icmpv4h = (ICMPV4Hdr *) (invalid_raw_ipv4 + IPV4_GET_RAW_HLEN(p2->ip4h) * 4);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_ICMP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert icmp any any -> any any "
+ "(content:\"/one/\"; icmpv4-csum:invalid; "
+ "msg:\"icmpv4-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert icmp any any -> any any "
+ "(content:\"/one/\"; icmpv4-csum:valid; "
+ "msg:\"icmpv4-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 0;
+ else {
+ result &= 1;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest36ICMPV6Keyword(void)
+{
+ uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x00};
+
+ uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x01};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->icmpv6h = (ICMPV6Hdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_ICMPV6;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->icmpv6h = (ICMPV6Hdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_ICMPV6;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert icmpv6 any any -> any any "
+ "(content:\"/one/\"; icmpv6-csum:valid; "
+ "msg:\"icmpv6-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert icmpv6 any any -> any any "
+ "(content:\"/one/\"; icmpv6-csum:invalid; "
+ "msg:\"icmpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 1;
+ else
+ result &= 0;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest37NegativeICMPV6Keyword(void)
+{
+ uint8_t valid_raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x00};
+
+ uint8_t invalid_raw_ipv6[] = {
+ 0x00, 0x00, 0x86, 0x05, 0x80, 0xda, 0x00, 0x60,
+ 0x97, 0x07, 0x69, 0xea, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x3a, 0x40, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x60,
+ 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x03, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x11, 0x01, 0x3f, 0xfe,
+ 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe,
+ 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0,
+ 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0xa0, 0x75,
+ 0x82, 0x9b, 0x00, 0x14, 0x82, 0x8b, 0x01, 0x01,
+ 0x00, 0x00, 0xf9, 0xc8, 0xe7, 0x36, 0xf5, 0xed,
+ 0x08, 0x01};
+
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ Packet *p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL)) {
+ SCFree(p1);
+ return 0;
+ }
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+ memset(p2, 0, SIZE_OF_PACKET);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ip6h = (IPV6Hdr *)(valid_raw_ipv6 + 14);
+ p1->icmpv6h = (ICMPV6Hdr *) (valid_raw_ipv6 + 54);
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = buf;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_ICMPV6;
+
+ PACKET_RESET_CHECKSUMS(p2);
+ p2->ip6h = (IPV6Hdr *)(invalid_raw_ipv6 + 14);
+ p2->icmpv6h = (ICMPV6Hdr *) (invalid_raw_ipv6 + 54);
+ p2->src.family = AF_INET;
+ p2->dst.family = AF_INET;
+ p2->payload = buf;
+ p2->payload_len = buflen;
+ p2->proto = IPPROTO_ICMPV6;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert icmpv6 any any -> any any "
+ "(content:\"/one/\"; icmpv6-csum:invalid; "
+ "msg:\"icmpv6-csum keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert icmpv6 any any -> any any "
+ "(content:\"/one/\"; icmpv6-csum:valid; "
+ "msg:\"icmpv6-csum keyword check(1)\"; "
+ "sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (PacketAlertCheck(p2, 2))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p1);
+ SCFree(p2);
+ return result;
+}
+
+int SigTest38Real(int mpm_type)
+{
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+ uint8_t raw_eth[] = {
+ 0x00, 0x00, 0x03, 0x04, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00
+ };
+ uint8_t raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x7d, 0xd8, 0xf3, 0x40, 0x00,
+ 0x40, 0x06, 0x63, 0x85, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01
+ };
+ uint8_t raw_tcp[] = {
+ 0xad, 0x22, 0x04, 0x00, 0x16, 0x39, 0x72,
+ 0xe2, 0x16, 0x1f, 0x79, 0x84, 0x80, 0x18,
+ 0x01, 0x01, 0xfe, 0x71, 0x00, 0x00, 0x01,
+ 0x01, 0x08, 0x0a, 0x00, 0x22, 0xaa, 0x10,
+ 0x00, 0x22, 0xaa, 0x10
+ };
+ uint8_t buf[] = {
+ 0x00, 0x00, 0x00, 0x08, 0x62, 0x6f, 0x6f, 0x65,
+ 0x65, 0x6b, 0x0d, 0x0a, 0x4c, 0x45, 0x4e, 0x31,
+ 0x20, 0x38, 0x0d, 0x0a, 0x66, 0x6f, 0x30, 0x30, /* LEN1|20| ends at 17 */
+ 0x30, 0x38, 0x0d, 0x0a, 0x4c, 0x45, 0x4e, 0x32, /* "0008" at offset 5 */
+ 0x20, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x0d, 0x0a, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d,
+ 0x0a
+ };
+ uint16_t ethlen = sizeof(raw_eth);
+ uint16_t ipv4len = sizeof(raw_ipv4);
+ uint16_t tcplen = sizeof(raw_tcp);
+ uint16_t buflen = sizeof(buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+
+ /* Copy raw data into packet */
+ if (PacketCopyData(p1, raw_eth, ethlen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen, raw_ipv4, ipv4len) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen + ipv4len, raw_tcp, tcplen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen + ipv4len + tcplen, buf, buflen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ SET_PKT_LEN(p1, ethlen + ipv4len + tcplen + buflen);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ethh = (EthernetHdr *)raw_eth;
+ p1->ip4h = (IPV4Hdr *)raw_ipv4;
+ p1->tcph = (TCPHdr *)raw_tcp;
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = GET_PKT_DATA(p1) + ethlen + ipv4len + tcplen;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"LEN1|20|\"; "
+ "byte_test:4,=,8,0; "
+ "msg:\"byte_test keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"LEN1|20|\"; "
+ "byte_test:4,=,8,5,relative,string,dec; "
+ "msg:\"byte_test keyword check(2)\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ result = 1;
+ } else {
+ result = 0;
+ printf("sid 1 didn't alert, but should have: ");
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p1, 2)) {
+ result = 1;
+ } else {
+ result = 0;
+ printf("sid 2 didn't alert, but should have: ");
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p1);
+ return result;
+}
+static int SigTest38B2g (void)
+{
+ return SigTest38Real(MPM_B2G);
+}
+static int SigTest38B3g (void)
+{
+ return SigTest38Real(MPM_B3G);
+}
+static int SigTest38Wm (void)
+{
+ return SigTest38Real(MPM_WUMANBER);
+}
+
+int SigTest39Real(int mpm_type)
+{
+ Packet *p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+ uint8_t raw_eth[] = {
+ 0x00, 0x00, 0x03, 0x04, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00
+ };
+ uint8_t raw_ipv4[] = {
+ 0x45, 0x00, 0x00, 0x7d, 0xd8, 0xf3, 0x40, 0x00,
+ 0x40, 0x06, 0x63, 0x85, 0x7f, 0x00, 0x00, 0x01,
+ 0x7f, 0x00, 0x00, 0x01
+ };
+ uint8_t raw_tcp[] = {
+ 0xad, 0x22, 0x04, 0x00, 0x16, 0x39, 0x72,
+ 0xe2, 0x16, 0x1f, 0x79, 0x84, 0x80, 0x18,
+ 0x01, 0x01, 0xfe, 0x71, 0x00, 0x00, 0x01,
+ 0x01, 0x08, 0x0a, 0x00, 0x22, 0xaa, 0x10,
+ 0x00, 0x22, 0xaa, 0x10
+ };
+ uint8_t buf[] = {
+ 0x00, 0x00, 0x00, 0x08, 0x62, 0x6f, 0x6f, 0x65,
+ 0x65, 0x6b, 0x0d, 0x0a, 0x4c, 0x45, 0x4e, 0x31,
+ 0x20, 0x38, 0x0d, 0x0a, 0x66, 0x30, 0x30, 0x30,
+ 0x38, 0x72, 0x0d, 0x0a, 0x4c, 0x45, 0x4e, 0x32,
+ 0x20, 0x39, 0x39, 0x4c, 0x45, 0x4e, 0x32, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x0d, 0x0a, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d,
+ 0x0a
+ };
+ uint16_t ethlen = sizeof(raw_eth);
+ uint16_t ipv4len = sizeof(raw_ipv4);
+ uint16_t tcplen = sizeof(raw_tcp);
+ uint16_t buflen = sizeof(buf);
+
+ memset(&th_v, 0, sizeof(ThreadVars));
+ memset(p1, 0, SIZE_OF_PACKET);
+
+ /* Copy raw data into packet */
+ if (PacketCopyData(p1, raw_eth, ethlen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen, raw_ipv4, ipv4len) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen + ipv4len, raw_tcp, tcplen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ if (PacketCopyDataOffset(p1, ethlen + ipv4len + tcplen, buf, buflen) == -1) {
+ SCFree(p1);
+ return 1;
+ }
+ SET_PKT_LEN(p1, ethlen + ipv4len + tcplen + buflen);
+
+ PACKET_RESET_CHECKSUMS(p1);
+ p1->ethh = (EthernetHdr *)raw_eth;
+ p1->ip4h = (IPV4Hdr *)raw_ipv4;
+ p1->tcph = (TCPHdr *)raw_tcp;
+ p1->src.family = AF_INET;
+ p1->dst.family = AF_INET;
+ p1->payload = GET_PKT_DATA(p1) + ethlen + ipv4len + tcplen;
+ p1->payload_len = buflen;
+ p1->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"LEN1|20|\"; "
+ "byte_test:4,=,8,0; "
+ "byte_jump:4,0; "
+ "byte_test:6,=,0x4c454e312038,0,relative; "
+ "msg:\"byte_jump keyword check(1)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result &= 0;
+ goto end;
+ }
+ // XXX TODO
+ de_ctx->sig_list->next = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(content:\"LEN1|20|\"; "
+ "byte_test:4,=,8,4,relative,string,dec; "
+ "byte_jump:4,4,relative,string,dec,post_offset 2; "
+ "byte_test:4,=,0x4c454e32,0,relative; "
+ "msg:\"byte_jump keyword check(2)\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result &= 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (PacketAlertCheck(p1, 1)) {
+ result = 1;
+ } else {
+ result = 0;
+ printf("sid 1 didn't alert, but should have: ");
+ goto cleanup;
+ }
+ if (PacketAlertCheck(p1, 2)) {
+ result = 1;
+ } else {
+ result = 0;
+ printf("sid 2 didn't alert, but should have: ");
+ goto cleanup;
+ }
+
+cleanup:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+end:
+ SCFree(p1);
+ return result;
+}
+static int SigTest39B2g (void)
+{
+ return SigTest39Real(MPM_B2G);
+}
+static int SigTest39B3g (void)
+{
+ return SigTest39Real(MPM_B3G);
+}
+static int SigTest39Wm (void)
+{
+ return SigTest39Real(MPM_WUMANBER);
+}
+
+
+
+/**
+ * \test SigTest36ContentAndIsdataatKeywords01 is a test to check window with constructed packets,
+ * \brief expecting to match a size
+ */
+
+int SigTest36ContentAndIsdataatKeywords01Real (int mpm_type)
+{
+ int result = 0;
+
+ // Buid and decode the packet
+
+ uint8_t raw_eth [] = {
+ 0x00,0x25,0x00,0x9e,0xfa,0xfe,0x00,0x02,0xcf,0x74,0xfe,0xe1,0x08,0x00,0x45,0x00
+ ,0x01,0xcc,0xcb,0x91,0x00,0x00,0x34,0x06,0xdf,0xa8,0xd1,0x55,0xe3,0x67,0xc0,0xa8
+ ,0x64,0x8c,0x00,0x50,0xc0,0xb7,0xd1,0x11,0xed,0x63,0x81,0xa9,0x9a,0x05,0x80,0x18
+ ,0x00,0x75,0x0a,0xdd,0x00,0x00,0x01,0x01,0x08,0x0a,0x09,0x8a,0x06,0xd0,0x12,0x21
+ ,0x2a,0x3b,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20,0x33,0x30,0x32,0x20,0x46
+ ,0x6f,0x75,0x6e,0x64,0x0d,0x0a,0x4c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x20
+ ,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x67,0x6f,0x6f,0x67,0x6c
+ ,0x65,0x2e,0x65,0x73,0x2f,0x0d,0x0a,0x43,0x61,0x63,0x68,0x65,0x2d,0x43,0x6f,0x6e
+ ,0x74,0x72,0x6f,0x6c,0x3a,0x20,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x0d,0x0a,0x43
+ ,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78
+ ,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d
+ ,0x55,0x54,0x46,0x2d,0x38,0x0d,0x0a,0x44,0x61,0x74,0x65,0x3a,0x20,0x4d,0x6f,0x6e
+ ,0x2c,0x20,0x31,0x34,0x20,0x53,0x65,0x70,0x20,0x32,0x30,0x30,0x39,0x20,0x30,0x38
+ ,0x3a,0x34,0x38,0x3a,0x33,0x31,0x20,0x47,0x4d,0x54,0x0d,0x0a,0x53,0x65,0x72,0x76
+ ,0x65,0x72,0x3a,0x20,0x67,0x77,0x73,0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74
+ ,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,0x32,0x31,0x38,0x0d,0x0a,0x0d,0x0a
+ ,0x3c,0x48,0x54,0x4d,0x4c,0x3e,0x3c,0x48,0x45,0x41,0x44,0x3e,0x3c,0x6d,0x65,0x74
+ ,0x61,0x20,0x68,0x74,0x74,0x70,0x2d,0x65,0x71,0x75,0x69,0x76,0x3d,0x22,0x63,0x6f
+ ,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x22,0x20,0x63,0x6f,0x6e,0x74
+ ,0x65,0x6e,0x74,0x3d,0x22,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x63
+ ,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x22,0x3e,0x0a,0x3c
+ ,0x54,0x49,0x54,0x4c,0x45,0x3e,0x33,0x30,0x32,0x20,0x4d,0x6f,0x76,0x65,0x64,0x3c
+ ,0x2f,0x54,0x49,0x54,0x4c,0x45,0x3e,0x3c,0x2f,0x48,0x45,0x41,0x44,0x3e,0x3c,0x42
+ ,0x4f,0x44,0x59,0x3e,0x0a,0x3c,0x48,0x31,0x3e,0x33,0x30,0x32,0x20,0x4d,0x6f,0x76
+ ,0x65,0x64,0x3c,0x2f,0x48,0x31,0x3e,0x0a,0x54,0x68,0x65,0x20,0x64,0x6f,0x63,0x75
+ ,0x6d,0x65,0x6e,0x74,0x20,0x68,0x61,0x73,0x20,0x6d,0x6f,0x76,0x65,0x64,0x0a,0x3c
+ ,0x41,0x20,0x48,0x52,0x45,0x46,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77
+ ,0x77,0x77,0x2e,0x67,0x6f,0x6f,0x67,0x6c,0x65,0x2e,0x65,0x73,0x2f,0x22,0x3e,0x68
+ ,0x65,0x72,0x65,0x3c,0x2f,0x41,0x3e,0x2e,0x0d,0x0a,0x3c,0x2f,0x42,0x4f,0x44,0x59
+ ,0x3e,0x3c,0x2f,0x48,0x54,0x4d,0x4c,0x3e,0x0d,0x0a };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest36ContentAndIsdataatKeywords01 \"; content:\"HTTP\"; isdataat:404, relative; sid:101;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ //PatternMatchPrepare(mpm_ctx, mpm_type);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 101) == 0) {
+ result = 0;
+ goto end;
+ } else {
+ result=1;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if(de_ctx)
+ {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if(det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ //PatternMatchDestroy(mpm_ctx);
+
+ if(de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ if (p != NULL)
+ PACKET_RECYCLE(p);
+
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+
+/**
+ * \test SigTest37ContentAndIsdataatKeywords02 is a test to check window with constructed packets,
+ * \brief not expecting to match a size
+ */
+
+int SigTest37ContentAndIsdataatKeywords02Real (int mpm_type)
+{
+ int result = 0;
+
+ // Buid and decode the packet
+
+ uint8_t raw_eth [] = {
+ 0x00,0x25,0x00,0x9e,0xfa,0xfe,0x00,0x02,0xcf,0x74,0xfe,0xe1,0x08,0x00,0x45,0x00
+ ,0x01,0xcc,0xcb,0x91,0x00,0x00,0x34,0x06,0xdf,0xa8,0xd1,0x55,0xe3,0x67,0xc0,0xa8
+ ,0x64,0x8c,0x00,0x50,0xc0,0xb7,0xd1,0x11,0xed,0x63,0x81,0xa9,0x9a,0x05,0x80,0x18
+ ,0x00,0x75,0x0a,0xdd,0x00,0x00,0x01,0x01,0x08,0x0a,0x09,0x8a,0x06,0xd0,0x12,0x21
+ ,0x2a,0x3b,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20,0x33,0x30,0x32,0x20,0x46
+ ,0x6f,0x75,0x6e,0x64,0x0d,0x0a,0x4c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x20
+ ,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x67,0x6f,0x6f,0x67,0x6c
+ ,0x65,0x2e,0x65,0x73,0x2f,0x0d,0x0a,0x43,0x61,0x63,0x68,0x65,0x2d,0x43,0x6f,0x6e
+ ,0x74,0x72,0x6f,0x6c,0x3a,0x20,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x0d,0x0a,0x43
+ ,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78
+ ,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d
+ ,0x55,0x54,0x46,0x2d,0x38,0x0d,0x0a,0x44,0x61,0x74,0x65,0x3a,0x20,0x4d,0x6f,0x6e
+ ,0x2c,0x20,0x31,0x34,0x20,0x53,0x65,0x70,0x20,0x32,0x30,0x30,0x39,0x20,0x30,0x38
+ ,0x3a,0x34,0x38,0x3a,0x33,0x31,0x20,0x47,0x4d,0x54,0x0d,0x0a,0x53,0x65,0x72,0x76
+ ,0x65,0x72,0x3a,0x20,0x67,0x77,0x73,0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74
+ ,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,0x32,0x31,0x38,0x0d,0x0a,0x0d,0x0a
+ ,0x3c,0x48,0x54,0x4d,0x4c,0x3e,0x3c,0x48,0x45,0x41,0x44,0x3e,0x3c,0x6d,0x65,0x74
+ ,0x61,0x20,0x68,0x74,0x74,0x70,0x2d,0x65,0x71,0x75,0x69,0x76,0x3d,0x22,0x63,0x6f
+ ,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x22,0x20,0x63,0x6f,0x6e,0x74
+ ,0x65,0x6e,0x74,0x3d,0x22,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x63
+ ,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x22,0x3e,0x0a,0x3c
+ ,0x54,0x49,0x54,0x4c,0x45,0x3e,0x33,0x30,0x32,0x20,0x4d,0x6f,0x76,0x65,0x64,0x3c
+ ,0x2f,0x54,0x49,0x54,0x4c,0x45,0x3e,0x3c,0x2f,0x48,0x45,0x41,0x44,0x3e,0x3c,0x42
+ ,0x4f,0x44,0x59,0x3e,0x0a,0x3c,0x48,0x31,0x3e,0x33,0x30,0x32,0x20,0x4d,0x6f,0x76
+ ,0x65,0x64,0x3c,0x2f,0x48,0x31,0x3e,0x0a,0x54,0x68,0x65,0x20,0x64,0x6f,0x63,0x75
+ ,0x6d,0x65,0x6e,0x74,0x20,0x68,0x61,0x73,0x20,0x6d,0x6f,0x76,0x65,0x64,0x0a,0x3c
+ ,0x41,0x20,0x48,0x52,0x45,0x46,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77
+ ,0x77,0x77,0x2e,0x67,0x6f,0x6f,0x67,0x6c,0x65,0x2e,0x65,0x73,0x2f,0x22,0x3e,0x68
+ ,0x65,0x72,0x65,0x3c,0x2f,0x41,0x3e,0x2e,0x0d,0x0a,0x3c,0x2f,0x42,0x4f,0x44,0x59
+ ,0x3e,0x3c,0x2f,0x48,0x54,0x4d,0x4c,0x3e,0x0d,0x0a };
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth, sizeof(raw_eth), NULL);
+
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest37ContentAndIsdataatKeywords01 \"; content:\"HTTP\"; isdataat:500, relative; sid:101;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig parse failed: ");
+ result = 0;
+ goto end;
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH]->type != DETECT_CONTENT) {
+ printf("type not content: ");
+ goto end;
+ }
+/*
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH]->next == NULL) {
+ printf("s->sm_lists[DETECT_SM_LIST_PMATCH]->next == NULL: ");
+ goto end;
+ }
+ if (s->sm_lists[DETECT_SM_LIST_PMATCH]->next->type != DETECT_ISDATAAT) {
+ printf("type not isdataat: ");
+ goto end;
+ }
+*/
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 101) == 0) {
+ result = 1;
+ goto end;
+ } else {
+ printf("sig matched, but should not have: ");
+ result=0;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+
+end:
+ if(de_ctx)
+ {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if(det_ctx)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if(de_ctx)
+ DetectEngineCtxFree(de_ctx);
+
+ if (p != NULL)
+ PACKET_RECYCLE(p);
+
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+
+// Wrapper functions to pass the mpm_type
+static int SigTest36ContentAndIsdataatKeywords01B2g (void)
+{
+ return SigTest36ContentAndIsdataatKeywords01Real(MPM_B2G);
+}
+static int SigTest36ContentAndIsdataatKeywords01B3g (void)
+{
+ return SigTest36ContentAndIsdataatKeywords01Real(MPM_B3G);
+}
+static int SigTest36ContentAndIsdataatKeywords01Wm (void)
+{
+ return SigTest36ContentAndIsdataatKeywords01Real(MPM_WUMANBER);
+}
+
+static int SigTest37ContentAndIsdataatKeywords02B2g (void)
+{
+ return SigTest37ContentAndIsdataatKeywords02Real(MPM_B2G);
+}
+static int SigTest37ContentAndIsdataatKeywords02B3g (void)
+{
+ return SigTest37ContentAndIsdataatKeywords02Real(MPM_B3G);
+}
+static int SigTest37ContentAndIsdataatKeywords02Wm (void)
+{
+ return SigTest37ContentAndIsdataatKeywords02Real(MPM_WUMANBER);
+}
+
+
+/**
+ * \test SigTest41NoPacketInspection is a test to check that when PKT_NOPACKET_INSPECTION
+ * flag is set, we don't need to inspect the packet protocol header or its contents.
+ */
+
+int SigTest40NoPacketInspection01(void)
+{
+
+ uint8_t *buf = (uint8_t *)
+ "220 (vsFTPd 2.0.5)\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ TCPHdr tcphdr;
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ PacketQueue pq;
+ Flow f;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&pq, 0, sizeof(pq));
+ memset(&f, 0, sizeof(f));
+ memset(&tcphdr, 0, sizeof(tcphdr));
+
+ p->src.family = AF_INET;
+ p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->dp = 34260;
+ p->sp = 21;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flags |= PKT_NOPACKET_INSPECTION;
+ p->tcph = &tcphdr;
+ p->flow = &f;
+
+ FLOW_INITIALIZE(&f);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> 1.2.3.4 any (msg:\"No Packet Inspection Test\"; flow:to_server; sid:2; rev:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx);
+ det_ctx->de_ctx = de_ctx;
+
+ Detect(&th_v, p, det_ctx, &pq, NULL);
+ if (PacketAlertCheck(p, 2))
+ result = 0;
+ else
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ //PatternMatchDestroy(mpm_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test SigTest42NoPayloadInspection is a test to check that when PKT_NOPAYLOAD_INSPECTION
+ * flasg is set, we don't need to inspect the packet contents.
+ */
+
+int SigTest40NoPayloadInspection02(void)
+{
+
+ uint8_t *buf = (uint8_t *)
+ "220 (vsFTPd 2.0.5)\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 1;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+ p->flags |= PKT_NOPAYLOAD_INSPECTION;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"No Payload TEST\"; content:\"220 (vsFTPd 2.0.5)\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ if (!(de_ctx->sig_list->init_flags & SIG_FLAG_INIT_PAYLOAD))
+ result = 0;
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1))
+ result &= 0;
+ else
+ result &= 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ SCFree(p);
+ return result;
+}
+
+static int SigTestMemory01 (void)
+{
+ uint8_t *buf = (uint8_t *)
+ "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n"
+ "\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\n"
+ "Host: two.example.org\r\n"
+ "\r\n\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = buf;
+ p->payload_len = buflen;
+ p->proto = IPPROTO_TCP;
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+printf("@pre cleanup\n\n");
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ DetectPortPrintMemory();
+
+ SigGroupCleanup(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+printf("@exit\n\n");
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ DetectPortPrintMemory();
+
+ result = 1;
+end:
+ SCFree(p);
+ return result;
+}
+
+static int SigTestMemory02 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 456 (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any 1:1000 (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+
+printf("@cleanup\n\n");
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+printf("@exit\n\n");
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ DetectPortPrintMemory();
+printf("@exit\n\n");
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ DetectPortPrintMemory();
+
+ result = 1;
+end:
+ return result;
+}
+
+static int SigTestMemory03 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> 1.2.3.4 456 (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> 1.2.3.3-1.2.3.6 1:1000 (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next->next = SigInit(de_ctx,"alert tcp any any -> !1.2.3.5 1:990 (msg:\"HTTP URI cap\"; content:\"GET \"; depth:4; pcre:\"/GET (?P<pkt_http_uri>.*) HTTP\\/\\d\\.\\d\\r\\n/G\"; sid:3;)");
+ if (de_ctx->sig_list->next->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+
+printf("@cleanup\n\n");
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+printf("@exit\n\n");
+ DetectSigGroupPrintMemory();
+ DetectAddressPrintMemory();
+ DetectPortPrintMemory();
+
+ result = 1;
+end:
+ return result;
+}
+
+static int SigTestSgh01 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&th_v, 0, sizeof(th_v));
+
+ Packet *p = NULL;
+ p = UTHBuildPacketSrcDstPorts((uint8_t *)"a", 1, IPPROTO_TCP, 12345, 80);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"1\"; content:\"one\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->num != 0) {
+ printf("internal id != 0: ");
+ goto end;
+ }
+ if (de_ctx->sig_list->mpm_content_maxlen != 3) {
+ printf("de_ctx->sig_list->mpm_content_maxlen %u, expected 3: ", de_ctx->sig_list->mpm_content_maxlen);
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any 81 (msg:\"2\"; content:\"two\"; content:\"abcd\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->num != 1) {
+ printf("internal id != 1: ");
+ goto end;
+ }
+ if (de_ctx->sig_list->next->mpm_content_maxlen != 4) {
+ printf("de_ctx->sig_list->mpm_content_maxlen %u, expected 4: ", de_ctx->sig_list->next->mpm_content_maxlen);
+ goto end;
+ }
+
+ de_ctx->sig_list->next->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"3\"; content:\"three\"; sid:3;)");
+ if (de_ctx->sig_list->next->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->next->num != 2) {
+ printf("internal id != 2: ");
+ goto end;
+ }
+ if (de_ctx->sig_list->next->next->mpm_content_maxlen != 5) {
+ printf("de_ctx->sig_list->next->next->mpm_content_maxlen %u, expected 5: ", de_ctx->sig_list->next->next->mpm_content_maxlen);
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+#if 0
+ printf("-\n");
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+#endif
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->match_array == NULL) {
+ printf("sgh->match_array == NULL: ");
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have (sgh->match_array[0] %p, expected %p): ", sgh->match_array[0], de_ctx->sig_list);
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+
+ p->dp = 81;
+
+ SigGroupHead *sgh2 = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh2 == NULL) {
+ printf("no sgh2: ");
+ goto end;
+ }
+#if 0
+ if (!(SigGroupHeadContainsSigId(de_ctx, sgh2, 1))) {
+ printf("sgh2 doesn't have sid 1: ");
+ goto end;
+ }
+#endif
+ if (sgh2->sig_cnt != 1) {
+ printf("expected one sig, got %u in sgh2: ", sgh2->sig_cnt);
+ goto end;
+ }
+
+ if (sgh2->match_array[0] != de_ctx->sig_list->next) {
+ printf("sgh doesn't contain sid 2, should have (sgh2->match_array[0] %p, expected %p): ",
+ sgh2->match_array[0], de_ctx->sig_list->next);
+ goto end;
+ }
+
+#if 0
+ printf("-\n");
+ printf("sgh2->mpm_content_maxlen %u\n", sgh2->mpm_content_maxlen);
+ printf("sgh2->mpm_uricontent_maxlen %u\n", sgh2->mpm_uricontent_maxlen);
+ printf("sgh2->sig_cnt %u\n", sgh2->sig_cnt);
+ printf("sgh2->sig_size %u\n", sgh2->sig_size);
+#endif
+ if (sgh2->mpm_content_maxlen != 4) {
+ printf("sgh2->mpm_content_maxlen %u, expected 4: ", sgh2->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh2->match_array[0] != de_ctx->sig_list->next) {
+ printf("sgh2 doesn't contain sid 2, should have: ");
+ goto end;
+ }
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ result = 1;
+end:
+ return result;
+}
+
+static int SigTestSgh02 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ Packet *p = NULL;
+ p = UTHBuildPacketSrcDstPorts((uint8_t *)"a", 1, IPPROTO_TCP, 12345, 80);
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80:82 (msg:\"1\"; content:\"one\"; content:\"1\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->num != 0) {
+ printf("internal id != 0: ");
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any 81 (msg:\"2\"; content:\"two2\"; content:\"abcdef\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->num != 1) {
+ printf("internal id != 1: ");
+ goto end;
+ }
+ de_ctx->sig_list->next->next = SigInit(de_ctx,"alert tcp any any -> any 80:81 (msg:\"3\"; content:\"three\"; content:\"abcdefgh\"; sid:3;)");
+ if (de_ctx->sig_list->next->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->next->num != 2) {
+ printf("internal id != 2: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->match_array == NULL) {
+ printf("sgh->match_array == NULL: ");
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 2) {
+ printf("sgh sig cnt %u, expected 2: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have (sgh->match_array[0] %p, expected %p): ", sgh->match_array[0], de_ctx->sig_list);
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->dp = 81;
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 3) {
+ printf("sgh sig cnt %u, expected 3: ", sgh->sig_cnt);
+ goto end;
+ }
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next) {
+ printf("sgh doesn't contain sid 2, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[2] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->dp = 82;
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 1) {
+ printf("sgh sig cnt %u, expected 1: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->src.family = AF_INET6;
+ p->dst.family = AF_INET6;
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 1) {
+ printf("sgh sig cnt %u, expected 1: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ result = 1;
+end:
+ return result;
+}
+
+static int SigTestSgh03 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload_len = 1;
+ p->proto = IPPROTO_TCP;
+ p->dp = 80;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> 1.2.3.4-1.2.3.6 any (msg:\"1\"; content:\"one\"; content:\"1\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->num != 0) {
+ printf("internal id != 0: ");
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert ip any any -> 1.2.3.5 any (msg:\"2\"; content:\"two2\"; content:\"abcdef\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->num != 1) {
+ printf("internal id != 1: ");
+ goto end;
+ }
+ de_ctx->sig_list->next->next = SigInit(de_ctx,"alert ip any any -> 1.2.3.4-1.2.3.5 any (msg:\"3\"; content:\"three\"; content:\"abcdefgh\"; sid:3;)");
+ if (de_ctx->sig_list->next->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->next->num != 2) {
+ printf("internal id != 2: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+#if 0
+ printf("-\n");
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->match_array == NULL) {
+ printf("sgh->match_array == NULL: ");
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 2) {
+ printf("sgh sig cnt %u, expected 2: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have (sgh->match_array[0] %p, expected %p): ", sgh->match_array[0], de_ctx->sig_list);
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.5");
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+#if 0
+ printf("-\n");
+ printf("sgh %p\n", sgh);
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ if (sgh->sig_cnt != 3) {
+ printf("sgh sig cnt %u, expected 3: ", sgh->sig_cnt);
+ goto end;
+ }
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[2] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3 (%x): ", sgh->mpm_content_maxlen, p->dst.addr_data32[0]);
+ goto end;
+ }
+
+
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.6");
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+#if 0
+ printf("-\n");
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 1) {
+ printf("sgh sig cnt %u, expected 1: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ result = 1;
+end:
+ SCFree(p);
+ return result;
+}
+
+static int SigTestSgh04 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload_len = 1;
+ p->proto = IPPROTO_TCP;
+ p->dp = 80;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> 1.2.3.4-1.2.3.6 any (msg:\"1\"; content:\"one\"; content:\"1\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->num != 0) {
+ printf("internal id != 0: ");
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert ip any any -> 1.2.3.5 any (msg:\"2\"; content:\"two2\"; content:\"abcdef\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->num != 1) {
+ printf("internal id != 1: ");
+ goto end;
+ }
+ de_ctx->sig_list->next->next = SigInit(de_ctx,"alert ip any any -> 1.2.3.4-1.2.3.5 any (msg:\"3\"; content:\"three\"; content:\"abcdefgh\"; sid:3;)");
+ if (de_ctx->sig_list->next->next == NULL) {
+ result = 0;
+ goto end;
+ }
+ if (de_ctx->sig_list->next->next->num != 2) {
+ printf("internal id != 2: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->match_array == NULL) {
+ printf("sgh->match_array == NULL: ");
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 2) {
+ printf("sgh sig cnt %u, expected 2: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have (sgh->match_array[0] %p, expected %p): ", sgh->match_array[0], de_ctx->sig_list);
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.5");
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 3) {
+ printf("sgh sig cnt %u, expected 3: ", sgh->sig_cnt);
+ goto end;
+ }
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[1] != de_ctx->sig_list->next) {
+ printf("sgh doesn't contain sid 2, should have: ");
+ goto end;
+ }
+ if (sgh->match_array[2] != de_ctx->sig_list->next->next) {
+ printf("sgh doesn't contain sid 3, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.6");
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->sig_cnt != 1) {
+ printf("sgh sig cnt %u, expected 1: ", sgh->sig_cnt);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+#if 0
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ p->proto = IPPROTO_GRE;
+
+ sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+#if 0
+ printf("-\n");
+ printf("sgh->mpm_content_maxlen %u\n", sgh->mpm_content_maxlen);
+ printf("sgh->mpm_uricontent_maxlen %u\n", sgh->mpm_uricontent_maxlen);
+ printf("sgh->sig_cnt %u\n", sgh->sig_cnt);
+ printf("sgh->sig_size %u\n", sgh->sig_size);
+ printf("sgh->refcnt %u\n", sgh->refcnt);
+#endif
+ if (sgh->mpm_content_maxlen != 3) {
+ printf("sgh->mpm_content_maxlen %u, expected 3: ", sgh->mpm_content_maxlen);
+ goto end;
+ }
+
+ if (sgh->match_array[0] != de_ctx->sig_list) {
+ printf("sgh doesn't contain sid 1, should have: ");
+ goto end;
+ }
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ result = 1;
+end:
+ SCFree(p);
+ return result;
+}
+
+/** \test setting of mpm type */
+static int SigTestSgh05 (void)
+{
+ ThreadVars th_v;
+ int result = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(p, 0, SIZE_OF_PACKET);
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload_len = 1;
+ p->proto = IPPROTO_TCP;
+ p->dp = 80;
+ p->dst.addr_data32[0] = UTHSetIPv4Address("1.2.3.4");
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->mpm_matcher = MPM_WUMANBER;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> 1.2.3.4-1.2.3.6 any (msg:\"1\"; content:\"one\"; content:\"1\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
+ if (sgh == NULL) {
+ printf("no sgh: ");
+ goto end;
+ }
+
+ if (sgh->mpm_proto_tcp_ctx_ts != NULL || sgh->mpm_proto_tcp_ctx_tc != NULL ||
+ sgh->mpm_proto_udp_ctx_ts != NULL || sgh->mpm_proto_udp_ctx_tc != NULL ||
+ sgh->mpm_proto_other_ctx != NULL) {
+ printf("sgh->mpm_proto_tcp_ctx_ts != NULL || sgh->mpm_proto_tcp_ctx_tc != NULL"
+ "sgh->mpm_proto_udp_ctx_ts != NULL || sgh->mpm_proto_udp_ctx_tc != NULL"
+ "sgh->mpm_proto_other_ctx != NULL: ");
+ goto end;
+ }
+
+ if (sgh->mpm_stream_ctx_ts == NULL || sgh->mpm_stream_ctx_tc == NULL) {
+ printf("sgh->mpm_stream_ctx == NULL || sgh->mpm_stream_ctx_tc == NULL: ");
+ goto end;
+ }
+
+ if (sgh->mpm_stream_ctx_ts->mpm_type != MPM_WUMANBER) {
+ printf("sgh->mpm_type != MPM_WUMANBER, expected %d, got %d: ", MPM_WUMANBER, sgh->mpm_stream_ctx_ts->mpm_type);
+ goto end;
+ }
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ result = 1;
+end:
+ SCFree(p);
+ return result;
+}
+
+static int SigTestContent01Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+ else
+ printf("sig 1 didn't match: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestContent01B2g (void)
+{
+ return SigTestContent01Real(MPM_B2G);
+}
+static int SigTestContent01B3g (void)
+{
+ return SigTestContent01Real(MPM_B3G);
+}
+static int SigTestContent01Wm (void)
+{
+ return SigTestContent01Real(MPM_WUMANBER);
+}
+
+static int SigTestContent02Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 31\"; content:\"0123456789012345678901234567890\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ if (PacketAlertCheck(p, 2)) {
+ result = 1;
+ } else
+ printf("sig 2 didn't match: ");
+ }
+ else
+ printf("sig 1 didn't match: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestContent02B2g (void)
+{
+ return SigTestContent02Real(MPM_B2G);
+}
+static int SigTestContent02B3g (void)
+{
+ return SigTestContent02Real(MPM_B3G);
+}
+static int SigTestContent02Wm (void)
+{
+ return SigTestContent02Real(MPM_WUMANBER);
+}
+
+static int SigTestContent03Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; content:\"abcdefghijklmnopqrstuvwxyzABCDEF\"; distance:0; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+ else
+ printf("sig 1 didn't match: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestContent03B2g (void)
+{
+ return SigTestContent03Real(MPM_B2G);
+}
+static int SigTestContent03B3g (void)
+{
+ return SigTestContent03Real(MPM_B3G);
+}
+static int SigTestContent03Wm (void)
+{
+ return SigTestContent03Real(MPM_WUMANBER);
+}
+
+static int SigTestContent04Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; content:\"abcdefghijklmnopqrstuvwxyzABCDEF\"; distance:0; within:32; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+ else
+ printf("sig 1 didn't match: ");
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestContent04B2g (void)
+{
+ return SigTestContent04Real(MPM_B2G);
+}
+static int SigTestContent04B3g (void)
+{
+ return SigTestContent04Real(MPM_B3G);
+}
+static int SigTestContent04Wm (void)
+{
+ return SigTestContent04Real(MPM_WUMANBER);
+}
+
+/** \test sigs with patterns at the limit of the pm's size limit */
+static int SigTestContent05Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901PADabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx == NULL: ");
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; content:\"abcdefghijklmnopqrstuvwxyzABCDEF\"; distance:0; within:32; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("sig1 parse failed: ");
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Test 32\"; content:\"01234567890123456789012345678901\"; content:\"abcdefghijklmnopqrstuvwxyzABCDEF\"; distance:1; within:32; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 matched but shouldn't: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 2)) {
+ printf("sig 2 matched but shouldn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePackets(&p, 1);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ DetectEngineCtxFree(de_ctx);
+ }
+ return result;
+}
+static int SigTestContent05B2g (void)
+{
+ return SigTestContent05Real(MPM_B2G);
+}
+static int SigTestContent05B3g (void)
+{
+ return SigTestContent05Real(MPM_B3G);
+}
+static int SigTestContent05Wm (void)
+{
+ return SigTestContent05Real(MPM_WUMANBER);
+}
+
+static int SigTestContent06Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Packet *p = NULL;
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Test 32 sig1\"; content:\"01234567890123456789012345678901\"; content:\"abcdefghijklmnopqrstuvwxyzABCDEF\"; distance:0; within:32; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+ de_ctx->sig_list->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Test 32 sig2\"; content:\"01234567890123456789012345678901\"; content:\"abcdefg\"; sid:2;)");
+ if (de_ctx->sig_list->next == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)){
+ //printf("sig 1 matched :");
+ }else{
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p, 2)){
+ result = 1;
+ }else{
+ printf("sig 2 didn't match: ");
+ goto end;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestContent06B2g (void)
+{
+ return SigTestContent06Real(MPM_B2G);
+}
+static int SigTestContent06B3g (void)
+{
+ return SigTestContent06Real(MPM_B3G);
+}
+static int SigTestContent06Wm (void)
+{
+ return SigTestContent06Real(MPM_WUMANBER);
+}
+
+static int SigTestWithinReal01 (int mpm_type)
+{
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ int result = 0;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Packet *p3 = NULL;
+ Packet *p4 = NULL;
+
+ uint8_t rawpkt1[] = {
+ 0x00,0x04,0x76,0xd3,0xd8,0x6a,0x00,0x24,
+ 0xe8,0x29,0xfa,0x4f,0x08,0x00,0x45,0x00,
+ 0x00,0x8c,0x95,0x50,0x00,0x00,0x40,0x06,
+ 0x2d,0x45,0xc0,0xa8,0x02,0x03,0xd0,0x45,
+ 0x24,0xe6,0x06,0xcc,0x03,0x09,0x18,0x72,
+ 0xd0,0xe3,0x1a,0xab,0x7c,0x98,0x50,0x00,
+ 0x02,0x00,0x46,0xa0,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x0a,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00 }; /* end rawpkt1 */
+
+ uint8_t rawpkt2[] = {
+ 0x00,0x04,0x76,0xd3,0xd8,0x6a,0x00,0x24,
+ 0xe8,0x29,0xfa,0x4f,0x08,0x00,0x45,0x00,
+ 0x00,0x8c,0x30,0x87,0x00,0x00,0x40,0x06,
+ 0x92,0x0e,0xc0,0xa8,0x02,0x03,0xd0,0x45,
+ 0x24,0xe6,0x06,0xcd,0x03,0x09,0x73,0xec,
+ 0xd5,0x35,0x14,0x7d,0x7c,0x12,0x50,0x00,
+ 0x02,0x00,0xed,0x86,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x0a,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00 }; /* end rawpkt2 */
+
+ uint8_t rawpkt3[] = {
+ 0x00,0x04,0x76,0xd3,0xd8,0x6a,0x00,0x24,
+ 0xe8,0x29,0xfa,0x4f,0x08,0x00,0x45,0x00,
+ 0x00,0x8c,0x57,0xd8,0x00,0x00,0x40,0x06,
+ 0x6a,0xbd,0xc0,0xa8,0x02,0x03,0xd0,0x45,
+ 0x24,0xe6,0x06,0xce,0x03,0x09,0x06,0x3d,
+ 0x02,0x22,0x2f,0x9b,0x6f,0x8f,0x50,0x00,
+ 0x02,0x00,0x1f,0xae,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x0a,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00 }; /* end rawpkt3 */
+
+ uint8_t rawpkt4[] = {
+ 0x00,0x04,0x76,0xd3,0xd8,0x6a,0x00,0x24,
+ 0xe8,0x29,0xfa,0x4f,0x08,0x00,0x45,0x00,
+ 0x00,0x8c,0xa7,0x2e,0x00,0x00,0x40,0x06,
+ 0x1b,0x67,0xc0,0xa8,0x02,0x03,0xd0,0x45,
+ 0x24,0xe6,0x06,0xcf,0x03,0x09,0x00,0x0e,
+ 0xdf,0x72,0x3d,0xc2,0x21,0xce,0x50,0x00,
+ 0x02,0x00,0x88,0x25,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x0a,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00 }; /* end rawpkt4 */
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"within test\"; content:\"Hi, this is a big test to check \"; content:\"content matches\"; distance:0; within:15; sid:556;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ /* packet 1 */
+ p1 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p1 == NULL))
+ return 0;
+ memset(p1, 0, SIZE_OF_PACKET);
+ DecodeEthernet(&th_v, &dtv, p1, rawpkt1, sizeof(rawpkt1), NULL);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if (!(PacketAlertCheck(p1, 556))) {
+ printf("failed to match on packet 1: ");
+ goto end;
+ }
+
+ /* packet 2 */
+ p2 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p2 == NULL))
+ return 0;
+ memset(p2, 0, SIZE_OF_PACKET);
+ DecodeEthernet(&th_v, &dtv, p2, rawpkt2, sizeof(rawpkt2), NULL);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ if (!(PacketAlertCheck(p2, 556))) {
+ printf("failed to match on packet 2: ");
+ goto end;
+ }
+
+ /* packet 3 */
+ p3 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p3 == NULL))
+ return 0;
+ memset(p3, 0, SIZE_OF_PACKET);
+ DecodeEthernet(&th_v, &dtv, p3, rawpkt3, sizeof(rawpkt3), NULL);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
+ if (!(PacketAlertCheck(p3, 556))) {
+ printf("failed to match on packet 3: ");
+ goto end;
+ }
+
+ /* packet 4 */
+ p4 = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p4 == NULL))
+ return 0;
+ memset(p4, 0, SIZE_OF_PACKET);
+ DecodeEthernet(&th_v, &dtv, p4, rawpkt4, sizeof(rawpkt4), NULL);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p4);
+ if (!(PacketAlertCheck(p4, 556))) {
+ printf("failed to match on packet 4: ");
+ goto end;
+ }
+
+ /* packet 5 */
+ uint8_t *p5buf = (uint8_t *)"Hi, this is a big test to check content matches";
+ uint16_t p5buflen = strlen((char *)p5buf);
+ Packet *p5 = UTHBuildPacket(p5buf, p5buflen, IPPROTO_TCP);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p5);
+ if (!(PacketAlertCheck(p5, 556))) {
+ printf("failed to match on packet 5: ");
+ goto end;
+ }
+ UTHFreePackets(&p5, 1);
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ if (p1 != NULL) {
+ PACKET_RECYCLE(p1);
+ SCFree(p1);
+ }
+ if (p2 != NULL) {
+ PACKET_RECYCLE(p2);
+ SCFree(p2);
+ }
+ if (p3 != NULL) {
+ PACKET_RECYCLE(p3);
+ SCFree(p3);
+ }
+ if (p4 != NULL) {
+ PACKET_RECYCLE(p4);
+ SCFree(p4);
+ }
+ FlowShutdown();
+ return result;
+}
+
+static int SigTestWithinReal01B2g (void)
+{
+ return SigTestWithinReal01(MPM_B2G);
+}
+static int SigTestWithinReal01B3g (void)
+{
+ return SigTestWithinReal01(MPM_B3G);
+}
+static int SigTestWithinReal01Wm (void)
+{
+ return SigTestWithinReal01(MPM_WUMANBER);
+}
+
+static int SigTestDepthOffset01Real (int mpm_type)
+{
+ uint8_t *buf = (uint8_t *)"01234567890123456789012345678901abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = mpm_type;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"depth offset\"; content:\"456\"; offset:4; depth:3; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1))
+ result = 1;
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+end:
+ UTHFreePackets(&p, 1);
+ return result;
+}
+static int SigTestDepthOffset01B2g (void)
+{
+ return SigTestDepthOffset01Real(MPM_B2G);
+}
+static int SigTestDepthOffset01B3g (void)
+{
+ return SigTestDepthOffset01Real(MPM_B3G);
+}
+static int SigTestDepthOffset01Wm (void)
+{
+ return SigTestDepthOffset01Real(MPM_WUMANBER);
+}
+
+static int SigTestDetectAlertCounter(void)
+{
+ Packet *p = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&tv, 0, sizeof(tv));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Test counter\"; "
+ "content:\"boo\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ tv.name = "detect_test";
+ DetectEngineThreadCtxInit(&tv, de_ctx, (void *)&det_ctx);
+
+ /* init counters */
+ StatsSetupPrivate(&tv);
+
+ p = UTHBuildPacket((uint8_t *)"boo", strlen("boo"), IPPROTO_TCP);
+ Detect(&tv, p, det_ctx, NULL, NULL);
+ result = (StatsGetLocalCounterValue(&tv, det_ctx->counter_alerts) == 1);
+
+ Detect(&tv, p, det_ctx, NULL, NULL);
+ result &= (StatsGetLocalCounterValue(&tv, det_ctx->counter_alerts) == 2);
+ UTHFreePackets(&p, 1);
+
+ p = UTHBuildPacket((uint8_t *)"roo", strlen("roo"), IPPROTO_TCP);
+ Detect(&tv, p, det_ctx, NULL, NULL);
+ result &= (StatsGetLocalCounterValue(&tv, det_ctx->counter_alerts) == 2);
+ UTHFreePackets(&p, 1);
+
+ p = UTHBuildPacket((uint8_t *)"laboosa", strlen("laboosa"), IPPROTO_TCP);
+ Detect(&tv, p, det_ctx, NULL, NULL);
+ result &= (StatsGetLocalCounterValue(&tv, det_ctx->counter_alerts) == 3);
+ UTHFreePackets(&p, 1);
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/** \test test if the engine set flag to drop pkts of a flow that
+ * triggered a drop action on IPS mode */
+static int SigTestDropFlow01(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop http any any -> any any "
+ "(msg:\"Test proto match\"; "
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should: ");
+ goto end;
+ }
+
+ if ( !(p->flow->flags & FLOW_ACTION_DROP)) {
+ printf("sig 1 alerted but flow was not flagged correctly: ");
+ goto end;
+ }
+
+ /* Ok, now we know that the flag is set for proto http */
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test test if the engine set flag to drop pkts of a flow that
+ * triggered a drop action on IPS mode */
+static int SigTestDropFlow02(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+ TcpSession ssn;
+ Packet *p = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 "
+ "(msg:\"Test proto match\"; uricontent:\"one\";"
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+
+ if (!PacketAlertCheck(p, 1)) {
+ printf("sig 1 didn't alert, but it should: ");
+ goto end;
+ }
+
+ if ( !(p->flow->flags & FLOW_ACTION_DROP)) {
+ printf("sig 1 alerted but flow was not flagged correctly: ");
+ goto end;
+ }
+
+ /* Ok, now we know that the flag is set for app layer sigs
+ * (ex: inspecting uricontent) */
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/** \test test if the engine set flag to drop pkts of a flow that
+ * triggered a drop action on IPS mode, and it doesn't inspect
+ * any other packet of the stream */
+static int SigTestDropFlow03(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+
+ uint8_t http_buf2[] = "POST /two HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf1) - 1;
+
+ /* Set the engine mode to IPS */
+ EngineModeSetIPS();
+
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 "
+ "(msg:\"Test proto match\"; uricontent:\"one\";"
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ /* the no inspection flag should be set after the first sig gets triggered,
+ * so the second packet should not match the next sig (because of no inspection) */
+ s = de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any 80 "
+ "(msg:\"Test proto match\"; uricontent:\"two\";"
+ "sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (!PacketAlertCheck(p1, 1)) {
+ printf("sig 1 didn't alert on p1, but it should: ");
+ goto end;
+ }
+
+ if ( !(p1->flow->flags & FLOW_ACTION_DROP)) {
+ printf("sig 1 alerted but flow was not flagged correctly: ");
+ goto end;
+ }
+
+ /* Second part.. Let's feed with another packet */
+ if (StreamTcpCheckFlowDrops(p2) == 1) {
+ SCLogDebug("This flow/stream triggered a drop rule");
+ FlowSetNoPacketInspectionFlag(p2->flow);
+ DecodeSetNoPacketInspectionFlag(p2);
+ StreamTcpDisableAppLayer(p2->flow);
+ p2->action |= ACTION_DROP;
+ /* return the segments to the pool */
+ StreamTcpSessionPktFree(p2);
+ }
+
+
+ if ( !(p2->flags & PKT_NOPACKET_INSPECTION)) {
+ printf("The packet was not flagged with no-inspection: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sig 1 alerted, but it should not since the no pkt inspection should be set: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p2, 2)) {
+ printf("sig 2 alerted, but it should not since the no pkt inspection should be set: ");
+ goto end;
+ }
+
+ if ( !(PACKET_TEST_ACTION(p2, ACTION_DROP))) {
+ printf("A \"drop\" action should be set from the flow to the packet: ");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+
+ /* Restore mode to IDS */
+ EngineModeSetIDS();
+ return result;
+}
+
+/** \test test if the engine set flag to drop pkts of a flow that
+ * triggered a drop action on IDS mode, but continue the inspection
+ * as usual (instead of on IPS mode) */
+static int SigTestDropFlow04(void)
+{
+ int result = 0;
+ Flow f;
+ HtpState *http_state = NULL;
+ uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf1_len = sizeof(http_buf1) - 1;
+
+ uint8_t http_buf2[] = "POST /two HTTP/1.0\r\n"
+ "User-Agent: Mozilla/1.0\r\n"
+ "Cookie: hellocatch\r\n\r\n";
+ uint32_t http_buf2_len = sizeof(http_buf1) - 1;
+
+ TcpSession ssn;
+ Packet *p1 = NULL;
+ Packet *p2 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof(TcpSession));
+
+ p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+
+ p1->flow = &f;
+ p1->flowflags |= FLOW_PKT_TOSERVER;
+ p1->flowflags |= FLOW_PKT_ESTABLISHED;
+ p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+ p2->flow = &f;
+ p2->flowflags |= FLOW_PKT_TOSERVER;
+ p2->flowflags |= FLOW_PKT_ESTABLISHED;
+ p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ f.alproto = ALPROTO_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 "
+ "(msg:\"Test proto match\"; uricontent:\"one\";"
+ "sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ /* the no inspection flag should be set after the first sig gets triggered,
+ * so the second packet should not match the next sig (because of no inspection) */
+ s = de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any 80 "
+ "(msg:\"Test proto match\"; uricontent:\"two\";"
+ "sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ http_state = f.alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ goto end;
+ }
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (!PacketAlertCheck(p1, 1)) {
+ printf("sig 1 didn't alert on p1, but it should: ");
+ goto end;
+ }
+
+ if (PacketAlertCheck(p1, 2)) {
+ printf("sig 2 alerted on p1, but it should not: ");
+ goto end;
+ }
+
+ if ( !(p1->flow->flags & FLOW_ACTION_DROP)) {
+ printf("sig 1 alerted but flow was not flagged correctly: ");
+ goto end;
+ }
+
+ if (!(PACKET_TEST_ACTION(p1, ACTION_DROP))) {
+ printf("A \"drop\" action was set from the flow to the packet "
+ "which is right, but setting the flag shouldn't disable "
+ "inspection on the packet in IDS mode");
+ goto end;
+ }
+
+ /* Second part.. Let's feed with another packet */
+ if (StreamTcpCheckFlowDrops(p2) == 1) {
+ FlowSetNoPacketInspectionFlag(p2->flow);
+ DecodeSetNoPacketInspectionFlag(p2);
+ StreamTcpDisableAppLayer(p2->flow);
+ p2->action |= ACTION_DROP;
+ /* return the segments to the pool */
+ StreamTcpSessionPktFree(p2);
+ }
+
+ if ( (p2->flags & PKT_NOPACKET_INSPECTION)) {
+ printf("The packet was flagged with no-inspection but we are not on IPS mode: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
+
+ if (PacketAlertCheck(p2, 1)) {
+ printf("sig 1 alerted, but it should not: ");
+ goto end;
+ }
+
+ if (!PacketAlertCheck(p2, 2)) {
+ printf("sig 2 didn't alert, but it should, since we are not on IPS mode: ");
+ goto end;
+ }
+
+ if (!(PACKET_TEST_ACTION(p2, ACTION_DROP))) {
+ printf("A \"drop\" action was set from the flow to the packet "
+ "which is right, but setting the flag shouldn't disable "
+ "inspection on the packet in IDS mode");
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+
+ UTHFreePackets(&p1, 1);
+ UTHFreePackets(&p2, 1);
+
+ return result;
+}
+
+/** \test ICMP packet shouldn't be matching port based sig
+ * Bug #611 */
+static int SigTestPorts01(void)
+{
+ int result = 0;
+ Packet *p1 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ uint8_t payload[] = "AAAAAAAAAAAAAAAAAA";
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ p1 = UTHBuildPacket(payload, sizeof(payload), IPPROTO_ICMP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->mpm_matcher = MPM_B2G;
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert ip any any -> any 80 "
+ "(content:\"AAA\"; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sig 1 alerted on p1, but it should not: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+/** \test almost identical patterns */
+static int SigTestBug01(void)
+{
+ int result = 0;
+ Packet *p1 = NULL;
+ Signature *s = NULL;
+ ThreadVars tv;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ uint8_t payload[] = "!mymy";
+
+ memset(&tv, 0, sizeof(ThreadVars));
+
+ p1 = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(content:\"Omymy\"; nocase; sid:1;)");
+ if (s == NULL) {
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(content:\"!mymy\"; nocase; sid:2;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+ /* do detect */
+ SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
+
+ if (PacketAlertCheck(p1, 1)) {
+ printf("sig 1 alerted on p1, but it should not: ");
+ goto end;
+ }
+ if (!(PacketAlertCheck(p1, 2))) {
+ printf("sig 2 did not p1, but it should have: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&tv, det_ctx);
+ if (de_ctx != NULL)
+ SigGroupCleanup(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p1, 1);
+ return result;
+}
+
+static const char *dummy_conf_string2 =
+ "%YAML 1.1\n"
+ "---\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[10.10.10.0/24, !10.10.10.247]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n";
+
+static int DetectAddressYamlParsing01 (void)
+{
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string2, strlen(dummy_conf_string2));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> any any (sid:1;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp any any -> $HOME_NET any (sid:2;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> $HOME_NET any (sid:3;)")) == NULL)
+ goto end;
+
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return result;
+}
+
+static const char *dummy_conf_string3 =
+ "%YAML 1.1\n"
+ "---\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[10.10.10.0/24, !10.10.10.247/32]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n";
+
+static int DetectAddressYamlParsing02 (void)
+{
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string3, strlen(dummy_conf_string3));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> any any (sid:1;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp any any -> $HOME_NET any (sid:2;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> $HOME_NET any (sid:3;)")) == NULL)
+ goto end;
+
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return result;
+}
+
+static const char *dummy_conf_string4 =
+ "%YAML 1.1\n"
+ "---\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[10.10.10.0/24, !10.10.10.247/32]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n";
+
+static int DetectAddressYamlParsing03 (void)
+{
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string4, strlen(dummy_conf_string4));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> any any (sid:1;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp any any -> $HOME_NET any (sid:2;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> $HOME_NET any (sid:3;)")) == NULL)
+ goto end;
+
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return result;
+}
+
+static const char *dummy_conf_string5 =
+ "%YAML 1.1\n"
+ "---\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[10.196.0.0/24, !10.196.0.15]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"any\"\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n";
+
+/** \test bug #815 */
+static int DetectAddressYamlParsing04 (void)
+{
+ int result = 0;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string5, strlen(dummy_conf_string5));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> any any (sid:1;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp any any -> $HOME_NET any (sid:2;)")) == NULL)
+ goto end;
+ if ((DetectEngineAppendSig(de_ctx, "alert tcp $HOME_NET any -> $HOME_NET any (sid:3;)")) == NULL)
+ goto end;
+
+ result = 1;
+
+ DetectEngineCtxFree(de_ctx);
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ return result;
+}
+#endif /* UNITTESTS */
+
+void SigRegisterTests(void)
+{
+#ifdef UNITTESTS
+ SigParseRegisterTests();
+ IPOnlyRegisterTests();
+
+ UtRegisterTest("SigTest01B2g -- HTTP URI cap", SigTest01B2g, 1);
+ UtRegisterTest("SigTest01B3g -- HTTP URI cap", SigTest01B3g, 1);
+ UtRegisterTest("SigTest01Wm -- HTTP URI cap", SigTest01Wm, 1);
+
+ UtRegisterTest("SigTest02B2g -- Offset/Depth match", SigTest02B2g, 1);
+ UtRegisterTest("SigTest02B3g -- Offset/Depth match", SigTest02B3g, 1);
+ UtRegisterTest("SigTest02Wm -- Offset/Depth match", SigTest02Wm, 1);
+
+ UtRegisterTest("SigTest03B2g -- offset/depth mismatch", SigTest03B2g, 1);
+ UtRegisterTest("SigTest03B3g -- offset/depth mismatch", SigTest03B3g, 1);
+ UtRegisterTest("SigTest03Wm -- offset/depth mismatch", SigTest03Wm, 1);
+
+ UtRegisterTest("SigTest04B2g -- distance/within match", SigTest04B2g, 1);
+ UtRegisterTest("SigTest04B3g -- distance/within match", SigTest04B3g, 1);
+ UtRegisterTest("SigTest04Wm -- distance/within match", SigTest04Wm, 1);
+
+ UtRegisterTest("SigTest05B2g -- distance/within mismatch", SigTest05B2g, 1);
+ UtRegisterTest("SigTest05B3g -- distance/within mismatch", SigTest05B3g, 1);
+ UtRegisterTest("SigTest05Wm -- distance/within mismatch", SigTest05Wm, 1);
+
+ UtRegisterTest("SigTest06B2g -- uricontent HTTP/1.1 match test", SigTest06B2g, 1);
+ UtRegisterTest("SigTest06B3g -- uricontent HTTP/1.1 match test", SigTest06B3g, 1);
+ UtRegisterTest("SigTest06wm -- uricontent HTTP/1.1 match test", SigTest06Wm, 1);
+
+ UtRegisterTest("SigTest07B2g -- uricontent HTTP/1.1 mismatch test", SigTest07B2g, 1);
+ UtRegisterTest("SigTest07B3g -- uricontent HTTP/1.1 mismatch test", SigTest07B3g, 1);
+ UtRegisterTest("SigTest07Wm -- uricontent HTTP/1.1 mismatch test", SigTest07Wm, 1);
+
+ UtRegisterTest("SigTest08B2g -- uricontent HTTP/1.0 match test", SigTest08B2g, 1);
+ UtRegisterTest("SigTest08B3g -- uricontent HTTP/1.0 match test", SigTest08B3g, 1);
+ UtRegisterTest("SigTest08Wm -- uricontent HTTP/1.0 match test", SigTest08Wm, 1);
+
+ UtRegisterTest("SigTest09B2g -- uricontent HTTP/1.0 mismatch test", SigTest09B2g, 1);
+ UtRegisterTest("SigTest09B3g -- uricontent HTTP/1.0 mismatch test", SigTest09B3g, 1);
+ UtRegisterTest("SigTest09Wm -- uricontent HTTP/1.0 mismatch test", SigTest09Wm, 1);
+
+ UtRegisterTest("SigTest10B2g -- long content match, longer than pkt", SigTest10B2g, 1);
+ UtRegisterTest("SigTest10B3g -- long content match, longer than pkt", SigTest10B3g, 1);
+ UtRegisterTest("SigTest10Wm -- long content match, longer than pkt", SigTest10Wm, 1);
+
+ UtRegisterTest("SigTest11B2g -- mpm searching", SigTest11B2g, 1);
+ UtRegisterTest("SigTest11B3g -- mpm searching", SigTest11B3g, 1);
+ UtRegisterTest("SigTest11Wm -- mpm searching", SigTest11Wm, 1);
+
+ UtRegisterTest("SigTest12B2g -- content order matching, normal", SigTest12B2g, 1);
+ UtRegisterTest("SigTest12B3g -- content order matching, normal", SigTest12B3g, 1);
+ UtRegisterTest("SigTest12Wm -- content order matching, normal", SigTest12Wm, 1);
+
+ UtRegisterTest("SigTest13B2g -- content order matching, diff order", SigTest13B2g, 1);
+ UtRegisterTest("SigTest13B3g -- content order matching, diff order", SigTest13B3g, 1);
+ UtRegisterTest("SigTest13Wm -- content order matching, diff order", SigTest13Wm, 1);
+
+ UtRegisterTest("SigTest14B2g -- content order matching, distance 0", SigTest14B2g, 1);
+ UtRegisterTest("SigTest14B3g -- content order matching, distance 0", SigTest14B3g, 1);
+ UtRegisterTest("SigTest14Wm -- content order matching, distance 0", SigTest14Wm, 1);
+
+ UtRegisterTest("SigTest15B2g -- port negation sig (no match)", SigTest15B2g, 1);
+ UtRegisterTest("SigTest15B3g -- port negation sig (no match)", SigTest15B3g, 1);
+ UtRegisterTest("SigTest15Wm -- port negation sig (no match)", SigTest15Wm, 1);
+
+ UtRegisterTest("SigTest16B2g -- port negation sig (match)", SigTest16B2g, 1);
+ UtRegisterTest("SigTest16B3g -- port negation sig (match)", SigTest16B3g, 1);
+ UtRegisterTest("SigTest16Wm -- port negation sig (match)", SigTest16Wm, 1);
+
+ UtRegisterTest("SigTest17B2g -- HTTP Host Pkt var capture", SigTest17B2g, 1);
+ UtRegisterTest("SigTest17B3g -- HTTP Host Pkt var capture", SigTest17B3g, 1);
+ UtRegisterTest("SigTest17Wm -- HTTP Host Pkt var capture", SigTest17Wm, 1);
+
+ UtRegisterTest("SigTest18B2g -- Ftp negation sig test", SigTest18B2g, 1);
+ UtRegisterTest("SigTest18B3g -- Ftp negation sig test", SigTest18B3g, 1);
+ UtRegisterTest("SigTest18Wm -- Ftp negation sig test", SigTest18Wm, 1);
+
+ UtRegisterTest("SigTest19B2g -- IP-ONLY test (1)", SigTest19B2g, 1);
+ UtRegisterTest("SigTest19B3g -- IP-ONLY test (1)", SigTest19B3g, 1);
+ UtRegisterTest("SigTest19Wm -- IP-ONLY test (1)", SigTest19Wm, 1);
+
+ UtRegisterTest("SigTest20B2g -- IP-ONLY test (2)", SigTest20B2g, 1);
+ UtRegisterTest("SigTest20B3g -- IP-ONLY test (2)", SigTest20B3g, 1);
+ UtRegisterTest("SigTest20Wm -- IP-ONLY test (2)", SigTest20Wm, 1);
+
+ UtRegisterTest("SigTest21B2g -- FLOWBIT test (1)", SigTest21B2g, 1);
+ UtRegisterTest("SigTest21B3g -- FLOWBIT test (1)", SigTest21B3g, 1);
+ UtRegisterTest("SigTest21Wm -- FLOWBIT test (1)", SigTest21Wm, 1);
+
+ UtRegisterTest("SigTest22B2g -- FLOWBIT test (2)", SigTest22B2g, 1);
+ UtRegisterTest("SigTest22B3g -- FLOWBIT test (2)", SigTest22B3g, 1);
+ UtRegisterTest("SigTest22Wm -- FLOWBIT test (2)", SigTest22Wm, 1);
+
+ UtRegisterTest("SigTest23B2g -- FLOWBIT test (3)", SigTest23B2g, 1);
+ UtRegisterTest("SigTest23B3g -- FLOWBIT test (3)", SigTest23B3g, 1);
+ UtRegisterTest("SigTest23Wm -- FLOWBIT test (3)", SigTest23Wm, 1);
+
+ UtRegisterTest("SigTest24IPV4Keyword", SigTest24IPV4Keyword, 1);
+ UtRegisterTest("SigTest25NegativeIPV4Keyword",
+ SigTest25NegativeIPV4Keyword, 1);
+
+ UtRegisterTest("SigTest26TCPV4Keyword", SigTest26TCPV4Keyword, 1);
+ UtRegisterTest("SigTest26TCPV4AndNegativeIPV4Keyword", SigTest26TCPV4AndNegativeIPV4Keyword, 1);
+ UtRegisterTest("SigTest26TCPV4AndIPV4Keyword", SigTest26TCPV4AndIPV4Keyword, 1);
+ UtRegisterTest("SigTest27NegativeTCPV4Keyword",
+ SigTest27NegativeTCPV4Keyword, 1);
+
+ UtRegisterTest("SigTest28TCPV6Keyword", SigTest28TCPV6Keyword, 1);
+ UtRegisterTest("SigTest29NegativeTCPV6Keyword",
+ SigTest29NegativeTCPV6Keyword, 1);
+
+ UtRegisterTest("SigTest30UDPV4Keyword", SigTest30UDPV4Keyword, 1);
+ UtRegisterTest("SigTest31NegativeUDPV4Keyword",
+ SigTest31NegativeUDPV4Keyword, 1);
+
+ UtRegisterTest("SigTest32UDPV6Keyword", SigTest32UDPV6Keyword, 1);
+ UtRegisterTest("SigTest33NegativeUDPV6Keyword",
+ SigTest33NegativeUDPV6Keyword, 1);
+
+ UtRegisterTest("SigTest34ICMPV4Keyword", SigTest34ICMPV4Keyword, 1);
+ UtRegisterTest("SigTest35NegativeICMPV4Keyword",
+ SigTest35NegativeICMPV4Keyword, 1);
+
+ /* The following tests check content options with isdataat options
+ relative to that content match
+ */
+
+ UtRegisterTest("SigTest36ContentAndIsdataatKeywords01B2g",
+ SigTest36ContentAndIsdataatKeywords01B2g, 1);
+ UtRegisterTest("SigTest36ContentAndIsdataatKeywords01B3g",
+ SigTest36ContentAndIsdataatKeywords01B3g, 1);
+ UtRegisterTest("SigTest36ContentAndIsdataatKeywords01Wm" ,
+ SigTest36ContentAndIsdataatKeywords01Wm, 1);
+
+ UtRegisterTest("SigTest37ContentAndIsdataatKeywords02B2g",
+ SigTest37ContentAndIsdataatKeywords02B2g, 1);
+ UtRegisterTest("SigTest37ContentAndIsdataatKeywords02B3g",
+ SigTest37ContentAndIsdataatKeywords02B3g, 1);
+ UtRegisterTest("SigTest37ContentAndIsdataatKeywords02Wm" ,
+ SigTest37ContentAndIsdataatKeywords02Wm, 1);
+
+ /* We need to enable these tests, as soon as we add the ICMPv6 protocol
+ support in our rules engine */
+ //UtRegisterTest("SigTest36ICMPV6Keyword", SigTest36ICMPV6Keyword, 1);
+ //UtRegisterTest("SigTest37NegativeICMPV6Keyword",
+ // SigTest37NegativeICMPV6Keyword, 1);
+
+ UtRegisterTest("SigTest38B2g -- byte_test test (1)", SigTest38B2g, 1);
+ UtRegisterTest("SigTest38B3g -- byte_test test (1)", SigTest38B3g, 1);
+ UtRegisterTest("SigTest38Wm -- byte_test test (1)", SigTest38Wm, 1);
+
+ UtRegisterTest("SigTest39B2g -- byte_jump test (2)", SigTest39B2g, 1);
+ UtRegisterTest("SigTest39B3g -- byte_jump test (2)", SigTest39B3g, 1);
+ UtRegisterTest("SigTest39Wm -- byte_jump test (2)", SigTest39Wm, 1);
+
+ UtRegisterTest("SigTest40NoPacketInspection01", SigTest40NoPacketInspection01, 1);
+ UtRegisterTest("SigTest40NoPayloadInspection02", SigTest40NoPayloadInspection02, 1);
+
+ UtRegisterTest("SigTestMemory01", SigTestMemory01, 1);
+ UtRegisterTest("SigTestMemory02", SigTestMemory02, 1);
+ UtRegisterTest("SigTestMemory03", SigTestMemory03, 1);
+
+ UtRegisterTest("SigTestSgh01", SigTestSgh01, 1);
+ UtRegisterTest("SigTestSgh02", SigTestSgh02, 1);
+ UtRegisterTest("SigTestSgh03", SigTestSgh03, 1);
+ UtRegisterTest("SigTestSgh04", SigTestSgh04, 1);
+ UtRegisterTest("SigTestSgh05", SigTestSgh05, 1);
+
+ UtRegisterTest("SigTestContent01B2g -- 32 byte pattern", SigTestContent01B2g, 1);
+ UtRegisterTest("SigTestContent01B3g -- 32 byte pattern", SigTestContent01B3g, 1);
+ UtRegisterTest("SigTestContent01Wm -- 32 byte pattern", SigTestContent01Wm, 1);
+
+ UtRegisterTest("SigTestContent02B2g -- 32+31 byte pattern", SigTestContent02B2g, 1);
+ UtRegisterTest("SigTestContent02B3g -- 32+31 byte pattern", SigTestContent02B3g, 1);
+ UtRegisterTest("SigTestContent02Wm -- 32+31 byte pattern", SigTestContent02Wm, 1);
+
+ UtRegisterTest("SigTestContent03B2g -- 32 byte pattern, x2 + distance", SigTestContent03B2g, 1);
+ UtRegisterTest("SigTestContent03B3g -- 32 byte pattern, x2 + distance", SigTestContent03B3g, 1);
+ UtRegisterTest("SigTestContent03Wm -- 32 byte pattern, x2 + distance", SigTestContent03Wm, 1);
+
+ UtRegisterTest("SigTestContent04B2g -- 32 byte pattern, x2 + distance/within", SigTestContent04B2g, 1);
+ UtRegisterTest("SigTestContent04B3g -- 32 byte pattern, x2 + distance/within", SigTestContent04B3g, 1);
+ UtRegisterTest("SigTestContent04Wm -- 32 byte pattern, x2 + distance/within", SigTestContent04Wm, 1);
+
+ UtRegisterTest("SigTestContent05B2g -- distance/within", SigTestContent05B2g, 1);
+ UtRegisterTest("SigTestContent05B3g -- distance/within", SigTestContent05B3g, 1);
+ UtRegisterTest("SigTestContent05Wm -- distance/within", SigTestContent05Wm, 1);
+
+ UtRegisterTest("SigTestContent06B2g -- distance/within ip only", SigTestContent06B2g, 1);
+ UtRegisterTest("SigTestContent06B3g -- distance/within ip only", SigTestContent06B3g, 1);
+ UtRegisterTest("SigTestContent06Wm -- distance/within ip only", SigTestContent06Wm, 1);
+
+ UtRegisterTest("SigTestWithinReal01B2g", SigTestWithinReal01B2g, 1);
+ UtRegisterTest("SigTestWithinReal01B3g", SigTestWithinReal01B3g, 1);
+ UtRegisterTest("SigTestWithinReal01Wm", SigTestWithinReal01Wm, 1);
+
+ UtRegisterTest("SigTestDepthOffset01B2g", SigTestDepthOffset01B2g, 1);
+ UtRegisterTest("SigTestDepthOffset01B3g", SigTestDepthOffset01B3g, 1);
+ UtRegisterTest("SigTestDepthOffset01Wm", SigTestDepthOffset01Wm, 1);
+
+ UtRegisterTest("SigTestDetectAlertCounter", SigTestDetectAlertCounter, 1);
+
+ UtRegisterTest("SigTestDropFlow01", SigTestDropFlow01, 1);
+ UtRegisterTest("SigTestDropFlow02", SigTestDropFlow02, 1);
+ UtRegisterTest("SigTestDropFlow03", SigTestDropFlow03, 1);
+ UtRegisterTest("SigTestDropFlow04", SigTestDropFlow04, 1);
+
+ UtRegisterTest("DetectAddressYamlParsing01", DetectAddressYamlParsing01, 1);
+ UtRegisterTest("DetectAddressYamlParsing02", DetectAddressYamlParsing02, 1);
+ UtRegisterTest("DetectAddressYamlParsing03", DetectAddressYamlParsing03, 1);
+ UtRegisterTest("DetectAddressYamlParsing04", DetectAddressYamlParsing04, 1);
+
+ UtRegisterTest("SigTestPorts01", SigTestPorts01, 1);
+ UtRegisterTest("SigTestBug01", SigTestBug01, 1);
+
+#if 0
+ DetectSimdRegisterTests();
+#endif
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/detect.h b/framework/src/suricata/src/detect.h
new file mode 100644
index 00000000..236f69aa
--- /dev/null
+++ b/framework/src/suricata/src/detect.h
@@ -0,0 +1,1284 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_H__
+#define __DETECT_H__
+
+#include <stdint.h>
+
+#include "flow.h"
+
+#include "detect-engine-proto.h"
+#include "detect-reference.h"
+
+#include "packet-queue.h"
+#include "util-mpm.h"
+#include "util-hash.h"
+#include "util-hashlist.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-radix-tree.h"
+#include "util-file.h"
+#include "reputation.h"
+
+#include "detect-mark.h"
+
+#define DETECT_MAX_RULE_SIZE 8192
+
+/* forward declarations for the structures from detect-engine-sigorder.h */
+struct SCSigOrderFunc_;
+struct SCSigSignatureWrapper_;
+
+/*
+
+ The detection engine groups similar signatures/rules together. Internally a
+ tree of different types of data is created on initialization. This is it's
+ global layout:
+
+ For TCP/UDP
+
+ - Flow direction
+ -- Protocol
+ -=- Src address
+ -==- Dst address
+ -===- Src port
+ -====- Dst port
+
+ For the other protocols
+
+ - Flow direction
+ -- Protocol
+ -=- Src address
+ -==- Dst address
+
+*/
+
+/*
+ * DETECT ADDRESS
+ */
+
+/* holds the values for different possible lists in struct Signature.
+ * These codes are access points to particular lists in the array
+ * Signature->sm_lists[DETECT_SM_LIST_MAX]. */
+enum DetectSigmatchListEnum {
+ DETECT_SM_LIST_MATCH = 0,
+ DETECT_SM_LIST_PMATCH,
+ /* list for http_uri keyword and the ones relative to it */
+ DETECT_SM_LIST_UMATCH,
+ /* list for http_raw_uri keyword and the ones relative to it */
+ DETECT_SM_LIST_HRUDMATCH,
+ /* list for http_client_body keyword and the ones relative to it */
+ DETECT_SM_LIST_HCBDMATCH,
+ /* list for http_server_body keyword and the ones relative to it */
+ DETECT_SM_LIST_FILEDATA,
+ /* list for http_header keyword and the ones relative to it */
+ DETECT_SM_LIST_HHDMATCH,
+ /* list for http_raw_header keyword and the ones relative to it */
+ DETECT_SM_LIST_HRHDMATCH,
+ /* list for http_stat_msg keyword and the ones relative to it */
+ DETECT_SM_LIST_HSMDMATCH,
+ /* list for http_stat_code keyword and the ones relative to it */
+ DETECT_SM_LIST_HSCDMATCH,
+ /* list for http_host keyword and the ones relative to it */
+ DETECT_SM_LIST_HHHDMATCH,
+ /* list for http_raw_host keyword and the ones relative to it */
+ DETECT_SM_LIST_HRHHDMATCH,
+ /* list for http_method keyword and the ones relative to it */
+ DETECT_SM_LIST_HMDMATCH,
+ /* list for http_cookie keyword and the ones relative to it */
+ DETECT_SM_LIST_HCDMATCH,
+ /* list for http_user_agent keyword and the ones relative to it */
+ DETECT_SM_LIST_HUADMATCH,
+ /* list for http_request_line keyword and the ones relative to it */
+ DETECT_SM_LIST_HRLMATCH,
+ /* app event engine sm list */
+ DETECT_SM_LIST_APP_EVENT,
+
+ DETECT_SM_LIST_AMATCH,
+ DETECT_SM_LIST_DMATCH,
+ DETECT_SM_LIST_TMATCH,
+
+ DETECT_SM_LIST_FILEMATCH,
+
+ DETECT_SM_LIST_DNSREQUEST_MATCH, /**< per DNS query tx match list */
+ DETECT_SM_LIST_DNSRESPONSE_MATCH, /**< per DNS response tx match list */
+ DETECT_SM_LIST_DNSQUERYNAME_MATCH, /**< per query in a tx list */
+
+ DETECT_SM_LIST_MODBUS_MATCH,
+
+ /* list for post match actions: flowbit set, flowint increment, etc */
+ DETECT_SM_LIST_POSTMATCH,
+
+ /* lists for alert thresholding and suppression */
+ DETECT_SM_LIST_SUPPRESS,
+ DETECT_SM_LIST_THRESHOLD,
+ DETECT_SM_LIST_MAX,
+
+ /* used for Signature->list, which indicates which list
+ * we're adding keywords to in cases of sticky buffers like
+ * file_data */
+ DETECT_SM_LIST_NOTSET,
+};
+
+/* a is ... than b */
+enum {
+ ADDRESS_ER = -1, /**< error e.g. compare ipv4 and ipv6 */
+ ADDRESS_LT, /**< smaller [aaa] [bbb] */
+ ADDRESS_LE, /**< smaller with overlap [aa[bab]bb] */
+ ADDRESS_EQ, /**< exactly equal [abababab] */
+ ADDRESS_ES, /**< within [bb[aaa]bb] and [[abab]bbb] and [bbb[abab]] */
+ ADDRESS_EB, /**< completely overlaps [aa[bbb]aa] and [[baba]aaa] and [aaa[baba]] */
+ ADDRESS_GE, /**< bigger with overlap [bb[aba]aa] */
+ ADDRESS_GT, /**< bigger [bbb] [aaa] */
+};
+
+#define ADDRESS_FLAG_ANY 0x01 /**< address is "any" */
+#define ADDRESS_FLAG_NOT 0x02 /**< address is negated */
+
+#define ADDRESS_SIGGROUPHEAD_COPY 0x04 /**< sgh is a ptr to another sgh */
+#define ADDRESS_PORTS_COPY 0x08 /**< ports are a ptr to other ports */
+#define ADDRESS_PORTS_NOTUNIQ 0x10
+#define ADDRESS_HAVEPORT 0x20 /**< address has a ports ptr */
+
+/** \brief address structure for use in the detection engine.
+ *
+ * Contains the address information and matching information.
+ */
+typedef struct DetectAddress_ {
+ /** address data for this group */
+ Address ip;
+ Address ip2;
+
+ /** ptr to the next address (dst addr in that case) or to the src port */
+ union {
+ struct DetectAddressHead_ *dst_gh; /**< destination address */
+ struct DetectPort_ *port; /**< source port */
+ };
+
+ /** signatures that belong in this group */
+ struct SigGroupHead_ *sh;
+
+ /** flags affecting this address */
+ uint8_t flags;
+
+ /** ptr to the previous address in the list */
+ struct DetectAddress_ *prev;
+ /** ptr to the next address in the list */
+ struct DetectAddress_ *next;
+
+ uint32_t cnt;
+} DetectAddress;
+
+/** Signature grouping head. Here 'any', ipv4 and ipv6 are split out */
+typedef struct DetectAddressHead_ {
+ DetectAddress *any_head;
+ DetectAddress *ipv4_head;
+ DetectAddress *ipv6_head;
+} DetectAddressHead;
+
+
+#include "detect-threshold.h"
+
+typedef struct DetectMatchAddressIPv4_ {
+ uint32_t ip; /**< address in host order, start of range */
+ uint32_t ip2; /**< address in host order, end of range */
+} DetectMatchAddressIPv4;
+
+typedef struct DetectMatchAddressIPv6_ {
+ uint32_t ip[4];
+ uint32_t ip2[4];
+} DetectMatchAddressIPv6;
+
+/*
+ * DETECT PORT
+ */
+
+/* a is ... than b */
+enum {
+ PORT_ER = -1, /* error e.g. compare ipv4 and ipv6 */
+ PORT_LT, /* smaller [aaa] [bbb] */
+ PORT_LE, /* smaller with overlap [aa[bab]bb] */
+ PORT_EQ, /* exactly equal [abababab] */
+ PORT_ES, /* within [bb[aaa]bb] and [[abab]bbb] and [bbb[abab]] */
+ PORT_EB, /* completely overlaps [aa[bbb]aa] and [[baba]aaa] and [aaa[baba]] */
+ PORT_GE, /* bigger with overlap [bb[aba]aa] */
+ PORT_GT, /* bigger [bbb] [aaa] */
+};
+
+#define PORT_FLAG_ANY 0x01 /**< 'any' special port */
+#define PORT_FLAG_NOT 0x02 /**< negated port */
+#define PORT_SIGGROUPHEAD_COPY 0x04 /**< sgh is a ptr copy */
+#define PORT_GROUP_PORTS_COPY 0x08 /**< dst_ph is a ptr copy */
+
+/** \brief Port structure for detection engine */
+typedef struct DetectPort_ {
+ uint16_t port;
+ uint16_t port2;
+
+ /* signatures that belong in this group */
+ struct SigGroupHead_ *sh;
+
+ struct DetectPort_ *dst_ph;
+
+ /* double linked list */
+ union {
+ struct DetectPort_ *prev;
+ struct DetectPort_ *hnext; /* hash next */
+ };
+ struct DetectPort_ *next;
+
+ uint32_t cnt;
+ uint8_t flags; /**< flags for this port */
+} DetectPort;
+
+/* Signature flags */
+#define SIG_FLAG_SRC_ANY (1) /**< source is any */
+#define SIG_FLAG_DST_ANY (1<<1) /**< destination is any */
+#define SIG_FLAG_SP_ANY (1<<2) /**< source port is any */
+#define SIG_FLAG_DP_ANY (1<<3) /**< destination port is any */
+
+#define SIG_FLAG_NOALERT (1<<4) /**< no alert flag is set */
+#define SIG_FLAG_DSIZE (1<<5) /**< signature has a dsize setting */
+#define SIG_FLAG_APPLAYER (1<<6) /**< signature applies to app layer instead of packets */
+#define SIG_FLAG_IPONLY (1<<7) /**< ip only signature */
+
+#define SIG_FLAG_STATE_MATCH (1<<8) /**< signature has matches that require stateful inspection */
+
+#define SIG_FLAG_REQUIRE_PACKET (1<<9) /**< signature is requiring packet match */
+#define SIG_FLAG_REQUIRE_STREAM (1<<10) /**< signature is requiring stream match */
+
+#define SIG_FLAG_MPM_PACKET (1<<11)
+#define SIG_FLAG_MPM_PACKET_NEG (1<<12)
+#define SIG_FLAG_MPM_STREAM (1<<13)
+#define SIG_FLAG_MPM_STREAM_NEG (1<<14)
+#define SIG_FLAG_MPM_APPLAYER (1<<15)
+#define SIG_FLAG_MPM_APPLAYER_NEG (1<<16)
+
+#define SIG_FLAG_REQUIRE_FLOWVAR (1<<17) /**< signature can only match if a flowbit, flowvar or flowint is available. */
+
+#define SIG_FLAG_FILESTORE (1<<18) /**< signature has filestore keyword */
+
+#define SIG_FLAG_TOSERVER (1<<19)
+#define SIG_FLAG_TOCLIENT (1<<20)
+
+#define SIG_FLAG_TLSSTORE (1<<21)
+
+/* signature init flags */
+#define SIG_FLAG_INIT_DEONLY 1 /**< decode event only signature */
+#define SIG_FLAG_INIT_PACKET (1<<1) /**< signature has matches against a packet (as opposed to app layer) */
+#define SIG_FLAG_INIT_FLOW (1<<2) /**< signature has a flow setting */
+#define SIG_FLAG_INIT_BIDIREC (1<<3) /**< signature has bidirectional operator */
+#define SIG_FLAG_INIT_PAYLOAD (1<<4) /**< signature is inspecting the packet payload */
+#define SIG_FLAG_INIT_FIRST_IPPROTO_SEEN (1 << 5) /** < signature has seen the first ip_proto keyword */
+
+/* signature mask flags */
+#define SIG_MASK_REQUIRE_PAYLOAD (1<<0)
+#define SIG_MASK_REQUIRE_FLOW (1<<1)
+#define SIG_MASK_REQUIRE_FLAGS_INITDEINIT (1<<2) /* SYN, FIN, RST */
+#define SIG_MASK_REQUIRE_FLAGS_UNUSUAL (1<<3) /* URG, ECN, CWR */
+#define SIG_MASK_REQUIRE_NO_PAYLOAD (1<<4)
+#define SIG_MASK_REQUIRE_HTTP_STATE (1<<5)
+#define SIG_MASK_REQUIRE_DCE_STATE (1<<6)
+#define SIG_MASK_REQUIRE_ENGINE_EVENT (1<<7)
+#define SIG_MASK_REQUIRE_SSH_STATE (1<<8)
+#define SIG_MASK_REQUIRE_TLS_STATE (1<<9)
+#define SIG_MASK_REQUIRE_DNS_STATE (1<<10)
+#define SIG_MASK_REQUIRE_FTP_STATE (1<<11)
+#define SIG_MASK_REQUIRE_SMTP_STATE (1<<12)
+
+/* for now a uint8_t is enough */
+#define SignatureMask uint16_t
+
+#define DETECT_ENGINE_THREAD_CTX_INSPECTING_PACKET 0x0001
+#define DETECT_ENGINE_THREAD_CTX_INSPECTING_STREAM 0x0002
+#define DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH 0x0004
+
+#define FILE_SIG_NEED_FILE 0x01
+#define FILE_SIG_NEED_FILENAME 0x02
+#define FILE_SIG_NEED_TYPE 0x04
+#define FILE_SIG_NEED_MAGIC 0x08 /**< need the start of the file */
+#define FILE_SIG_NEED_FILECONTENT 0x10
+#define FILE_SIG_NEED_MD5 0x20
+#define FILE_SIG_NEED_SIZE 0x40
+
+/* Detection Engine flags */
+#define DE_QUIET 0x01 /**< DE is quiet (esp for unittests) */
+
+typedef struct IPOnlyCIDRItem_ {
+ /* address data for this item */
+ uint8_t family;
+ uint32_t ip[4];
+ /* netmask in CIDR values (ex. /16 /18 /24..) */
+ uint8_t netmask;
+
+ /* If this host or net is negated for the signum */
+ uint8_t negated;
+ SigIntId signum; /**< our internal id */
+
+ /* linked list, the header should be the biggest network */
+ struct IPOnlyCIDRItem_ *next;
+
+} IPOnlyCIDRItem;
+
+/** \brief Used to start a pointer to SigMatch context
+ * Should never be dereferenced without casting to something else.
+ */
+typedef struct SigMatchCtx_ {
+ int foo;
+} SigMatchCtx;
+
+/** \brief a single match condition for a signature */
+typedef struct SigMatch_ {
+ uint8_t type; /**< match type */
+ uint16_t idx; /**< position in the signature */
+ SigMatchCtx *ctx; /**< plugin specific data */
+ struct SigMatch_ *next;
+ struct SigMatch_ *prev;
+} SigMatch;
+
+/** \brief Data needed for Match() */
+typedef struct SigMatchData_ {
+ uint8_t type; /**< match type */
+ uint8_t is_last; /**< Last element of the list */
+ SigMatchCtx *ctx; /**< plugin specific data */
+} SigMatchData;
+
+
+/** \brief Signature container */
+typedef struct Signature_ {
+ /* coccinelle: Signature:flags:SIG_FLAG */
+ uint32_t flags;
+
+ AppProto alproto;
+
+ uint16_t dsize_low;
+ uint16_t dsize_high;
+
+ uint16_t mpm_pattern_id_div_8;
+ uint8_t mpm_pattern_id_mod_8;
+
+ SignatureMask mask;
+ SigIntId num; /**< signature number, internal id */
+
+ /** inline -- action */
+ uint8_t action;
+ uint8_t file_flags;
+
+ /** addresses, ports and proto this sig matches on */
+ DetectProto proto;
+
+ /** classification id **/
+ uint8_t class;
+
+ /** ipv4 match arrays */
+ uint16_t addr_dst_match4_cnt;
+ uint16_t addr_src_match4_cnt;
+ uint16_t addr_dst_match6_cnt;
+ uint16_t addr_src_match6_cnt;
+ DetectMatchAddressIPv4 *addr_dst_match4;
+ DetectMatchAddressIPv4 *addr_src_match4;
+ /** ipv6 match arrays */
+ DetectMatchAddressIPv6 *addr_dst_match6;
+ DetectMatchAddressIPv6 *addr_src_match6;
+
+ uint32_t id; /**< sid, set by the 'sid' rule keyword */
+ uint32_t gid; /**< generator id */
+ uint32_t rev;
+ int prio;
+
+ /** port settings for this signature */
+ DetectPort *sp, *dp;
+
+#ifdef PROFILING
+ uint16_t profiling_id;
+#endif
+ /** number of sigmatches in the match and pmatch list */
+ uint16_t sm_cnt;
+
+ /* used to hold flags that are predominantly used during init */
+ uint32_t init_flags;
+ /* coccinelle: Signature:init_flags:SIG_FLAG_INIT_ */
+
+ /** netblocks and hosts specified at the sid, in CIDR format */
+ IPOnlyCIDRItem *CidrSrc, *CidrDst;
+
+ /* Hold copies of the sm lists for Match() */
+ SigMatchData *sm_arrays[DETECT_SM_LIST_MAX];
+
+ /* holds all sm lists */
+ struct SigMatch_ *sm_lists[DETECT_SM_LIST_MAX];
+ /* holds all sm lists' tails */
+ struct SigMatch_ *sm_lists_tail[DETECT_SM_LIST_MAX];
+
+ SigMatch *filestore_sm;
+
+ char *msg;
+
+ /** classification message */
+ char *class_msg;
+ /** Reference */
+ DetectReference *references;
+
+ /** address settings for this signature */
+ DetectAddressHead src, dst;
+
+ /* used at init to determine max dsize */
+ SigMatch *dsize_sm;
+ /* the fast pattern added from this signature */
+ SigMatch *mpm_sm;
+ /* helper for init phase */
+ uint16_t mpm_content_maxlen;
+ uint16_t mpm_uricontent_maxlen;
+
+ int list;
+
+ /* Be careful, this pointer is only valid while parsing the sig,
+ * to warn the user about any possible problem */
+ char *sig_str;
+
+ /** ptr to the next sig in the list */
+ struct Signature_ *next;
+} Signature;
+
+typedef struct DetectReplaceList_ {
+ struct DetectContentData_ *cd;
+ uint8_t *found;
+ struct DetectReplaceList_ *next;
+} DetectReplaceList;
+
+/** only execute flowvar storage if rule matched */
+#define DETECT_FLOWVAR_TYPE_POSTMATCH 1
+/** execute flowvar storage even if rule doesn't match (for luajit) */
+#define DETECT_FLOWVAR_TYPE_ALWAYS 2
+
+/** list for flowvar store candidates, to be stored from
+ * post-match function */
+typedef struct DetectFlowvarList_ {
+ uint16_t idx; /**< flowvar name idx */
+ uint16_t len; /**< data len */
+ uint8_t *buffer; /**< alloc'd buffer, may be freed by
+ post-match, post-non-match */
+ int type; /**< type of store candidate POSTMATCH or ALWAYS */
+ struct DetectFlowvarList_ *next;
+} DetectFlowvarList;
+
+typedef struct DetectEngineIPOnlyThreadCtx_ {
+ uint8_t *sig_match_array; /* bit array of sig nums */
+ uint32_t sig_match_size; /* size in bytes of the array */
+} DetectEngineIPOnlyThreadCtx;
+
+/** \brief IP only rules matching ctx. */
+typedef struct DetectEngineIPOnlyCtx_ {
+ /* lookup hashes */
+ HashListTable *ht16_src, *ht16_dst;
+ HashListTable *ht24_src, *ht24_dst;
+
+ /* Lookup trees */
+ SCRadixTree *tree_ipv4src, *tree_ipv4dst;
+ SCRadixTree *tree_ipv6src, *tree_ipv6dst;
+
+ /* Used to build the radix trees */
+ IPOnlyCIDRItem *ip_src, *ip_dst;
+
+ /* counters */
+ uint32_t a_src_uniq16, a_src_total16;
+ uint32_t a_dst_uniq16, a_dst_total16;
+ uint32_t a_src_uniq24, a_src_total24;
+ uint32_t a_dst_uniq24, a_dst_total24;
+
+ uint32_t max_idx;
+
+ uint8_t *sig_init_array; /* bit array of sig nums */
+ uint32_t sig_init_size; /* size in bytes of the array */
+
+ /* number of sigs in this head */
+ uint32_t sig_cnt;
+ uint32_t *match_array;
+} DetectEngineIPOnlyCtx;
+
+typedef struct DetectEngineLookupFlow_ {
+ DetectAddressHead *src_gh[256]; /* a head for each protocol */
+ DetectAddressHead *tmp_gh[256];
+} DetectEngineLookupFlow;
+
+/* Flow status
+ *
+ * to server
+ * to client
+ */
+#define FLOW_STATES 2
+
+/* mpm pattern id api */
+typedef struct MpmPatternIdStore_ {
+ HashTable *hash;
+ PatIntId max_id;
+
+ uint32_t unique_patterns;
+ uint32_t shared_patterns;
+} MpmPatternIdStore;
+
+/** \brief threshold ctx */
+typedef struct ThresholdCtx_ {
+ SCMutex threshold_table_lock; /**< Mutex for hash table */
+
+ /** to support rate_filter "by_rule" option */
+ DetectThresholdEntry **th_entry;
+ uint32_t th_size;
+} ThresholdCtx;
+
+typedef struct DetectEngineThreadKeywordCtxItem_ {
+ void *(*InitFunc)(void *);
+ void (*FreeFunc)(void *);
+ void *data;
+ struct DetectEngineThreadKeywordCtxItem_ *next;
+ int id;
+ const char *name; /* keyword name, for error printing */
+} DetectEngineThreadKeywordCtxItem;
+
+/** \brief main detection engine ctx */
+typedef struct DetectEngineCtx_ {
+ uint8_t flags;
+ int failure_fatal;
+
+ int tenant_id;
+
+ Signature *sig_list;
+ uint32_t sig_cnt;
+
+ /* version of the srep data */
+ uint32_t srep_version;
+
+ /* reputation for netblocks */
+ SRepCIDRTree *srepCIDR_ctx;
+
+ Signature **sig_array;
+ uint32_t sig_array_size; /* size in bytes */
+ uint32_t sig_array_len; /* size in array members */
+
+ uint32_t signum;
+
+ /** Maximum value of all our sgh's non_mpm_store_cnt setting,
+ * used to alloc det_ctx::non_mpm_id_array */
+ uint32_t non_mpm_store_cnt_max;
+
+ /* used by the signature ordering module */
+ struct SCSigOrderFunc_ *sc_sig_order_funcs;
+
+ /* hash table used for holding the classification config info */
+ HashTable *class_conf_ht;
+ /* hash table used for holding the reference config info */
+ HashTable *reference_conf_ht;
+
+ /* main sigs */
+ DetectEngineLookupFlow flow_gh[FLOW_STATES];
+
+ uint32_t mpm_unique, mpm_reuse, mpm_none,
+ mpm_uri_unique, mpm_uri_reuse, mpm_uri_none;
+ uint32_t gh_unique, gh_reuse;
+
+ uint32_t mpm_max_patcnt, mpm_min_patcnt, mpm_tot_patcnt,
+ mpm_uri_max_patcnt, mpm_uri_min_patcnt, mpm_uri_tot_patcnt;
+
+ /* init phase vars */
+ HashListTable *sgh_hash_table;
+
+ HashListTable *sgh_mpm_hash_table;
+ HashListTable *sgh_mpm_uri_hash_table;
+ HashListTable *sgh_mpm_stream_hash_table;
+
+ HashListTable *sgh_sport_hash_table;
+ HashListTable *sgh_dport_hash_table;
+
+ HashListTable *sport_hash_table;
+ HashListTable *dport_hash_table;
+
+ HashListTable *variable_names;
+ HashListTable *variable_idxs;
+ uint16_t variable_names_idx;
+
+ /* hash table used to cull out duplicate sigs */
+ HashListTable *dup_sig_hash_table;
+
+ /* memory counters */
+ uint32_t mpm_memory_size;
+
+ DetectEngineIPOnlyCtx io_ctx;
+ ThresholdCtx ths_ctx;
+
+ uint16_t mpm_matcher; /**< mpm matcher this ctx uses */
+
+ /* Config options */
+
+ uint16_t max_uniq_toclient_src_groups;
+ uint16_t max_uniq_toclient_dst_groups;
+ uint16_t max_uniq_toclient_sp_groups;
+ uint16_t max_uniq_toclient_dp_groups;
+
+ uint16_t max_uniq_toserver_src_groups;
+ uint16_t max_uniq_toserver_dst_groups;
+ uint16_t max_uniq_toserver_sp_groups;
+ uint16_t max_uniq_toserver_dp_groups;
+
+ /* specify the configuration for mpm context factory */
+ uint8_t sgh_mpm_context;
+
+ /** hash table for looking up patterns for
+ * id sharing and id tracking. */
+ MpmPatternIdStore *mpm_pattern_id_store;
+ uint16_t max_fp_id;
+
+ MpmCtxFactoryContainer *mpm_ctx_factory_container;
+
+ /* maximum recursion depth for content inspection */
+ int inspection_recursion_limit;
+
+ /* conf parameter that limits the length of the http request body inspected */
+ int hcbd_buffer_limit;
+ /* conf parameter that limits the length of the http response body inspected */
+ int hsbd_buffer_limit;
+
+ /* array containing all sgh's in use so we can loop
+ * through it in Stage4. */
+ struct SigGroupHead_ **sgh_array;
+ uint32_t sgh_array_cnt;
+ uint32_t sgh_array_size;
+
+ int32_t sgh_mpm_context_proto_tcp_packet;
+ int32_t sgh_mpm_context_proto_udp_packet;
+ int32_t sgh_mpm_context_proto_other_packet;
+ int32_t sgh_mpm_context_stream;
+ int32_t sgh_mpm_context_uri;
+ int32_t sgh_mpm_context_hcbd;
+ int32_t sgh_mpm_context_hsbd;
+ int32_t sgh_mpm_context_hhd;
+ int32_t sgh_mpm_context_hrhd;
+ int32_t sgh_mpm_context_hmd;
+ int32_t sgh_mpm_context_hcd;
+ int32_t sgh_mpm_context_hrud;
+ int32_t sgh_mpm_context_hsmd;
+ int32_t sgh_mpm_context_hscd;
+ int32_t sgh_mpm_context_huad;
+ int32_t sgh_mpm_context_hhhd;
+ int32_t sgh_mpm_context_hrhhd;
+ int32_t sgh_mpm_context_app_proto_detect;
+ int32_t sgh_mpm_context_dnsquery;
+ int32_t sgh_mpm_context_smtp;
+
+ /* the max local id used amongst all sigs */
+ int32_t byte_extract_max_local_id;
+
+ /* id used by every detect engine ctx instance */
+ uint32_t id;
+
+ /** sgh for signatures that match against invalid packets. In those cases
+ * we can't lookup by proto, address, port as we don't have these */
+ struct SigGroupHead_ *decoder_event_sgh;
+
+ /** Store rule file and line so that parsers can use them in errors. */
+ char *rule_file;
+ int rule_line;
+
+ /** list of keywords that need thread local ctxs */
+ DetectEngineThreadKeywordCtxItem *keyword_list;
+ int keyword_id;
+
+ int detect_luajit_instances;
+
+#ifdef PROFILING
+ struct SCProfileDetectCtx_ *profile_ctx;
+ struct SCProfileKeywordDetectCtx_ *profile_keyword_ctx;
+ struct SCProfileKeywordDetectCtx_ *profile_keyword_ctx_per_list[DETECT_SM_LIST_MAX];
+#endif
+
+ char config_prefix[64];
+
+ /** minimal: essentially a stub */
+ int minimal;
+
+ /** how many de_ctx' are referencing this */
+ uint32_t ref_cnt;
+ /** list in master: either active or freelist */
+ struct DetectEngineCtx_ *next;
+
+ /** id of loader thread 'owning' this de_ctx */
+ int loader_id;
+
+} DetectEngineCtx;
+
+/* Engine groups profiles (low, medium, high, custom) */
+enum {
+ ENGINE_PROFILE_UNKNOWN,
+ ENGINE_PROFILE_LOW,
+ ENGINE_PROFILE_MEDIUM,
+ ENGINE_PROFILE_HIGH,
+ ENGINE_PROFILE_CUSTOM,
+ ENGINE_PROFILE_MAX
+};
+
+/* Siggroup mpm context profile */
+enum {
+ ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL,
+ ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE,
+ ENGINE_SGH_MPM_FACTORY_CONTEXT_AUTO
+};
+
+typedef struct HttpReassembledBody_ {
+ uint8_t *buffer;
+ uint32_t buffer_size; /**< size of the buffer itself */
+ uint32_t buffer_len; /**< data len in the buffer */
+ uint64_t offset; /**< data offset */
+} HttpReassembledBody;
+
+typedef struct FiledataReassembledBody_ {
+ uint8_t *buffer;
+ uint32_t buffer_size; /**< size of the buffer itself */
+ uint32_t buffer_len; /**< data len in the buffer */
+ uint64_t offset; /**< data offset */
+} FiledataReassembledBody;
+
+#define DETECT_FILESTORE_MAX 15
+/** \todo review how many we actually need here */
+#define DETECT_SMSG_PMQ_NUM 256
+
+/**
+ * Detection engine thread data.
+ */
+typedef struct DetectEngineThreadCtx_ {
+ /** \note multi-tenant hash lookup code from Detect() *depends*
+ * on this beeing the first member */
+ uint32_t tenant_id;
+
+ /* the thread to which this detection engine thread belongs */
+ ThreadVars *tv;
+
+ SigIntId *non_mpm_id_array;
+ uint32_t non_mpm_id_cnt; // size is cnt * sizeof(uint32_t)
+
+ uint32_t mt_det_ctxs_cnt;
+ struct DetectEngineThreadCtx_ **mt_det_ctxs;
+ HashTable *mt_det_ctxs_hash;
+
+ struct DetectEngineTenantMapping_ *tenant_array;
+ uint32_t tenant_array_size;
+
+ uint32_t (*TenantGetId)(const void *, const Packet *p);
+
+ /* detection engine variables */
+
+ /** offset into the payload of the last match by:
+ * content, pcre, etc */
+ uint32_t buffer_offset;
+ /* used by pcre match function alone */
+ uint32_t pcre_match_start_offset;
+
+ /* counter for the filestore array below -- up here for cache reasons. */
+ uint16_t filestore_cnt;
+
+ /* bool to hint the POSTMATCH list members about the lock status of the
+ * flow. If locked this is TRUE, unlocked or no-flow: FALSE */
+ uint8_t flow_locked;
+
+ HttpReassembledBody *hsbd;
+ uint64_t hsbd_start_tx_id;
+ uint16_t hsbd_buffers_size;
+ uint16_t hsbd_buffers_list_len;
+
+ HttpReassembledBody *hcbd;
+ uint64_t hcbd_start_tx_id;
+ uint16_t hcbd_buffers_size;
+ uint16_t hcbd_buffers_list_len;
+
+ uint8_t **hhd_buffers;
+ uint32_t *hhd_buffers_len;
+ uint16_t hhd_buffers_size;
+ uint16_t hhd_buffers_list_len;
+ uint64_t hhd_start_tx_id;
+
+ FiledataReassembledBody *smtp;
+ uint64_t smtp_start_tx_id;
+ uint16_t smtp_buffers_size;
+ uint16_t smtp_buffers_list_len;
+
+ /** id for alert counter */
+ uint16_t counter_alerts;
+#ifdef PROFILING
+ uint16_t counter_mpm_list;
+ uint16_t counter_nonmpm_list;
+ uint16_t counter_fnonmpm_list;
+ uint16_t counter_match_list;
+#endif
+
+ /* used to discontinue any more matching */
+ uint16_t discontinue_matching;
+ uint16_t flags;
+
+ /* bool: if tx_id is set, this is 1, otherwise 0 */
+ uint16_t tx_id_set;
+ /** ID of the transaction currently being inspected. */
+ uint64_t tx_id;
+
+ SC_ATOMIC_DECLARE(int, so_far_used_by_detect);
+
+ /* holds the current recursion depth on content inspection */
+ int inspection_recursion_counter;
+
+ /** array of signature pointers we're going to inspect in the detection
+ * loop. */
+ Signature **match_array;
+ /** size of the array in items (mem size if * sizeof(Signature *)
+ * Only used during initialization. */
+ uint32_t match_array_len;
+ /** size in use */
+ SigIntId match_array_cnt;
+
+ /** Array of sigs that had a state change */
+ SigIntId de_state_sig_array_len;
+ uint8_t *de_state_sig_array;
+
+ struct SigGroupHead_ *sgh;
+ /** pointer to the current mpm ctx that is stored
+ * in a rule group head -- can be either a content
+ * or uricontent ctx. */
+ MpmThreadCtx mtc; /**< thread ctx for the mpm */
+ MpmThreadCtx mtcu; /**< thread ctx for uricontent mpm */
+ MpmThreadCtx mtcs; /**< thread ctx for stream mpm */
+ PatternMatcherQueue pmq;
+ PatternMatcherQueue smsg_pmq[DETECT_SMSG_PMQ_NUM];
+
+ /** ip only rules ctx */
+ DetectEngineIPOnlyThreadCtx io_ctx;
+
+ /* byte jump values */
+ uint64_t *bj_values;
+
+ /* string to replace */
+ DetectReplaceList *replist;
+ /* flowvars to store in post match function */
+ DetectFlowvarList *flowvarlist;
+
+ /* Array in which the filestore keyword stores file id and tx id. If the
+ * full signature matches, these are processed by a post-match filestore
+ * function to finalize the store. */
+ struct {
+ uint16_t file_id;
+ uint64_t tx_id;
+ } filestore[DETECT_FILESTORE_MAX];
+
+ DetectEngineCtx *de_ctx;
+ /** store for keyword contexts that need a per thread storage because of
+ * thread safety issues */
+ void **keyword_ctxs_array;
+ int keyword_ctxs_size;
+
+#ifdef PROFILING
+ struct SCProfileData_ *rule_perf_data;
+ int rule_perf_data_size;
+ struct SCProfileKeywordData_ *keyword_perf_data;
+ struct SCProfileKeywordData_ *keyword_perf_data_per_list[DETECT_SM_LIST_MAX];
+ int keyword_perf_list; /**< list we're currently inspecting, DETECT_SM_LIST_* */
+#endif
+} DetectEngineThreadCtx;
+
+/** \brief element in sigmatch type table.
+ * \note FileMatch pointer below takes a locked flow, AppLayerMatch an unlocked flow
+ */
+typedef struct SigTableElmt_ {
+ /** Packet match function pointer */
+ int (*Match)(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *);
+
+ /** AppLayer match function pointer */
+ int (*AppLayerMatch)(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, Signature *, SigMatch *);
+
+ /** AppLayer TX match function pointer */
+ int (*AppLayerTxMatch)(ThreadVars *, DetectEngineThreadCtx *, Flow *,
+ uint8_t flags, void *alstate, void *txv,
+ const Signature *, const SigMatchCtx *);
+
+ /** File match function pointer */
+ int (*FileMatch)(ThreadVars *, /**< thread local vars */
+ DetectEngineThreadCtx *,
+ Flow *, /**< *LOCKED* flow */
+ uint8_t flags, File *, Signature *, SigMatch *);
+
+ /** app layer proto from app-layer-protos.h this match applies to */
+ AppProto alproto;
+
+ /** keyword setup function pointer */
+ int (*Setup)(DetectEngineCtx *, Signature *, char *);
+
+ void (*Free)(void *);
+ void (*RegisterTests)(void);
+
+ uint8_t flags;
+ char *name; /**< keyword name alias */
+ char *alias; /**< name alias */
+ char *desc;
+ char *url;
+
+} SigTableElmt;
+
+#define SIG_GROUP_HEAD_MPM_URI (1)
+#define SIG_GROUP_HEAD_MPM_HCBD (1 << 1)
+#define SIG_GROUP_HEAD_MPM_HHD (1 << 2)
+#define SIG_GROUP_HEAD_MPM_HRHD (1 << 3)
+#define SIG_GROUP_HEAD_MPM_HMD (1 << 4)
+#define SIG_GROUP_HEAD_MPM_HCD (1 << 5)
+#define SIG_GROUP_HEAD_MPM_HRUD (1 << 6)
+#define SIG_GROUP_HEAD_MPM_HSBD (1 << 7)
+#define SIG_GROUP_HEAD_MPM_HSMD (1 << 8)
+#define SIG_GROUP_HEAD_MPM_HSCD (1 << 9)
+#define SIG_GROUP_HEAD_MPM_HUAD (1 << 10)
+#define SIG_GROUP_HEAD_MPM_HHHD (1 << 11)
+#define SIG_GROUP_HEAD_MPM_HRHHD (1 << 12)
+
+#define SIG_GROUP_HEAD_MPM_COPY (1 << 13)
+#define SIG_GROUP_HEAD_MPM_URI_COPY (1 << 14)
+#define SIG_GROUP_HEAD_MPM_STREAM_COPY (1 << 15)
+#define SIG_GROUP_HEAD_FREE (1 << 16)
+#define SIG_GROUP_HEAD_MPM_PACKET (1 << 17)
+#define SIG_GROUP_HEAD_MPM_STREAM (1 << 18)
+#define SIG_GROUP_HEAD_REFERENCED (1 << 19) /**< sgh is being referenced by others, don't clear */
+#define SIG_GROUP_HEAD_HAVEFILEMAGIC (1 << 20)
+#define SIG_GROUP_HEAD_HAVEFILEMD5 (1 << 21)
+#define SIG_GROUP_HEAD_HAVEFILESIZE (1 << 22)
+#define SIG_GROUP_HEAD_MPM_DNSQUERY (1 << 23)
+#define SIG_GROUP_HEAD_MPM_FD_SMTP (1 << 24)
+
+typedef struct SigGroupHeadInitData_ {
+ /* list of content containers */
+ uint8_t *content_array;
+ uint32_t content_size;
+ uint8_t *uri_content_array;
+ uint32_t uri_content_size;
+ uint8_t *stream_content_array;
+ uint32_t stream_content_size;
+
+ uint8_t *sig_array; /**< bit array of sig nums (internal id's) */
+ uint32_t sig_size; /**< size in bytes */
+
+ /* port ptr */
+ struct DetectPort_ *port;
+} SigGroupHeadInitData;
+
+typedef struct SignatureNonMpmStore_ {
+ SigIntId id;
+ SignatureMask mask;
+} SignatureNonMpmStore;
+
+/** \brief Container for matching data for a signature group */
+typedef struct SigGroupHead_ {
+ uint32_t flags;
+ /* number of sigs in this head */
+ SigIntId sig_cnt;
+
+ uint16_t mpm_content_maxlen;
+
+ /** array of masks, used to check multiple masks against
+ * a packet using SIMD. */
+#if defined(__SSE3__) || defined(__tile__)
+ SignatureMask *mask_array;
+#endif
+
+ SignatureNonMpmStore *non_mpm_store_array; // size is non_mpm_store_cnt * sizeof(SignatureNonMpmStore)
+ uint32_t non_mpm_store_cnt;
+
+ /* pattern matcher instances */
+ MpmCtx *mpm_proto_other_ctx;
+
+ MpmCtx *mpm_proto_tcp_ctx_ts;
+ MpmCtx *mpm_proto_udp_ctx_ts;
+ MpmCtx *mpm_stream_ctx_ts;
+ MpmCtx *mpm_uri_ctx_ts;
+ MpmCtx *mpm_hcbd_ctx_ts;
+ MpmCtx *mpm_hhd_ctx_ts;
+ MpmCtx *mpm_hrhd_ctx_ts;
+ MpmCtx *mpm_hmd_ctx_ts;
+ MpmCtx *mpm_hcd_ctx_ts;
+ MpmCtx *mpm_hrud_ctx_ts;
+ MpmCtx *mpm_huad_ctx_ts;
+ MpmCtx *mpm_hhhd_ctx_ts;
+ MpmCtx *mpm_hrhhd_ctx_ts;
+ MpmCtx *mpm_dnsquery_ctx_ts;
+ MpmCtx *mpm_smtp_filedata_ctx_ts;
+
+ MpmCtx *mpm_proto_tcp_ctx_tc;
+ MpmCtx *mpm_proto_udp_ctx_tc;
+ MpmCtx *mpm_stream_ctx_tc;
+ MpmCtx *mpm_hsbd_ctx_tc;
+ MpmCtx *mpm_hhd_ctx_tc;
+ MpmCtx *mpm_hrhd_ctx_tc;
+ MpmCtx *mpm_hcd_ctx_tc;
+ MpmCtx *mpm_hsmd_ctx_tc;
+ MpmCtx *mpm_hscd_ctx_tc;
+
+ uint16_t mpm_uricontent_maxlen;
+
+ /** the number of signatures in this sgh that have the filestore keyword
+ * set. */
+ uint16_t filestore_cnt;
+
+ /** Array with sig ptrs... size is sig_cnt * sizeof(Signature *) */
+ Signature **match_array;
+
+ /* ptr to our init data we only use at... init :) */
+ SigGroupHeadInitData *init;
+} SigGroupHead;
+
+/** sigmatch has no options, so the parser shouldn't expect any */
+#define SIGMATCH_NOOPT (1 << 0)
+/** sigmatch is compatible with a ip only rule */
+#define SIGMATCH_IPONLY_COMPAT (1 << 1)
+/** sigmatch is compatible with a decode event only rule */
+#define SIGMATCH_DEONLY_COMPAT (1 << 2)
+/**< Flag to indicate that the signature inspects the packet payload */
+#define SIGMATCH_PAYLOAD (1 << 3)
+/**< Flag to indicate that the signature is not built-in */
+#define SIGMATCH_NOT_BUILT (1 << 4)
+/** sigmatch may have options, so the parser should be ready to
+ * deal with both cases */
+#define SIGMATCH_OPTIONAL_OPT (1 << 5)
+
+enum DetectEngineTenantSelectors
+{
+ TENANT_SELECTOR_UNKNOWN = 0, /**< not set */
+ TENANT_SELECTOR_DIRECT, /**< method provides direct tenant id */
+ TENANT_SELECTOR_VLAN, /**< map vlan to tenant id */
+};
+
+typedef struct DetectEngineTenantMapping_ {
+ uint32_t tenant_id;
+
+ /* traffic id that maps to the tenant id */
+ uint32_t traffic_id;
+
+ struct DetectEngineTenantMapping_ *next;
+} DetectEngineTenantMapping;
+
+typedef struct DetectEngineMasterCtx_ {
+ SCMutex lock;
+
+ /** enable multi tenant mode */
+ int multi_tenant_enabled;
+
+ /** list of active detection engines. This list is used to generate the
+ * threads det_ctx's */
+ DetectEngineCtx *list;
+
+ /** free list, containing detection engines that will be removed but may
+ * still be referenced by det_ctx's. Freed as soon as all references are
+ * gone. */
+ DetectEngineCtx *free_list;
+
+ enum DetectEngineTenantSelectors tenant_selector;
+
+ /** list of tenant mappings. Updated under lock. Used to generate lookup
+ * structures. */
+ DetectEngineTenantMapping *tenant_mapping_list;
+
+} DetectEngineMasterCtx;
+
+/** \brief Signature loader statistics */
+typedef struct SigFileLoaderStat_ {
+ int bad_files;
+ int total_files;
+ int good_sigs_total;
+ int bad_sigs_total;
+} SigFileLoaderStat;
+
+/** Remember to add the options in SignatureIsIPOnly() at detect.c otherwise it wont be part of a signature group */
+
+enum {
+ DETECT_SID,
+ DETECT_PRIORITY,
+ DETECT_REV,
+ DETECT_CLASSTYPE,
+ DETECT_THRESHOLD,
+ DETECT_METADATA,
+ DETECT_REFERENCE,
+ DETECT_TAG,
+ DETECT_MSG,
+ DETECT_CONTENT,
+ DETECT_URICONTENT,
+ DETECT_PCRE,
+ DETECT_ACK,
+ DETECT_SEQ,
+ DETECT_DEPTH,
+ DETECT_DISTANCE,
+ DETECT_WITHIN,
+ DETECT_OFFSET,
+ DETECT_REPLACE,
+ DETECT_NOCASE,
+ DETECT_FAST_PATTERN,
+ DETECT_RAWBYTES,
+ DETECT_BYTETEST,
+ DETECT_BYTEJUMP,
+ DETECT_SAMEIP,
+ DETECT_GEOIP,
+ DETECT_IPPROTO,
+ DETECT_FLOW,
+ DETECT_WINDOW,
+ DETECT_FTPBOUNCE,
+ DETECT_ISDATAAT,
+ DETECT_ID,
+ DETECT_RPC,
+ DETECT_DSIZE,
+ DETECT_FLOWVAR,
+ DETECT_FLOWVAR_POSTMATCH,
+ DETECT_FLOWINT,
+ DETECT_PKTVAR,
+ DETECT_NOALERT,
+ DETECT_FLOWBITS,
+ DETECT_HOSTBITS,
+ DETECT_IPV4_CSUM,
+ DETECT_TCPV4_CSUM,
+ DETECT_TCPV6_CSUM,
+ DETECT_UDPV4_CSUM,
+ DETECT_UDPV6_CSUM,
+ DETECT_ICMPV4_CSUM,
+ DETECT_ICMPV6_CSUM,
+ DETECT_STREAM_SIZE,
+ DETECT_TTL,
+ DETECT_ITYPE,
+ DETECT_ICODE,
+ DETECT_TOS,
+ DETECT_ICMP_ID,
+ DETECT_ICMP_SEQ,
+ DETECT_DETECTION_FILTER,
+
+ DETECT_DECODE_EVENT,
+ DETECT_IPOPTS,
+ DETECT_FLAGS,
+ DETECT_FRAGBITS,
+ DETECT_FRAGOFFSET,
+ DETECT_GID,
+ DETECT_MARK,
+
+ DETECT_AL_TLS_VERSION,
+ DETECT_AL_TLS_SUBJECT,
+ DETECT_AL_TLS_ISSUERDN,
+ DETECT_AL_TLS_FINGERPRINT,
+ DETECT_AL_TLS_STORE,
+
+ DETECT_AL_HTTP_COOKIE,
+ DETECT_AL_HTTP_METHOD,
+ DETECT_AL_URILEN,
+ DETECT_AL_HTTP_CLIENT_BODY,
+ DETECT_AL_HTTP_SERVER_BODY,
+ DETECT_AL_HTTP_HEADER,
+ DETECT_AL_HTTP_RAW_HEADER,
+ DETECT_AL_HTTP_URI,
+ DETECT_AL_HTTP_RAW_URI,
+ DETECT_AL_HTTP_STAT_MSG,
+ DETECT_AL_HTTP_STAT_CODE,
+ DETECT_AL_HTTP_USER_AGENT,
+ DETECT_AL_HTTP_HOST,
+ DETECT_AL_HTTP_RAW_HOST,
+ DETECT_AL_SSH_PROTOVERSION,
+ DETECT_AL_SSH_SOFTWAREVERSION,
+ DETECT_AL_SSL_VERSION,
+ DETECT_AL_SSL_STATE,
+ DETECT_BYTE_EXTRACT,
+ DETECT_FILE_DATA,
+ DETECT_PKT_DATA,
+ DETECT_AL_APP_LAYER_EVENT,
+ DETECT_AL_APP_LAYER_PROTOCOL,
+
+ DETECT_DCE_IFACE,
+ DETECT_DCE_OPNUM,
+ DETECT_DCE_STUB_DATA,
+
+ DETECT_ASN1,
+
+ DETECT_ENGINE_EVENT,
+ DETECT_STREAM_EVENT,
+
+ DETECT_FILENAME,
+ DETECT_FILEEXT,
+ DETECT_FILESTORE,
+ DETECT_FILEMAGIC,
+ DETECT_FILEMD5,
+ DETECT_FILESIZE,
+
+ DETECT_L3PROTO,
+ DETECT_LUA,
+ DETECT_IPREP,
+
+ DETECT_AL_DNS_QUERY,
+ DETECT_AL_MODBUS,
+
+ DETECT_XBITS,
+
+ DETECT_TEMPLATE,
+
+ /* make sure this stays last */
+ DETECT_TBLSIZE,
+};
+
+/* Table with all SigMatch registrations */
+SigTableElmt sigmatch_table[DETECT_TBLSIZE];
+
+/* detection api */
+SigMatch *SigMatchAlloc(void);
+Signature *SigFindSignatureBySidGid(DetectEngineCtx *, uint32_t, uint32_t);
+void SigMatchSignaturesBuildMatchArray(DetectEngineThreadCtx *,
+ Packet *, SignatureMask,
+ uint16_t);
+void SigMatchFree(SigMatch *sm);
+void SigCleanSignatures(DetectEngineCtx *);
+
+void SigTableRegisterTests(void);
+void SigRegisterTests(void);
+void DetectSimdRegisterTests(void);
+void TmModuleDetectRegister (void);
+
+int SigGroupBuild(DetectEngineCtx *);
+int SigGroupCleanup (DetectEngineCtx *de_ctx);
+void SigAddressPrepareBidirectionals (DetectEngineCtx *);
+
+char *DetectLoadCompleteSigPath(const DetectEngineCtx *, char *sig_file);
+int SigLoadSignatures (DetectEngineCtx *, char *, int);
+void SigTableList(const char *keyword);
+void SigTableSetup(void);
+int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, Packet *p);
+
+int SignatureIsIPOnly(DetectEngineCtx *de_ctx, Signature *s);
+SigGroupHead *SigMatchSignaturesGetSgh(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p);
+
+Signature *DetectGetTagSignature(void);
+
+int SignatureIsFilestoring(Signature *);
+int SignatureIsFilemagicInspecting(Signature *);
+int SignatureIsFileMd5Inspecting(Signature *);
+int SignatureIsFilesizeInspecting(Signature *);
+
+int DetectRegisterThreadCtxFuncs(DetectEngineCtx *, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int);
+void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *, int);
+
+int SigMatchSignaturesRunPostMatch(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p,
+ Signature *s);
+void DetectSignatureApplyActions(Packet *p, const Signature *s);
+
+#endif /* __DETECT_H__ */
+
diff --git a/framework/src/suricata/src/flow-bit.c b/framework/src/suricata/src/flow-bit.c
new file mode 100644
index 00000000..2e52b9ef
--- /dev/null
+++ b/framework/src/suricata/src/flow-bit.c
@@ -0,0 +1,483 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements per flow bits. Actually, not a bit,
+ * but called that way because of Snort's flowbits.
+ * It's a binary storage.
+ *
+ * \todo move away from a linked list implementation
+ * \todo use different datatypes, such as string, int, etc.
+ * \todo have more than one instance of the same var, and be able to match on a
+ * specific one, or one all at a time. So if a certain capture matches
+ * multiple times, we can operate on all of them.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "flow-bit.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "flow-private.h"
+#include "detect.h"
+#include "util-var.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+/* get the flowbit with idx from the flow */
+static FlowBit *FlowBitGet(Flow *f, uint16_t idx)
+{
+ GenericVar *gv = f->flowvar;
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
+ return (FlowBit *)gv;
+ }
+ }
+
+ return NULL;
+}
+
+/* add a flowbit to the flow */
+static void FlowBitAdd(Flow *f, uint16_t idx)
+{
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb == NULL) {
+ fb = SCMalloc(sizeof(FlowBit));
+ if (unlikely(fb == NULL))
+ return;
+
+ fb->type = DETECT_FLOWBITS;
+ fb->idx = idx;
+ fb->next = NULL;
+ GenericVarAppend(&f->flowvar, (GenericVar *)fb);
+
+ //printf("FlowBitAdd: adding flowbit with idx %" PRIu32 "\n", idx);
+#ifdef FLOWBITS_STATS
+ SCMutexLock(&flowbits_mutex);
+ flowbits_added++;
+ flowbits_memuse += sizeof(FlowBit);
+ if (flowbits_memuse > flowbits_memuse_max)
+ flowbits_memuse_max = flowbits_memuse;
+ SCMutexUnlock(&flowbits_mutex);
+#endif /* FLOWBITS_STATS */
+ }
+}
+
+static void FlowBitRemove(Flow *f, uint16_t idx)
+{
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb == NULL)
+ return;
+
+ GenericVarRemove(&f->flowvar, (GenericVar *)fb);
+
+ //printf("FlowBitRemove: remove flowbit with idx %" PRIu32 "\n", idx);
+#ifdef FLOWBITS_STATS
+ SCMutexLock(&flowbits_mutex);
+ flowbits_removed++;
+ if (flowbits_memuse >= sizeof(FlowBit))
+ flowbits_memuse -= sizeof(FlowBit);
+ else {
+ printf("ERROR: flowbits memory usage going below 0!\n");
+ flowbits_memuse = 0;
+ }
+ SCMutexUnlock(&flowbits_mutex);
+#endif /* FLOWBITS_STATS */
+}
+
+void FlowBitSetNoLock(Flow *f, uint16_t idx)
+{
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb == NULL) {
+ FlowBitAdd(f, idx);
+ }
+}
+
+void FlowBitSet(Flow *f, uint16_t idx)
+{
+ FLOWLOCK_WRLOCK(f);
+ FlowBitSetNoLock(f, idx);
+ FLOWLOCK_UNLOCK(f);
+}
+
+void FlowBitUnsetNoLock(Flow *f, uint16_t idx)
+{
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb != NULL) {
+ FlowBitRemove(f, idx);
+ }
+}
+
+void FlowBitUnset(Flow *f, uint16_t idx)
+{
+ FLOWLOCK_WRLOCK(f);
+ FlowBitUnsetNoLock(f, idx);
+ FLOWLOCK_UNLOCK(f);
+}
+
+void FlowBitToggleNoLock(Flow *f, uint16_t idx)
+{
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb != NULL) {
+ FlowBitRemove(f, idx);
+ } else {
+ FlowBitAdd(f, idx);
+ }
+}
+
+void FlowBitToggle(Flow *f, uint16_t idx)
+{
+ FLOWLOCK_WRLOCK(f);
+ FlowBitToggleNoLock(f, idx);
+ FLOWLOCK_UNLOCK(f);
+}
+
+int FlowBitIsset(Flow *f, uint16_t idx)
+{
+ int r = 0;
+ FLOWLOCK_RDLOCK(f);
+
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb != NULL) {
+ r = 1;
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ return r;
+}
+
+int FlowBitIsnotset(Flow *f, uint16_t idx)
+{
+ int r = 0;
+ FLOWLOCK_RDLOCK(f);
+
+ FlowBit *fb = FlowBitGet(f, idx);
+ if (fb == NULL) {
+ r = 1;
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ return r;
+}
+
+void FlowBitFree(FlowBit *fb)
+{
+ if (fb == NULL)
+ return;
+
+ SCFree(fb);
+
+#ifdef FLOWBITS_STATS
+ SCMutexLock(&flowbits_mutex);
+ flowbits_removed++;
+ if (flowbits_memuse >= sizeof(FlowBit))
+ flowbits_memuse -= sizeof(FlowBit);
+ else {
+ printf("ERROR: flowbits memory usage going below 0!\n");
+ flowbits_memuse = 0;
+ }
+ SCMutexUnlock(&flowbits_mutex);
+#endif /* FLOWBITS_STATS */
+}
+
+
+/* TESTS */
+#ifdef UNITTESTS
+static int FlowBitTest01 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+
+ FlowBit *fb = FlowBitGet(&f,0);
+ if (fb != NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest02 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBit *fb = FlowBitGet(&f,0);
+ if (fb == NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest03 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+
+ FlowBit *fb = FlowBitGet(&f,0);
+ if (fb == NULL) {
+ printf("fb == NULL although it was just added: ");
+ goto end;
+ }
+
+ FlowBitRemove(&f, 0);
+
+ fb = FlowBitGet(&f,0);
+ if (fb != NULL) {
+ printf("fb != NULL although it was just removed: ");
+ goto end;
+ } else {
+ ret = 1;
+ }
+end:
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest04 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,0);
+ if (fb != NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest05 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,1);
+ if (fb != NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest06 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,2);
+ if (fb != NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest07 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,3);
+ if (fb != NULL)
+ ret = 1;
+
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest08 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,0);
+ if (fb == NULL)
+ goto end;
+
+ FlowBitRemove(&f,0);
+
+ fb = FlowBitGet(&f,0);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest09 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,1);
+ if (fb == NULL)
+ goto end;
+
+ FlowBitRemove(&f,1);
+
+ fb = FlowBitGet(&f,1);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest10 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,2);
+ if (fb == NULL)
+ goto end;
+
+ FlowBitRemove(&f,2);
+
+ fb = FlowBitGet(&f,2);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+static int FlowBitTest11 (void)
+{
+ int ret = 0;
+
+ Flow f;
+ memset(&f, 0, sizeof(Flow));
+
+ FlowBitAdd(&f, 0);
+ FlowBitAdd(&f, 1);
+ FlowBitAdd(&f, 2);
+ FlowBitAdd(&f, 3);
+
+ FlowBit *fb = FlowBitGet(&f,3);
+ if (fb == NULL)
+ goto end;
+
+ FlowBitRemove(&f,3);
+
+ fb = FlowBitGet(&f,3);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ GenericVarFree(f.flowvar);
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void FlowBitRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlowBitTest01", FlowBitTest01, 1);
+ UtRegisterTest("FlowBitTest02", FlowBitTest02, 1);
+ UtRegisterTest("FlowBitTest03", FlowBitTest03, 1);
+ UtRegisterTest("FlowBitTest04", FlowBitTest04, 1);
+ UtRegisterTest("FlowBitTest05", FlowBitTest05, 1);
+ UtRegisterTest("FlowBitTest06", FlowBitTest06, 1);
+ UtRegisterTest("FlowBitTest07", FlowBitTest07, 1);
+ UtRegisterTest("FlowBitTest08", FlowBitTest08, 1);
+ UtRegisterTest("FlowBitTest09", FlowBitTest09, 1);
+ UtRegisterTest("FlowBitTest10", FlowBitTest10, 1);
+ UtRegisterTest("FlowBitTest11", FlowBitTest11, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/flow-bit.h b/framework/src/suricata/src/flow-bit.h
new file mode 100644
index 00000000..1b966a00
--- /dev/null
+++ b/framework/src/suricata/src/flow-bit.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_BIT_H__
+#define __FLOW_BIT_H__
+
+#include "flow.h"
+#include "util-var.h"
+
+typedef struct FlowBit_ {
+ uint8_t type; /* type, DETECT_FLOWBITS in this case */
+ uint16_t idx; /* name idx */
+ GenericVar *next; /* right now just implement this as a list,
+ * in the long run we have think of something
+ * faster. */
+} FlowBit;
+
+void FlowBitFree(FlowBit *);
+void FlowBitRegisterTests(void);
+
+void FlowBitSetNoLock(Flow *, uint16_t);
+void FlowBitSet(Flow *, uint16_t);
+void FlowBitUnsetNoLock(Flow *, uint16_t);
+void FlowBitUnset(Flow *, uint16_t);
+void FlowBitToggleNoLock(Flow *, uint16_t);
+void FlowBitToggle(Flow *, uint16_t);
+int FlowBitIsset(Flow *, uint16_t);
+int FlowBitIsnotset(Flow *, uint16_t);
+#endif /* __FLOW_BIT_H__ */
+
diff --git a/framework/src/suricata/src/flow-hash.c b/framework/src/suricata/src/flow-hash.c
new file mode 100644
index 00000000..7a151199
--- /dev/null
+++ b/framework/src/suricata/src/flow-hash.c
@@ -0,0 +1,851 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Flow Hashing functions.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+
+#include "decode.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-hash.h"
+#include "flow-util.h"
+#include "flow-private.h"
+#include "flow-manager.h"
+#include "app-layer-parser.h"
+
+#include "util-time.h"
+#include "util-debug.h"
+
+#include "util-hash-lookup3.h"
+
+#include "conf.h"
+#include "output.h"
+#include "output-flow.h"
+
+#define FLOW_DEFAULT_FLOW_PRUNE 5
+
+SC_ATOMIC_EXTERN(unsigned int, flow_prune_idx);
+SC_ATOMIC_EXTERN(unsigned int, flow_flags);
+
+static Flow *FlowGetUsedFlow(ThreadVars *tv, DecodeThreadVars *dtv);
+static int handle_tcp_reuse = 1;
+
+#ifdef FLOW_DEBUG_STATS
+#define FLOW_DEBUG_STATS_PROTO_ALL 0
+#define FLOW_DEBUG_STATS_PROTO_TCP 1
+#define FLOW_DEBUG_STATS_PROTO_UDP 2
+#define FLOW_DEBUG_STATS_PROTO_ICMP 3
+#define FLOW_DEBUG_STATS_PROTO_OTHER 4
+
+static uint64_t flow_hash_count[5] = { 0, 0, 0, 0, 0 }; /* how often are we looking for a hash */
+static uint64_t flow_hash_loop_count[5] = { 0, 0, 0, 0, 0 }; /* how often do we loop through a hash bucket */
+static FILE *flow_hash_count_fp = NULL;
+static SCSpinlock flow_hash_count_lock;
+
+#define FlowHashCountUpdate do { \
+ SCSpinLock(&flow_hash_count_lock); \
+ flow_hash_count[FLOW_DEBUG_STATS_PROTO_ALL]++; \
+ flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ALL] += _flow_hash_counter; \
+ if (f != NULL) { \
+ if (p->proto == IPPROTO_TCP) { \
+ flow_hash_count[FLOW_DEBUG_STATS_PROTO_TCP]++; \
+ flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_TCP] += _flow_hash_counter; \
+ } else if (p->proto == IPPROTO_UDP) {\
+ flow_hash_count[FLOW_DEBUG_STATS_PROTO_UDP]++; \
+ flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_UDP] += _flow_hash_counter; \
+ } else if (p->proto == IPPROTO_ICMP) {\
+ flow_hash_count[FLOW_DEBUG_STATS_PROTO_ICMP]++; \
+ flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ICMP] += _flow_hash_counter; \
+ } else {\
+ flow_hash_count[FLOW_DEBUG_STATS_PROTO_OTHER]++; \
+ flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_OTHER] += _flow_hash_counter; \
+ } \
+ } \
+ SCSpinUnlock(&flow_hash_count_lock); \
+} while(0);
+
+#define FlowHashCountInit uint64_t _flow_hash_counter = 0
+#define FlowHashCountIncr _flow_hash_counter++;
+
+void FlowHashDebugInit(void)
+{
+#ifdef FLOW_DEBUG_STATS
+ SCSpinInit(&flow_hash_count_lock, 0);
+#endif
+ flow_hash_count_fp = fopen("flow-debug.log", "w+");
+ if (flow_hash_count_fp != NULL) {
+ fprintf(flow_hash_count_fp, "ts,all,tcp,udp,icmp,other\n");
+ }
+}
+
+void FlowHashDebugPrint(uint32_t ts)
+{
+#ifdef FLOW_DEBUG_STATS
+ if (flow_hash_count_fp == NULL)
+ return;
+
+ float avg_all = 0, avg_tcp = 0, avg_udp = 0, avg_icmp = 0, avg_other = 0;
+ SCSpinLock(&flow_hash_count_lock);
+ if (flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ALL] != 0)
+ avg_all = (float)(flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ALL]/(float)(flow_hash_count[FLOW_DEBUG_STATS_PROTO_ALL]));
+ if (flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_TCP] != 0)
+ avg_tcp = (float)(flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_TCP]/(float)(flow_hash_count[FLOW_DEBUG_STATS_PROTO_TCP]));
+ if (flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_UDP] != 0)
+ avg_udp = (float)(flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_UDP]/(float)(flow_hash_count[FLOW_DEBUG_STATS_PROTO_UDP]));
+ if (flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ICMP] != 0)
+ avg_icmp= (float)(flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_ICMP]/(float)(flow_hash_count[FLOW_DEBUG_STATS_PROTO_ICMP]));
+ if (flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_OTHER] != 0)
+ avg_other= (float)(flow_hash_loop_count[FLOW_DEBUG_STATS_PROTO_OTHER]/(float)(flow_hash_count[FLOW_DEBUG_STATS_PROTO_OTHER]));
+ fprintf(flow_hash_count_fp, "%"PRIu32",%02.3f,%02.3f,%02.3f,%02.3f,%02.3f\n", ts, avg_all, avg_tcp, avg_udp, avg_icmp, avg_other);
+ fflush(flow_hash_count_fp);
+ memset(&flow_hash_count, 0, sizeof(flow_hash_count));
+ memset(&flow_hash_loop_count, 0, sizeof(flow_hash_loop_count));
+ SCSpinUnlock(&flow_hash_count_lock);
+#endif
+}
+
+void FlowHashDebugDeinit(void)
+{
+#ifdef FLOW_DEBUG_STATS
+ struct timeval ts;
+ memset(&ts, 0, sizeof(ts));
+ TimeGet(&ts);
+ FlowHashDebugPrint((uint32_t)ts.tv_sec);
+ if (flow_hash_count_fp != NULL)
+ fclose(flow_hash_count_fp);
+ SCSpinDestroy(&flow_hash_count_lock);
+#endif
+}
+
+#else
+
+#define FlowHashCountUpdate
+#define FlowHashCountInit
+#define FlowHashCountIncr
+
+#endif /* FLOW_DEBUG_STATS */
+
+void FlowDisableTcpReuseHandling(void)
+{
+ handle_tcp_reuse = 0;
+}
+
+/** \brief compare two raw ipv6 addrs
+ *
+ * \note we don't care about the real ipv6 ip's, this is just
+ * to consistently fill the FlowHashKey6 struct, without all
+ * the ntohl calls.
+ *
+ * \warning do not use elsewhere unless you know what you're doing.
+ * detect-engine-address-ipv6.c's AddressIPv6GtU32 is likely
+ * what you are looking for.
+ */
+static inline int FlowHashRawAddressIPv6GtU32(const uint32_t *a, const uint32_t *b)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (a[i] > b[i])
+ return 1;
+ if (a[i] < b[i])
+ break;
+ }
+
+ return 0;
+}
+
+typedef struct FlowHashKey4_ {
+ union {
+ struct {
+ uint32_t src, dst;
+ uint16_t sp, dp;
+ uint16_t proto; /**< u16 so proto and recur add up to u32 */
+ uint16_t recur; /**< u16 so proto and recur add up to u32 */
+ uint16_t vlan_id[2];
+ };
+ const uint32_t u32[5];
+ };
+} FlowHashKey4;
+
+typedef struct FlowHashKey6_ {
+ union {
+ struct {
+ uint32_t src[4], dst[4];
+ uint16_t sp, dp;
+ uint16_t proto; /**< u16 so proto and recur add up to u32 */
+ uint16_t recur; /**< u16 so proto and recur add up to u32 */
+ uint16_t vlan_id[2];
+ };
+ const uint32_t u32[11];
+ };
+} FlowHashKey6;
+
+/* calculate the hash key for this packet
+ *
+ * we're using:
+ * hash_rand -- set at init time
+ * source port
+ * destination port
+ * source address
+ * destination address
+ * recursion level -- for tunnels, make sure different tunnel layers can
+ * never get mixed up.
+ *
+ * For ICMP we only consider UNREACHABLE errors atm.
+ */
+static inline uint32_t FlowGetKey(const Packet *p)
+{
+ uint32_t key;
+
+ if (p->ip4h != NULL) {
+ if (p->tcph != NULL || p->udph != NULL) {
+ FlowHashKey4 fhk;
+ if (p->src.addr_data32[0] > p->dst.addr_data32[0]) {
+ fhk.src = p->src.addr_data32[0];
+ fhk.dst = p->dst.addr_data32[0];
+ } else {
+ fhk.src = p->dst.addr_data32[0];
+ fhk.dst = p->src.addr_data32[0];
+ }
+ if (p->sp > p->dp) {
+ fhk.sp = p->sp;
+ fhk.dp = p->dp;
+ } else {
+ fhk.sp = p->dp;
+ fhk.dp = p->sp;
+ }
+ fhk.proto = (uint16_t)p->proto;
+ fhk.recur = (uint16_t)p->recursion_level;
+ fhk.vlan_id[0] = p->vlan_id[0];
+ fhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(fhk.u32, 5, flow_config.hash_rand);
+ key = hash % flow_config.hash_size;
+
+ } else if (ICMPV4_DEST_UNREACH_IS_VALID(p)) {
+ uint32_t psrc = IPV4_GET_RAW_IPSRC_U32(ICMPV4_GET_EMB_IPV4(p));
+ uint32_t pdst = IPV4_GET_RAW_IPDST_U32(ICMPV4_GET_EMB_IPV4(p));
+ FlowHashKey4 fhk;
+ if (psrc > pdst) {
+ fhk.src = psrc;
+ fhk.dst = pdst;
+ } else {
+ fhk.src = pdst;
+ fhk.dst = psrc;
+ }
+ if (p->icmpv4vars.emb_sport > p->icmpv4vars.emb_dport) {
+ fhk.sp = p->icmpv4vars.emb_sport;
+ fhk.dp = p->icmpv4vars.emb_dport;
+ } else {
+ fhk.sp = p->icmpv4vars.emb_dport;
+ fhk.dp = p->icmpv4vars.emb_sport;
+ }
+ fhk.proto = (uint16_t)ICMPV4_GET_EMB_PROTO(p);
+ fhk.recur = (uint16_t)p->recursion_level;
+ fhk.vlan_id[0] = p->vlan_id[0];
+ fhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(fhk.u32, 5, flow_config.hash_rand);
+ key = hash % flow_config.hash_size;
+
+ } else {
+ FlowHashKey4 fhk;
+ if (p->src.addr_data32[0] > p->dst.addr_data32[0]) {
+ fhk.src = p->src.addr_data32[0];
+ fhk.dst = p->dst.addr_data32[0];
+ } else {
+ fhk.src = p->dst.addr_data32[0];
+ fhk.dst = p->src.addr_data32[0];
+ }
+ fhk.sp = 0xfeed;
+ fhk.dp = 0xbeef;
+ fhk.proto = (uint16_t)p->proto;
+ fhk.recur = (uint16_t)p->recursion_level;
+ fhk.vlan_id[0] = p->vlan_id[0];
+ fhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(fhk.u32, 5, flow_config.hash_rand);
+ key = hash % flow_config.hash_size;
+ }
+ } else if (p->ip6h != NULL) {
+ FlowHashKey6 fhk;
+ if (FlowHashRawAddressIPv6GtU32(p->src.addr_data32, p->dst.addr_data32)) {
+ fhk.src[0] = p->src.addr_data32[0];
+ fhk.src[1] = p->src.addr_data32[1];
+ fhk.src[2] = p->src.addr_data32[2];
+ fhk.src[3] = p->src.addr_data32[3];
+ fhk.dst[0] = p->dst.addr_data32[0];
+ fhk.dst[1] = p->dst.addr_data32[1];
+ fhk.dst[2] = p->dst.addr_data32[2];
+ fhk.dst[3] = p->dst.addr_data32[3];
+ } else {
+ fhk.src[0] = p->dst.addr_data32[0];
+ fhk.src[1] = p->dst.addr_data32[1];
+ fhk.src[2] = p->dst.addr_data32[2];
+ fhk.src[3] = p->dst.addr_data32[3];
+ fhk.dst[0] = p->src.addr_data32[0];
+ fhk.dst[1] = p->src.addr_data32[1];
+ fhk.dst[2] = p->src.addr_data32[2];
+ fhk.dst[3] = p->src.addr_data32[3];
+ }
+ if (p->sp > p->dp) {
+ fhk.sp = p->sp;
+ fhk.dp = p->dp;
+ } else {
+ fhk.sp = p->dp;
+ fhk.dp = p->sp;
+ }
+ fhk.proto = (uint16_t)p->proto;
+ fhk.recur = (uint16_t)p->recursion_level;
+ fhk.vlan_id[0] = p->vlan_id[0];
+ fhk.vlan_id[1] = p->vlan_id[1];
+
+ uint32_t hash = hashword(fhk.u32, 11, flow_config.hash_rand);
+ key = hash % flow_config.hash_size;
+ } else
+ key = 0;
+
+ return key;
+}
+
+/* Since two or more flows can have the same hash key, we need to compare
+ * the flow with the current flow key. */
+#define CMP_FLOW(f1,f2) \
+ (((CMP_ADDR(&(f1)->src, &(f2)->src) && \
+ CMP_ADDR(&(f1)->dst, &(f2)->dst) && \
+ CMP_PORT((f1)->sp, (f2)->sp) && CMP_PORT((f1)->dp, (f2)->dp)) || \
+ (CMP_ADDR(&(f1)->src, &(f2)->dst) && \
+ CMP_ADDR(&(f1)->dst, &(f2)->src) && \
+ CMP_PORT((f1)->sp, (f2)->dp) && CMP_PORT((f1)->dp, (f2)->sp))) && \
+ (f1)->proto == (f2)->proto && \
+ (f1)->recursion_level == (f2)->recursion_level && \
+ (f1)->vlan_id[0] == (f2)->vlan_id[0] && \
+ (f1)->vlan_id[1] == (f2)->vlan_id[1])
+
+/**
+ * \brief See if a ICMP packet belongs to a flow by comparing the embedded
+ * packet in the ICMP error packet to the flow.
+ *
+ * \param f flow
+ * \param p ICMP packet
+ *
+ * \retval 1 match
+ * \retval 0 no match
+ */
+static inline int FlowCompareICMPv4(Flow *f, const Packet *p)
+{
+ if (ICMPV4_DEST_UNREACH_IS_VALID(p)) {
+ /* first check the direction of the flow, in other words, the client ->
+ * server direction as it's most likely the ICMP error will be a
+ * response to the clients traffic */
+ if ((f->src.addr_data32[0] == IPV4_GET_RAW_IPSRC_U32( ICMPV4_GET_EMB_IPV4(p) )) &&
+ (f->dst.addr_data32[0] == IPV4_GET_RAW_IPDST_U32( ICMPV4_GET_EMB_IPV4(p) )) &&
+ f->sp == p->icmpv4vars.emb_sport &&
+ f->dp == p->icmpv4vars.emb_dport &&
+ f->proto == ICMPV4_GET_EMB_PROTO(p) &&
+ f->recursion_level == p->recursion_level &&
+ f->vlan_id[0] == p->vlan_id[0] &&
+ f->vlan_id[1] == p->vlan_id[1])
+ {
+ return 1;
+
+ /* check the less likely case where the ICMP error was a response to
+ * a packet from the server. */
+ } else if ((f->dst.addr_data32[0] == IPV4_GET_RAW_IPSRC_U32( ICMPV4_GET_EMB_IPV4(p) )) &&
+ (f->src.addr_data32[0] == IPV4_GET_RAW_IPDST_U32( ICMPV4_GET_EMB_IPV4(p) )) &&
+ f->dp == p->icmpv4vars.emb_sport &&
+ f->sp == p->icmpv4vars.emb_dport &&
+ f->proto == ICMPV4_GET_EMB_PROTO(p) &&
+ f->recursion_level == p->recursion_level &&
+ f->vlan_id[0] == p->vlan_id[0] &&
+ f->vlan_id[1] == p->vlan_id[1])
+ {
+ return 1;
+ }
+
+ /* no match, fall through */
+ } else {
+ /* just treat ICMP as a normal proto for now */
+ return CMP_FLOW(f, p);
+ }
+
+ return 0;
+}
+
+int TcpSessionPacketSsnReuse(const Packet *p, const Flow *f, void *tcp_ssn);
+
+static inline int FlowCompare(Flow *f, const Packet *p)
+{
+ if (p->proto == IPPROTO_ICMP) {
+ return FlowCompareICMPv4(f, p);
+ } else if (p->proto == IPPROTO_TCP) {
+ if (CMP_FLOW(f, p) == 0)
+ return 0;
+
+ /* if this session is 'reused', we don't return it anymore,
+ * so return false on the compare */
+ if (f->flags & FLOW_TCP_REUSED)
+ return 0;
+
+ if (handle_tcp_reuse == 1) {
+ /* lets see if we need to consider the existing session reuse */
+ if (unlikely(TcpSessionPacketSsnReuse(p, f, f->protoctx) == 1)) {
+ /* okay, we need to setup a new flow for this packet.
+ * Flag the flow that it's been replaced by a new one */
+ f->flags |= FLOW_TCP_REUSED;
+ SCLogDebug("flow obsolete: TCP reuse will use a new flow "
+ "starting with packet %"PRIu64, p->pcap_cnt);
+ return 0;
+ }
+ }
+ return 1;
+ } else {
+ return CMP_FLOW(f, p);
+ }
+}
+
+/**
+ * \brief Check if we should create a flow based on a packet
+ *
+ * We use this check to filter out flow creation based on:
+ * - ICMP error messages
+ *
+ * \param p packet
+ * \retval 1 true
+ * \retval 0 false
+ */
+static inline int FlowCreateCheck(const Packet *p)
+{
+ if (PKT_IS_ICMPV4(p)) {
+ if (ICMPV4_IS_ERROR_MSG(p)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Get a new flow
+ *
+ * Get a new flow. We're checking memcap first and will try to make room
+ * if the memcap is reached.
+ *
+ * \param tv thread vars
+ * \param dtv decode thread vars (for flow log api thread data)
+ *
+ * \retval f *LOCKED* flow on succes, NULL on error.
+ */
+static Flow *FlowGetNew(ThreadVars *tv, DecodeThreadVars *dtv, const Packet *p)
+{
+ Flow *f = NULL;
+
+ if (FlowCreateCheck(p) == 0) {
+ return NULL;
+ }
+
+ /* get a flow from the spare queue */
+ f = FlowDequeue(&flow_spare_q);
+ if (f == NULL) {
+ /* If we reached the max memcap, we get a used flow */
+ if (!(FLOW_CHECK_MEMCAP(sizeof(Flow)))) {
+ /* declare state of emergency */
+ if (!(SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)) {
+ SC_ATOMIC_OR(flow_flags, FLOW_EMERGENCY);
+
+ /* under high load, waking up the flow mgr each time leads
+ * to high cpu usage. Flows are not timed out much faster if
+ * we check a 1000 times a second. */
+ FlowWakeupFlowManagerThread();
+ }
+
+ f = FlowGetUsedFlow(tv, dtv);
+ if (f == NULL) {
+ /* very rare, but we can fail. Just giving up */
+ return NULL;
+ }
+
+ /* freed a flow, but it's unlocked */
+ } else {
+ /* now see if we can alloc a new flow */
+ f = FlowAlloc();
+ if (f == NULL) {
+ return NULL;
+ }
+
+ /* flow is initialized but *unlocked* */
+ }
+ } else {
+ /* flow has been recycled before it went into the spare queue */
+
+ /* flow is initialized (recylced) but *unlocked* */
+ }
+
+ FLOWLOCK_WRLOCK(f);
+ return f;
+}
+
+Flow *FlowGetFlowFromHashByPacket(const Packet *p)
+{
+ Flow *f = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = FlowGetKey(p);
+ /* get our hash bucket and lock it */
+ FlowBucket *fb = &flow_hash[key];
+ FBLOCK_LOCK(fb);
+
+ SCLogDebug("fb %p fb->head %p", fb, fb->head);
+
+ f = FlowGetNew(NULL, NULL, p);
+ if (f != NULL) {
+ /* flow is locked */
+ if (fb->head == NULL) {
+ fb->head = f;
+ fb->tail = f;
+ } else {
+ f->hprev = fb->tail;
+ fb->tail->hnext = f;
+ fb->tail = f;
+ }
+
+ /* got one, now lock, initialize and return */
+ FlowInit(f, p);
+ f->fb = fb;
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ }
+ FBLOCK_UNLOCK(fb);
+ return f;
+}
+
+/** \brief Lookup flow based on packet
+ *
+ * Find the flow belonging to this packet. If not found, no new flow
+ * is set up.
+ *
+ * \param p packet to lookup the flow for
+ *
+ * \retval f flow or NULL if not found
+ */
+Flow *FlowLookupFlowFromHash(const Packet *p)
+{
+ Flow *f = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = FlowGetKey(p);
+ /* get our hash bucket and lock it */
+ FlowBucket *fb = &flow_hash[key];
+ FBLOCK_LOCK(fb);
+
+ SCLogDebug("fb %p fb->head %p", fb, fb->head);
+
+ /* see if the bucket already has a flow */
+ if (fb->head == NULL) {
+ FBLOCK_UNLOCK(fb);
+ return NULL;
+ }
+
+ /* ok, we have a flow in the bucket. Let's find out if it is our flow */
+ f = fb->head;
+
+ /* see if this is the flow we are looking for */
+ if (FlowCompare(f, p) == 0) {
+ while (f) {
+ FlowHashCountIncr;
+
+ f = f->hnext;
+
+ if (f == NULL) {
+ FBLOCK_UNLOCK(fb);
+ return NULL;
+ }
+
+ if (FlowCompare(f, p) != 0) {
+ /* we found our flow, lets put it on top of the
+ * hash list -- this rewards active flows */
+ if (f->hnext) {
+ f->hnext->hprev = f->hprev;
+ }
+ if (f->hprev) {
+ f->hprev->hnext = f->hnext;
+ }
+ if (f == fb->tail) {
+ fb->tail = f->hprev;
+ }
+
+ f->hnext = fb->head;
+ f->hprev = NULL;
+ fb->head->hprev = f;
+ fb->head = f;
+
+ /* found our flow, lock & return */
+ FLOWLOCK_WRLOCK(f);
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ return f;
+ }
+ }
+ }
+
+ /* lock & return */
+ FLOWLOCK_WRLOCK(f);
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ return f;
+}
+
+/** \brief Get Flow for packet
+ *
+ * Hash retrieval function for flows. Looks up the hash bucket containing the
+ * flow pointer. Then compares the packet with the found flow to see if it is
+ * the flow we need. If it isn't, walk the list until the right flow is found.
+ *
+ * If the flow is not found or the bucket was emtpy, a new flow is taken from
+ * the queue. FlowDequeue() will alloc new flows as long as we stay within our
+ * memcap limit.
+ *
+ * The p->flow pointer is updated to point to the flow.
+ *
+ * \param tv thread vars
+ * \param dtv decode thread vars (for flow log api thread data)
+ *
+ * \retval f *LOCKED* flow or NULL
+ */
+Flow *FlowGetFlowFromHash(ThreadVars *tv, DecodeThreadVars *dtv, const Packet *p)
+{
+ Flow *f = NULL;
+ FlowHashCountInit;
+
+ /* get the key to our bucket */
+ uint32_t key = FlowGetKey(p);
+ /* get our hash bucket and lock it */
+ FlowBucket *fb = &flow_hash[key];
+ FBLOCK_LOCK(fb);
+
+ SCLogDebug("fb %p fb->head %p", fb, fb->head);
+
+ FlowHashCountIncr;
+
+ /* see if the bucket already has a flow */
+ if (fb->head == NULL) {
+ f = FlowGetNew(tv, dtv, p);
+ if (f == NULL) {
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return NULL;
+ }
+
+ /* flow is locked */
+ fb->head = f;
+ fb->tail = f;
+
+ /* got one, now lock, initialize and return */
+ FlowInit(f, p);
+ f->fb = fb;
+
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return f;
+ }
+
+ /* ok, we have a flow in the bucket. Let's find out if it is our flow */
+ f = fb->head;
+
+ /* see if this is the flow we are looking for */
+ if (FlowCompare(f, p) == 0) {
+ Flow *pf = NULL; /* previous flow */
+
+ while (f) {
+ FlowHashCountIncr;
+
+ pf = f;
+ f = f->hnext;
+
+ if (f == NULL) {
+ f = pf->hnext = FlowGetNew(tv, dtv, p);
+ if (f == NULL) {
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return NULL;
+ }
+ fb->tail = f;
+
+ /* flow is locked */
+
+ f->hprev = pf;
+
+ /* initialize and return */
+ FlowInit(f, p);
+ f->fb = fb;
+
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return f;
+ }
+
+ if (FlowCompare(f, p) != 0) {
+ /* we found our flow, lets put it on top of the
+ * hash list -- this rewards active flows */
+ if (f->hnext) {
+ f->hnext->hprev = f->hprev;
+ }
+ if (f->hprev) {
+ f->hprev->hnext = f->hnext;
+ }
+ if (f == fb->tail) {
+ fb->tail = f->hprev;
+ }
+
+ f->hnext = fb->head;
+ f->hprev = NULL;
+ fb->head->hprev = f;
+ fb->head = f;
+
+ /* found our flow, lock & return */
+ FLOWLOCK_WRLOCK(f);
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return f;
+ }
+ }
+ }
+
+ /* lock & return */
+ FLOWLOCK_WRLOCK(f);
+ /* update the last seen timestamp of this flow */
+ COPY_TIMESTAMP(&p->ts,&f->lastts);
+
+ FBLOCK_UNLOCK(fb);
+ FlowHashCountUpdate;
+ return f;
+}
+
+/** \internal
+ * \brief Get a flow from the hash directly.
+ *
+ * Called in conditions where the spare queue is empty and memcap is reached.
+ *
+ * Walks the hash until a flow can be freed. Timeouts are disregarded, use_cnt
+ * is adhered to. "flow_prune_idx" atomic int makes sure we don't start at the
+ * top each time since that would clear the top of the hash leading to longer
+ * and longer search times under high pressure (observed).
+ *
+ * \param tv thread vars
+ * \param dtv decode thread vars (for flow log api thread data)
+ *
+ * \retval f flow or NULL
+ */
+static Flow *FlowGetUsedFlow(ThreadVars *tv, DecodeThreadVars *dtv)
+{
+ uint32_t idx = SC_ATOMIC_GET(flow_prune_idx) % flow_config.hash_size;
+ uint32_t cnt = flow_config.hash_size;
+
+ while (cnt--) {
+ if (++idx >= flow_config.hash_size)
+ idx = 0;
+
+ FlowBucket *fb = &flow_hash[idx];
+
+ if (FBLOCK_TRYLOCK(fb) != 0)
+ continue;
+
+ Flow *f = fb->tail;
+ if (f == NULL) {
+ FBLOCK_UNLOCK(fb);
+ continue;
+ }
+
+ if (FLOWLOCK_TRYWRLOCK(f) != 0) {
+ FBLOCK_UNLOCK(fb);
+ continue;
+ }
+
+ /** never prune a flow that is used by a packet or stream msg
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(f->use_cnt) > 0) {
+ FBLOCK_UNLOCK(fb);
+ FLOWLOCK_UNLOCK(f);
+ continue;
+ }
+
+ /* remove from the hash */
+ if (f->hprev != NULL)
+ f->hprev->hnext = f->hnext;
+ if (f->hnext != NULL)
+ f->hnext->hprev = f->hprev;
+ if (fb->head == f)
+ fb->head = f->hnext;
+ if (fb->tail == f)
+ fb->tail = f->hprev;
+
+ f->hnext = NULL;
+ f->hprev = NULL;
+ f->fb = NULL;
+ FBLOCK_UNLOCK(fb);
+
+ int state = SC_ATOMIC_GET(f->flow_state);
+ if (state == FLOW_STATE_NEW)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_NEW;
+ else if (state == FLOW_STATE_ESTABLISHED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_ESTABLISHED;
+ else if (state == FLOW_STATE_CLOSED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_CLOSED;
+
+ f->flow_end_flags |= FLOW_END_FLAG_FORCED;
+
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
+ f->flow_end_flags |= FLOW_END_FLAG_EMERGENCY;
+
+ /* invoke flow log api */
+ if (dtv && dtv->output_flow_thread_data)
+ (void)OutputFlowLog(tv, dtv->output_flow_thread_data, f);
+
+ FlowClearMemory(f, f->protomap);
+
+ FLOWLOCK_UNLOCK(f);
+
+ (void) SC_ATOMIC_ADD(flow_prune_idx, (flow_config.hash_size - cnt));
+ return f;
+ }
+
+ return NULL;
+}
diff --git a/framework/src/suricata/src/flow-hash.h b/framework/src/suricata/src/flow-hash.h
new file mode 100644
index 00000000..4272896b
--- /dev/null
+++ b/framework/src/suricata/src/flow-hash.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_HASH_H__
+#define __FLOW_HASH_H__
+
+/** Spinlocks or Mutex for the flow buckets. */
+//#define FBLOCK_SPIN
+#define FBLOCK_MUTEX
+
+#ifdef FBLOCK_SPIN
+ #ifdef FBLOCK_MUTEX
+ #error Cannot enable both FBLOCK_SPIN and FBLOCK_MUTEX
+ #endif
+#endif
+
+/* flow hash bucket -- the hash is basically an array of these buckets.
+ * Each bucket contains a flow or list of flows. All these flows have
+ * the same hashkey (the hash is a chained hash). When doing modifications
+ * to the list, the entire bucket is locked. */
+typedef struct FlowBucket_ {
+ Flow *head;
+ Flow *tail;
+#ifdef FBLOCK_MUTEX
+ SCMutex m;
+#elif defined FBLOCK_SPIN
+ SCSpinlock s;
+#else
+ #error Enable FBLOCK_SPIN or FBLOCK_MUTEX
+#endif
+} __attribute__((aligned(CLS))) FlowBucket;
+
+#ifdef FBLOCK_SPIN
+ #define FBLOCK_INIT(fb) SCSpinInit(&(fb)->s, 0)
+ #define FBLOCK_DESTROY(fb) SCSpinDestroy(&(fb)->s)
+ #define FBLOCK_LOCK(fb) SCSpinLock(&(fb)->s)
+ #define FBLOCK_TRYLOCK(fb) SCSpinTrylock(&(fb)->s)
+ #define FBLOCK_UNLOCK(fb) SCSpinUnlock(&(fb)->s)
+#elif defined FBLOCK_MUTEX
+ #define FBLOCK_INIT(fb) SCMutexInit(&(fb)->m, NULL)
+ #define FBLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->m)
+ #define FBLOCK_LOCK(fb) SCMutexLock(&(fb)->m)
+ #define FBLOCK_TRYLOCK(fb) SCMutexTrylock(&(fb)->m)
+ #define FBLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->m)
+#else
+ #error Enable FBLOCK_SPIN or FBLOCK_MUTEX
+#endif
+
+/* prototypes */
+
+Flow *FlowGetFlowFromHash(ThreadVars *tv, DecodeThreadVars *dtv, const Packet *);
+
+void FlowDisableTcpReuseHandling(void);
+
+/** enable to print stats on hash lookups in flow-debug.log */
+//#define FLOW_DEBUG_STATS
+
+#ifdef FLOW_DEBUG_STATS
+void FlowHashDebugInit(void);
+void FlowHashDebugDeinit(void);
+void FlowHashDebugPrint(uint32_t);
+#else
+#define FlowHashDebugInit(...)
+#define FlowHashDebugPrint(...)
+#define FlowHashDebugDeinit(...)
+#endif
+
+#endif /* __FLOW_HASH_H__ */
+
diff --git a/framework/src/suricata/src/flow-manager.c b/framework/src/suricata/src/flow-manager.c
new file mode 100644
index 00000000..15ad6162
--- /dev/null
+++ b/framework/src/suricata/src/flow-manager.c
@@ -0,0 +1,1285 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "conf.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "runmodes.h"
+
+#include "util-random.h"
+#include "util-time.h"
+
+#include "flow.h"
+#include "flow-queue.h"
+#include "flow-hash.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "flow-private.h"
+#include "flow-timeout.h"
+#include "flow-manager.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+
+#include "util-debug.h"
+#include "util-privs.h"
+#include "util-signal.h"
+
+#include "threads.h"
+#include "detect.h"
+#include "detect-engine-state.h"
+#include "stream.h"
+
+#include "app-layer-parser.h"
+
+#include "host-timeout.h"
+#include "defrag-timeout.h"
+#include "ippair-timeout.h"
+
+#include "output-flow.h"
+
+/* Run mode selected at suricata.c */
+extern int run_mode;
+
+/* multi flow mananger support */
+static uint32_t flowmgr_number = 1;
+/* atomic counter for flow managers, to assign instance id */
+SC_ATOMIC_DECLARE(uint32_t, flowmgr_cnt);
+
+/* multi flow recycler support */
+static uint32_t flowrec_number = 1;
+/* atomic counter for flow recyclers, to assign instance id */
+SC_ATOMIC_DECLARE(uint32_t, flowrec_cnt);
+
+SC_ATOMIC_EXTERN(unsigned int, flow_flags);
+
+/* 1 seconds */
+#define FLOW_NORMAL_MODE_UPDATE_DELAY_SEC 1
+#define FLOW_NORMAL_MODE_UPDATE_DELAY_NSEC 0
+/* 0.1 seconds */
+#define FLOW_EMERG_MODE_UPDATE_DELAY_SEC 0
+#define FLOW_EMERG_MODE_UPDATE_DELAY_NSEC 100000
+#define NEW_FLOW_COUNT_COND 10
+
+typedef struct FlowTimeoutCounters_ {
+ uint32_t new;
+ uint32_t est;
+ uint32_t clo;
+ uint32_t tcp_reuse;
+} FlowTimeoutCounters;
+
+/**
+ * \brief Used to disable flow manager thread(s).
+ *
+ * \todo Kinda hackish since it uses the tv name to identify flow manager
+ * thread. We need an all weather identification scheme.
+ */
+void FlowDisableFlowManagerThread(void)
+{
+ ThreadVars *tv = NULL;
+ int cnt = 0;
+
+ /* wake up threads */
+ uint32_t u;
+ for (u = 0; u < flowmgr_number; u++)
+ SCCtrlCondSignal(&flow_manager_ctrl_cond);
+
+ SCMutexLock(&tv_root_lock);
+
+ /* flow manager thread(s) is/are a part of mgmt threads */
+ tv = tv_root[TVT_MGMT];
+
+ while (tv != NULL) {
+ if (strcasecmp(tv->name, "FlowManagerThread") == 0) {
+ TmThreadsSetFlag(tv, THV_KILL);
+ cnt++;
+
+ /* value in seconds */
+#define THREAD_KILL_MAX_WAIT_TIME 60
+ /* value in microseconds */
+#define WAIT_TIME 100
+
+ double total_wait_time = 0;
+ while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
+ usleep(WAIT_TIME);
+ total_wait_time += WAIT_TIME / 1000000.0;
+ if (total_wait_time > THREAD_KILL_MAX_WAIT_TIME) {
+ SCLogError(SC_ERR_FATAL, "Engine unable to "
+ "disable detect thread - \"%s\". "
+ "Killing engine", tv->name);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ tv = tv->next;
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ /* wake up threads, another try */
+ for (u = 0; u < flowmgr_number; u++)
+ SCCtrlCondSignal(&flow_manager_ctrl_cond);
+
+ /* reset count, so we can kill and respawn (unix socket) */
+ SC_ATOMIC_SET(flowmgr_cnt, 0);
+ return;
+}
+
+/** \internal
+ * \brief get timeout for flow
+ *
+ * \param f flow
+ * \param state flow state
+ * \param emergency bool indicating emergency mode 1 yes, 0 no
+ *
+ * \retval timeout timeout in seconds
+ */
+static inline uint32_t FlowGetFlowTimeout(const Flow *f, int state, int emergency)
+{
+ uint32_t timeout;
+
+ if (emergency) {
+ switch(state) {
+ default:
+ case FLOW_STATE_NEW:
+ timeout = flow_proto[f->protomap].emerg_new_timeout;
+ break;
+ case FLOW_STATE_ESTABLISHED:
+ timeout = flow_proto[f->protomap].emerg_est_timeout;
+ break;
+ case FLOW_STATE_CLOSED:
+ timeout = flow_proto[f->protomap].emerg_closed_timeout;
+ break;
+ }
+ } else { /* implies no emergency */
+ switch(state) {
+ default:
+ case FLOW_STATE_NEW:
+ timeout = flow_proto[f->protomap].new_timeout;
+ break;
+ case FLOW_STATE_ESTABLISHED:
+ timeout = flow_proto[f->protomap].est_timeout;
+ break;
+ case FLOW_STATE_CLOSED:
+ timeout = flow_proto[f->protomap].closed_timeout;
+ break;
+ }
+ }
+
+ return timeout;
+}
+
+/** \internal
+ * \brief check if a flow is timed out
+ *
+ * \param f flow
+ * \param ts timestamp
+ * \param emergency bool indicating emergency mode
+ *
+ * \retval 0 not timed out
+ * \retval 1 timed out
+ */
+static int FlowManagerFlowTimeout(const Flow *f, int state, struct timeval *ts, int emergency)
+{
+ /* set the timeout value according to the flow operating mode,
+ * flow's state and protocol.*/
+ uint32_t timeout = FlowGetFlowTimeout(f, state, emergency);
+
+ /* do the timeout check */
+ if ((int32_t)(f->lastts.tv_sec + timeout) >= ts->tv_sec) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/** \internal
+ * \brief See if we can really discard this flow. Check use_cnt reference
+ * counter and force reassembly if necessary.
+ *
+ * \param f flow
+ * \param ts timestamp
+ * \param emergency bool indicating emergency mode
+ *
+ * \retval 0 not timed out just yet
+ * \retval 1 fully timed out, lets kill it
+ */
+static int FlowManagerFlowTimedOut(Flow *f, struct timeval *ts)
+{
+ /** never prune a flow that is used by a packet or stream msg
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(f->use_cnt) > 0) {
+ return 0;
+ }
+
+ int server = 0, client = 0;
+ if (!(f->flags & FLOW_TIMEOUT_REASSEMBLY_DONE) &&
+ FlowForceReassemblyNeedReassembly(f, &server, &client) == 1) {
+ FlowForceReassemblyForFlow(f, server, client);
+ return 0;
+ }
+#ifdef DEBUG
+ /* this should not be possible */
+ BUG_ON(SC_ATOMIC_GET(f->use_cnt) > 0);
+#endif
+
+ return 1;
+}
+
+/**
+ * \internal
+ *
+ * \brief check all flows in a hash row for timing out
+ *
+ * \param f last flow in the hash row
+ * \param ts timestamp
+ * \param emergency bool indicating emergency mode
+ * \param counters ptr to FlowTimeoutCounters structure
+ *
+ * \retval cnt timed out flows
+ */
+static uint32_t FlowManagerHashRowTimeout(Flow *f, struct timeval *ts,
+ int emergency, FlowTimeoutCounters *counters)
+{
+ uint32_t cnt = 0;
+
+ do {
+ /* check flow timeout based on lastts and state. Both can be
+ * accessed w/o Flow lock as we do have the hash row lock (so flow
+ * can't disappear) and flow_state is atomic. lastts can only
+ * be modified when we have both the flow and hash row lock */
+
+ int state = SC_ATOMIC_GET(f->flow_state);
+
+ /* timeout logic goes here */
+ if (FlowManagerFlowTimeout(f, state, ts, emergency) == 0) {
+ f = f->hprev;
+ continue;
+ }
+
+ /* before grabbing the flow lock, make sure we have at least
+ * 3 packets in the pool */
+ PacketPoolWaitForN(3);
+
+ FLOWLOCK_WRLOCK(f);
+
+ Flow *next_flow = f->hprev;
+
+ /* check if the flow is fully timed out and
+ * ready to be discarded. */
+ if (FlowManagerFlowTimedOut(f, ts) == 1) {
+ /* remove from the hash */
+ if (f->hprev != NULL)
+ f->hprev->hnext = f->hnext;
+ if (f->hnext != NULL)
+ f->hnext->hprev = f->hprev;
+ if (f->fb->head == f)
+ f->fb->head = f->hnext;
+ if (f->fb->tail == f)
+ f->fb->tail = f->hprev;
+
+ f->hnext = NULL;
+ f->hprev = NULL;
+
+ if (f->flags & FLOW_TCP_REUSED)
+ counters->tcp_reuse++;
+
+ if (state == FLOW_STATE_NEW)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_NEW;
+ else if (state == FLOW_STATE_ESTABLISHED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_ESTABLISHED;
+ else if (state == FLOW_STATE_CLOSED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_CLOSED;
+
+ if (emergency)
+ f->flow_end_flags |= FLOW_END_FLAG_EMERGENCY;
+ f->flow_end_flags |= FLOW_END_FLAG_TIMEOUT;
+
+// FlowClearMemory (f, f->protomap);
+
+ /* no one is referring to this flow, use_cnt 0, removed from hash
+ * so we can unlock it and move it back to the spare queue. */
+ FLOWLOCK_UNLOCK(f);
+ FlowEnqueue(&flow_recycle_q, f);
+ /* move to spare list */
+// FlowMoveToSpare(f);
+
+ cnt++;
+
+ switch (state) {
+ case FLOW_STATE_NEW:
+ default:
+ counters->new++;
+ break;
+ case FLOW_STATE_ESTABLISHED:
+ counters->est++;
+ break;
+ case FLOW_STATE_CLOSED:
+ counters->clo++;
+ break;
+ }
+ } else {
+ FLOWLOCK_UNLOCK(f);
+ }
+
+ f = next_flow;
+ } while (f != NULL);
+
+ return cnt;
+}
+
+/**
+ * \brief time out flows from the hash
+ *
+ * \param ts timestamp
+ * \param try_cnt number of flows to time out max (0 is unlimited)
+ * \param hash_min min hash index to consider
+ * \param hash_max max hash index to consider
+ * \param counters ptr to FlowTimeoutCounters structure
+ *
+ * \retval cnt number of timed out flow
+ */
+static uint32_t FlowTimeoutHash(struct timeval *ts, uint32_t try_cnt,
+ uint32_t hash_min, uint32_t hash_max,
+ FlowTimeoutCounters *counters)
+{
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+ int emergency = 0;
+
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
+ emergency = 1;
+
+ for (idx = hash_min; idx < hash_max; idx++) {
+ FlowBucket *fb = &flow_hash[idx];
+
+ /* before grabbing the row lock, make sure we have at least
+ * 9 packets in the pool */
+ PacketPoolWaitForN(9);
+
+ if (FBLOCK_TRYLOCK(fb) != 0)
+ continue;
+
+ /* flow hash bucket is now locked */
+
+ if (fb->tail == NULL)
+ goto next;
+
+ /* we have a flow, or more than one */
+ cnt += FlowManagerHashRowTimeout(fb->tail, ts, emergency, counters);
+
+next:
+ FBLOCK_UNLOCK(fb);
+
+ if (try_cnt > 0 && cnt >= try_cnt)
+ break;
+ }
+
+ return cnt;
+}
+
+/**
+ * \internal
+ *
+ * \brief move all flows out of a hash row
+ *
+ * \param f last flow in the hash row
+ *
+ * \retval cnt removed out flows
+ */
+static uint32_t FlowManagerHashRowCleanup(Flow *f)
+{
+ uint32_t cnt = 0;
+
+ do {
+ FLOWLOCK_WRLOCK(f);
+
+ Flow *next_flow = f->hprev;
+
+ int state = SC_ATOMIC_GET(f->flow_state);
+
+ /* remove from the hash */
+ if (f->hprev != NULL)
+ f->hprev->hnext = f->hnext;
+ if (f->hnext != NULL)
+ f->hnext->hprev = f->hprev;
+ if (f->fb->head == f)
+ f->fb->head = f->hnext;
+ if (f->fb->tail == f)
+ f->fb->tail = f->hprev;
+
+ f->hnext = NULL;
+ f->hprev = NULL;
+
+ if (state == FLOW_STATE_NEW)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_NEW;
+ else if (state == FLOW_STATE_ESTABLISHED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_ESTABLISHED;
+ else if (state == FLOW_STATE_CLOSED)
+ f->flow_end_flags |= FLOW_END_FLAG_STATE_CLOSED;
+
+ f->flow_end_flags |= FLOW_END_FLAG_SHUTDOWN;
+
+ /* no one is referring to this flow, use_cnt 0, removed from hash
+ * so we can unlock it and move it to the recycle queue. */
+ FLOWLOCK_UNLOCK(f);
+
+ FlowEnqueue(&flow_recycle_q, f);
+
+ cnt++;
+
+ f = next_flow;
+ } while (f != NULL);
+
+ return cnt;
+}
+
+/**
+ * \brief remove all flows from the hash
+ *
+ * \retval cnt number of removes out flows
+ */
+static uint32_t FlowCleanupHash(void){
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+
+ for (idx = 0; idx < flow_config.hash_size; idx++) {
+ FlowBucket *fb = &flow_hash[idx];
+
+ FBLOCK_LOCK(fb);
+
+ if (fb->tail != NULL) {
+ /* we have a flow, or more than one */
+ cnt += FlowManagerHashRowCleanup(fb->tail);
+ }
+
+ FBLOCK_UNLOCK(fb);
+ }
+
+ return cnt;
+}
+
+extern int g_detect_disabled;
+
+typedef struct FlowManagerThreadData_ {
+ uint32_t instance;
+ uint32_t min;
+ uint32_t max;
+
+ uint16_t flow_mgr_cnt_clo;
+ uint16_t flow_mgr_cnt_new;
+ uint16_t flow_mgr_cnt_est;
+ uint16_t flow_mgr_spare;
+ uint16_t flow_emerg_mode_enter;
+ uint16_t flow_emerg_mode_over;
+ uint16_t flow_tcp_reuse;
+} FlowManagerThreadData;
+
+static TmEcode FlowManagerThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ FlowManagerThreadData *ftd = SCCalloc(1, sizeof(FlowManagerThreadData));
+ if (ftd == NULL)
+ return TM_ECODE_FAILED;
+
+ ftd->instance = SC_ATOMIC_ADD(flowmgr_cnt, 1);
+ SCLogDebug("flow manager instance %u", ftd->instance);
+
+ /* set the min and max value used for hash row walking
+ * each thread has it's own section of the flow hash */
+ uint32_t range = flow_config.hash_size / flowmgr_number;
+ if (ftd->instance == 1)
+ ftd->max = range;
+ else if (ftd->instance == flowmgr_number) {
+ ftd->min = (range * (ftd->instance - 1));
+ ftd->max = flow_config.hash_size;
+ } else {
+ ftd->min = (range * (ftd->instance - 1));
+ ftd->max = (range * ftd->instance);
+ }
+ BUG_ON(ftd->min > flow_config.hash_size || ftd->max > flow_config.hash_size);
+
+ SCLogDebug("instance %u hash range %u %u", ftd->instance, ftd->min, ftd->max);
+
+ /* pass thread data back to caller */
+ *data = ftd;
+
+ ftd->flow_mgr_cnt_clo = StatsRegisterCounter("flow_mgr.closed_pruned", t);
+ ftd->flow_mgr_cnt_new = StatsRegisterCounter("flow_mgr.new_pruned", t);
+ ftd->flow_mgr_cnt_est = StatsRegisterCounter("flow_mgr.est_pruned", t);
+ ftd->flow_mgr_spare = StatsRegisterCounter("flow.spare", t);
+ ftd->flow_emerg_mode_enter = StatsRegisterCounter("flow.emerg_mode_entered", t);
+ ftd->flow_emerg_mode_over = StatsRegisterCounter("flow.emerg_mode_over", t);
+ ftd->flow_tcp_reuse = StatsRegisterCounter("flow.tcp_reuse", t);
+
+ PacketPoolInit();
+ return TM_ECODE_OK;
+}
+
+static TmEcode FlowManagerThreadDeinit(ThreadVars *t, void *data)
+{
+ PacketPoolDestroy();
+ SCFree(data);
+ return TM_ECODE_OK;
+}
+
+
+/** \brief Thread that manages the flow table and times out flows.
+ *
+ * \param td ThreadVars casted to void ptr
+ *
+ * Keeps an eye on the spare list, alloc flows if needed...
+ */
+static TmEcode FlowManager(ThreadVars *th_v, void *thread_data)
+{
+ /* block usr2. usr1 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ FlowManagerThreadData *ftd = thread_data;
+ struct timeval ts;
+ uint32_t established_cnt = 0, new_cnt = 0, closing_cnt = 0;
+ int emerg = FALSE;
+ int prev_emerg = FALSE;
+ uint32_t last_sec = 0;
+ struct timespec cond_time;
+ int flow_update_delay_sec = FLOW_NORMAL_MODE_UPDATE_DELAY_SEC;
+ int flow_update_delay_nsec = FLOW_NORMAL_MODE_UPDATE_DELAY_NSEC;
+/* VJ leaving disabled for now, as hosts are only used by tags and the numbers
+ * are really low. Might confuse ppl
+ uint16_t flow_mgr_host_prune = StatsRegisterCounter("hosts.pruned", th_v);
+ uint16_t flow_mgr_host_active = StatsRegisterCounter("hosts.active", th_v);
+ uint16_t flow_mgr_host_spare = StatsRegisterCounter("hosts.spare", th_v);
+*/
+ memset(&ts, 0, sizeof(ts));
+
+ FlowHashDebugInit();
+
+ while (1)
+ {
+ if (TmThreadsCheckFlag(th_v, THV_PAUSE)) {
+ TmThreadsSetFlag(th_v, THV_PAUSED);
+ TmThreadTestThreadUnPaused(th_v);
+ TmThreadsUnsetFlag(th_v, THV_PAUSED);
+ }
+
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY) {
+ emerg = TRUE;
+
+ if (emerg == TRUE && prev_emerg == FALSE) {
+ prev_emerg = TRUE;
+
+ SCLogDebug("Flow emergency mode entered...");
+
+ StatsIncr(th_v, ftd->flow_emerg_mode_enter);
+ }
+ }
+
+ /* Get the time */
+ memset(&ts, 0, sizeof(ts));
+ TimeGet(&ts);
+ SCLogDebug("ts %" PRIdMAX "", (intmax_t)ts.tv_sec);
+
+ if (((uint32_t)ts.tv_sec - last_sec) > 600) {
+ FlowHashDebugPrint((uint32_t)ts.tv_sec);
+ last_sec = (uint32_t)ts.tv_sec;
+ }
+
+ /* see if we still have enough spare flows */
+ if (ftd->instance == 1)
+ FlowUpdateSpareFlows();
+
+ /* try to time out flows */
+ FlowTimeoutCounters counters = { 0, 0, 0, 0, };
+ FlowTimeoutHash(&ts, 0 /* check all */, ftd->min, ftd->max, &counters);
+
+
+ if (ftd->instance == 1) {
+ DefragTimeoutHash(&ts);
+ //uint32_t hosts_pruned =
+ HostTimeoutHash(&ts);
+ IPPairTimeoutHash(&ts);
+ }
+/*
+ StatsAddUI64(th_v, flow_mgr_host_prune, (uint64_t)hosts_pruned);
+ uint32_t hosts_active = HostGetActiveCount();
+ StatsSetUI64(th_v, flow_mgr_host_active, (uint64_t)hosts_active);
+ uint32_t hosts_spare = HostGetSpareCount();
+ StatsSetUI64(th_v, flow_mgr_host_spare, (uint64_t)hosts_spare);
+*/
+ StatsAddUI64(th_v, ftd->flow_mgr_cnt_clo, (uint64_t)counters.clo);
+ StatsAddUI64(th_v, ftd->flow_mgr_cnt_new, (uint64_t)counters.new);
+ StatsAddUI64(th_v, ftd->flow_mgr_cnt_est, (uint64_t)counters.est);
+ StatsAddUI64(th_v, ftd->flow_tcp_reuse, (uint64_t)counters.tcp_reuse);
+
+ uint32_t len = 0;
+ FQLOCK_LOCK(&flow_spare_q);
+ len = flow_spare_q.len;
+ FQLOCK_UNLOCK(&flow_spare_q);
+ StatsSetUI64(th_v, ftd->flow_mgr_spare, (uint64_t)len);
+
+ /* Don't fear, FlowManagerThread is here...
+ * clear emergency bit if we have at least xx flows pruned. */
+ if (emerg == TRUE) {
+ SCLogDebug("flow_sparse_q.len = %"PRIu32" prealloc: %"PRIu32
+ "flow_spare_q status: %"PRIu32"%% flows at the queue",
+ len, flow_config.prealloc, len * 100 / flow_config.prealloc);
+ /* only if we have pruned this "emergency_recovery" percentage
+ * of flows, we will unset the emergency bit */
+ if (len * 100 / flow_config.prealloc > flow_config.emergency_recovery) {
+ SC_ATOMIC_AND(flow_flags, ~FLOW_EMERGENCY);
+
+ emerg = FALSE;
+ prev_emerg = FALSE;
+
+ flow_update_delay_sec = FLOW_NORMAL_MODE_UPDATE_DELAY_SEC;
+ flow_update_delay_nsec = FLOW_NORMAL_MODE_UPDATE_DELAY_NSEC;
+ SCLogInfo("Flow emergency mode over, back to normal... unsetting"
+ " FLOW_EMERGENCY bit (ts.tv_sec: %"PRIuMAX", "
+ "ts.tv_usec:%"PRIuMAX") flow_spare_q status(): %"PRIu32
+ "%% flows at the queue", (uintmax_t)ts.tv_sec,
+ (uintmax_t)ts.tv_usec, len * 100 / flow_config.prealloc);
+
+ StatsIncr(th_v, ftd->flow_emerg_mode_over);
+ } else {
+ flow_update_delay_sec = FLOW_EMERG_MODE_UPDATE_DELAY_SEC;
+ flow_update_delay_nsec = FLOW_EMERG_MODE_UPDATE_DELAY_NSEC;
+ }
+ }
+
+ if (TmThreadsCheckFlag(th_v, THV_KILL)) {
+ StatsSyncCounters(th_v);
+ break;
+ }
+
+ cond_time.tv_sec = time(NULL) + flow_update_delay_sec;
+ cond_time.tv_nsec = flow_update_delay_nsec;
+ SCCtrlMutexLock(&flow_manager_ctrl_mutex);
+ SCCtrlCondTimedwait(&flow_manager_ctrl_cond, &flow_manager_ctrl_mutex,
+ &cond_time);
+ SCCtrlMutexUnlock(&flow_manager_ctrl_mutex);
+
+ SCLogDebug("woke up... %s", SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY ? "emergency":"");
+
+ StatsSyncCountersIfSignalled(th_v);
+ }
+
+ FlowHashDebugDeinit();
+
+ SCLogInfo("%" PRIu32 " new flows, %" PRIu32 " established flows were "
+ "timed out, %"PRIu32" flows in closed state", new_cnt,
+ established_cnt, closing_cnt);
+
+ return TM_ECODE_OK;
+}
+
+static uint64_t FlowGetMemuse(void)
+{
+ uint64_t flow_memuse = SC_ATOMIC_GET(flow_memuse);
+ return flow_memuse;
+}
+
+/** \brief spawn the flow manager thread */
+void FlowManagerThreadSpawn()
+{
+ intmax_t setting = 1;
+ (void)ConfGetInt("flow.managers", &setting);
+
+ if (setting < 1 || setting > 1024) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS,
+ "invalid flow.managers setting %"PRIdMAX, setting);
+ exit(EXIT_FAILURE);
+ }
+ flowmgr_number = (uint32_t)setting;
+
+ SCLogInfo("using %u flow manager threads", flowmgr_number);
+ SCCtrlCondInit(&flow_manager_ctrl_cond, NULL);
+ SCCtrlMutexInit(&flow_manager_ctrl_mutex, NULL);
+
+ StatsRegisterGlobalCounter("flow.memuse", FlowGetMemuse);
+
+ uint32_t u;
+ for (u = 0; u < flowmgr_number; u++) {
+ ThreadVars *tv_flowmgr = NULL;
+
+ char name[32] = "";
+ snprintf(name, sizeof(name), "FlowManagerThread%02u", u+1);
+
+ tv_flowmgr = TmThreadCreateMgmtThreadByName("FlowManagerThread",
+ "FlowManager", 0);
+ BUG_ON(tv_flowmgr == NULL);
+
+ if (tv_flowmgr == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(1);
+ }
+ if (TmThreadSpawn(tv_flowmgr) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(1);
+ }
+ }
+ return;
+}
+
+typedef struct FlowRecyclerThreadData_ {
+ void *output_thread_data;
+} FlowRecyclerThreadData;
+
+static TmEcode FlowRecyclerThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ FlowRecyclerThreadData *ftd = SCCalloc(1, sizeof(FlowRecyclerThreadData));
+ if (ftd == NULL)
+ return TM_ECODE_FAILED;
+
+ if (OutputFlowLogThreadInit(t, NULL, &ftd->output_thread_data) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_THREAD_INIT, "initializing flow log API for thread failed");
+ SCFree(ftd);
+ return TM_ECODE_FAILED;
+ }
+ SCLogDebug("output_thread_data %p", ftd->output_thread_data);
+
+ *data = ftd;
+ return TM_ECODE_OK;
+}
+
+static TmEcode FlowRecyclerThreadDeinit(ThreadVars *t, void *data)
+{
+ FlowRecyclerThreadData *ftd = (FlowRecyclerThreadData *)data;
+ if (ftd->output_thread_data != NULL)
+ OutputFlowLogThreadDeinit(t, ftd->output_thread_data);
+
+ SCFree(data);
+ return TM_ECODE_OK;
+}
+
+/** \brief Thread that manages timed out flows.
+ *
+ * \param td ThreadVars casted to void ptr
+ */
+static TmEcode FlowRecycler(ThreadVars *th_v, void *thread_data)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ struct timeval ts;
+ struct timespec cond_time;
+ int flow_update_delay_sec = FLOW_NORMAL_MODE_UPDATE_DELAY_SEC;
+ int flow_update_delay_nsec = FLOW_NORMAL_MODE_UPDATE_DELAY_NSEC;
+ uint64_t recycled_cnt = 0;
+ FlowRecyclerThreadData *ftd = (FlowRecyclerThreadData *)thread_data;
+ BUG_ON(ftd == NULL);
+
+ memset(&ts, 0, sizeof(ts));
+
+ while (1)
+ {
+ if (TmThreadsCheckFlag(th_v, THV_PAUSE)) {
+ TmThreadsSetFlag(th_v, THV_PAUSED);
+ TmThreadTestThreadUnPaused(th_v);
+ TmThreadsUnsetFlag(th_v, THV_PAUSED);
+ }
+
+ /* Get the time */
+ memset(&ts, 0, sizeof(ts));
+ TimeGet(&ts);
+ SCLogDebug("ts %" PRIdMAX "", (intmax_t)ts.tv_sec);
+
+ uint32_t len = 0;
+ FQLOCK_LOCK(&flow_recycle_q);
+ len = flow_recycle_q.len;
+ FQLOCK_UNLOCK(&flow_recycle_q);
+
+ /* Loop through the queue and clean up all flows in it */
+ if (len) {
+ Flow *f;
+
+ while ((f = FlowDequeue(&flow_recycle_q)) != NULL) {
+ FLOWLOCK_WRLOCK(f);
+
+ (void)OutputFlowLog(th_v, ftd->output_thread_data, f);
+
+ FlowClearMemory (f, f->protomap);
+ FLOWLOCK_UNLOCK(f);
+ FlowMoveToSpare(f);
+ recycled_cnt++;
+ }
+ }
+
+ SCLogDebug("%u flows to recycle", len);
+
+ if (TmThreadsCheckFlag(th_v, THV_KILL)) {
+ StatsSyncCounters(th_v);
+ break;
+ }
+
+ cond_time.tv_sec = time(NULL) + flow_update_delay_sec;
+ cond_time.tv_nsec = flow_update_delay_nsec;
+ SCCtrlMutexLock(&flow_recycler_ctrl_mutex);
+ SCCtrlCondTimedwait(&flow_recycler_ctrl_cond,
+ &flow_recycler_ctrl_mutex, &cond_time);
+ SCCtrlMutexUnlock(&flow_recycler_ctrl_mutex);
+
+ SCLogDebug("woke up...");
+
+ StatsSyncCountersIfSignalled(th_v);
+ }
+
+ SCLogInfo("%"PRIu64" flows processed", recycled_cnt);
+
+ return TM_ECODE_OK;
+}
+
+int FlowRecyclerReadyToShutdown(void)
+{
+ uint32_t len = 0;
+ FQLOCK_LOCK(&flow_recycle_q);
+ len = flow_recycle_q.len;
+ FQLOCK_UNLOCK(&flow_recycle_q);
+
+ return ((len == 0));
+}
+
+/** \brief spawn the flow recycler thread */
+void FlowRecyclerThreadSpawn()
+{
+ intmax_t setting = 1;
+ (void)ConfGetInt("flow.recyclers", &setting);
+
+ if (setting < 1 || setting > 1024) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS,
+ "invalid flow.recyclers setting %"PRIdMAX, setting);
+ exit(EXIT_FAILURE);
+ }
+ flowrec_number = (uint32_t)setting;
+
+ SCLogInfo("using %u flow recycler threads", flowrec_number);
+
+ SCCtrlCondInit(&flow_recycler_ctrl_cond, NULL);
+ SCCtrlMutexInit(&flow_recycler_ctrl_mutex, NULL);
+
+
+ uint32_t u;
+ for (u = 0; u < flowrec_number; u++) {
+ ThreadVars *tv_flowmgr = NULL;
+
+ char name[32] = "";
+ snprintf(name, sizeof(name), "FlowRecyclerThread%02u", u+1);
+
+ tv_flowmgr = TmThreadCreateMgmtThreadByName("FlowRecyclerThread",
+ "FlowRecycler", 0);
+ BUG_ON(tv_flowmgr == NULL);
+
+ if (tv_flowmgr == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(1);
+ }
+ if (TmThreadSpawn(tv_flowmgr) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(1);
+ }
+ }
+ return;
+}
+
+/**
+ * \brief Used to disable flow recycler thread(s).
+ *
+ * \note this should only be called when the flow manager is already gone
+ *
+ * \todo Kinda hackish since it uses the tv name to identify flow recycler
+ * thread. We need an all weather identification scheme.
+ */
+void FlowDisableFlowRecyclerThread(void)
+{
+ ThreadVars *tv = NULL;
+ int cnt = 0;
+
+ /* move all flows still in the hash to the recycler queue */
+ FlowCleanupHash();
+
+ /* make sure all flows are processed */
+ do {
+ SCCtrlCondSignal(&flow_recycler_ctrl_cond);
+ usleep(10);
+ } while (FlowRecyclerReadyToShutdown() == 0);
+
+ /* wake up threads */
+ uint32_t u;
+ for (u = 0; u < flowrec_number; u++)
+ SCCtrlCondSignal(&flow_recycler_ctrl_cond);
+
+ SCMutexLock(&tv_root_lock);
+
+ /* flow recycler thread(s) is/are a part of mgmt threads */
+ tv = tv_root[TVT_MGMT];
+
+ while (tv != NULL) {
+ if (strcasecmp(tv->name, "FlowRecyclerThread") == 0) {
+ TmThreadsSetFlag(tv, THV_KILL);
+ cnt++;
+
+ /* value in seconds */
+#define THREAD_KILL_MAX_WAIT_TIME 60
+ /* value in microseconds */
+#define WAIT_TIME 100
+
+ double total_wait_time = 0;
+ while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
+ usleep(WAIT_TIME);
+ total_wait_time += WAIT_TIME / 1000000.0;
+ if (total_wait_time > THREAD_KILL_MAX_WAIT_TIME) {
+ SCLogError(SC_ERR_FATAL, "Engine unable to "
+ "disable detect thread - \"%s\". "
+ "Killing engine", tv->name);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ tv = tv->next;
+ }
+
+ /* wake up threads, another try */
+ for (u = 0; u < flowrec_number; u++)
+ SCCtrlCondSignal(&flow_recycler_ctrl_cond);
+
+ SCMutexUnlock(&tv_root_lock);
+
+ /* reset count, so we can kill and respawn (unix socket) */
+ SC_ATOMIC_SET(flowrec_cnt, 0);
+ return;
+}
+
+void TmModuleFlowManagerRegister (void)
+{
+ tmm_modules[TMM_FLOWMANAGER].name = "FlowManager";
+ tmm_modules[TMM_FLOWMANAGER].ThreadInit = FlowManagerThreadInit;
+ tmm_modules[TMM_FLOWMANAGER].ThreadDeinit = FlowManagerThreadDeinit;
+// tmm_modules[TMM_FLOWMANAGER].RegisterTests = FlowManagerRegisterTests;
+ tmm_modules[TMM_FLOWMANAGER].Management = FlowManager;
+ tmm_modules[TMM_FLOWMANAGER].cap_flags = 0;
+ tmm_modules[TMM_FLOWMANAGER].flags = TM_FLAG_MANAGEMENT_TM;
+ SCLogDebug("%s registered", tmm_modules[TMM_FLOWMANAGER].name);
+
+ SC_ATOMIC_INIT(flowmgr_cnt);
+}
+
+void TmModuleFlowRecyclerRegister (void)
+{
+ tmm_modules[TMM_FLOWRECYCLER].name = "FlowRecycler";
+ tmm_modules[TMM_FLOWRECYCLER].ThreadInit = FlowRecyclerThreadInit;
+ tmm_modules[TMM_FLOWRECYCLER].ThreadDeinit = FlowRecyclerThreadDeinit;
+// tmm_modules[TMM_FLOWRECYCLER].RegisterTests = FlowRecyclerRegisterTests;
+ tmm_modules[TMM_FLOWRECYCLER].Management = FlowRecycler;
+ tmm_modules[TMM_FLOWRECYCLER].cap_flags = 0;
+ tmm_modules[TMM_FLOWRECYCLER].flags = TM_FLAG_MANAGEMENT_TM;
+ SCLogDebug("%s registered", tmm_modules[TMM_FLOWRECYCLER].name);
+
+ SC_ATOMIC_INIT(flowrec_cnt);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the timing out of a flow with a fresh TcpSession
+ * (just initialized, no data segments) in normal mode.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowMgrTest01 (void)
+{
+ TcpSession ssn;
+ Flow f;
+ FlowBucket fb;
+ struct timeval ts;
+
+ FlowQueueInit(&flow_spare_q);
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ts, 0, sizeof(ts));
+ memset(&fb, 0, sizeof(FlowBucket));
+
+ FBLOCK_INIT(&fb);
+
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
+
+ TimeGet(&ts);
+ f.lastts.tv_sec = ts.tv_sec - 5000;
+ f.protoctx = &ssn;
+ f.fb = &fb;
+
+ f.proto = IPPROTO_TCP;
+
+ int state = SC_ATOMIC_GET(f.flow_state);
+ if (FlowManagerFlowTimeout(&f, state, &ts, 0) != 1 && FlowManagerFlowTimedOut(&f, &ts) != 1) {
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 0;
+ }
+
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+
+ FlowQueueDestroy(&flow_spare_q);
+ return 1;
+}
+
+/**
+ * \test Test the timing out of a flow with a TcpSession
+ * (with data segments) in normal mode.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowMgrTest02 (void)
+{
+ TcpSession ssn;
+ Flow f;
+ FlowBucket fb;
+ struct timeval ts;
+ TcpSegment seg;
+ TcpStream client;
+ uint8_t payload[3] = {0x41, 0x41, 0x41};
+
+ FlowQueueInit(&flow_spare_q);
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&f, 0, sizeof(Flow));
+ memset(&fb, 0, sizeof(FlowBucket));
+ memset(&ts, 0, sizeof(ts));
+ memset(&seg, 0, sizeof(TcpSegment));
+ memset(&client, 0, sizeof(TcpSegment));
+
+ FBLOCK_INIT(&fb);
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
+
+ TimeGet(&ts);
+ seg.payload = payload;
+ seg.payload_len = 3;
+ seg.next = NULL;
+ seg.prev = NULL;
+ client.seg_list = &seg;
+ ssn.client = client;
+ ssn.server = client;
+ ssn.state = TCP_ESTABLISHED;
+ f.lastts.tv_sec = ts.tv_sec - 5000;
+ f.protoctx = &ssn;
+ f.fb = &fb;
+ f.proto = IPPROTO_TCP;
+
+ int state = SC_ATOMIC_GET(f.flow_state);
+ if (FlowManagerFlowTimeout(&f, state, &ts, 0) != 1 && FlowManagerFlowTimedOut(&f, &ts) != 1) {
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 0;
+ }
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 1;
+
+}
+
+/**
+ * \test Test the timing out of a flow with a fresh TcpSession
+ * (just initialized, no data segments) in emergency mode.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowMgrTest03 (void)
+{
+ TcpSession ssn;
+ Flow f;
+ FlowBucket fb;
+ struct timeval ts;
+
+ FlowQueueInit(&flow_spare_q);
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ts, 0, sizeof(ts));
+ memset(&fb, 0, sizeof(FlowBucket));
+
+ FBLOCK_INIT(&fb);
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
+
+ TimeGet(&ts);
+ ssn.state = TCP_SYN_SENT;
+ f.lastts.tv_sec = ts.tv_sec - 300;
+ f.protoctx = &ssn;
+ f.fb = &fb;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_EMERGENCY;
+
+ int state = SC_ATOMIC_GET(f.flow_state);
+ if (FlowManagerFlowTimeout(&f, state, &ts, 0) != 1 && FlowManagerFlowTimedOut(&f, &ts) != 1) {
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 0;
+ }
+
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 1;
+}
+
+/**
+ * \test Test the timing out of a flow with a TcpSession
+ * (with data segments) in emergency mode.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowMgrTest04 (void)
+{
+
+ TcpSession ssn;
+ Flow f;
+ FlowBucket fb;
+ struct timeval ts;
+ TcpSegment seg;
+ TcpStream client;
+ uint8_t payload[3] = {0x41, 0x41, 0x41};
+
+ FlowQueueInit(&flow_spare_q);
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&f, 0, sizeof(Flow));
+ memset(&fb, 0, sizeof(FlowBucket));
+ memset(&ts, 0, sizeof(ts));
+ memset(&seg, 0, sizeof(TcpSegment));
+ memset(&client, 0, sizeof(TcpSegment));
+
+ FBLOCK_INIT(&fb);
+ FLOW_INITIALIZE(&f);
+ f.flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
+
+ TimeGet(&ts);
+ seg.payload = payload;
+ seg.payload_len = 3;
+ seg.next = NULL;
+ seg.prev = NULL;
+ client.seg_list = &seg;
+ ssn.client = client;
+ ssn.server = client;
+ ssn.state = TCP_ESTABLISHED;
+ f.lastts.tv_sec = ts.tv_sec - 5000;
+ f.protoctx = &ssn;
+ f.fb = &fb;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_EMERGENCY;
+
+ int state = SC_ATOMIC_GET(f.flow_state);
+ if (FlowManagerFlowTimeout(&f, state, &ts, 0) != 1 && FlowManagerFlowTimedOut(&f, &ts) != 1) {
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 0;
+ }
+
+ FBLOCK_DESTROY(&fb);
+ FLOW_DESTROY(&f);
+ FlowQueueDestroy(&flow_spare_q);
+ return 1;
+}
+
+/**
+ * \test Test flow allocations when it reach memcap
+ *
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowMgrTest05 (void)
+{
+ int result = 0;
+
+ FlowInitConfig(FLOW_QUIET);
+ FlowConfig backup;
+ memcpy(&backup, &flow_config, sizeof(FlowConfig));
+
+ uint32_t ini = 0;
+ uint32_t end = flow_spare_q.len;
+ flow_config.memcap = 10000;
+ flow_config.prealloc = 100;
+
+ /* Let's get the flow_spare_q empty */
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* And now let's try to reach the memcap val */
+ while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+ }
+
+ /* should time out normal */
+ TimeSetIncrementTime(2000);
+ ini = end + 1;
+ end = end + 2;;
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ struct timeval ts;
+ TimeGet(&ts);
+ /* try to time out flows */
+ FlowTimeoutCounters counters = { 0, 0, 0, 0, };
+ FlowTimeoutHash(&ts, 0 /* check all */, 0, flow_config.hash_size, &counters);
+
+ if (flow_recycle_q.len > 0) {
+ result = 1;
+ }
+
+ memcpy(&flow_config, &backup, sizeof(FlowConfig));
+ FlowShutdown();
+ return result;
+}
+#endif /* UNITTESTS */
+
+/**
+ * \brief Function to register the Flow Unitests.
+ */
+void FlowMgrRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlowMgrTest01 -- Timeout a flow having fresh TcpSession", FlowMgrTest01, 1);
+ UtRegisterTest("FlowMgrTest02 -- Timeout a flow having TcpSession with segments", FlowMgrTest02, 1);
+ UtRegisterTest("FlowMgrTest03 -- Timeout a flow in emergency having fresh TcpSession", FlowMgrTest03, 1);
+ UtRegisterTest("FlowMgrTest04 -- Timeout a flow in emergency having TcpSession with segments", FlowMgrTest04, 1);
+ UtRegisterTest("FlowMgrTest05 -- Test flow Allocations when it reach memcap", FlowMgrTest05, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/flow-manager.h b/framework/src/suricata/src/flow-manager.h
new file mode 100644
index 00000000..32ae2b26
--- /dev/null
+++ b/framework/src/suricata/src/flow-manager.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __FLOW_MANAGER_H__
+#define __FLOW_MANAGER_H__
+
+/** flow manager scheduling condition */
+SCCtrlCondT flow_manager_ctrl_cond;
+SCCtrlMutex flow_manager_ctrl_mutex;
+#define FlowWakeupFlowManagerThread() SCCtrlCondSignal(&flow_manager_ctrl_cond)
+
+void FlowManagerThreadSpawn(void);
+void FlowDisableFlowManagerThread(void);
+void FlowMgrRegisterTests (void);
+
+/** flow recycler scheduling condition */
+SCCtrlCondT flow_recycler_ctrl_cond;
+SCCtrlMutex flow_recycler_ctrl_mutex;
+#define FlowWakeupFlowRecyclerThread() \
+ SCCtrlCondSignal(&flow_recycler_ctrl_cond)
+
+void FlowRecyclerThreadSpawn(void);
+void FlowDisableFlowRecyclerThread(void);
+
+void TmModuleFlowManagerRegister (void);
+void TmModuleFlowRecyclerRegister (void);
+
+#endif /* __FLOW_MANAGER_H__ */
diff --git a/framework/src/suricata/src/flow-private.h b/framework/src/suricata/src/flow-private.h
new file mode 100644
index 00000000..bd25960b
--- /dev/null
+++ b/framework/src/suricata/src/flow-private.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_PRIVATE_H__
+#define __FLOW_PRIVATE_H__
+
+#include "flow-hash.h"
+#include "flow-queue.h"
+
+#include "util-atomic.h"
+
+/* global flow flags */
+
+/** Flow engine is in emergency mode. This means it doesn't have enough spare
+ * flows for new flows and/or it's memcap limit it reached. In this state the
+ * flow engine with evaluate flows with lower timeout settings. */
+#define FLOW_EMERGENCY 0x01
+
+/* Flow Time out values */
+#define FLOW_DEFAULT_NEW_TIMEOUT 30
+#define FLOW_DEFAULT_EST_TIMEOUT 300
+#define FLOW_DEFAULT_CLOSED_TIMEOUT 0
+#define FLOW_IPPROTO_TCP_NEW_TIMEOUT 30
+#define FLOW_IPPROTO_TCP_EST_TIMEOUT 300
+#define FLOW_IPPROTO_UDP_NEW_TIMEOUT 30
+#define FLOW_IPPROTO_UDP_EST_TIMEOUT 300
+#define FLOW_IPPROTO_ICMP_NEW_TIMEOUT 30
+#define FLOW_IPPROTO_ICMP_EST_TIMEOUT 300
+
+#define FLOW_DEFAULT_EMERG_NEW_TIMEOUT 10
+#define FLOW_DEFAULT_EMERG_EST_TIMEOUT 100
+#define FLOW_DEFAULT_EMERG_CLOSED_TIMEOUT 0
+#define FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT 10
+#define FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT 100
+#define FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT 10
+#define FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT 100
+#define FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT 10
+#define FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT 100
+
+enum {
+ FLOW_PROTO_TCP = 0,
+ FLOW_PROTO_UDP,
+ FLOW_PROTO_ICMP,
+ FLOW_PROTO_SCTP,
+ FLOW_PROTO_DEFAULT,
+
+ /* should be last */
+ FLOW_PROTO_MAX,
+};
+
+/*
+ * Variables
+ */
+
+/** FlowProto specific timeouts and free/state functions */
+FlowProto flow_proto[FLOW_PROTO_MAX];
+
+/** spare/unused/prealloced flows live here */
+FlowQueue flow_spare_q;
+
+/** queue to pass flows to cleanup/log thread(s) */
+FlowQueue flow_recycle_q;
+
+FlowBucket *flow_hash;
+FlowConfig flow_config;
+
+/** flow memuse counter (atomic), for enforcing memcap limit */
+SC_ATOMIC_DECLARE(long long unsigned int, flow_memuse);
+
+//#define FLOWBITS_STATS
+#ifdef FLOWBITS_STATS
+uint64_t flowbits_memuse;
+uint64_t flowbits_memuse_max;
+uint32_t flowbits_added;
+uint32_t flowbits_removed;
+SCMutex flowbits_mutex;
+#endif /* FLOWBITS_STATS */
+
+#endif /* __FLOW_PRIVATE_H__ */
+
diff --git a/framework/src/suricata/src/flow-queue.c b/framework/src/suricata/src/flow-queue.c
new file mode 100644
index 00000000..b6a1138d
--- /dev/null
+++ b/framework/src/suricata/src/flow-queue.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow queue handler functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "flow-private.h"
+#include "flow-queue.h"
+#include "flow-util.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+FlowQueue *FlowQueueNew()
+{
+ FlowQueue *q = (FlowQueue *)SCMalloc(sizeof(FlowQueue));
+ if (q == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in FlowQueueNew. Exiting...");
+ exit(EXIT_SUCCESS);
+ }
+ q = FlowQueueInit(q);
+ return q;
+}
+
+FlowQueue *FlowQueueInit (FlowQueue *q)
+{
+ if (q != NULL) {
+ memset(q, 0, sizeof(FlowQueue));
+ FQLOCK_INIT(q);
+ }
+ return q;
+}
+
+/**
+ * \brief Destroy a flow queue
+ *
+ * \param q the flow queue to destroy
+ */
+void FlowQueueDestroy (FlowQueue *q)
+{
+ FQLOCK_DESTROY(q);
+}
+
+/**
+ * \brief add a flow to a queue
+ *
+ * \param q queue
+ * \param f flow
+ */
+void FlowEnqueue (FlowQueue *q, Flow *f)
+{
+#ifdef DEBUG
+ BUG_ON(q == NULL || f == NULL);
+#endif
+
+ FQLOCK_LOCK(q);
+
+ /* more flows in queue */
+ if (q->top != NULL) {
+ f->lnext = q->top;
+ q->top->lprev = f;
+ q->top = f;
+ /* only flow */
+ } else {
+ q->top = f;
+ q->bot = f;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ FQLOCK_UNLOCK(q);
+}
+
+/**
+ * \brief remove a flow from the queue
+ *
+ * \param q queue
+ *
+ * \retval f flow or NULL if empty list.
+ */
+Flow *FlowDequeue (FlowQueue *q)
+{
+ FQLOCK_LOCK(q);
+
+ Flow *f = q->bot;
+ if (f == NULL) {
+ FQLOCK_UNLOCK(q);
+ return NULL;
+ }
+
+ /* more packets in queue */
+ if (q->bot->lprev != NULL) {
+ q->bot = q->bot->lprev;
+ q->bot->lnext = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+#ifdef DEBUG
+ BUG_ON(q->len == 0);
+#endif
+ if (q->len > 0)
+ q->len--;
+
+ f->lnext = NULL;
+ f->lprev = NULL;
+
+ FQLOCK_UNLOCK(q);
+ return f;
+}
+
+/**
+ * \brief Transfer a flow from a queue to the spare queue
+ *
+ * \param f the flow to be transfered
+ * \param q the source queue, where the flow will be removed. This queue is locked.
+ *
+ * \note spare queue needs locking
+ */
+void FlowMoveToSpare(Flow *f)
+{
+ /* now put it in spare */
+ FQLOCK_LOCK(&flow_spare_q);
+
+ /* add to new queue (append) */
+ f->lprev = flow_spare_q.bot;
+ if (f->lprev != NULL)
+ f->lprev->lnext = f;
+ f->lnext = NULL;
+ flow_spare_q.bot = f;
+ if (flow_spare_q.top == NULL)
+ flow_spare_q.top = f;
+
+ flow_spare_q.len++;
+#ifdef DBG_PERF
+ if (flow_spare_q.len > flow_spare_q.dbg_maxlen)
+ flow_spare_q.dbg_maxlen = flow_spare_q.len;
+#endif /* DBG_PERF */
+
+ FQLOCK_UNLOCK(&flow_spare_q);
+}
+
diff --git a/framework/src/suricata/src/flow-queue.h b/framework/src/suricata/src/flow-queue.h
new file mode 100644
index 00000000..b370cd22
--- /dev/null
+++ b/framework/src/suricata/src/flow-queue.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_QUEUE_H__
+#define __FLOW_QUEUE_H__
+
+#include "suricata-common.h"
+#include "flow.h"
+
+/** Spinlocks or Mutex for the flow queues. */
+//#define FQLOCK_SPIN
+#define FQLOCK_MUTEX
+
+#ifdef FQLOCK_SPIN
+ #ifdef FQLOCK_MUTEX
+ #error Cannot enable both FQLOCK_SPIN and FQLOCK_MUTEX
+ #endif
+#endif
+
+/* Define a queue for storing flows */
+typedef struct FlowQueue_
+{
+ Flow *top;
+ Flow *bot;
+ uint32_t len;
+#ifdef DBG_PERF
+ uint32_t dbg_maxlen;
+#endif /* DBG_PERF */
+#ifdef FQLOCK_MUTEX
+ SCMutex m;
+#elif defined FQLOCK_SPIN
+ SCSpinlock s;
+#else
+ #error Enable FQLOCK_SPIN or FQLOCK_MUTEX
+#endif
+} FlowQueue;
+
+#ifdef FQLOCK_SPIN
+ #define FQLOCK_INIT(q) SCSpinInit(&(q)->s, 0)
+ #define FQLOCK_DESTROY(q) SCSpinDestroy(&(q)->s)
+ #define FQLOCK_LOCK(q) SCSpinLock(&(q)->s)
+ #define FQLOCK_TRYLOCK(q) SCSpinTrylock(&(q)->s)
+ #define FQLOCK_UNLOCK(q) SCSpinUnlock(&(q)->s)
+#elif defined FQLOCK_MUTEX
+ #define FQLOCK_INIT(q) SCMutexInit(&(q)->m, NULL)
+ #define FQLOCK_DESTROY(q) SCMutexDestroy(&(q)->m)
+ #define FQLOCK_LOCK(q) SCMutexLock(&(q)->m)
+ #define FQLOCK_TRYLOCK(q) SCMutexTrylock(&(q)->m)
+ #define FQLOCK_UNLOCK(q) SCMutexUnlock(&(q)->m)
+#else
+ #error Enable FQLOCK_SPIN or FQLOCK_MUTEX
+#endif
+
+/* prototypes */
+FlowQueue *FlowQueueNew();
+FlowQueue *FlowQueueInit(FlowQueue *);
+void FlowQueueDestroy (FlowQueue *);
+
+void FlowEnqueue (FlowQueue *, Flow *);
+Flow *FlowDequeue (FlowQueue *);
+
+void FlowMoveToSpare(Flow *);
+
+#endif /* __FLOW_QUEUE_H__ */
+
diff --git a/framework/src/suricata/src/flow-storage.c b/framework/src/suricata/src/flow-storage.c
new file mode 100644
index 00000000..e4f4f8ae
--- /dev/null
+++ b/framework/src/suricata/src/flow-storage.c
@@ -0,0 +1,296 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * based on host-storage by Victor Julien <victor@inliniac.net>
+ *
+ * Flow wrapper around storage api
+ */
+
+#include "suricata-common.h"
+#include "host-storage.h"
+#include "flow-hash.h"
+#include "flow-util.h"
+#include "util-unittest.h"
+
+unsigned int FlowStorageSize(void)
+{
+ return StorageGetSize(STORAGE_FLOW);
+}
+
+void *FlowGetStorageById(Flow *f, int id)
+{
+ return StorageGetById((Storage *)((void *)f + sizeof(Flow)), STORAGE_FLOW, id);
+}
+
+int FlowSetStorageById(Flow *f, int id, void *ptr)
+{
+ return StorageSetById((Storage *)((void *)f + sizeof(Flow)), STORAGE_FLOW, id, ptr);
+}
+
+void *FlowAllocStorageById(Flow *f, int id)
+{
+ return StorageAllocByIdPrealloc((Storage *)((void *)f + sizeof(Flow)), STORAGE_FLOW, id);
+}
+
+void FlowFreeStorageById(Flow *f, int id)
+{
+ StorageFreeById((Storage *)((void *)f + sizeof(Flow)), STORAGE_FLOW, id);
+}
+
+void FlowFreeStorage(Flow *f)
+{
+ if (FlowStorageSize() > 0)
+ StorageFreeAll((Storage *)((void *)f + sizeof(Flow)), STORAGE_FLOW);
+}
+
+int FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *)) {
+ return StorageRegister(STORAGE_FLOW, name, size, Alloc, Free);
+}
+
+#ifdef UNITTESTS
+
+static void *StorageTestAlloc(unsigned int size)
+{
+ void *x = SCMalloc(size);
+ return x;
+}
+static void StorageTestFree(void *x)
+{
+ if (x)
+ SCFree(x);
+}
+
+static int FlowStorageTest01(void)
+{
+ Flow *f = NULL;
+
+ StorageInit();
+
+ int id1 = FlowStorageRegister("test", 8, StorageTestAlloc, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = FlowStorageRegister("variable", 24, StorageTestAlloc, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = FlowStorageRegister("store", sizeof(void *), StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ FlowInitConfig(FLOW_QUIET);
+
+ f = FlowAlloc();
+ if (f == NULL) {
+ goto error;
+ }
+
+ void *ptr = FlowGetStorageById(f, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = FlowGetStorageById(f, id2);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = FlowGetStorageById(f, id3);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = FlowAllocStorageById(f, id1);
+ if (ptr1a == NULL) {
+ goto error;
+ }
+ void *ptr2a = FlowAllocStorageById(f, id2);
+ if (ptr2a == NULL) {
+ goto error;
+ }
+ void *ptr3a = FlowAllocStorageById(f, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = FlowGetStorageById(f, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = FlowGetStorageById(f, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = FlowGetStorageById(f, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ FlowShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ if (f != NULL) {
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ }
+ FlowShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int FlowStorageTest02(void)
+{
+ Flow *f = NULL;
+
+ StorageInit();
+
+ int id1 = FlowStorageRegister("test", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ FlowInitConfig(FLOW_QUIET);
+ f = FlowAlloc();
+ if (f == NULL) {
+ goto error;
+ }
+
+ void *ptr = FlowGetStorageById(f, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ FlowSetStorageById(f, id1, ptr1a);
+
+ void *ptr1b = FlowGetStorageById(f, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+
+
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ FlowShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ if (f != NULL) {
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ }
+ FlowShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int FlowStorageTest03(void)
+{
+ Flow *f = NULL;
+
+ StorageInit();
+
+ int id1 = FlowStorageRegister("test1", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = FlowStorageRegister("test2", sizeof(void *), NULL, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = FlowStorageRegister("test3", 32, StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ FlowInitConfig(FLOW_QUIET);
+ f = FlowAlloc();
+ if (f == NULL) {
+ goto error;
+ }
+
+ void *ptr = FlowGetStorageById(f, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ FlowSetStorageById(f, id1, ptr1a);
+
+ void *ptr2a = SCMalloc(256);
+ if (unlikely(ptr2a == NULL)) {
+ goto error;
+ }
+ FlowSetStorageById(f, id2, ptr2a);
+
+ void *ptr3a = FlowAllocStorageById(f, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = FlowGetStorageById(f, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = FlowGetStorageById(f, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = FlowGetStorageById(f, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ FlowShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ if (f != NULL) {
+ FlowClearMemory(f, 0);
+ FlowFree(f);
+ }
+ FlowShutdown();
+ StorageCleanup();
+ return 0;
+}
+#endif
+
+void RegisterFlowStorageTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlowStorageTest01", FlowStorageTest01, 1);
+ UtRegisterTest("FlowStorageTest02", FlowStorageTest02, 1);
+ UtRegisterTest("FlowStorageTest03", FlowStorageTest03, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/flow-storage.h b/framework/src/suricata/src/flow-storage.h
new file mode 100644
index 00000000..93892d16
--- /dev/null
+++ b/framework/src/suricata/src/flow-storage.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow wrapper around storage api
+ */
+
+#ifndef __FLOW_STORAGE_H__
+#define __FLOW_STORAGE_H__
+
+#include "util-storage.h"
+#include "flow.h"
+
+unsigned int FlowStorageSize(void);
+
+void *FlowGetStorageById(Flow *h, int id);
+int FlowSetStorageById(Flow *h, int id, void *ptr);
+void *FlowAllocStorageById(Flow *h, int id);
+
+void FlowFreeStorageById(Flow *h, int id);
+void FlowFreeStorage(Flow *h);
+
+void RegisterFlowStorageTests(void);
+
+int FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *));
+
+#endif /* __FLOW_STORAGE_H__ */
diff --git a/framework/src/suricata/src/flow-timeout.c b/framework/src/suricata/src/flow-timeout.c
new file mode 100644
index 00000000..8df85cdd
--- /dev/null
+++ b/framework/src/suricata/src/flow-timeout.c
@@ -0,0 +1,572 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "conf.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "runmodes.h"
+
+#include "util-random.h"
+#include "util-time.h"
+
+#include "flow.h"
+#include "flow-queue.h"
+#include "flow-hash.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "flow-private.h"
+#include "flow-manager.h"
+#include "pkt-var.h"
+#include "host.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+
+#include "util-debug.h"
+#include "util-privs.h"
+
+#include "detect.h"
+#include "detect-engine-state.h"
+#include "stream.h"
+
+#include "app-layer-parser.h"
+#include "app-layer.h"
+
+#include "util-profiling.h"
+
+/**
+ * \internal
+ * \brief Pseudo packet setup for flow forced reassembly.
+ *
+ * \param direction Direction of the packet. 0 indicates toserver and 1
+ * indicates toclient.
+ * \param f Pointer to the flow.
+ * \param ssn Pointer to the tcp session.
+ * \param dummy Indicates to create a dummy pseudo packet. Not all pseudo
+ * packets need to force reassembly, in which case we just
+ * set dummy ack/seq values.
+ */
+static inline Packet *FlowForceReassemblyPseudoPacketSetup(Packet *p,
+ int direction,
+ Flow *f,
+ TcpSession *ssn,
+ int dummy)
+{
+ p->tenant_id = f->tenant_id;
+ p->datalink = DLT_RAW;
+ p->proto = IPPROTO_TCP;
+ FlowReference(&p->flow, f);
+ p->flags |= PKT_STREAM_EST;
+ p->flags |= PKT_STREAM_EOF;
+ p->flags |= PKT_HAS_FLOW;
+ p->flags |= PKT_PSEUDO_STREAM_END;
+
+ if (f->flags & FLOW_NOPACKET_INSPECTION) {
+ DecodeSetNoPacketInspectionFlag(p);
+ }
+ if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
+ DecodeSetNoPayloadInspectionFlag(p);
+ }
+
+ if (direction == 0)
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ else
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->payload = NULL;
+ p->payload_len = 0;
+
+ if (FLOW_IS_IPV4(f)) {
+ if (direction == 0) {
+ FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, &p->src);
+ FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, &p->dst);
+ p->sp = f->sp;
+ p->dp = f->dp;
+ } else {
+ FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, &p->dst);
+ FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, &p->src);
+ p->sp = f->dp;
+ p->dp = f->sp;
+ }
+
+ /* Check if we have enough room in direct data. We need ipv4 hdr + tcp hdr.
+ * Force an allocation if it is not the case.
+ */
+ if (GET_PKT_DIRECT_MAX_SIZE(p) < 40) {
+ if (PacketCallocExtPkt(p, 40) == -1) {
+ return NULL;
+ }
+ }
+ /* set the ip header */
+ p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ /* version 4 and length 20 bytes for the tcp header */
+ p->ip4h->ip_verhl = 0x45;
+ p->ip4h->ip_tos = 0;
+ p->ip4h->ip_len = htons(40);
+ p->ip4h->ip_id = 0;
+ p->ip4h->ip_off = 0;
+ p->ip4h->ip_ttl = 64;
+ p->ip4h->ip_proto = IPPROTO_TCP;
+ //p->ip4h->ip_csum =
+ if (direction == 0) {
+ p->ip4h->s_ip_src.s_addr = f->src.addr_data32[0];
+ p->ip4h->s_ip_dst.s_addr = f->dst.addr_data32[0];
+ } else {
+ p->ip4h->s_ip_src.s_addr = f->dst.addr_data32[0];
+ p->ip4h->s_ip_dst.s_addr = f->src.addr_data32[0];
+ }
+
+ /* set the tcp header */
+ p->tcph = (TCPHdr *)((uint8_t *)GET_PKT_DATA(p) + 20);
+
+ SET_PKT_LEN(p, 40); /* ipv4 hdr + tcp hdr */
+
+ } else if (FLOW_IS_IPV6(f)) {
+ if (direction == 0) {
+ FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, &p->src);
+ FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, &p->dst);
+ p->sp = f->sp;
+ p->dp = f->dp;
+ } else {
+ FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, &p->dst);
+ FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, &p->src);
+ p->sp = f->dp;
+ p->dp = f->sp;
+ }
+
+ /* Check if we have enough room in direct data. We need ipv6 hdr + tcp hdr.
+ * Force an allocation if it is not the case.
+ */
+ if (GET_PKT_DIRECT_MAX_SIZE(p) < 60) {
+ if (PacketCallocExtPkt(p, 60) == -1) {
+ return NULL;
+ }
+ }
+ /* set the ip header */
+ p->ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+ /* version 6 */
+ p->ip6h->s_ip6_vfc = 0x60;
+ p->ip6h->s_ip6_flow = 0;
+ p->ip6h->s_ip6_nxt = IPPROTO_TCP;
+ p->ip6h->s_ip6_plen = htons(20);
+ p->ip6h->s_ip6_hlim = 64;
+ if (direction == 0) {
+ p->ip6h->s_ip6_src[0] = f->src.addr_data32[0];
+ p->ip6h->s_ip6_src[1] = f->src.addr_data32[1];
+ p->ip6h->s_ip6_src[2] = f->src.addr_data32[2];
+ p->ip6h->s_ip6_src[3] = f->src.addr_data32[3];
+ p->ip6h->s_ip6_dst[0] = f->dst.addr_data32[0];
+ p->ip6h->s_ip6_dst[1] = f->dst.addr_data32[1];
+ p->ip6h->s_ip6_dst[2] = f->dst.addr_data32[2];
+ p->ip6h->s_ip6_dst[3] = f->dst.addr_data32[3];
+ } else {
+ p->ip6h->s_ip6_src[0] = f->dst.addr_data32[0];
+ p->ip6h->s_ip6_src[1] = f->dst.addr_data32[1];
+ p->ip6h->s_ip6_src[2] = f->dst.addr_data32[2];
+ p->ip6h->s_ip6_src[3] = f->dst.addr_data32[3];
+ p->ip6h->s_ip6_dst[0] = f->src.addr_data32[0];
+ p->ip6h->s_ip6_dst[1] = f->src.addr_data32[1];
+ p->ip6h->s_ip6_dst[2] = f->src.addr_data32[2];
+ p->ip6h->s_ip6_dst[3] = f->src.addr_data32[3];
+ }
+
+ /* set the tcp header */
+ p->tcph = (TCPHdr *)((uint8_t *)GET_PKT_DATA(p) + 40);
+
+ SET_PKT_LEN(p, 60); /* ipv6 hdr + tcp hdr */
+ }
+
+ p->tcph->th_offx2 = 0x50;
+ p->tcph->th_flags |= TH_ACK;
+ p->tcph->th_win = 10;
+ p->tcph->th_urp = 0;
+
+ /* to server */
+ if (direction == 0) {
+ p->tcph->th_sport = htons(f->sp);
+ p->tcph->th_dport = htons(f->dp);
+
+ if (dummy) {
+ p->tcph->th_seq = htonl(ssn->client.next_seq);
+ p->tcph->th_ack = htonl(ssn->server.last_ack);
+ } else {
+ p->tcph->th_seq = htonl(ssn->client.next_seq);
+ p->tcph->th_ack = htonl(ssn->server.seg_list_tail->seq +
+ ssn->server.seg_list_tail->payload_len);
+ }
+
+ /* to client */
+ } else {
+ p->tcph->th_sport = htons(f->dp);
+ p->tcph->th_dport = htons(f->sp);
+
+ if (dummy) {
+ p->tcph->th_seq = htonl(ssn->server.next_seq);
+ p->tcph->th_ack = htonl(ssn->client.last_ack);
+ } else {
+ p->tcph->th_seq = htonl(ssn->server.next_seq);
+ p->tcph->th_ack = htonl(ssn->client.seg_list_tail->seq +
+ ssn->client.seg_list_tail->payload_len);
+ }
+ }
+
+ if (FLOW_IS_IPV4(f)) {
+ p->tcph->th_sum = TCPCalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->tcph, 20);
+ /* calc ipv4 csum as we may log it and barnyard might reject
+ * a wrong checksum */
+ p->ip4h->ip_csum = IPV4CalculateChecksum((uint16_t *)p->ip4h,
+ IPV4_GET_RAW_HLEN(p->ip4h));
+ } else if (FLOW_IS_IPV6(f)) {
+ p->tcph->th_sum = TCPCalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->tcph, 20);
+ }
+
+ memset(&p->ts, 0, sizeof(struct timeval));
+ TimeGet(&p->ts);
+
+ AppLayerParserSetEOF(f->alparser);
+
+ return p;
+}
+
+static inline Packet *FlowForceReassemblyPseudoPacketGet(int direction,
+ Flow *f,
+ TcpSession *ssn,
+ int dummy)
+{
+ PacketPoolWait();
+ Packet *p = PacketPoolGetPacket();
+ if (p == NULL) {
+ return NULL;
+ }
+
+ PACKET_PROFILING_START(p);
+
+ return FlowForceReassemblyPseudoPacketSetup(p, direction, f, ssn, dummy);
+}
+
+/**
+ * \brief Check if a flow needs forced reassembly, or any other processing
+ *
+ * \param f *LOCKED* flow
+ * \param server ptr to int that should be set to 1 or 2 if we return 1
+ * \param client ptr to int that should be set to 1 or 2 if we return 1
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int FlowForceReassemblyNeedReassembly(Flow *f, int *server, int *client)
+{
+ TcpSession *ssn;
+
+ if (f == NULL) {
+ *server = *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NONE;
+ SCReturnInt(0);
+ }
+
+ /* Get the tcp session for the flow */
+ ssn = (TcpSession *)f->protoctx;
+ if (ssn == NULL) {
+ *server = *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NONE;
+ SCReturnInt(0);
+ }
+
+ *client = StreamNeedsReassembly(ssn, 0);
+ *server = StreamNeedsReassembly(ssn, 1);
+
+ /* if state is not fully closed we assume that we haven't fully
+ * inspected the app layer state yet */
+ if (ssn->state >= TCP_ESTABLISHED && ssn->state != TCP_CLOSED)
+ {
+ if (*client != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
+ *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+
+ if (*server != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
+ *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ }
+
+ /* if app layer still needs some love, push through */
+ if (f->alproto != ALPROTO_UNKNOWN && f->alstate != NULL &&
+ AppLayerParserProtocolSupportsTxs(f->proto, f->alproto))
+ {
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, f->alproto, f->alstate);
+
+ if (AppLayerParserGetTransactionActive(f->proto, f->alproto,
+ f->alparser, STREAM_TOCLIENT) < total_txs)
+ {
+ if (*server != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
+ *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ }
+ if (AppLayerParserGetTransactionActive(f->proto, f->alproto,
+ f->alparser, STREAM_TOSERVER) < total_txs)
+ {
+ if (*client != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
+ *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ }
+ }
+
+ /* nothing to do */
+ if (*client == STREAM_HAS_UNPROCESSED_SEGMENTS_NONE &&
+ *server == STREAM_HAS_UNPROCESSED_SEGMENTS_NONE) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \internal
+ * \brief Forces reassembly for flow if it needs it.
+ *
+ * The function requires flow to be locked beforehand.
+ *
+ * \param f Pointer to the flow.
+ * \param server action required for server: 1 or 2
+ * \param client action required for client: 1 or 2
+ *
+ * \retval 0 This flow doesn't need any reassembly processing; 1 otherwise.
+ */
+int FlowForceReassemblyForFlow(Flow *f, int server, int client)
+{
+ Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ TcpSession *ssn;
+
+ /* looks like we have no flows in this queue */
+ if (f == NULL) {
+ return 0;
+ }
+
+ /* Get the tcp session for the flow */
+ ssn = (TcpSession *)f->protoctx;
+ if (ssn == NULL) {
+ return 0;
+ }
+
+ /* The packets we use are based on what segments in what direction are
+ * unprocessed.
+ * p1 if we have client segments for reassembly purpose only. If we
+ * have no server segments p2 can be a toserver packet with dummy
+ * seq/ack, and if we have server segments p2 has to carry out reassembly
+ * for server segment as well, in which case we will also need a p3 in the
+ * toclient which is now dummy since all we need it for is detection */
+
+ /* insert a pseudo packet in the toserver direction */
+ if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
+ p1 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 0);
+ if (p1 == NULL) {
+ goto done;
+ }
+ PKT_SET_SRC(p1, PKT_SRC_FFR);
+
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
+ p2 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
+ if (p2 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ goto done;
+ }
+ PKT_SET_SRC(p2, PKT_SRC_FFR);
+
+ p3 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
+ if (p3 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ FlowDeReference(&p2->flow);
+ TmqhOutputPacketpool(NULL, p2);
+ goto done;
+ }
+ PKT_SET_SRC(p3, PKT_SRC_FFR);
+ } else {
+ p2 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 1);
+ if (p2 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ goto done;
+ }
+ PKT_SET_SRC(p2, PKT_SRC_FFR);
+ }
+
+ } else if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
+ p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
+ if (p1 == NULL) {
+ goto done;
+ }
+ PKT_SET_SRC(p1, PKT_SRC_FFR);
+
+ p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
+ if (p2 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ goto done;
+ }
+ PKT_SET_SRC(p2, PKT_SRC_FFR);
+ } else {
+ p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 1);
+ if (p1 == NULL) {
+ goto done;
+ }
+ PKT_SET_SRC(p1, PKT_SRC_FFR);
+
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
+ p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
+ if (p2 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ goto done;
+ }
+ PKT_SET_SRC(p2, PKT_SRC_FFR);
+ }
+ }
+
+ } else {
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
+ p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
+ if (p1 == NULL) {
+ goto done;
+ }
+ PKT_SET_SRC(p1, PKT_SRC_FFR);
+
+ p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
+ if (p2 == NULL) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ goto done;
+ }
+ PKT_SET_SRC(p2, PKT_SRC_FFR);
+ } else if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
+ p1 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
+ if (p1 == NULL) {
+ goto done;
+ }
+ PKT_SET_SRC(p1, PKT_SRC_FFR);
+ } else {
+ /* impossible */
+ BUG_ON(1);
+ }
+ }
+
+ /* inject the packet(s) into the appropriate thread */
+ int thread_id = (int)f->thread_id;
+ Packet *packets[4] = { p1, p2 ? p2 : p3, p2 ? p3 : NULL, NULL }; /**< null terminated array of packets */
+ if (unlikely(!(TmThreadsInjectPacketsById(packets, thread_id)))) {
+ FlowDeReference(&p1->flow);
+ TmqhOutputPacketpool(NULL, p1);
+ if (p2) {
+ FlowDeReference(&p2->flow);
+ TmqhOutputPacketpool(NULL, p2);
+ }
+ if (p3) {
+ FlowDeReference(&p3->flow);
+ TmqhOutputPacketpool(NULL, p3);
+ }
+ }
+
+ /* done, in case of error (no packet) we still tag flow as complete
+ * as we're probably resource stress if we couldn't get packets */
+done:
+ f->flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief Forces reassembly for flows that need it.
+ *
+ * When this function is called we're running in virtually dead engine,
+ * so locking the flows is not strictly required. The reasons it is still
+ * done are:
+ * - code consistency
+ * - silence complaining profilers
+ * - allow us to aggressively check using debug valdation assertions
+ * - be robust in case of future changes
+ * - locking overhead if neglectable when no other thread fights us
+ *
+ * \param q The queue to process flows from.
+ */
+static inline void FlowForceReassemblyForHash(void)
+{
+ Flow *f;
+ TcpSession *ssn;
+ int client_ok = 0;
+ int server_ok = 0;
+ uint32_t idx = 0;
+
+ for (idx = 0; idx < flow_config.hash_size; idx++) {
+ FlowBucket *fb = &flow_hash[idx];
+
+ PacketPoolWaitForN(9);
+ FBLOCK_LOCK(fb);
+
+ /* get the topmost flow from the QUEUE */
+ f = fb->head;
+
+ /* we need to loop through all the flows in the queue */
+ while (f != NULL) {
+ PacketPoolWaitForN(3);
+
+ FLOWLOCK_WRLOCK(f);
+
+ /* Get the tcp session for the flow */
+ ssn = (TcpSession *)f->protoctx;
+
+ /* \todo Also skip flows that shouldn't be inspected */
+ if (ssn == NULL) {
+ FLOWLOCK_UNLOCK(f);
+ f = f->hnext;
+ continue;
+ }
+
+ if (FlowForceReassemblyNeedReassembly(f, &server_ok, &client_ok) == 1) {
+ FlowForceReassemblyForFlow(f, server_ok, client_ok);
+ }
+
+ FLOWLOCK_UNLOCK(f);
+
+ /* next flow in the queue */
+ f = f->hnext;
+ }
+ FBLOCK_UNLOCK(fb);
+ }
+ return;
+}
+
+/**
+ * \brief Force reassembly for all the flows that have unprocessed segments.
+ */
+void FlowForceReassembly(void)
+{
+ /* Carry out flow reassembly for unattended flows */
+ FlowForceReassemblyForHash();
+ return;
+}
+
diff --git a/framework/src/suricata/src/flow-timeout.h b/framework/src/suricata/src/flow-timeout.h
new file mode 100644
index 00000000..50e007ae
--- /dev/null
+++ b/framework/src/suricata/src/flow-timeout.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __FLOW_TIMEOUT_H__
+#define __FLOW_TIMEOUT_H__
+
+int FlowForceReassemblyForFlow(Flow *f, int server, int client);
+int FlowForceReassemblyNeedReassembly(Flow *f, int *server, int *client);
+void FlowForceReassembly(void);
+void FlowForceReassemblySetup(int detect_disabled);
+
+#endif /* __FLOW_TIMEOUT_H__ */
diff --git a/framework/src/suricata/src/flow-util.c b/framework/src/suricata/src/flow-util.c
new file mode 100644
index 00000000..b4b54c18
--- /dev/null
+++ b/framework/src/suricata/src/flow-util.c
@@ -0,0 +1,179 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow utility functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+
+#include "flow.h"
+#include "flow-private.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "app-layer.h"
+
+#include "util-var.h"
+#include "util-debug.h"
+#include "flow-storage.h"
+
+#include "detect.h"
+#include "detect-engine-state.h"
+
+/** \brief allocate a flow
+ *
+ * We check against the memuse counter. If it passes that check we increment
+ * the counter first, then we try to alloc.
+ *
+ * \retval f the flow or NULL on out of memory
+ */
+Flow *FlowAlloc(void)
+{
+ Flow *f;
+ size_t size = sizeof(Flow) + FlowStorageSize();
+
+ if (!(FLOW_CHECK_MEMCAP(size))) {
+ return NULL;
+ }
+
+ (void) SC_ATOMIC_ADD(flow_memuse, size);
+
+ f = SCMalloc(size);
+ if (unlikely(f == NULL)) {
+ (void)SC_ATOMIC_SUB(flow_memuse, size);
+ return NULL;
+ }
+ memset(f, 0, size);
+
+ FLOW_INITIALIZE(f);
+ return f;
+}
+
+
+/**
+ * \brief cleanup & free the memory of a flow
+ *
+ * \param f flow to clear & destroy
+ */
+void FlowFree(Flow *f)
+{
+ FLOW_DESTROY(f);
+ SCFree(f);
+
+ size_t size = sizeof(Flow) + FlowStorageSize();
+ (void) SC_ATOMIC_SUB(flow_memuse, size);
+}
+
+/**
+ * \brief Function to map the protocol to the defined FLOW_PROTO_* enumeration.
+ *
+ * \param proto protocol which is needed to be mapped
+ */
+
+uint8_t FlowGetProtoMapping(uint8_t proto)
+{
+ switch (proto) {
+ case IPPROTO_TCP:
+ return FLOW_PROTO_TCP;
+ case IPPROTO_UDP:
+ return FLOW_PROTO_UDP;
+ case IPPROTO_ICMP:
+ return FLOW_PROTO_ICMP;
+ case IPPROTO_SCTP:
+ return FLOW_PROTO_SCTP;
+ default:
+ return FLOW_PROTO_DEFAULT;
+ }
+}
+
+uint8_t FlowGetReverseProtoMapping(uint8_t rproto)
+{
+ switch (rproto) {
+ case FLOW_PROTO_TCP:
+ return IPPROTO_TCP;
+ case FLOW_PROTO_UDP:
+ return IPPROTO_UDP;
+ case FLOW_PROTO_ICMP:
+ return IPPROTO_ICMP;
+ case FLOW_PROTO_SCTP:
+ return IPPROTO_SCTP;
+ default:
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* initialize the flow from the first packet
+ * we see from it. */
+void FlowInit(Flow *f, const Packet *p)
+{
+ SCEnter();
+ SCLogDebug("flow %p", f);
+
+ f->proto = p->proto;
+ f->recursion_level = p->recursion_level;
+ f->vlan_id[0] = p->vlan_id[0];
+ f->vlan_id[1] = p->vlan_id[1];
+
+ if (PKT_IS_IPV4(p)) {
+ FLOW_SET_IPV4_SRC_ADDR_FROM_PACKET(p, &f->src);
+ FLOW_SET_IPV4_DST_ADDR_FROM_PACKET(p, &f->dst);
+ f->flags |= FLOW_IPV4;
+ } else if (PKT_IS_IPV6(p)) {
+ FLOW_SET_IPV6_SRC_ADDR_FROM_PACKET(p, &f->src);
+ FLOW_SET_IPV6_DST_ADDR_FROM_PACKET(p, &f->dst);
+ f->flags |= FLOW_IPV6;
+ }
+#ifdef DEBUG
+ /* XXX handle default */
+ else {
+ printf("FIXME: %s:%s:%" PRId32 "\n", __FILE__, __FUNCTION__, __LINE__);
+ }
+#endif
+
+ if (p->tcph != NULL) { /* XXX MACRO */
+ SET_TCP_SRC_PORT(p,&f->sp);
+ SET_TCP_DST_PORT(p,&f->dp);
+ } else if (p->udph != NULL) { /* XXX MACRO */
+ SET_UDP_SRC_PORT(p,&f->sp);
+ SET_UDP_DST_PORT(p,&f->dp);
+ } else if (p->icmpv4h != NULL) {
+ f->type = p->type;
+ f->code = p->code;
+ } else if (p->icmpv6h != NULL) {
+ f->type = p->type;
+ f->code = p->code;
+ } else if (p->sctph != NULL) { /* XXX MACRO */
+ SET_SCTP_SRC_PORT(p,&f->sp);
+ SET_SCTP_DST_PORT(p,&f->dp);
+ } /* XXX handle default */
+#ifdef DEBUG
+ else {
+ printf("FIXME: %s:%s:%" PRId32 "\n", __FILE__, __FUNCTION__, __LINE__);
+ }
+#endif
+ COPY_TIMESTAMP(&p->ts, &f->startts);
+
+ f->protomap = FlowGetProtoMapping(f->proto);
+
+ SCReturn;
+}
+
diff --git a/framework/src/suricata/src/flow-util.h b/framework/src/suricata/src/flow-util.h
new file mode 100644
index 00000000..ca6a49cc
--- /dev/null
+++ b/framework/src/suricata/src/flow-util.h
@@ -0,0 +1,153 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_UTIL_H__
+#define __FLOW_UTIL_H__
+
+#include "detect-engine-state.h"
+#include "tmqh-flow.h"
+
+#define COPY_TIMESTAMP(src,dst) ((dst)->tv_sec = (src)->tv_sec, (dst)->tv_usec = (src)->tv_usec)
+
+#define RESET_COUNTERS(f) do { \
+ (f)->todstpktcnt = 0; \
+ (f)->tosrcpktcnt = 0; \
+ (f)->todstbytecnt = 0; \
+ (f)->tosrcbytecnt = 0; \
+ } while (0)
+
+#define FLOW_INITIALIZE(f) do { \
+ (f)->sp = 0; \
+ (f)->dp = 0; \
+ (f)->proto = 0; \
+ SC_ATOMIC_INIT((f)->flow_state); \
+ SC_ATOMIC_INIT((f)->use_cnt); \
+ (f)->tenant_id = 0; \
+ (f)->probing_parser_toserver_alproto_masks = 0; \
+ (f)->probing_parser_toclient_alproto_masks = 0; \
+ (f)->flags = 0; \
+ (f)->lastts.tv_sec = 0; \
+ (f)->lastts.tv_usec = 0; \
+ FLOWLOCK_INIT((f)); \
+ (f)->protoctx = NULL; \
+ (f)->flow_end_flags = 0; \
+ (f)->alproto = 0; \
+ (f)->alproto_ts = 0; \
+ (f)->alproto_tc = 0; \
+ (f)->data_al_so_far[0] = 0; \
+ (f)->data_al_so_far[1] = 0; \
+ (f)->de_ctx_id = 0; \
+ (f)->thread_id = 0; \
+ (f)->detect_alversion[0] = 0; \
+ (f)->detect_alversion[1] = 0; \
+ (f)->alparser = NULL; \
+ (f)->alstate = NULL; \
+ (f)->de_state = NULL; \
+ (f)->sgh_toserver = NULL; \
+ (f)->sgh_toclient = NULL; \
+ (f)->flowvar = NULL; \
+ (f)->hnext = NULL; \
+ (f)->hprev = NULL; \
+ (f)->lnext = NULL; \
+ (f)->lprev = NULL; \
+ SC_ATOMIC_INIT((f)->autofp_tmqh_flow_qid); \
+ (void) SC_ATOMIC_SET((f)->autofp_tmqh_flow_qid, -1); \
+ RESET_COUNTERS((f)); \
+ } while (0)
+
+/** \brief macro to recycle a flow before it goes into the spare queue for reuse.
+ *
+ * Note that the lnext, lprev, hnext, hprev fields are untouched, those are
+ * managed by the queueing code. Same goes for fb (FlowBucket ptr) field.
+ */
+#define FLOW_RECYCLE(f) do { \
+ FlowCleanupAppLayer((f)); \
+ (f)->sp = 0; \
+ (f)->dp = 0; \
+ (f)->proto = 0; \
+ SC_ATOMIC_RESET((f)->flow_state); \
+ SC_ATOMIC_RESET((f)->use_cnt); \
+ (f)->tenant_id = 0; \
+ (f)->probing_parser_toserver_alproto_masks = 0; \
+ (f)->probing_parser_toclient_alproto_masks = 0; \
+ (f)->flags = 0; \
+ (f)->lastts.tv_sec = 0; \
+ (f)->lastts.tv_usec = 0; \
+ (f)->protoctx = NULL; \
+ (f)->flow_end_flags = 0; \
+ (f)->alparser = NULL; \
+ (f)->alstate = NULL; \
+ (f)->alproto = 0; \
+ (f)->alproto_ts = 0; \
+ (f)->alproto_tc = 0; \
+ (f)->data_al_so_far[0] = 0; \
+ (f)->data_al_so_far[1] = 0; \
+ (f)->de_ctx_id = 0; \
+ (f)->thread_id = 0; \
+ (f)->detect_alversion[0] = 0; \
+ (f)->detect_alversion[1] = 0; \
+ if ((f)->de_state != NULL) { \
+ DetectEngineStateReset((f)->de_state, (STREAM_TOSERVER | STREAM_TOCLIENT)); \
+ } \
+ (f)->sgh_toserver = NULL; \
+ (f)->sgh_toclient = NULL; \
+ GenericVarFree((f)->flowvar); \
+ (f)->flowvar = NULL; \
+ if (SC_ATOMIC_GET((f)->autofp_tmqh_flow_qid) != -1) { \
+ (void) SC_ATOMIC_SET((f)->autofp_tmqh_flow_qid, -1); \
+ } \
+ RESET_COUNTERS((f)); \
+ } while(0)
+
+#define FLOW_DESTROY(f) do { \
+ FlowCleanupAppLayer((f)); \
+ SC_ATOMIC_DESTROY((f)->flow_state); \
+ SC_ATOMIC_DESTROY((f)->use_cnt); \
+ \
+ FLOWLOCK_DESTROY((f)); \
+ if ((f)->de_state != NULL) { \
+ DetectEngineStateFlowFree((f)->de_state); \
+ } \
+ GenericVarFree((f)->flowvar); \
+ SC_ATOMIC_DESTROY((f)->autofp_tmqh_flow_qid); \
+ } while(0)
+
+/** \brief check if a memory alloc would fit in the memcap
+ *
+ * \param size memory allocation size to check
+ *
+ * \retval 1 it fits
+ * \retval 0 no fit
+ */
+#define FLOW_CHECK_MEMCAP(size) \
+ ((((uint64_t)SC_ATOMIC_GET(flow_memuse) + (uint64_t)(size)) <= flow_config.memcap))
+
+Flow *FlowAlloc(void);
+Flow *FlowAllocDirect(void);
+void FlowFree(Flow *);
+uint8_t FlowGetProtoMapping(uint8_t);
+void FlowInit(Flow *, const Packet *);
+uint8_t FlowGetReverseProtoMapping(uint8_t rproto);
+
+#endif /* __FLOW_UTIL_H__ */
+
diff --git a/framework/src/suricata/src/flow-var.c b/framework/src/suricata/src/flow-var.c
new file mode 100644
index 00000000..5262b5a7
--- /dev/null
+++ b/framework/src/suricata/src/flow-var.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Flow level variable support for complex detection rules
+ * Supported types atm are String and Integers
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "flow-var.h"
+#include "flow.h"
+#include "detect.h"
+#include "util-debug.h"
+
+/* puts a new value into a flowvar */
+static void FlowVarUpdateStr(FlowVar *fv, uint8_t *value, uint16_t size)
+{
+ if (fv->data.fv_str.value)
+ SCFree(fv->data.fv_str.value);
+ fv->data.fv_str.value = value;
+ fv->data.fv_str.value_len = size;
+}
+
+/* puts a new value into a flowvar */
+static void FlowVarUpdateInt(FlowVar *fv, uint32_t value)
+{
+ fv->data.fv_int.value = value;
+}
+
+/** \brief get the flowvar with index 'idx' from the flow
+ * \note flow is not locked by this function, caller is
+ * responsible
+ */
+FlowVar *FlowVarGet(Flow *f, uint16_t idx)
+{
+ GenericVar *gv = f->flowvar;
+
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_FLOWVAR && gv->idx == idx)
+ return (FlowVar *)gv;
+ }
+
+ return NULL;
+}
+
+/* add a flowvar to the flow, or update it */
+void FlowVarAddStrNoLock(Flow *f, uint16_t idx, uint8_t *value, uint16_t size)
+{
+ FlowVar *fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ fv = SCMalloc(sizeof(FlowVar));
+ if (unlikely(fv == NULL))
+ return;
+
+ fv->type = DETECT_FLOWVAR;
+ fv->datatype = FLOWVAR_TYPE_STR;
+ fv->idx = idx;
+ fv->data.fv_str.value = value;
+ fv->data.fv_str.value_len = size;
+ fv->next = NULL;
+
+ GenericVarAppend(&f->flowvar, (GenericVar *)fv);
+ } else {
+ FlowVarUpdateStr(fv, value, size);
+ }
+}
+
+/* add a flowvar to the flow, or update it */
+void FlowVarAddStr(Flow *f, uint16_t idx, uint8_t *value, uint16_t size)
+{
+ FLOWLOCK_WRLOCK(f);
+ FlowVarAddStrNoLock(f, idx, value, size);
+ FLOWLOCK_UNLOCK(f);
+}
+
+/* add a flowvar to the flow, or update it */
+void FlowVarAddIntNoLock(Flow *f, uint16_t idx, uint32_t value)
+{
+ FlowVar *fv = FlowVarGet(f, idx);
+ if (fv == NULL) {
+ fv = SCMalloc(sizeof(FlowVar));
+ if (unlikely(fv == NULL))
+ return;
+
+ fv->type = DETECT_FLOWVAR;
+ fv->datatype = FLOWVAR_TYPE_INT;
+ fv->idx = idx;
+ fv->data.fv_int.value= value;
+ fv->next = NULL;
+
+ GenericVarAppend(&f->flowvar, (GenericVar *)fv);
+ } else {
+ FlowVarUpdateInt(fv, value);
+ }
+}
+
+/* add a flowvar to the flow, or update it */
+void FlowVarAddInt(Flow *f, uint16_t idx, uint32_t value)
+{
+ FLOWLOCK_WRLOCK(f);
+ FlowVarAddIntNoLock(f, idx, value);
+ FLOWLOCK_UNLOCK(f);
+}
+
+void FlowVarFree(FlowVar *fv)
+{
+ if (fv == NULL)
+ return;
+
+ if (fv->datatype == FLOWVAR_TYPE_STR) {
+ if (fv->data.fv_str.value != NULL)
+ SCFree(fv->data.fv_str.value);
+ }
+ SCFree(fv);
+}
+
+void FlowVarPrint(GenericVar *gv)
+{
+ uint16_t u;
+
+ if (!SCLogDebugEnabled())
+ return;
+
+ if (gv == NULL)
+ return;
+
+ if (gv->type == DETECT_FLOWVAR || gv->type == DETECT_FLOWINT) {
+ FlowVar *fv = (FlowVar *)gv;
+
+ if (fv->datatype == FLOWVAR_TYPE_STR) {
+ SCLogDebug("Name idx \"%" PRIu16 "\", Value \"", fv->idx);
+ for (u = 0; u < fv->data.fv_str.value_len; u++) {
+ if (isprint(fv->data.fv_str.value[u]))
+ SCLogDebug("%c", fv->data.fv_str.value[u]);
+ else
+ SCLogDebug("\\%02X", fv->data.fv_str.value[u]);
+ }
+ SCLogDebug("\", Len \"%" PRIu16 "\"\n", fv->data.fv_str.value_len);
+ } else if (fv->datatype == FLOWVAR_TYPE_INT) {
+ SCLogDebug("Name idx \"%" PRIu16 "\", Value \"%" PRIu32 "\"", fv->idx,
+ fv->data.fv_int.value);
+ } else {
+ SCLogDebug("Unknown data type at flowvars\n");
+ }
+ }
+ FlowVarPrint(gv->next);
+}
+
diff --git a/framework/src/suricata/src/flow-var.h b/framework/src/suricata/src/flow-var.h
new file mode 100644
index 00000000..e45d8030
--- /dev/null
+++ b/framework/src/suricata/src/flow-var.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __FLOW_VAR_H__
+#define __FLOW_VAR_H__
+
+#include "flow.h"
+#include "util-var.h"
+
+/** Available data types for Flowvars */
+
+#define FLOWVAR_TYPE_STR 1
+#define FLOWVAR_TYPE_INT 2
+
+/** Struct used to hold the string data type for flowvars */
+typedef struct FlowVarTypeStr {
+ uint8_t *value;
+ uint16_t value_len;
+} FlowVarTypeStr;
+
+/** Struct used to hold the integer data type for flowvars */
+typedef struct FlowVarTypeInt_ {
+ uint32_t value;
+} FlowVarTypeInt;
+
+/** Generic Flowvar Structure */
+typedef struct FlowVar_ {
+ uint8_t type; /* type, DETECT_FLOWVAR in this case */
+ uint16_t idx; /* name idx */
+ GenericVar *next; /* right now just implement this as a list,
+ * in the long run we have think of something
+ * faster. */
+ uint8_t datatype;
+ union {
+ FlowVarTypeStr fv_str;
+ FlowVarTypeInt fv_int;
+ } data;
+
+} FlowVar;
+
+/** Flowvar Interface API */
+
+void FlowVarAddStrNoLock(Flow *, uint16_t, uint8_t *, uint16_t);
+void FlowVarAddStr(Flow *, uint16_t, uint8_t *, uint16_t);
+void FlowVarAddIntNoLock(Flow *, uint16_t, uint32_t);
+void FlowVarAddInt(Flow *, uint16_t, uint32_t);
+FlowVar *FlowVarGet(Flow *, uint16_t);
+void FlowVarFree(FlowVar *);
+void FlowVarPrint(GenericVar *);
+
+#endif /* __FLOW_VAR_H__ */
+
diff --git a/framework/src/suricata/src/flow.c b/framework/src/suricata/src/flow.c
new file mode 100644
index 00000000..42099c3a
--- /dev/null
+++ b/framework/src/suricata/src/flow.c
@@ -0,0 +1,1147 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow implementation.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "conf.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "runmodes.h"
+
+#include "util-random.h"
+#include "util-time.h"
+
+#include "flow.h"
+#include "flow-queue.h"
+#include "flow-hash.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "flow-private.h"
+#include "flow-timeout.h"
+#include "flow-manager.h"
+#include "flow-storage.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+#include "util-misc.h"
+
+#include "util-debug.h"
+#include "util-privs.h"
+
+#include "detect.h"
+#include "detect-engine-state.h"
+#include "stream.h"
+
+#include "app-layer-parser.h"
+
+#define FLOW_DEFAULT_EMERGENCY_RECOVERY 30
+
+//#define FLOW_DEFAULT_HASHSIZE 262144
+#define FLOW_DEFAULT_HASHSIZE 65536
+//#define FLOW_DEFAULT_MEMCAP 128 * 1024 * 1024 /* 128 MB */
+#define FLOW_DEFAULT_MEMCAP (32 * 1024 * 1024) /* 32 MB */
+
+#define FLOW_DEFAULT_PREALLOC 10000
+
+/** atomic int that is used when freeing a flow from the hash. In this
+ * case we walk the hash to find a flow to free. This var records where
+ * we left off in the hash. Without this only the top rows of the hash
+ * are freed. This isn't just about fairness. Under severe presure, the
+ * hash rows on top would be all freed and the time to find a flow to
+ * free increased with every run. */
+SC_ATOMIC_DECLARE(unsigned int, flow_prune_idx);
+
+/** atomic flags */
+SC_ATOMIC_DECLARE(unsigned int, flow_flags);
+
+void FlowRegisterTests(void);
+void FlowInitFlowProto();
+int FlowSetProtoTimeout(uint8_t , uint32_t ,uint32_t ,uint32_t);
+int FlowSetProtoEmergencyTimeout(uint8_t , uint32_t ,uint32_t ,uint32_t);
+int FlowSetProtoFreeFunc(uint8_t, void (*Free)(void *));
+
+/* Run mode selected at suricata.c */
+extern int run_mode;
+
+void FlowCleanupAppLayer(Flow *f)
+{
+ if (f == NULL || f->proto == 0)
+ return;
+
+ AppLayerParserStateCleanup(f->proto, f->alproto, f->alstate, f->alparser);
+ f->alstate = NULL;
+ f->alparser = NULL;
+ return;
+}
+
+/** \brief Make sure we have enough spare flows.
+ *
+ * Enforce the prealloc parameter, so keep at least prealloc flows in the
+ * spare queue and free flows going over the limit.
+ *
+ * \retval 1 if the queue was properly updated (or if it already was in good shape)
+ * \retval 0 otherwise.
+ */
+int FlowUpdateSpareFlows(void)
+{
+ SCEnter();
+ uint32_t toalloc = 0, tofree = 0, len;
+
+ FQLOCK_LOCK(&flow_spare_q);
+ len = flow_spare_q.len;
+ FQLOCK_UNLOCK(&flow_spare_q);
+
+ if (len < flow_config.prealloc) {
+ toalloc = flow_config.prealloc - len;
+
+ uint32_t i;
+ for (i = 0; i < toalloc; i++) {
+ Flow *f = FlowAlloc();
+ if (f == NULL)
+ return 0;
+
+ FlowEnqueue(&flow_spare_q,f);
+ }
+ } else if (len > flow_config.prealloc) {
+ tofree = len - flow_config.prealloc;
+
+ uint32_t i;
+ for (i = 0; i < tofree; i++) {
+ /* FlowDequeue locks the queue */
+ Flow *f = FlowDequeue(&flow_spare_q);
+ if (f == NULL)
+ return 1;
+
+ FlowFree(f);
+ }
+ }
+
+ return 1;
+}
+
+/** \brief Set the IPOnly scanned flag for 'direction'. This function
+ * handles the locking too.
+ * \param f Flow to set the flag in
+ * \param direction direction to set the flag in
+ */
+void FlowSetIPOnlyFlag(Flow *f, char direction)
+{
+ FLOWLOCK_WRLOCK(f);
+ direction ? (f->flags |= FLOW_TOSERVER_IPONLY_SET) :
+ (f->flags |= FLOW_TOCLIENT_IPONLY_SET);
+ FLOWLOCK_UNLOCK(f);
+ return;
+}
+
+/** \brief Set the IPOnly scanned flag for 'direction'.
+ *
+ * \param f Flow to set the flag in
+ * \param direction direction to set the flag in
+ */
+void FlowSetIPOnlyFlagNoLock(Flow *f, char direction)
+{
+ direction ? (f->flags |= FLOW_TOSERVER_IPONLY_SET) :
+ (f->flags |= FLOW_TOCLIENT_IPONLY_SET);
+ return;
+}
+
+/**
+ * \brief determine the direction of the packet compared to the flow
+ * \retval 0 to_server
+ * \retval 1 to_client
+ */
+int FlowGetPacketDirection(const Flow *f, const Packet *p)
+{
+ if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
+ if (!(CMP_PORT(p->sp,p->dp))) {
+ /* update flags and counters */
+ if (CMP_PORT(f->sp,p->sp)) {
+ return TOSERVER;
+ } else {
+ return TOCLIENT;
+ }
+ } else {
+ if (CMP_ADDR(&f->src,&p->src)) {
+ return TOSERVER;
+ } else {
+ return TOCLIENT;
+ }
+ }
+ } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) {
+ if (CMP_ADDR(&f->src,&p->src)) {
+ return TOSERVER;
+ } else {
+ return TOCLIENT;
+ }
+ }
+
+ /* default to toserver */
+ return TOSERVER;
+}
+
+/**
+ * \brief Check to update "seen" flags
+ *
+ * \param p packet
+ *
+ * \retval 1 true
+ * \retval 0 false
+ */
+static inline int FlowUpdateSeenFlag(const Packet *p)
+{
+ if (PKT_IS_ICMPV4(p)) {
+ if (ICMPV4_IS_ERROR_MSG(p)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ *
+ * Remove packet from flow. This assumes this happens *before* the packet
+ * is added to the stream engine and other higher state.
+ *
+ * \todo we can't restore the lastts
+ */
+void FlowHandlePacketUpdateRemove(Flow *f, Packet *p)
+{
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ f->todstpktcnt--;
+ f->todstbytecnt -= GET_PKT_LEN(p);
+ p->flowflags &= ~(FLOW_PKT_TOSERVER|FLOW_PKT_TOSERVER_FIRST);
+ } else {
+ f->tosrcpktcnt--;
+ f->tosrcbytecnt -= GET_PKT_LEN(p);
+ p->flowflags &= ~(FLOW_PKT_TOCLIENT|FLOW_PKT_TOCLIENT_FIRST);
+ }
+ p->flowflags &= ~FLOW_PKT_ESTABLISHED;
+
+ /*set the detection bypass flags*/
+ if (f->flags & FLOW_NOPACKET_INSPECTION) {
+ SCLogDebug("unsetting FLOW_NOPACKET_INSPECTION flag on flow %p", f);
+ DecodeUnsetNoPacketInspectionFlag(p);
+ }
+ if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
+ SCLogDebug("unsetting FLOW_NOPAYLOAD_INSPECTION flag on flow %p", f);
+ DecodeUnsetNoPayloadInspectionFlag(p);
+ }
+}
+
+/** \brief Update Packet and Flow
+ *
+ * Updates packet and flow based on the new packet.
+ *
+ * \param f locked flow
+ * \param p packet
+ *
+ * \note overwrites p::flowflags
+ */
+void FlowHandlePacketUpdate(Flow *f, Packet *p)
+{
+ SCLogDebug("packet %"PRIu64" -- flow %p", p->pcap_cnt, f);
+
+ /* Point the Packet at the Flow */
+ FlowReference(&p->flow, f);
+
+ /* update flags and counters */
+ if (FlowGetPacketDirection(f, p) == TOSERVER) {
+ f->todstpktcnt++;
+ f->todstbytecnt += GET_PKT_LEN(p);
+ p->flowflags = FLOW_PKT_TOSERVER;
+ if (!(f->flags & FLOW_TO_DST_SEEN)) {
+ if (FlowUpdateSeenFlag(p)) {
+ f->flags |= FLOW_TO_DST_SEEN;
+ p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
+ }
+ }
+ } else {
+ f->tosrcpktcnt++;
+ f->tosrcbytecnt += GET_PKT_LEN(p);
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (!(f->flags & FLOW_TO_SRC_SEEN)) {
+ if (FlowUpdateSeenFlag(p)) {
+ f->flags |= FLOW_TO_SRC_SEEN;
+ p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
+ }
+ }
+ }
+
+ if ((f->flags & (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) == (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) {
+ SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ if (f->proto != IPPROTO_TCP) {
+ SC_ATOMIC_SET(f->flow_state, FLOW_STATE_ESTABLISHED);
+ }
+ }
+
+ /*set the detection bypass flags*/
+ if (f->flags & FLOW_NOPACKET_INSPECTION) {
+ SCLogDebug("setting FLOW_NOPACKET_INSPECTION flag on flow %p", f);
+ DecodeSetNoPacketInspectionFlag(p);
+ }
+ if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
+ SCLogDebug("setting FLOW_NOPAYLOAD_INSPECTION flag on flow %p", f);
+ DecodeSetNoPayloadInspectionFlag(p);
+ }
+}
+
+/** \brief Entry point for packet flow handling
+ *
+ * This is called for every packet.
+ *
+ * \param tv threadvars
+ * \param dtv decode thread vars (for flow output api thread data)
+ * \param p packet to handle flow for
+ */
+void FlowHandlePacket(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
+{
+ /* Get this packet's flow from the hash. FlowHandlePacket() will setup
+ * a new flow if nescesary. If we get NULL, we're out of flow memory.
+ * The returned flow is locked. */
+ Flow *f = FlowGetFlowFromHash(tv, dtv, p);
+ if (f == NULL)
+ return;
+
+ FlowHandlePacketUpdate(f, p);
+
+ FLOWLOCK_UNLOCK(f);
+
+ /* set the flow in the packet */
+ p->flags |= PKT_HAS_FLOW;
+ return;
+}
+
+/** \brief initialize the configuration
+ * \warning Not thread safe */
+void FlowInitConfig(char quiet)
+{
+ SCLogDebug("initializing flow engine...");
+
+ memset(&flow_config, 0, sizeof(flow_config));
+ SC_ATOMIC_INIT(flow_flags);
+ SC_ATOMIC_INIT(flow_memuse);
+ SC_ATOMIC_INIT(flow_prune_idx);
+ FlowQueueInit(&flow_spare_q);
+ FlowQueueInit(&flow_recycle_q);
+
+ unsigned int seed = RandomTimePreseed();
+ /* set defaults */
+ flow_config.hash_rand = (int)( FLOW_DEFAULT_HASHSIZE * (rand_r(&seed) / RAND_MAX + 1.0));
+
+ flow_config.hash_size = FLOW_DEFAULT_HASHSIZE;
+ flow_config.memcap = FLOW_DEFAULT_MEMCAP;
+ flow_config.prealloc = FLOW_DEFAULT_PREALLOC;
+
+ /* If we have specific config, overwrite the defaults with them,
+ * otherwise, leave the default values */
+ intmax_t val = 0;
+ if (ConfGetInt("flow.emergency-recovery", &val) == 1) {
+ if (val <= 100 && val >= 1) {
+ flow_config.emergency_recovery = (uint8_t)val;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "flow.emergency-recovery must be in the range of 1 and 100 (as percentage)");
+ flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
+ }
+ } else {
+ SCLogDebug("flow.emergency-recovery, using default value");
+ flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
+ }
+
+ /* Check if we have memcap and hash_size defined at config */
+ char *conf_val;
+ uint32_t configval = 0;
+
+ /** set config values for memcap, prealloc and hash_size */
+ if ((ConfGet("flow.memcap", &conf_val)) == 1)
+ {
+ if (ParseSizeStringU64(conf_val, &flow_config.memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing flow.memcap "
+ "from conf file - %s. Killing engine",
+ conf_val);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if ((ConfGet("flow.hash-size", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ flow_config.hash_size = configval;
+ }
+ }
+ if ((ConfGet("flow.prealloc", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ flow_config.prealloc = configval;
+ }
+ }
+ SCLogDebug("Flow config from suricata.yaml: memcap: %"PRIu64", hash-size: "
+ "%"PRIu32", prealloc: %"PRIu32, flow_config.memcap,
+ flow_config.hash_size, flow_config.prealloc);
+
+ /* alloc hash memory */
+ uint64_t hash_size = flow_config.hash_size * sizeof(FlowBucket);
+ if (!(FLOW_CHECK_MEMCAP(hash_size))) {
+ SCLogError(SC_ERR_FLOW_INIT, "allocating flow hash failed: "
+ "max flow memcap is smaller than projected hash size. "
+ "Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate "
+ "total hash size by multiplying \"flow.hash-size\" with %"PRIuMAX", "
+ "which is the hash bucket size.", flow_config.memcap, hash_size,
+ (uintmax_t)sizeof(FlowBucket));
+ exit(EXIT_FAILURE);
+ }
+ flow_hash = SCCalloc(flow_config.hash_size, sizeof(FlowBucket));
+ if (unlikely(flow_hash == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in FlowInitConfig. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(flow_hash, 0, flow_config.hash_size * sizeof(FlowBucket));
+
+ uint32_t i = 0;
+ for (i = 0; i < flow_config.hash_size; i++) {
+ FBLOCK_INIT(&flow_hash[i]);
+ }
+ (void) SC_ATOMIC_ADD(flow_memuse, (flow_config.hash_size * sizeof(FlowBucket)));
+
+ if (quiet == FALSE) {
+ SCLogInfo("allocated %llu bytes of memory for the flow hash... "
+ "%" PRIu32 " buckets of size %" PRIuMAX "",
+ SC_ATOMIC_GET(flow_memuse), flow_config.hash_size,
+ (uintmax_t)sizeof(FlowBucket));
+ }
+
+ /* pre allocate flows */
+ for (i = 0; i < flow_config.prealloc; i++) {
+ if (!(FLOW_CHECK_MEMCAP(sizeof(Flow)))) {
+ SCLogError(SC_ERR_FLOW_INIT, "preallocating flows failed: "
+ "max flow memcap reached. Memcap %"PRIu64", "
+ "Memuse %"PRIu64".", flow_config.memcap,
+ ((uint64_t)SC_ATOMIC_GET(flow_memuse) + (uint64_t)sizeof(Flow)));
+ exit(EXIT_FAILURE);
+ }
+
+ Flow *f = FlowAlloc();
+ if (f == NULL) {
+ SCLogError(SC_ERR_FLOW_INIT, "preallocating flow failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ FlowEnqueue(&flow_spare_q,f);
+ }
+
+ if (quiet == FALSE) {
+ SCLogInfo("preallocated %" PRIu32 " flows of size %" PRIuMAX "",
+ flow_spare_q.len, (uintmax_t)sizeof(Flow));
+ SCLogInfo("flow memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(flow_memuse), flow_config.memcap);
+ }
+
+ FlowInitFlowProto();
+
+ return;
+}
+
+/** \brief print some flow stats
+ * \warning Not thread safe */
+static void FlowPrintStats (void)
+{
+#ifdef FLOWBITS_STATS
+ SCLogInfo("flowbits added: %" PRIu32 ", removed: %" PRIu32 ", max memory usage: %" PRIu32 "",
+ flowbits_added, flowbits_removed, flowbits_memuse_max);
+#endif /* FLOWBITS_STATS */
+ return;
+}
+
+/** \brief shutdown the flow engine
+ * \warning Not thread safe */
+void FlowShutdown(void)
+{
+ Flow *f;
+ uint32_t u;
+
+ FlowPrintStats();
+
+ /* free queues */
+ while((f = FlowDequeue(&flow_spare_q))) {
+ FlowFree(f);
+ }
+ while((f = FlowDequeue(&flow_recycle_q))) {
+ FlowFree(f);
+ }
+
+ /* clear and free the hash */
+ if (flow_hash != NULL) {
+ /* clean up flow mutexes */
+ for (u = 0; u < flow_config.hash_size; u++) {
+ Flow *f = flow_hash[u].head;
+ while (f) {
+#ifdef DEBUG_VALIDATION
+ BUG_ON(SC_ATOMIC_GET(f->use_cnt) != 0);
+#endif
+ Flow *n = f->hnext;
+ uint8_t proto_map = FlowGetProtoMapping(f->proto);
+ FlowClearMemory(f, proto_map);
+ FlowFree(f);
+ f = n;
+ }
+
+ FBLOCK_DESTROY(&flow_hash[u]);
+ }
+ SCFree(flow_hash);
+ flow_hash = NULL;
+ }
+ (void) SC_ATOMIC_SUB(flow_memuse, flow_config.hash_size * sizeof(FlowBucket));
+ FlowQueueDestroy(&flow_spare_q);
+ FlowQueueDestroy(&flow_recycle_q);
+
+ SC_ATOMIC_DESTROY(flow_prune_idx);
+ SC_ATOMIC_DESTROY(flow_memuse);
+ SC_ATOMIC_DESTROY(flow_flags);
+ return;
+}
+
+/**
+ * \brief Function to set the default timeout, free function and flow state
+ * function for all supported flow_proto.
+ */
+
+void FlowInitFlowProto(void)
+{
+ /*Default*/
+ flow_proto[FLOW_PROTO_DEFAULT].new_timeout = FLOW_DEFAULT_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].est_timeout = FLOW_DEFAULT_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].closed_timeout =
+ FLOW_DEFAULT_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_new_timeout =
+ FLOW_DEFAULT_EMERG_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_est_timeout =
+ FLOW_DEFAULT_EMERG_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_closed_timeout =
+ FLOW_DEFAULT_EMERG_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_DEFAULT].Freefunc = NULL;
+ /*TCP*/
+ flow_proto[FLOW_PROTO_TCP].new_timeout = FLOW_IPPROTO_TCP_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].est_timeout = FLOW_IPPROTO_TCP_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].closed_timeout = FLOW_DEFAULT_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].emerg_new_timeout =
+ FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].emerg_est_timeout =
+ FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].emerg_closed_timeout =
+ FLOW_DEFAULT_EMERG_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_TCP].Freefunc = NULL;
+ /*UDP*/
+ flow_proto[FLOW_PROTO_UDP].new_timeout = FLOW_IPPROTO_UDP_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].est_timeout = FLOW_IPPROTO_UDP_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].closed_timeout = FLOW_DEFAULT_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].emerg_new_timeout =
+ FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].emerg_est_timeout =
+ FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].emerg_closed_timeout =
+ FLOW_DEFAULT_EMERG_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_UDP].Freefunc = NULL;
+ /*ICMP*/
+ flow_proto[FLOW_PROTO_ICMP].new_timeout = FLOW_IPPROTO_ICMP_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].est_timeout = FLOW_IPPROTO_ICMP_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].closed_timeout = FLOW_DEFAULT_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].emerg_new_timeout =
+ FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].emerg_est_timeout =
+ FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].emerg_closed_timeout =
+ FLOW_DEFAULT_EMERG_CLOSED_TIMEOUT;
+ flow_proto[FLOW_PROTO_ICMP].Freefunc = NULL;
+
+ /* Let's see if we have custom timeouts defined from config */
+ const char *new = NULL;
+ const char *established = NULL;
+ const char *closed = NULL;
+ const char *emergency_new = NULL;
+ const char *emergency_established = NULL;
+ const char *emergency_closed = NULL;
+
+ ConfNode *flow_timeouts = ConfGetNode("flow-timeouts");
+ if (flow_timeouts != NULL) {
+ ConfNode *proto = NULL;
+ uint32_t configval = 0;
+
+ /* Defaults. */
+ proto = ConfNodeLookupChild(flow_timeouts, "default");
+ if (proto != NULL) {
+ new = ConfNodeLookupChildValue(proto, "new");
+ established = ConfNodeLookupChildValue(proto, "established");
+ closed = ConfNodeLookupChildValue(proto, "closed");
+ emergency_new = ConfNodeLookupChildValue(proto, "emergency-new");
+ emergency_established = ConfNodeLookupChildValue(proto,
+ "emergency-established");
+ emergency_closed = ConfNodeLookupChildValue(proto,
+ "emergency-closed");
+
+ if (new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(new), new) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].new_timeout = configval;
+ }
+ if (established != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(established),
+ established) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].est_timeout = configval;
+ }
+ if (closed != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(closed),
+ closed) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].closed_timeout = configval;
+ }
+ if (emergency_new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(emergency_new),
+ emergency_new) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_new_timeout = configval;
+ }
+ if (emergency_established != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_established),
+ emergency_established) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_est_timeout= configval;
+ }
+ if (emergency_closed != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_closed),
+ emergency_closed) > 0) {
+
+ flow_proto[FLOW_PROTO_DEFAULT].emerg_closed_timeout = configval;
+ }
+ }
+
+ /* TCP. */
+ proto = ConfNodeLookupChild(flow_timeouts, "tcp");
+ if (proto != NULL) {
+ new = ConfNodeLookupChildValue(proto, "new");
+ established = ConfNodeLookupChildValue(proto, "established");
+ closed = ConfNodeLookupChildValue(proto, "closed");
+ emergency_new = ConfNodeLookupChildValue(proto, "emergency-new");
+ emergency_established = ConfNodeLookupChildValue(proto,
+ "emergency-established");
+ emergency_closed = ConfNodeLookupChildValue(proto,
+ "emergency-closed");
+
+ if (new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(new), new) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].new_timeout = configval;
+ }
+ if (established != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(established),
+ established) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].est_timeout = configval;
+ }
+ if (closed != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(closed),
+ closed) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].closed_timeout = configval;
+ }
+ if (emergency_new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(emergency_new),
+ emergency_new) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].emerg_new_timeout = configval;
+ }
+ if (emergency_established != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_established),
+ emergency_established) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].emerg_est_timeout = configval;
+ }
+ if (emergency_closed != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_closed),
+ emergency_closed) > 0) {
+
+ flow_proto[FLOW_PROTO_TCP].emerg_closed_timeout = configval;
+ }
+ }
+
+ /* UDP. */
+ proto = ConfNodeLookupChild(flow_timeouts, "udp");
+ if (proto != NULL) {
+ new = ConfNodeLookupChildValue(proto, "new");
+ established = ConfNodeLookupChildValue(proto, "established");
+ emergency_new = ConfNodeLookupChildValue(proto, "emergency-new");
+ emergency_established = ConfNodeLookupChildValue(proto,
+ "emergency-established");
+ if (new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(new), new) > 0) {
+
+ flow_proto[FLOW_PROTO_UDP].new_timeout = configval;
+ }
+ if (established != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(established),
+ established) > 0) {
+
+ flow_proto[FLOW_PROTO_UDP].est_timeout = configval;
+ }
+ if (emergency_new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(emergency_new),
+ emergency_new) > 0) {
+
+ flow_proto[FLOW_PROTO_UDP].emerg_new_timeout = configval;
+ }
+ if (emergency_established != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_established),
+ emergency_established) > 0) {
+
+ flow_proto[FLOW_PROTO_UDP].emerg_est_timeout = configval;
+ }
+ }
+
+ /* ICMP. */
+ proto = ConfNodeLookupChild(flow_timeouts, "icmp");
+ if (proto != NULL) {
+ new = ConfNodeLookupChildValue(proto, "new");
+ established = ConfNodeLookupChildValue(proto, "established");
+ emergency_new = ConfNodeLookupChildValue(proto, "emergency-new");
+ emergency_established = ConfNodeLookupChildValue(proto,
+ "emergency-established");
+
+ if (new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(new), new) > 0) {
+
+ flow_proto[FLOW_PROTO_ICMP].new_timeout = configval;
+ }
+ if (established != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(established),
+ established) > 0) {
+
+ flow_proto[FLOW_PROTO_ICMP].est_timeout = configval;
+ }
+ if (emergency_new != NULL &&
+ ByteExtractStringUint32(&configval, 10, strlen(emergency_new),
+ emergency_new) > 0) {
+
+ flow_proto[FLOW_PROTO_ICMP].emerg_new_timeout = configval;
+ }
+ if (emergency_established != NULL &&
+ ByteExtractStringUint32(&configval, 10,
+ strlen(emergency_established),
+ emergency_established) > 0) {
+
+ flow_proto[FLOW_PROTO_ICMP].emerg_est_timeout = configval;
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Function clear the flow memory before queueing it to spare flow
+ * queue.
+ *
+ * \param f pointer to the flow needed to be cleared.
+ * \param proto_map mapped value of the protocol to FLOW_PROTO's.
+ */
+
+int FlowClearMemory(Flow* f, uint8_t proto_map)
+{
+ SCEnter();
+
+ /* call the protocol specific free function if we have one */
+ if (flow_proto[proto_map].Freefunc != NULL) {
+ flow_proto[proto_map].Freefunc(f->protoctx);
+ }
+
+ FlowFreeStorage(f);
+
+ FLOW_RECYCLE(f);
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief Function to set the function to get protocol specific flow state.
+ *
+ * \param proto protocol of which function is needed to be set.
+ * \param Free Function pointer which will be called to free the protocol
+ * specific memory.
+ */
+
+int FlowSetProtoFreeFunc (uint8_t proto, void (*Free)(void *))
+{
+ uint8_t proto_map;
+ proto_map = FlowGetProtoMapping(proto);
+
+ flow_proto[proto_map].Freefunc = Free;
+ return 1;
+}
+
+/**
+ * \brief Function to set the timeout values for the specified protocol.
+ *
+ * \param proto protocol of which timeout value is needed to be set.
+ * \param new_timeout timeout value for the new flows.
+ * \param est_timeout timeout value for the established flows.
+ * \param closed_timeout timeout value for the closed flows.
+ */
+
+int FlowSetProtoTimeout(uint8_t proto, uint32_t new_timeout,
+ uint32_t est_timeout, uint32_t closed_timeout)
+{
+ uint8_t proto_map;
+ proto_map = FlowGetProtoMapping(proto);
+
+ flow_proto[proto_map].new_timeout = new_timeout;
+ flow_proto[proto_map].est_timeout = est_timeout;
+ flow_proto[proto_map].closed_timeout = closed_timeout;
+
+ return 1;
+}
+
+/**
+ * \brief Function to set the emergency timeout values for the specified
+ * protocol.
+ *
+ * \param proto protocol of which timeout value is needed to be set.
+ * \param emerg_new_timeout timeout value for the new flows.
+ * \param emerg_est_timeout timeout value for the established flows.
+ * \param emerg_closed_timeout timeout value for the closed flows.
+ */
+
+int FlowSetProtoEmergencyTimeout(uint8_t proto, uint32_t emerg_new_timeout,
+ uint32_t emerg_est_timeout,
+ uint32_t emerg_closed_timeout)
+{
+
+ uint8_t proto_map;
+ proto_map = FlowGetProtoMapping(proto);
+
+ flow_proto[proto_map].emerg_new_timeout = emerg_new_timeout;
+ flow_proto[proto_map].emerg_est_timeout = emerg_est_timeout;
+ flow_proto[proto_map].emerg_closed_timeout = emerg_closed_timeout;
+
+ return 1;
+}
+
+AppProto FlowGetAppProtocol(const Flow *f)
+{
+ return f->alproto;
+}
+
+void *FlowGetAppState(const Flow *f)
+{
+ return f->alstate;
+}
+
+/**
+ * \brief get 'disruption' flags: GAP/DEPTH/PASS
+ * \param f locked flow
+ * \param flags existing flags to be ammended
+ * \retval flags original flags + disrupt flags (if any)
+ * \TODO handle UDP
+ */
+uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags)
+{
+ if (f->proto != IPPROTO_TCP) {
+ return flags;
+ }
+ if (f->protoctx == NULL) {
+ return flags;
+ }
+
+ uint8_t newflags = flags;
+ TcpSession *ssn = f->protoctx;
+ TcpStream *stream = flags & STREAM_TOSERVER ? &ssn->client : &ssn->server;
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ newflags |= STREAM_DEPTH;
+ }
+ if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
+ newflags |= STREAM_GAP;
+ }
+ /* todo: handle pass case (also for UDP!) */
+
+ return newflags;
+}
+
+/************************************Unittests*******************************/
+
+#ifdef UNITTESTS
+#include "stream-tcp-private.h"
+#include "threads.h"
+
+/**
+ * \test Test the setting of the per protocol timeouts.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowTest01 (void)
+{
+ uint8_t proto_map;
+
+ FlowInitFlowProto();
+ proto_map = FlowGetProtoMapping(IPPROTO_TCP);
+
+ if ((flow_proto[proto_map].new_timeout != FLOW_IPPROTO_TCP_NEW_TIMEOUT) && (flow_proto[proto_map].est_timeout != FLOW_IPPROTO_TCP_EST_TIMEOUT)
+ && (flow_proto[proto_map].emerg_new_timeout != FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT) && (flow_proto[proto_map].emerg_est_timeout != FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT)){
+ printf ("failed in setting TCP flow timeout");
+ return 0;
+ }
+
+ proto_map = FlowGetProtoMapping(IPPROTO_UDP);
+ if ((flow_proto[proto_map].new_timeout != FLOW_IPPROTO_UDP_NEW_TIMEOUT) && (flow_proto[proto_map].est_timeout != FLOW_IPPROTO_UDP_EST_TIMEOUT)
+ && (flow_proto[proto_map].emerg_new_timeout != FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT) && (flow_proto[proto_map].emerg_est_timeout != FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT)){
+ printf ("failed in setting UDP flow timeout");
+ return 0;
+ }
+
+ proto_map = FlowGetProtoMapping(IPPROTO_ICMP);
+ if ((flow_proto[proto_map].new_timeout != FLOW_IPPROTO_ICMP_NEW_TIMEOUT) && (flow_proto[proto_map].est_timeout != FLOW_IPPROTO_ICMP_EST_TIMEOUT)
+ && (flow_proto[proto_map].emerg_new_timeout != FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT) && (flow_proto[proto_map].emerg_est_timeout != FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT)){
+ printf ("failed in setting ICMP flow timeout");
+ return 0;
+ }
+
+ proto_map = FlowGetProtoMapping(IPPROTO_DCCP);
+ if ((flow_proto[proto_map].new_timeout != FLOW_DEFAULT_NEW_TIMEOUT) && (flow_proto[proto_map].est_timeout != FLOW_DEFAULT_EST_TIMEOUT)
+ && (flow_proto[proto_map].emerg_new_timeout != FLOW_DEFAULT_EMERG_NEW_TIMEOUT) && (flow_proto[proto_map].emerg_est_timeout != FLOW_DEFAULT_EMERG_EST_TIMEOUT)){
+ printf ("failed in setting default flow timeout");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*Test function for the unit test FlowTest02*/
+
+void test(void *f) {}
+
+/**
+ * \test Test the setting of the per protocol free function to free the
+ * protocol specific memory.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowTest02 (void)
+{
+ FlowSetProtoFreeFunc(IPPROTO_DCCP, test);
+ FlowSetProtoFreeFunc(IPPROTO_TCP, test);
+ FlowSetProtoFreeFunc(IPPROTO_UDP, test);
+ FlowSetProtoFreeFunc(IPPROTO_ICMP, test);
+
+ if (flow_proto[FLOW_PROTO_DEFAULT].Freefunc != test) {
+ printf("Failed in setting default free function\n");
+ return 0;
+ }
+ if (flow_proto[FLOW_PROTO_TCP].Freefunc != test) {
+ printf("Failed in setting TCP free function\n");
+ return 0;
+ }
+ if (flow_proto[FLOW_PROTO_UDP].Freefunc != test) {
+ printf("Failed in setting UDP free function\n");
+ return 0;
+ }
+ if (flow_proto[FLOW_PROTO_ICMP].Freefunc != test) {
+ printf("Failed in setting ICMP free function\n");
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * \test Test flow allocations when it reach memcap
+ *
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowTest07 (void)
+{
+ int result = 0;
+
+ FlowInitConfig(FLOW_QUIET);
+ FlowConfig backup;
+ memcpy(&backup, &flow_config, sizeof(FlowConfig));
+
+ uint32_t ini = 0;
+ uint32_t end = flow_spare_q.len;
+ flow_config.memcap = 10000;
+ flow_config.prealloc = 100;
+
+ /* Let's get the flow_spare_q empty */
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* And now let's try to reach the memcap val */
+ while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+ }
+
+ /* should time out normal */
+ TimeSetIncrementTime(2000);
+ ini = end + 1;
+ end = end + 2;;
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* This means that the engine entered emerg mode: should happen as easy
+ * with flow mgr activated */
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
+ result = 1;
+
+ memcpy(&flow_config, &backup, sizeof(FlowConfig));
+ FlowShutdown();
+
+ return result;
+}
+
+/**
+ * \test Test flow allocations when it reach memcap
+ *
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowTest08 (void)
+{
+ int result = 0;
+
+ FlowInitConfig(FLOW_QUIET);
+ FlowConfig backup;
+ memcpy(&backup, &flow_config, sizeof(FlowConfig));
+
+ uint32_t ini = 0;
+ uint32_t end = flow_spare_q.len;
+ flow_config.memcap = 10000;
+ flow_config.prealloc = 100;
+
+ /* Let's get the flow_spare_q empty */
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* And now let's try to reach the memcap val */
+ while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+ }
+
+ /* By default we use 30 for timing out new flows. This means
+ * that the Emergency mode should be set */
+ TimeSetIncrementTime(20);
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* This means that the engine released 5 flows by emergency timeout */
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
+ result = 1;
+
+ memcpy(&flow_config, &backup, sizeof(FlowConfig));
+ FlowShutdown();
+
+ return result;
+}
+
+/**
+ * \test Test flow allocations when it reach memcap
+ *
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int FlowTest09 (void)
+{
+ int result = 0;
+
+ FlowInitConfig(FLOW_QUIET);
+ FlowConfig backup;
+ memcpy(&backup, &flow_config, sizeof(FlowConfig));
+
+ uint32_t ini = 0;
+ uint32_t end = flow_spare_q.len;
+ flow_config.memcap = 10000;
+ flow_config.prealloc = 100;
+
+ /* Let's get the flow_spare_q empty */
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* And now let's try to reach the memcap val */
+ while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+ }
+
+ /* No timeout will work */
+ TimeSetIncrementTime(5);
+ ini = end + 1;
+ end = end + 2;
+ UTHBuildPacketOfFlows(ini, end, 0);
+
+ /* engine in emerg mode */
+ if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
+ result = 1;
+
+ memcpy(&flow_config, &backup, sizeof(FlowConfig));
+ FlowShutdown();
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief Function to register the Flow Unitests.
+ */
+void FlowRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("FlowTest01 -- Protocol Specific Timeouts", FlowTest01, 1);
+ UtRegisterTest("FlowTest02 -- Setting Protocol Specific Free Function", FlowTest02, 1);
+ UtRegisterTest("FlowTest07 -- Test flow Allocations when it reach memcap", FlowTest07, 1);
+ UtRegisterTest("FlowTest08 -- Test flow Allocations when it reach memcap", FlowTest08, 1);
+ UtRegisterTest("FlowTest09 -- Test flow Allocations when it reach memcap", FlowTest09, 1);
+
+ FlowMgrRegisterTests();
+ RegisterFlowStorageTests();
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/flow.h b/framework/src/suricata/src/flow.h
new file mode 100644
index 00000000..eab73776
--- /dev/null
+++ b/framework/src/suricata/src/flow.h
@@ -0,0 +1,584 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __FLOW_H__
+#define __FLOW_H__
+
+#include "decode.h"
+#include "util-var.h"
+#include "util-atomic.h"
+#include "detect-tag.h"
+#include "util-optimize.h"
+
+/* Part of the flow structure, so we declare it here.
+ * The actual declaration is in app-layer-parser.c */
+typedef struct AppLayerParserState_ AppLayerParserState;
+
+#define FLOW_QUIET TRUE
+#define FLOW_VERBOSE FALSE
+
+#define TOSERVER 0
+#define TOCLIENT 1
+
+/* per flow flags */
+
+/** At least on packet from the source address was seen */
+#define FLOW_TO_SRC_SEEN 0x00000001
+/** At least on packet from the destination address was seen */
+#define FLOW_TO_DST_SEEN 0x00000002
+/** Don't return this from the flow hash. It has been replaced. */
+#define FLOW_TCP_REUSED 0x00000004
+/** no magic on files in this flow */
+#define FLOW_FILE_NO_MAGIC_TS 0x00000008
+#define FLOW_FILE_NO_MAGIC_TC 0x00000010
+
+/** Flow was inspected against IP-Only sigs in the toserver direction */
+#define FLOW_TOSERVER_IPONLY_SET 0x00000020
+/** Flow was inspected against IP-Only sigs in the toclient direction */
+#define FLOW_TOCLIENT_IPONLY_SET 0x00000040
+
+/** Packet belonging to this flow should not be inspected at all */
+#define FLOW_NOPACKET_INSPECTION 0x00000080
+/** Packet payloads belonging to this flow should not be inspected */
+#define FLOW_NOPAYLOAD_INSPECTION 0x00000100
+
+/** All packets in this flow should be dropped */
+#define FLOW_ACTION_DROP 0x00000200
+
+/** Sgh for toserver direction set (even if it's NULL) */
+#define FLOW_SGH_TOSERVER 0x00000800
+/** Sgh for toclient direction set (even if it's NULL) */
+#define FLOW_SGH_TOCLIENT 0x00001000
+
+/** packet to server direction has been logged in drop file (only in IPS mode) */
+#define FLOW_TOSERVER_DROP_LOGGED 0x00002000
+/** packet to client direction has been logged in drop file (only in IPS mode) */
+#define FLOW_TOCLIENT_DROP_LOGGED 0x00004000
+/** alproto detect done. Right now we need it only for udp */
+#define FLOW_ALPROTO_DETECT_DONE 0x00008000
+
+// vacany 1x
+
+/** Pattern matcher alproto detection done */
+#define FLOW_TS_PM_ALPROTO_DETECT_DONE 0x00020000
+/** Probing parser alproto detection done */
+#define FLOW_TS_PP_ALPROTO_DETECT_DONE 0x00040000
+/** Pattern matcher alproto detection done */
+#define FLOW_TC_PM_ALPROTO_DETECT_DONE 0x00100000
+/** Probing parser alproto detection done */
+#define FLOW_TC_PP_ALPROTO_DETECT_DONE 0x00200000
+#define FLOW_TIMEOUT_REASSEMBLY_DONE 0x00800000
+/** even if the flow has files, don't store 'm */
+#define FLOW_FILE_NO_STORE_TS 0x01000000
+#define FLOW_FILE_NO_STORE_TC 0x02000000
+
+/** flow is ipv4 */
+#define FLOW_IPV4 0x04000000
+/** flow is ipv6 */
+#define FLOW_IPV6 0x08000000
+
+/** no md5 on files in this flow */
+#define FLOW_FILE_NO_MD5_TS 0x10000000
+#define FLOW_FILE_NO_MD5_TC 0x20000000
+
+/** no size tracking of files in this flow */
+#define FLOW_FILE_NO_SIZE_TS 0x40000000
+#define FLOW_FILE_NO_SIZE_TC 0x80000000
+
+#define FLOW_IS_IPV4(f) \
+ (((f)->flags & FLOW_IPV4) == FLOW_IPV4)
+#define FLOW_IS_IPV6(f) \
+ (((f)->flags & FLOW_IPV6) == FLOW_IPV6)
+
+#define FLOW_COPY_IPV4_ADDR_TO_PACKET(fa, pa) do { \
+ (pa)->family = AF_INET; \
+ (pa)->addr_data32[0] = (fa)->addr_data32[0]; \
+ } while (0)
+
+#define FLOW_COPY_IPV6_ADDR_TO_PACKET(fa, pa) do { \
+ (pa)->family = AF_INET6; \
+ (pa)->addr_data32[0] = (fa)->addr_data32[0]; \
+ (pa)->addr_data32[1] = (fa)->addr_data32[1]; \
+ (pa)->addr_data32[2] = (fa)->addr_data32[2]; \
+ (pa)->addr_data32[3] = (fa)->addr_data32[3]; \
+ } while (0)
+
+/* Set the IPv4 addressesinto the Addrs of the Packet.
+ * Make sure p->ip4h is initialized and validated.
+ *
+ * We set the rest of the struct to 0 so we can
+ * prevent using memset. */
+#define FLOW_SET_IPV4_SRC_ADDR_FROM_PACKET(p, a) do { \
+ (a)->addr_data32[0] = (uint32_t)(p)->ip4h->s_ip_src.s_addr; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+#define FLOW_SET_IPV4_DST_ADDR_FROM_PACKET(p, a) do { \
+ (a)->addr_data32[0] = (uint32_t)(p)->ip4h->s_ip_dst.s_addr; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+/* clear the address structure by setting all fields to 0 */
+#define FLOW_CLEAR_ADDR(a) do { \
+ (a)->addr_data32[0] = 0; \
+ (a)->addr_data32[1] = 0; \
+ (a)->addr_data32[2] = 0; \
+ (a)->addr_data32[3] = 0; \
+ } while (0)
+
+/* Set the IPv6 addressesinto the Addrs of the Packet.
+ * Make sure p->ip6h is initialized and validated. */
+#define FLOW_SET_IPV6_SRC_ADDR_FROM_PACKET(p, a) do { \
+ (a)->addr_data32[0] = (p)->ip6h->s_ip6_src[0]; \
+ (a)->addr_data32[1] = (p)->ip6h->s_ip6_src[1]; \
+ (a)->addr_data32[2] = (p)->ip6h->s_ip6_src[2]; \
+ (a)->addr_data32[3] = (p)->ip6h->s_ip6_src[3]; \
+ } while (0)
+
+#define FLOW_SET_IPV6_DST_ADDR_FROM_PACKET(p, a) do { \
+ (a)->addr_data32[0] = (p)->ip6h->s_ip6_dst[0]; \
+ (a)->addr_data32[1] = (p)->ip6h->s_ip6_dst[1]; \
+ (a)->addr_data32[2] = (p)->ip6h->s_ip6_dst[2]; \
+ (a)->addr_data32[3] = (p)->ip6h->s_ip6_dst[3]; \
+ } while (0)
+
+/* pkt flow flags */
+#define FLOW_PKT_TOSERVER 0x01
+#define FLOW_PKT_TOCLIENT 0x02
+#define FLOW_PKT_ESTABLISHED 0x04
+#define FLOW_PKT_TOSERVER_IPONLY_SET 0x08
+#define FLOW_PKT_TOCLIENT_IPONLY_SET 0x10
+#define FLOW_PKT_TOSERVER_FIRST 0x20
+#define FLOW_PKT_TOCLIENT_FIRST 0x40
+
+#define FLOW_END_FLAG_STATE_NEW 0x01
+#define FLOW_END_FLAG_STATE_ESTABLISHED 0x02
+#define FLOW_END_FLAG_STATE_CLOSED 0x04
+#define FLOW_END_FLAG_EMERGENCY 0x08
+#define FLOW_END_FLAG_TIMEOUT 0x10
+#define FLOW_END_FLAG_FORCED 0x20
+#define FLOW_END_FLAG_SHUTDOWN 0x40
+
+/** Mutex or RWLocks for the flow. */
+//#define FLOWLOCK_RWLOCK
+#define FLOWLOCK_MUTEX
+
+#ifdef FLOWLOCK_RWLOCK
+ #ifdef FLOWLOCK_MUTEX
+ #error Cannot enable both FLOWLOCK_RWLOCK and FLOWLOCK_MUTEX
+ #endif
+#endif
+
+#ifdef FLOWLOCK_RWLOCK
+ #define FLOWLOCK_INIT(fb) SCRWLockInit(&(fb)->r, NULL)
+ #define FLOWLOCK_DESTROY(fb) SCRWLockDestroy(&(fb)->r)
+ #define FLOWLOCK_RDLOCK(fb) SCRWLockRDLock(&(fb)->r)
+ #define FLOWLOCK_WRLOCK(fb) SCRWLockWRLock(&(fb)->r)
+ #define FLOWLOCK_TRYRDLOCK(fb) SCRWLockTryRDLock(&(fb)->r)
+ #define FLOWLOCK_TRYWRLOCK(fb) SCRWLockTryWRLock(&(fb)->r)
+ #define FLOWLOCK_UNLOCK(fb) SCRWLockUnlock(&(fb)->r)
+#elif defined FLOWLOCK_MUTEX
+ #define FLOWLOCK_INIT(fb) SCMutexInit(&(fb)->m, NULL)
+ #define FLOWLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->m)
+ #define FLOWLOCK_RDLOCK(fb) SCMutexLock(&(fb)->m)
+ #define FLOWLOCK_WRLOCK(fb) SCMutexLock(&(fb)->m)
+ #define FLOWLOCK_TRYRDLOCK(fb) SCMutexTrylock(&(fb)->m)
+ #define FLOWLOCK_TRYWRLOCK(fb) SCMutexTrylock(&(fb)->m)
+ #define FLOWLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->m)
+#else
+ #error Enable FLOWLOCK_RWLOCK or FLOWLOCK_MUTEX
+#endif
+
+#define FLOW_IS_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags & FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags & FLOW_TC_PM_ALPROTO_DETECT_DONE))
+#define FLOW_IS_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags & FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags & FLOW_TC_PP_ALPROTO_DETECT_DONE))
+
+#define FLOW_SET_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags |= FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags |= FLOW_TC_PM_ALPROTO_DETECT_DONE))
+#define FLOW_SET_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags |= FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags |= FLOW_TC_PP_ALPROTO_DETECT_DONE))
+
+#define FLOW_RESET_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags &= ~FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags &= ~FLOW_TC_PM_ALPROTO_DETECT_DONE))
+#define FLOW_RESET_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags &= ~FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags &= ~FLOW_TC_PP_ALPROTO_DETECT_DONE))
+
+/* global flow config */
+typedef struct FlowCnf_
+{
+ uint32_t hash_rand;
+ uint32_t hash_size;
+ uint64_t memcap;
+ uint32_t max_flows;
+ uint32_t prealloc;
+
+ uint32_t timeout_new;
+ uint32_t timeout_est;
+
+ uint32_t emerg_timeout_new;
+ uint32_t emerg_timeout_est;
+ uint32_t emergency_recovery;
+
+} FlowConfig;
+
+/* Hash key for the flow hash */
+typedef struct FlowKey_
+{
+ Address src, dst;
+ Port sp, dp;
+ uint8_t proto;
+ uint8_t recursion_level;
+
+} FlowKey;
+
+typedef struct FlowAddress_ {
+ union {
+ uint32_t address_un_data32[4]; /* type-specific field */
+ uint16_t address_un_data16[8]; /* type-specific field */
+ uint8_t address_un_data8[16]; /* type-specific field */
+ } address;
+} FlowAddress;
+
+#define addr_data32 address.address_un_data32
+#define addr_data16 address.address_un_data16
+#define addr_data8 address.address_un_data8
+
+#ifdef __tile__
+/* Atomic Ints performance better on Tile. */
+typedef unsigned int FlowRefCount;
+#else
+typedef unsigned short FlowRefCount;
+#endif
+
+#ifdef __tile__
+/* Atomic Ints performance better on Tile. */
+typedef unsigned int FlowStateType;
+#else
+typedef unsigned short FlowStateType;
+#endif
+
+/** Local Thread ID */
+typedef uint16_t FlowThreadId;
+
+/**
+ * \brief Flow data structure.
+ *
+ * The flow is a global data structure that is created for new packets of a
+ * flow and then looked up for the following packets of a flow.
+ *
+ * Locking
+ *
+ * The flow is updated/used by multiple packets at the same time. This is why
+ * there is a flow-mutex. It's a mutex and not a spinlock because some
+ * operations on the flow can be quite expensive, thus spinning would be
+ * too expensive.
+ *
+ * The flow "header" (addresses, ports, proto, recursion level) are static
+ * after the initialization and remain read-only throughout the entire live
+ * of a flow. This is why we can access those without protection of the lock.
+ */
+
+typedef struct Flow_
+{
+ /* flow "header", used for hashing and flow lookup. Static after init,
+ * so safe to look at without lock */
+ FlowAddress src, dst;
+ union {
+ Port sp; /**< tcp/udp source port */
+ uint8_t type; /**< icmp type */
+ };
+ union {
+ Port dp; /**< tcp/udp destination port */
+ uint8_t code; /**< icmp code */
+ };
+ uint8_t proto;
+ uint8_t recursion_level;
+ uint16_t vlan_id[2];
+
+ /* end of flow "header" */
+
+ SC_ATOMIC_DECLARE(FlowStateType, flow_state);
+
+ /** how many pkts and stream msgs are using the flow *right now*. This
+ * variable is atomic so not protected by the Flow mutex "m".
+ *
+ * On receiving a packet the counter is incremented while the flow
+ * bucked is locked, which is also the case on timeout pruning.
+ */
+ SC_ATOMIC_DECLARE(FlowRefCount, use_cnt);
+
+ /** flow queue id, used with autofp */
+ SC_ATOMIC_DECLARE(int16_t, autofp_tmqh_flow_qid);
+
+ /** flow tenant id, used to setup flow timeout and stream pseudo
+ * packets with the correct tenant id set */
+ uint32_t tenant_id;
+
+ uint32_t probing_parser_toserver_alproto_masks;
+ uint32_t probing_parser_toclient_alproto_masks;
+
+ uint32_t flags;
+
+ /* time stamp of last update (last packet). Set/updated under the
+ * flow and flow hash row locks, safe to read under either the
+ * flow lock or flow hash row lock. */
+ struct timeval lastts;
+
+#ifdef FLOWLOCK_RWLOCK
+ SCRWLock r;
+#elif defined FLOWLOCK_MUTEX
+ SCMutex m;
+#else
+ #error Enable FLOWLOCK_RWLOCK or FLOWLOCK_MUTEX
+#endif
+
+ /** protocol specific data pointer, e.g. for TcpSession */
+ void *protoctx;
+
+ /** mapping to Flow's protocol specific protocols for timeouts
+ and state and free functions. */
+ uint8_t protomap;
+
+ uint8_t flow_end_flags;
+ /* coccinelle: Flow:flow_end_flags:FLOW_END_FLAG_ */
+
+ AppProto alproto; /**< \brief application level protocol */
+ AppProto alproto_ts;
+ AppProto alproto_tc;
+
+ uint32_t data_al_so_far[2];
+
+ /** detection engine ctx id used to inspect this flow. Set at initial
+ * inspection. If it doesn't match the currently in use de_ctx, the
+ * de_state and stored sgh ptrs are reset. */
+ uint32_t de_ctx_id;
+
+ /** Thread ID for the stream/detect portion of this flow */
+ FlowThreadId thread_id;
+
+ /** detect state 'alversion' inspected for both directions */
+ uint8_t detect_alversion[2];
+
+ /** application level storage ptrs.
+ *
+ */
+ AppLayerParserState *alparser; /**< parser internal state */
+ void *alstate; /**< application layer state */
+
+ /** detection engine state */
+ struct DetectEngineStateFlow_ *de_state;
+
+ /** toclient sgh for this flow. Only use when FLOW_SGH_TOCLIENT flow flag
+ * has been set. */
+ struct SigGroupHead_ *sgh_toclient;
+ /** toserver sgh for this flow. Only use when FLOW_SGH_TOSERVER flow flag
+ * has been set. */
+ struct SigGroupHead_ *sgh_toserver;
+
+ /* pointer to the var list */
+ GenericVar *flowvar;
+
+ /** hash list pointers, protected by fb->s */
+ struct Flow_ *hnext; /* hash list */
+ struct Flow_ *hprev;
+ struct FlowBucket_ *fb;
+
+ /** queue list pointers, protected by queue mutex */
+ struct Flow_ *lnext; /* list */
+ struct Flow_ *lprev;
+ struct timeval startts;
+
+ uint32_t todstpktcnt;
+ uint32_t tosrcpktcnt;
+ uint64_t todstbytecnt;
+ uint64_t tosrcbytecnt;
+} Flow;
+
+enum {
+ FLOW_STATE_NEW = 0,
+ FLOW_STATE_ESTABLISHED,
+ FLOW_STATE_CLOSED,
+};
+
+typedef struct FlowProto_ {
+ uint32_t new_timeout;
+ uint32_t est_timeout;
+ uint32_t closed_timeout;
+ uint32_t emerg_new_timeout;
+ uint32_t emerg_est_timeout;
+ uint32_t emerg_closed_timeout;
+ void (*Freefunc)(void *);
+} FlowProto;
+
+void FlowHandlePacket (ThreadVars *, DecodeThreadVars *, Packet *);
+void FlowInitConfig (char);
+void FlowPrintQueueInfo (void);
+void FlowShutdown(void);
+void FlowSetIPOnlyFlag(Flow *, char);
+void FlowSetIPOnlyFlagNoLock(Flow *, char);
+
+void FlowRegisterTests (void);
+int FlowSetProtoTimeout(uint8_t ,uint32_t ,uint32_t ,uint32_t);
+int FlowSetProtoEmergencyTimeout(uint8_t ,uint32_t ,uint32_t ,uint32_t);
+int FlowSetProtoFreeFunc (uint8_t , void (*Free)(void *));
+void FlowUpdateQueue(Flow *);
+
+struct FlowQueue_;
+
+int FlowUpdateSpareFlows(void);
+
+static inline void FlowLockSetNoPacketInspectionFlag(Flow *);
+static inline void FlowSetNoPacketInspectionFlag(Flow *);
+static inline void FlowLockSetNoPayloadInspectionFlag(Flow *);
+static inline void FlowSetNoPayloadInspectionFlag(Flow *);
+
+int FlowGetPacketDirection(const Flow *, const Packet *);
+
+void FlowCleanupAppLayer(Flow *);
+
+/** ----- Inline functions ----- */
+
+/** \brief Set the No Packet Inspection Flag after locking the flow.
+ *
+ * \param f Flow to set the flag in
+ */
+static inline void FlowLockSetNoPacketInspectionFlag(Flow *f)
+{
+ SCEnter();
+
+ SCLogDebug("flow %p", f);
+ FLOWLOCK_WRLOCK(f);
+ f->flags |= FLOW_NOPACKET_INSPECTION;
+ FLOWLOCK_UNLOCK(f);
+
+ SCReturn;
+}
+
+/** \brief Set the No Packet Inspection Flag without locking the flow.
+ *
+ * \param f Flow to set the flag in
+ */
+static inline void FlowSetNoPacketInspectionFlag(Flow *f)
+{
+ SCEnter();
+
+ SCLogDebug("flow %p", f);
+ f->flags |= FLOW_NOPACKET_INSPECTION;
+
+ SCReturn;
+}
+
+/** \brief Set the No payload inspection Flag after locking the flow.
+ *
+ * \param f Flow to set the flag in
+ */
+static inline void FlowLockSetNoPayloadInspectionFlag(Flow *f)
+{
+ SCEnter();
+
+ SCLogDebug("flow %p", f);
+ FLOWLOCK_WRLOCK(f);
+ f->flags |= FLOW_NOPAYLOAD_INSPECTION;
+ FLOWLOCK_UNLOCK(f);
+
+ SCReturn;
+}
+
+/** \brief Set the No payload inspection Flag without locking the flow.
+ *
+ * \param f Flow to set the flag in
+ */
+static inline void FlowSetNoPayloadInspectionFlag(Flow *f)
+{
+ SCEnter();
+
+ SCLogDebug("flow %p", f);
+ f->flags |= FLOW_NOPAYLOAD_INSPECTION;
+
+ SCReturn;
+}
+
+/**
+ * \brief increase the use count of a flow
+ *
+ * \param f flow to decrease use count for
+ */
+static inline void FlowIncrUsecnt(Flow *f)
+{
+ if (f == NULL)
+ return;
+
+ (void) SC_ATOMIC_ADD(f->use_cnt, 1);
+}
+
+/**
+ * \brief decrease the use count of a flow
+ *
+ * \param f flow to decrease use count for
+ */
+static inline void FlowDecrUsecnt(Flow *f)
+{
+ if (f == NULL)
+ return;
+
+ (void) SC_ATOMIC_SUB(f->use_cnt, 1);
+}
+
+/** \brief Reference the flow, bumping the flows use_cnt
+ * \note This should only be called once for a destination
+ * pointer */
+static inline void FlowReference(Flow **d, Flow *f)
+{
+ if (likely(f != NULL)) {
+#ifdef DEBUG_VALIDATION
+ BUG_ON(*d == f);
+#else
+ if (*d == f)
+ return;
+#endif
+ FlowIncrUsecnt(f);
+ *d = f;
+ }
+}
+
+static inline void FlowDeReference(Flow **d)
+{
+ if (likely(*d != NULL)) {
+ FlowDecrUsecnt(*d);
+ *d = NULL;
+ }
+}
+
+int FlowClearMemory(Flow *,uint8_t );
+
+AppProto FlowGetAppProtocol(const Flow *f);
+void *FlowGetAppState(const Flow *f);
+uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags);
+
+void FlowHandlePacketUpdateRemove(Flow *f, Packet *p);
+void FlowHandlePacketUpdate(Flow *f, Packet *p);
+
+Flow *FlowGetFlowFromHashByPacket(const Packet *p);
+Flow *FlowLookupFlowFromHash(const Packet *p);
+
+#endif /* __FLOW_H__ */
+
diff --git a/framework/src/suricata/src/host-bit.c b/framework/src/suricata/src/host-bit.c
new file mode 100644
index 00000000..6225673f
--- /dev/null
+++ b/framework/src/suricata/src/host-bit.c
@@ -0,0 +1,503 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements per host bits. Actually, not a bit,
+ * but called that way because of Snort's flowbits.
+ * It's a binary storage.
+ *
+ * \todo move away from a linked list implementation
+ * \todo use different datatypes, such as string, int, etc.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "host-bit.h"
+#include "host.h"
+#include "detect.h"
+#include "util-var.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "host-storage.h"
+
+static int host_bit_id = -1; /**< Host storage id for bits */
+
+void HostBitFreeAll(void *store) {
+ GenericVar *gv = store;
+ GenericVarFree(gv);
+}
+
+void HostBitInitCtx(void)
+{
+ host_bit_id = HostStorageRegister("bit", sizeof(void *), NULL, HostBitFreeAll);
+ if (host_bit_id == -1) {
+ SCLogError(SC_ERR_HOST_INIT, "Can't initiate host storage for bits");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* lock before using this */
+int HostHasHostBits(Host *host)
+{
+ if (host == NULL)
+ return 0;
+ return HostGetStorageById(host, host_bit_id) ? 1 : 0;
+}
+
+/** \retval 1 host timed out wrt xbits
+ * \retval 0 host still has active (non-expired) xbits */
+int HostBitsTimedoutCheck(Host *h, struct timeval *ts)
+{
+ GenericVar *gv = HostGetStorageById(h, host_bit_id);
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_XBITS) {
+ XBit *xb = (XBit *)gv;
+ if (xb->expire > (uint32_t)ts->tv_sec)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* get the bit with idx from the host */
+static XBit *HostBitGet(Host *h, uint16_t idx)
+{
+ GenericVar *gv = HostGetStorageById(h, host_bit_id);
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_XBITS && gv->idx == idx) {
+ return (XBit *)gv;
+ }
+ }
+
+ return NULL;
+}
+
+/* add a flowbit to the flow */
+static void HostBitAdd(Host *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb == NULL) {
+ fb = SCMalloc(sizeof(XBit));
+ if (unlikely(fb == NULL))
+ return;
+
+ fb->type = DETECT_XBITS;
+ fb->idx = idx;
+ fb->next = NULL;
+ fb->expire = expire;
+
+ GenericVar *gv = HostGetStorageById(h, host_bit_id);
+ GenericVarAppend(&gv, (GenericVar *)fb);
+ HostSetStorageById(h, host_bit_id, gv);
+
+ // bit already set, lets update it's time
+ } else {
+ fb->expire = expire;
+ }
+}
+
+static void HostBitRemove(Host *h, uint16_t idx)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb == NULL)
+ return;
+
+ GenericVar *gv = HostGetStorageById(h, host_bit_id);
+ if (gv) {
+ GenericVarRemove(&gv, (GenericVar *)fb);
+ HostSetStorageById(h, host_bit_id, gv);
+ }
+}
+
+void HostBitSet(Host *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb == NULL) {
+ HostBitAdd(h, idx, expire);
+ }
+}
+
+void HostBitUnset(Host *h, uint16_t idx)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb != NULL) {
+ HostBitRemove(h, idx);
+ }
+}
+
+void HostBitToggle(Host *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb != NULL) {
+ HostBitRemove(h, idx);
+ } else {
+ HostBitAdd(h, idx, expire);
+ }
+}
+
+int HostBitIsset(Host *h, uint16_t idx, uint32_t ts)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb != NULL) {
+ if (fb->expire < ts) {
+ HostBitRemove(h,idx);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int HostBitIsnotset(Host *h, uint16_t idx, uint32_t ts)
+{
+ XBit *fb = HostBitGet(h, idx);
+ if (fb == NULL) {
+ return 1;
+ }
+
+ if (fb->expire < ts) {
+ HostBitRemove(h,idx);
+ return 1;
+ }
+ return 0;
+}
+
+/* TESTS */
+#ifdef UNITTESTS
+static int HostBitTest01 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 0);
+
+ XBit *fb = HostBitGet(h,0);
+ if (fb != NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest02 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ XBit *fb = HostBitGet(h,0);
+ if (fb == NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest03 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 30);
+
+ XBit *fb = HostBitGet(h,0);
+ if (fb == NULL) {
+ printf("fb == NULL although it was just added: ");
+ goto end;
+ }
+
+ HostBitRemove(h, 0);
+
+ fb = HostBitGet(h,0);
+ if (fb != NULL) {
+ printf("fb != NULL although it was just removed: ");
+ goto end;
+ } else {
+ ret = 1;
+ }
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest04 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 30);
+ HostBitAdd(h, 1, 30);
+ HostBitAdd(h, 2, 30);
+ HostBitAdd(h, 3, 30);
+
+ XBit *fb = HostBitGet(h,0);
+ if (fb != NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest05 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 30);
+ HostBitAdd(h, 1, 30);
+ HostBitAdd(h, 2, 30);
+ HostBitAdd(h, 3, 30);
+
+ XBit *fb = HostBitGet(h,1);
+ if (fb != NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest06 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,2);
+ if (fb != NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest07 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,3);
+ if (fb != NULL)
+ ret = 1;
+
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest08 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,0);
+ if (fb == NULL)
+ goto end;
+
+ HostBitRemove(h,0);
+
+ fb = HostBitGet(h,0);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest09 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,1);
+ if (fb == NULL)
+ goto end;
+
+ HostBitRemove(h,1);
+
+ fb = HostBitGet(h,1);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest10 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,2);
+ if (fb == NULL)
+ goto end;
+
+ HostBitRemove(h,2);
+
+ fb = HostBitGet(h,2);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+static int HostBitTest11 (void)
+{
+ int ret = 0;
+
+ HostInitConfig(TRUE);
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto end;
+
+ HostBitAdd(h, 0, 90);
+ HostBitAdd(h, 1, 90);
+ HostBitAdd(h, 2, 90);
+ HostBitAdd(h, 3, 90);
+
+ XBit *fb = HostBitGet(h,3);
+ if (fb == NULL)
+ goto end;
+
+ HostBitRemove(h,3);
+
+ fb = HostBitGet(h,3);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ HostFree(h);
+end:
+ HostCleanup();
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void HostBitRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HostBitTest01", HostBitTest01, 1);
+ UtRegisterTest("HostBitTest02", HostBitTest02, 1);
+ UtRegisterTest("HostBitTest03", HostBitTest03, 1);
+ UtRegisterTest("HostBitTest04", HostBitTest04, 1);
+ UtRegisterTest("HostBitTest05", HostBitTest05, 1);
+ UtRegisterTest("HostBitTest06", HostBitTest06, 1);
+ UtRegisterTest("HostBitTest07", HostBitTest07, 1);
+ UtRegisterTest("HostBitTest08", HostBitTest08, 1);
+ UtRegisterTest("HostBitTest09", HostBitTest09, 1);
+ UtRegisterTest("HostBitTest10", HostBitTest10, 1);
+ UtRegisterTest("HostBitTest11", HostBitTest11, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/host-bit.h b/framework/src/suricata/src/host-bit.h
new file mode 100644
index 00000000..d4bba11a
--- /dev/null
+++ b/framework/src/suricata/src/host-bit.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HOST_BIT_H__
+#define __HOST_BIT_H__
+
+#include "host.h"
+#include "util-var.h"
+
+void HostBitInitCtx(void);
+void HostBitRegisterTests(void);
+
+int HostHasHostBits(Host *host);
+int HostBitsTimedoutCheck(Host *h, struct timeval *ts);
+
+void HostBitSet(Host *, uint16_t, uint32_t);
+void HostBitUnset(Host *, uint16_t);
+void HostBitToggle(Host *, uint16_t, uint32_t);
+int HostBitIsset(Host *, uint16_t, uint32_t);
+int HostBitIsnotset(Host *, uint16_t, uint32_t);
+#endif /* __HOST_BIT_H__ */
diff --git a/framework/src/suricata/src/host-queue.c b/framework/src/suricata/src/host-queue.c
new file mode 100644
index 00000000..2ef1628d
--- /dev/null
+++ b/framework/src/suricata/src/host-queue.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Host queue handler functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "host-queue.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+HostQueue *HostQueueInit (HostQueue *q)
+{
+ if (q != NULL) {
+ memset(q, 0, sizeof(HostQueue));
+ HQLOCK_INIT(q);
+ }
+ return q;
+}
+
+HostQueue *HostQueueNew()
+{
+ HostQueue *q = (HostQueue *)SCMalloc(sizeof(HostQueue));
+ if (q == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in HostQueueNew. Exiting...");
+ exit(EXIT_SUCCESS);
+ }
+ q = HostQueueInit(q);
+ return q;
+}
+
+/**
+ * \brief Destroy a host queue
+ *
+ * \param q the host queue to destroy
+ */
+void HostQueueDestroy (HostQueue *q)
+{
+ HQLOCK_DESTROY(q);
+}
+
+/**
+ * \brief add a host to a queue
+ *
+ * \param q queue
+ * \param h host
+ */
+void HostEnqueue (HostQueue *q, Host *h)
+{
+#ifdef DEBUG
+ BUG_ON(q == NULL || h == NULL);
+#endif
+
+ HQLOCK_LOCK(q);
+
+ /* more hosts in queue */
+ if (q->top != NULL) {
+ h->lnext = q->top;
+ q->top->lprev = h;
+ q->top = h;
+ /* only host */
+ } else {
+ q->top = h;
+ q->bot = h;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ HQLOCK_UNLOCK(q);
+}
+
+/**
+ * \brief remove a host from the queue
+ *
+ * \param q queue
+ *
+ * \retval h host or NULL if empty list.
+ */
+Host *HostDequeue (HostQueue *q)
+{
+ HQLOCK_LOCK(q);
+
+ Host *h = q->bot;
+ if (h == NULL) {
+ HQLOCK_UNLOCK(q);
+ return NULL;
+ }
+
+ /* more packets in queue */
+ if (q->bot->lprev != NULL) {
+ q->bot = q->bot->lprev;
+ q->bot->lnext = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+#ifdef DEBUG
+ BUG_ON(q->len == 0);
+#endif
+ if (q->len > 0)
+ q->len--;
+
+ h->lnext = NULL;
+ h->lprev = NULL;
+
+ HQLOCK_UNLOCK(q);
+ return h;
+}
+
+uint32_t HostQueueLen(HostQueue *q)
+{
+ uint32_t len;
+ HQLOCK_LOCK(q);
+ len = q->len;
+ HQLOCK_UNLOCK(q);
+ return len;
+}
+
diff --git a/framework/src/suricata/src/host-queue.h b/framework/src/suricata/src/host-queue.h
new file mode 100644
index 00000000..386d0f6e
--- /dev/null
+++ b/framework/src/suricata/src/host-queue.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HOST_QUEUE_H__
+#define __HOST_QUEUE_H__
+
+#include "suricata-common.h"
+#include "host.h"
+
+/** Spinlocks or Mutex for the host queues. */
+//#define HQLOCK_SPIN
+#define HQLOCK_MUTEX
+
+#ifdef HQLOCK_SPIN
+ #ifdef HQLOCK_MUTEX
+ #error Cannot enable both HQLOCK_SPIN and HQLOCK_MUTEX
+ #endif
+#endif
+
+/* Define a queue for storing hosts */
+typedef struct HostQueue_
+{
+ Host *top;
+ Host *bot;
+ uint32_t len;
+#ifdef DBG_PERF
+ uint32_t dbg_maxlen;
+#endif /* DBG_PERF */
+#ifdef HQLOCK_MUTEX
+ SCMutex m;
+#elif defined HQLOCK_SPIN
+ SCSpinlock s;
+#else
+ #error Enable HQLOCK_SPIN or HQLOCK_MUTEX
+#endif
+} HostQueue;
+
+#ifdef HQLOCK_SPIN
+ #define HQLOCK_INIT(q) SCSpinInit(&(q)->s, 0)
+ #define HQLOCK_DESTROY(q) SCSpinDestroy(&(q)->s)
+ #define HQLOCK_LOCK(q) SCSpinLock(&(q)->s)
+ #define HQLOCK_TRYLOCK(q) SCSpinTrylock(&(q)->s)
+ #define HQLOCK_UNLOCK(q) SCSpinUnlock(&(q)->s)
+#elif defined HQLOCK_MUTEX
+ #define HQLOCK_INIT(q) SCMutexInit(&(q)->m, NULL)
+ #define HQLOCK_DESTROY(q) SCMutexDestroy(&(q)->m)
+ #define HQLOCK_LOCK(q) SCMutexLock(&(q)->m)
+ #define HQLOCK_TRYLOCK(q) SCMutexTrylock(&(q)->m)
+ #define HQLOCK_UNLOCK(q) SCMutexUnlock(&(q)->m)
+#else
+ #error Enable HQLOCK_SPIN or HQLOCK_MUTEX
+#endif
+
+/* prototypes */
+HostQueue *HostQueueNew();
+HostQueue *HostQueueInit(HostQueue *);
+void HostQueueDestroy (HostQueue *);
+
+void HostEnqueue (HostQueue *, Host *);
+Host *HostDequeue (HostQueue *);
+uint32_t HostQueueLen(HostQueue *);
+
+#endif /* __HOST_QUEUE_H__ */
+
diff --git a/framework/src/suricata/src/host-storage.c b/framework/src/suricata/src/host-storage.c
new file mode 100644
index 00000000..6748089c
--- /dev/null
+++ b/framework/src/suricata/src/host-storage.c
@@ -0,0 +1,290 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Host wrapper around storage api
+ */
+
+#include "suricata-common.h"
+#include "host-storage.h"
+#include "util-unittest.h"
+
+unsigned int HostStorageSize(void)
+{
+ return StorageGetSize(STORAGE_HOST);
+}
+
+void *HostGetStorageById(Host *h, int id)
+{
+ return StorageGetById((Storage *)((void *)h + sizeof(Host)), STORAGE_HOST, id);
+}
+
+int HostSetStorageById(Host *h, int id, void *ptr)
+{
+ return StorageSetById((Storage *)((void *)h + sizeof(Host)), STORAGE_HOST, id, ptr);
+}
+
+void *HostAllocStorageById(Host *h, int id)
+{
+ return StorageAllocByIdPrealloc((Storage *)((void *)h + sizeof(Host)), STORAGE_HOST, id);
+}
+
+void HostFreeStorageById(Host *h, int id)
+{
+ StorageFreeById((Storage *)((void *)h + sizeof(Host)), STORAGE_HOST, id);
+}
+
+void HostFreeStorage(Host *h)
+{
+ if (HostStorageSize() > 0)
+ StorageFreeAll((Storage *)((void *)h + sizeof(Host)), STORAGE_HOST);
+}
+
+int HostStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *)) {
+ return StorageRegister(STORAGE_HOST, name, size, Alloc, Free);
+}
+
+#ifdef UNITTESTS
+
+static void *StorageTestAlloc(unsigned int size)
+{
+ void *x = SCMalloc(size);
+ return x;
+}
+static void StorageTestFree(void *x)
+{
+ if (x)
+ SCFree(x);
+}
+
+static int HostStorageTest01(void)
+{
+ StorageInit();
+
+ int id1 = HostStorageRegister("test", 8, StorageTestAlloc, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = HostStorageRegister("variable", 24, StorageTestAlloc, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = HostStorageRegister("store", sizeof(void *), StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ HostInitConfig(1);
+
+ Address a;
+ memset(&a, 0x00, sizeof(a));
+ a.addr_data32[0] = 0x01020304;
+ a.family = AF_INET;
+ Host *h = HostGetHostFromHash(&a);
+ if (h == NULL) {
+ printf("failed to get host: ");
+ goto error;
+ }
+
+ void *ptr = HostGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = HostGetStorageById(h, id2);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = HostGetStorageById(h, id3);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = HostAllocStorageById(h, id1);
+ if (ptr1a == NULL) {
+ goto error;
+ }
+ void *ptr2a = HostAllocStorageById(h, id2);
+ if (ptr2a == NULL) {
+ goto error;
+ }
+ void *ptr3a = HostAllocStorageById(h, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = HostGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = HostGetStorageById(h, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = HostGetStorageById(h, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ HostRelease(h);
+
+ HostShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ HostShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int HostStorageTest02(void)
+{
+ StorageInit();
+
+ int id1 = HostStorageRegister("test", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ HostInitConfig(1);
+
+ Address a;
+ memset(&a, 0x00, sizeof(a));
+ a.addr_data32[0] = 0x01020304;
+ a.family = AF_INET;
+ Host *h = HostGetHostFromHash(&a);
+ if (h == NULL) {
+ printf("failed to get host: ");
+ goto error;
+ }
+
+ void *ptr = HostGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ HostSetStorageById(h, id1, ptr1a);
+
+ void *ptr1b = HostGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+
+ HostRelease(h);
+
+ HostShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ HostShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int HostStorageTest03(void)
+{
+ StorageInit();
+
+ int id1 = HostStorageRegister("test1", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = HostStorageRegister("test2", sizeof(void *), NULL, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = HostStorageRegister("test3", 32, StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ HostInitConfig(1);
+
+ Address a;
+ memset(&a, 0x00, sizeof(a));
+ a.addr_data32[0] = 0x01020304;
+ a.family = AF_INET;
+ Host *h = HostGetHostFromHash(&a);
+ if (h == NULL) {
+ printf("failed to get host: ");
+ goto error;
+ }
+
+ void *ptr = HostGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ HostSetStorageById(h, id1, ptr1a);
+
+ void *ptr2a = SCMalloc(256);
+ if (unlikely(ptr2a == NULL)) {
+ goto error;
+ }
+ HostSetStorageById(h, id2, ptr2a);
+
+ void *ptr3a = HostAllocStorageById(h, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = HostGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = HostGetStorageById(h, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = HostGetStorageById(h, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ HostRelease(h);
+
+ HostShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ HostShutdown();
+ StorageCleanup();
+ return 0;
+}
+#endif
+
+void RegisterHostStorageTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HostStorageTest01", HostStorageTest01, 1);
+ UtRegisterTest("HostStorageTest02", HostStorageTest02, 1);
+ UtRegisterTest("HostStorageTest03", HostStorageTest03, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/host-storage.h b/framework/src/suricata/src/host-storage.h
new file mode 100644
index 00000000..b2bccbe2
--- /dev/null
+++ b/framework/src/suricata/src/host-storage.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Host wrapper around storage api
+ */
+
+#ifndef __HOST_STORAGE_H__
+#define __HOST_STORAGE_H__
+
+#include "util-storage.h"
+#include "host.h"
+
+unsigned int HostStorageSize(void);
+
+void *HostGetStorageById(Host *h, int id);
+int HostSetStorageById(Host *h, int id, void *ptr);
+void *HostAllocStorageById(Host *h, int id);
+
+void HostFreeStorageById(Host *h, int id);
+void HostFreeStorage(Host *h);
+
+void RegisterHostStorageTests(void);
+
+int HostStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *));
+
+#endif /* __HOST_STORAGE_H__ */
diff --git a/framework/src/suricata/src/host-timeout.c b/framework/src/suricata/src/host-timeout.c
new file mode 100644
index 00000000..c8be4e05
--- /dev/null
+++ b/framework/src/suricata/src/host-timeout.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "host.h"
+
+#include "detect-engine-tag.h"
+#include "detect-engine-threshold.h"
+
+#include "host-bit.h"
+
+#include "reputation.h"
+
+uint32_t HostGetSpareCount(void)
+{
+ return HostSpareQueueGetSize();
+}
+
+uint32_t HostGetActiveCount(void)
+{
+ return SC_ATOMIC_GET(host_counter);
+}
+
+/** \internal
+ * \brief See if we can really discard this host. Check use_cnt reference.
+ *
+ * \param h host
+ * \param ts timestamp
+ *
+ * \retval 0 not timed out just yet
+ * \retval 1 fully timed out, lets kill it
+ */
+static int HostHostTimedOut(Host *h, struct timeval *ts)
+{
+ int tags = 0;
+ int thresholds = 0;
+ int vars = 0;
+
+ /** never prune a host that is used by a packet
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(h->use_cnt) > 0) {
+ return 0;
+ }
+
+ if (h->iprep) {
+ if (SRepHostTimedOut(h) == 0)
+ return 0;
+
+ SCLogDebug("host %p reputation timed out", h);
+ }
+
+ if (TagHostHasTag(h) && TagTimeoutCheck(h, ts) == 0) {
+ tags = 1;
+ }
+ if (ThresholdHostHasThreshold(h) && ThresholdTimeoutCheck(h, ts) == 0) {
+ thresholds = 1;
+ }
+ if (HostHasHostBits(h) && HostBitsTimedoutCheck(h, ts) == 0) {
+ vars = 1;
+ }
+
+ if (tags || thresholds || vars)
+ return 0;
+
+ SCLogDebug("host %p timed out", h);
+ return 1;
+}
+
+/**
+ * \internal
+ *
+ * \brief check all hosts in a hash row for timing out
+ *
+ * \param hb host hash row *LOCKED*
+ * \param h last host in the hash row
+ * \param ts timestamp
+ *
+ * \retval cnt timed out hosts
+ */
+static uint32_t HostHashRowTimeout(HostHashRow *hb, Host *h, struct timeval *ts)
+{
+ uint32_t cnt = 0;
+
+ do {
+ if (SCMutexTrylock(&h->m) != 0) {
+ h = h->hprev;
+ continue;
+ }
+
+ Host *next_host = h->hprev;
+
+ /* check if the host is fully timed out and
+ * ready to be discarded. */
+ if (HostHostTimedOut(h, ts) == 1) {
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+
+ h->hnext = NULL;
+ h->hprev = NULL;
+
+ HostClearMemory (h);
+
+ /* no one is referring to this host, use_cnt 0, removed from hash
+ * so we can unlock it and move it back to the spare queue. */
+ SCMutexUnlock(&h->m);
+
+ /* move to spare list */
+ HostMoveToSpare(h);
+
+ cnt++;
+ } else {
+ SCMutexUnlock(&h->m);
+ }
+
+ h = next_host;
+ } while (h != NULL);
+
+ return cnt;
+}
+
+/**
+ * \brief time out hosts from the hash
+ *
+ * \param ts timestamp
+ *
+ * \retval cnt number of timed out host
+ */
+uint32_t HostTimeoutHash(struct timeval *ts)
+{
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+
+ for (idx = 0; idx < host_config.hash_size; idx++) {
+ HostHashRow *hb = &host_hash[idx];
+
+ if (HRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ /* host hash bucket is now locked */
+
+ if (hb->tail == NULL) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /* we have a host, or more than one */
+ cnt += HostHashRowTimeout(hb, hb->tail, ts);
+ HRLOCK_UNLOCK(hb);
+ }
+
+ return cnt;
+}
+
diff --git a/framework/src/suricata/src/host-timeout.h b/framework/src/suricata/src/host-timeout.h
new file mode 100644
index 00000000..6ea1e894
--- /dev/null
+++ b/framework/src/suricata/src/host-timeout.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HOST_TIMEOUT_H__
+#define __HOST_TIMEOUT_H__
+
+uint32_t HostTimeoutHash(struct timeval *ts);
+
+uint32_t HostGetSpareCount(void);
+uint32_t HostGetActiveCount(void);
+
+#endif
+
diff --git a/framework/src/suricata/src/host.c b/framework/src/suricata/src/host.c
new file mode 100644
index 00000000..7c3c5841
--- /dev/null
+++ b/framework/src/suricata/src/host.c
@@ -0,0 +1,692 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Information about hosts.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "util-debug.h"
+#include "host.h"
+#include "host-storage.h"
+#include "host-bit.h"
+
+#include "util-random.h"
+#include "util-misc.h"
+#include "util-byte.h"
+
+#include "host-queue.h"
+
+#include "detect-tag.h"
+#include "detect-engine-tag.h"
+#include "detect-engine-threshold.h"
+
+#include "util-hash-lookup3.h"
+
+static Host *HostGetUsedHost(void);
+
+/** queue with spare hosts */
+static HostQueue host_spare_q;
+
+uint32_t HostSpareQueueGetSize(void)
+{
+ return HostQueueLen(&host_spare_q);
+}
+
+void HostMoveToSpare(Host *h)
+{
+ HostEnqueue(&host_spare_q, h);
+ (void) SC_ATOMIC_SUB(host_counter, 1);
+}
+
+Host *HostAlloc(void)
+{
+ size_t size = sizeof(Host) + HostStorageSize();
+
+ if (!(HOST_CHECK_MEMCAP(size))) {
+ return NULL;
+ }
+
+ (void) SC_ATOMIC_ADD(host_memuse, size);
+
+ Host *h = SCMalloc(size);
+ if (unlikely(h == NULL))
+ goto error;
+
+ memset(h, 0x00, size);
+
+ SCMutexInit(&h->m, NULL);
+ SC_ATOMIC_INIT(h->use_cnt);
+ return h;
+
+error:
+ return NULL;
+}
+
+void HostFree(Host *h)
+{
+ if (h != NULL) {
+ HostClearMemory(h);
+
+ SC_ATOMIC_DESTROY(h->use_cnt);
+ SCMutexDestroy(&h->m);
+ SCFree(h);
+ (void) SC_ATOMIC_SUB(host_memuse, (sizeof(Host) + HostStorageSize()));
+ }
+}
+
+Host *HostNew(Address *a)
+{
+ Host *h = HostAlloc();
+ if (h == NULL)
+ goto error;
+
+ /* copy address */
+ COPY_ADDRESS(a, &h->a);
+
+ return h;
+
+error:
+ return NULL;
+}
+
+void HostClearMemory(Host *h)
+{
+ if (h->iprep != NULL) {
+ SCFree(h->iprep);
+ h->iprep = NULL;
+ }
+
+ if (HostStorageSize() > 0)
+ HostFreeStorage(h);
+}
+
+#define HOST_DEFAULT_HASHSIZE 4096
+#define HOST_DEFAULT_MEMCAP 16777216
+#define HOST_DEFAULT_PREALLOC 1000
+
+/** \brief initialize the configuration
+ * \warning Not thread safe */
+void HostInitConfig(char quiet)
+{
+ SCLogDebug("initializing host engine...");
+
+ memset(&host_config, 0, sizeof(host_config));
+ //SC_ATOMIC_INIT(flow_flags);
+ SC_ATOMIC_INIT(host_counter);
+ SC_ATOMIC_INIT(host_memuse);
+ SC_ATOMIC_INIT(host_prune_idx);
+ HostQueueInit(&host_spare_q);
+
+ unsigned int seed = RandomTimePreseed();
+ /* set defaults */
+ host_config.hash_rand = (int)( HOST_DEFAULT_HASHSIZE * (rand_r(&seed) / RAND_MAX + 1.0));
+
+ host_config.hash_size = HOST_DEFAULT_HASHSIZE;
+ host_config.memcap = HOST_DEFAULT_MEMCAP;
+ host_config.prealloc = HOST_DEFAULT_PREALLOC;
+
+ /* Check if we have memcap and hash_size defined at config */
+ char *conf_val;
+ uint32_t configval = 0;
+
+ /** set config values for memcap, prealloc and hash_size */
+ if ((ConfGet("host.memcap", &conf_val)) == 1)
+ {
+ if (ParseSizeStringU64(conf_val, &host_config.memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing host.memcap "
+ "from conf file - %s. Killing engine",
+ conf_val);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if ((ConfGet("host.hash-size", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ host_config.hash_size = configval;
+ }
+ }
+
+ if ((ConfGet("host.prealloc", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ host_config.prealloc = configval;
+ } else {
+ WarnInvalidConfEntry("host.prealloc", "%"PRIu32, host_config.prealloc);
+ }
+ }
+ SCLogDebug("Host config from suricata.yaml: memcap: %"PRIu64", hash-size: "
+ "%"PRIu32", prealloc: %"PRIu32, host_config.memcap,
+ host_config.hash_size, host_config.prealloc);
+
+ /* alloc hash memory */
+ uint64_t hash_size = host_config.hash_size * sizeof(HostHashRow);
+ if (!(HOST_CHECK_MEMCAP(hash_size))) {
+ SCLogError(SC_ERR_HOST_INIT, "allocating host hash failed: "
+ "max host memcap is smaller than projected hash size. "
+ "Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate "
+ "total hash size by multiplying \"host.hash-size\" with %"PRIuMAX", "
+ "which is the hash bucket size.", host_config.memcap, hash_size,
+ (uintmax_t)sizeof(HostHashRow));
+ exit(EXIT_FAILURE);
+ }
+ host_hash = SCCalloc(host_config.hash_size, sizeof(HostHashRow));
+ if (unlikely(host_hash == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in HostInitConfig. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(host_hash, 0, host_config.hash_size * sizeof(HostHashRow));
+
+ uint32_t i = 0;
+ for (i = 0; i < host_config.hash_size; i++) {
+ HRLOCK_INIT(&host_hash[i]);
+ }
+ (void) SC_ATOMIC_ADD(host_memuse, (host_config.hash_size * sizeof(HostHashRow)));
+
+ if (quiet == FALSE) {
+ SCLogInfo("allocated %llu bytes of memory for the host hash... "
+ "%" PRIu32 " buckets of size %" PRIuMAX "",
+ SC_ATOMIC_GET(host_memuse), host_config.hash_size,
+ (uintmax_t)sizeof(HostHashRow));
+ }
+
+ /* pre allocate hosts */
+ for (i = 0; i < host_config.prealloc; i++) {
+ if (!(HOST_CHECK_MEMCAP(sizeof(Host)))) {
+ SCLogError(SC_ERR_HOST_INIT, "preallocating hosts failed: "
+ "max host memcap reached. Memcap %"PRIu64", "
+ "Memuse %"PRIu64".", host_config.memcap,
+ ((uint64_t)SC_ATOMIC_GET(host_memuse) + (uint64_t)sizeof(Host)));
+ exit(EXIT_FAILURE);
+ }
+
+ Host *h = HostAlloc();
+ if (h == NULL) {
+ SCLogError(SC_ERR_HOST_INIT, "preallocating host failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ HostEnqueue(&host_spare_q,h);
+ }
+
+ if (quiet == FALSE) {
+ SCLogInfo("preallocated %" PRIu32 " hosts of size %" PRIuMAX "",
+ host_spare_q.len, (uintmax_t)sizeof(Host));
+ SCLogInfo("host memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(host_memuse), host_config.memcap);
+ }
+
+ return;
+}
+
+/** \brief print some host stats
+ * \warning Not thread safe */
+void HostPrintStats (void)
+{
+#ifdef HOSTBITS_STATS
+ SCLogInfo("hostbits added: %" PRIu32 ", removed: %" PRIu32 ", max memory usage: %" PRIu32 "",
+ hostbits_added, hostbits_removed, hostbits_memuse_max);
+#endif /* HOSTBITS_STATS */
+ SCLogInfo("host memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(host_memuse), host_config.memcap);
+ return;
+}
+
+/** \brief shutdown the flow engine
+ * \warning Not thread safe */
+void HostShutdown(void)
+{
+ Host *h;
+ uint32_t u;
+
+ HostPrintStats();
+
+ /* free spare queue */
+ while((h = HostDequeue(&host_spare_q))) {
+ BUG_ON(SC_ATOMIC_GET(h->use_cnt) > 0);
+ HostFree(h);
+ }
+
+ /* clear and free the hash */
+ if (host_hash != NULL) {
+ for (u = 0; u < host_config.hash_size; u++) {
+ Host *h = host_hash[u].head;
+ while (h) {
+ Host *n = h->hnext;
+ HostFree(h);
+ h = n;
+ }
+
+ HRLOCK_DESTROY(&host_hash[u]);
+ }
+ SCFree(host_hash);
+ host_hash = NULL;
+ }
+ (void) SC_ATOMIC_SUB(host_memuse, host_config.hash_size * sizeof(HostHashRow));
+ HostQueueDestroy(&host_spare_q);
+
+ SC_ATOMIC_DESTROY(host_prune_idx);
+ SC_ATOMIC_DESTROY(host_memuse);
+ SC_ATOMIC_DESTROY(host_counter);
+ //SC_ATOMIC_DESTROY(flow_flags);
+ return;
+}
+
+/** \brief Cleanup the host engine
+ *
+ * Cleanup the host engine from tag and threshold.
+ *
+ */
+void HostCleanup(void)
+{
+ Host *h;
+ uint32_t u;
+
+ if (host_hash != NULL) {
+ for (u = 0; u < host_config.hash_size; u++) {
+ h = host_hash[u].head;
+ HostHashRow *hb = &host_hash[u];
+ HRLOCK_LOCK(hb);
+ while (h) {
+ if ((SC_ATOMIC_GET(h->use_cnt) > 0) && (h->iprep != NULL)) {
+ /* iprep is attached to host only clear local storage */
+ HostFreeStorage(h);
+ h = h->hnext;
+ } else {
+ Host *n = h->hnext;
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+ h->hnext = NULL;
+ h->hprev = NULL;
+ HostClearMemory(h);
+ HostMoveToSpare(h);
+ h = n;
+ }
+ }
+ HRLOCK_UNLOCK(hb);
+ }
+ }
+
+ return;
+}
+
+/* calculate the hash key for this packet
+ *
+ * we're using:
+ * hash_rand -- set at init time
+ * source address
+ */
+uint32_t HostGetKey(Address *a)
+{
+ uint32_t key;
+
+ if (a->family == AF_INET) {
+ uint32_t hash = hashword(&a->addr_data32[0], 1, host_config.hash_rand);
+ key = hash % host_config.hash_size;
+ } else if (a->family == AF_INET6) {
+ uint32_t hash = hashword(a->addr_data32, 4, host_config.hash_rand);
+ key = hash % host_config.hash_size;
+ } else
+ key = 0;
+
+ return key;
+}
+
+/* Since two or more hosts can have the same hash key, we need to compare
+ * the flow with the current flow key. */
+#define CMP_HOST(h,a) \
+ (CMP_ADDR(&(h)->a, (a)))
+
+static inline int HostCompare(Host *h, Address *a)
+{
+ return CMP_HOST(h, a);
+}
+
+/**
+ * \brief Get a new host
+ *
+ * Get a new host. We're checking memcap first and will try to make room
+ * if the memcap is reached.
+ *
+ * \retval h *LOCKED* host on succes, NULL on error.
+ */
+static Host *HostGetNew(Address *a)
+{
+ Host *h = NULL;
+
+ /* get a host from the spare queue */
+ h = HostDequeue(&host_spare_q);
+ if (h == NULL) {
+ /* If we reached the max memcap, we get a used host */
+ if (!(HOST_CHECK_MEMCAP(sizeof(Host)))) {
+ /* declare state of emergency */
+ //if (!(SC_ATOMIC_GET(host_flags) & HOST_EMERGENCY)) {
+ // SC_ATOMIC_OR(host_flags, HOST_EMERGENCY);
+
+ /* under high load, waking up the flow mgr each time leads
+ * to high cpu usage. Flows are not timed out much faster if
+ * we check a 1000 times a second. */
+ // FlowWakeupFlowManagerThread();
+ //}
+
+ h = HostGetUsedHost();
+ if (h == NULL) {
+ return NULL;
+ }
+
+ /* freed a host, but it's unlocked */
+ } else {
+ /* now see if we can alloc a new host */
+ h = HostNew(a);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ /* host is initialized but *unlocked* */
+ }
+ } else {
+ /* host has been recycled before it went into the spare queue */
+
+ /* host is initialized (recylced) but *unlocked* */
+ }
+
+ (void) SC_ATOMIC_ADD(host_counter, 1);
+ SCMutexLock(&h->m);
+ return h;
+}
+
+void HostInit(Host *h, Address *a)
+{
+ COPY_ADDRESS(a, &h->a);
+ (void) HostIncrUsecnt(h);
+}
+
+void HostRelease(Host *h)
+{
+ (void) HostDecrUsecnt(h);
+ SCMutexUnlock(&h->m);
+}
+
+void HostLock(Host *h)
+{
+ SCMutexLock(&h->m);
+}
+
+void HostUnlock(Host *h)
+{
+ SCMutexUnlock(&h->m);
+}
+
+
+/* HostGetHostFromHash
+ *
+ * Hash retrieval function for hosts. Looks up the hash bucket containing the
+ * host pointer. Then compares the packet with the found host to see if it is
+ * the host we need. If it isn't, walk the list until the right host is found.
+ *
+ * returns a *LOCKED* host or NULL
+ */
+Host *HostGetHostFromHash (Address *a)
+{
+ Host *h = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = HostGetKey(a);
+ /* get our hash bucket and lock it */
+ HostHashRow *hb = &host_hash[key];
+ HRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a host */
+ if (hb->head == NULL) {
+ h = HostGetNew(a);
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+
+ /* host is locked */
+ hb->head = h;
+ hb->tail = h;
+
+ /* got one, now lock, initialize and return */
+ HostInit(h,a);
+
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ /* ok, we have a host in the bucket. Let's find out if it is our host */
+ h = hb->head;
+
+ /* see if this is the host we are looking for */
+ if (HostCompare(h, a) == 0) {
+ Host *ph = NULL; /* previous host */
+
+ while (h) {
+ ph = h;
+ h = h->hnext;
+
+ if (h == NULL) {
+ h = ph->hnext = HostGetNew(a);
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+ hb->tail = h;
+
+ /* host is locked */
+
+ h->hprev = ph;
+
+ /* initialize and return */
+ HostInit(h,a);
+
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ if (HostCompare(h, a) != 0) {
+ /* we found our host, lets put it on top of the
+ * hash list -- this rewards active hosts */
+ if (h->hnext) {
+ h->hnext->hprev = h->hprev;
+ }
+ if (h->hprev) {
+ h->hprev->hnext = h->hnext;
+ }
+ if (h == hb->tail) {
+ hb->tail = h->hprev;
+ }
+
+ h->hnext = hb->head;
+ h->hprev = NULL;
+ hb->head->hprev = h;
+ hb->head = h;
+
+ /* found our host, lock & return */
+ SCMutexLock(&h->m);
+ (void) HostIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&h->m);
+ (void) HostIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+}
+
+/** \brief look up a host in the hash
+ *
+ * \param a address to look up
+ *
+ * \retval h *LOCKED* host or NULL
+ */
+Host *HostLookupHostFromHash (Address *a)
+{
+ Host *h = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = HostGetKey(a);
+ /* get our hash bucket and lock it */
+ HostHashRow *hb = &host_hash[key];
+ HRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a host */
+ if (hb->head == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ /* ok, we have a host in the bucket. Let's find out if it is our host */
+ h = hb->head;
+
+ /* see if this is the host we are looking for */
+ if (HostCompare(h, a) == 0) {
+ while (h) {
+ h = h->hnext;
+
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ if (HostCompare(h, a) != 0) {
+ /* we found our host, lets put it on top of the
+ * hash list -- this rewards active hosts */
+ if (h->hnext) {
+ h->hnext->hprev = h->hprev;
+ }
+ if (h->hprev) {
+ h->hprev->hnext = h->hnext;
+ }
+ if (h == hb->tail) {
+ hb->tail = h->hprev;
+ }
+
+ h->hnext = hb->head;
+ h->hprev = NULL;
+ hb->head->hprev = h;
+ hb->head = h;
+
+ /* found our host, lock & return */
+ SCMutexLock(&h->m);
+ (void) HostIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&h->m);
+ (void) HostIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+}
+
+/** \internal
+ * \brief Get a host from the hash directly.
+ *
+ * Called in conditions where the spare queue is empty and memcap is reached.
+ *
+ * Walks the hash until a host can be freed. "host_prune_idx" atomic int makes
+ * sure we don't start at the top each time since that would clear the top of
+ * the hash leading to longer and longer search times under high pressure (observed).
+ *
+ * \retval h host or NULL
+ */
+static Host *HostGetUsedHost(void)
+{
+ uint32_t idx = SC_ATOMIC_GET(host_prune_idx) % host_config.hash_size;
+ uint32_t cnt = host_config.hash_size;
+
+ while (cnt--) {
+ if (++idx >= host_config.hash_size)
+ idx = 0;
+
+ HostHashRow *hb = &host_hash[idx];
+
+ if (HRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ Host *h = hb->tail;
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ if (SCMutexTrylock(&h->m) != 0) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /** never prune a host that is used by a packets
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(h->use_cnt) > 0) {
+ HRLOCK_UNLOCK(hb);
+ SCMutexUnlock(&h->m);
+ continue;
+ }
+
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+
+ h->hnext = NULL;
+ h->hprev = NULL;
+ HRLOCK_UNLOCK(hb);
+
+ HostClearMemory (h);
+
+ SCMutexUnlock(&h->m);
+
+ (void) SC_ATOMIC_ADD(host_prune_idx, (host_config.hash_size - cnt));
+ return h;
+ }
+
+ return NULL;
+}
+
+void HostRegisterUnittests(void)
+{
+ RegisterHostStorageTests();
+}
+
diff --git a/framework/src/suricata/src/host.h b/framework/src/suricata/src/host.h
new file mode 100644
index 00000000..e3dd167a
--- /dev/null
+++ b/framework/src/suricata/src/host.h
@@ -0,0 +1,157 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HOST_H__
+#define __HOST_H__
+
+#include "decode.h"
+#include "util-storage.h"
+
+/** Spinlocks or Mutex for the flow buckets. */
+//#define HRLOCK_SPIN
+#define HRLOCK_MUTEX
+
+#ifdef HRLOCK_SPIN
+ #ifdef HRLOCK_MUTEX
+ #error Cannot enable both HRLOCK_SPIN and HRLOCK_MUTEX
+ #endif
+#endif
+
+#ifdef HRLOCK_SPIN
+ #define HRLOCK_TYPE SCSpinlock
+ #define HRLOCK_INIT(fb) SCSpinInit(&(fb)->lock, 0)
+ #define HRLOCK_DESTROY(fb) SCSpinDestroy(&(fb)->lock)
+ #define HRLOCK_LOCK(fb) SCSpinLock(&(fb)->lock)
+ #define HRLOCK_TRYLOCK(fb) SCSpinTrylock(&(fb)->lock)
+ #define HRLOCK_UNLOCK(fb) SCSpinUnlock(&(fb)->lock)
+#elif defined HRLOCK_MUTEX
+ #define HRLOCK_TYPE SCMutex
+ #define HRLOCK_INIT(fb) SCMutexInit(&(fb)->lock, NULL)
+ #define HRLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->lock)
+ #define HRLOCK_LOCK(fb) SCMutexLock(&(fb)->lock)
+ #define HRLOCK_TRYLOCK(fb) SCMutexTrylock(&(fb)->lock)
+ #define HRLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->lock)
+#else
+ #error Enable HRLOCK_SPIN or HRLOCK_MUTEX
+#endif
+
+typedef struct Host_ {
+ /** host mutex */
+ SCMutex m;
+
+ /** host address -- ipv4 or ipv6 */
+ Address a;
+
+ /** use cnt, reference counter */
+ SC_ATOMIC_DECLARE(unsigned int, use_cnt);
+
+ /** pointers to iprep storage */
+ void *iprep;
+
+ /** storage api handle */
+ Storage *storage;
+
+ /** hash pointers, protected by hash row mutex/spin */
+ struct Host_ *hnext;
+ struct Host_ *hprev;
+
+ /** list pointers, protected by host-queue mutex/spin */
+ struct Host_ *lnext;
+ struct Host_ *lprev;
+} Host;
+
+typedef struct HostHashRow_ {
+ HRLOCK_TYPE lock;
+ Host *head;
+ Host *tail;
+} __attribute__((aligned(CLS))) HostHashRow;
+
+/** host hash table */
+HostHashRow *host_hash;
+
+#define HOST_VERBOSE 0
+#define HOST_QUIET 1
+
+typedef struct HostConfig_ {
+ uint64_t memcap;
+ uint32_t hash_rand;
+ uint32_t hash_size;
+ uint32_t prealloc;
+} HostConfig;
+
+/** \brief check if a memory alloc would fit in the memcap
+ *
+ * \param size memory allocation size to check
+ *
+ * \retval 1 it fits
+ * \retval 0 no fit
+ */
+#define HOST_CHECK_MEMCAP(size) \
+ ((((uint64_t)SC_ATOMIC_GET(host_memuse) + (uint64_t)(size)) <= host_config.memcap))
+
+#define HostIncrUsecnt(h) \
+ (void)SC_ATOMIC_ADD((h)->use_cnt, 1)
+#define HostDecrUsecnt(h) \
+ (void)SC_ATOMIC_SUB((h)->use_cnt, 1)
+
+#define HostReference(dst_h_ptr, h) do { \
+ if ((h) != NULL) { \
+ HostIncrUsecnt((h)); \
+ *(dst_h_ptr) = h; \
+ } \
+ } while (0)
+
+#define HostDeReference(src_h_ptr) do { \
+ if (*(src_h_ptr) != NULL) { \
+ HostDecrUsecnt(*(src_h_ptr)); \
+ *(src_h_ptr) = NULL; \
+ } \
+ } while (0)
+
+HostConfig host_config;
+SC_ATOMIC_DECLARE(unsigned long long int,host_memuse);
+SC_ATOMIC_DECLARE(unsigned int,host_counter);
+SC_ATOMIC_DECLARE(unsigned int,host_prune_idx);
+
+void HostInitConfig(char quiet);
+void HostShutdown(void);
+void HostCleanup(void);
+
+Host *HostLookupHostFromHash (Address *);
+Host *HostGetHostFromHash (Address *);
+void HostRelease(Host *);
+void HostLock(Host *);
+void HostClearMemory(Host *);
+void HostMoveToSpare(Host *);
+uint32_t HostSpareQueueGetSize(void);
+void HostPrintStats (void);
+
+void HostRegisterUnittests(void);
+
+Host *HostAlloc();
+void HostFree();
+
+void HostUnlock(Host *h);
+
+#endif /* __HOST_H__ */
+
diff --git a/framework/src/suricata/src/ippair-bit.c b/framework/src/suricata/src/ippair-bit.c
new file mode 100644
index 00000000..0cf5c60c
--- /dev/null
+++ b/framework/src/suricata/src/ippair-bit.c
@@ -0,0 +1,506 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements per ippair bits. Actually, not a bit,
+ * but called that way because of Snort's flowbits.
+ * It's a binary storage.
+ *
+ * \todo move away from a linked list implementation
+ * \todo use different datatypes, such as string, int, etc.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "ippair-bit.h"
+#include "ippair.h"
+#include "detect.h"
+#include "util-var.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "ippair-storage.h"
+
+static int ippair_bit_id = -1; /**< IPPair storage id for bits */
+
+void XBitFreeAll(void *store) {
+ GenericVar *gv = store;
+ GenericVarFree(gv);
+}
+
+void IPPairBitInitCtx(void)
+{
+ ippair_bit_id = IPPairStorageRegister("bit", sizeof(void *), NULL, XBitFreeAll);
+ if (ippair_bit_id == -1) {
+ SCLogError(SC_ERR_IPPAIR_INIT, "Can't initiate ippair storage for bits");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* lock before using this */
+int IPPairHasBits(IPPair *ippair)
+{
+ if (ippair == NULL)
+ return 0;
+ return IPPairGetStorageById(ippair, ippair_bit_id) ? 1 : 0;
+}
+
+/** \retval 1 ippair timed out wrt xbits
+ * \retval 0 ippair still has active (non-expired) xbits */
+int IPPairBitsTimedoutCheck(IPPair *h, struct timeval *ts)
+{
+ GenericVar *gv = IPPairGetStorageById(h, ippair_bit_id);
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_XBITS) {
+ XBit *xb = (XBit *)gv;
+ if (xb->expire > (uint32_t)ts->tv_sec)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* get the bit with idx from the ippair */
+static XBit *IPPairBitGet(IPPair *h, uint16_t idx)
+{
+ GenericVar *gv = IPPairGetStorageById(h, ippair_bit_id);
+ for ( ; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_XBITS && gv->idx == idx) {
+ return (XBit *)gv;
+ }
+ }
+
+ return NULL;
+}
+
+/* add a flowbit to the flow */
+static void IPPairBitAdd(IPPair *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb == NULL) {
+ fb = SCMalloc(sizeof(XBit));
+ if (unlikely(fb == NULL))
+ return;
+
+ fb->type = DETECT_XBITS;
+ fb->idx = idx;
+ fb->next = NULL;
+ fb->expire = expire;
+
+ GenericVar *gv = IPPairGetStorageById(h, ippair_bit_id);
+ GenericVarAppend(&gv, (GenericVar *)fb);
+ IPPairSetStorageById(h, ippair_bit_id, gv);
+
+ // bit already set, lets update it's timer
+ } else {
+ fb->expire = expire;
+ }
+}
+
+static void IPPairBitRemove(IPPair *h, uint16_t idx)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb == NULL)
+ return;
+
+ GenericVar *gv = IPPairGetStorageById(h, ippair_bit_id);
+ if (gv) {
+ GenericVarRemove(&gv, (GenericVar *)fb);
+ IPPairSetStorageById(h, ippair_bit_id, gv);
+ }
+}
+
+void IPPairBitSet(IPPair *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb == NULL) {
+ IPPairBitAdd(h, idx, expire);
+ }
+}
+
+void IPPairBitUnset(IPPair *h, uint16_t idx)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb != NULL) {
+ IPPairBitRemove(h, idx);
+ }
+}
+
+void IPPairBitToggle(IPPair *h, uint16_t idx, uint32_t expire)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb != NULL) {
+ IPPairBitRemove(h, idx);
+ } else {
+ IPPairBitAdd(h, idx, expire);
+ }
+}
+
+int IPPairBitIsset(IPPair *h, uint16_t idx, uint32_t ts)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb != NULL) {
+ if (fb->expire < ts) {
+ IPPairBitRemove(h, idx);
+ return 0;
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+int IPPairBitIsnotset(IPPair *h, uint16_t idx, uint32_t ts)
+{
+ XBit *fb = IPPairBitGet(h, idx);
+ if (fb == NULL) {
+ return 1;
+ }
+
+ if (fb->expire < ts) {
+ IPPairBitRemove(h, idx);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* TESTS */
+#ifdef UNITTESTS
+static int IPPairBitTest01 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0, 0);
+
+ XBit *fb = IPPairBitGet(h,0);
+ if (fb != NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest02 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ XBit *fb = IPPairBitGet(h,0);
+ if (fb == NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest03 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0, 30);
+
+ XBit *fb = IPPairBitGet(h,0);
+ if (fb == NULL) {
+ printf("fb == NULL although it was just added: ");
+ goto end;
+ }
+
+ IPPairBitRemove(h, 0);
+
+ fb = IPPairBitGet(h,0);
+ if (fb != NULL) {
+ printf("fb != NULL although it was just removed: ");
+ goto end;
+ } else {
+ ret = 1;
+ }
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest04 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,30);
+ IPPairBitAdd(h, 1,30);
+ IPPairBitAdd(h, 2,30);
+ IPPairBitAdd(h, 3,30);
+
+ XBit *fb = IPPairBitGet(h,0);
+ if (fb != NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest05 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,1);
+ if (fb != NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest06 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,2);
+ if (fb != NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest07 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,3);
+ if (fb != NULL)
+ ret = 1;
+
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest08 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,0);
+ if (fb == NULL)
+ goto end;
+
+ IPPairBitRemove(h,0);
+
+ fb = IPPairBitGet(h,0);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest09 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,1);
+ if (fb == NULL)
+ goto end;
+
+ IPPairBitRemove(h,1);
+
+ fb = IPPairBitGet(h,1);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest10 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,2);
+ if (fb == NULL)
+ goto end;
+
+ IPPairBitRemove(h,2);
+
+ fb = IPPairBitGet(h,2);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+static int IPPairBitTest11 (void)
+{
+ int ret = 0;
+
+ IPPairInitConfig(TRUE);
+ IPPair *h = IPPairAlloc();
+ if (h == NULL)
+ goto end;
+
+ IPPairBitAdd(h, 0,90);
+ IPPairBitAdd(h, 1,90);
+ IPPairBitAdd(h, 2,90);
+ IPPairBitAdd(h, 3,90);
+
+ XBit *fb = IPPairBitGet(h,3);
+ if (fb == NULL)
+ goto end;
+
+ IPPairBitRemove(h,3);
+
+ fb = IPPairBitGet(h,3);
+ if (fb != NULL) {
+ printf("fb != NULL even though it was removed: ");
+ goto end;
+ }
+
+ ret = 1;
+ IPPairFree(h);
+end:
+ IPPairCleanup();
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void IPPairBitRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IPPairBitTest01", IPPairBitTest01, 1);
+ UtRegisterTest("IPPairBitTest02", IPPairBitTest02, 1);
+ UtRegisterTest("IPPairBitTest03", IPPairBitTest03, 1);
+ UtRegisterTest("IPPairBitTest04", IPPairBitTest04, 1);
+ UtRegisterTest("IPPairBitTest05", IPPairBitTest05, 1);
+ UtRegisterTest("IPPairBitTest06", IPPairBitTest06, 1);
+ UtRegisterTest("IPPairBitTest07", IPPairBitTest07, 1);
+ UtRegisterTest("IPPairBitTest08", IPPairBitTest08, 1);
+ UtRegisterTest("IPPairBitTest09", IPPairBitTest09, 1);
+ UtRegisterTest("IPPairBitTest10", IPPairBitTest10, 1);
+ UtRegisterTest("IPPairBitTest11", IPPairBitTest11, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/ippair-bit.h b/framework/src/suricata/src/ippair-bit.h
new file mode 100644
index 00000000..44a0ac46
--- /dev/null
+++ b/framework/src/suricata/src/ippair-bit.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __IPPAIR_BIT_H__
+#define __IPPAIR_BIT_H__
+
+#include "ippair.h"
+#include "util-var.h"
+
+void IPPairBitInitCtx(void);
+void IPPairBitRegisterTests(void);
+
+int IPPairHasBits(IPPair *host);
+int IPPairBitsTimedoutCheck(IPPair *h, struct timeval *ts);
+
+void IPPairBitSet(IPPair *, uint16_t, uint32_t);
+void IPPairBitUnset(IPPair *, uint16_t);
+void IPPairBitToggle(IPPair *, uint16_t, uint32_t);
+int IPPairBitIsset(IPPair *, uint16_t, uint32_t);
+int IPPairBitIsnotset(IPPair *, uint16_t, uint32_t);
+
+#endif /* __IPPAIR_BIT_H__ */
diff --git a/framework/src/suricata/src/ippair-queue.c b/framework/src/suricata/src/ippair-queue.c
new file mode 100644
index 00000000..0f68200d
--- /dev/null
+++ b/framework/src/suricata/src/ippair-queue.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * IPPair queue handler functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "ippair-queue.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-print.h"
+
+IPPairQueue *IPPairQueueInit (IPPairQueue *q)
+{
+ if (q != NULL) {
+ memset(q, 0, sizeof(IPPairQueue));
+ HQLOCK_INIT(q);
+ }
+ return q;
+}
+
+IPPairQueue *IPPairQueueNew()
+{
+ IPPairQueue *q = (IPPairQueue *)SCMalloc(sizeof(IPPairQueue));
+ if (q == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in IPPairQueueNew. Exiting...");
+ exit(EXIT_SUCCESS);
+ }
+ q = IPPairQueueInit(q);
+ return q;
+}
+
+/**
+ * \brief Destroy a ippair queue
+ *
+ * \param q the ippair queue to destroy
+ */
+void IPPairQueueDestroy (IPPairQueue *q)
+{
+ HQLOCK_DESTROY(q);
+}
+
+/**
+ * \brief add a ippair to a queue
+ *
+ * \param q queue
+ * \param h ippair
+ */
+void IPPairEnqueue (IPPairQueue *q, IPPair *h)
+{
+#ifdef DEBUG
+ BUG_ON(q == NULL || h == NULL);
+#endif
+
+ HQLOCK_LOCK(q);
+
+ /* more ippairs in queue */
+ if (q->top != NULL) {
+ h->lnext = q->top;
+ q->top->lprev = h;
+ q->top = h;
+ /* only ippair */
+ } else {
+ q->top = h;
+ q->bot = h;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ HQLOCK_UNLOCK(q);
+}
+
+/**
+ * \brief remove a ippair from the queue
+ *
+ * \param q queue
+ *
+ * \retval h ippair or NULL if empty list.
+ */
+IPPair *IPPairDequeue (IPPairQueue *q)
+{
+ HQLOCK_LOCK(q);
+
+ IPPair *h = q->bot;
+ if (h == NULL) {
+ HQLOCK_UNLOCK(q);
+ return NULL;
+ }
+
+ /* more packets in queue */
+ if (q->bot->lprev != NULL) {
+ q->bot = q->bot->lprev;
+ q->bot->lnext = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+#ifdef DEBUG
+ BUG_ON(q->len == 0);
+#endif
+ if (q->len > 0)
+ q->len--;
+
+ h->lnext = NULL;
+ h->lprev = NULL;
+
+ HQLOCK_UNLOCK(q);
+ return h;
+}
+
+uint32_t IPPairQueueLen(IPPairQueue *q)
+{
+ uint32_t len;
+ HQLOCK_LOCK(q);
+ len = q->len;
+ HQLOCK_UNLOCK(q);
+ return len;
+}
diff --git a/framework/src/suricata/src/ippair-queue.h b/framework/src/suricata/src/ippair-queue.h
new file mode 100644
index 00000000..5c80cf39
--- /dev/null
+++ b/framework/src/suricata/src/ippair-queue.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __IPPAIR_QUEUE_H__
+#define __IPPAIR_QUEUE_H__
+
+#include "suricata-common.h"
+#include "ippair.h"
+
+/** Spinlocks or Mutex for the ippair queues. */
+//#define HQLOCK_SPIN
+#define HQLOCK_MUTEX
+
+#ifdef HQLOCK_SPIN
+ #ifdef HQLOCK_MUTEX
+ #error Cannot enable both HQLOCK_SPIN and HQLOCK_MUTEX
+ #endif
+#endif
+
+/* Define a queue for storing ippairs */
+typedef struct IPPairQueue_
+{
+ IPPair *top;
+ IPPair *bot;
+ uint32_t len;
+#ifdef DBG_PERF
+ uint32_t dbg_maxlen;
+#endif /* DBG_PERF */
+#ifdef HQLOCK_MUTEX
+ SCMutex m;
+#elif defined HQLOCK_SPIN
+ SCSpinlock s;
+#else
+ #error Enable HQLOCK_SPIN or HQLOCK_MUTEX
+#endif
+} IPPairQueue;
+
+#ifdef HQLOCK_SPIN
+ #define HQLOCK_INIT(q) SCSpinInit(&(q)->s, 0)
+ #define HQLOCK_DESTROY(q) SCSpinDestroy(&(q)->s)
+ #define HQLOCK_LOCK(q) SCSpinLock(&(q)->s)
+ #define HQLOCK_TRYLOCK(q) SCSpinTrylock(&(q)->s)
+ #define HQLOCK_UNLOCK(q) SCSpinUnlock(&(q)->s)
+#elif defined HQLOCK_MUTEX
+ #define HQLOCK_INIT(q) SCMutexInit(&(q)->m, NULL)
+ #define HQLOCK_DESTROY(q) SCMutexDestroy(&(q)->m)
+ #define HQLOCK_LOCK(q) SCMutexLock(&(q)->m)
+ #define HQLOCK_TRYLOCK(q) SCMutexTrylock(&(q)->m)
+ #define HQLOCK_UNLOCK(q) SCMutexUnlock(&(q)->m)
+#else
+ #error Enable HQLOCK_SPIN or HQLOCK_MUTEX
+#endif
+
+/* prototypes */
+IPPairQueue *IPPairQueueNew();
+IPPairQueue *IPPairQueueInit(IPPairQueue *);
+void IPPairQueueDestroy (IPPairQueue *);
+
+void IPPairEnqueue (IPPairQueue *, IPPair *);
+IPPair *IPPairDequeue (IPPairQueue *);
+uint32_t IPPairQueueLen(IPPairQueue *);
+
+#endif /* __IPPAIR_QUEUE_H__ */
diff --git a/framework/src/suricata/src/ippair-storage.c b/framework/src/suricata/src/ippair-storage.c
new file mode 100644
index 00000000..dddc7136
--- /dev/null
+++ b/framework/src/suricata/src/ippair-storage.c
@@ -0,0 +1,299 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * IPPair wrapper around storage api
+ */
+
+#include "suricata-common.h"
+#include "ippair-storage.h"
+#include "util-unittest.h"
+
+unsigned int IPPairStorageSize(void)
+{
+ return StorageGetSize(STORAGE_IPPAIR);
+}
+
+void *IPPairGetStorageById(IPPair *h, int id)
+{
+ return StorageGetById((Storage *)((void *)h + sizeof(IPPair)), STORAGE_IPPAIR, id);
+}
+
+int IPPairSetStorageById(IPPair *h, int id, void *ptr)
+{
+ return StorageSetById((Storage *)((void *)h + sizeof(IPPair)), STORAGE_IPPAIR, id, ptr);
+}
+
+void *IPPairAllocStorageById(IPPair *h, int id)
+{
+ return StorageAllocByIdPrealloc((Storage *)((void *)h + sizeof(IPPair)), STORAGE_IPPAIR, id);
+}
+
+void IPPairFreeStorageById(IPPair *h, int id)
+{
+ StorageFreeById((Storage *)((void *)h + sizeof(IPPair)), STORAGE_IPPAIR, id);
+}
+
+void IPPairFreeStorage(IPPair *h)
+{
+ if (IPPairStorageSize() > 0)
+ StorageFreeAll((Storage *)((void *)h + sizeof(IPPair)), STORAGE_IPPAIR);
+}
+
+int IPPairStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *)) {
+ return StorageRegister(STORAGE_IPPAIR, name, size, Alloc, Free);
+}
+
+#ifdef UNITTESTS
+
+static void *StorageTestAlloc(unsigned int size)
+{
+ void *x = SCMalloc(size);
+ return x;
+}
+static void StorageTestFree(void *x)
+{
+ if (x)
+ SCFree(x);
+}
+
+static int IPPairStorageTest01(void)
+{
+ StorageInit();
+
+ int id1 = IPPairStorageRegister("test", 8, StorageTestAlloc, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = IPPairStorageRegister("variable", 24, StorageTestAlloc, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = IPPairStorageRegister("store", sizeof(void *), StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ IPPairInitConfig(1);
+
+ Address a, b;
+ memset(&a, 0x00, sizeof(a));
+ memset(&b, 0x00, sizeof(b));
+ a.addr_data32[0] = 0x01020304;
+ b.addr_data32[0] = 0x04030201;
+ a.family = AF_INET;
+ b.family = AF_INET;
+ IPPair *h = IPPairGetIPPairFromHash(&a, &b);
+ if (h == NULL) {
+ printf("failed to get ippair: ");
+ goto error;
+ }
+
+ void *ptr = IPPairGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = IPPairGetStorageById(h, id2);
+ if (ptr != NULL) {
+ goto error;
+ }
+ ptr = IPPairGetStorageById(h, id3);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = IPPairAllocStorageById(h, id1);
+ if (ptr1a == NULL) {
+ goto error;
+ }
+ void *ptr2a = IPPairAllocStorageById(h, id2);
+ if (ptr2a == NULL) {
+ goto error;
+ }
+ void *ptr3a = IPPairAllocStorageById(h, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = IPPairGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = IPPairGetStorageById(h, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = IPPairGetStorageById(h, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ IPPairRelease(h);
+
+ IPPairShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ IPPairShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int IPPairStorageTest02(void)
+{
+ StorageInit();
+
+ int id1 = IPPairStorageRegister("test", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ IPPairInitConfig(1);
+
+ Address a, b;
+ memset(&a, 0x00, sizeof(a));
+ memset(&b, 0x00, sizeof(b));
+ a.addr_data32[0] = 0x01020304;
+ b.addr_data32[0] = 0x04030201;
+ a.family = AF_INET;
+ b.family = AF_INET;
+ IPPair *h = IPPairGetIPPairFromHash(&a, &b);
+ if (h == NULL) {
+ printf("failed to get ippair: ");
+ goto error;
+ }
+
+ void *ptr = IPPairGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ IPPairSetStorageById(h, id1, ptr1a);
+
+ void *ptr1b = IPPairGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+
+ IPPairRelease(h);
+
+ IPPairShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ IPPairShutdown();
+ StorageCleanup();
+ return 0;
+}
+
+static int IPPairStorageTest03(void)
+{
+ StorageInit();
+
+ int id1 = IPPairStorageRegister("test1", sizeof(void *), NULL, StorageTestFree);
+ if (id1 < 0)
+ goto error;
+ int id2 = IPPairStorageRegister("test2", sizeof(void *), NULL, StorageTestFree);
+ if (id2 < 0)
+ goto error;
+ int id3 = IPPairStorageRegister("test3", 32, StorageTestAlloc, StorageTestFree);
+ if (id3 < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ IPPairInitConfig(1);
+
+ Address a, b;
+ memset(&a, 0x00, sizeof(a));
+ memset(&b, 0x00, sizeof(b));
+ a.addr_data32[0] = 0x01020304;
+ b.addr_data32[0] = 0x04030201;
+ a.family = AF_INET;
+ b.family = AF_INET;
+ IPPair *h = IPPairGetIPPairFromHash(&a, &b);
+ if (h == NULL) {
+ printf("failed to get ippair: ");
+ goto error;
+ }
+
+ void *ptr = IPPairGetStorageById(h, id1);
+ if (ptr != NULL) {
+ goto error;
+ }
+
+ void *ptr1a = SCMalloc(128);
+ if (unlikely(ptr1a == NULL)) {
+ goto error;
+ }
+ IPPairSetStorageById(h, id1, ptr1a);
+
+ void *ptr2a = SCMalloc(256);
+ if (unlikely(ptr2a == NULL)) {
+ goto error;
+ }
+ IPPairSetStorageById(h, id2, ptr2a);
+
+ void *ptr3a = IPPairAllocStorageById(h, id3);
+ if (ptr3a == NULL) {
+ goto error;
+ }
+
+ void *ptr1b = IPPairGetStorageById(h, id1);
+ if (ptr1a != ptr1b) {
+ goto error;
+ }
+ void *ptr2b = IPPairGetStorageById(h, id2);
+ if (ptr2a != ptr2b) {
+ goto error;
+ }
+ void *ptr3b = IPPairGetStorageById(h, id3);
+ if (ptr3a != ptr3b) {
+ goto error;
+ }
+
+ IPPairRelease(h);
+
+ IPPairShutdown();
+ StorageCleanup();
+ return 1;
+error:
+ IPPairShutdown();
+ StorageCleanup();
+ return 0;
+}
+#endif
+
+void RegisterIPPairStorageTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IPPairStorageTest01", IPPairStorageTest01, 1);
+ UtRegisterTest("IPPairStorageTest02", IPPairStorageTest02, 1);
+ UtRegisterTest("IPPairStorageTest03", IPPairStorageTest03, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/ippair-storage.h b/framework/src/suricata/src/ippair-storage.h
new file mode 100644
index 00000000..0671ac91
--- /dev/null
+++ b/framework/src/suricata/src/ippair-storage.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * IPPair wrapper around storage api
+ */
+
+#ifndef __IPPAIR_STORAGE_H__
+#define __IPPAIR_STORAGE_H__
+
+#include "util-storage.h"
+#include "ippair.h"
+
+unsigned int IPPairStorageSize(void);
+
+void *IPPairGetStorageById(IPPair *h, int id);
+int IPPairSetStorageById(IPPair *h, int id, void *ptr);
+void *IPPairAllocStorageById(IPPair *h, int id);
+
+void IPPairFreeStorageById(IPPair *h, int id);
+void IPPairFreeStorage(IPPair *h);
+
+void RegisterIPPairStorageTests(void);
+
+int IPPairStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *));
+
+#endif /* __IPPAIR_STORAGE_H__ */
diff --git a/framework/src/suricata/src/ippair-timeout.c b/framework/src/suricata/src/ippair-timeout.c
new file mode 100644
index 00000000..1225f825
--- /dev/null
+++ b/framework/src/suricata/src/ippair-timeout.c
@@ -0,0 +1,159 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "ippair.h"
+#include "ippair-bit.h"
+
+uint32_t IPPairGetSpareCount(void)
+{
+ return IPPairSpareQueueGetSize();
+}
+
+uint32_t IPPairGetActiveCount(void)
+{
+ return SC_ATOMIC_GET(ippair_counter);
+}
+
+/** \internal
+ * \brief See if we can really discard this ippair. Check use_cnt reference.
+ *
+ * \param h ippair
+ * \param ts timestamp
+ *
+ * \retval 0 not timed out just yet
+ * \retval 1 fully timed out, lets kill it
+ */
+static int IPPairTimedOut(IPPair *h, struct timeval *ts)
+{
+ int vars = 0;
+
+ /** never prune a ippair that is used by a packet
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(h->use_cnt) > 0) {
+ return 0;
+ }
+
+ if (IPPairHasBits(h) && IPPairBitsTimedoutCheck(h, ts) == 0) {
+ vars = 1;
+ }
+
+ if (vars) {
+ return 0;
+ }
+
+ SCLogDebug("ippair %p timed out", h);
+ return 1;
+}
+
+/**
+ * \internal
+ *
+ * \brief check all ippairs in a hash row for timing out
+ *
+ * \param hb ippair hash row *LOCKED*
+ * \param h last ippair in the hash row
+ * \param ts timestamp
+ *
+ * \retval cnt timed out ippairs
+ */
+static uint32_t IPPairHashRowTimeout(IPPairHashRow *hb, IPPair *h, struct timeval *ts)
+{
+ uint32_t cnt = 0;
+
+ do {
+ if (SCMutexTrylock(&h->m) != 0) {
+ h = h->hprev;
+ continue;
+ }
+
+ IPPair *next_ippair = h->hprev;
+
+ /* check if the ippair is fully timed out and
+ * ready to be discarded. */
+ if (IPPairTimedOut(h, ts) == 1) {
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+
+ h->hnext = NULL;
+ h->hprev = NULL;
+
+ IPPairClearMemory (h);
+
+ /* no one is referring to this ippair, use_cnt 0, removed from hash
+ * so we can unlock it and move it back to the spare queue. */
+ SCMutexUnlock(&h->m);
+
+ /* move to spare list */
+ IPPairMoveToSpare(h);
+
+ cnt++;
+ } else {
+ SCMutexUnlock(&h->m);
+ }
+
+ h = next_ippair;
+ } while (h != NULL);
+
+ return cnt;
+}
+
+/**
+ * \brief time out ippairs from the hash
+ *
+ * \param ts timestamp
+ *
+ * \retval cnt number of timed out ippair
+ */
+uint32_t IPPairTimeoutHash(struct timeval *ts)
+{
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+
+ for (idx = 0; idx < ippair_config.hash_size; idx++) {
+ IPPairHashRow *hb = &ippair_hash[idx];
+
+ if (HRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ /* ippair hash bucket is now locked */
+
+ if (hb->tail == NULL) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /* we have a ippair, or more than one */
+ cnt += IPPairHashRowTimeout(hb, hb->tail, ts);
+ HRLOCK_UNLOCK(hb);
+ }
+
+ return cnt;
+}
diff --git a/framework/src/suricata/src/ippair-timeout.h b/framework/src/suricata/src/ippair-timeout.h
new file mode 100644
index 00000000..15d8e96a
--- /dev/null
+++ b/framework/src/suricata/src/ippair-timeout.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __IPPAIR_TIMEOUT_H__
+#define __IPPAIR_TIMEOUT_H__
+
+uint32_t IPPairTimeoutHash(struct timeval *ts);
+
+uint32_t IPPairGetSpareCount(void);
+uint32_t IPPairGetActiveCount(void);
+
+#endif
diff --git a/framework/src/suricata/src/ippair.c b/framework/src/suricata/src/ippair.c
new file mode 100644
index 00000000..780ee6ce
--- /dev/null
+++ b/framework/src/suricata/src/ippair.c
@@ -0,0 +1,687 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Information about ippairs.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "util-debug.h"
+#include "ippair.h"
+#include "ippair-storage.h"
+
+#include "util-random.h"
+#include "util-misc.h"
+#include "util-byte.h"
+
+#include "ippair-queue.h"
+
+#include "detect-tag.h"
+#include "detect-engine-tag.h"
+#include "detect-engine-threshold.h"
+
+#include "util-hash-lookup3.h"
+
+static IPPair *IPPairGetUsedIPPair(void);
+
+/** queue with spare ippairs */
+static IPPairQueue ippair_spare_q;
+
+uint32_t IPPairSpareQueueGetSize(void)
+{
+ return IPPairQueueLen(&ippair_spare_q);
+}
+
+void IPPairMoveToSpare(IPPair *h)
+{
+ IPPairEnqueue(&ippair_spare_q, h);
+ (void) SC_ATOMIC_SUB(ippair_counter, 1);
+}
+
+IPPair *IPPairAlloc(void)
+{
+ size_t size = sizeof(IPPair) + IPPairStorageSize();
+
+ if (!(IPPAIR_CHECK_MEMCAP(size))) {
+ return NULL;
+ }
+
+ (void) SC_ATOMIC_ADD(ippair_memuse, size);
+
+ IPPair *h = SCMalloc(size);
+ if (unlikely(h == NULL))
+ goto error;
+
+ memset(h, 0x00, size);
+
+ SCMutexInit(&h->m, NULL);
+ SC_ATOMIC_INIT(h->use_cnt);
+ return h;
+
+error:
+ return NULL;
+}
+
+void IPPairFree(IPPair *h)
+{
+ if (h != NULL) {
+ IPPairClearMemory(h);
+
+ SC_ATOMIC_DESTROY(h->use_cnt);
+ SCMutexDestroy(&h->m);
+ SCFree(h);
+ (void) SC_ATOMIC_SUB(ippair_memuse, (sizeof(IPPair) + IPPairStorageSize()));
+ }
+}
+
+IPPair *IPPairNew(Address *a, Address *b)
+{
+ IPPair *p = IPPairAlloc();
+ if (p == NULL)
+ goto error;
+
+ /* copy addresses */
+ COPY_ADDRESS(a, &p->a[0]);
+ COPY_ADDRESS(b, &p->a[1]);
+
+ return p;
+
+error:
+ return NULL;
+}
+
+void IPPairClearMemory(IPPair *h)
+{
+ if (IPPairStorageSize() > 0)
+ IPPairFreeStorage(h);
+}
+
+#define IPPAIR_DEFAULT_HASHSIZE 4096
+#define IPPAIR_DEFAULT_MEMCAP 16777216
+#define IPPAIR_DEFAULT_PREALLOC 1000
+
+/** \brief initialize the configuration
+ * \warning Not thread safe */
+void IPPairInitConfig(char quiet)
+{
+ SCLogDebug("initializing ippair engine...");
+
+ memset(&ippair_config, 0, sizeof(ippair_config));
+ //SC_ATOMIC_INIT(flow_flags);
+ SC_ATOMIC_INIT(ippair_counter);
+ SC_ATOMIC_INIT(ippair_memuse);
+ SC_ATOMIC_INIT(ippair_prune_idx);
+ IPPairQueueInit(&ippair_spare_q);
+
+ unsigned int seed = RandomTimePreseed();
+ /* set defaults */
+ ippair_config.hash_rand = (int)( IPPAIR_DEFAULT_HASHSIZE * (rand_r(&seed) / RAND_MAX + 1.0));
+
+ ippair_config.hash_size = IPPAIR_DEFAULT_HASHSIZE;
+ ippair_config.memcap = IPPAIR_DEFAULT_MEMCAP;
+ ippair_config.prealloc = IPPAIR_DEFAULT_PREALLOC;
+
+ /* Check if we have memcap and hash_size defined at config */
+ char *conf_val;
+ uint32_t configval = 0;
+
+ /** set config values for memcap, prealloc and hash_size */
+ if ((ConfGet("ippair.memcap", &conf_val)) == 1)
+ {
+ if (ParseSizeStringU64(conf_val, &ippair_config.memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ippair.memcap "
+ "from conf file - %s. Killing engine",
+ conf_val);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if ((ConfGet("ippair.hash-size", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ ippair_config.hash_size = configval;
+ }
+ }
+
+ if ((ConfGet("ippair.prealloc", &conf_val)) == 1)
+ {
+ if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
+ conf_val) > 0) {
+ ippair_config.prealloc = configval;
+ } else {
+ WarnInvalidConfEntry("ippair.prealloc", "%"PRIu32, ippair_config.prealloc);
+ }
+ }
+ SCLogDebug("IPPair config from suricata.yaml: memcap: %"PRIu64", hash-size: "
+ "%"PRIu32", prealloc: %"PRIu32, ippair_config.memcap,
+ ippair_config.hash_size, ippair_config.prealloc);
+
+ /* alloc hash memory */
+ uint64_t hash_size = ippair_config.hash_size * sizeof(IPPairHashRow);
+ if (!(IPPAIR_CHECK_MEMCAP(hash_size))) {
+ SCLogError(SC_ERR_IPPAIR_INIT, "allocating ippair hash failed: "
+ "max ippair memcap is smaller than projected hash size. "
+ "Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate "
+ "total hash size by multiplying \"ippair.hash-size\" with %"PRIuMAX", "
+ "which is the hash bucket size.", ippair_config.memcap, hash_size,
+ (uintmax_t)sizeof(IPPairHashRow));
+ exit(EXIT_FAILURE);
+ }
+ ippair_hash = SCCalloc(ippair_config.hash_size, sizeof(IPPairHashRow));
+ if (unlikely(ippair_hash == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in IPPairInitConfig. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(ippair_hash, 0, ippair_config.hash_size * sizeof(IPPairHashRow));
+
+ uint32_t i = 0;
+ for (i = 0; i < ippair_config.hash_size; i++) {
+ HRLOCK_INIT(&ippair_hash[i]);
+ }
+ (void) SC_ATOMIC_ADD(ippair_memuse, (ippair_config.hash_size * sizeof(IPPairHashRow)));
+
+ if (quiet == FALSE) {
+ SCLogInfo("allocated %llu bytes of memory for the ippair hash... "
+ "%" PRIu32 " buckets of size %" PRIuMAX "",
+ SC_ATOMIC_GET(ippair_memuse), ippair_config.hash_size,
+ (uintmax_t)sizeof(IPPairHashRow));
+ }
+
+ /* pre allocate ippairs */
+ for (i = 0; i < ippair_config.prealloc; i++) {
+ if (!(IPPAIR_CHECK_MEMCAP(sizeof(IPPair)))) {
+ SCLogError(SC_ERR_IPPAIR_INIT, "preallocating ippairs failed: "
+ "max ippair memcap reached. Memcap %"PRIu64", "
+ "Memuse %"PRIu64".", ippair_config.memcap,
+ ((uint64_t)SC_ATOMIC_GET(ippair_memuse) + (uint64_t)sizeof(IPPair)));
+ exit(EXIT_FAILURE);
+ }
+
+ IPPair *h = IPPairAlloc();
+ if (h == NULL) {
+ SCLogError(SC_ERR_IPPAIR_INIT, "preallocating ippair failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ IPPairEnqueue(&ippair_spare_q,h);
+ }
+
+ if (quiet == FALSE) {
+ SCLogInfo("preallocated %" PRIu32 " ippairs of size %" PRIuMAX "",
+ ippair_spare_q.len, (uintmax_t)sizeof(IPPair));
+ SCLogInfo("ippair memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(ippair_memuse), ippair_config.memcap);
+ }
+
+ return;
+}
+
+/** \brief print some ippair stats
+ * \warning Not thread safe */
+void IPPairPrintStats (void)
+{
+#ifdef IPPAIRBITS_STATS
+ SCLogInfo("ippairbits added: %" PRIu32 ", removed: %" PRIu32 ", max memory usage: %" PRIu32 "",
+ ippairbits_added, ippairbits_removed, ippairbits_memuse_max);
+#endif /* IPPAIRBITS_STATS */
+ SCLogInfo("ippair memory usage: %llu bytes, maximum: %"PRIu64,
+ SC_ATOMIC_GET(ippair_memuse), ippair_config.memcap);
+ return;
+}
+
+/** \brief shutdown the flow engine
+ * \warning Not thread safe */
+void IPPairShutdown(void)
+{
+ IPPair *h;
+ uint32_t u;
+
+ IPPairPrintStats();
+
+ /* free spare queue */
+ while((h = IPPairDequeue(&ippair_spare_q))) {
+ BUG_ON(SC_ATOMIC_GET(h->use_cnt) > 0);
+ IPPairFree(h);
+ }
+
+ /* clear and free the hash */
+ if (ippair_hash != NULL) {
+ for (u = 0; u < ippair_config.hash_size; u++) {
+ IPPair *h = ippair_hash[u].head;
+ while (h) {
+ IPPair *n = h->hnext;
+ IPPairFree(h);
+ h = n;
+ }
+
+ HRLOCK_DESTROY(&ippair_hash[u]);
+ }
+ SCFree(ippair_hash);
+ ippair_hash = NULL;
+ }
+ (void) SC_ATOMIC_SUB(ippair_memuse, ippair_config.hash_size * sizeof(IPPairHashRow));
+ IPPairQueueDestroy(&ippair_spare_q);
+
+ SC_ATOMIC_DESTROY(ippair_prune_idx);
+ SC_ATOMIC_DESTROY(ippair_memuse);
+ SC_ATOMIC_DESTROY(ippair_counter);
+ //SC_ATOMIC_DESTROY(flow_flags);
+ return;
+}
+
+/** \brief Cleanup the ippair engine
+ *
+ * Cleanup the ippair engine from tag and threshold.
+ *
+ */
+void IPPairCleanup(void)
+{
+ IPPair *h;
+ uint32_t u;
+
+ if (ippair_hash != NULL) {
+ for (u = 0; u < ippair_config.hash_size; u++) {
+ h = ippair_hash[u].head;
+ IPPairHashRow *hb = &ippair_hash[u];
+ HRLOCK_LOCK(hb);
+ while (h) {
+ if ((SC_ATOMIC_GET(h->use_cnt) > 0)) {
+ /* iprep is attached to ippair only clear local storage */
+ IPPairFreeStorage(h);
+ h = h->hnext;
+ } else {
+ IPPair *n = h->hnext;
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+ h->hnext = NULL;
+ h->hprev = NULL;
+ IPPairClearMemory(h);
+ IPPairMoveToSpare(h);
+ h = n;
+ }
+ }
+ HRLOCK_UNLOCK(hb);
+ }
+ }
+
+ return;
+}
+
+/* calculate the hash key for this packet
+ *
+ * we're using:
+ * hash_rand -- set at init time
+ * source address
+ */
+static uint32_t IPPairGetKey(Address *a, Address *b)
+{
+ uint32_t key;
+
+ if (a->family == AF_INET) {
+ uint32_t hash = hashword(&a->addr_data32[0], 1, ippair_config.hash_rand);
+ key = hash % ippair_config.hash_size;
+ } else if (a->family == AF_INET6) {
+ uint32_t hash = hashword(a->addr_data32, 4, ippair_config.hash_rand);
+ key = hash % ippair_config.hash_size;
+ } else
+ key = 0;
+
+ return key;
+}
+
+/* Since two or more ippairs can have the same hash key, we need to compare
+ * the ippair with the current addresses. */
+static inline int IPPairCompare(IPPair *p, Address *a, Address *b)
+{
+ /* compare in both directions */
+ if ((CMP_ADDR(&p->a[0], a) && CMP_ADDR(&p->a[1], b)) ||
+ (CMP_ADDR(&p->a[0], b) && CMP_ADDR(&p->a[1], a)))
+ return 1;
+ return 0;
+}
+
+/**
+ * \brief Get a new ippair
+ *
+ * Get a new ippair. We're checking memcap first and will try to make room
+ * if the memcap is reached.
+ *
+ * \retval h *LOCKED* ippair on succes, NULL on error.
+ */
+static IPPair *IPPairGetNew(Address *a, Address *b)
+{
+ IPPair *h = NULL;
+
+ /* get a ippair from the spare queue */
+ h = IPPairDequeue(&ippair_spare_q);
+ if (h == NULL) {
+ /* If we reached the max memcap, we get a used ippair */
+ if (!(IPPAIR_CHECK_MEMCAP(sizeof(IPPair)))) {
+ /* declare state of emergency */
+ //if (!(SC_ATOMIC_GET(ippair_flags) & IPPAIR_EMERGENCY)) {
+ // SC_ATOMIC_OR(ippair_flags, IPPAIR_EMERGENCY);
+
+ /* under high load, waking up the flow mgr each time leads
+ * to high cpu usage. Flows are not timed out much faster if
+ * we check a 1000 times a second. */
+ // FlowWakeupFlowManagerThread();
+ //}
+
+ h = IPPairGetUsedIPPair();
+ if (h == NULL) {
+ return NULL;
+ }
+
+ /* freed a ippair, but it's unlocked */
+ } else {
+ /* now see if we can alloc a new ippair */
+ h = IPPairNew(a,b);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ /* ippair is initialized but *unlocked* */
+ }
+ } else {
+ /* ippair has been recycled before it went into the spare queue */
+
+ /* ippair is initialized (recylced) but *unlocked* */
+ }
+
+ (void) SC_ATOMIC_ADD(ippair_counter, 1);
+ SCMutexLock(&h->m);
+ return h;
+}
+
+void IPPairInit(IPPair *h, Address *a, Address *b)
+{
+ COPY_ADDRESS(a, &h->a[0]);
+ COPY_ADDRESS(b, &h->a[1]);
+ (void) IPPairIncrUsecnt(h);
+}
+
+void IPPairRelease(IPPair *h)
+{
+ (void) IPPairDecrUsecnt(h);
+ SCMutexUnlock(&h->m);
+}
+
+void IPPairLock(IPPair *h)
+{
+ SCMutexLock(&h->m);
+}
+
+void IPPairUnlock(IPPair *h)
+{
+ SCMutexUnlock(&h->m);
+}
+
+/* IPPairGetIPPairFromHash
+ *
+ * Hash retrieval function for ippairs. Looks up the hash bucket containing the
+ * ippair pointer. Then compares the packet with the found ippair to see if it is
+ * the ippair we need. If it isn't, walk the list until the right ippair is found.
+ *
+ * returns a *LOCKED* ippair or NULL
+ */
+IPPair *IPPairGetIPPairFromHash (Address *a, Address *b)
+{
+ IPPair *h = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = IPPairGetKey(a, b);
+ /* get our hash bucket and lock it */
+ IPPairHashRow *hb = &ippair_hash[key];
+ HRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a ippair */
+ if (hb->head == NULL) {
+ h = IPPairGetNew(a,b);
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+
+ /* ippair is locked */
+ hb->head = h;
+ hb->tail = h;
+
+ /* got one, now lock, initialize and return */
+ IPPairInit(h,a,b);
+
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ /* ok, we have a ippair in the bucket. Let's find out if it is our ippair */
+ h = hb->head;
+
+ /* see if this is the ippair we are looking for */
+ if (IPPairCompare(h, a, b) == 0) {
+ IPPair *ph = NULL; /* previous ippair */
+
+ while (h) {
+ ph = h;
+ h = h->hnext;
+
+ if (h == NULL) {
+ h = ph->hnext = IPPairGetNew(a,b);
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return NULL;
+ }
+ hb->tail = h;
+
+ /* ippair is locked */
+
+ h->hprev = ph;
+
+ /* initialize and return */
+ IPPairInit(h,a,b);
+
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ if (IPPairCompare(h, a, b) != 0) {
+ /* we found our ippair, lets put it on top of the
+ * hash list -- this rewards active ippairs */
+ if (h->hnext) {
+ h->hnext->hprev = h->hprev;
+ }
+ if (h->hprev) {
+ h->hprev->hnext = h->hnext;
+ }
+ if (h == hb->tail) {
+ hb->tail = h->hprev;
+ }
+
+ h->hnext = hb->head;
+ h->hprev = NULL;
+ hb->head->hprev = h;
+ hb->head = h;
+
+ /* found our ippair, lock & return */
+ SCMutexLock(&h->m);
+ (void) IPPairIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&h->m);
+ (void) IPPairIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+}
+
+/** \brief look up a ippair in the hash
+ *
+ * \param a address to look up
+ *
+ * \retval h *LOCKED* ippair or NULL
+ */
+IPPair *IPPairLookupIPPairFromHash (Address *a, Address *b)
+{
+ IPPair *h = NULL;
+
+ /* get the key to our bucket */
+ uint32_t key = IPPairGetKey(a, b);
+ /* get our hash bucket and lock it */
+ IPPairHashRow *hb = &ippair_hash[key];
+ HRLOCK_LOCK(hb);
+
+ /* see if the bucket already has a ippair */
+ if (hb->head == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ /* ok, we have a ippair in the bucket. Let's find out if it is our ippair */
+ h = hb->head;
+
+ /* see if this is the ippair we are looking for */
+ if (IPPairCompare(h, a, b) == 0) {
+ while (h) {
+ h = h->hnext;
+
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+
+ if (IPPairCompare(h, a, b) != 0) {
+ /* we found our ippair, lets put it on top of the
+ * hash list -- this rewards active ippairs */
+ if (h->hnext) {
+ h->hnext->hprev = h->hprev;
+ }
+ if (h->hprev) {
+ h->hprev->hnext = h->hnext;
+ }
+ if (h == hb->tail) {
+ hb->tail = h->hprev;
+ }
+
+ h->hnext = hb->head;
+ h->hprev = NULL;
+ hb->head->hprev = h;
+ hb->head = h;
+
+ /* found our ippair, lock & return */
+ SCMutexLock(&h->m);
+ (void) IPPairIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+ }
+ }
+ }
+
+ /* lock & return */
+ SCMutexLock(&h->m);
+ (void) IPPairIncrUsecnt(h);
+ HRLOCK_UNLOCK(hb);
+ return h;
+}
+
+/** \internal
+ * \brief Get a ippair from the hash directly.
+ *
+ * Called in conditions where the spare queue is empty and memcap is reached.
+ *
+ * Walks the hash until a ippair can be freed. "ippair_prune_idx" atomic int makes
+ * sure we don't start at the top each time since that would clear the top of
+ * the hash leading to longer and longer search times under high pressure (observed).
+ *
+ * \retval h ippair or NULL
+ */
+static IPPair *IPPairGetUsedIPPair(void)
+{
+ uint32_t idx = SC_ATOMIC_GET(ippair_prune_idx) % ippair_config.hash_size;
+ uint32_t cnt = ippair_config.hash_size;
+
+ while (cnt--) {
+ if (++idx >= ippair_config.hash_size)
+ idx = 0;
+
+ IPPairHashRow *hb = &ippair_hash[idx];
+
+ if (HRLOCK_TRYLOCK(hb) != 0)
+ continue;
+
+ IPPair *h = hb->tail;
+ if (h == NULL) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ if (SCMutexTrylock(&h->m) != 0) {
+ HRLOCK_UNLOCK(hb);
+ continue;
+ }
+
+ /** never prune a ippair that is used by a packets
+ * we are currently processing in one of the threads */
+ if (SC_ATOMIC_GET(h->use_cnt) > 0) {
+ HRLOCK_UNLOCK(hb);
+ SCMutexUnlock(&h->m);
+ continue;
+ }
+
+ /* remove from the hash */
+ if (h->hprev != NULL)
+ h->hprev->hnext = h->hnext;
+ if (h->hnext != NULL)
+ h->hnext->hprev = h->hprev;
+ if (hb->head == h)
+ hb->head = h->hnext;
+ if (hb->tail == h)
+ hb->tail = h->hprev;
+
+ h->hnext = NULL;
+ h->hprev = NULL;
+ HRLOCK_UNLOCK(hb);
+
+ IPPairClearMemory (h);
+
+ SCMutexUnlock(&h->m);
+
+ (void) SC_ATOMIC_ADD(ippair_prune_idx, (ippair_config.hash_size - cnt));
+ return h;
+ }
+
+ return NULL;
+}
+
+void IPPairRegisterUnittests(void)
+{
+ RegisterIPPairStorageTests();
+}
diff --git a/framework/src/suricata/src/ippair.h b/framework/src/suricata/src/ippair.h
new file mode 100644
index 00000000..79affc62
--- /dev/null
+++ b/framework/src/suricata/src/ippair.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __IPPAIR_H__
+#define __IPPAIR_H__
+
+#include "decode.h"
+#include "util-storage.h"
+
+/** Spinlocks or Mutex for the flow buckets. */
+//#define HRLOCK_SPIN
+#define HRLOCK_MUTEX
+
+#ifdef HRLOCK_SPIN
+ #ifdef HRLOCK_MUTEX
+ #error Cannot enable both HRLOCK_SPIN and HRLOCK_MUTEX
+ #endif
+#endif
+
+#ifdef HRLOCK_SPIN
+ #define HRLOCK_TYPE SCSpinlock
+ #define HRLOCK_INIT(fb) SCSpinInit(&(fb)->lock, 0)
+ #define HRLOCK_DESTROY(fb) SCSpinDestroy(&(fb)->lock)
+ #define HRLOCK_LOCK(fb) SCSpinLock(&(fb)->lock)
+ #define HRLOCK_TRYLOCK(fb) SCSpinTrylock(&(fb)->lock)
+ #define HRLOCK_UNLOCK(fb) SCSpinUnlock(&(fb)->lock)
+#elif defined HRLOCK_MUTEX
+ #define HRLOCK_TYPE SCMutex
+ #define HRLOCK_INIT(fb) SCMutexInit(&(fb)->lock, NULL)
+ #define HRLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->lock)
+ #define HRLOCK_LOCK(fb) SCMutexLock(&(fb)->lock)
+ #define HRLOCK_TRYLOCK(fb) SCMutexTrylock(&(fb)->lock)
+ #define HRLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->lock)
+#else
+ #error Enable HRLOCK_SPIN or HRLOCK_MUTEX
+#endif
+
+typedef struct IPPair_ {
+ /** ippair mutex */
+ SCMutex m;
+
+ /** ippair addresses -- ipv4 or ipv6 */
+ Address a[2];
+
+ /** use cnt, reference counter */
+ SC_ATOMIC_DECLARE(unsigned int, use_cnt);
+
+ /** storage api handle */
+ Storage *storage;
+
+ /** hash pointers, protected by hash row mutex/spin */
+ struct IPPair_ *hnext;
+ struct IPPair_ *hprev;
+
+ /** list pointers, protected by ippair-queue mutex/spin */
+ struct IPPair_ *lnext;
+ struct IPPair_ *lprev;
+} IPPair;
+
+typedef struct IPPairHashRow_ {
+ HRLOCK_TYPE lock;
+ IPPair *head;
+ IPPair *tail;
+} __attribute__((aligned(CLS))) IPPairHashRow;
+
+/** ippair hash table */
+IPPairHashRow *ippair_hash;
+
+#define IPPAIR_VERBOSE 0
+#define IPPAIR_QUIET 1
+
+typedef struct IPPairConfig_ {
+ uint64_t memcap;
+ uint32_t hash_rand;
+ uint32_t hash_size;
+ uint32_t prealloc;
+} IPPairConfig;
+
+/** \brief check if a memory alloc would fit in the memcap
+ *
+ * \param size memory allocation size to check
+ *
+ * \retval 1 it fits
+ * \retval 0 no fit
+ */
+#define IPPAIR_CHECK_MEMCAP(size) \
+ ((((uint64_t)SC_ATOMIC_GET(ippair_memuse) + (uint64_t)(size)) <= ippair_config.memcap))
+
+#define IPPairIncrUsecnt(h) \
+ (void)SC_ATOMIC_ADD((h)->use_cnt, 1)
+#define IPPairDecrUsecnt(h) \
+ (void)SC_ATOMIC_SUB((h)->use_cnt, 1)
+
+#define IPPairReference(dst_h_ptr, h) do { \
+ if ((h) != NULL) { \
+ IPPairIncrUsecnt((h)); \
+ *(dst_h_ptr) = h; \
+ } \
+ } while (0)
+
+#define IPPairDeReference(src_h_ptr) do { \
+ if (*(src_h_ptr) != NULL) { \
+ IPPairDecrUsecnt(*(src_h_ptr)); \
+ *(src_h_ptr) = NULL; \
+ } \
+ } while (0)
+
+IPPairConfig ippair_config;
+SC_ATOMIC_DECLARE(unsigned long long int,ippair_memuse);
+SC_ATOMIC_DECLARE(unsigned int,ippair_counter);
+SC_ATOMIC_DECLARE(unsigned int,ippair_prune_idx);
+
+void IPPairInitConfig(char quiet);
+void IPPairShutdown(void);
+void IPPairCleanup(void);
+
+IPPair *IPPairLookupIPPairFromHash (Address *, Address *);
+IPPair *IPPairGetIPPairFromHash (Address *, Address *);
+void IPPairRelease(IPPair *);
+void IPPairLock(IPPair *);
+void IPPairClearMemory(IPPair *);
+void IPPairMoveToSpare(IPPair *);
+uint32_t IPPairSpareQueueGetSize(void);
+void IPPairPrintStats (void);
+
+void IPPairRegisterUnittests(void);
+
+IPPair *IPPairAlloc(void);
+void IPPairFree(IPPair *);
+
+void IPPairLock(IPPair *);
+void IPPairUnlock(IPPair *);
+
+#endif /* __IPPAIR_H__ */
diff --git a/framework/src/suricata/src/log-dnslog.c b/framework/src/suricata/src/log-dnslog.c
new file mode 100644
index 00000000..e30c3d3e
--- /dev/null
+++ b/framework/src/suricata/src/log-dnslog.c
@@ -0,0 +1,362 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements dns logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-dnslog.h"
+#include "app-layer-dns-common.h"
+#include "app-layer-dns-udp.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "dns.log"
+
+#define MODULE_NAME "LogDnsLog"
+
+#define OUTPUT_BUFFER_SIZE 65535
+
+/* we can do query logging as well, but it's disabled for now as the
+ * TX id handling doesn't expect it */
+#define QUERY 0
+
+typedef struct LogDnsFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} LogDnsFileCtx;
+
+typedef struct LogDnsLogThread_ {
+ LogDnsFileCtx *dnslog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t dns_cnt;
+
+ MemBuffer *buffer;
+} LogDnsLogThread;
+
+static void LogQuery(LogDnsLogThread *aft, char *timebuf, char *srcip, char *dstip, Port sp, Port dp, DNSTransaction *tx, DNSQueryEntry *entry)
+{
+ LogDnsFileCtx *hlog = aft->dnslog_ctx;
+
+ SCLogDebug("got a DNS request and now logging !!");
+
+ /* reset */
+ MemBufferReset(aft->buffer);
+
+ /* time & tx */
+ MemBufferWriteString(aft->buffer,
+ "%s [**] Query TX %04x [**] ", timebuf, tx->tx_id);
+
+ /* query */
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)),
+ entry->len);
+
+ char record[16] = "";
+ DNSCreateTypeString(entry->type, record, sizeof(record));
+ MemBufferWriteString(aft->buffer,
+ " [**] %s [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n",
+ record, srcip, sp, dstip, dp);
+
+ SCMutexLock(&hlog->file_ctx->fp_mutex);
+ hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
+ SCMutexUnlock(&hlog->file_ctx->fp_mutex);
+}
+
+static void LogAnswer(LogDnsLogThread *aft, char *timebuf, char *srcip, char *dstip, Port sp, Port dp, DNSTransaction *tx, DNSAnswerEntry *entry)
+{
+ LogDnsFileCtx *hlog = aft->dnslog_ctx;
+
+ SCLogDebug("got a DNS response and now logging !!");
+
+ /* reset */
+ MemBufferReset(aft->buffer);
+ /* time & tx*/
+ MemBufferWriteString(aft->buffer,
+ "%s [**] Response TX %04x [**] ", timebuf, tx->tx_id);
+
+ if (entry == NULL) {
+ if (tx->rcode) {
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ MemBufferWriteString(aft->buffer, "%s", rcode);
+ } else if (tx->recursion_desired) {
+ MemBufferWriteString(aft->buffer, "Recursion Desired");
+ }
+ } else {
+ /* query */
+ if (entry->fqdn_len > 0) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)),
+ entry->fqdn_len);
+ } else {
+ MemBufferWriteString(aft->buffer, "<no data>");
+ }
+
+ char record[16] = "";
+ DNSCreateTypeString(entry->type, record, sizeof(record));
+ MemBufferWriteString(aft->buffer,
+ " [**] %s [**] TTL %u [**] ", record, entry->ttl);
+
+ uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry) + entry->fqdn_len);
+ if (entry->type == DNS_RECORD_TYPE_A) {
+ char a[16] = "";
+ PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
+ MemBufferWriteString(aft->buffer, "%s", a);
+ } else if (entry->type == DNS_RECORD_TYPE_AAAA) {
+ char a[46];
+ PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
+ MemBufferWriteString(aft->buffer, "%s", a);
+ } else if (entry->data_len == 0) {
+ MemBufferWriteString(aft->buffer, "<no data>");
+ } else {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, ptr, entry->data_len);
+ }
+ }
+
+ /* ip/tcp header info */
+ MemBufferWriteString(aft->buffer,
+ " [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n",
+ srcip, sp, dstip, dp);
+
+ SCMutexLock(&hlog->file_ctx->fp_mutex);
+ hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
+ SCMutexUnlock(&hlog->file_ctx->fp_mutex);
+}
+
+static int LogDnsLogger(ThreadVars *tv, void *data, const Packet *p, Flow *f,
+ void *state, void *tx, uint64_t tx_id)
+{
+ LogDnsLogThread *aft = (LogDnsLogThread *)data;
+ DNSTransaction *dns_tx = (DNSTransaction *)tx;
+ SCLogDebug("pcap_cnt %ju", p->pcap_cnt);
+ char timebuf[64];
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ int ipproto = 0;
+ if (PKT_IS_IPV4(p))
+ ipproto = AF_INET;
+ else if (PKT_IS_IPV6(p))
+ ipproto = AF_INET6;
+
+ char srcip[46], dstip[46];
+ Port sp, dp;
+ if ((PKT_IS_TOCLIENT(p))) {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ goto end;
+ }
+ sp = p->sp;
+ dp = p->dp;
+ } else {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ goto end;
+ }
+ sp = p->dp;
+ dp = p->sp;
+ }
+
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &dns_tx->query_list, next) {
+ LogQuery(aft, timebuf, dstip, srcip, dp, sp, dns_tx, query);
+ }
+
+ if (dns_tx->rcode)
+ LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, NULL);
+ if (dns_tx->recursion_desired)
+ LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, NULL);
+
+ DNSAnswerEntry *entry = NULL;
+ TAILQ_FOREACH(entry, &dns_tx->answer_list, next) {
+ LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, entry);
+ }
+
+ entry = NULL;
+ TAILQ_FOREACH(entry, &dns_tx->authority_list, next) {
+ LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, entry);
+ }
+
+ aft->dns_cnt++;
+end:
+ return 0;
+}
+
+static TmEcode LogDnsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogDnsLogThread *aft = SCMalloc(sizeof(LogDnsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogDnsLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for DNSLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->dnslog_ctx= ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode LogDnsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogDnsLogThread *aft = (LogDnsLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogDnsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void LogDnsLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogDnsLogThread *aft = (LogDnsLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("DNS logger logged %" PRIu32 " transactions", aft->dns_cnt);
+}
+
+static void LogDnsLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogDnsFileCtx *dnslog_ctx = (LogDnsFileCtx *)output_ctx->data;
+ LogFileFreeCtx(dnslog_ctx->file_ctx);
+ SCFree(dnslog_ctx);
+ SCFree(output_ctx);
+}
+
+/** \brief Create a new dns log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *LogDnsLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx* file_ctx = LogFileNewCtx();
+
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_DNS_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogDnsFileCtx *dnslog_ctx = SCMalloc(sizeof(LogDnsFileCtx));
+ if (unlikely(dnslog_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ memset(dnslog_ctx, 0x00, sizeof(LogDnsFileCtx));
+
+ dnslog_ctx->file_ctx = file_ctx;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(dnslog_ctx);
+ return NULL;
+ }
+
+ output_ctx->data = dnslog_ctx;
+ output_ctx->DeInit = LogDnsLogDeInitCtx;
+
+ SCLogDebug("DNS log output initialized");
+
+ AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS);
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS);
+
+ return output_ctx;
+}
+
+void TmModuleLogDnsLogRegister (void)
+{
+ tmm_modules[TMM_LOGDNSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGDNSLOG].ThreadInit = LogDnsLogThreadInit;
+ tmm_modules[TMM_LOGDNSLOG].ThreadExitPrintStats = LogDnsLogExitPrintStats;
+ tmm_modules[TMM_LOGDNSLOG].ThreadDeinit = LogDnsLogThreadDeinit;
+ tmm_modules[TMM_LOGDNSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_LOGDNSLOG].cap_flags = 0;
+ tmm_modules[TMM_LOGDNSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterTxModule(MODULE_NAME, "dns-log", LogDnsLogInitCtx,
+ ALPROTO_DNS, LogDnsLogger);
+
+ /* enable the logger for the app layer */
+ SCLogDebug("registered %s", MODULE_NAME);
+}
diff --git a/framework/src/suricata/src/log-dnslog.h b/framework/src/suricata/src/log-dnslog.h
new file mode 100644
index 00000000..acd00bc4
--- /dev/null
+++ b/framework/src/suricata/src/log-dnslog.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_DNSLOG_H__
+#define __LOG_DNSLOG_H__
+
+void TmModuleLogDnsLogRegister (void);
+
+#endif /* __LOG_DNSLOG_H__ */
diff --git a/framework/src/suricata/src/log-droplog.c b/framework/src/suricata/src/log-droplog.c
new file mode 100644
index 00000000..6eafd9d5
--- /dev/null
+++ b/framework/src/suricata/src/log-droplog.c
@@ -0,0 +1,507 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Drop log module to log the dropped packet information in a format
+ * compatible to Linux' Netfilter.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "decode-ipv4.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+
+#include "output.h"
+#include "log-droplog.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-classification-config.h"
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "drop.log"
+
+#define MODULE_NAME "LogDropLog"
+
+typedef struct LogDropLogThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx* file_ctx;
+ uint64_t drop_cnt;
+} LogDropLogThread;
+
+/**
+ * \brief Initialize the droplog thread
+ * \param t Pointer the current thread variable
+ * \param initdata pointer to the output context
+ * \param data Pointer to the pointer to droplog thread to be initialized
+ *
+ * \return TM_ECODE_OK on success
+ */
+static TmEcode LogDropLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ if(initdata == NULL) {
+ SCLogDebug("Error getting context for LogDropLog. \"initdata\" argument NULL");
+ return TM_ECODE_FAILED;
+ }
+
+ LogDropLogThread *dlt = SCMalloc(sizeof(LogDropLogThread));
+ if (unlikely(dlt == NULL))
+ return TM_ECODE_FAILED;
+ memset(dlt, 0, sizeof(LogDropLogThread));
+
+ /** Use the Ouptut Context (file pointer and mutex) */
+ dlt->file_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)dlt;
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Deinitialize the droplog thread
+ * \param t Pointer the current thread variable
+ * \param data Pointer to the droplog thread to be cleared
+ *
+ * \return TM_ECODE_OK on success
+ */
+static TmEcode LogDropLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogDropLogThread *dlt = (LogDropLogThread *)data;
+ if (dlt == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(dlt, 0, sizeof(LogDropLogThread));
+
+ SCFree(dlt);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Destroy the LogFileCtx and cleared "drop" output module
+ *
+ * \param output_ctx pointer the output context to be cleared
+ */
+static void LogDropLogDeInitCtx(OutputCtx *output_ctx)
+{
+ OutputDropLoggerDisable();
+
+ if (output_ctx != NULL) {
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ if (logfile_ctx != NULL) {
+ LogFileFreeCtx(logfile_ctx);
+ }
+ SCFree(output_ctx);
+ }
+}
+
+/**
+ * \brief Create a new LogFileCtx for "drop" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+static OutputCtx *LogDropLogInitCtx(ConfNode *conf)
+{
+ if (OutputDropLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'drop' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("LogDropLogInitCtx: Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = LogDropLogDeInitCtx;
+
+ return output_ctx;
+}
+
+/**
+ * \brief Log the dropped packets in netfilter format when engine is running
+ * in inline mode
+ *
+ * \param tv Pointer the current thread variables
+ * \param p Pointer the packet which is being logged
+ * \param data Pointer to the droplog struct
+ *
+ * \return return TM_EODE_OK on success
+ */
+static int LogDropLogNetFilter (ThreadVars *tv, const Packet *p, void *data)
+{
+ LogDropLogThread *dlt = (LogDropLogThread *)data;
+ uint16_t proto = 0;
+ char timebuf[64];
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ SCMutexLock(&dlt->file_ctx->fp_mutex);
+
+ if (dlt->file_ctx->rotation_flag) {
+ dlt->file_ctx->rotation_flag = 0;
+ if (SCConfLogReopen(dlt->file_ctx) != 0) {
+ /* Rotation failed, error already logged. */
+ SCMutexUnlock(&dlt->file_ctx->fp_mutex);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ char srcip[46] = "";
+ char dstip[46] = "";
+
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, 16);
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, 16);
+ fprintf(dlt->file_ctx->fp, "%s: IN= OUT= SRC=%s DST=%s LEN=%"PRIu16" "
+ "TOS=0x%02"PRIu8" TTL=%"PRIu8" ID=%"PRIu16"", timebuf,
+ srcip, dstip, IPV4_GET_IPLEN(p), IPV4_GET_IPTOS(p),
+ IPV4_GET_IPTTL(p), IPV4_GET_IPID(p));
+ proto = IPV4_GET_IPPROTO(p);
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ fprintf(dlt->file_ctx->fp, "%s: IN= OUT= SRC=%s DST=%s LEN=%"PRIu16""
+ " TC=%"PRIu32" HOPLIMIT=%"PRIu8" FLOWLBL=%"PRIu32"", timebuf,
+ srcip, dstip, IPV6_GET_PLEN(p), IPV6_GET_CLASS(p),
+ IPV6_GET_HLIM(p), IPV6_GET_FLOW(p));
+ proto = IPV6_GET_L4PROTO(p);
+ }
+
+ if (SCProtoNameValid(proto) == TRUE) {
+ fprintf(dlt->file_ctx->fp, " PROTO=%s",known_proto[proto]);
+ } else {
+ fprintf(dlt->file_ctx->fp, " PROTO=%03"PRIu16"",proto);
+ }
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ fprintf(dlt->file_ctx->fp, " SPT=%"PRIu16" DPT=%"PRIu16" "
+ "SEQ=%"PRIu32" ACK=%"PRIu32" WINDOW=%"PRIu32"",
+ GET_TCP_SRC_PORT(p), GET_TCP_DST_PORT(p), TCP_GET_SEQ(p),
+ TCP_GET_ACK(p), TCP_GET_WINDOW(p));
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_SYN(p) ? " SYN" : "");
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_ACK(p) ? " ACK" : "");
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_PUSH(p) ? " PSH" : "");
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_RST(p) ? " RST" : "");
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_URG(p) ? " URG" : "");
+ fprintf(dlt->file_ctx->fp, TCP_ISSET_FLAG_FIN(p) ? " FIN" : "");
+ fprintf(dlt->file_ctx->fp, " RES=0x%02"PRIu8" URGP=%"PRIu16"",
+ TCP_GET_RAW_X2(p->tcph), TCP_GET_URG_POINTER(p));
+ break;
+ case IPPROTO_UDP:
+ fprintf(dlt->file_ctx->fp, " SPT=%"PRIu16" DPT=%"PRIu16""
+ " LEN=%"PRIu16"", UDP_GET_SRC_PORT(p),
+ UDP_GET_DST_PORT(p), UDP_GET_LEN(p));
+ break;
+ case IPPROTO_ICMP:
+ if (PKT_IS_ICMPV4(p)) {
+ fprintf(dlt->file_ctx->fp, " TYPE=%"PRIu8" CODE=%"PRIu8""
+ " ID=%"PRIu16" SEQ=%"PRIu16"", ICMPV4_GET_TYPE(p),
+ ICMPV4_GET_CODE(p), ICMPV4_GET_ID(p), ICMPV4_GET_SEQ(p));
+ } else if(PKT_IS_ICMPV6(p)) {
+ fprintf(dlt->file_ctx->fp, " TYPE=%"PRIu8" CODE=%"PRIu8""
+ " ID=%"PRIu16" SEQ=%"PRIu16"", ICMPV6_GET_TYPE(p),
+ ICMPV6_GET_CODE(p), ICMPV6_GET_ID(p), ICMPV6_GET_SEQ(p));
+ }
+ break;
+ default:
+ fprintf(dlt->file_ctx->fp," Unknown protocol");
+ }
+
+ fprintf(dlt->file_ctx->fp,"\n");
+
+ fflush(dlt->file_ctx->fp);
+
+ dlt->drop_cnt++;
+ SCMutexUnlock(&dlt->file_ctx->fp_mutex);
+
+ return TM_ECODE_OK;
+
+}
+
+/**
+ * \brief Check if we need to drop-log this packet
+ *
+ * \param tv Pointer the current thread variables
+ * \param p Pointer the packet which is tested
+ *
+ * \retval bool TRUE or FALSE
+ */
+static int LogDropCondition(ThreadVars *tv, const Packet *p)
+{
+ if (!EngineModeIsIPS()) {
+ SCLogDebug("engine is not running in inline mode, so returning");
+ return FALSE;
+ }
+ if (PKT_IS_PSEUDOPKT(p)) {
+ SCLogDebug("drop log doesn't log pseudo packets");
+ return FALSE;
+ }
+
+ if (p->flow != NULL) {
+ int ret = FALSE;
+ FLOWLOCK_RDLOCK(p->flow);
+ if (p->flow->flags & FLOW_ACTION_DROP) {
+ if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
+ ret = TRUE;
+ else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
+ ret = TRUE;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ return ret;
+ } else if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * \brief Log the dropped packets when engine is running in inline mode
+ *
+ * \param tv Pointer the current thread variables
+ * \param data Pointer to the droplog struct
+ * \param p Pointer the packet which is being logged
+ *
+ * \retval 0 on succes
+ */
+static int LogDropLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+
+ int r = LogDropLogNetFilter(tv, p, thread_data);
+ if (r < 0)
+ return -1;
+
+ if (p->flow) {
+ FLOWLOCK_RDLOCK(p->flow);
+ if (p->flow->flags & FLOW_ACTION_DROP) {
+ if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
+ p->flow->flags |= FLOW_TOSERVER_DROP_LOGGED;
+ else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
+ p->flow->flags |= FLOW_TOCLIENT_DROP_LOGGED;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ return 0;
+}
+
+static void LogDropLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogDropLogThread *dlt = (LogDropLogThread *)data;
+ if (dlt == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) Dropped Packets %" PRIu64 "", tv->name, dlt->drop_cnt);
+}
+
+/***************************** Unittests ****************************/
+
+#ifdef UNITTESTS
+
+/** \brief test if the action is drop then packet should be logged */
+int LogDropLogTest01()
+{
+ int result = 0;
+ EngineModeSetIPS();
+
+ uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n";
+
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ LogDropLogThread dlt;
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ printf("Could not create new LogFileCtx\n");
+ return 0;
+ }
+
+ memset (&dlt, 0, sizeof(LogDropLogThread));
+ dlt.file_ctx = logfile_ctx;
+ dlt.file_ctx->fp = stdout;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any any "
+ "(msg:\"LogDropLog test\"; content:\"GET\"; Classtype:unknown; sid:1;)");
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1 && (PACKET_TEST_ACTION(p, ACTION_DROP)))
+ result = (strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
+
+ if (LogDropCondition(NULL, p) == TRUE)
+ LogDropLogger(NULL, &dlt, p);
+
+ if (dlt.drop_cnt == 0) {
+ printf("Packet should be logged but its not\n");
+ result = 0;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ EngineModeSetIDS();
+ return result;
+}
+
+/** \brief test if the action is alert then packet shouldn't be logged */
+int LogDropLogTest02()
+{
+ int result = 0;
+ EngineModeSetIPS();
+
+ uint8_t *buf = (uint8_t *) "GET";
+
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+ LogDropLogThread dlt;
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ printf("Could not create new LogFileCtx\n");
+ return 0;
+ }
+
+ memset (&dlt, 0, sizeof(LogDropLogThread));
+ dlt.file_ctx = logfile_ctx;
+ dlt.file_ctx->fp = stdout;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_UDP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert udp any any -> any any "
+ "(msg:\"LogDropLog test\"; content:\"GET\"; Classtype:unknown; sid:1;)");
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1 && p->alerts.alerts[0].action != ACTION_DROP)
+ result = (strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
+
+ if (LogDropCondition(NULL, p) == TRUE)
+ LogDropLogger(NULL, &dlt, p);
+
+ if (dlt.drop_cnt != 0) {
+ printf("Packet shouldn't be logged but it is\n");
+ result = 0;
+ }
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+
+ EngineModeSetIDS();
+ return result;
+}
+
+/**
+ * \brief This function registers unit tests for AlertFastLog API.
+ */
+static void LogDropLogRegisterTests(void)
+{
+ UtRegisterTest("LogDropLogTest01", LogDropLogTest01, 1);
+ UtRegisterTest("LogDropLogTest02", LogDropLogTest02, 1);
+}
+#endif
+
+/** \brief function to register the drop log module */
+void TmModuleLogDropLogRegister (void)
+{
+
+ tmm_modules[TMM_LOGDROPLOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGDROPLOG].ThreadInit = LogDropLogThreadInit;
+ tmm_modules[TMM_LOGDROPLOG].ThreadExitPrintStats = LogDropLogExitPrintStats;
+ tmm_modules[TMM_LOGDROPLOG].ThreadDeinit = LogDropLogThreadDeinit;
+#ifdef UNITTESTS
+ tmm_modules[TMM_LOGDROPLOG].RegisterTests = LogDropLogRegisterTests;
+#endif
+ tmm_modules[TMM_LOGDROPLOG].cap_flags = 0;
+ tmm_modules[TMM_LOGDROPLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "drop", LogDropLogInitCtx,
+ LogDropLogger, LogDropCondition);
+}
diff --git a/framework/src/suricata/src/log-droplog.h b/framework/src/suricata/src/log-droplog.h
new file mode 100644
index 00000000..2b266ad5
--- /dev/null
+++ b/framework/src/suricata/src/log-droplog.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ */
+
+
+#ifndef ALERT_DROPLOG_H
+#define ALERT_DROPLOG_H
+
+void TmModuleLogDropLogRegister(void);
+
+#endif /* ALERT_DROPLOG_H */
diff --git a/framework/src/suricata/src/log-file.c b/framework/src/suricata/src/log-file.c
new file mode 100644
index 00000000..0c41e38d
--- /dev/null
+++ b/framework/src/suricata/src/log-file.c
@@ -0,0 +1,465 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Log files we track.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threadvars.h"
+#include "tm-modules.h"
+
+#include "threads.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-filemagic.h"
+
+#include "stream.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+#include "util-privs.h"
+#include "util-debug.h"
+#include "util-atomic.h"
+#include "util-file.h"
+#include "util-time.h"
+
+#include "output.h"
+
+#include "log-file.h"
+#include "util-logopenfile.h"
+
+#include "app-layer-htp.h"
+#include "app-layer-smtp.h"
+#include "util-decode-mime.h"
+#include "util-memcmp.h"
+#include "stream-tcp-reassemble.h"
+
+#define MODULE_NAME "LogFileLog"
+
+#define DEFAULT_LOG_FILENAME "files-json.log"
+
+typedef struct LogFileLogThread_ {
+ LogFileCtx *file_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t file_cnt;
+} LogFileLogThread;
+
+static void LogFileMetaGetUri(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ if (tx_ud != NULL) {
+ if (tx_ud->request_uri_normalized != NULL) {
+ PrintRawJsonFp(fp,
+ bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+ return;
+ }
+ }
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFileMetaGetHost(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL && tx->request_hostname != NULL) {
+ PrintRawJsonFp(fp, (uint8_t *)bstr_ptr(tx->request_hostname),
+ bstr_len(tx->request_hostname));
+ return;
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFileMetaGetReferer(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "Referer");
+ if (h != NULL) {
+ PrintRawJsonFp(fp, (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value));
+ return;
+ }
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFileMetaGetUserAgent(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "User-Agent");
+ if (h != NULL) {
+ PrintRawJsonFp(fp, (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value));
+ return;
+ }
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFileMetaGetSmtp(FILE *fp, const Packet *p, const File *ff)
+{
+ SMTPState *state = (SMTPState *) p->flow->alstate;
+ if (state != NULL) {
+ SMTPTransaction *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_SMTP, state, ff->txid);
+ if (tx == NULL || tx->msg_tail == NULL)
+ return;
+
+ /* Message Id */
+ if (tx->msg_tail->msg_id != NULL) {
+
+ fprintf(fp, "\"message-id\": \"");
+ PrintRawJsonFp(fp, (uint8_t *) tx->msg_tail->msg_id,
+ (int) tx->msg_tail->msg_id_len);
+ fprintf(fp, "\", ");
+ }
+
+ /* Sender */
+ MimeDecField *field = MimeDecFindField(tx->msg_tail, "from");
+ if (field != NULL) {
+ fprintf(fp, "\"sender\": \"");
+ PrintRawJsonFp(fp, (uint8_t *) field->value,
+ (int) field->value_len);
+ fprintf(fp, "\", ");
+ }
+ }
+}
+
+/**
+ * \internal
+ * \brief Write meta data on a single line json record
+ */
+static void LogFileWriteJsonRecord(LogFileLogThread *aft, const Packet *p, const File *ff, int ipver)
+{
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+
+ /* As writes are done via the LogFileCtx, check for rotation here. */
+ if (aft->file_ctx->rotation_flag) {
+ aft->file_ctx->rotation_flag = 0;
+ if (SCConfLogReopen(aft->file_ctx) != 0) {
+ SCLogWarning(SC_ERR_FOPEN, "Failed to re-open log file. "
+ "Logging for this module will be disabled.");
+ }
+ }
+
+ /* Bail early if no file pointer to write to (in the unlikely
+ * event file rotation failed. */
+ if (aft->file_ctx->fp == NULL) {
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+ return;
+ }
+
+ FILE *fp = aft->file_ctx->fp;
+ char timebuf[64];
+ AppProto alproto = FlowGetAppProtocol(p->flow);
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ fprintf(fp, "{ ");
+
+ if (ff->file_id > 0)
+ fprintf(fp, "\"id\": %u, ", ff->file_id);
+
+ fprintf(fp, "\"timestamp\": \"");
+ PrintRawJsonFp(fp, (uint8_t *)timebuf, strlen(timebuf));
+ fprintf(fp, "\", ");
+ if (p->pcap_cnt > 0) {
+ fprintf(fp, "\"pcap_pkt_num\": %"PRIu64", ", p->pcap_cnt);
+ }
+
+ fprintf(fp, "\"ipver\": %d, ", ipver == AF_INET ? 4 : 6);
+
+ char srcip[46], dstip[46];
+ Port sp, dp;
+ switch (ipver) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ strlcpy(srcip, "<unknown>", sizeof(srcip));
+ strlcpy(dstip, "<unknown>", sizeof(dstip));
+ break;
+ }
+ sp = p->sp;
+ dp = p->dp;
+
+ fprintf(fp, "\"srcip\": \"%s\", ", srcip);
+ fprintf(fp, "\"dstip\": \"%s\", ", dstip);
+ fprintf(fp, "\"protocol\": %" PRIu32 ", ", p->proto);
+ if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
+ fprintf(fp, "\"sp\": %" PRIu16 ", ", sp);
+ fprintf(fp, "\"dp\": %" PRIu16 ", ", dp);
+ }
+
+ if (alproto == ALPROTO_HTTP) {
+ fprintf(fp, "\"http_uri\": \"");
+ LogFileMetaGetUri(fp, p, ff);
+ fprintf(fp, "\", ");
+
+ fprintf(fp, "\"http_host\": \"");
+ LogFileMetaGetHost(fp, p, ff);
+ fprintf(fp, "\", ");
+
+ fprintf(fp, "\"http_referer\": \"");
+ LogFileMetaGetReferer(fp, p, ff);
+ fprintf(fp, "\", ");
+
+ fprintf(fp, "\"http_user_agent\": \"");
+ LogFileMetaGetUserAgent(fp, p, ff);
+ fprintf(fp, "\", ");
+ } else if (p->flow->alproto == ALPROTO_SMTP) {
+ /* Only applicable to SMTP */
+ LogFileMetaGetSmtp(fp, p, ff);
+ }
+
+ fprintf(fp, "\"filename\": \"");
+ PrintRawJsonFp(fp, ff->name, ff->name_len);
+ fprintf(fp, "\", ");
+
+ fprintf(fp, "\"magic\": \"");
+ if (ff->magic) {
+ PrintRawJsonFp(fp, (uint8_t *)ff->magic, strlen(ff->magic));
+ } else {
+ fprintf(fp, "unknown");
+ }
+ fprintf(fp, "\", ");
+
+ switch (ff->state) {
+ case FILE_STATE_CLOSED:
+ fprintf(fp, "\"state\": \"CLOSED\", ");
+#ifdef HAVE_NSS
+ if (ff->flags & FILE_MD5) {
+ fprintf(fp, "\"md5\": \"");
+ size_t x;
+ for (x = 0; x < sizeof(ff->md5); x++) {
+ fprintf(fp, "%02x", ff->md5[x]);
+ }
+ fprintf(fp, "\", ");
+ }
+#endif
+ break;
+ case FILE_STATE_TRUNCATED:
+ fprintf(fp, "\"state\": \"TRUNCATED\", ");
+ break;
+ case FILE_STATE_ERROR:
+ fprintf(fp, "\"state\": \"ERROR\", ");
+ break;
+ default:
+ fprintf(fp, "\"state\": \"UNKNOWN\", ");
+ break;
+ }
+ fprintf(fp, "\"stored\": %s, ", ff->flags & FILE_STORED ? "true" : "false");
+ fprintf(fp, "\"size\": %"PRIu64" ", ff->size);
+ fprintf(fp, "}\n");
+ fflush(fp);
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+}
+
+static int LogFileLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff)
+{
+ SCEnter();
+ LogFileLogThread *aft = (LogFileLogThread *)thread_data;
+ int ipver = -1;
+
+ if (PKT_IS_IPV4(p)) {
+ ipver = AF_INET;
+ } else if (PKT_IS_IPV6(p)) {
+ ipver = AF_INET6;
+ } else {
+ return 0;
+ }
+
+ BUG_ON(ff->flags & FILE_LOGGED);
+
+ SCLogDebug("ff %p", ff);
+
+ LogFileWriteJsonRecord(aft, p, ff, ipver);
+
+ aft->file_cnt++;
+ return 0;
+}
+
+static TmEcode LogFileLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogFileLogThread *aft = SCMalloc(sizeof(LogFileLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogFileLogThread));
+
+ if (initdata == NULL)
+ {
+ SCLogDebug("Error getting context for LogFile. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->file_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode LogFileLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogFileLogThread *aft = (LogFileLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(aft, 0, sizeof(LogFileLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void LogFileLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogFileLogThread *aft = (LogFileLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) Files logged: %" PRIu32 "", tv->name, aft->file_cnt);
+}
+
+/**
+ * \internal
+ *
+ * \brief deinit the log ctx and write out the waldo
+ *
+ * \param output_ctx output context to deinit
+ */
+static void LogFileLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ free(output_ctx);
+}
+
+/** \brief Create a new http log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *LogFileLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = LogFileLogDeInitCtx;
+
+ const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic");
+ if (force_magic != NULL && ConfValIsTrue(force_magic)) {
+ FileForceMagicEnable();
+ SCLogInfo("forcing magic lookup for logged files");
+ }
+
+ const char *force_md5 = ConfNodeLookupChildValue(conf, "force-md5");
+ if (force_md5 != NULL && ConfValIsTrue(force_md5)) {
+#ifdef HAVE_NSS
+ FileForceMd5Enable();
+ SCLogInfo("forcing md5 calculation for logged files");
+#else
+ SCLogInfo("md5 calculation requires linking against libnss");
+#endif
+ }
+
+ FileForceTrackingEnable();
+ SCReturnPtr(output_ctx, "OutputCtx");
+}
+
+/** \brief Read the config set the file pointer, open the file
+ * \param file_ctx pointer to a created LogFileCtx using LogFileNewCtx()
+ * \param config_file for loading separate configs
+ * \return -1 if failure, 0 if succesful
+ * */
+int LogFileLogOpenFileCtx(LogFileCtx *file_ctx, const char *filename, const
+ char *mode)
+{
+ return 0;
+}
+
+void TmModuleLogFileLogRegister (void)
+{
+ tmm_modules[TMM_FILELOG].name = MODULE_NAME;
+ tmm_modules[TMM_FILELOG].ThreadInit = LogFileLogThreadInit;
+ tmm_modules[TMM_FILELOG].Func = NULL;
+ tmm_modules[TMM_FILELOG].ThreadExitPrintStats = LogFileLogExitPrintStats;
+ tmm_modules[TMM_FILELOG].ThreadDeinit = LogFileLogThreadDeinit;
+ tmm_modules[TMM_FILELOG].RegisterTests = NULL;
+ tmm_modules[TMM_FILELOG].cap_flags = 0;
+ tmm_modules[TMM_FILELOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterFileModule(MODULE_NAME, "file-log", LogFileLogInitCtx,
+ LogFileLogger);
+
+ SCLogDebug("registered");
+}
diff --git a/framework/src/suricata/src/log-file.h b/framework/src/suricata/src/log-file.h
new file mode 100644
index 00000000..08e0785b
--- /dev/null
+++ b/framework/src/suricata/src/log-file.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_FILELOG_H__
+#define __LOG_FILELOG_H__
+
+void TmModuleLogFileLogRegister (void);
+
+#endif /* __LOG_FILELOG_H__ */
diff --git a/framework/src/suricata/src/log-filestore.c b/framework/src/suricata/src/log-filestore.c
new file mode 100644
index 00000000..4244b9a4
--- /dev/null
+++ b/framework/src/suricata/src/log-filestore.c
@@ -0,0 +1,499 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threadvars.h"
+#include "tm-modules.h"
+
+#include "threads.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-filemagic.h"
+
+#include "stream.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+#include "util-privs.h"
+#include "util-debug.h"
+#include "util-atomic.h"
+#include "util-file.h"
+#include "util-time.h"
+
+#include "output.h"
+
+#include "log-file.h"
+#include "util-logopenfile.h"
+
+#include "app-layer-htp.h"
+#include "app-layer-smtp.h"
+#include "util-decode-mime.h"
+#include "util-memcmp.h"
+#include "stream-tcp-reassemble.h"
+
+#define MODULE_NAME "LogFilestoreLog"
+
+static char g_logfile_base_dir[PATH_MAX] = "/tmp";
+
+typedef struct LogFilestoreLogThread_ {
+ LogFileCtx *file_ctx;
+ /** LogFilestoreCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t file_cnt;
+} LogFilestoreLogThread;
+
+static void LogFilestoreMetaGetUri(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ if (tx_ud->request_uri_normalized != NULL) {
+ PrintRawUriFp(fp, bstr_ptr(tx_ud->request_uri_normalized),
+ bstr_len(tx_ud->request_uri_normalized));
+ }
+ return;
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFilestoreMetaGetHost(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL && tx->request_hostname != NULL) {
+ PrintRawUriFp(fp, (uint8_t *)bstr_ptr(tx->request_hostname),
+ bstr_len(tx->request_hostname));
+ return;
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFilestoreMetaGetReferer(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "Referer");
+ if (h != NULL) {
+ PrintRawUriFp(fp, (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value));
+ return;
+ }
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFilestoreMetaGetUserAgent(FILE *fp, const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "User-Agent");
+ if (h != NULL) {
+ PrintRawUriFp(fp, (uint8_t *)bstr_ptr(h->value),
+ bstr_len(h->value));
+ return;
+ }
+ }
+ }
+
+ fprintf(fp, "<unknown>");
+}
+
+static void LogFilestoreMetaGetSmtp(FILE *fp, const Packet *p, const File *ff)
+{
+ SMTPState *state = (SMTPState *) p->flow->alstate;
+ if (state != NULL) {
+ SMTPTransaction *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_SMTP, state, ff->txid);
+ if (tx == NULL || tx->msg_tail == NULL)
+ return;
+
+ /* Message Id */
+ if (tx->msg_tail->msg_id != NULL) {
+ fprintf(fp, "MESSAGE-ID: ");
+ PrintRawUriFp(fp, (uint8_t *) tx->msg_tail->msg_id, tx->msg_tail->msg_id_len);
+ fprintf(fp, "\n");
+ }
+
+ /* Sender */
+ MimeDecField *field = MimeDecFindField(tx->msg_tail, "from");
+ if (field != NULL) {
+ fprintf(fp, "SENDER: ");
+ PrintRawUriFp(fp, (uint8_t *) field->value, field->value_len);
+ fprintf(fp, "\n");
+ }
+ }
+}
+
+static void LogFilestoreLogCreateMetaFile(const Packet *p, const File *ff, char *filename, int ipver) {
+ char metafilename[PATH_MAX] = "";
+ snprintf(metafilename, sizeof(metafilename), "%s.meta", filename);
+ FILE *fp = fopen(metafilename, "w+");
+ if (fp != NULL) {
+ char timebuf[64];
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ fprintf(fp, "TIME: %s\n", timebuf);
+ if (p->pcap_cnt > 0) {
+ fprintf(fp, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt);
+ }
+
+ char srcip[46], dstip[46];
+ Port sp, dp;
+ switch (ipver) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ strlcpy(srcip, "<unknown>", sizeof(srcip));
+ strlcpy(dstip, "<unknown>", sizeof(dstip));
+ break;
+ }
+ sp = p->sp;
+ dp = p->dp;
+
+ fprintf(fp, "SRC IP: %s\n", srcip);
+ fprintf(fp, "DST IP: %s\n", dstip);
+ fprintf(fp, "PROTO: %" PRIu32 "\n", p->proto);
+ if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
+ fprintf(fp, "SRC PORT: %" PRIu16 "\n", sp);
+ fprintf(fp, "DST PORT: %" PRIu16 "\n", dp);
+ }
+
+ fprintf(fp, "APP PROTO: %s\n",
+ AppProtoToString(p->flow->alproto));
+
+ /* Only applicable to HTTP traffic */
+ if (p->flow->alproto == ALPROTO_HTTP) {
+ fprintf(fp, "HTTP URI: ");
+ LogFilestoreMetaGetUri(fp, p, ff);
+ fprintf(fp, "\n");
+ fprintf(fp, "HTTP HOST: ");
+ LogFilestoreMetaGetHost(fp, p, ff);
+ fprintf(fp, "\n");
+ fprintf(fp, "HTTP REFERER: ");
+ LogFilestoreMetaGetReferer(fp, p, ff);
+ fprintf(fp, "\n");
+ fprintf(fp, "HTTP USER AGENT: ");
+ LogFilestoreMetaGetUserAgent(fp, p, ff);
+ fprintf(fp, "\n");
+ } else if (p->flow->alproto == ALPROTO_SMTP) {
+ /* Only applicable to SMTP */
+ LogFilestoreMetaGetSmtp(fp, p, ff);
+ }
+
+ fprintf(fp, "FILENAME: ");
+ PrintRawUriFp(fp, ff->name, ff->name_len);
+ fprintf(fp, "\n");
+
+ fclose(fp);
+ }
+}
+
+static void LogFilestoreLogCloseMetaFile(const File *ff)
+{
+ char filename[PATH_MAX] = "";
+ snprintf(filename, sizeof(filename), "%s/file.%u",
+ g_logfile_base_dir, ff->file_id);
+ char metafilename[PATH_MAX] = "";
+ snprintf(metafilename, sizeof(metafilename), "%s.meta", filename);
+ FILE *fp = fopen(metafilename, "a");
+ if (fp != NULL) {
+ fprintf(fp, "MAGIC: %s\n",
+ ff->magic ? ff->magic : "<unknown>");
+
+ switch (ff->state) {
+ case FILE_STATE_CLOSED:
+ fprintf(fp, "STATE: CLOSED\n");
+#ifdef HAVE_NSS
+ if (ff->flags & FILE_MD5) {
+ fprintf(fp, "MD5: ");
+ size_t x;
+ for (x = 0; x < sizeof(ff->md5); x++) {
+ fprintf(fp, "%02x", ff->md5[x]);
+ }
+ fprintf(fp, "\n");
+ }
+#endif
+ break;
+ case FILE_STATE_TRUNCATED:
+ fprintf(fp, "STATE: TRUNCATED\n");
+ break;
+ case FILE_STATE_ERROR:
+ fprintf(fp, "STATE: ERROR\n");
+ break;
+ default:
+ fprintf(fp, "STATE: UNKNOWN\n");
+ break;
+ }
+ fprintf(fp, "SIZE: %"PRIu64"\n", ff->size);
+
+ fclose(fp);
+ } else {
+ SCLogInfo("opening %s failed: %s", metafilename, strerror(errno));
+ }
+}
+
+static int LogFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff, const FileData *ffd, uint8_t flags)
+{
+ SCEnter();
+ LogFilestoreLogThread *aft = (LogFilestoreLogThread *)thread_data;
+ char filename[PATH_MAX] = "";
+ int file_fd = -1;
+ int ipver = -1;
+
+ /* no flow, no htp state */
+ if (p->flow == NULL) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ if (PKT_IS_IPV4(p)) {
+ ipver = AF_INET;
+ } else if (PKT_IS_IPV6(p)) {
+ ipver = AF_INET6;
+ } else {
+ return 0;
+ }
+
+ SCLogDebug("ff %p, ffd %p", ff, ffd);
+
+ snprintf(filename, sizeof(filename), "%s/file.%u",
+ g_logfile_base_dir, ff->file_id);
+
+ if (flags & OUTPUT_FILEDATA_FLAG_OPEN) {
+ aft->file_cnt++;
+
+ /* create a .meta file that contains time, src/dst/sp/dp/proto */
+ LogFilestoreLogCreateMetaFile(p, ff, filename, ipver);
+
+ file_fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
+ if (file_fd == -1) {
+ SCLogDebug("failed to create file");
+ return -1;
+ }
+ /* we can get called with a NULL ffd when we need to close */
+ } else if (ffd != NULL) {
+ file_fd = open(filename, O_APPEND | O_NOFOLLOW | O_WRONLY);
+ if (file_fd == -1) {
+ SCLogDebug("failed to open file %s: %s", filename, strerror(errno));
+ return -1;
+ }
+ }
+
+ if (file_fd != -1) {
+ ssize_t r = write(file_fd, (const void *)ffd->data, (size_t)ffd->len);
+ if (r == -1) {
+ SCLogDebug("write failed: %s", strerror(errno));
+ }
+ close(file_fd);
+ }
+
+ if (flags & OUTPUT_FILEDATA_FLAG_CLOSE) {
+ LogFilestoreLogCloseMetaFile(ff);
+ }
+
+ return 0;
+}
+
+static TmEcode LogFilestoreLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogFilestoreLogThread *aft = SCMalloc(sizeof(LogFilestoreLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogFilestoreLogThread));
+
+ if (initdata == NULL)
+ {
+ SCLogDebug("Error getting context for LogFilestore. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->file_ctx = ((OutputCtx *)initdata)->data;
+
+ struct stat stat_buf;
+ if (stat(g_logfile_base_dir, &stat_buf) != 0) {
+ int ret;
+ ret = mkdir(g_logfile_base_dir, S_IRWXU|S_IXGRP|S_IRGRP);
+ if (ret != 0) {
+ int err = errno;
+ if (err != EEXIST) {
+ SCLogError(SC_ERR_LOGDIR_CONFIG,
+ "Cannot create file drop directory %s: %s",
+ g_logfile_base_dir, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ SCLogInfo("Created file drop directory %s",
+ g_logfile_base_dir);
+ }
+
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode LogFilestoreLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogFilestoreLogThread *aft = (LogFilestoreLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(aft, 0, sizeof(LogFilestoreLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void LogFilestoreLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogFilestoreLogThread *aft = (LogFilestoreLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) Files extracted %" PRIu32 "", tv->name, aft->file_cnt);
+}
+
+/**
+ * \internal
+ *
+ * \brief deinit the log ctx and write out the waldo
+ *
+ * \param output_ctx output context to deinit
+ */
+static void LogFilestoreLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(output_ctx);
+
+}
+
+/** \brief Create a new http log LogFilestoreCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *LogFilestoreLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("Could not create new LogFilestoreCtx");
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+
+ output_ctx->data = NULL;
+ output_ctx->DeInit = LogFilestoreLogDeInitCtx;
+
+ char *s_default_log_dir = NULL;
+ s_default_log_dir = ConfigGetLogDirectory();
+
+ const char *s_base_dir = NULL;
+ s_base_dir = ConfNodeLookupChildValue(conf, "log-dir");
+ if (s_base_dir == NULL || strlen(s_base_dir) == 0) {
+ strlcpy(g_logfile_base_dir,
+ s_default_log_dir, sizeof(g_logfile_base_dir));
+ } else {
+ if (PathIsAbsolute(s_base_dir)) {
+ strlcpy(g_logfile_base_dir,
+ s_base_dir, sizeof(g_logfile_base_dir));
+ } else {
+ snprintf(g_logfile_base_dir, sizeof(g_logfile_base_dir),
+ "%s/%s", s_default_log_dir, s_base_dir);
+ }
+ }
+
+ const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic");
+ if (force_magic != NULL && ConfValIsTrue(force_magic)) {
+ FileForceMagicEnable();
+ SCLogInfo("forcing magic lookup for stored files");
+ }
+
+ const char *force_md5 = ConfNodeLookupChildValue(conf, "force-md5");
+ if (force_md5 != NULL && ConfValIsTrue(force_md5)) {
+#ifdef HAVE_NSS
+ FileForceMd5Enable();
+ SCLogInfo("forcing md5 calculation for stored files");
+#else
+ SCLogInfo("md5 calculation requires linking against libnss");
+#endif
+ }
+ SCLogInfo("storing files in %s", g_logfile_base_dir);
+
+ SCReturnPtr(output_ctx, "OutputCtx");
+}
+
+void TmModuleLogFilestoreRegister (void)
+{
+ tmm_modules[TMM_FILESTORE].name = MODULE_NAME;
+ tmm_modules[TMM_FILESTORE].ThreadInit = LogFilestoreLogThreadInit;
+ tmm_modules[TMM_FILESTORE].Func = NULL;
+ tmm_modules[TMM_FILESTORE].ThreadExitPrintStats = LogFilestoreLogExitPrintStats;
+ tmm_modules[TMM_FILESTORE].ThreadDeinit = LogFilestoreLogThreadDeinit;
+ tmm_modules[TMM_FILESTORE].RegisterTests = NULL;
+ tmm_modules[TMM_FILESTORE].cap_flags = 0;
+ tmm_modules[TMM_FILESTORE].flags = TM_FLAG_LOGAPI_TM;
+ tmm_modules[TMM_FILESTORE].priority = 10;
+
+ OutputRegisterFiledataModule(MODULE_NAME, "file", LogFilestoreLogInitCtx,
+ LogFilestoreLogger);
+ OutputRegisterFiledataModule(MODULE_NAME, "file-store", LogFilestoreLogInitCtx,
+ LogFilestoreLogger);
+
+ SCLogDebug("registered");
+}
diff --git a/framework/src/suricata/src/log-filestore.h b/framework/src/suricata/src/log-filestore.h
new file mode 100644
index 00000000..bfbfd5fc
--- /dev/null
+++ b/framework/src/suricata/src/log-filestore.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_FILESTORE_H__
+#define __LOG_FILESTORE_H__
+
+void TmModuleLogFilestoreRegister (void);
+
+#endif /* __LOG_FILELOG_H__ */
diff --git a/framework/src/suricata/src/log-httplog.c b/framework/src/suricata/src/log-httplog.c
new file mode 100644
index 00000000..b9723d8c
--- /dev/null
+++ b/framework/src/suricata/src/log-httplog.c
@@ -0,0 +1,738 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ *
+ * Implements http logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-httplog.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "http.log"
+
+#define MODULE_NAME "LogHttpLog"
+
+#define OUTPUT_BUFFER_SIZE 65535
+
+TmEcode LogHttpLogThreadInit(ThreadVars *, void *, void **);
+TmEcode LogHttpLogThreadDeinit(ThreadVars *, void *);
+void LogHttpLogExitPrintStats(ThreadVars *, void *);
+static void LogHttpLogDeInitCtx(OutputCtx *);
+
+int LogHttpLogger(ThreadVars *tv, void *thread_data, const Packet *, Flow *f, void *state, void *tx, uint64_t tx_id);
+
+void TmModuleLogHttpLogRegister (void)
+{
+ tmm_modules[TMM_LOGHTTPLOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGHTTPLOG].ThreadInit = LogHttpLogThreadInit;
+ tmm_modules[TMM_LOGHTTPLOG].ThreadExitPrintStats = LogHttpLogExitPrintStats;
+ tmm_modules[TMM_LOGHTTPLOG].ThreadDeinit = LogHttpLogThreadDeinit;
+ tmm_modules[TMM_LOGHTTPLOG].RegisterTests = NULL;
+ tmm_modules[TMM_LOGHTTPLOG].cap_flags = 0;
+ tmm_modules[TMM_LOGHTTPLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterTxModule(MODULE_NAME, "http-log", LogHttpLogInitCtx,
+ ALPROTO_HTTP, LogHttpLogger);
+}
+
+#define LOG_HTTP_MAXN_NODES 64
+#define LOG_HTTP_NODE_STRLEN 256
+#define LOG_HTTP_NODE_MAXOUTPUTLEN 8192
+
+#define TIMESTAMP_DEFAULT_FORMAT "%b %d, %Y; %H:%M:%S"
+#define LOG_HTTP_CF_NONE "-"
+#define LOG_HTTP_CF_LITERAL '%'
+#define LOG_HTTP_CF_REQUEST_HOST 'h'
+#define LOG_HTTP_CF_REQUEST_PROTOCOL 'H'
+#define LOG_HTTP_CF_REQUEST_METHOD 'm'
+#define LOG_HTTP_CF_REQUEST_URI 'u'
+#define LOG_HTTP_CF_REQUEST_TIME 't'
+#define LOG_HTTP_CF_REQUEST_HEADER 'i'
+#define LOG_HTTP_CF_REQUEST_COOKIE 'C'
+#define LOG_HTTP_CF_REQUEST_LEN 'b'
+#define LOG_HTTP_CF_RESPONSE_STATUS 's'
+#define LOG_HTTP_CF_RESPONSE_HEADER 'o'
+#define LOG_HTTP_CF_RESPONSE_LEN 'B'
+#define LOG_HTTP_CF_TIMESTAMP 't'
+#define LOG_HTTP_CF_TIMESTAMP_U 'z'
+#define LOG_HTTP_CF_CLIENT_IP 'a'
+#define LOG_HTTP_CF_SERVER_IP 'A'
+#define LOG_HTTP_CF_CLIENT_PORT 'p'
+#define LOG_HTTP_CF_SERVER_PORT 'P'
+
+typedef struct LogHttpCustomFormatNode_ {
+ uint32_t type; /** Node format type. ie: LOG_HTTP_CF_LITERAL, LOG_HTTP_CF_REQUEST_HEADER */
+ uint32_t maxlen; /** Maximun length of the data */
+ char data[LOG_HTTP_NODE_STRLEN]; /** optional data. ie: http header name */
+} LogHttpCustomFormatNode;
+
+typedef struct LogHttpFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+ uint32_t cf_n; /** Total number of custom string format nodes */
+ LogHttpCustomFormatNode *cf_nodes[LOG_HTTP_MAXN_NODES]; /** Custom format string nodes */
+} LogHttpFileCtx;
+
+#define LOG_HTTP_DEFAULT 0
+#define LOG_HTTP_EXTENDED 1
+#define LOG_HTTP_CUSTOM 2
+
+typedef struct LogHttpLogThread_ {
+ LogHttpFileCtx *httplog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t uri_cnt;
+
+ MemBuffer *buffer;
+} LogHttpLogThread;
+
+/* Retrieves the selected cookie value */
+static uint32_t GetCookieValue(uint8_t *rawcookies, uint32_t rawcookies_len, char *cookiename,
+ uint8_t **cookievalue)
+{
+ uint8_t *p = rawcookies;
+ uint8_t *cn = p; /* ptr to cookie name start */
+ uint8_t *cv = NULL; /* ptr to cookie value start */
+ while (p < rawcookies + rawcookies_len) {
+ if (cv == NULL && *p == '=') {
+ cv = p + 1;
+ } else if (cv != NULL && (*p == ';' || p == rawcookies + rawcookies_len - 1) ) {
+ /* Found end of cookie */
+ p++;
+ if (strlen(cookiename) == (unsigned int) (cv-cn-1) &&
+ strncmp(cookiename, (char *) cn, cv-cn-1) == 0) {
+ *cookievalue = cv;
+ return (uint32_t) (p-cv);
+ }
+ cv = NULL;
+ cn = p + 1;
+ }
+ p++;
+ }
+ return 0;
+}
+
+/* Custom format logging */
+static void LogHttpLogCustom(LogHttpLogThread *aft, htp_tx_t *tx, const struct timeval *ts,
+ char *srcip, Port sp, char *dstip, Port dp)
+{
+ LogHttpFileCtx *httplog_ctx = aft->httplog_ctx;
+ uint32_t i;
+ uint32_t datalen;
+ char buf[128];
+
+ uint8_t *cvalue = NULL;
+ uint32_t cvalue_len = 0;
+
+ htp_header_t *h_request_hdr;
+ htp_header_t *h_response_hdr;
+
+ time_t time = ts->tv_sec;
+ struct tm local_tm;
+ struct tm *timestamp = SCLocalTime(time, &local_tm);
+
+ for (i = 0; i < httplog_ctx->cf_n; i++) {
+ h_request_hdr = NULL;
+ h_response_hdr = NULL;
+ switch (httplog_ctx->cf_nodes[i]->type){
+ case LOG_HTTP_CF_LITERAL:
+ /* LITERAL */
+ MemBufferWriteString(aft->buffer, "%s", httplog_ctx->cf_nodes[i]->data);
+ break;
+ case LOG_HTTP_CF_TIMESTAMP:
+ /* TIMESTAMP */
+ if (httplog_ctx->cf_nodes[i]->data[0] == '\0') {
+ strftime(buf, 62, TIMESTAMP_DEFAULT_FORMAT, timestamp);
+ } else {
+ strftime(buf, 62, httplog_ctx->cf_nodes[i]->data, timestamp);
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)buf,strlen(buf));
+ break;
+ case LOG_HTTP_CF_TIMESTAMP_U:
+ /* TIMESTAMP USECONDS */
+ snprintf(buf, 62, "%06u", (unsigned int) ts->tv_usec);
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)buf,strlen(buf));
+ break;
+ case LOG_HTTP_CF_CLIENT_IP:
+ /* CLIENT IP ADDRESS */
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)srcip,strlen(srcip));
+ break;
+ case LOG_HTTP_CF_SERVER_IP:
+ /* SERVER IP ADDRESS */
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)dstip,strlen(dstip));
+ break;
+ case LOG_HTTP_CF_CLIENT_PORT:
+ /* CLIENT PORT */
+ MemBufferWriteString(aft->buffer, "%" PRIu16 "", sp);
+ break;
+ case LOG_HTTP_CF_SERVER_PORT:
+ /* SERVER PORT */
+ MemBufferWriteString(aft->buffer, "%" PRIu16 "", dp);
+ break;
+ case LOG_HTTP_CF_REQUEST_METHOD:
+ /* METHOD */
+ if (tx->request_method != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_method),
+ bstr_len(tx->request_method));
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_URI:
+ /* URI */
+ if (tx->request_uri != NULL) {
+ datalen = httplog_ctx->cf_nodes[i]->maxlen;
+ if (datalen == 0 || datalen > bstr_len(tx->request_uri)) {
+ datalen = bstr_len(tx->request_uri);
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_uri),
+ datalen);
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_HOST:
+ /* HOSTNAME */
+ if (tx->request_hostname != NULL)
+ {
+ datalen = httplog_ctx->cf_nodes[i]->maxlen;
+ if (datalen == 0 || datalen > bstr_len(tx->request_hostname)) {
+ datalen = bstr_len(tx->request_hostname);
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_hostname),
+ datalen);
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_PROTOCOL:
+ /* PROTOCOL */
+ if (tx->request_protocol != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_protocol),
+ bstr_len(tx->request_protocol));
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_HEADER:
+ /* REQUEST HEADER */
+ if (tx->request_headers != NULL) {
+ h_request_hdr = htp_table_get_c(tx->request_headers, httplog_ctx->cf_nodes[i]->data);
+ }
+ if (h_request_hdr != NULL) {
+ datalen = httplog_ctx->cf_nodes[i]->maxlen;
+ if (datalen == 0 || datalen > bstr_len(h_request_hdr->value)) {
+ datalen = bstr_len(h_request_hdr->value);
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(h_request_hdr->value),
+ datalen);
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_COOKIE:
+ /* REQUEST COOKIE */
+ if (tx->request_headers != NULL) {
+ h_request_hdr = htp_table_get_c(tx->request_headers, "Cookie");
+ if (h_request_hdr != NULL) {
+ cvalue_len = GetCookieValue((uint8_t *) bstr_ptr(h_request_hdr->value),
+ bstr_len(h_request_hdr->value), (char *) httplog_ctx->cf_nodes[i]->data,
+ &cvalue);
+ }
+ }
+ if (cvalue_len > 0 && cvalue != NULL) {
+ datalen = httplog_ctx->cf_nodes[i]->maxlen;
+ if (datalen == 0 || datalen > cvalue_len) {
+ datalen = cvalue_len;
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, cvalue, datalen);
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_REQUEST_LEN:
+ /* REQUEST LEN */
+ MemBufferWriteString(aft->buffer, "%"PRIuMAX"", (uintmax_t)tx->request_message_len);
+ break;
+ case LOG_HTTP_CF_RESPONSE_STATUS:
+ /* RESPONSE STATUS */
+ if (tx->response_status != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(tx->response_status),
+ bstr_len(tx->response_status));
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_RESPONSE_HEADER:
+ /* RESPONSE HEADER */
+ if (tx->response_headers != NULL) {
+ h_response_hdr = htp_table_get_c(tx->response_headers,
+ httplog_ctx->cf_nodes[i]->data);
+ }
+ if (h_response_hdr != NULL) {
+ datalen = httplog_ctx->cf_nodes[i]->maxlen;
+ if (datalen == 0 || datalen > bstr_len(h_response_hdr->value)) {
+ datalen = bstr_len(h_response_hdr->value);
+ }
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)bstr_ptr(h_response_hdr->value),
+ datalen);
+ } else {
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ }
+ break;
+ case LOG_HTTP_CF_RESPONSE_LEN:
+ /* RESPONSE LEN */
+ MemBufferWriteString(aft->buffer, "%"PRIuMAX"", (uintmax_t)tx->response_message_len);
+ break;
+ default:
+ /* NO MATCH */
+ MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE);
+ SCLogDebug("No matching parameter %%%c for custom http log.", httplog_ctx->cf_nodes[i]->type);
+ break;
+ }
+ }
+ MemBufferWriteString(aft->buffer, "\n");
+}
+
+static void LogHttpLogExtended(LogHttpLogThread *aft, htp_tx_t *tx)
+{
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* referer */
+ htp_header_t *h_referer = NULL;
+ if (tx->request_headers != NULL) {
+ h_referer = htp_table_get_c(tx->request_headers, "referer");
+ }
+ if (h_referer != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(h_referer->value),
+ bstr_len(h_referer->value));
+ } else {
+ MemBufferWriteString(aft->buffer, "<no referer>");
+ }
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* method */
+ if (tx->request_method != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(tx->request_method),
+ bstr_len(tx->request_method));
+ }
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* protocol */
+ if (tx->request_protocol != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(tx->request_protocol),
+ bstr_len(tx->request_protocol));
+ } else {
+ MemBufferWriteString(aft->buffer, "<no protocol>");
+ }
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* response status */
+ if (tx->response_status != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(tx->response_status),
+ bstr_len(tx->response_status));
+ /* Redirect? */
+ if ((tx->response_status_number > 300) && ((tx->response_status_number) < 303)) {
+ htp_header_t *h_location = htp_table_get_c(tx->response_headers, "location");
+ if (h_location != NULL) {
+ MemBufferWriteString(aft->buffer, " => ");
+
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(h_location->value),
+ bstr_len(h_location->value));
+ }
+ }
+ } else {
+ MemBufferWriteString(aft->buffer, "<no status>");
+ }
+
+ /* length */
+ MemBufferWriteString(aft->buffer, " [**] %"PRIuMAX" bytes", (uintmax_t)tx->response_message_len);
+}
+
+static TmEcode LogHttpLogIPWrapper(ThreadVars *tv, void *data, const Packet *p, Flow *f, HtpState *htp_state, htp_tx_t *tx, uint64_t tx_id, int ipproto)
+{
+ SCEnter();
+
+ LogHttpLogThread *aft = (LogHttpLogThread *)data;
+ LogHttpFileCtx *hlog = aft->httplog_ctx;
+ char timebuf[64];
+
+ /* check if we have HTTP state or not */
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ char srcip[46], dstip[46];
+ Port sp, dp;
+ if ((PKT_IS_TOSERVER(p))) {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ goto end;
+ }
+ sp = p->sp;
+ dp = p->dp;
+ } else {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip));
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip));
+ break;
+ default:
+ goto end;
+ }
+ sp = p->dp;
+ dp = p->sp;
+ }
+
+ SCLogDebug("got a HTTP request and now logging !!");
+
+ /* reset */
+ MemBufferReset(aft->buffer);
+
+ if (hlog->flags & LOG_HTTP_CUSTOM) {
+ LogHttpLogCustom(aft, tx, &p->ts, srcip, sp, dstip, dp);
+ } else {
+ /* time */
+ MemBufferWriteString(aft->buffer, "%s ", timebuf);
+
+ /* hostname */
+ if (tx->request_hostname != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(tx->request_hostname),
+ bstr_len(tx->request_hostname));
+ } else {
+ MemBufferWriteString(aft->buffer, "<hostname unknown>");
+ }
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* uri */
+ if (tx->request_uri != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(tx->request_uri),
+ bstr_len(tx->request_uri));
+ }
+ MemBufferWriteString(aft->buffer, " [**] ");
+
+ /* user agent */
+ htp_header_t *h_user_agent = NULL;
+ if (tx->request_headers != NULL) {
+ h_user_agent = htp_table_get_c(tx->request_headers, "user-agent");
+ }
+ if (h_user_agent != NULL) {
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
+ (uint8_t *)bstr_ptr(h_user_agent->value),
+ bstr_len(h_user_agent->value));
+ } else {
+ MemBufferWriteString(aft->buffer, "<useragent unknown>");
+ }
+ if (hlog->flags & LOG_HTTP_EXTENDED) {
+ LogHttpLogExtended(aft, tx);
+ }
+
+ /* ip/tcp header info */
+ MemBufferWriteString(aft->buffer,
+ " [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n",
+ srcip, sp, dstip, dp);
+ }
+
+ aft->uri_cnt ++;
+
+ SCMutexLock(&hlog->file_ctx->fp_mutex);
+ hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
+ SCMutexUnlock(&hlog->file_ctx->fp_mutex);
+
+end:
+ SCReturnInt(0);
+
+}
+
+int LogHttpLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
+{
+ SCEnter();
+
+ if (!(PKT_IS_TCP(p))) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ int r = 0;
+ if (PKT_IS_IPV4(p)) {
+ r = LogHttpLogIPWrapper(tv, thread_data, p, f, (HtpState *)state, (htp_tx_t *)tx, tx_id, AF_INET);
+ } else if (PKT_IS_IPV6(p)) {
+ r = LogHttpLogIPWrapper(tv, thread_data, p, f, (HtpState *)state, (htp_tx_t *)tx, tx_id, AF_INET6);
+ }
+
+ SCReturnInt(r);
+}
+
+TmEcode LogHttpLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogHttpLogThread *aft = SCMalloc(sizeof(LogHttpLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogHttpLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->httplog_ctx= ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode LogHttpLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogHttpLogThread *aft = (LogHttpLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogHttpLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void LogHttpLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogHttpLogThread *aft = (LogHttpLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("HTTP logger logged %" PRIu32 " requests", aft->uri_cnt);
+}
+
+/** \brief Create a new http log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+OutputCtx *LogHttpLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx* file_ctx = LogFileNewCtx();
+ const char *p, *np;
+ uint32_t n;
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogHttpFileCtx *httplog_ctx = SCMalloc(sizeof(LogHttpFileCtx));
+ if (unlikely(httplog_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ memset(httplog_ctx, 0x00, sizeof(LogHttpFileCtx));
+
+ httplog_ctx->file_ctx = file_ctx;
+ httplog_ctx->cf_n=0;
+
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+ const char *custom = ConfNodeLookupChildValue(conf, "custom");
+ const char *customformat = ConfNodeLookupChildValue(conf, "customformat");
+
+ /* If custom logging format is selected, lets parse it */
+ if (custom != NULL && customformat != NULL && ConfValIsTrue(custom)) {
+ p=customformat;
+ httplog_ctx->flags |= LOG_HTTP_CUSTOM;
+ for (httplog_ctx->cf_n = 0; httplog_ctx->cf_n < LOG_HTTP_MAXN_NODES-1 && p && *p != '\0';
+ httplog_ctx->cf_n++){
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n] = SCMalloc(sizeof(LogHttpCustomFormatNode));
+ if (httplog_ctx->cf_nodes[httplog_ctx->cf_n] == NULL) {
+ for (n = 0; n < httplog_ctx->cf_n; n++) {
+ SCFree(httplog_ctx->cf_nodes[n]);
+ }
+ LogFileFreeCtx(file_ctx);
+ SCFree(httplog_ctx);
+ return NULL;
+ }
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->maxlen = 0;
+
+ if (*p != '%'){
+ /* Literal found in format string */
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->type = LOG_HTTP_CF_LITERAL;
+ np = strchr(p, '%');
+ if (np == NULL){
+ n = LOG_HTTP_NODE_STRLEN-2;
+ np = NULL; /* End */
+ }else{
+ n = np-p;
+ }
+ strlcpy(httplog_ctx->cf_nodes[httplog_ctx->cf_n]->data,p,n+1);
+ p = np;
+ } else {
+ /* Non Literal found in format string */
+ p++;
+ if (*p == '[') { /* Check if maxlength has been specified (ie: [25]) */
+ p++;
+ np = strchr(p, ']');
+ if (np != NULL) {
+ if (np-p > 0 && np-p < 10){
+ long maxlen = strtol(p,NULL,10);
+ if (maxlen > 0 && maxlen < LOG_HTTP_NODE_MAXOUTPUTLEN) {
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->maxlen = (uint32_t) maxlen;
+ }
+ } else {
+ goto parsererror;
+ }
+ p = np + 1;
+ } else {
+ goto parsererror;
+ }
+ }
+ if (*p == '{') { /* Simple format char */
+ np = strchr(p, '}');
+ if (np != NULL && np-p > 1 && np-p < LOG_HTTP_NODE_STRLEN-2) {
+ p++;
+ n = np-p;
+ strlcpy(httplog_ctx->cf_nodes[httplog_ctx->cf_n]->data, p, n+1);
+ p = np;
+ } else {
+ goto parsererror;
+ }
+ p++;
+ } else {
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->data[0] = '\0';
+ }
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->type = *p;
+ if (*p == '%'){
+ httplog_ctx->cf_nodes[httplog_ctx->cf_n]->type = LOG_HTTP_CF_LITERAL;
+ strlcpy(httplog_ctx->cf_nodes[httplog_ctx->cf_n]->data, "%", 2);
+ }
+ p++;
+ }
+ }
+ } else {
+ if (extended == NULL) {
+ httplog_ctx->flags |= LOG_HTTP_DEFAULT;
+ } else {
+ if (ConfValIsTrue(extended)) {
+ httplog_ctx->flags |= LOG_HTTP_EXTENDED;
+ }
+ }
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ goto parsererror;
+ }
+
+ output_ctx->data = httplog_ctx;
+ output_ctx->DeInit = LogHttpLogDeInitCtx;
+
+ SCLogDebug("HTTP log output initialized");
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP);
+
+ return output_ctx;
+
+parsererror:
+ for (n = 0;n < httplog_ctx->cf_n;n++) {
+ SCFree(httplog_ctx->cf_nodes[n]);
+ }
+ LogFileFreeCtx(file_ctx);
+ SCFree(httplog_ctx);
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Syntax error in custom http log format string.");
+ return NULL;
+
+}
+
+static void LogHttpLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogHttpFileCtx *httplog_ctx = (LogHttpFileCtx *)output_ctx->data;
+ uint32_t i;
+ for (i = 0; i < httplog_ctx->cf_n; i++) {
+ SCFree(httplog_ctx->cf_nodes[i]);
+ }
+ LogFileFreeCtx(httplog_ctx->file_ctx);
+ SCFree(httplog_ctx);
+ SCFree(output_ctx);
+}
diff --git a/framework/src/suricata/src/log-httplog.h b/framework/src/suricata/src/log-httplog.h
new file mode 100644
index 00000000..c02d0d9d
--- /dev/null
+++ b/framework/src/suricata/src/log-httplog.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_HTTPLOG_H__
+#define __LOG_HTTPLOG_H__
+
+void TmModuleLogHttpLogRegister (void);
+void TmModuleLogHttpLogIPv4Register (void);
+void TmModuleLogHttpLogIPv6Register (void);
+OutputCtx *LogHttpLogInitCtx(ConfNode *);
+
+#endif /* __LOG_HTTPLOG_H__ */
+
diff --git a/framework/src/suricata/src/log-pcap.c b/framework/src/suricata/src/log-pcap.c
new file mode 100644
index 00000000..21d41e8b
--- /dev/null
+++ b/framework/src/suricata/src/log-pcap.c
@@ -0,0 +1,1197 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/**
+ * \file
+ *
+ * \author William Metcalf <William.Metcalf@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pcap packet logging module.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "log-pcap.h"
+#include "decode-ipv4.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-byte.h"
+#include "util-misc.h"
+#include "util-cpu.h"
+#include "util-atomic.h"
+
+#include "source-pcap.h"
+
+#include "output.h"
+
+#include "queue.h"
+
+#define DEFAULT_LOG_FILENAME "pcaplog"
+#define MODULE_NAME "PcapLog"
+#define MIN_LIMIT 1 * 1024 * 1024
+#define DEFAULT_LIMIT 100 * 1024 * 1024
+#define DEFAULT_FILE_LIMIT 0
+
+#define LOGMODE_NORMAL 0
+#define LOGMODE_SGUIL 1
+#define LOGMODE_MULTI 2
+
+#define RING_BUFFER_MODE_DISABLED 0
+#define RING_BUFFER_MODE_ENABLED 1
+
+#define TS_FORMAT_SEC 0
+#define TS_FORMAT_USEC 1
+
+#define USE_STREAM_DEPTH_DISABLED 0
+#define USE_STREAM_DEPTH_ENABLED 1
+
+#define HONOR_PASS_RULES_DISABLED 0
+#define HONOR_PASS_RULES_ENABLED 1
+
+SC_ATOMIC_DECLARE(uint32_t, thread_cnt);
+
+typedef struct PcapFileName_ {
+ char *filename;
+ char *dirname;
+ TAILQ_ENTRY(PcapFileName_) next; /**< Pointer to next Pcap File for tailq. */
+} PcapFileName;
+
+typedef struct PcapLogProfileData_ {
+ uint64_t total;
+ uint64_t cnt;
+} PcapLogProfileData;
+
+#define MAX_TOKS 9
+
+/**
+ * PcapLog thread vars
+ *
+ * Used for storing file options.
+ */
+typedef struct PcapLogData_ {
+ int use_stream_depth; /**< use stream depth i.e. ignore packets that reach limit */
+ int honor_pass_rules; /**< don't log if pass rules have matched */
+ int is_private; /**< TRUE if ctx is thread local */
+ SCMutex plog_lock;
+ uint64_t pkt_cnt; /**< total number of packets */
+ struct pcap_pkthdr *h; /**< pcap header struct */
+ char *filename; /**< current filename */
+ int mode; /**< normal or sguil */
+ int prev_day; /**< last day, for finding out when */
+ uint64_t size_current; /**< file current size */
+ uint64_t size_limit; /**< file size limit */
+ pcap_t *pcap_dead_handle; /**< pcap_dumper_t needs a handle */
+ pcap_dumper_t *pcap_dumper; /**< actually writes the packets */
+ uint64_t profile_data_size; /**< track in bytes how many bytes we wrote */
+ uint32_t file_cnt; /**< count of pcap files we currently have */
+ uint32_t max_files; /**< maximum files to use in ring buffer mode */
+
+ PcapLogProfileData profile_lock;
+ PcapLogProfileData profile_write;
+ PcapLogProfileData profile_unlock;
+ PcapLogProfileData profile_handles; // open handles
+ PcapLogProfileData profile_close;
+ PcapLogProfileData profile_open;
+ PcapLogProfileData profile_rotate;
+
+ TAILQ_HEAD(, PcapFileName_) pcap_file_list;
+
+ uint32_t thread_number; /**< thread number, first thread is 1, second 2, etc */
+ int use_ringbuffer; /**< ring buffer mode enabled or disabled */
+ int timestamp_format; /**< timestamp format sec or usec */
+ char *prefix; /**< filename prefix */
+ char dir[PATH_MAX]; /**< pcap log directory */
+ int reported;
+ int threads; /**< number of threads (only set in the global) */
+ char *filename_parts[MAX_TOKS];
+ int filename_part_cnt;
+} PcapLogData;
+
+typedef struct PcapLogThreadData_ {
+ PcapLogData *pcap_log;
+} PcapLogThreadData;
+
+/* global pcap data for when we're using multi mode. At exit we'll
+ * merge counters into this one and then report counters. */
+static PcapLogData *g_pcap_data = NULL;
+
+static int PcapLogOpenFileCtx(PcapLogData *);
+static TmEcode PcapLog(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+static TmEcode PcapLogDataInit(ThreadVars *, void *, void **);
+static TmEcode PcapLogDataDeinit(ThreadVars *, void *);
+static void PcapLogFileDeInitCtx(OutputCtx *);
+static OutputCtx *PcapLogInitCtx(ConfNode *);
+static void PcapLogProfilingDump(PcapLogData *);
+
+void TmModulePcapLogRegister(void)
+{
+ tmm_modules[TMM_PCAPLOG].name = MODULE_NAME;
+ tmm_modules[TMM_PCAPLOG].ThreadInit = PcapLogDataInit;
+ tmm_modules[TMM_PCAPLOG].Func = PcapLog;
+ tmm_modules[TMM_PCAPLOG].ThreadDeinit = PcapLogDataDeinit;
+ tmm_modules[TMM_PCAPLOG].RegisterTests = NULL;
+
+ OutputRegisterModule(MODULE_NAME, "pcap-log", PcapLogInitCtx);
+
+ SC_ATOMIC_INIT(thread_cnt);
+ return;
+}
+
+#define PCAPLOG_PROFILE_START \
+ uint64_t pcaplog_profile_ticks = UtilCpuGetTicks()
+
+#define PCAPLOG_PROFILE_END(prof) \
+ (prof).total += (UtilCpuGetTicks() - pcaplog_profile_ticks); \
+ (prof).cnt++
+
+/**
+ * \brief Function to close pcaplog file
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param pl PcapLog thread variable.
+ */
+static int PcapLogCloseFile(ThreadVars *t, PcapLogData *pl)
+{
+ if (pl != NULL) {
+ PCAPLOG_PROFILE_START;
+
+ if (pl->pcap_dumper != NULL)
+ pcap_dump_close(pl->pcap_dumper);
+ pl->size_current = 0;
+ pl->pcap_dumper = NULL;
+
+ if (pl->pcap_dead_handle != NULL)
+ pcap_close(pl->pcap_dead_handle);
+ pl->pcap_dead_handle = NULL;
+
+ PCAPLOG_PROFILE_END(pl->profile_close);
+ }
+
+ return 0;
+}
+
+static void PcapFileNameFree(PcapFileName *pf)
+{
+ if (pf != NULL) {
+ if (pf->filename != NULL) {
+ SCFree(pf->filename);
+ }
+ if (pf->dirname != NULL) {
+ SCFree(pf->dirname);
+ }
+ SCFree(pf);
+ }
+
+ return;
+}
+
+/**
+ * \brief Function to rotate pcaplog file
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param pl PcapLog thread variable.
+ *
+ * \retval 0 on succces
+ * \retval -1 on failure
+ */
+static int PcapLogRotateFile(ThreadVars *t, PcapLogData *pl)
+{
+ PcapFileName *pf;
+ PcapFileName *pfnext;
+
+ PCAPLOG_PROFILE_START;
+
+ if (PcapLogCloseFile(t,pl) < 0) {
+ SCLogDebug("PcapLogCloseFile failed");
+ return -1;
+ }
+
+ if (pl->use_ringbuffer == RING_BUFFER_MODE_ENABLED && pl->file_cnt >= pl->max_files) {
+ pf = TAILQ_FIRST(&pl->pcap_file_list);
+ SCLogDebug("Removing pcap file %s", pf->filename);
+
+ if (remove(pf->filename) != 0) {
+ // VJ remove can fail because file is already gone
+ //LogWarning(SC_ERR_PCAP_FILE_DELETE_FAILED,
+ // "failed to remove log file %s: %s",
+ // pf->filename, strerror( errno ));
+ }
+
+ /* Remove directory if Sguil mode and no files left in sguil dir */
+ if (pl->mode == LOGMODE_SGUIL) {
+ pfnext = TAILQ_NEXT(pf,next);
+
+ if (strcmp(pf->dirname, pfnext->dirname) == 0) {
+ SCLogDebug("Current entry dir %s and next entry %s "
+ "are equal: not removing dir",
+ pf->dirname, pfnext->dirname);
+ } else {
+ SCLogDebug("current entry %s and %s are "
+ "not equal: removing dir",
+ pf->dirname, pfnext->dirname);
+
+ if (remove(pf->dirname) != 0) {
+ SCLogWarning(SC_ERR_PCAP_FILE_DELETE_FAILED,
+ "failed to remove sguil log %s: %s",
+ pf->dirname, strerror( errno ));
+ }
+ }
+ }
+
+ TAILQ_REMOVE(&pl->pcap_file_list, pf, next);
+ PcapFileNameFree(pf);
+ pl->file_cnt--;
+ }
+
+ if (PcapLogOpenFileCtx(pl) < 0) {
+ SCLogError(SC_ERR_FOPEN, "opening new pcap log file failed");
+ return -1;
+ }
+ pl->file_cnt++;
+ SCLogDebug("file_cnt %u", pl->file_cnt);
+
+ PCAPLOG_PROFILE_END(pl->profile_rotate);
+ return 0;
+}
+
+static int PcapLogOpenHandles(PcapLogData *pl, Packet *p)
+{
+ PCAPLOG_PROFILE_START;
+
+ SCLogDebug("Setting pcap-log link type to %u", p->datalink);
+
+ if (pl->pcap_dead_handle == NULL) {
+ if ((pl->pcap_dead_handle = pcap_open_dead(p->datalink,
+ -1)) == NULL) {
+ SCLogDebug("Error opening dead pcap handle");
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ if (pl->pcap_dumper == NULL) {
+ if ((pl->pcap_dumper = pcap_dump_open(pl->pcap_dead_handle,
+ pl->filename)) == NULL) {
+ SCLogInfo("Error opening dump file %s", pcap_geterr(pl->pcap_dead_handle));
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ PCAPLOG_PROFILE_END(pl->profile_handles);
+ return TM_ECODE_OK;
+}
+
+/** \internal
+ * \brief lock wrapper for main PcapLog() function
+ * NOTE: only meant for use in main PcapLog() function.
+ */
+static void PcapLogLock(PcapLogData *pl)
+{
+ if (!(pl->is_private)) {
+ PCAPLOG_PROFILE_START;
+ SCMutexLock(&pl->plog_lock);
+ PCAPLOG_PROFILE_END(pl->profile_lock);
+ }
+}
+
+/** \internal
+ * \brief unlock wrapper for main PcapLog() function
+ * NOTE: only meant for use in main PcapLog() function.
+ */
+static void PcapLogUnlock(PcapLogData *pl)
+{
+ if (!(pl->is_private)) {
+ PCAPLOG_PROFILE_START;
+ SCMutexUnlock(&pl->plog_lock);
+ PCAPLOG_PROFILE_END(pl->profile_unlock);
+ }
+}
+
+/**
+ * \brief Pcap logging main function
+ *
+ * \param t threadvar
+ * \param p packet
+ * \param data thread module specific data
+ * \param pq pre-packet-queue
+ * \param postpq post-packet-queue
+ *
+ * \retval TM_ECODE_OK on succes
+ * \retval TM_ECODE_FAILED on serious error
+ */
+static TmEcode PcapLog (ThreadVars *t, Packet *p, void *thread_data, PacketQueue *pq,
+ PacketQueue *postpq)
+{
+ size_t len;
+ int rotate = 0;
+ int ret = 0;
+
+ PcapLogThreadData *td = (PcapLogThreadData *)thread_data;
+ PcapLogData *pl = td->pcap_log;
+
+ if ((p->flags & PKT_PSEUDO_STREAM_END) ||
+ ((p->flags & PKT_STREAM_NOPCAPLOG) &&
+ (pl->use_stream_depth == USE_STREAM_DEPTH_ENABLED)) ||
+ (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) ||
+ (pl->honor_pass_rules && (p->flags & PKT_NOPACKET_INSPECTION)))
+ {
+ return TM_ECODE_OK;
+ }
+
+ PcapLogLock(pl);
+
+ pl->pkt_cnt++;
+ pl->h->ts.tv_sec = p->ts.tv_sec;
+ pl->h->ts.tv_usec = p->ts.tv_usec;
+ pl->h->caplen = GET_PKT_LEN(p);
+ pl->h->len = GET_PKT_LEN(p);
+ len = sizeof(*pl->h) + GET_PKT_LEN(p);
+
+ if (pl->filename == NULL) {
+ ret = PcapLogOpenFileCtx(pl);
+ if (ret < 0) {
+ PcapLogUnlock(pl);
+ return TM_ECODE_FAILED;
+ }
+ SCLogDebug("Opening PCAP log file %s", pl->filename);
+ }
+
+ if (pl->mode == LOGMODE_SGUIL) {
+ struct tm local_tm;
+ struct tm *tms = SCLocalTime(p->ts.tv_sec, &local_tm);
+ if (tms->tm_mday != pl->prev_day) {
+ rotate = 1;
+ pl->prev_day = tms->tm_mday;
+ }
+ }
+
+ if ((pl->size_current + len) > pl->size_limit || rotate) {
+ if (PcapLogRotateFile(t,pl) < 0) {
+ PcapLogUnlock(pl);
+ SCLogDebug("rotation of pcap failed");
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ /* XXX pcap handles, nfq, pfring, can only have one link type ipfw? we do
+ * this here as we don't know the link type until we get our first packet */
+ if (pl->pcap_dead_handle == NULL || pl->pcap_dumper == NULL) {
+ if (PcapLogOpenHandles(pl, p) != TM_ECODE_OK) {
+ PcapLogUnlock(pl);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ PCAPLOG_PROFILE_START;
+ pcap_dump((u_char *)pl->pcap_dumper, pl->h, GET_PKT_DATA(p));
+ pl->size_current += len;
+ PCAPLOG_PROFILE_END(pl->profile_write);
+ pl->profile_data_size += len;
+
+ SCLogDebug("pl->size_current %"PRIu64", pl->size_limit %"PRIu64,
+ pl->size_current, pl->size_limit);
+
+ PcapLogUnlock(pl);
+ return TM_ECODE_OK;
+}
+
+static PcapLogData *PcapLogDataCopy(const PcapLogData *pl)
+{
+ BUG_ON(pl->mode != LOGMODE_MULTI);
+ PcapLogData *copy = SCCalloc(1, sizeof(*copy));
+ if (unlikely(copy == NULL)) {
+ return NULL;
+ }
+
+ copy->h = SCCalloc(1, sizeof(*copy->h));
+ if (unlikely(copy->h == NULL)) {
+ SCFree(copy);
+ return NULL;
+ }
+
+ copy->prefix = SCStrdup(pl->prefix);
+ if (unlikely(copy->prefix == NULL)) {
+ SCFree(copy->h);
+ SCFree(copy);
+ return NULL;
+ }
+
+ /* settings TODO move to global cfg struct */
+ copy->is_private = TRUE;
+ copy->mode = pl->mode;
+ copy->max_files = pl->max_files;
+ copy->use_ringbuffer = pl->use_ringbuffer;
+ copy->timestamp_format = pl->timestamp_format;
+ copy->use_stream_depth = pl->use_stream_depth;
+ copy->size_limit = pl->size_limit;
+
+ TAILQ_INIT(&copy->pcap_file_list);
+ SCMutexInit(&copy->plog_lock, NULL);
+
+ strlcpy(copy->dir, pl->dir, sizeof(copy->dir));
+
+ int i;
+ for (i = 0; i < pl->filename_part_cnt && i < MAX_TOKS; i++)
+ copy->filename_parts[i] = pl->filename_parts[i];
+ copy->filename_part_cnt = pl->filename_part_cnt;
+
+ /* set thread number, first thread is 1 */
+ copy->thread_number = SC_ATOMIC_ADD(thread_cnt, 1);
+
+ SCLogDebug("copied, returning %p", copy);
+ return copy;
+}
+
+static TmEcode PcapLogDataInit(ThreadVars *t, void *initdata, void **data)
+{
+ if (initdata == NULL) {
+ SCLogDebug("Error getting context for PcapLog. \"initdata\" argument NULL");
+ return TM_ECODE_FAILED;
+ }
+
+ PcapLogData *pl = ((OutputCtx *)initdata)->data;
+
+ PcapLogThreadData *td = SCCalloc(1, sizeof(*td));
+ if (unlikely(td == NULL))
+ return TM_ECODE_FAILED;
+
+ if (pl->mode == LOGMODE_MULTI)
+ td->pcap_log = PcapLogDataCopy(pl);
+ else
+ td->pcap_log = pl;
+ BUG_ON(td->pcap_log == NULL);
+
+ PcapLogLock(td->pcap_log);
+
+ /** Use the Ouptut Context (file pointer and mutex) */
+ td->pcap_log->pkt_cnt = 0;
+ td->pcap_log->pcap_dead_handle = NULL;
+ td->pcap_log->pcap_dumper = NULL;
+ td->pcap_log->file_cnt = 1;
+
+ struct timeval ts;
+ memset(&ts, 0x00, sizeof(struct timeval));
+ TimeGet(&ts);
+ struct tm local_tm;
+ struct tm *tms = SCLocalTime(ts.tv_sec, &local_tm);
+ td->pcap_log->prev_day = tms->tm_mday;
+
+ PcapLogUnlock(td->pcap_log);
+
+ /* count threads in the global structure */
+ SCMutexLock(&pl->plog_lock);
+ pl->threads++;
+ SCMutexUnlock(&pl->plog_lock);
+
+ *data = (void *)td;
+
+ return TM_ECODE_OK;
+}
+
+static void StatsMerge(PcapLogData *dst, PcapLogData *src)
+{
+ dst->profile_open.total += src->profile_open.total;
+ dst->profile_open.cnt += src->profile_open.cnt;
+
+ dst->profile_close.total += src->profile_close.total;
+ dst->profile_close.cnt += src->profile_close.cnt;
+
+ dst->profile_write.total += src->profile_write.total;
+ dst->profile_write.cnt += src->profile_write.cnt;
+
+ dst->profile_rotate.total += src->profile_rotate.total;
+ dst->profile_rotate.cnt += src->profile_rotate.cnt;
+
+ dst->profile_handles.total += src->profile_handles.total;
+ dst->profile_handles.cnt += src->profile_handles.cnt;
+
+ dst->profile_lock.total += src->profile_lock.total;
+ dst->profile_lock.cnt += src->profile_lock.cnt;
+
+ dst->profile_unlock.total += src->profile_unlock.total;
+ dst->profile_unlock.cnt += src->profile_unlock.cnt;
+
+ dst->profile_data_size += src->profile_data_size;
+}
+
+/**
+ * \brief Thread deinit function.
+ *
+ * \param t Thread Variable containing input/output queue, cpu affinity etc.
+ * \param data PcapLog thread data.
+ * \retval TM_ECODE_OK on succces
+ * \retval TM_ECODE_FAILED on failure
+ */
+static TmEcode PcapLogDataDeinit(ThreadVars *t, void *thread_data)
+{
+ PcapLogThreadData *td = (PcapLogThreadData *)thread_data;
+ PcapLogData *pl = td->pcap_log;
+
+ if (pl->pcap_dumper != NULL) {
+ if (PcapLogCloseFile(t,pl) < 0) {
+ SCLogDebug("PcapLogCloseFile failed");
+ }
+ }
+
+ if (pl->mode == LOGMODE_MULTI) {
+ SCMutexLock(&g_pcap_data->plog_lock);
+ StatsMerge(g_pcap_data, pl);
+ g_pcap_data->reported++;
+ if (g_pcap_data->threads == g_pcap_data->reported)
+ PcapLogProfilingDump(g_pcap_data);
+ SCMutexUnlock(&g_pcap_data->plog_lock);
+ } else {
+ if (pl->reported == 0) {
+ PcapLogProfilingDump(pl);
+ pl->reported = 1;
+ }
+ }
+ return TM_ECODE_OK;
+}
+
+static int ParseFilename(PcapLogData *pl, const char *filename)
+{
+ char *toks[MAX_TOKS] = { NULL };
+ int tok = 0;
+ char str[512] = "";
+ int s = 0;
+ int i, x;
+ char *p = NULL;
+
+ if (filename) {
+ for (i = 0; i < (int)strlen(filename); i++) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 2 %%-sign options");
+ goto error;
+ }
+
+ str[s++] = filename[i];
+
+ if (filename[i] == '%') {
+ str[s-1] = '\0';
+ SCLogDebug("filename with %%-sign: %s", str);
+
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+
+ s = 0;
+
+ if (i+1 < (int)strlen(filename)) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 2 %%-sign options");
+ goto error;
+ }
+
+ if (filename[i+1] != 'n' && filename[i+1] != 't' && filename[i+1] != 'i') {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Valid %%-sign options: %%n, %%i and %%t");
+ goto error;
+ }
+ str[0] = '%';
+ str[1] = filename[i+1];
+ str[2] = '\0';
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+ i++;
+ }
+ }
+ }
+ if (s) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 3 %%-sign options");
+ goto error;
+
+ }
+ str[s++] = '\0';
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+ }
+
+ /* finally, store tokens in the pl */
+ for (i = 0; i < tok; i++) {
+ if (toks[i] == NULL)
+ goto error;
+
+ SCLogDebug("toks[%d] %s", i, toks[i]);
+ pl->filename_parts[i] = toks[i];
+ }
+ pl->filename_part_cnt = tok;
+ }
+ return 0;
+error:
+ for (x = 0; x < MAX_TOKS; x++) {
+ if (toks[x] != NULL)
+ SCFree(toks[x]);
+ }
+ return -1;
+}
+
+/** \brief Fill in pcap logging struct from the provided ConfNode.
+ * \param conf The configuration node for this output.
+ * \retval output_ctx
+ * */
+static OutputCtx *PcapLogInitCtx(ConfNode *conf)
+{
+ PcapLogData *pl = SCMalloc(sizeof(PcapLogData));
+ if (unlikely(pl == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate Memory for PcapLogData");
+ exit(EXIT_FAILURE);
+ }
+ memset(pl, 0, sizeof(PcapLogData));
+
+ pl->h = SCMalloc(sizeof(*pl->h));
+ if (pl->h == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate Memory for pcap header struct");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set the defaults */
+ pl->mode = LOGMODE_NORMAL;
+ pl->max_files = DEFAULT_FILE_LIMIT;
+ pl->use_ringbuffer = RING_BUFFER_MODE_DISABLED;
+ pl->timestamp_format = TS_FORMAT_SEC;
+ pl->use_stream_depth = USE_STREAM_DEPTH_DISABLED;
+ pl->honor_pass_rules = HONOR_PASS_RULES_DISABLED;
+
+ TAILQ_INIT(&pl->pcap_file_list);
+
+ SCMutexInit(&pl->plog_lock, NULL);
+
+ /* conf params */
+
+ const char *filename = NULL;
+
+ if (conf != NULL) { /* To faciliate unit tests. */
+ filename = ConfNodeLookupChildValue(conf, "filename");
+ }
+
+ if (filename == NULL)
+ filename = DEFAULT_LOG_FILENAME;
+
+ if ((pl->prefix = SCStrdup(filename)) == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (filename) {
+ if (ParseFilename(pl, filename) != 0)
+ exit(EXIT_FAILURE);
+ }
+
+ pl->size_limit = DEFAULT_LIMIT;
+ if (conf != NULL) {
+ const char *s_limit = NULL;
+ s_limit = ConfNodeLookupChildValue(conf, "limit");
+ if (s_limit != NULL) {
+ if (ParseSizeStringU64(s_limit, &pl->size_limit) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize unified2 output, invalid limit: %s",
+ s_limit);
+ exit(EXIT_FAILURE);
+ }
+ if (pl->size_limit < 4096) {
+ SCLogInfo("pcap-log \"limit\" value of %"PRIu64" assumed to be pre-1.2 "
+ "style: setting limit to %"PRIu64"mb", pl->size_limit, pl->size_limit);
+ uint64_t size = pl->size_limit * 1024 * 1024;
+ pl->size_limit = size;
+ } else if (pl->size_limit < MIN_LIMIT) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Fail to initialize pcap-log output, limit less than "
+ "allowed minimum.");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (conf != NULL) {
+ const char *s_mode = NULL;
+ s_mode = ConfNodeLookupChildValue(conf, "mode");
+ if (s_mode != NULL) {
+ if (strcasecmp(s_mode, "sguil") == 0) {
+ pl->mode = LOGMODE_SGUIL;
+ } else if (strcasecmp(s_mode, "multi") == 0) {
+ pl->mode = LOGMODE_MULTI;
+ } else if (strcasecmp(s_mode, "normal") != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "log-pcap: invalid mode \"%s\". Valid options: \"normal\", "
+ "\"sguil\", or \"multi\" mode ", s_mode);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ const char *s_dir = NULL;
+ s_dir = ConfNodeLookupChildValue(conf, "dir");
+ if (s_dir == NULL) {
+ s_dir = ConfNodeLookupChildValue(conf, "sguil-base-dir");
+ }
+ if (s_dir == NULL) {
+ if (pl->mode == LOGMODE_SGUIL) {
+ SCLogError(SC_ERR_LOGPCAP_SGUIL_BASE_DIR_MISSING,
+ "log-pcap \"sguil\" mode requires \"sguil-base-dir\" "
+ "option to be set.");
+ exit(EXIT_FAILURE);
+ } else {
+ char *log_dir = NULL;
+ log_dir = ConfigGetLogDirectory();
+
+ strlcpy(pl->dir,
+ log_dir, sizeof(pl->dir));
+ SCLogInfo("Using log dir %s", pl->dir);
+ }
+ } else {
+ if (PathIsAbsolute(s_dir)) {
+ strlcpy(pl->dir,
+ s_dir, sizeof(pl->dir));
+ } else {
+ char *log_dir = NULL;
+ log_dir = ConfigGetLogDirectory();
+
+ snprintf(pl->dir, sizeof(pl->dir), "%s/%s",
+ log_dir, s_dir);
+ }
+
+ struct stat stat_buf;
+ if (stat(pl->dir, &stat_buf) != 0) {
+ SCLogError(SC_ERR_LOGDIR_CONFIG, "The sguil-base-dir directory \"%s\" "
+ "supplied doesn't exist. Shutting down the engine",
+ pl->dir);
+ exit(EXIT_FAILURE);
+ }
+ SCLogInfo("Using log dir %s", pl->dir);
+ }
+ }
+
+ SCLogInfo("using %s logging", pl->mode == LOGMODE_SGUIL ?
+ "Sguil compatible" : (pl->mode == LOGMODE_MULTI ? "multi" : "normal"));
+
+ uint32_t max_file_limit = DEFAULT_FILE_LIMIT;
+ if (conf != NULL) {
+ const char *max_number_of_files_s = NULL;
+ max_number_of_files_s = ConfNodeLookupChildValue(conf, "max-files");
+ if (max_number_of_files_s != NULL) {
+ if (ByteExtractStringUint32(&max_file_limit, 10, 0,
+ max_number_of_files_s) == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to initialize "
+ "pcap-log output, invalid number of files limit: %s",
+ max_number_of_files_s);
+ exit(EXIT_FAILURE);
+ } else if (max_file_limit < 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize pcap-log output, limit less than "
+ "allowed minimum.");
+ exit(EXIT_FAILURE);
+ } else {
+ pl->max_files = max_file_limit;
+ pl->use_ringbuffer = RING_BUFFER_MODE_ENABLED;
+ }
+ }
+ }
+
+ const char *ts_format = NULL;
+ if (conf != NULL) { /* To faciliate unit tests. */
+ ts_format = ConfNodeLookupChildValue(conf, "ts-format");
+ }
+ if (ts_format != NULL) {
+ if (strcasecmp(ts_format, "usec") == 0) {
+ pl->timestamp_format = TS_FORMAT_USEC;
+ } else if (strcasecmp(ts_format, "sec") != 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "log-pcap ts_format specified %s is invalid must be"
+ " \"sec\" or \"usec\"", ts_format);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ const char *use_stream_depth = NULL;
+ if (conf != NULL) { /* To faciliate unit tests. */
+ use_stream_depth = ConfNodeLookupChildValue(conf, "use-stream-depth");
+ }
+ if (use_stream_depth != NULL) {
+ if (ConfValIsFalse(use_stream_depth)) {
+ pl->use_stream_depth = USE_STREAM_DEPTH_DISABLED;
+ } else if (ConfValIsTrue(use_stream_depth)) {
+ pl->use_stream_depth = USE_STREAM_DEPTH_ENABLED;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "log-pcap use_stream_depth specified is invalid must be");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ const char *honor_pass_rules = NULL;
+ if (conf != NULL) { /* To faciliate unit tests. */
+ honor_pass_rules = ConfNodeLookupChildValue(conf, "honor-pass-rules");
+ }
+ if (honor_pass_rules != NULL) {
+ if (ConfValIsFalse(honor_pass_rules)) {
+ pl->honor_pass_rules = HONOR_PASS_RULES_DISABLED;
+ } else if (ConfValIsTrue(honor_pass_rules)) {
+ pl->honor_pass_rules = HONOR_PASS_RULES_ENABLED;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "log-pcap honor-pass-rules specified is invalid");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* create the output ctx and send it back */
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for OutputCtx.");
+ exit(EXIT_FAILURE);
+ }
+ output_ctx->data = pl;
+ output_ctx->DeInit = PcapLogFileDeInitCtx;
+ g_pcap_data = pl;
+
+ return output_ctx;
+}
+
+static void PcapLogFileDeInitCtx(OutputCtx *output_ctx)
+{
+ if (output_ctx == NULL)
+ return;
+
+ PcapLogData *pl = output_ctx->data;
+
+ PcapFileName *pf = NULL;
+ TAILQ_FOREACH(pf, &pl->pcap_file_list, next) {
+ SCLogDebug("PCAP files left at exit: %s\n", pf->filename);
+ }
+
+ return;
+}
+
+/**
+ * \brief Read the config set the file pointer, open the file
+ *
+ * \param PcapLogData.
+ *
+ * \retval -1 if failure
+ * \retval 0 if succesful
+ */
+static int PcapLogOpenFileCtx(PcapLogData *pl)
+{
+ char *filename = NULL;
+
+ PCAPLOG_PROFILE_START;
+
+ if (pl->filename != NULL)
+ filename = pl->filename;
+ else {
+ filename = SCMalloc(PATH_MAX);
+ if (unlikely(filename == NULL)) {
+ return -1;
+ }
+ pl->filename = filename;
+ }
+
+ /** get the time so we can have a filename with seconds since epoch */
+ struct timeval ts;
+ memset(&ts, 0x00, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ /* Place to store the name of our PCAP file */
+ PcapFileName *pf = SCMalloc(sizeof(PcapFileName));
+ if (unlikely(pf == NULL)) {
+ return -1;
+ }
+ memset(pf, 0, sizeof(PcapFileName));
+
+ if (pl->mode == LOGMODE_SGUIL) {
+ struct tm local_tm;
+ struct tm *tms = SCLocalTime(ts.tv_sec, &local_tm);
+
+ char dirname[32], dirfull[PATH_MAX] = "";
+
+ snprintf(dirname, sizeof(dirname), "%04d-%02d-%02d",
+ tms->tm_year + 1900, tms->tm_mon + 1, tms->tm_mday);
+
+ /* create the filename to use */
+ snprintf(dirfull, PATH_MAX, "%s/%s", pl->dir, dirname);
+
+ /* if mkdir fails file open will fail, so deal with errors there */
+#ifndef OS_WIN32
+ (void)mkdir(dirfull, 0700);
+#else
+ (void)mkdir(dirfull);
+#endif
+ if ((pf->dirname = SCStrdup(dirfull)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for "
+ "directory name");
+ goto error;
+ }
+
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32, dirfull,
+ pl->prefix, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32 ".%" PRIu32,
+ dirfull, pl->prefix, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
+
+ } else if (pl->mode == LOGMODE_NORMAL) {
+ /* create the filename to use */
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32, pl->dir,
+ pl->prefix, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32 ".%" PRIu32, pl->dir,
+ pl->prefix, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
+ } else if (pl->mode == LOGMODE_MULTI) {
+ if (pl->filename_part_cnt > 0) {
+ /* assemble filename from stored tokens */
+
+ strlcpy(filename, pl->dir, PATH_MAX);
+ strlcat(filename, "/", PATH_MAX);
+
+ int i;
+ for (i = 0; i < pl->filename_part_cnt; i++) {
+ if (pl->filename_parts[i] == NULL ||strlen(pl->filename_parts[i]) == 0)
+ continue;
+
+ /* handle variables */
+ if (pl->filename_parts[i][0] == '%') {
+ char str[64] = "";
+ if (strlen(pl->filename_parts[i]) < 2)
+ continue;
+
+ switch(pl->filename_parts[i][1]) {
+ case 'n':
+ snprintf(str, sizeof(str), "%u", pl->thread_number);
+ break;
+ case 'i':
+ {
+ long thread_id = SCGetThreadIdLong();
+ snprintf(str, sizeof(str), "%"PRIu64, (uint64_t)thread_id);
+ break;
+ }
+ case 't':
+ /* create the filename to use */
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(str, sizeof(str), "%"PRIu32, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(str, sizeof(str), "%"PRIu32".%"PRIu32,
+ (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
+ }
+ strlcat(filename, str, PATH_MAX);
+
+ /* copy the rest over */
+ } else {
+ strlcat(filename, pl->filename_parts[i], PATH_MAX);
+ }
+ }
+ } else {
+ /* create the filename to use */
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32, pl->dir,
+ pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32 ".%" PRIu32, pl->dir,
+ pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
+ }
+ SCLogDebug("multi-mode: filename %s", filename);
+ }
+
+ if ((pf->filename = SCStrdup(pl->filename)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory. For filename");
+ goto error;
+ }
+ SCLogDebug("Opening pcap file log %s", pf->filename);
+ TAILQ_INSERT_TAIL(&pl->pcap_file_list, pf, next);
+
+ PCAPLOG_PROFILE_END(pl->profile_open);
+ return 0;
+
+error:
+ PcapFileNameFree(pf);
+ return -1;
+}
+
+static int profiling_pcaplog_enabled = 0;
+static int profiling_pcaplog_output_to_file = 0;
+static char *profiling_pcaplog_file_name = NULL;
+static char *profiling_pcaplog_file_mode = "a";
+
+static void FormatNumber(uint64_t num, char *str, size_t size)
+{
+ if (num < 1000UL)
+ snprintf(str, size, "%"PRIu64, num);
+ else if (num < 1000000UL)
+ snprintf(str, size, "%3.1fk", (float)num/1000UL);
+ else if (num < 1000000000UL)
+ snprintf(str, size, "%3.1fm", (float)num/1000000UL);
+ else
+ snprintf(str, size, "%3.1fb", (float)num/1000000000UL);
+}
+
+static void ProfileReportPair(FILE *fp, const char *name, PcapLogProfileData *p)
+{
+ char ticks_str[32] = "n/a";
+ char cnt_str[32] = "n/a";
+ char avg_str[32] = "n/a";
+
+ FormatNumber((uint64_t)p->cnt, cnt_str, sizeof(cnt_str));
+ FormatNumber((uint64_t)p->total, ticks_str, sizeof(ticks_str));
+ if (p->cnt && p->total)
+ FormatNumber((uint64_t)(p->total/p->cnt), avg_str, sizeof(avg_str));
+
+ fprintf(fp, "%-28s %-10s %-10s %-10s\n", name, cnt_str, avg_str, ticks_str);
+}
+
+static void ProfileReport(FILE *fp, PcapLogData *pl)
+{
+ ProfileReportPair(fp, "open", &pl->profile_open);
+ ProfileReportPair(fp, "close", &pl->profile_close);
+ ProfileReportPair(fp, "write", &pl->profile_write);
+ ProfileReportPair(fp, "rotate (incl open/close)", &pl->profile_rotate);
+ ProfileReportPair(fp, "handles", &pl->profile_handles);
+ ProfileReportPair(fp, "lock", &pl->profile_lock);
+ ProfileReportPair(fp, "unlock", &pl->profile_unlock);
+}
+
+static void FormatBytes(uint64_t num, char *str, size_t size)
+{
+ if (num < 1000UL)
+ snprintf(str, size, "%"PRIu64, num);
+ else if (num < 1048576UL)
+ snprintf(str, size, "%3.1fKiB", (float)num/1000UL);
+ else if (num < 1073741824UL)
+ snprintf(str, size, "%3.1fMiB", (float)num/1000000UL);
+ else
+ snprintf(str, size, "%3.1fGiB", (float)num/1000000000UL);
+}
+
+static void PcapLogProfilingDump(PcapLogData *pl)
+{
+ FILE *fp = NULL;
+
+ if (profiling_pcaplog_enabled == 0)
+ return;
+
+ if (profiling_pcaplog_output_to_file == 1) {
+ fp = fopen(profiling_pcaplog_file_name, profiling_pcaplog_file_mode);
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s",
+ profiling_pcaplog_file_name, strerror(errno));
+ return;
+ }
+ } else {
+ fp = stdout;
+ }
+
+ /* counters */
+ fprintf(fp, "\n\nOperation Cnt Avg ticks Total ticks\n");
+ fprintf(fp, "---------------------------- ---------- ---------- -----------\n");
+
+ ProfileReport(fp, pl);
+ uint64_t total = pl->profile_write.total + pl->profile_rotate.total +
+ pl->profile_handles.total + pl->profile_open.total +
+ pl->profile_close.total + pl->profile_lock.total +
+ pl->profile_unlock.total;
+
+ /* overall stats */
+ fprintf(fp, "\nOverall: %"PRIu64" bytes written, average %d bytes per write.\n",
+ pl->profile_data_size, (int)(pl->profile_data_size / pl->profile_write.cnt));
+ fprintf(fp, " PCAP data structure overhead: %"PRIuMAX" per write.\n",
+ (uintmax_t)sizeof(struct pcap_pkthdr));
+
+ /* print total bytes written */
+ char bytes_str[32];
+ FormatBytes(pl->profile_data_size, bytes_str, sizeof(bytes_str));
+ fprintf(fp, " Size written: %s\n", bytes_str);
+
+ /* ticks per MiB and GiB */
+ uint64_t ticks_per_mib = 0, ticks_per_gib = 0;
+ uint64_t mib = pl->profile_data_size/(1024*1024);
+ if (mib)
+ ticks_per_mib = total/mib;
+ char ticks_per_mib_str[32] = "n/a";
+ if (ticks_per_mib > 0)
+ FormatNumber(ticks_per_mib, ticks_per_mib_str, sizeof(ticks_per_mib_str));
+ fprintf(fp, " Ticks per MiB: %s\n", ticks_per_mib_str);
+
+ uint64_t gib = pl->profile_data_size/(1024*1024*1024);
+ if (gib)
+ ticks_per_gib = total/gib;
+ char ticks_per_gib_str[32] = "n/a";
+ if (ticks_per_gib > 0)
+ FormatNumber(ticks_per_gib, ticks_per_gib_str, sizeof(ticks_per_gib_str));
+ fprintf(fp, " Ticks per GiB: %s\n", ticks_per_gib_str);
+
+ if (fp != stdout)
+ fclose(fp);
+}
+
+void PcapLogProfileSetup(void)
+{
+ ConfNode *conf = ConfGetNode("profiling.pcap-log");
+ if (conf != NULL && ConfNodeChildValueIsTrue(conf, "enabled")) {
+ profiling_pcaplog_enabled = 1;
+ SCLogInfo("pcap-log profiling enabled");
+
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename != NULL) {
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_pcaplog_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_pcaplog_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name");
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(profiling_pcaplog_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ const char *v = ConfNodeLookupChildValue(conf, "append");
+ if (v == NULL || ConfValIsTrue(v)) {
+ profiling_pcaplog_file_mode = "a";
+ } else {
+ profiling_pcaplog_file_mode = "w";
+ }
+
+ profiling_pcaplog_output_to_file = 1;
+ SCLogInfo("pcap-log profiling output goes to %s (mode %s)",
+ profiling_pcaplog_file_name, profiling_pcaplog_file_mode);
+ }
+ }
+}
diff --git a/framework/src/suricata/src/log-pcap.h b/framework/src/suricata/src/log-pcap.h
new file mode 100644
index 00000000..0c6e7011
--- /dev/null
+++ b/framework/src/suricata/src/log-pcap.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/**
+ * \file
+ *
+ * \author William Metcalf <William.Metcalf@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pcap packet logging module.
+ */
+
+#ifndef __LOG_PCAP_H__
+#define __LOG_PCAP_H__
+
+void TmModulePcapLogRegister (void);
+void PcapLogProfileSetup(void);
+
+#endif /* __LOG_PCAP_H__ */
diff --git a/framework/src/suricata/src/log-stats.c b/framework/src/suricata/src/log-stats.c
new file mode 100644
index 00000000..5fcb1c9f
--- /dev/null
+++ b/framework/src/suricata/src/log-stats.c
@@ -0,0 +1,288 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-stats.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "stats.log"
+#define MODULE_NAME "LogStatsLog"
+#define OUTPUT_BUFFER_SIZE 16384
+
+#define LOG_STATS_TOTALS (1<<0)
+#define LOG_STATS_THREADS (1<<1)
+
+TmEcode LogStatsLogThreadInit(ThreadVars *, void *, void **);
+TmEcode LogStatsLogThreadDeinit(ThreadVars *, void *);
+void LogStatsLogExitPrintStats(ThreadVars *, void *);
+static void LogStatsLogDeInitCtx(OutputCtx *);
+
+typedef struct LogStatsFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} LogStatsFileCtx;
+
+typedef struct LogStatsLogThread_ {
+ LogStatsFileCtx *statslog_ctx;
+ MemBuffer *buffer;
+} LogStatsLogThread;
+
+int LogStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
+{
+ SCEnter();
+ LogStatsLogThread *aft = (LogStatsLogThread *)thread_data;
+
+ struct timeval tval;
+ struct tm *tms;
+
+ gettimeofday(&tval, NULL);
+ struct tm local_tm;
+ tms = SCLocalTime(tval.tv_sec, &local_tm);
+
+ /* Calculate the Engine uptime */
+ int up_time = (int)difftime(tval.tv_sec, st->start_time);
+ int sec = up_time % 60; // Seconds in a minute
+ int in_min = up_time / 60;
+ int min = in_min % 60; // Minutes in a hour
+ int in_hours = in_min / 60;
+ int hours = in_hours % 24; // Hours in a day
+ int days = in_hours / 24;
+
+ MemBufferWriteString(aft->buffer, "----------------------------------------------"
+ "---------------------\n");
+ MemBufferWriteString(aft->buffer, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
+ "%02d:%02d:%02d (uptime: %"PRId32"d, %02dh %02dm %02ds)\n",
+ tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900, tms->tm_hour,
+ tms->tm_min, tms->tm_sec, days, hours, min, sec);
+ MemBufferWriteString(aft->buffer, "----------------------------------------------"
+ "---------------------\n");
+ MemBufferWriteString(aft->buffer, "%-25s | %-25s | %-s\n", "Counter", "TM Name",
+ "Value");
+ MemBufferWriteString(aft->buffer, "----------------------------------------------"
+ "---------------------\n");
+
+ /* global stats */
+ uint32_t u = 0;
+ if (aft->statslog_ctx->flags & LOG_STATS_TOTALS) {
+ for (u = 0; u < st->nstats; u++) {
+ if (st->stats[u].name == NULL)
+ continue;
+
+ char line[1024];
+ size_t len = snprintf(line, sizeof(line), "%-25s | %-25s | %-" PRIu64 "\n",
+ st->stats[u].name, st->stats[u].tm_name, st->stats[u].value);
+
+ /* since we can have many threads, the buffer might not be big enough.
+ * Expand if necessary. */
+ if (MEMBUFFER_OFFSET(aft->buffer) + len > MEMBUFFER_SIZE(aft->buffer)) {
+ MemBufferExpand(&aft->buffer, OUTPUT_BUFFER_SIZE);
+ }
+
+ MemBufferWriteString(aft->buffer, "%s", line);
+ }
+ }
+
+ /* per thread stats */
+ if (st->tstats != NULL && aft->statslog_ctx->flags & LOG_STATS_THREADS) {
+ /* for each thread (store) */
+ uint32_t x;
+ for (x = 0; x < st->ntstats; x++) {
+ uint32_t offset = x * st->nstats;
+
+ /* for each counter */
+ for (u = offset; u < (offset + st->nstats); u++) {
+ if (st->tstats[u].name == NULL)
+ continue;
+
+ char line[1024];
+ size_t len = snprintf(line, sizeof(line), "%-25s | %-25s | %-" PRIu64 "\n",
+ st->tstats[u].name, st->tstats[u].tm_name, st->tstats[u].value);
+
+ /* since we can have many threads, the buffer might not be big enough.
+ * Expand if necessary. */
+ if (MEMBUFFER_OFFSET(aft->buffer) + len > MEMBUFFER_SIZE(aft->buffer)) {
+ MemBufferExpand(&aft->buffer, OUTPUT_BUFFER_SIZE);
+ }
+
+ MemBufferWriteString(aft->buffer, "%s", line);
+ }
+ }
+ }
+
+ SCMutexLock(&aft->statslog_ctx->file_ctx->fp_mutex);
+ aft->statslog_ctx->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), aft->statslog_ctx->file_ctx);
+ SCMutexUnlock(&aft->statslog_ctx->file_ctx->fp_mutex);
+
+ MemBufferReset(aft->buffer);
+
+ SCReturnInt(0);
+}
+
+TmEcode LogStatsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogStatsLogThread *aft = SCMalloc(sizeof(LogStatsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogStatsLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->statslog_ctx= ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode LogStatsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogStatsLogThread *aft = (LogStatsLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogStatsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void LogStatsLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogStatsLogThread *aft = (LogStatsLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+}
+
+/** \brief Create a new http log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+OutputCtx *LogStatsLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if (file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogStatsFileCtx *statslog_ctx = SCMalloc(sizeof(LogStatsFileCtx));
+ if (unlikely(statslog_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ memset(statslog_ctx, 0x00, sizeof(LogStatsFileCtx));
+
+ statslog_ctx->flags = LOG_STATS_TOTALS;
+
+ if (conf != NULL) {
+ const char *totals = ConfNodeLookupChildValue(conf, "totals");
+ const char *threads = ConfNodeLookupChildValue(conf, "threads");
+ SCLogDebug("totals %s threads %s", totals, threads);
+
+ if (totals != NULL && ConfValIsFalse(totals)) {
+ statslog_ctx->flags &= ~LOG_STATS_TOTALS;
+ }
+ if (threads != NULL && ConfValIsTrue(threads)) {
+ statslog_ctx->flags |= LOG_STATS_THREADS;
+ }
+ SCLogDebug("statslog_ctx->flags %08x", statslog_ctx->flags);
+ }
+
+ statslog_ctx->file_ctx = file_ctx;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(statslog_ctx);
+ return NULL;
+ }
+
+ output_ctx->data = statslog_ctx;
+ output_ctx->DeInit = LogStatsLogDeInitCtx;
+
+ SCLogDebug("STATS log output initialized");
+
+ return output_ctx;
+}
+
+static void LogStatsLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogStatsFileCtx *statslog_ctx = (LogStatsFileCtx *)output_ctx->data;
+ LogFileFreeCtx(statslog_ctx->file_ctx);
+ SCFree(statslog_ctx);
+ SCFree(output_ctx);
+}
+
+void TmModuleLogStatsLogRegister (void)
+{
+ tmm_modules[TMM_LOGSTATSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGSTATSLOG].ThreadInit = LogStatsLogThreadInit;
+ tmm_modules[TMM_LOGSTATSLOG].ThreadExitPrintStats = LogStatsLogExitPrintStats;
+ tmm_modules[TMM_LOGSTATSLOG].ThreadDeinit = LogStatsLogThreadDeinit;
+ tmm_modules[TMM_LOGSTATSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_LOGSTATSLOG].cap_flags = 0;
+ tmm_modules[TMM_LOGSTATSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterStatsModule(MODULE_NAME, "stats", LogStatsLogInitCtx, LogStatsLogger);
+}
diff --git a/framework/src/suricata/src/log-stats.h b/framework/src/suricata/src/log-stats.h
new file mode 100644
index 00000000..957104f3
--- /dev/null
+++ b/framework/src/suricata/src/log-stats.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_STATS_H__
+#define __LOG_STATS_H__
+
+void TmModuleLogStatsLogRegister (void);
+
+#endif /* __LOG_HTTPLOG_H__ */
diff --git a/framework/src/suricata/src/log-tcp-data.c b/framework/src/suricata/src/log-tcp-data.c
new file mode 100644
index 00000000..c41dd4c2
--- /dev/null
+++ b/framework/src/suricata/src/log-tcp-data.c
@@ -0,0 +1,345 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-tcp-data.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "tcp-data.log"
+
+#define MODULE_NAME "LogTcpDataLog"
+
+#define OUTPUT_BUFFER_SIZE 65535
+
+TmEcode LogTcpDataLogThreadInit(ThreadVars *, void *, void **);
+TmEcode LogTcpDataLogThreadDeinit(ThreadVars *, void *);
+void LogTcpDataLogExitPrintStats(ThreadVars *, void *);
+static void LogTcpDataLogDeInitCtx(OutputCtx *);
+
+int LogTcpDataLogger(ThreadVars *tv, void *thread_data, const Flow *f, const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags);
+
+void TmModuleLogTcpDataLogRegister (void) {
+ tmm_modules[TMM_LOGTCPDATALOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGTCPDATALOG].ThreadInit = LogTcpDataLogThreadInit;
+ tmm_modules[TMM_LOGTCPDATALOG].ThreadExitPrintStats = LogTcpDataLogExitPrintStats;
+ tmm_modules[TMM_LOGTCPDATALOG].ThreadDeinit = LogTcpDataLogThreadDeinit;
+ tmm_modules[TMM_LOGTCPDATALOG].RegisterTests = NULL;
+ tmm_modules[TMM_LOGTCPDATALOG].cap_flags = 0;
+ tmm_modules[TMM_LOGTCPDATALOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterStreamingModule(MODULE_NAME, "tcp-data", LogTcpDataLogInitCtx,
+ LogTcpDataLogger, STREAMING_TCP_DATA);
+ OutputRegisterStreamingModule(MODULE_NAME, "http-body-data", LogTcpDataLogInitCtx,
+ LogTcpDataLogger, STREAMING_HTTP_BODIES);
+}
+
+typedef struct LogTcpDataFileCtx_ {
+ LogFileCtx *file_ctx;
+ enum OutputStreamingType type;
+ const char *log_dir;
+ int file;
+ int dir;
+} LogTcpDataFileCtx;
+
+typedef struct LogTcpDataLogThread_ {
+ LogTcpDataFileCtx *tcpdatalog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ MemBuffer *buffer;
+} LogTcpDataLogThread;
+
+static int LogTcpDataLoggerDir(ThreadVars *tv, void *thread_data, const Flow *f,
+ const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
+{
+ SCEnter();
+ LogTcpDataLogThread *aft = thread_data;
+ LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
+ char *mode = "a";
+
+ if (flags & OUTPUT_STREAMING_FLAG_OPEN)
+ mode = "w";
+
+ if (data && data_len) {
+ char srcip[46] = "", dstip[46] = "";
+ if (FLOW_IS_IPV4(f)) {
+ PrintInet(AF_INET, (const void *)&f->src.addr_data32[0], srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&f->dst.addr_data32[0], dstip, sizeof(dstip));
+ } else if (FLOW_IS_IPV6(f)) {
+ PrintInet(AF_INET6, (const void *)f->src.addr_data32, srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)f->dst.addr_data32, dstip, sizeof(dstip));
+ }
+
+ char name[PATH_MAX];
+
+ char tx[64] = "";
+ if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION)
+ snprintf(tx, sizeof(tx), "%"PRIu64, tx_id);
+
+ snprintf(name, sizeof(name), "%s/%s/%s_%u-%s_%u-%s-%s.data",
+ td->log_dir,
+ td->type == STREAMING_HTTP_BODIES ? "http" : "tcp",
+ srcip, f->sp, dstip, f->dp, tx,
+ flags & OUTPUT_STREAMING_FLAG_TOSERVER ? "ts" : "tc");
+
+ FILE *fp = fopen(name, mode);
+ BUG_ON(fp == NULL);
+
+ // PrintRawDataFp(stdout, (uint8_t *)data, data_len);
+ fwrite(data, data_len, 1, fp);
+
+ fclose(fp);
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static int LogTcpDataLoggerFile(ThreadVars *tv, void *thread_data, const Flow *f,
+ const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
+{
+ SCEnter();
+ LogTcpDataLogThread *aft = thread_data;
+ LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
+
+ if (data && data_len) {
+ MemBufferReset(aft->buffer);
+
+ char srcip[46] = "", dstip[46] = "";
+ if (FLOW_IS_IPV4(f)) {
+ PrintInet(AF_INET, (const void *)&f->src.addr_data32[0], srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&f->dst.addr_data32[0], dstip, sizeof(dstip));
+ } else if (FLOW_IS_IPV6(f)) {
+ PrintInet(AF_INET6, (const void *)f->src.addr_data32, srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)f->dst.addr_data32, dstip, sizeof(dstip));
+ }
+
+ char name[PATH_MAX];
+ snprintf(name, sizeof(name), "%s_%u-%s_%u-%s:",
+ srcip, f->sp, dstip, f->dp,
+ flags & OUTPUT_STREAMING_FLAG_TOSERVER ? "ts" : "tc");
+
+ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)name,strlen(name));
+ MemBufferWriteString(aft->buffer, "\n");
+
+ PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset,
+ aft->buffer->size, (uint8_t *)data,data_len);
+
+ SCMutexLock(&td->file_ctx->fp_mutex);
+ td->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), td->file_ctx);
+ SCMutexUnlock(&td->file_ctx->fp_mutex);
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+int LogTcpDataLogger(ThreadVars *tv, void *thread_data, const Flow *f,
+ const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
+{
+ SCEnter();
+ LogTcpDataLogThread *aft = thread_data;
+ LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
+
+ if (td->dir == 1)
+ LogTcpDataLoggerDir(tv, thread_data, f, data, data_len, tx_id, flags);
+ if (td->file == 1)
+ LogTcpDataLoggerFile(tv, thread_data, f, data, data_len, tx_id, flags);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode LogTcpDataLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogTcpDataLogThread *aft = SCMalloc(sizeof(LogTcpDataLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogTcpDataLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->tcpdatalog_ctx= ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode LogTcpDataLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogTcpDataLogThread *aft = (LogTcpDataLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogTcpDataLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void LogTcpDataLogExitPrintStats(ThreadVars *tv, void *data) {
+ LogTcpDataLogThread *aft = (LogTcpDataLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+}
+
+/** \brief Create a new http log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+OutputCtx *LogTcpDataLogInitCtx(ConfNode *conf)
+{
+ char filename[PATH_MAX] = "";
+ char dirname[32] = "";
+ strlcpy(filename, DEFAULT_LOG_FILENAME, sizeof(filename));
+
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ LogTcpDataFileCtx *tcpdatalog_ctx = SCMalloc(sizeof(LogTcpDataFileCtx));
+ if (unlikely(tcpdatalog_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ memset(tcpdatalog_ctx, 0x00, sizeof(LogTcpDataFileCtx));
+
+ tcpdatalog_ctx->file_ctx = file_ctx;
+
+ if (conf) {
+ if (conf->name) {
+ if (strcmp(conf->name, "tcp-data") == 0) {
+ tcpdatalog_ctx->type = STREAMING_TCP_DATA;
+ snprintf(filename, sizeof(filename), "%s.log", conf->name);
+ strlcpy(dirname, "tcp", sizeof(dirname));
+ } else if (strcmp(conf->name, "http-body-data") == 0) {
+ tcpdatalog_ctx->type = STREAMING_HTTP_BODIES;
+ snprintf(filename, sizeof(filename), "%s.log", conf->name);
+ strlcpy(dirname, "http", sizeof(dirname));
+ }
+ }
+
+ const char *logtype = ConfNodeLookupChildValue(conf, "type");
+ if (logtype == NULL)
+ logtype = "file";
+
+ if (strcmp(logtype, "file") == 0) {
+ tcpdatalog_ctx->file = 1;
+ } else if (strcmp(logtype, "dir") == 0) {
+ tcpdatalog_ctx->dir = 1;
+ } else if (strcmp(logtype, "both") == 0) {
+ tcpdatalog_ctx->file = 1;
+ tcpdatalog_ctx->dir = 1;
+ }
+ } else {
+ tcpdatalog_ctx->file = 1;
+ tcpdatalog_ctx->dir = 0;
+ }
+
+ if (tcpdatalog_ctx->file == 1) {
+ SCLogInfo("opening logfile");
+ if (SCConfLogOpenGeneric(conf, file_ctx, filename, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(tcpdatalog_ctx);
+ return NULL;
+ }
+ }
+
+ if (tcpdatalog_ctx->dir == 1) {
+ tcpdatalog_ctx->log_dir = ConfigGetLogDirectory();
+ char dirfull[PATH_MAX];
+
+ /* create the filename to use */
+ snprintf(dirfull, PATH_MAX, "%s/%s", tcpdatalog_ctx->log_dir, dirname);
+
+ SCLogInfo("using directory %s", dirfull);
+
+ /* if mkdir fails file open will fail, so deal with errors there */
+#ifndef OS_WIN32
+ (void)mkdir(dirfull, 0700);
+#else
+ (void)mkdir(dirfull);
+#endif
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ goto parsererror;
+ }
+
+ output_ctx->data = tcpdatalog_ctx;
+ output_ctx->DeInit = LogTcpDataLogDeInitCtx;
+
+ SCLogDebug("Streaming log output initialized");
+ return output_ctx;
+
+parsererror:
+ LogFileFreeCtx(file_ctx);
+ SCFree(tcpdatalog_ctx);
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Syntax error in custom http log format string.");
+ return NULL;
+
+}
+
+static void LogTcpDataLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogTcpDataFileCtx *tcpdatalog_ctx = (LogTcpDataFileCtx *)output_ctx->data;
+ LogFileFreeCtx(tcpdatalog_ctx->file_ctx);
+ SCFree(tcpdatalog_ctx);
+ SCFree(output_ctx);
+}
diff --git a/framework/src/suricata/src/log-tcp-data.h b/framework/src/suricata/src/log-tcp-data.h
new file mode 100644
index 00000000..5123c3b2
--- /dev/null
+++ b/framework/src/suricata/src/log-tcp-data.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_TCPDATALOG_H__
+#define __LOG_TCPDATALOG_H__
+
+void TmModuleLogTcpDataLogRegister (void);
+OutputCtx *LogTcpDataLogInitCtx(ConfNode *);
+
+#endif /* __LOG_TCPDATALOG_H__ */
diff --git a/framework/src/suricata/src/log-tlslog.c b/framework/src/suricata/src/log-tlslog.c
new file mode 100644
index 00000000..edb0ded2
--- /dev/null
+++ b/framework/src/suricata/src/log-tlslog.c
@@ -0,0 +1,393 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Roliers Jean-Paul <popof.fpn@gmail.co>
+ * \author Eric Leblond <eric@regit.org>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements TLS logging portion of the engine. The TLS logger is
+ * implemented as a packet logger, as the TLS parser is not transaction
+ * aware.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-tlslog.h"
+#include "app-layer-ssl.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+#include "util-time.h"
+
+#define DEFAULT_LOG_FILENAME "tls.log"
+
+#define MODULE_NAME "LogTlsLog"
+
+#define OUTPUT_BUFFER_SIZE 65535
+#define CERT_ENC_BUFFER_SIZE 2048
+
+#define LOG_TLS_DEFAULT 0
+#define LOG_TLS_EXTENDED 1
+
+typedef struct LogTlsFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} LogTlsFileCtx;
+
+typedef struct LogTlsLogThread_ {
+ LogTlsFileCtx *tlslog_ctx;
+
+ /** LogTlsFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t tls_cnt;
+
+ MemBuffer *buffer;
+} LogTlsLogThread;
+
+static void LogTlsLogExtended(LogTlsLogThread *aft, SSLState * state)
+{
+ if (state->server_connp.cert0_fingerprint != NULL) {
+ MemBufferWriteString(aft->buffer, " SHA1='%s'", state->server_connp.cert0_fingerprint);
+ }
+ switch (state->server_connp.version) {
+ case TLS_VERSION_UNKNOWN:
+ MemBufferWriteString(aft->buffer, " VERSION='UNDETERMINED'");
+ break;
+ case SSL_VERSION_2:
+ MemBufferWriteString(aft->buffer, " VERSION='SSLv2'");
+ break;
+ case SSL_VERSION_3:
+ MemBufferWriteString(aft->buffer, " VERSION='SSLv3'");
+ break;
+ case TLS_VERSION_10:
+ MemBufferWriteString(aft->buffer, " VERSION='TLSv1'");
+ break;
+ case TLS_VERSION_11:
+ MemBufferWriteString(aft->buffer, " VERSION='TLS 1.1'");
+ break;
+ case TLS_VERSION_12:
+ MemBufferWriteString(aft->buffer, " VERSION='TLS 1.2'");
+ break;
+ default:
+ MemBufferWriteString(aft->buffer, " VERSION='0x%04x'",
+ state->server_connp.version);
+ break;
+ }
+ MemBufferWriteString(aft->buffer, "\n");
+}
+
+int TLSGetIPInformations(const Packet *p, char* srcip, size_t srcip_len,
+ Port* sp, char* dstip, size_t dstip_len,
+ Port* dp, int ipproto)
+{
+ if ((PKT_IS_TOSERVER(p))) {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), srcip, srcip_len);
+ PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), dstip, dstip_len);
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), srcip, srcip_len);
+ PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), dstip, dstip_len);
+ break;
+ default:
+ return 0;
+ }
+ *sp = p->sp;
+ *dp = p->dp;
+ } else {
+ switch (ipproto) {
+ case AF_INET:
+ PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), srcip, srcip_len);
+ PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), dstip, dstip_len);
+ break;
+ case AF_INET6:
+ PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), srcip, srcip_len);
+ PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), dstip, dstip_len);
+ break;
+ default:
+ return 0;
+ }
+ *sp = p->dp;
+ *dp = p->sp;
+ }
+ return 1;
+}
+
+static TmEcode LogTlsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogTlsLogThread *aft = SCMalloc(sizeof(LogTlsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogTlsLogThread));
+
+ if (initdata == NULL) {
+ SCLogDebug( "Error getting context for TLSLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->tlslog_ctx = ((OutputCtx *) initdata)->data;
+
+ *data = (void *) aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode LogTlsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogTlsLogThread *aft = (LogTlsLogThread *) data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogTlsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void LogTlsLogDeInitCtx(OutputCtx *output_ctx)
+{
+ OutputTlsLoggerDisable();
+
+ LogTlsFileCtx *tlslog_ctx = (LogTlsFileCtx *) output_ctx->data;
+ LogFileFreeCtx(tlslog_ctx->file_ctx);
+ SCFree(tlslog_ctx);
+ SCFree(output_ctx);
+}
+
+static void LogTlsLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogTlsLogThread *aft = (LogTlsLogThread *) data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("TLS logger logged %" PRIu32 " requests", aft->tls_cnt);
+}
+
+/** \brief Create a new tls log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *LogTlsLogInitCtx(ConfNode *conf)
+{
+ if (OutputTlsLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'tls' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ LogFileCtx* file_ctx = LogFileNewCtx();
+
+ if (file_ctx == NULL) {
+ SCLogError(SC_ERR_TLS_LOG_GENERIC, "LogTlsLogInitCtx: Couldn't "
+ "create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ goto filectx_error;
+ }
+
+ LogTlsFileCtx *tlslog_ctx = SCCalloc(1, sizeof(LogTlsFileCtx));
+ if (unlikely(tlslog_ctx == NULL))
+ goto filectx_error;
+ tlslog_ctx->file_ctx = file_ctx;
+
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+ if (extended == NULL) {
+ tlslog_ctx->flags |= LOG_TLS_DEFAULT;
+ } else {
+ if (ConfValIsTrue(extended)) {
+ tlslog_ctx->flags |= LOG_TLS_EXTENDED;
+ }
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ goto tlslog_error;
+ output_ctx->data = tlslog_ctx;
+ output_ctx->DeInit = LogTlsLogDeInitCtx;
+
+ SCLogDebug("TLS log output initialized");
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TLS);
+
+ return output_ctx;
+
+tlslog_error:
+ SCFree(tlslog_ctx);
+filectx_error:
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+}
+
+/** \internal
+ * \brief Condition function for TLS logger
+ * \retval bool true or false -- log now?
+ */
+static int LogTlsCondition(ThreadVars *tv, const Packet *p)
+{
+ if (p->flow == NULL) {
+ return FALSE;
+ }
+
+ if (!(PKT_IS_TCP(p))) {
+ return FALSE;
+ }
+
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto dontlog;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, so no request logging");
+ goto dontlog;
+ }
+
+ /* we only log the state once if we don't have to write
+ * the cert due to tls.store keyword. */
+ if (!(ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) &&
+ (ssl_state->flags & SSL_AL_FLAG_STATE_LOGGED))
+ goto dontlog;
+
+ if (ssl_state->server_connp.cert0_issuerdn == NULL ||
+ ssl_state->server_connp.cert0_subject == NULL)
+ goto dontlog;
+
+ /* todo: logic to log once */
+
+ FLOWLOCK_UNLOCK(p->flow);
+ return TRUE;
+dontlog:
+ FLOWLOCK_UNLOCK(p->flow);
+ return FALSE;
+}
+
+static int LogTlsLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ LogTlsLogThread *aft = (LogTlsLogThread *)thread_data;
+ LogTlsFileCtx *hlog = aft->tlslog_ctx;
+ char timebuf[64];
+ int ipproto = (PKT_IS_IPV4(p)) ? AF_INET : AF_INET6;
+
+ if (unlikely(p->flow == NULL)) {
+ return 0;
+ }
+
+ /* check if we have TLS state or not */
+ FLOWLOCK_WRLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto end;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (unlikely(ssl_state == NULL)) {
+ goto end;
+ }
+
+ if (ssl_state->server_connp.cert0_issuerdn == NULL || ssl_state->server_connp.cert0_subject == NULL)
+ goto end;
+
+ /* Don't log again the state. If we are here it was because we had
+ * to store the cert. */
+ if (ssl_state->flags & SSL_AL_FLAG_STATE_LOGGED)
+ goto end;
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+#define PRINT_BUF_LEN 46
+ char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN];
+ Port sp, dp;
+ if (!TLSGetIPInformations(p, srcip, PRINT_BUF_LEN,
+ &sp, dstip, PRINT_BUF_LEN, &dp, ipproto)) {
+ goto end;
+ }
+
+ MemBufferReset(aft->buffer);
+ MemBufferWriteString(aft->buffer,
+ "%s %s:%d -> %s:%d TLS: Subject='%s' Issuerdn='%s'",
+ timebuf, srcip, sp, dstip, dp,
+ ssl_state->server_connp.cert0_subject,
+ ssl_state->server_connp.cert0_issuerdn);
+
+ if (hlog->flags & LOG_TLS_EXTENDED) {
+ LogTlsLogExtended(aft, ssl_state);
+ } else {
+ MemBufferWriteString(aft->buffer, "\n");
+ }
+
+ aft->tls_cnt++;
+
+ SCMutexLock(&hlog->file_ctx->fp_mutex);
+ hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
+ MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
+ SCMutexUnlock(&hlog->file_ctx->fp_mutex);
+
+ /* we only log the state once */
+ ssl_state->flags |= SSL_AL_FLAG_STATE_LOGGED;
+end:
+ FLOWLOCK_UNLOCK(p->flow);
+ return 0;
+}
+
+void TmModuleLogTlsLogRegister(void)
+{
+ tmm_modules[TMM_LOGTLSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_LOGTLSLOG].ThreadInit = LogTlsLogThreadInit;
+ tmm_modules[TMM_LOGTLSLOG].Func = NULL;
+ tmm_modules[TMM_LOGTLSLOG].ThreadExitPrintStats = LogTlsLogExitPrintStats;
+ tmm_modules[TMM_LOGTLSLOG].ThreadDeinit = LogTlsLogThreadDeinit;
+ tmm_modules[TMM_LOGTLSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_LOGTLSLOG].cap_flags = 0;
+ tmm_modules[TMM_LOGTLSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "tls-log", LogTlsLogInitCtx,
+ LogTlsLogger, LogTlsCondition);
+}
diff --git a/framework/src/suricata/src/log-tlslog.h b/framework/src/suricata/src/log-tlslog.h
new file mode 100644
index 00000000..d3c9ce51
--- /dev/null
+++ b/framework/src/suricata/src/log-tlslog.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Roliers Jean-Paul <popof.fpn@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __LOG_TLSLOG_H__
+#define __LOG_TLSLOG_H__
+
+void TmModuleLogTlsLogRegister (void);
+
+int TLSGetIPInformations(const Packet *p, char* srcip, size_t srcip_len,
+ Port* sp, char* dstip, size_t dstip_len,
+ Port* dp, int ipproto);
+
+#endif /* __LOG_TLSLOG_H__ */
+
diff --git a/framework/src/suricata/src/log-tlsstore.c b/framework/src/suricata/src/log-tlsstore.c
new file mode 100644
index 00000000..da23908e
--- /dev/null
+++ b/framework/src/suricata/src/log-tlsstore.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Roliers Jean-Paul <popof.fpn@gmail.co>
+ * \author Eric Leblond <eric@regit.org>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements TLS store portion of the engine.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "log-tlslog.h"
+#include "app-layer-ssl.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+#include "util-time.h"
+
+#define MODULE_NAME "LogTlsStoreLog"
+
+static char tls_logfile_base_dir[PATH_MAX] = "/tmp";
+SC_ATOMIC_DECLARE(unsigned int, cert_id);
+static char logging_dir_not_writable;
+
+#define LOGGING_WRITE_ISSUE_LIMIT 6
+
+typedef struct LogTlsStoreLogThread_ {
+ uint32_t tls_cnt;
+
+ uint8_t* enc_buf;
+ size_t enc_buf_len;
+} LogTlsStoreLogThread;
+
+static int CreateFileName(const Packet *p, SSLState *state, char *filename)
+{
+#define FILELEN 64 //filename len + extention + ending path / + some space
+
+ int filenamelen = FILELEN + strlen(tls_logfile_base_dir);
+ int file_id = SC_ATOMIC_ADD(cert_id, 1);
+
+ if (filenamelen + 1 > PATH_MAX) {
+ return 0;
+ }
+
+ /* Use format : packet time + incremental ID
+ * When running on same pcap it will overwrite
+ * On a live device, we will not be able to overwrite */
+ snprintf(filename, filenamelen, "%s/%ld.%ld-%d.pem",
+ tls_logfile_base_dir,
+ (long int)p->ts.tv_sec,
+ (long int)p->ts.tv_usec,
+ file_id);
+ return 1;
+}
+
+static void LogTlsLogPem(LogTlsStoreLogThread *aft, const Packet *p, SSLState *state, int ipproto)
+{
+#define PEMHEADER "-----BEGIN CERTIFICATE-----\n"
+#define PEMFOOTER "-----END CERTIFICATE-----\n"
+ //Logging pem certificate
+ char filename[PATH_MAX] = "";
+ FILE* fp = NULL;
+ FILE* fpmeta = NULL;
+ unsigned long pemlen;
+ unsigned char* pembase64ptr = NULL;
+ int ret;
+ uint8_t *ptmp;
+ SSLCertsChain *cert;
+
+ if ((state->server_connp.cert_input == NULL) || (state->server_connp.cert_input_len == 0))
+ SCReturn;
+
+ CreateFileName(p, state, filename);
+ if (strlen(filename) == 0) {
+ SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename");
+ SCReturn;
+ }
+
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
+ SCLogWarning(SC_ERR_FOPEN,
+ "Can't create PEM file '%s' in '%s' directory",
+ filename, tls_logfile_base_dir);
+ logging_dir_not_writable++;
+ }
+ SCReturn;
+ }
+
+ TAILQ_FOREACH(cert, &state->server_connp.certs, next) {
+ pemlen = (4 * (cert->cert_len + 2) / 3) +1;
+ if (pemlen > aft->enc_buf_len) {
+ ptmp = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen);
+ if (ptmp == NULL) {
+ SCFree(aft->enc_buf);
+ aft->enc_buf = NULL;
+ SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding");
+ goto end_fp;
+ }
+ aft->enc_buf = ptmp;
+ aft->enc_buf_len = pemlen;
+ }
+
+ memset(aft->enc_buf, 0, aft->enc_buf_len);
+
+ ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, aft->enc_buf, &pemlen);
+ if (ret != SC_BASE64_OK) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function");
+ goto end_fwrite_fp;
+ }
+
+ if (fprintf(fp, PEMHEADER) < 0)
+ goto end_fwrite_fp;
+
+ pembase64ptr = aft->enc_buf;
+ while (pemlen > 0) {
+ size_t loffset = pemlen >= 64 ? 64 : pemlen;
+ if (fwrite(pembase64ptr, 1, loffset, fp) != loffset)
+ goto end_fwrite_fp;
+ if (fwrite("\n", 1, 1, fp) != 1)
+ goto end_fwrite_fp;
+ pembase64ptr += 64;
+ if (pemlen < 64)
+ break;
+ pemlen -= 64;
+ }
+
+ if (fprintf(fp, PEMFOOTER) < 0)
+ goto end_fwrite_fp;
+ }
+ fclose(fp);
+
+ //Logging certificate informations
+ memcpy(filename + (strlen(filename) - 3), "meta", 4);
+ fpmeta = fopen(filename, "w");
+ if (fpmeta != NULL) {
+ #define PRINT_BUF_LEN 46
+ char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN];
+ char timebuf[64];
+ Port sp, dp;
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+ if (!TLSGetIPInformations(p, srcip, PRINT_BUF_LEN, &sp, dstip, PRINT_BUF_LEN, &dp, ipproto))
+ goto end_fwrite_fpmeta;
+ if (fprintf(fpmeta, "TIME: %s\n", timebuf) < 0)
+ goto end_fwrite_fpmeta;
+ if (p->pcap_cnt > 0) {
+ if (fprintf(fpmeta, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt) < 0)
+ goto end_fwrite_fpmeta;
+ }
+ if (fprintf(fpmeta, "SRC IP: %s\n", srcip) < 0)
+ goto end_fwrite_fpmeta;
+ if (fprintf(fpmeta, "DST IP: %s\n", dstip) < 0)
+ goto end_fwrite_fpmeta;
+ if (fprintf(fpmeta, "PROTO: %" PRIu32 "\n", p->proto) < 0)
+ goto end_fwrite_fpmeta;
+ if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
+ if (fprintf(fpmeta, "SRC PORT: %" PRIu16 "\n", sp) < 0)
+ goto end_fwrite_fpmeta;
+ if (fprintf(fpmeta, "DST PORT: %" PRIu16 "\n", dp) < 0)
+ goto end_fwrite_fpmeta;
+ }
+
+ if (fprintf(fpmeta, "TLS SUBJECT: %s\n"
+ "TLS ISSUERDN: %s\n"
+ "TLS FINGERPRINT: %s\n",
+ state->server_connp.cert0_subject,
+ state->server_connp.cert0_issuerdn,
+ state->server_connp.cert0_fingerprint) < 0)
+ goto end_fwrite_fpmeta;
+
+ fclose(fpmeta);
+ } else {
+ if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
+ SCLogWarning(SC_ERR_FOPEN,
+ "Can't create meta file '%s' in '%s' directory",
+ filename, tls_logfile_base_dir);
+ logging_dir_not_writable++;
+ }
+ SCReturn;
+ }
+
+ /* Reset the store flag */
+ state->server_connp.cert_log_flag &= ~SSL_TLS_LOG_PEM;
+ SCReturn;
+
+end_fwrite_fp:
+ fclose(fp);
+ if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
+ SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate");
+ logging_dir_not_writable++;
+ }
+end_fwrite_fpmeta:
+ if (fpmeta) {
+ fclose(fpmeta);
+ if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
+ SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate metafile");
+ logging_dir_not_writable++;
+ }
+ }
+ SCReturn;
+end_fp:
+ fclose(fp);
+ SCReturn;
+}
+
+/** \internal
+ * \brief Condition function for TLS logger
+ * \retval bool true or false -- log now?
+ */
+static int LogTlsStoreCondition(ThreadVars *tv, const Packet *p)
+{
+ if (p->flow == NULL) {
+ return FALSE;
+ }
+
+ if (!(PKT_IS_TCP(p))) {
+ return FALSE;
+ }
+
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto dontlog;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, so no request logging");
+ goto dontlog;
+ }
+
+ /* we only log the state once if we don't have to write
+ * the cert due to tls.store keyword. */
+ if (!(ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) &&
+ (ssl_state->flags & SSL_AL_FLAG_STATE_STORED))
+ goto dontlog;
+
+ if (ssl_state->server_connp.cert0_issuerdn == NULL ||
+ ssl_state->server_connp.cert0_subject == NULL)
+ goto dontlog;
+
+ FLOWLOCK_UNLOCK(p->flow);
+ return TRUE;
+dontlog:
+ FLOWLOCK_UNLOCK(p->flow);
+ return FALSE;
+}
+
+static int LogTlsStoreLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)thread_data;
+ int ipproto = (PKT_IS_IPV4(p)) ? AF_INET : AF_INET6;
+ /* check if we have TLS state or not */
+ FLOWLOCK_WRLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto end;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (unlikely(ssl_state == NULL)) {
+ goto end;
+ }
+
+ if (ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) {
+ LogTlsLogPem(aft, p, ssl_state, ipproto);
+ }
+
+ /* we only store the state once */
+ ssl_state->flags |= SSL_AL_FLAG_STATE_STORED;
+end:
+ FLOWLOCK_UNLOCK(p->flow);
+ return 0;
+}
+
+static TmEcode LogTlsStoreLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogTlsStoreLogThread *aft = SCMalloc(sizeof(LogTlsStoreLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogTlsStoreLogThread));
+
+ if (initdata == NULL) {
+ SCLogDebug("Error getting context for LogTlsStore. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ struct stat stat_buf;
+ if (stat(tls_logfile_base_dir, &stat_buf) != 0) {
+ int ret;
+ ret = mkdir(tls_logfile_base_dir, S_IRWXU|S_IXGRP|S_IRGRP);
+ if (ret != 0) {
+ int err = errno;
+ if (err != EEXIST) {
+ SCLogError(SC_ERR_LOGDIR_CONFIG,
+ "Cannot create certs drop directory %s: %s",
+ tls_logfile_base_dir, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ SCLogInfo("Created certs drop directory %s",
+ tls_logfile_base_dir);
+ }
+
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode LogTlsStoreLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* clear memory */
+ memset(aft, 0, sizeof(LogTlsStoreLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void LogTlsStoreLogExitPrintStats(ThreadVars *tv, void *data)
+{
+ LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("(%s) certificates extracted %" PRIu32 "", tv->name, aft->tls_cnt);
+}
+
+/**
+ * \internal
+ *
+ * \brief deinit the log ctx and write out the waldo
+ *
+ * \param output_ctx output context to deinit
+ */
+static void LogTlsStoreLogDeInitCtx(OutputCtx *output_ctx)
+{
+ SCFree(output_ctx);
+}
+
+/** \brief Create a new http log LogFilestoreCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *LogTlsStoreLogInitCtx(ConfNode *conf)
+{
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+
+ output_ctx->data = NULL;
+ output_ctx->DeInit = LogTlsStoreLogDeInitCtx;
+
+ /* FIXME we need to implement backward compability here */
+ char *s_default_log_dir = NULL;
+ s_default_log_dir = ConfigGetLogDirectory();
+
+ const char *s_base_dir = NULL;
+ s_base_dir = ConfNodeLookupChildValue(conf, "certs-log-dir");
+ if (s_base_dir == NULL || strlen(s_base_dir) == 0) {
+ strlcpy(tls_logfile_base_dir,
+ s_default_log_dir, sizeof(tls_logfile_base_dir));
+ } else {
+ if (PathIsAbsolute(s_base_dir)) {
+ strlcpy(tls_logfile_base_dir,
+ s_base_dir, sizeof(tls_logfile_base_dir));
+ } else {
+ snprintf(tls_logfile_base_dir, sizeof(tls_logfile_base_dir),
+ "%s/%s", s_default_log_dir, s_base_dir);
+ }
+ }
+
+ SCLogInfo("storing certs in %s", tls_logfile_base_dir);
+
+ SCReturnPtr(output_ctx, "OutputCtx");
+}
+
+void TmModuleLogTlsStoreRegister (void)
+{
+ tmm_modules[TMM_TLSSTORE].name = MODULE_NAME;
+ tmm_modules[TMM_TLSSTORE].ThreadInit = LogTlsStoreLogThreadInit;
+ tmm_modules[TMM_TLSSTORE].Func = NULL;
+ tmm_modules[TMM_TLSSTORE].ThreadExitPrintStats = LogTlsStoreLogExitPrintStats;
+ tmm_modules[TMM_TLSSTORE].ThreadDeinit = LogTlsStoreLogThreadDeinit;
+ tmm_modules[TMM_TLSSTORE].RegisterTests = NULL;
+ tmm_modules[TMM_TLSSTORE].cap_flags = 0;
+ tmm_modules[TMM_TLSSTORE].flags = TM_FLAG_LOGAPI_TM;
+ tmm_modules[TMM_TLSSTORE].priority = 10;
+
+ OutputRegisterPacketModule(MODULE_NAME, "tls-store", LogTlsStoreLogInitCtx,
+ LogTlsStoreLogger, LogTlsStoreCondition);
+
+ SC_ATOMIC_INIT(cert_id);
+
+ SCLogDebug("registered");
+}
diff --git a/framework/src/suricata/src/log-tlsstore.h b/framework/src/suricata/src/log-tlsstore.h
new file mode 100644
index 00000000..6cbacab6
--- /dev/null
+++ b/framework/src/suricata/src/log-tlsstore.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __LOG_TLSSTORE_H__
+#define __LOG_TLSSTORE_H__
+
+void TmModuleLogTlsStoreRegister (void);
+
+#endif /* __LOG_TLSSTORE_H__ */
diff --git a/framework/src/suricata/src/output-file.c b/framework/src/suricata/src/output-file.c
new file mode 100644
index 00000000..0f1e8521
--- /dev/null
+++ b/framework/src/suricata/src/output-file.c
@@ -0,0 +1,298 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer File Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-file.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "detect-filemagic.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputFileLogger_ {
+ FileLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputFileLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputFileLogger;
+
+static OutputFileLogger *list = NULL;
+
+int OutputRegisterFileLogger(const char *name, FileLogger LogFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputFileLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputFileLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterTxLogger happy");
+ return 0;
+}
+
+static TmEcode OutputFileLog(ThreadVars *tv, Packet *p, void *thread_data, PacketQueue *pq, PacketQueue *postpq)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputFileLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ uint8_t flags = 0;
+ Flow * const f = p->flow;
+
+ /* no flow, no files */
+ if (f == NULL) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT)
+ flags |= STREAM_TOCLIENT;
+ else
+ flags |= STREAM_TOSERVER;
+
+ int file_close = (p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0;
+ int file_trunc = 0;
+
+ FLOWLOCK_WRLOCK(f); // < need write lock for FilePrune below
+ file_trunc = StreamTcpReassembleDepthReached(p);
+
+ FileContainer *ffc = AppLayerParserGetFiles(p->proto, f->alproto,
+ f->alstate, flags);
+ SCLogDebug("ffc %p", ffc);
+ if (ffc != NULL) {
+ File *ff;
+ for (ff = ffc->head; ff != NULL; ff = ff->next) {
+ if (ff->flags & FILE_LOGGED)
+ continue;
+
+ SCLogDebug("ff %p", ff);
+
+ if (file_trunc && ff->state < FILE_STATE_CLOSED)
+ ff->state = FILE_STATE_TRUNCATED;
+
+ if (file_close && ff->state < FILE_STATE_CLOSED)
+ ff->state = FILE_STATE_TRUNCATED;
+
+ if (ff->state == FILE_STATE_CLOSED ||
+ ff->state == FILE_STATE_TRUNCATED ||
+ ff->state == FILE_STATE_ERROR)
+ {
+ int file_logged = 0;
+
+ if (FileForceMagic() && ff->magic == NULL) {
+ FilemagicGlobalLookup(ff);
+ }
+
+ logger = list;
+ store = op_thread_data->store;
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ SCLogDebug("logger %p", logger);
+ PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, (const Packet *)p, (const File *)ff);
+ PACKET_PROFILING_TMM_END(p, logger->module_id);
+ file_logged = 1;
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ if (file_logged) {
+ ff->flags |= FILE_LOGGED;
+ }
+ }
+ }
+
+ FilePrune(ffc);
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the tx logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputFileLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputFileLogThreadInit happy (*data %p)", *data);
+
+ OutputFileLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputFileLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFileLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+ logger = logger->next;
+ }
+ return TM_ECODE_OK;
+}
+
+static void OutputFileLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFileLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModuleFileLoggerRegister (void)
+{
+ tmm_modules[TMM_FILELOGGER].name = "__file_logger__";
+ tmm_modules[TMM_FILELOGGER].ThreadInit = OutputFileLogThreadInit;
+ tmm_modules[TMM_FILELOGGER].Func = OutputFileLog;
+ tmm_modules[TMM_FILELOGGER].ThreadExitPrintStats = OutputFileLogExitPrintStats;
+ tmm_modules[TMM_FILELOGGER].ThreadDeinit = OutputFileLogThreadDeinit;
+ tmm_modules[TMM_FILELOGGER].cap_flags = 0;
+}
+
+void OutputFileShutdown(void)
+{
+ OutputFileLogger *logger = list;
+ while (logger) {
+ OutputFileLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-file.h b/framework/src/suricata/src/output-file.h
new file mode 100644
index 00000000..8b203e26
--- /dev/null
+++ b/framework/src/suricata/src/output-file.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer File Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_FILE_H__
+#define __OUTPUT_FILE_H__
+
+#include "decode.h"
+#include "util-file.h"
+
+/** packet logger function pointer type */
+typedef int (*FileLogger)(ThreadVars *, void *thread_data, const Packet *, const File *);
+
+/** packet logger condition function pointer type,
+ * must return true for packets that should be logged
+ */
+//typedef int (*TxLogCondition)(ThreadVars *, const Packet *);
+
+int OutputRegisterFileLogger(const char *name, FileLogger LogFunc, OutputCtx *);
+
+void TmModuleFileLoggerRegister (void);
+
+void OutputFileShutdown(void);
+
+#endif /* __OUTPUT_FILE_H__ */
diff --git a/framework/src/suricata/src/output-filedata.c b/framework/src/suricata/src/output-filedata.c
new file mode 100644
index 00000000..8f466baf
--- /dev/null
+++ b/framework/src/suricata/src/output-filedata.c
@@ -0,0 +1,485 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer Filedata Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-filedata.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "detect-filemagic.h"
+#include "conf.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputFiledataLogger_ {
+ FiledataLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputFiledataLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputFiledataLogger;
+
+static OutputFiledataLogger *list = NULL;
+static char g_waldo[PATH_MAX] = "";
+static SCMutex g_waldo_mutex = SCMUTEX_INITIALIZER;
+static int g_waldo_init = 0;
+static int g_waldo_deinit = 0;
+
+int OutputRegisterFiledataLogger(const char *name, FiledataLogger LogFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputFiledataLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputFiledataLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterTxLogger happy");
+ return 0;
+}
+
+SC_ATOMIC_DECLARE(unsigned int, file_id);
+
+static int CallLoggers(ThreadVars *tv, OutputLoggerThreadStore *store_list,
+ Packet *p, const File *ff, const FileData *ffd, uint8_t flags)
+{
+ OutputFiledataLogger *logger = list;
+ OutputLoggerThreadStore *store = store_list;
+ int file_logged = 0;
+
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ SCLogDebug("logger %p", logger);
+ PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, (const Packet *)p, ff, ffd, flags);
+ PACKET_PROFILING_TMM_END(p, logger->module_id);
+
+ file_logged = 1;
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+ return file_logged;
+}
+
+static TmEcode OutputFiledataLog(ThreadVars *tv, Packet *p, void *thread_data, PacketQueue *pq, PacketQueue *postpq)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputFiledataLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ uint8_t flags = 0;
+ Flow * const f = p->flow;
+
+ /* no flow, no files */
+ if (f == NULL) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT)
+ flags |= STREAM_TOCLIENT;
+ else
+ flags |= STREAM_TOSERVER;
+
+ int file_close = (p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0;
+ int file_trunc = 0;
+
+ FLOWLOCK_WRLOCK(f); // < need write lock for FiledataPrune below
+ file_trunc = StreamTcpReassembleDepthReached(p);
+
+ FileContainer *ffc = AppLayerParserGetFiles(p->proto, f->alproto,
+ f->alstate, flags);
+ SCLogDebug("ffc %p", ffc);
+ if (ffc != NULL) {
+ File *ff;
+ for (ff = ffc->head; ff != NULL; ff = ff->next) {
+ if (FileForceMagic() && ff->magic == NULL) {
+ FilemagicGlobalLookup(ff);
+ }
+
+ SCLogDebug("ff %p", ff);
+ if (ff->flags & FILE_STORED) {
+ SCLogDebug("stored flag set");
+ continue;
+ }
+
+ if (!(ff->flags & FILE_STORE)) {
+ SCLogDebug("ff FILE_STORE not set");
+ continue;
+ }
+
+ /* if we have no data chunks left to log, we should still
+ * close the logger(s) */
+ if (ff->chunks_head == NULL && (file_trunc || file_close)) {
+ CallLoggers(tv, store, p, ff, NULL, OUTPUT_FILEDATA_FLAG_CLOSE);
+ ff->flags |= FILE_STORED;
+ continue;
+ }
+
+ FileData *ffd;
+ for (ffd = ff->chunks_head; ffd != NULL; ffd = ffd->next) {
+ uint8_t flags = 0;
+ int file_logged = 0;
+ FileData *write_ffd = ffd;
+
+ SCLogDebug("ffd %p", ffd);
+
+ /* special case: on stream end we may inform the
+ * loggers that the file is truncated. In this
+ * case we already logged the current ffd, which
+ * is the last in our list. */
+ if (ffd->stored == 1) {
+ if (!(file_close == 1 || ffd->next == NULL)) {
+ continue;
+ }
+
+ // call writer with NULL ffd, so it can 'close'
+ // so really a 'close' msg
+ write_ffd = NULL;
+ flags |= OUTPUT_FILEDATA_FLAG_CLOSE;
+ }
+
+ /* store */
+
+ /* if file_id == 0, this is the first store of this file */
+ if (ff->file_id == 0) {
+ /* new file */
+ ff->file_id = SC_ATOMIC_ADD(file_id, 1);
+ flags |= OUTPUT_FILEDATA_FLAG_OPEN;
+ } else {
+ /* existing file */
+ }
+
+ /* if file needs to be closed or truncated, inform
+ * loggers */
+ if ((file_close || file_trunc) && ff->state < FILE_STATE_CLOSED) {
+ ff->state = FILE_STATE_TRUNCATED;
+ }
+
+ /* for the last data chunk we have, also tell the logger
+ * we're closing up */
+ if (ffd->next == NULL && ff->state >= FILE_STATE_CLOSED)
+ flags |= OUTPUT_FILEDATA_FLAG_CLOSE;
+
+ /* do the actual logging */
+ file_logged = CallLoggers(tv, store, p, ff, write_ffd, flags);
+
+ if (file_logged) {
+ ffd->stored = 1;
+
+ /* all done */
+ if (flags & OUTPUT_FILEDATA_FLAG_CLOSE) {
+ ff->flags |= FILE_STORED;
+ break;
+ }
+ }
+ }
+ }
+
+ FilePrune(ffc);
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \internal
+ *
+ * \brief Open the waldo file (if available) and load the file_id
+ *
+ * \param path full path for the waldo file
+ */
+static void LogFiledataLogLoadWaldo(const char *path)
+{
+ char line[16] = "";
+ unsigned int id = 0;
+
+ FILE *fp = fopen(path, "r");
+ if (fp == NULL) {
+ SCLogInfo("couldn't open waldo: %s", strerror(errno));
+ SCReturn;
+ }
+
+ if (fgets(line, (int)sizeof(line), fp) != NULL) {
+ if (sscanf(line, "%10u", &id) == 1) {
+ SCLogInfo("id %u", id);
+ (void) SC_ATOMIC_CAS(&file_id, 0, id);
+ }
+ }
+ fclose(fp);
+}
+
+/**
+ * \internal
+ *
+ * \brief Store the waldo file based on the file_id
+ *
+ * \param path full path for the waldo file
+ */
+static void LogFiledataLogStoreWaldo(const char *path)
+{
+ char line[16] = "";
+
+ if (SC_ATOMIC_GET(file_id) == 0) {
+ SCReturn;
+ }
+
+ FILE *fp = fopen(path, "w");
+ if (fp == NULL) {
+ SCLogInfo("couldn't open waldo: %s", strerror(errno));
+ SCReturn;
+ }
+
+ snprintf(line, sizeof(line), "%u\n", SC_ATOMIC_GET(file_id));
+ if (fwrite(line, strlen(line), 1, fp) != 1) {
+ SCLogError(SC_ERR_FWRITE, "fwrite failed: %s", strerror(errno));
+ }
+ fclose(fp);
+}
+
+/** \brief thread init for the tx logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputFiledataLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputFiledataLogThreadInit happy (*data %p)", *data);
+
+ OutputFiledataLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ SCMutexLock(&g_waldo_mutex);
+ if (g_waldo_init == 0) {
+ ConfNode *node = ConfGetNode("file-store-waldo");
+ if (node == NULL) {
+ ConfNode *outputs = ConfGetNode("outputs");
+ if (outputs) {
+ ConfNode *output;
+ TAILQ_FOREACH(output, &outputs->head, next) {
+ /* we only care about file and file-store */
+ if (!(strcmp(output->val, "file") == 0 || strcmp(output->val, "file-store") == 0))
+ continue;
+
+ ConfNode *file = ConfNodeLookupChild(output, output->val);
+ BUG_ON(file == NULL);
+ if (file == NULL) {
+ SCLogDebug("file-store failed, lets try 'file'");
+ file = ConfNodeLookupChild(outputs, "file");
+ if (file == NULL)
+ SCLogDebug("file failed as well, giving up");
+ }
+
+ if (file != NULL) {
+ node = ConfNodeLookupChild(file, "waldo");
+ if (node == NULL)
+ SCLogDebug("no waldo node");
+ }
+ }
+ }
+ }
+ if (node != NULL) {
+ char *s_default_log_dir = NULL;
+ s_default_log_dir = ConfigGetLogDirectory();
+
+ const char *waldo = node->val;
+ SCLogDebug("loading waldo %s", waldo);
+ if (waldo != NULL && strlen(waldo) > 0) {
+ if (PathIsAbsolute(waldo)) {
+ snprintf(g_waldo, sizeof(g_waldo), "%s", waldo);
+ } else {
+ snprintf(g_waldo, sizeof(g_waldo), "%s/%s", s_default_log_dir, waldo);
+ }
+
+ SCLogDebug("loading waldo file %s", g_waldo);
+ LogFiledataLogLoadWaldo(g_waldo);
+ }
+ }
+ g_waldo_init = 1;
+ }
+ SCMutexUnlock(&g_waldo_mutex);
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputFiledataLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFiledataLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+ logger = logger->next;
+ }
+
+ SCMutexLock(&g_waldo_mutex);
+ if (g_waldo_deinit == 0) {
+ if (strlen(g_waldo) > 0) {
+ SCLogDebug("we have a waldo at %s", g_waldo);
+ LogFiledataLogStoreWaldo(g_waldo);
+ }
+ g_waldo_deinit = 1;
+ }
+ SCMutexUnlock(&g_waldo_mutex);
+ return TM_ECODE_OK;
+}
+
+static void OutputFiledataLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFiledataLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModuleFiledataLoggerRegister (void)
+{
+ tmm_modules[TMM_FILEDATALOGGER].name = "__filedata_logger__";
+ tmm_modules[TMM_FILEDATALOGGER].ThreadInit = OutputFiledataLogThreadInit;
+ tmm_modules[TMM_FILEDATALOGGER].Func = OutputFiledataLog;
+ tmm_modules[TMM_FILEDATALOGGER].ThreadExitPrintStats = OutputFiledataLogExitPrintStats;
+ tmm_modules[TMM_FILEDATALOGGER].ThreadDeinit = OutputFiledataLogThreadDeinit;
+ tmm_modules[TMM_FILEDATALOGGER].cap_flags = 0;
+
+ SC_ATOMIC_INIT(file_id);
+}
+
+void OutputFiledataShutdown(void)
+{
+ OutputFiledataLogger *logger = list;
+ while (logger) {
+ OutputFiledataLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-filedata.h b/framework/src/suricata/src/output-filedata.h
new file mode 100644
index 00000000..8900cd48
--- /dev/null
+++ b/framework/src/suricata/src/output-filedata.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer Filedata Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_FILEDATA_H__
+#define __OUTPUT_FILEDATA_H__
+
+#include "decode.h"
+#include "util-file.h"
+
+#define OUTPUT_FILEDATA_FLAG_OPEN 0x01
+#define OUTPUT_FILEDATA_FLAG_CLOSE 0x02
+
+/** filedata logger function pointer type */
+typedef int (*FiledataLogger)(ThreadVars *, void *thread_data, const Packet *,
+ const File *, const FileData *, uint8_t);
+
+/** packet logger condition function pointer type,
+ * must return true for packets that should be logged
+ */
+//typedef int (*TxLogCondition)(ThreadVars *, const Packet *);
+
+int OutputRegisterFiledataLogger(const char *name, FiledataLogger LogFunc, OutputCtx *);
+
+void TmModuleFiledataLoggerRegister (void);
+
+void OutputFiledataShutdown(void);
+
+#endif /* __OUTPUT_FILE_H__ */
diff --git a/framework/src/suricata/src/output-flow.c b/framework/src/suricata/src/output-flow.c
new file mode 100644
index 00000000..20d5d7d4
--- /dev/null
+++ b/framework/src/suricata/src/output-flow.c
@@ -0,0 +1,235 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-flow.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputFlowLogger_ {
+ FlowLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputFlowLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputFlowLogger;
+
+static OutputFlowLogger *list = NULL;
+
+int OutputRegisterFlowLogger(const char *name, FlowLogger LogFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputFlowLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputFlowLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterFlowLogger happy");
+ return 0;
+}
+
+/** \brief Run flow logger(s)
+ * \note flow is already write locked
+ */
+TmEcode OutputFlowLog(ThreadVars *tv, void *thread_data, Flow *f)
+{
+ BUG_ON(thread_data == NULL);
+
+ if (list == NULL)
+ return TM_ECODE_OK;
+ //BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputFlowLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ logger = list;
+ store = op_thread_data->store;
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ SCLogDebug("logger %p", logger);
+ //PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, f);
+ //PACKET_PROFILING_TMM_END(p, logger->module_id);
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the flow logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+TmEcode OutputFlowLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputFlowLogThreadInit happy (*data %p)", *data);
+
+ OutputFlowLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode OutputFlowLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFlowLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+ logger = logger->next;
+ }
+
+ SCFree(op_thread_data);
+ return TM_ECODE_OK;
+}
+
+void OutputFlowLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputFlowLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void OutputFlowShutdown(void)
+{
+ OutputFlowLogger *logger = list;
+ while (logger) {
+ OutputFlowLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-flow.h b/framework/src/suricata/src/output-flow.h
new file mode 100644
index 00000000..af093e3e
--- /dev/null
+++ b/framework/src/suricata/src/output-flow.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Flow Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_FLOW_H__
+#define __OUTPUT_FLOW_H__
+
+#include "decode.h"
+
+/** flow logger function pointer type */
+typedef int (*FlowLogger)(ThreadVars *, void *thread_data, Flow *f);
+
+/** packet logger condition function pointer type,
+ * must return true for packets that should be logged
+ */
+//typedef int (*TxLogCondition)(ThreadVars *, const Packet *);
+
+int OutputRegisterFlowLogger(const char *name, FlowLogger LogFunc, OutputCtx *);
+
+void OutputFlowShutdown(void);
+
+
+TmEcode OutputFlowLog(ThreadVars *tv, void *thread_data, Flow *f);
+TmEcode OutputFlowLogThreadInit(ThreadVars *tv, void *initdata, void **data);
+TmEcode OutputFlowLogThreadDeinit(ThreadVars *tv, void *thread_data);
+void OutputFlowLogExitPrintStats(ThreadVars *tv, void *thread_data);
+
+
+#endif /* __OUTPUT_FLOW_H__ */
diff --git a/framework/src/suricata/src/output-json-alert.c b/framework/src/suricata/src/output-json-alert.c
new file mode 100644
index 00000000..3c4219b4
--- /dev/null
+++ b/framework/src/suricata/src/output-json-alert.c
@@ -0,0 +1,690 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Logs alerts in JSON format.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-xff.h"
+#include "util-classification-config.h"
+#include "util-syslog.h"
+
+#include "output.h"
+#include "output-json.h"
+#include "output-json-http.h"
+#include "output-json-tls.h"
+#include "output-json-ssh.h"
+
+#include "util-byte.h"
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-optimize.h"
+#include "util-buffer.h"
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#define MODULE_NAME "JsonAlertLog"
+
+#ifdef HAVE_LIBJANSSON
+
+#define LOG_JSON_PAYLOAD 1
+#define LOG_JSON_PACKET 2
+#define LOG_JSON_PAYLOAD_BASE64 4
+#define LOG_JSON_HTTP 8
+#define LOG_JSON_TLS 16
+#define LOG_JSON_SSH 32
+
+#define JSON_STREAM_BUFFER_SIZE 4096
+
+typedef struct AlertJsonOutputCtx_ {
+ LogFileCtx* file_ctx;
+ uint8_t flags;
+ HttpXFFCfg *xff_cfg;
+} AlertJsonOutputCtx;
+
+typedef struct JsonAlertLogThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx* file_ctx;
+ MemBuffer *json_buffer;
+ MemBuffer *payload_buffer;
+ AlertJsonOutputCtx* json_output_ctx;
+} JsonAlertLogThread;
+
+/* Callback function to pack payload contents from a stream into a buffer
+ * so we can report them in JSON output. */
+static int AlertJsonDumpStreamSegmentCallback(const Packet *p, void *data, uint8_t *buf, uint32_t buflen)
+{
+ MemBuffer *payload = (MemBuffer *)data;
+ MemBufferWriteRaw(payload, buf, buflen);
+
+ return 1;
+}
+
+/** Handle the case where no JSON support is compiled in.
+ *
+ */
+static void AlertJsonHttp(const Flow *f, json_t *js)
+{
+ HtpState *htp_state = (HtpState *)FlowGetAppState(f);
+ if (htp_state) {
+ uint64_t tx_id = AppLayerParserGetTransactionLogId(f->alparser);
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, tx_id);
+
+ if (tx) {
+ json_t *hjs = json_object();
+ if (unlikely(hjs == NULL))
+ return;
+
+ JsonHttpLogJSONBasic(hjs, tx);
+ JsonHttpLogJSONExtended(hjs, tx);
+
+ json_object_set_new(js, "http", hjs);
+ }
+ }
+
+ return;
+}
+
+static void AlertJsonTls(const Flow *f, json_t *js)
+{
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(f);
+ if (ssl_state) {
+ json_t *tjs = json_object();
+ if (unlikely(tjs == NULL))
+ return;
+
+ JsonTlsLogJSONBasic(tjs, ssl_state);
+ JsonTlsLogJSONExtended(tjs, ssl_state);
+
+ json_object_set_new(js, "tls", tjs);
+ }
+
+ return;
+}
+
+static void AlertJsonSsh(const Flow *f, json_t *js)
+{
+ SshState *ssh_state = (SshState *)FlowGetAppState(f);
+ if (ssh_state) {
+ json_t *tjs = json_object();
+ if (unlikely(tjs == NULL))
+ return;
+
+ JsonSshLogJSON(tjs, ssh_state);
+
+ json_object_set_new(js, "ssh", tjs);
+ }
+
+ return;
+}
+
+void AlertJsonHeader(const Packet *p, const PacketAlert *pa, json_t *js)
+{
+ char *action = "allowed";
+ if (pa->action & (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)) {
+ action = "blocked";
+ } else if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "blocked";
+ }
+
+ json_t *ajs = json_object();
+ if (ajs == NULL) {
+ json_decref(js);
+ return;
+ }
+
+ json_object_set_new(ajs, "action", json_string(action));
+ json_object_set_new(ajs, "gid", json_integer(pa->s->gid));
+ json_object_set_new(ajs, "signature_id", json_integer(pa->s->id));
+ json_object_set_new(ajs, "rev", json_integer(pa->s->rev));
+ json_object_set_new(ajs, "signature",
+ json_string((pa->s->msg) ? pa->s->msg : ""));
+ json_object_set_new(ajs, "category",
+ json_string((pa->s->class_msg) ? pa->s->class_msg : ""));
+ json_object_set_new(ajs, "severity", json_integer(pa->s->prio));
+
+ if (pa->flags & PACKET_ALERT_FLAG_TX)
+ json_object_set_new(ajs, "tx_id", json_integer(pa->tx_id));
+
+ if (p->tenant_id > 0)
+ json_object_set_new(ajs, "tenant_id", json_integer(p->tenant_id));
+
+ /* alert */
+ json_object_set_new(js, "alert", ajs);
+}
+
+static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
+{
+ MemBuffer *payload = aft->payload_buffer;
+ AlertJsonOutputCtx *json_output_ctx = aft->json_output_ctx;
+
+ int i;
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ json_t *js = CreateJSONHeader((Packet *)p, 0, "alert");
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ MemBufferReset(aft->json_buffer);
+
+ /* alert */
+ AlertJsonHeader(p, pa, js);
+
+ if (json_output_ctx->flags & LOG_JSON_HTTP) {
+ if (p->flow != NULL) {
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+
+ /* http alert */
+ if (proto == ALPROTO_HTTP)
+ AlertJsonHttp(p->flow, js);
+
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+
+ if (json_output_ctx->flags & LOG_JSON_TLS) {
+ if (p->flow != NULL) {
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+
+ /* http alert */
+ if (proto == ALPROTO_TLS)
+ AlertJsonTls(p->flow, js);
+
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+
+ if (json_output_ctx->flags & LOG_JSON_SSH) {
+ if (p->flow != NULL) {
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+
+ /* http alert */
+ if (proto == ALPROTO_SSH)
+ AlertJsonSsh(p->flow, js);
+
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ }
+
+ /* payload */
+ if (json_output_ctx->flags & (LOG_JSON_PAYLOAD | LOG_JSON_PAYLOAD_BASE64)) {
+ int stream = (p->proto == IPPROTO_TCP) ?
+ (pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH) ?
+ 1 : 0) : 0;
+
+ /* Is this a stream? If so, pack part of it into the payload field */
+ if (stream) {
+ uint8_t flag;
+
+ MemBufferReset(payload);
+
+ if (!EngineModeIsIPS()) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag = FLOW_PKT_TOCLIENT;
+ } else {
+ flag = FLOW_PKT_TOSERVER;
+ }
+ } else {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag = FLOW_PKT_TOSERVER;
+ } else {
+ flag = FLOW_PKT_TOCLIENT;
+ }
+ }
+
+ StreamSegmentForEach((const Packet *)p, flag,
+ AlertJsonDumpStreamSegmentCallback,
+ (void *)payload);
+
+ if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) {
+ unsigned long len = JSON_STREAM_BUFFER_SIZE * 2;
+ uint8_t encoded[len];
+ Base64Encode(payload->buffer, payload->offset, encoded, &len);
+ json_object_set_new(js, "payload", json_string((char *)encoded));
+ }
+
+ if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
+ uint8_t printable_buf[payload->offset + 1];
+ uint32_t offset = 0;
+ PrintStringsToBuffer(printable_buf, &offset,
+ sizeof(printable_buf),
+ payload->buffer, payload->offset);
+ json_object_set_new(js, "payload_printable",
+ json_string((char *)printable_buf));
+ }
+ } else {
+ /* This is a single packet and not a stream */
+ if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) {
+ unsigned long len = p->payload_len * 2 + 1;
+ uint8_t encoded[len];
+ Base64Encode(p->payload, p->payload_len, encoded, &len);
+ json_object_set_new(js, "payload", json_string((char *)encoded));
+ }
+
+ if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
+ uint8_t printable_buf[p->payload_len + 1];
+ uint32_t offset = 0;
+ PrintStringsToBuffer(printable_buf, &offset,
+ p->payload_len + 1,
+ p->payload, p->payload_len);
+ json_object_set_new(js, "payload_printable", json_string((char *)printable_buf));
+ }
+ }
+
+ json_object_set_new(js, "stream", json_integer(stream));
+ }
+
+ /* base64-encoded full packet */
+ if (json_output_ctx->flags & LOG_JSON_PACKET) {
+ unsigned long len = GET_PKT_LEN(p) * 2;
+ uint8_t encoded_packet[len];
+ Base64Encode((unsigned char*) GET_PKT_DATA(p), GET_PKT_LEN(p), encoded_packet, &len);
+ json_object_set_new(js, "packet", json_string((char *)encoded_packet));
+ }
+
+ HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg;
+
+ /* xff header */
+ if (!(xff_cfg->flags & XFF_DISABLED) && p->flow != NULL) {
+ int have_xff_ip = 0;
+ char buffer[XFF_MAXLEN];
+
+ FLOWLOCK_RDLOCK(p->flow);
+ if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
+ if (pa->flags & PACKET_ALERT_FLAG_TX) {
+ have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg, buffer, XFF_MAXLEN);
+ } else {
+ have_xff_ip = HttpXFFGetIP(p, xff_cfg, buffer, XFF_MAXLEN);
+ }
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ if (have_xff_ip) {
+ if (xff_cfg->flags & XFF_EXTRADATA) {
+ json_object_set_new(js, "xff", json_string(buffer));
+ }
+ else if (xff_cfg->flags & XFF_OVERWRITE) {
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ json_object_set(js, "dest_ip", json_string(buffer));
+ } else {
+ json_object_set(js, "src_ip", json_string(buffer));
+ }
+ }
+ }
+ }
+
+ OutputJSONBuffer(js, aft->file_ctx, aft->json_buffer);
+ json_object_del(js, "alert");
+ }
+ json_object_clear(js);
+ json_decref(js);
+
+ return TM_ECODE_OK;
+}
+
+static int AlertJsonDecoderEvent(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->json_buffer;
+ int i;
+ char timebuf[64];
+ json_t *js;
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ CreateIsoTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ MemBufferReset(buffer);
+
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ char *action = "allowed";
+ if (pa->action & (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)) {
+ action = "blocked";
+ } else if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
+ action = "blocked";
+ }
+
+ char buf[(32 * 3) + 1];
+ PrintRawLineHexBuf(buf, sizeof(buf), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
+
+ js = json_object();
+ if (js == NULL)
+ return TM_ECODE_OK;
+
+ json_t *ajs = json_object();
+ if (ajs == NULL) {
+ json_decref(js);
+ return TM_ECODE_OK;
+ }
+
+ /* time & tx */
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ /* tuple */
+ //json_object_set_new(js, "srcip", json_string(srcip));
+ //json_object_set_new(js, "sp", json_integer(p->sp));
+ //json_object_set_new(js, "dstip", json_string(dstip));
+ //json_object_set_new(js, "dp", json_integer(p->dp));
+ //json_object_set_new(js, "proto", json_integer(proto));
+
+ json_object_set_new(ajs, "action", json_string(action));
+ json_object_set_new(ajs, "gid", json_integer(pa->s->gid));
+ json_object_set_new(ajs, "signature_id", json_integer(pa->s->id));
+ json_object_set_new(ajs, "rev", json_integer(pa->s->rev));
+ json_object_set_new(ajs, "signature",
+ json_string((pa->s->msg) ? pa->s->msg : ""));
+ json_object_set_new(ajs, "category",
+ json_string((pa->s->class_msg) ? pa->s->class_msg : ""));
+ json_object_set_new(ajs, "severity", json_integer(pa->s->prio));
+
+ if (p->tenant_id > 0)
+ json_object_set_new(ajs, "tenant_id", json_integer(p->tenant_id));
+
+ /* alert */
+ json_object_set_new(js, "alert", ajs);
+ OutputJSONBuffer(js, aft->file_ctx, buffer);
+ json_object_clear(js);
+ json_decref(js);
+ }
+
+ return TM_ECODE_OK;
+}
+
+static int JsonAlertLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ JsonAlertLogThread *aft = thread_data;
+
+ if (PKT_IS_IPV4(p) || PKT_IS_IPV6(p)) {
+ return AlertJson(tv, aft, p);
+ } else if (p->alerts.cnt > 0) {
+ return AlertJsonDecoderEvent(tv, aft, p);
+ }
+ return 0;
+}
+
+static int JsonAlertLogCondition(ThreadVars *tv, const Packet *p)
+{
+ return (p->alerts.cnt ? TRUE : FALSE);
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonAlertLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonAlertLogThread *aft = SCMalloc(sizeof(JsonAlertLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonAlertLogThread));
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for AlertFastLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->json_buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->json_buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->payload_buffer = MemBufferCreateNew(JSON_STREAM_BUFFER_SIZE);
+ if (aft->payload_buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /** Use the Output Context (file pointer and mutex) */
+ AlertJsonOutputCtx *json_output_ctx = ((OutputCtx *)initdata)->data;
+ aft->file_ctx = json_output_ctx->file_ctx;
+ aft->json_output_ctx = json_output_ctx;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonAlertLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonAlertLogThread *aft = (JsonAlertLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->json_buffer);
+ MemBufferFree(aft->payload_buffer);
+
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonAlertLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void JsonAlertLogDeInitCtx(OutputCtx *output_ctx)
+{
+ SCLogDebug("cleaning up output_ctx");
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(output_ctx);
+}
+
+static void JsonAlertLogDeInitCtxSub(OutputCtx *output_ctx)
+{
+ SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
+
+ AlertJsonOutputCtx *json_output_ctx = (AlertJsonOutputCtx *) output_ctx->data;
+
+ if (json_output_ctx != NULL) {
+ HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg;
+ if (xff_cfg != NULL) {
+ SCFree(xff_cfg);
+ }
+
+ SCFree(json_output_ctx);
+ }
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "alert.json"
+
+/**
+ * \brief Create a new LogFileCtx for "fast" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+static OutputCtx *JsonAlertLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("AlertFastLogInitCtx2: Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = JsonAlertLogDeInitCtx;
+
+ return output_ctx;
+}
+
+/**
+ * \brief Create a new LogFileCtx for "fast" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+static OutputCtx *JsonAlertLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ajt = parent_ctx->data;
+ AlertJsonOutputCtx *json_output_ctx = NULL;
+ HttpXFFCfg *xff_cfg = NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+
+ json_output_ctx = SCMalloc(sizeof(AlertJsonOutputCtx));
+ if (unlikely(json_output_ctx == NULL)) {
+ goto error;
+ }
+ memset(json_output_ctx, 0, sizeof(AlertJsonOutputCtx));
+
+ xff_cfg = SCMalloc(sizeof(HttpXFFCfg));
+ if (unlikely(xff_cfg == NULL)) {
+ goto error;
+ }
+ memset(xff_cfg, 0, sizeof(HttpXFFCfg));
+
+ json_output_ctx->file_ctx = ajt->file_ctx;
+ json_output_ctx->xff_cfg = xff_cfg;
+
+ if (conf != NULL) {
+ const char *payload = ConfNodeLookupChildValue(conf, "payload");
+ const char *packet = ConfNodeLookupChildValue(conf, "packet");
+ const char *payload_printable = ConfNodeLookupChildValue(conf, "payload-printable");
+ const char *http = ConfNodeLookupChildValue(conf, "http");
+ const char *tls = ConfNodeLookupChildValue(conf, "tls");
+ const char *ssh = ConfNodeLookupChildValue(conf, "ssh");
+
+ if (ssh != NULL) {
+ if (ConfValIsTrue(ssh)) {
+ json_output_ctx->flags |= LOG_JSON_SSH;
+ }
+ }
+ if (tls != NULL) {
+ if (ConfValIsTrue(tls)) {
+ json_output_ctx->flags |= LOG_JSON_TLS;
+ }
+ }
+ if (http != NULL) {
+ if (ConfValIsTrue(http)) {
+ json_output_ctx->flags |= LOG_JSON_HTTP;
+ }
+ }
+ if (payload_printable != NULL) {
+ if (ConfValIsTrue(payload_printable)) {
+ json_output_ctx->flags |= LOG_JSON_PAYLOAD;
+ }
+ }
+ if (payload != NULL) {
+ if (ConfValIsTrue(payload)) {
+ json_output_ctx->flags |= LOG_JSON_PAYLOAD_BASE64;
+ }
+ }
+ if (packet != NULL) {
+ if (ConfValIsTrue(packet)) {
+ json_output_ctx->flags |= LOG_JSON_PACKET;
+ }
+ }
+
+ HttpXFFGetCfg(conf, xff_cfg);
+ }
+
+ output_ctx->data = json_output_ctx;
+ output_ctx->DeInit = JsonAlertLogDeInitCtxSub;
+
+ return output_ctx;
+
+error:
+ if (json_output_ctx != NULL) {
+ SCFree(json_output_ctx);
+ }
+ if (output_ctx != NULL) {
+ SCFree(output_ctx);
+ }
+
+ return NULL;
+}
+
+void TmModuleJsonAlertLogRegister (void)
+{
+ tmm_modules[TMM_JSONALERTLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONALERTLOG].ThreadInit = JsonAlertLogThreadInit;
+ tmm_modules[TMM_JSONALERTLOG].ThreadDeinit = JsonAlertLogThreadDeinit;
+ tmm_modules[TMM_JSONALERTLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONALERTLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "alert-json-log",
+ JsonAlertLogInitCtx, JsonAlertLogger, JsonAlertLogCondition);
+ OutputRegisterPacketSubModule("eve-log", MODULE_NAME, "eve-log.alert",
+ JsonAlertLogInitCtxSub, JsonAlertLogger, JsonAlertLogCondition);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonAlertLogRegister (void)
+{
+ tmm_modules[TMM_JSONALERTLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONALERTLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
+
diff --git a/framework/src/suricata/src/output-json-alert.h b/framework/src/suricata/src/output-json-alert.h
new file mode 100644
index 00000000..a10a316d
--- /dev/null
+++ b/framework/src/suricata/src/output-json-alert.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Logs alerts in JSON format.
+ *
+ */
+
+#ifndef __OUTPUT_JSON_ALERT_H__
+#define __OUTPUT_JSON_ALERT_H__
+
+void TmModuleJsonAlertLogRegister (void);
+#ifdef HAVE_LIBJANSSON
+void AlertJsonHeader(const Packet *p, const PacketAlert *pa, json_t *js);
+#endif /* HAVE_LIBJANSSON */
+
+#endif /* __OUTPUT_JSON_ALERT_H__ */
+
diff --git a/framework/src/suricata/src/output-json-dns.c b/framework/src/suricata/src/output-json-dns.c
new file mode 100644
index 00000000..89c11b3e
--- /dev/null
+++ b/framework/src/suricata/src/output-json-dns.c
@@ -0,0 +1,448 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements JSON DNS logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "util-mem.h"
+#include "app-layer-parser.h"
+#include "output.h"
+#include "app-layer-dns-udp.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#include "output-json.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+/* we can do query logging as well, but it's disabled for now as the
+ * TX id handling doesn't expect it */
+#define QUERY 0
+
+typedef struct LogDnsFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} LogDnsFileCtx;
+
+typedef struct LogDnsLogThread_ {
+ LogDnsFileCtx *dnslog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t dns_cnt;
+
+ MemBuffer *buffer;
+} LogDnsLogThread;
+
+static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
+ uint64_t tx_id, DNSQueryEntry *entry)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+
+ SCLogDebug("got a DNS request and now logging !!");
+
+ json_t *djs = json_object();
+ if (djs == NULL) {
+ return;
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ /* type */
+ json_object_set_new(djs, "type", json_string("query"));
+
+ /* id */
+ json_object_set_new(djs, "id", json_integer(tx->tx_id));
+
+ /* query */
+ char *c;
+ c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)), entry->len);
+ if (c != NULL) {
+ json_object_set_new(djs, "rrname", json_string(c));
+ SCFree(c);
+ }
+
+ /* name */
+ char record[16] = "";
+ DNSCreateTypeString(entry->type, record, sizeof(record));
+ json_object_set_new(djs, "rrtype", json_string(record));
+
+ /* tx id (tx counter) */
+ json_object_set_new(djs, "tx_id", json_integer(tx_id));
+
+ /* dns */
+ json_object_set_new(js, "dns", djs);
+ OutputJSONBuffer(js, aft->dnslog_ctx->file_ctx, buffer);
+ json_object_del(js, "dns");
+}
+
+static void OutputAnswer(LogDnsLogThread *aft, json_t *djs, DNSTransaction *tx, DNSAnswerEntry *entry)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ json_t *js = json_object();
+ if (js == NULL)
+ return;
+
+ /* type */
+ json_object_set_new(js, "type", json_string("answer"));
+
+ /* id */
+ json_object_set_new(js, "id", json_integer(tx->tx_id));
+
+ /* rcode */
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ json_object_set_new(js, "rcode", json_string(rcode));
+
+ /* we are logging an answer RR */
+ if (entry != NULL) {
+ /* query */
+ if (entry->fqdn_len > 0) {
+ char *c;
+ c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)),
+ entry->fqdn_len);
+ if (c != NULL) {
+ json_object_set_new(js, "rrname", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* name */
+ char record[16] = "";
+ DNSCreateTypeString(entry->type, record, sizeof(record));
+ json_object_set_new(js, "rrtype", json_string(record));
+
+ /* ttl */
+ json_object_set_new(js, "ttl", json_integer(entry->ttl));
+
+ uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)+ entry->fqdn_len);
+ if (entry->type == DNS_RECORD_TYPE_A) {
+ char a[16] = "";
+ PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
+ json_object_set_new(js, "rdata", json_string(a));
+ } else if (entry->type == DNS_RECORD_TYPE_AAAA) {
+ char a[46] = "";
+ PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
+ json_object_set_new(js, "rdata", json_string(a));
+ } else if (entry->data_len == 0) {
+ json_object_set_new(js, "rdata", json_string(""));
+ } else if (entry->type == DNS_RECORD_TYPE_TXT || entry->type == DNS_RECORD_TYPE_CNAME ||
+ entry->type == DNS_RECORD_TYPE_MX || entry->type == DNS_RECORD_TYPE_PTR) {
+ if (entry->data_len != 0) {
+ char buffer[256] = "";
+ uint16_t copy_len = entry->data_len < (sizeof(buffer) - 1) ?
+ entry->data_len : sizeof(buffer) - 1;
+ memcpy(buffer, ptr, copy_len);
+ buffer[copy_len] = '\0';
+ json_object_set_new(js, "rdata", json_string(buffer));
+ } else {
+ json_object_set_new(js, "rdata", json_string(""));
+ }
+ }
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+ json_object_set_new(djs, "dns", js);
+ OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, buffer);
+ json_object_del(djs, "dns");
+
+ return;
+}
+
+static void OutputFailure(LogDnsLogThread *aft, json_t *djs, DNSTransaction *tx, DNSQueryEntry *entry)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ json_t *js = json_object();
+ if (js == NULL)
+ return;
+
+ /* type */
+ json_object_set_new(js, "type", json_string("answer"));
+
+ /* id */
+ json_object_set_new(js, "id", json_integer(tx->tx_id));
+
+ /* rcode */
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ json_object_set_new(js, "rcode", json_string(rcode));
+
+ /* no answer RRs, use query for rname */
+ char *c;
+ c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)), entry->len);
+ if (c != NULL) {
+ json_object_set_new(js, "rrname", json_string(c));
+ SCFree(c);
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+ json_object_set_new(djs, "dns", js);
+ OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, buffer);
+ json_object_del(djs, "dns");
+
+ return;
+}
+
+static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
+{
+
+ SCLogDebug("got a DNS response and now logging !!");
+
+ /* rcode != noerror */
+ if (tx->rcode) {
+ /* Most DNS servers do not support multiple queries because
+ * the rcode in response is not per-query. Multiple queries
+ * are likely to lead to FORMERR, so log this. */
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ OutputFailure(aft, js, tx, query);
+ }
+ }
+
+ DNSAnswerEntry *entry = NULL;
+ TAILQ_FOREACH(entry, &tx->answer_list, next) {
+ OutputAnswer(aft, js, tx, entry);
+ }
+
+ entry = NULL;
+ TAILQ_FOREACH(entry, &tx->authority_list, next) {
+ OutputAnswer(aft, js, tx, entry);
+ }
+
+}
+
+static int JsonDnsLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
+{
+ SCEnter();
+
+ LogDnsLogThread *td = (LogDnsLogThread *)thread_data;
+ DNSTransaction *tx = txptr;
+ json_t *js;
+
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ js = CreateJSONHeader((Packet *)p, 1, "dns");
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ LogQuery(td, js, tx, tx_id, query);
+
+ json_decref(js);
+ }
+
+ js = CreateJSONHeader((Packet *)p, 0, "dns");
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ LogAnswers(td, js, tx, tx_id);
+
+ json_decref(js);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#define OUTPUT_BUFFER_SIZE 65536
+static TmEcode LogDnsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogDnsLogThread *aft = SCMalloc(sizeof(LogDnsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(LogDnsLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for DNSLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->dnslog_ctx= ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode LogDnsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogDnsLogThread *aft = (LogDnsLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(LogDnsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void LogDnsLogDeInitCtx(OutputCtx *output_ctx)
+{
+ LogDnsFileCtx *dnslog_ctx = (LogDnsFileCtx *)output_ctx->data;
+ LogFileFreeCtx(dnslog_ctx->file_ctx);
+ SCFree(dnslog_ctx);
+ SCFree(output_ctx);
+}
+
+static void LogDnsLogDeInitCtxSub(OutputCtx *output_ctx)
+{
+ SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
+ LogDnsFileCtx *dnslog_ctx = (LogDnsFileCtx *)output_ctx->data;
+ SCFree(dnslog_ctx);
+ SCFree(output_ctx);
+}
+
+static OutputCtx *JsonDnsLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ AlertJsonThread *ajt = parent_ctx->data;
+
+ LogDnsFileCtx *dnslog_ctx = SCMalloc(sizeof(LogDnsFileCtx));
+ if (unlikely(dnslog_ctx == NULL)) {
+ return NULL;
+ }
+ memset(dnslog_ctx, 0x00, sizeof(LogDnsFileCtx));
+
+ dnslog_ctx->file_ctx = ajt->file_ctx;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(dnslog_ctx);
+ return NULL;
+ }
+
+ output_ctx->data = dnslog_ctx;
+ output_ctx->DeInit = LogDnsLogDeInitCtxSub;
+
+ SCLogDebug("DNS log sub-module initialized");
+
+ AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS);
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS);
+
+ return output_ctx;
+}
+
+#define DEFAULT_LOG_FILENAME "dns.json"
+/** \brief Create a new dns log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+static OutputCtx *JsonDnsLogInitCtx(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = LogFileNewCtx();
+
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_DNS_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogDnsFileCtx *dnslog_ctx = SCMalloc(sizeof(LogDnsFileCtx));
+ if (unlikely(dnslog_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ memset(dnslog_ctx, 0x00, sizeof(LogDnsFileCtx));
+
+ dnslog_ctx->file_ctx = file_ctx;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(dnslog_ctx);
+ return NULL;
+ }
+
+ output_ctx->data = dnslog_ctx;
+ output_ctx->DeInit = LogDnsLogDeInitCtx;
+
+ SCLogDebug("DNS log output initialized");
+
+ AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS);
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS);
+
+ return output_ctx;
+}
+
+
+#define MODULE_NAME "JsonDnsLog"
+void TmModuleJsonDnsLogRegister (void)
+{
+ tmm_modules[TMM_JSONDNSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONDNSLOG].ThreadInit = LogDnsLogThreadInit;
+ tmm_modules[TMM_JSONDNSLOG].ThreadDeinit = LogDnsLogThreadDeinit;
+ tmm_modules[TMM_JSONDNSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONDNSLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONDNSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterTxModule(MODULE_NAME, "dns-json-log", JsonDnsLogInitCtx,
+ ALPROTO_DNS, JsonDnsLogger);
+ OutputRegisterTxSubModule("eve-log", MODULE_NAME, "eve-log.dns", JsonDnsLogInitCtxSub,
+ ALPROTO_DNS, JsonDnsLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonDnsLogRegister (void)
+{
+ tmm_modules[TMM_JSONDNSLOG].name = "JsonDnsLog";
+ tmm_modules[TMM_JSONDNSLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-dns.h b/framework/src/suricata/src/output-json-dns.h
new file mode 100644
index 00000000..f26227e3
--- /dev/null
+++ b/framework/src/suricata/src/output-json-dns.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_DNS_H__
+#define __OUTPUT_JSON_DNS_H__
+
+void TmModuleJsonDnsLogRegister (void);
+
+#endif /* __OUTPUT_JSON_DNS_H__ */
diff --git a/framework/src/suricata/src/output-json-drop.c b/framework/src/suricata/src/output-json-drop.c
new file mode 100644
index 00000000..c9b01df8
--- /dev/null
+++ b/framework/src/suricata/src/output-json-drop.c
@@ -0,0 +1,427 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * JSON Drop log module to log the dropped packet information
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "decode-ipv4.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+
+#include "output.h"
+#include "output-json.h"
+#include "output-json-alert.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-classification-config.h"
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+#include "util-buffer.h"
+
+#define MODULE_NAME "JsonDropLog"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+#define LOG_DROP_ALERTS 1
+
+typedef struct JsonDropOutputCtx_ {
+ LogFileCtx *file_ctx;
+ uint8_t flags;
+} JsonDropOutputCtx;
+
+typedef struct JsonDropLogThread_ {
+ JsonDropOutputCtx *drop_ctx;
+ MemBuffer *buffer;
+} JsonDropLogThread;
+
+/**
+ * \brief Log the dropped packets in netfilter format when engine is running
+ * in inline mode
+ *
+ * \param tv Pointer the current thread variables
+ * \param p Pointer the packet which is being logged
+ *
+ * \return return TM_EODE_OK on success
+ */
+static int DropLogJSON (JsonDropLogThread *aft, const Packet *p)
+{
+ uint16_t proto = 0;
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ json_t *js = CreateJSONHeader((Packet *)p, 0, "drop");//TODO const
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ json_t *djs = json_object();
+ if (unlikely(djs == NULL)) {
+ json_decref(js);
+ return TM_ECODE_OK;
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ if (PKT_IS_IPV4(p)) {
+ json_object_set_new(djs, "len", json_integer(IPV4_GET_IPLEN(p)));
+ json_object_set_new(djs, "tos", json_integer(IPV4_GET_IPTOS(p)));
+ json_object_set_new(djs, "ttl", json_integer(IPV4_GET_IPTTL(p)));
+ json_object_set_new(djs, "ipid", json_integer(IPV4_GET_IPID(p)));
+ proto = IPV4_GET_IPPROTO(p);
+ } else if (PKT_IS_IPV6(p)) {
+ json_object_set_new(djs, "len", json_integer(IPV6_GET_PLEN(p)));
+ json_object_set_new(djs, "tc", json_integer(IPV6_GET_CLASS(p)));
+ json_object_set_new(djs, "hoplimit", json_integer(IPV6_GET_HLIM(p)));
+ json_object_set_new(djs, "flowlbl", json_integer(IPV6_GET_FLOW(p)));
+ proto = IPV6_GET_L4PROTO(p);
+ }
+ switch (proto) {
+ case IPPROTO_TCP:
+ json_object_set_new(djs, "tcpseq", json_integer(TCP_GET_SEQ(p)));
+ json_object_set_new(djs, "tcpack", json_integer(TCP_GET_ACK(p)));
+ json_object_set_new(djs, "tcpwin", json_integer(TCP_GET_WINDOW(p)));
+ json_object_set_new(djs, "syn", TCP_ISSET_FLAG_SYN(p) ? json_true() : json_false());
+ json_object_set_new(djs, "ack", TCP_ISSET_FLAG_ACK(p) ? json_true() : json_false());
+ json_object_set_new(djs, "psh", TCP_ISSET_FLAG_PUSH(p) ? json_true() : json_false());
+ json_object_set_new(djs, "rst", TCP_ISSET_FLAG_RST(p) ? json_true() : json_false());
+ json_object_set_new(djs, "urg", TCP_ISSET_FLAG_URG(p) ? json_true() : json_false());
+ json_object_set_new(djs, "fin", TCP_ISSET_FLAG_FIN(p) ? json_true() : json_false());
+ json_object_set_new(djs, "tcpres", json_integer(TCP_GET_RAW_X2(p->tcph)));
+ json_object_set_new(djs, "tcpurgp", json_integer(TCP_GET_URG_POINTER(p)));
+ break;
+ case IPPROTO_UDP:
+ json_object_set_new(djs, "udplen", json_integer(UDP_GET_LEN(p)));
+ break;
+ case IPPROTO_ICMP:
+ if (PKT_IS_ICMPV4(p)) {
+ json_object_set_new(djs, "icmp_id", json_integer(ICMPV4_GET_ID(p)));
+ json_object_set_new(djs, "icmp_seq", json_integer(ICMPV4_GET_SEQ(p)));
+ } else if(PKT_IS_ICMPV6(p)) {
+ json_object_set_new(djs, "icmp_id", json_integer(ICMPV6_GET_ID(p)));
+ json_object_set_new(djs, "icmp_seq", json_integer(ICMPV6_GET_SEQ(p)));
+ }
+ break;
+ }
+ json_object_set_new(js, "drop", djs);
+
+ if (aft->drop_ctx->flags & LOG_DROP_ALERTS) {
+ int logged = 0;
+ int i;
+ for (i = 0; i < p->alerts.cnt; i++) {
+ const PacketAlert *pa = &p->alerts.alerts[i];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+ if ((pa->action & (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)) ||
+ ((pa->action & ACTION_DROP) && EngineModeIsIPS()))
+ {
+ AlertJsonHeader(p, pa, js);
+ logged = 1;
+ }
+ }
+ if (logged == 0) {
+ if (p->alerts.drop.action != 0) {
+ const PacketAlert *pa = &p->alerts.drop;
+ AlertJsonHeader(p, pa, js);
+ }
+ }
+ }
+
+ OutputJSONBuffer(js, aft->drop_ctx->file_ctx, buffer);
+ json_object_del(js, "drop");
+ json_object_clear(js);
+ json_decref(js);
+
+ return TM_ECODE_OK;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonDropLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonDropLogThread *aft = SCMalloc(sizeof(JsonDropLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(*aft));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for AlertFastLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /** Use the Ouptut Context (file pointer and mutex) */
+ aft->drop_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonDropLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonDropLogThread *aft = (JsonDropLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+
+ /* clear memory */
+ memset(aft, 0, sizeof(*aft));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void JsonDropLogDeInitCtx(OutputCtx *output_ctx)
+{
+ OutputDropLoggerDisable();
+
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(output_ctx);
+}
+
+static void JsonDropLogDeInitCtxSub(OutputCtx *output_ctx)
+{
+ OutputDropLoggerDisable();
+
+ SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
+ SCFree(output_ctx);
+}
+
+static void JsonDropOutputCtxFree(JsonDropOutputCtx *drop_ctx)
+{
+ if (drop_ctx != NULL) {
+ if (drop_ctx->file_ctx != NULL)
+ LogFileFreeCtx(drop_ctx->file_ctx);
+ SCFree(drop_ctx);
+ }
+}
+
+#define DEFAULT_LOG_FILENAME "drop.json"
+static OutputCtx *JsonDropLogInitCtx(ConfNode *conf)
+{
+ if (OutputDropLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'drop' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ JsonDropOutputCtx *drop_ctx = SCCalloc(1, sizeof(*drop_ctx));
+ if (drop_ctx == NULL)
+ return NULL;
+
+ drop_ctx->file_ctx = LogFileNewCtx();
+ if (drop_ctx->file_ctx == NULL) {
+ JsonDropOutputCtxFree(drop_ctx);
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, drop_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ JsonDropOutputCtxFree(drop_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ JsonDropOutputCtxFree(drop_ctx);
+ return NULL;
+ }
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "alerts");
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ drop_ctx->flags = LOG_DROP_ALERTS;
+ }
+ }
+ }
+
+ output_ctx->data = drop_ctx;
+ output_ctx->DeInit = JsonDropLogDeInitCtx;
+ return output_ctx;
+}
+
+static OutputCtx *JsonDropLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ if (OutputDropLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'drop' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ AlertJsonThread *ajt = parent_ctx->data;
+
+ JsonDropOutputCtx *drop_ctx = SCCalloc(1, sizeof(*drop_ctx));
+ if (drop_ctx == NULL)
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ JsonDropOutputCtxFree(drop_ctx);
+ return NULL;
+ }
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "alerts");
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ drop_ctx->flags = LOG_DROP_ALERTS;
+ }
+ }
+ }
+
+ drop_ctx->file_ctx = ajt->file_ctx;
+
+ output_ctx->data = drop_ctx;
+ output_ctx->DeInit = JsonDropLogDeInitCtxSub;
+ return output_ctx;
+}
+
+/**
+ * \brief Log the dropped packets when engine is running in inline mode
+ *
+ * \param tv Pointer the current thread variables
+ * \param data Pointer to the droplog struct
+ * \param p Pointer the packet which is being logged
+ *
+ * \retval 0 on succes
+ */
+static int JsonDropLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ JsonDropLogThread *td = thread_data;
+ int r = DropLogJSON(td, p);
+ if (r < 0)
+ return -1;
+
+ if (p->flow) {
+ FLOWLOCK_RDLOCK(p->flow);
+ if (p->flow->flags & FLOW_ACTION_DROP) {
+ if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
+ p->flow->flags |= FLOW_TOSERVER_DROP_LOGGED;
+ else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
+ p->flow->flags |= FLOW_TOCLIENT_DROP_LOGGED;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ }
+ return 0;
+}
+
+
+/**
+ * \brief Check if we need to drop-log this packet
+ *
+ * \param tv Pointer the current thread variables
+ * \param p Pointer the packet which is tested
+ *
+ * \retval bool TRUE or FALSE
+ */
+static int JsonDropLogCondition(ThreadVars *tv, const Packet *p)
+{
+ if (!EngineModeIsIPS()) {
+ SCLogDebug("engine is not running in inline mode, so returning");
+ return FALSE;
+ }
+ if (PKT_IS_PSEUDOPKT(p)) {
+ SCLogDebug("drop log doesn't log pseudo packets");
+ return FALSE;
+ }
+
+ if (p->flow != NULL) {
+ int ret = FALSE;
+
+ /* for a flow that will be dropped fully, log just once per direction */
+ FLOWLOCK_RDLOCK(p->flow);
+ if (p->flow->flags & FLOW_ACTION_DROP) {
+ if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
+ ret = TRUE;
+ else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
+ ret = TRUE;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+
+ /* if drop is caused by signature, log anyway */
+ if (p->alerts.drop.action != 0)
+ ret = TRUE;
+
+ return ret;
+ } else if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void TmModuleJsonDropLogRegister (void)
+{
+ tmm_modules[TMM_JSONDROPLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONDROPLOG].ThreadInit = JsonDropLogThreadInit;
+ tmm_modules[TMM_JSONDROPLOG].ThreadDeinit = JsonDropLogThreadDeinit;
+ tmm_modules[TMM_JSONDROPLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONDROPLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ OutputRegisterPacketModule(MODULE_NAME, "drop-json-log",
+ JsonDropLogInitCtx, JsonDropLogger, JsonDropLogCondition);
+ OutputRegisterPacketSubModule("eve-log", MODULE_NAME, "eve-log.drop",
+ JsonDropLogInitCtxSub, JsonDropLogger, JsonDropLogCondition);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonDropLogRegister (void)
+{
+ tmm_modules[TMM_JSONDROPLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONDROPLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-drop.h b/framework/src/suricata/src/output-json-drop.h
new file mode 100644
index 00000000..c02057c1
--- /dev/null
+++ b/framework/src/suricata/src/output-json-drop.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ */
+
+#ifndef __OUTPUT_JSON_DROP_H__
+#define __OUTPUT_JSON_DROP_H__
+
+void TmModuleJsonDropLogRegister (void);
+
+#endif /* __OUTPUT_DROPLOG_H__ */
diff --git a/framework/src/suricata/src/output-json-email-common.c b/framework/src/suricata/src/output-json-email-common.c
new file mode 100644
index 00000000..1efa9ce8
--- /dev/null
+++ b/framework/src/suricata/src/output-json-email-common.c
@@ -0,0 +1,266 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements json common email logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "tm-threads-common.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "app-layer-parser.h"
+#include "output.h"
+#include "app-layer-smtp.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-byte.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#include "output-json.h"
+#include "output-json-email-common.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+/* JSON format logging */
+static TmEcode JsonEmailLogJson(JsonEmailLogThread *aft, json_t *js, const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
+{
+ SMTPState *smtp_state;
+ MimeDecParseState *mime_state;
+ MimeDecEntity *entity;
+ char *protos = NULL;
+
+ json_t *sjs = json_object();
+ if (sjs == NULL) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* check if we have SMTP state or not */
+ AppProto proto = FlowGetAppProtocol(p->flow);
+ switch (proto) {
+ case ALPROTO_SMTP:
+ smtp_state = (SMTPState *)state;
+ if (smtp_state == NULL) {
+ SCLogDebug("no smtp state, so no request logging");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ SMTPTransaction *tx = vtx;
+ mime_state = tx->mime_state;
+ entity = tx->msg_tail;
+ protos = "smtp";
+ SCLogDebug("lets go mime_state %p, entity %p, state_flag %u", mime_state, entity, mime_state ? mime_state->state_flag : 0);
+ break;
+ default:
+ /* don't know how we got here */
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if ((mime_state != NULL)) {
+ if (entity == NULL) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if ((entity->header_flags & HDR_IS_LOGGED) == 0) {
+ MimeDecField *field;
+ //printf("email LOG\n");
+
+ /* From: */
+ field = MimeDecFindField(entity, "from");
+ if (field != NULL) {
+ char *s = BytesToString((uint8_t *)field->value,
+ (size_t)field->value_len);
+ if (likely(s != NULL)) {
+ //printf("From: \"%s\"\n", s);
+ json_object_set_new(sjs, "from", json_string(s));
+ SCFree(s);
+ }
+ }
+
+ /* To: */
+ char *to_line = NULL;
+ field = MimeDecFindField(entity, "to");
+ if (field != NULL) {
+ json_t *js_to = json_array();
+ if (likely(js_to != NULL)) {
+ to_line = BytesToString((uint8_t *)field->value,
+ (size_t)field->value_len);
+ if (likely(to_line != NULL)) {
+ char *savep = NULL;
+ char *p;
+ //printf("to_line:: TO: \"%s\" (%d)\n", to_line, strlen(to_line));
+ p = strtok_r(to_line, ",", &savep);
+ //printf("got another addr: \"%s\"\n", p);
+ json_array_append_new(js_to, json_string(p));
+ while ((p = strtok_r(NULL, ",", &savep)) != NULL) {
+ //printf("got another addr: \"%s\"\n", p);
+ json_array_append_new(js_to, json_string(&p[strspn(p, " ")]));
+ }
+ SCFree(to_line);
+ }
+ json_object_set_new(sjs, "to", js_to);
+ }
+ }
+
+ /* Cc: */
+ char *cc_line = NULL;
+ field = MimeDecFindField(entity, "cc");
+ if (field != NULL) {
+ json_t *js_cc = json_array();
+ if (likely(js_cc != NULL)) {
+ cc_line = BytesToString((uint8_t *)field->value,
+ (size_t)field->value_len);
+ if (likely(cc_line != NULL)) {
+ char *savep = NULL;
+ char *p;
+ //printf("cc_line:: CC: \"%s\" (%d)\n", to_line, strlen(to_line));
+ p = strtok_r(cc_line, ",", &savep);
+ //printf("got another addr: \"%s\"\n", p);
+ json_array_append_new(js_cc, json_string(p));
+ while ((p = strtok_r(NULL, ",", &savep)) != NULL) {
+ //printf("got another addr: \"%s\"\n", p);
+ json_array_append_new(js_cc, json_string(&p[strspn(p, " ")]));
+ }
+ SCFree(cc_line);
+ }
+ json_object_set_new(sjs, "cc", js_cc);
+ }
+ }
+
+ /* Subject: */
+ field = MimeDecFindField(entity, "subject");
+ if (field != NULL) {
+ char *s = BytesToString((uint8_t *)field->value, (size_t) field->value_len);
+ if (likely(s != NULL)) {
+ //printf("Subject: \"%s\"\n", s);
+ json_object_set_new(sjs, "subject", json_string(s));
+ SCFree(s);
+ }
+ }
+
+ entity->header_flags |= HDR_IS_LOGGED;
+
+ if (mime_state->stack == NULL || mime_state->stack->top == NULL || mime_state->stack->top->data == NULL)
+ SCReturnInt(TM_ECODE_OK);
+
+ entity = (MimeDecEntity *)mime_state->stack->top->data;
+ int attch_cnt = 0;
+ int url_cnt = 0;
+ json_t *js_attch = json_array();
+ json_t *js_url = json_array();
+ if (entity->url_list != NULL) {
+ MimeDecUrl *url;
+ for (url = entity->url_list; url != NULL; url = url->next) {
+ char *s = BytesToString((uint8_t *)url->url,
+ (size_t)url->url_len);
+ if (s != NULL) {
+ //printf("URL: \"%s\"\n", s);
+ json_array_append_new(js_url,
+ json_string(s));
+ SCFree(s);
+ url_cnt += 1;
+ }
+ }
+ }
+ for (entity = entity->child; entity != NULL; entity = entity->next) {
+ if (entity->ctnt_flags & CTNT_IS_ATTACHMENT) {
+
+ char *s = BytesToString((uint8_t *)entity->filename,
+ (size_t)entity->filename_len);
+ //printf("found attachment \"%s\"\n", s);
+ json_array_append_new(js_attch,
+ json_string(s));
+ SCFree(s);
+ attch_cnt += 1;
+ }
+ if (entity->url_list != NULL) {
+ MimeDecUrl *url;
+ for (url = entity->url_list; url != NULL; url = url->next) {
+ char *s = BytesToString((uint8_t *)url->url,
+ (size_t)url->url_len);
+ if (s != NULL) {
+ //printf("URL: \"%s\"\n", s);
+ json_array_append_new(js_url,
+ json_string(s));
+ SCFree(s);
+ url_cnt += 1;
+ }
+ }
+ }
+ }
+ if (attch_cnt > 0) {
+ json_object_set_new(sjs, "attachment", js_attch);
+ } else {
+ json_decref(js_attch);
+ }
+ if (url_cnt > 0) {
+ json_object_set_new(sjs, "url", js_url);
+ } else {
+ json_decref(js_url);
+ }
+ json_object_set_new(js, protos, sjs);
+
+// FLOWLOCK_UNLOCK(p->flow);
+ SCReturnInt(TM_ECODE_OK);
+ }
+ }
+
+// FLOWLOCK_UNLOCK(p->flow);
+ SCReturnInt(TM_ECODE_DONE);
+}
+
+int JsonEmailLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) {
+ SCEnter();
+ JsonEmailLogThread *jhl = (JsonEmailLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)jhl->buffer;
+
+ json_t *js = CreateJSONHeader((Packet *)p, 1, "smtp");
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ if (JsonEmailLogJson(jhl, js, p, f, state, tx, tx_id) == TM_ECODE_OK) {
+ OutputJSONBuffer(js, jhl->emaillog_ctx->file_ctx, buffer);
+ }
+ json_object_del(js, "smtp");
+
+ json_object_clear(js);
+ json_decref(js);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-email-common.h b/framework/src/suricata/src/output-json-email-common.h
new file mode 100644
index 00000000..7a95954c
--- /dev/null
+++ b/framework/src/suricata/src/output-json-email-common.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_EMAIL_COMMON_H__
+#define __OUTPUT_JSON_EMAIL_COMMON_H__
+
+typedef struct OutputJsonEmailCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} OutputJsonEmailCtx;
+
+
+typedef struct JsonEmailLogThread_ {
+ OutputJsonEmailCtx *emaillog_ctx;
+ MemBuffer *buffer;
+} JsonEmailLogThread;
+
+int JsonEmailLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id);
+
+#endif /* __OUTPUT_JSON_EMAIL_COMMON_H__ */
diff --git a/framework/src/suricata/src/output-json-file.c b/framework/src/suricata/src/output-json-file.c
new file mode 100644
index 00000000..cbfa0c4d
--- /dev/null
+++ b/framework/src/suricata/src/output-json-file.c
@@ -0,0 +1,396 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Log files we track.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threadvars.h"
+#include "tm-modules.h"
+
+#include "threads.h"
+
+#include "app-layer-parser.h"
+
+#include "detect-filemagic.h"
+
+#include "stream.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+#include "util-privs.h"
+#include "util-debug.h"
+#include "util-atomic.h"
+#include "util-file.h"
+#include "util-time.h"
+#include "util-buffer.h"
+#include "util-byte.h"
+
+#include "output.h"
+#include "output-json.h"
+
+#include "log-file.h"
+#include "util-logopenfile.h"
+
+#include "app-layer-htp.h"
+#include "util-memcmp.h"
+#include "stream-tcp-reassemble.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+typedef struct OutputFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t file_cnt;
+} OutputFileCtx;
+
+typedef struct JsonFileLogThread_ {
+ OutputFileCtx *filelog_ctx;
+ MemBuffer *buffer;
+} JsonFileLogThread;
+
+static json_t *LogFileMetaGetUri(const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ json_t *js = NULL;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
+ char *s = bstr_util_strdup_to_c(tx_ud->request_uri_normalized);
+ if (s != NULL) {
+ js = json_string(s);
+ SCFree(s);
+ if (js != NULL)
+ return js;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static json_t *LogFileMetaGetHost(const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ json_t *js = NULL;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL && tx->request_hostname != NULL) {
+ char *s = bstr_util_strdup_to_c(tx->request_hostname);
+ if (s != NULL) {
+ js = json_string(s);
+ SCFree(s);
+ if (js != NULL)
+ return js;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static json_t *LogFileMetaGetReferer(const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ json_t *js = NULL;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "Referer");
+ if (h != NULL) {
+ char *s = bstr_util_strdup_to_c(h->value);
+ if (s != NULL) {
+ js = json_string(s);
+ SCFree(s);
+ if (js != NULL)
+ return js;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static json_t *LogFileMetaGetUserAgent(const Packet *p, const File *ff)
+{
+ HtpState *htp_state = (HtpState *)p->flow->alstate;
+ json_t *js = NULL;
+ if (htp_state != NULL) {
+ htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
+ if (tx != NULL) {
+ htp_header_t *h = NULL;
+ h = (htp_header_t *)htp_table_get_c(tx->request_headers,
+ "User-Agent");
+ if (h != NULL) {
+ char *s = bstr_util_strdup_to_c(h->value);
+ if (s != NULL) {
+ js = json_string(s);
+ SCFree(s);
+ if (js != NULL)
+ return js;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Write meta data on a single line json record
+ */
+static void FileWriteJsonRecord(JsonFileLogThread *aft, const Packet *p, const File *ff)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ json_t *js = CreateJSONHeader((Packet *)p, 0, "fileinfo"); //TODO const
+ if (unlikely(js == NULL))
+ return;
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ json_t *hjs = json_object();
+ if (unlikely(hjs == NULL)) {
+ json_decref(js);
+ return;
+ }
+
+ json_object_set_new(hjs, "app_proto", json_string(AppProtoToString(p->flow->alproto)));
+ switch (p->flow->alproto) {
+ case ALPROTO_HTTP:
+ json_object_set_new(hjs, "url", LogFileMetaGetUri(p, ff));
+ json_object_set_new(hjs, "hostname", LogFileMetaGetHost(p, ff));
+ json_object_set_new(hjs, "http_refer", LogFileMetaGetReferer(p, ff));
+ json_object_set_new(hjs, "http_user_agent", LogFileMetaGetUserAgent(p, ff));
+ json_object_set_new(js, "http", hjs);
+ break;
+ }
+
+
+ json_t *fjs = json_object();
+ if (unlikely(fjs == NULL)) {
+ json_decref(hjs);
+ json_decref(js);
+ return;
+ }
+
+ char *s = BytesToString(ff->name, ff->name_len);
+ json_object_set_new(fjs, "filename", json_string(s));
+ if (s != NULL)
+ SCFree(s);
+ if (ff->magic)
+ json_object_set_new(fjs, "magic", json_string((char *)ff->magic));
+ switch (ff->state) {
+ case FILE_STATE_CLOSED:
+ json_object_set_new(fjs, "state", json_string("CLOSED"));
+#ifdef HAVE_NSS
+ if (ff->flags & FILE_MD5) {
+ size_t x;
+ int i;
+ char *s = SCMalloc(256);
+ if (likely(s != NULL)) {
+ for (i = 0, x = 0; x < sizeof(ff->md5); x++) {
+ i += snprintf(&s[i], 255-i, "%02x", ff->md5[x]);
+ }
+ json_object_set_new(fjs, "md5", json_string(s));
+ SCFree(s);
+ }
+ }
+#endif
+ break;
+ case FILE_STATE_TRUNCATED:
+ json_object_set_new(fjs, "state", json_string("TRUNCATED"));
+ break;
+ case FILE_STATE_ERROR:
+ json_object_set_new(fjs, "state", json_string("ERROR"));
+ break;
+ default:
+ json_object_set_new(fjs, "state", json_string("UNKNOWN"));
+ break;
+ }
+ json_object_set_new(fjs, "stored",
+ (ff->flags & FILE_STORED) ? json_true() : json_false());
+ if (ff->flags & FILE_STORED) {
+ json_object_set_new(fjs, "file_id", json_integer(ff->file_id));
+ }
+ json_object_set_new(fjs, "size", json_integer(ff->size));
+ json_object_set_new(fjs, "tx_id", json_integer(ff->txid));
+
+ /* originally just 'file', but due to bug 1127 naming it fileinfo */
+ json_object_set_new(js, "fileinfo", fjs);
+ OutputJSONBuffer(js, aft->filelog_ctx->file_ctx, buffer);
+ json_object_del(js, "fileinfo");
+ json_object_del(js, "http");
+
+ json_object_clear(js);
+ json_decref(js);
+}
+
+static int JsonFileLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff)
+{
+ SCEnter();
+ JsonFileLogThread *aft = (JsonFileLogThread *)thread_data;
+
+ BUG_ON(ff->flags & FILE_LOGGED);
+
+ SCLogDebug("ff %p", ff);
+
+ FileWriteJsonRecord(aft, p, ff);
+ return 0;
+}
+
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonFileLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonFileLogThread *aft = SCMalloc(sizeof(JsonFileLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonFileLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->filelog_ctx = ((OutputCtx *)initdata)->data;
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonFileLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonFileLogThread *aft = (JsonFileLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonFileLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void OutputFileLogDeinitSub(OutputCtx *output_ctx)
+{
+ OutputFileCtx *ff_ctx = output_ctx->data;
+ SCFree(ff_ctx);
+ SCFree(output_ctx);
+}
+
+/** \brief Create a new http log LogFileCtx.
+ * \param conf Pointer to ConfNode containing this loggers configuration.
+ * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
+ * */
+OutputCtx *OutputFileLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ OutputFileCtx *output_file_ctx = SCMalloc(sizeof(OutputFileCtx));
+ if (unlikely(output_file_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(output_file_ctx);
+ return NULL;
+ }
+
+ output_file_ctx->file_ctx = ojc->file_ctx;
+
+ if (conf) {
+ const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic");
+ if (force_magic != NULL && ConfValIsTrue(force_magic)) {
+ FileForceMagicEnable();
+ SCLogInfo("forcing magic lookup for logged files");
+ }
+
+ const char *force_md5 = ConfNodeLookupChildValue(conf, "force-md5");
+ if (force_md5 != NULL && ConfValIsTrue(force_md5)) {
+#ifdef HAVE_NSS
+ FileForceMd5Enable();
+ SCLogInfo("forcing md5 calculation for logged files");
+#else
+ SCLogInfo("md5 calculation requires linking against libnss");
+#endif
+ }
+ }
+
+ output_ctx->data = output_file_ctx;
+ output_ctx->DeInit = OutputFileLogDeinitSub;
+
+ FileForceTrackingEnable();
+ return output_ctx;
+}
+
+void TmModuleJsonFileLogRegister (void)
+{
+ tmm_modules[TMM_JSONFILELOG].name = "JsonFileLog";
+ tmm_modules[TMM_JSONFILELOG].ThreadInit = JsonFileLogThreadInit;
+ tmm_modules[TMM_JSONFILELOG].ThreadDeinit = JsonFileLogThreadDeinit;
+ tmm_modules[TMM_JSONFILELOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as child of eve-log */
+ OutputRegisterFileSubModule("eve-log", "JsonFileLog", "eve-log.files",
+ OutputFileLogInitSub, JsonFileLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonFileLogRegister (void)
+{
+ tmm_modules[TMM_JSONFILELOG].name = "JsonFileLog";
+ tmm_modules[TMM_JSONFILELOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-file.h b/framework/src/suricata/src/output-json-file.h
new file mode 100644
index 00000000..e101897f
--- /dev/null
+++ b/framework/src/suricata/src/output-json-file.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_FILE_H__
+#define __OUTPUT_JSON_FILE_H__
+
+void TmModuleJsonFileLogRegister (void);
+
+#endif /* __OUTPUT_JSON_FILE_H__ */
diff --git a/framework/src/suricata/src/output-json-flow.c b/framework/src/suricata/src/output-json-flow.c
new file mode 100644
index 00000000..7ff0d3d2
--- /dev/null
+++ b/framework/src/suricata/src/output-json-flow.c
@@ -0,0 +1,485 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements Flow JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+#include "output-json.h"
+
+#include "stream-tcp-private.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+typedef struct LogJsonFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} LogJsonFileCtx;
+
+typedef struct JsonFlowLogThread_ {
+ LogJsonFileCtx *flowlog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t uri_cnt;
+
+ MemBuffer *buffer;
+} JsonFlowLogThread;
+
+
+#define LOG_HTTP_DEFAULT 0
+#define LOG_HTTP_EXTENDED 1
+#define LOG_HTTP_CUSTOM 2
+
+static json_t *CreateJSONHeaderFromFlow(Flow *f, char *event_type)
+{
+ char timebuf[64];
+ char srcip[46], dstip[46];
+ Port sp, dp;
+
+ json_t *js = json_object();
+ if (unlikely(js == NULL))
+ return NULL;
+
+ struct timeval tv;
+ memset(&tv, 0x00, sizeof(tv));
+ TimeGet(&tv);
+
+ CreateIsoTimeString(&tv, timebuf, sizeof(timebuf));
+
+ srcip[0] = '\0';
+ dstip[0] = '\0';
+ if (FLOW_IS_IPV4(f)) {
+ PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
+ } else if (FLOW_IS_IPV6(f)) {
+ PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
+ }
+
+ sp = f->sp;
+ dp = f->dp;
+
+ char proto[16];
+ if (SCProtoNameValid(f->proto) == TRUE) {
+ strlcpy(proto, known_proto[f->proto], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "%03" PRIu32, f->proto);
+ }
+
+ /* time */
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ CreateJSONFlowId(js, (const Flow *)f);
+
+#if 0 // TODO
+ /* sensor id */
+ if (sensor_id >= 0)
+ json_object_set_new(js, "sensor_id", json_integer(sensor_id));
+#endif
+ if (event_type) {
+ json_object_set_new(js, "event_type", json_string(event_type));
+ }
+#if 0
+ /* vlan */
+ if (f->vlan_id[0] > 0) {
+ json_t *js_vlan;
+ switch (f->vlan_idx) {
+ case 1:
+ json_object_set_new(js, "vlan",
+ json_integer(f->vlan_id[0]));
+ break;
+ case 2:
+ js_vlan = json_array();
+ if (unlikely(js != NULL)) {
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID1(p)));
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID2(p)));
+ json_object_set_new(js, "vlan", js_vlan);
+ }
+ break;
+ default:
+ /* shouldn't get here */
+ break;
+ }
+ }
+#endif
+ /* tuple */
+ json_object_set_new(js, "src_ip", json_string(srcip));
+ switch(f->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "src_port", json_integer(sp));
+ break;
+ }
+ json_object_set_new(js, "dest_ip", json_string(dstip));
+ switch(f->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "dest_port", json_integer(dp));
+ break;
+ }
+ json_object_set_new(js, "proto", json_string(proto));
+ switch (f->proto) {
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ json_object_set_new(js, "icmp_type",
+ json_integer(f->type));
+ json_object_set_new(js, "icmp_code",
+ json_integer(f->code));
+ break;
+ }
+ return js;
+}
+
+/* JSON format logging */
+static void JsonFlowLogJSON(JsonFlowLogThread *aft, json_t *js, Flow *f)
+{
+#if 0
+ LogJsonFileCtx *flow_ctx = aft->flowlog_ctx;
+#endif
+ json_t *hjs = json_object();
+ if (hjs == NULL) {
+ return;
+ }
+
+ json_object_set_new(hjs, "app_proto", json_string(AppProtoToString(f->alproto)));
+
+ json_object_set_new(hjs, "pkts_toserver",
+ json_integer(f->todstpktcnt));
+ json_object_set_new(hjs, "pkts_toclient",
+ json_integer(f->tosrcpktcnt));
+ json_object_set_new(hjs, "bytes_toserver",
+ json_integer(f->todstbytecnt));
+ json_object_set_new(hjs, "bytes_toclient",
+ json_integer(f->tosrcbytecnt));
+
+ char timebuf1[64], timebuf2[64];
+
+ CreateIsoTimeString(&f->startts, timebuf1, sizeof(timebuf1));
+ CreateIsoTimeString(&f->lastts, timebuf2, sizeof(timebuf2));
+
+ json_object_set_new(hjs, "start", json_string(timebuf1));
+ json_object_set_new(hjs, "end", json_string(timebuf2));
+
+ int32_t age = f->lastts.tv_sec - f->startts.tv_sec;
+ json_object_set_new(hjs, "age",
+ json_integer(age));
+
+ if (f->flow_end_flags & FLOW_END_FLAG_EMERGENCY)
+ json_object_set_new(hjs, "emergency", json_true());
+ const char *state = NULL;
+ if (f->flow_end_flags & FLOW_END_FLAG_STATE_NEW)
+ state = "new";
+ else if (f->flow_end_flags & FLOW_END_FLAG_STATE_ESTABLISHED)
+ state = "established";
+ else if (f->flow_end_flags & FLOW_END_FLAG_STATE_CLOSED)
+ state = "closed";
+
+ json_object_set_new(hjs, "state",
+ json_string(state));
+
+ const char *reason = NULL;
+ if (f->flow_end_flags & FLOW_END_FLAG_TIMEOUT)
+ reason = "timeout";
+ else if (f->flow_end_flags & FLOW_END_FLAG_FORCED)
+ reason = "forced";
+ else if (f->flow_end_flags & FLOW_END_FLAG_SHUTDOWN)
+ reason = "shutdown";
+
+ json_object_set_new(hjs, "reason",
+ json_string(reason));
+
+ json_object_set_new(js, "flow", hjs);
+
+
+ /* TCP */
+ if (f->proto == IPPROTO_TCP) {
+ json_t *tjs = json_object();
+ if (tjs == NULL) {
+ return;
+ }
+
+ TcpSession *ssn = f->protoctx;
+
+ char hexflags[3] = "";
+ snprintf(hexflags, sizeof(hexflags), "%02x",
+ ssn ? ssn->tcp_packet_flags : 0);
+ json_object_set_new(tjs, "tcp_flags", json_string(hexflags));
+
+ snprintf(hexflags, sizeof(hexflags), "%02x",
+ ssn ? ssn->client.tcp_flags : 0);
+ json_object_set_new(tjs, "tcp_flags_ts", json_string(hexflags));
+
+ snprintf(hexflags, sizeof(hexflags), "%02x",
+ ssn ? ssn->server.tcp_flags : 0);
+ json_object_set_new(tjs, "tcp_flags_tc", json_string(hexflags));
+
+ JsonTcpFlags(ssn ? ssn->tcp_packet_flags : 0, tjs);
+
+ if (ssn) {
+ char *state = NULL;
+ switch (ssn->state) {
+ case TCP_NONE:
+ state = "none";
+ break;
+ case TCP_LISTEN:
+ state = "listen";
+ break;
+ case TCP_SYN_SENT:
+ state = "syn_sent";
+ break;
+ case TCP_SYN_RECV:
+ state = "syn_recv";
+ break;
+ case TCP_ESTABLISHED:
+ state = "established";
+ break;
+ case TCP_FIN_WAIT1:
+ state = "fin_wait1";
+ break;
+ case TCP_FIN_WAIT2:
+ state = "fin_wait2";
+ break;
+ case TCP_TIME_WAIT:
+ state = "time_wait";
+ break;
+ case TCP_LAST_ACK:
+ state = "last_ack";
+ break;
+ case TCP_CLOSE_WAIT:
+ state = "close_wait";
+ break;
+ case TCP_CLOSING:
+ state = "closing";
+ break;
+ case TCP_CLOSED:
+ state = "closed";
+ break;
+ }
+ json_object_set_new(tjs, "state", json_string(state));
+ }
+
+ json_object_set_new(js, "tcp", tjs);
+ }
+}
+
+static int JsonFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
+{
+ SCEnter();
+ JsonFlowLogThread *jhl = (JsonFlowLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)jhl->buffer;
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ json_t *js = CreateJSONHeaderFromFlow(f, "flow"); //TODO const
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ JsonFlowLogJSON(jhl, js, f);
+
+ OutputJSONBuffer(js, jhl->flowlog_ctx->file_ctx, buffer);
+ json_object_del(js, "http");
+
+ json_object_clear(js);
+ json_decref(js);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void OutputFlowLogDeinit(OutputCtx *output_ctx)
+{
+ LogJsonFileCtx *flow_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = flow_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(flow_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "flow.json"
+OutputCtx *OutputFlowLogInit(ConfNode *conf)
+{
+ SCLogInfo("hi");
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogJsonFileCtx *flow_ctx = SCMalloc(sizeof(LogJsonFileCtx));
+ if (unlikely(flow_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(flow_ctx);
+ return NULL;
+ }
+
+ flow_ctx->file_ctx = file_ctx;
+ output_ctx->data = flow_ctx;
+ output_ctx->DeInit = OutputFlowLogDeinit;
+
+ return output_ctx;
+}
+
+static void OutputFlowLogDeinitSub(OutputCtx *output_ctx)
+{
+ LogJsonFileCtx *flow_ctx = output_ctx->data;
+ SCFree(flow_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputFlowLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ LogJsonFileCtx *flow_ctx = SCMalloc(sizeof(LogJsonFileCtx));
+ if (unlikely(flow_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(flow_ctx);
+ return NULL;
+ }
+
+ flow_ctx->file_ctx = ojc->file_ctx;
+ flow_ctx->flags = LOG_HTTP_DEFAULT;
+
+ output_ctx->data = flow_ctx;
+ output_ctx->DeInit = OutputFlowLogDeinitSub;
+
+ return output_ctx;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonFlowLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonFlowLogThread *aft = SCMalloc(sizeof(JsonFlowLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonFlowLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->flowlog_ctx = ((OutputCtx *)initdata)->data; //TODO
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonFlowLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonFlowLogThread *aft = (JsonFlowLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonFlowLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void TmModuleJsonFlowLogRegister (void)
+{
+ tmm_modules[TMM_JSONFLOWLOG].name = "JsonFlowLog";
+ tmm_modules[TMM_JSONFLOWLOG].ThreadInit = JsonFlowLogThreadInit;
+ tmm_modules[TMM_JSONFLOWLOG].ThreadDeinit = JsonFlowLogThreadDeinit;
+ tmm_modules[TMM_JSONFLOWLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONFLOWLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONFLOWLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterFlowModule("JsonFlowLog", "flow-json-log",
+ OutputFlowLogInit, JsonFlowLogger);
+
+ /* also register as child of eve-log */
+ OutputRegisterFlowSubModule("eve-log", "JsonFlowLog", "eve-log.flow",
+ OutputFlowLogInitSub, JsonFlowLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonFlowLogRegister (void)
+{
+ tmm_modules[TMM_JSONFLOWLOG].name = "JsonFlowLog";
+ tmm_modules[TMM_JSONFLOWLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-flow.h b/framework/src/suricata/src/output-json-flow.h
new file mode 100644
index 00000000..1d32c9eb
--- /dev/null
+++ b/framework/src/suricata/src/output-json-flow.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __OUTPUT_JSON_FLOW_H__
+#define __OUTPUT_JSON_FLOW_H__
+
+void TmModuleJsonFlowLogRegister (void);
+
+#endif /* __OUTPUT_JSON_FLOW_H__ */
diff --git a/framework/src/suricata/src/output-json-http.c b/framework/src/suricata/src/output-json-http.c
new file mode 100644
index 00000000..31641985
--- /dev/null
+++ b/framework/src/suricata/src/output-json-http.c
@@ -0,0 +1,597 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements HTTP JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+#include "output-json.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+typedef struct LogHttpFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+ uint64_t fields;/** Store fields */
+} LogHttpFileCtx;
+
+typedef struct JsonHttpLogThread_ {
+ LogHttpFileCtx *httplog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ uint32_t uri_cnt;
+
+ MemBuffer *buffer;
+} JsonHttpLogThread;
+
+
+#define LOG_HTTP_DEFAULT 0
+#define LOG_HTTP_EXTENDED 1
+#define LOG_HTTP_REQUEST 2 /* request field */
+#define LOG_HTTP_ARRAY 4 /* require array handling */
+
+typedef enum {
+ HTTP_FIELD_ACCEPT = 0,
+ HTTP_FIELD_ACCEPT_CHARSET,
+ HTTP_FIELD_ACCEPT_ENCODING,
+ HTTP_FIELD_ACCEPT_LANGUAGE,
+ HTTP_FIELD_ACCEPT_DATETIME,
+ HTTP_FIELD_AUTHORIZATION,
+ HTTP_FIELD_CACHE_CONTROL,
+ HTTP_FIELD_CONNECTION,
+ HTTP_FIELD_FROM,
+ HTTP_FIELD_MAX_FORWARDS,
+ HTTP_FIELD_ORIGIN,
+ HTTP_FIELD_PRAGMA,
+ HTTP_FIELD_PROXY_AUTHORIZATION,
+ HTTP_FIELD_RANGE,
+ HTTP_FIELD_TE,
+ HTTP_FIELD_VIA,
+ HTTP_FIELD_X_REQUESTED_WITH,
+ HTTP_FIELD_DNT,
+ HTTP_FIELD_X_FORWARDED_PROTO,
+ HTTP_FIELD_ACCEPT_RANGES,
+ HTTP_FIELD_AGE,
+ HTTP_FIELD_ALLOW,
+ HTTP_FIELD_CONTENT_ENCODING,
+ HTTP_FIELD_CONTENT_LANGUAGE,
+ HTTP_FIELD_CONTENT_LENGTH,
+ HTTP_FIELD_CONTENT_LOCATION,
+ HTTP_FIELD_CONTENT_MD5,
+ HTTP_FIELD_CONTENT_RANGE,
+ HTTP_FIELD_CONTENT_TYPE,
+ HTTP_FIELD_DATE,
+ HTTP_FIELD_ETAG,
+ HTTP_FIELD_EXPIRES,
+ HTTP_FIELD_LAST_MODIFIED,
+ HTTP_FIELD_LINK,
+ HTTP_FIELD_LOCATION,
+ HTTP_FIELD_PROXY_AUTHENTICATE,
+ HTTP_FIELD_REFERRER,
+ HTTP_FIELD_REFRESH,
+ HTTP_FIELD_RETRY_AFTER,
+ HTTP_FIELD_SERVER,
+ HTTP_FIELD_SET_COOKIE,
+ HTTP_FIELD_TRAILER,
+ HTTP_FIELD_TRANSFER_ENCODING,
+ HTTP_FIELD_UPGRADE,
+ HTTP_FIELD_VARY,
+ HTTP_FIELD_WARNING,
+ HTTP_FIELD_WWW_AUTHENTICATE,
+ HTTP_FIELD_SIZE
+} HttpField;
+
+struct {
+ char *config_field;
+ char *htp_field;
+ uint32_t flags;
+} http_fields[] = {
+ { "accept", "accept", LOG_HTTP_REQUEST },
+ { "accept_charset", "accept-charset", LOG_HTTP_REQUEST },
+ { "accept_encoding", "accept-encoding", LOG_HTTP_REQUEST },
+ { "accept_language", "accept-language", LOG_HTTP_REQUEST },
+ { "accept_datetime", "accept-datetime", LOG_HTTP_REQUEST },
+ { "authorization", "authorization", LOG_HTTP_REQUEST },
+ { "cache_control", "cache-control", LOG_HTTP_REQUEST },
+ { "cookie", "cookie", LOG_HTTP_REQUEST|LOG_HTTP_ARRAY },
+ { "from", "from", LOG_HTTP_REQUEST },
+ { "max_forwards", "max-forwards", LOG_HTTP_REQUEST },
+ { "origin", "origin", LOG_HTTP_REQUEST },
+ { "pragma", "pragma", LOG_HTTP_REQUEST },
+ { "proxy_authorization", "proxy-authorization", LOG_HTTP_REQUEST },
+ { "range", "range", LOG_HTTP_REQUEST },
+ { "te", "te", LOG_HTTP_REQUEST },
+ { "via", "via", LOG_HTTP_REQUEST },
+ { "x_requested_with", "x-requested-with", LOG_HTTP_REQUEST },
+ { "dnt", "dnt", LOG_HTTP_REQUEST },
+ { "x_forwarded_proto", "x-forwarded-proto", LOG_HTTP_REQUEST },
+ { "accept_range", "accept-range", 0 },
+ { "age", "age", 0 },
+ { "allow", "allow", 0 },
+ { "connection", "connection", 0 },
+ { "content_encoding", "content-encoding", 0 },
+ { "content_language", "content-language", 0 },
+ { "content_length", "content-length", 0 },
+ { "content_location", "content-location", 0 },
+ { "content_md5", "content-md5", 0 },
+ { "content_range", "content-range", 0 },
+ { "content_type", "content-type", 0 },
+ { "date", "date", 0 },
+ { "etag", "etags", 0 },
+ { "expires", "expires" , 0 },
+ { "last_modified", "last-modified", 0 },
+ { "link", "link", 0 },
+ { "location", "location", 0 },
+ { "proxy_authenticate", "proxy-authenticate", 0 },
+ { "referrer", "referrer", LOG_HTTP_EXTENDED },
+ { "refresh", "refresh", 0 },
+ { "retry_after", "retry-after", 0 },
+ { "server", "server", 0 },
+ { "set_cookie", "set-cookie", 0 },
+ { "trailer", "trailer", 0 },
+ { "transfer_encoding", "transfer-encoding", 0 },
+ { "upgrade", "upgrade", 0 },
+ { "vary", "vary", 0 },
+ { "warning", "warning", 0 },
+ { "www_authenticate", "www-authenticate", 0 },
+};
+
+void JsonHttpLogJSONBasic(json_t *js, htp_tx_t *tx)
+{
+ char *c;
+
+ /* hostname */
+ if (tx->request_hostname != NULL)
+ {
+ c = bstr_util_strdup_to_c(tx->request_hostname);
+ if (c != NULL) {
+ json_object_set_new(js, "hostname", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* uri */
+ if (tx->request_uri != NULL)
+ {
+ c = bstr_util_strdup_to_c(tx->request_uri);
+ if (c != NULL) {
+ json_object_set_new(js, "url", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* user agent */
+ htp_header_t *h_user_agent = NULL;
+ if (tx->request_headers != NULL) {
+ h_user_agent = htp_table_get_c(tx->request_headers, "user-agent");
+ }
+ if (h_user_agent != NULL) {
+ c = bstr_util_strdup_to_c(h_user_agent->value);
+ if (c != NULL) {
+ json_object_set_new(js, "http_user_agent", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* x-forwarded-for */
+ htp_header_t *h_x_forwarded_for = NULL;
+ if (tx->request_headers != NULL) {
+ h_x_forwarded_for = htp_table_get_c(tx->request_headers, "x-forwarded-for");
+ }
+ if (h_x_forwarded_for != NULL) {
+ c = bstr_util_strdup_to_c(h_x_forwarded_for->value);
+ if (c != NULL) {
+ json_object_set_new(js, "xff", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* content-type */
+ htp_header_t *h_content_type = NULL;
+ if (tx->response_headers != NULL) {
+ h_content_type = htp_table_get_c(tx->response_headers, "content-type");
+ }
+ if (h_content_type != NULL) {
+ char *p;
+ c = bstr_util_strdup_to_c(h_content_type->value);
+ if (c != NULL) {
+ p = strchr(c, ';');
+ if (p != NULL)
+ *p = '\0';
+ json_object_set_new(js, "http_content_type", json_string(c));
+ SCFree(c);
+ }
+ }
+}
+
+static void JsonHttpLogJSONCustom(LogHttpFileCtx *http_ctx, json_t *js, htp_tx_t *tx)
+{
+ char *c;
+ HttpField f;
+
+ for (f = HTTP_FIELD_ACCEPT; f < HTTP_FIELD_SIZE; f++)
+ {
+ if ((http_ctx->fields & (1ULL<<f)) != 0)
+ {
+ /* prevent logging a field twice if extended logging is
+ enabled */
+ if (((http_ctx->flags & LOG_HTTP_EXTENDED) == 0) ||
+ ((http_ctx->flags & LOG_HTTP_EXTENDED) !=
+ (http_fields[f].flags & LOG_HTTP_EXTENDED)))
+ {
+ htp_header_t *h_field = NULL;
+ if ((http_fields[f].flags & LOG_HTTP_REQUEST) != 0)
+ {
+ if (tx->request_headers != NULL) {
+ h_field = htp_table_get_c(tx->request_headers,
+ http_fields[f].htp_field);
+ }
+ } else {
+ if (tx->response_headers != NULL) {
+ h_field = htp_table_get_c(tx->response_headers,
+ http_fields[f].htp_field);
+ }
+ }
+ if (h_field != NULL) {
+ c = bstr_util_strdup_to_c(h_field->value);
+ if (c != NULL) {
+ json_object_set_new(js,
+ http_fields[f].config_field,
+ json_string(c));
+ SCFree(c);
+ }
+ }
+ }
+ }
+ }
+}
+
+void JsonHttpLogJSONExtended(json_t *js, htp_tx_t *tx)
+{
+ char *c;
+
+ /* referer */
+ htp_header_t *h_referer = NULL;
+ if (tx->request_headers != NULL) {
+ h_referer = htp_table_get_c(tx->request_headers, "referer");
+ }
+ if (h_referer != NULL) {
+ c = bstr_util_strdup_to_c(h_referer->value);
+ if (c != NULL) {
+ json_object_set_new(js, "http_refer", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* method */
+ if (tx->request_method != NULL) {
+ c = bstr_util_strdup_to_c(tx->request_method);
+ if (c != NULL) {
+ json_object_set_new(js, "http_method", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* protocol */
+ if (tx->request_protocol != NULL) {
+ c = bstr_util_strdup_to_c(tx->request_protocol);
+ if (c != NULL) {
+ json_object_set_new(js, "protocol", json_string(c));
+ SCFree(c);
+ }
+ }
+
+ /* response status */
+ if (tx->response_status != NULL) {
+ c = bstr_util_strdup_to_c(tx->response_status);
+ if (c != NULL) {
+ unsigned int val = strtoul(c, NULL, 10);
+ json_object_set_new(js, "status", json_integer(val));
+ SCFree(c);
+ }
+
+ htp_header_t *h_location = htp_table_get_c(tx->response_headers, "location");
+ if (h_location != NULL) {
+ c = bstr_util_strdup_to_c(h_location->value);
+ if (c != NULL) {
+ json_object_set_new(js, "redirect", json_string(c));
+ SCFree(c);
+ }
+ }
+ }
+
+ /* length */
+ json_object_set_new(js, "length", json_integer(tx->response_message_len));
+}
+
+/* JSON format logging */
+static void JsonHttpLogJSON(JsonHttpLogThread *aft, json_t *js, htp_tx_t *tx, uint64_t tx_id)
+{
+ LogHttpFileCtx *http_ctx = aft->httplog_ctx;
+ json_t *hjs = json_object();
+ if (hjs == NULL) {
+ return;
+ }
+
+ JsonHttpLogJSONBasic(hjs, tx);
+ /* log custom fields if configured */
+ if (http_ctx->fields != 0)
+ JsonHttpLogJSONCustom(http_ctx, hjs, tx);
+ if (http_ctx->flags & LOG_HTTP_EXTENDED)
+ JsonHttpLogJSONExtended(hjs, tx);
+
+ /* tx id for correlation with alerts */
+ json_object_set_new(hjs, "tx_id", json_integer(tx_id));
+
+ json_object_set_new(js, "http", hjs);
+}
+
+static int JsonHttpLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
+{
+ SCEnter();
+
+ htp_tx_t *tx = txptr;
+ JsonHttpLogThread *jhl = (JsonHttpLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)jhl->buffer;
+
+ json_t *js = CreateJSONHeader((Packet *)p, 1, "http"); //TODO const
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+
+ SCLogDebug("got a HTTP request and now logging !!");
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ JsonHttpLogJSON(jhl, js, tx, tx_id);
+
+ OutputJSONBuffer(js, jhl->httplog_ctx->file_ctx, buffer);
+ json_object_del(js, "http");
+
+ json_object_clear(js);
+ json_decref(js);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void OutputHttpLogDeinit(OutputCtx *output_ctx)
+{
+ LogHttpFileCtx *http_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = http_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(http_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "http.json"
+OutputCtx *OutputHttpLogInit(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogHttpFileCtx *http_ctx = SCMalloc(sizeof(LogHttpFileCtx));
+ if (unlikely(http_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(http_ctx);
+ return NULL;
+ }
+
+ http_ctx->file_ctx = file_ctx;
+ http_ctx->flags = LOG_HTTP_DEFAULT;
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ http_ctx->flags = LOG_HTTP_EXTENDED;
+ }
+ }
+ }
+ output_ctx->data = http_ctx;
+ output_ctx->DeInit = OutputHttpLogDeinit;
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP);
+
+ return output_ctx;
+}
+
+static void OutputHttpLogDeinitSub(OutputCtx *output_ctx)
+{
+ LogHttpFileCtx *http_ctx = output_ctx->data;
+ SCFree(http_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputHttpLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ LogHttpFileCtx *http_ctx = SCMalloc(sizeof(LogHttpFileCtx));
+ if (unlikely(http_ctx == NULL))
+ return NULL;
+ memset(http_ctx, 0x00, sizeof(*http_ctx));
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(http_ctx);
+ return NULL;
+ }
+
+ http_ctx->file_ctx = ojc->file_ctx;
+ http_ctx->flags = LOG_HTTP_DEFAULT;
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ http_ctx->flags = LOG_HTTP_EXTENDED;
+ }
+ }
+
+ ConfNode *custom;
+ if ((custom = ConfNodeLookupChild(conf, "custom")) != NULL) {
+ ConfNode *field;
+ TAILQ_FOREACH(field, &custom->head, next)
+ {
+ if (field != NULL)
+ {
+ HttpField f;
+ for (f = HTTP_FIELD_ACCEPT; f < HTTP_FIELD_SIZE; f++)
+ {
+ if ((strcmp(http_fields[f].config_field,
+ field->val) == 0) ||
+ (strcasecmp(http_fields[f].htp_field,
+ field->val) == 0))
+ {
+ http_ctx->fields |= (1ULL<<f);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ output_ctx->data = http_ctx;
+ output_ctx->DeInit = OutputHttpLogDeinitSub;
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP);
+
+ return output_ctx;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonHttpLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonHttpLogThread *aft = SCMalloc(sizeof(JsonHttpLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonHttpLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->httplog_ctx = ((OutputCtx *)initdata)->data; //TODO
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonHttpLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonHttpLogThread *aft = (JsonHttpLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonHttpLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void TmModuleJsonHttpLogRegister (void)
+{
+ tmm_modules[TMM_JSONHTTPLOG].name = "JsonHttpLog";
+ tmm_modules[TMM_JSONHTTPLOG].ThreadInit = JsonHttpLogThreadInit;
+ tmm_modules[TMM_JSONHTTPLOG].ThreadDeinit = JsonHttpLogThreadDeinit;
+ tmm_modules[TMM_JSONHTTPLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONHTTPLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONHTTPLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterTxModule("JsonHttpLog", "http-json-log", OutputHttpLogInit,
+ ALPROTO_HTTP, JsonHttpLogger);
+
+ /* also register as child of eve-log */
+ OutputRegisterTxSubModule("eve-log", "JsonHttpLog", "eve-log.http", OutputHttpLogInitSub,
+ ALPROTO_HTTP, JsonHttpLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonHttpLogRegister (void)
+{
+ tmm_modules[TMM_JSONHTTPLOG].name = "JsonHttpLog";
+ tmm_modules[TMM_JSONHTTPLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-http.h b/framework/src/suricata/src/output-json-http.h
new file mode 100644
index 00000000..ab412d22
--- /dev/null
+++ b/framework/src/suricata/src/output-json-http.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_HTTP_H__
+#define __OUTPUT_JSON_HTTP_H__
+
+void TmModuleJsonHttpLogRegister (void);
+
+#ifdef HAVE_LIBJANSSON
+void JsonHttpLogJSONBasic(json_t *js, htp_tx_t *tx);
+void JsonHttpLogJSONExtended(json_t *js, htp_tx_t *tx);
+#endif /* HAVE_LIBJANSSON */
+
+#endif /* __OUTPUT_JSON_HTTP_H__ */
+
diff --git a/framework/src/suricata/src/output-json-netflow.c b/framework/src/suricata/src/output-json-netflow.c
new file mode 100644
index 00000000..153beb3d
--- /dev/null
+++ b/framework/src/suricata/src/output-json-netflow.c
@@ -0,0 +1,467 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements Unidirectiontal NetFlow JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+#include "output-json.h"
+
+#include "stream-tcp-private.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+typedef struct LogJsonFileCtx_ {
+ LogFileCtx *file_ctx;
+} LogJsonFileCtx;
+
+typedef struct JsonNetFlowLogThread_ {
+ LogJsonFileCtx *flowlog_ctx;
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+
+ MemBuffer *buffer;
+} JsonNetFlowLogThread;
+
+
+static json_t *CreateJSONHeaderFromFlow(Flow *f, char *event_type, int dir)
+{
+ char timebuf[64];
+ char srcip[46], dstip[46];
+ Port sp, dp;
+
+ json_t *js = json_object();
+ if (unlikely(js == NULL))
+ return NULL;
+
+ struct timeval tv;
+ memset(&tv, 0x00, sizeof(tv));
+ TimeGet(&tv);
+
+ CreateIsoTimeString(&tv, timebuf, sizeof(timebuf));
+
+ srcip[0] = '\0';
+ dstip[0] = '\0';
+ if (FLOW_IS_IPV4(f)) {
+ if (dir == 0) {
+ PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
+ } else {
+ PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), dstip, sizeof(dstip));
+ }
+ } else if (FLOW_IS_IPV6(f)) {
+ if (dir == 0) {
+ PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
+ } else {
+ PrintInet(AF_INET6, (const void *)&(f->dst.address), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)&(f->src.address), dstip, sizeof(dstip));
+ }
+ }
+
+ if (dir == 0) {
+ sp = f->sp;
+ dp = f->dp;
+ } else {
+ sp = f->dp;
+ dp = f->sp;
+ }
+
+ char proto[16];
+ if (SCProtoNameValid(f->proto) == TRUE) {
+ strlcpy(proto, known_proto[f->proto], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "%03" PRIu32, f->proto);
+ }
+
+ /* time */
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ CreateJSONFlowId(js, (const Flow *)f);
+
+#if 0 // TODO
+ /* sensor id */
+ if (sensor_id >= 0)
+ json_object_set_new(js, "sensor_id", json_integer(sensor_id));
+#endif
+ if (event_type) {
+ json_object_set_new(js, "event_type", json_string(event_type));
+ }
+#if 0
+ /* vlan */
+ if (f->vlan_id[0] > 0) {
+ json_t *js_vlan;
+ switch (f->vlan_idx) {
+ case 1:
+ json_object_set_new(js, "vlan",
+ json_integer(f->vlan_id[0]));
+ break;
+ case 2:
+ js_vlan = json_array();
+ if (unlikely(js != NULL)) {
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID1(p)));
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID2(p)));
+ json_object_set_new(js, "vlan", js_vlan);
+ }
+ break;
+ default:
+ /* shouldn't get here */
+ break;
+ }
+ }
+#endif
+ /* tuple */
+ json_object_set_new(js, "src_ip", json_string(srcip));
+ switch(f->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "src_port", json_integer(sp));
+ break;
+ }
+ json_object_set_new(js, "dest_ip", json_string(dstip));
+ switch(f->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "dest_port", json_integer(dp));
+ break;
+ }
+ json_object_set_new(js, "proto", json_string(proto));
+ switch (f->proto) {
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ json_object_set_new(js, "icmp_type",
+ json_integer(f->type));
+ json_object_set_new(js, "icmp_code",
+ json_integer(f->code));
+ break;
+ }
+ return js;
+}
+
+/* JSON format logging */
+static void JsonNetFlowLogJSONToServer(JsonNetFlowLogThread *aft, json_t *js, Flow *f)
+{
+ json_t *hjs = json_object();
+ if (hjs == NULL) {
+ return;
+ }
+
+ json_object_set_new(hjs, "app_proto",
+ json_string(AppProtoToString(f->alproto_ts ? f->alproto_ts : f->alproto)));
+
+ json_object_set_new(hjs, "pkts",
+ json_integer(f->todstpktcnt));
+ json_object_set_new(hjs, "bytes",
+ json_integer(f->todstbytecnt));
+
+ char timebuf1[64], timebuf2[64];
+
+ CreateIsoTimeString(&f->startts, timebuf1, sizeof(timebuf1));
+ CreateIsoTimeString(&f->lastts, timebuf2, sizeof(timebuf2));
+
+ json_object_set_new(hjs, "start", json_string(timebuf1));
+ json_object_set_new(hjs, "end", json_string(timebuf2));
+
+ int32_t age = f->lastts.tv_sec - f->startts.tv_sec;
+ json_object_set_new(hjs, "age",
+ json_integer(age));
+
+ json_object_set_new(js, "netflow", hjs);
+
+ /* TCP */
+ if (f->proto == IPPROTO_TCP) {
+ json_t *tjs = json_object();
+ if (tjs == NULL) {
+ return;
+ }
+
+ TcpSession *ssn = f->protoctx;
+
+ char hexflags[3] = "";
+ snprintf(hexflags, sizeof(hexflags), "%02x",
+ ssn ? ssn->client.tcp_flags : 0);
+ json_object_set_new(tjs, "tcp_flags", json_string(hexflags));
+
+ JsonTcpFlags(ssn ? ssn->client.tcp_flags : 0, tjs);
+
+ json_object_set_new(js, "tcp", tjs);
+ }
+}
+
+static void JsonNetFlowLogJSONToClient(JsonNetFlowLogThread *aft, json_t *js, Flow *f)
+{
+ json_t *hjs = json_object();
+ if (hjs == NULL) {
+ return;
+ }
+
+ json_object_set_new(hjs, "app_proto",
+ json_string(AppProtoToString(f->alproto_tc ? f->alproto_tc : f->alproto)));
+
+ json_object_set_new(hjs, "pkts",
+ json_integer(f->tosrcpktcnt));
+ json_object_set_new(hjs, "bytes",
+ json_integer(f->tosrcbytecnt));
+
+ char timebuf1[64], timebuf2[64];
+
+ CreateIsoTimeString(&f->startts, timebuf1, sizeof(timebuf1));
+ CreateIsoTimeString(&f->lastts, timebuf2, sizeof(timebuf2));
+
+ json_object_set_new(hjs, "start", json_string(timebuf1));
+ json_object_set_new(hjs, "end", json_string(timebuf2));
+
+ int32_t age = f->lastts.tv_sec - f->startts.tv_sec;
+ json_object_set_new(hjs, "age",
+ json_integer(age));
+
+ json_object_set_new(js, "netflow", hjs);
+
+ /* TCP */
+ if (f->proto == IPPROTO_TCP) {
+ json_t *tjs = json_object();
+ if (tjs == NULL) {
+ return;
+ }
+
+ TcpSession *ssn = f->protoctx;
+
+ char hexflags[3] = "";
+ snprintf(hexflags, sizeof(hexflags), "%02x",
+ ssn ? ssn->server.tcp_flags : 0);
+ json_object_set_new(tjs, "tcp_flags", json_string(hexflags));
+
+ JsonTcpFlags(ssn ? ssn->server.tcp_flags : 0, tjs);
+
+ json_object_set_new(js, "tcp", tjs);
+ }
+}
+
+static int JsonNetFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
+{
+ SCEnter();
+ JsonNetFlowLogThread *jhl = (JsonNetFlowLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)jhl->buffer;
+
+ /* reset */
+ MemBufferReset(buffer);
+ json_t *js = CreateJSONHeaderFromFlow(f, "netflow", 0); //TODO const
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+ JsonNetFlowLogJSONToServer(jhl, js, f);
+ OutputJSONBuffer(js, jhl->flowlog_ctx->file_ctx, buffer);
+ json_object_del(js, "netflow");
+ json_object_clear(js);
+ json_decref(js);
+
+ /* reset */
+ MemBufferReset(buffer);
+ js = CreateJSONHeaderFromFlow(f, "netflow", 1); //TODO const
+ if (unlikely(js == NULL))
+ return TM_ECODE_OK;
+ JsonNetFlowLogJSONToClient(jhl, js, f);
+ OutputJSONBuffer(js, jhl->flowlog_ctx->file_ctx, buffer);
+ json_object_del(js, "netflow");
+ json_object_clear(js);
+ json_decref(js);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void OutputNetFlowLogDeinit(OutputCtx *output_ctx)
+{
+ LogJsonFileCtx *flow_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = flow_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(flow_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "netflow.json"
+OutputCtx *OutputNetFlowLogInit(ConfNode *conf)
+{
+ SCLogInfo("hi");
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ LogJsonFileCtx *flow_ctx = SCMalloc(sizeof(LogJsonFileCtx));
+ if (unlikely(flow_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(flow_ctx);
+ return NULL;
+ }
+
+ flow_ctx->file_ctx = file_ctx;
+ output_ctx->data = flow_ctx;
+ output_ctx->DeInit = OutputNetFlowLogDeinit;
+
+ return output_ctx;
+}
+
+static void OutputNetFlowLogDeinitSub(OutputCtx *output_ctx)
+{
+ LogJsonFileCtx *flow_ctx = output_ctx->data;
+ SCFree(flow_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputNetFlowLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ LogJsonFileCtx *flow_ctx = SCMalloc(sizeof(LogJsonFileCtx));
+ if (unlikely(flow_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(flow_ctx);
+ return NULL;
+ }
+
+ flow_ctx->file_ctx = ojc->file_ctx;
+
+ output_ctx->data = flow_ctx;
+ output_ctx->DeInit = OutputNetFlowLogDeinitSub;
+
+ return output_ctx;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonNetFlowLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonNetFlowLogThread *aft = SCMalloc(sizeof(JsonNetFlowLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonNetFlowLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->flowlog_ctx = ((OutputCtx *)initdata)->data; //TODO
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonNetFlowLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonNetFlowLogThread *aft = (JsonNetFlowLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonNetFlowLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void TmModuleJsonNetFlowLogRegister (void)
+{
+ tmm_modules[TMM_JSONNETFLOWLOG].name = "JsonNetFlowLog";
+ tmm_modules[TMM_JSONNETFLOWLOG].ThreadInit = JsonNetFlowLogThreadInit;
+ tmm_modules[TMM_JSONNETFLOWLOG].ThreadDeinit = JsonNetFlowLogThreadDeinit;
+ tmm_modules[TMM_JSONNETFLOWLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONNETFLOWLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONNETFLOWLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterFlowModule("JsonNetFlowLog", "netflow-json-log",
+ OutputNetFlowLogInit, JsonNetFlowLogger);
+
+ /* also register as child of eve-log */
+ OutputRegisterFlowSubModule("eve-log", "JsonNetFlowLog", "eve-log.netflow",
+ OutputNetFlowLogInitSub, JsonNetFlowLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonNetFlowLogRegister (void)
+{
+ tmm_modules[TMM_JSONNETFLOWLOG].name = "JsonNetFlowLog";
+ tmm_modules[TMM_JSONNETFLOWLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-netflow.h b/framework/src/suricata/src/output-json-netflow.h
new file mode 100644
index 00000000..361cc3a0
--- /dev/null
+++ b/framework/src/suricata/src/output-json-netflow.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __OUTPUT_JSON_NETFLOW_H__
+#define __OUTPUT_JSON_NETFLOW_H__
+
+void TmModuleJsonNetFlowLogRegister (void);
+
+#endif /* __OUTPUT_JSON_FLOW_H__ */
diff --git a/framework/src/suricata/src/output-json-smtp.c b/framework/src/suricata/src/output-json-smtp.c
new file mode 100644
index 00000000..f722c383
--- /dev/null
+++ b/framework/src/suricata/src/output-json-smtp.c
@@ -0,0 +1,224 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements SMTP JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-smtp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#include "output-json.h"
+#include "output-json-email-common.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+static int JsonSmtpLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
+{
+ SCEnter();
+ int r = JsonEmailLogger(tv, thread_data, p, f, state, tx, tx_id);
+ SCReturnInt(r);
+}
+
+static void OutputSmtpLogDeInitCtx(OutputCtx *output_ctx)
+{
+ OutputJsonEmailCtx *email_ctx = output_ctx->data;
+ if (email_ctx != NULL) {
+ LogFileFreeCtx(email_ctx->file_ctx);
+ SCFree(email_ctx);
+ }
+ SCFree(output_ctx);
+}
+
+static void OutputSmtpLogDeInitCtxSub(OutputCtx *output_ctx)
+{
+ SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
+ OutputJsonEmailCtx *email_ctx = output_ctx->data;
+ if (email_ctx != NULL) {
+ SCFree(email_ctx);
+ }
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "smtp.json"
+OutputCtx *OutputSmtpLogInit(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputJsonEmailCtx *email_ctx = SCMalloc(sizeof(OutputJsonEmailCtx));
+ if (unlikely(email_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(email_ctx);
+ return NULL;
+ }
+
+ email_ctx->file_ctx = file_ctx;
+
+ output_ctx->data = email_ctx;
+ output_ctx->DeInit = OutputSmtpLogDeInitCtx;
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SMTP);
+
+ return output_ctx;
+}
+
+static OutputCtx *OutputSmtpLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ OutputJsonEmailCtx *email_ctx = SCMalloc(sizeof(OutputJsonEmailCtx));
+ if (unlikely(email_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(email_ctx);
+ return NULL;
+ }
+
+ email_ctx->file_ctx = ojc->file_ctx;
+
+ output_ctx->data = email_ctx;
+ output_ctx->DeInit = OutputSmtpLogDeInitCtxSub;
+
+ /* enable the logger for the app layer */
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SMTP);
+
+ return output_ctx;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonSmtpLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonEmailLogThread *aft = SCMalloc(sizeof(JsonEmailLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonEmailLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->emaillog_ctx = ((OutputCtx *)initdata)->data;
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonSmtpLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonEmailLogThread *aft = (JsonEmailLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonEmailLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void TmModuleJsonSmtpLogRegister (void) {
+ tmm_modules[TMM_JSONSMTPLOG].name = "JsonSmtpLog";
+ tmm_modules[TMM_JSONSMTPLOG].ThreadInit = JsonSmtpLogThreadInit;
+ tmm_modules[TMM_JSONSMTPLOG].ThreadDeinit = JsonSmtpLogThreadDeinit;
+ tmm_modules[TMM_JSONSMTPLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONSMTPLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONSMTPLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterTxModule("JsonSmtpLog", "smtp-json-log",
+ OutputSmtpLogInit, ALPROTO_SMTP,
+ JsonSmtpLogger);
+
+ /* also register as child of eve-log */
+ OutputRegisterTxSubModule("eve-log", "JsonSmtpLog",
+ "eve-log.smtp",
+ OutputSmtpLogInitSub, ALPROTO_SMTP,
+ JsonSmtpLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonSmtpLogRegister (void)
+{
+ tmm_modules[TMM_JSONSMTPLOG].name = "JsonSmtpLog";
+ tmm_modules[TMM_JSONSMTPLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-smtp.h b/framework/src/suricata/src/output-json-smtp.h
new file mode 100644
index 00000000..d38187c7
--- /dev/null
+++ b/framework/src/suricata/src/output-json-smtp.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_SMTP_H__
+#define __OUTPUT_JSON_SMTP_H__
+
+void TmModuleJsonSmtpLogRegister (void);
+
+#endif /* __OUTPUT_JSON_SMTP_H__ */
diff --git a/framework/src/suricata/src/output-json-ssh.c b/framework/src/suricata/src/output-json-ssh.c
new file mode 100644
index 00000000..3dc4d10f
--- /dev/null
+++ b/framework/src/suricata/src/output-json-ssh.c
@@ -0,0 +1,351 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements SSH JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "app-layer-parser.h"
+#include "output.h"
+#include "app-layer-ssh.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#include "output-json.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+#define MODULE_NAME "LogSshLog"
+
+typedef struct OutputSshCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} OutputSshCtx;
+
+
+typedef struct JsonSshLogThread_ {
+ OutputSshCtx *sshlog_ctx;
+ MemBuffer *buffer;
+} JsonSshLogThread;
+
+
+void JsonSshLogJSON(json_t *tjs, SshState *ssh_state)
+{
+ json_t *cjs = json_object();
+ if (cjs != NULL) {
+ json_object_set_new(cjs, "proto_version",
+ json_string((char *)ssh_state->cli_hdr.proto_version));
+
+ json_object_set_new(cjs, "software_version",
+ json_string((char *)ssh_state->cli_hdr.software_version));
+ }
+ json_object_set_new(tjs, "client", cjs);
+
+ json_t *sjs = json_object();
+ if (sjs != NULL) {
+ json_object_set_new(sjs, "proto_version",
+ json_string((char *)ssh_state->srv_hdr.proto_version));
+
+ json_object_set_new(sjs, "software_version",
+ json_string((char *)ssh_state->srv_hdr.software_version));
+ }
+ json_object_set_new(tjs, "server", sjs);
+
+}
+
+static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ JsonSshLogThread *aft = (JsonSshLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ OutputSshCtx *ssh_ctx = aft->sshlog_ctx;
+
+ if (unlikely(p->flow == NULL)) {
+ return 0;
+ }
+
+ /* check if we have SSH state or not */
+ FLOWLOCK_WRLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_SSH)
+ goto end;
+
+ SshState *ssh_state = (SshState *)FlowGetAppState(p->flow);
+ if (unlikely(ssh_state == NULL)) {
+ goto end;
+ }
+
+ if (ssh_state->cli_hdr.software_version == NULL || ssh_state->srv_hdr.software_version == NULL)
+ goto end;
+
+ json_t *js = CreateJSONHeader((Packet *)p, 1, "ssh");//TODO
+ if (unlikely(js == NULL))
+ goto end;
+
+ json_t *tjs = json_object();
+ if (tjs == NULL) {
+ free(js);
+ goto end;
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ JsonSshLogJSON(tjs, ssh_state);
+
+ json_object_set_new(js, "ssh", tjs);
+
+ OutputJSONBuffer(js, ssh_ctx->file_ctx, buffer);
+ json_object_clear(js);
+ json_decref(js);
+
+ /* we only log the state once */
+ ssh_state->cli_hdr.flags |= SSH_FLAG_STATE_LOGGED;
+end:
+ FLOWLOCK_UNLOCK(p->flow);
+ return 0;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonSshLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonSshLogThread *aft = SCMalloc(sizeof(JsonSshLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonSshLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->sshlog_ctx = ((OutputCtx *)initdata)->data;
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonSshLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonSshLogThread *aft = (JsonSshLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonSshLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void OutputSshLogDeinit(OutputCtx *output_ctx)
+{
+ OutputSshLoggerDisable();
+
+ OutputSshCtx *ssh_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = ssh_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(ssh_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "ssh.json"
+OutputCtx *OutputSshLogInit(ConfNode *conf)
+{
+ if (OutputSshLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'ssh' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputSshCtx *ssh_ctx = SCMalloc(sizeof(OutputSshCtx));
+ if (unlikely(ssh_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(ssh_ctx);
+ return NULL;
+ }
+
+ ssh_ctx->file_ctx = file_ctx;
+
+ output_ctx->data = ssh_ctx;
+ output_ctx->DeInit = OutputSshLogDeinit;
+
+ return output_ctx;
+}
+
+static void OutputSshLogDeinitSub(OutputCtx *output_ctx)
+{
+ OutputSshLoggerDisable();
+
+ OutputSshCtx *ssh_ctx = output_ctx->data;
+ SCFree(ssh_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputSshLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ if (OutputSshLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'ssh' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ OutputSshCtx *ssh_ctx = SCMalloc(sizeof(OutputSshCtx));
+ if (unlikely(ssh_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(ssh_ctx);
+ return NULL;
+ }
+
+ ssh_ctx->file_ctx = ojc->file_ctx;
+
+ output_ctx->data = ssh_ctx;
+ output_ctx->DeInit = OutputSshLogDeinitSub;
+
+ return output_ctx;
+}
+
+/** \internal
+ * \brief Condition function for SSH logger
+ * \retval bool true or false -- log now?
+ */
+static int JsonSshCondition(ThreadVars *tv, const Packet *p)
+{
+ if (p->flow == NULL) {
+ return FALSE;
+ }
+
+ if (!(PKT_IS_TCP(p))) {
+ return FALSE;
+ }
+
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_SSH)
+ goto dontlog;
+
+ SshState *ssh_state = (SshState *)FlowGetAppState(p->flow);
+ if (ssh_state == NULL) {
+ SCLogDebug("no ssh state, so no logging");
+ goto dontlog;
+ }
+
+ /* we only log the state once */
+ if (ssh_state->cli_hdr.flags & SSH_FLAG_STATE_LOGGED)
+ goto dontlog;
+
+ if (ssh_state->cli_hdr.software_version == NULL ||
+ ssh_state->srv_hdr.software_version == NULL)
+ goto dontlog;
+
+ /* todo: logic to log once */
+
+ FLOWLOCK_UNLOCK(p->flow);
+ return TRUE;
+dontlog:
+ FLOWLOCK_UNLOCK(p->flow);
+ return FALSE;
+}
+
+void TmModuleJsonSshLogRegister (void)
+{
+ tmm_modules[TMM_JSONSSHLOG].name = "JsonSshLog";
+ tmm_modules[TMM_JSONSSHLOG].ThreadInit = JsonSshLogThreadInit;
+ tmm_modules[TMM_JSONSSHLOG].ThreadDeinit = JsonSshLogThreadDeinit;
+ tmm_modules[TMM_JSONSSHLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONSSHLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONSSHLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterPacketModule("JsonSshLog", "ssh-json-log", OutputSshLogInit,
+ JsonSshLogger, JsonSshCondition);
+
+ /* also register as child of eve-log */
+ OutputRegisterPacketSubModule("eve-log", "JsonSshLog", "eve-log.ssh", OutputSshLogInitSub,
+ JsonSshLogger, JsonSshCondition);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonSshLogRegister (void)
+{
+ tmm_modules[TMM_JSONSSHLOG].name = "JsonSshLog";
+ tmm_modules[TMM_JSONSSHLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-ssh.h b/framework/src/suricata/src/output-json-ssh.h
new file mode 100644
index 00000000..5d0752bf
--- /dev/null
+++ b/framework/src/suricata/src/output-json-ssh.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __OUTPUT_JSON_SSH_H__
+#define __OUTPUT_JSON_SSH_H__
+
+void TmModuleJsonSshLogRegister (void);
+
+#ifdef HAVE_LIBJANSSON
+#include "app-layer-ssh.h"
+
+void JsonSshLogJSON(json_t *js, SshState *tx);
+#endif
+
+#endif /* __OUTPUT_JSON_SSH_H__ */
diff --git a/framework/src/suricata/src/output-json-stats.c b/framework/src/suricata/src/output-json-stats.c
new file mode 100644
index 00000000..fea7177a
--- /dev/null
+++ b/framework/src/suricata/src/output-json-stats.c
@@ -0,0 +1,387 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements JSON stats counters logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "output.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#include "output-json.h"
+
+#define MODULE_NAME "JsonStatsLog"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+#define JSON_STATS_TOTALS (1<<0)
+#define JSON_STATS_THREADS (1<<1)
+#define JSON_STATS_DELTAS (1<<2)
+
+typedef struct OutputStatsCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} OutputStatsCtx;
+
+typedef struct JsonStatsLogThread_ {
+ OutputStatsCtx *statslog_ctx;
+ MemBuffer *buffer;
+} JsonStatsLogThread;
+
+static json_t *OutputStats2Json(json_t *js, const char *key)
+{
+ void *iter;
+
+ const char *dot = index(key, '.');
+ if (dot == NULL)
+ return NULL;
+
+ size_t predot_len = (dot - key) + 1;
+ char s[predot_len];
+ strlcpy(s, key, predot_len);
+
+ iter = json_object_iter_at(js, s);
+ const char *s2 = index(dot+1, '.');
+
+ json_t *value = json_object_iter_value(iter);
+ if (value == NULL) {
+ value = json_object();
+ json_object_set_new(js, s, value);
+ }
+ if (s2 != NULL) {
+ return OutputStats2Json(value, &key[dot-key+1]);
+ }
+ return value;
+}
+
+static int JsonStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
+{
+ SCEnter();
+ JsonStatsLogThread *aft = (JsonStatsLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ const char delta_suffix[] = "_delta";
+
+ struct timeval tval;
+ gettimeofday(&tval, NULL);
+
+ json_t *js = json_object();
+ if (unlikely(js == NULL))
+ return 0;
+
+ char timebuf[64];
+ CreateIsoTimeString(&tval, timebuf, sizeof(timebuf));
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ json_object_set_new(js, "event_type", json_string("stats"));
+ json_t *js_stats = json_object();
+ if (unlikely(js_stats == NULL)) {
+ json_decref(js);
+ return 0;
+ }
+
+ /* Uptime, in seconds. */
+ json_object_set_new(js_stats, "uptime",
+ json_integer((int)difftime(tval.tv_sec, st->start_time)));
+
+ uint32_t u = 0;
+ if (aft->statslog_ctx->flags & JSON_STATS_TOTALS) {
+ for (u = 0; u < st->nstats; u++) {
+ if (st->stats[u].name == NULL)
+ continue;
+ const char *name = st->stats[u].name;
+ const char *shortname = name;
+ if (rindex(name, '.') != NULL) {
+ shortname = &name[rindex(name, '.') - name + 1];
+ }
+ json_t *js_type = OutputStats2Json(js_stats, name);
+ if (js_type != NULL) {
+ json_object_set_new(js_type, shortname,
+ json_integer(st->stats[u].value));
+ if (aft->statslog_ctx->flags & JSON_STATS_DELTAS) {
+ char deltaname[strlen(shortname) + strlen(delta_suffix) + 1];
+ snprintf(deltaname, sizeof(deltaname), "%s%s", shortname,
+ delta_suffix);
+ json_object_set_new(js_type, deltaname,
+ json_integer(st->stats[u].value - st->stats[u].pvalue));
+ }
+ }
+ }
+ }
+
+ /* per thread stats - stored in a "threads" object. */
+ if (st->tstats != NULL && (aft->statslog_ctx->flags & JSON_STATS_THREADS)) {
+ /* for each thread (store) */
+ json_t *threads = json_object();
+ if (unlikely(threads == NULL)) {
+ json_decref(js);
+ return 0;
+ }
+ uint32_t x;
+ for (x = 0; x < st->ntstats; x++) {
+ uint32_t offset = x * st->nstats;
+
+ /* for each counter */
+ for (u = offset; u < (offset + st->nstats); u++) {
+ if (st->tstats[u].name == NULL)
+ continue;
+
+ char str[256];
+ snprintf(str, sizeof(str), "%s.%s", st->tstats[u].tm_name, st->tstats[u].name);
+ char *shortname = &str[rindex(str, '.') - str + 1];
+ json_t *js_type = OutputStats2Json(threads, str);
+
+ if (js_type != NULL) {
+ json_object_set_new(js_type, shortname, json_integer(st->tstats[u].value));
+
+ if (aft->statslog_ctx->flags & JSON_STATS_DELTAS) {
+ char deltaname[strlen(shortname) + strlen(delta_suffix) + 1];
+ snprintf(deltaname, sizeof(deltaname), "%s%s",
+ shortname, delta_suffix);
+ json_object_set_new(js_type, deltaname,
+ json_integer(st->tstats[u].value - st->tstats[u].pvalue));
+ }
+ }
+ }
+ }
+ json_object_set_new(js_stats, "threads", threads);
+ }
+
+ json_object_set_new(js, "stats", js_stats);
+
+ OutputJSONBuffer(js, aft->statslog_ctx->file_ctx, buffer);
+ MemBufferReset(buffer);
+
+ json_object_clear(js_stats);
+ json_object_del(js, "stats");
+ json_object_clear(js);
+ json_decref(js);
+
+ SCReturnInt(0);
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonStatsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonStatsLogThread *aft = SCMalloc(sizeof(JsonStatsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonStatsLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for json stats. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->statslog_ctx = ((OutputCtx *)initdata)->data;
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonStatsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonStatsLogThread *aft = (JsonStatsLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonStatsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void OutputStatsLogDeinit(OutputCtx *output_ctx)
+{
+
+ OutputStatsCtx *stats_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = stats_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(stats_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "stats.json"
+OutputCtx *OutputStatsLogInit(ConfNode *conf)
+{
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputStatsCtx *stats_ctx = SCMalloc(sizeof(OutputStatsCtx));
+ if (unlikely(stats_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+ stats_ctx->flags = JSON_STATS_TOTALS;
+
+ if (conf != NULL) {
+ const char *totals = ConfNodeLookupChildValue(conf, "totals");
+ const char *threads = ConfNodeLookupChildValue(conf, "threads");
+ const char *deltas = ConfNodeLookupChildValue(conf, "deltas");
+ SCLogDebug("totals %s threads %s deltas %s", totals, threads, deltas);
+
+ if (totals != NULL && ConfValIsFalse(totals)) {
+ stats_ctx->flags &= ~JSON_STATS_TOTALS;
+ }
+ if (threads != NULL && ConfValIsTrue(threads)) {
+ stats_ctx->flags |= JSON_STATS_THREADS;
+ }
+ if (deltas != NULL && ConfValIsTrue(deltas)) {
+ stats_ctx->flags |= JSON_STATS_DELTAS;
+ }
+ SCLogDebug("stats_ctx->flags %08x", stats_ctx->flags);
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(stats_ctx);
+ return NULL;
+ }
+
+ stats_ctx->file_ctx = file_ctx;
+
+ output_ctx->data = stats_ctx;
+ output_ctx->DeInit = OutputStatsLogDeinit;
+
+ return output_ctx;
+}
+
+static void OutputStatsLogDeinitSub(OutputCtx *output_ctx)
+{
+ OutputStatsCtx *stats_ctx = output_ctx->data;
+ SCFree(stats_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputStatsLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ AlertJsonThread *ajt = parent_ctx->data;
+
+ OutputStatsCtx *stats_ctx = SCMalloc(sizeof(OutputStatsCtx));
+ if (unlikely(stats_ctx == NULL))
+ return NULL;
+
+ stats_ctx->flags = JSON_STATS_TOTALS;
+
+ if (conf != NULL) {
+ const char *totals = ConfNodeLookupChildValue(conf, "totals");
+ const char *threads = ConfNodeLookupChildValue(conf, "threads");
+ const char *deltas = ConfNodeLookupChildValue(conf, "deltas");
+ SCLogDebug("totals %s threads %s deltas %s", totals, threads, deltas);
+
+ if (totals != NULL && ConfValIsFalse(totals)) {
+ stats_ctx->flags &= ~JSON_STATS_TOTALS;
+ }
+ if (threads != NULL && ConfValIsTrue(threads)) {
+ stats_ctx->flags |= JSON_STATS_THREADS;
+ }
+ if (deltas != NULL && ConfValIsTrue(deltas)) {
+ stats_ctx->flags |= JSON_STATS_DELTAS;
+ }
+ SCLogDebug("stats_ctx->flags %08x", stats_ctx->flags);
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(stats_ctx);
+ return NULL;
+ }
+
+ stats_ctx->file_ctx = ajt->file_ctx;
+
+ output_ctx->data = stats_ctx;
+ output_ctx->DeInit = OutputStatsLogDeinitSub;
+
+ return output_ctx;
+}
+
+void TmModuleJsonStatsLogRegister (void) {
+ tmm_modules[TMM_JSONSTATSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONSTATSLOG].ThreadInit = JsonStatsLogThreadInit;
+ tmm_modules[TMM_JSONSTATSLOG].ThreadDeinit = JsonStatsLogThreadDeinit;
+ tmm_modules[TMM_JSONSTATSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONSTATSLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONSTATSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterStatsModule(MODULE_NAME, "stats-json", OutputStatsLogInit,
+ JsonStatsLogger);
+
+ /* also register as child of eve-log */
+ OutputRegisterStatsSubModule("eve-log", MODULE_NAME, "eve-log.stats",
+ OutputStatsLogInitSub, JsonStatsLogger);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonStatsLogRegister (void)
+{
+ tmm_modules[TMM_JSONSTATSLOG].name = MODULE_NAME;
+ tmm_modules[TMM_JSONSTATSLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-stats.h b/framework/src/suricata/src/output-json-stats.h
new file mode 100644
index 00000000..9ad1d9b8
--- /dev/null
+++ b/framework/src/suricata/src/output-json-stats.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_COUNTERS_H__
+#define __OUTPUT_JSON_COUNTERS_H__
+
+void TmModuleJsonStatsLogRegister (void);
+
+#endif /* __OUTPUT_JSON_COUNTERS_H__ */
diff --git a/framework/src/suricata/src/output-json-tls.c b/framework/src/suricata/src/output-json-tls.c
new file mode 100644
index 00000000..4cbd21ae
--- /dev/null
+++ b/framework/src/suricata/src/output-json-tls.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Implements TLS JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "app-layer-parser.h"
+#include "output.h"
+#include "app-layer-ssl.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#include "output-json.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+SC_ATOMIC_DECLARE(unsigned int, cert_id);
+
+#define MODULE_NAME "LogTlsLog"
+
+#define LOG_TLS_DEFAULT 0
+#define LOG_TLS_EXTENDED (1 << 0)
+
+typedef struct OutputTlsCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags; /** Store mode */
+} OutputTlsCtx;
+
+
+typedef struct JsonTlsLogThread_ {
+ OutputTlsCtx *tlslog_ctx;
+ MemBuffer *buffer;
+} JsonTlsLogThread;
+
+#define SSL_VERSION_LENGTH 13
+
+void JsonTlsLogJSONBasic(json_t *js, SSLState *ssl_state)
+{
+ /* tls.subject */
+ json_object_set_new(js, "subject",
+ json_string(ssl_state->server_connp.cert0_subject));
+
+ /* tls.issuerdn */
+ json_object_set_new(js, "issuerdn",
+ json_string(ssl_state->server_connp.cert0_issuerdn));
+
+}
+
+void JsonTlsLogJSONExtended(json_t *tjs, SSLState * state)
+{
+ char ssl_version[SSL_VERSION_LENGTH + 1];
+
+ /* tls.fingerprint */
+ json_object_set_new(tjs, "fingerprint",
+ json_string(state->server_connp.cert0_fingerprint));
+
+ /* tls.version */
+ switch (state->server_connp.version) {
+ case TLS_VERSION_UNKNOWN:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "UNDETERMINED");
+ break;
+ case SSL_VERSION_2:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "SSLv2");
+ break;
+ case SSL_VERSION_3:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "SSLv3");
+ break;
+ case TLS_VERSION_10:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "TLSv1");
+ break;
+ case TLS_VERSION_11:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "TLS 1.1");
+ break;
+ case TLS_VERSION_12:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "TLS 1.2");
+ break;
+ default:
+ snprintf(ssl_version, SSL_VERSION_LENGTH, "0x%04x",
+ state->server_connp.version);
+ break;
+ }
+ json_object_set_new(tjs, "version", json_string(ssl_version));
+}
+
+static int JsonTlsLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ JsonTlsLogThread *aft = (JsonTlsLogThread *)thread_data;
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ OutputTlsCtx *tls_ctx = aft->tlslog_ctx;
+
+ if (unlikely(p->flow == NULL)) {
+ return 0;
+ }
+
+ /* check if we have TLS state or not */
+ FLOWLOCK_WRLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto end;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (unlikely(ssl_state == NULL)) {
+ goto end;
+ }
+
+ if (ssl_state->server_connp.cert0_issuerdn == NULL || ssl_state->server_connp.cert0_subject == NULL)
+ goto end;
+
+ json_t *js = CreateJSONHeader((Packet *)p, 0, "tls");//TODO
+ if (unlikely(js == NULL))
+ goto end;
+
+ json_t *tjs = json_object();
+ if (tjs == NULL) {
+ free(js);
+ goto end;
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+
+ JsonTlsLogJSONBasic(tjs, ssl_state);
+
+ if (tls_ctx->flags & LOG_TLS_EXTENDED) {
+ JsonTlsLogJSONExtended(tjs, ssl_state);
+ }
+
+ json_object_set_new(js, "tls", tjs);
+
+ OutputJSONBuffer(js, tls_ctx->file_ctx, buffer);
+ json_object_clear(js);
+ json_decref(js);
+
+ /* we only log the state once */
+ ssl_state->flags |= SSL_AL_FLAG_STATE_LOGGED;
+end:
+ FLOWLOCK_UNLOCK(p->flow);
+ return 0;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonTlsLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ JsonTlsLogThread *aft = SCMalloc(sizeof(JsonTlsLogThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(JsonTlsLogThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for HTTPLog. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ /* Use the Ouptut Context (file pointer and mutex) */
+ aft->tlslog_ctx = ((OutputCtx *)initdata)->data;
+
+ aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (aft->buffer == NULL) {
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonTlsLogThreadDeinit(ThreadVars *t, void *data)
+{
+ JsonTlsLogThread *aft = (JsonTlsLogThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ MemBufferFree(aft->buffer);
+ /* clear memory */
+ memset(aft, 0, sizeof(JsonTlsLogThread));
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+static void OutputTlsLogDeinit(OutputCtx *output_ctx)
+{
+ OutputTlsLoggerDisable();
+
+ OutputTlsCtx *tls_ctx = output_ctx->data;
+ LogFileCtx *logfile_ctx = tls_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(tls_ctx);
+ SCFree(output_ctx);
+}
+
+#define DEFAULT_LOG_FILENAME "tls.json"
+OutputCtx *OutputTlsLogInit(ConfNode *conf)
+{
+ if (OutputTlsLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'tls' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ LogFileCtx *file_ctx = LogFileNewCtx();
+ if(file_ctx == NULL) {
+ SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+ return NULL;
+ }
+
+ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputTlsCtx *tls_ctx = SCMalloc(sizeof(OutputTlsCtx));
+ if (unlikely(tls_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(file_ctx);
+ SCFree(tls_ctx);
+ return NULL;
+ }
+
+ tls_ctx->file_ctx = file_ctx;
+ tls_ctx->flags = LOG_TLS_DEFAULT;
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ tls_ctx->flags = LOG_TLS_EXTENDED;
+ }
+ }
+ }
+ output_ctx->data = tls_ctx;
+ output_ctx->DeInit = OutputTlsLogDeinit;
+
+ return output_ctx;
+}
+
+static void OutputTlsLogDeinitSub(OutputCtx *output_ctx)
+{
+ OutputTlsLoggerDisable();
+
+ OutputTlsCtx *tls_ctx = output_ctx->data;
+ SCFree(tls_ctx);
+ SCFree(output_ctx);
+}
+
+OutputCtx *OutputTlsLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ojc = parent_ctx->data;
+
+ if (OutputTlsLoggerEnable() != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'tls' logger "
+ "can be enabled");
+ return NULL;
+ }
+
+ OutputTlsCtx *tls_ctx = SCMalloc(sizeof(OutputTlsCtx));
+ if (unlikely(tls_ctx == NULL))
+ return NULL;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(tls_ctx);
+ return NULL;
+ }
+
+ tls_ctx->file_ctx = ojc->file_ctx;
+ tls_ctx->flags = LOG_TLS_DEFAULT;
+
+ if (conf) {
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+
+ if (extended != NULL) {
+ if (ConfValIsTrue(extended)) {
+ tls_ctx->flags = LOG_TLS_EXTENDED;
+ }
+ }
+ }
+ output_ctx->data = tls_ctx;
+ output_ctx->DeInit = OutputTlsLogDeinitSub;
+
+ return output_ctx;
+}
+
+/** \internal
+ * \brief Condition function for TLS logger
+ * \retval bool true or false -- log now?
+ */
+static int JsonTlsCondition(ThreadVars *tv, const Packet *p)
+{
+ if (p->flow == NULL) {
+ return FALSE;
+ }
+
+ if (!(PKT_IS_TCP(p))) {
+ return FALSE;
+ }
+
+ FLOWLOCK_RDLOCK(p->flow);
+ uint16_t proto = FlowGetAppProtocol(p->flow);
+ if (proto != ALPROTO_TLS)
+ goto dontlog;
+
+ SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow);
+ if (ssl_state == NULL) {
+ SCLogDebug("no tls state, so no request logging");
+ goto dontlog;
+ }
+
+ /* we only log the state once */
+ if (ssl_state->flags & SSL_AL_FLAG_STATE_LOGGED)
+ goto dontlog;
+
+ if (ssl_state->server_connp.cert0_issuerdn == NULL ||
+ ssl_state->server_connp.cert0_subject == NULL)
+ goto dontlog;
+
+ /* todo: logic to log once */
+
+ FLOWLOCK_UNLOCK(p->flow);
+ return TRUE;
+dontlog:
+ FLOWLOCK_UNLOCK(p->flow);
+ return FALSE;
+}
+
+void TmModuleJsonTlsLogRegister (void)
+{
+ tmm_modules[TMM_JSONTLSLOG].name = "JsonTlsLog";
+ tmm_modules[TMM_JSONTLSLOG].ThreadInit = JsonTlsLogThreadInit;
+ tmm_modules[TMM_JSONTLSLOG].ThreadDeinit = JsonTlsLogThreadDeinit;
+ tmm_modules[TMM_JSONTLSLOG].RegisterTests = NULL;
+ tmm_modules[TMM_JSONTLSLOG].cap_flags = 0;
+ tmm_modules[TMM_JSONTLSLOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterPacketModule("JsonTlsLog", "tls-json-log", OutputTlsLogInit,
+ JsonTlsLogger, JsonTlsCondition);
+
+ /* also register as child of eve-log */
+ OutputRegisterPacketSubModule("eve-log", "JsonTlsLog", "eve-log.tls", OutputTlsLogInitSub,
+ JsonTlsLogger, JsonTlsCondition);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonTlsLogRegister (void)
+{
+ tmm_modules[TMM_JSONTLSLOG].name = "JsonTlsLog";
+ tmm_modules[TMM_JSONTLSLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-json-tls.h b/framework/src/suricata/src/output-json-tls.h
new file mode 100644
index 00000000..f330ad89
--- /dev/null
+++ b/framework/src/suricata/src/output-json-tls.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_TLS_H__
+#define __OUTPUT_JSON_TLS_H__
+
+void TmModuleJsonTlsLogRegister (void);
+
+#ifdef HAVE_LIBJANSSON
+#include "app-layer-ssl.h"
+
+void JsonTlsLogJSONBasic(json_t *js, SSLState *ssl_state);
+void JsonTlsLogJSONExtended(json_t *js, SSLState *ssl_state);
+#endif /* HAVE_LIBJANSSON */
+
+#endif /* __OUTPUT_JSON_TLS_H__ */
diff --git a/framework/src/suricata/src/output-json.c b/framework/src/suricata/src/output-json.c
new file mode 100644
index 00000000..74289f1b
--- /dev/null
+++ b/framework/src/suricata/src/output-json.c
@@ -0,0 +1,584 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ *
+ * Logs alerts in JSON format.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+#include "app-layer-parser.h"
+#include "util-classification-config.h"
+#include "util-syslog.h"
+
+#include "output.h"
+#include "output-json.h"
+
+#include "util-byte.h"
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-optimize.h"
+#include "util-buffer.h"
+#include "util-logopenfile.h"
+#include "util-device.h"
+
+
+#ifndef HAVE_LIBJANSSON
+
+/** Handle the case where no JSON support is compiled in.
+ *
+ */
+
+TmEcode OutputJson (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode OutputJsonThreadInit(ThreadVars *, void *, void **);
+TmEcode OutputJsonThreadDeinit(ThreadVars *, void *);
+int OutputJsonOpenFileCtx(LogFileCtx *, char *);
+void OutputJsonRegisterTests(void);
+
+void TmModuleOutputJsonRegister (void)
+{
+ tmm_modules[TMM_OUTPUTJSON].name = "OutputJSON";
+ tmm_modules[TMM_OUTPUTJSON].ThreadInit = OutputJsonThreadInit;
+ tmm_modules[TMM_OUTPUTJSON].Func = OutputJson;
+ tmm_modules[TMM_OUTPUTJSON].ThreadDeinit = OutputJsonThreadDeinit;
+ tmm_modules[TMM_OUTPUTJSON].RegisterTests = OutputJsonRegisterTests;
+}
+
+OutputCtx *OutputJsonInitCtx(ConfNode *conf)
+{
+ SCLogDebug("Can't init JSON output - JSON support was disabled during build.");
+ return NULL;
+}
+
+TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogDebug("Can't init JSON output thread - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+TmEcode OutputJson (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ return TM_ECODE_OK;
+}
+
+TmEcode OutputJsonThreadDeinit(ThreadVars *t, void *data)
+{
+ return TM_ECODE_FAILED;
+}
+
+void OutputJsonRegisterTests (void)
+{
+}
+
+#else /* implied we do have JSON support */
+
+#include <jansson.h>
+
+#define DEFAULT_LOG_FILENAME "eve.json"
+#define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
+#define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
+#define DEFAULT_ALERT_SYSLOG_LEVEL LOG_INFO
+#define MODULE_NAME "OutputJSON"
+
+#define OUTPUT_BUFFER_SIZE 65535
+
+#ifndef OS_WIN32
+static int alert_syslog_level = DEFAULT_ALERT_SYSLOG_LEVEL;
+#endif /* OS_WIN32 */
+
+TmEcode OutputJson (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode OutputJsonThreadInit(ThreadVars *, void *, void **);
+TmEcode OutputJsonThreadDeinit(ThreadVars *, void *);
+void OutputJsonExitPrintStats(ThreadVars *, void *);
+void OutputJsonRegisterTests(void);
+static void OutputJsonDeInitCtx(OutputCtx *);
+
+void TmModuleOutputJsonRegister (void)
+{
+ tmm_modules[TMM_OUTPUTJSON].name = MODULE_NAME;
+ tmm_modules[TMM_OUTPUTJSON].ThreadInit = OutputJsonThreadInit;
+ tmm_modules[TMM_OUTPUTJSON].Func = OutputJson;
+ tmm_modules[TMM_OUTPUTJSON].ThreadExitPrintStats = OutputJsonExitPrintStats;
+ tmm_modules[TMM_OUTPUTJSON].ThreadDeinit = OutputJsonThreadDeinit;
+ tmm_modules[TMM_OUTPUTJSON].RegisterTests = OutputJsonRegisterTests;
+ tmm_modules[TMM_OUTPUTJSON].cap_flags = 0;
+
+ OutputRegisterModule(MODULE_NAME, "eve-log", OutputJsonInitCtx);
+}
+
+/* Default Sensor ID value */
+static int64_t sensor_id = -1; /* -1 = not defined */
+
+/** \brief jsonify tcp flags field
+ * Only add 'true' fields in an attempt to keep things reasonably compact.
+ */
+void JsonTcpFlags(uint8_t flags, json_t *js)
+{
+ if (flags & TH_SYN)
+ json_object_set_new(js, "syn", json_true());
+ if (flags & TH_FIN)
+ json_object_set_new(js, "fin", json_true());
+ if (flags & TH_RST)
+ json_object_set_new(js, "rst", json_true());
+ if (flags & TH_PUSH)
+ json_object_set_new(js, "psh", json_true());
+ if (flags & TH_ACK)
+ json_object_set_new(js, "ack", json_true());
+ if (flags & TH_URG)
+ json_object_set_new(js, "urg", json_true());
+ if (flags & TH_ECN)
+ json_object_set_new(js, "ecn", json_true());
+ if (flags & TH_CWR)
+ json_object_set_new(js, "cwr", json_true());
+}
+
+void CreateJSONFlowId(json_t *js, const Flow *f)
+{
+ if (f == NULL)
+ return;
+#if __WORDSIZE == 64
+ uint64_t addr = (uint64_t)f;
+#else
+ uint32_t addr = (uint32_t)f;
+#endif
+ json_object_set_new(js, "flow_id", json_integer(addr));
+}
+
+json_t *CreateJSONHeader(Packet *p, int direction_sensitive, char *event_type)
+{
+ char timebuf[64];
+ char srcip[46], dstip[46];
+ Port sp, dp;
+
+ json_t *js = json_object();
+ if (unlikely(js == NULL))
+ return NULL;
+
+ CreateIsoTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ srcip[0] = '\0';
+ dstip[0] = '\0';
+ if (direction_sensitive) {
+ if ((PKT_IS_TOSERVER(p))) {
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ }
+ sp = p->sp;
+ dp = p->dp;
+ } else {
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip));
+ }
+ sp = p->dp;
+ dp = p->sp;
+ }
+ } else {
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ }
+ sp = p->sp;
+ dp = p->dp;
+ }
+
+ char proto[16];
+ if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "%03" PRIu32, IP_GET_IPPROTO(p));
+ }
+
+ /* time & tx */
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ CreateJSONFlowId(js, (const Flow *)p->flow);
+
+ /* sensor id */
+ if (sensor_id >= 0)
+ json_object_set_new(js, "sensor_id", json_integer(sensor_id));
+
+ /* input interface */
+ if (p->livedev) {
+ json_object_set_new(js, "in_iface", json_string(p->livedev->dev));
+ }
+
+ /* pcap_cnt */
+ if (p->pcap_cnt != 0) {
+ json_object_set_new(js, "pcap_cnt", json_integer(p->pcap_cnt));
+ }
+
+ if (event_type) {
+ json_object_set_new(js, "event_type", json_string(event_type));
+ }
+
+ /* vlan */
+ if (p->vlan_idx > 0) {
+ json_t *js_vlan;
+ switch (p->vlan_idx) {
+ case 1:
+ json_object_set_new(js, "vlan",
+ json_integer(VLAN_GET_ID1(p)));
+ break;
+ case 2:
+ js_vlan = json_array();
+ if (unlikely(js != NULL)) {
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID1(p)));
+ json_array_append_new(js_vlan,
+ json_integer(VLAN_GET_ID2(p)));
+ json_object_set_new(js, "vlan", js_vlan);
+ }
+ break;
+ default:
+ /* shouldn't get here */
+ break;
+ }
+ }
+
+ /* tuple */
+ json_object_set_new(js, "src_ip", json_string(srcip));
+ switch(p->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "src_port", json_integer(sp));
+ break;
+ }
+ json_object_set_new(js, "dest_ip", json_string(dstip));
+ switch(p->proto) {
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ json_object_set_new(js, "dest_port", json_integer(dp));
+ break;
+ }
+ json_object_set_new(js, "proto", json_string(proto));
+ switch (p->proto) {
+ case IPPROTO_ICMP:
+ if (p->icmpv4h) {
+ json_object_set_new(js, "icmp_type",
+ json_integer(p->icmpv4h->type));
+ json_object_set_new(js, "icmp_code",
+ json_integer(p->icmpv4h->code));
+ }
+ break;
+ case IPPROTO_ICMPV6:
+ if (p->icmpv6h) {
+ json_object_set_new(js, "icmp_type",
+ json_integer(p->icmpv6h->type));
+ json_object_set_new(js, "icmp_code",
+ json_integer(p->icmpv6h->code));
+ }
+ break;
+ }
+
+ return js;
+}
+
+int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer *buffer)
+{
+ char *js_s = json_dumps(js,
+ JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
+#ifdef JSON_ESCAPE_SLASH
+ JSON_ESCAPE_SLASH
+#else
+ 0
+#endif
+ );
+ if (unlikely(js_s == NULL))
+ return TM_ECODE_OK;
+
+ SCMutexLock(&file_ctx->fp_mutex);
+ if (file_ctx->type == LOGFILE_TYPE_SYSLOG)
+ {
+ if (file_ctx->prefix != NULL)
+ {
+ syslog(alert_syslog_level, "%s%s", file_ctx->prefix, js_s);
+ }
+ else
+ {
+ syslog(alert_syslog_level, "%s", js_s);
+ }
+ }
+ else if (file_ctx->type == LOGFILE_TYPE_FILE ||
+ file_ctx->type == LOGFILE_TYPE_UNIX_DGRAM ||
+ file_ctx->type == LOGFILE_TYPE_UNIX_STREAM)
+ {
+ if (file_ctx->prefix != NULL)
+ {
+ MemBufferWriteString(buffer, "%s%s\n", file_ctx->prefix, js_s);
+ }
+ else
+ {
+ MemBufferWriteString(buffer, "%s\n", js_s);
+ }
+ file_ctx->Write((const char *)MEMBUFFER_BUFFER(buffer),
+ MEMBUFFER_OFFSET(buffer), file_ctx);
+ }
+ SCMutexUnlock(&file_ctx->fp_mutex);
+ free(js_s);
+ return 0;
+}
+
+TmEcode OutputJson (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ return TM_ECODE_OK;
+}
+
+TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ AlertJsonThread *aft = SCMalloc(sizeof(AlertJsonThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(AlertJsonThread));
+
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for AlertJson. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode OutputJsonThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void OutputJsonExitPrintStats(ThreadVars *tv, void *data)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("JSON output wrote %" PRIu64 " alerts", aft->file_ctx->alerts);
+
+}
+
+/**
+ * \brief Create a new LogFileCtx for "fast" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+OutputCtx *OutputJsonInitCtx(ConfNode *conf)
+{
+ OutputJsonCtx *json_ctx = SCCalloc(1, sizeof(OutputJsonCtx));;
+ if (unlikely(json_ctx == NULL)) {
+ SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx");
+ return NULL;
+ }
+
+ json_ctx->file_ctx = LogFileNewCtx();
+ if (unlikely(json_ctx->file_ctx == NULL)) {
+ SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx");
+ SCFree(json_ctx);
+ return NULL;
+ }
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ LogFileFreeCtx(json_ctx->file_ctx);
+ SCFree(json_ctx);
+ return NULL;
+ }
+
+ output_ctx->data = json_ctx;
+ output_ctx->DeInit = OutputJsonDeInitCtx;
+
+ if (conf) {
+ const char *output_s = ConfNodeLookupChildValue(conf, "filetype");
+
+ // Backwards compatibility
+ if (output_s == NULL) {
+ output_s = ConfNodeLookupChildValue(conf, "type");
+ }
+
+ if (output_s != NULL) {
+ if (strcmp(output_s, "file") == 0 ||
+ strcmp(output_s, "regular") == 0) {
+ json_ctx->json_out = LOGFILE_TYPE_FILE;
+ } else if (strcmp(output_s, "syslog") == 0) {
+ json_ctx->json_out = LOGFILE_TYPE_SYSLOG;
+ } else if (strcmp(output_s, "unix_dgram") == 0) {
+ json_ctx->json_out = LOGFILE_TYPE_UNIX_DGRAM;
+ } else if (strcmp(output_s, "unix_stream") == 0) {
+ json_ctx->json_out = LOGFILE_TYPE_UNIX_STREAM;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON output option: %s", output_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ const char *prefix = ConfNodeLookupChildValue(conf, "prefix");
+ if (prefix != NULL)
+ {
+ json_ctx->file_ctx->prefix = SCStrdup(prefix);
+ if (json_ctx->file_ctx->prefix == NULL)
+ {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for eve-log.prefix setting.");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (json_ctx->json_out == LOGFILE_TYPE_FILE ||
+ json_ctx->json_out == LOGFILE_TYPE_UNIX_DGRAM ||
+ json_ctx->json_out == LOGFILE_TYPE_UNIX_STREAM)
+ {
+ if (SCConfLogOpenGeneric(conf, json_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
+ LogFileFreeCtx(json_ctx->file_ctx);
+ SCFree(json_ctx);
+ SCFree(output_ctx);
+ return NULL;
+ }
+ OutputRegisterFileRotationFlag(&json_ctx->file_ctx->rotation_flag);
+
+ const char *format_s = ConfNodeLookupChildValue(conf, "format");
+ if (format_s != NULL) {
+ if (strcmp(format_s, "indent") == 0) {
+ json_ctx->format = INDENT;
+ } else if (strcmp(format_s, "compact") == 0) {
+ json_ctx->format = COMPACT;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON format option: %s", format_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else if (json_ctx->json_out == LOGFILE_TYPE_SYSLOG) {
+ const char *facility_s = ConfNodeLookupChildValue(conf, "facility");
+ if (facility_s == NULL) {
+ facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR;
+ }
+
+ int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
+ if (facility == -1) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\","
+ " now using \"%s\" as syslog facility", facility_s,
+ DEFAULT_ALERT_SYSLOG_FACILITY_STR);
+ facility = DEFAULT_ALERT_SYSLOG_FACILITY;
+ }
+
+ const char *level_s = ConfNodeLookupChildValue(conf, "level");
+ if (level_s != NULL) {
+ int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap());
+ if (level != -1) {
+ alert_syslog_level = level;
+ }
+ }
+
+ const char *ident = ConfNodeLookupChildValue(conf, "identity");
+ /* if null we just pass that to openlog, which will then
+ * figure it out by itself. */
+
+ openlog(ident, LOG_PID|LOG_NDELAY, facility);
+
+ }
+
+ const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
+ if (sensor_id_s != NULL) {
+ if (ByteExtractStringUint64((uint64_t *)&sensor_id, 10, 0, sensor_id_s) == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize JSON output, "
+ "invalid sensor-is: %s", sensor_id_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ json_ctx->file_ctx->type = json_ctx->json_out;
+ }
+
+ SCLogDebug("returning output_ctx %p", output_ctx);
+ return output_ctx;
+}
+
+static void OutputJsonDeInitCtx(OutputCtx *output_ctx)
+{
+ OutputJsonCtx *json_ctx = (OutputJsonCtx *)output_ctx->data;
+ LogFileCtx *logfile_ctx = json_ctx->file_ctx;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(json_ctx);
+ SCFree(output_ctx);
+}
+
+/*------------------------------Unittests-------------------------------------*/
+
+#ifdef UNITTESTS
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for AlertFastLog API.
+ */
+void OutputJsonRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+#endif /* UNITTESTS */
+
+}
+#endif
diff --git a/framework/src/suricata/src/output-json.h b/framework/src/suricata/src/output-json.h
new file mode 100644
index 00000000..1acde3e6
--- /dev/null
+++ b/framework/src/suricata/src/output-json.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <td@npulsetech.com>
+ */
+
+#ifndef __OUTPUT_JSON_H__
+#define __OUTPUT_JSON_H__
+
+void TmModuleOutputJsonRegister (void);
+
+#ifdef HAVE_LIBJANSSON
+
+#include "suricata-common.h"
+#include "util-buffer.h"
+#include "util-logopenfile.h"
+
+void CreateJSONFlowId(json_t *js, const Flow *f);
+void JsonTcpFlags(uint8_t flags, json_t *js);
+json_t *CreateJSONHeader(Packet *p, int direction_sensative, char *event_type);
+TmEcode OutputJSON(json_t *js, void *data, uint64_t *count);
+int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer *buffer);
+OutputCtx *OutputJsonInitCtx(ConfNode *);
+
+
+enum JsonFormat { COMPACT, INDENT };
+
+/*
+ * Global configuration context data
+ */
+typedef struct OutputJsonCtx_ {
+ LogFileCtx *file_ctx;
+ enum LogFileType json_out;
+ enum JsonFormat format;
+} OutputJsonCtx;
+
+typedef struct AlertJsonThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx *file_ctx;
+} AlertJsonThread;
+
+#endif /* HAVE_LIBJANSSON */
+
+#endif /* __OUTPUT_JSON_H__ */
diff --git a/framework/src/suricata/src/output-lua.c b/framework/src/suricata/src/output-lua.c
new file mode 100644
index 00000000..fc5a5621
--- /dev/null
+++ b/framework/src/suricata/src/output-lua.c
@@ -0,0 +1,893 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+#include "util-lua-http.h"
+#include "util-lua-dns.h"
+
+#define MODULE_NAME "LuaLog"
+
+/** \brief structure containing global config
+ * The OutputLuaLogInitSub which is run per script
+ * can access this to get global config info through
+ * it's parent_ctx->data ptr.
+ */
+typedef struct LogLuaMasterCtx_ {
+ char path[PATH_MAX]; /**< contains script-dir */
+} LogLuaMasterCtx;
+
+typedef struct LogLuaCtx_ {
+ SCMutex m;
+ lua_State *luastate;
+ int deinit_once;
+} LogLuaCtx;
+
+typedef struct LogLuaThreadCtx_ {
+ LogLuaCtx *lua_ctx;
+} LogLuaThreadCtx;
+
+/** \internal
+ * \brief TX logger for lua scripts
+ *
+ * A single call to this function will run one script on a single
+ * transaction.
+ *
+ * NOTE: The flow (f) also referenced by p->flow is locked.
+ */
+static int LuaTxLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
+{
+ SCEnter();
+
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ SCMutexLock(&td->lua_ctx->m);
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
+ LuaStateSetTX(td->lua_ctx->luastate, txptr);
+ LuaStateSetFlow(td->lua_ctx->luastate, f, /* locked */LUA_FLOW_LOCKED_BY_PARENT);
+
+ /* prepare data to pass to script */
+ lua_getglobal(td->lua_ctx->luastate, "log");
+ lua_newtable(td->lua_ctx->luastate);
+ LuaPushTableKeyValueInt(td->lua_ctx->luastate, "tx_id", (int)(tx_id));
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+
+ SCMutexUnlock(&td->lua_ctx->m);
+ SCReturnInt(0);
+}
+
+/** \internal
+ * \brief Streaming logger for lua scripts
+ *
+ * Hooks into the Streaming Logger API. Gets called for each chunk of new
+ * streaming data.
+ */
+static int LuaStreamingLogger(ThreadVars *tv, void *thread_data, const Flow *f,
+ const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
+{
+ SCEnter();
+
+ void *txptr = NULL;
+ LuaStreamingBuffer b = { data, data_len, flags };
+
+ SCLogDebug("flags %02x", flags);
+
+ if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) {
+ if (f && f->alstate)
+ txptr = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, tx_id);
+ }
+
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ SCMutexLock(&td->lua_ctx->m);
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION)
+ LuaStateSetTX(td->lua_ctx->luastate, txptr);
+ LuaStateSetFlow(td->lua_ctx->luastate, (Flow *)f, /* locked */LUA_FLOW_LOCKED_BY_PARENT);
+ LuaStateSetStreamingBuffer(td->lua_ctx->luastate, &b);
+
+ /* prepare data to pass to script */
+ lua_getglobal(td->lua_ctx->luastate, "log");
+ lua_newtable(td->lua_ctx->luastate);
+
+ if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION)
+ LuaPushTableKeyValueInt(td->lua_ctx->luastate, "tx_id", (int)(tx_id));
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+
+ SCMutexUnlock(&td->lua_ctx->m);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/** \internal
+ * \brief Packet Logger for lua scripts, for alerts
+ *
+ * A single call to this function will run one script for a single
+ * packet. If it is called, it means that the registered condition
+ * function has returned TRUE.
+ *
+ * The script is called once for each alert stored in the packet.
+ *
+ * NOTE: p->flow is UNlocked
+ */
+static int LuaPacketLoggerAlerts(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ char timebuf[64];
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ if (!(PKT_IS_IPV4(p)) && !(PKT_IS_IPV6(p))) {
+ /* decoder event */
+ goto not_supported;
+ }
+
+ char proto[16] = "";
+ if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
+ }
+
+ /* loop through alerts stored in the packet */
+ SCMutexLock(&td->lua_ctx->m);
+ uint16_t cnt;
+ for (cnt = 0; cnt < p->alerts.cnt; cnt++) {
+ const PacketAlert *pa = &p->alerts.alerts[cnt];
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ lua_getglobal(td->lua_ctx->luastate, "log");
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
+ LuaStateSetFlow(td->lua_ctx->luastate, p->flow, /* unlocked */LUA_FLOW_NOT_LOCKED_BY_PARENT);
+ LuaStateSetPacketAlert(td->lua_ctx->luastate, (PacketAlert *)pa);
+
+ /* prepare data to pass to script */
+ //lua_newtable(td->lua_ctx->luastate);
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+not_supported:
+ SCReturnInt(0);
+}
+
+static int LuaPacketConditionAlerts(ThreadVars *tv, const Packet *p)
+{
+ if (p->alerts.cnt > 0)
+ return TRUE;
+ return FALSE;
+}
+
+/** \internal
+ * \brief Packet Logger for lua scripts, for packets
+ *
+ * A single call to this function will run one script for a single
+ * packet. If it is called, it means that the registered condition
+ * function has returned TRUE.
+ *
+ * The script is called once for each packet.
+ *
+ * NOTE: p->flow is UNlocked
+ */
+static int LuaPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ char timebuf[64];
+
+ if ((!(PKT_IS_IPV4(p))) && (!(PKT_IS_IPV6(p)))) {
+ goto not_supported;
+ }
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ char proto[16] = "";
+ if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
+ }
+
+ /* loop through alerts stored in the packet */
+ SCMutexLock(&td->lua_ctx->m);
+ lua_getglobal(td->lua_ctx->luastate, "log");
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
+ LuaStateSetFlow(td->lua_ctx->luastate, p->flow, /* unlocked */LUA_FLOW_NOT_LOCKED_BY_PARENT);
+
+ /* prepare data to pass to script */
+ lua_newtable(td->lua_ctx->luastate);
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+not_supported:
+ SCReturnInt(0);
+}
+
+static int LuaPacketCondition(ThreadVars *tv, const Packet *p)
+{
+ return TRUE;
+}
+
+/** \internal
+ * \brief File API Logger function for Lua scripts
+ *
+ * Executes a script once for one file.
+ *
+ * TODO non-http support
+ *
+ * NOTE p->flow is locked at this point
+ */
+static int LuaFileLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff)
+{
+ SCEnter();
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ if ((!(PKT_IS_IPV4(p))) && (!(PKT_IS_IPV6(p))))
+ return 0;
+
+ BUG_ON(ff->flags & FILE_LOGGED);
+
+ SCLogDebug("ff %p", ff);
+
+ /* Get the TX so the script can get more context about it.
+ * TODO hardcoded to HTTP currently */
+ void *txptr = NULL;
+ if (p && p->flow && p->flow->alstate)
+ txptr = AppLayerParserGetTx(p->proto, ALPROTO_HTTP, p->flow->alstate, ff->txid);
+
+ SCMutexLock(&td->lua_ctx->m);
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
+ LuaStateSetTX(td->lua_ctx->luastate, txptr);
+ LuaStateSetFlow(td->lua_ctx->luastate, p->flow, /* locked */LUA_FLOW_LOCKED_BY_PARENT);
+ LuaStateSetFile(td->lua_ctx->luastate, (File *)ff);
+
+ /* get the lua function to call */
+ lua_getglobal(td->lua_ctx->luastate, "log");
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+ return 0;
+}
+
+/** \internal
+ * \brief Flow API Logger function for Lua scripts
+ *
+ * Executes a script once for one flow
+ *
+ * Note: flow 'f' is locked
+ */
+static int LuaFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
+{
+ SCEnter();
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ SCLogDebug("f %p", f);
+
+ SCMutexLock(&td->lua_ctx->m);
+
+ LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
+ LuaStateSetFlow(td->lua_ctx->luastate, f, /* locked */LUA_FLOW_LOCKED_BY_PARENT);
+
+ /* get the lua function to call */
+ lua_getglobal(td->lua_ctx->luastate, "log");
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+ return 0;
+}
+
+
+
+static int LuaStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
+{
+ SCEnter();
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
+
+ SCMutexLock(&td->lua_ctx->m);
+
+ lua_State *luastate = td->lua_ctx->luastate;
+ /* get the lua function to call */
+ lua_getglobal(td->lua_ctx->luastate, "log");
+
+ /* create lua array, which is really just a table. The key is an int (1-x),
+ * the value another table with named fields: name, tm_name, value, pvalue.
+ * { 1, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}}
+ * { 2, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}}
+ * etc
+ */
+ lua_newtable(luastate);
+ uint32_t u = 0;
+ for (; u < st->nstats; u++) {
+ lua_pushinteger(luastate, u + 1);
+
+ lua_newtable(luastate);
+
+ lua_pushstring(luastate, "name");
+ lua_pushstring(luastate, st->stats[u].name);
+ lua_settable(luastate, -3);
+
+ lua_pushstring(luastate, "tmname");
+ lua_pushstring(luastate, st->stats[u].tm_name);
+ lua_settable(luastate, -3);
+
+ lua_pushstring(luastate, "value");
+ lua_pushinteger(luastate, st->stats[u].value);
+ lua_settable(luastate, -3);
+
+ lua_pushstring(luastate, "pvalue");
+ lua_pushinteger(luastate, st->stats[u].pvalue);
+ lua_settable(luastate, -3);
+
+ lua_settable(luastate, -3);
+ }
+
+ int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
+ if (retval != 0) {
+ SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+ return 0;
+
+}
+
+typedef struct LogLuaScriptOptions_ {
+ AppProto alproto;
+ int packet;
+ int alerts;
+ int file;
+ int streaming;
+ int tcp_data;
+ int http_body;
+ int flow;
+ int stats;
+} LogLuaScriptOptions;
+
+/** \brief load and evaluate the script
+ *
+ * This function parses the script, checks if all the required functions
+ * are defined and runs the 'init' function. The init function will inform
+ * us what the scripts needs are.
+ *
+ * \param filename filename of lua script file
+ * \param options struct to pass script requirements/options back to caller
+ * \retval errcode 0 ok, -1 error
+ */
+static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options) {
+ int status;
+
+ lua_State *luastate = luaL_newstate();
+ if (luastate == NULL)
+ goto error;
+ luaL_openlibs(luastate);
+
+ /* hackish, needed to allow unittests to pass buffers as scripts instead of files */
+#if 0//def UNITTESTS
+ if (ut_script != NULL) {
+ status = luaL_loadbuffer(luastate, ut_script, strlen(ut_script), "unittest");
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+ } else {
+#endif
+ status = luaL_loadfile(luastate, filename);
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+#if 0//def UNITTESTS
+ }
+#endif
+
+ /* prime the script (or something) */
+ if (lua_pcall(luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't prime file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ lua_getglobal(luastate, "init");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no init function in script");
+ goto error;
+ }
+
+ lua_newtable(luastate); /* stack at -1 */
+ if (lua_gettop(luastate) == 0 || lua_type(luastate, 2) != LUA_TTABLE) {
+ SCLogError(SC_ERR_LUA_ERROR, "no table setup");
+ goto error;
+ }
+
+ lua_pushliteral(luastate, "script_api_ver");
+ lua_pushnumber (luastate, 1);
+ lua_settable(luastate, -3);
+
+ if (lua_pcall(luastate, 1, 1, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't run script 'init' function: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ /* process returns from script */
+ if (lua_gettop(luastate) == 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "init function in script should return table, nothing returned");
+ goto error;
+ }
+ if (lua_type(luastate, 1) != LUA_TTABLE) {
+ SCLogError(SC_ERR_LUA_ERROR, "init function in script should return table, returned is not table");
+ goto error;
+ }
+
+ lua_pushnil(luastate);
+ const char *k, *v;
+ while (lua_next(luastate, -2)) {
+ k = lua_tostring(luastate, -2);
+ if (k == NULL)
+ continue;
+
+ v = lua_tostring(luastate, -1);
+ lua_pop(luastate, 1);
+ if (v == NULL)
+ continue;
+
+ SCLogDebug("k='%s', v='%s'", k, v);
+
+ if (strcmp(k,"protocol") == 0 && strcmp(v, "http") == 0)
+ options->alproto = ALPROTO_HTTP;
+ else if (strcmp(k,"protocol") == 0 && strcmp(v, "dns") == 0)
+ options->alproto = ALPROTO_DNS;
+ else if (strcmp(k, "type") == 0 && strcmp(v, "packet") == 0)
+ options->packet = 1;
+ else if (strcmp(k, "filter") == 0 && strcmp(v, "alerts") == 0)
+ options->alerts = 1;
+ else if (strcmp(k, "type") == 0 && strcmp(v, "file") == 0)
+ options->file = 1;
+ else if (strcmp(k, "type") == 0 && strcmp(v, "streaming") == 0)
+ options->streaming = 1;
+ else if (strcmp(k, "type") == 0 && strcmp(v, "flow") == 0)
+ options->flow = 1;
+ else if (strcmp(k, "filter") == 0 && strcmp(v, "tcp") == 0)
+ options->tcp_data = 1;
+ else if (strcmp(k, "type") == 0 && strcmp(v, "stats") == 0)
+ options->stats = 1;
+ else
+ SCLogInfo("unknown key and/or value: k='%s', v='%s'", k, v);
+ }
+
+ if (((options->alproto != ALPROTO_UNKNOWN)) + options->packet + options->file > 1) {
+ SCLogError(SC_ERR_LUA_ERROR, "invalid combination of 'needs' in the script");
+ goto error;
+ }
+
+ lua_getglobal(luastate, "setup");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no setup function in script");
+ goto error;
+ }
+
+ lua_getglobal(luastate, "log");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no log function in script");
+ goto error;
+ }
+
+ lua_getglobal(luastate, "deinit");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no deinit function in script");
+ goto error;
+ }
+
+ /* pop the table */
+ lua_pop(luastate, 1);
+ lua_close(luastate);
+ return 0;
+error:
+ lua_close(luastate);
+ return -1;
+}
+
+/** \brief setup a luastate for use at runtime
+ *
+ * This loads the script, primes it and then runs the 'setup' function.
+ *
+ * \retval state Returns the set up luastate on success, NULL on error
+ */
+static lua_State *LuaScriptSetup(const char *filename)
+{
+ lua_State *luastate = luaL_newstate();
+ if (luastate == NULL) {
+ SCLogError(SC_ERR_LUA_ERROR, "luaL_newstate failed");
+ goto error;
+ }
+
+ luaL_openlibs(luastate);
+
+ int status;
+ /* hackish, needed to allow unittests to pass buffers as scripts instead of files */
+#if 0//def UNITTESTS
+ if (ut_script != NULL) {
+ status = luaL_loadbuffer(t->luastate, ut_script, strlen(ut_script), "unittest");
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(t->luastate, -1));
+ goto error;
+ }
+ } else {
+#endif
+ status = luaL_loadfile(luastate, filename);
+ if (status) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+#if 0//def UNITTESTS
+ }
+#endif
+
+ /* prime the script */
+ if (lua_pcall(luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't prime file: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ lua_getglobal(luastate, "setup");
+
+ /* register functions common to all */
+ LuaRegisterFunctions(luastate);
+ /* unconditionally register http function. They will only work
+ * if the tx is registered in the state at runtime though. */
+ LuaRegisterHttpFunctions(luastate);
+ LuaRegisterDnsFunctions(luastate);
+
+ if (lua_pcall(luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't run script 'setup' function: %s", lua_tostring(luastate, -1));
+ goto error;
+ }
+
+ SCLogDebug("lua_State %p is set up", luastate);
+ return luastate;
+error:
+ lua_close(luastate);
+ return NULL;
+}
+
+/** \brief initialize output for a script instance
+ *
+ * Runs script 'setup' function.
+ */
+static OutputCtx *OutputLuaLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+ if (conf == NULL)
+ return NULL;
+
+ LogLuaCtx *lua_ctx = SCMalloc(sizeof(LogLuaCtx));
+ if (unlikely(lua_ctx == NULL))
+ return NULL;
+ memset(lua_ctx, 0x00, sizeof(*lua_ctx));
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(lua_ctx);
+ return NULL;
+ }
+
+ SCMutexInit(&lua_ctx->m, NULL);
+
+ const char *dir = "";
+ if (parent_ctx && parent_ctx->data) {
+ LogLuaMasterCtx *mc = parent_ctx->data;
+ dir = mc->path;
+ }
+
+ char path[PATH_MAX] = "";
+ snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", conf->val);
+ SCLogDebug("script full path %s", path);
+
+ SCMutexLock(&lua_ctx->m);
+ lua_ctx->luastate = LuaScriptSetup(path);
+ SCMutexUnlock(&lua_ctx->m);
+ if (lua_ctx->luastate == NULL)
+ goto error;
+
+ SCLogDebug("lua_ctx %p", lua_ctx);
+
+ output_ctx->data = lua_ctx;
+ output_ctx->DeInit = NULL;
+
+ return output_ctx;
+error:
+ SCMutexDestroy(&lua_ctx->m);
+ SCFree(lua_ctx);
+ SCFree(output_ctx);
+ return NULL;
+}
+
+static void LogLuaMasterFree(OutputCtx *oc) {
+ BUG_ON(oc == NULL);
+ if (oc->data)
+ SCFree(oc->data);
+}
+
+/** \internal
+ * \brief initialize output instance for lua module
+ *
+ * Parses nested script list, primes them to find out what they
+ * inspect, then fills the OutputCtx::submodules list with the
+ * proper Logger function for the data type the script needs.
+ */
+static OutputCtx *OutputLuaLogInit(ConfNode *conf)
+{
+ const char *dir = ConfNodeLookupChildValue(conf, "scripts-dir");
+ if (dir == NULL)
+ dir = "";
+
+ ConfNode *scripts = ConfNodeLookupChild(conf, "scripts");
+ if (scripts == NULL) {
+ /* No "outputs" section in the configuration. */
+ SCLogInfo("scripts not defined");
+ return NULL;
+ }
+
+ /* global output ctx setup */
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL)) {
+ return NULL;
+ }
+ output_ctx->data = SCCalloc(1, sizeof(LogLuaMasterCtx));
+ if (unlikely(output_ctx->data == NULL)) {
+ SCFree(output_ctx);
+ return NULL;
+ }
+ output_ctx->DeInit = LogLuaMasterFree;
+ LogLuaMasterCtx *master_config = output_ctx->data;
+ strlcpy(master_config->path, dir, sizeof(master_config->path));
+ TAILQ_INIT(&output_ctx->submodules);
+
+ /* check the enables scripts and set them up as submodules */
+ ConfNode *script;
+ TAILQ_FOREACH(script, &scripts->head, next) {
+ SCLogInfo("enabling script %s", script->val);
+ LogLuaScriptOptions opts;
+ memset(&opts, 0x00, sizeof(opts));
+
+ char path[PATH_MAX] = "";
+ snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", script->val);
+ SCLogDebug("script full path %s", path);
+
+ int r = LuaScriptInit(path, &opts);
+ if (r != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't initialize scipt");
+ goto error;
+ }
+
+ /* create an OutputModule for this script, based
+ * on it's needs. */
+ OutputModule *om = SCCalloc(1, sizeof(*om));
+ if (om == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "calloc() failed");
+ goto error;
+ }
+
+ om->name = MODULE_NAME;
+ om->conf_name = script->val;
+ om->InitSubFunc = OutputLuaLogInitSub;
+
+ if (opts.alproto == ALPROTO_HTTP && opts.streaming) {
+ om->StreamingLogFunc = LuaStreamingLogger;
+ om->alproto = ALPROTO_HTTP;
+ AppLayerHtpEnableRequestBodyCallback();
+ AppLayerHtpEnableResponseBodyCallback();
+ } else if (opts.alproto == ALPROTO_HTTP) {
+ om->TxLogFunc = LuaTxLogger;
+ om->alproto = ALPROTO_HTTP;
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP);
+ } else if (opts.alproto == ALPROTO_DNS) {
+ om->TxLogFunc = LuaTxLogger;
+ om->alproto = ALPROTO_DNS;
+ AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS);
+ AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS);
+ } else if (opts.packet && opts.alerts) {
+ om->PacketLogFunc = LuaPacketLoggerAlerts;
+ om->PacketConditionFunc = LuaPacketConditionAlerts;
+ } else if (opts.packet && opts.alerts == 0) {
+ om->PacketLogFunc = LuaPacketLogger;
+ om->PacketConditionFunc = LuaPacketCondition;
+ } else if (opts.file) {
+ om->FileLogFunc = LuaFileLogger;
+ AppLayerHtpNeedFileInspection();
+ } else if (opts.streaming && opts.tcp_data) {
+ om->StreamingLogFunc = LuaStreamingLogger;
+ } else if (opts.flow) {
+ om->FlowLogFunc = LuaFlowLogger;
+ } else if (opts.stats) {
+ om->StatsLogFunc = LuaStatsLogger;
+ } else {
+ SCLogError(SC_ERR_LUA_ERROR, "failed to setup thread module");
+ SCFree(om);
+ goto error;
+ }
+
+ TAILQ_INSERT_TAIL(&output_ctx->submodules, om, entries);
+ }
+
+ return output_ctx;
+
+error:
+
+ if (output_ctx != NULL) {
+ if (output_ctx->DeInit && output_ctx->data)
+ output_ctx->DeInit(output_ctx->data);
+ SCFree(output_ctx);
+ }
+ return NULL;
+}
+
+/** \internal
+ * \brief Run the scripts 'deinit' function
+ */
+static void OutputLuaLogDoDeinit(LogLuaCtx *lua_ctx)
+{
+ lua_State *luastate = lua_ctx->luastate;
+
+ lua_getglobal(luastate, "deinit");
+ if (lua_type(luastate, -1) != LUA_TFUNCTION) {
+ SCLogError(SC_ERR_LUA_ERROR, "no deinit function in script");
+ return;
+ }
+ //LuaPrintStack(luastate);
+
+ if (lua_pcall(luastate, 0, 0, 0) != 0) {
+ SCLogError(SC_ERR_LUA_ERROR, "couldn't run script 'deinit' function: %s", lua_tostring(luastate, -1));
+ return;
+ }
+}
+
+/** \internal
+ * \brief Initialize the thread storage for lua
+ *
+ * Currently only stores a pointer to the global LogLuaCtx
+ */
+static TmEcode LuaLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ LogLuaThreadCtx *td = SCMalloc(sizeof(*td));
+ if (unlikely(td == NULL))
+ return TM_ECODE_FAILED;
+ memset(td, 0, sizeof(*td));
+
+ if (initdata == NULL) {
+ SCLogDebug("Error getting context for LuaLog. \"initdata\" argument NULL");
+ SCFree(td);
+ return TM_ECODE_FAILED;
+ }
+
+ LogLuaCtx *lua_ctx = ((OutputCtx *)initdata)->data;
+ SCLogDebug("lua_ctx %p", lua_ctx);
+ td->lua_ctx = lua_ctx;
+ *data = (void *)td;
+ return TM_ECODE_OK;
+}
+
+/** \internal
+ * \brief Deinit the thread storage for lua
+ *
+ * Calls OutputLuaLogDoDeinit if no-one else already did.
+ */
+static TmEcode LuaLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogLuaThreadCtx *td = (LogLuaThreadCtx *)data;
+ if (td == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ SCMutexLock(&td->lua_ctx->m);
+ if (td->lua_ctx->deinit_once == 0) {
+ OutputLuaLogDoDeinit(td->lua_ctx);
+ td->lua_ctx->deinit_once = 1;
+ }
+ SCMutexUnlock(&td->lua_ctx->m);
+
+ /* clear memory */
+ memset(td, 0, sizeof(*td));
+
+ SCFree(td);
+ return TM_ECODE_OK;
+}
+
+void TmModuleLuaLogRegister (void) {
+ tmm_modules[TMM_LUALOG].name = MODULE_NAME;
+ tmm_modules[TMM_LUALOG].ThreadInit = LuaLogThreadInit;
+ tmm_modules[TMM_LUALOG].ThreadDeinit = LuaLogThreadDeinit;
+ tmm_modules[TMM_LUALOG].RegisterTests = NULL;
+ tmm_modules[TMM_LUALOG].cap_flags = 0;
+ tmm_modules[TMM_LUALOG].flags = TM_FLAG_LOGAPI_TM;
+
+ /* register as separate module */
+ OutputRegisterModule(MODULE_NAME, "lua", OutputLuaLogInit);
+}
+
+#else
+
+void TmModuleLuaLogRegister (void) {
+ /* no-op */
+}
+
+#endif
diff --git a/framework/src/suricata/src/output-lua.h b/framework/src/suricata/src/output-lua.h
new file mode 100644
index 00000000..627cdc37
--- /dev/null
+++ b/framework/src/suricata/src/output-lua.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __OUTPUT_LUA_H__
+#define __OUTPUT_LUA_H__
+
+void TmModuleLuaLogRegister (void);
+
+#endif /* __OUTPUT_LUA_H__ */
diff --git a/framework/src/suricata/src/output-packet.c b/framework/src/suricata/src/output-packet.c
new file mode 100644
index 00000000..d2cd901c
--- /dev/null
+++ b/framework/src/suricata/src/output-packet.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Packet Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-packet.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. fast.log) with different output ctx'. */
+typedef struct OutputPacketLogger_ {
+ PacketLogger LogFunc;
+ PacketLogCondition ConditionFunc;
+ OutputCtx *output_ctx;
+ struct OutputPacketLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputPacketLogger;
+
+static OutputPacketLogger *list = NULL;
+
+int OutputRegisterPacketLogger(const char *name, PacketLogger LogFunc, PacketLogCondition ConditionFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputPacketLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->ConditionFunc = ConditionFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputPacketLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterPacketLogger happy");
+ return 0;
+}
+
+static TmEcode OutputPacketLog(ThreadVars *tv, Packet *p, void *thread_data, PacketQueue *pq, PacketQueue *postpq)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputPacketLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL || logger->ConditionFunc == NULL);
+
+ if ((logger->ConditionFunc(tv, (const Packet *)p)) == TRUE) {
+ PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, (const Packet *)p);
+ PACKET_PROFILING_TMM_END(p, logger->module_id);
+ }
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the packet logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputPacketLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputPacketLogThreadInit happy (*data %p)", *data);
+
+ OutputPacketLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputPacketLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputPacketLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+
+ logger = logger->next;
+ }
+ return TM_ECODE_OK;
+}
+
+static void OutputPacketLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputPacketLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModulePacketLoggerRegister (void)
+{
+ tmm_modules[TMM_PACKETLOGGER].name = "__packet_logger__";
+ tmm_modules[TMM_PACKETLOGGER].ThreadInit = OutputPacketLogThreadInit;
+ tmm_modules[TMM_PACKETLOGGER].Func = OutputPacketLog;
+ tmm_modules[TMM_PACKETLOGGER].ThreadExitPrintStats = OutputPacketLogExitPrintStats;
+ tmm_modules[TMM_PACKETLOGGER].ThreadDeinit = OutputPacketLogThreadDeinit;
+ tmm_modules[TMM_PACKETLOGGER].cap_flags = 0;
+}
+
+void OutputPacketShutdown(void)
+{
+ OutputPacketLogger *logger = list;
+ while (logger) {
+ OutputPacketLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+
+ /* reset list pointer */
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-packet.h b/framework/src/suricata/src/output-packet.h
new file mode 100644
index 00000000..5ae1adf8
--- /dev/null
+++ b/framework/src/suricata/src/output-packet.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Packet Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_PACKET_H__
+#define __OUTPUT_PACKET_H__
+
+#include "decode.h"
+
+/** packet logger function pointer type */
+typedef int (*PacketLogger)(ThreadVars *, void *thread_data, const Packet *);
+
+/** packet logger condition function pointer type,
+ * must return true for packets that should be logged
+ */
+typedef int (*PacketLogCondition)(ThreadVars *, const Packet *);
+
+int OutputRegisterPacketLogger(const char *name, PacketLogger LogFunc,
+ PacketLogCondition ConditionFunc, OutputCtx *);
+
+void TmModulePacketLoggerRegister (void);
+
+void OutputPacketShutdown(void);
+
+#endif /* __OUTPUT_PACKET_H__ */
diff --git a/framework/src/suricata/src/output-stats.c b/framework/src/suricata/src/output-stats.c
new file mode 100644
index 00000000..a6752fc5
--- /dev/null
+++ b/framework/src/suricata/src/output-stats.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Stats Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-stats.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputStatsLogger_ {
+ StatsLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputStatsLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputStatsLogger;
+
+static OutputStatsLogger *list = NULL;
+
+int OutputRegisterStatsLogger(const char *name, StatsLogger LogFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputStatsLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputStatsLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterStatsLogger happy");
+ return 0;
+}
+
+TmEcode OutputStatsLog(ThreadVars *tv, void *thread_data, StatsTable *st)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputStatsLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ logger = list;
+ store = op_thread_data->store;
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ logger->LogFunc(tv, store->thread_data, st);
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the tx logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputStatsLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputStatsLogThreadInit happy (*data %p)", *data);
+
+ OutputStatsLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ SCLogDebug("OutputStatsLogThreadInit happy (*data %p)", *data);
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputStatsLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputStatsLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+ logger = logger->next;
+ }
+ return TM_ECODE_OK;
+}
+
+static void OutputStatsLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputStatsLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModuleStatsLoggerRegister (void)
+{
+ tmm_modules[TMM_STATSLOGGER].name = "__stats_logger__";
+ tmm_modules[TMM_STATSLOGGER].ThreadInit = OutputStatsLogThreadInit;
+ //tmm_modules[TMM_STATSLOGGER].Func = OutputStatsLog;
+ tmm_modules[TMM_STATSLOGGER].ThreadExitPrintStats = OutputStatsLogExitPrintStats;
+ tmm_modules[TMM_STATSLOGGER].ThreadDeinit = OutputStatsLogThreadDeinit;
+ tmm_modules[TMM_STATSLOGGER].cap_flags = 0;
+}
+
+int OutputStatsLoggersRegistered(void)
+{
+ if (list != NULL)
+ return 1;
+ return 0;
+}
+
+void OutputStatsShutdown(void)
+{
+ OutputStatsLogger *logger = list;
+ while (logger) {
+ OutputStatsLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-stats.h b/framework/src/suricata/src/output-stats.h
new file mode 100644
index 00000000..75017ca2
--- /dev/null
+++ b/framework/src/suricata/src/output-stats.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Stats Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_STATS_H__
+#define __OUTPUT_STATS_H__
+
+typedef struct StatsRecord_ {
+ const char *name;
+ const char *tm_name;
+ uint64_t value; /**< total value */
+ uint64_t pvalue; /**< prev value (may be higher for memuse counters) */
+} StatsRecord;
+
+typedef struct StatsTable_ {
+ StatsRecord *stats; /**< array of global stats, indexed by counters gid */
+ StatsRecord *tstats; /**< array of arrays with per thread stats */
+ uint32_t nstats; /**< size in records of 'stats' */
+ uint32_t ntstats; /**< number of threads for which tstats stores stats */
+ time_t start_time;
+ struct timeval ts;
+} StatsTable;
+
+TmEcode OutputStatsLog(ThreadVars *tv, void *thread_data, StatsTable *st);
+
+typedef int (*StatsLogger)(ThreadVars *, void *thread_data, const StatsTable *);
+
+int OutputRegisterStatsLogger(const char *name, StatsLogger LogFunc, OutputCtx *);
+
+void TmModuleStatsLoggerRegister (void);
+
+int OutputStatsLoggersRegistered(void);
+
+void OutputStatsShutdown(void);
+
+#endif /* __OUTPUT_STATS_H__ */
diff --git a/framework/src/suricata/src/output-streaming.c b/framework/src/suricata/src/output-streaming.c
new file mode 100644
index 00000000..d416cbc3
--- /dev/null
+++ b/framework/src/suricata/src/output-streaming.c
@@ -0,0 +1,469 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Logger for streaming data
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-streaming.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "util-print.h"
+#include "conf.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+ uint32_t loggers;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputStreamingLogger_ {
+ StreamingLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputStreamingLogger_ *next;
+ const char *name;
+ TmmId module_id;
+ enum OutputStreamingType type;
+} OutputStreamingLogger;
+
+static OutputStreamingLogger *list = NULL;
+
+int OutputRegisterStreamingLogger(const char *name, StreamingLogger LogFunc,
+ OutputCtx *output_ctx, enum OutputStreamingType type )
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputStreamingLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+ op->type = type;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputStreamingLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterTxLogger happy");
+ return 0;
+}
+
+typedef struct StreamerCallbackData_ {
+ OutputStreamingLogger *logger;
+ OutputLoggerThreadStore *store;
+ ThreadVars *tv;
+ Packet *p;
+ enum OutputStreamingType type;
+} StreamerCallbackData;
+
+int Streamer(void *cbdata, Flow *f, uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
+{
+ StreamerCallbackData *streamer_cbdata = (StreamerCallbackData *)cbdata;
+ BUG_ON(streamer_cbdata == NULL);
+ OutputStreamingLogger *logger = streamer_cbdata->logger;
+ OutputLoggerThreadStore *store = streamer_cbdata->store;
+ ThreadVars *tv = streamer_cbdata->tv;
+#ifdef PROFILING
+ Packet *p = streamer_cbdata->p;
+#endif
+ BUG_ON(logger == NULL);
+ BUG_ON(store == NULL);
+
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ if (logger->type == streamer_cbdata->type) {
+ SCLogDebug("logger %p", logger);
+ PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, (const Flow *)f, data, data_len, tx_id, flags);
+ PACKET_PROFILING_TMM_END(p, logger->module_id);
+ }
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ return 0;
+}
+
+/** \brief Http Body Iterator for logging
+ *
+ * Global logic:
+ *
+ * - For each tx
+ * - For each body chunk
+ * - Invoke Streamer
+ */
+
+int HttpBodyIterator(Flow *f, int close, void *cbdata, uint8_t iflags)
+{
+ SCLogDebug("called with %p, %d, %p, %02x", f, close, cbdata, iflags);
+
+ HtpState *s = f->alstate;
+ if (s != NULL && s->conn != NULL) {
+ int tx_progress_done_value_ts =
+ AppLayerParserGetStateProgressCompletionStatus(IPPROTO_TCP,
+ ALPROTO_HTTP, STREAM_TOSERVER);
+ int tx_progress_done_value_tc =
+ AppLayerParserGetStateProgressCompletionStatus(IPPROTO_TCP,
+ ALPROTO_HTTP, STREAM_TOCLIENT);
+
+ // for each tx
+ uint64_t tx_id = 0;
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, f->alproto, f->alstate);
+ SCLogDebug("s->conn %p", s->conn);
+ for (tx_id = 0; tx_id < total_txs; tx_id++) { // TODO optimization store log tx
+ htp_tx_t *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, tx_id);
+ if (tx != NULL) {
+ int tx_done = 0;
+ int tx_logged = 0;
+
+ int tx_progress_ts = AppLayerParserGetStateProgress(
+ IPPROTO_TCP, ALPROTO_HTTP, tx, FlowGetDisruptionFlags(f, STREAM_TOSERVER));
+ if (tx_progress_ts >= tx_progress_done_value_ts) {
+ int tx_progress_tc = AppLayerParserGetStateProgress(
+ IPPROTO_TCP, ALPROTO_HTTP, tx, FlowGetDisruptionFlags(f, STREAM_TOCLIENT));
+ if (tx_progress_tc >= tx_progress_done_value_tc) {
+ tx_done = 1;
+ }
+ }
+
+ SCLogDebug("tx %p", tx);
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud != NULL) {
+ SCLogDebug("htud %p", htud);
+ HtpBody *body = NULL;
+ if (iflags & OUTPUT_STREAMING_FLAG_TOCLIENT)
+ body = &htud->request_body;
+ else if (iflags & OUTPUT_STREAMING_FLAG_TOSERVER)
+ body = &htud->response_body;
+
+ if (body == NULL) {
+ SCLogDebug("no body");
+ goto next;
+ }
+ if (body->first == NULL) {
+ SCLogDebug("no body chunks");
+ goto next;
+ }
+ if (body->last->logged == 1) {
+ SCLogDebug("all logged already");
+ goto next;
+ }
+
+ // for each chunk
+ HtpBodyChunk *chunk = body->first;
+ for ( ; chunk != NULL; chunk = chunk->next) {
+ if (chunk->logged) {
+ SCLogDebug("logged %d", chunk->logged);
+ continue;
+ }
+
+ uint8_t flags = iflags | OUTPUT_STREAMING_FLAG_TRANSACTION;
+ if (chunk->stream_offset == 0)
+ flags |= OUTPUT_STREAMING_FLAG_OPEN;
+ /* if we need to close and we're at the last segment in the list
+ * we add the 'close' flag so the logger can close up. */
+ if ((tx_done || close) && chunk->next == NULL) {
+ flags |= OUTPUT_STREAMING_FLAG_CLOSE;
+ }
+
+ // invoke Streamer
+ Streamer(cbdata, f, chunk->data, (uint32_t)chunk->len, tx_id, flags);
+ //PrintRawDataFp(stdout, chunk->data, chunk->len);
+ chunk->logged = 1;
+ tx_logged = 1;
+ }
+
+ next:
+ /* if we need to close we need to invoke the Streamer for sure. If we
+ * logged no chunks, we call the Streamer with NULL data so it can
+ * close up. */
+ if (tx_logged == 0 && (close||tx_done)) {
+ Streamer(cbdata, f, NULL, 0, tx_id,
+ OUTPUT_STREAMING_FLAG_CLOSE|OUTPUT_STREAMING_FLAG_TRANSACTION);
+ }
+ }
+ }
+ }
+ }
+
+
+ return 0;
+}
+
+int StreamIterator(Flow *f, TcpStream *stream, int close, void *cbdata, uint8_t iflags)
+{
+ SCLogDebug("called with %p, %d, %p, %02x", f, close, cbdata, iflags);
+ int logged = 0;
+
+ /* optimization: don't iterate list if we've logged all,
+ * so check the last segment's flags */
+ if (stream->seg_list_tail != NULL &&
+ (!(stream->seg_list_tail->flags & SEGMENTTCP_FLAG_LOGAPI_PROCESSED)))
+ {
+ TcpSegment *seg = stream->seg_list;
+ while (seg) {
+ uint8_t flags = iflags;
+
+ if (seg->flags & SEGMENTTCP_FLAG_LOGAPI_PROCESSED) {
+ seg = seg->next;
+ continue;
+ }
+
+ if (SEQ_GT(seg->seq + seg->payload_len, stream->last_ack)) {
+ SCLogDebug("seg not (fully) acked yet");
+ break;
+ }
+
+ if (seg->seq == stream->isn + 1)
+ flags |= OUTPUT_STREAMING_FLAG_OPEN;
+ /* if we need to close and we're at the last segment in the list
+ * we add the 'close' flag so the logger can close up. */
+ if (close && seg->next == NULL)
+ flags |= OUTPUT_STREAMING_FLAG_CLOSE;
+
+ Streamer(cbdata, f, seg->payload, (uint32_t)seg->payload_len, 0, flags);
+
+ seg->flags |= SEGMENTTCP_FLAG_LOGAPI_PROCESSED;
+
+ seg = seg->next;
+
+ logged = 1;
+ }
+ }
+
+ /* if we need to close we need to invoke the Streamer for sure. If we
+ * logged no segments, we call the Streamer with NULL data so it can
+ * close up. */
+ if (logged == 0 && close) {
+ Streamer(cbdata, f, NULL, 0, 0, OUTPUT_STREAMING_FLAG_CLOSE);
+ }
+
+ return 0;
+}
+
+static TmEcode OutputStreamingLog(ThreadVars *tv, Packet *p, void *thread_data, PacketQueue *pq, PacketQueue *postpq)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputStreamingLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ StreamerCallbackData streamer_cbdata = { logger, store, tv, p , 0};
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ uint8_t flags = 0;
+ Flow * const f = p->flow;
+
+ /* no flow, no streaming */
+ if (f == NULL) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT)
+ flags |= OUTPUT_STREAMING_FLAG_TOCLIENT;
+ else
+ flags |= OUTPUT_STREAMING_FLAG_TOSERVER;
+
+ FLOWLOCK_WRLOCK(f);
+
+ if (op_thread_data->loggers & (1<<STREAMING_TCP_DATA)) {
+ TcpSession *ssn = f->protoctx;
+ if (ssn) {
+ int close = (ssn->state >= TCP_CLOSED);
+ close |= ((p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0);
+ SCLogDebug("close ? %s", close ? "yes" : "no");
+
+ TcpStream *stream = flags & OUTPUT_STREAMING_FLAG_TOSERVER ? &ssn->client : &ssn->server;
+
+ streamer_cbdata.type = STREAMING_TCP_DATA;
+ StreamIterator(p->flow, stream, close, (void *)&streamer_cbdata, flags);
+ }
+ }
+ if (op_thread_data->loggers & (1<<STREAMING_HTTP_BODIES)) {
+ if (f->alproto == ALPROTO_HTTP && f->alstate != NULL) {
+ int close = 0;
+ TcpSession *ssn = f->protoctx;
+ if (ssn) {
+ close = (ssn->state >= TCP_CLOSED);
+ close |= ((p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0);
+ }
+ SCLogDebug("close ? %s", close ? "yes" : "no");
+ streamer_cbdata.type = STREAMING_HTTP_BODIES;
+ HttpBodyIterator(f, close, (void *)&streamer_cbdata, flags);
+ }
+ }
+
+ FLOWLOCK_UNLOCK(f);
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the tx logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputStreamingLogThreadInit(ThreadVars *tv, void *initdata, void **data) {
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputStreamingLogThreadInit happy (*data %p)", *data);
+
+ OutputStreamingLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogInfo("%s is now set up", logger->name);
+ }
+ }
+
+ td->loggers |= (1<<logger->type);
+
+ logger = logger->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputStreamingLogThreadDeinit(ThreadVars *tv, void *thread_data) {
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputStreamingLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+static void OutputStreamingLogExitPrintStats(ThreadVars *tv, void *thread_data) {
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputStreamingLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModuleStreamingLoggerRegister (void) {
+ tmm_modules[TMM_STREAMINGLOGGER].name = "__streaming_logger__";
+ tmm_modules[TMM_STREAMINGLOGGER].ThreadInit = OutputStreamingLogThreadInit;
+ tmm_modules[TMM_STREAMINGLOGGER].Func = OutputStreamingLog;
+ tmm_modules[TMM_STREAMINGLOGGER].ThreadExitPrintStats = OutputStreamingLogExitPrintStats;
+ tmm_modules[TMM_STREAMINGLOGGER].ThreadDeinit = OutputStreamingLogThreadDeinit;
+ tmm_modules[TMM_STREAMINGLOGGER].cap_flags = 0;
+}
+
+void OutputStreamingShutdown(void)
+{
+ OutputStreamingLogger *logger = list;
+ while (logger) {
+ OutputStreamingLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-streaming.h b/framework/src/suricata/src/output-streaming.h
new file mode 100644
index 00000000..8b303742
--- /dev/null
+++ b/framework/src/suricata/src/output-streaming.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer Filedata Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_STREAMING_H__
+#define __OUTPUT_STREAMING_H__
+
+#include "decode.h"
+#include "util-file.h"
+
+#define OUTPUT_STREAMING_FLAG_OPEN 0x01
+#define OUTPUT_STREAMING_FLAG_CLOSE 0x02
+#define OUTPUT_STREAMING_FLAG_TOSERVER 0x04
+#define OUTPUT_STREAMING_FLAG_TOCLIENT 0x08
+#define OUTPUT_STREAMING_FLAG_TRANSACTION 0x10
+
+enum OutputStreamingType {
+ STREAMING_TCP_DATA,
+ STREAMING_HTTP_BODIES,
+};
+
+/** filedata logger function pointer type */
+typedef int (*StreamingLogger)(ThreadVars *, void *thread_data,
+ const Flow *f, const uint8_t *data, uint32_t data_len,
+ uint64_t tx_id, uint8_t flags);
+
+int OutputRegisterStreamingLogger(const char *name, StreamingLogger LogFunc, OutputCtx *,
+ enum OutputStreamingType);
+
+void TmModuleStreamingLoggerRegister (void);
+
+void OutputStreamingShutdown(void);
+
+#endif /* __OUTPUT_STREAMING_H__ */
diff --git a/framework/src/suricata/src/output-tx.c b/framework/src/suricata/src/output-tx.c
new file mode 100644
index 00000000..93ba9956
--- /dev/null
+++ b/framework/src/suricata/src/output-tx.c
@@ -0,0 +1,308 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer TX Logger Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "tm-modules.h"
+#include "output-tx.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-profiling.h"
+
+typedef struct OutputLoggerThreadStore_ {
+ void *thread_data;
+ struct OutputLoggerThreadStore_ *next;
+} OutputLoggerThreadStore;
+
+/** per thread data for this module, contains a list of per thread
+ * data for the packet loggers. */
+typedef struct OutputLoggerThreadData_ {
+ OutputLoggerThreadStore *store;
+} OutputLoggerThreadData;
+
+/* logger instance, a module + a output ctx,
+ * it's perfectly valid that have multiple instances of the same
+ * log module (e.g. http.log) with different output ctx'. */
+typedef struct OutputTxLogger_ {
+ AppProto alproto;
+ TxLogger LogFunc;
+ OutputCtx *output_ctx;
+ struct OutputTxLogger_ *next;
+ const char *name;
+ TmmId module_id;
+} OutputTxLogger;
+
+static OutputTxLogger *list = NULL;
+
+int OutputRegisterTxLogger(const char *name, AppProto alproto, TxLogger LogFunc, OutputCtx *output_ctx)
+{
+ int module_id = TmModuleGetIdByName(name);
+ if (module_id < 0)
+ return -1;
+
+ OutputTxLogger *op = SCMalloc(sizeof(*op));
+ if (op == NULL)
+ return -1;
+ memset(op, 0x00, sizeof(*op));
+
+ op->alproto = alproto;
+ op->LogFunc = LogFunc;
+ op->output_ctx = output_ctx;
+ op->name = name;
+ op->module_id = (TmmId) module_id;
+
+ if (list == NULL)
+ list = op;
+ else {
+ OutputTxLogger *t = list;
+ while (t->next)
+ t = t->next;
+ t->next = op;
+ }
+
+ SCLogDebug("OutputRegisterTxLogger happy");
+ return 0;
+}
+
+static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data, PacketQueue *pq, PacketQueue *postpq)
+{
+ BUG_ON(thread_data == NULL);
+ BUG_ON(list == NULL);
+
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputTxLogger *logger = list;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ BUG_ON(logger == NULL && store == NULL);
+
+ if (p->flow == NULL)
+ return TM_ECODE_OK;
+
+ Flow * const f = p->flow;
+
+ FLOWLOCK_WRLOCK(f); /* WRITE lock before we updated flow logged id */
+ AppProto alproto = f->alproto;
+
+ if (AppLayerParserProtocolIsTxAware(p->proto, alproto) == 0)
+ goto end;
+ if (AppLayerParserProtocolHasLogger(p->proto, alproto) == 0)
+ goto end;
+
+ void *alstate = f->alstate;
+ if (alstate == NULL) {
+ SCLogDebug("no alstate");
+ goto end;
+ }
+
+ uint64_t total_txs = AppLayerParserGetTxCnt(p->proto, alproto, alstate);
+ uint64_t tx_id = AppLayerParserGetTransactionLogId(f->alparser);
+ int tx_progress_done_value_ts =
+ AppLayerParserGetStateProgressCompletionStatus(p->proto, alproto,
+ STREAM_TOSERVER);
+ int tx_progress_done_value_tc =
+ AppLayerParserGetStateProgressCompletionStatus(p->proto, alproto,
+ STREAM_TOCLIENT);
+ int proto_logged = 0;
+
+ for (; tx_id < total_txs; tx_id++)
+ {
+ void *tx = AppLayerParserGetTx(p->proto, alproto, alstate, tx_id);
+ if (tx == NULL) {
+ SCLogDebug("tx is NULL not logging");
+ continue;
+ }
+
+ if (!(AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_EOF)))
+ {
+ int tx_progress = AppLayerParserGetStateProgress(p->proto, alproto,
+ tx, FlowGetDisruptionFlags(f, STREAM_TOSERVER));
+ if (tx_progress < tx_progress_done_value_ts) {
+ SCLogDebug("progress not far enough, not logging");
+ break;
+ }
+
+ tx_progress = AppLayerParserGetStateProgress(p->proto, alproto,
+ tx, FlowGetDisruptionFlags(f, STREAM_TOCLIENT));
+ if (tx_progress < tx_progress_done_value_tc) {
+ SCLogDebug("progress not far enough, not logging");
+ break;
+ }
+ }
+
+ // call each logger here (pseudo code)
+ logger = list;
+ store = op_thread_data->store;
+ while (logger && store) {
+ BUG_ON(logger->LogFunc == NULL);
+
+ SCLogDebug("logger %p", logger);
+ if (logger->alproto == alproto) {
+ SCLogDebug("alproto match, logging tx_id %ju", tx_id);
+ PACKET_PROFILING_TMM_START(p, logger->module_id);
+ logger->LogFunc(tv, store->thread_data, p, f, alstate, tx, tx_id);
+ PACKET_PROFILING_TMM_END(p, logger->module_id);
+ proto_logged = 1;
+ }
+
+ logger = logger->next;
+ store = store->next;
+
+ BUG_ON(logger == NULL && store != NULL);
+ BUG_ON(logger != NULL && store == NULL);
+ }
+
+ if (proto_logged) {
+ SCLogDebug("updating log tx_id %ju", tx_id);
+ AppLayerParserSetTransactionLogId(f->alparser);
+ }
+ }
+
+end:
+ FLOWLOCK_UNLOCK(f);
+ return TM_ECODE_OK;
+}
+
+/** \brief thread init for the tx logger
+ * This will run the thread init functions for the individual registered
+ * loggers */
+static TmEcode OutputTxLogThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ OutputLoggerThreadData *td = SCMalloc(sizeof(*td));
+ if (td == NULL)
+ return TM_ECODE_FAILED;
+ memset(td, 0x00, sizeof(*td));
+
+ *data = (void *)td;
+
+ SCLogDebug("OutputTxLogThreadInit happy (*data %p)", *data);
+
+ OutputTxLogger *logger = list;
+ while (logger) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadInit) {
+ void *retptr = NULL;
+ if (tm_module->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
+ OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
+/* todo */ BUG_ON(ts == NULL);
+ memset(ts, 0x00, sizeof(*ts));
+
+ /* store thread handle */
+ ts->thread_data = retptr;
+
+ if (td->store == NULL) {
+ td->store = ts;
+ } else {
+ OutputLoggerThreadStore *tmp = td->store;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ts;
+ }
+
+ SCLogDebug("%s is now set up", logger->name);
+ }
+ }
+
+ logger = logger->next;
+ }
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode OutputTxLogThreadDeinit(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputTxLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadDeinit) {
+ tm_module->ThreadDeinit(tv, store->thread_data);
+ }
+
+ OutputLoggerThreadStore *next_store = store->next;
+ SCFree(store);
+ store = next_store;
+ logger = logger->next;
+ }
+ return TM_ECODE_OK;
+}
+
+static void OutputTxLogExitPrintStats(ThreadVars *tv, void *thread_data)
+{
+ OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
+ OutputLoggerThreadStore *store = op_thread_data->store;
+ OutputTxLogger *logger = list;
+
+ while (logger && store) {
+ TmModule *tm_module = TmModuleGetByName((char *)logger->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", logger->name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm_module->ThreadExitPrintStats) {
+ tm_module->ThreadExitPrintStats(tv, store->thread_data);
+ }
+
+ logger = logger->next;
+ store = store->next;
+ }
+}
+
+void TmModuleTxLoggerRegister (void)
+{
+ tmm_modules[TMM_TXLOGGER].name = "__tx_logger__";
+ tmm_modules[TMM_TXLOGGER].ThreadInit = OutputTxLogThreadInit;
+ tmm_modules[TMM_TXLOGGER].Func = OutputTxLog;
+ tmm_modules[TMM_TXLOGGER].ThreadExitPrintStats = OutputTxLogExitPrintStats;
+ tmm_modules[TMM_TXLOGGER].ThreadDeinit = OutputTxLogThreadDeinit;
+ tmm_modules[TMM_TXLOGGER].cap_flags = 0;
+}
+
+void OutputTxShutdown(void)
+{
+ OutputTxLogger *logger = list;
+ while (logger) {
+ OutputTxLogger *next_logger = logger->next;
+ SCFree(logger);
+ logger = next_logger;
+ }
+ list = NULL;
+}
diff --git a/framework/src/suricata/src/output-tx.h b/framework/src/suricata/src/output-tx.h
new file mode 100644
index 00000000..7ffb8a6d
--- /dev/null
+++ b/framework/src/suricata/src/output-tx.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * AppLayer TX Logger Output registration functions
+ */
+
+#ifndef __OUTPUT_TX_H__
+#define __OUTPUT_TX_H__
+
+#include "decode.h"
+
+/** packet logger function pointer type */
+typedef int (*TxLogger)(ThreadVars *, void *thread_data, const Packet *, Flow *f, void *state, void *tx, uint64_t tx_id);
+
+/** packet logger condition function pointer type,
+ * must return true for packets that should be logged
+ */
+//typedef int (*TxLogCondition)(ThreadVars *, const Packet *);
+
+int OutputRegisterTxLogger(const char *name, AppProto alproto, TxLogger LogFunc, OutputCtx *);
+
+void TmModuleTxLoggerRegister (void);
+
+void OutputTxShutdown(void);
+
+#endif /* __OUTPUT_PACKET_H__ */
diff --git a/framework/src/suricata/src/output.c b/framework/src/suricata/src/output.c
new file mode 100644
index 00000000..1146b7b2
--- /dev/null
+++ b/framework/src/suricata/src/output.c
@@ -0,0 +1,701 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited, Jason Ish <jason.ish@endace.com>
+ *
+ * Output registration functions
+ */
+
+#include "suricata-common.h"
+#include "flow.h"
+#include "conf.h"
+#include "tm-threads.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "output.h"
+
+static TAILQ_HEAD(, OutputModule_) output_modules =
+ TAILQ_HEAD_INITIALIZER(output_modules);
+
+/**
+ * Registry of flags to be updated on file rotation notification.
+ */
+typedef struct OutputFileRolloverFlag_ {
+ int *flag;
+
+ TAILQ_ENTRY(OutputFileRolloverFlag_) entries;
+} OutputFileRolloverFlag;
+
+TAILQ_HEAD(, OutputFileRolloverFlag_) output_file_rotation_flags =
+ TAILQ_HEAD_INITIALIZER(output_file_rotation_flags);
+
+/**
+ * \brief Register an output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *))
+{
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL))
+ goto error;
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Output module \"%s\" registered.", name);
+
+ return;
+
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in OutputRegisterModule. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a packet output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterPacketModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *),
+ PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc)
+{
+ if (unlikely(PacketLogFunc == NULL || PacketConditionFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->PacketLogFunc = PacketLogFunc;
+ module->PacketConditionFunc = PacketConditionFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Packet logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a packet output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterPacketSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *parent_ctx),
+ PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc)
+{
+ if (unlikely(PacketLogFunc == NULL || PacketConditionFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->PacketLogFunc = PacketLogFunc;
+ module->PacketConditionFunc = PacketConditionFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Packet logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a tx output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterTxModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), AppProto alproto,
+ TxLogger TxLogFunc)
+{
+ if (unlikely(TxLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->TxLogFunc = TxLogFunc;
+ module->alproto = alproto;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Tx logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+void
+OutputRegisterTxSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *parent_ctx),
+ AppProto alproto, TxLogger TxLogFunc)
+{
+ if (unlikely(TxLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->TxLogFunc = TxLogFunc;
+ module->alproto = alproto;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Tx logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a file output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFileModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FileLogger FileLogFunc)
+{
+ if (unlikely(FileLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->FileLogFunc = FileLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("File logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a file output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFileSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FileLogger FileLogFunc)
+{
+ if (unlikely(FileLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->FileLogFunc = FileLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("File logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a file data output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFiledataModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FiledataLogger FiledataLogFunc)
+{
+ if (unlikely(FiledataLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->FiledataLogFunc = FiledataLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Filedata logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a file data output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFiledataSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FiledataLogger FiledataLogFunc)
+{
+ if (unlikely(FiledataLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->FiledataLogFunc = FiledataLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Filedata logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a flow output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFlowModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FlowLogger FlowLogFunc)
+{
+ if (unlikely(FlowLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->FlowLogFunc = FlowLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Flow logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a flow output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterFlowSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FlowLogger FlowLogFunc)
+{
+ if (unlikely(FlowLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->FlowLogFunc = FlowLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Flow logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a streaming data output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterStreamingModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), StreamingLogger StreamingLogFunc,
+ enum OutputStreamingType stream_type)
+{
+ if (unlikely(StreamingLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->StreamingLogFunc = StreamingLogFunc;
+ module->stream_type = stream_type;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Streaming logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a streaming data output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterStreamingSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ StreamingLogger StreamingLogFunc, enum OutputStreamingType stream_type)
+{
+ if (unlikely(StreamingLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->StreamingLogFunc = StreamingLogFunc;
+ module->stream_type = stream_type;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Streaming logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a stats data output module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterStatsModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), StatsLogger StatsLogFunc)
+{
+ if (unlikely(StatsLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->InitFunc = InitFunc;
+ module->StatsLogFunc = StatsLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Stats logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Register a stats data output sub-module.
+ *
+ * This function will register an output module so it can be
+ * configured with the configuration file.
+ *
+ * \retval Returns 0 on success, -1 on failure.
+ */
+void
+OutputRegisterStatsSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ StatsLogger StatsLogFunc)
+{
+ if (unlikely(StatsLogFunc == NULL)) {
+ goto error;
+ }
+
+ OutputModule *module = SCCalloc(1, sizeof(*module));
+ if (unlikely(module == NULL)) {
+ goto error;
+ }
+
+ module->name = name;
+ module->conf_name = conf_name;
+ module->parent_name = parent_name;
+ module->InitSubFunc = InitFunc;
+ module->StatsLogFunc = StatsLogFunc;
+ TAILQ_INSERT_TAIL(&output_modules, module, entries);
+
+ SCLogDebug("Stats logger \"%s\" registered.", name);
+ return;
+error:
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * \brief Get an output module by name.
+ *
+ * \retval The OutputModule with the given name or NULL if no output module
+ * with the given name is registered.
+ */
+OutputModule *
+OutputGetModuleByConfName(const char *conf_name)
+{
+ OutputModule *module;
+
+ TAILQ_FOREACH(module, &output_modules, entries) {
+ if (strcmp(module->conf_name, conf_name) == 0)
+ return module;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Deregister all modules. Useful for a memory clean exit.
+ */
+void
+OutputDeregisterAll(void)
+{
+ OutputModule *module;
+
+ while ((module = TAILQ_FIRST(&output_modules))) {
+ TAILQ_REMOVE(&output_modules, module, entries);
+ SCFree(module);
+ }
+}
+
+static int drop_loggers = 0;
+
+int OutputDropLoggerEnable(void)
+{
+ if (drop_loggers)
+ return -1;
+ drop_loggers++;
+ return 0;
+}
+
+void OutputDropLoggerDisable(void)
+{
+ if (drop_loggers)
+ drop_loggers--;
+}
+
+static int tls_loggers = 0;
+
+int OutputTlsLoggerEnable(void)
+{
+ if (tls_loggers)
+ return -1;
+ tls_loggers++;
+ return 0;
+}
+
+void OutputTlsLoggerDisable(void)
+{
+ if (tls_loggers)
+ tls_loggers--;
+}
+
+static int ssh_loggers = 0;
+
+int OutputSshLoggerEnable(void)
+{
+ if (ssh_loggers)
+ return -1;
+ ssh_loggers++;
+ return 0;
+}
+
+void OutputSshLoggerDisable(void)
+{
+ if (ssh_loggers)
+ ssh_loggers--;
+}
+
+/**
+ * \brief Register a flag for file rotation notification.
+ *
+ * \param flag A pointer that will be set to 1 when file rotation is
+ * requested.
+ */
+void OutputRegisterFileRotationFlag(int *flag)
+{
+ OutputFileRolloverFlag *flag_entry = SCCalloc(1, sizeof(*flag_entry));
+ if (unlikely(flag_entry == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory to register file rotation flag");
+ return;
+ }
+ flag_entry->flag = flag;
+ TAILQ_INSERT_TAIL(&output_file_rotation_flags, flag_entry, entries);
+}
+
+/**
+ * \brief Unregister a file rotation flag.
+ *
+ * Note that it is safe to call this function with a flag that may not
+ * have been registered, in which case this function won't do
+ * anything.
+ *
+ * \param flag A pointer that has been previously registered for file
+ * rotation notifications.
+ */
+void OutputUnregisterFileRotationFlag(int *flag)
+{
+ OutputFileRolloverFlag *entry, *next;
+ for (entry = TAILQ_FIRST(&output_file_rotation_flags); entry != NULL;
+ entry = next) {
+ next = TAILQ_NEXT(entry, entries);
+ if (entry->flag == flag) {
+ TAILQ_REMOVE(&output_file_rotation_flags, entry, entries);
+ SCFree(entry);
+ break;
+ }
+ }
+}
+
+/**
+ * \brief Notifies all registered file rotation notification flags.
+ */
+void OutputNotifyFileRotation(void) {
+ OutputFileRolloverFlag *flag;
+ TAILQ_FOREACH(flag, &output_file_rotation_flags, entries) {
+ *(flag->flag) = 1;
+ }
+}
diff --git a/framework/src/suricata/src/output.h b/framework/src/suricata/src/output.h
new file mode 100644
index 00000000..9a4add0e
--- /dev/null
+++ b/framework/src/suricata/src/output.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited, Jason Ish <jason.ish@endace.com>
+ */
+
+#ifndef __OUTPUT_H__
+#define __OUTPUT_H__
+
+#include "suricata.h"
+#include "tm-threads.h"
+
+#define DEFAULT_LOG_MODE_APPEND "yes"
+#define DEFAULT_LOG_FILETYPE "regular"
+
+#include "output-packet.h"
+#include "output-tx.h"
+#include "output-file.h"
+#include "output-filedata.h"
+#include "output-flow.h"
+#include "output-streaming.h"
+#include "output-stats.h"
+
+typedef struct OutputModule_ {
+ const char *name;
+ const char *conf_name;
+ const char *parent_name;
+ OutputCtx *(*InitFunc)(ConfNode *);
+ OutputCtx *(*InitSubFunc)(ConfNode *, OutputCtx *parent_ctx);
+
+ PacketLogger PacketLogFunc;
+ PacketLogCondition PacketConditionFunc;
+ TxLogger TxLogFunc;
+ FileLogger FileLogFunc;
+ FiledataLogger FiledataLogFunc;
+ FlowLogger FlowLogFunc;
+ StreamingLogger StreamingLogFunc;
+ StatsLogger StatsLogFunc;
+ AppProto alproto;
+ enum OutputStreamingType stream_type;
+
+ TAILQ_ENTRY(OutputModule_) entries;
+} OutputModule;
+
+void OutputRegisterModule(const char *, const char *, OutputCtx *(*)(ConfNode *));
+
+void OutputRegisterPacketModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *),
+ PacketLogger LogFunc, PacketLogCondition ConditionFunc);
+void OutputRegisterPacketSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ PacketLogger LogFunc, PacketLogCondition ConditionFunc);
+
+void OutputRegisterTxModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), AppProto alproto,
+ TxLogger TxLogFunc);
+void OutputRegisterTxSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *parent_ctx),
+ AppProto alproto, TxLogger TxLogFunc);
+
+void OutputRegisterFileModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FileLogger FileLogFunc);
+void OutputRegisterFileSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FileLogger FileLogFunc);
+
+void OutputRegisterFiledataModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FiledataLogger FiledataLogFunc);
+void OutputRegisterFiledataSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FiledataLogger FiledataLogFunc);
+
+void OutputRegisterFlowModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), FlowLogger FlowLogFunc);
+void OutputRegisterFlowSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ FlowLogger FlowLogFunc);
+
+void OutputRegisterStreamingModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), StreamingLogger StreamingLogFunc,
+ enum OutputStreamingType stream_type);
+void OutputRegisterStreamingSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ StreamingLogger StreamingLogFunc, enum OutputStreamingType stream_type);
+
+void OutputRegisterStatsModule(const char *name, const char *conf_name,
+ OutputCtx *(*InitFunc)(ConfNode *), StatsLogger StatsLogFunc);
+void OutputRegisterStatsSubModule(const char *parent_name, const char *name,
+ const char *conf_name, OutputCtx *(*InitFunc)(ConfNode *, OutputCtx *),
+ StatsLogger StatsLogFunc);
+
+OutputModule *OutputGetModuleByConfName(const char *name);
+void OutputDeregisterAll(void);
+
+int OutputDropLoggerEnable(void);
+void OutputDropLoggerDisable(void);
+
+int OutputTlsLoggerEnable(void);
+void OutputTlsLoggerDisable(void);
+
+int OutputSshLoggerEnable(void);
+void OutputSshLoggerDisable(void);
+
+void OutputRegisterFileRotationFlag(int *flag);
+void OutputUnregisterFileRotationFlag(int *flag);
+void OutputNotifyFileRotation(void);
+
+#endif /* ! __OUTPUT_H__ */
diff --git a/framework/src/suricata/src/packet-queue.c b/framework/src/suricata/src/packet-queue.c
new file mode 100644
index 00000000..c500971b
--- /dev/null
+++ b/framework/src/suricata/src/packet-queue.c
@@ -0,0 +1,198 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Packet Queue portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "suricata.h"
+#include "util-var.h"
+#include "pkt-var.h"
+
+#ifdef DEBUG
+void PacketQueueValidateDebug(PacketQueue *q)
+{
+ SCLogDebug("q->len %u, q->top %p, q->bot %p", q->len, q->top, q->bot);
+
+ if (q->len == 0) {
+ BUG_ON(q->top != NULL);
+ BUG_ON(q->bot != NULL);
+ } else if(q->len == 1) {
+ SCLogDebug("q->top->next %p, q->top->prev %p", q->top->next, q->top->prev);
+ SCLogDebug("q->bot->next %p, q->bot->prev %p", q->bot->next, q->bot->prev);
+
+ BUG_ON(q->top != q->bot);
+ BUG_ON(q->top->next != NULL);
+ BUG_ON(q->bot->next != NULL);
+ BUG_ON(q->top->prev != NULL);
+ BUG_ON(q->bot->prev != NULL);
+ } else if (q->len == 2) {
+ SCLogDebug("q->top->next %p, q->top->prev %p", q->top->next, q->top->prev);
+ SCLogDebug("q->bot->next %p, q->bot->prev %p", q->bot->next, q->bot->prev);
+
+ BUG_ON(q->top == NULL);
+ BUG_ON(q->bot == NULL);
+
+ BUG_ON(q->top == q->bot);
+
+ BUG_ON(q->top->prev != NULL);
+ BUG_ON(q->top->next != q->bot);
+
+ BUG_ON(q->bot->prev != q->top);
+ BUG_ON(q->bot->next != NULL);
+ } else {
+ BUG_ON(q->top == NULL);
+ BUG_ON(q->bot == NULL);
+
+ SCLogDebug("q->top->next %p, q->top->prev %p", q->top->next, q->top->prev);
+ SCLogDebug("q->bot->next %p, q->bot->prev %p", q->bot->next, q->bot->prev);
+
+ BUG_ON(q->top == q->bot);
+ BUG_ON(q->top->prev != NULL);
+ BUG_ON(q->bot->next != NULL);
+
+ BUG_ON(q->top->next == q->bot);
+ BUG_ON(q->bot->prev == q->top);
+
+ Packet *p, *pp;
+ for (p = q->top, pp = p->prev; p != NULL; pp = p, p = p->next) {
+ SCLogDebug("p %p, pp %p, p->next %p, p->prev %p", p, pp, p->next, p->prev);
+ BUG_ON(pp != p->prev);
+ }
+
+ }
+}
+
+#define BUGGER_ON(cond) { \
+ if ((cond)) { \
+ PacketQueueValidateDebug(q); \
+ } \
+}
+
+void PacketQueueValidate(PacketQueue *q)
+{
+ if (q->len == 0) {
+ BUGGER_ON(q->top != NULL);
+ BUGGER_ON(q->bot != NULL);
+ } else if(q->len == 1) {
+ BUGGER_ON(q->top != q->bot);
+ BUGGER_ON(q->top->next != NULL);
+ BUGGER_ON(q->bot->next != NULL);
+ BUGGER_ON(q->top->prev != NULL);
+ BUGGER_ON(q->bot->prev != NULL);
+ } else if (q->len == 2) {
+ BUGGER_ON(q->top == NULL);
+ BUGGER_ON(q->bot == NULL);
+
+ BUGGER_ON(q->top == q->bot);
+
+ BUGGER_ON(q->top->prev != NULL);
+ BUGGER_ON(q->top->next != q->bot);
+
+ BUGGER_ON(q->bot->prev != q->top);
+ BUGGER_ON(q->bot->next != NULL);
+ } else {
+ BUGGER_ON(q->top == NULL);
+ BUGGER_ON(q->bot == NULL);
+
+ BUGGER_ON(q->top == q->bot);
+ BUGGER_ON(q->top->prev != NULL);
+ BUGGER_ON(q->bot->next != NULL);
+
+ BUGGER_ON(q->top->next == q->bot);
+ BUGGER_ON(q->bot->prev == q->top);
+
+ Packet *p, *pp;
+ for (p = q->top, pp = p->prev; p != NULL; pp = p, p = p->next) {
+ BUGGER_ON(pp != p->prev);
+ }
+
+ }
+}
+#endif /* DEBUG */
+
+void PacketEnqueue (PacketQueue *q, Packet *p)
+{
+ //PacketQueueValidateDebug(q);
+
+ if (p == NULL)
+ return;
+
+ /* more packets in queue */
+ if (q->top != NULL) {
+ p->prev = NULL;
+ p->next = q->top;
+ q->top->prev = p;
+ q->top = p;
+ /* only packet */
+ } else {
+ p->prev = NULL;
+ p->next = NULL;
+ q->top = p;
+ q->bot = p;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ //PacketQueueValidateDebug(q);
+}
+
+Packet *PacketDequeue (PacketQueue *q)
+{
+ Packet *p = NULL;
+
+ //PacketQueueValidateDebug(q);
+ /* if the queue is empty there are no packets left. */
+ if (q->len == 0) {
+ return NULL;
+ }
+
+ q->len--;
+
+ /* pull the bottom packet from the queue */
+ p = q->bot;
+ /* Weird issue: sometimes it looks that two thread arrive
+ * here at the same time so the bot ptr is NULL (only on OS X?)
+ */
+ if (p == NULL) {
+ return NULL;
+ }
+
+ /* more packets in queue */
+ if (q->bot->prev != NULL) {
+ q->bot = q->bot->prev;
+ q->bot->next = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+
+ //PacketQueueValidateDebug(q);
+ return p;
+}
+
diff --git a/framework/src/suricata/src/packet-queue.h b/framework/src/suricata/src/packet-queue.h
new file mode 100644
index 00000000..b93a942b
--- /dev/null
+++ b/framework/src/suricata/src/packet-queue.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __PACKET_QUEUE_H__
+#define __PACKET_QUEUE_H__
+
+#include "threads.h"
+#include "decode.h"
+
+void PacketEnqueue (PacketQueue *, Packet *);
+Packet *PacketDequeue (PacketQueue *);
+
+#endif /* __PACKET_QUEUE_H__ */
+
diff --git a/framework/src/suricata/src/pkt-var.c b/framework/src/suricata/src/pkt-var.c
new file mode 100644
index 00000000..b3878bde
--- /dev/null
+++ b/framework/src/suricata/src/pkt-var.c
@@ -0,0 +1,124 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements per packet vars
+ *
+ * \todo move away from a linked list implementation
+ * \todo use different datatypes, such as string, int, etc.
+ * \todo have more than one instance of the same var, and be able to match on a
+ * specific one, or one all at a time. So if a certain capture matches
+ * multiple times, we can operate on all of them.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "pkt-var.h"
+#include "util-debug.h"
+
+/* puts a new value into a pktvar */
+void PktVarUpdate(PktVar *pv, uint8_t *value, uint16_t size)
+{
+ if (pv->value) SCFree(pv->value);
+ pv->value = value;
+ pv->value_len = size;
+}
+
+/* get the pktvar with name 'name' from the pkt
+ *
+ * name is a normal string*/
+PktVar *PktVarGet(Packet *p, char *name)
+{
+ PktVar *pv = p->pktvar;
+
+ for (;pv != NULL; pv = pv->next) {
+ if (pv->name && strcmp(pv->name, name) == 0)
+ return pv;
+ }
+
+ return NULL;
+}
+
+/* add a pktvar to the pkt, or update it */
+void PktVarAdd(Packet *p, char *name, uint8_t *value, uint16_t size)
+{
+ //printf("Adding packet var \"%s\" with value(%" PRId32 ") \"%s\"\n", name, size, value);
+
+ PktVar *pv = PktVarGet(p, name);
+ if (pv == NULL) {
+ pv = SCMalloc(sizeof(PktVar));
+ if (unlikely(pv == NULL))
+ return;
+
+ pv->name = name;
+ pv->value = value;
+ pv->value_len = size;
+ pv->next = NULL;
+
+ PktVar *tpv = p->pktvar;
+ if (p->pktvar == NULL) p->pktvar = pv;
+ else {
+ while(tpv) {
+ if (tpv->next == NULL) {
+ tpv->next = pv;
+ return;
+ }
+ tpv = tpv->next;
+ }
+ }
+ } else {
+ PktVarUpdate(pv, value, size);
+ }
+}
+
+void PktVarFree(PktVar *pv)
+{
+ if (pv == NULL)
+ return;
+
+ pv->name = NULL;
+ if (pv->value != NULL)
+ SCFree(pv->value);
+ PktVar *pv_next = pv->next;
+
+ SCFree(pv);
+
+ if (pv_next != NULL)
+ PktVarFree(pv_next);
+}
+
+void PktVarPrint(PktVar *pv)
+{
+ uint16_t i;
+
+ if (pv == NULL)
+ return;
+
+ printf("Name \"%s\", Value \"", pv->name);
+ for (i = 0; i < pv->value_len; i++) {
+ if (isprint(pv->value[i])) printf("%c", pv->value[i]);
+ else printf("\\%02X", pv->value[i]);
+ }
+ printf("\", Len \"%" PRIu32 "\"\n", pv->value_len);
+
+ PktVarPrint(pv->next);
+}
+
diff --git a/framework/src/suricata/src/pkt-var.h b/framework/src/suricata/src/pkt-var.h
new file mode 100644
index 00000000..0ba87572
--- /dev/null
+++ b/framework/src/suricata/src/pkt-var.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __PKT_VAR_H__
+#define __PKT_VAR_H__
+
+void PktVarAdd(Packet *, char *, uint8_t *, uint16_t);
+PktVar *PktVarGet(Packet *, char *);
+void PktVarFree(PktVar *);
+void PktVarPrint(PktVar *);
+
+#endif /* __PKT_VAR_H__ */
+
diff --git a/framework/src/suricata/src/ptxdump.py b/framework/src/suricata/src/ptxdump.py
new file mode 100644
index 00000000..097e5173
--- /dev/null
+++ b/framework/src/suricata/src/ptxdump.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+from string import *
+import os, getopt, sys, platform
+
+header = '''/* Auto-generated by ptxdump.py DO NOT EDIT
+*
+* This file contains the ptx code of the Cuda kernels.
+* A kernel is identified by its name and the compute capability (e.g. _sm_10).
+*/
+'''
+
+def FormatCharHex(d):
+ s = hex(ord(d))
+ if len(s) == 3:
+ s = "0x0" + s[2]
+ return s
+
+def CleanFileName(f):
+ v = f.replace("-","_")
+ v = v.replace(".ptx","")
+ return v
+
+if not(len(sys.argv[1:]) >= 2):
+ print("Usage: ptx2c.py <output> <in.ptx ..> ")
+ print("Description: creates a header file containing the ptx files as character array" + os.linesep)
+ sys.exit(0)
+
+out_h = sys.argv[1] + ".h"
+out = open(out_h, 'w')
+
+out.writelines(header)
+out.writelines("#ifdef __SC_CUDA_SUPPORT__\n")
+out.writelines("#ifndef __ptxdump_h__\n")
+out.writelines("#define __ptxdump_h__\n\n")
+
+# write char arrays
+for file in sys.argv[2:]:
+ in_ptx = open(file, 'r')
+ source = in_ptx.read()
+ source_len = len(source)
+
+ varname = CleanFileName(file)
+
+ out.writelines("const unsigned char " + varname + "[" + str(source_len+1) + "] = {\n")
+ newlinecnt = 0
+ for i in range(0, source_len):
+ out.write(FormatCharHex(source[i]) + ", ")
+ newlinecnt += 1
+ if newlinecnt == 16:
+ newlinecnt = 0
+ out.write("\n")
+ out.write("0x00\n};\n\n")
+
+ print(sys.argv[0] + ": CUmodule " + varname + " packed successfully")
+
+# write retrieval function
+out.writelines("const unsigned char* SCCudaPtxDumpGetModule(const char* module){\n");
+for file in sys.argv[2:]:
+ out.writelines('\tif (!strcmp(module, "' + file.replace(".ptx","")+'"))\n')
+ out.writelines("\t\treturn " + CleanFileName(file)+";\n")
+out.writelines('\tSCLogError(SC_ERR_FATAL, "Error in SCCudaPtxDumpGetModule, module %s not found. Exiting...",module);\n')
+out.writelines("\texit(EXIT_FAILURE);\n")
+out.writelines("};\n")
+
+out.writelines("#endif /* __ptxdump_h__ */\n")
+out.writelines("#endif /* __SC_CUDA_SUPPORT__ */\n")
+
+print(sys.argv[0] + ": " + out_h + " written successfully")
+
+in_ptx.close()
+out.close()
diff --git a/framework/src/suricata/src/queue.h b/framework/src/suricata/src/queue.h
new file mode 100644
index 00000000..13455aa4
--- /dev/null
+++ b/framework/src/suricata/src/queue.h
@@ -0,0 +1,543 @@
+/* $OpenBSD: queue.h,v 1.32 2007/04/30 18:42:34 pedro Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) ((a) = ((void *)-1))
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+
+/*
+ * The following macros are not used and are in conflict with Win32 API
+ */
+
+#if 0
+
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+#endif /* 0 */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+/* removal safe iterator using a temprary element has last param */
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for((var) = TAILQ_FIRST(head), \
+ (tvar) = TAILQ_FIRST(head) ? TAILQ_NEXT(TAILQ_FIRST(head), field): NULL ; \
+ (var) != TAILQ_END(head); \
+ (var = tvar), (tvar) = var ? TAILQ_NEXT(var, field): NULL)
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/framework/src/suricata/src/reputation.c b/framework/src/suricata/src/reputation.c
new file mode 100644
index 00000000..d1aef713
--- /dev/null
+++ b/framework/src/suricata/src/reputation.c
@@ -0,0 +1,2354 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ * Original Idea by Matt Jonkman
+ *
+ * IP Reputation Module, initial API for IPV4 and IPV6 feed
+ */
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-ip.h"
+#include "util-radix-tree.h"
+#include "util-unittest.h"
+#include "suricata-common.h"
+#include "threads.h"
+#include "util-print.h"
+#include "host.h"
+#include "conf.h"
+#include "detect.h"
+#include "reputation.h"
+
+/** effective reputation version, atomic as the host
+ * time out code will use it to check if a host's
+ * reputation info is outdated. */
+SC_ATOMIC_DECLARE(uint32_t, srep_eversion);
+/** reputation version set to the host's reputation,
+ * this will be set to 1 before rep files are loaded,
+ * so hosts will always have a minial value of 1 */
+static uint32_t srep_version = 0;
+
+static uint32_t SRepIncrVersion(void)
+{
+ return ++srep_version;
+}
+
+static uint32_t SRepGetVersion(void)
+{
+ return srep_version;
+}
+
+void SRepResetVersion(void)
+{
+ srep_version = 0;
+}
+
+static uint32_t SRepGetEffectiveVersion(void)
+{
+ return SC_ATOMIC_GET(srep_eversion);
+}
+
+static void SRepCIDRFreeUserData(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+static void SRepCIDRAddNetblock(SRepCIDRTree *cidr_ctx, char *ip, int cat, int value)
+{
+ SReputation *user_data = NULL;
+ if ((user_data = SCMalloc(sizeof(SReputation))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error allocating memory. Exiting");
+ exit(EXIT_FAILURE);
+ }
+ memset(user_data, 0x00, sizeof(SReputation));
+
+ user_data->version = SRepGetVersion();
+ user_data->rep[cat] = value;
+
+ if (strchr(ip, ':') != NULL) {
+ if (cidr_ctx->srepIPV6_tree[cat] == NULL) {
+ cidr_ctx->srepIPV6_tree[cat] = SCRadixCreateRadixTree(SRepCIDRFreeUserData, NULL);
+ if (cidr_ctx->srepIPV6_tree[cat] == NULL) {
+ SCLogDebug("Error initializing Reputation IPV6 with CIDR module for cat %d", cat);
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("Reputation IPV6 with CIDR module for cat %d initialized", cat);
+ }
+
+ SCLogDebug("adding ipv6 host %s", ip);
+ if (SCRadixAddKeyIPV6String(ip, cidr_ctx->srepIPV6_tree[cat], (void *)user_data) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "failed to add ipv6 host %s", ip);
+ }
+
+ } else {
+ if (cidr_ctx->srepIPV4_tree[cat] == NULL) {
+ cidr_ctx->srepIPV4_tree[cat] = SCRadixCreateRadixTree(SRepCIDRFreeUserData, NULL);
+ if (cidr_ctx->srepIPV4_tree[cat] == NULL) {
+ SCLogDebug("Error initializing Reputation IPV4 with CIDR module for cat %d", cat);
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("Reputation IPV4 with CIDR module for cat %d initialized", cat);
+ }
+
+ SCLogDebug("adding ipv4 host %s", ip);
+ if (SCRadixAddKeyIPV4String(ip, cidr_ctx->srepIPV4_tree[cat], (void *)user_data) == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "failed to add ipv4 host %s", ip);
+ }
+ }
+}
+
+static uint8_t SRepCIDRGetIPv4IPRep(SRepCIDRTree *cidr_ctx, uint8_t *ipv4_addr, uint8_t cat)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch(ipv4_addr, cidr_ctx->srepIPV4_tree[cat], &user_data);
+ if (user_data == NULL)
+ return 0;
+
+ SReputation *r = (SReputation *)user_data;
+ return r->rep[cat];
+}
+
+static uint8_t SRepCIDRGetIPv6IPRep(SRepCIDRTree *cidr_ctx, uint8_t *ipv6_addr, uint8_t cat)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6BestMatch(ipv6_addr, cidr_ctx->srepIPV6_tree[cat], &user_data);
+ if (user_data == NULL)
+ return 0;
+
+ SReputation *r = (SReputation *)user_data;
+ return r->rep[cat];
+}
+
+uint8_t SRepCIDRGetIPRepSrc(SRepCIDRTree *cidr_ctx, Packet *p, uint8_t cat, uint32_t version)
+{
+ uint8_t rep = 0;
+
+ if (PKT_IS_IPV4(p))
+ rep = SRepCIDRGetIPv4IPRep(cidr_ctx, (uint8_t *)GET_IPV4_SRC_ADDR_PTR(p), cat);
+ else if (PKT_IS_IPV6(p))
+ rep = SRepCIDRGetIPv6IPRep(cidr_ctx, (uint8_t *)GET_IPV6_SRC_ADDR(p), cat);
+
+ return rep;
+}
+
+uint8_t SRepCIDRGetIPRepDst(SRepCIDRTree *cidr_ctx, Packet *p, uint8_t cat, uint32_t version)
+{
+ uint8_t rep = 0;
+
+ if (PKT_IS_IPV4(p))
+ rep = SRepCIDRGetIPv4IPRep(cidr_ctx, (uint8_t *)GET_IPV4_DST_ADDR_PTR(p), cat);
+ else if (PKT_IS_IPV6(p))
+ rep = SRepCIDRGetIPv6IPRep(cidr_ctx, (uint8_t *)GET_IPV6_DST_ADDR(p), cat);
+
+ return rep;
+}
+
+/** \brief Increment effective reputation version after
+ * a rule/reputatio reload is complete. */
+void SRepReloadComplete(void)
+{
+ (void) SC_ATOMIC_ADD(srep_eversion, 1);
+ SCLogDebug("effective Reputation version %u", SRepGetEffectiveVersion());
+}
+
+/** \brief Set effective reputation version after
+ * reputation initialization is complete. */
+void SRepInitComplete(void)
+{
+ (void) SC_ATOMIC_SET(srep_eversion, 1);
+ SCLogDebug("effective Reputation version %u", SRepGetEffectiveVersion());
+}
+
+/** \brief Check if a Host is timed out wrt ip rep, meaning a new
+ * version is in place.
+ *
+ * We clean up the old version here.
+ *
+ * \param h host
+ *
+ * \retval 0 not timed out
+ * \retval 1 timed out
+ */
+int SRepHostTimedOut(Host *h)
+{
+ BUG_ON(h == NULL);
+
+ if (h->iprep == NULL)
+ return 1;
+
+ uint32_t eversion = SRepGetEffectiveVersion();
+ SReputation *r = h->iprep;
+ if (r->version < eversion) {
+ SCLogDebug("host %p has reputation version %u, "
+ "effective version is %u", h, r->version, eversion);
+
+ SCFree(h->iprep);
+ h->iprep = NULL;
+
+ HostDecrUsecnt(h);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int SRepCatSplitLine(char *line, uint8_t *cat, char *shortname, size_t shortname_len)
+{
+ size_t line_len = strlen(line);
+ char *ptrs[2] = {NULL,NULL};
+ int i = 0;
+ int idx = 0;
+ char *origline = line;
+
+ while (i < (int)line_len) {
+ if (line[i] == ',' || line[i] == '\n' || line[i] == '\0' || i == (int)(line_len - 1)) {
+ line[i] = '\0';
+
+ ptrs[idx] = line;
+ idx++;
+
+ line += (i+1);
+ i = 0;
+
+ if (line >= origline + line_len)
+ break;
+ if (strlen(line) == 0)
+ break;
+ if (idx == 2)
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (idx != 2) {
+ return -1;
+ }
+
+ SCLogDebug("%s, %s", ptrs[0], ptrs[1]);
+
+ int c = atoi(ptrs[0]);
+ if (c < 0 || c >= SREP_MAX_CATS) {
+ return -1;
+ }
+
+ *cat = (uint8_t)c;
+ strlcpy(shortname, ptrs[1], shortname_len);
+ return 0;
+
+}
+
+/**
+ * \retval 0 valid
+ * \retval 1 header
+ * \retval -1 boo
+ */
+static int SRepSplitLine(SRepCIDRTree *cidr_ctx, char *line, Address *ip, uint8_t *cat, uint8_t *value)
+{
+ size_t line_len = strlen(line);
+ char *ptrs[3] = {NULL,NULL,NULL};
+ int i = 0;
+ int idx = 0;
+ char *origline = line;
+
+ while (i < (int)line_len) {
+ if (line[i] == ',' || line[i] == '\n' || line[i] == '\0' || i == (int)(line_len - 1)) {
+ line[i] = '\0';
+
+ ptrs[idx] = line;
+ idx++;
+
+ line += (i+1);
+ i = 0;
+
+ if (line >= origline + line_len)
+ break;
+ if (strlen(line) == 0)
+ break;
+ if (idx == 3)
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (idx != 3) {
+ return -1;
+ }
+
+ //SCLogInfo("%s, %s, %s", ptrs[0], ptrs[1], ptrs[2]);
+
+ if (strcmp(ptrs[0], "ip") == 0)
+ return 1;
+
+ int c = atoi(ptrs[1]);
+ if (c < 0 || c >= SREP_MAX_CATS) {
+ return -1;
+ }
+
+ int v = atoi(ptrs[2]);
+ if (v < 0 || v > 127) {
+ return -1;
+ }
+
+ if (strchr(ptrs[0], '/') != NULL) {
+ SRepCIDRAddNetblock(cidr_ctx, ptrs[0], c, v);
+ return 1;
+ } else {
+ if (inet_pton(AF_INET, ptrs[0], &ip->address) == 1) {
+ ip->family = AF_INET;
+ } else if (inet_pton(AF_INET6, ptrs[0], &ip->address) == 1) {
+ ip->family = AF_INET6;
+ } else {
+ return -1;
+ }
+
+ *cat = c;
+ *value = v;
+ }
+
+ return 0;
+}
+
+#define SREP_SHORTNAME_LEN 32
+static char srep_cat_table[SREP_MAX_CATS][SREP_SHORTNAME_LEN];
+
+int SRepCatValid(uint8_t cat)
+{
+ if (cat >= SREP_MAX_CATS)
+ return 0;
+
+ if (strlen(srep_cat_table[cat]) == 0)
+ return 0;
+
+ return 1;
+}
+
+uint8_t SRepCatGetByShortname(char *shortname)
+{
+ uint8_t cat;
+ for (cat = 0; cat < SREP_MAX_CATS; cat++) {
+ if (strcmp(srep_cat_table[cat], shortname) == 0)
+ return cat;
+ }
+
+ return 0;
+}
+
+static int SRepLoadCatFile(char *filename)
+{
+ int r = 0;
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_OPENING_RULE_FILE, "opening ip rep file %s: %s", filename, strerror(errno));
+ return -1;
+ }
+
+ r = SRepLoadCatFileFromFD(fp);
+
+ fclose(fp);
+ fp = NULL;
+ return r;
+}
+
+int SRepLoadCatFileFromFD(FILE *fp)
+{
+ char line[8192] = "";
+ Address a;
+ memset(&a, 0x00, sizeof(a));
+ a.family = AF_INET;
+ memset(&srep_cat_table, 0x00, sizeof(srep_cat_table));
+
+ BUG_ON(SRepGetVersion() > 0);
+
+ while(fgets(line, (int)sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ if (len == 0)
+ continue;
+
+ /* ignore comments and empty lines */
+ if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
+ continue;
+
+ while (isspace((unsigned char)line[--len]));
+
+ /* Check if we have a trailing newline, and remove it */
+ len = strlen(line);
+ if (len == 0)
+ continue;
+
+ if (line[len - 1] == '\n' || line[len - 1] == '\r') {
+ line[len - 1] = '\0';
+ }
+
+ uint8_t cat = 0;
+ char shortname[SREP_SHORTNAME_LEN];
+ if (SRepCatSplitLine(line, &cat, shortname, sizeof(shortname)) == 0) {
+ strlcpy(srep_cat_table[cat], shortname, SREP_SHORTNAME_LEN);
+ } else {
+ SCLogError(SC_ERR_NO_REPUTATION, "bad line \"%s\"", line);
+ }
+ }
+
+ SCLogDebug("IP Rep categories:");
+ int i;
+ for (i = 0; i < SREP_MAX_CATS; i++) {
+ if (strlen(srep_cat_table[i]) == 0)
+ continue;
+ SCLogDebug("CAT %d, name %s", i, srep_cat_table[i]);
+ }
+ return 0;
+}
+
+static int SRepLoadFile(SRepCIDRTree *cidr_ctx, char *filename)
+{
+ int r = 0;
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_OPENING_RULE_FILE, "opening ip rep file %s: %s", filename, strerror(errno));
+ return -1;
+ }
+
+ r = SRepLoadFileFromFD(cidr_ctx, fp);
+
+ fclose(fp);
+ fp = NULL;
+ return r;
+
+}
+
+int SRepLoadFileFromFD(SRepCIDRTree *cidr_ctx, FILE *fp)
+{
+ char line[8192] = "";
+ Address a;
+ memset(&a, 0x00, sizeof(a));
+ a.family = AF_INET;
+
+ while(fgets(line, (int)sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ if (len == 0)
+ continue;
+
+ /* ignore comments and empty lines */
+ if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
+ continue;
+
+ while (isspace((unsigned char)line[--len]));
+
+ /* Check if we have a trailing newline, and remove it */
+ len = strlen(line);
+ if (len == 0)
+ continue;
+
+ if (line[len - 1] == '\n' || line[len - 1] == '\r') {
+ line[len - 1] = '\0';
+ }
+
+ uint8_t cat = 0, value = 0;
+ int r = SRepSplitLine(cidr_ctx, line, &a, &cat, &value);
+ if (r < 0) {
+ SCLogError(SC_ERR_NO_REPUTATION, "bad line \"%s\"", line);
+ } else if (r == 0) {
+ if (a.family == AF_INET) {
+ char ipstr[16];
+ PrintInet(AF_INET, (const void *)&a.address, ipstr, sizeof(ipstr));
+ SCLogDebug("%s %u %u", ipstr, cat, value);
+ } else {
+ char ipstr[128];
+ PrintInet(AF_INET6, (const void *)&a.address, ipstr, sizeof(ipstr));
+ SCLogDebug("%s %u %u", ipstr, cat, value);
+ }
+
+ Host *h = HostGetHostFromHash(&a);
+ if (h == NULL) {
+ SCLogError(SC_ERR_NO_REPUTATION, "failed to get a host, increase host.memcap");
+ break;
+ } else {
+ //SCLogInfo("host %p", h);
+
+ if (h->iprep == NULL) {
+ h->iprep = SCMalloc(sizeof(SReputation));
+ if (h->iprep != NULL) {
+ memset(h->iprep, 0x00, sizeof(SReputation));
+
+ HostIncrUsecnt(h);
+ }
+ }
+ if (h->iprep != NULL) {
+ SReputation *rep = h->iprep;
+
+ /* if version is outdated, it's an older entry that we'll
+ * now replace. */
+ if (rep->version != SRepGetVersion()) {
+ memset(rep, 0x00, sizeof(SReputation));
+ }
+
+ rep->version = SRepGetVersion();
+ rep->rep[cat] = value;
+
+ SCLogDebug("host %p iprep %p setting cat %u to value %u",
+ h, h->iprep, cat, value);
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ int i;
+ for (i = 0; i < SREP_MAX_CATS; i++) {
+ if (rep->rep[i] == 0)
+ continue;
+
+ SCLogDebug("--> host %p iprep %p cat %d to value %u",
+ h, h->iprep, i, rep->rep[i]);
+ }
+ }
+#endif
+ }
+
+ HostRelease(h);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Create the path if default-rule-path was specified
+ * \param sig_file The name of the file
+ * \retval str Pointer to the string path + sig_file
+ */
+static char *SRepCompleteFilePath(char *file)
+{
+ char *defaultpath = NULL;
+ char *path = NULL;
+
+ /* Path not specified */
+ if (PathIsRelative(file)) {
+ if (ConfGet("default-reputation-path", &defaultpath) == 1) {
+ SCLogDebug("Default path: %s", defaultpath);
+ size_t path_len = sizeof(char) * (strlen(defaultpath) +
+ strlen(file) + 2);
+ path = SCMalloc(path_len);
+ if (unlikely(path == NULL))
+ return NULL;
+ strlcpy(path, defaultpath, path_len);
+#if defined OS_WIN32 || defined __CYGWIN__
+ if (path[strlen(path) - 1] != '\\')
+ strlcat(path, "\\\\", path_len);
+#else
+ if (path[strlen(path) - 1] != '/')
+ strlcat(path, "/", path_len);
+#endif
+ strlcat(path, file, path_len);
+ } else {
+ path = SCStrdup(file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ } else {
+ path = SCStrdup(file);
+ if (unlikely(path == NULL))
+ return NULL;
+ }
+ return path;
+}
+
+/** \brief init reputation
+ *
+ * \param de_ctx detection engine ctx for tracking iprep version
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ *
+ * If this function is called more than once, the category file
+ * is not reloaded.
+ */
+int SRepInit(DetectEngineCtx *de_ctx)
+{
+ ConfNode *files;
+ ConfNode *file = NULL;
+ int r = 0;
+ char *sfile = NULL;
+ char *filename = NULL;
+ int init = 0;
+ int i = 0;
+
+ de_ctx->srepCIDR_ctx = (SRepCIDRTree *)SCMalloc(sizeof(SRepCIDRTree));
+ if (de_ctx->srepCIDR_ctx == NULL)
+ exit(EXIT_FAILURE);
+ memset(de_ctx->srepCIDR_ctx, 0, sizeof(SRepCIDRTree));
+ SRepCIDRTree *cidr_ctx = de_ctx->srepCIDR_ctx;
+
+ for (i = 0; i < SREP_MAX_CATS; i++) {
+ cidr_ctx->srepIPV4_tree[i] = NULL;
+ cidr_ctx->srepIPV6_tree[i] = NULL;
+ }
+
+ if (SRepGetVersion() == 0) {
+ SC_ATOMIC_INIT(srep_eversion);
+ init = 1;
+ }
+
+ /* if both settings are missing, we assume the user doesn't want ip rep */
+ (void)ConfGet("reputation-categories-file", &filename);
+ files = ConfGetNode("reputation-files");
+ if (filename == NULL && files == NULL) {
+ SCLogInfo("IP reputation disabled");
+ return 0;
+ }
+
+ if (files == NULL) {
+ SCLogError(SC_ERR_NO_REPUTATION, "\"reputation-files\" not set");
+ return -1;
+ }
+
+ if (init) {
+ if (filename == NULL) {
+ SCLogError(SC_ERR_NO_REPUTATION, "\"reputation-categories-file\" not set");
+ return -1;
+ }
+
+ /* init even if we have reputation files, so that when we
+ * have a live reload, we have inited the cats */
+ if (SRepLoadCatFile(filename) < 0) {
+ SCLogError(SC_ERR_NO_REPUTATION, "failed to load reputation "
+ "categories file %s", filename);
+ return -1;
+ }
+ }
+
+ de_ctx->srep_version = SRepIncrVersion();
+ SCLogDebug("Reputation version %u", de_ctx->srep_version);
+
+ /* ok, let's load signature files from the general config */
+ if (files != NULL) {
+ TAILQ_FOREACH(file, &files->head, next) {
+ sfile = SRepCompleteFilePath(file->val);
+ SCLogInfo("Loading reputation file: %s", sfile);
+
+ r = SRepLoadFile(cidr_ctx, sfile);
+ if (r < 0){
+ if (de_ctx->failure_fatal == 1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ SCFree(sfile);
+ }
+ }
+
+ /* Set effective rep version.
+ * On live reload we will handle this after de_ctx has been swapped */
+ if (init) {
+ SRepInitComplete();
+ }
+
+ HostPrintStats();
+ return 0;
+}
+
+void SRepDestroy(DetectEngineCtx *de_ctx) {
+ if (de_ctx->srepCIDR_ctx != NULL) {
+ int i;
+ for (i = 0; i < SREP_MAX_CATS; i++) {
+ if (de_ctx->srepCIDR_ctx->srepIPV4_tree[i] != NULL) {
+ SCRadixReleaseRadixTree(de_ctx->srepCIDR_ctx->srepIPV4_tree[i]);
+ de_ctx->srepCIDR_ctx->srepIPV4_tree[i] = NULL;
+ }
+
+ if (de_ctx->srepCIDR_ctx->srepIPV6_tree[i] != NULL) {
+ SCRadixReleaseRadixTree(de_ctx->srepCIDR_ctx->srepIPV6_tree[i]);
+ de_ctx->srepCIDR_ctx->srepIPV6_tree[i] = NULL;
+ }
+ }
+
+ SCFree(de_ctx->srepCIDR_ctx);
+ de_ctx->srepCIDR_ctx = NULL;
+ }
+}
+
+#ifdef UNITTESTS
+
+#include "conf-yaml-loader.h"
+#include "detect-engine.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+static int SRepTest01(void)
+{
+ char str[] = "1.2.3.4,1,2";
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return 0;
+ }
+
+ SRepInit(de_ctx);
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) != 0) {
+ goto end;
+ }
+
+ char ipstr[16];
+ PrintInet(AF_INET, (const void *)&a.address, ipstr, sizeof(ipstr));
+
+ if (strcmp(ipstr, "1.2.3.4") != 0)
+ goto end;
+
+ if (cat != 1)
+ goto end;
+
+ if (value != 2)
+ goto end;
+
+ result = 1;
+
+end:
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SRepTest02(void)
+{
+ char str[] = "1.1.1.1,";
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return 0;
+ }
+
+ SRepInit(de_ctx);
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) == 0) {
+ goto end;
+ }
+ result = 1;
+
+end:
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SRepTest03(void)
+{
+ char str[] = "1,Shortname,Long Name";
+
+ uint8_t cat = 0;
+ char shortname[SREP_SHORTNAME_LEN];
+
+ if (SRepCatSplitLine(str, &cat, shortname, sizeof(shortname)) != 0) {
+ printf("split failed: ");
+ return 0;
+ }
+
+ if (strcmp(shortname, "Shortname") != 0) {
+ printf("%s != Shortname: ", shortname);
+ return 0;
+ }
+
+ if (cat != 1) {
+ printf("cat 1 != %u: ", cat);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int SRepTest04(void)
+{
+ int result = 0;
+
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ SRepInit(de_ctx);
+
+ char str[] = "10.0.0.0/16,1,2";
+
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) != 1) {
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SRepTest05(void)
+{
+ Packet *p = NULL;
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ if (p == NULL) {
+ return result;
+ }
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("10.0.0.1");
+
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+ SRepInit(de_ctx);
+
+ char str[] = "10.0.0.0/16,1,20";
+
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) != 1) {
+ goto end;
+ }
+ cat = 1;
+ value = SRepCIDRGetIPRepSrc(de_ctx->srepCIDR_ctx, p, cat, 0);
+ if (value != 20) {
+ goto end;
+ }
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SRepTest06(void)
+{
+ Packet *p = NULL;
+ int result = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+
+ p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
+ if (p == NULL) {
+ return result;
+ }
+
+ p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+
+ DetectEngineCtx *de_ctx;
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+ SRepInit(de_ctx);
+
+ char str[] =
+ "0.0.0.0/0,1,10\n"
+ "192.168.0.0/16,2,127";
+
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) != 1) {
+ goto end;
+ }
+ cat = 1;
+ value = SRepCIDRGetIPRepSrc(de_ctx->srepCIDR_ctx, p, cat, 0);
+ if (value != 10) {
+ goto end;
+ }
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static int SRepTest07(void) {
+ char str[] = "2000:0000:0000:0000:0000:0000:0000:0001,";
+ int result = 0;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return 0;
+ }
+
+ SRepInit(de_ctx);
+ Address a;
+ uint8_t cat = 0, value = 0;
+ if (SRepSplitLine(de_ctx->srepCIDR_ctx, str, &a, &cat, &value) == 0) {
+ goto end;
+ }
+ result = 1;
+end:
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+#endif
+
+/** Global trees that hold host reputation for IPV4 and IPV6 hosts */
+IPReputationCtx *rep_ctx;
+
+/**
+ * \brief Initialization fuction for the Reputation Context (IPV4 and IPV6)
+ *
+ * \retval Pointer to the IPReputationCtx created
+ * NULL Error initializing moule;
+ */
+IPReputationCtx *SCReputationInitCtx(void)
+{
+ rep_ctx = (IPReputationCtx *)SCMalloc(sizeof(IPReputationCtx));
+ if (rep_ctx == NULL)
+ return NULL;
+ memset(rep_ctx,0,sizeof(IPReputationCtx));
+
+ rep_ctx->reputationIPV4_tree = SCRadixCreateRadixTree(SCReputationFreeData, NULL);
+ if (rep_ctx->reputationIPV4_tree == NULL) {
+ SCLogDebug("Error initializing Reputation IPV4 module");
+ return NULL;
+ }
+
+ SCLogDebug("Reputation IPV4 module initialized");
+
+ rep_ctx->reputationIPV6_tree = SCRadixCreateRadixTree(SCReputationFreeData, NULL);
+ if (rep_ctx->reputationIPV6_tree == NULL) {
+ SCLogDebug("Error initializing Reputation IPV6 module");
+ return NULL;
+ }
+
+ SCLogDebug("Reputation IPV6 module initialized");
+ if (SCMutexInit(&rep_ctx->reputationIPV4_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX, "Mutex not correctly initialized");
+ exit(EXIT_FAILURE);
+ }
+ if (SCMutexInit(&rep_ctx->reputationIPV6_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX, "Mutex not correctly initialized");
+ exit(EXIT_FAILURE);
+ }
+
+ return rep_ctx;
+}
+
+
+/**
+ * \brief Allocates the Reputation structure for a host/netblock
+ *
+ * \retval rep_data On success, pointer to the rep_data that has to be sent
+ * along with the key, to be added to the Radix tree
+ */
+Reputation *SCReputationAllocData(void)
+{
+ Reputation *rep_data = NULL;
+
+ if ( (rep_data = SCMalloc(sizeof(Reputation))) == NULL)
+ return NULL;
+ memset(rep_data,0, sizeof(Reputation));
+ rep_data->ctime = time(NULL);
+ rep_data->mtime= time(NULL);
+
+ return rep_data;
+}
+
+/**
+ * \brief Used to SCFree the reputation data that is allocated by Reputation API
+ *
+ * \param Pointer to the data that has to be SCFreed
+ */
+void SCReputationFreeData(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+/**
+ * \brief Allocates the Reputation structure for a host/netblock
+ *
+ * \retval ReputationTransaction pointer On success
+ */
+ReputationTransaction *SCReputationTransactionAlloc(void)
+{
+ ReputationTransaction *rtx = NULL;
+
+ if ( (rtx = SCMalloc(sizeof(ReputationTransaction))) == NULL)
+ return NULL;
+ memset(rtx, 0, sizeof(ReputationTransaction));
+
+ return rtx;
+}
+
+/**
+ * \brief Used to SCFree the transaction data
+ *
+ * \param Pointer to the data that has to be SCFreed
+ */
+void SCReputationTransactionFreeData(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+/**
+ * \brief Apply the transaction of changes to the reputation
+ * We use transactions because we cant be locking/unlocking the
+ * trees foreach update. This help for a better performance
+ *
+ * \param rep_data pointer to the reputation to update
+ * \param rtx pointer to the transaction data
+ */
+void SCReputationApplyTransaction(Reputation *rep_data, ReputationTransaction *rtx)
+{
+ int i = 0;
+
+ /* No modification needed */
+ if ( !(rtx->flags & TRANSACTION_FLAG_NEEDSYNC))
+ return;
+
+ /* Here we should apply a formula, a threshold or similar,
+ * maybe values loaded from config */
+ for (; i < REPUTATION_NUMBER; i++) {
+ if (rtx->flags & TRANSACTION_FLAG_INCS) {
+ if (rep_data->reps[i] + rtx->inc[i] < 255)
+ rep_data->reps[i] += rtx->inc[i];
+ else
+ rep_data->reps[i] = 255;
+ }
+ if (rtx->flags & TRANSACTION_FLAG_DECS) {
+ if (rep_data->reps[i] - rtx->dec[i] > 0)
+ rep_data->reps[i] -= rtx->dec[i];
+ else
+ rep_data->reps[i] = 0;
+ }
+ }
+ rep_data->mtime = time(NULL);
+ rep_data->flags |= REPUTATION_FLAG_NEEDSYNC;
+}
+
+/**
+ * \brief Function that compare two reputation structs to determine if they are equal
+ *
+ * \param rep1 pointer to reputation 1
+ * \param rep2 pointer to reputation 2
+ *
+ * \retval 1 if they are equal; 0 if not
+ */
+int SCReputationEqual(Reputation *rep1, Reputation *rep2)
+{
+ return (memcmp(rep1->reps, rep2->reps, REPUTATION_NUMBER * sizeof(uint8_t)) == 0)? 1 : 0;
+}
+
+
+/**
+ * \brief Helper function to print the Reputation structure
+ *
+ * \param Pointer rep_data to a Reputation structure
+ */
+void SCReputationPrint(Reputation *rep_data)
+{
+ if (rep_data == NULL) {
+ printf("No Reputation Data!\n");
+ return;
+ }
+ int i = 0;
+ for (; i < REPUTATION_NUMBER; i++)
+ printf("Rep_type %d = %d\n", i, rep_data->reps[i]);
+
+ if (rep_data->flags & REPUTATION_FLAG_NEEDSYNC)
+ printf("REPUTATION_FLAG_NEEDSYNC = 1\n");
+}
+
+/**
+ * \brief Clone all the data of a reputation
+ * When you try to update the feed, if the data you have belongs
+ * to a netblock, it will be cloned and inserted or a host, with
+ * the modifications that you add
+ *
+ * \param orig Pointer to the original reputation (probably of a netblock)
+ *
+ * \retval Reputation Pointer to the reputation copy
+ */
+Reputation *SCReputationClone(Reputation *orig)
+{
+ Reputation *rep = NULL;
+ if (orig == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return NULL;
+ }
+
+ if ( (rep = SCMalloc(sizeof(Reputation))) == NULL)
+ return NULL;
+ memcpy(rep, orig, sizeof(Reputation));
+ return rep;
+}
+
+void SCReputationFreeCtx(IPReputationCtx *rep_ctx)
+{
+ if (rep_ctx->reputationIPV4_tree != NULL) {
+ SCRadixReleaseRadixTree(rep_ctx->reputationIPV4_tree);
+ rep_ctx->reputationIPV4_tree = NULL;
+ SCMutexDestroy(&rep_ctx->reputationIPV4_lock);
+ }
+ if (rep_ctx->reputationIPV6_tree != NULL) {
+ SCRadixReleaseRadixTree(rep_ctx->reputationIPV6_tree);
+ rep_ctx->reputationIPV6_tree = NULL;
+ SCMutexDestroy(&rep_ctx->reputationIPV6_lock);
+ }
+}
+
+/**
+ * \brief Used to add a new reputation to the reputation module (only at the startup)
+ *
+ * \param ipv4addr pointer to the ipv4 address key
+ * \param netmask_value of the ipv4 address (can be a subnet or a host (32))
+ * \param rep_data Reputation pointer to the Reputation associated to the host/net
+ *
+ * \retval NULL On failure; rep_data on success
+ */
+Reputation *SCReputationAddIPV4Data(uint8_t *ipv4addr, int netmask_value, Reputation *rep_data)
+{
+ struct in_addr *ipv4_addr = (struct in_addr *) ipv4addr;
+
+ if (ipv4_addr == NULL || rep_data == NULL || rep_ctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return NULL;
+ }
+
+ /* If the reputation tree is not initialized yet */
+ if (rep_ctx->reputationIPV4_tree == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Reputation trees not initialized");
+ return NULL;
+ }
+
+ if (netmask_value == 32) {
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+ SCRadixAddKeyIPV4((uint8_t *)ipv4_addr, rep_ctx->reputationIPV4_tree,
+ (void *)rep_data);
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+
+ } else {
+ if (netmask_value < 0 || netmask_value > 31) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Invalid IPV4 Netblock");
+ return NULL;
+ }
+
+ MaskIPNetblock((uint8_t *)ipv4_addr, netmask_value, 32);
+
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+ SCRadixAddKeyIPV4Netblock((uint8_t *)ipv4_addr, rep_ctx->reputationIPV4_tree,
+ (void *)rep_data, netmask_value);
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+ }
+
+ return rep_data;
+}
+
+/**
+ * \brief Retrieves the Reputation of a host (exact match), given an ipv4 address in the raw
+ * address format.
+ *
+ * \param ipv4_addr Pointer to a raw ipv4 address.
+ *
+ * \retval Pointer to a copy of the host Reputation on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV4ExactMatch(uint8_t *ipv4_addr)
+{
+ Reputation *rep_data = NULL;
+
+ /* Be careful with this (locking)*/
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4ExactMatch(ipv4_addr, rep_ctx->reputationIPV4_tree, &user_data);
+ if (user_data == NULL) {
+ rep_data = NULL;
+ } else {
+ /* Yes, we clone it because the pointer can be outdated
+ * while another thread remove this reputation */
+ rep_data = SCReputationClone((Reputation *)user_data);
+ }
+
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+ return rep_data;
+}
+
+/**
+ * \brief Retrieves the Reputation of a host (best match), given an ipv4 address in the raw
+ * address format.
+ *
+ * \param ipv4_addr Pointer to a raw ipv4 address.
+ *
+ * \retval Pointer to a copy of the host Reputation on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV4BestMatch(uint8_t *ipv4_addr)
+{
+ Reputation *rep_data;
+
+ /* Be careful with this (locking)*/
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch(ipv4_addr, rep_ctx->reputationIPV4_tree, &user_data);
+ if (user_data == NULL) {
+ rep_data = NULL;
+ } else {
+ /* Yes, we clone it because the pointer can be outdated
+ * while another thread remove this reputation */
+ rep_data = SCReputationClone((Reputation *)user_data);
+ }
+
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+ return rep_data;
+}
+
+/**
+ * \brief Retrieves the Reputation of a host (best match), given an ipv6 address in the raw
+ * address format.
+ *
+ * \param Pointer to a raw ipv6 address.
+ *
+ * \retval Pointer to a copy of the host Reputation on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV6BestMatch(uint8_t *ipv6_addr)
+{
+ Reputation *rep_data;
+
+ /* Be careful with this (locking)*/
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6BestMatch(ipv6_addr, rep_ctx->reputationIPV6_tree, &user_data);
+ if (user_data == NULL) {
+ rep_data = NULL;
+ } else {
+ /* Yes, we clone it because the pointer can be outdated
+ * while another thread remove this reputation */
+ rep_data = SCReputationClone((Reputation *)user_data);
+ }
+
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+ return rep_data;
+}
+
+/**
+ * \brief Retrieves the Reputation of a host (exact match), given an ipv6 address in the raw
+ * address format.
+ *
+ * \param Pointer to a raw ipv6 address.
+ *
+ * \retval Pointer to a copy of the host reputation on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV6ExactMatch(uint8_t *ipv6_addr)
+{
+ Reputation *rep_data;
+
+ /* Be careful with this (locking)*/
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6ExactMatch(ipv6_addr, rep_ctx->reputationIPV6_tree, &user_data);
+ if (user_data == NULL) {
+ rep_data = NULL;
+ } else {
+ /* Yes, we clone it because the pointer can be outdated
+ * while another thread remove this reputation */
+ rep_data = SCReputationClone((Reputation *)user_data);
+ }
+
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+ return rep_data;
+}
+
+
+/**
+ * \brief Retrieves the Real Reputation of a host (exact match), given an ipv4 address in the raw
+ * address format. (Not thread safe!)
+ *
+ * \param ipv4_addr Pointer to a raw ipv4 address.
+ *
+ * \retval Pointer to the Reputation of the host on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV4ExactMatchReal(uint8_t *ipv4_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4ExactMatch(ipv4_addr, rep_ctx->reputationIPV4_tree, &user_data);
+ if (user_data == NULL) {
+ return NULL;
+ } else {
+ return (Reputation *)user_data;
+ }
+}
+
+/**
+ * \brief Retrieves the Real Reputation of a host (best match), given an ipv4 address in the raw
+ * address format. (Not thread safe!)
+ *
+ * \param ipv4_addr Pointer to a raw ipv4 address.
+ *
+ * \retval Pointer to the Reputation of the host on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV4BestMatchReal(uint8_t *ipv4_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch(ipv4_addr, rep_ctx->reputationIPV4_tree, &user_data);
+ if (user_data == NULL) {
+ return NULL;
+ } else {
+ return (Reputation *)user_data;
+ }
+}
+
+/**
+ * \brief Retrieves the Real Reputation of a host (best match), given an ipv6 address in the raw
+ * address format. (Not thread safe!)
+ *
+ * \param Pointer to a raw ipv6 address.
+ *
+ * \retval Pointer to the Reputation of the host on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV6BestMatchReal(uint8_t *ipv6_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6BestMatch(ipv6_addr, rep_ctx->reputationIPV6_tree, &user_data);
+ if (user_data == NULL) {
+ return NULL;
+ } else {
+ return (Reputation *)user_data;
+ }
+}
+
+/**
+ * \brief Retrieves the Real Reputation of a host (exact match), given an ipv6 address in the raw
+ * address format. (Not thread safe!)
+ *
+ * \param Pointer to a raw ipv6 address.
+ *
+ * \retval Pointer to the Reputation of the host on success;
+ * NULL on failure, or on not finding the key;
+ */
+Reputation *SCReputationLookupIPV6ExactMatchReal(uint8_t *ipv6_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6ExactMatch(ipv6_addr, rep_ctx->reputationIPV6_tree, &user_data);
+ if (user_data == NULL) {
+ return NULL;
+ } else {
+ return (Reputation *)user_data;
+ }
+}
+
+/**
+ * \brief Remove the node of the reputation tree associated to the ipv4 address
+ *
+ * \param ipv4_addr Pointer to a raw ipv4 address
+ * \param netmask_value netmask to apply to the address (32 for host)
+ *
+ */
+void SCReputationRemoveIPV4Data(uint8_t * ipv4_addr, uint8_t netmask_value)
+{
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+ SCRadixRemoveKeyIPV4Netblock(ipv4_addr, rep_ctx->reputationIPV4_tree, netmask_value);
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+}
+
+/**
+ * \brief Remove the node of the reputation tree associated to the ipv6 address
+ *
+ * \param ipv6_addr Pointer to a raw ipv6 address
+ * \param netmask_value netmask to apply to the address (128 for host)
+ *
+ */
+void SCReputationRemoveIPV6Data(uint8_t * ipv6_addr, uint8_t netmask_value)
+{
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+ SCRadixRemoveKeyIPV6Netblock(ipv6_addr, rep_ctx->reputationIPV6_tree, netmask_value);
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+}
+
+/**
+ * \brief Used to add a new reputation to the reputation module (only at the startup)
+ *
+ * \param ipv6addr pointer to the ipv6 address key
+ * \param netmask_value of the ipv6 address (can be a subnet)
+ * \param rep_data Reputation pointer to the Reputation associated to the host/net
+ *
+ * \retval NULL On failure
+ */
+Reputation *SCReputationAddIPV6Data(uint8_t *ipv6addr, int netmask_value, Reputation *rep_data)
+{
+ struct in_addr *ipv6_addr = (struct in_addr *) ipv6addr;
+
+ if (ipv6_addr == NULL || rep_data == NULL || rep_ctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return NULL;
+ }
+
+ /* If the reputation tree is not initialized yet */
+ if (rep_ctx->reputationIPV6_tree == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Reputation trees not initialized");
+ return NULL;
+ }
+
+ if (netmask_value == 128) {
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+ SCRadixAddKeyIPV6((uint8_t *)ipv6_addr, rep_ctx->reputationIPV6_tree,
+ (void *)rep_data);
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+
+ } else {
+ if (netmask_value < 0 || netmask_value > 127) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Invalid IPV6 Netblock");
+ return NULL;
+ }
+
+ MaskIPNetblock((uint8_t *)ipv6_addr, netmask_value, 128);
+
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+ SCRadixAddKeyIPV6Netblock((uint8_t *)ipv6_addr, rep_ctx->reputationIPV6_tree,
+ (void *)rep_data, netmask_value);
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+ }
+
+ return rep_data;
+}
+
+/**
+ * \brief Update a reputation or insert a new one. If it doesn't exist
+ * it will try to search for the reputation of parent subnets to
+ * create the new reputation data based on this one
+ *
+ * \param ipv6addr pointer to the ipv6 address key
+ * \param rep_data Reputation pointer to the Reputation associated to the host/net
+ *
+ * \retval NULL On failure
+ */
+Reputation *SCReputationUpdateIPV4Data(uint8_t *ipv4addr, ReputationTransaction *rtx)
+{
+ struct in_addr *ipv4_addr = (struct in_addr *) ipv4addr;
+ Reputation *actual_rep;
+
+ if (ipv4_addr == NULL || rtx == NULL || rep_ctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return NULL;
+ }
+
+ /* If the reputation tree is not initialized yet */
+ if (rep_ctx->reputationIPV4_tree == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Reputation trees not initialized");
+ return NULL;
+ }
+
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV4_lock);
+
+ /* Search exact match and update */
+ actual_rep = SCReputationLookupIPV4ExactMatchReal(ipv4addr);
+ if (actual_rep == NULL) {
+ /* else search best match (parent subnets) */
+ actual_rep =SCReputationLookupIPV4BestMatchReal(ipv4addr);
+
+ if (actual_rep != NULL) {
+ /* clone from parent and insert host */
+ actual_rep = SCReputationClone(actual_rep);
+ } else {
+ /* else insert a new reputation data for the host */
+ actual_rep = SCReputationAllocData();
+ /* If new, we only increment values */
+ rtx->flags = TRANSACTION_FLAG_INCS;
+ rtx->flags |= TRANSACTION_FLAG_NEEDSYNC;
+ }
+
+ /* insert the reputation data in the tree */
+ SCRadixAddKeyIPV4((uint8_t *)ipv4_addr, rep_ctx->reputationIPV4_tree,
+ (void *)actual_rep);
+ }
+ /* Apply updates */
+ SCReputationApplyTransaction(actual_rep, rtx);
+
+ /* Unlock! */
+ SCMutexUnlock(&rep_ctx->reputationIPV4_lock);
+
+ return actual_rep;
+}
+
+/**
+ * \brief Update a reputation or insert a new one. If it doesn't exist
+ * it will try to search for the reputation of parent subnets to
+ * create the new reputation data based on this one
+ *
+ * \param ipv6addr pointer to the ipv6 address key
+ * \param rep_data Reputation pointer to the Reputation associated to the host/net
+ *
+ * \retval NULL On failure
+ */
+Reputation *SCReputationUpdateIPV6Data(uint8_t *ipv6addr, ReputationTransaction *rtx)
+{
+ struct in_addr *ipv6_addr = (struct in_addr *) ipv6addr;
+ Reputation *actual_rep;
+
+ if (ipv6_addr == NULL || rtx == NULL || rep_ctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return NULL;
+ }
+
+ /* If the reputation tree is not initialized yet */
+ if (rep_ctx->reputationIPV6_tree == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Reputation trees not initialized");
+ return NULL;
+ }
+
+ /* Be careful with the mutex */
+ SCMutexLock(&rep_ctx->reputationIPV6_lock);
+
+ /* Search exact match and update */
+ actual_rep = SCReputationLookupIPV6ExactMatchReal(ipv6addr);
+ if (actual_rep == NULL) {
+ /* else search best match (parent subnets) */
+ actual_rep =SCReputationLookupIPV6BestMatchReal(ipv6addr);
+
+ if (actual_rep != NULL) {
+ /* clone from parent and insert host */
+ actual_rep = SCReputationClone(actual_rep);
+ } else {
+ /* else insert a new reputation data for the host */
+ actual_rep = SCReputationAllocData();
+ /* If new, we only increment values */
+ rtx->flags = TRANSACTION_FLAG_INCS;
+ rtx->flags |= TRANSACTION_FLAG_NEEDSYNC;
+ }
+
+ /* insert the reputation data in the tree */
+ SCRadixAddKeyIPV6((uint8_t *)ipv6_addr, rep_ctx->reputationIPV6_tree,
+ (void *)actual_rep);
+ }
+ /* Apply updates */
+ SCReputationApplyTransaction(actual_rep, rtx);
+
+ /* Unlock! */
+ SCMutexUnlock(&rep_ctx->reputationIPV6_lock);
+
+ return actual_rep;
+}
+
+
+/* ----------------- UNITTESTS-------------------- */
+#ifdef UNITTESTS
+
+/**
+ * \test Adding (from numeric ipv4) and removing host reputation in the Reputation context
+ * tree. THe reputation data is the real one, no copies here.
+ */
+int SCReputationTestIPV4AddRemoveHost01(void)
+{
+ int i = 0;
+ struct in_addr in;
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.6", &in) < 0)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 6;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 32, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ Reputation *rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_orig)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.7", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 32, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_orig)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.7", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 31, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data == NULL || rep_data == rep_orig)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.8", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data != NULL)
+ goto error;
+
+
+ /* Removing */
+ if (inet_pton(AF_INET, "192.168.1.7", &in) < 0)
+ goto error;
+
+ SCReputationRemoveIPV4Data((uint8_t *) &in, 32);
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.6", &in) < 0)
+ goto error;
+
+ SCReputationRemoveIPV4Data((uint8_t *) &in, 32);
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV4_tree);
+
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Adding (from numeric ipv6) and removing host reputation in the Reputation context
+ * tree. THe reputation data is the real one, no copies here.
+ */
+int SCReputationTestIPV6AddRemoveHost01(void)
+{
+ uint8_t in[16];
+ uint8_t i = 0;
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2362", &in) < 0)
+ goto error;
+
+ Reputation *rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 1;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 128, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ Reputation *rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_orig)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2363", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 8;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 128, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+
+ if (rep_data == NULL || rep_data != rep_orig)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2363", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 127, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data == NULL || rep_data == rep_orig)
+ goto error;
+
+
+ /* Removing */
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2363", &in) < 0)
+ goto error;
+
+ SCReputationRemoveIPV6Data((uint8_t *) &in, 128);
+
+ rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2362", &in) < 0)
+ goto error;
+
+ SCReputationRemoveIPV6Data((uint8_t *) &in, 128);
+ rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV6_tree);
+
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Adding (from numeric ipv4) and retrieving reputations
+ * tree. The reputation data retireved are copies of the original.
+ */
+int SCReputationTestIPV4AddRemoveHost02(void)
+{
+ int i = 0;
+ struct in_addr in;
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.6", &in) < 0)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 6;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 32, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ Reputation *rep_data = SCReputationLookupIPV4ExactMatch((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 0)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.7", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 32, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatch((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 0)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.7", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 9;
+
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 31, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatch((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 1)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV4_tree);
+
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Adding (from numeric ipv6) and removing host reputation in the Reputation context
+ * tree. The reputation data retireved are copies of the original.
+ */
+int SCReputationTestIPV6AddRemoveHost02(void)
+{
+ int i = 0;
+ uint8_t in[16];
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2362", &in) < 0)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 6;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 128, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ Reputation *rep_data = SCReputationLookupIPV6ExactMatch((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 0)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2363", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 128, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV6ExactMatch((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 0)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2363", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 9;
+
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 127, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV6ExactMatch((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_orig) == 1)
+ goto error;
+
+ if (inet_pton(AF_INET6, "aaaa:bbbb:cccc:dddd:1223:1722:3425:2364", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV6ExactMatch((uint8_t *) &in);
+ /* It should return the host info and not the subnet info */
+ if (rep_data != NULL)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV6_tree);
+
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Test searches (best and exact matches)
+ */
+int SCReputationTestIPV4BestExactMatch01(void)
+{
+ int i = 0;
+ struct in_addr in;
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_origC = NULL;
+ Reputation *rep_origB = NULL;
+ Reputation *rep_origA = NULL;
+
+ Reputation *rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.6", &in) < 0)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 6;
+
+ /* adding a host */
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 32, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+ Reputation *rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_orig)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ if (rep_orig == NULL)
+ goto error;
+
+ /* Adding C subnet */
+ if (inet_pton(AF_INET, "192.168.1.0", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_origC = SCReputationAddIPV4Data((uint8_t *) &in, 24, rep_orig);
+ if (rep_origC == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origC)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ /* Adding B subnet */
+ if (inet_pton(AF_INET, "192.168.0.0", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_origB = SCReputationAddIPV4Data((uint8_t *) &in, 16, rep_orig);
+ if (rep_origB == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origC)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.2.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origB)
+ goto error;
+
+ rep_orig = SCReputationAllocData();
+ /* Adding A subnet */
+ if (inet_pton(AF_INET, "192.0.0.0", &in) < 0)
+ goto error;
+
+ for (i = 0; i < REPUTATION_NUMBER; i++)
+ rep_orig->reps[i] = i * 10 + 7;
+
+ rep_origA = SCReputationAddIPV4Data((uint8_t *) &in, 8, rep_orig);
+ if (rep_origA == NULL)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.1.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origC)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.168.2.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origB)
+ goto error;
+
+ if (inet_pton(AF_INET, "192.167.2.5", &in) < 0)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data != NULL)
+ goto error;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || rep_data != rep_origA)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV4_tree);
+
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Update transactions
+ */
+int SCReputationTestIPV4Update01(void)
+{
+ int i = 0;
+ struct in_addr in;
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_orig = SCReputationAllocData();
+
+ ReputationTransaction rtx;
+ memset(&rtx, 0, sizeof(ReputationTransaction));
+ if (rep_orig == NULL)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++) {
+ rep_orig->reps[i] = 10;
+ }
+
+ if (inet_pton(AF_INET, "192.168.0.0", &in) < 0)
+ goto error;
+
+ /* Add add it as net */
+ rep_orig = SCReputationAddIPV4Data((uint8_t *) &in, 16, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rtx.dec[REPUTATION_DDOS] = 5;
+ rtx.inc[REPUTATION_PHISH] = 50;
+ rtx.inc[REPUTATION_MALWARE] = 30;
+ rtx.flags |= TRANSACTION_FLAG_NEEDSYNC;
+ rtx.flags |= TRANSACTION_FLAG_INCS;
+ rtx.flags |= TRANSACTION_FLAG_DECS;
+
+ if (inet_pton(AF_INET, "192.168.10.100", &in) < 0)
+ goto error;
+
+ /* Update (it will create the host entry with the data of the net) */
+ SCReputationUpdateIPV4Data((uint8_t *)&in, &rtx);
+
+ /* Create the reputation that any host 192.168.* should have */
+ Reputation *rep_aux = SCReputationAllocData();
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++) {
+ rep_aux->reps[i] = 10;
+ }
+
+ rep_aux->reps[REPUTATION_DDOS] = 5;
+ rep_aux->reps[REPUTATION_PHISH] = 60;
+ rep_aux->reps[REPUTATION_MALWARE] = 40;
+
+ Reputation *rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ /* Now that is created, it should update only the host */
+ rtx.dec[REPUTATION_DDOS] = 50;
+ rtx.inc[REPUTATION_PHISH] = 50;
+ rtx.inc[REPUTATION_MALWARE] = 50;
+
+ rep_aux->reps[REPUTATION_DDOS] = 0;
+ rep_aux->reps[REPUTATION_PHISH] = 110;
+ rep_aux->reps[REPUTATION_MALWARE] = 90;
+
+ SCReputationUpdateIPV4Data((uint8_t *)&in, &rtx);
+
+ rep_data = SCReputationLookupIPV4ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ /* So let's see if we add a host and get the parent data again */
+ if (inet_pton(AF_INET, "192.168.10.101", &in) < 0)
+ goto error;
+
+ rep_aux->reps[REPUTATION_DDOS] = 10;
+ rep_aux->reps[REPUTATION_PHISH] = 10;
+ rep_aux->reps[REPUTATION_MALWARE] = 10;
+
+ rep_data = SCReputationLookupIPV4BestMatchReal((uint8_t *) &in);
+
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV4_tree);
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+/**
+ * \test Update transactions
+ */
+int SCReputationTestIPV6Update01(void)
+{
+ int i = 0;
+ uint8_t in[16];
+
+ SCReputationInitCtx();
+ if (rep_ctx == NULL) {
+ SCLogInfo("Error initializing Reputation Module");
+ return 0;
+ }
+
+ Reputation *rep_orig = SCReputationAllocData();
+
+ ReputationTransaction rtx;
+ memset(&rtx, 0, sizeof(ReputationTransaction));
+ if (rep_orig == NULL)
+ goto error;
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++) {
+ rep_orig->reps[i] = 10;
+ }
+
+ if (inet_pton(AF_INET6, "8762:2352:6261:7265:EE23:21AD:2121:1DDD", &in) < 0)
+ goto error;
+
+ /* Add add it as net */
+ rep_orig = SCReputationAddIPV6Data((uint8_t *) &in, 98, rep_orig);
+ if (rep_orig == NULL)
+ goto error;
+
+ rtx.dec[REPUTATION_DDOS] = 5;
+ rtx.inc[REPUTATION_PHISH] = 50;
+ rtx.inc[REPUTATION_MALWARE] = 30;
+ rtx.flags |= TRANSACTION_FLAG_NEEDSYNC;
+ rtx.flags |= TRANSACTION_FLAG_INCS;
+ rtx.flags |= TRANSACTION_FLAG_DECS;
+
+ if (inet_pton(AF_INET6, "8762:2352:6261:7265:EE23:21AD:2121:1ABA", &in) < 0)
+ goto error;
+
+ /* Update (it will create the host entry with the data of the net) */
+ SCReputationUpdateIPV6Data((uint8_t *)&in, &rtx);
+
+ /* Create the reputation that any host 192.168.* should have */
+ Reputation *rep_aux = SCReputationAllocData();
+
+ /* Fill the reputation with some values.. */
+ for (i = 0; i < REPUTATION_NUMBER; i++) {
+ rep_aux->reps[i] = 10;
+ }
+
+ rep_aux->reps[REPUTATION_DDOS] = 5;
+ rep_aux->reps[REPUTATION_PHISH] = 60;
+ rep_aux->reps[REPUTATION_MALWARE] = 40;
+
+ Reputation *rep_data = SCReputationLookupIPV6BestMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ /* Now that is created, it should update only the host */
+ rtx.dec[REPUTATION_DDOS] = 50;
+ rtx.inc[REPUTATION_PHISH] = 50;
+ rtx.inc[REPUTATION_MALWARE] = 50;
+
+ rep_aux->reps[REPUTATION_DDOS] = 0;
+ rep_aux->reps[REPUTATION_PHISH] = 110;
+ rep_aux->reps[REPUTATION_MALWARE] = 90;
+
+ SCReputationUpdateIPV6Data((uint8_t *)&in, &rtx);
+
+ rep_data = SCReputationLookupIPV6ExactMatchReal((uint8_t *) &in);
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ /* So let's see if we add a host and get the parent data again */
+ if (inet_pton(AF_INET6, "8762:2352:6261:7265:EE23:21AD:2121:1ACB", &in) < 0)
+ goto error;
+
+ rep_aux->reps[REPUTATION_DDOS] = 10;
+ rep_aux->reps[REPUTATION_PHISH] = 10;
+ rep_aux->reps[REPUTATION_MALWARE] = 10;
+
+ rep_data = SCReputationLookupIPV6BestMatchReal((uint8_t *) &in);
+
+
+ if (rep_data == NULL || SCReputationEqual(rep_data, rep_aux) != 1)
+ goto error;
+
+ SCRadixPrintTree(rep_ctx->reputationIPV6_tree);
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 1;
+
+error:
+ SCReputationFreeCtx(rep_ctx);
+ rep_ctx = NULL;
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+/** Register the following unittests for the Reputation module */
+void SCReputationRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SCReputationTestIPV4AddRemoveHost01",
+ SCReputationTestIPV4AddRemoveHost01, 1);
+ UtRegisterTest("SCReputationTestIPV6AddRemoveHost01",
+ SCReputationTestIPV6AddRemoveHost01, 1);
+
+ UtRegisterTest("SCReputationTestIPV4BestExactMatch01",
+ SCReputationTestIPV4BestExactMatch01, 1);
+
+ UtRegisterTest("SCReputationTestIPV4AddRemoveHost02",
+ SCReputationTestIPV4AddRemoveHost02, 1);
+ UtRegisterTest("SCReputationTestIPV6AddRemoveHost02",
+ SCReputationTestIPV6AddRemoveHost02, 1);
+
+ UtRegisterTest("SCReputationTestIPV4Update01",
+ SCReputationTestIPV4Update01, 1);
+ UtRegisterTest("SCReputationTestIPV6Update01",
+ SCReputationTestIPV6Update01, 1);
+
+ UtRegisterTest("SRepTest01", SRepTest01, 1);
+ UtRegisterTest("SRepTest02", SRepTest02, 1);
+ UtRegisterTest("SRepTest03", SRepTest03, 1);
+ UtRegisterTest("SRepTest04", SRepTest04, 1);
+ UtRegisterTest("SRepTest05", SRepTest05, 1);
+ UtRegisterTest("SRepTest06", SRepTest06, 1);
+ UtRegisterTest("SRepTest07", SRepTest07, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/reputation.h b/framework/src/suricata/src/reputation.h
new file mode 100644
index 00000000..347731fe
--- /dev/null
+++ b/framework/src/suricata/src/reputation.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ * Original Idea by Matt Jonkman
+ */
+
+#ifndef __REPUTATION_H__
+#define __REPUTATION_H__
+
+#include "host.h"
+
+#define SREP_MAX_CATS 60
+
+typedef struct SRepCIDRTree_ {
+ SCRadixTree *srepIPV4_tree[SREP_MAX_CATS];
+ SCRadixTree *srepIPV6_tree[SREP_MAX_CATS];
+} SRepCIDRTree;
+
+typedef struct SReputation_ {
+ uint32_t version;
+ uint8_t rep[SREP_MAX_CATS];
+} SReputation;
+
+uint8_t SRepCatGetByShortname(char *shortname);
+int SRepInit(struct DetectEngineCtx_ *de_ctx);
+void SRepDestroy(struct DetectEngineCtx_ *de_ctx);
+void SRepReloadComplete(void);
+int SRepHostTimedOut(Host *);
+
+/** Reputation numbers (types) that we can use to lookup/update, etc
+ * Please, dont convert this to a enum since we want the same reputation
+ * codes always. */
+#define REPUTATION_SPAM 0 /**< spammer */
+#define REPUTATION_CNC 1 /**< CnC server */
+#define REPUTATION_SCAN 2 /**< scanner */
+#define REPUTATION_HOSTILE 3 /**< hijacked nets, RBN nets, etc */
+#define REPUTATION_DYNAMIC 4 /**< Known dial up, residential, user networks */
+#define REPUTATION_PUBLICACCESS 5 /**< known internet cafe's open access points */
+#define REPUTATION_PROXY 6 /**< known tor out nodes, proxy servers, etc */
+#define REPUTATION_P2P 7 /**< Heavy p2p node, torrent server, other sharing services */
+#define REPUTATION_UTILITY 8 /**< known good places like google, yahoo, msn.com, etc */
+#define REPUTATION_DDOS 9 /**< Known ddos participant */
+#define REPUTATION_PHISH 10 /**< Known Phishing site */
+#define REPUTATION_MALWARE 11 /**< Known Malware distribution site. Hacked web server, etc */
+#define REPUTATION_ZOMBIE 12 /**< Known Zombie (botnet member) They typically are Scanner or Hostile,
+ but if collaboration with botnet snooping, like we did back in
+ 2005 or so, can proactively identify online zombies that joined a
+ botnet, you may want to break those out separately */
+#define REPUTATION_NUMBER 13 /**< number of rep types we have for data structure size (be careful with this) */
+
+
+/* Flags for reputation */
+#define REPUTATION_FLAG_NEEDSYNC 0x01 /**< rep was changed by engine, needs sync with external hub */
+
+/** Reputation Context for IPV4 IPV6 */
+typedef struct IPReputationCtx_ {
+ /** Radix trees that holds the host reputation information */
+ SCRadixTree *reputationIPV4_tree;
+ SCRadixTree *reputationIPV6_tree;
+
+ /** Mutex to support concurrent access */
+ SCMutex reputationIPV4_lock;
+ SCMutex reputationIPV6_lock;
+}IPReputationCtx;
+
+uint8_t SRepCIDRGetIPRepSrc(SRepCIDRTree *cidr_ctx, Packet *p, uint8_t cat, uint32_t version);
+uint8_t SRepCIDRGetIPRepDst(SRepCIDRTree *cidr_ctx, Packet *p, uint8_t cat, uint32_t version);
+void SRepResetVersion();
+int SRepLoadCatFileFromFD(FILE *fp);
+int SRepLoadFileFromFD(SRepCIDRTree *cidr_ctx, FILE *fp);
+
+/** Reputation Data */
+//TODO: Add a timestamp here to know the last update of this reputation.
+typedef struct Reputation_ {
+ uint8_t reps[REPUTATION_NUMBER]; /**< array of 8 bit reputations */
+ uint8_t flags; /**< reputation flags */
+ time_t ctime; /**< creation time (epoch) */
+ time_t mtime; /**< modification time (epoch) */
+} Reputation;
+
+/* flags for transactions */
+#define TRANSACTION_FLAG_NEEDSYNC 0x01 /**< We will apply the transaction only if necesary */
+#define TRANSACTION_FLAG_INCS 0x02 /**< We will increment only if necesary */
+#define TRANSACTION_FLAG_DECS 0x04 /**< We will decrement only if necesary */
+
+/* transaction for feedback */
+typedef struct ReputationTransaction_ {
+ uint16_t inc[REPUTATION_NUMBER];
+ uint16_t dec[REPUTATION_NUMBER];
+ uint8_t flags;
+} ReputationTransaction;
+
+/* API */
+Reputation *SCReputationAllocData();
+Reputation *SCReputationClone(Reputation *);
+void SCReputationFreeData(void *);
+
+IPReputationCtx *SCReputationInitCtx(void);
+void SCReputationFreeCtx(IPReputationCtx *);
+
+void SCReputationPrint(Reputation *);
+void SCReputationRegisterTests(void);
+
+#endif /* __REPUTATION_H__ */
diff --git a/framework/src/suricata/src/respond-reject-libnet11.c b/framework/src/suricata/src/respond-reject-libnet11.c
new file mode 100644
index 00000000..b27b74e9
--- /dev/null
+++ b/framework/src/suricata/src/respond-reject-libnet11.c
@@ -0,0 +1,541 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author William Metcalf <william.metcalf@gmail.com>
+ *
+ * RespondRejectLibnet11 used to send out libnet based
+ * TCP resets and ICMP unreachables.
+ *
+ * \todo calculate TTL base on average from stream tracking
+ * \todo come up with a way for users to specify icmp unreachable type
+ * \todo Possibly default to port unreachable for UDP traffic this seems
+ * to be the default in flexresp and iptables
+ * \todo implement ipv6 resets
+ * \todo implement pre-alloc resets for speed
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+#include "decode-ipv4.h"
+#include "decode-tcp.h"
+#include "decode-sctp.h"
+#include "decode-udp.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "action-globals.h"
+#include "respond-reject.h"
+#include "respond-reject-libnet11.h"
+#include "util-device.h"
+
+#ifdef HAVE_LIBNET11
+
+/** set to true in main if we're setting caps. We need it here if we're using
+ * reject rules as libnet 1.1 is not compatible with caps. */
+extern int sc_set_caps;
+
+#include <libnet.h>
+
+extern uint8_t host_mode;
+
+typedef struct Libnet11Packet_ {
+ uint32_t ack, seq;
+ uint16_t window, dsize;
+ uint8_t ttl;
+ uint16_t id;
+ uint32_t flow;
+ uint8_t class;
+ struct libnet_in6_addr src6, dst6;
+ uint32_t src4, dst4;
+ uint16_t sp, dp;
+ size_t len;
+} Libnet11Packet;
+
+int RejectSendLibnet11L3IPv4TCP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+
+ Libnet11Packet lpacket;
+ libnet_t *c; /* libnet context */
+ char ebuf[LIBNET_ERRBUF_SIZE];
+ int result;
+ char *devname = NULL;
+
+ /* fill in struct defaults */
+ lpacket.ttl = 0;
+ lpacket.id = 0;
+ lpacket.flow = 0;
+ lpacket.class = 0;
+
+ if (IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode) && (p->livedev)) {
+ devname = p->livedev->dev;
+ SCLogDebug("Will emit reject packet on dev %s", devname);
+ }
+ if ((c = libnet_init(LIBNET_RAW4, devname, ebuf)) == NULL) {
+ SCLogError(SC_ERR_LIBNET_INIT,"libnet_init failed: %s", ebuf);
+ return 1;
+ }
+
+ if (p->tcph == NULL)
+ return 1;
+
+ /* save payload len */
+ lpacket.dsize = p->payload_len;
+
+ switch (dir) {
+ case REJECT_DIR_SRC:
+ SCLogDebug("sending a tcp reset to src");
+ /* We follow http://tools.ietf.org/html/rfc793#section-3.4 :
+ * If packet has no ACK, the seq number is 0 and the ACK is built
+ * the normal way. If packet has a ACK, the seq of the RST packet
+ * is equal to the ACK of incoming packet and the ACK is build
+ * using packet sequence number and size of the data. */
+ if (TCP_GET_ACK(p) == 0) {
+ lpacket.seq = 0;
+ lpacket.ack = TCP_GET_SEQ(p) + lpacket.dsize + 1;
+ } else {
+ lpacket.seq = TCP_GET_ACK(p);
+ lpacket.ack = TCP_GET_SEQ(p) + lpacket.dsize;
+ }
+
+ lpacket.sp = TCP_GET_DST_PORT(p);
+ lpacket.dp = TCP_GET_SRC_PORT(p);
+
+ lpacket.src4 = GET_IPV4_DST_ADDR_U32(p);
+ lpacket.dst4 = GET_IPV4_SRC_ADDR_U32(p);
+ break;
+ case REJECT_DIR_DST:
+ SCLogDebug("sending a tcp reset to dst");
+ lpacket.seq = TCP_GET_SEQ(p);
+ lpacket.ack = TCP_GET_ACK(p);
+
+ lpacket.sp = TCP_GET_SRC_PORT(p);
+ lpacket.dp = TCP_GET_DST_PORT(p);
+
+ lpacket.src4 = GET_IPV4_SRC_ADDR_U32(p);
+ lpacket.dst4 = GET_IPV4_DST_ADDR_U32(p);
+ break;
+ default:
+ SCLogError(SC_ERR_LIBNET_INVALID_DIR,
+ "reset not src or dst returning");
+ return 1;
+ }
+
+ lpacket.window = TCP_GET_WINDOW(p);
+ //lpacket.seq += lpacket.dsize;
+
+ /* TODO come up with ttl calc function */
+ lpacket.ttl = 64;
+
+ /* build the package */
+ if ((libnet_build_tcp(
+ lpacket.sp, /* source port */
+ lpacket.dp, /* dst port */
+ lpacket.seq, /* seq number */
+ lpacket.ack, /* ack number */
+ TH_RST|TH_ACK, /* flags */
+ lpacket.window, /* window size */
+ 0, /* checksum */
+ 0, /* urgent flag */
+ LIBNET_TCP_H, /* header length */
+ NULL, /* payload */
+ 0, /* payload length */
+ c, /* libnet context */
+ 0)) < 0) /* libnet ptag */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_tcp %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ if ((libnet_build_ipv4(
+ LIBNET_TCP_H + LIBNET_IPV4_H, /* entire packet length */
+ 0, /* tos */
+ lpacket.id, /* ID */
+ 0, /* fragmentation flags and offset */
+ lpacket.ttl, /* TTL */
+ IPPROTO_TCP, /* protocol */
+ 0, /* checksum */
+ lpacket.src4, /* source address */
+ lpacket.dst4, /* destination address */
+ NULL, /* pointer to packet data (or NULL) */
+ 0, /* payload length */
+ c, /* libnet context pointer */
+ 0)) < 0) /* packet id */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_ipv4 %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ result = libnet_write(c);
+ if (result == -1) {
+ SCLogError(SC_ERR_LIBNET_WRITE_FAILED,"libnet_write failed: %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+cleanup:
+ libnet_destroy (c);
+ return 0;
+}
+
+int RejectSendLibnet11L3IPv4ICMP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ Libnet11Packet lpacket;
+ libnet_t *c; /* libnet context */
+ char ebuf[LIBNET_ERRBUF_SIZE];
+ int result;
+ char *devname = NULL;
+
+ /* fill in struct defaults */
+ lpacket.ttl = 0;
+ lpacket.id = 0;
+ lpacket.flow = 0;
+ lpacket.class = 0;
+
+ lpacket.len = (IPV4_GET_HLEN(p) + p->payload_len);
+
+ if (IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode) && (p->livedev)) {
+ devname = p->livedev->dev;
+ }
+ if ((c = libnet_init(LIBNET_RAW4, devname, ebuf)) == NULL) {
+ SCLogError(SC_ERR_LIBNET_INIT,"libnet_inint failed: %s", ebuf);
+ return 1;
+ }
+
+ switch (dir) {
+ case REJECT_DIR_SRC:
+ lpacket.src4 = GET_IPV4_DST_ADDR_U32(p);
+ lpacket.dst4 = GET_IPV4_SRC_ADDR_U32(p);
+ break;
+ case REJECT_DIR_DST:
+ lpacket.src4 = GET_IPV4_SRC_ADDR_U32(p);
+ lpacket.dst4 = GET_IPV4_DST_ADDR_U32(p);
+ break;
+ default:
+ SCLogError(SC_ERR_LIBNET_INVALID_DIR,
+ "reset not src or dst returning");
+ return 1;
+ }
+
+ /* TODO come up with ttl calc function */
+ lpacket.ttl = 64;
+
+ /* build the package */
+ if ((libnet_build_icmpv4_unreach(
+ ICMP_DEST_UNREACH, /* type */
+ ICMP_HOST_ANO, /* code */
+ 0, /* checksum */
+ (uint8_t *)p->ip4h, /* payload */
+ lpacket.len, /* payload length */
+ c, /* libnet context */
+ 0)) < 0) /* libnet ptag */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_icmpv4_unreach %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ if ((libnet_build_ipv4(
+ LIBNET_ICMPV4_H + LIBNET_IPV4_H +
+ lpacket.len, /* entire packet length */
+ 0, /* tos */
+ lpacket.id, /* ID */
+ 0, /* fragmentation flags and offset */
+ lpacket.ttl, /* TTL */
+ IPPROTO_ICMP, /* protocol */
+ 0, /* checksum */
+ lpacket.src4, /* source address */
+ lpacket.dst4, /* destination address */
+ NULL, /* pointer to packet data (or NULL) */
+ 0, /* payload length */
+ c, /* libnet context pointer */
+ 0)) < 0) /* packet id */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_ipv4 %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ result = libnet_write(c);
+ if (result == -1) {
+ SCLogError(SC_ERR_LIBNET_WRITE_FAILED,"libnet_write_raw_ipv4 failed: %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+cleanup:
+ libnet_destroy (c);
+ return 0;
+}
+
+int RejectSendLibnet11L3IPv6TCP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+
+ Libnet11Packet lpacket;
+ libnet_t *c; /* libnet context */
+ char ebuf[LIBNET_ERRBUF_SIZE];
+ int result;
+ char *devname = NULL;
+
+ /* fill in struct defaults */
+ lpacket.ttl = 0;
+ lpacket.id = 0;
+ lpacket.flow = 0;
+ lpacket.class = 0;
+
+ if (IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode) && (p->livedev)) {
+ devname = p->livedev->dev;
+ }
+ if ((c = libnet_init(LIBNET_RAW6, devname, ebuf)) == NULL) {
+ SCLogError(SC_ERR_LIBNET_INIT,"libnet_init failed: %s", ebuf);
+ return 1;
+ }
+
+ if (p->tcph == NULL)
+ return 1;
+
+ /* save payload len */
+ lpacket.dsize = p->payload_len;
+
+ switch (dir) {
+ case REJECT_DIR_SRC:
+ SCLogDebug("sending a tcp reset to src");
+ /* We follow http://tools.ietf.org/html/rfc793#section-3.4 :
+ * If packet has no ACK, the seq number is 0 and the ACK is built
+ * the normal way. If packet has a ACK, the seq of the RST packet
+ * is equal to the ACK of incoming packet and the ACK is build
+ * using packet sequence number and size of the data. */
+ if (TCP_GET_ACK(p) == 0) {
+ lpacket.seq = 0;
+ lpacket.ack = TCP_GET_SEQ(p) + lpacket.dsize + 1;
+ } else {
+ lpacket.seq = TCP_GET_ACK(p);
+ lpacket.ack = TCP_GET_SEQ(p) + lpacket.dsize;
+ }
+
+ lpacket.sp = TCP_GET_DST_PORT(p);
+ lpacket.dp = TCP_GET_SRC_PORT(p);
+
+ memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
+ memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
+
+ break;
+ case REJECT_DIR_DST:
+ SCLogDebug("sending a tcp reset to dst");
+ lpacket.seq = TCP_GET_SEQ(p);
+ lpacket.ack = TCP_GET_ACK(p);
+
+ lpacket.sp = TCP_GET_SRC_PORT(p);
+ lpacket.dp = TCP_GET_DST_PORT(p);
+
+ memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
+ memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
+ break;
+ default:
+ SCLogError(SC_ERR_LIBNET_INVALID_DIR,
+ "reset not src or dst returning");
+ return 1;
+ }
+
+ lpacket.window = TCP_GET_WINDOW(p);
+ //lpacket.seq += lpacket.dsize;
+
+ /* TODO come up with ttl calc function */
+ lpacket.ttl = 64;
+
+ /* build the package */
+ if ((libnet_build_tcp(
+ lpacket.sp, /* source port */
+ lpacket.dp, /* dst port */
+ lpacket.seq, /* seq number */
+ lpacket.ack, /* ack number */
+ TH_RST|TH_ACK, /* flags */
+ lpacket.window, /* window size */
+ 0, /* checksum */
+ 0, /* urgent flag */
+ LIBNET_TCP_H, /* header length */
+ NULL, /* payload */
+ 0, /* payload length */
+ c, /* libnet context */
+ 0)) < 0) /* libnet ptag */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_tcp %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ if ((libnet_build_ipv6(
+ lpacket.class, /* traffic class */
+ lpacket.flow, /* Flow label */
+ LIBNET_TCP_H, /* payload length */
+ IPPROTO_TCP, /* next header */
+ lpacket.ttl, /* TTL */
+ lpacket.src6, /* source address */
+ lpacket.dst6, /* destination address */
+ NULL, /* pointer to packet data (or NULL) */
+ 0, /* payload length */
+ c, /* libnet context pointer */
+ 0)) < 0) /* packet id */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_ipv6 %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ result = libnet_write(c);
+ if (result == -1) {
+ SCLogError(SC_ERR_LIBNET_WRITE_FAILED,"libnet_write failed: %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+cleanup:
+ libnet_destroy (c);
+ return 0;
+}
+
+#ifdef HAVE_LIBNET_ICMPV6_UNREACH
+int RejectSendLibnet11L3IPv6ICMP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ Libnet11Packet lpacket;
+ libnet_t *c; /* libnet context */
+ char ebuf[LIBNET_ERRBUF_SIZE];
+ int result;
+ char *devname = NULL;
+
+ /* fill in struct defaults */
+ lpacket.ttl = 0;
+ lpacket.id = 0;
+ lpacket.flow = 0;
+ lpacket.class = 0;
+
+
+ lpacket.len = IPV6_GET_PLEN(p) + IPV6_HEADER_LEN;
+
+ if (IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode) && (p->livedev)) {
+ devname = p->livedev->dev;
+ }
+ if ((c = libnet_init(LIBNET_RAW6, devname, ebuf)) == NULL) {
+ SCLogError(SC_ERR_LIBNET_INIT,"libnet_inint failed: %s", ebuf);
+ return 1;
+ }
+
+ switch (dir) {
+ case REJECT_DIR_SRC:
+ memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
+ memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
+ break;
+ case REJECT_DIR_DST:
+ memcpy(lpacket.src6.libnet_s6_addr, GET_IPV6_SRC_ADDR(p), 16);
+ memcpy(lpacket.dst6.libnet_s6_addr, GET_IPV6_DST_ADDR(p), 16);
+ break;
+ default:
+ SCLogError(SC_ERR_LIBNET_INVALID_DIR,
+ "reset not src or dst returning");
+ return 1;
+ }
+
+ /* TODO come up with ttl calc function */
+ lpacket.ttl = 64;
+
+ /* build the package */
+ if ((libnet_build_icmpv6_unreach(
+ ICMP6_DST_UNREACH, /* type */
+ ICMP6_DST_UNREACH_ADMIN, /* code */
+ 0, /* checksum */
+ (uint8_t *)p->ip6h, /* payload */
+ lpacket.len, /* payload length */
+ c, /* libnet context */
+ 0)) < 0) /* libnet ptag */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_icmpv6_unreach %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ if ((libnet_build_ipv6(
+ lpacket.class, /* traffic class */
+ lpacket.flow, /* Flow label */
+ LIBNET_ICMPV6_H + lpacket.len, /* IPv6 payload length */
+ IPPROTO_ICMPV6, /* next header */
+ lpacket.ttl, /* TTL */
+ lpacket.src6, /* source address */
+ lpacket.dst6, /* destination address */
+ NULL, /* pointer to packet data (or NULL) */
+ 0, /* payload length */
+ c, /* libnet context pointer */
+ 0)) < 0) /* packet id */
+ {
+ SCLogError(SC_ERR_LIBNET_BUILD_FAILED,"libnet_build_ipv6 %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+ result = libnet_write(c);
+ if (result == -1) {
+ SCLogError(SC_ERR_LIBNET_WRITE_FAILED,"libnet_write_raw_ipv6 failed: %s", libnet_geterror(c));
+ goto cleanup;
+ }
+
+cleanup:
+ libnet_destroy (c);
+ return 0;
+}
+#else /* HAVE_LIBNET_ICMPV6_UNREACH */
+
+int RejectSendLibnet11L3IPv6ICMP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ SCLogError(SC_ERR_LIBNET_NOT_ENABLED, "Libnet ICMPv6 based rejects are disabled."
+ "Usually this means that you don't have a patched libnet installed,"
+ " or configure couldn't find it.");
+ return 0;
+}
+#endif /* HAVE_LIBNET_ICMPV6_UNREACH */
+
+
+#else
+
+int RejectSendLibnet11L3IPv4TCP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ SCLogError(SC_ERR_LIBNET_NOT_ENABLED, "Libnet based rejects are disabled."
+ "Usually this means that you don't have libnet installed,"
+ " or configure couldn't find it.");
+ return 0;
+}
+
+int RejectSendLibnet11L3IPv4ICMP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ SCLogError(SC_ERR_LIBNET_NOT_ENABLED, "Libnet based rejects are disabled."
+ "Usually this means that you don't have libnet installed,"
+ " or configure couldn't find it.");
+ return 0;
+}
+
+int RejectSendLibnet11L3IPv6TCP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ SCLogError(SC_ERR_LIBNET_NOT_ENABLED, "Libnet based rejects are disabled."
+ "Usually this means that you don't have libnet installed,"
+ " or configure couldn't find it.");
+ return 0;
+}
+
+int RejectSendLibnet11L3IPv6ICMP(ThreadVars *tv, Packet *p, void *data, int dir)
+{
+ SCLogError(SC_ERR_LIBNET_NOT_ENABLED, "Libnet based rejects are disabled."
+ "Usually this means that you don't have libnet installed,"
+ " or configure couldn't find it.");
+ return 0;
+}
+
+#endif /* HAVE_LIBNET11 */
diff --git a/framework/src/suricata/src/respond-reject-libnet11.h b/framework/src/suricata/src/respond-reject-libnet11.h
new file mode 100644
index 00000000..f2e605b9
--- /dev/null
+++ b/framework/src/suricata/src/respond-reject-libnet11.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author William Metcalf <william.metcalf@gmail.com>
+ */
+
+#ifndef __RESPOND_REJECT_LIBNET11_H__
+#define __RESPOND_REJECT_LIBNET11_H__
+
+int RejectSendLibnet11L3IPv4TCP(ThreadVars *, Packet *, void *,int);
+int RejectSendLibnet11L3IPv4ICMP(ThreadVars *, Packet *, void *,int);
+
+int RejectSendLibnet11L3IPv6TCP(ThreadVars *, Packet *, void *,int);
+int RejectSendLibnet11L3IPv6ICMP(ThreadVars *, Packet *, void *,int);
+#endif /* __RESPOND_REJECT_LIBNET11_H__ */
diff --git a/framework/src/suricata/src/respond-reject.c b/framework/src/suricata/src/respond-reject.c
new file mode 100644
index 00000000..ea756d7e
--- /dev/null
+++ b/framework/src/suricata/src/respond-reject.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ *
+ * RespondReject is a threaded wrapper for sending Rejects
+ *
+ * \todo RespondRejectFunc returns 1 on error, 0 on ok... why? For now it should
+ * just return 0 always, error handling is a TODO in the threading model (VJ)
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "action-globals.h"
+
+#include "respond-reject.h"
+#include "respond-reject-libnet11.h"
+
+#include "util-debug.h"
+#include "util-privs.h"
+
+int RejectSendIPv4TCP(ThreadVars *, Packet *, void *);
+int RejectSendIPv4ICMP(ThreadVars *, Packet *, void *);
+int RejectSendIPv6TCP(ThreadVars *, Packet *, void *);
+int RejectSendIPv6ICMP(ThreadVars *, Packet *, void *);
+
+void TmModuleRespondRejectRegister (void)
+{
+ tmm_modules[TMM_RESPONDREJECT].name = "RespondReject";
+ tmm_modules[TMM_RESPONDREJECT].ThreadInit = NULL;
+ tmm_modules[TMM_RESPONDREJECT].Func = RespondRejectFunc;
+ tmm_modules[TMM_RESPONDREJECT].ThreadDeinit = NULL;
+ tmm_modules[TMM_RESPONDREJECT].RegisterTests = NULL;
+ tmm_modules[TMM_RESPONDREJECT].cap_flags = 0; /* libnet is not compat with caps */
+}
+
+TmEcode RespondRejectFunc(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ int ret = 0;
+
+ /* ACTION_REJECT defaults to rejecting the SRC */
+ if (!(PACKET_TEST_ACTION(p, ACTION_REJECT)) &&
+ !(PACKET_TEST_ACTION(p, ACTION_REJECT_DST)) &&
+ !(PACKET_TEST_ACTION(p, ACTION_REJECT_BOTH))) {
+ return TM_ECODE_OK;
+ }
+
+ if (PKT_IS_IPV4(p)) {
+ if (PKT_IS_TCP(p)) {
+ ret = RejectSendIPv4TCP(tv, p, data);
+ } else {
+ ret = RejectSendIPv4ICMP(tv, p, data);
+ }
+ } else if (PKT_IS_IPV6(p)) {
+ if (PKT_IS_TCP(p)) {
+ ret = RejectSendIPv6TCP(tv, p, data);
+ } else {
+ ret = RejectSendIPv6ICMP(tv, p, data);
+ }
+ } else {
+ /* we're only supporting IPv4 and IPv6 */
+ return TM_ECODE_OK;
+ }
+
+ if (ret)
+ return TM_ECODE_FAILED;
+ else
+ return TM_ECODE_OK;
+}
+
+int RejectSendIPv4TCP(ThreadVars *tv, Packet *p, void *data)
+{
+ SCEnter();
+ int r = 0;
+ if (PACKET_TEST_ACTION(p, ACTION_REJECT)) {
+ r = RejectSendLibnet11L3IPv4TCP(tv, p, data, REJECT_DIR_SRC);
+ SCReturnInt(r);
+ } else if (PACKET_TEST_ACTION(p, ACTION_REJECT_DST)) {
+ r = RejectSendLibnet11L3IPv4TCP(tv, p, data, REJECT_DIR_DST);
+ SCReturnInt(r);
+ } else if(PACKET_TEST_ACTION(p, ACTION_REJECT_BOTH)) {
+ int ret;
+ ret = RejectSendLibnet11L3IPv4TCP(tv, p, data, REJECT_DIR_SRC);
+ if (RejectSendLibnet11L3IPv4TCP(tv, p, data, REJECT_DIR_DST) == 0) {
+ SCReturnInt(0);
+ } else {
+ SCReturnInt(ret);
+ }
+ }
+ SCReturnInt(0);
+}
+
+int RejectSendIPv4ICMP(ThreadVars *tv, Packet *p, void *data)
+{
+ SCEnter();
+ int r = 0;
+ if (PACKET_TEST_ACTION(p, ACTION_REJECT)) {
+ r = RejectSendLibnet11L3IPv4ICMP(tv, p, data, REJECT_DIR_SRC);
+ SCReturnInt(r);
+ } else if (PACKET_TEST_ACTION(p, ACTION_REJECT_DST)) {
+ r = RejectSendLibnet11L3IPv4ICMP(tv, p, data, REJECT_DIR_DST);
+ SCReturnInt(r);
+ } else if(PACKET_TEST_ACTION(p, ACTION_REJECT_BOTH)) {
+ int ret;
+ ret = RejectSendLibnet11L3IPv4ICMP(tv, p, data, REJECT_DIR_SRC);
+ if (RejectSendLibnet11L3IPv4ICMP(tv, p, data, REJECT_DIR_DST) == 0) {
+ SCReturnInt(0);
+ } else {
+ SCReturnInt(ret);
+ }
+ }
+ SCReturnInt(0);
+}
+
+int RejectSendIPv6TCP(ThreadVars *tv, Packet *p, void *data)
+{
+ SCEnter();
+ int r = 0;
+ if (PACKET_TEST_ACTION(p, ACTION_REJECT)) {
+ r = RejectSendLibnet11L3IPv6TCP(tv, p, data, REJECT_DIR_SRC);
+ SCReturnInt(r);
+ } else if (PACKET_TEST_ACTION(p, ACTION_REJECT_DST)) {
+ r = RejectSendLibnet11L3IPv6TCP(tv, p, data, REJECT_DIR_DST);
+ SCReturnInt(r);
+ } else if(PACKET_TEST_ACTION(p, ACTION_REJECT_BOTH)) {
+ int ret;
+ ret = RejectSendLibnet11L3IPv6TCP(tv, p, data, REJECT_DIR_SRC);
+ if (RejectSendLibnet11L3IPv6TCP(tv, p, data, REJECT_DIR_DST) == 0) {
+ SCReturnInt(0);
+ } else {
+ SCReturnInt(ret);
+ }
+ }
+ SCReturnInt(0);
+}
+
+int RejectSendIPv6ICMP(ThreadVars *tv, Packet *p, void *data)
+{
+ SCEnter();
+ int r = 0;
+ if (PACKET_TEST_ACTION(p, ACTION_REJECT)) {
+ r = RejectSendLibnet11L3IPv6ICMP(tv, p, data, REJECT_DIR_SRC);
+ SCReturnInt(r);
+ } else if (PACKET_TEST_ACTION(p, ACTION_REJECT_DST)) {
+ r = RejectSendLibnet11L3IPv6ICMP(tv, p, data, REJECT_DIR_DST);
+ SCReturnInt(r);
+ } else if(PACKET_TEST_ACTION(p, ACTION_REJECT_BOTH)) {
+ int ret;
+ ret = RejectSendLibnet11L3IPv6ICMP(tv, p, data, REJECT_DIR_SRC);
+ if (RejectSendLibnet11L3IPv6ICMP(tv, p, data, REJECT_DIR_DST) == 0) {
+ SCReturnInt(0);
+ } else {
+ SCReturnInt(ret);
+ }
+ }
+ SCReturnInt(0);
+}
+
diff --git a/framework/src/suricata/src/respond-reject.h b/framework/src/suricata/src/respond-reject.h
new file mode 100644
index 00000000..6a004f4d
--- /dev/null
+++ b/framework/src/suricata/src/respond-reject.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ */
+
+#ifndef __RESPOND_REJECT_H__
+#define __RESPOND_REJECT_H__
+
+#include "tm-threads.h"
+
+#define REJECT_DIR_SRC 0
+#define REJECT_DIR_DST 1
+
+void TmModuleRespondRejectRegister (void);
+TmEcode RespondRejectFunc(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+#endif /* __RESPOND_REJECT_H__ */
diff --git a/framework/src/suricata/src/runmode-af-packet.c b/framework/src/suricata/src/runmode-af-packet.c
new file mode 100644
index 00000000..fc17a4bd
--- /dev/null
+++ b/framework/src/suricata/src/runmode-af-packet.c
@@ -0,0 +1,590 @@
+/* Copyright (C) 2011,2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup afppacket
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * AF_PACKET socket runmode
+ *
+ */
+
+
+#include "suricata-common.h"
+#include "config.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-af-packet.h"
+#include "log-httplog.h"
+#include "output.h"
+#include "detect-engine-mpm.h"
+
+#include "alert-fastlog.h"
+#include "alert-prelude.h"
+#include "alert-unified2-alert.h"
+#include "alert-debuglog.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-ioctl.h"
+
+#include "source-af-packet.h"
+
+extern int max_pending_packets;
+
+static const char *default_mode_workers = NULL;
+
+const char *RunModeAFPGetDefaultMode(void)
+{
+ return default_mode_workers;
+}
+
+void RunModeIdsAFPRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "single",
+ "Single threaded af-packet mode",
+ RunModeIdsAFPSingle);
+ RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "workers",
+ "Workers af-packet mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsAFPWorkers);
+ default_mode_workers = "workers";
+ RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "autofp",
+ "Multi socket AF_PACKET mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread.",
+ RunModeIdsAFPAutoFp);
+ return;
+}
+
+void AFPDerefConfig(void *conf)
+{
+ AFPIfaceConfig *pfp = (AFPIfaceConfig *)conf;
+ /* Pcap config is used only once but cost of this low. */
+ if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
+ SCFree(pfp);
+ }
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to or copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * \return a AFPIfaceConfig corresponding to the interface name
+ */
+void *ParseAFPConfig(const char *iface)
+{
+ char *threadsstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *af_packet_node;
+ AFPIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
+ char *tmpclusterid;
+ char *tmpctype;
+ char *copymodestr;
+ intmax_t value;
+ int boolval;
+ char *bpf_filter = NULL;
+ char *out_iface = NULL;
+
+ if (unlikely(aconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(aconf);
+ return NULL;
+ }
+
+ strlcpy(aconf->iface, iface, sizeof(aconf->iface));
+ aconf->threads = 1;
+ SC_ATOMIC_INIT(aconf->ref);
+ (void) SC_ATOMIC_ADD(aconf->ref, 1);
+ aconf->buffer_size = 0;
+ aconf->cluster_id = 1;
+ aconf->cluster_type = PACKET_FANOUT_HASH;
+ aconf->promisc = 1;
+ aconf->checksum_mode = CHECKSUM_VALIDATION_KERNEL;
+ aconf->DerefFunc = AFPDerefConfig;
+ aconf->flags = 0;
+ aconf->bpf_filter = NULL;
+ aconf->out_iface = NULL;
+ aconf->copy_mode = AFP_COPY_MODE_NONE;
+
+ if (ConfGet("bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ aconf->bpf_filter = bpf_filter;
+ SCLogInfo("Going to use command-line provided bpf filter '%s'",
+ aconf->bpf_filter);
+ }
+ }
+
+ /* Find initial node */
+ af_packet_node = ConfGetNode("af-packet");
+ if (af_packet_node == NULL) {
+ SCLogInfo("Unable to find af-packet config using default value");
+ return aconf;
+ }
+
+ if_root = ConfNodeLookupKeyValue(af_packet_node, "interface", iface);
+
+ if_default = ConfNodeLookupKeyValue(af_packet_node, "interface", "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("Unable to find af-packet config for "
+ "interface \"%s\" or \"default\", using default value",
+ iface);
+ return aconf;
+ }
+
+ /* If there is no setting for current interface use default one as main iface */
+ if (if_root == NULL) {
+ if_root = if_default;
+ if_default = NULL;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
+ aconf->threads = 0;
+ } else {
+ if (threadsstr != NULL) {
+ if (strcmp(threadsstr, "auto") == 0) {
+ aconf->threads = 0;
+ } else {
+ aconf->threads = (uint8_t)atoi(threadsstr);
+ }
+ }
+ }
+ if (aconf->threads == 0) {
+ int rss_queues;
+ aconf->threads = (int)UtilCpuGetNumProcessorsOnline();
+ /* Get the number of RSS queues and take the min */
+ rss_queues = GetIfaceRSSQueuesNum(iface);
+ if (rss_queues > 0) {
+ if (rss_queues < aconf->threads) {
+ aconf->threads = rss_queues;
+ SCLogInfo("More core than RSS queues, using %d threads for interface %s",
+ aconf->threads, iface);
+ }
+ }
+ if (aconf->threads)
+ SCLogInfo("Using %d AF_PACKET threads for interface %s", aconf->threads, iface);
+ }
+ if (aconf->threads <= 0) {
+ aconf->threads = 1;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
+ if (strlen(out_iface) > 0) {
+ aconf->out_iface = out_iface;
+ }
+ }
+
+ (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "use-mmap", (int *)&boolval);
+ if (boolval) {
+ SCLogInfo("Enabling mmaped capture on iface %s",
+ aconf->iface);
+ aconf->flags |= AFP_RING_MODE;
+ }
+ (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "use-emergency-flush", (int *)&boolval);
+ if (boolval) {
+ SCLogInfo("Enabling ring emergency flush on iface %s",
+ aconf->iface);
+ aconf->flags |= AFP_EMERGENCY_MODE;
+ }
+
+
+ aconf->copy_mode = AFP_COPY_MODE_NONE;
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
+ if (aconf->out_iface == NULL) {
+ SCLogInfo("Copy mode activated but no destination"
+ " iface. Disabling feature");
+ } else if (!(aconf->flags & AFP_RING_MODE)) {
+ SCLogInfo("Copy mode activated but use-mmap "
+ "set to no. Disabling feature");
+ } else if (strlen(copymodestr) <= 0) {
+ aconf->out_iface = NULL;
+ } else if (strcmp(copymodestr, "ips") == 0) {
+ SCLogInfo("AF_PACKET IPS mode activated %s->%s",
+ iface,
+ aconf->out_iface);
+ aconf->copy_mode = AFP_COPY_MODE_IPS;
+ } else if (strcmp(copymodestr, "tap") == 0) {
+ SCLogInfo("AF_PACKET TAP mode activated %s->%s",
+ iface,
+ aconf->out_iface);
+ aconf->copy_mode = AFP_COPY_MODE_TAP;
+ } else {
+ SCLogInfo("Invalid mode (not in tap, ips)");
+ }
+ }
+
+ SC_ATOMIC_RESET(aconf->ref);
+ (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads);
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Could not get cluster-id from config");
+ } else {
+ aconf->cluster_id = (uint16_t)atoi(tmpclusterid);
+ SCLogDebug("Going to use cluster-id %" PRId32, aconf->cluster_id);
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
+ SCLogError(SC_ERR_GET_CLUSTER_TYPE_FAILED,"Could not get cluster-type from config");
+ } else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
+ SCLogInfo("Using round-robin cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type = PACKET_FANOUT_LB;
+ } else if (strcmp(tmpctype, "cluster_flow") == 0) {
+ /* In hash mode, we also ask for defragmentation needed to
+ * compute the hash */
+ uint16_t defrag = 0;
+ int conf_val = 0;
+ SCLogInfo("Using flow cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ ConfGetChildValueBoolWithDefault(if_root, if_default, "defrag", &conf_val);
+ if (conf_val) {
+ SCLogInfo("Using defrag kernel functionality for AF_PACKET (iface %s)",
+ aconf->iface);
+ defrag = PACKET_FANOUT_FLAG_DEFRAG;
+ }
+ aconf->cluster_type = PACKET_FANOUT_HASH | defrag;
+ } else if (strcmp(tmpctype, "cluster_cpu") == 0) {
+ SCLogInfo("Using cpu cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type = PACKET_FANOUT_CPU;
+ } else if (strcmp(tmpctype, "cluster_qm") == 0) {
+ SCLogInfo("Using queue based cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type = PACKET_FANOUT_QM;
+ } else if (strcmp(tmpctype, "cluster_random") == 0) {
+ SCLogInfo("Using random based cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type = PACKET_FANOUT_RND;
+ } else if (strcmp(tmpctype, "cluster_rollover") == 0) {
+ SCLogInfo("Using rollover based cluster mode for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type = PACKET_FANOUT_ROLLOVER;
+
+ } else {
+ SCLogError(SC_ERR_INVALID_CLUSTER_TYPE,"invalid cluster-type %s",tmpctype);
+ SCFree(aconf);
+ return NULL;
+ }
+
+ int conf_val = 0;
+ ConfGetChildValueBoolWithDefault(if_root, if_default, "rollover", &conf_val);
+ if (conf_val) {
+ SCLogInfo("Using rollover kernel functionality for AF_PACKET (iface %s)",
+ aconf->iface);
+ aconf->cluster_type |= PACKET_FANOUT_FLAG_ROLLOVER;
+ }
+
+ /*load af_packet bpf filter*/
+ /* command line value has precedence */
+ if (ConfGet("bpf-filter", &bpf_filter) != 1) {
+ if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ aconf->bpf_filter = bpf_filter;
+ SCLogInfo("Going to use bpf filter %s", aconf->bpf_filter);
+ }
+ }
+ }
+
+ if ((ConfGetChildValueIntWithDefault(if_root, if_default, "buffer-size", &value)) == 1) {
+ aconf->buffer_size = value;
+ } else {
+ aconf->buffer_size = 0;
+ }
+ if ((ConfGetChildValueIntWithDefault(if_root, if_default, "ring-size", &value)) == 1) {
+ aconf->ring_size = value;
+ if (value * aconf->threads < max_pending_packets) {
+ aconf->ring_size = max_pending_packets / aconf->threads + 1;
+ SCLogWarning(SC_ERR_AFP_CREATE, "Inefficient setup: ring-size < max_pending_packets. "
+ "Resetting to decent value %d.", aconf->ring_size);
+ /* We want at least that max_pending_packets packets can be handled by the
+ * interface. This is generous if we have multiple interfaces listening. */
+ }
+ } else {
+ /* We want that max_pending_packets packets can be handled by suricata
+ * for this interface. To take burst into account we multiply the obtained
+ * size by 2. */
+ aconf->ring_size = max_pending_packets * 2 / aconf->threads;
+ }
+
+ (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
+ if (boolval) {
+ SCLogInfo("Disabling promiscuous mode on iface %s",
+ aconf->iface);
+ aconf->promisc = 0;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
+ if (strcmp(tmpctype, "auto") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (strcmp(tmpctype, "yes") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(tmpctype, "no") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else if (strcmp(tmpctype, "kernel") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_KERNEL;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", aconf->iface);
+ }
+ }
+
+ if (GetIfaceOffloading(iface) == 1) {
+ SCLogWarning(SC_ERR_AFP_CREATE,
+ "Using AF_PACKET with GRO or LRO activated can lead to capture problems");
+ }
+
+ return aconf;
+}
+
+int AFPConfigGeThreadsCount(void *conf)
+{
+ AFPIfaceConfig *afp = (AFPIfaceConfig *)conf;
+ return afp->threads;
+}
+
+int AFPRunModeIsIPS()
+{
+ int nlive = LiveGetDeviceCount();
+ int ldev;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *af_packet_node;
+ int has_ips = 0;
+ int has_ids = 0;
+
+ /* Find initial node */
+ af_packet_node = ConfGetNode("af-packet");
+ if (af_packet_node == NULL) {
+ return 0;
+ }
+
+ if_default = ConfNodeLookupKeyValue(af_packet_node, "interface", "default");
+
+ for (ldev = 0; ldev < nlive; ldev++) {
+ char *live_dev = LiveGetDeviceName(ldev);
+ if (live_dev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ char *copymodestr = NULL;
+ if_root = ConfNodeLookupKeyValue(af_packet_node, "interface", live_dev);
+
+ if (if_root == NULL) {
+ if (if_default == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = if_default;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
+ if (strcmp(copymodestr, "ips") == 0) {
+ has_ips = 1;
+ } else {
+ has_ids = 1;
+ }
+ } else {
+ has_ids = 1;
+ }
+ }
+
+ if (has_ids && has_ips) {
+ SCLogInfo("AF_PACKET mode using IPS and IDS mode");
+ for (ldev = 0; ldev < nlive; ldev++) {
+ char *live_dev = LiveGetDeviceName(ldev);
+ if (live_dev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = ConfNodeLookupKeyValue(af_packet_node, "interface", live_dev);
+ char *copymodestr = NULL;
+
+ if (if_root == NULL) {
+ if (if_default == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = if_default;
+ }
+
+ if (! ((ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) &&
+ (strcmp(copymodestr, "ips") == 0))) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "AF_PACKET IPS mode used and interface '%s' is in IDS or TAP mode. "
+ "Sniffing '%s' but expect bad result as stream-inline is activated.",
+ live_dev, live_dev);
+ }
+ }
+ }
+
+ return has_ips;
+}
+
+int RunModeIdsAFPAutoFp(void)
+{
+ SCEnter();
+
+/* We include only if AF_PACKET is enabled */
+#ifdef HAVE_AF_PACKET
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ (void)ConfGet("af-packet.live-interface", &live_dev);
+
+ SCLogDebug("live_dev %s", live_dev);
+
+ if (AFPPeersListInit() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to init peers list.");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureAutoFp(ParseAFPConfig,
+ AFPConfigGeThreadsCount,
+ "ReceiveAFP",
+ "DecodeAFP", "RxAFP",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ /* In IPS mode each threads must have a peer */
+ if (AFPPeersListCheck() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer.");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsAFPAutoFp initialised");
+#endif /* HAVE_AF_PACKET */
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Single thread version of the AF_PACKET processing.
+ */
+int RunModeIdsAFPSingle(void)
+{
+ SCEnter();
+#ifdef HAVE_AF_PACKET
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void)ConfGet("af-packet.live-interface", &live_dev);
+
+ if (AFPPeersListInit() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to init peers list.");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureSingle(ParseAFPConfig,
+ AFPConfigGeThreadsCount,
+ "ReceiveAFP",
+ "DecodeAFP", "AFPacket",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ /* In IPS mode each threads must have a peer */
+ if (AFPPeersListCheck() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer.");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsAFPSingle initialised");
+
+#endif /* HAVE_AF_PACKET */
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Workers version of the AF_PACKET processing.
+ *
+ * Start N threads with each thread doing all the work.
+ *
+ */
+int RunModeIdsAFPWorkers(void)
+{
+ SCEnter();
+#ifdef HAVE_AF_PACKET
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void)ConfGet("af-packet.live-interface", &live_dev);
+
+ if (AFPPeersListInit() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to init peers list.");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureWorkers(ParseAFPConfig,
+ AFPConfigGeThreadsCount,
+ "ReceiveAFP",
+ "DecodeAFP", "AFPacket",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ /* In IPS mode each threads must have a peer */
+ if (AFPPeersListCheck() != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer.");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsAFPWorkers initialised");
+
+#endif /* HAVE_AF_PACKET */
+ SCReturnInt(0);
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/runmode-af-packet.h b/framework/src/suricata/src/runmode-af-packet.h
new file mode 100644
index 00000000..79fe436a
--- /dev/null
+++ b/framework/src/suricata/src/runmode-af-packet.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __RUNMODE_AF_PACKET_H__
+#define __RUNMODE_AF_PACKET_H__
+
+int RunModeIdsAFPSingle(void);
+int RunModeIdsAFPAutoFp(void);
+int RunModeIdsAFPWorkers(void);
+void RunModeIdsAFPRegister(void);
+const char *RunModeAFPGetDefaultMode(void);
+int AFPRunModeIsIPS();
+
+#endif /* __RUNMODE_AF_PACKET_H__ */
diff --git a/framework/src/suricata/src/runmode-erf-dag.c b/framework/src/suricata/src/runmode-erf-dag.c
new file mode 100644
index 00000000..5653b9ec
--- /dev/null
+++ b/framework/src/suricata/src/runmode-erf-dag.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-erf-dag.h"
+#include "output.h"
+
+#include "detect-engine.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-runmodes.h"
+
+static const char *default_mode;
+
+static int DagConfigGetThreadCount(void *conf)
+{
+ return 1;
+}
+
+static void *ParseDagConfig(const char *iface)
+{
+ return (void *)iface;
+}
+
+const char *RunModeErfDagGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeErfDagRegister(void)
+{
+ default_mode = "autofp";
+
+ RunModeRegisterNewRunMode(RUNMODE_DAG, "autofp",
+ "Multi threaded DAG mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread, unlike \"dag_auto\" where packets "
+ "from the same flow can be processed by any "
+ "detect thread",
+ RunModeIdsErfDagAutoFp);
+
+ RunModeRegisterNewRunMode(RUNMODE_DAG, "single",
+ "Singled threaded DAG mode",
+ RunModeIdsErfDagSingle);
+
+ RunModeRegisterNewRunMode(RUNMODE_DAG, "workers",
+ "Workers DAG mode, each thread does all "
+ " tasks from acquisition to logging",
+ RunModeIdsErfDagWorkers);
+
+ return;
+}
+
+int RunModeIdsErfDagSingle(void)
+{
+ int ret;
+
+ SCEnter();
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureSingle(ParseDagConfig,
+ DagConfigGetThreadCount,
+ "ReceiveErfDag",
+ "DecodeErfDag",
+ "RxDAG",
+ NULL);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "DAG single runmode failed to start");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsDagSingle initialised");
+
+ SCReturnInt(0);
+}
+
+int RunModeIdsErfDagAutoFp(void)
+{
+ int ret;
+
+ SCEnter();
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureAutoFp(ParseDagConfig,
+ DagConfigGetThreadCount,
+ "ReceiveErfDag",
+ "DecodeErfDag",
+ "RxDAG",
+ NULL);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "DAG autofp runmode failed to start");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsDagAutoFp initialised");
+
+ SCReturnInt(0);
+}
+
+int RunModeIdsErfDagWorkers(void)
+{
+ int ret;
+
+ SCEnter();
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureWorkers(ParseDagConfig,
+ DagConfigGetThreadCount,
+ "ReceiveErfDag",
+ "DecodeErfDag",
+ "RxDAG",
+ NULL);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "DAG workers runmode failed to start");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsErfDagWorkers initialised");
+
+ SCReturnInt(0);
+}
diff --git a/framework/src/suricata/src/runmode-erf-dag.h b/framework/src/suricata/src/runmode-erf-dag.h
new file mode 100644
index 00000000..c4b2a59c
--- /dev/null
+++ b/framework/src/suricata/src/runmode-erf-dag.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_ERF_DAG_H__
+#define __RUNMODE_ERF_DAG_H__
+
+int RunModeIdsErfDagAutoFp(void);
+int RunModeIdsErfDagSingle(void);
+int RunModeIdsErfDagWorkers(void);
+void RunModeErfDagRegister(void);
+const char *RunModeErfDagGetDefaultMode(void);
+
+#endif /* __RUNMODE_ERF_DAG_H__ */
diff --git a/framework/src/suricata/src/runmode-erf-file.c b/framework/src/suricata/src/runmode-erf-file.c
new file mode 100644
index 00000000..19dbb683
--- /dev/null
+++ b/framework/src/suricata/src/runmode-erf-file.c
@@ -0,0 +1,279 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-erf-file.h"
+#include "output.h"
+
+#include "detect-engine.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+
+#include "util-runmodes.h"
+
+static const char *default_mode;
+
+const char *RunModeErfFileGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeErfFileRegister(void)
+{
+ default_mode = "autofp";
+
+ RunModeRegisterNewRunMode(RUNMODE_ERF_FILE, "single",
+ "Single threaded ERF file mode",
+ RunModeErfFileSingle);
+
+ RunModeRegisterNewRunMode(RUNMODE_ERF_FILE, "autofp",
+ "Multi threaded ERF file mode. Packets from "
+ "each flow are assigned to a single detect thread",
+ RunModeErfFileAutoFp);
+
+ return;
+}
+
+int RunModeErfFileSingle(void)
+{
+ char *file;
+
+ SCEnter();
+
+ if (ConfGet("erf-file.file", &file) == 0) {
+ SCLogError(SC_ERR_RUNMODE, "Failed to get erf-file.file from config.");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeInitialize();
+
+ TimeModeSetOffline();
+
+ /* Basically the same setup as PCAP files. */
+
+ ThreadVars *tv = TmThreadCreatePacketHandler("ErfFile",
+ "packetpool", "packetpool",
+ "packetpool", "packetpool",
+ "pktacqloop");
+ if (tv == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ TmModule *tm_module = TmModuleGetByName("ReceiveErfFile");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName failed for ReceiveErfFile\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, file);
+
+ tm_module = TmModuleGetByName("DecodeErfFile");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName DecodeErfFile failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName StreamTcp failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName Detect failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+ }
+
+ SetupOutputs(tv);
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeErfFileSingle initialised");
+
+ SCReturnInt(0);
+}
+
+int RunModeErfFileAutoFp(void)
+{
+ SCEnter();
+ char tname[TM_THREAD_NAME_MAX];
+ char qname[TM_QUEUE_NAME_MAX];
+ uint16_t cpu = 0;
+ char *queues = NULL;
+ int thread;
+
+ RunModeInitialize();
+ RunmodeSetFlowStreamAsync();
+
+ char *file = NULL;
+ if (ConfGet("erf-file.file", &file) == 0) {
+ SCLogError(SC_ERR_RUNMODE,
+ "Failed retrieving erf-file.file from config");
+ exit(EXIT_FAILURE);
+ }
+
+ TimeModeSetOffline();
+
+ /* Available cpus */
+ uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
+
+ /* start with cpu 1 so that if we're creating an odd number of detect
+ * threads we're not creating the most on CPU0. */
+ if (ncpus > 0)
+ cpu = 1;
+
+ /* always create at least one thread */
+ int thread_max = TmThreadGetNbThreads(DETECT_CPU_SET);
+ if (thread_max == 0)
+ thread_max = ncpus * threading_detect_ratio;
+ if (thread_max < 1)
+ thread_max = 1;
+
+ queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
+ if (queues == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "RunmodeAutoFpCreatePickupQueuesString failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* create the threads */
+ ThreadVars *tv =
+ TmThreadCreatePacketHandler("ReceiveErfFile",
+ "packetpool", "packetpool",
+ queues, "flow",
+ "pktacqloop");
+ SCFree(queues);
+
+ if (tv == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName("ReceiveErfFile");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName failed for ReceiveErfFile\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, file);
+
+ tm_module = TmModuleGetByName("DecodeErfFile");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName DecodeErfFile failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ if (threading_set_cpu_affinity) {
+ TmThreadSetCPUAffinity(tv, 0);
+ if (ncpus > 1)
+ TmThreadSetThreadPriority(tv, PRIO_MEDIUM);
+ }
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (thread = 0; thread < thread_max; thread++) {
+ snprintf(tname, sizeof(tname), "Detect%d", thread+1);
+ snprintf(qname, sizeof(qname), "pickup%d", thread+1);
+
+ SCLogDebug("tname %s, qname %s", tname, qname);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ printf("ERROR: Can't allocate thread name\n");
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("Assigning %s affinity to cpu %u", thread_name, cpu);
+
+ ThreadVars *tv_detect_ncpu =
+ TmThreadCreatePacketHandler(thread_name,
+ qname, "flow",
+ "packetpool", "packetpool",
+ "varslot");
+ if (tv_detect_ncpu == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(EXIT_FAILURE);
+ }
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName StreamTcp failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName Detect failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+ }
+
+ if (threading_set_cpu_affinity) {
+ TmThreadSetCPUAffinity(tv_detect_ncpu, (int)cpu);
+ /* If we have more than one core/cpu, the first Detect thread
+ * (at cpu 0) will have less priority (higher 'nice' value)
+ * In this case we will set the thread priority to +10 (default is 0)
+ */
+ if (cpu == 0 && ncpus > 1) {
+ TmThreadSetThreadPriority(tv_detect_ncpu, PRIO_LOW);
+ } else if (ncpus > 1) {
+ TmThreadSetThreadPriority(tv_detect_ncpu, PRIO_MEDIUM);
+ }
+ }
+
+ char *thread_group_name = SCStrdup("Detect");
+ if (unlikely(thread_group_name == NULL)) {
+ printf("Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+ tv_detect_ncpu->thread_group_name = thread_group_name;
+
+ /* add outputs as well */
+ SetupOutputs(tv_detect_ncpu);
+
+ if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((cpu + 1) == ncpus)
+ cpu = 0;
+ else
+ cpu++;
+ }
+
+ SCLogInfo("RunModeErfFileAutoFp initialised");
+
+ SCReturnInt(0);
+}
diff --git a/framework/src/suricata/src/runmode-erf-file.h b/framework/src/suricata/src/runmode-erf-file.h
new file mode 100644
index 00000000..54a8ba89
--- /dev/null
+++ b/framework/src/suricata/src/runmode-erf-file.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_ERF_FILE_H__
+#define __RUNMODE_ERF_FILE_H__
+
+int RunModeErfFileSingle(void);
+int RunModeErfFileAutoFp(void);
+void RunModeErfFileRegister(void);
+const char *RunModeErfFileGetDefaultMode(void);
+
+#endif /* __RUNMODE_ERF_FILE_H__ */
diff --git a/framework/src/suricata/src/runmode-ipfw.c b/framework/src/suricata/src/runmode-ipfw.c
new file mode 100644
index 00000000..841692e1
--- /dev/null
+++ b/framework/src/suricata/src/runmode-ipfw.c
@@ -0,0 +1,104 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Handling of ipfw runmodes.
+ */
+
+
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-ipfw.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-runmodes.h"
+#include "source-ipfw.h"
+#include "util-device.h"
+
+static const char *default_mode;
+
+const char *RunModeIpsIPFWGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeIpsIPFWRegister(void)
+{
+ default_mode = "autofp";
+
+ RunModeRegisterNewRunMode(RUNMODE_IPFW, "autofp",
+ "Multi threaded IPFW IPS mode with respect to flow",
+ RunModeIpsIPFWAutoFp);
+
+ RunModeRegisterNewRunMode(RUNMODE_IPFW, "workers",
+ "Multi queue IPFW IPS mode with one thread per queue",
+ RunModeIpsIPFWWorker);
+
+ return;
+}
+
+int RunModeIpsIPFWAutoFp(void)
+{
+ SCEnter();
+ int ret = 0;
+#ifdef IPFW
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ LiveDeviceHasNoStats();
+
+ ret = RunModeSetIPSAutoFp(IPFWGetThread,
+ "ReceiveIPFW",
+ "VerdictIPFW",
+ "DecodeIPFW");
+#endif /* IPFW */
+ return ret;
+}
+
+int RunModeIpsIPFWWorker(void)
+{
+ SCEnter();
+ int ret = 0;
+#ifdef IPFW
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ LiveDeviceHasNoStats();
+
+ ret = RunModeSetIPSWorker(IPFWGetThread,
+ "ReceiveIPFW",
+ "VerdictIPFW",
+ "DecodeIPFW");
+#endif /* IPFW */
+ return ret;
+}
diff --git a/framework/src/suricata/src/runmode-ipfw.h b/framework/src/suricata/src/runmode-ipfw.h
new file mode 100644
index 00000000..5c8345b1
--- /dev/null
+++ b/framework/src/suricata/src/runmode-ipfw.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_IPFW_H__
+#define __RUNMODE_IPFW_H__
+
+int RunModeIpsIPFWAutoFp(void);
+int RunModeIpsIPFWWorker(void);
+void RunModeIpsIPFWRegister(void);
+const char *RunModeIpsIPFWGetDefaultMode(void);
+
+#endif /* __RUNMODE_IPFW_H__ */
diff --git a/framework/src/suricata/src/runmode-napatech.c b/framework/src/suricata/src/runmode-napatech.c
new file mode 100644
index 00000000..7c161ad8
--- /dev/null
+++ b/framework/src/suricata/src/runmode-napatech.c
@@ -0,0 +1,235 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author nPulse Technologies, LLC.
+ * \author Matt Keeler <mk@npulsetech.com>
+ */
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-runmodes.h"
+#include "util-device.h"
+
+#include "runmode-napatech.h"
+
+// need NapatechStreamDevConf structure
+#include "source-napatech.h"
+
+#define NT_RUNMODE_AUTOFP 1
+#define NT_RUNMODE_WORKERS 2
+
+static const char *default_mode = NULL;
+#ifdef HAVE_NAPATECH
+static int num_configured_streams = 0;
+#endif
+
+const char *RunModeNapatechGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeNapatechRegister(void)
+{
+#ifdef HAVE_NAPATECH
+ default_mode = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_NAPATECH, "autofp",
+ "Multi threaded Napatech mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread instead of any detect thread",
+ RunModeNapatechAutoFp);
+ RunModeRegisterNewRunMode(RUNMODE_NAPATECH, "workers",
+ "Workers Napatech mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeNapatechWorkers);
+ return;
+#endif
+}
+
+#ifdef HAVE_NAPATECH
+int NapatechRegisterDeviceStreams()
+{
+ NtInfoStream_t info_stream;
+ NtInfo_t info;
+ char error_buf[100];
+ int status;
+ int i;
+ char live_dev_buf[9];
+ int use_all_streams;
+ ConfNode *ntstreams;
+ ConfNode *stream_id;
+
+ if (ConfGetBool("napatech.use-all-streams", &use_all_streams) == 0)
+ {
+ SCLogError(SC_ERR_RUNMODE, "Failed retrieving napatech.use-all-streams from Conf");
+ exit(EXIT_FAILURE);
+ }
+
+ if (use_all_streams)
+ {
+ SCLogInfo("Using All Napatech Streams");
+ // When using the default streams we need to query the service for a list of all configured
+ if ((status = NT_InfoOpen(&info_stream, "SuricataStreamInfo")) != NT_SUCCESS)
+ {
+ NT_ExplainError(status, error_buf, sizeof(error_buf) -1);
+ SCLogError(SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED, "NT_InfoOpen failed: %s", error_buf);
+ return -1;
+ }
+
+ info.cmd = NT_INFO_CMD_READ_STREAM;
+ if ((status = NT_InfoRead(info_stream, &info)) != NT_SUCCESS)
+ {
+ NT_ExplainError(status, error_buf, sizeof(error_buf) -1);
+ SCLogError(SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED, "NT_InfoRead failed: %s", error_buf);
+ return -1;
+ }
+
+ num_configured_streams = info.u.stream.data.count;
+ for (i = 0; i < num_configured_streams; i++)
+ {
+ // The Stream IDs do not have to be sequential
+ snprintf(live_dev_buf, sizeof(live_dev_buf), "nt%d", info.u.stream.data.streamIDList[i]);
+ LiveRegisterDevice(live_dev_buf);
+ }
+
+ if ((status = NT_InfoClose(info_stream)) != NT_SUCCESS)
+ {
+ NT_ExplainError(status, error_buf, sizeof(error_buf) -1);
+ SCLogError(SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED, "NT_InfoClose failed: %s", error_buf);
+ return -1;
+ }
+ }
+ else
+ {
+ SCLogInfo("Using Selected Napatech Streams");
+ // When not using the default streams we need to parse the array of streams from the conf
+ if ((ntstreams = ConfGetNode("napatech.streams")) == NULL)
+ {
+ SCLogError(SC_ERR_RUNMODE, "Failed retrieving napatech.streams from Conf");
+ exit(EXIT_FAILURE);
+ }
+
+ // Loop through all stream numbers in the array and register the devices
+ TAILQ_FOREACH(stream_id, &ntstreams->head, next)
+ {
+ if (stream_id == NULL)
+ {
+ SCLogError(SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED, "Couldn't Parse Stream Configuration");
+ exit(EXIT_FAILURE);
+ }
+ num_configured_streams++;
+
+ snprintf(live_dev_buf, sizeof(live_dev_buf), "nt%d", atoi(stream_id->val));
+ LiveRegisterDevice(live_dev_buf);
+ }
+ }
+ return 0;
+}
+
+void *NapatechConfigParser(const char *device)
+{
+ // Expect device to be of the form nt%d where %d is the stream id to use
+ int dev_len = strlen(device);
+ struct NapatechStreamDevConf *conf = SCMalloc(sizeof(struct NapatechStreamDevConf));
+ if (unlikely(conf == NULL))
+ return NULL;
+ if (dev_len < 3 || dev_len > 5)
+ {
+ SCLogError(SC_ERR_NAPATECH_PARSE_CONFIG, "Could not parse config for device: %s - invalid length", device);
+ return NULL;
+ }
+
+ // device+5 is a pointer to the beginning of the stream id after the constant nt portion
+ conf->stream_id = atoi(device+2);
+
+ // Set the host buffer allowance for this stream
+ // Right now we just look at the global default - there is no per-stream hba configuration
+ if (ConfGetInt("napatech.hba", &conf->hba) == 0)
+ conf->hba = -1;
+
+ return (void *) conf;
+}
+
+int NapatechGetThreadsCount(void *conf __attribute__((unused))) {
+ // No matter which live device it is there is no reason to ever use more than 1 thread
+ // 2 or more thread would cause packet duplication
+ return 1;
+}
+
+static int NapatechInit(int runmode)
+{
+ int ret;
+ char errbuf[100];
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ /* Initialize the API and check version compatibility */
+ if ((ret = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
+ NT_ExplainError(ret, errbuf, sizeof(errbuf));
+ SCLogError(SC_ERR_NAPATECH_INIT_FAILED ,"NT_Init failed. Code 0x%X = %s", ret, errbuf);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = NapatechRegisterDeviceStreams();
+ if (ret < 0 || num_configured_streams <= 0) {
+ SCLogError(SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED, "Unable to setup up Napatech Streams");
+ exit(EXIT_FAILURE);
+ }
+
+ switch(runmode) {
+ case NT_RUNMODE_AUTOFP:
+ ret = RunModeSetLiveCaptureAutoFp(NapatechConfigParser, NapatechGetThreadsCount,
+ "NapatechStream", "NapatechDecode",
+ "RxNT", NULL);
+ break;
+ case NT_RUNMODE_WORKERS:
+ ret = RunModeSetLiveCaptureWorkers(NapatechConfigParser, NapatechGetThreadsCount,
+ "NapatechStream", "NapatechDecode",
+ "RxNT", NULL);
+ break;
+ default:
+ ret = -1;
+ }
+
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+int RunModeNapatechAutoFp(void)
+{
+ return NapatechInit(NT_RUNMODE_AUTOFP);
+}
+
+int RunModeNapatechWorkers(void)
+{
+ return NapatechInit(NT_RUNMODE_WORKERS);
+}
+
+#endif
diff --git a/framework/src/suricata/src/runmode-napatech.h b/framework/src/suricata/src/runmode-napatech.h
new file mode 100644
index 00000000..d10b4063
--- /dev/null
+++ b/framework/src/suricata/src/runmode-napatech.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \autor nPulse Technologies, LLC.
+ * \author Matt Keeler <mk@npulsetech.com>
+ */
+
+#ifndef __RUNMODE_NAPATECH_H__
+#define __RUNMODE_NAPATECH_H__
+
+#ifdef HAVE_NAPATECH
+#include <nt.h>
+#endif
+
+int RunModeNapatechAutoFp(void);
+int RunModeNapatechWorkers(void);
+void RunModeNapatechRegister(void);
+const char *RunModeNapatechGetDefaultMode(void);
+
+#endif /* __RUNMODE_NAPATECH_H__ */
diff --git a/framework/src/suricata/src/runmode-netmap.c b/framework/src/suricata/src/runmode-netmap.c
new file mode 100644
index 00000000..3289275e
--- /dev/null
+++ b/framework/src/suricata/src/runmode-netmap.c
@@ -0,0 +1,459 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+* \ingroup netmap
+*
+* @{
+*/
+
+/**
+* \file
+*
+* \author Aleksey Katargin <gureedo@gmail.com>
+*
+* Netmap runmode
+*
+*/
+
+#include "suricata-common.h"
+#include "config.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-netmap.h"
+#include "log-httplog.h"
+#include "output.h"
+#include "detect-engine-mpm.h"
+
+#include "alert-fastlog.h"
+#include "alert-prelude.h"
+#include "alert-unified2-alert.h"
+#include "alert-debuglog.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-ioctl.h"
+
+#include "source-netmap.h"
+
+extern int max_pending_packets;
+
+static const char *default_mode_workers = NULL;
+
+const char *RunModeNetmapGetDefaultMode(void)
+{
+ return default_mode_workers;
+}
+
+void RunModeIdsNetmapRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_NETMAP, "single",
+ "Single threaded netmap mode",
+ RunModeIdsNetmapSingle);
+ RunModeRegisterNewRunMode(RUNMODE_NETMAP, "workers",
+ "Workers netmap mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsNetmapWorkers);
+ default_mode_workers = "workers";
+ RunModeRegisterNewRunMode(RUNMODE_NETMAP, "autofp",
+ "Multi threaded netmap mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread.",
+ RunModeIdsNetmapAutoFp);
+ return;
+}
+
+#ifdef HAVE_NETMAP
+
+static void NetmapDerefConfig(void *conf)
+{
+ NetmapIfaceConfig *pfp = (NetmapIfaceConfig *)conf;
+ /* config is used only once but cost of this low. */
+ if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
+ SCFree(pfp);
+ }
+}
+
+/**
+* \brief extract information from config file
+*
+* The returned structure will be freed by the thread init function.
+* This is thus necessary to or copy the structure before giving it
+* to thread or to reparse the file for each thread (and thus have
+* new structure.
+*
+* \return a NetmapIfaceConfig corresponding to the interface name
+*/
+static void *ParseNetmapConfig(const char *iface_name)
+{
+ char *threadsstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *netmap_node;
+ NetmapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
+ char *tmpctype;
+ char *copymodestr;
+ int boolval;
+ char *bpf_filter = NULL;
+ char *out_iface = NULL;
+
+ if (unlikely(aconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface_name == NULL) {
+ SCFree(aconf);
+ return NULL;
+ }
+
+ memset(aconf, 0, sizeof(*aconf));
+ aconf->DerefFunc = NetmapDerefConfig;
+ aconf->threads = 1;
+ aconf->promisc = 1;
+ aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ aconf->copy_mode = NETMAP_COPY_MODE_NONE;
+ strlcpy(aconf->iface_name, iface_name, sizeof(aconf->iface_name));
+ SC_ATOMIC_INIT(aconf->ref);
+ (void) SC_ATOMIC_ADD(aconf->ref, 1);
+
+ strlcpy(aconf->iface, aconf->iface_name, sizeof(aconf->iface));
+ if (aconf->iface[0]) {
+ size_t len = strlen(aconf->iface);
+ if (aconf->iface[len-1] == '+') {
+ aconf->iface[len-1] = '\0';
+ aconf->iface_sw = 1;
+ }
+ }
+
+ if (ConfGet("bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ aconf->bpf_filter = bpf_filter;
+ SCLogInfo("Going to use command-line provided bpf filter '%s'",
+ aconf->bpf_filter);
+ }
+ }
+
+ /* Find initial node */
+ netmap_node = ConfGetNode("netmap");
+ if (netmap_node == NULL) {
+ SCLogInfo("Unable to find netmap config using default value");
+ return aconf;
+ }
+
+ if_root = ConfNodeLookupKeyValue(netmap_node, "interface", aconf->iface_name);
+
+ if_default = ConfNodeLookupKeyValue(netmap_node, "interface", "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("Unable to find netmap config for "
+ "interface \"%s\" or \"default\", using default value",
+ aconf->iface_name);
+ return aconf;
+ }
+
+ /* If there is no setting for current interface use default one as main iface */
+ if (if_root == NULL) {
+ if_root = if_default;
+ if_default = NULL;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
+ aconf->threads = 1;
+ } else {
+ if (strcmp(threadsstr, "auto") == 0) {
+ aconf->threads = GetIfaceRSSQueuesNum(aconf->iface);
+ } else {
+ aconf->threads = (uint8_t)atoi(threadsstr);
+ }
+ }
+
+ if (aconf->threads <= 0) {
+ aconf->threads = 1;
+ }
+ if (aconf->threads) {
+ SCLogInfo("Using %d threads for interface %s", aconf->threads,
+ aconf->iface_name);
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
+ if (strlen(out_iface) > 0) {
+ aconf->out_iface_name = out_iface;
+ }
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
+ if (aconf->out_iface_name == NULL) {
+ SCLogInfo("Copy mode activated but no destination"
+ " iface. Disabling feature");
+ } else if (strlen(copymodestr) <= 0) {
+ aconf->out_iface_name = NULL;
+ } else if (strcmp(copymodestr, "ips") == 0) {
+ SCLogInfo("Netmap IPS mode activated %s->%s",
+ aconf->iface_name,
+ aconf->out_iface_name);
+ aconf->copy_mode = NETMAP_COPY_MODE_IPS;
+ } else if (strcmp(copymodestr, "tap") == 0) {
+ SCLogInfo("Netmap TAP mode activated %s->%s",
+ aconf->iface_name,
+ aconf->out_iface_name);
+ aconf->copy_mode = NETMAP_COPY_MODE_TAP;
+ } else {
+ SCLogInfo("Invalid mode (not in tap, ips)");
+ }
+ }
+
+ if (aconf->out_iface_name && aconf->out_iface_name[0]) {
+ strlcpy(aconf->out_iface, aconf->out_iface_name,
+ sizeof(aconf->out_iface));
+ size_t len = strlen(aconf->out_iface);
+ if (aconf->out_iface[len-1] == '+') {
+ aconf->out_iface[len-1] = '\0';
+ aconf->out_iface_sw = 1;
+ }
+ }
+
+ SC_ATOMIC_RESET(aconf->ref);
+ (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads);
+
+ /* load netmap bpf filter */
+ /* command line value has precedence */
+ if (ConfGet("bpf-filter", &bpf_filter) != 1) {
+ if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ aconf->bpf_filter = bpf_filter;
+ SCLogInfo("Going to use bpf filter %s", aconf->bpf_filter);
+ }
+ }
+ }
+
+ (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
+ if (boolval) {
+ SCLogInfo("Disabling promiscuous mode on iface %s", aconf->iface);
+ aconf->promisc = 0;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
+ if (strcmp(tmpctype, "auto") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (strcmp(tmpctype, "yes") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(tmpctype, "no") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", aconf->iface_name);
+ }
+ }
+
+ return aconf;
+}
+
+static int NetmapConfigGeThreadsCount(void *conf)
+{
+ NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)conf;
+ return aconf->threads;
+}
+
+int NetmapRunModeIsIPS()
+{
+ int nlive = LiveGetDeviceCount();
+ int ldev;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *netmap_node;
+ int has_ips = 0;
+ int has_ids = 0;
+
+ /* Find initial node */
+ netmap_node = ConfGetNode("netmap");
+ if (netmap_node == NULL) {
+ return 0;
+ }
+
+ if_default = ConfNodeLookupKeyValue(netmap_node, "interface", "default");
+
+ for (ldev = 0; ldev < nlive; ldev++) {
+ char *live_dev = LiveGetDeviceName(ldev);
+ if (live_dev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ char *copymodestr = NULL;
+ if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev);
+
+ if (if_root == NULL) {
+ if (if_default == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = if_default;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
+ if (strcmp(copymodestr, "ips") == 0) {
+ has_ips = 1;
+ } else {
+ has_ids = 1;
+ }
+ } else {
+ has_ids = 1;
+ }
+ }
+
+ if (has_ids && has_ips) {
+ SCLogInfo("Netmap mode using IPS and IDS mode");
+ for (ldev = 0; ldev < nlive; ldev++) {
+ char *live_dev = LiveGetDeviceName(ldev);
+ if (live_dev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev);
+ char *copymodestr = NULL;
+
+ if (if_root == NULL) {
+ if (if_default == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
+ return 0;
+ }
+ if_root = if_default;
+ }
+
+ if (! ((ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) &&
+ (strcmp(copymodestr, "ips") == 0))) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Netmap IPS mode used and interface '%s' is in IDS or TAP mode. "
+ "Sniffing '%s' but expect bad result as stream-inline is activated.",
+ live_dev, live_dev);
+ }
+ }
+ }
+
+ return has_ips;
+}
+
+#endif // #ifdef HAVE_NETMAP
+
+int RunModeIdsNetmapAutoFp(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NETMAP
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ (void)ConfGet("netmap.live-interface", &live_dev);
+
+ SCLogDebug("live_dev %s", live_dev);
+
+ ret = RunModeSetLiveCaptureAutoFp(
+ ParseNetmapConfig,
+ NetmapConfigGeThreadsCount,
+ "ReceiveNetmap",
+ "DecodeNetmap", "RxNetmap",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNetmapAutoFp initialised");
+#endif /* HAVE_NETMAP */
+
+ SCReturnInt(0);
+}
+
+/**
+* \brief Single thread version of the netmap processing.
+*/
+int RunModeIdsNetmapSingle(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NETMAP
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void)ConfGet("netmap.live-interface", &live_dev);
+
+ ret = RunModeSetLiveCaptureSingle(
+ ParseNetmapConfig,
+ NetmapConfigGeThreadsCount,
+ "ReceiveNetmap",
+ "DecodeNetmap", "NetmapPkt",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNetmapSingle initialised");
+
+#endif /* HAVE_NETMAP */
+ SCReturnInt(0);
+}
+
+/**
+* \brief Workers version of the netmap processing.
+*
+* Start N threads with each thread doing all the work.
+*
+*/
+int RunModeIdsNetmapWorkers(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NETMAP
+ int ret;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void)ConfGet("netmap.live-interface", &live_dev);
+
+ ret = RunModeSetLiveCaptureWorkers(
+ ParseNetmapConfig,
+ NetmapConfigGeThreadsCount,
+ "ReceiveNetmap",
+ "DecodeNetmap", "NetmapPkt",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNetmapWorkers initialised");
+
+#endif /* HAVE_NETMAP */
+ SCReturnInt(0);
+}
+
+/**
+* @}
+*/
diff --git a/framework/src/suricata/src/runmode-netmap.h b/framework/src/suricata/src/runmode-netmap.h
new file mode 100644
index 00000000..988c349a
--- /dev/null
+++ b/framework/src/suricata/src/runmode-netmap.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+*
+* \author Aleksey Katargin <gureedo@gmail.com>
+*/
+
+#ifndef __RUNMODE_NETMAP_H__
+#define __RUNMODE_NETMAP_H__
+
+int RunModeIdsNetmapSingle(void);
+int RunModeIdsNetmapAutoFp(void);
+int RunModeIdsNetmapWorkers(void);
+void RunModeIdsNetmapRegister(void);
+const char *RunModeNetmapGetDefaultMode(void);
+int NetmapRunModeIsIPS();
+
+#endif /* __RUNMODE_NETMAP_H__ */
diff --git a/framework/src/suricata/src/runmode-nflog.c b/framework/src/suricata/src/runmode-nflog.c
new file mode 100644
index 00000000..9c08f9ae
--- /dev/null
+++ b/framework/src/suricata/src/runmode-nflog.c
@@ -0,0 +1,254 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ */
+#include "suricata-common.h"
+#include "config.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-nflog.h"
+
+#include "util-debug.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-misc.h"
+
+#include "source-nflog.h"
+
+static const char *default_mode = NULL;
+
+const char *RunModeIdsNflogGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeIdsNflogRegister(void)
+{
+ default_mode = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_NFLOG, "autofp",
+ "Multi threaded nflog mode",
+ RunModeIdsNflogAutoFp);
+ RunModeRegisterNewRunMode(RUNMODE_NFLOG, "single",
+ "Single threaded nflog mode",
+ RunModeIdsNflogSingle);
+ RunModeRegisterNewRunMode(RUNMODE_NFLOG, "workers",
+ "Workers nflog mode",
+ RunModeIdsNflogWorkers);
+ return;
+}
+
+
+static void NflogDerefConfig(void *data)
+{
+ NflogGroupConfig *nflogconf = (NflogGroupConfig *)data;
+ SCFree(nflogconf);
+}
+
+void *ParseNflogConfig(const char *group)
+{
+ ConfNode *group_root;
+ ConfNode *group_default = NULL;
+ ConfNode *nflog_node;
+ NflogGroupConfig *nflogconf = SCMalloc(sizeof(*nflogconf));
+ intmax_t bufsize;
+ intmax_t bufsize_max;
+ intmax_t qthreshold;
+ intmax_t qtimeout;
+ int boolval;
+
+ if (unlikely(nflogconf == NULL))
+ return NULL;
+
+ if (group == NULL) {
+ SCFree(nflogconf);
+ return NULL;
+ }
+
+ nflogconf->DerefFunc = NflogDerefConfig;
+ nflog_node = ConfGetNode("nflog");
+
+ if (nflog_node == NULL) {
+ SCLogInfo("Unable to find nflog config using default value");
+ return nflogconf;
+ }
+
+ group_root = ConfNodeLookupKeyValue(nflog_node, "group", group);
+
+ group_default = ConfNodeLookupKeyValue(nflog_node, "group", "default");
+
+ if (group_root == NULL && group_default == NULL) {
+ SCLogInfo("Unable to find nflog config for "
+ "group \"%s\" or \"default\", using default value",
+ group);
+ return nflogconf;
+ }
+
+ nflogconf->nful_overrun_warned = 0;
+ strlcpy(nflogconf->numgroup, group, sizeof(nflogconf->numgroup));
+
+ if (ParseSizeStringU16(group, &nflogconf->group) < 0) {
+ SCLogError(SC_ERR_NFLOG_GROUP, "NFLOG's group number invalid.");
+ exit(EXIT_FAILURE);
+ }
+
+ boolval = ConfGetChildValueIntWithDefault(group_root, group_default,
+ "buffer-size", &bufsize);
+
+ if (boolval)
+ nflogconf->nlbufsiz = bufsize;
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid buffer-size value");
+ SCFree(nflogconf);
+ return NULL;
+ }
+
+ boolval = ConfGetChildValueIntWithDefault(group_root, group_default,
+ "max-size", &bufsize_max);
+
+ if (boolval)
+ nflogconf->nlbufsiz_max = bufsize_max;
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid max-size value");
+ SCFree(nflogconf);
+ return NULL;
+ }
+
+ if (nflogconf->nlbufsiz > nflogconf->nlbufsiz_max) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "buffer-size value larger "
+ "than max-size value, adjusting buffer-size");
+ nflogconf->nlbufsiz = nflogconf->nlbufsiz_max;
+ }
+
+ boolval = ConfGetChildValueIntWithDefault(group_root, group_default,
+ "qthreshold", &qthreshold);
+
+ if (boolval)
+ nflogconf->qthreshold = qthreshold;
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid qthreshold value");
+ SCFree(nflogconf);
+ return NULL;
+ }
+
+ boolval = ConfGetChildValueIntWithDefault(group_root, group_default,
+ "qtimeout", &qtimeout);
+
+ if (boolval)
+ nflogconf->qtimeout = qtimeout;
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid qtimeout value");
+ SCFree(nflogconf);
+ return NULL;
+ }
+
+ return nflogconf;
+}
+
+int NflogConfigGeThreadsCount(void *conf)
+{
+ /* for each nflog group there is no reason to use more than 1 thread */
+ return 1;
+}
+
+int RunModeIdsNflogAutoFp(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NFLOG
+ int ret = 0;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureAutoFp(ParseNflogConfig,
+ NflogConfigGeThreadsCount,
+ "ReceiveNFLOG",
+ "DecodeNFLOG",
+ "RecvNFLOG",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNflogAutoFp initialised");
+#endif /* HAVE_NFLOG */
+
+ SCReturnInt(0);
+}
+
+int RunModeIdsNflogSingle(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NFLOG
+ int ret = 0;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureSingle(ParseNflogConfig,
+ NflogConfigGeThreadsCount,
+ "ReceiveNFLOG",
+ "DecodeNFLOG",
+ "RecvNFLOG",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNflogSingle initialised");
+#endif /* HAVE_NFLOG */
+
+ SCReturnInt(0);
+}
+
+int RunModeIdsNflogWorkers(void)
+{
+ SCEnter();
+
+#ifdef HAVE_NFLOG
+ int ret = 0;
+ char *live_dev = NULL;
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ ret = RunModeSetLiveCaptureWorkers(ParseNflogConfig,
+ NflogConfigGeThreadsCount,
+ "ReceiveNFLOG",
+ "DecodeNFLOG",
+ "RecvNFLOG",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsNflogWorkers initialised");
+#endif /* HAVE_NFLOG */
+
+ SCReturnInt(0);
+}
diff --git a/framework/src/suricata/src/runmode-nflog.h b/framework/src/suricata/src/runmode-nflog.h
new file mode 100644
index 00000000..9c70c693
--- /dev/null
+++ b/framework/src/suricata/src/runmode-nflog.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ */
+#ifndef __RUNMODE_NFLOG_H__
+#define __RUNMODE_NFLOG_H__
+
+int RunModeIdsNflogAutoFp(void);
+int RunModeIdsNflogSingle(void);
+int RunModeIdsNflogWorkers(void);
+void RunModeIdsNflogRegister(void);
+const char *RunModeIdsNflogGetDefaultMode(void);
+
+#endif /* __RUNMODE_NFLOG_H__ */
diff --git a/framework/src/suricata/src/runmode-nfq.c b/framework/src/suricata/src/runmode-nfq.c
new file mode 100644
index 00000000..67fa5bcb
--- /dev/null
+++ b/framework/src/suricata/src/runmode-nfq.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Handling of NFQ runmodes.
+ */
+
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-nfq.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-runmodes.h"
+#include "util-device.h"
+
+static const char *default_mode;
+
+const char *RunModeIpsNFQGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeIpsNFQRegister(void)
+{
+ default_mode = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_NFQ, "autofp",
+ "Multi threaded NFQ IPS mode with respect to flow",
+ RunModeIpsNFQAutoFp);
+
+ RunModeRegisterNewRunMode(RUNMODE_NFQ, "workers",
+ "Multi queue NFQ IPS mode with one thread per queue",
+ RunModeIpsNFQWorker);
+ return;
+}
+
+int RunModeIpsNFQAutoFp(void)
+{
+ SCEnter();
+ int ret = 0;
+#ifdef NFQ
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ LiveDeviceHasNoStats();
+
+ ret = RunModeSetIPSAutoFp(NFQGetThread,
+ "ReceiveNFQ",
+ "VerdictNFQ",
+ "DecodeNFQ");
+#endif /* NFQ */
+ return ret;
+}
+
+int RunModeIpsNFQWorker(void)
+{
+ SCEnter();
+ int ret = 0;
+#ifdef NFQ
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ LiveDeviceHasNoStats();
+
+ ret = RunModeSetIPSWorker(NFQGetThread,
+ "ReceiveNFQ",
+ "VerdictNFQ",
+ "DecodeNFQ");
+#endif /* NFQ */
+ return ret;
+}
diff --git a/framework/src/suricata/src/runmode-nfq.h b/framework/src/suricata/src/runmode-nfq.h
new file mode 100644
index 00000000..6ad88014
--- /dev/null
+++ b/framework/src/suricata/src/runmode-nfq.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_NFQ_H__
+#define __RUNMODE_NFQ_H__
+
+int RunModeIpsNFQAutoFp(void);
+int RunModeIpsNFQWorker(void);
+void RunModeIpsNFQRegister(void);
+const char *RunModeIpsNFQGetDefaultMode(void);
+
+#endif /* __RUNMODE_NFQ_H__ */
diff --git a/framework/src/suricata/src/runmode-pcap-file.c b/framework/src/suricata/src/runmode-pcap-file.c
new file mode 100644
index 00000000..fab14639
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pcap-file.c
@@ -0,0 +1,281 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-pcap-file.h"
+#include "output.h"
+
+#include "detect-engine.h"
+#include "source-pcap-file.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+
+#include "util-runmodes.h"
+
+static const char *default_mode = NULL;
+
+const char *RunModeFilePcapGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+void RunModeFilePcapRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_PCAP_FILE, "single",
+ "Single threaded pcap file mode",
+ RunModeFilePcapSingle);
+ default_mode = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_PCAP_FILE, "autofp",
+ "Multi threaded pcap file mode. Packets from "
+ "each flow are assigned to a single detect thread, "
+ "unlike \"pcap-file-auto\" where packets from "
+ "the same flow can be processed by any detect "
+ "thread",
+ RunModeFilePcapAutoFp);
+
+ return;
+}
+
+/**
+ * \brief Single thread version of the Pcap file processing.
+ */
+int RunModeFilePcapSingle(void)
+{
+ char *file = NULL;
+ if (ConfGet("pcap-file.file", &file) == 0) {
+ SCLogError(SC_ERR_RUNMODE, "Failed retrieving pcap-file from Conf");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeInitialize();
+ TimeModeSetOffline();
+
+ PcapFileGlobalInit();
+
+ /* create the threads */
+ ThreadVars *tv = TmThreadCreatePacketHandler("PcapFile",
+ "packetpool", "packetpool",
+ "packetpool", "packetpool",
+ "pktacqloop");
+ if (tv == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "threading setup failed");
+ exit(EXIT_FAILURE);
+ }
+
+ TmModule *tm_module = TmModuleGetByName("ReceivePcapFile");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName failed for ReceivePcap");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, file);
+
+ tm_module = TmModuleGetByName("DecodePcapFile");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName DecodePcap failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+ }
+
+ SetupOutputs(tv);
+
+ TmThreadSetCPU(tv, DETECT_CPU_SET);
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief RunModeFilePcapAutoFp set up the following thread packet handlers:
+ * - Receive thread (from pcap file)
+ * - Decode thread
+ * - Stream thread
+ * - Detect: If we have only 1 cpu, it will setup one Detect thread
+ * If we have more than one, it will setup num_cpus - 1
+ * starting from the second cpu available.
+ * - Outputs thread
+ * By default the threads will use the first cpu available
+ * except the Detection threads if we have more than one cpu.
+ *
+ * \retval 0 If all goes well. (If any problem is detected the engine will
+ * exit()).
+ */
+int RunModeFilePcapAutoFp(void)
+{
+ SCEnter();
+ char tname[TM_THREAD_NAME_MAX];
+ char qname[TM_QUEUE_NAME_MAX];
+ uint16_t cpu = 0;
+ char *queues = NULL;
+ int thread;
+
+ RunModeInitialize();
+ RunmodeSetFlowStreamAsync();
+
+ char *file = NULL;
+ if (ConfGet("pcap-file.file", &file) == 0) {
+ SCLogError(SC_ERR_RUNMODE, "Failed retrieving pcap-file from Conf");
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("file %s", file);
+
+ TimeModeSetOffline();
+
+ PcapFileGlobalInit();
+
+ /* Available cpus */
+ uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
+
+ /* start with cpu 1 so that if we're creating an odd number of detect
+ * threads we're not creating the most on CPU0. */
+ if (ncpus > 0)
+ cpu = 1;
+
+ /* always create at least one thread */
+ int thread_max = TmThreadGetNbThreads(DETECT_CPU_SET);
+ if (thread_max == 0)
+ thread_max = ncpus * threading_detect_ratio;
+ if (thread_max < 1)
+ thread_max = 1;
+
+ queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
+ if (queues == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "RunmodeAutoFpCreatePickupQueuesString failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* create the threads */
+ ThreadVars *tv_receivepcap =
+ TmThreadCreatePacketHandler("ReceivePcapFile",
+ "packetpool", "packetpool",
+ queues, "flow",
+ "pktacqloop");
+ SCFree(queues);
+
+ if (tv_receivepcap == NULL) {
+ SCLogError(SC_ERR_FATAL, "threading setup failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName("ReceivePcapFile");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName failed for ReceivePcap");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receivepcap, tm_module, file);
+
+ tm_module = TmModuleGetByName("DecodePcapFile");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName DecodePcap failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receivepcap, tm_module, NULL);
+
+ TmThreadSetCPU(tv_receivepcap, RECEIVE_CPU_SET);
+
+ if (TmThreadSpawn(tv_receivepcap) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+
+ for (thread = 0; thread < thread_max; thread++) {
+ snprintf(tname, sizeof(tname), "Detect%d", thread+1);
+ snprintf(qname, sizeof(qname), "pickup%d", thread+1);
+
+ SCLogDebug("tname %s, qname %s", tname, qname);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "failed to strdup thread name");
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("Assigning %s affinity to cpu %u", thread_name, cpu);
+
+ ThreadVars *tv_detect_ncpu =
+ TmThreadCreatePacketHandler(thread_name,
+ qname, "flow",
+ "packetpool", "packetpool",
+ "varslot");
+ if (tv_detect_ncpu == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+ }
+
+ char *thread_group_name = SCStrdup("Detect");
+ if (unlikely(thread_group_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ tv_detect_ncpu->thread_group_name = thread_group_name;
+
+ /* add outputs as well */
+ SetupOutputs(tv_detect_ncpu);
+
+ TmThreadSetCPU(tv_detect_ncpu, DETECT_CPU_SET);
+
+ if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((cpu + 1) == ncpus)
+ cpu = 0;
+ else
+ cpu++;
+ }
+
+ return 0;
+}
diff --git a/framework/src/suricata/src/runmode-pcap-file.h b/framework/src/suricata/src/runmode-pcap-file.h
new file mode 100644
index 00000000..52cab022
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pcap-file.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_PCAP_FILE_H__
+#define __RUNMODE_PCAP_FILE_H__
+
+int RunModeFilePcapSingle(void);
+int RunModeFilePcapAutoFp(void);
+void RunModeFilePcapRegister(void);
+const char *RunModeFilePcapGetDefaultMode(void);
+
+#endif /* __RUNMODE_PCAP_FILE_H__ */
diff --git a/framework/src/suricata/src/runmode-pcap.c b/framework/src/suricata/src/runmode-pcap.c
new file mode 100644
index 00000000..6561c1c8
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pcap.c
@@ -0,0 +1,328 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-pcap.h"
+#include "log-httplog.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-atomic.h"
+#include "util-misc.h"
+
+static const char *default_mode = NULL;
+
+const char *RunModeIdsGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+int RunModeIdsPcapWorkers(void);
+
+void RunModeIdsPcapRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "single",
+ "Single threaded pcap live mode",
+ RunModeIdsPcapSingle);
+ default_mode = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "autofp",
+ "Multi threaded pcap live mode. Packets from "
+ "each flow are assigned to a single detect thread, "
+ "unlike \"pcap_live_auto\" where packets from "
+ "the same flow can be processed by any detect "
+ "thread",
+ RunModeIdsPcapAutoFp);
+ RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "workers",
+ "Workers pcap live mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsPcapWorkers);
+
+ return;
+}
+
+void PcapDerefConfig(void *conf)
+{
+ PcapIfaceConfig *pfp = (PcapIfaceConfig *)conf;
+ /* Pcap config is used only once but cost of this low. */
+ if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
+ SCFree(pfp);
+ }
+}
+
+
+void *ParsePcapConfig(const char *iface)
+{
+ char *threadsstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *pcap_node;
+ PcapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
+ char *tmpbpf;
+ char *tmpctype;
+ intmax_t value;
+ int promisc = 0;
+ intmax_t snaplen = 0;
+
+ if (unlikely(aconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(aconf);
+ return NULL;
+ }
+
+ memset(aconf, 0x00, sizeof(*aconf));
+ strlcpy(aconf->iface, iface, sizeof(aconf->iface));
+
+ aconf->buffer_size = 0;
+ /* If set command line option has precedence over config */
+ if ((ConfGetInt("pcap.buffer-size", &value)) == 1) {
+ SCLogInfo("Pcap will use %d buffer size", (int)value);
+ aconf->buffer_size = value;
+ }
+
+ aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ aconf->bpf_filter = NULL;
+ if ((ConfGet("bpf-filter", &tmpbpf)) == 1) {
+ aconf->bpf_filter = tmpbpf;
+ }
+
+ SC_ATOMIC_INIT(aconf->ref);
+ aconf->DerefFunc = PcapDerefConfig;
+ aconf->threads = 1;
+
+ /* Find initial node */
+ pcap_node = ConfGetNode("pcap");
+ if (pcap_node == NULL) {
+ SCLogInfo("Unable to find pcap config using default value");
+ return aconf;
+ }
+
+ if_root = ConfNodeLookupKeyValue(pcap_node, "interface", iface);
+
+ if_default = ConfNodeLookupKeyValue(pcap_node, "interface", "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("Unable to find pcap config for "
+ "interface %s, using default value",
+ iface);
+ return aconf;
+ }
+
+ /* If there is no setting for current interface use default one as main iface */
+ if (if_root == NULL) {
+ if_root = if_default;
+ if_default = NULL;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
+ aconf->threads = 1;
+ } else {
+ if (threadsstr != NULL) {
+ aconf->threads = (uint8_t)atoi(threadsstr);
+ }
+ }
+ if (aconf->threads == 0) {
+ aconf->threads = 1;
+ }
+ (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads);
+
+ if (aconf->buffer_size == 0) {
+ char *s_limit = NULL;
+ int ret;
+ ret = ConfGetChildValueWithDefault(if_root, if_default, "buffer-size", &s_limit);
+ if (ret == 1 && s_limit) {
+ uint64_t bsize = 0;
+
+ if (ParseSizeStringU64(s_limit, &bsize) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to parse pcap buffer size: %s",
+ s_limit);
+ } else {
+ /* the string 2gb returns 2147483648 which is 1 to high
+ * for a int. */
+ if (bsize == (uint64_t)((uint64_t)INT_MAX + (uint64_t)1))
+ bsize = (uint64_t)INT_MAX;
+
+ if (bsize > INT_MAX) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to set pcap buffer size: 2gb max. %"PRIu64" > %d", bsize, INT_MAX);
+ } else {
+ aconf->buffer_size = (int)bsize;
+ }
+ }
+ }
+ }
+
+ if (aconf->bpf_filter == NULL) {
+ /* set bpf filter if we have one */
+ if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &tmpbpf) != 1) {
+ SCLogDebug("could not get bpf or none specified");
+ } else {
+ aconf->bpf_filter = tmpbpf;
+ }
+ } else {
+ SCLogInfo("BPF filter set from command line or via old 'bpf-filter' option.");
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
+ if (strcmp(tmpctype, "auto") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (strcmp(tmpctype, "yes") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(tmpctype, "no") == 0) {
+ aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", aconf->iface);
+ }
+ }
+
+ aconf->promisc = LIBPCAP_PROMISC;
+ if (ConfGetChildValueBoolWithDefault(if_root, if_default, "promisc", &promisc) != 1) {
+ SCLogDebug("could not get promisc or none specified");
+ } else {
+ aconf->promisc = promisc;
+ }
+
+ aconf->snaplen = 0;
+ if (ConfGetChildValueIntWithDefault(if_root, if_default, "snaplen", &snaplen) != 1) {
+ SCLogDebug("could not get snaplen or none specified");
+ } else {
+ aconf->snaplen = snaplen;
+ }
+
+
+ return aconf;
+}
+
+int PcapConfigGeThreadsCount(void *conf)
+{
+ PcapIfaceConfig *pfp = (PcapIfaceConfig *)conf;
+ return pfp->threads;
+}
+
+/**
+ * \brief Single thread version of the Pcap live processing.
+ */
+int RunModeIdsPcapSingle(void)
+{
+ int ret;
+ char *live_dev = NULL;
+
+ SCEnter();
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void)ConfGet("pcap.single-pcap-dev", &live_dev);
+
+ ret = RunModeSetLiveCaptureSingle(ParsePcapConfig,
+ PcapConfigGeThreadsCount,
+ "ReceivePcap",
+ "DecodePcap", "PcapLive",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPcapSingle initialised");
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief RunModIdsPcapAutoFp set up the following thread packet handlers:
+ * - Receive thread (from pcap device)
+ * - Decode thread
+ * - Stream thread
+ * - Detect: If we have only 1 cpu, it will setup one Detect thread
+ * If we have more than one, it will setup num_cpus - 1
+ * starting from the second cpu available.
+ * - Outputs thread
+ * By default the threads will use the first cpu available
+ * except the Detection threads if we have more than one cpu.
+ *
+ * \retval 0 If all goes well. (If any problem is detected the engine will
+ * exit()).
+ */
+int RunModeIdsPcapAutoFp(void)
+{
+ int ret;
+ char *live_dev = NULL;
+
+ SCEnter();
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void) ConfGet("pcap.single-pcap-dev", &live_dev);
+
+ ret = RunModeSetLiveCaptureAutoFp(ParsePcapConfig,
+ PcapConfigGeThreadsCount,
+ "ReceivePcap",
+ "DecodePcap", "RxPcap",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPcapAutoFp initialised");
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Workers version of the PCAP LIVE processing.
+ *
+ * Start N threads with each thread doing all the work.
+ *
+ */
+int RunModeIdsPcapWorkers(void)
+{
+ int ret;
+ char *live_dev = NULL;
+ SCEnter();
+
+ RunModeInitialize();
+ TimeModeSetLive();
+
+ (void) ConfGet("pcap.single-pcap-dev", &live_dev);
+
+ ret = RunModeSetLiveCaptureWorkers(ParsePcapConfig,
+ PcapConfigGeThreadsCount,
+ "ReceivePcap",
+ "DecodePcap", "RxPcap",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPcapWorkers initialised");
+
+ SCReturnInt(0);
+}
diff --git a/framework/src/suricata/src/runmode-pcap.h b/framework/src/suricata/src/runmode-pcap.h
new file mode 100644
index 00000000..b68c6ddc
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pcap.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_PCAP_H__
+#define __RUNMODE_PCAP_H__
+
+int RunModeIdsPcapSingle(void);
+int RunModeIdsPcapAutoFp(void);
+void RunModeIdsPcapRegister(void);
+const char *RunModeIdsGetDefaultMode(void);
+
+#endif /* __RUNMODE_PCAP_H__ */
diff --git a/framework/src/suricata/src/runmode-pfring.c b/framework/src/suricata/src/runmode-pfring.c
new file mode 100644
index 00000000..fbbd8c91
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pfring.c
@@ -0,0 +1,521 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-pfring.h"
+#include "source-pfring.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-runmodes.h"
+#include "util-device.h"
+
+static const char *default_mode_autofp = NULL;
+
+
+#define PFRING_CONF_V1 1
+#define PFRING_CONF_V2 2
+
+const char *RunModeIdsPfringGetDefaultMode(void)
+{
+#ifdef HAVE_PFRING
+ return default_mode_autofp;
+#else
+ return NULL;
+#endif
+}
+
+void RunModeIdsPfringRegister(void)
+{
+ default_mode_autofp = "autofp";
+ RunModeRegisterNewRunMode(RUNMODE_PFRING, "autofp",
+ "Multi threaded pfring mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread, unlike \"pfring_auto\" where packets "
+ "from the same flow can be processed by any "
+ "detect thread",
+ RunModeIdsPfringAutoFp);
+ RunModeRegisterNewRunMode(RUNMODE_PFRING, "single",
+ "Single threaded pfring mode",
+ RunModeIdsPfringSingle);
+ RunModeRegisterNewRunMode(RUNMODE_PFRING, "workers",
+ "Workers pfring mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsPfringWorkers);
+ return;
+}
+
+void PfringDerefConfig(void *conf)
+{
+ PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
+ if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
+ if (pfp->bpf_filter) {
+ SCFree(pfp->bpf_filter);
+ }
+ SCFree(pfp);
+ }
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to or copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * If old config system is used, then return the smae parameters
+ * value for each interface.
+ *
+ * \return a PfringIfaceConfig corresponding to the interface name
+ */
+void *OldParsePfringConfig(const char *iface)
+{
+ char *threadsstr = NULL;
+ PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
+ char *tmpclusterid;
+#ifdef HAVE_PFRING
+ char *tmpctype = NULL;
+ cluster_type default_ctype = CLUSTER_ROUND_ROBIN;
+#endif
+
+ if (unlikely(pfconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(pfconf);
+ return NULL;
+ }
+
+ strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
+ pfconf->threads = 1;
+ pfconf->cluster_id = 1;
+#ifdef HAVE_PFRING
+ pfconf->ctype = default_ctype;
+#endif
+ pfconf->DerefFunc = PfringDerefConfig;
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ SC_ATOMIC_INIT(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, 1);
+
+ /* Find initial node */
+ if (ConfGet("pfring.threads", &threadsstr) != 1) {
+ pfconf->threads = 1;
+ } else {
+ if (threadsstr != NULL) {
+ pfconf->threads = (uint8_t)atoi(threadsstr);
+ }
+ }
+ if (pfconf->threads == 0) {
+ pfconf->threads = 1;
+ }
+
+ SC_ATOMIC_RESET(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
+
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("ZC interface detected, not setting cluster-id");
+ }
+ else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not setting cluster-id");
+ } else if (ConfGet("pfring.cluster-id", &tmpclusterid) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,"Could not get cluster-id from config");
+ } else {
+ pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
+ SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
+ }
+
+#ifdef HAVE_PFRING
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("ZC interface detected, not setting cluster type for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not setting cluster type for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if (ConfGet("pfring.cluster-type", &tmpctype) != 1) {
+ SCLogError(SC_ERR_GET_CLUSTER_TYPE_FAILED,"Could not get cluster-type from config");
+ } else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
+ SCLogInfo("Using round-robin cluster mode for PF_RING (iface %s)",
+ pfconf->iface);
+ pfconf->ctype = (cluster_type)tmpctype;
+ } else if (strcmp(tmpctype, "cluster_flow") == 0) {
+ SCLogInfo("Using flow cluster mode for PF_RING (iface %s)",
+ pfconf->iface);
+ pfconf->ctype = (cluster_type)tmpctype;
+ } else {
+ SCLogError(SC_ERR_INVALID_CLUSTER_TYPE,"invalid cluster-type %s",tmpctype);
+ SCFree(pfconf);
+ return NULL;
+ }
+#endif /* HAVE_PFRING */
+
+ return pfconf;
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to or copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * If old config system is used, then return the smae parameters
+ * value for each interface.
+ *
+ * \return a PfringIfaceConfig corresponding to the interface name
+ */
+void *ParsePfringConfig(const char *iface)
+{
+ char *threadsstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *pf_ring_node;
+ PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
+ char *tmpclusterid;
+ char *tmpctype = NULL;
+#ifdef HAVE_PFRING
+ cluster_type default_ctype = CLUSTER_ROUND_ROBIN;
+ int getctype = 0;
+#endif
+ char *bpf_filter = NULL;
+
+ if (unlikely(pfconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(pfconf);
+ return NULL;
+ }
+
+ memset(pfconf, 0, sizeof(PfringIfaceConfig));
+ strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
+ pfconf->threads = 1;
+ pfconf->cluster_id = 1;
+#ifdef HAVE_PFRING
+ pfconf->ctype = (cluster_type)default_ctype;
+#endif
+ pfconf->DerefFunc = PfringDerefConfig;
+ SC_ATOMIC_INIT(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, 1);
+
+ /* Find initial node */
+ pf_ring_node = ConfGetNode("pfring");
+ if (pf_ring_node == NULL) {
+ SCLogInfo("Unable to find pfring config using default value");
+ return pfconf;
+ }
+
+ if_root = ConfNodeLookupKeyValue(pf_ring_node, "interface", iface);
+
+ if_default = ConfNodeLookupKeyValue(pf_ring_node, "interface", "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("Unable to find pfring config for "
+ "interface %s, using default value or 1.0 "
+ "configuration system. ",
+ iface);
+ return pfconf;
+ }
+
+ /* If there is no setting for current interface use default one as main iface */
+ if (if_root == NULL) {
+ if_root = if_default;
+ if_default = NULL;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
+ pfconf->threads = 1;
+ } else {
+ if (threadsstr != NULL) {
+ pfconf->threads = (uint8_t)atoi(threadsstr);
+ }
+ }
+ if (pfconf->threads == 0) {
+ pfconf->threads = 1;
+ }
+
+ SC_ATOMIC_RESET(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
+
+ /* command line value has precedence */
+ if (ConfGet("pfring.cluster-id", &tmpclusterid) == 1) {
+ pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
+ SCLogDebug("Going to use command-line provided cluster-id %" PRId32,
+ pfconf->cluster_id);
+ } else {
+
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("ZC interface detected, not setting cluster-id for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not setting cluster-id for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Could not get cluster-id from config");
+ } else {
+ pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
+ SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
+ }
+ }
+
+ /*load pfring bpf filter*/
+ /* command line value has precedence */
+ if (ConfGet("bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ pfconf->bpf_filter = SCStrdup(bpf_filter);
+ if (unlikely(pfconf->bpf_filter == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Can't allocate BPF filter string");
+ } else {
+ SCLogDebug("Going to use command-line provided bpf filter %s",
+ pfconf->bpf_filter);
+ }
+ }
+ } else {
+ if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) {
+ if (strlen(bpf_filter) > 0) {
+ pfconf->bpf_filter = SCStrdup(bpf_filter);
+ if (unlikely(pfconf->bpf_filter == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Can't allocate BPF filter string");
+ } else {
+ SCLogDebug("Going to use bpf filter %s",
+ pfconf->bpf_filter);
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_PFRING
+ if (ConfGet("pfring.cluster-type", &tmpctype) == 1) {
+ SCLogDebug("Going to use command-line provided cluster-type");
+ getctype = 1;
+ } else {
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("ZC interface detected, not setting cluster type for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not setting cluster type for PF_RING (iface %s)",
+ pfconf->iface);
+ } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
+ SCLogError(SC_ERR_GET_CLUSTER_TYPE_FAILED,
+ "Could not get cluster-type from config");
+ } else {
+ getctype = 1;
+ }
+ }
+
+ if (getctype) {
+ if (strcmp(tmpctype, "cluster_round_robin") == 0) {
+ SCLogInfo("Using round-robin cluster mode for PF_RING (iface %s)",
+ pfconf->iface);
+ pfconf->ctype = CLUSTER_ROUND_ROBIN;
+ } else if (strcmp(tmpctype, "cluster_flow") == 0) {
+ SCLogInfo("Using flow cluster mode for PF_RING (iface %s)",
+ pfconf->iface);
+ pfconf->ctype = CLUSTER_FLOW;
+ } else {
+ SCLogError(SC_ERR_INVALID_CLUSTER_TYPE,
+ "invalid cluster-type %s",
+ tmpctype);
+ SCFree(pfconf);
+ return NULL;
+ }
+ }
+#endif /* HAVE_PFRING */
+ if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
+ if (strcmp(tmpctype, "auto") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (strcmp(tmpctype, "yes") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(tmpctype, "no") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else if (strcmp(tmpctype, "rx-only") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_RXONLY;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", pfconf->iface);
+ }
+ }
+
+ return pfconf;
+}
+
+int PfringConfigGeThreadsCount(void *conf)
+{
+ PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
+ return pfp->threads;
+}
+
+int PfringConfLevel()
+{
+ char *def_dev;
+ /* 1.0 config should return a string */
+ if (ConfGet("pfring.interface", &def_dev) != 1) {
+ return PFRING_CONF_V2;
+ } else {
+ return PFRING_CONF_V1;
+ }
+ return PFRING_CONF_V2;
+}
+
+#ifdef HAVE_PFRING
+static int GetDevAndParser(char **live_dev, ConfigIfaceParserFunc *parser)
+{
+ ConfGet("pfring.live-interface", live_dev);
+
+ /* determine which config type we have */
+ if (PfringConfLevel() > PFRING_CONF_V1) {
+ *parser = ParsePfringConfig;
+ } else {
+ SCLogInfo("Using 1.0 style configuration for pfring");
+ *parser = OldParsePfringConfig;
+ /* In v1: try to get interface name from config */
+ if (*live_dev == NULL) {
+ if (ConfGet("pfring.interface", live_dev) == 1) {
+ SCLogInfo("Using interface %s", *live_dev);
+ LiveRegisterDevice(*live_dev);
+ } else {
+ SCLogInfo("No interface found, problem incoming");
+ *live_dev = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int RunModeIdsPfringAutoFp(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
+ "Unable to get parser and interface params");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureAutoFp(tparser,
+ PfringConfigGeThreadsCount,
+ "ReceivePfring",
+ "DecodePfring", "RxPFR",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPfringAutoFp initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}
+
+int RunModeIdsPfringSingle(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
+ "Unable to get parser and interface params");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureSingle(tparser,
+ PfringConfigGeThreadsCount,
+ "ReceivePfring",
+ "DecodePfring", "RxPFR",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPfringSingle initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}
+
+int RunModeIdsPfringWorkers(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ RunModeInitialize();
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
+ "Unable to get parser and interface params");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = RunModeSetLiveCaptureWorkers(tparser,
+ PfringConfigGeThreadsCount,
+ "ReceivePfring",
+ "DecodePfring", "RxPFR",
+ live_dev);
+ if (ret != 0) {
+ SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("RunModeIdsPfringWorkers initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}
diff --git a/framework/src/suricata/src/runmode-pfring.h b/framework/src/suricata/src/runmode-pfring.h
new file mode 100644
index 00000000..316c82f7
--- /dev/null
+++ b/framework/src/suricata/src/runmode-pfring.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODE_PFRING_H__
+#define __RUNMODE_PFRING_H__
+
+#include "suricata-common.h"
+
+int RunModeIdsPfringAutoFp(void);
+int RunModeIdsPfringSingle(void);
+int RunModeIdsPfringWorkers(void);
+void RunModeIdsPfringRegister(void);
+const char *RunModeIdsPfringGetDefaultMode(void);
+
+#endif /* __RUNMODE_PFRING_H__ */
diff --git a/framework/src/suricata/src/runmode-tile.c b/framework/src/suricata/src/runmode-tile.c
new file mode 100644
index 00000000..38f5afec
--- /dev/null
+++ b/framework/src/suricata/src/runmode-tile.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2011-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <decanio.tom@gmail.com>
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ *
+ * Tilera TILE-Gx runmode support
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-tile.h"
+#include "output.h"
+#include "source-mpipe.h"
+
+#include "detect-engine.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+
+#ifdef HAVE_MPIPE
+/* Number of configured parallel pipelines. */
+int tile_num_pipelines;
+#endif
+
+/*
+ * runmode support for tilegx
+ */
+
+static const char *mpipe_default_mode = "workers";
+
+const char *RunModeTileMpipeGetDefaultMode(void)
+{
+ return mpipe_default_mode;
+}
+
+void RunModeTileMpipeRegister(void)
+{
+#ifdef HAVE_MPIPE
+ RunModeRegisterNewRunMode(RUNMODE_TILERA_MPIPE, "workers",
+ "Workers tilegx mpipe mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeTileMpipeWorkers);
+ mpipe_default_mode = "workers";
+#endif
+}
+
+#ifdef HAVE_MPIPE
+
+void *ParseMpipeConfig(const char *iface)
+{
+ ConfNode *if_root;
+ ConfNode *mpipe_node;
+ MpipeIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
+ char *copymodestr;
+ char *out_iface = NULL;
+
+ if (unlikely(aconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(aconf);
+ return NULL;
+ }
+
+ strlcpy(aconf->iface, iface, sizeof(aconf->iface));
+
+ /* Find initial node */
+ mpipe_node = ConfGetNode("mpipe.inputs");
+ if (mpipe_node == NULL) {
+ SCLogInfo("Unable to find mpipe config using default value");
+ return aconf;
+ }
+
+ if_root = ConfNodeLookupKeyValue(mpipe_node, "interface", iface);
+ if (if_root == NULL) {
+ SCLogInfo("Unable to find mpipe config for "
+ "interface %s, using default value",
+ iface);
+ return aconf;
+ }
+
+ if (ConfGetChildValue(if_root, "copy-iface", &out_iface) == 1) {
+ if (strlen(out_iface) > 0) {
+ aconf->out_iface = out_iface;
+ }
+ }
+ aconf->copy_mode = MPIPE_COPY_MODE_NONE;
+ if (ConfGetChildValue(if_root, "copy-mode", &copymodestr) == 1) {
+ if (aconf->out_iface == NULL) {
+ SCLogInfo("Copy mode activated but no destination"
+ " iface. Disabling feature");
+ } else if (strlen(copymodestr) <= 0) {
+ aconf->out_iface = NULL;
+ } else if (strcmp(copymodestr, "ips") == 0) {
+ SCLogInfo("MPIPE IPS mode activated %s->%s",
+ iface,
+ aconf->out_iface);
+ aconf->copy_mode = MPIPE_COPY_MODE_IPS;
+ } else if (strcmp(copymodestr, "tap") == 0) {
+ SCLogInfo("MPIPE TAP mode activated %s->%s",
+ iface,
+ aconf->out_iface);
+ aconf->copy_mode = MPIPE_COPY_MODE_TAP;
+ } else {
+ SCLogError(SC_ERR_RUNMODE, "Invalid mode (expected tap or ips)");
+ exit(EXIT_FAILURE);
+ }
+ }
+ return aconf;
+}
+
+/**
+ * \brief RunModeTileMpipeWorkers set up to process all modules in each thread.
+ *
+ * \param iface pointer to the name of the interface from which we will
+ * fetch the packets
+ * \retval 0 if all goes well. (If any problem is detected the engine will
+ * exit())
+ */
+int RunModeTileMpipeWorkers(void)
+{
+ SCEnter();
+ char tname[TM_THREAD_NAME_MAX];
+ char *thread_name;
+ TmModule *tm_module;
+ int pipe;
+
+ RunModeInitialize();
+
+ /* Available cpus */
+ uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
+
+ TimeModeSetLive();
+
+ unsigned int pipe_max = 1;
+ if (ncpus > 1)
+ pipe_max = ncpus - 1;
+
+ intmax_t threads;
+
+ if (ConfGetInt("mpipe.threads", &threads) == 1) {
+ tile_num_pipelines = threads;
+ } else {
+ tile_num_pipelines = pipe_max;
+ }
+ SCLogInfo("%d Tilera worker threads", tile_num_pipelines);
+
+ ReceiveMpipeInit();
+
+ char *mpipe_dev = NULL;
+ int nlive = LiveGetDeviceCount();
+ if (nlive > 0) {
+ SCLogInfo("Using %d live device(s).", nlive);
+ /*mpipe_dev = LiveGetDevice(0);*/
+ } else {
+ /*
+ * Attempt to get interface from config file
+ * overrides -i from command line.
+ */
+ if (ConfGet("mpipe.interface", &mpipe_dev) == 0) {
+ if (ConfGet("mpipe.single_mpipe_dev", &mpipe_dev) == 0) {
+ SCLogError(SC_ERR_RUNMODE, "Failed retrieving "
+ "mpipe.single_mpipe_dev from Conf");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* Get affinity for worker */
+ cpu_set_t cpus;
+ //int result = tmc_cpus_get_my_affinity(&cpus);
+ int result = tmc_cpus_get_dataplane_cpus(&cpus);
+ if (result < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "tmc_cpus_get_my_affinity() returned=%d", result);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ for (pipe = 0; pipe < tile_num_pipelines; pipe++) {
+ char *mpipe_devc;
+
+ if (nlive > 0) {
+ mpipe_devc = SCStrdup("multi");
+ } else {
+ mpipe_devc = SCStrdup(mpipe_dev);
+ }
+ if (unlikely(mpipe_devc == NULL)) {
+ printf("ERROR: SCStrdup failed for ReceiveMpipe\n");
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(tname, sizeof(tname), "Worker%d", pipe+1);
+ thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ printf("ERROR: SCStrdup failed for ReceiveMpipe\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* create the threads */
+ ThreadVars *tv_worker =
+ TmThreadCreatePacketHandler(thread_name,
+ "packetpool", "packetpool",
+ "packetpool", "packetpool",
+ "pktacqloop");
+ if (tv_worker == NULL) {
+ printf("ERROR: TmThreadsCreate failed\n");
+ exit(EXIT_FAILURE);
+ }
+ tm_module = TmModuleGetByName("ReceiveMpipe");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName failed for ReceiveMpipe\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_worker, tm_module, (void *)mpipe_devc);
+
+ /* Bind to a single cpu. */
+ int pipe_cpu = tmc_cpus_find_nth_cpu(&cpus, pipe);
+ tv_worker->rank = pipe;
+
+ TmThreadSetCPUAffinity(tv_worker, pipe_cpu);
+
+ tm_module = TmModuleGetByName("DecodeMpipe");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName DecodeMpipe failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_worker, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName StreamTcp failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_worker, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName Detect failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_worker, tm_module, NULL);
+ }
+
+ tm_module = TmModuleGetByName("RespondReject");
+ if (tm_module == NULL) {
+ printf("ERROR: TmModuleGetByName for RespondReject failed\n");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_worker, tm_module, NULL);
+
+ SetupOutputs(tv_worker);
+
+ if (TmThreadSpawn(tv_worker) != TM_ECODE_OK) {
+ printf("ERROR: TmThreadSpawn failed\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/framework/src/suricata/src/runmode-tile.h b/framework/src/suricata/src/runmode-tile.h
new file mode 100644
index 00000000..ec6c9e50
--- /dev/null
+++ b/framework/src/suricata/src/runmode-tile.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2011-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <decanio.tom@gmail.com>
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ *
+ * Tilera TILE-Gx runmode support
+ */
+
+#ifndef __RUNMODE_TILE_H__
+#define __RUNMODE_TILE_H__
+
+#include "suricata-common.h"
+
+const char *RunModeTileMpipeGetDefaultMode(void);
+void RunModeTileMpipeRegister(void);
+
+extern int tile_num_pipelines;
+
+int RunModeTileMpipeWorkers(void);
+
+void *ParseMpipeConfig(const char *iface);
+
+#endif /* __RUNMODE_TILE_H__ */
diff --git a/framework/src/suricata/src/runmode-unittests.c b/framework/src/suricata/src/runmode-unittests.c
new file mode 100644
index 00000000..4f3fc8bf
--- /dev/null
+++ b/framework/src/suricata/src/runmode-unittests.c
@@ -0,0 +1,311 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "util-unittest.h"
+
+#ifdef UNITTESTS
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-address.h"
+#include "detect-engine-proto.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-sigorder.h"
+#include "detect-engine-payload.h"
+#include "detect-engine-dcepayload.h"
+#include "detect-engine-uri.h"
+#include "detect-engine-hcbd.h"
+#include "detect-engine-hsbd.h"
+#include "detect-engine-hhd.h"
+#include "detect-engine-hrhd.h"
+#include "detect-engine-hmd.h"
+#include "detect-engine-hcd.h"
+#include "detect-engine-hrud.h"
+#include "detect-engine-hsmd.h"
+#include "detect-engine-hscd.h"
+#include "detect-engine-hua.h"
+#include "detect-engine-hhhd.h"
+#include "detect-engine-hrhhd.h"
+#include "detect-engine-state.h"
+#include "detect-engine-tag.h"
+#include "detect-engine-modbus.h"
+#include "detect-engine-filedata-smtp.h"
+#include "detect-fast-pattern.h"
+#include "flow.h"
+#include "flow-timeout.h"
+#include "flow-manager.h"
+#include "flow-var.h"
+#include "flow-bit.h"
+#include "pkt-var.h"
+
+#include "host.h"
+#include "host-bit.h"
+#include "ippair.h"
+#include "ippair-bit.h"
+#include "unix-manager.h"
+
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+#include "app-layer.h"
+#include "app-layer-smb.h"
+#include "app-layer-dcerpc.h"
+#include "app-layer-dcerpc-udp.h"
+#include "app-layer-htp.h"
+#include "app-layer-ftp.h"
+#include "app-layer-ssl.h"
+#include "app-layer-ssh.h"
+#include "app-layer-smtp.h"
+
+#include "util-action.h"
+#include "util-radix-tree.h"
+#include "util-host-os-info.h"
+#include "util-cidr.h"
+#include "util-unittest-helper.h"
+#include "util-time.h"
+#include "util-rule-vars.h"
+#include "util-classification-config.h"
+#include "util-threshold-config.h"
+#include "util-reference-config.h"
+#include "util-profiling.h"
+#include "util-magic.h"
+#include "util-memcmp.h"
+#include "util-misc.h"
+#include "util-ringbuffer.h"
+#include "util-signal.h"
+
+#include "reputation.h"
+#include "util-atomic.h"
+#include "util-spm.h"
+#include "util-hash.h"
+#include "util-hashlist.h"
+#include "util-bloomfilter.h"
+#include "util-bloomfilter-counting.h"
+#include "util-pool.h"
+#include "util-byte.h"
+#include "util-proto-name.h"
+#include "util-memrchr.h"
+
+#include "util-mpm-ac.h"
+#include "detect-engine-mpm.h"
+
+#include "util-decode-asn1.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+#include "tmqh-flow.h"
+#include "defrag.h"
+#include "detect-engine-siggroup.h"
+
+#endif /* UNITTESTS */
+
+void RegisterAllModules();
+void TmqhSetup (void);
+
+/**
+ * Run or list unittests
+ *
+ * \param list_unittests If set to 1, list unittests. Run them if set to 0.
+ * \param regex_arg A regular expression to select unittests to run
+ *
+ * This function is terminal and will call exit after being called.
+ */
+
+void RunUnittests(int list_unittests, char *regex_arg)
+{
+#ifdef UNITTESTS
+ /* Initializations for global vars, queues, etc (memsets, mutex init..) */
+ GlobalInits();
+ TimeInit();
+ SupportFastPatternForSigMatchTypes();
+
+ default_packet_size = DEFAULT_PACKET_SIZE;
+#ifdef __SC_CUDA_SUPPORT__
+ /* Init the CUDA environment */
+ SCCudaInitCudaEnvironment();
+ CudaBufferInit();
+#endif
+ /* load the pattern matchers */
+ MpmTableSetup();
+#ifdef __SC_CUDA_SUPPORT__
+ MpmCudaEnvironmentSetup();
+#endif
+
+ AppLayerSetup();
+
+ /* hardcoded initialization code */
+ SigTableSetup(); /* load the rule keywords */
+ TmqhSetup();
+
+ StorageInit();
+ CIDRInit();
+ SigParsePrepare();
+
+#ifdef DBG_MEM_ALLOC
+ SCLogInfo("Memory used at startup: %"PRIdMAX, (intmax_t)global_mem);
+#endif
+ SCReputationInitCtx();
+ SCProtoNameInit();
+
+ TagInitCtx();
+ SCReferenceConfInit();
+ SCClassConfInit();
+
+ RegisterAllModules();
+
+ DetectEngineRegisterAppInspectionEngines();
+
+ HostBitInitCtx();
+
+ StorageFinalize();
+ /* test and initialize the unittesting subsystem */
+ if(regex_arg == NULL){
+ regex_arg = ".*";
+ UtRunSelftest(regex_arg); /* inits and cleans up again */
+ }
+
+ AppLayerHtpEnableRequestBodyCallback();
+ AppLayerHtpNeedFileInspection();
+
+ UtInitialize();
+ UTHRegisterTests();
+ SCReputationRegisterTests();
+ TmModuleRegisterTests();
+ SigTableRegisterTests();
+ HashTableRegisterTests();
+ HashListTableRegisterTests();
+ BloomFilterRegisterTests();
+ BloomFilterCountingRegisterTests();
+ PoolRegisterTests();
+ ByteRegisterTests();
+ MpmRegisterTests();
+ FlowBitRegisterTests();
+ HostBitRegisterTests();
+ IPPairBitRegisterTests();
+ StatsRegisterTests();
+ DecodePPPRegisterTests();
+ DecodeVLANRegisterTests();
+ HTPParserRegisterTests();
+ DecodeRawRegisterTests();
+ DecodePPPOERegisterTests();
+ DecodeICMPV4RegisterTests();
+ DecodeICMPV6RegisterTests();
+ DecodeIPV4RegisterTests();
+ DecodeIPV6RegisterTests();
+ DecodeTCPRegisterTests();
+ DecodeUDPV4RegisterTests();
+ DecodeGRERegisterTests();
+ DecodeAsn1RegisterTests();
+ DecodeMPLSRegisterTests();
+ AppLayerProtoDetectUnittestsRegister();
+ ConfRegisterTests();
+ ConfYamlRegisterTests();
+ TmqhFlowRegisterTests();
+ FlowRegisterTests();
+ HostRegisterUnittests();
+ IPPairRegisterUnittests();
+ SCSigRegisterSignatureOrderingTests();
+ SCRadixRegisterTests();
+ DefragRegisterTests();
+ SigGroupHeadRegisterTests();
+ SCHInfoRegisterTests();
+ SCRuleVarsRegisterTests();
+ AppLayerParserRegisterUnittests();
+ ThreadMacrosRegisterTests();
+ UtilSpmSearchRegistertests();
+ UtilActionRegisterTests();
+ SCClassConfRegisterTests();
+ SCThresholdConfRegisterTests();
+ SCRConfRegisterTests();
+#ifdef __SC_CUDA_SUPPORT__
+ SCCudaRegisterTests();
+#endif
+ PayloadRegisterTests();
+ DcePayloadRegisterTests();
+ UriRegisterTests();
+#ifdef PROFILING
+ SCProfilingRegisterTests();
+#endif
+ DeStateRegisterTests();
+ DetectRingBufferRegisterTests();
+ MemcmpRegisterTests();
+ DetectEngineHttpClientBodyRegisterTests();
+ DetectEngineHttpServerBodyRegisterTests();
+ DetectEngineHttpHeaderRegisterTests();
+ DetectEngineHttpRawHeaderRegisterTests();
+ DetectEngineHttpMethodRegisterTests();
+ DetectEngineHttpCookieRegisterTests();
+ DetectEngineHttpRawUriRegisterTests();
+ DetectEngineHttpStatMsgRegisterTests();
+ DetectEngineHttpStatCodeRegisterTests();
+ DetectEngineHttpUARegisterTests();
+ DetectEngineHttpHHRegisterTests();
+ DetectEngineHttpHRHRegisterTests();
+ DetectEngineInspectModbusRegisterTests();
+ DetectEngineRegisterTests();
+ DetectEngineSMTPFiledataRegisterTests();
+ SCLogRegisterTests();
+ MagicRegisterTests();
+ UtilMiscRegisterTests();
+ DetectAddressTests();
+ DetectProtoTests();
+ DetectPortTests();
+ SCAtomicRegisterTests();
+ MemrchrRegisterTests();
+#ifdef __SC_CUDA_SUPPORT__
+ CudaBufferRegisterUnittests();
+#endif
+ AppLayerUnittestsRegister();
+ if (list_unittests) {
+ UtListTests(regex_arg);
+ } else {
+ /* global packet pool */
+ extern intmax_t max_pending_packets;
+ max_pending_packets = 128;
+ PacketPoolInit();
+
+ uint32_t failed = UtRunTests(regex_arg);
+ PacketPoolDestroy();
+ UtCleanup();
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA)
+ MpmCudaBufferDeSetup();
+ CudaHandlerFreeProfiles();
+#endif
+ if (failed) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+#ifdef DBG_MEM_ALLOC
+ SCLogInfo("Total memory used (without SCFree()): %"PRIdMAX, (intmax_t)global_mem);
+#endif
+
+ exit(EXIT_SUCCESS);
+#else
+ SCLogError(SC_ERR_NOT_SUPPORTED, "Unittests are not build-in");
+ exit(EXIT_FAILURE);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/runmode-unittests.h b/framework/src/suricata/src/runmode-unittests.h
new file mode 100644
index 00000000..8ccd296d
--- /dev/null
+++ b/framework/src/suricata/src/runmode-unittests.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+
+#ifndef __UTIL_RUNMODE_UNITTESTS_H__
+#define __UTIL_RUNMODE_UNITTESTS_H__
+
+int RunUnittests(int list_unittests, char *regex_arg);
+
+#endif /* __UTIL_RUNMODE_UNITTESTS_H__ */
diff --git a/framework/src/suricata/src/runmode-unix-socket.c b/framework/src/suricata/src/runmode-unix-socket.c
new file mode 100644
index 00000000..06a71869
--- /dev/null
+++ b/framework/src/suricata/src/runmode-unix-socket.c
@@ -0,0 +1,838 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-pcap-file.h"
+#include "output.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "unix-manager.h"
+
+#include "detect-engine.h"
+
+#include "flow-manager.h"
+#include "flow-timeout.h"
+#include "stream-tcp.h"
+#include "output.h"
+#include "host.h"
+#include "defrag.h"
+#include "ippair.h"
+#include "app-layer.h"
+
+#include "util-profiling.h"
+
+#include "conf-yaml-loader.h"
+
+#include "detect-engine.h"
+
+static const char *default_mode = NULL;
+
+int unix_socket_mode_is_running = 0;
+
+typedef struct PcapFiles_ {
+ char *filename;
+ char *output_dir;
+ int tenant_id;
+ TAILQ_ENTRY(PcapFiles_) next;
+} PcapFiles;
+
+typedef struct PcapCommand_ {
+ TAILQ_HEAD(, PcapFiles_) files;
+ int running;
+ char *currentfile;
+} PcapCommand;
+
+const char *RunModeUnixSocketGetDefaultMode(void)
+{
+ return default_mode;
+}
+
+#ifdef BUILD_UNIX_SOCKET
+
+static int unix_manager_file_task_running = 0;
+static int unix_manager_file_task_failed = 0;
+
+/**
+ * \brief return list of files in the queue
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+static TmEcode UnixSocketPcapFilesList(json_t *cmd, json_t* answer, void *data)
+{
+ PcapCommand *this = (PcapCommand *) data;
+ int i = 0;
+ PcapFiles *file;
+ json_t *jdata;
+ json_t *jarray;
+
+ jdata = json_object();
+ if (jdata == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+ jarray = json_array();
+ if (jarray == NULL) {
+ json_decref(jdata);
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+ TAILQ_FOREACH(file, &this->files, next) {
+ json_array_append_new(jarray, json_string(file->filename));
+ i++;
+ }
+ json_object_set_new(jdata, "count", json_integer(i));
+ json_object_set_new(jdata, "files", jarray);
+ json_object_set_new(answer, "message", jdata);
+ return TM_ECODE_OK;
+}
+
+static TmEcode UnixSocketPcapFilesNumber(json_t *cmd, json_t* answer, void *data)
+{
+ PcapCommand *this = (PcapCommand *) data;
+ int i = 0;
+ PcapFiles *file;
+
+ TAILQ_FOREACH(file, &this->files, next) {
+ i++;
+ }
+ json_object_set_new(answer, "message", json_integer(i));
+ return TM_ECODE_OK;
+}
+
+static TmEcode UnixSocketPcapCurrent(json_t *cmd, json_t* answer, void *data)
+{
+ PcapCommand *this = (PcapCommand *) data;
+
+ if (this->currentfile) {
+ json_object_set_new(answer, "message", json_string(this->currentfile));
+ } else {
+ json_object_set_new(answer, "message", json_string("None"));
+ }
+ return TM_ECODE_OK;
+}
+
+
+
+static void PcapFilesFree(PcapFiles *cfile)
+{
+ if (cfile == NULL)
+ return;
+ if (cfile->filename)
+ SCFree(cfile->filename);
+ if (cfile->output_dir)
+ SCFree(cfile->output_dir);
+ SCFree(cfile);
+}
+
+/**
+ * \brief Add file to file queue
+ *
+ * \param this a UnixCommand:: structure
+ * \param filename absolute filename
+ * \param output_dir absolute name of directory where log will be put
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+static TmEcode UnixListAddFile(PcapCommand *this,
+ const char *filename, const char *output_dir, int tenant_id)
+{
+ PcapFiles *cfile = NULL;
+ if (filename == NULL || this == NULL)
+ return TM_ECODE_FAILED;
+ cfile = SCMalloc(sizeof(PcapFiles));
+ if (unlikely(cfile == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate new file");
+ return TM_ECODE_FAILED;
+ }
+ memset(cfile, 0, sizeof(PcapFiles));
+
+ cfile->filename = SCStrdup(filename);
+ if (unlikely(cfile->filename == NULL)) {
+ SCFree(cfile);
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup filename");
+ return TM_ECODE_FAILED;
+ }
+
+ if (output_dir) {
+ cfile->output_dir = SCStrdup(output_dir);
+ if (unlikely(cfile->output_dir == NULL)) {
+ SCFree(cfile->filename);
+ SCFree(cfile);
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup output_dir");
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ cfile->tenant_id = tenant_id;
+
+ TAILQ_INSERT_TAIL(&this->files, cfile, next);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Command to add a file to treatment list
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketAddPcapFile(json_t *cmd, json_t* answer, void *data)
+{
+ PcapCommand *this = (PcapCommand *) data;
+ int ret;
+ const char *filename;
+ const char *output_dir;
+ int tenant_id = 0;
+#ifdef OS_WIN32
+ struct _stat st;
+#else
+ struct stat st;
+#endif /* OS_WIN32 */
+
+ json_t *jarg = json_object_get(cmd, "filename");
+ if(!json_is_string(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("command is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ filename = json_string_value(jarg);
+#ifdef OS_WIN32
+ if(_stat(filename, &st) != 0) {
+#else
+ if(stat(filename, &st) != 0) {
+#endif /* OS_WIN32 */
+ json_object_set_new(answer, "message", json_string("File does not exist"));
+ return TM_ECODE_FAILED;
+ }
+
+ json_t *oarg = json_object_get(cmd, "output-dir");
+ if (oarg != NULL) {
+ if(!json_is_string(oarg)) {
+ SCLogInfo("error: output dir is not a string");
+ json_object_set_new(answer, "message", json_string("output dir is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ output_dir = json_string_value(oarg);
+ } else {
+ SCLogInfo("error: can't get output-dir");
+ json_object_set_new(answer, "message", json_string("output dir param is mandatory"));
+ return TM_ECODE_FAILED;
+ }
+
+#ifdef OS_WIN32
+ if(_stat(output_dir, &st) != 0) {
+#else
+ if(stat(output_dir, &st) != 0) {
+#endif /* OS_WIN32 */
+ json_object_set_new(answer, "message", json_string("Output directory does not exist"));
+ return TM_ECODE_FAILED;
+ }
+
+ json_t *targ = json_object_get(cmd, "tenant");
+ if (targ != NULL) {
+ if(!json_is_number(targ)) {
+ json_object_set_new(answer, "message", json_string("tenant is not a number"));
+ return TM_ECODE_FAILED;
+ }
+ tenant_id = json_number_value(targ);
+ }
+
+ ret = UnixListAddFile(this, filename, output_dir, tenant_id);
+ switch(ret) {
+ case TM_ECODE_FAILED:
+ json_object_set_new(answer, "message", json_string("Unable to add file to list"));
+ return TM_ECODE_FAILED;
+ case TM_ECODE_OK:
+ SCLogInfo("Added file '%s' to list", filename);
+ json_object_set_new(answer, "message", json_string("Successfully added file to list"));
+ return TM_ECODE_OK;
+ }
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Handle the file queue
+ *
+ * This function check if there is currently a file
+ * being parse. If it is not the case, it will start to
+ * work on a new file. This implies to start a new 'pcap-file'
+ * running mode after having set the file and the output dir.
+ * This function also handles the cleaning of the previous
+ * running mode.
+ *
+ * \param this a UnixCommand:: structure
+ * \retval 0 in case of error, 1 in case of success
+ */
+TmEcode UnixSocketPcapFilesCheck(void *data)
+{
+ PcapCommand *this = (PcapCommand *) data;
+ if (unix_manager_file_task_running == 1) {
+ return TM_ECODE_OK;
+ }
+ if ((unix_manager_file_task_failed == 1) || (this->running == 1)) {
+ if (unix_manager_file_task_failed) {
+ SCLogInfo("Preceeding task failed, cleaning the running mode");
+ }
+ unix_manager_file_task_failed = 0;
+ this->running = 0;
+ if (this->currentfile) {
+ SCFree(this->currentfile);
+ }
+ this->currentfile = NULL;
+
+ /* needed by FlowForceReassembly */
+ PacketPoolInit();
+
+ /* handle graceful shutdown of the flow engine, it's helper
+ * threads and the packet threads */
+ FlowDisableFlowManagerThread();
+ TmThreadDisableReceiveThreads();
+ FlowForceReassembly();
+ TmThreadDisablePacketThreads();
+ FlowDisableFlowRecyclerThread();
+
+ /* kill the stats threads */
+ TmThreadKillThreadsFamily(TVT_MGMT);
+ TmThreadClearThreadsFamily(TVT_MGMT);
+
+ /* kill packet threads -- already in 'disabled' state */
+ TmThreadKillThreadsFamily(TVT_PPT);
+ TmThreadClearThreadsFamily(TVT_PPT);
+
+ PacketPoolDestroy();
+
+ /* mgt and ppt threads killed, we can run non thread-safe
+ * shutdown functions */
+ StatsReleaseResources();
+ RunModeShutDown();
+ FlowShutdown();
+ IPPairShutdown();
+ HostCleanup();
+ StreamTcpFreeConfig(STREAM_VERBOSE);
+ DefragDestroy();
+ TmqResetQueues();
+#ifdef PROFILING
+ if (profiling_rules_enabled)
+ SCProfilingDump();
+ SCProfilingDestroy();
+#endif
+ }
+ if (!TAILQ_EMPTY(&this->files)) {
+ PcapFiles *cfile = TAILQ_FIRST(&this->files);
+ TAILQ_REMOVE(&this->files, cfile, next);
+ SCLogInfo("Starting run for '%s'", cfile->filename);
+ unix_manager_file_task_running = 1;
+ this->running = 1;
+ if (ConfSet("pcap-file.file", cfile->filename) != 1) {
+ SCLogInfo("Can not set working file to '%s'", cfile->filename);
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
+ if (cfile->output_dir) {
+ if (ConfSet("default-log-dir", cfile->output_dir) != 1) {
+ SCLogInfo("Can not set output dir to '%s'", cfile->output_dir);
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
+ }
+ if (cfile->tenant_id > 0) {
+ char tstr[16] = "";
+ snprintf(tstr, sizeof(tstr), "%d", cfile->tenant_id);
+ if (ConfSet("pcap-file.tenant-id", tstr) != 1) {
+ SCLogInfo("Can not set working tenant-id to '%s'", tstr);
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
+ } else {
+ SCLogInfo("pcap-file.tenant-id not set");
+ }
+ this->currentfile = SCStrdup(cfile->filename);
+ if (unlikely(this->currentfile == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed file name allocation");
+ return TM_ECODE_FAILED;
+ }
+ PcapFilesFree(cfile);
+ StatsInit();
+#ifdef PROFILING
+ SCProfilingRulesGlobalInit();
+ SCProfilingKeywordsGlobalInit();
+ SCProfilingInit();
+#endif /* PROFILING */
+ DefragInit();
+ FlowInitConfig(FLOW_QUIET);
+ IPPairInitConfig(FLOW_QUIET);
+ StreamTcpInitConfig(STREAM_VERBOSE);
+ AppLayerRegisterGlobalCounters();
+ RunModeInitializeOutputs();
+ StatsSetupPostConfig();
+ RunModeDispatch(RUNMODE_PCAP_FILE, NULL);
+ FlowManagerThreadSpawn();
+ FlowRecyclerThreadSpawn();
+ StatsSpawnThreads();
+ /* Un-pause all the paused threads */
+ TmThreadContinueThreads();
+ }
+ return TM_ECODE_OK;
+}
+#endif
+
+void RunModeUnixSocketRegister(void)
+{
+#ifdef BUILD_UNIX_SOCKET
+ RunModeRegisterNewRunMode(RUNMODE_UNIX_SOCKET, "single",
+ "Unix socket mode",
+ RunModeUnixSocketSingle);
+ default_mode = "single";
+#endif
+ return;
+}
+
+void UnixSocketPcapFile(TmEcode tm)
+{
+#ifdef BUILD_UNIX_SOCKET
+ switch (tm) {
+ case TM_ECODE_DONE:
+ unix_manager_file_task_running = 0;
+ break;
+ case TM_ECODE_FAILED:
+ unix_manager_file_task_running = 0;
+ unix_manager_file_task_failed = 1;
+ break;
+ case TM_ECODE_OK:
+ break;
+ }
+#endif
+}
+
+#ifdef BUILD_UNIX_SOCKET
+/**
+ * \brief Command to add a tenant handler
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketRegisterTenantHandler(json_t *cmd, json_t* answer, void *data)
+{
+ const char *htype;
+ json_int_t traffic_id = -1;
+
+ if (!(DetectEngineMultiTenantEnabled())) {
+ SCLogInfo("error: multi-tenant support not enabled");
+ json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 1 get tenant id */
+ json_t *jarg = json_object_get(cmd, "id");
+ if (!json_is_integer(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("id is not an integer"));
+ return TM_ECODE_FAILED;
+ }
+ int tenant_id = json_integer_value(jarg);
+
+ /* 2 get tenant handler type */
+ jarg = json_object_get(cmd, "htype");
+ if (!json_is_string(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("command is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ htype = json_string_value(jarg);
+
+ SCLogDebug("add-tenant-handler: %d %s", tenant_id, htype);
+
+ /* 3 get optional hargs */
+ json_t *hargs = json_object_get(cmd, "hargs");
+ if (hargs != NULL) {
+ if (!json_is_integer(hargs)) {
+ SCLogInfo("error: hargs not a number");
+ json_object_set_new(answer, "message", json_string("hargs not a number"));
+ return TM_ECODE_FAILED;
+ }
+ traffic_id = json_integer_value(hargs);
+ }
+
+ /* 4 add to system */
+ int r = -1;
+ if (strcmp(htype, "pcap") == 0) {
+ r = DetectEngineTentantRegisterPcapFile(tenant_id);
+ } else if (strcmp(htype, "vlan") == 0) {
+ if (traffic_id < 0) {
+ json_object_set_new(answer, "message", json_string("vlan requires argument"));
+ return TM_ECODE_FAILED;
+ }
+ if (traffic_id > USHRT_MAX) {
+ json_object_set_new(answer, "message", json_string("vlan argument out of range"));
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogInfo("VLAN handler: id %u maps to tenant %u", (uint32_t)traffic_id, tenant_id);
+ r = DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)traffic_id);
+ }
+ if (r != 0) {
+ json_object_set_new(answer, "message", json_string("handler setup failure"));
+ return TM_ECODE_FAILED;
+ }
+
+ if (DetectEngineMTApply() < 0) {
+ json_object_set_new(answer, "message", json_string("couldn't apply settings"));
+ // TODO cleanup
+ return TM_ECODE_FAILED;
+ }
+
+ json_object_set_new(answer, "message", json_string("handler added"));
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Command to remove a tenant handler
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketUnregisterTenantHandler(json_t *cmd, json_t* answer, void *data)
+{
+ const char *htype;
+ json_int_t traffic_id = -1;
+
+ if (!(DetectEngineMultiTenantEnabled())) {
+ SCLogInfo("error: multi-tenant support not enabled");
+ json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 1 get tenant id */
+ json_t *jarg = json_object_get(cmd, "id");
+ if (!json_is_integer(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("id is not an integer"));
+ return TM_ECODE_FAILED;
+ }
+ int tenant_id = json_integer_value(jarg);
+
+ /* 2 get tenant handler type */
+ jarg = json_object_get(cmd, "htype");
+ if (!json_is_string(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("command is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ htype = json_string_value(jarg);
+
+ SCLogDebug("add-tenant-handler: %d %s", tenant_id, htype);
+
+ /* 3 get optional hargs */
+ json_t *hargs = json_object_get(cmd, "hargs");
+ if (hargs != NULL) {
+ if (!json_is_integer(hargs)) {
+ SCLogInfo("error: hargs not a number");
+ json_object_set_new(answer, "message", json_string("hargs not a number"));
+ return TM_ECODE_FAILED;
+ }
+ traffic_id = json_integer_value(hargs);
+ }
+
+ /* 4 add to system */
+ int r = -1;
+ if (strcmp(htype, "pcap") == 0) {
+ r = DetectEngineTentantUnregisterPcapFile(tenant_id);
+ } else if (strcmp(htype, "vlan") == 0) {
+ if (traffic_id < 0) {
+ json_object_set_new(answer, "message", json_string("vlan requires argument"));
+ return TM_ECODE_FAILED;
+ }
+ if (traffic_id > USHRT_MAX) {
+ json_object_set_new(answer, "message", json_string("vlan argument out of range"));
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogInfo("VLAN handler: id %u maps to tenant %u", (uint32_t)traffic_id, tenant_id);
+ r = DetectEngineTentantUnregisterVlanId(tenant_id, (uint32_t)traffic_id);
+ }
+ if (r != 0) {
+ json_object_set_new(answer, "message", json_string("handler unregister failure"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 5 apply it */
+ if (DetectEngineMTApply() < 0) {
+ json_object_set_new(answer, "message", json_string("couldn't apply settings"));
+ // TODO cleanup
+ return TM_ECODE_FAILED;
+ }
+
+ json_object_set_new(answer, "message", json_string("handler added"));
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Command to add a tenant
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketRegisterTenant(json_t *cmd, json_t* answer, void *data)
+{
+ const char *filename;
+#ifdef OS_WIN32
+ struct _stat st;
+#else
+ struct stat st;
+#endif /* OS_WIN32 */
+
+ if (!(DetectEngineMultiTenantEnabled())) {
+ SCLogInfo("error: multi-tenant support not enabled");
+ json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 1 get tenant id */
+ json_t *jarg = json_object_get(cmd, "id");
+ if (!json_is_integer(jarg)) {
+ json_object_set_new(answer, "message", json_string("id is not an integer"));
+ return TM_ECODE_FAILED;
+ }
+ int tenant_id = json_integer_value(jarg);
+
+ /* 2 get tenant yaml */
+ jarg = json_object_get(cmd, "filename");
+ if (!json_is_string(jarg)) {
+ json_object_set_new(answer, "message", json_string("command is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ filename = json_string_value(jarg);
+#ifdef OS_WIN32
+ if(_stat(filename, &st) != 0) {
+#else
+ if(stat(filename, &st) != 0) {
+#endif /* OS_WIN32 */
+ json_object_set_new(answer, "message", json_string("file does not exist"));
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("add-tenant: %d %s", tenant_id, filename);
+
+ /* setup the yaml in this loop so that it's not done by the loader
+ * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
+ if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
+ SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", filename);
+ json_object_set_new(answer, "message", json_string("failed to load yaml"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 3 load into the system */
+ if (DetectEngineLoadTenantBlocking(tenant_id, filename) != 0) {
+ json_object_set_new(answer, "message", json_string("adding tenant failed"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 4 apply to the running system */
+ if (DetectEngineMTApply() < 0) {
+ json_object_set_new(answer, "message", json_string("couldn't apply settings"));
+ // TODO cleanup
+ return TM_ECODE_FAILED;
+ }
+
+ json_object_set_new(answer, "message", json_string("adding tenant succeeded"));
+ return TM_ECODE_OK;
+}
+
+static int reload_cnt = 1;
+/**
+ * \brief Command to reload a tenant
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketReloadTenant(json_t *cmd, json_t* answer, void *data)
+{
+ const char *filename;
+#ifdef OS_WIN32
+ struct _stat st;
+#else
+ struct stat st;
+#endif /* OS_WIN32 */
+
+ if (!(DetectEngineMultiTenantEnabled())) {
+ SCLogInfo("error: multi-tenant support not enabled");
+ json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 1 get tenant id */
+ json_t *jarg = json_object_get(cmd, "id");
+ if (!json_is_integer(jarg)) {
+ json_object_set_new(answer, "message", json_string("id is not an integer"));
+ return TM_ECODE_FAILED;
+ }
+ int tenant_id = json_integer_value(jarg);
+
+ /* 2 get tenant yaml */
+ jarg = json_object_get(cmd, "filename");
+ if (!json_is_string(jarg)) {
+ json_object_set_new(answer, "message", json_string("command is not a string"));
+ return TM_ECODE_FAILED;
+ }
+ filename = json_string_value(jarg);
+#ifdef OS_WIN32
+ if(_stat(filename, &st) != 0) {
+#else
+ if(stat(filename, &st) != 0) {
+#endif /* OS_WIN32 */
+ json_object_set_new(answer, "message", json_string("file does not exist"));
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("reload-tenant: %d %s", tenant_id, filename);
+
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt);
+ SCLogInfo("prefix %s", prefix);
+
+ if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
+ json_object_set_new(answer, "message", json_string("failed to load yaml"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 3 load into the system */
+ if (DetectEngineReloadTenantBlocking(tenant_id, filename, reload_cnt) != 0) {
+ json_object_set_new(answer, "message", json_string("reload tenant failed"));
+ return TM_ECODE_FAILED;
+ }
+
+ reload_cnt++;
+
+ /* apply to the running system */
+ if (DetectEngineMTApply() < 0) {
+ json_object_set_new(answer, "message", json_string("couldn't apply settings"));
+ // TODO cleanup
+ return TM_ECODE_FAILED;
+ }
+
+ json_object_set_new(answer, "message", json_string("reloading tenant succeeded"));
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Command to remove a tenant
+ *
+ * \param cmd the content of command Arguments as a json_t object
+ * \param answer the json_t object that has to be used to answer
+ * \param data pointer to data defining the context here a PcapCommand::
+ */
+TmEcode UnixSocketUnregisterTenant(json_t *cmd, json_t* answer, void *data)
+{
+ if (!(DetectEngineMultiTenantEnabled())) {
+ SCLogInfo("error: multi-tenant support not enabled");
+ json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* 1 get tenant id */
+ json_t *jarg = json_object_get(cmd, "id");
+ if (!json_is_integer(jarg)) {
+ SCLogInfo("error: command is not a string");
+ json_object_set_new(answer, "message", json_string("id is not an integer"));
+ return TM_ECODE_FAILED;
+ }
+ int tenant_id = json_integer_value(jarg);
+
+ SCLogInfo("remove-tenant: %d TODO", tenant_id);
+
+ /* 2 remove it from the system */
+ char prefix[64];
+ snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
+
+ DetectEngineCtx *de_ctx = DetectEngineGetByTenantId(tenant_id);
+ if (de_ctx == NULL) {
+ json_object_set_new(answer, "message", json_string("tenant detect engine not found"));
+ return TM_ECODE_FAILED;
+ }
+
+ /* move to free list */
+ DetectEngineMoveToFreeList(de_ctx);
+ DetectEngineDeReference(&de_ctx);
+
+ /* update the threads */
+ if (DetectEngineMTApply() < 0) {
+ json_object_set_new(answer, "message", json_string("couldn't apply settings"));
+ // TODO cleanup
+ return TM_ECODE_FAILED;
+ }
+
+ /* walk free list, freeing the removed de_ctx */
+ DetectEnginePruneFreeList();
+
+ json_object_set_new(answer, "message", json_string("work in progress"));
+ return TM_ECODE_OK;
+}
+#endif /* BUILD_UNIX_SOCKET */
+
+/**
+ * \brief Single thread version of the Pcap file processing.
+ */
+int RunModeUnixSocketSingle(void)
+{
+#ifdef BUILD_UNIX_SOCKET
+ PcapCommand *pcapcmd = SCMalloc(sizeof(PcapCommand));
+
+ if (unlikely(pcapcmd == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can not allocate pcap command");
+ return 1;
+ }
+ TAILQ_INIT(&pcapcmd->files);
+ pcapcmd->running = 0;
+ pcapcmd->currentfile = NULL;
+
+ UnixManagerThreadSpawn(1);
+
+ unix_socket_mode_is_running = 1;
+
+ UnixManagerRegisterCommand("pcap-file", UnixSocketAddPcapFile, pcapcmd, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("pcap-file-number", UnixSocketPcapFilesNumber, pcapcmd, 0);
+ UnixManagerRegisterCommand("pcap-file-list", UnixSocketPcapFilesList, pcapcmd, 0);
+ UnixManagerRegisterCommand("pcap-current", UnixSocketPcapCurrent, pcapcmd, 0);
+
+ UnixManagerRegisterBackgroundTask(UnixSocketPcapFilesCheck, pcapcmd);
+#endif
+
+ return 0;
+}
+
+int RunModeUnixSocketIsActive(void)
+{
+ return unix_socket_mode_is_running;
+}
+
+
+
+
diff --git a/framework/src/suricata/src/runmode-unix-socket.h b/framework/src/suricata/src/runmode-unix-socket.h
new file mode 100644
index 00000000..20b0fea4
--- /dev/null
+++ b/framework/src/suricata/src/runmode-unix-socket.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __RUNMODE_UNIX_SOCKET_H__
+#define __RUNMODE_UNIX_SOCKET_H__
+
+int RunModeUnixSocketSingle(void);
+void RunModeUnixSocketRegister(void);
+const char *RunModeUnixSocketGetDefaultMode(void);
+
+int RunModeUnixSocketIsActive(void);
+
+void UnixSocketPcapFile(TmEcode tm);
+
+#ifdef BUILD_UNIX_SOCKET
+TmEcode UnixSocketRegisterTenantHandler(json_t *cmd, json_t* answer, void *data);
+TmEcode UnixSocketUnregisterTenantHandler(json_t *cmd, json_t* answer, void *data);
+TmEcode UnixSocketRegisterTenant(json_t *cmd, json_t* answer, void *data);
+TmEcode UnixSocketReloadTenant(json_t *cmd, json_t* answer, void *data);
+TmEcode UnixSocketUnregisterTenant(json_t *cmd, json_t* answer, void *data);
+#endif
+
+#endif /* __RUNMODE_UNIX_SOCKET_H__ */
diff --git a/framework/src/suricata/src/runmodes.c b/framework/src/suricata/src/runmodes.c
new file mode 100644
index 00000000..5a9f9762
--- /dev/null
+++ b/framework/src/suricata/src/runmodes.c
@@ -0,0 +1,927 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pre-cooked threading runmodes.
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "tm-threads.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-byte.h"
+#include "util-affinity.h"
+#include "conf.h"
+#include "queue.h"
+#include "runmodes.h"
+#include "util-unittest.h"
+#include "util-misc.h"
+
+#include "alert-fastlog.h"
+#include "alert-prelude.h"
+#include "alert-unified2-alert.h"
+#include "alert-debuglog.h"
+
+#include "log-httplog.h"
+
+#include "output.h"
+
+#include "source-pfring.h"
+
+int debuglog_enabled = 0;
+
+/**
+ * \brief Holds description for a runmode.
+ */
+typedef struct RunMode_ {
+ /* the runmode type */
+ int runmode;
+ const char *name;
+ const char *description;
+ /* runmode function */
+ int (*RunModeFunc)(void);
+} RunMode;
+
+typedef struct RunModes_ {
+ int no_of_runmodes;
+ RunMode *runmodes;
+} RunModes;
+
+/**
+ * A list of output modules that will be active for the run mode.
+ */
+typedef struct RunModeOutput_ {
+ const char *name;
+ TmModule *tm_module;
+ OutputCtx *output_ctx;
+
+ TAILQ_ENTRY(RunModeOutput_) entries;
+} RunModeOutput;
+TAILQ_HEAD(, RunModeOutput_) RunModeOutputs =
+ TAILQ_HEAD_INITIALIZER(RunModeOutputs);
+
+static RunModes runmodes[RUNMODE_USER_MAX];
+
+static char *active_runmode;
+
+/* free list for our outputs */
+typedef struct OutputFreeList_ {
+ TmModule *tm_module;
+ OutputCtx *output_ctx;
+
+ TAILQ_ENTRY(OutputFreeList_) entries;
+} OutputFreeList;
+TAILQ_HEAD(, OutputFreeList_) output_free_list =
+ TAILQ_HEAD_INITIALIZER(output_free_list);
+
+/**
+ * \internal
+ * \brief Translate a runmode mode to a printale string.
+ *
+ * \param runmode Runmode to be converted into a printable string.
+ *
+ * \retval string Printable string.
+ */
+static const char *RunModeTranslateModeToName(int runmode)
+{
+ switch (runmode) {
+ case RUNMODE_PCAP_DEV:
+ return "PCAP_DEV";
+ case RUNMODE_PCAP_FILE:
+ return "PCAP_FILE";
+ case RUNMODE_PFRING:
+#ifdef HAVE_PFRING
+ return "PFRING";
+#else
+ return "PFRING(DISABLED)";
+#endif
+ case RUNMODE_NFQ:
+ return "NFQ";
+ case RUNMODE_NFLOG:
+ return "NFLOG";
+ case RUNMODE_IPFW:
+ return "IPFW";
+ case RUNMODE_ERF_FILE:
+ return "ERF_FILE";
+ case RUNMODE_DAG:
+ return "ERF_DAG";
+ case RUNMODE_NAPATECH:
+ return "NAPATECH";
+ case RUNMODE_UNITTEST:
+ return "UNITTEST";
+ case RUNMODE_TILERA_MPIPE:
+ return "MPIPE";
+ case RUNMODE_AFP_DEV:
+ return "AF_PACKET_DEV";
+ case RUNMODE_NETMAP:
+#ifdef HAVE_NETMAP
+ return "NETMAP";
+#else
+ return "NETMAP(DISABLED)";
+#endif
+ case RUNMODE_UNIX_SOCKET:
+ return "UNIX_SOCKET";
+ default:
+ SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/**
+ * \internal
+ * \brief Dispatcher function for runmodes. Calls the required runmode function
+ * based on runmode + runmode_custom_id.
+ *
+ * \param runmode The runmode type.
+ * \param runmode_customd_id The runmode custom id.
+ */
+static RunMode *RunModeGetCustomMode(int runmode, const char *custom_mode)
+{
+ int i;
+
+ for (i = 0; i < runmodes[runmode].no_of_runmodes; i++) {
+ if (strcmp(runmodes[runmode].runmodes[i].name, custom_mode) == 0)
+ return &runmodes[runmode].runmodes[i];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Return the running mode
+ *
+ * The returned string must not be freed.
+ *
+ * \return a string containing the current running mode
+ */
+char *RunmodeGetActive(void)
+{
+ return active_runmode;
+}
+
+/**
+ * Return the running mode
+ *
+ * The returned string must not be freed.
+ *
+ * \return a string containing the current running mode
+ */
+const char *RunModeGetMainMode(void)
+{
+ int mainmode = RunmodeGetCurrent();
+
+ return RunModeTranslateModeToName(mainmode);
+}
+
+/**
+ * \brief Register all runmodes in the engine.
+ */
+void RunModeRegisterRunModes(void)
+{
+ memset(runmodes, 0, sizeof(runmodes));
+
+ RunModeIdsPcapRegister();
+ RunModeFilePcapRegister();
+ RunModeIdsPfringRegister();
+ RunModeIpsNFQRegister();
+ RunModeIpsIPFWRegister();
+ RunModeErfFileRegister();
+ RunModeErfDagRegister();
+ RunModeNapatechRegister();
+ RunModeIdsAFPRegister();
+ RunModeIdsNetmapRegister();
+ RunModeIdsNflogRegister();
+ RunModeTileMpipeRegister();
+ RunModeUnixSocketRegister();
+#ifdef UNITTESTS
+ UtRunModeRegister();
+#endif
+ return;
+}
+
+/**
+ * \brief Lists all registered runmodes.
+ */
+void RunModeListRunmodes(void)
+{
+ printf("------------------------------------- Runmodes -------------------"
+ "-----------------------\n");
+
+ printf("| %-17s | %-17s | %-10s \n",
+ "RunMode Type", "Custom Mode ", "Description");
+ printf("|-----------------------------------------------------------------"
+ "-----------------------\n");
+ int i = RUNMODE_UNKNOWN + 1;
+ int j = 0;
+ for ( ; i < RUNMODE_USER_MAX; i++) {
+ int mode_displayed = 0;
+ for (j = 0; j < runmodes[i].no_of_runmodes; j++) {
+ if (mode_displayed == 1) {
+ printf("| ----------------------------------------------"
+ "-----------------------\n");
+ RunMode *runmode = &runmodes[i].runmodes[j];
+ printf("| %-17s | %-17s | %-27s \n",
+ "",
+ runmode->name,
+ runmode->description);
+ } else {
+ RunMode *runmode = &runmodes[i].runmodes[j];
+ printf("| %-17s | %-17s | %-27s \n",
+ RunModeTranslateModeToName(runmode->runmode),
+ runmode->name,
+ runmode->description);
+ }
+ if (mode_displayed == 0)
+ mode_displayed = 1;
+ }
+ if (mode_displayed == 1) {
+ printf("|-----------------------------------------------------------------"
+ "-----------------------\n");
+ }
+ }
+
+ return;
+}
+
+/**
+ */
+void RunModeDispatch(int runmode, const char *custom_mode)
+{
+ char *local_custom_mode = NULL;
+
+ if (custom_mode == NULL) {
+ char *val = NULL;
+ if (ConfGet("runmode", &val) != 1) {
+ custom_mode = NULL;
+ } else {
+ custom_mode = val;
+ }
+ }
+
+ if (custom_mode == NULL || strcmp(custom_mode, "auto") == 0) {
+ switch (runmode) {
+ case RUNMODE_PCAP_DEV:
+ custom_mode = RunModeIdsGetDefaultMode();
+ break;
+ case RUNMODE_PCAP_FILE:
+ custom_mode = RunModeFilePcapGetDefaultMode();
+ break;
+#ifdef HAVE_PFRING
+ case RUNMODE_PFRING:
+ custom_mode = RunModeIdsPfringGetDefaultMode();
+ break;
+#endif
+ case RUNMODE_NFQ:
+ custom_mode = RunModeIpsNFQGetDefaultMode();
+ break;
+ case RUNMODE_IPFW:
+ custom_mode = RunModeIpsIPFWGetDefaultMode();
+ break;
+ case RUNMODE_ERF_FILE:
+ custom_mode = RunModeErfFileGetDefaultMode();
+ break;
+ case RUNMODE_DAG:
+ custom_mode = RunModeErfDagGetDefaultMode();
+ break;
+ case RUNMODE_TILERA_MPIPE:
+ custom_mode = RunModeTileMpipeGetDefaultMode();
+ break;
+ case RUNMODE_NAPATECH:
+ custom_mode = RunModeNapatechGetDefaultMode();
+ break;
+ case RUNMODE_AFP_DEV:
+ custom_mode = RunModeAFPGetDefaultMode();
+ break;
+ case RUNMODE_NETMAP:
+ custom_mode = RunModeNetmapGetDefaultMode();
+ break;
+ case RUNMODE_UNIX_SOCKET:
+ custom_mode = RunModeUnixSocketGetDefaultMode();
+ break;
+ case RUNMODE_NFLOG:
+ custom_mode = RunModeIdsNflogGetDefaultMode();
+ break;
+ default:
+ SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting");
+ exit(EXIT_FAILURE);
+ }
+ } else { /* if (custom_mode == NULL) */
+ /* Add compability with old 'worker' name */
+ if (!strcmp("worker", custom_mode)) {
+ SCLogWarning(SC_ERR_RUNMODE, "'worker' mode have been renamed "
+ "to 'workers', please modify your setup.");
+ local_custom_mode = SCStrdup("workers");
+ if (unlikely(local_custom_mode == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup custom mode");
+ exit(EXIT_FAILURE);
+ }
+ custom_mode = local_custom_mode;
+ }
+ }
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA &&
+ strcasecmp(custom_mode, "autofp") != 0) {
+ SCLogError(SC_ERR_RUNMODE, "When using a cuda mpm, the only runmode we "
+ "support is autofp.");
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ RunMode *mode = RunModeGetCustomMode(runmode, custom_mode);
+ if (mode == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "The custom type \"%s\" doesn't exist "
+ "for this runmode type \"%s\". Please use --list-runmodes to "
+ "see available custom types for this runmode",
+ custom_mode, RunModeTranslateModeToName(runmode));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Export the custom mode */
+ active_runmode = SCStrdup(custom_mode);
+ if (unlikely(active_runmode == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup active mode");
+ exit(EXIT_FAILURE);
+ }
+
+ mode->RunModeFunc();
+
+ if (local_custom_mode != NULL)
+ SCFree(local_custom_mode);
+ return;
+}
+
+/**
+ * \brief Registers a new runmode.
+ *
+ * \param runmode Runmode type.
+ * \param name Custom mode for this specific runmode type. Within each
+ * runmode type, each custom name is a primary key.
+ * \param description Description for this runmode.
+ * \param RunModeFunc The function to be run for this runmode.
+ */
+void RunModeRegisterNewRunMode(int runmode, const char *name,
+ const char *description,
+ int (*RunModeFunc)(void))
+{
+ void *ptmp;
+ if (RunModeGetCustomMode(runmode, name) != NULL) {
+ SCLogError(SC_ERR_RUNMODE, "A runmode by this custom name has already "
+ "been registered. Please use an unique name");
+ return;
+ }
+
+ ptmp = SCRealloc(runmodes[runmode].runmodes,
+ (runmodes[runmode].no_of_runmodes + 1) * sizeof(RunMode));
+ if (ptmp == NULL) {
+ SCFree(runmodes[runmode].runmodes);
+ runmodes[runmode].runmodes = NULL;
+ exit(EXIT_FAILURE);
+ }
+ runmodes[runmode].runmodes = ptmp;
+
+ RunMode *mode = &runmodes[runmode].runmodes[runmodes[runmode].no_of_runmodes];
+ runmodes[runmode].no_of_runmodes++;
+
+ mode->runmode = runmode;
+ mode->name = SCStrdup(name);
+ if (unlikely(mode->name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate string");
+ exit(EXIT_FAILURE);
+ }
+ mode->description = SCStrdup(description);
+ if (unlikely(mode->description == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate string");
+ exit(EXIT_FAILURE);
+ }
+ mode->RunModeFunc = RunModeFunc;
+
+ return;
+}
+
+/**
+ * Setup the outputs for this run mode.
+ *
+ * \param tv The ThreadVars for the thread the outputs will be
+ * appended to.
+ */
+void RunOutputFreeList(void)
+{
+ OutputFreeList *output;
+ while ((output = TAILQ_FIRST(&output_free_list))) {
+ SCLogDebug("output %s %p %p", output->tm_module->name, output, output->output_ctx);
+
+ if (output->output_ctx != NULL && output->output_ctx->DeInit != NULL)
+ output->output_ctx->DeInit(output->output_ctx);
+
+ TAILQ_REMOVE(&output_free_list, output, entries);
+ SCFree(output);
+ }
+}
+
+static TmModule *pkt_logger_module = NULL;
+static TmModule *tx_logger_module = NULL;
+static TmModule *file_logger_module = NULL;
+static TmModule *filedata_logger_module = NULL;
+static TmModule *streaming_logger_module = NULL;
+
+int RunModeOutputFileEnabled(void)
+{
+ return (file_logger_module != NULL);
+}
+
+int RunModeOutputFiledataEnabled(void)
+{
+ return (filedata_logger_module != NULL);
+}
+
+/**
+ * Cleanup the run mode.
+ */
+void RunModeShutDown(void)
+{
+ RunOutputFreeList();
+
+ OutputPacketShutdown();
+ OutputTxShutdown();
+ OutputFileShutdown();
+ OutputFiledataShutdown();
+ OutputStreamingShutdown();
+ OutputStatsShutdown();
+ OutputFlowShutdown();
+
+ /* Close any log files. */
+ RunModeOutput *output;
+ while ((output = TAILQ_FIRST(&RunModeOutputs))) {
+ SCLogDebug("Shutting down output %s.", output->tm_module->name);
+ TAILQ_REMOVE(&RunModeOutputs, output, entries);
+ SCFree(output);
+ }
+
+ /* reset logger pointers */
+ pkt_logger_module = NULL;
+ tx_logger_module = NULL;
+ file_logger_module = NULL;
+ filedata_logger_module = NULL;
+ streaming_logger_module = NULL;
+}
+
+/** \internal
+ * \brief add Sub RunModeOutput to list for Submodule so we can free
+ * the output ctx at shutdown and unix socket reload */
+static void AddOutputToFreeList(OutputModule *module, OutputCtx *output_ctx)
+{
+ TmModule *tm_module = TmModuleGetByName(module->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", module->name);
+ exit(EXIT_FAILURE);
+ }
+ OutputFreeList *fl_output = SCCalloc(1, sizeof(OutputFreeList));
+ if (unlikely(fl_output == NULL))
+ return;
+ fl_output->tm_module = tm_module;
+ fl_output->output_ctx = output_ctx;
+ TAILQ_INSERT_TAIL(&output_free_list, fl_output, entries);
+}
+
+
+static int GetRunModeOutputPriority(RunModeOutput *module)
+{
+ TmModule *tm = TmModuleGetByName(module->name);
+ if (tm == NULL)
+ return 0;
+
+ return tm->priority;
+}
+
+static void InsertInRunModeOutputs(RunModeOutput *runmode_output)
+{
+ RunModeOutput *r_output = NULL;
+ int output_priority = GetRunModeOutputPriority(runmode_output);
+
+ TAILQ_FOREACH(r_output, &RunModeOutputs, entries) {
+ if (GetRunModeOutputPriority(r_output) < output_priority)
+ break;
+ }
+ if (r_output) {
+ TAILQ_INSERT_BEFORE(r_output, runmode_output, entries);
+ } else {
+ TAILQ_INSERT_TAIL(&RunModeOutputs, runmode_output, entries);
+ }
+}
+
+/** \brief Turn output into thread module */
+static void SetupOutput(const char *name, OutputModule *module, OutputCtx *output_ctx)
+{
+ /* flow logger doesn't run in the packet path */
+ if (module->FlowLogFunc) {
+ OutputRegisterFlowLogger(module->name, module->FlowLogFunc, output_ctx);
+ return;
+ }
+ /* stats logger doesn't run in the packet path */
+ if (module->StatsLogFunc) {
+ OutputRegisterStatsLogger(module->name, module->StatsLogFunc, output_ctx);
+ return;
+ }
+
+ TmModule *tm_module = TmModuleGetByName(module->name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for %s failed", module->name);
+ exit(EXIT_FAILURE);
+ }
+ if (strcmp(tmm_modules[TMM_ALERTDEBUGLOG].name, tm_module->name) == 0)
+ debuglog_enabled = 1;
+
+ if (module->PacketLogFunc) {
+ SCLogDebug("%s is a packet logger", module->name);
+ OutputRegisterPacketLogger(module->name, module->PacketLogFunc,
+ module->PacketConditionFunc, output_ctx);
+
+ /* need one instance of the packet logger module */
+ if (pkt_logger_module == NULL) {
+ pkt_logger_module = TmModuleGetByName("__packet_logger__");
+ if (pkt_logger_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for __packet_logger__ failed");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = pkt_logger_module;
+ runmode_output->output_ctx = NULL;
+ InsertInRunModeOutputs(runmode_output);
+ SCLogDebug("__packet_logger__ added");
+ }
+ } else if (module->TxLogFunc) {
+ SCLogDebug("%s is a tx logger", module->name);
+ OutputRegisterTxLogger(module->name, module->alproto,
+ module->TxLogFunc, output_ctx);
+
+ /* need one instance of the tx logger module */
+ if (tx_logger_module == NULL) {
+ tx_logger_module = TmModuleGetByName("__tx_logger__");
+ if (tx_logger_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for __tx_logger__ failed");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = tx_logger_module;
+ runmode_output->output_ctx = NULL;
+ InsertInRunModeOutputs(runmode_output);
+ SCLogDebug("__tx_logger__ added");
+ }
+ } else if (module->FiledataLogFunc) {
+ SCLogDebug("%s is a filedata logger", module->name);
+ OutputRegisterFiledataLogger(module->name, module->FiledataLogFunc, output_ctx);
+
+ /* need one instance of the tx logger module */
+ if (filedata_logger_module == NULL) {
+ filedata_logger_module = TmModuleGetByName("__filedata_logger__");
+ if (filedata_logger_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for __filedata_logger__ failed");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = filedata_logger_module;
+ runmode_output->output_ctx = NULL;
+ InsertInRunModeOutputs(runmode_output);
+ SCLogDebug("__filedata_logger__ added");
+ }
+ } else if (module->FileLogFunc) {
+ SCLogDebug("%s is a file logger", module->name);
+ OutputRegisterFileLogger(module->name, module->FileLogFunc, output_ctx);
+
+ /* need one instance of the tx logger module */
+ if (file_logger_module == NULL) {
+ file_logger_module = TmModuleGetByName("__file_logger__");
+ if (file_logger_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for __file_logger__ failed");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = file_logger_module;
+ runmode_output->output_ctx = NULL;
+ InsertInRunModeOutputs(runmode_output);
+ SCLogDebug("__file_logger__ added");
+ }
+ } else if (module->StreamingLogFunc) {
+ SCLogDebug("%s is a streaming logger", module->name);
+ OutputRegisterStreamingLogger(module->name, module->StreamingLogFunc,
+ output_ctx, module->stream_type);
+
+ /* need one instance of the streaming logger module */
+ if (streaming_logger_module == NULL) {
+ streaming_logger_module = TmModuleGetByName("__streaming_logger__");
+ if (streaming_logger_module == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "TmModuleGetByName for __streaming_logger__ failed");
+ exit(EXIT_FAILURE);
+ }
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = streaming_logger_module;
+ runmode_output->output_ctx = NULL;
+ InsertInRunModeOutputs(runmode_output);
+ SCLogDebug("__streaming_logger__ added");
+ }
+ } else {
+ SCLogDebug("%s is a regular logger", module->name);
+
+ RunModeOutput *runmode_output = SCCalloc(1, sizeof(RunModeOutput));
+ if (unlikely(runmode_output == NULL))
+ return;
+ runmode_output->name = module->name;
+ runmode_output->tm_module = tm_module;
+ runmode_output->output_ctx = output_ctx;
+ InsertInRunModeOutputs(runmode_output);
+ }
+}
+
+/**
+ * Initialize the output modules.
+ */
+void RunModeInitializeOutputs(void)
+{
+ ConfNode *outputs = ConfGetNode("outputs");
+ if (outputs == NULL) {
+ /* No "outputs" section in the configuration. */
+ return;
+ }
+
+ ConfNode *output, *output_config;
+ const char *enabled;
+ char tls_log_enabled = 0;
+ char tls_store_present = 0;
+
+ TAILQ_FOREACH(output, &outputs->head, next) {
+
+ output_config = ConfNodeLookupChild(output, output->val);
+ if (output_config == NULL) {
+ /* Shouldn't happen. */
+ FatalError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to lookup configuration child node: %s", output->val);
+ }
+
+ if (strcmp(output->val, "tls-store") == 0) {
+ tls_store_present = 1;
+ }
+
+ enabled = ConfNodeLookupChildValue(output_config, "enabled");
+ if (enabled == NULL || !ConfValIsTrue(enabled)) {
+ continue;
+ }
+
+ if (strncmp(output->val, "unified-", sizeof("unified-") - 1) == 0) {
+ SCLogWarning(SC_ERR_NOT_SUPPORTED,
+ "Unified1 is no longer supported,"
+ " use Unified2 instead "
+ "(see https://redmine.openinfosecfoundation.org/issues/353"
+ " for an explanation)");
+ continue;
+ } else if (strcmp(output->val, "alert-prelude") == 0) {
+#ifndef PRELUDE
+ SCLogWarning(SC_ERR_NOT_SUPPORTED,
+ "Prelude support not compiled in. Reconfigure/"
+ "recompile with --enable-prelude to add Prelude "
+ "support.");
+ continue;
+#endif
+ } else if (strcmp(output->val, "eve-log") == 0) {
+#ifndef HAVE_LIBJANSSON
+ SCLogWarning(SC_ERR_NOT_SUPPORTED,
+ "Eve-log support not compiled in. Reconfigure/"
+ "recompile with libjansson and its development "
+ "files installed to add eve-log support.");
+ continue;
+#endif
+ } else if (strcmp(output->val, "lua") == 0) {
+#ifndef HAVE_LUA
+ SCLogWarning(SC_ERR_NOT_SUPPORTED,
+ "lua support not compiled in. Reconfigure/"
+ "recompile with lua(jit) and its development "
+ "files installed to add lua support.");
+ continue;
+#endif
+ } else if (strcmp(output->val, "tls-log") == 0) {
+ tls_log_enabled = 1;
+ }
+
+ OutputModule *module = OutputGetModuleByConfName(output->val);
+ if (module == NULL) {
+ FatalErrorOnInit(SC_ERR_INVALID_ARGUMENT,
+ "No output module named %s", output->val);
+ continue;
+ }
+
+ OutputCtx *output_ctx = NULL;
+ if (module->InitFunc != NULL) {
+ output_ctx = module->InitFunc(output_config);
+ if (output_ctx == NULL) {
+ FatalErrorOnInit(SC_ERR_INVALID_ARGUMENT, "output module setup failed");
+ continue;
+ }
+ } else if (module->InitSubFunc != NULL) {
+ SCLogInfo("skipping submodule");
+ continue;
+ }
+
+ // TODO if module == parent, find it's children
+ if (strcmp(output->val, "eve-log") == 0) {
+ ConfNode *types = ConfNodeLookupChild(output_config, "types");
+ SCLogDebug("types %p", types);
+ if (types != NULL) {
+ ConfNode *type = NULL;
+ TAILQ_FOREACH(type, &types->head, next) {
+ SCLogInfo("enabling 'eve-log' module '%s'", type->val);
+
+ char subname[256];
+ snprintf(subname, sizeof(subname), "%s.%s", output->val, type->val);
+
+ OutputModule *sub_module = OutputGetModuleByConfName(subname);
+ if (sub_module == NULL) {
+ FatalErrorOnInit(SC_ERR_INVALID_ARGUMENT,
+ "No output module named %s", subname);
+ continue;
+ }
+ if (sub_module->parent_name == NULL ||
+ strcmp(sub_module->parent_name,output->val) != 0) {
+ FatalError(SC_ERR_INVALID_ARGUMENT,
+ "bad parent for %s", subname);
+ }
+ if (sub_module->InitSubFunc == NULL) {
+ FatalError(SC_ERR_INVALID_ARGUMENT,
+ "bad sub-module for %s", subname);
+ }
+ ConfNode *sub_output_config = ConfNodeLookupChild(type, type->val);
+ // sub_output_config may be NULL if no config
+
+ /* pass on parent output_ctx */
+ OutputCtx *sub_output_ctx =
+ sub_module->InitSubFunc(sub_output_config, output_ctx);
+ if (sub_output_ctx == NULL) {
+ continue;
+ }
+
+ AddOutputToFreeList(sub_module, sub_output_ctx);
+ SetupOutput(sub_module->name, sub_module, sub_output_ctx);
+ }
+ }
+ /* add 'eve-log' to free list as it's the owner of the
+ * main output ctx from which the sub-modules share the
+ * LogFileCtx */
+ AddOutputToFreeList(module, output_ctx);
+
+ } else if (strcmp(output->val, "lua") == 0) {
+ SCLogDebug("handle lua");
+
+ ConfNode *scripts = ConfNodeLookupChild(output_config, "scripts");
+ BUG_ON(scripts == NULL); //TODO
+
+ OutputModule *m;
+ TAILQ_FOREACH(m, &output_ctx->submodules, entries) {
+ SCLogDebug("m %p %s:%s", m, m->name, m->conf_name);
+
+ ConfNode *script = NULL;
+ TAILQ_FOREACH(script, &scripts->head, next) {
+ SCLogDebug("script %s", script->val);
+ if (strcmp(script->val, m->conf_name) == 0) {
+ break;
+ }
+ }
+ BUG_ON(script == NULL);
+
+ /* pass on parent output_ctx */
+ OutputCtx *sub_output_ctx =
+ m->InitSubFunc(script, output_ctx);
+ if (sub_output_ctx == NULL) {
+ SCLogInfo("sub_output_ctx NULL, skipping");
+ continue;
+ }
+
+ SetupOutput(m->name, m, sub_output_ctx);
+ }
+
+ } else {
+ AddOutputToFreeList(module, output_ctx);
+ SetupOutput(module->name, module, output_ctx);
+ }
+ }
+
+ /* Backward compatibility code */
+ if (!tls_store_present && tls_log_enabled) {
+ /* old YAML with no "tls-store" in outputs. "tls-log" value needs
+ * to be started using 'tls-log' config as own config */
+ SCLogWarning(SC_ERR_CONF_YAML_ERROR,
+ "Please use 'tls-store' in YAML to configure TLS storage");
+
+ TAILQ_FOREACH(output, &outputs->head, next) {
+ output_config = ConfNodeLookupChild(output, output->val);
+
+ if (strcmp(output->val, "tls-log") == 0) {
+
+ OutputModule *module = OutputGetModuleByConfName("tls-store");
+ if (module == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT,
+ "No output module named %s, ignoring", "tls-store");
+ continue;
+ }
+
+ OutputCtx *output_ctx = NULL;
+ if (module->InitFunc != NULL) {
+ output_ctx = module->InitFunc(output_config);
+ if (output_ctx == NULL) {
+ continue;
+ }
+ }
+
+ AddOutputToFreeList(module, output_ctx);
+ SetupOutput(module->name, module, output_ctx);
+ }
+ }
+ }
+
+}
+
+/**
+ * Setup the outputs for this run mode.
+ *
+ * \param tv The ThreadVars for the thread the outputs will be
+ * appended to.
+ */
+void SetupOutputs(ThreadVars *tv)
+{
+ RunModeOutput *output;
+ TAILQ_FOREACH(output, &RunModeOutputs, entries) {
+ tv->cap_flags |= output->tm_module->cap_flags;
+ TmSlotSetFuncAppend(tv, output->tm_module, output->output_ctx);
+ }
+}
+
+float threading_detect_ratio = 1;
+
+/**
+ * Initialize multithreading settings.
+ */
+void RunModeInitialize(void)
+{
+ threading_set_cpu_affinity = FALSE;
+ if ((ConfGetBool("threading.set-cpu-affinity", &threading_set_cpu_affinity)) == 0) {
+ threading_set_cpu_affinity = FALSE;
+ }
+ /* try to get custom cpu mask value if needed */
+ if (threading_set_cpu_affinity == TRUE) {
+ AffinitySetupLoadFromConfig();
+ }
+ if ((ConfGetFloat("threading.detect-thread-ratio", &threading_detect_ratio)) != 1) {
+ if (ConfGetNode("threading.detect-thread-ratio") != NULL)
+ WarnInvalidConfEntry("threading.detect-thread-ratio", "%s", "1");
+ threading_detect_ratio = 1;
+ }
+
+ SCLogDebug("threading.detect-thread-ratio %f", threading_detect_ratio);
+}
diff --git a/framework/src/suricata/src/runmodes.h b/framework/src/suricata/src/runmodes.h
new file mode 100644
index 00000000..bd8d0326
--- /dev/null
+++ b/framework/src/suricata/src/runmodes.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __RUNMODES_H__
+#define __RUNMODES_H__
+
+/* Run mode */
+enum {
+ RUNMODE_UNKNOWN = 0,
+ RUNMODE_PCAP_DEV,
+ RUNMODE_PCAP_FILE,
+ RUNMODE_PFRING,
+ RUNMODE_NFQ,
+ RUNMODE_NFLOG,
+ RUNMODE_IPFW,
+ RUNMODE_ERF_FILE,
+ RUNMODE_DAG,
+ RUNMODE_AFP_DEV,
+ RUNMODE_NETMAP,
+ RUNMODE_TILERA_MPIPE,
+ RUNMODE_UNITTEST,
+ RUNMODE_NAPATECH,
+ RUNMODE_UNIX_SOCKET,
+ RUNMODE_USER_MAX, /* Last standard running mode */
+ RUNMODE_LIST_KEYWORDS,
+ RUNMODE_LIST_APP_LAYERS,
+ RUNMODE_LIST_CUDA_CARDS,
+ RUNMODE_LIST_RUNMODES,
+ RUNMODE_PRINT_VERSION,
+ RUNMODE_PRINT_BUILDINFO,
+ RUNMODE_PRINT_USAGE,
+ RUNMODE_DUMP_CONFIG,
+ RUNMODE_CONF_TEST,
+ RUNMODE_LIST_UNITTEST,
+ RUNMODE_ENGINE_ANALYSIS,
+#ifdef OS_WIN32
+ RUNMODE_INSTALL_SERVICE,
+ RUNMODE_REMOVE_SERVICE,
+ RUNMODE_CHANGE_SERVICE_PARAMS,
+#endif
+ RUNMODE_MAX,
+};
+
+char *RunmodeGetActive(void);
+const char *RunModeGetMainMode(void);
+
+void RunModeListRunmodes(void);
+void RunModeDispatch(int, const char *);
+void RunModeRegisterRunModes(void);
+void RunModeRegisterNewRunMode(int, const char *, const char *,
+ int (*RunModeFunc)(void));
+void RunModeInitialize(void);
+void RunModeInitializeOutputs(void);
+void SetupOutputs(ThreadVars *);
+void RunModeShutDown(void);
+
+/* bool indicating if file logger is enabled */
+int RunModeOutputFileEnabled(void);
+/* bool indicating if filedata logger is enabled */
+int RunModeOutputFiledataEnabled(void);
+
+#include "runmode-pcap.h"
+#include "runmode-pcap-file.h"
+#include "runmode-pfring.h"
+#include "runmode-tile.h"
+#include "runmode-nfq.h"
+#include "runmode-ipfw.h"
+#include "runmode-erf-file.h"
+#include "runmode-erf-dag.h"
+#include "runmode-napatech.h"
+#include "runmode-af-packet.h"
+#include "runmode-nflog.h"
+#include "runmode-unix-socket.h"
+#include "runmode-netmap.h"
+
+int threading_set_cpu_affinity;
+extern float threading_detect_ratio;
+
+extern int debuglog_enabled;
+
+#endif /* __RUNMODES_H__ */
diff --git a/framework/src/suricata/src/source-af-packet.c b/framework/src/suricata/src/source-af-packet.c
new file mode 100644
index 00000000..3f1f44e1
--- /dev/null
+++ b/framework/src/suricata/src/source-af-packet.c
@@ -0,0 +1,1919 @@
+/* Copyright (C) 2011-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup afppacket AF_PACKET running mode
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * AF_PACKET socket acquisition support
+ *
+ * \todo watch other interface event to detect suppression of the monitored
+ * interface
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-modules.h"
+#include "tm-threads.h"
+#include "tm-threads-common.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-device.h"
+#include "util-error.h"
+#include "util-privs.h"
+#include "util-optimize.h"
+#include "util-checksum.h"
+#include "util-ioctl.h"
+#include "util-host-info.h"
+#include "tmqh-packetpool.h"
+#include "source-af-packet.h"
+#include "runmodes.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+#ifdef HAVE_AF_PACKET
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#if HAVE_LINUX_IF_PACKET_H
+#include <linux/if_packet.h>
+#endif
+
+#if HAVE_LINUX_IF_ARP_H
+#include <linux/if_arp.h>
+#endif
+
+#if HAVE_LINUX_FILTER_H
+#include <linux/filter.h>
+#endif
+
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#endif /* HAVE_AF_PACKET */
+
+extern int max_pending_packets;
+
+#ifndef HAVE_AF_PACKET
+
+TmEcode NoAFPSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceiveAFPRegister (void)
+{
+ tmm_modules[TMM_RECEIVEAFP].name = "ReceiveAFP";
+ tmm_modules[TMM_RECEIVEAFP].ThreadInit = NoAFPSupportExit;
+ tmm_modules[TMM_RECEIVEAFP].Func = NULL;
+ tmm_modules[TMM_RECEIVEAFP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVEAFP].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEAFP].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEAFP].cap_flags = 0;
+ tmm_modules[TMM_RECEIVEAFP].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration Function for DecodeAFP.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodeAFPRegister (void)
+{
+ tmm_modules[TMM_DECODEAFP].name = "DecodeAFP";
+ tmm_modules[TMM_DECODEAFP].ThreadInit = NoAFPSupportExit;
+ tmm_modules[TMM_DECODEAFP].Func = NULL;
+ tmm_modules[TMM_DECODEAFP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEAFP].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODEAFP].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEAFP].cap_flags = 0;
+ tmm_modules[TMM_DECODEAFP].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief this function prints an error message and exits.
+ */
+TmEcode NoAFPSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NO_AF_PACKET,"Error creating thread %s: you do not have "
+ "support for AF_PACKET enabled, on Linux host please recompile "
+ "with --enable-af-packet", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* We have AF_PACKET support */
+
+#define AFP_IFACE_NAME_LENGTH 48
+
+#define AFP_STATE_DOWN 0
+#define AFP_STATE_UP 1
+
+#define AFP_RECONNECT_TIMEOUT 500000
+#define AFP_DOWN_COUNTER_INTERVAL 40
+
+#define POLL_TIMEOUT 100
+
+#ifndef TP_STATUS_USER_BUSY
+/* for new use latest bit available in tp_status */
+#define TP_STATUS_USER_BUSY (1 << 31)
+#endif
+
+#ifndef TP_STATUS_VLAN_VALID
+#define TP_STATUS_VLAN_VALID (1 << 4)
+#endif
+
+/** protect pfring_set_bpf_filter, as it is not thread safe */
+static SCMutex afpacket_bpf_set_filter_lock = SCMUTEX_INITIALIZER;
+
+enum {
+ AFP_READ_OK,
+ AFP_READ_FAILURE,
+ AFP_FAILURE,
+ AFP_KERNEL_DROP,
+};
+
+enum {
+ AFP_FATAL_ERROR = 1,
+ AFP_RECOVERABLE_ERROR,
+};
+
+union thdr {
+ struct tpacket2_hdr *h2;
+ void *raw;
+};
+
+/**
+ * \brief Structure to hold thread specific variables.
+ */
+typedef struct AFPThreadVars_
+{
+ /* thread specific socket */
+ int socket;
+ /* handle state */
+ unsigned char afp_state;
+
+ /* data link type for the thread */
+ int datalink;
+ int cooked;
+
+ /* counters */
+ uint64_t pkts;
+ uint64_t bytes;
+ uint64_t errs;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ uint8_t *data; /** Per function and thread data */
+ int datalen; /** Length of per function and thread data */
+
+ int vlan_disabled;
+
+ char iface[AFP_IFACE_NAME_LENGTH];
+ LiveDevice *livedev;
+ int down_count;
+
+ /* Filter */
+ char *bpf_filter;
+
+ /* socket buffer size */
+ int buffer_size;
+ int promisc;
+ ChecksumValidationMode checksum_mode;
+
+ /* IPS stuff */
+ char out_iface[AFP_IFACE_NAME_LENGTH];
+ AFPPeer *mpeer;
+
+ int flags;
+ uint16_t capture_kernel_packets;
+ uint16_t capture_kernel_drops;
+
+ int cluster_id;
+ int cluster_type;
+
+ int threads;
+ int copy_mode;
+
+ struct tpacket_req req;
+ unsigned int tp_hdrlen;
+ unsigned int ring_buflen;
+ char *ring_buf;
+ char *frame_buf;
+ unsigned int frame_offset;
+ int ring_size;
+
+} AFPThreadVars;
+
+TmEcode ReceiveAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode ReceiveAFPThreadInit(ThreadVars *, void *, void **);
+void ReceiveAFPThreadExitStats(ThreadVars *, void *);
+TmEcode ReceiveAFPThreadDeinit(ThreadVars *, void *);
+TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot);
+
+TmEcode DecodeAFPThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeAFPThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+TmEcode AFPSetBPFFilter(AFPThreadVars *ptv);
+static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose);
+static int AFPGetDevFlags(int fd, const char *ifname);
+static int AFPDerefSocket(AFPPeer* peer);
+static int AFPRefSocket(AFPPeer* peer);
+
+/**
+ * \brief Registration Function for RecieveAFP.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleReceiveAFPRegister (void)
+{
+ tmm_modules[TMM_RECEIVEAFP].name = "ReceiveAFP";
+ tmm_modules[TMM_RECEIVEAFP].ThreadInit = ReceiveAFPThreadInit;
+ tmm_modules[TMM_RECEIVEAFP].Func = NULL;
+ tmm_modules[TMM_RECEIVEAFP].PktAcqLoop = ReceiveAFPLoop;
+ tmm_modules[TMM_RECEIVEAFP].ThreadExitPrintStats = ReceiveAFPThreadExitStats;
+ tmm_modules[TMM_RECEIVEAFP].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEAFP].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEAFP].cap_flags = SC_CAP_NET_RAW;
+ tmm_modules[TMM_RECEIVEAFP].flags = TM_FLAG_RECEIVE_TM;
+}
+
+
+/**
+ * \defgroup afppeers AFP peers list
+ *
+ * AF_PACKET has an IPS mode were interface are peered: packet from
+ * on interface are sent the peered interface and the other way. The ::AFPPeer
+ * list is maitaining the list of peers. Each ::AFPPeer is storing the needed
+ * information to be able to send packet on the interface.
+ * A element of the list must not be destroyed during the run of Suricata as it
+ * is used by ::Packet and other threads.
+ *
+ * @{
+ */
+
+typedef struct AFPPeersList_ {
+ TAILQ_HEAD(, AFPPeer_) peers; /**< Head of list of fragments. */
+ int cnt;
+ int peered;
+ int turn; /**< Next value for initialisation order */
+ SC_ATOMIC_DECLARE(int, reached); /**< Counter used to synchronize start */
+} AFPPeersList;
+
+/**
+ * \brief Update the peer.
+ *
+ * Update the AFPPeer of a thread ie set new state, socket number
+ * or iface index.
+ *
+ */
+void AFPPeerUpdate(AFPThreadVars *ptv)
+{
+ if (ptv->mpeer == NULL) {
+ return;
+ }
+ (void)SC_ATOMIC_SET(ptv->mpeer->if_idx, AFPGetIfnumByDev(ptv->socket, ptv->iface, 0));
+ (void)SC_ATOMIC_SET(ptv->mpeer->socket, ptv->socket);
+ (void)SC_ATOMIC_SET(ptv->mpeer->state, ptv->afp_state);
+}
+
+/**
+ * \brief Clean and free ressource used by an ::AFPPeer
+ */
+void AFPPeerClean(AFPPeer *peer)
+{
+ if (peer->flags & AFP_SOCK_PROTECT)
+ SCMutexDestroy(&peer->sock_protect);
+ SC_ATOMIC_DESTROY(peer->socket);
+ SC_ATOMIC_DESTROY(peer->if_idx);
+ SC_ATOMIC_DESTROY(peer->state);
+ SCFree(peer);
+}
+
+AFPPeersList peerslist;
+
+
+/**
+ * \brief Init the global list of ::AFPPeer
+ */
+TmEcode AFPPeersListInit()
+{
+ SCEnter();
+ TAILQ_INIT(&peerslist.peers);
+ peerslist.peered = 0;
+ peerslist.cnt = 0;
+ peerslist.turn = 0;
+ SC_ATOMIC_INIT(peerslist.reached);
+ (void) SC_ATOMIC_SET(peerslist.reached, 0);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Check that all ::AFPPeer got a peer
+ *
+ * \retval TM_ECODE_FAILED if some threads are not peered or TM_ECODE_OK else.
+ */
+TmEcode AFPPeersListCheck()
+{
+#define AFP_PEERS_MAX_TRY 4
+#define AFP_PEERS_WAIT 20000
+ int try = 0;
+ SCEnter();
+ while (try < AFP_PEERS_MAX_TRY) {
+ if (peerslist.cnt != peerslist.peered) {
+ usleep(AFP_PEERS_WAIT);
+ } else {
+ SCReturnInt(TM_ECODE_OK);
+ }
+ try++;
+ }
+ SCLogError(SC_ERR_AFP_CREATE, "Threads number not equals");
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+/**
+ * \brief Declare a new AFP thread to AFP peers list.
+ */
+TmEcode AFPPeersListAdd(AFPThreadVars *ptv)
+{
+ SCEnter();
+ AFPPeer *peer = SCMalloc(sizeof(AFPPeer));
+ AFPPeer *pitem;
+ int mtu, out_mtu;
+
+ if (unlikely(peer == NULL)) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(peer, 0, sizeof(AFPPeer));
+ SC_ATOMIC_INIT(peer->socket);
+ SC_ATOMIC_INIT(peer->sock_usage);
+ SC_ATOMIC_INIT(peer->if_idx);
+ SC_ATOMIC_INIT(peer->state);
+ peer->flags = ptv->flags;
+ peer->turn = peerslist.turn++;
+
+ if (peer->flags & AFP_SOCK_PROTECT) {
+ SCMutexInit(&peer->sock_protect, NULL);
+ }
+
+ (void)SC_ATOMIC_SET(peer->sock_usage, 0);
+ (void)SC_ATOMIC_SET(peer->state, AFP_STATE_DOWN);
+ strlcpy(peer->iface, ptv->iface, AFP_IFACE_NAME_LENGTH);
+ ptv->mpeer = peer;
+ /* add element to iface list */
+ TAILQ_INSERT_TAIL(&peerslist.peers, peer, next);
+
+ if (ptv->copy_mode != AFP_COPY_MODE_NONE) {
+ peerslist.cnt++;
+
+ /* Iter to find a peer */
+ TAILQ_FOREACH(pitem, &peerslist.peers, next) {
+ if (pitem->peer)
+ continue;
+ if (strcmp(pitem->iface, ptv->out_iface))
+ continue;
+ peer->peer = pitem;
+ pitem->peer = peer;
+ mtu = GetIfaceMTU(ptv->iface);
+ out_mtu = GetIfaceMTU(ptv->out_iface);
+ if (mtu != out_mtu) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "MTU on %s (%d) and %s (%d) are not equal, "
+ "transmission of packets bigger than %d will fail.",
+ ptv->iface, mtu,
+ ptv->out_iface, out_mtu,
+ (out_mtu > mtu) ? mtu : out_mtu);
+ }
+ peerslist.peered += 2;
+ break;
+ }
+ }
+
+ AFPPeerUpdate(ptv);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+int AFPPeersListWaitTurn(AFPPeer *peer)
+{
+ /* If turn is zero, we already have started threads once */
+ if (peerslist.turn == 0)
+ return 0;
+
+ if (peer->turn == SC_ATOMIC_GET(peerslist.reached))
+ return 0;
+ return 1;
+}
+
+void AFPPeersListReachedInc()
+{
+ if (peerslist.turn == 0)
+ return;
+
+ if (SC_ATOMIC_ADD(peerslist.reached, 1) == peerslist.turn) {
+ SCLogInfo("All AFP capture threads are running.");
+ (void)SC_ATOMIC_SET(peerslist.reached, 0);
+ /* Set turn to 0 to skip syncrhonization when ReceiveAFPLoop is
+ * restarted.
+ */
+ peerslist.turn = 0;
+ }
+}
+
+static int AFPPeersListStarted()
+{
+ return !peerslist.turn;
+}
+
+/**
+ * \brief Clean the global peers list.
+ */
+void AFPPeersListClean()
+{
+ AFPPeer *pitem;
+
+ while ((pitem = TAILQ_FIRST(&peerslist.peers))) {
+ TAILQ_REMOVE(&peerslist.peers, pitem, next);
+ AFPPeerClean(pitem);
+ }
+}
+
+/**
+ * @}
+ */
+
+/**
+ * \brief Registration Function for DecodeAFP.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodeAFPRegister (void)
+{
+ tmm_modules[TMM_DECODEAFP].name = "DecodeAFP";
+ tmm_modules[TMM_DECODEAFP].ThreadInit = DecodeAFPThreadInit;
+ tmm_modules[TMM_DECODEAFP].Func = DecodeAFP;
+ tmm_modules[TMM_DECODEAFP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEAFP].ThreadDeinit = DecodeAFPThreadDeinit;
+ tmm_modules[TMM_DECODEAFP].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEAFP].cap_flags = 0;
+ tmm_modules[TMM_DECODEAFP].flags = TM_FLAG_DECODE_TM;
+}
+
+
+static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose);
+
+static inline void AFPDumpCounters(AFPThreadVars *ptv)
+{
+#ifdef PACKET_STATISTICS
+ struct tpacket_stats kstats;
+ socklen_t len = sizeof (struct tpacket_stats);
+ if (getsockopt(ptv->socket, SOL_PACKET, PACKET_STATISTICS,
+ &kstats, &len) > -1) {
+ SCLogDebug("(%s) Kernel: Packets %" PRIu32 ", dropped %" PRIu32 "",
+ ptv->tv->name,
+ kstats.tp_packets, kstats.tp_drops);
+ StatsAddUI64(ptv->tv, ptv->capture_kernel_packets, kstats.tp_packets);
+ StatsAddUI64(ptv->tv, ptv->capture_kernel_drops, kstats.tp_drops);
+ (void) SC_ATOMIC_ADD(ptv->livedev->drop, (uint64_t) kstats.tp_drops);
+ (void) SC_ATOMIC_ADD(ptv->livedev->pkts, (uint64_t) kstats.tp_packets);
+ }
+#endif
+}
+
+/**
+ * \brief AF packet read function.
+ *
+ * This function fills
+ * From here the packets are picked up by the DecodeAFP thread.
+ *
+ * \param user pointer to AFPThreadVars
+ * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success
+ */
+int AFPRead(AFPThreadVars *ptv)
+{
+ Packet *p = NULL;
+ /* XXX should try to use read that get directly to packet */
+ int offset = 0;
+ int caplen;
+ struct sockaddr_ll from;
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr cmsg;
+ char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+ } cmsg_buf;
+ unsigned char aux_checksum = 0;
+
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ msg.msg_flags = 0;
+
+ if (ptv->cooked)
+ offset = SLL_HEADER_LEN;
+ else
+ offset = 0;
+ iov.iov_len = ptv->datalen - offset;
+ iov.iov_base = ptv->data + offset;
+
+ caplen = recvmsg(ptv->socket, &msg, MSG_TRUNC);
+
+ if (caplen < 0) {
+ SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32,
+ errno);
+ SCReturnInt(AFP_READ_FAILURE);
+ }
+
+ p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCReturnInt(AFP_FAILURE);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ /* get timestamp of packet via ioctl */
+ if (ioctl(ptv->socket, SIOCGSTAMP, &p->ts) == -1) {
+ SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32,
+ errno);
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_READ_FAILURE);
+ }
+
+ ptv->pkts++;
+ ptv->bytes += caplen + offset;
+ p->livedev = ptv->livedev;
+
+ /* add forged header */
+ if (ptv->cooked) {
+ SllHdr * hdrp = (SllHdr *)ptv->data;
+ /* XXX this is minimalist, but this seems enough */
+ hdrp->sll_protocol = from.sll_protocol;
+ }
+
+ p->datalink = ptv->datalink;
+ SET_PKT_LEN(p, caplen + offset);
+ if (PacketCopyData(p, ptv->data, GET_PKT_LEN(p)) == -1) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_FAILURE);
+ }
+ SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)",
+ GET_PKT_LEN(p), p, GET_PKT_DATA(p));
+
+ /* We only check for checksum disable */
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ptv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ if (ptv->livedev->ignore_checksum) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ChecksumAutoModeCheck(ptv->pkts,
+ SC_ATOMIC_GET(ptv->livedev->pkts),
+ SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
+ ptv->livedev->ignore_checksum = 1;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ } else {
+ aux_checksum = 1;
+ }
+
+ /* List is NULL if we don't have activated auxiliary data */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ struct tpacket_auxdata *aux;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) ||
+ cmsg->cmsg_level != SOL_PACKET ||
+ cmsg->cmsg_type != PACKET_AUXDATA)
+ continue;
+
+ aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
+
+ if (aux_checksum && (aux->tp_status & TP_STATUS_CSUMNOTREADY)) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ break;
+ }
+
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_FAILURE);
+ }
+ SCReturnInt(AFP_READ_OK);
+}
+
+TmEcode AFPWritePacket(Packet *p)
+{
+ struct sockaddr_ll socket_address;
+ int socket;
+
+ if (p->afp_v.copy_mode == AFP_COPY_MODE_IPS) {
+ if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ return TM_ECODE_OK;
+ }
+ }
+
+ if (SC_ATOMIC_GET(p->afp_v.peer->state) == AFP_STATE_DOWN)
+ return TM_ECODE_OK;
+
+ if (p->ethh == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "Should have an Ethernet header");
+ return TM_ECODE_FAILED;
+ }
+ /* Index of the network device */
+ socket_address.sll_ifindex = SC_ATOMIC_GET(p->afp_v.peer->if_idx);
+ /* Address length*/
+ socket_address.sll_halen = ETH_ALEN;
+ /* Destination MAC */
+ memcpy(socket_address.sll_addr, p->ethh, 6);
+
+ /* Send packet, locking the socket if necessary */
+ if (p->afp_v.peer->flags & AFP_SOCK_PROTECT)
+ SCMutexLock(&p->afp_v.peer->sock_protect);
+ socket = SC_ATOMIC_GET(p->afp_v.peer->socket);
+ if (sendto(socket, GET_PKT_DATA(p), GET_PKT_LEN(p), 0,
+ (struct sockaddr*) &socket_address,
+ sizeof(struct sockaddr_ll)) < 0) {
+ SCLogWarning(SC_ERR_SOCKET, "Sending packet failed on socket %d: %s",
+ socket,
+ strerror(errno));
+ if (p->afp_v.peer->flags & AFP_SOCK_PROTECT)
+ SCMutexUnlock(&p->afp_v.peer->sock_protect);
+ return TM_ECODE_FAILED;
+ }
+ if (p->afp_v.peer->flags & AFP_SOCK_PROTECT)
+ SCMutexUnlock(&p->afp_v.peer->sock_protect);
+
+ return TM_ECODE_OK;
+}
+
+void AFPReleaseDataFromRing(Packet *p)
+{
+ /* Need to be in copy mode and need to detect early release
+ where Ethernet header could not be set (and pseudo packet) */
+ if ((p->afp_v.copy_mode != AFP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
+ AFPWritePacket(p);
+ }
+
+ if (AFPDerefSocket(p->afp_v.mpeer) == 0)
+ goto cleanup;
+
+ if (p->afp_v.relptr) {
+ union thdr h;
+ h.raw = p->afp_v.relptr;
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ }
+
+cleanup:
+ AFPV_CLEANUP(&p->afp_v);
+}
+
+void AFPReleasePacket(Packet *p)
+{
+ AFPReleaseDataFromRing(p);
+ PacketFreeOrRelease(p);
+}
+
+/**
+ * \brief AF packet read function for ring
+ *
+ * This function fills
+ * From here the packets are picked up by the DecodeAFP thread.
+ *
+ * \param user pointer to AFPThreadVars
+ * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success
+ */
+int AFPReadFromRing(AFPThreadVars *ptv)
+{
+ Packet *p = NULL;
+ union thdr h;
+ struct sockaddr_ll *from;
+ uint8_t emergency_flush = 0;
+ int read_pkts = 0;
+ int loop_start = -1;
+
+
+ /* Loop till we have packets available */
+ while (1) {
+ if (unlikely(suricata_ctl_flags != 0)) {
+ break;
+ }
+
+ /* Read packet from ring */
+ h.raw = (((union thdr **)ptv->frame_buf)[ptv->frame_offset]);
+ if (h.raw == NULL) {
+ SCReturnInt(AFP_FAILURE);
+ }
+
+ if ((! h.h2->tp_status) || (h.h2->tp_status & TP_STATUS_USER_BUSY)) {
+ if (read_pkts == 0) {
+ if (loop_start == -1) {
+ loop_start = ptv->frame_offset;
+ } else if (unlikely(loop_start == (int)ptv->frame_offset)) {
+ SCReturnInt(AFP_READ_OK);
+ }
+ if (++ptv->frame_offset >= ptv->req.tp_frame_nr) {
+ ptv->frame_offset = 0;
+ }
+ continue;
+ }
+ if ((emergency_flush) && (ptv->flags & AFP_EMERGENCY_MODE)) {
+ SCReturnInt(AFP_KERNEL_DROP);
+ } else {
+ SCReturnInt(AFP_READ_OK);
+ }
+ }
+
+ read_pkts++;
+ loop_start = -1;
+
+ /* Our packet is still used by suricata, we exit read loop to
+ * gain some time */
+ if (h.h2->tp_status & TP_STATUS_USER_BUSY) {
+ SCReturnInt(AFP_READ_OK);
+ }
+
+ if ((ptv->flags & AFP_EMERGENCY_MODE) && (emergency_flush == 1)) {
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ goto next_frame;
+ }
+
+ p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCReturnInt(AFP_FAILURE);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ /* Suricata will treat packet so telling it is busy, this
+ * status will be reset to 0 (ie TP_STATUS_KERNEL) in the release
+ * function. */
+ h.h2->tp_status |= TP_STATUS_USER_BUSY;
+
+ from = (void *)h.raw + TPACKET_ALIGN(ptv->tp_hdrlen);
+
+ ptv->pkts++;
+ ptv->bytes += h.h2->tp_len;
+ p->livedev = ptv->livedev;
+
+ /* add forged header */
+ if (ptv->cooked) {
+ SllHdr * hdrp = (SllHdr *)ptv->data;
+ /* XXX this is minimalist, but this seems enough */
+ hdrp->sll_protocol = from->sll_protocol;
+ }
+
+ p->datalink = ptv->datalink;
+ if (h.h2->tp_len > h.h2->tp_snaplen) {
+ SCLogDebug("Packet length (%d) > snaplen (%d), truncating",
+ h.h2->tp_len, h.h2->tp_snaplen);
+ }
+
+ /* get vlan id from header */
+ if ((!ptv->vlan_disabled) &&
+ (h.h2->tp_status & TP_STATUS_VLAN_VALID || h.h2->tp_vlan_tci)) {
+ p->vlan_id[0] = h.h2->tp_vlan_tci;
+ p->vlan_idx = 1;
+ p->vlanh[0] = NULL;
+ }
+
+ if (ptv->flags & AFP_ZERO_COPY) {
+ if (PacketSetData(p, (unsigned char*)h.raw + h.h2->tp_mac, h.h2->tp_snaplen) == -1) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_FAILURE);
+ } else {
+ p->afp_v.relptr = h.raw;
+ p->ReleasePacket = AFPReleasePacket;
+ p->afp_v.mpeer = ptv->mpeer;
+ AFPRefSocket(ptv->mpeer);
+
+ p->afp_v.copy_mode = ptv->copy_mode;
+ if (p->afp_v.copy_mode != AFP_COPY_MODE_NONE) {
+ p->afp_v.peer = ptv->mpeer->peer;
+ } else {
+ p->afp_v.peer = NULL;
+ }
+ }
+ } else {
+ if (PacketCopyData(p, (unsigned char*)h.raw + h.h2->tp_mac, h.h2->tp_snaplen) == -1) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_FAILURE);
+ }
+ }
+ /* Timestamp */
+ p->ts.tv_sec = h.h2->tp_sec;
+ p->ts.tv_usec = h.h2->tp_nsec/1000;
+ SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)",
+ GET_PKT_LEN(p), p, GET_PKT_DATA(p));
+
+ /* We only check for checksum disable */
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ptv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ if (ptv->livedev->ignore_checksum) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ChecksumAutoModeCheck(ptv->pkts,
+ SC_ATOMIC_GET(ptv->livedev->pkts),
+ SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
+ ptv->livedev->ignore_checksum = 1;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ } else {
+ if (h.h2->tp_status & TP_STATUS_CSUMNOTREADY) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ }
+ if (h.h2->tp_status & TP_STATUS_LOSING) {
+ emergency_flush = 1;
+ AFPDumpCounters(ptv);
+ }
+
+ /* release frame if not in zero copy mode */
+ if (!(ptv->flags & AFP_ZERO_COPY)) {
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ }
+
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ if (++ptv->frame_offset >= ptv->req.tp_frame_nr) {
+ ptv->frame_offset = 0;
+ }
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(AFP_FAILURE);
+ }
+
+next_frame:
+ if (++ptv->frame_offset >= ptv->req.tp_frame_nr) {
+ ptv->frame_offset = 0;
+ /* Get out of loop to be sure we will reach maintenance tasks */
+ SCReturnInt(AFP_READ_OK);
+ }
+ }
+
+ SCReturnInt(AFP_READ_OK);
+}
+
+/**
+ * \brief Reference socket
+ *
+ * \retval O in case of failure, 1 in case of success
+ */
+static int AFPRefSocket(AFPPeer* peer)
+{
+ if (unlikely(peer == NULL))
+ return 0;
+
+ (void)SC_ATOMIC_ADD(peer->sock_usage, 1);
+ return 1;
+}
+
+
+/**
+ * \brief Dereference socket
+ *
+ * \retval 1 if socket is still alive, 0 if not
+ */
+static int AFPDerefSocket(AFPPeer* peer)
+{
+ if (peer == NULL)
+ return 1;
+
+ if (SC_ATOMIC_SUB(peer->sock_usage, 1) == 0) {
+ if (SC_ATOMIC_GET(peer->state) == AFP_STATE_DOWN) {
+ SCLogInfo("Cleaning socket connected to '%s'", peer->iface);
+ close(SC_ATOMIC_GET(peer->socket));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void AFPSwitchState(AFPThreadVars *ptv, int state)
+{
+ ptv->afp_state = state;
+ ptv->down_count = 0;
+
+ AFPPeerUpdate(ptv);
+
+ /* Do cleaning if switching to down state */
+ if (state == AFP_STATE_DOWN) {
+ if (ptv->frame_buf) {
+ /* only used in reading phase, we can free it */
+ SCFree(ptv->frame_buf);
+ ptv->frame_buf = NULL;
+ }
+ if (ptv->socket != -1) {
+ /* we need to wait for all packets to return data */
+ if (SC_ATOMIC_SUB(ptv->mpeer->sock_usage, 1) == 0) {
+ SCLogInfo("Cleaning socket connected to '%s'", ptv->iface);
+ close(ptv->socket);
+ ptv->socket = -1;
+ }
+ }
+ }
+ if (state == AFP_STATE_UP) {
+ (void)SC_ATOMIC_SET(ptv->mpeer->sock_usage, 1);
+ }
+}
+
+static int AFPReadAndDiscard(AFPThreadVars *ptv, struct timeval *synctv)
+{
+ struct sockaddr_ll from;
+ struct iovec iov;
+ struct msghdr msg;
+ struct timeval ts;
+ union {
+ struct cmsghdr cmsg;
+ char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+ } cmsg_buf;
+
+
+ if (unlikely(suricata_ctl_flags != 0)) {
+ return 1;
+ }
+
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ msg.msg_flags = 0;
+
+ iov.iov_len = ptv->datalen;
+ iov.iov_base = ptv->data;
+
+ recvmsg(ptv->socket, &msg, MSG_TRUNC);
+
+ if (ioctl(ptv->socket, SIOCGSTAMP, &ts) == -1) {
+ /* FIXME */
+ return -1;
+ }
+
+ if ((ts.tv_sec > synctv->tv_sec) ||
+ (ts.tv_sec >= synctv->tv_sec &&
+ ts.tv_usec > synctv->tv_usec)) {
+ return 1;
+ }
+ return 0;
+}
+
+static int AFPReadAndDiscardFromRing(AFPThreadVars *ptv, struct timeval *synctv)
+{
+ union thdr h;
+
+ if (unlikely(suricata_ctl_flags != 0)) {
+ return 1;
+ }
+
+ /* Read packet from ring */
+ h.raw = (((union thdr **)ptv->frame_buf)[ptv->frame_offset]);
+ if (h.raw == NULL) {
+ return -1;
+ }
+
+ if (((time_t)h.h2->tp_sec > synctv->tv_sec) ||
+ ((time_t)h.h2->tp_sec == synctv->tv_sec &&
+ (suseconds_t) (h.h2->tp_nsec / 1000) > synctv->tv_usec)) {
+ return 1;
+ }
+
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ if (++ptv->frame_offset >= ptv->req.tp_frame_nr) {
+ ptv->frame_offset = 0;
+ }
+
+
+ return 0;
+}
+
+/** \brief wait for all afpacket threads to fully init
+ *
+ * Discard packets before all threads are ready, as the cluster
+ * setup is not complete yet.
+ *
+ * if AFPPeersListStarted() returns true init is complete
+ *
+ * \retval r 1 = happy, otherwise unhappy
+ */
+static int AFPSynchronizeStart(AFPThreadVars *ptv)
+{
+ int r;
+ struct timeval synctv;
+ struct pollfd fds;
+
+ fds.fd = ptv->socket;
+ fds.events = POLLIN;
+
+ /* Set timeval to end of the world */
+ synctv.tv_sec = 0xffffffff;
+ synctv.tv_usec = 0xffffffff;
+
+ while (1) {
+ r = poll(&fds, 1, POLL_TIMEOUT);
+ if (r > 0 &&
+ (fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+ SCLogWarning(SC_ERR_AFP_READ, "poll failed %02x",
+ fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL));
+ return 0;
+ } else if (r > 0) {
+ if (AFPPeersListStarted() && synctv.tv_sec == (time_t) 0xffffffff) {
+ gettimeofday(&synctv, NULL);
+ }
+ if (ptv->flags & AFP_RING_MODE) {
+ r = AFPReadAndDiscardFromRing(ptv, &synctv);
+ } else {
+ r = AFPReadAndDiscard(ptv, &synctv);
+ }
+ SCLogDebug("Discarding on %s", ptv->tv->name);
+ switch (r) {
+ case 1:
+ SCLogInfo("Starting to read on %s", ptv->tv->name);
+ return 1;
+ case -1:
+ return r;
+ }
+ /* no packets */
+ } else if (r == 0 && AFPPeersListStarted()) {
+ SCLogInfo("Starting to read on %s", ptv->tv->name);
+ return 1;
+ } else if (r < 0) { /* only exit on error */
+ SCLogWarning(SC_ERR_AFP_READ, "poll failed with retval %d", r);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * \brief Try to reopen socket
+ *
+ * \retval 0 in case of success, negative if error occurs or a condition
+ * is not met.
+ */
+static int AFPTryReopen(AFPThreadVars *ptv)
+{
+ int afp_activate_r;
+
+ ptv->down_count++;
+
+
+ /* Don't reconnect till we have packet that did not release data */
+ if (SC_ATOMIC_GET(ptv->mpeer->sock_usage) != 0) {
+ return -1;
+ }
+
+ afp_activate_r = AFPCreateSocket(ptv, ptv->iface, 0);
+ if (afp_activate_r != 0) {
+ if (ptv->down_count % AFP_DOWN_COUNTER_INTERVAL == 0) {
+ SCLogWarning(SC_ERR_AFP_CREATE, "Can not open iface '%s'",
+ ptv->iface);
+ }
+ return afp_activate_r;
+ }
+
+ SCLogInfo("Interface '%s' is back", ptv->iface);
+ return 0;
+}
+
+/**
+ * \brief Main AF_PACKET reading Loop function
+ */
+TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ AFPThreadVars *ptv = (AFPThreadVars *)data;
+ struct pollfd fds;
+ int r;
+ TmSlot *s = (TmSlot *)slot;
+ time_t last_dump = 0;
+ struct timeval current_time;
+
+ ptv->slot = s->slot_next;
+
+ if (ptv->afp_state == AFP_STATE_DOWN) {
+ /* Wait for our turn, threads before us must have opened the socket */
+ while (AFPPeersListWaitTurn(ptv->mpeer)) {
+ usleep(1000);
+ if (suricata_ctl_flags != 0) {
+ break;
+ }
+ }
+ r = AFPCreateSocket(ptv, ptv->iface, 1);
+ if (r < 0) {
+ switch (-r) {
+ case AFP_FATAL_ERROR:
+ SCLogError(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket, fatal error");
+ /* fatal is fatal, we want suri to exit */
+ EngineKill();
+ //tv->aof = THV_ENGINE_EXIT;
+ SCReturnInt(TM_ECODE_FAILED);
+ case AFP_RECOVERABLE_ERROR:
+ SCLogWarning(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket, retrying soon");
+ }
+ }
+ AFPPeersListReachedInc();
+ }
+ if (ptv->afp_state == AFP_STATE_UP) {
+ SCLogInfo("Thread %s using socket %d", tv->name, ptv->socket);
+ AFPSynchronizeStart(ptv);
+ }
+
+ fds.fd = ptv->socket;
+ fds.events = POLLIN;
+
+ while (1) {
+ /* Start by checking the state of our interface */
+ if (unlikely(ptv->afp_state == AFP_STATE_DOWN)) {
+ int dbreak = 0;
+
+ do {
+ usleep(AFP_RECONNECT_TIMEOUT);
+ if (suricata_ctl_flags != 0) {
+ dbreak = 1;
+ break;
+ }
+ r = AFPTryReopen(ptv);
+ fds.fd = ptv->socket;
+ } while (r < 0);
+ if (dbreak == 1)
+ break;
+ }
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ r = poll(&fds, 1, POLL_TIMEOUT);
+
+ if (suricata_ctl_flags != 0) {
+ break;
+ }
+
+ if (r > 0 &&
+ (fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+ if (fds.revents & (POLLHUP | POLLRDHUP)) {
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ continue;
+ } else if (fds.revents & POLLERR) {
+ char c;
+ /* Do a recv to get errno */
+ if (recv(ptv->socket, &c, sizeof c, MSG_PEEK) != -1)
+ continue; /* what, no error? */
+ SCLogError(SC_ERR_AFP_READ,
+ "Error reading data from iface '%s': (%d" PRIu32 ") %s",
+ ptv->iface, errno, strerror(errno));
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ continue;
+ } else if (fds.revents & POLLNVAL) {
+ SCLogError(SC_ERR_AFP_READ, "Invalid polling request");
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ continue;
+ }
+ } else if (r > 0) {
+ if (ptv->flags & AFP_RING_MODE) {
+ r = AFPReadFromRing(ptv);
+ } else {
+ /* AFPRead will call TmThreadsSlotProcessPkt on read packets */
+ r = AFPRead(ptv);
+ }
+ switch (r) {
+ case AFP_READ_FAILURE:
+ /* AFPRead in error: best to reset the socket */
+ SCLogError(SC_ERR_AFP_READ,
+ "AFPRead error reading data from iface '%s': (%d" PRIu32 ") %s",
+ ptv->iface, errno, strerror(errno));
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ continue;
+ case AFP_FAILURE:
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ SCReturnInt(TM_ECODE_FAILED);
+ break;
+ case AFP_READ_OK:
+ /* Trigger one dump of stats every second */
+ TimeGet(&current_time);
+ if (current_time.tv_sec != last_dump) {
+ AFPDumpCounters(ptv);
+ last_dump = current_time.tv_sec;
+ }
+ break;
+ case AFP_KERNEL_DROP:
+ AFPDumpCounters(ptv);
+ break;
+ }
+ } else if ((r < 0) && (errno != EINTR)) {
+ SCLogError(SC_ERR_AFP_READ, "Error reading data from iface '%s': (%d" PRIu32 ") %s",
+ ptv->iface,
+ errno, strerror(errno));
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+ continue;
+ }
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ AFPDumpCounters(ptv);
+ StatsSyncCountersIfSignalled(tv);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static int AFPGetDevFlags(int fd, const char *ifname)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
+ SCLogError(SC_ERR_AFP_CREATE, "Unable to find type for iface \"%s\": %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ return ifr.ifr_flags;
+}
+
+
+static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
+ if (verbose)
+ SCLogError(SC_ERR_AFP_CREATE, "Unable to find iface %s: %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ return ifr.ifr_ifindex;
+}
+
+static int AFPGetDevLinktype(int fd, const char *ifname)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
+ SCLogError(SC_ERR_AFP_CREATE, "Unable to find type for iface \"%s\": %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ switch (ifr.ifr_hwaddr.sa_family) {
+ case ARPHRD_LOOPBACK:
+ return LINKTYPE_ETHERNET;
+ case ARPHRD_PPP:
+ return LINKTYPE_RAW;
+ default:
+ return ifr.ifr_hwaddr.sa_family;
+ }
+}
+
+static int AFPComputeRingParams(AFPThreadVars *ptv, int order)
+{
+ /* Compute structure:
+ Target is to store all pending packets
+ with a size equal to MTU + auxdata
+ And we keep a decent number of block
+
+ To do so:
+ Compute frame_size (aligned to be able to fit in block
+ Check which block size we need. Blocksize is a 2^n * pagesize
+ We then need to get order, big enough to have
+ frame_size < block size
+ Find number of frame per block (divide)
+ Fill in packet_req
+
+ Compute frame size:
+ described in packet_mmap.txt
+ dependant on snaplen (need to use a variable ?)
+snaplen: MTU ?
+tp_hdrlen determine_version in daq_afpacket
+in V1: sizeof(struct tpacket_hdr);
+in V2: val in getsockopt(instance->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len)
+frame size: TPACKET_ALIGN(snaplen + TPACKET_ALIGN(TPACKET_ALIGN(tp_hdrlen) + sizeof(struct sockaddr_ll) + ETH_HLEN) - ETH_HLEN);
+
+ */
+ int tp_hdrlen = sizeof(struct tpacket_hdr);
+ int snaplen = default_packet_size;
+
+ if (snaplen == 0) {
+ snaplen = GetIfaceMaxPacketSize(ptv->iface);
+ if (snaplen <= 0) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "Unable to get MTU, setting snaplen to sane default of 1514");
+ snaplen = 1514;
+ }
+ }
+
+ ptv->req.tp_frame_size = TPACKET_ALIGN(snaplen +TPACKET_ALIGN(TPACKET_ALIGN(tp_hdrlen) + sizeof(struct sockaddr_ll) + ETH_HLEN) - ETH_HLEN);
+ ptv->req.tp_block_size = getpagesize() << order;
+ int frames_per_block = ptv->req.tp_block_size / ptv->req.tp_frame_size;
+ if (frames_per_block == 0) {
+ SCLogInfo("frame size to big");
+ return -1;
+ }
+ ptv->req.tp_frame_nr = ptv->ring_size;
+ ptv->req.tp_block_nr = ptv->req.tp_frame_nr / frames_per_block + 1;
+ /* exact division */
+ ptv->req.tp_frame_nr = ptv->req.tp_block_nr * frames_per_block;
+ SCLogInfo("AF_PACKET RX Ring params: block_size=%d block_nr=%d frame_size=%d frame_nr=%d",
+ ptv->req.tp_block_size, ptv->req.tp_block_nr,
+ ptv->req.tp_frame_size, ptv->req.tp_frame_nr);
+ return 1;
+}
+
+static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose)
+{
+ int r;
+ int ret = AFP_FATAL_ERROR;
+ struct packet_mreq sock_params;
+ struct sockaddr_ll bind_address;
+ int order;
+ unsigned int i;
+ int if_idx;
+
+ /* open socket */
+ ptv->socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (ptv->socket == -1) {
+ SCLogError(SC_ERR_AFP_CREATE, "Couldn't create a AF_PACKET socket, error %s", strerror(errno));
+ goto error;
+ }
+ if_idx = AFPGetIfnumByDev(ptv->socket, devname, verbose);
+ /* bind socket */
+ memset(&bind_address, 0, sizeof(bind_address));
+ bind_address.sll_family = AF_PACKET;
+ bind_address.sll_protocol = htons(ETH_P_ALL);
+ bind_address.sll_ifindex = if_idx;
+ if (bind_address.sll_ifindex == -1) {
+ if (verbose)
+ SCLogError(SC_ERR_AFP_CREATE, "Couldn't find iface %s", devname);
+ ret = AFP_RECOVERABLE_ERROR;
+ goto socket_err;
+ }
+
+ if (ptv->promisc != 0) {
+ /* Force promiscuous mode */
+ memset(&sock_params, 0, sizeof(sock_params));
+ sock_params.mr_type = PACKET_MR_PROMISC;
+ sock_params.mr_ifindex = bind_address.sll_ifindex;
+ r = setsockopt(ptv->socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP,(void *)&sock_params, sizeof(sock_params));
+ if (r < 0) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Couldn't switch iface %s to promiscuous, error %s",
+ devname, strerror(errno));
+ goto frame_err;
+ }
+ }
+
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_KERNEL) {
+ int val = 1;
+ if (setsockopt(ptv->socket, SOL_PACKET, PACKET_AUXDATA, &val,
+ sizeof(val)) == -1 && errno != ENOPROTOOPT) {
+ SCLogWarning(SC_ERR_NO_AF_PACKET,
+ "'kernel' checksum mode not supported, failling back to full mode.");
+ ptv->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ }
+ }
+
+ /* set socket recv buffer size */
+ if (ptv->buffer_size != 0) {
+ /*
+ * Set the socket buffer size to the specified value.
+ */
+ SCLogInfo("Setting AF_PACKET socket buffer to %d", ptv->buffer_size);
+ if (setsockopt(ptv->socket, SOL_SOCKET, SO_RCVBUF,
+ &ptv->buffer_size,
+ sizeof(ptv->buffer_size)) == -1) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Couldn't set buffer size to %d on iface %s, error %s",
+ ptv->buffer_size, devname, strerror(errno));
+ goto frame_err;
+ }
+ }
+
+ r = bind(ptv->socket, (struct sockaddr *)&bind_address, sizeof(bind_address));
+ if (r < 0) {
+ if (verbose) {
+ if (errno == ENETDOWN) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Couldn't bind AF_PACKET socket, iface %s is down",
+ devname);
+ } else {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Couldn't bind AF_PACKET socket to iface %s, error %s",
+ devname, strerror(errno));
+ }
+ }
+ ret = AFP_RECOVERABLE_ERROR;
+ goto frame_err;
+ }
+
+#ifdef HAVE_PACKET_FANOUT
+ /* add binded socket to fanout group */
+ if (ptv->threads > 1) {
+ uint32_t option = 0;
+ uint16_t mode = ptv->cluster_type;
+ uint16_t id = ptv->cluster_id;
+ option = (mode << 16) | (id & 0xffff);
+ r = setsockopt(ptv->socket, SOL_PACKET, PACKET_FANOUT,(void *)&option, sizeof(option));
+ if (r < 0) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Coudn't set fanout mode, error %s",
+ strerror(errno));
+ goto frame_err;
+ }
+ }
+#endif
+
+ int if_flags = AFPGetDevFlags(ptv->socket, ptv->iface);
+ if (if_flags == -1) {
+ if (verbose) {
+ SCLogError(SC_ERR_AFP_READ,
+ "Can not acces to interface '%s'",
+ ptv->iface);
+ }
+ ret = AFP_RECOVERABLE_ERROR;
+ goto frame_err;
+ }
+ if ((if_flags & IFF_UP) == 0) {
+ if (verbose) {
+ SCLogError(SC_ERR_AFP_READ,
+ "Interface '%s' is down",
+ ptv->iface);
+ }
+ ret = AFP_RECOVERABLE_ERROR;
+ goto frame_err;
+ }
+
+ if (ptv->flags & AFP_RING_MODE) {
+ int val = TPACKET_V2;
+ unsigned int len = sizeof(val);
+ if (getsockopt(ptv->socket, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
+ if (errno == ENOPROTOOPT) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Too old kernel giving up (need 2.6.27 at least)");
+ }
+ SCLogError(SC_ERR_AFP_CREATE, "Error when retrieving packet header len");
+ goto socket_err;
+ }
+ ptv->tp_hdrlen = val;
+
+ val = TPACKET_V2;
+ if (setsockopt(ptv->socket, SOL_PACKET, PACKET_VERSION, &val,
+ sizeof(val)) < 0) {
+ SCLogError(SC_ERR_AFP_CREATE,
+ "Can't activate TPACKET_V2 on packet socket: %s",
+ strerror(errno));
+ goto socket_err;
+ }
+
+ /* Allocate RX ring */
+#define DEFAULT_ORDER 3
+ for (order = DEFAULT_ORDER; order >= 0; order--) {
+ if (AFPComputeRingParams(ptv, order) != 1) {
+ SCLogInfo("Ring parameter are incorrect. Please correct the devel");
+ }
+
+ r = setsockopt(ptv->socket, SOL_PACKET, PACKET_RX_RING, (void *) &ptv->req, sizeof(ptv->req));
+ if (r < 0) {
+ if (errno == ENOMEM) {
+ SCLogInfo("Memory issue with ring parameters. Retrying.");
+ continue;
+ }
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Unable to allocate RX Ring for iface %s: (%d) %s",
+ devname,
+ errno,
+ strerror(errno));
+ goto socket_err;
+ } else {
+ break;
+ }
+ }
+
+ if (order < 0) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Unable to allocate RX Ring for iface %s (order 0 failed)",
+ devname);
+ goto socket_err;
+ }
+
+ /* Allocate the Ring */
+ ptv->ring_buflen = ptv->req.tp_block_nr * ptv->req.tp_block_size;
+ ptv->ring_buf = mmap(0, ptv->ring_buflen, PROT_READ|PROT_WRITE,
+ MAP_SHARED, ptv->socket, 0);
+ if (ptv->ring_buf == MAP_FAILED) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to mmap");
+ goto socket_err;
+ }
+ /* allocate a ring for each frame header pointer*/
+ ptv->frame_buf = SCMalloc(ptv->req.tp_frame_nr * sizeof (union thdr *));
+ if (ptv->frame_buf == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate frame buf");
+ goto mmap_err;
+ }
+ memset(ptv->frame_buf, 0, ptv->req.tp_frame_nr * sizeof (union thdr *));
+ /* fill the header ring with proper frame ptr*/
+ ptv->frame_offset = 0;
+ for (i = 0; i < ptv->req.tp_block_nr; ++i) {
+ void *base = &ptv->ring_buf[i * ptv->req.tp_block_size];
+ unsigned int j;
+ for (j = 0; j < ptv->req.tp_block_size / ptv->req.tp_frame_size; ++j, ++ptv->frame_offset) {
+ (((union thdr **)ptv->frame_buf)[ptv->frame_offset]) = base;
+ base += ptv->req.tp_frame_size;
+ }
+ }
+ ptv->frame_offset = 0;
+ }
+
+ SCLogInfo("Using interface '%s' via socket %d", (char *)devname, ptv->socket);
+
+
+ ptv->datalink = AFPGetDevLinktype(ptv->socket, ptv->iface);
+ switch (ptv->datalink) {
+ case ARPHRD_PPP:
+ case ARPHRD_ATM:
+ ptv->cooked = 1;
+ break;
+ }
+
+ TmEcode rc;
+ rc = AFPSetBPFFilter(ptv);
+ if (rc == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_AFP_CREATE, "Set AF_PACKET bpf filter \"%s\" failed.", ptv->bpf_filter);
+ goto frame_err;
+ }
+
+ /* Init is ok */
+ AFPSwitchState(ptv, AFP_STATE_UP);
+ return 0;
+
+frame_err:
+ if (ptv->frame_buf)
+ SCFree(ptv->frame_buf);
+mmap_err:
+ /* Packet mmap does the cleaning when socket is closed */
+socket_err:
+ close(ptv->socket);
+ ptv->socket = -1;
+error:
+ return -ret;
+}
+
+TmEcode AFPSetBPFFilter(AFPThreadVars *ptv)
+{
+ struct bpf_program filter;
+ struct sock_fprog fcode;
+ int rc;
+
+ if (!ptv->bpf_filter)
+ return TM_ECODE_OK;
+
+ SCMutexLock(&afpacket_bpf_set_filter_lock);
+
+ SCLogInfo("Using BPF '%s' on iface '%s'",
+ ptv->bpf_filter,
+ ptv->iface);
+ if (pcap_compile_nopcap(default_packet_size, /* snaplen_arg */
+ ptv->datalink, /* linktype_arg */
+ &filter, /* program */
+ ptv->bpf_filter, /* const char *buf */
+ 0, /* optimize */
+ 0 /* mask */
+ ) == -1) {
+ SCLogError(SC_ERR_AFP_CREATE, "Filter compilation failed.");
+ SCMutexUnlock(&afpacket_bpf_set_filter_lock);
+ return TM_ECODE_FAILED;
+ }
+ SCMutexUnlock(&afpacket_bpf_set_filter_lock);
+
+ if (filter.bf_insns == NULL) {
+ SCLogError(SC_ERR_AFP_CREATE, "Filter badly setup.");
+ return TM_ECODE_FAILED;
+ }
+
+ fcode.len = filter.bf_len;
+ fcode.filter = (struct sock_filter*)filter.bf_insns;
+
+ rc = setsockopt(ptv->socket, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof(fcode));
+
+ if(rc == -1) {
+ SCLogError(SC_ERR_AFP_CREATE, "Failed to attach filter: %s", strerror(errno));
+ return TM_ECODE_FAILED;
+ }
+
+ return TM_ECODE_OK;
+}
+
+
+/**
+ * \brief Init function for ReceiveAFP.
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the interface passed from the user
+ * \param data pointer gets populated with AFPThreadVars
+ *
+ * \todo Create a general AFP setup function.
+ */
+TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ AFPIfaceConfig *afpconfig = initdata;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ AFPThreadVars *ptv = SCMalloc(sizeof(AFPThreadVars));
+ if (unlikely(ptv == NULL)) {
+ afpconfig->DerefFunc(afpconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(ptv, 0, sizeof(AFPThreadVars));
+
+ ptv->tv = tv;
+ ptv->cooked = 0;
+
+ strlcpy(ptv->iface, afpconfig->iface, AFP_IFACE_NAME_LENGTH);
+ ptv->iface[AFP_IFACE_NAME_LENGTH - 1]= '\0';
+
+ ptv->livedev = LiveGetDevice(ptv->iface);
+ if (ptv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ SCFree(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ptv->buffer_size = afpconfig->buffer_size;
+ ptv->ring_size = afpconfig->ring_size;
+
+ ptv->promisc = afpconfig->promisc;
+ ptv->checksum_mode = afpconfig->checksum_mode;
+ ptv->bpf_filter = NULL;
+
+ ptv->threads = 1;
+#ifdef HAVE_PACKET_FANOUT
+ ptv->cluster_type = PACKET_FANOUT_LB;
+ ptv->cluster_id = 1;
+ /* We only set cluster info if the number of reader threads is greater than 1 */
+ if (afpconfig->threads > 1) {
+ ptv->cluster_id = afpconfig->cluster_id;
+ ptv->cluster_type = afpconfig->cluster_type;
+ ptv->threads = afpconfig->threads;
+ }
+#endif
+ ptv->flags = afpconfig->flags;
+
+ if (afpconfig->bpf_filter) {
+ ptv->bpf_filter = afpconfig->bpf_filter;
+ }
+
+#ifdef PACKET_STATISTICS
+ ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ptv->tv);
+ ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ptv->tv);
+#endif
+
+ char *active_runmode = RunmodeGetActive();
+
+ if (active_runmode && !strcmp("workers", active_runmode)) {
+ ptv->flags |= AFP_ZERO_COPY;
+ SCLogInfo("Enabling zero copy mode");
+ } else {
+ /* If we are using copy mode we need a lock */
+ ptv->flags |= AFP_SOCK_PROTECT;
+ }
+
+ /* If we are in RING mode, then we can use ZERO copy
+ * by using the data release mechanism */
+ if (ptv->flags & AFP_RING_MODE) {
+ ptv->flags |= AFP_ZERO_COPY;
+ SCLogInfo("Enabling zero copy mode by using data release call");
+ }
+
+ ptv->copy_mode = afpconfig->copy_mode;
+ if (ptv->copy_mode != AFP_COPY_MODE_NONE) {
+ strlcpy(ptv->out_iface, afpconfig->out_iface, AFP_IFACE_NAME_LENGTH);
+ ptv->out_iface[AFP_IFACE_NAME_LENGTH - 1]= '\0';
+ /* Warn about BPF filter consequence */
+ if (ptv->bpf_filter) {
+ SCLogWarning(SC_WARN_UNCOMMON, "Enabling a BPF filter in IPS mode result"
+ " in dropping all non matching packets.");
+ }
+ }
+
+
+ if (AFPPeersListAdd(ptv) == TM_ECODE_FAILED) {
+ SCFree(ptv);
+ afpconfig->DerefFunc(afpconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+#define T_DATA_SIZE 70000
+ ptv->data = SCMalloc(T_DATA_SIZE);
+ if (ptv->data == NULL) {
+ afpconfig->DerefFunc(afpconfig);
+ SCFree(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ ptv->datalen = T_DATA_SIZE;
+#undef T_DATA_SIZE
+
+ *data = (void *)ptv;
+
+ afpconfig->DerefFunc(afpconfig);
+
+ /* A bit strange to have this here but we only have vlan information
+ * during reading so we need to know if we want to keep vlan during
+ * the capture phase */
+ int vlanbool = 0;
+ if ((ConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
+ ptv->vlan_disabled = 1;
+ }
+
+ /* If kernel is older than 3.0, VLAN is not stripped so we don't
+ * get the info from packet extended header but we will use a standard
+ * parsing of packet data (See Linux commit bcc6d47903612c3861201cc3a866fb604f26b8b2) */
+ if (! SCKernelVersionIsAtLeast(3, 0)) {
+ ptv->vlan_disabled = 1;
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into AFPThreadVars for ptv
+ */
+void ReceiveAFPThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ AFPThreadVars *ptv = (AFPThreadVars *)data;
+
+#ifdef PACKET_STATISTICS
+ AFPDumpCounters(ptv);
+ SCLogInfo("(%s) Kernel: Packets %" PRIu64 ", dropped %" PRIu64 "",
+ tv->name,
+ StatsGetLocalCounterValue(tv, ptv->capture_kernel_packets),
+ StatsGetLocalCounterValue(tv, ptv->capture_kernel_drops));
+#endif
+
+ SCLogInfo("(%s) Packets %" PRIu64 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
+}
+
+/**
+ * \brief DeInit function closes af packet socket at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into AFPThreadVars for ptv
+ */
+TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data)
+{
+ AFPThreadVars *ptv = (AFPThreadVars *)data;
+
+ AFPSwitchState(ptv, AFP_STATE_DOWN);
+
+ if (ptv->data != NULL) {
+ SCFree(ptv->data);
+ ptv->data = NULL;
+ }
+ ptv->datalen = 0;
+
+ ptv->bpf_filter = NULL;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * DecodeAFP reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param t pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into AFPThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ */
+TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* If suri has set vlan during reading, we increase vlan counter */
+ if (p->vlan_idx) {
+ StatsIncr(tv, dtv->counter_vlan);
+ }
+
+ /* call the decoder */
+ switch(p->datalink) {
+ case LINKTYPE_LINUX_SLL:
+ DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_ETHERNET:
+ DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_PPP:
+ DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_RAW:
+ DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ default:
+ SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodeAFP", p->datalink);
+ break;
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeAFPThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
+ SCReturnInt(TM_ECODE_FAILED);
+#endif
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeAFPThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif /* HAVE_AF_PACKET */
+/* eof */
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/source-af-packet.h b/framework/src/suricata/src/source-af-packet.h
new file mode 100644
index 00000000..61f4e69e
--- /dev/null
+++ b/framework/src/suricata/src/source-af-packet.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2011,2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __SOURCE_AFP_H__
+#define __SOURCE_AFP_H__
+
+#ifndef HAVE_PACKET_FANOUT /* not defined if linux/if_packet.h trying to force */
+#define HAVE_PACKET_FANOUT 1
+
+#define PACKET_FANOUT 18
+
+#define PACKET_FANOUT_HASH 0
+#define PACKET_FANOUT_LB 1
+#define PACKET_FANOUT_CPU 2
+#define PACKET_FANOUT_ROLLOVER 3
+#define PACKET_FANOUT_RND 4
+#define PACKET_FANOUT_QM 5
+
+#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000
+#define PACKET_FANOUT_FLAG_DEFRAG 0x8000
+#else /* HAVE_PACKET_FANOUT */
+#include <linux/if_packet.h>
+#endif /* HAVE_PACKET_FANOUT */
+#include "queue.h"
+
+/* value for flags */
+#define AFP_RING_MODE (1<<0)
+#define AFP_ZERO_COPY (1<<1)
+#define AFP_SOCK_PROTECT (1<<2)
+#define AFP_EMERGENCY_MODE (1<<3)
+
+#define AFP_COPY_MODE_NONE 0
+#define AFP_COPY_MODE_TAP 1
+#define AFP_COPY_MODE_IPS 2
+
+#define AFP_FILE_MAX_PKTS 256
+#define AFP_IFACE_NAME_LENGTH 48
+
+typedef struct AFPIfaceConfig_
+{
+ char iface[AFP_IFACE_NAME_LENGTH];
+ /* number of threads */
+ int threads;
+ /* socket buffer size */
+ int buffer_size;
+ /* ring size in number of packets */
+ int ring_size;
+ /* cluster param */
+ int cluster_id;
+ int cluster_type;
+ /* promisc mode */
+ int promisc;
+ /* misc use flags including ring mode */
+ int flags;
+ int copy_mode;
+ ChecksumValidationMode checksum_mode;
+ char *bpf_filter;
+ char *out_iface;
+ SC_ATOMIC_DECLARE(unsigned int, ref);
+ void (*DerefFunc)(void *);
+} AFPIfaceConfig;
+
+/**
+ * \ingroup afppeers
+ * @{
+ */
+
+typedef struct AFPPeer_ {
+ char iface[AFP_IFACE_NAME_LENGTH];
+ SC_ATOMIC_DECLARE(int, socket);
+ SC_ATOMIC_DECLARE(int, sock_usage);
+ SC_ATOMIC_DECLARE(int, if_idx);
+ SC_ATOMIC_DECLARE(uint8_t, state);
+ SCMutex sock_protect;
+ int flags;
+ int turn; /**< Field used to store initialisation order. */
+ struct AFPPeer_ *peer;
+ TAILQ_ENTRY(AFPPeer_) next;
+} AFPPeer;
+
+/**
+ * \brief per packet AF_PACKET vars
+ *
+ * This structure is used y the release data system and is cleaned
+ * up by the AFPV_CLEANUP macro below.
+ */
+typedef struct AFPPacketVars_
+{
+ void *relptr;
+ int copy_mode;
+ AFPPeer *peer; /**< Sending peer for IPS/TAP mode */
+ /** Pointer to ::AFPPeer used for capture. Field is used to be able
+ * to do reference counting.
+ */
+ AFPPeer *mpeer;
+} AFPPacketVars;
+
+#define AFPV_CLEANUP(afpv) do { \
+ (afpv)->relptr = NULL; \
+ (afpv)->copy_mode = 0; \
+ (afpv)->peer = NULL; \
+ (afpv)->mpeer = NULL; \
+} while(0)
+
+/**
+ * @}
+ */
+
+void TmModuleReceiveAFPRegister (void);
+void TmModuleDecodeAFPRegister (void);
+
+TmEcode AFPPeersListInit();
+TmEcode AFPPeersListCheck();
+void AFPPeersListClean();
+
+
+#endif /* __SOURCE_AFP_H__ */
diff --git a/framework/src/suricata/src/source-erf-dag.c b/framework/src/suricata/src/source-erf-dag.c
new file mode 100644
index 00000000..f6640712
--- /dev/null
+++ b/framework/src/suricata/src/source-erf-dag.c
@@ -0,0 +1,670 @@
+/* Copyright (C) 2010-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ * \author Jason MacLulich <jason.maclulich@endace.com>
+ *
+ * Support for reading ERF records from a DAG card.
+ *
+ * Only ethernet supported at this time.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "tm-threads.h"
+
+#include "util-privs.h"
+#include "util-device.h"
+#include "tmqh-packetpool.h"
+
+#ifndef HAVE_DAG
+
+TmEcode NoErfDagSupportExit(ThreadVars *, void *, void **);
+
+void
+TmModuleReceiveErfDagRegister(void)
+{
+ tmm_modules[TMM_RECEIVEERFDAG].name = "ReceiveErfDag";
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadInit = NoErfDagSupportExit;
+ tmm_modules[TMM_RECEIVEERFDAG].Func = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].cap_flags = SC_CAP_NET_ADMIN;
+ tmm_modules[TMM_RECEIVEERFDAG].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void
+TmModuleDecodeErfDagRegister(void)
+{
+ tmm_modules[TMM_DECODEERFDAG].name = "DecodeErfDag";
+ tmm_modules[TMM_DECODEERFDAG].ThreadInit = NoErfDagSupportExit;
+ tmm_modules[TMM_DECODEERFDAG].Func = NULL;
+ tmm_modules[TMM_DECODEERFDAG].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEERFDAG].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODEERFDAG].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEERFDAG].cap_flags = 0;
+ tmm_modules[TMM_DECODEERFDAG].flags = TM_FLAG_DECODE_TM;
+}
+
+TmEcode
+NoErfDagSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_DAG_NOSUPPORT,
+ "Error creating thread %s: you do not have support for DAG cards "
+ "enabled please recompile with --enable-dag", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* Implied we do have DAG support */
+
+#include "source-erf-dag.h"
+#include <dagapi.h>
+
+/* Minimum amount of data to read from the DAG at a time. */
+#define MINDATA 32768
+
+/* Maximum time (us) to wait for MINDATA to be read. */
+#define MAXWAIT 20000
+
+/* Poll interval in microseconds. */
+#define POLL_INTERVAL 1000;
+
+/* Number of bytes per loop to process before fetching more data. */
+#define BYTES_PER_LOOP (4 * 1024 * 1024) /* 4 MB */
+
+extern int max_pending_packets;
+
+typedef struct ErfDagThreadVars_ {
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ int dagfd;
+ int dagstream;
+ char dagname[DAGNAME_BUFSIZE];
+
+ struct timeval maxwait, poll; /* Could possibly be made static */
+
+ LiveDevice *livedev;
+
+ uint64_t bytes;
+ uint16_t packets;
+ uint16_t drops;
+
+ /* Current location in the DAG stream input buffer.
+ */
+ uint8_t *top;
+ uint8_t *btm;
+
+} ErfDagThreadVars;
+
+static inline TmEcode ProcessErfDagRecords(ErfDagThreadVars *ewtn, uint8_t *top,
+ uint32_t *pkts_read);
+static inline TmEcode ProcessErfDagRecord(ErfDagThreadVars *ewtn, char *prec);
+TmEcode ReceiveErfDagLoop(ThreadVars *, void *data, void *slot);
+TmEcode ReceiveErfDagThreadInit(ThreadVars *, void *, void **);
+void ReceiveErfDagThreadExitStats(ThreadVars *, void *);
+TmEcode ReceiveErfDagThreadDeinit(ThreadVars *, void *);
+TmEcode DecodeErfDagThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeErfDagThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeErfDag(ThreadVars *, Packet *, void *, PacketQueue *,
+ PacketQueue *);
+void ReceiveErfDagCloseStream(int dagfd, int stream);
+
+/**
+ * \brief Register the ERF file receiver (reader) module.
+ */
+void
+TmModuleReceiveErfDagRegister(void)
+{
+ tmm_modules[TMM_RECEIVEERFDAG].name = "ReceiveErfDag";
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadInit = ReceiveErfDagThreadInit;
+ tmm_modules[TMM_RECEIVEERFDAG].Func = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].PktAcqLoop = ReceiveErfDagLoop;
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadExitPrintStats =
+ ReceiveErfDagThreadExitStats;
+ tmm_modules[TMM_RECEIVEERFDAG].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEERFDAG].cap_flags = 0;
+ tmm_modules[TMM_RECEIVEERFDAG].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Register the ERF file decoder module.
+ */
+void
+TmModuleDecodeErfDagRegister(void)
+{
+ tmm_modules[TMM_DECODEERFDAG].name = "DecodeErfDag";
+ tmm_modules[TMM_DECODEERFDAG].ThreadInit = DecodeErfDagThreadInit;
+ tmm_modules[TMM_DECODEERFDAG].Func = DecodeErfDag;
+ tmm_modules[TMM_DECODEERFDAG].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEERFDAG].ThreadDeinit = DecodeErfDagThreadDeinit;
+ tmm_modules[TMM_DECODEERFDAG].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEERFDAG].cap_flags = 0;
+ tmm_modules[TMM_DECODEERFDAG].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief Initialize the ERF receiver thread, generate a single
+ * ErfDagThreadVar structure for each thread, this will
+ * contain a DAG file descriptor which is read when the
+ * thread executes.
+ *
+ * \param tv Thread variable to ThreadVars
+ * \param initdata Initial data to the interface passed from the user,
+ * this is processed by the user.
+ *
+ * We assume that we have only a single name for the DAG
+ * interface.
+ *
+ * \param data data pointer gets populated with
+ *
+ */
+TmEcode
+ReceiveErfDagThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ int stream_count = 0;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Error: No DAG interface provided.");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ErfDagThreadVars *ewtn = SCMalloc(sizeof(ErfDagThreadVars));
+ if (unlikely(ewtn == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for ERF DAG thread vars.");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(ewtn, 0, sizeof(*ewtn));
+
+ /* dag_parse_name will return a DAG device name and stream number
+ * to open for this thread.
+ */
+ if (dag_parse_name(initdata, ewtn->dagname, DAGNAME_BUFSIZE,
+ &ewtn->dagstream) < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to parse DAG interface: %s",
+ (char*)initdata);
+ SCFree(ewtn);
+ exit(EXIT_FAILURE);
+ }
+
+ ewtn->livedev = LiveGetDevice(initdata);
+ if (ewtn->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to get %s live device",
+ (char *)initdata);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("Opening DAG: %s on stream: %d for processing",
+ ewtn->dagname, ewtn->dagstream);
+
+ if ((ewtn->dagfd = dag_open(ewtn->dagname)) < 0) {
+ SCLogError(SC_ERR_ERF_DAG_OPEN_FAILED, "Failed to open DAG: %s",
+ ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* Check to make sure the card has enough available streams to
+ * support reading from the one specified.
+ */
+ if ((stream_count = dag_rx_get_stream_count(ewtn->dagfd)) < 0) {
+ SCLogError(SC_ERR_ERF_DAG_OPEN_FAILED,
+ "Failed to open stream: %d, DAG: %s, could not query stream count",
+ ewtn->dagstream, ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* Check to make sure we have enough rx streams to open the stream
+ * the user is asking for.
+ */
+ if (ewtn->dagstream > stream_count * 2) {
+ SCLogError(SC_ERR_ERF_DAG_OPEN_FAILED,
+ "Failed to open stream: %d, DAG: %s, insufficient streams: %d",
+ ewtn->dagstream, ewtn->dagname, stream_count);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* If we are transmitting into a soft DAG card then set the stream
+ * to act in reverse mode.
+ */
+ if (0 != (ewtn->dagstream & 0x01)) {
+ /* Setting reverse mode for using with soft dag from daemon side */
+ if (dag_set_mode(ewtn->dagfd, ewtn->dagstream, DAG_REVERSE_MODE)) {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_OPEN_FAILED,
+ "Failed to set mode to DAG_REVERSE_MODE on stream: %d, DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ if (dag_attach_stream(ewtn->dagfd, ewtn->dagstream, 0, 0) < 0) {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_OPEN_FAILED,
+ "Failed to open DAG stream: %d, DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (dag_start_stream(ewtn->dagfd, ewtn->dagstream) < 0) {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_START_FAILED,
+ "Failed to start DAG stream: %d, DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("Attached and started stream: %d on DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+
+ /*
+ * Initialise DAG Polling parameters.
+ */
+ timerclear(&ewtn->maxwait);
+ ewtn->maxwait.tv_usec = MAXWAIT;
+ timerclear(&ewtn->poll);
+ ewtn->poll.tv_usec = POLL_INTERVAL;
+
+ /* 32kB minimum data to return -- we still restrict the number of
+ * pkts that are processed to a maximum of dag_max_read_packets.
+ */
+ if (dag_set_stream_poll(ewtn->dagfd, ewtn->dagstream, MINDATA,
+ &(ewtn->maxwait), &(ewtn->poll)) < 0) {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_SET_FAILED,
+ "Failed to set poll parameters for stream: %d, DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+ SCFree(ewtn);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ewtn->packets = StatsRegisterCounter("capture.dag_packets", tv);
+ ewtn->drops = StatsRegisterCounter("capture.dag_drops", tv);
+
+ ewtn->tv = tv;
+ *data = (void *)ewtn;
+
+ SCLogInfo("Starting processing packets from stream: %d on DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Receives packets from a DAG interface.
+ *
+ * \param tv pointer to ThreadVars
+ * \param data pointer to ErfDagThreadVars
+ * \param slot slot containing task information
+ *
+ * \retval TM_ECODE_OK on success
+ * \retval TM_ECODE_FAILED on failure
+ */
+TmEcode
+ReceiveErfDagLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ ErfDagThreadVars *dtv = (ErfDagThreadVars *)data;
+ uint32_t diff = 0;
+ int err;
+ uint8_t *top = NULL;
+ uint32_t pkts_read = 0;
+ TmSlot *s = (TmSlot *)slot;
+
+ dtv->slot = s->slot_next;
+
+ while (1) {
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ top = dag_advance_stream(dtv->dagfd, dtv->dagstream, &(dtv->btm));
+ if (top == NULL) {
+ if (errno == EAGAIN) {
+ if (dtv->dagstream & 0x1) {
+ usleep(10 * 1000);
+ dtv->btm = dtv->top;
+ }
+ continue;
+ } else {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_READ_FAILED,
+ "Failed to read from stream: %d, DAG: %s when "
+ "using dag_advance_stream",
+ dtv->dagstream, dtv->dagname);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ diff = top - dtv->btm;
+ if (diff == 0) {
+ continue;
+ }
+
+ assert(diff >= dag_record_size);
+
+ err = ProcessErfDagRecords(dtv, top, &pkts_read);
+
+ if (err == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_ERF_DAG_STREAM_READ_FAILED,
+ "Failed to read from stream: %d, DAG: %s",
+ dtv->dagstream, dtv->dagname);
+ ReceiveErfDagCloseStream(dtv->dagfd, dtv->dagstream);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ StatsSyncCountersIfSignalled(tv);
+
+ SCLogDebug("Read %d records from stream: %d, DAG: %s",
+ pkts_read, dtv->dagstream, dtv->dagname);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Process a chunk of records read from a DAG interface.
+ *
+ * This function takes a pointer to buffer read from the DAG interface
+ * and processes it individual records.
+ */
+static inline TmEcode
+ProcessErfDagRecords(ErfDagThreadVars *ewtn, uint8_t *top, uint32_t *pkts_read)
+{
+ SCEnter();
+
+ int err = 0;
+ dag_record_t *dr = NULL;
+ char *prec = NULL;
+ int rlen;
+ char hdr_type = 0;
+ int processed = 0;
+
+ *pkts_read = 0;
+
+ while (((top - ewtn->btm) >= dag_record_size) &&
+ ((processed + dag_record_size) < BYTES_PER_LOOP)) {
+
+ /* Make sure we have at least one packet in the packet pool,
+ * to prevent us from alloc'ing packets at line rate. */
+ PacketPoolWait();
+
+ prec = (char *)ewtn->btm;
+ dr = (dag_record_t*)prec;
+ rlen = ntohs(dr->rlen);
+ hdr_type = dr->type;
+
+ /* If we don't have enough data to finish processing this ERF
+ * record return and maybe next time we will.
+ */
+ if ((top - ewtn->btm) < rlen)
+ SCReturnInt(TM_ECODE_OK);
+
+ ewtn->btm += rlen;
+ processed += rlen;
+
+ /* Only support ethernet at this time. */
+ switch (hdr_type & 0x7f) {
+ case TYPE_PAD:
+ /* Skip. */
+ continue;
+ case TYPE_DSM_COLOR_ETH:
+ case TYPE_COLOR_ETH:
+ case TYPE_COLOR_HASH_ETH:
+ /* In these types the color value overwrites the lctr
+ * (drop count). */
+ break;
+ case TYPE_ETH:
+ if (dr->lctr) {
+ StatsAddUI64(ewtn->tv, ewtn->drops, ntohs(dr->lctr));
+ }
+ break;
+ default:
+ SCLogError(SC_ERR_UNIMPLEMENTED,
+ "Processing of DAG record type: %d not implemented.", dr->type);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ err = ProcessErfDagRecord(ewtn, prec);
+ if (err != TM_ECODE_OK) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ (*pkts_read)++;
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Process a DAG record into a TM packet buffer.
+ * \param prec pointer to a DAG record.
+ * \param
+ */
+static inline TmEcode
+ProcessErfDagRecord(ErfDagThreadVars *ewtn, char *prec)
+{
+ SCEnter();
+
+ int wlen = 0;
+ int rlen = 0;
+ int hdr_num = 0;
+ char hdr_type = 0;
+ dag_record_t *dr = (dag_record_t*)prec;
+ erf_payload_t *pload;
+ Packet *p;
+
+ hdr_type = dr->type;
+ wlen = ntohs(dr->wlen);
+ rlen = ntohs(dr->rlen);
+
+ /* count extension headers */
+ while (hdr_type & 0x80) {
+ if (rlen < (dag_record_size + (hdr_num * 8))) {
+ SCLogError(SC_ERR_UNIMPLEMENTED,
+ "Insufficient captured packet length.");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ hdr_type = prec[(dag_record_size + (hdr_num * 8))];
+ hdr_num++;
+ }
+
+ /* Check that the whole frame was captured */
+ if (rlen < (dag_record_size + (8 * hdr_num) + 2 + wlen)) {
+ SCLogInfo("Incomplete frame captured.");
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* skip over extension headers */
+ pload = (erf_payload_t *)(prec + dag_record_size + (8 * hdr_num));
+
+ p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate a Packet on stream: %d, DAG: %s",
+ ewtn->dagstream, ewtn->dagname);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ SET_PKT_LEN(p, wlen);
+ p->datalink = LINKTYPE_ETHERNET;
+
+ /* Take into account for link type Ethernet ETH frame starts
+ * after ther ERF header + pad.
+ */
+ if (unlikely(PacketCopyData(p, pload->eth.dst, GET_PKT_LEN(p)))) {
+ TmqhOutputPacketpool(ewtn->tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* Convert ERF time to timeval - from libpcap. */
+ uint64_t ts = dr->ts;
+ p->ts.tv_sec = ts >> 32;
+ ts = (ts & 0xffffffffULL) * 1000000;
+ ts += 0x80000000; /* rounding */
+ p->ts.tv_usec = ts >> 32;
+ if (p->ts.tv_usec >= 1000000) {
+ p->ts.tv_usec -= 1000000;
+ p->ts.tv_sec++;
+ }
+
+ StatsIncr(ewtn->tv, ewtn->packets);
+ ewtn->bytes += wlen;
+
+ if (TmThreadsSlotProcessPkt(ewtn->tv, ewtn->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ewtn->tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Print some stats to the log at program exit.
+ *
+ * \param tv Pointer to ThreadVars.
+ * \param data Pointer to data, ErfFileThreadVars.
+ */
+void
+ReceiveErfDagThreadExitStats(ThreadVars *tv, void *data)
+{
+ ErfDagThreadVars *ewtn = (ErfDagThreadVars *)data;
+
+ (void)SC_ATOMIC_SET(ewtn->livedev->pkts,
+ StatsGetLocalCounterValue(tv, ewtn->packets));
+ (void)SC_ATOMIC_SET(ewtn->livedev->drop,
+ StatsGetLocalCounterValue(tv, ewtn->drops));
+
+ SCLogInfo("Stream: %d; Bytes: %"PRIu64"; Packets: %"PRIu64
+ "; Drops: %"PRIu64,
+ ewtn->dagstream,
+ ewtn->bytes,
+ StatsGetLocalCounterValue(tv, ewtn->packets),
+ StatsGetLocalCounterValue(tv, ewtn->drops));
+}
+
+/**
+ * \brief Deinitializes the DAG card.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ */
+TmEcode
+ReceiveErfDagThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+
+ ErfDagThreadVars *ewtn = (ErfDagThreadVars *)data;
+
+ ReceiveErfDagCloseStream(ewtn->dagfd, ewtn->dagstream);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void
+ReceiveErfDagCloseStream(int dagfd, int stream)
+{
+ dag_stop_stream(dagfd, stream);
+ dag_detach_stream(dagfd, stream);
+ dag_close(dagfd);
+}
+
+/** Decode ErfDag */
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * DecodeErfDag reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param t pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ */
+TmEcode
+DecodeErfDag(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
+ PacketQueue *postpq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* call the decoder */
+ switch(p->datalink) {
+ case LINKTYPE_ETHERNET:
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ default:
+ SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED,
+ "Error: datalink type %" PRId32
+ " not yet supported in module DecodeErfDag",
+ p->datalink);
+ break;
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode
+DecodeErfDagThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode
+DecodeErfDagThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif /* HAVE_DAG */
diff --git a/framework/src/suricata/src/source-erf-dag.h b/framework/src/suricata/src/source-erf-dag.h
new file mode 100644
index 00000000..e712798b
--- /dev/null
+++ b/framework/src/suricata/src/source-erf-dag.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited
+ * \author Jason MacLulich <jason.maclulich@endace.com>
+ */
+
+#ifndef __SOURCE_ERF_DAG_H__
+#define __SOURCE_ERF_DAG_H__
+
+void TmModuleReceiveErfDagRegister(void);
+void TmModuleDecodeErfDagRegister(void);
+
+#endif /* __SOURCE_ERF_DAG_H__ */
+
diff --git a/framework/src/suricata/src/source-erf-file.c b/framework/src/suricata/src/source-erf-file.c
new file mode 100644
index 00000000..938621c4
--- /dev/null
+++ b/framework/src/suricata/src/source-erf-file.c
@@ -0,0 +1,308 @@
+/* Copyright (C) 2010-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ *
+ * Support for reading ERF files.
+ *
+ * Only ethernet supported at this time.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "tm-threads.h"
+
+#define DAG_TYPE_ETH 2
+
+typedef struct DagFlags_ {
+ uint8_t iface:2;
+ uint8_t vlen:1;
+ uint8_t trunc:1;
+ uint8_t rxerror:1;
+ uint8_t dserror:1;
+ uint8_t reserved:1;
+ uint8_t direction:1;
+} DagFlags;
+
+typedef struct DagRecord_ {
+ uint64_t ts;
+ uint8_t type;
+ DagFlags flags;
+ uint16_t rlen;
+ uint16_t lctr;
+ uint16_t wlen;
+ uint16_t pad;
+} __attribute__((packed)) DagRecord;
+
+typedef struct ErfFileThreadVars_ {
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ FILE *erf;
+
+ uint32_t pkts;
+ uint64_t bytes;
+} ErfFileThreadVars;
+
+static inline TmEcode ReadErfRecord(ThreadVars *, Packet *, void *);
+TmEcode ReceiveErfFileLoop(ThreadVars *, void *, void *);
+TmEcode ReceiveErfFileThreadInit(ThreadVars *, void *, void **);
+void ReceiveErfFileThreadExitStats(ThreadVars *, void *);
+TmEcode ReceiveErfFileThreadDeinit(ThreadVars *, void *);
+
+TmEcode DecodeErfFileThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeErfFileThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeErfFile(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+/**
+ * \brief Register the ERF file receiver (reader) module.
+ */
+void
+TmModuleReceiveErfFileRegister(void)
+{
+ tmm_modules[TMM_RECEIVEERFFILE].name = "ReceiveErfFile";
+ tmm_modules[TMM_RECEIVEERFFILE].ThreadInit = ReceiveErfFileThreadInit;
+ tmm_modules[TMM_RECEIVEERFFILE].Func = NULL;
+ tmm_modules[TMM_RECEIVEERFFILE].PktAcqLoop = ReceiveErfFileLoop;
+ tmm_modules[TMM_RECEIVEERFFILE].ThreadExitPrintStats =
+ ReceiveErfFileThreadExitStats;
+ tmm_modules[TMM_RECEIVEERFFILE].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEERFFILE].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEERFFILE].cap_flags = 0;
+ tmm_modules[TMM_RECEIVEERFFILE].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Register the ERF file decoder module.
+ */
+void
+TmModuleDecodeErfFileRegister(void)
+{
+ tmm_modules[TMM_DECODEERFFILE].name = "DecodeErfFile";
+ tmm_modules[TMM_DECODEERFFILE].ThreadInit = DecodeErfFileThreadInit;
+ tmm_modules[TMM_DECODEERFFILE].Func = DecodeErfFile;
+ tmm_modules[TMM_DECODEERFFILE].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEERFFILE].ThreadDeinit = DecodeErfFileThreadDeinit;
+ tmm_modules[TMM_DECODEERFFILE].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEERFFILE].cap_flags = 0;
+ tmm_modules[TMM_DECODEERFFILE].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief ERF file reading loop.
+ */
+TmEcode ReceiveErfFileLoop(ThreadVars *tv, void *data, void *slot)
+{
+ Packet *p = NULL;
+ ErfFileThreadVars *etv = (ErfFileThreadVars *)data;
+
+ etv->slot = ((TmSlot *)slot)->slot_next;
+
+ while (1) {
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* Make sure we have at least one packet in the packet pool,
+ * to prevent us from alloc'ing packets at line rate. */
+ PacketPoolWait();
+
+ p = PacketGetFromQueueOrAlloc();
+ if (unlikely(p == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate a packet.");
+ EngineStop();
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ if (ReadErfRecord(tv, p, data) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(etv->tv, p);
+ EngineStop();
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (TmThreadsSlotProcessPkt(etv->tv, etv->slot, p) != TM_ECODE_OK) {
+ EngineStop();
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+static inline TmEcode ReadErfRecord(ThreadVars *tv, Packet *p, void *data)
+{
+ SCEnter();
+
+ ErfFileThreadVars *etv = (ErfFileThreadVars *)data;
+ DagRecord dr;
+
+ int r = fread(&dr, sizeof(DagRecord), 1, etv->erf);
+ if (r < 1) {
+ if (feof(etv->erf)) {
+ SCLogInfo("End of ERF file reached");
+ }
+ else {
+ SCLogInfo("Error reading ERF record");
+ }
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ int rlen = ntohs(dr.rlen);
+ int wlen = ntohs(dr.wlen);
+ r = fread(GET_PKT_DATA(p), rlen - sizeof(DagRecord), 1, etv->erf);
+ if (r < 1) {
+ if (feof(etv->erf)) {
+ SCLogInfo("End of ERF file reached");
+ }
+ else {
+ SCLogInfo("Error reading ERF record");
+ }
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* Only support ethernet at this time. */
+ if (dr.type != DAG_TYPE_ETH) {
+ SCLogError(SC_ERR_UNIMPLEMENTED,
+ "DAG record type %d not implemented.", dr.type);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ GET_PKT_LEN(p) = wlen;
+ p->datalink = LINKTYPE_ETHERNET;
+
+ /* Convert ERF time to timeval - from libpcap. */
+ uint64_t ts = dr.ts;
+ p->ts.tv_sec = ts >> 32;
+ ts = (ts & 0xffffffffULL) * 1000000;
+ ts += 0x80000000; /* rounding */
+ p->ts.tv_usec = ts >> 32;
+ if (p->ts.tv_usec >= 1000000) {
+ p->ts.tv_usec -= 1000000;
+ p->ts.tv_sec++;
+ }
+
+ etv->pkts++;
+ etv->bytes += wlen;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Initialize the ERF receiver thread.
+ */
+TmEcode
+ReceiveErfFileThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Error: No filename provided.");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ FILE *erf = fopen((const char *)initdata, "r");
+ if (erf == NULL) {
+ SCLogError(SC_ERR_FOPEN, "Failed to open %s: %s", (char *)initdata,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ ErfFileThreadVars *etv = SCMalloc(sizeof(ErfFileThreadVars));
+ if (unlikely(etv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for ERF file thread vars.");
+ fclose(erf);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(etv, 0, sizeof(*etv));
+ etv->erf = erf;
+ etv->tv = tv;
+ *data = (void *)etv;
+
+ SCLogInfo("Processing ERF file %s", (char *)initdata);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Initialize the ERF decoder thread.
+ */
+TmEcode
+DecodeErfFileThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeErfFileThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Decode the ERF file.
+ *
+ * This function ups the decoder counters and then passes the packet
+ * off to the ethernet decoder.
+ */
+TmEcode
+DecodeErfFile(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* Update counters. */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Print some stats to the log at program exit.
+ *
+ * \param tv Pointer to ThreadVars.
+ * \param data Pointer to data, ErfFileThreadVars.
+ */
+void
+ReceiveErfFileThreadExitStats(ThreadVars *tv, void *data)
+{
+ ErfFileThreadVars *etv = (ErfFileThreadVars *)data;
+
+ SCLogInfo("Packets: %"PRIu32"; Bytes: %"PRIu64, etv->pkts, etv->bytes);
+}
diff --git a/framework/src/suricata/src/source-erf-file.h b/framework/src/suricata/src/source-erf-file.h
new file mode 100644
index 00000000..fc56f743
--- /dev/null
+++ b/framework/src/suricata/src/source-erf-file.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited
+ */
+
+#ifndef __SOURCE_ERF_H__
+#define __SOURCE_ERF_H__
+
+void TmModuleReceiveErfFileRegister(void);
+void TmModuleDecodeErfFileRegister(void);
+
+#endif /* __SOURCE_ERF_H__ */
diff --git a/framework/src/suricata/src/source-ipfw.c b/framework/src/suricata/src/source-ipfw.c
new file mode 100644
index 00000000..4c68958b
--- /dev/null
+++ b/framework/src/suricata/src/source-ipfw.c
@@ -0,0 +1,796 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Nick Rogness <nick@rogness.net>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * IPFW packet acquisition support
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "source-ipfw.h"
+#include "util-debug.h"
+#include "conf.h"
+#include "util-byte.h"
+#include "util-privs.h"
+#include "util-device.h"
+#include "runmodes.h"
+
+#define IPFW_ACCEPT 0
+#define IPFW_DROP 1
+
+#define IPFW_SOCKET_POLL_MSEC 300
+
+#ifndef IP_MAXPACKET
+#define IP_MAXPACKET 65535
+#endif
+
+#ifndef IPFW
+/* Handle the case if --enable-ipfw was not used
+ *
+ */
+
+TmEcode NoIPFWSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceiveIPFWRegister (void)
+{
+
+ tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW";
+ tmm_modules[TMM_RECEIVEIPFW].ThreadInit = NoIPFWSupportExit;
+ tmm_modules[TMM_RECEIVEIPFW].Func = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleVerdictIPFWRegister (void)
+{
+ tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW";
+ tmm_modules[TMM_VERDICTIPFW].ThreadInit = NoIPFWSupportExit;
+ tmm_modules[TMM_VERDICTIPFW].Func = NULL;
+ tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = NULL;
+ tmm_modules[TMM_VERDICTIPFW].RegisterTests = NULL;
+}
+
+void TmModuleDecodeIPFWRegister (void)
+{
+ tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW";
+ tmm_modules[TMM_DECODEIPFW].ThreadInit = NoIPFWSupportExit;
+ tmm_modules[TMM_DECODEIPFW].Func = NULL;
+ tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEIPFW].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODEIPFW].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEIPFW].cap_flags = 0;
+ tmm_modules[TMM_DECODEIPFW].flags = TM_FLAG_DECODE_TM;
+}
+
+TmEcode NoIPFWSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+
+ SCLogError(SC_ERR_IPFW_NOSUPPORT,"Error creating thread %s: you do not have support for ipfw "
+ "enabled please recompile with --enable-ipfw", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* We have IPFW compiled in */
+
+extern int max_pending_packets;
+
+/**
+ * \brief Structure to hold thread specific variables.
+ */
+typedef struct IPFWThreadVars_
+{
+ /* data link type for the thread, probably not needed */
+ int datalink;
+
+ /* this one should be not changing after init */
+ uint16_t port_num;
+ /* position into the NFQ queue var array */
+ uint16_t ipfw_index;
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+ uint32_t accepted;
+ uint32_t dropped;
+} IPFWThreadVars;
+
+static IPFWThreadVars ipfw_t[IPFW_MAX_QUEUE];
+static IPFWQueueVars ipfw_q[IPFW_MAX_QUEUE];
+static uint16_t receive_port_num = 0;
+static SCMutex ipfw_init_lock;
+
+/* IPFW Prototypes */
+void *IPFWGetQueue(int number);
+TmEcode ReceiveIPFWThreadInit(ThreadVars *, void *, void **);
+TmEcode ReceiveIPFW(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode ReceiveIPFWLoop(ThreadVars *tv, void *data, void *slot);
+void ReceiveIPFWThreadExitStats(ThreadVars *, void *);
+TmEcode ReceiveIPFWThreadDeinit(ThreadVars *, void *);
+
+TmEcode IPFWSetVerdict(ThreadVars *, IPFWThreadVars *, Packet *);
+TmEcode VerdictIPFW(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode VerdictIPFWThreadInit(ThreadVars *, void *, void **);
+void VerdictIPFWThreadExitStats(ThreadVars *, void *);
+TmEcode VerdictIPFWThreadDeinit(ThreadVars *, void *);
+
+TmEcode DecodeIPFWThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeIPFWThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeIPFW(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+/**
+ * \brief Registration Function for RecieveIPFW.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleReceiveIPFWRegister (void)
+{
+ SCMutexInit(&ipfw_init_lock, NULL);
+
+ tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW";
+ tmm_modules[TMM_RECEIVEIPFW].ThreadInit = ReceiveIPFWThreadInit;
+ tmm_modules[TMM_RECEIVEIPFW].Func = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].PktAcqLoop = ReceiveIPFWLoop;
+ tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = ReceiveIPFWThreadExitStats;
+ tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = ReceiveIPFWThreadDeinit;
+ tmm_modules[TMM_RECEIVEIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
+ SC_CAP_NET_BIND_SERVICE |
+ SC_CAP_NET_BROADCAST; /** \todo untested */
+ tmm_modules[TMM_RECEIVEIPFW].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEIPFW].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration Function for VerdictIPFW.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleVerdictIPFWRegister (void)
+{
+ tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW";
+ tmm_modules[TMM_VERDICTIPFW].ThreadInit = VerdictIPFWThreadInit;
+ tmm_modules[TMM_VERDICTIPFW].Func = VerdictIPFW;
+ tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = VerdictIPFWThreadExitStats;
+ tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = VerdictIPFWThreadDeinit;
+ tmm_modules[TMM_VERDICTIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
+ SC_CAP_NET_BIND_SERVICE; /** \todo untested */
+ tmm_modules[TMM_VERDICTIPFW].RegisterTests = NULL;
+}
+
+/**
+ * \brief Registration Function for DecodeIPFW.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodeIPFWRegister (void)
+{
+ tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW";
+ tmm_modules[TMM_DECODEIPFW].ThreadInit = DecodeIPFWThreadInit;
+ tmm_modules[TMM_DECODEIPFW].Func = DecodeIPFW;
+ tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEIPFW].ThreadDeinit = DecodeIPFWThreadDeinit;
+ tmm_modules[TMM_DECODEIPFW].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEIPFW].flags = TM_FLAG_DECODE_TM;
+}
+
+static inline void IPFWMutexInit(IPFWQueueVars *nq)
+{
+ char *active_runmode = RunmodeGetActive();
+
+ if (active_runmode && !strcmp("workers", active_runmode)) {
+ nq->use_mutex = 0;
+ SCLogInfo("IPFW running in 'workers' runmode, will not use mutex.");
+ } else {
+ nq->use_mutex = 1;
+ }
+ if (nq->use_mutex)
+ SCMutexInit(&nq->socket_lock, NULL);
+}
+
+static inline void IPFWMutexLock(IPFWQueueVars *nq)
+{
+ if (nq->use_mutex)
+ SCMutexLock(&nq->socket_lock);
+}
+
+static inline void IPFWMutexUnlock(IPFWQueueVars *nq)
+{
+ if (nq->use_mutex)
+ SCMutexUnlock(&nq->socket_lock);
+}
+
+TmEcode ReceiveIPFWLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ IPFWThreadVars *ptv = (IPFWThreadVars *)data;
+ IPFWQueueVars *nq = NULL;
+ uint8_t pkt[IP_MAXPACKET];
+ int pktlen=0;
+ struct pollfd IPFWpoll;
+ struct timeval IPFWts;
+ Packet *p = NULL;
+
+ nq = IPFWGetQueue(ptv->ipfw_index);
+ if (nq == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Can't get thread variable");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("Thread '%s' will run on port %d (item %d)",
+ tv->name, nq->port_num, ptv->ipfw_index);
+ while (1) {
+ if (unlikely(suricata_ctl_flags != 0)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ IPFWpoll.fd = nq->fd;
+ IPFWpoll.events = POLLRDNORM;
+ /* Poll the socket for status */
+ if ( (poll(&IPFWpoll, 1, IPFW_SOCKET_POLL_MSEC)) > 0) {
+ if (!(IPFWpoll.revents & (POLLRDNORM | POLLERR)))
+ continue;
+ }
+
+ if ((pktlen = recvfrom(nq->fd, pkt, sizeof(pkt), 0,
+ (struct sockaddr *)&nq->ipfw_sin,
+ &nq->ipfw_sinlen)) == -1) {
+ /* We received an error on socket read */
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* Nothing for us to process */
+ continue;
+ } else {
+ SCLogWarning(SC_WARN_IPFW_RECV,
+ "Read from IPFW divert socket failed: %s",
+ strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+ /* We have a packet to process */
+ memset (&IPFWts, 0, sizeof(struct timeval));
+ gettimeofday(&IPFWts, NULL);
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ SCLogDebug("Received Packet Len: %d", pktlen);
+
+ p->ts.tv_sec = IPFWts.tv_sec;
+ p->ts.tv_usec = IPFWts.tv_usec;
+
+ ptv->pkts++;
+ ptv->bytes += pktlen;
+
+ p->datalink = ptv->datalink;
+
+ p->ipfw_v.ipfw_index = ptv->ipfw_index;
+
+ PacketCopyData(p, pkt, pktlen);
+ SCLogDebug("Packet info: pkt_len: %" PRIu32 " (pkt %02x, pkt_data %02x)",
+ GET_PKT_LEN(p), *pkt, GET_PKT_DATA(p));
+
+ if (TmThreadsSlotProcessPkt(tv, ((TmSlot *) slot)->slot_next, p)
+ != TM_ECODE_OK) {
+ TmqhOutputPacketpool(tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Init function for RecieveIPFW.
+ *
+ * This is a setup function for recieving packets
+ * via ipfw divert, binds a socket, and prepares to
+ * to read from it.
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the divert port passed from the user
+ * \param data pointer gets populated with IPFWThreadVars
+ *
+ */
+TmEcode ReceiveIPFWThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ struct timeval timev;
+ int flag;
+ IPFWThreadVars *ntv = (IPFWThreadVars *) initdata;
+ IPFWQueueVars *nq = IPFWGetQueue(ntv->ipfw_index);
+
+ sigset_t sigs;
+ sigfillset(&sigs);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+
+ SCEnter();
+
+ IPFWMutexInit(nq);
+ /* We need a divert socket to play with */
+ if ((nq->fd = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) {
+ SCLogError(SC_ERR_IPFW_SOCK,"Can't create divert socket: %s", strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* set a timeout to the socket so we can check for a signal
+ * in case we don't get packets for a longer period. */
+ timev.tv_sec = 1;
+ timev.tv_usec = 0;
+
+ if (setsockopt(nq->fd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)) == -1) {
+ SCLogError(SC_ERR_IPFW_SETSOCKOPT,"Can't set IPFW divert socket timeout: %s", strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* set SO_BROADCAST on the divert socket, otherwise sendto()
+ * returns EACCES when reinjecting broadcast packets. */
+ flag = 1;
+
+ if (setsockopt(nq->fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) == -1) {
+ SCLogError(SC_ERR_IPFW_SETSOCKOPT,"Can't set IPFW divert socket broadcast flag: %s", strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ nq->ipfw_sinlen=sizeof(nq->ipfw_sin);
+ memset(&nq->ipfw_sin, 0, nq->ipfw_sinlen);
+ nq->ipfw_sin.sin_family = PF_INET;
+ nq->ipfw_sin.sin_addr.s_addr = INADDR_ANY;
+ nq->ipfw_sin.sin_port = htons(nq->port_num);
+
+ /* Bind that SOB */
+ if (bind(nq->fd, (struct sockaddr *)&nq->ipfw_sin, nq->ipfw_sinlen) == -1) {
+ SCLogError(SC_ERR_IPFW_BIND,"Can't bind divert socket on port %d: %s",nq->port_num,strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ntv->datalink = DLT_RAW;
+
+ *data = (void *)ntv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \todo Unit tests are needed for this module.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+void ReceiveIPFWThreadExitStats(ThreadVars *tv, void *data)
+{
+ IPFWThreadVars *ptv = (IPFWThreadVars *)data;
+
+ SCEnter();
+
+ SCLogNotice("(%s) Treated: Pkts %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
+ tv->name, ptv->pkts, ptv->bytes, ptv->errs);
+ SCLogNotice("(%s) Verdict: Accepted %"PRIu32", Dropped %"PRIu32 "",
+ tv->name, ptv->accepted, ptv->dropped);
+
+
+ SCReturn;
+}
+
+/**
+ * \brief DeInit function closes divert socket at exit.
+ * \todo Unit tests are needed for this module.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+TmEcode ReceiveIPFWThreadDeinit(ThreadVars *tv, void *data)
+{
+ IPFWThreadVars *ptv = (IPFWThreadVars *)data;
+ IPFWQueueVars *nq = IPFWGetQueue(ptv->ipfw_index);
+
+ SCEnter();
+
+ /* Attempt to shut the socket down...close instead? */
+ if (shutdown(nq->fd, SHUT_RD) < 0) {
+ SCLogWarning(SC_WARN_IPFW_UNBIND,"Unable to disable ipfw socket: %s",strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function passes off to link type decoders.
+ * \todo Unit tests are needed for this module.
+ *
+ * DecodeIPFW reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param tv pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ * \param pq pointer to the PacketQueue
+ */
+TmEcode DecodeIPFW(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ SCEnter();
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* Process IP packets */
+ if (IPV4_GET_RAW_VER(ip4h) == 4) {
+ SCLogDebug("DecodeIPFW ip4 processing");
+ DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ } else if(IPV6_GET_RAW_VER(ip6h) == 6) {
+ SCLogDebug("DecodeIPFW ip6 processing");
+ DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ } else {
+ /* We don't support anything besides IP packets for now, bridged packets? */
+ SCLogInfo("IPFW unknown protocol support %02x", *GET_PKT_DATA(p));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function initializes the DecodeThreadVariables
+ *
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer for passing in args
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+TmEcode DecodeIPFWThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ DecodeThreadVars *dtv = NULL;
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeIPFWThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function sets the Verdict and processes the packet
+ *
+ *
+ * \param tv pointer to ThreadVars
+ * \param p pointer to the Packet
+ */
+TmEcode IPFWSetVerdict(ThreadVars *tv, IPFWThreadVars *ptv, Packet *p)
+{
+ uint32_t verdict;
+ struct pollfd IPFWpoll;
+ IPFWQueueVars *nq = NULL;
+
+ SCEnter();
+
+ if (p == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Packet is NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ nq = IPFWGetQueue(p->ipfw_v.ipfw_index);
+ if (nq == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "No thread found");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ IPFWpoll.fd = nq->fd;
+ IPFWpoll.events = POLLWRNORM;
+
+ if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ verdict = IPFW_DROP;
+ } else {
+ verdict = IPFW_ACCEPT;
+ }
+
+ if (verdict == IPFW_ACCEPT) {
+ SCLogDebug("IPFW Verdict is to Accept");
+ ptv->accepted++;
+
+ /* For divert sockets, accepting means writing the
+ * packet back to the socket for ipfw to pick up
+ */
+ SCLogDebug("IPFWSetVerdict writing to socket %d, %p, %u", nq->fd, GET_PKT_DATA(p),GET_PKT_LEN(p));
+
+#if 0
+ while ((poll(&IPFWpoll,1,IPFW_SOCKET_POLL_MSEC)) < 1) {
+ /* Did we receive a signal to shutdown */
+ if (TmThreadsCheckFlag(tv, THV_KILL) || TmThreadsCheckFlag(tv, THV_PAUSE)) {
+ SCLogInfo("Received ThreadShutdown: IPFW divert socket writing interrupted");
+ SCReturnInt(TM_ECODE_OK);
+ }
+ }
+#endif
+
+ IPFWMutexLock(nq);
+ if (sendto(nq->fd, GET_PKT_DATA(p), GET_PKT_LEN(p), 0,(struct sockaddr *)&nq->ipfw_sin, nq->ipfw_sinlen) == -1) {
+ int r = errno;
+ switch (r) {
+ default:
+ SCLogWarning(SC_WARN_IPFW_XMIT,"Write to ipfw divert socket failed: %s",strerror(r));
+ IPFWMutexUnlock(nq);
+ SCReturnInt(TM_ECODE_FAILED);
+ case EHOSTDOWN:
+ case ENETDOWN:
+ break;
+ }
+ }
+
+ IPFWMutexUnlock(nq);
+
+ SCLogDebug("Sent Packet back into IPFW Len: %d",GET_PKT_LEN(p));
+
+ } /* end IPFW_ACCEPT */
+
+
+ if (verdict == IPFW_DROP) {
+ SCLogDebug("IPFW SetVerdict is to DROP");
+ ptv->dropped++;
+
+ /** \todo For divert sockets, dropping means not writing the packet back to the socket.
+ * Need to see if there is some better way to free the packet from the queue */
+
+ } /* end IPFW_DROP */
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+
+/**
+ * \brief This function handles the Verdict processing
+ * \todo Unit tests are needed for this module.
+ *
+ *
+ * \param tv pointer to ThreadVars
+ * \param p pointer to the Packet
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ * \param pq pointer for the Packet Queue access (Not used)
+ */
+TmEcode VerdictIPFW(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ IPFWThreadVars *ptv = (IPFWThreadVars *)data;
+ TmEcode retval = TM_ECODE_OK;
+
+ SCEnter();
+
+ /* can't verdict a "fake" packet */
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* This came from NFQ.
+ * if this is a tunnel packet we check if we are ready to verdict
+ * already. */
+ if (IS_TUNNEL_PKT(p)) {
+ char verdict = 1;
+
+ SCMutex *m = p->root ? &p->root->tunnel_mutex : &p->tunnel_mutex;
+ SCMutexLock(m);
+
+ /* if there are more tunnel packets than ready to verdict packets,
+ * we won't verdict this one
+ */
+ if (TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p)) {
+ SCLogDebug("VerdictIPFW: not ready to verdict yet: "
+ "TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p) = %" PRId32
+ " > %" PRId32 "", TUNNEL_PKT_TPR(p), TUNNEL_PKT_RTV(p));
+ verdict = 0;
+ }
+
+ SCMutexUnlock(m);
+
+ /* don't verdict if we are not ready */
+ if (verdict == 1) {
+ SCLogDebug("Setting verdict on tunnel");
+ retval = IPFWSetVerdict(tv, ptv, p->root ? p->root : p);
+
+ } else {
+ TUNNEL_INCR_PKT_RTV(p);
+ }
+ } else {
+ /* no tunnel, verdict normally */
+ SCLogDebug("Setting verdict on non-tunnel");
+ retval = IPFWSetVerdict(tv, ptv, p);
+ } /* IS_TUNNEL_PKT end */
+
+ SCReturnInt(retval);
+}
+
+/**
+ * \brief This function initializes the VerdictThread
+ *
+ *
+ * \param t pointer to ThreadVars
+ * \param initdata pointer for passing in args
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+TmEcode VerdictIPFWThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+
+ IPFWThreadVars *ptv = NULL;
+
+ SCEnter();
+
+ /* Setup Thread vars */
+ if ( (ptv = SCMalloc(sizeof(IPFWThreadVars))) == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+ memset(ptv, 0, sizeof(IPFWThreadVars));
+
+
+ *data = (void *)ptv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function deinitializes the VerdictThread
+ *
+ *
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+TmEcode VerdictIPFWThreadDeinit(ThreadVars *tv, void *data)
+{
+
+ SCEnter();
+
+ /* We don't need to do anything...not sure quite yet */
+
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats for the VerdictThread
+ *
+ *
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into IPFWThreadVars for ptv
+ */
+void VerdictIPFWThreadExitStats(ThreadVars *tv, void *data)
+{
+ IPFWThreadVars *ptv = (IPFWThreadVars *)data;
+ SCLogInfo("IPFW Processing: - (%s) Pkts accepted %" PRIu32 ", dropped %" PRIu32 "", tv->name, ptv->accepted, ptv->dropped);
+}
+
+/**
+ * \brief Add an IPFW divert
+ *
+ * \param string with the queue name
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int IPFWRegisterQueue(char *queue)
+{
+ IPFWThreadVars *ntv = NULL;
+ IPFWQueueVars *nq = NULL;
+ /* Extract the queue number from the specified command line argument */
+ uint16_t port_num = 0;
+ if ((ByteExtractStringUint16(&port_num, 10, strlen(queue), queue)) < 0)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified queue number %s is not "
+ "valid", queue);
+ return -1;
+ }
+
+ SCMutexLock(&ipfw_init_lock);
+ if (receive_port_num >= IPFW_MAX_QUEUE) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "too much IPFW divert port registered (%d)",
+ receive_port_num);
+ SCMutexUnlock(&ipfw_init_lock);
+ return -1;
+ }
+ if (receive_port_num == 0) {
+ memset(&ipfw_t, 0, sizeof(ipfw_t));
+ memset(&ipfw_q, 0, sizeof(ipfw_q));
+ }
+
+ ntv = &ipfw_t[receive_port_num];
+ ntv->ipfw_index = receive_port_num;
+
+ nq = &ipfw_q[receive_port_num];
+ nq->port_num = port_num;
+ receive_port_num++;
+ SCMutexUnlock(&ipfw_init_lock);
+ LiveRegisterDevice(queue);
+
+ SCLogDebug("Queue \"%s\" registered.", queue);
+ return 0;
+}
+
+/**
+ * \brief Get a pointer to the IPFW queue at index
+ *
+ * \param number idx of the queue in our array
+ *
+ * \retval ptr pointer to the IPFWThreadVars at index
+ * \retval NULL on error
+ */
+void *IPFWGetQueue(int number)
+{
+ if (number >= receive_port_num)
+ return NULL;
+
+ return (void *)&ipfw_q[number];
+}
+
+/**
+ * \brief Get a pointer to the IPFW thread at index
+ *
+ * This function is temporary used as configuration parser.
+ *
+ * \param number idx of the queue in our array
+ *
+ * \retval ptr pointer to the IPFWThreadVars at index
+ * \retval NULL on error
+ */
+void *IPFWGetThread(int number)
+{
+ if (number >= receive_port_num)
+ return NULL;
+
+ return (void *)&ipfw_t[number];
+}
+
+#endif /* End ifdef IPFW */
+
+/* eof */
+
diff --git a/framework/src/suricata/src/source-ipfw.h b/framework/src/suricata/src/source-ipfw.h
new file mode 100644
index 00000000..94c560a4
--- /dev/null
+++ b/framework/src/suricata/src/source-ipfw.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Nick Rogness <nick@rogness.net>
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __SOURCE_IPFW_H__
+#define __SOURCE_IPFW_H__
+
+#define IPFW_MAX_QUEUE 16
+
+/* per packet IPFW vars (Not used) */
+typedef struct IPFWPacketVars_
+{
+ int ipfw_index;
+} IPFWPacketVars;
+
+typedef struct IPFWQueueVars_
+{
+ int fd;
+ SCMutex socket_lock;
+ uint8_t use_mutex;
+ /* this one should be not changing after init */
+ uint16_t port_num;
+ /* position into the ipfw queue var array */
+ uint16_t ipfw_index;
+ struct sockaddr_in ipfw_sin;
+ socklen_t ipfw_sinlen;
+
+#ifdef DBG_PERF
+ int dbg_maxreadsize;
+#endif /* DBG_PERF */
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+ uint32_t accepted;
+ uint32_t dropped;
+ uint32_t replaced;
+
+} IPFWQueueVars;
+
+void *IPFWGetThread(int number);
+int IPFWRegisterQueue(char *queue);
+
+void TmModuleReceiveIPFWRegister (void);
+void TmModuleVerdictIPFWRegister (void);
+void TmModuleDecodeIPFWRegister (void);
+
+
+#endif /* __SOURCE_IPFW_H__ */
diff --git a/framework/src/suricata/src/source-mpipe.c b/framework/src/suricata/src/source-mpipe.c
new file mode 100644
index 00000000..9fdebf65
--- /dev/null
+++ b/framework/src/suricata/src/source-mpipe.c
@@ -0,0 +1,1095 @@
+/* Copyright (C) 2011-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <decanio.tom@gmail.com>
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ *
+ * Tilera TILE-Gx mpipe ingress packet support.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "host.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "tm-threads-common.h"
+#include "runmode-tile.h"
+#include "source-mpipe.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-privs.h"
+#include "util-device.h"
+#include "util-mem.h"
+#include "util-profiling.h"
+#include "tmqh-packetpool.h"
+#include "pkt-var.h"
+
+#ifdef HAVE_MPIPE
+
+#include <mde-version.h>
+#include <tmc/alloc.h>
+#include <arch/sim.h>
+#include <arch/atomic.h>
+#include <arch/cycle.h>
+#include <gxio/mpipe.h>
+#include <gxio/trio.h>
+#include <tmc/cpus.h>
+#include <tmc/spin.h>
+#include <tmc/sync.h>
+#include <tmc/task.h>
+#include <tmc/perf.h>
+#include <arch/sim.h>
+
+/* Align "p" mod "align", assuming "p" is a "void*". */
+#define ALIGN(p, align) do { (p) += -(long)(p) & ((align) - 1); } while(0)
+
+#define VERIFY(VAL, WHAT) \
+ do { \
+ int __val = (VAL); \
+ if (__val < 0) { \
+ SCLogError(SC_ERR_INVALID_ARGUMENT,(WHAT)); \
+ SCReturnInt(TM_ECODE_FAILED); \
+ } \
+ } while (0)
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/** storage for mpipe device names */
+typedef struct MpipeDevice_ {
+ char *dev; /**< the device (e.g. "xgbe1") */
+ TAILQ_ENTRY(MpipeDevice_) next;
+} MpipeDevice;
+
+
+/** private device list */
+static TAILQ_HEAD(, MpipeDevice_) mpipe_devices =
+ TAILQ_HEAD_INITIALIZER(mpipe_devices);
+
+static int first_stack;
+static uint32_t headroom = 2;
+
+/**
+ * \brief Structure to hold thread specific variables.
+ */
+typedef struct MpipeThreadVars_
+{
+ ChecksumValidationMode checksum_mode;
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ Packet *in_p;
+
+ /** stats/counters */
+ uint16_t max_mpipe_depth;
+ uint16_t mpipe_drop;
+ uint16_t counter_no_buffers_0;
+ uint16_t counter_no_buffers_1;
+ uint16_t counter_no_buffers_2;
+ uint16_t counter_no_buffers_3;
+ uint16_t counter_no_buffers_4;
+ uint16_t counter_no_buffers_5;
+ uint16_t counter_no_buffers_6;
+ uint16_t counter_no_buffers_7;
+
+} MpipeThreadVars;
+
+TmEcode ReceiveMpipeLoop(ThreadVars *tv, void *data, void *slot);
+TmEcode ReceiveMpipeThreadInit(ThreadVars *, void *, void **);
+void ReceiveMpipeThreadExitStats(ThreadVars *, void *);
+
+TmEcode DecodeMpipeThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeMpipeThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeMpipe(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+static int MpipeReceiveOpenIqueue(int rank);
+
+#define MAX_CHANNELS 32 /* can probably find this in the MDE */
+
+/*
+ * mpipe configuration.
+ */
+
+/* The mpipe context (shared by all workers) */
+static gxio_mpipe_context_t context_body;
+static gxio_mpipe_context_t* context = &context_body;
+
+/* First allocated Notification ring for iQueues. */
+static int first_notif_ring;
+
+/* The ingress queue for this worker thread */
+static __thread gxio_mpipe_iqueue_t* thread_iqueue;
+
+/* The egress queues (one per port) */
+static gxio_mpipe_equeue_t equeue[MAX_CHANNELS];
+
+/* the number of entries in an equeue ring */
+static const int equeue_entries = 8192;
+
+/* Array of mpipe links */
+static gxio_mpipe_link_t mpipe_link[MAX_CHANNELS];
+
+/* Per interface configuration data */
+static MpipeIfaceConfig *mpipe_conf[MAX_CHANNELS];
+
+/* Per interface TAP/IPS configuration */
+
+/* egress equeue associated with each ingress channel */
+static MpipePeerVars channel_to_equeue[MAX_CHANNELS];
+
+/**
+ * \brief Registration Function for ReceiveMpipe.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleReceiveMpipeRegister (void)
+{
+ tmm_modules[TMM_RECEIVEMPIPE].name = "ReceiveMpipe";
+ tmm_modules[TMM_RECEIVEMPIPE].ThreadInit = ReceiveMpipeThreadInit;
+ tmm_modules[TMM_RECEIVEMPIPE].Func = NULL;
+ tmm_modules[TMM_RECEIVEMPIPE].PktAcqLoop = ReceiveMpipeLoop;
+ tmm_modules[TMM_RECEIVEMPIPE].ThreadExitPrintStats = ReceiveMpipeThreadExitStats;
+ tmm_modules[TMM_RECEIVEMPIPE].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEMPIPE].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEMPIPE].cap_flags = SC_CAP_NET_RAW;
+ tmm_modules[TMM_RECEIVEMPIPE].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registraction Function for DecodeNetio.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodeMpipeRegister (void)
+{
+ tmm_modules[TMM_DECODEMPIPE].name = "DecodeMpipe";
+ tmm_modules[TMM_DECODEMPIPE].ThreadInit = DecodeMpipeThreadInit;
+ tmm_modules[TMM_DECODEMPIPE].Func = DecodeMpipe;
+ tmm_modules[TMM_DECODEMPIPE].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEMPIPE].ThreadDeinit = DecodeMpipeThreadDeinit;
+ tmm_modules[TMM_DECODEMPIPE].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEMPIPE].cap_flags = 0;
+ tmm_modules[TMM_DECODEMPIPE].flags = TM_FLAG_DECODE_TM;
+}
+
+/* Release Packet without sending. */
+void MpipeReleasePacket(Packet *p)
+{
+ /* Use this thread's context to free the packet. */
+ // TODO: Check for dual mPipes.
+ gxio_mpipe_iqueue_t* iqueue = thread_iqueue;
+ int bucket = p->mpipe_v.idesc.bucket_id;
+ gxio_mpipe_credit(iqueue->context, iqueue->ring, bucket, 1);
+
+ gxio_mpipe_push_buffer(iqueue->context,
+ p->mpipe_v.idesc.stack_idx,
+ (void*)(intptr_t)p->mpipe_v.idesc.va);
+}
+
+/* Unconditionally send packet, then release packet buffer. */
+void MpipeReleasePacketCopyTap(Packet *p)
+{
+ gxio_mpipe_iqueue_t* iqueue = thread_iqueue;
+ int bucket = p->mpipe_v.idesc.bucket_id;
+ gxio_mpipe_credit(iqueue->context, iqueue->ring, bucket, 1);
+ gxio_mpipe_edesc_t edesc;
+ edesc.words[0] = 0;
+ edesc.words[1] = 0;
+ edesc.bound = 1;
+ edesc.xfer_size = p->mpipe_v.idesc.l2_size;
+ edesc.va = p->mpipe_v.idesc.va;
+ edesc.stack_idx = p->mpipe_v.idesc.stack_idx;
+ edesc.hwb = 1; /* mPIPE will return packet buffer to proper stack. */
+ edesc.size = p->mpipe_v.idesc.size;
+ int channel = p->mpipe_v.idesc.channel;
+ /* Tell mPIPE to egress the packet. */
+ gxio_mpipe_equeue_put(channel_to_equeue[channel].peer_equeue, edesc);
+}
+
+/* Release Packet and send copy if action is not DROP. */
+void MpipeReleasePacketCopyIPS(Packet *p)
+{
+ if (unlikely(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ /* Return packet buffer without sending the packet. */
+ MpipeReleasePacket(p);
+ } else {
+ /* Send packet */
+ MpipeReleasePacketCopyTap(p);
+ }
+}
+
+/**
+ * \brief Mpipe Packet Process function.
+ *
+ * This function fills in our packet structure from mpipe.
+ * From here the packets are picked up by the DecodeMpipe thread.
+ *
+ * \param user pointer to MpipeThreadVars passed from pcap_dispatch
+ * \param h pointer to gxio packet header
+ * \param pkt pointer to current packet
+ */
+static inline
+Packet *MpipeProcessPacket(MpipeThreadVars *ptv, gxio_mpipe_idesc_t *idesc)
+{
+ int caplen = idesc->l2_size;
+ u_char *pkt = gxio_mpipe_idesc_get_va(idesc);
+ Packet *p = (Packet *)(pkt - sizeof(Packet) - headroom/*2*/);
+
+ PACKET_RECYCLE(p);
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ ptv->bytes += caplen;
+ ptv->pkts++;
+
+ gettimeofday(&p->ts, NULL);
+
+ p->datalink = LINKTYPE_ETHERNET;
+ /* No need to check return value, since the only error is pkt == NULL which can't happen here. */
+ PacketSetData(p, pkt, caplen);
+
+ /* copy only the fields we use later */
+ p->mpipe_v.idesc.bucket_id = idesc->bucket_id;
+ p->mpipe_v.idesc.nr = idesc->nr;
+ p->mpipe_v.idesc.cs = idesc->cs;
+ p->mpipe_v.idesc.va = idesc->va;
+ p->mpipe_v.idesc.stack_idx = idesc->stack_idx;
+ MpipePeerVars *equeue_info = &channel_to_equeue[idesc->channel];
+ if (equeue_info->copy_mode != MPIPE_COPY_MODE_NONE) {
+ p->mpipe_v.idesc.size = idesc->size;
+ p->mpipe_v.idesc.l2_size = idesc->l2_size;
+ p->mpipe_v.idesc.channel = idesc->channel;
+ p->ReleasePacket = equeue_info->ReleasePacket;
+ } else {
+ p->ReleasePacket = MpipeReleasePacket;
+ }
+
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE)
+ p->flags |= PKT_IGNORE_CHECKSUM;
+
+ return p;
+}
+
+static uint16_t XlateStack(MpipeThreadVars *ptv, int stack_idx)
+{
+ switch(stack_idx - first_stack) {
+ case 0:
+ return ptv->counter_no_buffers_0;
+ case 1:
+ return ptv->counter_no_buffers_1;
+ case 2:
+ return ptv->counter_no_buffers_2;
+ case 3:
+ return ptv->counter_no_buffers_3;
+ case 4:
+ return ptv->counter_no_buffers_4;
+ case 5:
+ return ptv->counter_no_buffers_5;
+ case 6:
+ return ptv->counter_no_buffers_6;
+ case 7:
+ return ptv->counter_no_buffers_7;
+ default:
+ return ptv->counter_no_buffers_7;
+ }
+}
+
+static void SendNoOpPacket(ThreadVars *tv, TmSlot *slot)
+{
+ Packet *p = PacketPoolGetPacket();
+ if (p == NULL) {
+ return;
+ }
+
+ p->datalink = DLT_RAW;
+ p->proto = IPPROTO_TCP;
+
+ /* So that DecodeMpipe ignores is. */
+ p->flags |= PKT_PSEUDO_STREAM_END;
+
+ p->flow = NULL;
+
+ TmThreadsSlotProcessPkt(tv, slot, p);
+}
+
+/**
+ * \brief Receives packets from an interface via gxio mpipe.
+ */
+TmEcode ReceiveMpipeLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ MpipeThreadVars *ptv = (MpipeThreadVars *)data;
+ TmSlot *s = (TmSlot *)slot;
+ ptv->slot = s->slot_next;
+ Packet *p = NULL;
+ int rank = tv->rank;
+ int max_queued = 0;
+ char *ctype;
+
+ ptv->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ if (ConfGet("mpipe.checksum-checks", &ctype) == 1) {
+ if (strcmp(ctype, "yes") == 0) {
+ ptv->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(ctype, "no") == 0) {
+ ptv->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid value for checksum-check for mpipe");
+ }
+ }
+
+ /* Open Ingress Queue for this worker thread. */
+ MpipeReceiveOpenIqueue(rank);
+ gxio_mpipe_iqueue_t* iqueue = thread_iqueue;
+ int update_counter = 0;
+ uint64_t last_packet_time = get_cycle_count();
+
+ for (;;) {
+
+ /* Check to see how many packets are available to process. */
+ gxio_mpipe_idesc_t *idesc;
+ int n = gxio_mpipe_iqueue_try_peek(iqueue, &idesc);
+ if (likely(n > 0)) {
+ int i;
+ int m = min(n, 16);
+
+ /* Prefetch the idescs (64 bytes each). */
+ for (i = 0; i < m; i++) {
+ __insn_prefetch(&idesc[i]);
+ }
+ if (unlikely(n > max_queued)) {
+ StatsSetUI64(tv, ptv->max_mpipe_depth,
+ (uint64_t)n);
+ max_queued = n;
+ }
+ for (i = 0; i < m; i++, idesc++) {
+ if (likely(!gxio_mpipe_idesc_has_error(idesc))) {
+ p = MpipeProcessPacket(ptv, idesc);
+ p->mpipe_v.rank = rank;
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ if (idesc->be) {
+ /* Buffer Error - No buffer available, so mPipe
+ * dropped the packet. */
+ StatsIncr(tv, XlateStack(ptv, idesc->stack_idx));
+ } else {
+ /* Bad packet. CRC error */
+ StatsIncr(tv, ptv->mpipe_drop);
+ gxio_mpipe_iqueue_drop(iqueue, idesc);
+ }
+ gxio_mpipe_iqueue_release(iqueue, idesc);
+ }
+ }
+ /* Move forward M packets in ingress ring. */
+ gxio_mpipe_iqueue_advance(iqueue, m);
+
+ last_packet_time = get_cycle_count();
+ }
+ if (update_counter-- <= 0) {
+ /* Only periodically update and check for termination. */
+ StatsSyncCountersIfSignalled(tv);
+ update_counter = 10000;
+
+ if (suricata_ctl_flags != 0) {
+ break;
+ }
+
+ // If no packet has been received for some period of time, process a NOP packet
+ // just to make sure that pseudo packets from the Flow manager get processed.
+ uint64_t now = get_cycle_count();
+ if (now - last_packet_time > 100000000) {
+ SendNoOpPacket(ptv->tv, ptv->slot);
+ last_packet_time = now;
+ }
+ }
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void MpipeRegisterPerfCounters(MpipeThreadVars *ptv, ThreadVars *tv)
+{
+ /* register counters */
+ ptv->max_mpipe_depth = StatsRegisterCounter("mpipe.max_mpipe_depth", tv);
+ ptv->mpipe_drop = StatsRegisterCounter("mpipe.drop", tv);
+ ptv->counter_no_buffers_0 = StatsRegisterCounter("mpipe.no_buf0", tv);
+ ptv->counter_no_buffers_1 = StatsRegisterCounter("mpipe.no_buf1", tv);
+ ptv->counter_no_buffers_2 = StatsRegisterCounter("mpipe.no_buf2", tv);
+ ptv->counter_no_buffers_3 = StatsRegisterCounter("mpipe.no_buf3", tv);
+ ptv->counter_no_buffers_4 = StatsRegisterCounter("mpipe.no_buf4", tv);
+ ptv->counter_no_buffers_5 = StatsRegisterCounter("mpipe.no_buf5", tv);
+ ptv->counter_no_buffers_6 = StatsRegisterCounter("mpipe.no_buf6", tv);
+ ptv->counter_no_buffers_7 = StatsRegisterCounter("mpipe.no_buf7", tv);
+}
+
+static const gxio_mpipe_buffer_size_enum_t gxio_buffer_sizes[] = {
+ GXIO_MPIPE_BUFFER_SIZE_128,
+ GXIO_MPIPE_BUFFER_SIZE_256,
+ GXIO_MPIPE_BUFFER_SIZE_512,
+ GXIO_MPIPE_BUFFER_SIZE_1024,
+ GXIO_MPIPE_BUFFER_SIZE_1664,
+ GXIO_MPIPE_BUFFER_SIZE_4096,
+ GXIO_MPIPE_BUFFER_SIZE_10368,
+ GXIO_MPIPE_BUFFER_SIZE_16384
+};
+
+static const int buffer_sizes[] = {
+ 128,
+ 256,
+ 512,
+ 1024,
+ 1664,
+ 4096,
+ 10368,
+ 16384
+};
+
+static int NormalizeBufferWeights(float buffer_weights[], int num_weights)
+{
+ int stack_count = 0;
+ /* Count required buffer stacks and normalize weights to sum to 1.0. */
+ float total_weight = 0;
+ for (int i = 0; i < num_weights; i++) {
+ if (buffer_weights[i] != 0) {
+ ++stack_count;
+ total_weight += buffer_weights[i];
+ }
+ }
+ /* Convert each weight to a value between 0 and 1. inclusive. */
+ for (int i = 0; i < num_weights; i++) {
+ if (buffer_weights[i] != 0) {
+ buffer_weights[i] /= total_weight;
+ }
+ }
+
+ SCLogInfo("DEBUG: %u non-zero sized stacks", stack_count);
+ return stack_count;
+}
+
+static TmEcode ReceiveMpipeAllocatePacketBuffers(void)
+{
+ SCEnter();
+ int num_buffers;
+ int result;
+ int total_buffers = 0;
+
+ /* Relative weighting for the number of buffers of each size.
+ */
+ float buffer_weight[] = {
+ 0 , /* 128 */
+ 4 , /* 256 */
+ 0 , /* 512 */
+ 0 , /* 1024 */
+ 4 , /* 1664 */
+ 0 , /* 4096 */
+ 0 , /* 10386 */
+ 0 /* 16384 */
+ };
+
+ int num_weights = sizeof(buffer_weight)/sizeof(buffer_weight[0]);
+ if (ConfGetNode("mpipe.stack") != NULL) {
+ float weight;
+ for (int i = 0; i < num_weights; i++)
+ buffer_weight[i] = 0;
+ if (ConfGetFloat("mpipe.stack.size128", &weight))
+ buffer_weight[0] = weight;
+ if (ConfGetFloat("mpipe.stack.size256", &weight))
+ buffer_weight[1] = weight;
+ if (ConfGetFloat("mpipe.stack.size512", &weight))
+ buffer_weight[2] = weight;
+ if (ConfGetFloat("mpipe.stack.size1024", &weight))
+ buffer_weight[3] = weight;
+ if (ConfGetFloat("mpipe.stack.size1664", &weight))
+ buffer_weight[4] = weight;
+ if (ConfGetFloat("mpipe.stack.size4096", &weight))
+ buffer_weight[5] = weight;
+ if (ConfGetFloat("mpipe.stack.size10386", &weight))
+ buffer_weight[6] = weight;
+ if (ConfGetFloat("mpipe.stack.size16384", &weight))
+ buffer_weight[7] = weight;
+ }
+
+ int stack_count = NormalizeBufferWeights(buffer_weight, num_weights);
+
+ /* Allocate one of the largest pages to hold our buffer stack,
+ * notif ring, and packets. First get a bit map of the
+ * available page sizes. */
+ unsigned long available_pagesizes = tmc_alloc_get_pagesizes();
+
+ void *packet_page = NULL;
+ size_t tile_vhuge_size = 64 * 1024;
+
+ /* Try the largest available page size first to see if any
+ * pages of that size can be allocated. */
+ for (int i = sizeof(available_pagesizes) * 8 - 1; i; i--) {
+ unsigned long size = 1UL<<i;
+ if (available_pagesizes & size) {
+ tile_vhuge_size = (size_t)size;
+
+ tmc_alloc_t alloc = TMC_ALLOC_INIT;
+ tmc_alloc_set_huge(&alloc);
+ tmc_alloc_set_home(&alloc, TMC_ALLOC_HOME_HASH);
+ if (tmc_alloc_set_pagesize_exact(&alloc, tile_vhuge_size) == NULL)
+ continue; // Couldn't get the page size requested
+ packet_page = tmc_alloc_map(&alloc, tile_vhuge_size);
+ if (packet_page)
+ break;
+ }
+ }
+ assert(packet_page);
+ void* packet_mem = packet_page;
+ SCLogInfo("DEBUG: tile_vhuge_size %"PRIuMAX, (uintmax_t)tile_vhuge_size);
+ /* Allocate one Huge page just to store buffer stacks, since they are
+ * only ever accessed by mPipe.
+ */
+ size_t stack_page_size = tmc_alloc_get_huge_pagesize();
+ tmc_alloc_t alloc = TMC_ALLOC_INIT;
+ tmc_alloc_set_huge(&alloc);
+ void *buffer_stack_page = tmc_alloc_map(&alloc, stack_page_size);
+ void *buffer_stack_mem = buffer_stack_page;
+ void *buffer_stack_mem_end = buffer_stack_mem + stack_page_size;
+ assert(buffer_stack_mem);
+
+ /* Allocate buffer stacks. */
+ result = gxio_mpipe_alloc_buffer_stacks(context, stack_count, 0, 0);
+ VERIFY(result, "gxio_mpipe_alloc_buffer_stacks()");
+ int stack = result;
+ first_stack = stack;
+
+ /* Divide up the Very Huge page into packet buffers. */
+ int i = 0;
+ for (int ss = 0; ss < stack_count; i++, ss++) {
+ /* Skip empty buffer stacks. */
+ for (;buffer_weight[i] == 0; i++) ;
+
+ int stackidx = first_stack + ss;
+ /* Bytes from the Huge page used for this buffer stack. */
+ size_t packet_buffer_slice = tile_vhuge_size * buffer_weight[i];
+ int buffer_size = buffer_sizes[i];
+ num_buffers = packet_buffer_slice / (buffer_size + sizeof(Packet));
+
+ /* Initialize the buffer stack. Must be aligned mod 64K. */
+ size_t stack_bytes = gxio_mpipe_calc_buffer_stack_bytes(num_buffers);
+ gxio_mpipe_buffer_size_enum_t buf_size = gxio_buffer_sizes[i];
+ result = gxio_mpipe_init_buffer_stack(context, stackidx, buf_size,
+ buffer_stack_mem, stack_bytes, 0);
+ VERIFY(result, "gxio_mpipe_init_buffer_stack()");
+ buffer_stack_mem += stack_bytes;
+
+ /* Buffer stack must be aligned to 64KB page boundary. */
+ ALIGN(buffer_stack_mem, 0x10000);
+ assert(buffer_stack_mem < buffer_stack_mem_end);
+
+ /* Register the entire huge page of memory which contains all
+ * the buffers.
+ */
+ result = gxio_mpipe_register_page(context, stackidx, packet_page,
+ tile_vhuge_size, 0);
+ VERIFY(result, "gxio_mpipe_register_page()");
+
+ /* And register the memory holding the buffer stack. */
+ result = gxio_mpipe_register_page(context, stackidx,
+ buffer_stack_page,
+ stack_page_size, 0);
+ VERIFY(result, "gxio_mpipe_register_page()");
+
+ total_buffers += num_buffers;
+
+ SCLogInfo("Adding %d %d byte packet buffers",
+ num_buffers, buffer_size);
+
+ /* Push some buffers onto the stack. */
+ for (int j = 0; j < num_buffers; j++) {
+ Packet *p = (Packet *)packet_mem;
+ memset(p, 0, sizeof(Packet));
+ PACKET_INITIALIZE(p);
+
+ gxio_mpipe_push_buffer(context, stackidx,
+ packet_mem + sizeof(Packet));
+ packet_mem += (sizeof(Packet) + buffer_size);
+ }
+
+ /* Paranoia. */
+ assert(packet_mem <= packet_page + tile_vhuge_size);
+ }
+ SCLogInfo("%d total packet buffers", total_buffers);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static TmEcode ReceiveMpipeCreateBuckets(int ring, int num_workers,
+ int *first_bucket, int *num_buckets)
+{
+ SCEnter();
+ int result;
+ int min_buckets = 256;
+
+ /* Allocate a NotifGroup. */
+ int group = gxio_mpipe_alloc_notif_groups(context, 1, 0, 0);
+ VERIFY(group, "gxio_mpipe_alloc_notif_groups()");
+
+ intmax_t value = 0;
+ if (ConfGetInt("mpipe.buckets", &value) == 1) {
+ /* range check */
+ if ((value >= 1) && (value <= 4096)) {
+ /* Must be a power of 2, so round up to next power of 2. */
+ int ceiling_log2 = 64 - __builtin_clz((int64_t)value - 1);
+ *num_buckets = 1 << (ceiling_log2);
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Illegal mpipe.buckets value (%ld). must be between 1 and 4096.", value);
+ }
+ }
+ if (ConfGetInt("mpipe.min-buckets", &value) == 1) {
+ /* range check */
+ if ((value >= 1) && (value <= 4096)) {
+ /* Must be a power of 2, so round up to next power of 2. */
+ int ceiling_log2 = 64 - __builtin_clz((int64_t)value - 1);
+ min_buckets = 1 << (ceiling_log2);
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Illegal min-mpipe.buckets value (%ld). must be between 1 and 4096.", value);
+ }
+ }
+
+ /* Allocate buckets. Keep trying half the number of requested buckets until min-bucket is reached. */
+ while (1) {
+ *first_bucket = gxio_mpipe_alloc_buckets(context, *num_buckets, 0, 0);
+ if (*first_bucket != GXIO_MPIPE_ERR_NO_BUCKET)
+ break;
+ /* Failed to allocate the requested number of buckets. Keep
+ * trying less buckets until min-buckets is reached.
+ */
+ if (*num_buckets <= min_buckets) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Could not allocate (%d) mpipe buckets. "
+ "Try a smaller mpipe.buckets value in suricata.yaml", *num_buckets);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ /* Cut the number of requested buckets in half and try again. */
+ *num_buckets /= 2;
+ }
+
+ /* Init group and buckets, preserving packet order among flows. */
+ gxio_mpipe_bucket_mode_t mode = GXIO_MPIPE_BUCKET_STATIC_FLOW_AFFINITY;
+ char *balance;
+ if (ConfGet("mpipe.load-balance", &balance) == 1) {
+ if (balance) {
+ if (strcmp(balance, "static") == 0) {
+ mode = GXIO_MPIPE_BUCKET_STATIC_FLOW_AFFINITY;
+ SCLogInfo("Using \"static\" flow affinity load balancing with %d buckets.", *num_buckets);
+ } else if (strcmp(balance, "dynamic") == 0) {
+ mode = GXIO_MPIPE_BUCKET_DYNAMIC_FLOW_AFFINITY;
+ SCLogInfo("Using \"dynamic\" flow affinity load balancing with %d buckets.", *num_buckets);
+ } else if (strcmp(balance, "sticky") == 0) {
+ mode = GXIO_MPIPE_BUCKET_STICKY_FLOW_LOCALITY;
+ SCLogInfo("Using \"sticky\" load balancing with %d buckets.", *num_buckets);
+ } else if (strcmp(balance, "round-robin") == 0) {
+ mode = GXIO_MPIPE_BUCKET_ROUND_ROBIN;
+ } else {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT,
+ "Illegal load balancing mode \"%s\"", balance);
+ balance = "static";
+ }
+ }
+ } else {
+ balance = "static";
+ }
+ SCLogInfo("Using \"%s\" load balancing with %d buckets.", balance, *num_buckets);
+
+ result = gxio_mpipe_init_notif_group_and_buckets(context, group,
+ ring, num_workers,
+ *first_bucket,
+ *num_buckets,
+ mode);
+ VERIFY(result, "gxio_mpipe_init_notif_group_and_buckets()");
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/* \brief Register mPIPE classifier rules to start receiving packets.
+ *
+ * \param Index of the first classifier bucket
+ * \param Number of classifier buckets.
+ *
+ * \return result code where <0 is an error.
+ */
+static int ReceiveMpipeRegisterRules(int bucket, int num_buckets)
+{
+ /* Register for packets. */
+ gxio_mpipe_rules_t rules;
+ gxio_mpipe_rules_init(&rules, context);
+ gxio_mpipe_rules_begin(&rules, bucket, num_buckets, NULL);
+ /* Give Suricata priority over Linux to receive packets. */
+ gxio_mpipe_rules_set_priority(&rules, -100);
+ return gxio_mpipe_rules_commit(&rules);
+}
+
+/* \brief Initialize mPIPE ingress ring
+ *
+ * \param name of interface to open
+ * \param Array of port configuations
+ *
+ * \return Output port channel number, or -1 on error
+ */
+static int MpipeReceiveOpenIqueue(int rank)
+{
+ /* Initialize the NotifRings. */
+ size_t notif_ring_entries = 2048;
+ intmax_t value = 0;
+ if (ConfGetInt("mpipe.iqueue-packets", &value) == 1) {
+ /* range check */
+ if (value == 128 || value == 512 || value == 2048 || value == (64 * 1024)) {
+ notif_ring_entries = value;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Illegal mpipe.iqueue_packets value. must be 128, 512, 2048 or 65536.");
+ }
+ }
+
+ size_t notif_ring_size = notif_ring_entries * sizeof(gxio_mpipe_idesc_t);
+
+ tmc_alloc_t alloc = TMC_ALLOC_INIT;
+ /* Allocate the memory locally on this thread's CPU. */
+ tmc_alloc_set_home(&alloc, TMC_ALLOC_HOME_TASK);
+ /* Allocate all the memory on one page. Which is required for the
+ notif ring, not the iqueue. */
+ if (notif_ring_size > (size_t)getpagesize())
+ tmc_alloc_set_huge(&alloc);
+ int needed = notif_ring_size + sizeof(gxio_mpipe_iqueue_t);
+ // TODO - Save the rest of the Huge Page for other allocations.
+ void *iqueue_mem = tmc_alloc_map(&alloc, needed);
+ if (iqueue_mem == NULL) {
+ SCLogError(SC_ERR_FATAL, "Failed to allocate memory for mPIPE iQueue");
+ return TM_ECODE_FAILED;
+ }
+
+ thread_iqueue = iqueue_mem + notif_ring_size;
+ int result = gxio_mpipe_iqueue_init(thread_iqueue, context, first_notif_ring + rank,
+ iqueue_mem, notif_ring_size, 0);
+ if (result < 0) {
+ VERIFY(result, "gxio_mpipe_iqueue_init()");
+ }
+
+ return TM_ECODE_OK;
+}
+
+/* \brief Initialize on MPIPE egress port
+ *
+ * Initialize one mPIPE egress port for use in IPS mode.
+ * The port must be one of the input ports.
+ *
+ * \param name of interface to open
+ * \param Array of port configuations
+ *
+ * \return Output port channel number, or -1 on error
+ */
+static int MpipeReceiveOpenEgress(char *out_iface, int iface_idx,
+ int copy_mode,
+ MpipeIfaceConfig *mpipe_conf[])
+{
+ int channel;
+ int nlive = LiveGetDeviceCount();
+ int result;
+
+ /* Initialize an equeue */
+ result = gxio_mpipe_alloc_edma_rings(context, 1, 0, 0);
+ if (result < 0) {
+ SCLogError(SC_ERR_FATAL, "Failed to allocate mPIPE egress ring");
+ return result;
+ }
+ uint32_t ering = result;
+ size_t edescs_size = equeue_entries * sizeof(gxio_mpipe_edesc_t);
+ tmc_alloc_t edescs_alloc = TMC_ALLOC_INIT;
+ tmc_alloc_set_pagesize(&edescs_alloc, edescs_size);
+ void *edescs = tmc_alloc_map(&edescs_alloc, edescs_size);
+ if (edescs == NULL) {
+ SCLogError(SC_ERR_FATAL,
+ "Failed to allocate egress descriptors");
+ return -1;
+ }
+ /* retrieve channel of outbound interface */
+ for (int j = 0; j < nlive; j++) {
+ if (strcmp(out_iface, mpipe_conf[j]->iface) == 0) {
+ channel = gxio_mpipe_link_channel(&mpipe_link[j]);
+ SCLogInfo("egress link: %s is channel: %d",
+ out_iface, channel);
+ result = gxio_mpipe_equeue_init(&equeue[iface_idx],
+ context,
+ ering,
+ channel,
+ edescs,
+ edescs_size,
+ 0);
+ if (result < 0) {
+ SCLogError(SC_ERR_FATAL,
+ "mPIPE Failed to initialize egress queue");
+ return -1;
+ }
+ /* Record the mapping from ingress port to egress port.
+ * The egress information is stored indexed by ingress channel.
+ */
+ channel = gxio_mpipe_link_channel(&mpipe_link[iface_idx]);
+ channel_to_equeue[channel].peer_equeue = &equeue[iface_idx];
+ channel_to_equeue[channel].copy_mode = copy_mode;
+ if (copy_mode == MPIPE_COPY_MODE_IPS)
+ channel_to_equeue[channel].ReleasePacket = MpipeReleasePacketCopyIPS;
+ else
+ channel_to_equeue[channel].ReleasePacket = MpipeReleasePacketCopyTap;
+
+ SCLogInfo("ingress link: %s is channel: %d copy_mode: %d",
+ out_iface, channel, copy_mode);
+
+ return channel;
+ }
+ }
+
+ /* Did not find matching interface name */
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Could not find egress interface: %s",
+ out_iface);
+ return -1;
+}
+
+TmEcode ReceiveMpipeThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ int rank = tv->rank;
+ int num_buckets = 4096;
+ int num_workers = tile_num_pipelines;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ MpipeThreadVars *ptv = SCMalloc(sizeof(MpipeThreadVars));
+ if (unlikely(ptv == NULL))
+ SCReturnInt(TM_ECODE_FAILED);
+
+ memset(ptv, 0, sizeof(MpipeThreadVars));
+
+ ptv->tv = tv;
+
+ int result;
+ char *link_name = (char *)initdata;
+
+ MpipeRegisterPerfCounters(ptv, tv);
+
+ *data = (void *)ptv;
+
+ /* Only rank 0 does initialization of mpipe */
+ if (rank != 0)
+ SCReturnInt(TM_ECODE_OK);
+
+ /* Initialize and configure mPIPE, which is only done by one core. */
+
+ if (strcmp(link_name, "multi") == 0) {
+ int nlive = LiveGetDeviceCount();
+ int instance = gxio_mpipe_link_instance(LiveGetDeviceName(0));
+ for (int i = 1; i < nlive; i++) {
+ link_name = LiveGetDeviceName(i);
+ if (gxio_mpipe_link_instance(link_name) != instance) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "All interfaces not on same mpipe instance");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+ result = gxio_mpipe_init(context, instance);
+ VERIFY(result, "gxio_mpipe_init()");
+ /* open ingress interfaces */
+ for (int i = 0; i < nlive; i++) {
+ link_name = LiveGetDeviceName(i);
+ SCLogInfo("opening interface %s", link_name);
+ result = gxio_mpipe_link_open(&mpipe_link[i], context,
+ link_name, 0);
+ VERIFY(result, "gxio_mpipe_link_open()");
+ mpipe_conf[i] = ParseMpipeConfig(link_name);
+ }
+ /* find and open egress interfaces for IPS modes */
+ for (int i = 0; i < nlive; i++) {
+ MpipeIfaceConfig *aconf = mpipe_conf[i];
+ if (aconf != NULL) {
+ if (aconf->copy_mode != MPIPE_COPY_MODE_NONE) {
+ int channel = MpipeReceiveOpenEgress(aconf->out_iface,
+ i, aconf->copy_mode,
+ mpipe_conf);
+ if (channel < 0) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+ }
+ }
+ } else {
+ SCLogInfo("using single interface %s", (char *)initdata);
+
+ /* Start the driver. */
+ result = gxio_mpipe_init(context, gxio_mpipe_link_instance(link_name));
+ VERIFY(result, "gxio_mpipe_init()");
+
+ gxio_mpipe_link_t link;
+ result = gxio_mpipe_link_open(&link, context, link_name, 0);
+ VERIFY(result, "gxio_mpipe_link_open()");
+ }
+ /* Allocate some NotifRings. */
+ result = gxio_mpipe_alloc_notif_rings(context,
+ num_workers,
+ 0, 0);
+ VERIFY(result, "gxio_mpipe_alloc_notif_rings()");
+ first_notif_ring = result;
+
+ int first_bucket = 0;
+ int rc;
+ rc = ReceiveMpipeCreateBuckets(first_notif_ring, num_workers,
+ &first_bucket, &num_buckets);
+ if (rc != TM_ECODE_OK)
+ SCReturnInt(rc);
+
+ rc = ReceiveMpipeAllocatePacketBuffers();
+ if (rc != TM_ECODE_OK)
+ SCReturnInt(rc);
+
+ result = ReceiveMpipeRegisterRules(first_bucket, num_buckets);
+ if (result < 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Registering mPIPE classifier rules, %s",
+ gxio_strerror(result));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode ReceiveMpipeInit(void)
+{
+ SCEnter();
+
+ SCLogInfo("tile_num_pipelines: %d", tile_num_pipelines);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into NetiohreadVars for ptv
+ */
+void ReceiveMpipeThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ SCReturn;
+}
+
+TmEcode DecodeMpipeThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeMpipeThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeMpipe(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
+ PacketQueue *postq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* call the decoder */
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Add a mpipe device for monitoring
+ *
+ * \param dev string with the device name
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int MpipeLiveRegisterDevice(char *dev)
+{
+ MpipeDevice *nd = SCMalloc(sizeof(MpipeDevice));
+ if (unlikely(nd == NULL)) {
+ return -1;
+ }
+
+ nd->dev = SCStrdup(dev);
+ if (unlikely(nd->dev == NULL)) {
+ SCFree(nd);
+ return -1;
+ }
+ TAILQ_INSERT_TAIL(&mpipe_devices, nd, next);
+
+ SCLogDebug("Mpipe device \"%s\" registered.", dev);
+ return 0;
+}
+
+/**
+ * \brief Get the number of registered devices
+ *
+ * \retval cnt the number of registered devices
+ */
+int MpipeLiveGetDeviceCount(void)
+{
+ int i = 0;
+ MpipeDevice *nd;
+
+ TAILQ_FOREACH(nd, &mpipe_devices, next) {
+ i++;
+ }
+
+ return i;
+}
+
+#endif // HAVE_MPIPE
diff --git a/framework/src/suricata/src/source-mpipe.h b/framework/src/suricata/src/source-mpipe.h
new file mode 100644
index 00000000..2c335bff
--- /dev/null
+++ b/framework/src/suricata/src/source-mpipe.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2011-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <decanio.tom@gmail.com>
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ */
+
+#ifndef __SOURCE_MPIPE_H__
+#define __SOURCE_MPIPE_H__
+
+#ifdef HAVE_MPIPE
+
+#include <gxio/mpipe.h>
+#include <tmc/cpus.h>
+
+#define MPIPE_FREE_PACKET(p) MpipeFreePacket((p))
+
+#define MPIPE_COPY_MODE_NONE 0
+#define MPIPE_COPY_MODE_TAP 1
+#define MPIPE_COPY_MODE_IPS 2
+
+#define MPIPE_IFACE_NAME_LENGTH 8
+
+typedef struct MpipeIfaceConfig_
+{
+ char iface[MPIPE_IFACE_NAME_LENGTH];
+ int copy_mode;
+ char *out_iface;
+} MpipeIfaceConfig;
+
+typedef struct MpipePeer_
+{
+ int channel;
+ char iface[MPIPE_IFACE_NAME_LENGTH];
+} MpipePeer;
+
+/* per interface TAP/IPS configuration */
+typedef struct MpipePeerVars_
+{
+ gxio_mpipe_equeue_t *peer_equeue;
+ void (*ReleasePacket)(struct Packet_ *);
+ int copy_mode;
+} MpipePeerVars;
+
+/* per packet Mpipe vars */
+typedef struct MpipePacketVars_
+{
+ /* TileGX mpipe stuff */
+ struct {
+ uint_reg_t channel : 5;
+ uint_reg_t l2_size : 14;
+ uint_reg_t size : 3;
+ uint_reg_t bucket_id : 13;
+ uint_reg_t nr : 1;
+ uint_reg_t cs : 1;
+ uint_reg_t va : 42;
+ uint_reg_t stack_idx : 5;
+ } idesc;
+
+ /* packetpool this was allocated from */
+ uint8_t rank;
+
+ gxio_mpipe_equeue_t *peer_equeue;
+} MpipePacketVars;
+
+int MpipeLiveRegisterDevice(char *);
+int MpipeLiveGetDeviceCount(void);
+char *MpipeLiveGetDevice(int);
+void MpipeFreePacket(void *arg);
+void TmModuleReceiveMpipeRegister (void);
+void TmModuleDecodeMpipeRegister (void);
+
+TmEcode ReceiveMpipeInit(void);
+
+#endif /* HAVE_MPIPE */
+#endif /* __SOURCE_MPIPE_H__ */
diff --git a/framework/src/suricata/src/source-napatech.c b/framework/src/suricata/src/source-napatech.c
new file mode 100644
index 00000000..27432314
--- /dev/null
+++ b/framework/src/suricata/src/source-napatech.c
@@ -0,0 +1,402 @@
+/* Copyright (C) 2012-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author nPulse Technologies, LLC.
+ * \author Matt Keeler <mk@npulsetech.com>
+ *
+ * Support for NAPATECH adapter with the 3GD Driver/API.
+ * Requires libntapi from Napatech A/S.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "threadvars.h"
+#include "util-optimize.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "tm-modules.h"
+
+#include "util-privs.h"
+#include "tmqh-packetpool.h"
+
+#ifndef HAVE_NAPATECH
+
+TmEcode NoNapatechSupportExit(ThreadVars *, void *, void **);
+
+
+void TmModuleNapatechStreamRegister (void)
+{
+ tmm_modules[TMM_RECEIVENAPATECH].name = "NapatechStream";
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadInit = NoNapatechSupportExit;
+ tmm_modules[TMM_RECEIVENAPATECH].Func = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].cap_flags = SC_CAP_NET_ADMIN;
+}
+
+void TmModuleNapatechDecodeRegister (void)
+{
+ tmm_modules[TMM_DECODENAPATECH].name = "NapatechDecode";
+ tmm_modules[TMM_DECODENAPATECH].ThreadInit = NoNapatechSupportExit;
+ tmm_modules[TMM_DECODENAPATECH].Func = NULL;
+ tmm_modules[TMM_DECODENAPATECH].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENAPATECH].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODENAPATECH].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENAPATECH].cap_flags = 0;
+ tmm_modules[TMM_DECODENAPATECH].flags = TM_FLAG_DECODE_TM;
+}
+
+TmEcode NoNapatechSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NAPATECH_NOSUPPORT,
+ "Error creating thread %s: you do not have support for Napatech adapter "
+ "enabled please recompile with --enable-napatech", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* Implied we do have NAPATECH support */
+
+#include "source-napatech.h"
+#include <nt.h>
+
+extern int max_pending_packets;
+
+typedef struct NapatechThreadVars_ {
+ ThreadVars *tv;
+ NtNetStreamRx_t rx_stream;
+ uint64_t stream_id;
+ int hba;
+ uint64_t pkts;
+ uint64_t drops;
+ uint64_t bytes;
+
+ TmSlot *slot;
+} NapatechThreadVars;
+
+
+TmEcode NapatechStreamThreadInit(ThreadVars *, void *, void **);
+void NapatechStreamThreadExitStats(ThreadVars *, void *);
+TmEcode NapatechStreamLoop(ThreadVars *tv, void *data, void *slot);
+
+TmEcode NapatechDecodeThreadInit(ThreadVars *, void *, void **);
+TmEcode NapatechDecodeThreadDeinit(ThreadVars *tv, void *data);
+TmEcode NapatechDecode(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+/**
+ * \brief Register the Napatech receiver (reader) module.
+ */
+void TmModuleNapatechStreamRegister(void)
+{
+ tmm_modules[TMM_RECEIVENAPATECH].name = "NapatechStream";
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadInit = NapatechStreamThreadInit;
+ tmm_modules[TMM_RECEIVENAPATECH].Func = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].PktAcqLoop = NapatechStreamLoop;
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadExitPrintStats = NapatechStreamThreadExitStats;
+ tmm_modules[TMM_RECEIVENAPATECH].ThreadDeinit = NapatechStreamThreadDeinit;
+ tmm_modules[TMM_RECEIVENAPATECH].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENAPATECH].cap_flags = SC_CAP_NET_RAW;
+ tmm_modules[TMM_RECEIVENAPATECH].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Register the Napatech decoder module.
+ */
+void TmModuleNapatechDecodeRegister(void)
+{
+ tmm_modules[TMM_DECODENAPATECH].name = "NapatechDecode";
+ tmm_modules[TMM_DECODENAPATECH].ThreadInit = NapatechDecodeThreadInit;
+ tmm_modules[TMM_DECODENAPATECH].Func = NapatechDecode;
+ tmm_modules[TMM_DECODENAPATECH].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENAPATECH].ThreadDeinit = NapatechDecodeThreadDeinit;
+ tmm_modules[TMM_DECODENAPATECH].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENAPATECH].cap_flags = 0;
+ tmm_modules[TMM_DECODENAPATECH].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief Initialize the Napatech receiver thread, generate a single
+ * NapatechThreadVar structure for each thread, this will
+ * contain a NtNetStreamRx_t stream handle which is used when the
+ * thread executes to acquire the packets.
+ *
+ * \param tv Thread variable to ThreadVars
+ * \param initdata Initial data to the adapter passed from the user,
+ * this is processed by the user.
+ *
+ * For now, we assume that we have only a single name for the NAPATECH
+ * adapter.
+ *
+ * \param data data pointer gets populated with
+ *
+ */
+TmEcode NapatechStreamThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ struct NapatechStreamDevConf *conf = (struct NapatechStreamDevConf *)initdata;
+ uintmax_t stream_id = conf->stream_id;
+ *data = NULL;
+
+ SCLogInfo("Napatech Thread Stream ID:%lu", stream_id);
+
+ NapatechThreadVars *ntv = SCMalloc(sizeof(NapatechThreadVars));
+ if (unlikely(ntv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for NAPATECH thread vars.");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(ntv, 0, sizeof (NapatechThreadVars));
+ ntv->stream_id = stream_id;
+ ntv->tv = tv;
+ ntv->hba = conf->hba;
+
+ SCLogInfo("Started processing packets from NAPATECH Stream: %lu", ntv->stream_id);
+
+ *data = (void *)ntv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Main Napatech reading Loop function
+ */
+TmEcode NapatechStreamLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ int32_t status;
+ char errbuf[100];
+ uint64_t pkt_ts;
+ NtNetBuf_t packet_buffer;
+ NapatechThreadVars *ntv = (NapatechThreadVars *)data;
+ NtNetRx_t stat_cmd;
+
+ SCLogInfo("Opening NAPATECH Stream: %lu for processing", ntv->stream_id);
+
+ if ((status = NT_NetRxOpen(&(ntv->rx_stream), "SuricataStream", NT_NET_INTERFACE_PACKET, ntv->stream_id, ntv->hba)) != NT_SUCCESS) {
+ NT_ExplainError(status, errbuf, sizeof(errbuf));
+ SCLogError(SC_ERR_NAPATECH_OPEN_FAILED, "Failed to open NAPATECH Stream: %lu - %s", ntv->stream_id, errbuf);
+ SCFree(ntv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ stat_cmd.cmd = NT_NETRX_READ_CMD_STREAM_DROP;
+
+ SCLogInfo("Napatech Packet Stream Loop Started for Stream ID: %lu", ntv->stream_id);
+
+ TmSlot *s = (TmSlot *)slot;
+ ntv->slot = s->slot_next;
+
+ while (!(suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL))) {
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ /*
+ * Napatech returns packets 1 at a time
+ */
+ status = NT_NetRxGet(ntv->rx_stream, &packet_buffer, 1000);
+ if (unlikely(status == NT_STATUS_TIMEOUT || status == NT_STATUS_TRYAGAIN)) {
+ /*
+ * no frames currently available
+ */
+ continue;
+ } else if (unlikely(status != NT_SUCCESS)) {
+ SCLogError(SC_ERR_NAPATECH_STREAM_NEXT_FAILED,
+ "Failed to read from Napatech Stream: %lu",
+ ntv->stream_id);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (unlikely(p == NULL)) {
+ NT_NetRxRelease(ntv->rx_stream, packet_buffer);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ pkt_ts = NT_NET_GET_PKT_TIMESTAMP(packet_buffer);
+
+ /*
+ * Handle the different timestamp forms that the napatech cards could use
+ * - NT_TIMESTAMP_TYPE_NATIVE is not supported due to having an base of 0 as opposed to NATIVE_UNIX which has a base of 1/1/1970
+ */
+ switch(NT_NET_GET_PKT_TIMESTAMP_TYPE(packet_buffer)) {
+ case NT_TIMESTAMP_TYPE_NATIVE_UNIX:
+ p->ts.tv_sec = pkt_ts / 100000000;
+ p->ts.tv_usec = ((pkt_ts % 100000000) / 100) + (pkt_ts % 100) > 50 ? 1 : 0;
+ break;
+ case NT_TIMESTAMP_TYPE_PCAP:
+ p->ts.tv_sec = pkt_ts >> 32;
+ p->ts.tv_usec = pkt_ts & 0xFFFFFFFF;
+ break;
+ case NT_TIMESTAMP_TYPE_PCAP_NANOTIME:
+ p->ts.tv_sec = pkt_ts >> 32;
+ p->ts.tv_usec = ((pkt_ts & 0xFFFFFFFF) / 1000) + (pkt_ts % 1000) > 500 ? 1 : 0;
+ break;
+ case NT_TIMESTAMP_TYPE_NATIVE_NDIS:
+ /* number of seconds between 1/1/1601 and 1/1/1970 */
+ p->ts.tv_sec = (pkt_ts / 100000000) - 11644473600;
+ p->ts.tv_usec = ((pkt_ts % 100000000) / 100) + (pkt_ts % 100) > 50 ? 1 : 0;
+ break;
+ default:
+ SCLogError(SC_ERR_NAPATECH_TIMESTAMP_TYPE_NOT_SUPPORTED,
+ "Packet from Napatech Stream: %lu does not have a supported timestamp format",
+ ntv->stream_id);
+ NT_NetRxRelease(ntv->rx_stream, packet_buffer);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
+ p->datalink = LINKTYPE_ETHERNET;
+
+ ntv->pkts++;
+ ntv->bytes += NT_NET_GET_PKT_WIRE_LENGTH(packet_buffer);
+
+ // Update drop counter
+ if (unlikely((status = NT_NetRxRead(ntv->rx_stream, &stat_cmd)) != NT_SUCCESS))
+ {
+ NT_ExplainError(status, errbuf, sizeof(errbuf));
+ SCLogWarning(SC_ERR_NAPATECH_STAT_DROPS_FAILED, "Couldn't retrieve drop statistics from the RX stream: %lu - %s", ntv->stream_id, errbuf);
+ }
+ else
+ {
+ ntv->drops += stat_cmd.u.streamDrop.pktsDropped;
+ }
+
+ if (unlikely(PacketCopyData(p, (uint8_t *)NT_NET_GET_PKT_L2_PTR(packet_buffer), NT_NET_GET_PKT_WIRE_LENGTH(packet_buffer)))) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ NT_NetRxRelease(ntv->rx_stream, packet_buffer);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (unlikely(TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p) != TM_ECODE_OK)) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ NT_NetRxRelease(ntv->rx_stream, packet_buffer);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ NT_NetRxRelease(ntv->rx_stream, packet_buffer);
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Print some stats to the log at program exit.
+ *
+ * \param tv Pointer to ThreadVars.
+ * \param data Pointer to data, ErfFileThreadVars.
+ */
+void NapatechStreamThreadExitStats(ThreadVars *tv, void *data)
+{
+ NapatechThreadVars *ntv = (NapatechThreadVars *)data;
+ double percent = 0;
+ if (ntv->drops > 0)
+ percent = (((double) ntv->drops) / (ntv->pkts+ntv->drops)) * 100;
+
+ SCLogNotice("Stream: %lu; Packets: %"PRIu64"; Drops: %"PRIu64" (%5.2f%%); Bytes: %"PRIu64, ntv->stream_id, ntv->pkts, ntv->drops, percent, ntv->bytes);
+}
+
+/**
+ * \brief Deinitializes the NAPATECH card.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ */
+TmEcode NapatechStreamThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ NapatechThreadVars *ntv = (NapatechThreadVars *)data;
+ SCLogDebug("Closing Napatech Stream: %d", ntv->stream_id);
+ NT_NetRxClose(ntv->rx_stream);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+
+/** Decode Napatech */
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * NapatechDecode reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param t pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ */
+TmEcode NapatechDecode(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
+ PacketQueue *postpq)
+{
+ SCEnter();
+
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ switch (p->datalink) {
+ case LINKTYPE_ETHERNET:
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ default:
+ SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED,
+ "Error: datalink type %" PRId32 " not yet supported in module NapatechDecode",
+ p->datalink);
+ break;
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode NapatechDecodeThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if(dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode NapatechDecodeThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif /* HAVE_NAPATECH */
diff --git a/framework/src/suricata/src/source-napatech.h b/framework/src/suricata/src/source-napatech.h
new file mode 100644
index 00000000..eee79dc7
--- /dev/null
+++ b/framework/src/suricata/src/source-napatech.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author nPulse Technologies, LLC
+ * \author Matt Keeler <mk@npulsetech.com>
+ */
+
+#ifndef __SOURCE_NAPATECH_H__
+#define __SOURCE_NAPATECH_H__
+
+void TmModuleNapatechStreamRegister (void);
+TmEcode NapatechStreamThreadDeinit(ThreadVars *tv, void *data);
+void TmModuleNapatechDecodeRegister (void);
+
+struct NapatechStreamDevConf
+{
+ int stream_id;
+ intmax_t hba;
+};
+
+#ifdef HAVE_NAPATECH
+
+#include <nt.h>
+
+#endif
+
+#endif /* __SOURCE_NAPATECH_H__ */
diff --git a/framework/src/suricata/src/source-netmap.c b/framework/src/suricata/src/source-netmap.c
new file mode 100644
index 00000000..73930118
--- /dev/null
+++ b/framework/src/suricata/src/source-netmap.c
@@ -0,0 +1,1098 @@
+/* Copyright (C) 2011-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+* \defgroup netmap Netmap running mode
+*
+* @{
+*/
+
+/**
+* \file
+*
+* \author Aleksey Katargin <gureedo@gmail.com>
+*
+* Netmap socket acquisition support
+*
+*/
+
+#include "suricata-common.h"
+#include "config.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-modules.h"
+#include "tm-threads.h"
+#include "tm-threads-common.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-device.h"
+#include "util-error.h"
+#include "util-privs.h"
+#include "util-optimize.h"
+#include "util-checksum.h"
+#include "util-ioctl.h"
+#include "util-host-info.h"
+#include "tmqh-packetpool.h"
+#include "source-netmap.h"
+#include "runmodes.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+#ifdef HAVE_NETMAP
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <net/netmap_user.h>
+
+#endif /* HAVE_NETMAP */
+
+extern int max_pending_packets;
+
+#ifndef HAVE_NETMAP
+
+TmEcode NoNetmapSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceiveNetmapRegister (void)
+{
+ tmm_modules[TMM_RECEIVENETMAP].name = "ReceiveNetmap";
+ tmm_modules[TMM_RECEIVENETMAP].ThreadInit = NoNetmapSupportExit;
+ tmm_modules[TMM_RECEIVENETMAP].Func = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].cap_flags = 0;
+ tmm_modules[TMM_RECEIVENETMAP].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+* \brief Registration Function for DecodeNetmap.
+* \todo Unit tests are needed for this module.
+*/
+void TmModuleDecodeNetmapRegister (void)
+{
+ tmm_modules[TMM_DECODENETMAP].name = "DecodeNetmap";
+ tmm_modules[TMM_DECODENETMAP].ThreadInit = NoNetmapSupportExit;
+ tmm_modules[TMM_DECODENETMAP].Func = NULL;
+ tmm_modules[TMM_DECODENETMAP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENETMAP].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODENETMAP].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENETMAP].cap_flags = 0;
+ tmm_modules[TMM_DECODENETMAP].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+* \brief this function prints an error message and exits.
+*/
+TmEcode NoNetmapSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NO_NETMAP,"Error creating thread %s: you do not have "
+ "support for netmap enabled, please recompile "
+ "with --enable-netmap", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* We have NETMAP support */
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+#define POLL_TIMEOUT 100
+
+#if defined(__linux__)
+#define POLL_EVENTS (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL)
+#else
+#define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
+#endif
+
+enum {
+ NETMAP_OK,
+ NETMAP_FAILURE,
+};
+
+enum {
+ NETMAP_FLAG_ZERO_COPY = 1,
+};
+
+/**
+ * \brief Netmap ring isntance.
+ */
+typedef struct NetmapRing
+{
+ int fd;
+ struct netmap_ring *rx;
+ struct netmap_ring *tx;
+ int dst_ring_from;
+ int dst_ring_to;
+ int dst_next_ring;
+ SCSpinlock tx_lock;
+} NetmapRing;
+
+/**
+ * \brief Netmap device instance.
+ */
+typedef struct NetmapDevice_
+{
+ char ifname[IFNAMSIZ];
+ void *mem;
+ size_t memsize;
+ struct netmap_if *nif;
+ int rings_cnt;
+ int rx_rings_cnt;
+ int tx_rings_cnt;
+ /* hw rings + sw ring */
+ NetmapRing *rings;
+ unsigned int ref;
+ SC_ATOMIC_DECLARE(unsigned int, threads_run);
+ TAILQ_ENTRY(NetmapDevice_) next;
+} NetmapDevice;
+
+/**
+ * \brief Module thread local variables.
+ */
+typedef struct NetmapThreadVars_
+{
+ /* receive inteface */
+ NetmapDevice *ifsrc;
+ /* dst interface for IPS mode */
+ NetmapDevice *ifdst;
+
+ int src_ring_from;
+ int src_ring_to;
+ int thread_idx;
+ int flags;
+ struct bpf_program bpf_prog;
+
+ /* internal shit */
+ TmSlot *slot;
+ ThreadVars *tv;
+ LiveDevice *livedev;
+
+ /* copy from config */
+ int copy_mode;
+ ChecksumValidationMode checksum_mode;
+
+ /* counters */
+ uint64_t pkts;
+ uint64_t bytes;
+ uint64_t drops;
+ uint16_t capture_kernel_packets;
+ uint16_t capture_kernel_drops;
+
+
+} NetmapThreadVars;
+
+typedef TAILQ_HEAD(NetmapDeviceList_, NetmapDevice_) NetmapDeviceList;
+
+static NetmapDeviceList netmap_devlist = TAILQ_HEAD_INITIALIZER(netmap_devlist);
+static SCMutex netmap_devlist_lock = SCMUTEX_INITIALIZER;
+
+/**
+ * \brief Get interface flags.
+ * \param fd Network susbystem file descritor.
+ * \param ifname Inteface name.
+ * \return Interface flags or -1 on error
+ */
+static int NetmapGetIfaceFlags(int fd, const char *ifname)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Unable to get flags for iface \"%s\": %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+#ifdef OS_FREEBSD
+ int flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
+ return flags;
+#else
+ return ifr.ifr_flags;
+#endif
+}
+
+/**
+ * \brief Set interface flags.
+ * \param fd Network susbystem file descritor.
+ * \param ifname Inteface name.
+ * \param flags Flags to set.
+ * \return Zero on success.
+ */
+static int NetmapSetIfaceFlags(int fd, const char *ifname, int flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef OS_FREEBSD
+ ifr.ifr_flags = flags & 0xffff;
+ ifr.ifr_flagshigh = flags >> 16;
+#else
+ ifr.ifr_flags = flags;
+#endif
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Unable to set flags for iface \"%s\": %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Open interface in netmap mode.
+ * \param ifname Interface name.
+ * \param promisc Enable promiscuous mode.
+ * \param dev Pointer to requested netmap device instance.
+ * \param verbose Verbose error logging.
+ * \return Zero on success.
+ */
+static int NetmapOpen(char *ifname, int promisc, NetmapDevice **pdevice, int verbose)
+{
+ NetmapDevice *pdev = NULL;
+ struct nmreq nm_req;
+
+ *pdevice = NULL;
+
+ SCMutexLock(&netmap_devlist_lock);
+
+ /* search interface in our already opened list */
+ TAILQ_FOREACH(pdev, &netmap_devlist, next) {
+ if (strcmp(ifname, pdev->ifname) == 0) {
+ *pdevice = pdev;
+ pdev->ref++;
+ SCMutexUnlock(&netmap_devlist_lock);
+ return 0;
+ }
+ }
+
+ /* not found, create new record */
+ pdev = SCMalloc(sizeof(*pdev));
+ if (unlikely(pdev == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ goto error;
+ }
+
+ memset(pdev, 0, sizeof(*pdev));
+ SC_ATOMIC_INIT(pdev->threads_run);
+ strlcpy(pdev->ifname, ifname, sizeof(pdev->ifname));
+
+ /* open netmap */
+ int fd = open("/dev/netmap", O_RDWR);
+ if (fd == -1) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't open netmap device, error %s",
+ strerror(errno));
+ goto error_pdev;
+ }
+
+ /* check interface is up */
+ int if_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (if_fd < 0) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't create control socket for '%s' interface",
+ ifname);
+ goto error_fd;
+ }
+ int if_flags = NetmapGetIfaceFlags(if_fd, ifname);
+ if (if_flags == -1) {
+ if (verbose) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Can not access to interface '%s'",
+ ifname);
+ }
+ close(if_fd);
+ goto error_fd;
+ }
+ if ((if_flags & IFF_UP) == 0) {
+ if (verbose) {
+ SCLogError(SC_ERR_NETMAP_CREATE, "Interface '%s' is down", ifname);
+ }
+ close(if_fd);
+ goto error_fd;
+ }
+ if (promisc) {
+ if_flags |= IFF_PROMISC;
+ NetmapSetIfaceFlags(if_fd, ifname, if_flags);
+ }
+ close(if_fd);
+
+ /* query netmap info */
+ memset(&nm_req, 0, sizeof(nm_req));
+ strlcpy(nm_req.nr_name, ifname, sizeof(nm_req.nr_name));
+ nm_req.nr_version = NETMAP_API;
+
+ if (ioctl(fd, NIOCGINFO, &nm_req) != 0) {
+ if (verbose) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't query netmap for %s, error %s",
+ ifname, strerror(errno));
+ }
+ goto error_fd;
+ };
+
+ pdev->memsize = nm_req.nr_memsize;
+ pdev->rx_rings_cnt = nm_req.nr_rx_rings;
+ pdev->tx_rings_cnt = nm_req.nr_tx_rings;
+ pdev->rings_cnt = max(pdev->rx_rings_cnt, pdev->tx_rings_cnt);
+
+ /* hw rings + sw ring */
+ pdev->rings = SCMalloc(sizeof(*pdev->rings) * (pdev->rings_cnt + 1));
+ if (unlikely(pdev->rings == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ goto error_fd;
+ }
+ memset(pdev->rings, 0, sizeof(*pdev->rings) * (pdev->rings_cnt + 1));
+
+ /* open individual instance for each ring */
+ int success_cnt = 0;
+ for (int i = 0; i <= pdev->rings_cnt; i++) {
+ NetmapRing *pring = &pdev->rings[i];
+ pring->fd = open("/dev/netmap", O_RDWR);
+ if (pring->fd == -1) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't open netmap device: %s",
+ strerror(errno));
+ break;
+ }
+
+ if (i < pdev->rings_cnt) {
+ nm_req.nr_flags = NR_REG_ONE_NIC;
+ nm_req.nr_ringid = i | NETMAP_NO_TX_POLL;
+ } else {
+ nm_req.nr_flags = NR_REG_SW;
+ nm_req.nr_ringid = NETMAP_NO_TX_POLL;
+ }
+ if (ioctl(pring->fd, NIOCREGIF, &nm_req) != 0) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't register %s with netmap: %s",
+ ifname, strerror(errno));
+ break;
+ }
+
+ if (pdev->mem == NULL) {
+ pdev->mem = mmap(0, pdev->memsize, PROT_WRITE | PROT_READ,
+ MAP_SHARED, pring->fd, 0);
+ if (pdev->mem == MAP_FAILED) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Couldn't mmap netmap device: %s",
+ strerror(errno));
+ break;
+ }
+ pdev->nif = NETMAP_IF(pdev->mem, nm_req.nr_offset);
+ }
+
+ if ((i < pdev->rx_rings_cnt) || (i == pdev->rings_cnt)) {
+ pring->rx = NETMAP_RXRING(pdev->nif, i);
+ }
+ if ((i < pdev->tx_rings_cnt) || (i == pdev->rings_cnt)) {
+ pring->tx = NETMAP_TXRING(pdev->nif, i);
+ }
+ SCSpinInit(&pring->tx_lock, 0);
+ success_cnt++;
+ }
+
+ if (success_cnt != (pdev->rings_cnt + 1)) {
+ for(int i = 0; i < success_cnt; i++) {
+ close(pdev->rings[i].fd);
+ }
+ if (pdev->mem) {
+ munmap(pdev->mem, pdev->memsize);
+ }
+ SCFree(pdev->rings);
+ goto error_fd;
+ }
+
+ close(fd);
+ *pdevice = pdev;
+
+ TAILQ_INSERT_TAIL(&netmap_devlist, pdev, next);
+ SCMutexUnlock(&netmap_devlist_lock);
+
+ return 0;
+
+error_fd:
+ close(fd);
+error_pdev:
+ SCFree(pdev);
+error:
+ SCMutexUnlock(&netmap_devlist_lock);
+ return -1;
+}
+
+/**
+ * \brief Close or dereference netmap device instance.
+ * \param pdev Netmap device instance.
+ * \return Zero on success.
+ */
+static int NetmapClose(NetmapDevice *dev)
+{
+ NetmapDevice *pdev, *tmp;
+
+ SCMutexLock(&netmap_devlist_lock);
+
+ TAILQ_FOREACH_SAFE(pdev, &netmap_devlist, next, tmp) {
+ if (pdev == dev) {
+ pdev->ref--;
+ if (!pdev->ref) {
+ munmap(pdev->mem, pdev->memsize);
+ for (int i = 0; i <= pdev->rings_cnt; i++) {
+ NetmapRing *pring = &pdev->rings[i];
+ close(pring->fd);
+ SCSpinDestroy(&pring->tx_lock);
+ }
+ SCFree(pdev->rings);
+ TAILQ_REMOVE(&netmap_devlist, pdev, next);
+ SCFree(pdev);
+ }
+ SCMutexUnlock(&netmap_devlist_lock);
+ return 0;
+ }
+ }
+
+ SCMutexUnlock(&netmap_devlist_lock);
+ return -1;
+}
+
+/**
+ * \brief PcapDumpCounters
+ * \param ntv
+ */
+static inline void NetmapDumpCounters(NetmapThreadVars *ntv)
+{
+ StatsAddUI64(ntv->tv, ntv->capture_kernel_packets, ntv->pkts);
+ StatsAddUI64(ntv->tv, ntv->capture_kernel_drops, ntv->drops);
+ (void) SC_ATOMIC_ADD(ntv->livedev->drop, ntv->drops);
+ (void) SC_ATOMIC_ADD(ntv->livedev->pkts, ntv->pkts);
+ ntv->drops = 0;
+ ntv->pkts = 0;
+}
+
+/**
+ * \brief Init function for ReceiveNetmap.
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the interface passed from the user
+ * \param data pointer gets populated with NetmapThreadVars
+ */
+static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ NetmapIfaceConfig *aconf = initdata;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ NetmapThreadVars *ntv = SCMalloc(sizeof(*ntv));
+ if (unlikely(ntv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ goto error;
+ }
+ memset(ntv, 0, sizeof(*ntv));
+
+ ntv->tv = tv;
+ ntv->checksum_mode = aconf->checksum_mode;
+ ntv->copy_mode = aconf->copy_mode;
+
+ ntv->livedev = LiveGetDevice(aconf->iface_name);
+ if (ntv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ goto error_ntv;
+ }
+
+ if (NetmapOpen(aconf->iface, aconf->promisc, &ntv->ifsrc, 1) != 0) {
+ goto error_ntv;
+ }
+
+ if (unlikely(!aconf->iface_sw && !ntv->ifsrc->rx_rings_cnt)) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Input interface '%s' does not have Rx rings",
+ aconf->iface_name);
+ goto error_src;
+ }
+
+ if (unlikely(aconf->iface_sw && aconf->threads > 1)) {
+ SCLogError(SC_ERR_INVALID_VALUE,
+ "Interface '%s+'. "
+ "Thread count can't be greater than 1 for SW ring.",
+ aconf->iface_name);
+ goto error_src;
+ } else if (unlikely(aconf->threads > ntv->ifsrc->rx_rings_cnt)) {
+ SCLogError(SC_ERR_INVALID_VALUE,
+ "Thread count can't be greater than Rx ring count. "
+ "Configured %d threads for interface '%s' with %d Rx rings.",
+ aconf->threads, aconf->iface_name, ntv->ifsrc->rx_rings_cnt);
+ goto error_src;
+ }
+
+ if (aconf->iface_sw) {
+ ntv->thread_idx = 0;
+ } else {
+ do {
+ ntv->thread_idx = SC_ATOMIC_GET(ntv->ifsrc->threads_run);
+ } while (SC_ATOMIC_CAS(&ntv->ifsrc->threads_run, ntv->thread_idx, ntv->thread_idx + 1) == 0);
+ }
+
+ /* calculate thread rings binding */
+ if (aconf->iface_sw) {
+ ntv->src_ring_from = ntv->src_ring_to = ntv->ifsrc->rings_cnt;
+ } else {
+ int tmp = (ntv->ifsrc->rx_rings_cnt + 1) / aconf->threads;
+ ntv->src_ring_from = ntv->thread_idx * tmp;
+ ntv->src_ring_to = ntv->src_ring_from + tmp - 1;
+ if (ntv->thread_idx == (aconf->threads - 1)) {
+ ntv->src_ring_to = ntv->ifsrc->rx_rings_cnt - 1;
+ }
+ }
+ SCLogDebug("netmap: %s thread:%d rings:%d-%d", aconf->iface_name,
+ ntv->thread_idx, ntv->src_ring_from, ntv->src_ring_to);
+
+ if (aconf->copy_mode != NETMAP_COPY_MODE_NONE) {
+ if (NetmapOpen(aconf->out_iface, 0, &ntv->ifdst, 1) != 0) {
+ goto error_src;
+ }
+
+ if (unlikely(!aconf->out_iface_sw && !ntv->ifdst->tx_rings_cnt)) {
+ SCLogError(SC_ERR_NETMAP_CREATE,
+ "Output interface '%s' does not have Tx rings",
+ aconf->out_iface_name);
+ goto error_dst;
+ }
+
+ /* calculate dst rings bindings */
+ for (int i = ntv->src_ring_from; i <= ntv->src_ring_to; i++) {
+ NetmapRing *ring = &ntv->ifsrc->rings[i];
+ if (aconf->out_iface_sw) {
+ ring->dst_ring_from = ring->dst_ring_to = ntv->ifdst->rings_cnt;
+ } else if (ntv->ifdst->tx_rings_cnt > ntv->ifsrc->rx_rings_cnt) {
+ int tmp = (ntv->ifdst->tx_rings_cnt + 1) / ntv->ifsrc->rx_rings_cnt;
+ ring->dst_ring_from = i * tmp;
+ ring->dst_ring_to = ring->dst_ring_from + tmp - 1;
+ if (i == (ntv->src_ring_to - 1)) {
+ ring->dst_ring_to = ntv->ifdst->tx_rings_cnt - 1;
+ }
+ } else {
+ ring->dst_ring_from = ring->dst_ring_to =
+ i % ntv->ifdst->tx_rings_cnt;
+ }
+ ring->dst_next_ring = ring->dst_ring_from;
+
+ SCLogDebug("netmap: %s(%d)->%s(%d-%d)",
+ aconf->iface_name, i, aconf->out_iface_name,
+ ring->dst_ring_from, ring->dst_ring_to);
+ }
+ }
+
+ /* basic counters */
+ ntv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ntv->tv);
+ ntv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ntv->tv);
+
+ /* enable zero-copy mode for workers runmode */
+ char const *active_runmode = RunmodeGetActive();
+ if ((aconf->copy_mode != NETMAP_COPY_MODE_NONE) && active_runmode
+ && !strcmp("workers", active_runmode)) {
+ if (likely(ntv->ifsrc->mem == ntv->ifdst->mem)) {
+ ntv->flags |= NETMAP_FLAG_ZERO_COPY;
+ SCLogInfo("Enabling zero copy mode for %s->%s",
+ aconf->iface_name, aconf->out_iface_name);
+ } else {
+ SCLogInfo("Unable to set zero copy mode for %s->%s",
+ aconf->iface_name, aconf->out_iface_name);
+ }
+ }
+
+ if (aconf->bpf_filter) {
+ SCLogInfo("Using BPF '%s' on iface '%s'",
+ aconf->bpf_filter, ntv->ifsrc->ifname);
+ if (pcap_compile_nopcap(default_packet_size, /* snaplen_arg */
+ LINKTYPE_ETHERNET, /* linktype_arg */
+ &ntv->bpf_prog, /* program */
+ aconf->bpf_filter, /* const char *buf */
+ 1, /* optimize */
+ PCAP_NETMASK_UNKNOWN /* mask */
+ ) == -1) {
+ SCLogError(SC_ERR_NETMAP_CREATE, "Filter compilation failed.");
+ goto error_dst;
+ }
+ }
+
+ if (GetIfaceOffloading(aconf->iface) == 1) {
+ SCLogWarning(SC_ERR_NETMAP_CREATE,
+ "Using mmap mode with GRO or LRO activated can lead to capture problems");
+ }
+
+ *data = (void *)ntv;
+ aconf->DerefFunc(aconf);
+ SCReturnInt(TM_ECODE_OK);
+
+error_dst:
+ if (aconf->copy_mode != NETMAP_COPY_MODE_NONE) {
+ NetmapClose(ntv->ifdst);
+ }
+error_src:
+ NetmapClose(ntv->ifsrc);
+error_ntv:
+ SCFree(ntv);
+error:
+ aconf->DerefFunc(aconf);
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+/**
+ * \brief Output packet to destination interface or drop.
+ * \param ntv Thread local variables.
+ * \param p Source packet.
+ */
+static TmEcode NetmapWritePacket(NetmapThreadVars *ntv, Packet *p)
+{
+ if (ntv->copy_mode == NETMAP_COPY_MODE_IPS) {
+ if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ return TM_ECODE_OK;
+ }
+ }
+
+ /* map src ring_id to dst ring_id */
+ NetmapRing *rxring = &ntv->ifsrc->rings[p->netmap_v.ring_id];
+ NetmapRing *txring = &ntv->ifdst->rings[p->netmap_v.dst_ring_id];
+
+ SCSpinLock(&txring->tx_lock);
+
+ if (!nm_ring_space(txring->tx)) {
+ ntv->drops++;
+ SCSpinUnlock(&txring->tx_lock);
+ return TM_ECODE_FAILED;
+ }
+
+ struct netmap_slot *ts = &txring->tx->slot[txring->tx->cur];
+
+ if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
+ struct netmap_slot *rs = &rxring->rx->slot[p->netmap_v.slot_id];
+
+ /* swap slot buffers */
+ uint32_t tmp_idx;
+ tmp_idx = ts->buf_idx;
+ ts->buf_idx = rs->buf_idx;
+ rs->buf_idx = tmp_idx;
+
+ ts->len = rs->len;
+
+ ts->flags |= NS_BUF_CHANGED;
+ rs->flags |= NS_BUF_CHANGED;
+ } else {
+ unsigned char *slot_data = (unsigned char *)NETMAP_BUF(txring->tx, ts->buf_idx);
+ memcpy(slot_data, GET_PKT_DATA(p), GET_PKT_LEN(p));
+ ts->len = GET_PKT_LEN(p);
+ ts->flags |= NS_BUF_CHANGED;
+ }
+
+ txring->tx->head = txring->tx->cur = nm_ring_next(txring->tx, txring->tx->cur);
+ if ((ntv->flags & NETMAP_FLAG_ZERO_COPY) == 0) {
+ ioctl(txring->fd, NIOCTXSYNC, 0);
+ }
+
+ SCSpinUnlock(&txring->tx_lock);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Packet release routine.
+ * \param p Packet.
+ */
+static void NetmapReleasePacket(Packet *p)
+{
+ NetmapThreadVars *ntv = (NetmapThreadVars *)p->netmap_v.ntv;
+
+ /* Need to be in copy mode and need to detect early release
+ where Ethernet header could not be set (and pseudo packet) */
+ if ((ntv->copy_mode != NETMAP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
+ NetmapWritePacket(ntv, p);
+ }
+
+ PacketFreeOrRelease(p);
+}
+
+/**
+ * \brief Read packets from ring and pass them further.
+ * \param ntv Thread local variables.
+ * \param ring_id Ring id to read.
+ */
+static int NetmapRingRead(NetmapThreadVars *ntv, int ring_id)
+{
+ SCEnter();
+
+ NetmapRing *ring = &ntv->ifsrc->rings[ring_id];
+ struct netmap_ring *rx = ring->rx;
+ uint32_t avail = nm_ring_space(rx);
+ uint32_t cur = rx->cur;
+
+ while (likely(avail-- > 0)) {
+ struct netmap_slot *slot = &rx->slot[cur];
+ unsigned char *slot_data = (unsigned char *)NETMAP_BUF(rx, slot->buf_idx);
+
+ if (ntv->bpf_prog.bf_len) {
+ struct pcap_pkthdr pkthdr = { {0, 0}, slot->len, slot->len };
+ if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, slot_data) == 0) {
+ /* rejected by bpf */
+ cur = nm_ring_next(rx, cur);
+ continue;
+ }
+ }
+
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (unlikely(p == NULL)) {
+ SCReturnInt(NETMAP_FAILURE);
+ }
+
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+ p->livedev = ntv->livedev;
+ p->datalink = LINKTYPE_ETHERNET;
+ p->ts = rx->ts;
+ ntv->pkts++;
+ ntv->bytes += slot->len;
+
+ /* checksum validation */
+ if (ntv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ntv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ if (ntv->livedev->ignore_checksum) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ChecksumAutoModeCheck(ntv->pkts,
+ SC_ATOMIC_GET(ntv->livedev->pkts),
+ SC_ATOMIC_GET(ntv->livedev->invalid_checksums))) {
+ ntv->livedev->ignore_checksum = 1;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ }
+
+ if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
+ if (PacketSetData(p, slot_data, slot->len) == -1) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ SCReturnInt(NETMAP_FAILURE);
+ }
+ } else {
+ if (PacketCopyData(p, slot_data, slot->len) == -1) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ SCReturnInt(NETMAP_FAILURE);
+ }
+ }
+
+ p->ReleasePacket = NetmapReleasePacket;
+ p->netmap_v.ring_id = ring_id;
+ p->netmap_v.slot_id = cur;
+ p->netmap_v.dst_ring_id = ring->dst_next_ring;
+ p->netmap_v.ntv = ntv;
+
+ if (ring->dst_ring_from != ring->dst_ring_to) {
+ ring->dst_next_ring++;
+ if (ring->dst_next_ring == ring->dst_ring_to) {
+ ring->dst_next_ring = ring->dst_ring_from;
+ }
+ }
+
+ SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)",
+ GET_PKT_LEN(p), p, GET_PKT_DATA(p));
+
+ if (TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ SCReturnInt(NETMAP_FAILURE);
+ }
+
+ cur = nm_ring_next(rx, cur);
+ }
+ rx->head = rx->cur = cur;
+
+ SCReturnInt(NETMAP_OK);
+}
+
+/**
+ * \brief Main netmap reading loop function
+ */
+static TmEcode ReceiveNetmapLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ TmSlot *s = (TmSlot *)slot;
+ NetmapThreadVars *ntv = (NetmapThreadVars *)data;
+ struct pollfd *fds;
+ int rings_count = ntv->src_ring_to - ntv->src_ring_from + 1;
+
+ ntv->slot = s->slot_next;
+
+ fds = SCMalloc(sizeof(*fds) * rings_count);
+ if (unlikely(fds == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ for (int i = 0; i < rings_count; i++) {
+ fds[i].fd = ntv->ifsrc->rings[ntv->src_ring_from + i].fd;
+ fds[i].events = POLLIN;
+ }
+
+ for(;;) {
+ if (suricata_ctl_flags != 0) {
+ break;
+ }
+
+ /* make sure we have at least one packet in the packet pool,
+ * to prevent us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ int r = poll(fds, rings_count, POLL_TIMEOUT);
+
+ if (r < 0) {
+ /* error */
+ if(errno != EINTR)
+ SCLogError(SC_ERR_NETMAP_READ,
+ "Error polling netmap from iface '%s': (%d" PRIu32 ") %s",
+ ntv->ifsrc->ifname, errno, strerror(errno));
+ continue;
+ } else if (r == 0) {
+ /* no events, timeout */
+ SCLogDebug("(%s:%d-%d) Poll timeout", ntv->ifsrc->ifname,
+ ntv->src_ring_from, ntv->src_ring_to);
+ continue;
+ }
+
+ for (int i = 0; i < rings_count; i++) {
+ if (fds[i].revents & POLL_EVENTS) {
+ if (fds[i].revents & POLLERR) {
+ SCLogError(SC_ERR_NETMAP_READ,
+ "Error reading data from iface '%s': (%d" PRIu32 ") %s",
+ ntv->ifsrc->ifname, errno, strerror(errno));
+ } else if (fds[i].revents & POLLNVAL) {
+ SCLogError(SC_ERR_NETMAP_READ,
+ "Invalid polling request");
+ }
+ continue;
+ }
+
+ if (likely(fds[i].revents & POLLIN)) {
+ int src_ring_id = ntv->src_ring_from + i;
+ NetmapRingRead(ntv, src_ring_id);
+
+ if ((ntv->copy_mode != NETMAP_COPY_MODE_NONE) &&
+ (ntv->flags & NETMAP_FLAG_ZERO_COPY)) {
+
+ NetmapRing *src_ring = &ntv->ifsrc->rings[src_ring_id];
+
+ /* sync dst tx rings */
+ for (int j = src_ring->dst_ring_from; j <= src_ring->dst_ring_to; j++) {
+ NetmapRing *dst_ring = &ntv->ifdst->rings[j];
+ /* if locked, another loop already do sync */
+ if (SCSpinTrylock(&dst_ring->tx_lock) == 0) {
+ ioctl(dst_ring->fd, NIOCTXSYNC, 0);
+ SCSpinUnlock(&dst_ring->tx_lock);
+ }
+ }
+ }
+ }
+ }
+
+ NetmapDumpCounters(ntv);
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ SCFree(fds);
+ StatsSyncCountersIfSignalled(tv);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into NetmapThreadVars for ntv
+ */
+static void ReceiveNetmapThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ NetmapThreadVars *ntv = (NetmapThreadVars *)data;
+
+ NetmapDumpCounters(ntv);
+ SCLogInfo("(%s) Kernel: Packets %" PRIu64 ", dropped %" PRIu64 ", bytes %" PRIu64 "",
+ tv->name,
+ StatsGetLocalCounterValue(tv, ntv->capture_kernel_packets),
+ StatsGetLocalCounterValue(tv, ntv->capture_kernel_drops),
+ ntv->bytes);
+}
+
+/**
+ * \brief
+ * \param tv
+ * \param data Pointer to NetmapThreadVars.
+ */
+static TmEcode ReceiveNetmapThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+
+ NetmapThreadVars *ntv = (NetmapThreadVars *)data;
+
+ if (ntv->ifsrc) {
+ NetmapClose(ntv->ifsrc);
+ ntv->ifsrc = NULL;
+ }
+ if (ntv->ifdst) {
+ NetmapClose(ntv->ifdst);
+ ntv->ifdst = NULL;
+ }
+ if (ntv->bpf_prog.bf_insns) {
+ pcap_freecode(&ntv->bpf_prog);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Prepare netmap decode thread.
+ * \param tv Thread local avariables.
+ * \param initdata Thread config.
+ * \param data Pointer to DecodeThreadVars placed here.
+ */
+static TmEcode DecodeNetmapThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
+ SCReturnInt(TM_ECODE_FAILED);
+#endif
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * DecodeNetmap reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param t pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into NetmapThreadVars for ntv
+ * \param pq pointer to the current PacketQueue
+ * \param postpq
+ */
+static TmEcode DecodeNetmap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ SCReturnInt(TM_ECODE_OK);
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief
+ * \param tv
+ * \param data Pointer to DecodeThreadVars.
+ */
+static TmEcode DecodeNetmapThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Registration Function for RecieveNetmap.
+ */
+void TmModuleReceiveNetmapRegister(void)
+{
+ tmm_modules[TMM_RECEIVENETMAP].name = "ReceiveNetmap";
+ tmm_modules[TMM_RECEIVENETMAP].ThreadInit = ReceiveNetmapThreadInit;
+ tmm_modules[TMM_RECEIVENETMAP].Func = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].PktAcqLoop = ReceiveNetmapLoop;
+ tmm_modules[TMM_RECEIVENETMAP].ThreadExitPrintStats = ReceiveNetmapThreadExitStats;
+ tmm_modules[TMM_RECEIVENETMAP].ThreadDeinit = ReceiveNetmapThreadDeinit;
+ tmm_modules[TMM_RECEIVENETMAP].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENETMAP].cap_flags = SC_CAP_NET_RAW;
+ tmm_modules[TMM_RECEIVENETMAP].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration Function for DecodeNetmap.
+ */
+void TmModuleDecodeNetmapRegister(void)
+{
+ tmm_modules[TMM_DECODENETMAP].name = "DecodeNetmap";
+ tmm_modules[TMM_DECODENETMAP].ThreadInit = DecodeNetmapThreadInit;
+ tmm_modules[TMM_DECODENETMAP].Func = DecodeNetmap;
+ tmm_modules[TMM_DECODENETMAP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENETMAP].ThreadDeinit = DecodeNetmapThreadDeinit;
+ tmm_modules[TMM_DECODENETMAP].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENETMAP].cap_flags = 0;
+ tmm_modules[TMM_DECODENETMAP].flags = TM_FLAG_DECODE_TM;
+}
+
+#endif /* HAVE_NETMAP */
+/* eof */
+/**
+* @}
+*/
diff --git a/framework/src/suricata/src/source-netmap.h b/framework/src/suricata/src/source-netmap.h
new file mode 100644
index 00000000..c52b5050
--- /dev/null
+++ b/framework/src/suricata/src/source-netmap.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+* \file
+*
+* \author Aleksey Katargin <gureedo@gmail.com>
+*/
+
+#ifndef __SOURCE_NETMAP_H__
+#define __SOURCE_NETMAP_H__
+
+#include "queue.h"
+
+/* copy modes */
+enum {
+ NETMAP_COPY_MODE_NONE,
+ NETMAP_COPY_MODE_TAP,
+ NETMAP_COPY_MODE_IPS,
+};
+
+#define NETMAP_IFACE_NAME_LENGTH 48
+
+typedef struct NetmapIfaceConfig_
+{
+ /* semantic interface name */
+ char iface_name[NETMAP_IFACE_NAME_LENGTH];
+ /* real inner interface name */
+ char iface[NETMAP_IFACE_NAME_LENGTH];
+ /* sw ring flag for iface */
+ int iface_sw;
+ int threads;
+ int promisc;
+ int copy_mode;
+ ChecksumValidationMode checksum_mode;
+ char *bpf_filter;
+ /* semantic interface name */
+ char *out_iface_name;
+ /* real inner interface name */
+ char out_iface[NETMAP_IFACE_NAME_LENGTH];
+ /* sw ring flag for out_iface */
+ int out_iface_sw;
+ SC_ATOMIC_DECLARE(unsigned int, ref);
+ void (*DerefFunc)(void *);
+} NetmapIfaceConfig;
+
+typedef struct NetmapPacketVars_
+{
+ int ring_id;
+ int slot_id;
+ int dst_ring_id;
+ /* NetmapThreadVars */
+ void *ntv;
+} NetmapPacketVars;
+
+void TmModuleReceiveNetmapRegister (void);
+void TmModuleDecodeNetmapRegister (void);
+
+#endif /* __SOURCE_NETMAP_H__ */
diff --git a/framework/src/suricata/src/source-nflog.c b/framework/src/suricata/src/source-nflog.c
new file mode 100644
index 00000000..7722244f
--- /dev/null
+++ b/framework/src/suricata/src/source-nflog.c
@@ -0,0 +1,550 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * Netfilter's netfilter_log support
+ */
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "tm-modules.h"
+#include "tm-queuehandlers.h"
+#include "tmqh-packetpool.h"
+
+#include "runmodes.h"
+#include "util-error.h"
+#include "util-device.h"
+
+#ifndef HAVE_NFLOG
+/** Handle the case where no NFLOG support is compiled in.
+ *
+ */
+
+TmEcode NoNFLOGSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceiveNFLOGRegister (void)
+{
+ tmm_modules[TMM_RECEIVENFLOG].name = "ReceiveNFLOG";
+ tmm_modules[TMM_RECEIVENFLOG].ThreadInit = NoNFLOGSupportExit;
+}
+
+void TmModuleDecodeNFLOGRegister (void)
+{
+ tmm_modules[TMM_DECODENFLOG].name = "DecodeNFLOG";
+ tmm_modules[TMM_DECODENFLOG].ThreadInit = NoNFLOGSupportExit;
+}
+
+TmEcode NoNFLOGSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NFLOG_NOSUPPORT,"Error creating thread %s: you do not have support for nflog "
+ "enabled please recompile with --enable-nflog", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* implied we do have NFLOG support */
+
+#include "source-nflog.h"
+
+TmEcode ReceiveNFLOGThreadInit(ThreadVars *, void *, void **);
+TmEcode ReceiveNFLOGThreadDeinit(ThreadVars *, void *);
+TmEcode ReceiveNFLOGLoop(ThreadVars *, void *, void *);
+void ReceiveNFLOGThreadExitStats(ThreadVars *, void *);
+
+TmEcode DecodeNFLOGThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeNFLOGThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodeNFLOG(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+static int runmode_workers;
+
+/* Structure to hold thread specific variables */
+typedef struct NFLOGThreadVars_ {
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ char *data;
+ int datalen;
+
+ uint16_t group;
+ uint32_t nlbufsiz;
+ uint32_t nlbufsiz_max;
+ uint32_t qthreshold;
+ uint32_t qtimeout;
+
+ struct nflog_handle *h;
+ struct nflog_g_handle *gh;
+
+ LiveDevice *livedev;
+ int nful_overrun_warned;
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+
+ uint16_t capture_kernel_packets;
+ uint16_t capture_kernel_drops;
+} NFLOGThreadVars;
+
+/**
+ * \brief Registration function for ReceiveNFLOG
+ */
+void TmModuleReceiveNFLOGRegister (void)
+{
+ tmm_modules[TMM_RECEIVENFLOG].name = "ReceiveNFLOG";
+ tmm_modules[TMM_RECEIVENFLOG].ThreadInit = ReceiveNFLOGThreadInit;
+ tmm_modules[TMM_RECEIVENFLOG].Func = NULL;
+ tmm_modules[TMM_RECEIVENFLOG].PktAcqLoop = ReceiveNFLOGLoop;
+ tmm_modules[TMM_RECEIVENFLOG].ThreadExitPrintStats = ReceiveNFLOGThreadExitStats;
+ tmm_modules[TMM_RECEIVENFLOG].ThreadDeinit = ReceiveNFLOGThreadDeinit;
+ tmm_modules[TMM_RECEIVENFLOG].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENFLOG].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration function for DecodeNFLOG
+ */
+void TmModuleDecodeNFLOGRegister (void)
+{
+ tmm_modules[TMM_DECODENFLOG].name = "DecodeNFLOG";
+ tmm_modules[TMM_DECODENFLOG].ThreadInit = DecodeNFLOGThreadInit;
+ tmm_modules[TMM_DECODENFLOG].Func = DecodeNFLOG;
+ tmm_modules[TMM_DECODENFLOG].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENFLOG].ThreadDeinit = DecodeNFLOGThreadDeinit;
+ tmm_modules[TMM_DECODENFLOG].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENFLOG].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief NFLOG callback function
+ * This function setup a packet from a nflog message
+ */
+static int NFLOGCallback(struct nflog_g_handle *gh, struct nfgenmsg *msg,
+ struct nflog_data *nfa, void *data)
+{
+ NFLOGThreadVars *ntv = (NFLOGThreadVars *) data;
+ struct nfulnl_msg_packet_hdr *ph;
+ char *payload;
+ int ret;
+
+ /* grab a packet*/
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (p == NULL)
+ return -1;
+
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ ph = nflog_get_msg_packet_hdr(nfa);
+ if (ph != NULL) {
+ p->nflog_v.hw_protocol = ph->hw_protocol;
+ }
+
+ p->nflog_v.ifi = nflog_get_indev(nfa);
+ p->nflog_v.ifo = nflog_get_outdev(nfa);
+
+ ret = nflog_get_payload(nfa, &payload);
+
+ if (ret > 0) {
+ if (ret > 65536) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "NFLOG sent too big packet");
+ SET_PKT_LEN(p, 0);
+ } else if (runmode_workers)
+ PacketSetData(p, (uint8_t *)payload, ret);
+ else
+ PacketCopyData(p, (uint8_t *)payload, ret);
+ } else if (ret == -1)
+ SET_PKT_LEN(p, 0);
+
+ ret = nflog_get_timestamp(nfa, &p->ts);
+ if (ret != 0) {
+ memset(&p->ts, 0, sizeof(struct timeval));
+ gettimeofday(&p->ts, NULL);
+ }
+
+ p->datalink = DLT_RAW;
+
+#ifdef COUNTERS
+ ntv->pkts++;
+ ntv->bytes += GET_PKT_LEN(p);
+#endif
+ (void) SC_ATOMIC_ADD(ntv->livedev->pkts, 1);
+
+ if (TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Receives packet from a nflog group via libnetfilter_log
+ * This is a setup function for recieving packets via libnetfilter_log.
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the group passed from the user
+ * \param data pointer gets populated with NFLOGThreadVars
+ * \retvalTM_ECODE_OK on success
+ * \retval TM_ECODE_FAILED on error
+ */
+TmEcode ReceiveNFLOGThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ NflogGroupConfig *nflconfig = initdata;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ NFLOGThreadVars *ntv = SCMalloc(sizeof(NFLOGThreadVars));
+ if (unlikely(ntv == NULL)) {
+ nflconfig->DerefFunc(nflconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(ntv, 0, sizeof(NFLOGThreadVars));
+
+ ntv->tv = tv;
+ ntv->group = nflconfig->group;
+ ntv->nlbufsiz = nflconfig->nlbufsiz;
+ ntv->nlbufsiz_max = nflconfig->nlbufsiz_max;
+ ntv->qthreshold = nflconfig->qthreshold;
+ ntv->qtimeout = nflconfig->qtimeout;
+ ntv->nful_overrun_warned = nflconfig->nful_overrun_warned;
+
+ ntv->h = nflog_open();
+ if (ntv->h == NULL) {
+ SCLogError(SC_ERR_NFLOG_OPEN, "nflog_open() failed");
+ SCFree(ntv);
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("binding netfilter_log as nflog handler for AF_INET and AF_INET6");
+
+ if (nflog_bind_pf(ntv->h, AF_INET) < 0) {
+ SCLogError(SC_ERR_NFLOG_BIND, "nflog_bind_pf() for AF_INET failed");
+ exit(EXIT_FAILURE);
+ }
+ if (nflog_bind_pf(ntv->h, AF_INET6) < 0) {
+ SCLogError(SC_ERR_NFLOG_BIND, "nflog_bind_pf() for AF_INET6 failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ntv->gh = nflog_bind_group(ntv->h, ntv->group);
+ if (!ntv->gh) {
+ SCLogError(SC_ERR_NFLOG_OPEN, "nflog_bind_group() failed");
+ SCFree(ntv);
+ return TM_ECODE_FAILED;
+ }
+
+ if (nflog_set_mode(ntv->gh, NFULNL_COPY_PACKET, 0xFFFF) < 0) {
+ SCLogError(SC_ERR_NFLOG_SET_MODE, "can't set packet_copy mode");
+ SCFree(ntv);
+ return TM_ECODE_FAILED;
+ }
+
+ nflog_callback_register(ntv->gh, &NFLOGCallback, (void *)ntv);
+
+ if (ntv->nlbufsiz < ntv->nlbufsiz_max)
+ ntv->nlbufsiz = nfnl_rcvbufsiz(nflog_nfnlh(ntv->h), ntv->nlbufsiz);
+ else {
+ SCLogError(SC_ERR_NFLOG_MAX_BUFSIZ, "Maximum buffer size (%d) in NFLOG "
+ "has been reached", ntv->nlbufsiz);
+ return TM_ECODE_FAILED;
+ }
+
+ if (nflog_set_qthresh(ntv->gh, ntv->qthreshold) >= 0)
+ SCLogDebug("NFLOG netlink queue threshold has been set to %d",
+ ntv->qthreshold);
+ else
+ SCLogDebug("NFLOG netlink queue threshold can't be set to %d",
+ ntv->qthreshold);
+
+ if (nflog_set_timeout(ntv->gh, ntv->qtimeout) >= 0)
+ SCLogDebug("NFLOG netlink queue timeout has been set to %d",
+ ntv->qtimeout);
+ else
+ SCLogDebug("NFLOG netlink queue timeout can't be set to %d",
+ ntv->qtimeout);
+
+ ntv->livedev = LiveGetDevice(nflconfig->numgroup);
+ if (ntv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ SCFree(ntv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* set a timeout to the socket so we can check for a signal
+ * in case we don't get packets for a longer period. */
+ struct timeval timev;
+ timev.tv_sec = 1;
+ timev.tv_usec = 0;
+
+ int fd = nflog_fd(ntv->h);
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)) == -1) {
+ SCLogWarning(SC_WARN_NFLOG_SETSOCKOPT, "can't set socket "
+ "timeout: %s", strerror(errno));
+ }
+
+#ifdef PACKET_STATISTICS
+ ntv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ntv->tv);
+ ntv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ntv->tv);
+#endif
+
+ char *active_runmode = RunmodeGetActive();
+ if (active_runmode && !strcmp("workers", active_runmode))
+ runmode_workers = 1;
+ else
+ runmode_workers = 0;
+
+#define T_DATA_SIZE 70000
+ ntv->data = SCMalloc(T_DATA_SIZE);
+ if (ntv->data == NULL) {
+ nflconfig->DerefFunc(nflconfig);
+ SCFree(ntv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ntv->datalen = T_DATA_SIZE;
+#undef T_DATA_SIZE
+
+ *data = (void *)ntv;
+
+ nflconfig->DerefFunc(nflconfig);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief DeInit function unbind group and close nflog's handle
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into NFLogThreadVars
+ * \retval TM_ECODE_OK is always returned
+ */
+TmEcode ReceiveNFLOGThreadDeinit(ThreadVars *tv, void *data)
+{
+ NFLOGThreadVars *ntv = (NFLOGThreadVars *)data;
+
+ SCLogDebug("closing nflog group %d", ntv->group);
+ if (nflog_unbind_pf(ntv->h, AF_INET) < 0) {
+ SCLogError(SC_ERR_NFLOG_UNBIND, "nflog_unbind_pf() for AF_INET failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (nflog_unbind_pf(ntv->h, AF_INET6) < 0) {
+ SCLogError(SC_ERR_NFLOG_UNBIND, "nflog_unbind_pf() for AF_INET6 failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (ntv->gh) {
+ nflog_unbind_group(ntv->gh);
+ ntv->gh = NULL;
+ }
+
+ if (ntv->h) {
+ nflog_close(ntv->h);
+ ntv->h = NULL;
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Increases netlink buffer size
+ *
+ * This function netlink's buffer size until
+ * the max buffer size is reached
+ *
+ * \param data pointer that gets cast into NFLOGThreadVars
+ * \param size netlink buffer size
+ */
+static int NFLOGSetnlbufsiz(void *data, unsigned int size)
+{
+ SCEnter();
+ NFLOGThreadVars *ntv = (NFLOGThreadVars *)data;
+
+ if (size < ntv->nlbufsiz_max) {
+ ntv->nlbufsiz = nfnl_rcvbufsiz(nflog_nfnlh(ntv->h), ntv->nlbufsiz);
+ return 1;
+ }
+
+ SCLogWarning(SC_WARN_NFLOG_MAXBUFSIZ_REACHED,
+ "Maximum buffer size (%d) in NFLOG has been "
+ "reached. Please, consider raising "
+ "`buffer-size` and `max-size` in nflog configuration",
+ ntv->nlbufsiz);
+ return 0;
+
+}
+
+/**
+ * \brief Recieves packets from a group via libnetfilter_log.
+ *
+ * This function recieves packets from a group and passes
+ * the packet on to the nflog callback function.
+ *
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into NFLOGThreadVars
+ * \param slot slot containing task information
+ * \retval TM_ECODE_OK on success
+ * \retval TM_ECODE_FAILED on failure
+ */
+TmEcode ReceiveNFLOGLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+ NFLOGThreadVars *ntv = (NFLOGThreadVars *)data;
+ int rv, fd;
+ int ret = -1;
+
+ ntv->slot = ((TmSlot *) slot)->slot_next;
+
+ fd = nflog_fd(ntv->h);
+ if (fd < 0) {
+ SCLogError(SC_ERR_NFLOG_FD, "Can't obtain a file descriptor");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ while (1) {
+ if (suricata_ctl_flags != 0)
+ break;
+
+ rv = recv(fd, ntv->data, ntv->datalen, 0);
+ if (rv < 0) {
+ /*We received an error on socket read */
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /*Nothing for us to process */
+ continue;
+ } else if (errno == ENOBUFS) {
+ if (!ntv->nful_overrun_warned) {
+ int s = ntv->nlbufsiz * 2;
+ if (NFLOGSetnlbufsiz((void *)ntv, s)) {
+ SCLogWarning(SC_WARN_NFLOG_LOSING_EVENTS,
+ "We are losing events, "
+ "increasing buffer size "
+ "to %d", ntv->nlbufsiz);
+ } else {
+ ntv->nful_overrun_warned = 1;
+ }
+ }
+ continue;
+ } else {
+ SCLogWarning(SC_WARN_NFLOG_RECV,
+ "Read from NFLOG fd failed: %s",
+ strerror(errno));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ ret = nflog_handle_packet(ntv->h, ntv->data, rv);
+ if (ret != 0)
+ SCLogWarning(SC_ERR_NFLOG_HANDLE_PKT,
+ "nflog_handle_packet error %" PRId32 "", ret);
+
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function prints stats to the screen at exit
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into NFLOGThreadVars
+ */
+void ReceiveNFLOGThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ NFLOGThreadVars *ntv = (NFLOGThreadVars *)data;
+
+ SCLogNotice("(%s) Pkts %" PRIu32 ", Bytes %" PRIu64 "",
+ tv->name, ntv->pkts, ntv->bytes);
+}
+
+
+/**
+ * \brief Decode IPv4/v6 packets.
+ *
+ * \param tv pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into NFLOGThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ *
+ * \retval TM_ECODE_OK is always returned
+ */
+TmEcode DecodeNFLOG(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+ IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ if (IPV4_GET_RAW_VER(ip4h) == 4) {
+ SCLogDebug("IPv4 packet");
+ DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else if(IPV6_GET_RAW_VER(ip6h) == 6) {
+ SCLogDebug("IPv6 packet");
+ DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else {
+ SCLogDebug("packet unsupported by NFLOG, first byte: %02x", *GET_PKT_DATA(p));
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This an Init function for DecodeNFLOG
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to initilization data.
+ * \param data pointer that gets cast into NFLOGThreadVars
+ * \retval TM_ECODE_OK is returned on success
+ * \retval TM_ECODE_FAILED is returned on error
+ */
+TmEcode DecodeNFLOGThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ DecodeThreadVars *dtv = NULL;
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeNFLOGThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif /* NFLOG */
diff --git a/framework/src/suricata/src/source-nflog.h b/framework/src/suricata/src/source-nflog.h
new file mode 100644
index 00000000..98dd9edf
--- /dev/null
+++ b/framework/src/suricata/src/source-nflog.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppelng@gmail.com>
+ */
+
+#ifndef __SOURCE_NFLOG_H__
+#define __SOURCE_NFLOG_H__
+
+#ifdef HAVE_NFLOG
+#include <libnetfilter_log/libnetfilter_log.h>
+#include <libnfnetlink/libnfnetlink.h>
+#endif /* HAVE_NFLOG */
+
+#define NFLOG_GROUP_NAME_LENGTH 48
+typedef struct NflogGroupConfig_
+{
+ /* nflog's group */
+ uint16_t group;
+ /* netlink buffer size */
+ uint32_t nlbufsiz;
+ /* netlink max buffer size */
+ uint32_t nlbufsiz_max;
+ /* max amount of logs in buffer*/
+ uint32_t qthreshold;
+ /* max time to push log buffer */
+ uint32_t qtimeout;
+
+ /* used to initialize livedev */
+ char numgroup[NFLOG_GROUP_NAME_LENGTH];
+
+ int nful_overrun_warned;
+
+ void (*DerefFunc)(void *);
+} NflogGroupConfig;
+
+typedef struct NFLOGPacketVars_
+{
+ uint32_t mark;
+ uint32_t ifi;
+ uint32_t ifo;
+ uint16_t hw_protocol;
+
+} NFLOGPacketVars;
+
+void TmModuleReceiveNFLOGRegister(void);
+void TmModuleDecodeNFLOGRegister(void);
+
+#endif /* __SOURCE_NFLOG_H__ */
diff --git a/framework/src/suricata/src/source-nfq-prototypes.h b/framework/src/suricata/src/source-nfq-prototypes.h
new file mode 100644
index 00000000..eef25884
--- /dev/null
+++ b/framework/src/suricata/src/source-nfq-prototypes.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __SOURCE_NFQ_PROTOTYPES_H__
+#define __SOURCE_NFQ_PROTOTYPES_H__
+
+void TmModuleReceiveNFQRegister (void);
+void TmModuleVerdictNFQRegister (void);
+void TmModuleDecodeNFQRegister (void);
+
+#endif /* __SOURCE_NFQ_PROTOTYPES_H__ */
+
diff --git a/framework/src/suricata/src/source-nfq.c b/framework/src/suricata/src/source-nfq.c
new file mode 100644
index 00000000..38770cb9
--- /dev/null
+++ b/framework/src/suricata/src/source-nfq.c
@@ -0,0 +1,1277 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Netfilter's netfilter_queue support for reading packets from the
+ * kernel and setting verdicts back to it (inline mode).
+ * Supported on Linux and Windows.
+ *
+ * \todo test if Receive and Verdict if both are present
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "tm-queuehandlers.h"
+#include "tmqh-packetpool.h"
+
+#include "conf.h"
+#include "config.h"
+#include "conf-yaml-loader.h"
+#include "source-nfq-prototypes.h"
+#include "action-globals.h"
+
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-byte.h"
+#include "util-privs.h"
+#include "util-device.h"
+
+#include "runmodes.h"
+
+#include "source-nfq.h"
+
+#ifndef NFQ
+/** Handle the case where no NFQ support is compiled in.
+ *
+ */
+
+TmEcode NoNFQSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceiveNFQRegister (void)
+{
+ tmm_modules[TMM_RECEIVENFQ].name = "ReceiveNFQ";
+ tmm_modules[TMM_RECEIVENFQ].ThreadInit = NoNFQSupportExit;
+ tmm_modules[TMM_RECEIVENFQ].Func = NULL;
+ tmm_modules[TMM_RECEIVENFQ].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVENFQ].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVENFQ].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENFQ].cap_flags = SC_CAP_NET_ADMIN;
+ tmm_modules[TMM_RECEIVENFQ].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleVerdictNFQRegister (void)
+{
+ tmm_modules[TMM_VERDICTNFQ].name = "VerdictNFQ";
+ tmm_modules[TMM_VERDICTNFQ].ThreadInit = NoNFQSupportExit;
+ tmm_modules[TMM_VERDICTNFQ].Func = NULL;
+ tmm_modules[TMM_VERDICTNFQ].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_VERDICTNFQ].ThreadDeinit = NULL;
+ tmm_modules[TMM_VERDICTNFQ].RegisterTests = NULL;
+ tmm_modules[TMM_VERDICTNFQ].cap_flags = SC_CAP_NET_ADMIN;
+}
+
+void TmModuleDecodeNFQRegister (void)
+{
+ tmm_modules[TMM_DECODENFQ].name = "DecodeNFQ";
+ tmm_modules[TMM_DECODENFQ].ThreadInit = NoNFQSupportExit;
+ tmm_modules[TMM_DECODENFQ].Func = NULL;
+ tmm_modules[TMM_DECODENFQ].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENFQ].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODENFQ].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENFQ].cap_flags = 0;
+ tmm_modules[TMM_DECODENFQ].flags = TM_FLAG_DECODE_TM;
+}
+
+TmEcode NoNFQSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NFQ_NOSUPPORT,"Error creating thread %s: you do not have support for nfqueue "
+ "enabled please recompile with --enable-nfqueue", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* implied we do have NFQ support */
+
+extern int max_pending_packets;
+
+#define MAX_ALREADY_TREATED 5
+#define NFQ_VERDICT_RETRY_TIME 3
+static int already_seen_warning;
+static int runmode_workers;
+
+#define NFQ_BURST_FACTOR 4
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+//#define NFQ_DFT_QUEUE_LEN NFQ_BURST_FACTOR * MAX_PENDING
+//#define NFQ_NF_BUFSIZE 1500 * NFQ_DFT_QUEUE_LEN
+
+typedef struct NFQThreadVars_
+{
+ uint16_t nfq_index;
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ char *data; /** Per function and thread data */
+ int datalen; /** Length of per function and thread data */
+
+ CaptureStats stats;
+
+} NFQThreadVars;
+/* shared vars for all for nfq queues and threads */
+static NFQGlobalVars nfq_g;
+
+static NFQThreadVars nfq_t[NFQ_MAX_QUEUE];
+static NFQQueueVars nfq_q[NFQ_MAX_QUEUE];
+static uint16_t receive_queue_num = 0;
+static SCMutex nfq_init_lock;
+
+TmEcode ReceiveNFQLoop(ThreadVars *tv, void *data, void *slot);
+TmEcode ReceiveNFQThreadInit(ThreadVars *, void *, void **);
+TmEcode ReceiveNFQThreadDeinit(ThreadVars *, void *);
+void ReceiveNFQThreadExitStats(ThreadVars *, void *);
+
+TmEcode VerdictNFQ(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode VerdictNFQThreadInit(ThreadVars *, void *, void **);
+TmEcode VerdictNFQThreadDeinit(ThreadVars *, void *);
+
+TmEcode DecodeNFQ(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode DecodeNFQThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodeNFQThreadDeinit(ThreadVars *tv, void *data);
+
+TmEcode NFQSetVerdict(Packet *p);
+
+typedef enum NFQMode_ {
+ NFQ_ACCEPT_MODE,
+ NFQ_REPEAT_MODE,
+ NFQ_ROUTE_MODE,
+} NFQMode;
+
+#define NFQ_FLAG_FAIL_OPEN (1 << 0)
+
+typedef struct NFQCnf_ {
+ NFQMode mode;
+ uint32_t mark;
+ uint32_t mask;
+ uint32_t next_queue;
+ uint32_t flags;
+ uint8_t batchcount;
+} NFQCnf;
+
+NFQCnf nfq_config;
+
+void TmModuleReceiveNFQRegister (void)
+{
+ /* XXX create a general NFQ setup function */
+ memset(&nfq_g, 0, sizeof(nfq_g));
+ SCMutexInit(&nfq_init_lock, NULL);
+
+ tmm_modules[TMM_RECEIVENFQ].name = "ReceiveNFQ";
+ tmm_modules[TMM_RECEIVENFQ].ThreadInit = ReceiveNFQThreadInit;
+ tmm_modules[TMM_RECEIVENFQ].Func = NULL;
+ tmm_modules[TMM_RECEIVENFQ].PktAcqLoop = ReceiveNFQLoop;
+ tmm_modules[TMM_RECEIVENFQ].ThreadExitPrintStats = ReceiveNFQThreadExitStats;
+ tmm_modules[TMM_RECEIVENFQ].ThreadDeinit = ReceiveNFQThreadDeinit;
+ tmm_modules[TMM_RECEIVENFQ].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVENFQ].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleVerdictNFQRegister (void)
+{
+ tmm_modules[TMM_VERDICTNFQ].name = "VerdictNFQ";
+ tmm_modules[TMM_VERDICTNFQ].ThreadInit = VerdictNFQThreadInit;
+ tmm_modules[TMM_VERDICTNFQ].Func = VerdictNFQ;
+ tmm_modules[TMM_VERDICTNFQ].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_VERDICTNFQ].ThreadDeinit = VerdictNFQThreadDeinit;
+ tmm_modules[TMM_VERDICTNFQ].RegisterTests = NULL;
+}
+
+void TmModuleDecodeNFQRegister (void)
+{
+ tmm_modules[TMM_DECODENFQ].name = "DecodeNFQ";
+ tmm_modules[TMM_DECODENFQ].ThreadInit = DecodeNFQThreadInit;
+ tmm_modules[TMM_DECODENFQ].Func = DecodeNFQ;
+ tmm_modules[TMM_DECODENFQ].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODENFQ].ThreadDeinit = DecodeNFQThreadDeinit;
+ tmm_modules[TMM_DECODENFQ].RegisterTests = NULL;
+ tmm_modules[TMM_DECODENFQ].flags = TM_FLAG_DECODE_TM;
+}
+
+/** \brief To initialize the NFQ global configuration data
+ *
+ * \param quiet It tells the mode of operation, if it is TRUE nothing will
+ * be get printed.
+ */
+void NFQInitConfig(char quiet)
+{
+ intmax_t value = 0;
+ char* nfq_mode = NULL;
+ int boolval;
+
+ SCLogDebug("Initializing NFQ");
+
+ memset(&nfq_config, 0, sizeof(nfq_config));
+
+ if ((ConfGet("nfq.mode", &nfq_mode)) == 0) {
+ nfq_config.mode = NFQ_ACCEPT_MODE;
+ } else {
+ if (!strcmp("accept", nfq_mode)) {
+ nfq_config.mode = NFQ_ACCEPT_MODE;
+ } else if (!strcmp("repeat", nfq_mode)) {
+ nfq_config.mode = NFQ_REPEAT_MODE;
+ } else if (!strcmp("route", nfq_mode)) {
+ nfq_config.mode = NFQ_ROUTE_MODE;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Unknown nfq.mode");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ (void)ConfGetBool("nfq.fail-open", (int *)&boolval);
+ if (boolval) {
+#ifdef HAVE_NFQ_SET_QUEUE_FLAGS
+ SCLogInfo("Enabling fail-open on queue");
+ nfq_config.flags |= NFQ_FLAG_FAIL_OPEN;
+#else
+ SCLogError(SC_ERR_NFQ_NOSUPPORT,
+ "nfq.%s set but NFQ library has no support for it.", "fail-open");
+#endif
+ }
+
+ if ((ConfGetInt("nfq.repeat-mark", &value)) == 1) {
+ nfq_config.mark = (uint32_t)value;
+ }
+
+ if ((ConfGetInt("nfq.repeat-mask", &value)) == 1) {
+ nfq_config.mask = (uint32_t)value;
+ }
+
+ if ((ConfGetInt("nfq.route-queue", &value)) == 1) {
+ nfq_config.next_queue = ((uint32_t)value) << 16;
+ }
+
+ if ((ConfGetInt("nfq.batchcount", &value)) == 1) {
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (value > 255) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "nfq.batchcount cannot exceed 255.");
+ value = 255;
+ }
+ if (value > 1)
+ nfq_config.batchcount = (uint8_t) (value - 1);
+#else
+ SCLogWarning(SC_ERR_NFQ_NOSUPPORT,
+ "nfq.%s set but NFQ library has no support for it.", "batchcount");
+#endif
+ }
+
+ if (!quiet) {
+ switch (nfq_config.mode) {
+ case NFQ_ACCEPT_MODE:
+ SCLogInfo("NFQ running in standard ACCEPT/DROP mode");
+ break;
+ case NFQ_REPEAT_MODE:
+ SCLogInfo("NFQ running in REPEAT mode with mark %"PRIu32"/%"PRIu32,
+ nfq_config.mark, nfq_config.mask);
+ break;
+ case NFQ_ROUTE_MODE:
+ SCLogInfo("NFQ running in route mode with next queue %"PRIu32,
+ nfq_config.next_queue >> 16);
+ break;
+ }
+ }
+
+}
+
+static uint8_t NFQVerdictCacheLen(NFQQueueVars *t)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ return t->verdict_cache.len;
+#else
+ return 0;
+#endif
+}
+
+static void NFQVerdictCacheFlush(NFQQueueVars *t)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ int ret;
+ int iter = 0;
+
+ do {
+ if (t->verdict_cache.mark_valid)
+ ret = nfq_set_verdict_batch2(t->qh,
+ t->verdict_cache.packet_id,
+ t->verdict_cache.verdict,
+ t->verdict_cache.mark);
+ else
+ ret = nfq_set_verdict_batch(t->qh,
+ t->verdict_cache.packet_id,
+ t->verdict_cache.verdict);
+ } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
+
+ if (ret < 0) {
+ SCLogWarning(SC_ERR_NFQ_SET_VERDICT, "nfq_set_verdict_batch failed: %s",
+ strerror(errno));
+ } else {
+ t->verdict_cache.len = 0;
+ t->verdict_cache.mark_valid = 0;
+ }
+#endif
+}
+
+static int NFQVerdictCacheAdd(NFQQueueVars *t, Packet *p, uint32_t verdict)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (t->verdict_cache.maxlen == 0)
+ return -1;
+
+ if (p->flags & PKT_STREAM_MODIFIED || verdict == NF_DROP)
+ goto flush;
+
+ if (p->flags & PKT_MARK_MODIFIED) {
+ if (!t->verdict_cache.mark_valid) {
+ if (t->verdict_cache.len)
+ goto flush;
+ t->verdict_cache.mark_valid = 1;
+ t->verdict_cache.mark = p->nfq_v.mark;
+ } else if (t->verdict_cache.mark != p->nfq_v.mark) {
+ goto flush;
+ }
+ } else if (t->verdict_cache.mark_valid) {
+ goto flush;
+ }
+
+ if (t->verdict_cache.len == 0) {
+ t->verdict_cache.verdict = verdict;
+ } else if (t->verdict_cache.verdict != verdict)
+ goto flush;
+
+ /* same verdict, mark not set or identical -> can cache */
+ t->verdict_cache.packet_id = p->nfq_v.id;
+
+ if (t->verdict_cache.len >= t->verdict_cache.maxlen)
+ NFQVerdictCacheFlush(t);
+ else
+ t->verdict_cache.len++;
+ return 0;
+ flush:
+ /* can't cache. Flush current cache and signal caller it should send single verdict */
+ if (NFQVerdictCacheLen(t) > 0)
+ NFQVerdictCacheFlush(t);
+#endif
+ return -1;
+}
+
+static inline void NFQMutexInit(NFQQueueVars *nq)
+{
+ char *active_runmode = RunmodeGetActive();
+
+ if (active_runmode && !strcmp("workers", active_runmode)) {
+ nq->use_mutex = 0;
+ runmode_workers = 1;
+ SCLogInfo("NFQ running in 'workers' runmode, will not use mutex.");
+ } else {
+ nq->use_mutex = 1;
+ runmode_workers = 0;
+ SCMutexInit(&nq->mutex_qh, NULL);
+ }
+}
+
+#define NFQMutexLock(nq) do { \
+ if ((nq)->use_mutex) \
+ SCMutexLock(&(nq)->mutex_qh); \
+} while (0)
+
+#define NFQMutexUnlock(nq) do { \
+ if ((nq)->use_mutex) \
+ SCMutexUnlock(&(nq)->mutex_qh); \
+} while (0)
+
+/**
+ * \brief Read data from nfq message and setup Packet
+ *
+ * \note
+ * In case of error, this function verdict the packet
+ * to avoid skb to get stuck in kernel.
+ */
+int NFQSetupPkt (Packet *p, struct nfq_q_handle *qh, void *data)
+{
+ struct nfq_data *tb = (struct nfq_data *)data;
+ int ret;
+ char *pktdata;
+ struct nfqnl_msg_packet_hdr *ph;
+
+ ph = nfq_get_msg_packet_hdr(tb);
+ if (ph != NULL) {
+ p->nfq_v.id = ntohl(ph->packet_id);
+ //p->nfq_v.hw_protocol = ntohs(p->nfq_v.ph->hw_protocol);
+ p->nfq_v.hw_protocol = ph->hw_protocol;
+ }
+ p->nfq_v.mark = nfq_get_nfmark(tb);
+ if (nfq_config.mode == NFQ_REPEAT_MODE) {
+ if ((nfq_config.mark & nfq_config.mask) ==
+ (p->nfq_v.mark & nfq_config.mask)) {
+ int iter = 0;
+ if (already_seen_warning < MAX_ALREADY_TREATED)
+ SCLogInfo("Packet seems already treated by suricata");
+ already_seen_warning++;
+ do {
+ ret = nfq_set_verdict(qh, p->nfq_v.id, NF_ACCEPT, 0, NULL);
+ } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
+ if (ret < 0) {
+ SCLogWarning(SC_ERR_NFQ_SET_VERDICT,
+ "nfq_set_verdict of %p failed %" PRId32 ": %s",
+ p, ret, strerror(errno));
+ }
+ return -1 ;
+ }
+ }
+ p->nfq_v.ifi = nfq_get_indev(tb);
+ p->nfq_v.ifo = nfq_get_outdev(tb);
+ p->nfq_v.verdicted = 0;
+
+#ifdef NFQ_GET_PAYLOAD_SIGNED
+ ret = nfq_get_payload(tb, &pktdata);
+#else
+ ret = nfq_get_payload(tb, (unsigned char **) &pktdata);
+#endif /* NFQ_GET_PAYLOAD_SIGNED */
+ if (ret > 0) {
+ /* nfq_get_payload returns a pointer to a part of memory
+ * that is not preserved over the lifetime of our packet.
+ * So we need to copy it. */
+ if (ret > 65536) {
+ /* Will not be able to copy data ! Set length to 0
+ * to trigger an error in packet decoding.
+ * This is unlikely to happen */
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "NFQ sent too big packet");
+ SET_PKT_LEN(p, 0);
+ } else if (runmode_workers) {
+ PacketSetData(p, (uint8_t *)pktdata, ret);
+ } else {
+ PacketCopyData(p, (uint8_t *)pktdata, ret);
+ }
+ } else if (ret == -1) {
+ /* unable to get pointer to data, ensure packet length is zero.
+ * This will trigger an error in packet decoding */
+ SET_PKT_LEN(p, 0);
+ }
+
+ ret = nfq_get_timestamp(tb, &p->ts);
+ if (ret != 0) {
+ memset (&p->ts, 0, sizeof(struct timeval));
+ gettimeofday(&p->ts, NULL);
+ }
+
+ p->datalink = DLT_RAW;
+ return 0;
+}
+
+static void NFQReleasePacket(Packet *p)
+{
+ if (unlikely(!p->nfq_v.verdicted)) {
+ PACKET_UPDATE_ACTION(p, ACTION_DROP);
+ NFQSetVerdict(p);
+ }
+ PacketFreeOrRelease(p);
+}
+
+static int NFQCallBack(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
+ struct nfq_data *nfa, void *data)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ ThreadVars *tv = ntv->tv;
+ int ret;
+
+ /* grab a packet */
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ return -1;
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ p->nfq_v.nfq_index = ntv->nfq_index;
+ ret = NFQSetupPkt(p, qh, (void *)nfa);
+ if (ret == -1) {
+#ifdef COUNTERS
+ NFQQueueVars *nfq_q = NFQGetQueue(ntv->nfq_index);
+ nfq_q->errs++;
+ nfq_q->pkts++;
+ nfq_q->bytes += GET_PKT_LEN(p);
+#endif /* COUNTERS */
+ /* NFQSetupPkt is issuing a verdict
+ so we only recycle Packet and leave */
+ TmqhOutputPacketpool(tv, p);
+ return 0;
+ }
+
+ p->ReleasePacket = NFQReleasePacket;
+
+#ifdef COUNTERS
+ NFQQueueVars *nfq_q = NFQGetQueue(ntv->nfq_index);
+ nfq_q->pkts++;
+ nfq_q->bytes += GET_PKT_LEN(p);
+#endif /* COUNTERS */
+
+ if (ntv->slot) {
+ if (TmThreadsSlotProcessPkt(tv, ntv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ntv->tv, p);
+ return -1;
+ }
+ } else {
+ /* pass on... */
+ tv->tmqh_out(tv, p);
+ }
+
+ return 0;
+}
+
+TmEcode NFQInitThread(NFQThreadVars *nfq_t, uint32_t queue_maxlen)
+{
+#ifndef OS_WIN32
+ struct timeval tv;
+ int opt;
+#endif
+ NFQQueueVars *nfq_q = NFQGetQueue(nfq_t->nfq_index);
+ if (nfq_q == NULL) {
+ SCLogError(SC_ERR_NFQ_OPEN, "no queue for given index");
+ return TM_ECODE_FAILED;
+ }
+ SCLogDebug("opening library handle");
+ nfq_q->h = nfq_open();
+ if (!nfq_q->h) {
+ SCLogError(SC_ERR_NFQ_OPEN, "nfq_open() failed");
+ return TM_ECODE_FAILED;
+ }
+
+ if (nfq_g.unbind == 0)
+ {
+ /* VJ: on my Ubuntu Hardy system this fails the first time it's
+ * run. Ignoring the error seems to have no bad effects. */
+ SCLogDebug("unbinding existing nf_queue handler for AF_INET (if any)");
+ if (nfq_unbind_pf(nfq_q->h, AF_INET) < 0) {
+ SCLogError(SC_ERR_NFQ_UNBIND, "nfq_unbind_pf() for AF_INET failed");
+ exit(EXIT_FAILURE);
+ }
+ if (nfq_unbind_pf(nfq_q->h, AF_INET6) < 0) {
+ SCLogError(SC_ERR_NFQ_UNBIND, "nfq_unbind_pf() for AF_INET6 failed");
+ exit(EXIT_FAILURE);
+ }
+ nfq_g.unbind = 1;
+
+ SCLogDebug("binding nfnetlink_queue as nf_queue handler for AF_INET and AF_INET6");
+
+ if (nfq_bind_pf(nfq_q->h, AF_INET) < 0) {
+ SCLogError(SC_ERR_NFQ_BIND, "nfq_bind_pf() for AF_INET failed");
+ exit(EXIT_FAILURE);
+ }
+ if (nfq_bind_pf(nfq_q->h, AF_INET6) < 0) {
+ SCLogError(SC_ERR_NFQ_BIND, "nfq_bind_pf() for AF_INET6 failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ SCLogInfo("binding this thread %d to queue '%" PRIu32 "'", nfq_t->nfq_index, nfq_q->queue_num);
+
+ /* pass the thread memory as a void ptr so the
+ * callback function has access to it. */
+ nfq_q->qh = nfq_create_queue(nfq_q->h, nfq_q->queue_num, &NFQCallBack, (void *)nfq_t);
+ if (nfq_q->qh == NULL)
+ {
+ SCLogError(SC_ERR_NFQ_CREATE_QUEUE, "nfq_create_queue failed");
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("setting copy_packet mode");
+
+ /* 05DC = 1500 */
+ //if (nfq_set_mode(nfq_t->qh, NFQNL_COPY_PACKET, 0x05DC) < 0) {
+ if (nfq_set_mode(nfq_q->qh, NFQNL_COPY_PACKET, 0xFFFF) < 0) {
+ SCLogError(SC_ERR_NFQ_SET_MODE, "can't set packet_copy mode");
+ return TM_ECODE_FAILED;
+ }
+
+#ifdef HAVE_NFQ_MAXLEN
+ if (queue_maxlen > 0) {
+ SCLogInfo("setting queue length to %" PRId32 "", queue_maxlen);
+
+ /* non-fatal if it fails */
+ if (nfq_set_queue_maxlen(nfq_q->qh, queue_maxlen) < 0) {
+ SCLogWarning(SC_ERR_NFQ_MAXLEN, "can't set queue maxlen: your kernel probably "
+ "doesn't support setting the queue length");
+ }
+ }
+#endif /* HAVE_NFQ_MAXLEN */
+
+#ifndef OS_WIN32
+ /* set netlink buffer size to a decent value */
+ nfnl_rcvbufsiz(nfq_nfnlh(nfq_q->h), queue_maxlen * 1500);
+ SCLogInfo("setting nfnl bufsize to %" PRId32 "", queue_maxlen * 1500);
+
+ nfq_q->nh = nfq_nfnlh(nfq_q->h);
+ nfq_q->fd = nfnl_fd(nfq_q->nh);
+ NFQMutexInit(nfq_q);
+
+ /* Set some netlink specific option on the socket to increase
+ performance */
+ opt = 1;
+#ifdef NETLINK_BROADCAST_SEND_ERROR
+ if (setsockopt(nfq_q->fd, SOL_NETLINK,
+ NETLINK_BROADCAST_SEND_ERROR, &opt, sizeof(int)) == -1) {
+ SCLogWarning(SC_ERR_NFQ_SETSOCKOPT,
+ "can't set netlink broadcast error: %s",
+ strerror(errno));
+ }
+#endif
+ /* Don't send error about no buffer space available but drop the
+ packets instead */
+#ifdef NETLINK_NO_ENOBUFS
+ if (setsockopt(nfq_q->fd, SOL_NETLINK,
+ NETLINK_NO_ENOBUFS, &opt, sizeof(int)) == -1) {
+ SCLogWarning(SC_ERR_NFQ_SETSOCKOPT,
+ "can't set netlink enobufs: %s",
+ strerror(errno));
+ }
+#endif
+
+#ifdef HAVE_NFQ_SET_QUEUE_FLAGS
+ if (nfq_config.flags & NFQ_FLAG_FAIL_OPEN) {
+ uint32_t flags = NFQA_CFG_F_FAIL_OPEN;
+ uint32_t mask = NFQA_CFG_F_FAIL_OPEN;
+ int r = nfq_set_queue_flags(nfq_q->qh, mask, flags);
+
+ if (r == -1) {
+ SCLogWarning(SC_ERR_NFQ_SET_MODE, "can't set fail-open mode: %s",
+ strerror(errno));
+ } else {
+ SCLogInfo("fail-open mode should be set on queue");
+ }
+ }
+#endif
+
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (runmode_workers) {
+ nfq_q->verdict_cache.maxlen = nfq_config.batchcount;
+ } else if (nfq_config.batchcount) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "nfq.batchcount is only valid in workers runmode.");
+ }
+#endif
+
+ /* set a timeout to the socket so we can check for a signal
+ * in case we don't get packets for a longer period. */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ if(setsockopt(nfq_q->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+ SCLogWarning(SC_ERR_NFQ_SETSOCKOPT, "can't set socket timeout: %s", strerror(errno));
+ }
+
+ SCLogDebug("nfq_q->h %p, nfq_q->nh %p, nfq_q->qh %p, nfq_q->fd %" PRId32 "",
+ nfq_q->h, nfq_q->nh, nfq_q->qh, nfq_q->fd);
+#else /* OS_WIN32 */
+ NFQMutexInit(nfq_q);
+ nfq_q->ovr.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ nfq_q->fd = nfq_fd(nfq_q->h);
+ SCLogDebug("nfq_q->h %p, nfq_q->qh %p, nfq_q->fd %p", nfq_q->h, nfq_q->qh, nfq_q->fd);
+#endif /* OS_WIN32 */
+
+ return TM_ECODE_OK;
+}
+
+TmEcode ReceiveNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCMutexLock(&nfq_init_lock);
+
+#ifndef OS_WIN32
+ sigset_t sigs;
+ sigfillset(&sigs);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+#endif /* OS_WIN32 */
+
+ NFQThreadVars *ntv = (NFQThreadVars *) initdata;
+ /* store the ThreadVars pointer in our NFQ thread context
+ * as we will need it in our callback function */
+ ntv->tv = tv;
+
+ int r = NFQInitThread(ntv, (max_pending_packets * NFQ_BURST_FACTOR));
+ if (r < 0) {
+ SCLogError(SC_ERR_NFQ_THREAD_INIT, "nfq thread failed to initialize");
+
+ SCMutexUnlock(&nfq_init_lock);
+ exit(EXIT_FAILURE);
+ }
+
+#define T_DATA_SIZE 70000
+ ntv->data = SCMalloc(T_DATA_SIZE);
+ if (ntv->data == NULL) {
+ SCMutexUnlock(&nfq_init_lock);
+ return TM_ECODE_FAILED;
+ }
+ ntv->datalen = T_DATA_SIZE;
+#undef T_DATA_SIZE
+
+ *data = (void *)ntv;
+
+ SCMutexUnlock(&nfq_init_lock);
+ return TM_ECODE_OK;
+}
+
+
+TmEcode ReceiveNFQThreadDeinit(ThreadVars *t, void *data)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
+
+ if (ntv->data != NULL) {
+ SCFree(ntv->data);
+ ntv->data = NULL;
+ }
+ ntv->datalen = 0;
+
+ NFQMutexLock(nq);
+ SCLogDebug("starting... will close queuenum %" PRIu32 "", nq->queue_num);
+ if (nq->qh) {
+ nfq_destroy_queue(nq->qh);
+ nq->qh = NULL;
+ }
+ NFQMutexUnlock(nq);
+
+ return TM_ECODE_OK;
+}
+
+
+TmEcode VerdictNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *) initdata;
+
+ CaptureStatsSetup(tv, &ntv->stats);
+
+ *data = (void *)ntv;
+ return TM_ECODE_OK;
+}
+
+TmEcode VerdictNFQThreadDeinit(ThreadVars *tv, void *data)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
+
+ SCLogDebug("starting... will close queuenum %" PRIu32 "", nq->queue_num);
+ NFQMutexLock(nq);
+ if (nq->qh) {
+ nfq_destroy_queue(nq->qh);
+ nq->qh = NULL;
+ }
+ NFQMutexUnlock(nq);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Add a Netfilter queue
+ *
+ * \param string with the queue name
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int NFQRegisterQueue(char *queue)
+{
+ NFQThreadVars *ntv = NULL;
+ NFQQueueVars *nq = NULL;
+ /* Extract the queue number from the specified command line argument */
+ uint16_t queue_num = 0;
+ if ((ByteExtractStringUint16(&queue_num, 10, strlen(queue), queue)) < 0)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "specified queue number %s is not "
+ "valid", queue);
+ return -1;
+ }
+
+ SCMutexLock(&nfq_init_lock);
+ if (receive_queue_num >= NFQ_MAX_QUEUE) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "too much Netfilter queue registered (%d)",
+ receive_queue_num);
+ SCMutexUnlock(&nfq_init_lock);
+ return -1;
+ }
+ if (receive_queue_num == 0) {
+ memset(&nfq_t, 0, sizeof(nfq_t));
+ memset(&nfq_q, 0, sizeof(nfq_q));
+ }
+
+ ntv = &nfq_t[receive_queue_num];
+ ntv->nfq_index = receive_queue_num;
+
+ nq = &nfq_q[receive_queue_num];
+ nq->queue_num = queue_num;
+ receive_queue_num++;
+ SCMutexUnlock(&nfq_init_lock);
+ LiveRegisterDevice(queue);
+
+ SCLogDebug("Queue \"%s\" registered.", queue);
+ return 0;
+}
+
+
+
+/**
+ * \brief Get a pointer to the NFQ queue at index
+ *
+ * \param number idx of the queue in our array
+ *
+ * \retval ptr pointer to the NFQThreadVars at index
+ * \retval NULL on error
+ */
+void *NFQGetQueue(int number)
+{
+ if (number >= receive_queue_num)
+ return NULL;
+
+ return (void *)&nfq_q[number];
+}
+
+/**
+ * \brief Get a pointer to the NFQ thread at index
+ *
+ * This function is temporary used as configuration parser.
+ *
+ * \param number idx of the queue in our array
+ *
+ * \retval ptr pointer to the NFQThreadVars at index
+ * \retval NULL on error
+ */
+void *NFQGetThread(int number)
+{
+ if (number >= receive_queue_num)
+ return NULL;
+
+ return (void *)&nfq_t[number];
+}
+
+/**
+ * \brief NFQ function to get a packet from the kernel
+ *
+ * \note separate functions for Linux and Win32 for readability.
+ */
+#ifndef OS_WIN32
+void NFQRecvPkt(NFQQueueVars *t, NFQThreadVars *tv)
+{
+ int rv, ret;
+ int flag = NFQVerdictCacheLen(t) ? MSG_DONTWAIT : 0;
+
+ /* XXX what happens on rv == 0? */
+ rv = recv(t->fd, tv->data, tv->datalen, flag);
+
+ if (rv < 0) {
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* no error on timeout */
+ if (flag)
+ NFQVerdictCacheFlush(t);
+ } else {
+#ifdef COUNTERS
+ NFQMutexLock(t);
+ t->errs++;
+ NFQMutexUnlock(t);
+#endif /* COUNTERS */
+ }
+ } else if(rv == 0) {
+ SCLogWarning(SC_ERR_NFQ_RECV, "recv got returncode 0");
+ } else {
+#ifdef DBG_PERF
+ if (rv > t->dbg_maxreadsize)
+ t->dbg_maxreadsize = rv;
+#endif /* DBG_PERF */
+
+ //printf("NFQRecvPkt: t %p, rv = %" PRId32 "\n", t, rv);
+
+ NFQMutexLock(t);
+ if (t->qh != NULL) {
+ ret = nfq_handle_packet(t->h, tv->data, rv);
+ } else {
+ SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "NFQ handle has been destroyed");
+ ret = -1;
+ }
+ NFQMutexUnlock(t);
+
+ if (ret != 0) {
+ SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "nfq_handle_packet error %" PRId32 "", ret);
+ }
+ }
+}
+#else /* WIN32 version of NFQRecvPkt */
+void NFQRecvPkt(NFQQueueVars *t, NFQThreadVars *tv)
+{
+ int rv, ret;
+ static int timeouted = 0;
+
+ if (timeouted) {
+ if (WaitForSingleObject(t->ovr.hEvent, 1000) == WAIT_TIMEOUT) {
+ rv = -1;
+ errno = EINTR;
+ goto process_rv;
+ }
+ timeouted = 0;
+ }
+
+read_packet_again:
+
+ if (!ReadFile(t->fd, tv->buf, sizeof(tv->buf), (DWORD*)&rv, &t->ovr)) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ rv = -1;
+ errno = EIO;
+ } else {
+ if (WaitForSingleObject(t->ovr.hEvent, 1000) == WAIT_TIMEOUT) {
+ rv = -1;
+ errno = EINTR;
+ timeouted = 1;
+ } else {
+ /* We needn't to call GetOverlappedResult() because it always
+ * fail with our error code ERROR_MORE_DATA. */
+ goto read_packet_again;
+ }
+ }
+ }
+
+process_rv:
+
+ if (rv < 0) {
+ if (errno == EINTR) {
+ /* no error on timeout */
+ } else {
+#ifdef COUNTERS
+ t->errs++;
+#endif /* COUNTERS */
+ }
+ } else if(rv == 0) {
+ SCLogWarning(SC_ERR_NFQ_RECV, "recv got returncode 0");
+ } else {
+#ifdef DBG_PERF
+ if (rv > t->dbg_maxreadsize)
+ t->dbg_maxreadsize = rv;
+#endif /* DBG_PERF */
+
+ //printf("NFQRecvPkt: t %p, rv = %" PRId32 "\n", t, rv);
+
+ NFQMutexLock(t);
+ if (t->qh) {
+ ret = nfq_handle_packet(t->h, buf, rv);
+ } else {
+ SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "NFQ handle has been destroyed");
+ ret = -1;
+ }
+ NFQMutexUnlock(t);
+
+ if (ret != 0) {
+ SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "nfq_handle_packet error %" PRId32 "", ret);
+ }
+ }
+}
+#endif /* OS_WIN32 */
+
+/**
+ * \brief Main NFQ reading Loop function
+ */
+TmEcode ReceiveNFQLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
+
+ ntv->slot = ((TmSlot *) slot)->slot_next;
+
+ while(1) {
+ if (suricata_ctl_flags != 0) {
+ NFQMutexLock(nq);
+ if (nq->qh) {
+ nfq_destroy_queue(nq->qh);
+ nq->qh = NULL;
+ }
+ NFQMutexUnlock(nq);
+ break;
+ }
+ NFQRecvPkt(nq, ntv);
+
+ StatsSyncCountersIfSignalled(tv);
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief NFQ receive module stats printing function
+ */
+void ReceiveNFQThreadExitStats(ThreadVars *tv, void *data)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
+#ifdef COUNTERS
+ SCLogNotice("(%s) Treated: Pkts %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
+ tv->name, nq->pkts, nq->bytes, nq->errs);
+ SCLogNotice("(%s) Verdict: Accepted %"PRIu32", Dropped %"PRIu32", Replaced %"PRIu32,
+ tv->name, nq->accepted, nq->dropped, nq->replaced);
+#endif
+}
+
+/**
+ * \brief NFQ verdict function
+ */
+TmEcode NFQSetVerdict(Packet *p)
+{
+ int iter = 0;
+ int ret = 0;
+ uint32_t verdict = NF_ACCEPT;
+ /* we could also have a direct pointer but we need to have a ref counf in this case */
+ NFQQueueVars *t = nfq_q + p->nfq_v.nfq_index;
+
+ /** \todo add a test on validity of the entry NFQQueueVars could have been
+ * wipeout
+ */
+
+ p->nfq_v.verdicted = 1;
+
+ /* can't verdict a "fake" packet */
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ return TM_ECODE_OK;
+ }
+
+ //printf("%p verdicting on queue %" PRIu32 "\n", t, t->queue_num);
+ NFQMutexLock(t);
+
+ if (t->qh == NULL) {
+ /* Somebody has started a clean-up, we leave */
+ NFQMutexUnlock(t);
+ return TM_ECODE_OK;
+ }
+
+ if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ verdict = NF_DROP;
+#ifdef COUNTERS
+ t->dropped++;
+#endif /* COUNTERS */
+ } else {
+ switch (nfq_config.mode) {
+ default:
+ case NFQ_ACCEPT_MODE:
+ verdict = NF_ACCEPT;
+ break;
+ case NFQ_REPEAT_MODE:
+ verdict = NF_REPEAT;
+ break;
+ case NFQ_ROUTE_MODE:
+ verdict = ((uint32_t) NF_QUEUE) | nfq_config.next_queue;
+ break;
+ }
+
+ if (p->flags & PKT_STREAM_MODIFIED) {
+#ifdef COUNTERS
+ t->replaced++;
+#endif /* COUNTERS */
+ }
+
+#ifdef COUNTERS
+ t->accepted++;
+#endif /* COUNTERS */
+ }
+
+ ret = NFQVerdictCacheAdd(t, p, verdict);
+ if (ret == 0) {
+ NFQMutexUnlock(t);
+ return TM_ECODE_OK;
+ }
+
+ do {
+ switch (nfq_config.mode) {
+ default:
+ case NFQ_ACCEPT_MODE:
+ case NFQ_ROUTE_MODE:
+ if (p->flags & PKT_MARK_MODIFIED) {
+#ifdef HAVE_NFQ_SET_VERDICT2
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
+ p->nfq_v.mark,
+ GET_PKT_LEN(p), GET_PKT_DATA(p));
+ } else {
+ ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
+ p->nfq_v.mark,
+ 0, NULL);
+ }
+#else /* fall back to old function */
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict,
+ htonl(p->nfq_v.mark),
+ GET_PKT_LEN(p), GET_PKT_DATA(p));
+ } else {
+ ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict,
+ htonl(p->nfq_v.mark),
+ 0, NULL);
+ }
+#endif /* HAVE_NFQ_SET_VERDICT2 */
+ } else {
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict,
+ GET_PKT_LEN(p), GET_PKT_DATA(p));
+ } else {
+ ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, 0, NULL);
+ }
+
+ }
+ break;
+ case NFQ_REPEAT_MODE:
+#ifdef HAVE_NFQ_SET_VERDICT2
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
+ (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask),
+ GET_PKT_LEN(p), GET_PKT_DATA(p));
+ } else {
+ ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
+ (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask),
+ 0, NULL);
+ }
+#else /* fall back to old function */
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict,
+ htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)),
+ GET_PKT_LEN(p), GET_PKT_DATA(p));
+ } else {
+ ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict,
+ htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)),
+ 0, NULL);
+ }
+#endif /* HAVE_NFQ_SET_VERDICT2 */
+ break;
+ }
+ } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
+
+ NFQMutexUnlock(t);
+
+ if (ret < 0) {
+ SCLogWarning(SC_ERR_NFQ_SET_VERDICT,
+ "nfq_set_verdict of %p failed %" PRId32 ": %s",
+ p, ret, strerror(errno));
+ return TM_ECODE_FAILED;
+ }
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief NFQ verdict module packet entry function
+ */
+TmEcode VerdictNFQ(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ NFQThreadVars *ntv = (NFQThreadVars *)data;
+ /* update counters */
+ CaptureStatsUpdate(tv, &ntv->stats, p);
+
+ int ret;
+ /* if this is a tunnel packet we check if we are ready to verdict
+ * already. */
+ if (IS_TUNNEL_PKT(p)) {
+ char verdict = 1;
+ //printf("VerdictNFQ: tunnel pkt: %p %s\n", p, p->root ? "upper layer" : "root");
+
+ SCMutex *m = p->root ? &p->root->tunnel_mutex : &p->tunnel_mutex;
+ SCMutexLock(m);
+
+ /* if there are more tunnel packets than ready to verdict packets,
+ * we won't verdict this one */
+ if (TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p)) {
+ SCLogDebug("not ready to verdict yet: TUNNEL_PKT_TPR(p) > "
+ "TUNNEL_PKT_RTV(p) = %" PRId32 " > %" PRId32,
+ TUNNEL_PKT_TPR(p), TUNNEL_PKT_RTV(p));
+ verdict = 0;
+ }
+
+ SCMutexUnlock(m);
+
+ /* don't verdict if we are not ready */
+ if (verdict == 1) {
+ //printf("VerdictNFQ: setting verdict\n");
+ ret = NFQSetVerdict(p->root ? p->root : p);
+ if (ret != TM_ECODE_OK)
+ return ret;
+ } else {
+ TUNNEL_INCR_PKT_RTV(p);
+ }
+ } else {
+ /* no tunnel, verdict normally */
+ ret = NFQSetVerdict(p);
+ if (ret != TM_ECODE_OK)
+ return ret;
+ }
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Decode a packet coming from NFQ
+ */
+TmEcode DecodeNFQ(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+
+ IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ if (IPV4_GET_RAW_VER(ip4h) == 4) {
+ SCLogDebug("IPv4 packet");
+ DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else if(IPV6_GET_RAW_VER(ip6h) == 6) {
+ SCLogDebug("IPv6 packet");
+ DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ } else {
+ SCLogDebug("packet unsupported by NFQ, first byte: %02x", *GET_PKT_DATA(p));
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Initialize the NFQ Decode threadvars
+ */
+TmEcode DecodeNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ DecodeThreadVars *dtv = NULL;
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+ return TM_ECODE_OK;
+}
+
+TmEcode DecodeNFQThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#endif /* NFQ */
+
diff --git a/framework/src/suricata/src/source-nfq.h b/framework/src/suricata/src/source-nfq.h
new file mode 100644
index 00000000..41a54b78
--- /dev/null
+++ b/framework/src/suricata/src/source-nfq.h
@@ -0,0 +1,110 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __SOURCE_NFQ_H__
+#define __SOURCE_NFQ_H__
+
+#ifdef NFQ
+
+#include "threads.h"
+#ifdef OS_WIN32
+#include <netfilter/netfilter.h>
+#else
+#include <linux/netfilter.h> /* for NF_ACCEPT */
+#endif
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+#define NFQ_MAX_QUEUE 16
+
+/* idea: set the recv-thread id in the packet to
+ * select an verdict-queue */
+
+typedef struct NFQPacketVars_
+{
+ int id; /* this nfq packets id */
+ uint16_t nfq_index; /* index in NFQ array */
+ uint8_t verdicted;
+
+ uint32_t mark;
+ uint32_t ifi;
+ uint32_t ifo;
+ uint16_t hw_protocol;
+} NFQPacketVars;
+
+typedef struct NFQQueueVars_
+{
+ struct nfq_handle *h;
+#ifndef OS_WIN32
+ struct nfnl_handle *nh;
+ int fd;
+#else
+ HANDLE fd;
+ OVERLAPPED ovr;
+#endif
+ uint8_t use_mutex;
+ /* 2 threads deal with the queue handle, so add a mutex */
+ struct nfq_q_handle *qh;
+ SCMutex mutex_qh;
+ /* this one should be not changing after init */
+ uint16_t queue_num;
+ /* position into the NFQ queue var array */
+ uint16_t nfq_index;
+
+#ifdef DBG_PERF
+ int dbg_maxreadsize;
+#endif /* DBG_PERF */
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+ uint32_t accepted;
+ uint32_t dropped;
+ uint32_t replaced;
+ struct {
+ uint32_t packet_id; /* id of last processed packet */
+ uint32_t verdict;
+ uint32_t mark;
+ uint8_t mark_valid:1;
+ uint8_t len;
+ uint8_t maxlen;
+ } verdict_cache;
+
+} NFQQueueVars;
+
+
+
+typedef struct NFQGlobalVars_
+{
+ char unbind;
+} NFQGlobalVars;
+
+void NFQInitConfig(char quiet);
+int NFQRegisterQueue(char *queue);
+int NFQGetQueueCount(void);
+void *NFQGetQueue(int number);
+int NFQGetQueueNum(int number);
+void *NFQGetThread(int number);
+#endif /* NFQ */
+#endif /* __SOURCE_NFQ_H__ */
+
diff --git a/framework/src/suricata/src/source-pcap-file.c b/framework/src/suricata/src/source-pcap-file.c
new file mode 100644
index 00000000..0b982fd3
--- /dev/null
+++ b/framework/src/suricata/src/source-pcap-file.c
@@ -0,0 +1,475 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * File based pcap packet acquisition support
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "source-pcap-file.h"
+#include "util-time.h"
+#include "util-debug.h"
+#include "conf.h"
+#include "util-error.h"
+#include "util-privs.h"
+#include "tmqh-packetpool.h"
+#include "tm-threads.h"
+#include "util-optimize.h"
+#include "flow-manager.h"
+#include "util-profiling.h"
+#include "runmode-unix-socket.h"
+#include "util-checksum.h"
+#include "util-atomic.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+extern int max_pending_packets;
+
+//static int pcap_max_read_packets = 0;
+
+typedef struct PcapFileGlobalVars_ {
+ pcap_t *pcap_handle;
+ int (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, u_int8_t *, u_int16_t, PacketQueue *);
+ int datalink;
+ struct bpf_program filter;
+ uint64_t cnt; /** packet counter */
+ ChecksumValidationMode conf_checksum_mode;
+ ChecksumValidationMode checksum_mode;
+ SC_ATOMIC_DECLARE(unsigned int, invalid_checksums);
+
+} PcapFileGlobalVars;
+
+/** max packets < 65536 */
+//#define PCAP_FILE_MAX_PKTS 256
+
+typedef struct PcapFileThreadVars_
+{
+ uint32_t tenant_id;
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ /** callback result -- set if one of the thread module failed. */
+ int cb_result;
+
+ uint8_t done;
+ uint32_t errs;
+} PcapFileThreadVars;
+
+static PcapFileGlobalVars pcap_g;
+
+TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *);
+
+TmEcode ReceivePcapFileThreadInit(ThreadVars *, void *, void **);
+void ReceivePcapFileThreadExitStats(ThreadVars *, void *);
+TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *);
+
+TmEcode DecodePcapFile(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode DecodePcapFileThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data);
+
+void TmModuleReceivePcapFileRegister (void)
+{
+ memset(&pcap_g, 0x00, sizeof(pcap_g));
+
+ tmm_modules[TMM_RECEIVEPCAPFILE].name = "ReceivePcapFile";
+ tmm_modules[TMM_RECEIVEPCAPFILE].ThreadInit = ReceivePcapFileThreadInit;
+ tmm_modules[TMM_RECEIVEPCAPFILE].Func = NULL;
+ tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqLoop = ReceivePcapFileLoop;
+ tmm_modules[TMM_RECEIVEPCAPFILE].ThreadExitPrintStats = ReceivePcapFileThreadExitStats;
+ tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit = ReceivePcapFileThreadDeinit;
+ tmm_modules[TMM_RECEIVEPCAPFILE].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEPCAPFILE].cap_flags = 0;
+ tmm_modules[TMM_RECEIVEPCAPFILE].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleDecodePcapFileRegister (void)
+{
+ tmm_modules[TMM_DECODEPCAPFILE].name = "DecodePcapFile";
+ tmm_modules[TMM_DECODEPCAPFILE].ThreadInit = DecodePcapFileThreadInit;
+ tmm_modules[TMM_DECODEPCAPFILE].Func = DecodePcapFile;
+ tmm_modules[TMM_DECODEPCAPFILE].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEPCAPFILE].ThreadDeinit = DecodePcapFileThreadDeinit;
+ tmm_modules[TMM_DECODEPCAPFILE].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0;
+ tmm_modules[TMM_DECODEPCAPFILE].flags = TM_FLAG_DECODE_TM;
+}
+
+void PcapFileGlobalInit()
+{
+ SC_ATOMIC_INIT(pcap_g.invalid_checksums);
+}
+
+void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
+{
+ SCEnter();
+
+ PcapFileThreadVars *ptv = (PcapFileThreadVars *)user;
+ Packet *p = PacketGetFromQueueOrAlloc();
+
+ if (unlikely(p == NULL)) {
+ SCReturn;
+ }
+ PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
+
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+ p->ts.tv_sec = h->ts.tv_sec;
+ p->ts.tv_usec = h->ts.tv_usec;
+ SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
+ p->datalink = pcap_g.datalink;
+ p->pcap_cnt = ++pcap_g.cnt;
+
+ p->pcap_v.tenant_id = ptv->tenant_id;
+ ptv->pkts++;
+ ptv->bytes += h->caplen;
+
+ if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
+ SCReturn;
+ }
+
+ /* We only check for checksum disable */
+ if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ if (ChecksumAutoModeCheck(ptv->pkts, p->pcap_cnt,
+ SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
+ pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ }
+
+ PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
+
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ pcap_breakloop(pcap_g.pcap_handle);
+ ptv->cb_result = TM_ECODE_FAILED;
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Main PCAP file reading Loop function
+ */
+TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ int packet_q_len = 64;
+ PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
+ int r;
+ TmSlot *s = (TmSlot *)slot;
+
+ ptv->slot = s->slot_next;
+ ptv->cb_result = TM_ECODE_OK;
+
+ while (1) {
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ /* Right now we just support reading packets one at a time. */
+ r = pcap_dispatch(pcap_g.pcap_handle, packet_q_len,
+ (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
+ if (unlikely(r == -1)) {
+ SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
+ r, pcap_geterr(pcap_g.pcap_handle));
+ if (! RunModeUnixSocketIsActive()) {
+ /* in the error state we just kill the engine */
+ EngineKill();
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ pcap_close(pcap_g.pcap_handle);
+ pcap_g.pcap_handle = NULL;
+ UnixSocketPcapFile(TM_ECODE_DONE);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ } else if (unlikely(r == 0)) {
+ SCLogInfo("pcap file end of file reached (pcap err code %" PRId32 ")", r);
+ if (! RunModeUnixSocketIsActive()) {
+ EngineStop();
+ } else {
+ pcap_close(pcap_g.pcap_handle);
+ pcap_g.pcap_handle = NULL;
+ UnixSocketPcapFile(TM_ECODE_DONE);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ break;
+ } else if (ptv->cb_result == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_PCAP_DISPATCH, "Pcap callback PcapFileCallbackLoop failed");
+ if (! RunModeUnixSocketIsActive()) {
+ EngineKill();
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ pcap_close(pcap_g.pcap_handle);
+ pcap_g.pcap_handle = NULL;
+ UnixSocketPcapFile(TM_ECODE_DONE);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ }
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ char *tmpbpfstring = NULL;
+ char *tmpstring = NULL;
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "error: initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("reading pcap file %s", (char *)initdata);
+
+ PcapFileThreadVars *ptv = SCMalloc(sizeof(PcapFileThreadVars));
+ if (unlikely(ptv == NULL))
+ SCReturnInt(TM_ECODE_FAILED);
+ memset(ptv, 0, sizeof(PcapFileThreadVars));
+
+ intmax_t tenant = 0;
+ if (ConfGetInt("pcap-file.tenant-id", &tenant) == 1) {
+ if (tenant > 0 && tenant < UINT_MAX) {
+ ptv->tenant_id = (uint32_t)tenant;
+ SCLogInfo("tenant %u", ptv->tenant_id);
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant out of range");
+ }
+ }
+
+ char errbuf[PCAP_ERRBUF_SIZE] = "";
+ pcap_g.pcap_handle = pcap_open_offline((char *)initdata, errbuf);
+ if (pcap_g.pcap_handle == NULL) {
+ SCLogError(SC_ERR_FOPEN, "%s\n", errbuf);
+ SCFree(ptv);
+ if (! RunModeUnixSocketIsActive()) {
+ return TM_ECODE_FAILED;
+ } else {
+ UnixSocketPcapFile(TM_ECODE_FAILED);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ }
+
+ if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
+ SCLogDebug("could not get bpf or none specified");
+ } else {
+ SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
+
+ if(pcap_compile(pcap_g.pcap_handle,&pcap_g.filter,tmpbpfstring,1,0) < 0) {
+ SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(pcap_g.pcap_handle));
+ SCFree(ptv);
+ return TM_ECODE_FAILED;
+ }
+
+ if(pcap_setfilter(pcap_g.pcap_handle,&pcap_g.filter) < 0) {
+ SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(pcap_g.pcap_handle));
+ SCFree(ptv);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ pcap_g.datalink = pcap_datalink(pcap_g.pcap_handle);
+ SCLogDebug("datalink %" PRId32 "", pcap_g.datalink);
+
+ switch(pcap_g.datalink) {
+ case LINKTYPE_LINUX_SLL:
+ pcap_g.Decoder = DecodeSll;
+ break;
+ case LINKTYPE_ETHERNET:
+ pcap_g.Decoder = DecodeEthernet;
+ break;
+ case LINKTYPE_PPP:
+ pcap_g.Decoder = DecodePPP;
+ break;
+ case LINKTYPE_RAW:
+ pcap_g.Decoder = DecodeRaw;
+ break;
+ case LINKTYPE_NULL:
+ pcap_g.Decoder = DecodeNull;
+ break;
+
+ default:
+ SCLogError(SC_ERR_UNIMPLEMENTED, "datalink type %" PRId32 " not "
+ "(yet) supported in module PcapFile.\n", pcap_g.datalink);
+ SCFree(ptv);
+ if (! RunModeUnixSocketIsActive()) {
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ pcap_close(pcap_g.pcap_handle);
+ pcap_g.pcap_handle = NULL;
+ UnixSocketPcapFile(TM_ECODE_DONE);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ }
+
+ if (ConfGet("pcap-file.checksum-checks", &tmpstring) != 1) {
+ pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else {
+ if (strcmp(tmpstring, "auto") == 0) {
+ pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (strcmp(tmpstring, "yes") == 0) {
+ pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (strcmp(tmpstring, "no") == 0) {
+ pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ }
+ }
+ pcap_g.checksum_mode = pcap_g.conf_checksum_mode;
+
+ ptv->tv = tv;
+ *data = (void *)ptv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void ReceivePcapFileThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
+
+ if (pcap_g.conf_checksum_mode == CHECKSUM_VALIDATION_AUTO &&
+ pcap_g.cnt < CHECKSUM_SAMPLE_COUNT &&
+ SC_ATOMIC_GET(pcap_g.invalid_checksums)) {
+ uint64_t chrate = pcap_g.cnt / SC_ATOMIC_GET(pcap_g.invalid_checksums);
+ if (chrate < CHECKSUM_INVALID_RATIO)
+ SCLogWarning(SC_ERR_INVALID_CHECKSUM,
+ "1/%" PRIu64 "th of packets have an invalid checksum,"
+ " consider setting pcap-file.checksum-checks variable to no"
+ " or use '-k none' option on command line.",
+ chrate);
+ else
+ SCLogInfo("1/%" PRIu64 "th of packets have an invalid checksum",
+ chrate);
+ }
+ SCLogNotice("Pcap-file module read %" PRIu32 " packets, %" PRIu64 " bytes", ptv->pkts, ptv->bytes);
+ return;
+}
+
+TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
+ if (ptv) {
+ SCFree(ptv);
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+double prev_signaled_ts = 0;
+
+TmEcode DecodePcapFile(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ double curr_ts = p->ts.tv_sec + p->ts.tv_usec / 1000.0;
+ if (curr_ts < prev_signaled_ts || (curr_ts - prev_signaled_ts) > 60.0) {
+ prev_signaled_ts = curr_ts;
+ FlowWakeupFlowManagerThread();
+ }
+
+ /* update the engine time representation based on the timestamp
+ * of the packet. */
+ TimeSet(&p->ts);
+
+ /* call the decoder */
+ pcap_g.Decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+#ifdef DEBUG
+ BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR);
+#endif
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
+ SCReturnInt(TM_ECODE_FAILED);
+#endif
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void PcapIncreaseInvalidChecksum()
+{
+ (void) SC_ATOMIC_ADD(pcap_g.invalid_checksums, 1);
+}
+
+/* eof */
+
diff --git a/framework/src/suricata/src/source-pcap-file.h b/framework/src/suricata/src/source-pcap-file.h
new file mode 100644
index 00000000..fa76a1a8
--- /dev/null
+++ b/framework/src/suricata/src/source-pcap-file.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __SOURCE_PCAP_FILE_H__
+#define __SOURCE_PCAP_FILE_H__
+
+void TmModuleReceivePcapFileRegister (void);
+void TmModuleDecodePcapFileRegister (void);
+
+void PcapIncreaseInvalidChecksum();
+
+void PcapFileGlobalInit();
+
+#endif /* __SOURCE_PCAP_FILE_H__ */
+
diff --git a/framework/src/suricata/src/source-pcap.c b/framework/src/suricata/src/source-pcap.c
new file mode 100644
index 00000000..0656f958
--- /dev/null
+++ b/framework/src/suricata/src/source-pcap.c
@@ -0,0 +1,826 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Live pcap packet acquisition support
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "source-pcap.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-privs.h"
+#include "util-device.h"
+#include "util-optimize.h"
+#include "util-checksum.h"
+#include "util-ioctl.h"
+#include "tmqh-packetpool.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+#define PCAP_STATE_DOWN 0
+#define PCAP_STATE_UP 1
+
+#define PCAP_RECONNECT_TIMEOUT 500000
+
+/**
+ * \brief Structure to hold thread specific variables.
+ */
+typedef struct PcapThreadVars_
+{
+ /* thread specific handle */
+ pcap_t *pcap_handle;
+ /* handle state */
+ unsigned char pcap_state;
+ /* thread specific bpf */
+ struct bpf_program filter;
+ /* ptr to string from config */
+ char *bpf_filter;
+
+ time_t last_stats_dump;
+
+ /* data link type for the thread */
+ int datalink;
+
+ /* counters */
+ uint32_t pkts;
+ uint64_t bytes;
+ uint32_t errs;
+
+ uint16_t capture_kernel_packets;
+ uint16_t capture_kernel_drops;
+ uint16_t capture_kernel_ifdrops;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ /** callback result -- set if one of the thread module failed. */
+ int cb_result;
+
+ /* pcap buffer size */
+ int pcap_buffer_size;
+ int pcap_snaplen;
+
+ ChecksumValidationMode checksum_mode;
+
+#if LIBPCAP_VERSION_MAJOR == 0
+ char iface[PCAP_IFACE_NAME_LENGTH];
+#endif
+ LiveDevice *livedev;
+} PcapThreadVars;
+
+TmEcode ReceivePcapThreadInit(ThreadVars *, void *, void **);
+void ReceivePcapThreadExitStats(ThreadVars *, void *);
+TmEcode ReceivePcapThreadDeinit(ThreadVars *, void *);
+TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot);
+
+TmEcode DecodePcapThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodePcapThreadDeinit(ThreadVars *tv, void *data);
+TmEcode DecodePcap(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+/** protect pcap_compile and pcap_setfilter, as they are not thread safe:
+ * http://seclists.org/tcpdump/2009/q1/62 */
+static SCMutex pcap_bpf_compile_lock = SCMUTEX_INITIALIZER;
+
+/**
+ * \brief Registration Function for RecievePcap.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleReceivePcapRegister (void)
+{
+ tmm_modules[TMM_RECEIVEPCAP].name = "ReceivePcap";
+ tmm_modules[TMM_RECEIVEPCAP].ThreadInit = ReceivePcapThreadInit;
+ tmm_modules[TMM_RECEIVEPCAP].Func = NULL;
+ tmm_modules[TMM_RECEIVEPCAP].PktAcqLoop = ReceivePcapLoop;
+ tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitStats;
+ tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEPCAP].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEPCAP].cap_flags = SC_CAP_NET_RAW;
+ tmm_modules[TMM_RECEIVEPCAP].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration Function for DecodePcap.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodePcapRegister (void)
+{
+ tmm_modules[TMM_DECODEPCAP].name = "DecodePcap";
+ tmm_modules[TMM_DECODEPCAP].ThreadInit = DecodePcapThreadInit;
+ tmm_modules[TMM_DECODEPCAP].Func = DecodePcap;
+ tmm_modules[TMM_DECODEPCAP].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEPCAP].ThreadDeinit = DecodePcapThreadDeinit;
+ tmm_modules[TMM_DECODEPCAP].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEPCAP].cap_flags = 0;
+ tmm_modules[TMM_DECODEPCAP].flags = TM_FLAG_DECODE_TM;
+}
+
+static inline void PcapDumpCounters(PcapThreadVars *ptv)
+{
+ struct pcap_stat pcap_s;
+ if (likely((pcap_stats(ptv->pcap_handle, &pcap_s) >= 0))) {
+ StatsSetUI64(ptv->tv, ptv->capture_kernel_packets, pcap_s.ps_recv);
+ StatsSetUI64(ptv->tv, ptv->capture_kernel_drops, pcap_s.ps_drop);
+ (void) SC_ATOMIC_SET(ptv->livedev->drop, pcap_s.ps_drop);
+ StatsSetUI64(ptv->tv, ptv->capture_kernel_ifdrops, pcap_s.ps_ifdrop);
+ }
+}
+
+
+#if LIBPCAP_VERSION_MAJOR == 1
+static int PcapTryReopen(PcapThreadVars *ptv)
+{
+ int pcap_activate_r;
+
+ ptv->pcap_state = PCAP_STATE_DOWN;
+ pcap_activate_r = pcap_activate(ptv->pcap_handle);
+ if (pcap_activate_r != 0) {
+ return pcap_activate_r;
+ }
+ /* set bpf filter if we have one */
+ if (ptv->bpf_filter != NULL) {
+ if(pcap_compile(ptv->pcap_handle,&ptv->filter,ptv->bpf_filter,1,0) < 0) {
+ SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
+ return -1;
+ }
+
+ if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
+ SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
+ return -1;
+ }
+ }
+
+ SCLogInfo("Recovering interface listening");
+ ptv->pcap_state = PCAP_STATE_UP;
+ return 0;
+}
+#else /* implied LIBPCAP_VERSION_MAJOR == 0 */
+static int PcapTryReopen(PcapThreadVars *ptv)
+{
+ char errbuf[PCAP_ERRBUF_SIZE] = "";
+
+ ptv->pcap_state = PCAP_STATE_DOWN;
+ pcap_close(ptv->pcap_handle);
+
+ ptv->pcap_handle = pcap_open_live((char *)ptv->iface, ptv->pcap_snaplen,
+ LIBPCAP_PROMISC, LIBPCAP_COPYWAIT, errbuf);
+ if (ptv->pcap_handle == NULL) {
+ SCLogError(SC_ERR_PCAP_OPEN_LIVE, "Problem creating pcap handler for live mode, error %s", errbuf);
+ return -1;
+ }
+
+ /* set bpf filter if we have one */
+ if (ptv->bpf_filter != NULL) {
+ SCLogInfo("using bpf-filter \"%s\"", ptv->bpf_filter);
+
+ if(pcap_compile(ptv->pcap_handle,&ptv->filter,ptv->bpf_filter,1,0) < 0) {
+ SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
+ return -1;
+ }
+
+ if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
+ SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
+ return -1;
+ }
+ }
+
+ SCLogInfo("Recovering interface listening");
+ ptv->pcap_state = PCAP_STATE_UP;
+ return 0;
+}
+
+#endif
+
+void PcapCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
+{
+ SCEnter();
+
+ PcapThreadVars *ptv = (PcapThreadVars *)user;
+ Packet *p = PacketGetFromQueueOrAlloc();
+ struct timeval current_time;
+
+ if (unlikely(p == NULL)) {
+ SCReturn;
+ }
+
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+ p->ts.tv_sec = h->ts.tv_sec;
+ p->ts.tv_usec = h->ts.tv_usec;
+ SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
+ p->datalink = ptv->datalink;
+
+ ptv->pkts++;
+ ptv->bytes += h->caplen;
+ (void) SC_ATOMIC_ADD(ptv->livedev->pkts, 1);
+ p->livedev = ptv->livedev;
+
+ if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturn;
+ }
+
+ switch (ptv->checksum_mode) {
+ case CHECKSUM_VALIDATION_AUTO:
+ if (ptv->livedev->ignore_checksum) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ChecksumAutoModeCheck(ptv->pkts,
+ SC_ATOMIC_GET(ptv->livedev->pkts),
+ SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
+ ptv->livedev->ignore_checksum = 1;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ break;
+ case CHECKSUM_VALIDATION_DISABLE:
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ break;
+ default:
+ break;
+ }
+
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ pcap_breakloop(ptv->pcap_handle);
+ ptv->cb_result = TM_ECODE_FAILED;
+ }
+
+ /* Trigger one dump of stats every second */
+ TimeGet(&current_time);
+ if (current_time.tv_sec != ptv->last_stats_dump) {
+ PcapDumpCounters(ptv);
+ ptv->last_stats_dump = current_time.tv_sec;
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Main PCAP reading Loop function
+ */
+TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ int packet_q_len = 64;
+ PcapThreadVars *ptv = (PcapThreadVars *)data;
+ int r;
+ TmSlot *s = (TmSlot *)slot;
+
+ ptv->slot = s->slot_next;
+ ptv->cb_result = TM_ECODE_OK;
+
+ while (1) {
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ /* Right now we just support reading packets one at a time. */
+ r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
+ (pcap_handler)PcapCallbackLoop, (u_char *)ptv);
+ if (unlikely(r < 0)) {
+ int dbreak = 0;
+ SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
+ r, pcap_geterr(ptv->pcap_handle));
+#ifdef PCAP_ERROR_BREAK
+ if (r == PCAP_ERROR_BREAK) {
+ SCReturnInt(ptv->cb_result);
+ }
+#endif
+ do {
+ usleep(PCAP_RECONNECT_TIMEOUT);
+ if (suricata_ctl_flags != 0) {
+ dbreak = 1;
+ break;
+ }
+ r = PcapTryReopen(ptv);
+ } while (r < 0);
+ if (dbreak) {
+ break;
+ }
+ } else if (ptv->cb_result == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_PCAP_DISPATCH, "Pcap callback PcapCallbackLoop failed");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ PcapDumpCounters(ptv);
+ StatsSyncCountersIfSignalled(tv);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Init function for ReceivePcap.
+ *
+ * This is a setup function for recieving packets
+ * via libpcap. There are two versions of this function
+ * depending on the major version of libpcap used.
+ * For versions prior to 1.x we use open_pcap_live,
+ * for versions 1.x and greater we use pcap_create + pcap_activate.
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the interface passed from the user
+ * \param data pointer gets populated with PcapThreadVars
+ *
+ * \todo Create a general pcap setup function.
+ */
+#if LIBPCAP_VERSION_MAJOR == 1
+TmEcode ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ PcapIfaceConfig *pcapconfig = initdata;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ PcapThreadVars *ptv = SCMalloc(sizeof(PcapThreadVars));
+ if (unlikely(ptv == NULL)) {
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(ptv, 0, sizeof(PcapThreadVars));
+
+ ptv->tv = tv;
+
+ ptv->livedev = LiveGetDevice(pcapconfig->iface);
+ if (ptv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ SCFree(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("using interface %s", (char *)pcapconfig->iface);
+
+ ptv->checksum_mode = pcapconfig->checksum_mode;
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ SCLogInfo("Running in 'auto' checksum mode. Detection of interface state will require "
+ xstr(CHECKSUM_SAMPLE_COUNT) " packets.");
+ }
+
+ /* XXX create a general pcap setup function */
+ char errbuf[PCAP_ERRBUF_SIZE];
+ ptv->pcap_handle = pcap_create((char *)pcapconfig->iface, errbuf);
+ if (ptv->pcap_handle == NULL) {
+ if (strlen(errbuf)) {
+ SCLogError(SC_ERR_PCAP_CREATE, "Couldn't create a new pcap handler for %s, error %s",
+ (char *)pcapconfig->iface, errbuf);
+ } else {
+ SCLogError(SC_ERR_PCAP_CREATE, "Couldn't create a new pcap handler for %s",
+ (char *)pcapconfig->iface);
+ }
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (pcapconfig->snaplen == 0) {
+ /* We set snaplen if we can get the MTU */
+ ptv->pcap_snaplen = GetIfaceMaxPacketSize(pcapconfig->iface);
+ } else {
+ ptv->pcap_snaplen = pcapconfig->snaplen;
+ }
+ if (ptv->pcap_snaplen > 0) {
+ /* set Snaplen. Must be called before pcap_activate */
+ int pcap_set_snaplen_r = pcap_set_snaplen(ptv->pcap_handle, ptv->pcap_snaplen);
+ if (pcap_set_snaplen_r != 0) {
+ SCLogError(SC_ERR_PCAP_SET_SNAPLEN, "Couldn't set snaplen, error: %s", pcap_geterr(ptv->pcap_handle));
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ SCLogInfo("Set snaplen to %d for '%s'", ptv->pcap_snaplen,
+ pcapconfig->iface);
+ }
+
+ /* set Promisc, and Timeout. Must be called before pcap_activate */
+ int pcap_set_promisc_r = pcap_set_promisc(ptv->pcap_handle, pcapconfig->promisc);
+ //printf("ReceivePcapThreadInit: pcap_set_promisc(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_promisc_r);
+ if (pcap_set_promisc_r != 0) {
+ SCLogError(SC_ERR_PCAP_SET_PROMISC, "Couldn't set promisc mode, error %s", pcap_geterr(ptv->pcap_handle));
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ int pcap_set_timeout_r = pcap_set_timeout(ptv->pcap_handle,LIBPCAP_COPYWAIT);
+ //printf("ReceivePcapThreadInit: pcap_set_timeout(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_timeout_r);
+ if (pcap_set_timeout_r != 0) {
+ SCLogError(SC_ERR_PCAP_SET_TIMEOUT, "Problems setting timeout, error %s", pcap_geterr(ptv->pcap_handle));
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+#ifdef HAVE_PCAP_SET_BUFF
+ ptv->pcap_buffer_size = pcapconfig->buffer_size;
+ if (ptv->pcap_buffer_size >= 0 && ptv->pcap_buffer_size <= INT_MAX) {
+ if (ptv->pcap_buffer_size > 0)
+ SCLogInfo("Going to use pcap buffer size of %" PRId32 "", ptv->pcap_buffer_size);
+
+ int pcap_set_buffer_size_r = pcap_set_buffer_size(ptv->pcap_handle,ptv->pcap_buffer_size);
+ //printf("ReceivePcapThreadInit: pcap_set_timeout(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_buffer_size_r);
+ if (pcap_set_buffer_size_r != 0) {
+ SCLogError(SC_ERR_PCAP_SET_BUFF_SIZE, "Problems setting pcap buffer size, error %s", pcap_geterr(ptv->pcap_handle));
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+#endif /* HAVE_PCAP_SET_BUFF */
+
+ /* activate the handle */
+ int pcap_activate_r = pcap_activate(ptv->pcap_handle);
+ //printf("ReceivePcapThreadInit: pcap_activate(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_activate_r);
+ if (pcap_activate_r != 0) {
+ SCLogError(SC_ERR_PCAP_ACTIVATE_HANDLE, "Couldn't activate the pcap handler, error %s", pcap_geterr(ptv->pcap_handle));
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ ptv->pcap_state = PCAP_STATE_UP;
+ }
+
+ /* set bpf filter if we have one */
+ if (pcapconfig->bpf_filter) {
+ SCMutexLock(&pcap_bpf_compile_lock);
+
+ ptv->bpf_filter = pcapconfig->bpf_filter;
+
+ if (pcap_compile(ptv->pcap_handle,&ptv->filter,ptv->bpf_filter,1,0) < 0) {
+ SCLogError(SC_ERR_BPF, "bpf compilation error %s", pcap_geterr(ptv->pcap_handle));
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ return TM_ECODE_FAILED;
+ }
+
+ if (pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
+ SCLogError(SC_ERR_BPF, "could not set bpf filter %s", pcap_geterr(ptv->pcap_handle));
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ SCFree(ptv);
+ pcapconfig->DerefFunc(pcapconfig);
+ return TM_ECODE_FAILED;
+ }
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ }
+
+ /* Making it conditional to Linux even if GetIfaceOffloading return 0
+ * for non Linux. */
+#ifdef HAVE_LINUX_ETHTOOL_H
+ if (GetIfaceOffloading(pcapconfig->iface) == 1) {
+ SCLogWarning(SC_ERR_PCAP_CREATE,
+ "Using Pcap capture with GRO or LRO activated can lead to "
+ "capture problems.");
+ }
+#endif /* HAVE_LINUX_ETHTOOL_H */
+
+ ptv->datalink = pcap_datalink(ptv->pcap_handle);
+
+ pcapconfig->DerefFunc(pcapconfig);
+
+ ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ptv->tv);
+ ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ptv->tv);
+ ptv->capture_kernel_ifdrops = StatsRegisterCounter("capture.kernel_ifdrops",
+ ptv->tv);
+
+ *data = (void *)ptv;
+ SCReturnInt(TM_ECODE_OK);
+}
+#else /* implied LIBPCAP_VERSION_MAJOR == 0 */
+TmEcode ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ PcapIfaceConfig *pcapconfig = initdata;
+
+ if (initdata == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ PcapThreadVars *ptv = SCMalloc(sizeof(PcapThreadVars));
+ if (unlikely(ptv == NULL)) {
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(ptv, 0, sizeof(PcapThreadVars));
+
+ ptv->tv = tv;
+
+ ptv->livedev = LiveGetDevice(pcapconfig->iface);
+ if (ptv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogInfo("using interface %s", pcapconfig->iface);
+ if (strlen(pcapconfig->iface) > PCAP_IFACE_NAME_LENGTH) {
+ SCFree(ptv);
+ /* Dereference config */
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ strlcpy(ptv->iface, pcapconfig->iface, PCAP_IFACE_NAME_LENGTH);
+
+ if (pcapconfig->snaplen == 0) {
+ /* We try to set snaplen from MTU value */
+ ptv->pcap_snaplen = GetIfaceMaxPacketSize(pcapconfig->iface);
+ /* be conservative with old pcap lib to mimic old tcpdump behavior
+ when MTU was not available. */
+ if (ptv->pcap_snaplen <= 0)
+ ptv->pcap_snaplen = LIBPCAP_SNAPLEN;
+ } else {
+ ptv->pcap_snaplen = pcapconfig->snaplen;
+ }
+
+ char errbuf[PCAP_ERRBUF_SIZE] = "";
+ ptv->pcap_handle = pcap_open_live(ptv->iface, ptv->pcap_snaplen,
+ LIBPCAP_PROMISC, LIBPCAP_COPYWAIT, errbuf);
+ if (ptv->pcap_handle == NULL) {
+ SCLogError(SC_ERR_PCAP_OPEN_LIVE, "Problem creating pcap handler for live mode, error %s", errbuf);
+ SCFree(ptv);
+ /* Dereference config */
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* set bpf filter if we have one */
+ if (pcapconfig->bpf_filter) {
+ SCMutexLock(&pcap_bpf_compile_lock);
+
+ ptv->bpf_filter = pcapconfig->bpf_filter;
+ SCLogInfo("using bpf-filter \"%s\"", ptv->bpf_filter);
+
+ if(pcap_compile(ptv->pcap_handle,&ptv->filter, ptv->bpf_filter,1,0) < 0) {
+ SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ SCFree(ptv);
+ /* Dereference config */
+ pcapconfig->DerefFunc(pcapconfig);
+ return TM_ECODE_FAILED;
+ }
+
+ if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
+ SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ SCFree(ptv);
+ /* Dereference config */
+ pcapconfig->DerefFunc(pcapconfig);
+ return TM_ECODE_FAILED;
+ }
+
+ SCMutexUnlock(&pcap_bpf_compile_lock);
+ }
+
+ ptv->datalink = pcap_datalink(ptv->pcap_handle);
+
+ ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ptv->tv);
+ ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ptv->tv);
+ ptv->capture_kernel_ifdrops = StatsRegisterCounter("capture.kernel_ifdrops",
+ ptv->tv);
+
+ *data = (void *)ptv;
+
+ /* Dereference config */
+ pcapconfig->DerefFunc(pcapconfig);
+ SCReturnInt(TM_ECODE_OK);
+}
+#endif /* LIBPCAP_VERSION_MAJOR */
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ */
+void ReceivePcapThreadExitStats(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ PcapThreadVars *ptv = (PcapThreadVars *)data;
+ struct pcap_stat pcap_s;
+
+ if (pcap_stats(ptv->pcap_handle, &pcap_s) < 0) {
+ SCLogError(SC_ERR_STAT,"(%s) Failed to get pcap_stats: %s", tv->name, pcap_geterr(ptv->pcap_handle));
+ SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
+
+ return;
+ } else {
+ SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
+
+ /* these numbers are not entirely accurate as ps_recv contains packets that are still waiting to be processed at exit.
+ * ps_drop only contains packets dropped by the driver and not any packets dropped by the interface.
+ * Additionally see http://tracker.icir.org/bro/ticket/18
+ *
+ * Note: ps_recv includes dropped packets and should be considered total.
+ * Unless we start to look at ps_ifdrop which isn't supported everywhere.
+ */
+ SCLogInfo("(%s) Pcap Total:%" PRIu64 " Recv:%" PRIu64 " Drop:%" PRIu64 " (%02.1f%%).", tv->name,
+ (uint64_t)pcap_s.ps_recv, (uint64_t)pcap_s.ps_recv - (uint64_t)pcap_s.ps_drop, (uint64_t)pcap_s.ps_drop,
+ (((float)(uint64_t)pcap_s.ps_drop)/(float)(uint64_t)pcap_s.ps_recv)*100);
+
+ return;
+ }
+}
+
+/**
+ * \brief DeInit function closes pcap_handle at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ */
+TmEcode ReceivePcapThreadDeinit(ThreadVars *tv, void *data)
+{
+ PcapThreadVars *ptv = (PcapThreadVars *)data;
+
+ pcap_close(ptv->pcap_handle);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * DecodePcap reads packets from the PacketQueue and passes
+ * them off to the proper link type decoder.
+ *
+ * \param t pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into PcapThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ */
+TmEcode DecodePcap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* call the decoder */
+ switch(p->datalink) {
+ case LINKTYPE_LINUX_SLL:
+ DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_ETHERNET:
+ DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_PPP:
+ DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_RAW:
+ DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ case LINKTYPE_NULL:
+ DecodeNull(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ break;
+ default:
+ SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodePcap", p->datalink);
+ break;
+ }
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodePcapThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
+ SCReturnInt(TM_ECODE_FAILED);
+#endif
+
+ *data = (void *)dtv;
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodePcapThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void PcapTranslateIPToDevice(char *pcap_dev, size_t len)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_if_t *alldevsp = NULL;
+ pcap_if_t *devsp = NULL;
+
+ struct addrinfo aiHints;
+ struct addrinfo *aiList = NULL;
+ int retVal = 0;
+
+ memset(&aiHints, 0, sizeof(aiHints));
+ aiHints.ai_family = AF_UNSPEC;
+ aiHints.ai_flags = AI_NUMERICHOST;
+
+ /* try to translate IP */
+ if ((retVal = getaddrinfo(pcap_dev, NULL, &aiHints, &aiList)) != 0) {
+ return;
+ }
+
+ if (pcap_findalldevs(&alldevsp, errbuf)) {
+ freeaddrinfo(aiList);
+ return;
+ }
+
+ for (devsp = alldevsp; devsp ; devsp = devsp->next) {
+ pcap_addr_t *ip = NULL;
+
+ for (ip = devsp->addresses; ip ; ip = ip->next) {
+
+ if (aiList->ai_family != ip->addr->sa_family) {
+ continue;
+ }
+
+ if (ip->addr->sa_family == AF_INET) {
+ if (memcmp(&((struct sockaddr_in*)aiList->ai_addr)->sin_addr, &((struct sockaddr_in*)ip->addr)->sin_addr, sizeof(struct in_addr))) {
+ continue;
+ }
+ } else if (ip->addr->sa_family == AF_INET6) {
+ if (memcmp(&((struct sockaddr_in6*)aiList->ai_addr)->sin6_addr, &((struct sockaddr_in6*)ip->addr)->sin6_addr, sizeof(struct in6_addr))) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ freeaddrinfo(aiList);
+
+ memset(pcap_dev, 0, len);
+ strlcpy(pcap_dev, devsp->name, len);
+
+ pcap_freealldevs(alldevsp);
+ return;
+ }
+ }
+
+ freeaddrinfo(aiList);
+
+ pcap_freealldevs(alldevsp);
+}
+
+/* eof */
+
diff --git a/framework/src/suricata/src/source-pcap.h b/framework/src/suricata/src/source-pcap.h
new file mode 100644
index 00000000..ac6d331d
--- /dev/null
+++ b/framework/src/suricata/src/source-pcap.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __SOURCE_PCAP_H__
+#define __SOURCE_PCAP_H__
+
+void TmModuleReceivePcapRegister (void);
+void TmModuleDecodePcapRegister (void);
+void PcapTranslateIPToDevice(char *pcap_dev, size_t len);
+
+int PcapLiveRegisterDevice(char *);
+int PcapLiveGetDeviceCount(void);
+char *PcapLiveGetDevice(int);
+
+#define LIBPCAP_SNAPLEN 1518
+#define LIBPCAP_COPYWAIT 500
+#define LIBPCAP_PROMISC 1
+
+/* per packet Pcap vars */
+typedef struct PcapPacketVars_
+{
+ uint32_t tenant_id;
+} PcapPacketVars;
+
+/** needs to be able to contain Windows adapter id's, so
+ * must be quite long. */
+#define PCAP_IFACE_NAME_LENGTH 128
+
+typedef struct PcapIfaceConfig_
+{
+ char iface[PCAP_IFACE_NAME_LENGTH];
+ /* number of threads */
+ int threads;
+ /* socket buffer size */
+ int buffer_size;
+ /* snapshot length */
+ int snaplen;
+ /* promiscuous value */
+ int promisc;
+ /* BPF filter */
+ char *bpf_filter;
+ ChecksumValidationMode checksum_mode;
+ SC_ATOMIC_DECLARE(unsigned int, ref);
+ void (*DerefFunc)(void *);
+} PcapIfaceConfig;
+
+
+
+#endif /* __SOURCE_PCAP_H__ */
+
diff --git a/framework/src/suricata/src/source-pfring.c b/framework/src/suricata/src/source-pfring.c
new file mode 100644
index 00000000..3b2b52df
--- /dev/null
+++ b/framework/src/suricata/src/source-pfring.c
@@ -0,0 +1,660 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * PF_RING packet acquisition support
+ *
+ * \todo remove requirement for setting cluster so old 3.x versions are supported
+ * \todo implement DNA support
+ * \todo Allow ring options such as snaplen etc, to be user configurable.
+ */
+
+#ifdef HAVE_PFRING
+#include <pfring.h>
+#endif /* HAVE_PFRING */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "conf.h"
+#include "decode.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "source-pfring.h"
+#include "util-debug.h"
+#include "util-checksum.h"
+#include "util-privs.h"
+#include "util-device.h"
+#include "util-host-info.h"
+#include "runmodes.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+TmEcode ReceivePfringLoop(ThreadVars *tv, void *data, void *slot);
+TmEcode ReceivePfringThreadInit(ThreadVars *, void *, void **);
+void ReceivePfringThreadExitStats(ThreadVars *, void *);
+TmEcode ReceivePfringThreadDeinit(ThreadVars *, void *);
+
+TmEcode DecodePfringThreadInit(ThreadVars *, void *, void **);
+TmEcode DecodePfring(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode DecodePfringThreadDeinit(ThreadVars *tv, void *data);
+
+extern int max_pending_packets;
+
+#ifndef HAVE_PFRING
+
+/*Handle cases where we don't have PF_RING support built-in*/
+TmEcode NoPfringSupportExit(ThreadVars *, void *, void **);
+
+void TmModuleReceivePfringRegister (void)
+{
+ tmm_modules[TMM_RECEIVEPFRING].name = "ReceivePfring";
+ tmm_modules[TMM_RECEIVEPFRING].ThreadInit = NoPfringSupportExit;
+ tmm_modules[TMM_RECEIVEPFRING].Func = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].ThreadDeinit = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
+ SC_CAP_NET_BIND_SERVICE | SC_CAP_NET_BROADCAST;
+ tmm_modules[TMM_RECEIVEPFRING].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleDecodePfringRegister (void)
+{
+ tmm_modules[TMM_DECODEPFRING].name = "DecodePfring";
+ tmm_modules[TMM_DECODEPFRING].ThreadInit = NoPfringSupportExit;
+ tmm_modules[TMM_DECODEPFRING].Func = NULL;
+ tmm_modules[TMM_DECODEPFRING].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEPFRING].ThreadDeinit = NULL;
+ tmm_modules[TMM_DECODEPFRING].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEPFRING].cap_flags = 0;
+ tmm_modules[TMM_DECODEPFRING].flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief this funciton prints an error message and exits.
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the interface passed from the user
+ * \param data pointer gets populated with PfringThreadVars
+ */
+TmEcode NoPfringSupportExit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCLogError(SC_ERR_NO_PF_RING,"Error creating thread %s: you do not have support for pfring "
+ "enabled please recompile with --enable-pfring", tv->name);
+ exit(EXIT_FAILURE);
+}
+
+#else /* implied we do have PF_RING support */
+
+/** protect pfring_set_bpf_filter, as it is not thread safe */
+static SCMutex pfring_bpf_set_filter_lock = SCMUTEX_INITIALIZER;
+
+/* XXX replace with user configurable options */
+#define LIBPFRING_PROMISC 1
+#define LIBPFRING_REENTRANT 0
+#define LIBPFRING_WAIT_FOR_INCOMING 1
+
+/**
+ * \brief Structure to hold thread specific variables.
+ */
+typedef struct PfringThreadVars_
+{
+ /* thread specific handle */
+ pfring *pd;
+
+ /* counters */
+ uint64_t bytes;
+ uint64_t pkts;
+
+ uint16_t capture_kernel_packets;
+ uint16_t capture_kernel_drops;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ int vlan_disabled;
+
+ /* threads count */
+ int threads;
+
+ cluster_type ctype;
+
+ uint8_t cluster_id;
+ char *interface;
+ LiveDevice *livedev;
+
+ char *bpf_filter;
+
+ ChecksumValidationMode checksum_mode;
+} PfringThreadVars;
+
+/**
+ * \brief Registration Function for RecievePfring.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleReceivePfringRegister (void)
+{
+ tmm_modules[TMM_RECEIVEPFRING].name = "ReceivePfring";
+ tmm_modules[TMM_RECEIVEPFRING].ThreadInit = ReceivePfringThreadInit;
+ tmm_modules[TMM_RECEIVEPFRING].Func = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].PktAcqLoop = ReceivePfringLoop;
+ tmm_modules[TMM_RECEIVEPFRING].ThreadExitPrintStats = ReceivePfringThreadExitStats;
+ tmm_modules[TMM_RECEIVEPFRING].ThreadDeinit = ReceivePfringThreadDeinit;
+ tmm_modules[TMM_RECEIVEPFRING].RegisterTests = NULL;
+ tmm_modules[TMM_RECEIVEPFRING].flags = TM_FLAG_RECEIVE_TM;
+}
+
+/**
+ * \brief Registration Function for DecodePfring.
+ * \todo Unit tests are needed for this module.
+ */
+void TmModuleDecodePfringRegister (void)
+{
+ tmm_modules[TMM_DECODEPFRING].name = "DecodePfring";
+ tmm_modules[TMM_DECODEPFRING].ThreadInit = DecodePfringThreadInit;
+ tmm_modules[TMM_DECODEPFRING].Func = DecodePfring;
+ tmm_modules[TMM_DECODEPFRING].ThreadExitPrintStats = NULL;
+ tmm_modules[TMM_DECODEPFRING].ThreadDeinit = DecodePfringThreadDeinit;
+ tmm_modules[TMM_DECODEPFRING].RegisterTests = NULL;
+ tmm_modules[TMM_DECODEPFRING].flags = TM_FLAG_DECODE_TM;
+}
+
+static inline void PfringDumpCounters(PfringThreadVars *ptv)
+{
+ pfring_stat pfring_s;
+ if (likely((pfring_stats(ptv->pd, &pfring_s) >= 0))) {
+ /* pfring counter is per socket and is not cleared after read.
+ * So to get the number of packet on the interface we can add
+ * the newly seen packets and drops for this thread and add it
+ * to the interface counter */
+ uint64_t th_pkts = StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_packets);
+ uint64_t th_drops = StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_drops);
+ SC_ATOMIC_ADD(ptv->livedev->pkts, pfring_s.recv - th_pkts);
+ SC_ATOMIC_ADD(ptv->livedev->drop, pfring_s.drop - th_drops);
+ StatsSetUI64(ptv->tv, ptv->capture_kernel_packets, pfring_s.recv);
+ StatsSetUI64(ptv->tv, ptv->capture_kernel_drops, pfring_s.drop);
+ }
+}
+
+/**
+ * \brief Pfring Packet Process function.
+ *
+ * This function fills in our packet structure from libpfring.
+ * From here the packets are picked up by the DecodePfring thread.
+ *
+ * \param user pointer to PfringThreadVars
+ * \param h pointer to pfring packet header
+ * \param p pointer to the current packet
+ */
+static inline void PfringProcessPacket(void *user, struct pfring_pkthdr *h, Packet *p)
+{
+
+ PfringThreadVars *ptv = (PfringThreadVars *)user;
+
+ ptv->bytes += h->caplen;
+ ptv->pkts++;
+ p->livedev = ptv->livedev;
+
+ /* PF_RING may fail to set timestamp */
+ if (h->ts.tv_sec == 0) {
+ gettimeofday((struct timeval *)&h->ts, NULL);
+ }
+
+ p->ts.tv_sec = h->ts.tv_sec;
+ p->ts.tv_usec = h->ts.tv_usec;
+
+ /* PF_RING all packets are marked as a link type of ethernet
+ * so that is what we do here. */
+ p->datalink = LINKTYPE_ETHERNET;
+
+ /* get vlan id from header. Check on vlan_id not null even if comment in
+ * header announce NO_VLAN is used when there is no VLAN. But NO_VLAN
+ * is not defined nor used in PF_RING code. And vlan_id is set to 0
+ * in PF_RING kernel code when there is no VLAN. */
+ if ((!ptv->vlan_disabled) && h->extended_hdr.parsed_pkt.vlan_id) {
+ p->vlan_id[0] = h->extended_hdr.parsed_pkt.vlan_id;
+ p->vlan_idx = 1;
+ p->vlanh[0] = NULL;
+ }
+
+ switch (ptv->checksum_mode) {
+ case CHECKSUM_VALIDATION_RXONLY:
+ if (h->extended_hdr.rx_direction == 0) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ break;
+ case CHECKSUM_VALIDATION_DISABLE:
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ break;
+ case CHECKSUM_VALIDATION_AUTO:
+ if (ptv->livedev->ignore_checksum) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (ChecksumAutoModeCheck(ptv->pkts,
+ SC_ATOMIC_GET(ptv->livedev->pkts),
+ SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
+ ptv->livedev->ignore_checksum = 1;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ break;
+ default:
+ break;
+ }
+
+ SET_PKT_LEN(p, h->caplen);
+}
+
+/**
+ * \brief Recieves packets from an interface via libpfring.
+ *
+ * This function recieves packets from an interface and passes
+ * the packet on to the pfring callback function.
+ *
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PfringThreadVars for ptv
+ * \param slot slot containing task information
+ * \retval TM_ECODE_OK on success
+ * \retval TM_ECODE_FAILED on failure
+ */
+TmEcode ReceivePfringLoop(ThreadVars *tv, void *data, void *slot)
+{
+ SCEnter();
+
+ PfringThreadVars *ptv = (PfringThreadVars *)data;
+ Packet *p = NULL;
+ struct pfring_pkthdr hdr;
+ TmSlot *s = (TmSlot *)slot;
+ time_t last_dump = 0;
+ struct timeval current_time;
+
+ ptv->slot = s->slot_next;
+
+ /* we have to enable the ring here as we need to do it after all
+ * the threads have called pfring_set_cluster(). */
+ int rc = pfring_enable_ring(ptv->pd);
+ if (rc != 0) {
+ SCLogError(SC_ERR_PF_RING_OPEN, "pfring_enable_ring failed returned %d ", rc);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ while(1) {
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+ /* Some flavours of PF_RING may fail to set timestamp - see PF-RING-enabled libpcap code*/
+ hdr.ts.tv_sec = hdr.ts.tv_usec = 0;
+
+ /* Depending on what compile time options are used for pfring we either return 0 or -1 on error and always 1 for success */
+ u_char *pkt_buffer = GET_PKT_DIRECT_DATA(p);
+ u_int buffer_size = GET_PKT_DIRECT_MAX_SIZE(p);
+ int r = pfring_recv(ptv->pd, &pkt_buffer,
+ buffer_size,
+ &hdr,
+ LIBPFRING_WAIT_FOR_INCOMING);
+
+ /* Check for Zero-copy if buffer size is zero */
+ if (buffer_size == 0) {
+ PacketSetData(p, pkt_buffer, hdr.caplen);
+ }
+
+ if (r == 1) {
+ //printf("RecievePfring src %" PRIu32 " sport %" PRIu32 " dst %" PRIu32 " dstport %" PRIu32 "\n",
+ // hdr.parsed_pkt.ipv4_src,hdr.parsed_pkt.l4_src_port, hdr.parsed_pkt.ipv4_dst,hdr.parsed_pkt.l4_dst_port);
+
+ PfringProcessPacket(ptv, &hdr, p);
+
+ if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ /* Trigger one dump of stats every second */
+ TimeGet(&current_time);
+ if (current_time.tv_sec != last_dump) {
+ PfringDumpCounters(ptv);
+ last_dump = current_time.tv_sec;
+ }
+ } else {
+ SCLogError(SC_ERR_PF_RING_RECV,"pfring_recv error %" PRId32 "", r);
+ TmqhOutputPacketpool(ptv->tv, p);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ StatsSyncCountersIfSignalled(tv);
+ }
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Init function for RecievePfring.
+ *
+ * This is a setup function for recieving packets
+ * via libpfring.
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to the interface passed from the user
+ * \param data pointer gets populated with PfringThreadVars
+ * \todo add a config option for setting cluster id
+ * \todo Create a general pfring setup function.
+ * \retval TM_ECODE_OK on success
+ * \retval TM_ECODE_FAILED on error
+ */
+TmEcode ReceivePfringThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ int rc;
+ u_int32_t version = 0;
+ PfringIfaceConfig *pfconf = (PfringIfaceConfig *) initdata;
+ unsigned int opflag;
+
+
+ if (pfconf == NULL)
+ return TM_ECODE_FAILED;
+
+ PfringThreadVars *ptv = SCMalloc(sizeof(PfringThreadVars));
+ if (unlikely(ptv == NULL)) {
+ pfconf->DerefFunc(pfconf);
+ return TM_ECODE_FAILED;
+ }
+ memset(ptv, 0, sizeof(PfringThreadVars));
+
+ ptv->tv = tv;
+ ptv->threads = 1;
+
+ ptv->interface = SCStrdup(pfconf->iface);
+ if (unlikely(ptv->interface == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate device string");
+ SCFree(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ptv->livedev = LiveGetDevice(pfconf->iface);
+ if (ptv->livedev == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
+ SCFree(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ ptv->checksum_mode = pfconf->checksum_mode;
+
+ opflag = PF_RING_REENTRANT | PF_RING_PROMISC;
+
+ /* if suri uses VLAN and if we have a recent kernel, we need
+ * to use parsed_pkt to get VLAN info */
+ if ((! ptv->vlan_disabled) && SCKernelVersionIsAtLeast(3, 0)) {
+ opflag |= PF_RING_LONG_HEADER;
+ }
+
+ if (ptv->checksum_mode == CHECKSUM_VALIDATION_RXONLY) {
+ if (strncmp(ptv->interface, "dna", 3) == 0) {
+ SCLogWarning(SC_ERR_INVALID_VALUE,
+ "Can't use rxonly checksum-checks on DNA interface,"
+ " resetting to auto");
+ ptv->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else {
+ opflag |= PF_RING_LONG_HEADER;
+ }
+ }
+
+ ptv->pd = pfring_open(ptv->interface, (uint32_t)default_packet_size, opflag);
+ if (ptv->pd == NULL) {
+ SCLogError(SC_ERR_PF_RING_OPEN,"Failed to open %s: pfring_open error."
+ " Check if %s exists and pf_ring module is loaded.",
+ ptv->interface,
+ ptv->interface);
+ pfconf->DerefFunc(pfconf);
+ SCFree(ptv);
+ return TM_ECODE_FAILED;
+ } else {
+ pfring_set_application_name(ptv->pd, PROG_NAME);
+ pfring_version(ptv->pd, &version);
+ }
+
+ /* We only set cluster info if the number of pfring threads is greater than 1 */
+ ptv->threads = pfconf->threads;
+
+ ptv->cluster_id = pfconf->cluster_id;
+
+ if ((ptv->threads == 1) && (strncmp(ptv->interface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not adding thread to cluster");
+ } else if (strncmp(ptv->interface, "zc", 2) == 0) {
+ SCLogInfo("ZC interface detected, not adding thread to cluster");
+ } else {
+ ptv->ctype = pfconf->ctype;
+ rc = pfring_set_cluster(ptv->pd, ptv->cluster_id, ptv->ctype);
+
+ if (rc != 0) {
+ SCLogError(SC_ERR_PF_RING_SET_CLUSTER_FAILED, "pfring_set_cluster "
+ "returned %d for cluster-id: %d", rc, ptv->cluster_id);
+ pfconf->DerefFunc(pfconf);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ if (ptv->threads > 1) {
+ SCLogInfo("(%s) Using PF_RING v.%d.%d.%d, interface %s, cluster-id %d",
+ tv->name, (version & 0xFFFF0000) >> 16, (version & 0x0000FF00) >> 8,
+ version & 0x000000FF, ptv->interface, ptv->cluster_id);
+ } else {
+ SCLogInfo("(%s) Using PF_RING v.%d.%d.%d, interface %s, cluster-id %d, single-pfring-thread",
+ tv->name, (version & 0xFFFF0000) >> 16, (version & 0x0000FF00) >> 8,
+ version & 0x000000FF, ptv->interface, ptv->cluster_id);
+ }
+
+ if (pfconf->bpf_filter) {
+ ptv->bpf_filter = SCStrdup(pfconf->bpf_filter);
+ if (unlikely(ptv->bpf_filter == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Set PF_RING bpf filter failed.");
+ } else {
+ SCMutexLock(&pfring_bpf_set_filter_lock);
+ rc = pfring_set_bpf_filter(ptv->pd, ptv->bpf_filter);
+ SCMutexUnlock(&pfring_bpf_set_filter_lock);
+
+ if (rc < 0) {
+ SCLogInfo("Set PF_RING bpf filter \"%s\" failed.",
+ ptv->bpf_filter);
+ }
+ }
+ }
+
+ ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
+ ptv->tv);
+ ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
+ ptv->tv);
+
+ /* A bit strange to have this here but we only have vlan information
+ * during reading so we need to know if we want to keep vlan during
+ * the capture phase */
+ int vlanbool = 0;
+ if ((ConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
+ ptv->vlan_disabled = 1;
+ }
+
+ /* If kernel is older than 3.8, VLAN is not stripped so we don't
+ * get the info from packt extended header but we will use a standard
+ * parsing */
+ if (! SCKernelVersionIsAtLeast(3, 0)) {
+ ptv->vlan_disabled = 1;
+ }
+
+ /* If VLAN tracking is disabled, set cluster type to 5-tuple or in case of a
+ * ZC interface, do nothing */
+ if (ptv->vlan_disabled && ptv->ctype == CLUSTER_FLOW &&
+ strncmp(ptv->interface, "zc", 2) != 0) {
+ SCLogInfo("VLAN disabled, setting cluster type to CLUSTER_FLOW_5_TUPLE");
+ rc = pfring_set_cluster(ptv->pd, ptv->cluster_id, CLUSTER_FLOW_5_TUPLE);
+
+ if (rc != 0) {
+ SCLogError(SC_ERR_PF_RING_SET_CLUSTER_FAILED, "pfring_set_cluster "
+ "returned %d for cluster-id: %d", rc, ptv->cluster_id);
+ pfconf->DerefFunc(pfconf);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ *data = (void *)ptv;
+ pfconf->DerefFunc(pfconf);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief This function prints stats to the screen at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PfringThreadVars for ptv
+ */
+void ReceivePfringThreadExitStats(ThreadVars *tv, void *data)
+{
+ PfringThreadVars *ptv = (PfringThreadVars *)data;
+
+ PfringDumpCounters(ptv);
+ SCLogInfo("(%s) Kernel: Packets %" PRIu64 ", dropped %" PRIu64 "",
+ tv->name,
+ StatsGetLocalCounterValue(tv, ptv->capture_kernel_packets),
+ StatsGetLocalCounterValue(tv, ptv->capture_kernel_drops));
+ SCLogInfo("(%s) Packets %" PRIu64 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
+}
+
+/**
+ * \brief DeInit function closes pd at exit.
+ * \param tv pointer to ThreadVars
+ * \param data pointer that gets cast into PfringThreadVars for ptvi
+ * \retval TM_ECODE_OK is always returned
+ */
+TmEcode ReceivePfringThreadDeinit(ThreadVars *tv, void *data)
+{
+ PfringThreadVars *ptv = (PfringThreadVars *)data;
+ if (ptv->interface)
+ SCFree(ptv->interface);
+ pfring_remove_from_cluster(ptv->pd);
+
+ if (ptv->bpf_filter) {
+ pfring_remove_bpf_filter(ptv->pd);
+ SCFree(ptv->bpf_filter);
+ }
+
+ pfring_close(ptv->pd);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief This function passes off to link type decoders.
+ *
+ * DecodePfring reads packets from the PacketQueue. Inside of libpcap version of
+ * PF_RING all packets are marked as a link type of ethernet so that is what we do here.
+ *
+ * \param tv pointer to ThreadVars
+ * \param p pointer to the current packet
+ * \param data pointer that gets cast into PfringThreadVars for ptv
+ * \param pq pointer to the current PacketQueue
+ *
+ * \todo Verify that PF_RING only deals with ethernet traffic
+ *
+ * \warning This function bypasses the pkt buf and len macro's
+ *
+ * \retval TM_ECODE_OK is always returned
+ */
+TmEcode DecodePfring(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ DecodeThreadVars *dtv = (DecodeThreadVars *)data;
+
+ /* XXX HACK: flow timeout can call us for injected pseudo packets
+ * see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return TM_ECODE_OK;
+
+ /* update counters */
+ DecodeUpdatePacketCounters(tv, dtv, p);
+
+ /* If suri has set vlan during reading, we increase vlan counter */
+ if (p->vlan_idx) {
+ StatsIncr(tv, dtv->counter_vlan);
+ }
+
+ DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+
+ PacketDecodeFinalize(tv, dtv, p);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief This an Init function for DecodePfring
+ *
+ * \param tv pointer to ThreadVars
+ * \param initdata pointer to initilization data.
+ * \param data pointer that gets cast into PfringThreadVars for ptv
+ * \retval TM_ECODE_OK is returned on success
+ * \retval TM_ECODE_FAILED is returned on error
+ */
+TmEcode DecodePfringThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ DecodeThreadVars *dtv = NULL;
+
+ dtv = DecodeThreadVarsAlloc(tv);
+
+ if (dtv == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ DecodeRegisterPerfCounters(dtv, tv);
+
+ *data = (void *)dtv;
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
+ SCReturnInt(TM_ECODE_FAILED);
+#endif
+
+ return TM_ECODE_OK;
+}
+
+TmEcode DecodePfringThreadDeinit(ThreadVars *tv, void *data)
+{
+ if (data != NULL)
+ DecodeThreadVarsFree(tv, data);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+
+#endif /* HAVE_PFRING */
+/* eof */
diff --git a/framework/src/suricata/src/source-pfring.h b/framework/src/suricata/src/source-pfring.h
new file mode 100644
index 00000000..e0878454
--- /dev/null
+++ b/framework/src/suricata/src/source-pfring.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author William Metcalf <william.metcalf@gmail.com>
+ */
+
+#ifndef __SOURCE_PFRING_H__
+#define __SOURCE_PFRING_H__
+
+#define PFRING_IFACE_NAME_LENGTH 48
+
+#include <config.h>
+#ifdef HAVE_PFRING
+#include <pfring.h>
+#endif
+
+typedef struct PfringIfaceConfig_
+{
+ /* cluster param */
+ int cluster_id;
+#ifdef HAVE_PFRING
+ cluster_type ctype;
+#endif
+ char iface[PFRING_IFACE_NAME_LENGTH];
+ /* number of threads */
+ int threads;
+
+ char *bpf_filter;
+
+ ChecksumValidationMode checksum_mode;
+ SC_ATOMIC_DECLARE(unsigned int, ref);
+ void (*DerefFunc)(void *);
+} PfringIfaceConfig;
+
+
+
+void TmModuleReceivePfringRegister (void);
+void TmModuleDecodePfringRegister (void);
+
+int PfringConfGetThreads(void);
+void PfringLoadConfig(void);
+
+/* We don't have to use an enum that sucks in our code */
+#define CLUSTER_FLOW 0
+#define CLUSTER_ROUND_ROBIN 1
+#define CLUSTER_FLOW_5_TUPLE 4
+#endif /* __SOURCE_PFRING_H__ */
diff --git a/framework/src/suricata/src/stream-tcp-inline.c b/framework/src/suricata/src/stream-tcp-inline.c
new file mode 100644
index 00000000..b8961ba2
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-inline.c
@@ -0,0 +1,657 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Functions for the "inline mode" of the stream engine.
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp-inline.h"
+
+#include "util-memcmp.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+/** defined in stream-tcp-reassemble.c */
+extern int stream_inline;
+
+/**
+ * \brief See if stream engine is operating in inline mode
+ *
+ * \retval 0 no
+ * \retval 1 yes
+ */
+int StreamTcpInlineMode(void)
+{
+ return stream_inline;
+}
+
+/**
+ * \brief Compare the shared data portion of two segments
+ *
+ * If no data is shared, 0 will be returned.
+ *
+ * \param seg1 first segment
+ * \param seg2 second segment
+ *
+ * \retval 0 shared data is the same (or no data is shared)
+ * \retval 1 shared data is different
+ */
+int StreamTcpInlineSegmentCompare(TcpSegment *seg1, TcpSegment *seg2)
+{
+ SCEnter();
+
+ if (seg1 == NULL || seg2 == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (SEQ_EQ(seg1->seq, seg2->seq) && seg1->payload_len == seg2->payload_len) {
+ int r = SCMemcmp(seg1->payload, seg2->payload, seg1->payload_len);
+#if 0
+ if (r) {
+ PrintRawDataFp(stdout,seg1->payload,seg1->payload_len);
+ PrintRawDataFp(stdout,seg2->payload,seg2->payload_len);
+ }
+#endif
+ SCReturnInt(r);
+ } else if (SEQ_GT(seg1->seq, (seg2->seq + seg2->payload_len))) {
+ SCReturnInt(0);
+ } else if (SEQ_GT(seg2->seq, (seg1->seq + seg1->payload_len))) {
+ SCReturnInt(0);
+ } else {
+ SCLogDebug("seg1 %u (%u), seg2 %u (%u)", seg1->seq,
+ seg1->payload_len, seg2->seq, seg2->payload_len);
+
+ uint32_t seg1_end = seg1->seq + seg1->payload_len;
+ uint32_t seg2_end = seg2->seq + seg2->payload_len;
+ SCLogDebug("seg1_end %u, seg2_end %u", seg1_end, seg2_end);
+#if 0
+ SCLogDebug("seg1");
+ PrintRawDataFp(stdout,seg1->payload,seg1->payload_len);
+ SCLogDebug("seg2");
+ PrintRawDataFp(stdout,seg2->payload,seg2->payload_len);
+#endif
+ /* get the minimal seg*_end */
+ uint32_t end = (SEQ_GT(seg1_end, seg2_end)) ? seg2_end : seg1_end;
+ /* and the max seq */
+ uint32_t seq = (SEQ_LT(seg1->seq, seg2->seq)) ? seg2->seq : seg1->seq;
+
+ SCLogDebug("seq %u, end %u", seq, end);
+
+ uint16_t seg1_off = seq - seg1->seq;
+ uint16_t seg2_off = seq - seg2->seq;
+ SCLogDebug("seg1_off %u, seg2_off %u", seg1_off, seg2_off);
+
+ uint32_t range = end - seq;
+ SCLogDebug("range %u", range);
+ BUG_ON(range > 65536);
+
+ if (range) {
+ int r = SCMemcmp(seg1->payload+seg1_off, seg2->payload+seg2_off, range);
+#if 0
+ if (r) {
+ PrintRawDataFp(stdout,seg1->payload+seg1_off,range);
+ PrintRawDataFp(stdout,seg2->payload+seg2_off,range);
+
+ PrintRawDataFp(stdout,seg1->payload,seg1->payload_len);
+ PrintRawDataFp(stdout,seg2->payload,seg2->payload_len);
+ }
+#endif
+ SCReturnInt(r);
+ }
+ SCReturnInt(0);
+ }
+}
+
+/**
+ * \brief Replace (part of) the payload portion of a packet by the data
+ * in a TCP segment
+ *
+ * \param p Packet
+ * \param seg TCP segment
+ *
+ * \todo What about reassembled fragments?
+ * \todo What about unwrapped tunnel packets?
+ */
+void StreamTcpInlineSegmentReplacePacket(Packet *p, TcpSegment *seg)
+{
+ SCEnter();
+
+ uint32_t pseq = TCP_GET_SEQ(p);
+ uint32_t tseq = seg->seq;
+
+ /* check if segment is within the packet */
+ if (tseq + seg->payload_len < pseq) {
+ SCReturn;
+ } else if (pseq + p->payload_len < tseq) {
+ SCReturn;
+ } else {
+ /** \todo review logic */
+ uint32_t pend = pseq + p->payload_len;
+ uint32_t tend = tseq + seg->payload_len;
+ SCLogDebug("pend %u, tend %u", pend, tend);
+
+ //SCLogDebug("packet");
+ //PrintRawDataFp(stdout,p->payload,p->payload_len);
+ //SCLogDebug("seg");
+ //PrintRawDataFp(stdout,seg->payload,seg->payload_len);
+
+ /* get the minimal seg*_end */
+ uint32_t end = (SEQ_GT(pend, tend)) ? tend : pend;
+ /* and the max seq */
+ uint32_t seq = (SEQ_LT(pseq, tseq)) ? tseq : pseq;
+
+ SCLogDebug("seq %u, end %u", seq, end);
+
+ uint16_t poff = seq - pseq;
+ uint16_t toff = seq - tseq;
+ SCLogDebug("poff %u, toff %u", poff, toff);
+
+ uint32_t range = end - seq;
+ SCLogDebug("range %u", range);
+ BUG_ON(range > 65536);
+
+ if (range) {
+ /* update the packets payload. As payload is a ptr to either
+ * p->pkt or p->ext_pkt that is updated as well */
+ memcpy(p->payload+poff, seg->payload+toff, range);
+
+ /* flag as modified so we can reinject / replace after
+ * recalculating the checksum */
+ p->flags |= PKT_STREAM_MODIFIED;
+ }
+ }
+}
+
+#ifdef UNITTESTS
+
+/** \test full overlap */
+static int StreamTcpInlineTest01(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "AAC"; /* packet */
+ uint8_t payload2[] = "ABC"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000000UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000000UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (!(p->flags & PKT_STREAM_MODIFIED)) {
+ printf("PKT_STREAM_MODIFIED pkt flag not set: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload, t->payload, p->payload_len) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,t->payload,t->payload_len);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt,payload2,sizeof(payload2)-1) != 0) {
+ PrintRawDataFp(stdout,pkt,3);
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+
+/** \test full overlap */
+static int StreamTcpInlineTest02(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000001UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000000UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (!(p->flags & PKT_STREAM_MODIFIED)) {
+ printf("PKT_STREAM_MODIFIED pkt flag not set: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload, t->payload+1, p->payload_len) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,t->payload,t->payload_len);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt,payload2+1,sizeof(payload2)-3) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2+1,sizeof(payload2)-3);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+
+/** \test partial overlap */
+static int StreamTcpInlineTest03(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxxxxxxxxxxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000000UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000003UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (!(p->flags & PKT_STREAM_MODIFIED)) {
+ printf("PKT_STREAM_MODIFIED pkt flag not set: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload+3, t->payload, t->payload_len) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,t->payload,t->payload_len);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1 + 3);
+ if (memcmp(pkt,payload2,sizeof(payload2)-1) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2+1,sizeof(payload2)-3);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+
+/** \test partial overlap */
+static int StreamTcpInlineTest04(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxxxxxxxxxxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000003UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000000UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (!(p->flags & PKT_STREAM_MODIFIED)) {
+ printf("PKT_STREAM_MODIFIED pkt flag not set: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload, t->payload+3, 2) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,t->payload,t->payload_len);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt,payload2+3,2) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2+3,2);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+/** \test partial overlap */
+static int StreamTcpInlineTest05(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxxxxxxxxxxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000000UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000010UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (!(p->flags & PKT_STREAM_MODIFIED)) {
+ printf("PKT_STREAM_MODIFIED pkt flag not set: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload+10, t->payload, 2) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,t->payload,t->payload_len);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt+10,payload2,2) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2,2);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+
+/** \test no overlap */
+static int StreamTcpInlineTest06(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxxxxxxxxxxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000020UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000000UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ printf("PKT_STREAM_MODIFIED pkt flag set, but it shouldn't: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload, payload1, sizeof(payload1)-1) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Original payload:\n");
+ PrintRawDataFp(stdout,payload1,sizeof(payload1)-1);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt,payload1,sizeof(payload1)-1) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2,2);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+
+/** \test no overlap */
+static int StreamTcpInlineTest07(void)
+{
+ SCEnter();
+
+ uint8_t payload1[] = "xxxxxxxxxxxx"; /* packet */
+ uint8_t payload2[] = "ABCDE"; /* segment */
+ int result = 0;
+ TcpSegment *t = NULL;
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload1, sizeof(payload1)-1, IPPROTO_TCP, 1024, 80);
+ if (p == NULL || p->tcph == NULL) {
+ printf("generating test packet failed: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(10000000UL);
+
+ t = SCMalloc(sizeof(TcpSegment));
+ if (unlikely(t == NULL)) {
+ printf("alloc TcpSegment failed: ");
+ goto end;
+ }
+ memset(t, 0x00, sizeof(TcpSegment));
+ t->payload = payload2;
+ t->payload_len = sizeof(payload2)-1;
+ t->seq = 10000020UL;
+
+ StreamTcpInlineSegmentReplacePacket(p, t);
+
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ printf("PKT_STREAM_MODIFIED pkt flag set, but it shouldn't: ");
+ goto end;
+ }
+
+ if (memcmp(p->payload, payload1, sizeof(payload1)-1) != 0) {
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,p->payload,p->payload_len);
+ printf("Original payload:\n");
+ PrintRawDataFp(stdout,payload1,sizeof(payload1)-1);
+ printf("payloads didn't match: ");
+ goto end;
+ }
+
+ uint8_t *pkt = GET_PKT_DATA(p)+(GET_PKT_LEN(p)-sizeof(payload1)+1);
+ if (memcmp(pkt,payload1,sizeof(payload1)-1) != 0) {
+ printf("Segment:\n");
+ PrintRawDataFp(stdout,payload2,2);
+ printf("Packet:\n");
+ PrintRawDataFp(stdout,pkt,3);
+ printf("Packet (full):\n");
+ PrintRawDataFp(stdout,GET_PKT_DATA(p),GET_PKT_LEN(p));
+ printf("packet data doesn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL) {
+ UTHFreePacket(p);
+ }
+ if (t != NULL) {
+ SCFree(t);
+ }
+ SCReturnInt(result);
+}
+#endif /* UNITTESTS */
+
+void StreamTcpInlineRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpInlineTest01", StreamTcpInlineTest01, 1);
+ UtRegisterTest("StreamTcpInlineTest02", StreamTcpInlineTest02, 1);
+ UtRegisterTest("StreamTcpInlineTest03", StreamTcpInlineTest03, 1);
+ UtRegisterTest("StreamTcpInlineTest04", StreamTcpInlineTest04, 1);
+ UtRegisterTest("StreamTcpInlineTest05", StreamTcpInlineTest05, 1);
+ UtRegisterTest("StreamTcpInlineTest06", StreamTcpInlineTest06, 1);
+ UtRegisterTest("StreamTcpInlineTest07", StreamTcpInlineTest07, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/stream-tcp-inline.h b/framework/src/suricata/src/stream-tcp-inline.h
new file mode 100644
index 00000000..49d49781
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-inline.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __STREAM_TCP_INLINE_H__
+#define __STREAM_TCP_INLINE_H__
+
+#include "stream-tcp-private.h"
+
+int StreamTcpInlineMode(void);
+int StreamTcpInlineSegmentCompare(TcpSegment *, TcpSegment *);
+void StreamTcpInlineSegmentReplacePacket(Packet *, TcpSegment *);
+
+void StreamTcpInlineRegisterTests(void);
+
+#endif /* __STREAM_TCP_INLINE_H__ */
+
diff --git a/framework/src/suricata/src/stream-tcp-private.h b/framework/src/suricata/src/stream-tcp-private.h
new file mode 100644
index 00000000..5c03c4bc
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-private.h
@@ -0,0 +1,246 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __STREAM_TCP_PRIVATE_H__
+#define __STREAM_TCP_PRIVATE_H__
+
+#include "decode.h"
+#include "util-pool.h"
+#include "util-pool-thread.h"
+
+#define STREAMTCP_QUEUE_FLAG_TS 0x01
+#define STREAMTCP_QUEUE_FLAG_WS 0x02
+#define STREAMTCP_QUEUE_FLAG_SACK 0x04
+
+/** currently only SYN/ACK */
+typedef struct TcpStateQueue_ {
+ uint8_t flags;
+ uint8_t wscale;
+ uint16_t win;
+ uint32_t seq;
+ uint32_t ack;
+ uint32_t ts;
+ uint32_t pkt_ts;
+ struct TcpStateQueue_ *next;
+} TcpStateQueue;
+
+typedef struct StreamTcpSackRecord_ {
+ uint32_t le; /**< left edge, host order */
+ uint32_t re; /**< right edge, host order */
+ struct StreamTcpSackRecord_ *next;
+} StreamTcpSackRecord;
+
+typedef struct TcpSegment_ {
+ uint8_t *payload;
+ uint16_t payload_len; /**< actual size of the payload */
+ uint16_t pool_size; /**< size of the memory */
+ uint32_t seq;
+ struct TcpSegment_ *next;
+ struct TcpSegment_ *prev;
+ /* coccinelle: TcpSegment:flags:SEGMENTTCP_FLAG */
+ uint8_t flags;
+} TcpSegment;
+
+typedef struct TcpStream_ {
+ uint16_t flags:12; /**< Flag specific to the stream e.g. Timestamp */
+ /* coccinelle: TcpStream:flags:STREAMTCP_STREAM_FLAG_ */
+ uint16_t wscale:4; /**< wscale setting in this direction, 4 bits as max val is 15 */
+ uint8_t os_policy; /**< target based OS policy used for reassembly and handling packets*/
+ uint8_t tcp_flags; /**< TCP flags seen */
+
+ uint32_t isn; /**< initial sequence number */
+ uint32_t next_seq; /**< next expected sequence number */
+ uint32_t last_ack; /**< last ack'd sequence number in this stream */
+ uint32_t next_win; /**< next max seq within window */
+ uint32_t window; /**< current window setting, after wscale is applied */
+
+ uint32_t last_ts; /**< Time stamp (TSVAL) of the last seen packet for this stream*/
+ uint32_t last_pkt_ts; /**< Time of last seen packet for this stream (needed for PAWS update)
+ This will be used to validate the last_ts, when connection has been idle for
+ longer time.(RFC 1323)*/
+ /* reassembly */
+ uint32_t ra_app_base_seq; /**< reassembled seq. We've reassembled up to this point. */
+ uint32_t ra_raw_base_seq; /**< reassembled seq. We've reassembled up to this point. */
+
+ TcpSegment *seg_list; /**< list of TCP segments that are not yet (fully) used in reassembly */
+ TcpSegment *seg_list_tail; /**< Last segment in the reassembled stream seg list*/
+
+ StreamTcpSackRecord *sack_head; /**< head of list of SACK records */
+ StreamTcpSackRecord *sack_tail; /**< tail of list of SACK records */
+} TcpStream;
+
+/* from /usr/include/netinet/tcp.h */
+enum
+{
+ TCP_NONE,
+ TCP_LISTEN,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_ESTABLISHED,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_LAST_ACK,
+ TCP_CLOSE_WAIT,
+ TCP_CLOSING,
+ TCP_CLOSED,
+};
+
+/*
+ * Per SESSION flags
+ */
+
+/** Flag for mid stream session */
+#define STREAMTCP_FLAG_MIDSTREAM 0x0001
+/** Flag for mid stream established session */
+#define STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED 0x0002
+/** Flag for mid session when syn/ack is received */
+#define STREAMTCP_FLAG_MIDSTREAM_SYNACK 0x0004
+/** Flag for TCP Timestamp option */
+#define STREAMTCP_FLAG_TIMESTAMP 0x0008
+/** Server supports wscale (even though it can be 0) */
+#define STREAMTCP_FLAG_SERVER_WSCALE 0x0010
+/** 'Raw' reassembly is disabled for this ssn. */
+#define STREAMTCP_FLAG_DISABLE_RAW 0x0020
+/** Flag to indicate that the session is handling asynchronous stream.*/
+#define STREAMTCP_FLAG_ASYNC 0x0040
+/** Flag to indicate we're dealing with 4WHS: SYN, SYN, SYN/ACK, ACK
+ * (http://www.breakingpointsystems.com/community/blog/tcp-portals-the-three-way-handshake-is-a-lie) */
+#define STREAMTCP_FLAG_4WHS 0x0080
+/** Flag to indicate that this session is possible trying to evade the detection
+ * (http://www.packetstan.com/2010/06/recently-ive-been-on-campaign-to-make.html) */
+#define STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT 0x0100
+/** Flag to indicate the client (SYN pkt) permits SACK */
+#define STREAMTCP_FLAG_CLIENT_SACKOK 0x0200
+/** Flag to indicate both sides of the session permit SACK (SYN + SYN/ACK) */
+#define STREAMTCP_FLAG_SACKOK 0x0400
+/** Flag for triggering RAW reassembly before the size limit is reached or
+ the stream reaches EOF. */
+#define STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY 0x0800
+/** 3WHS confirmed by server -- if suri sees 3whs ACK but server doesn't (pkt
+ * is lost on the way to server), SYN/ACK is retransmitted. If server sends
+ * normal packet we assume 3whs to be completed. Only used for SYN/ACK resend
+ * event. */
+#define STREAMTCP_FLAG_3WHS_CONFIRMED 0x1000
+/** App Layer tracking/reassembly is disabled */
+#define STREAMTCP_FLAG_APP_LAYER_DISABLED 0x2000
+
+/*
+ * Per STREAM flags
+ */
+
+/** stream is in a gap state */
+#define STREAMTCP_STREAM_FLAG_GAP 0x0001
+/** Flag to avoid stream reassembly/app layer inspection for the stream */
+#define STREAMTCP_STREAM_FLAG_NOREASSEMBLY 0x0002
+/** we received a keep alive */
+#define STREAMTCP_STREAM_FLAG_KEEPALIVE 0x0004
+/** Stream has reached it's reassembly depth, all further packets are ignored */
+#define STREAMTCP_STREAM_FLAG_DEPTH_REACHED 0x0008
+// vacancy
+/** Stream supports TIMESTAMP -- used to set ssn STREAMTCP_FLAG_TIMESTAMP
+ * flag. */
+#define STREAMTCP_STREAM_FLAG_TIMESTAMP 0x0020
+/** Flag to indicate the zero value of timestamp */
+#define STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP 0x0040
+/** App proto detection completed */
+#define STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED 0x0080
+/** App proto detection skipped */
+#define STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_SKIPPED 0x0100
+/** Raw reassembly disabled for new segments */
+#define STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED 0x0200
+// vacancy 2x
+/** NOTE: flags field is 12 bits */
+
+
+/*
+ * Per SEGMENT flags
+ */
+/** Flag to indicate that the current segment has been processed by the
+ * reassembly code and should be deleted after app layer protocol has been
+ * detected. */
+#define SEGMENTTCP_FLAG_RAW_PROCESSED 0x01
+/** App Layer reassembly code is done with this segment */
+#define SEGMENTTCP_FLAG_APPLAYER_PROCESSED 0x02
+/** Log API (streaming) has processed this segment */
+#define SEGMENTTCP_FLAG_LOGAPI_PROCESSED 0x04
+
+
+#define PAWS_24DAYS 2073600 /**< 24 days in seconds */
+
+#define PKT_IS_IN_RIGHT_DIR(ssn, p) ((ssn)->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK ? \
+ PKT_IS_TOSERVER(p) ? (p)->flowflags &= ~FLOW_PKT_TOSERVER \
+ (p)->flowflags |= FLOW_PKT_TOCLIENT : (p)->flowflags &= ~FLOW_PKT_TOCLIENT \
+ (p)->flowflags |= FLOW_PKT_TOSERVER : 0)
+
+/* Macro's for comparing Sequence numbers
+ * Page 810 from TCP/IP Illustrated, Volume 2. */
+#define SEQ_EQ(a,b) ((int32_t)((a) - (b)) == 0)
+#define SEQ_LT(a,b) ((int32_t)((a) - (b)) < 0)
+#define SEQ_LEQ(a,b) ((int32_t)((a) - (b)) <= 0)
+#define SEQ_GT(a,b) ((int32_t)((a) - (b)) > 0)
+#define SEQ_GEQ(a,b) ((int32_t)((a) - (b)) >= 0)
+
+#define STREAMTCP_SET_RA_BASE_SEQ(stream, seq) { \
+ do { \
+ (stream)->ra_raw_base_seq = (seq); \
+ (stream)->ra_app_base_seq = (seq); \
+ } while(0); \
+}
+
+#define StreamTcpSetEvent(p, e) { \
+ SCLogDebug("setting event %"PRIu8" on pkt %p (%"PRIu64")", (e), p, (p)->pcap_cnt); \
+ ENGINE_SET_EVENT((p), (e)); \
+}
+
+typedef struct TcpSession_ {
+ PoolThreadReserved res;
+ uint8_t state;
+ uint8_t queue_len; /**< length of queue list below */
+ int8_t data_first_seen_dir;
+ /** track all the tcp flags we've seen */
+ uint8_t tcp_packet_flags;
+ /* coccinelle: TcpSession:flags:STREAMTCP_FLAG */
+ uint16_t flags;
+ TcpStream server;
+ TcpStream client;
+ struct StreamMsg_ *toserver_smsg_head; /**< list of stream msgs (for detection inspection) */
+ struct StreamMsg_ *toserver_smsg_tail; /**< list of stream msgs (for detection inspection) */
+ struct StreamMsg_ *toclient_smsg_head; /**< list of stream msgs (for detection inspection) */
+ struct StreamMsg_ *toclient_smsg_tail; /**< list of stream msgs (for detection inspection) */
+
+ TcpStateQueue *queue; /**< list of SYN/ACK candidates */
+} TcpSession;
+
+#define StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream) \
+ ((stream)->flags |= STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED)
+#define StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream) \
+ ((stream)->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED)
+#define StreamTcpResetStreamFlagAppProtoDetectionCompleted(stream) \
+ ((stream)->flags &= ~STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED);
+#define StreamTcpDisableAppLayerReassembly(ssn) do { \
+ SCLogDebug("setting STREAMTCP_FLAG_APP_LAYER_DISABLED on ssn %p", ssn); \
+ ((ssn)->flags |= STREAMTCP_FLAG_APP_LAYER_DISABLED); \
+ } while (0);
+
+#endif /* __STREAM_TCP_PRIVATE_H__ */
diff --git a/framework/src/suricata/src/stream-tcp-reassemble.c b/framework/src/suricata/src/stream-tcp-reassemble.c
new file mode 100644
index 00000000..caf955af
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-reassemble.c
@@ -0,0 +1,8717 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Reference:
+ * Judy Novak, Steve Sturges: Target-Based TCP Stream Reassembly August, 2007
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "threads.h"
+#include "conf.h"
+
+#include "flow-util.h"
+
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-pool.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-host-os-info.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp-inline.h"
+#include "stream-tcp-util.h"
+
+#include "stream.h"
+
+#include "util-debug.h"
+#include "app-layer-protos.h"
+#include "app-layer.h"
+#include "app-layer-events.h"
+
+#include "detect-engine-state.h"
+
+#include "util-profiling.h"
+
+#define PSEUDO_PACKET_PAYLOAD_SIZE 65416 /* 64 Kb minus max IP and TCP header */
+
+#ifdef DEBUG
+static SCMutex segment_pool_memuse_mutex;
+static uint64_t segment_pool_memuse = 0;
+static uint64_t segment_pool_memcnt = 0;
+#endif
+
+/* We define several pools with prealloced segments with fixed size
+ * payloads. We do this to prevent having to do an SCMalloc call for every
+ * data segment we receive, which would be a large performance penalty.
+ * The cost is in memory of course. The number of pools and the properties
+ * of the pools are determined by the yaml. */
+static int segment_pool_num = 0;
+static Pool **segment_pool = NULL;
+static SCMutex *segment_pool_mutex = NULL;
+static uint16_t *segment_pool_pktsizes = NULL;
+#ifdef DEBUG
+static SCMutex segment_pool_cnt_mutex;
+static uint64_t segment_pool_cnt = 0;
+#endif
+/* index to the right pool for all packet sizes. */
+static uint16_t segment_pool_idx[65536]; /* O(1) lookups of the pool */
+static int check_overlap_different_data = 0;
+
+/* Memory use counter */
+SC_ATOMIC_DECLARE(uint64_t, ra_memuse);
+
+/* prototypes */
+static int HandleSegmentStartsBeforeListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+static int HandleSegmentStartsAtSameListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+static int HandleSegmentStartsAfterListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+void StreamTcpSegmentDataReplace(TcpSegment *, TcpSegment *, uint32_t, uint16_t);
+void StreamTcpSegmentDataCopy(TcpSegment *, TcpSegment *);
+TcpSegment* StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *, uint16_t);
+void StreamTcpCreateTestPacket(uint8_t *, uint8_t, uint8_t, uint8_t);
+void StreamTcpReassemblePseudoPacketCreate(TcpStream *, Packet *, PacketQueue *);
+static int StreamTcpSegmentDataCompare(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len);
+
+void StreamTcpReassembleConfigEnableOverlapCheck(void)
+{
+ check_overlap_different_data = 1;
+}
+
+/**
+ * \brief Function to Increment the memory usage counter for the TCP reassembly
+ * segments
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ */
+void StreamTcpReassembleIncrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_ADD(ra_memuse, size);
+ return;
+}
+
+/**
+ * \brief Function to Decrease the memory usage counter for the TCP reassembly
+ * segments
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ */
+void StreamTcpReassembleDecrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_SUB(ra_memuse, size);
+ return;
+}
+
+uint64_t StreamTcpReassembleMemuseGlobalCounter(void)
+{
+ uint64_t smemuse = SC_ATOMIC_GET(ra_memuse);
+ return smemuse;
+}
+
+/**
+ * \brief Function to Check the reassembly memory usage counter against the
+ * allowed max memory usgae for TCP segments.
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ * \retval 1 if in bounds
+ * \retval 0 if not in bounds
+ */
+int StreamTcpReassembleCheckMemcap(uint32_t size)
+{
+ if (stream_config.reassembly_memcap == 0 ||
+ (uint64_t)((uint64_t)size + SC_ATOMIC_GET(ra_memuse)) <= stream_config.reassembly_memcap)
+ return 1;
+ return 0;
+}
+
+/** \brief alloc a tcp segment pool entry */
+void *TcpSegmentPoolAlloc()
+{
+ if (StreamTcpReassembleCheckMemcap((uint32_t)sizeof(TcpSegment)) == 0) {
+ return NULL;
+ }
+
+ TcpSegment *seg = NULL;
+
+ seg = SCMalloc(sizeof (TcpSegment));
+ if (unlikely(seg == NULL))
+ return NULL;
+ return seg;
+}
+
+int TcpSegmentPoolInit(void *data, void *payload_len)
+{
+ TcpSegment *seg = (TcpSegment *) data;
+ uint16_t size = *((uint16_t *) payload_len);
+
+ /* do this before the can bail, so TcpSegmentPoolCleanup
+ * won't have uninitialized memory to consider. */
+ memset(seg, 0, sizeof (TcpSegment));
+
+ if (StreamTcpReassembleCheckMemcap((uint32_t)size + (uint32_t)sizeof(TcpSegment)) == 0) {
+ return 0;
+ }
+
+ seg->pool_size = size;
+ seg->payload_len = seg->pool_size;
+
+ seg->payload = SCMalloc(seg->payload_len);
+ if (seg->payload == NULL) {
+ return 0;
+ }
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_memuse_mutex);
+ segment_pool_memuse += seg->payload_len;
+ segment_pool_memcnt++;
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexUnlock(&segment_pool_memuse_mutex);
+#endif
+
+ StreamTcpReassembleIncrMemuse((uint32_t)seg->pool_size + sizeof(TcpSegment));
+ return 1;
+}
+
+/** \brief clean up a tcp segment pool entry */
+void TcpSegmentPoolCleanup(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ TcpSegment *seg = (TcpSegment *) ptr;
+
+ StreamTcpReassembleDecrMemuse((uint32_t)seg->pool_size + sizeof(TcpSegment));
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_memuse_mutex);
+ segment_pool_memuse -= seg->pool_size;
+ segment_pool_memcnt--;
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexUnlock(&segment_pool_memuse_mutex);
+#endif
+
+ SCFree(seg->payload);
+ return;
+}
+
+/**
+ * \brief Function to return the segment back to the pool.
+ *
+ * \param seg Segment which will be returned back to the pool.
+ */
+void StreamTcpSegmentReturntoPool(TcpSegment *seg)
+{
+ if (seg == NULL)
+ return;
+
+ seg->next = NULL;
+ seg->prev = NULL;
+
+ uint16_t idx = segment_pool_idx[seg->pool_size];
+ SCMutexLock(&segment_pool_mutex[idx]);
+ PoolReturn(segment_pool[idx], (void *) seg);
+ SCLogDebug("segment_pool[%"PRIu16"]->empty_stack_size %"PRIu32"",
+ idx,segment_pool[idx]->empty_stack_size);
+ SCMutexUnlock(&segment_pool_mutex[idx]);
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_cnt_mutex);
+ segment_pool_cnt--;
+ SCMutexUnlock(&segment_pool_cnt_mutex);
+#endif
+}
+
+/**
+ * \brief return all segments in this stream into the pool(s)
+ *
+ * \param stream the stream to cleanup
+ */
+void StreamTcpReturnStreamSegments (TcpStream *stream)
+{
+ TcpSegment *seg = stream->seg_list;
+ TcpSegment *next_seg;
+
+ if (seg == NULL)
+ return;
+
+ while (seg != NULL) {
+ next_seg = seg->next;
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ }
+
+ stream->seg_list = NULL;
+ stream->seg_list_tail = NULL;
+}
+
+/** \param f locked flow */
+void StreamTcpDisableAppLayer(Flow *f)
+{
+ if (f->protoctx == NULL)
+ return;
+
+ TcpSession *ssn = (TcpSession *)f->protoctx;
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(&ssn->client);
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(&ssn->server);
+ StreamTcpDisableAppLayerReassembly(ssn);
+}
+
+/** \param f locked flow */
+int StreamTcpAppLayerIsDisabled(Flow *f)
+{
+ if (f->protoctx == NULL || f->proto != IPPROTO_TCP)
+ return 0;
+
+ TcpSession *ssn = (TcpSession *)f->protoctx;
+ return (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+}
+
+typedef struct SegmentSizes_
+{
+ uint16_t pktsize;
+ uint32_t prealloc;
+} SegmentSizes;
+
+/* sort small to big */
+static int SortByPktsize(const void *a, const void *b)
+{
+ const SegmentSizes *s0 = a;
+ const SegmentSizes *s1 = b;
+ return s0->pktsize - s1->pktsize;
+}
+
+int StreamTcpReassemblyConfig(char quiet)
+{
+ Pool **my_segment_pool = NULL;
+ SCMutex *my_segment_lock = NULL;
+ uint16_t *my_segment_pktsizes = NULL;
+ SegmentSizes sizes[256];
+ memset(&sizes, 0x00, sizeof(sizes));
+
+ int npools = 0;
+ ConfNode *segs = ConfGetNode("stream.reassembly.segments");
+ if (segs != NULL) {
+ ConfNode *seg;
+ TAILQ_FOREACH(seg, &segs->head, next) {
+ ConfNode *segsize = ConfNodeLookupChild(seg,"size");
+ if (segsize == NULL)
+ continue;
+ ConfNode *segpre = ConfNodeLookupChild(seg,"prealloc");
+ if (segpre == NULL)
+ continue;
+
+ if (npools >= 256) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "too many segment packet "
+ "pools defined, max is 256");
+ return -1;
+ }
+
+ SCLogDebug("segsize->val %s", segsize->val);
+ SCLogDebug("segpre->val %s", segpre->val);
+
+ uint16_t pktsize = 0;
+ if (ByteExtractStringUint16(&pktsize, 10, strlen(segsize->val),
+ segsize->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "segment packet size "
+ "of %s is invalid", segsize->val);
+ return -1;
+ }
+ uint32_t prealloc = 0;
+ if (ByteExtractStringUint32(&prealloc, 10, strlen(segpre->val),
+ segpre->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "segment prealloc of "
+ "%s is invalid", segpre->val);
+ return -1;
+ }
+
+ sizes[npools].pktsize = pktsize;
+ sizes[npools].prealloc = prealloc;
+ SCLogDebug("pktsize %u, prealloc %u", sizes[npools].pktsize,
+ sizes[npools].prealloc);
+ npools++;
+ }
+ }
+
+ SCLogDebug("npools %d", npools);
+ if (npools > 0) {
+ /* sort the array as the index code below relies on it */
+ qsort(&sizes, npools, sizeof(sizes[0]), SortByPktsize);
+ if (sizes[npools - 1].pktsize != 0xffff) {
+ sizes[npools].pktsize = 0xffff;
+ sizes[npools].prealloc = 8;
+ npools++;
+ SCLogInfo("appended a segment pool for pktsize 65536");
+ }
+ } else if (npools == 0) {
+ /* defaults */
+ sizes[0].pktsize = 4;
+ sizes[0].prealloc = 256;
+ sizes[1].pktsize = 16;
+ sizes[1].prealloc = 512;
+ sizes[2].pktsize = 112;
+ sizes[2].prealloc = 512;
+ sizes[3].pktsize = 248;
+ sizes[3].prealloc = 512;
+ sizes[4].pktsize = 512;
+ sizes[4].prealloc = 512;
+ sizes[5].pktsize = 768;
+ sizes[5].prealloc = 1024;
+ sizes[6].pktsize = 1448;
+ sizes[6].prealloc = 1024;
+ sizes[7].pktsize = 0xffff;
+ sizes[7].prealloc = 128;
+ npools = 8;
+ }
+
+ int i = 0;
+ for (i = 0; i < npools; i++) {
+ SCLogDebug("pktsize %u, prealloc %u", sizes[i].pktsize, sizes[i].prealloc);
+ }
+
+ my_segment_pool = SCMalloc(npools * sizeof(Pool *));
+ if (my_segment_pool == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+ return -1;
+ }
+ my_segment_lock = SCMalloc(npools * sizeof(SCMutex));
+ if (my_segment_lock == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+
+ SCFree(my_segment_pool);
+ return -1;
+ }
+ my_segment_pktsizes = SCMalloc(npools * sizeof(uint16_t));
+ if (my_segment_pktsizes == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+
+ SCFree(my_segment_lock);
+ SCFree(my_segment_pool);
+ return -1;
+ }
+ uint32_t my_segment_poolsizes[npools];
+
+ for (i = 0; i < npools; i++) {
+ my_segment_pktsizes[i] = sizes[i].pktsize;
+ my_segment_poolsizes[i] = sizes[i].prealloc;
+ SCMutexInit(&my_segment_lock[i], NULL);
+
+ /* setup the pool */
+ SCMutexLock(&my_segment_lock[i]);
+ my_segment_pool[i] = PoolInit(0, my_segment_poolsizes[i], 0,
+ TcpSegmentPoolAlloc, TcpSegmentPoolInit,
+ (void *) &my_segment_pktsizes[i],
+ TcpSegmentPoolCleanup, NULL);
+ SCMutexUnlock(&my_segment_lock[i]);
+
+ if (my_segment_pool[i] == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "couldn't set up segment pool "
+ "for packet size %u. Memcap too low?", my_segment_pktsizes[i]);
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogDebug("my_segment_pktsizes[i] %u, my_segment_poolsizes[i] %u",
+ my_segment_pktsizes[i], my_segment_poolsizes[i]);
+ if (!quiet)
+ SCLogInfo("segment pool: pktsize %u, prealloc %u",
+ my_segment_pktsizes[i], my_segment_poolsizes[i]);
+ }
+
+ uint16_t idx = 0;
+ uint16_t u16 = 0;
+ while (1) {
+ if (idx <= my_segment_pktsizes[u16]) {
+ segment_pool_idx[idx] = u16;
+ if (my_segment_pktsizes[u16] == idx)
+ u16++;
+ }
+
+ if (idx == 0xffff)
+ break;
+
+ idx++;
+ }
+ /* set the globals */
+ segment_pool = my_segment_pool;
+ segment_pool_mutex = my_segment_lock;
+ segment_pool_pktsizes = my_segment_pktsizes;
+ segment_pool_num = npools;
+
+ uint32_t stream_chunk_prealloc = 250;
+ ConfNode *chunk = ConfGetNode("stream.reassembly.chunk-prealloc");
+ if (chunk) {
+ uint32_t prealloc = 0;
+ if (ByteExtractStringUint32(&prealloc, 10, strlen(chunk->val), chunk->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "chunk-prealloc of "
+ "%s is invalid", chunk->val);
+ return -1;
+ }
+ stream_chunk_prealloc = prealloc;
+ }
+ if (!quiet)
+ SCLogInfo("stream.reassembly \"chunk-prealloc\": %u", stream_chunk_prealloc);
+ StreamMsgQueuesInit(stream_chunk_prealloc);
+
+ intmax_t zero_copy_size = 128;
+ if (ConfGetInt("stream.reassembly.zero-copy-size", &zero_copy_size) == 1) {
+ if (zero_copy_size < 0 || zero_copy_size > 0xffff) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "stream.reassembly.zero-copy-size of "
+ "%"PRIiMAX" is invalid: valid values are 0 to 65535", zero_copy_size);
+ return -1;
+ }
+ }
+ stream_config.zero_copy_size = (uint16_t)zero_copy_size;
+ if (!quiet)
+ SCLogInfo("stream.reassembly \"zero-copy-size\": %u", stream_config.zero_copy_size);
+
+ return 0;
+}
+
+int StreamTcpReassembleInit(char quiet)
+{
+ /* init the memcap/use tracker */
+ SC_ATOMIC_INIT(ra_memuse);
+
+ if (StreamTcpReassemblyConfig(quiet) < 0)
+ return -1;
+#ifdef DEBUG
+ SCMutexInit(&segment_pool_memuse_mutex, NULL);
+ SCMutexInit(&segment_pool_cnt_mutex, NULL);
+#endif
+
+ StatsRegisterGlobalCounter("tcp.reassembly_memuse",
+ StreamTcpReassembleMemuseGlobalCounter);
+ return 0;
+}
+
+#ifdef DEBUG
+static uint32_t dbg_app_layer_gap;
+static uint32_t dbg_app_layer_gap_candidate;
+#endif
+
+void StreamTcpReassembleFree(char quiet)
+{
+ uint16_t u16 = 0;
+ for (u16 = 0; u16 < segment_pool_num; u16++) {
+ SCMutexLock(&segment_pool_mutex[u16]);
+
+ if (quiet == FALSE) {
+ PoolPrintSaturation(segment_pool[u16]);
+ SCLogDebug("segment_pool[u16]->empty_stack_size %"PRIu32", "
+ "segment_pool[u16]->alloc_stack_size %"PRIu32", alloced "
+ "%"PRIu32"", segment_pool[u16]->empty_stack_size,
+ segment_pool[u16]->alloc_stack_size,
+ segment_pool[u16]->allocated);
+
+ if (segment_pool[u16]->max_outstanding > segment_pool[u16]->allocated) {
+ SCLogInfo("TCP segment pool of size %u had a peak use of %u segments, "
+ "more than the prealloc setting of %u", segment_pool_pktsizes[u16],
+ segment_pool[u16]->max_outstanding, segment_pool[u16]->allocated);
+ }
+ }
+ PoolFree(segment_pool[u16]);
+
+ SCMutexUnlock(&segment_pool_mutex[u16]);
+ SCMutexDestroy(&segment_pool_mutex[u16]);
+ }
+ SCFree(segment_pool);
+ SCFree(segment_pool_mutex);
+ SCFree(segment_pool_pktsizes);
+ segment_pool = NULL;
+ segment_pool_mutex = NULL;
+ segment_pool_pktsizes = NULL;
+
+ StreamMsgQueuesDeinit(quiet);
+
+#ifdef DEBUG
+ SCLogDebug("segment_pool_cnt %"PRIu64"", segment_pool_cnt);
+ SCLogDebug("segment_pool_memuse %"PRIu64"", segment_pool_memuse);
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexDestroy(&segment_pool_memuse_mutex);
+ SCMutexDestroy(&segment_pool_cnt_mutex);
+ SCLogInfo("dbg_app_layer_gap %u", dbg_app_layer_gap);
+ SCLogInfo("dbg_app_layer_gap_candidate %u", dbg_app_layer_gap_candidate);
+#endif
+}
+
+TcpReassemblyThreadCtx *StreamTcpReassembleInitThreadCtx(ThreadVars *tv)
+{
+ SCEnter();
+ TcpReassemblyThreadCtx *ra_ctx = SCMalloc(sizeof(TcpReassemblyThreadCtx));
+ if (unlikely(ra_ctx == NULL))
+ return NULL;
+
+ memset(ra_ctx, 0x00, sizeof(TcpReassemblyThreadCtx));
+
+ ra_ctx->app_tctx = AppLayerGetCtxThread(tv);
+
+ SCReturnPtr(ra_ctx, "TcpReassemblyThreadCtx");
+}
+
+void StreamTcpReassembleFreeThreadCtx(TcpReassemblyThreadCtx *ra_ctx)
+{
+ SCEnter();
+ AppLayerDestroyCtxThread(ra_ctx->app_tctx);
+#ifdef DEBUG
+ SCLogDebug("reassembly fast path stats: fp1 %"PRIu64" fp2 %"PRIu64" sp %"PRIu64,
+ ra_ctx->fp1, ra_ctx->fp2, ra_ctx->sp);
+#endif
+ SCFree(ra_ctx);
+ SCReturn;
+}
+
+void PrintList2(TcpSegment *seg)
+{
+ TcpSegment *prev_seg = NULL;
+
+ if (seg == NULL)
+ return;
+
+ uint32_t next_seq = seg->seq;
+
+ while (seg != NULL) {
+ if (SEQ_LT(next_seq,seg->seq)) {
+ SCLogDebug("missing segment(s) for %" PRIu32 " bytes of data",
+ (seg->seq - next_seq));
+ }
+
+ SCLogDebug("seg %10"PRIu32" len %" PRIu16 ", seg %p, prev %p, next %p",
+ seg->seq, seg->payload_len, seg, seg->prev, seg->next);
+
+ if (seg->prev != NULL && SEQ_LT(seg->seq,seg->prev->seq)) {
+ /* check for SEQ_LT cornercase where a - b is exactly 2147483648,
+ * which makes the marco return TRUE in both directions. This is
+ * a hack though, we're going to check next how we end up with
+ * a segment list with seq differences that big */
+ if (!(SEQ_LT(seg->prev->seq,seg->seq))) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,seg->prev->seq)) =="
+ " TRUE, seg->seq %" PRIu32 ", seg->prev->seq %" PRIu32 ""
+ "", seg->seq, seg->prev->seq);
+ }
+ }
+
+ if (SEQ_LT(seg->seq,next_seq)) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,next_seq)) == TRUE, "
+ "seg->seq %" PRIu32 ", next_seq %" PRIu32 "", seg->seq,
+ next_seq);
+ }
+
+ if (prev_seg != seg->prev) {
+ SCLogDebug("inconsistent list: prev_seg %p != seg->prev %p",
+ prev_seg, seg->prev);
+ }
+
+ next_seq = seg->seq + seg->payload_len;
+ SCLogDebug("next_seq is now %"PRIu32"", next_seq);
+ prev_seg = seg;
+ seg = seg->next;
+ }
+}
+
+void PrintList(TcpSegment *seg)
+{
+ TcpSegment *prev_seg = NULL;
+ TcpSegment *head_seg = seg;
+
+ if (seg == NULL)
+ return;
+
+ uint32_t next_seq = seg->seq;
+
+ while (seg != NULL) {
+ if (SEQ_LT(next_seq,seg->seq)) {
+ SCLogDebug("missing segment(s) for %" PRIu32 " bytes of data",
+ (seg->seq - next_seq));
+ }
+
+ SCLogDebug("seg %10"PRIu32" len %" PRIu16 ", seg %p, prev %p, next %p, flags 0x%02x",
+ seg->seq, seg->payload_len, seg, seg->prev, seg->next, seg->flags);
+
+ if (seg->prev != NULL && SEQ_LT(seg->seq,seg->prev->seq)) {
+ /* check for SEQ_LT cornercase where a - b is exactly 2147483648,
+ * which makes the marco return TRUE in both directions. This is
+ * a hack though, we're going to check next how we end up with
+ * a segment list with seq differences that big */
+ if (!(SEQ_LT(seg->prev->seq,seg->seq))) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,seg->prev->seq)) == "
+ "TRUE, seg->seq %" PRIu32 ", seg->prev->seq %" PRIu32 "",
+ seg->seq, seg->prev->seq);
+ PrintList2(head_seg);
+ abort();
+ }
+ }
+
+ if (SEQ_LT(seg->seq,next_seq)) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,next_seq)) == TRUE, "
+ "seg->seq %" PRIu32 ", next_seq %" PRIu32 "", seg->seq,
+ next_seq);
+ PrintList2(head_seg);
+ abort();
+ }
+
+ if (prev_seg != seg->prev) {
+ SCLogDebug("inconsistent list: prev_seg %p != seg->prev %p",
+ prev_seg, seg->prev);
+ PrintList2(head_seg);
+ abort();
+ }
+
+ next_seq = seg->seq + seg->payload_len;
+ SCLogDebug("next_seq is now %"PRIu32"", next_seq);
+ prev_seg = seg;
+ seg = seg->next;
+ }
+}
+
+/**
+ * \internal
+ * \brief Get the active ra_base_seq, considering stream gaps
+ *
+ * \retval seq the active ra_base_seq
+ */
+static inline uint32_t StreamTcpReassembleGetRaBaseSeq(TcpStream *stream)
+{
+ if (!(stream->flags & STREAMTCP_STREAM_FLAG_GAP)) {
+ SCReturnUInt(stream->ra_app_base_seq);
+ } else {
+ SCReturnUInt(stream->ra_raw_base_seq);
+ }
+}
+
+/**
+ * \internal
+ * \brief Function to handle the insertion newly arrived segment,
+ * The packet is handled based on its target OS.
+ *
+ * \param stream The given TCP stream to which this new segment belongs
+ * \param seg Newly arrived segment
+ * \param p received packet
+ *
+ * \retval 0 success
+ * \retval -1 error -- either we hit a memory issue (OOM/memcap) or we received
+ * a segment before ra_base_seq.
+ */
+int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+
+ TcpSegment *list_seg = stream->seg_list;
+ TcpSegment *next_list_seg = NULL;
+
+#if DEBUG
+ PrintList(stream->seg_list);
+#endif
+
+ int ret_value = 0;
+ char return_seg = FALSE;
+
+ /* before our ra_app_base_seq we don't insert it in our list,
+ * or ra_raw_base_seq if in stream gap state */
+ if (SEQ_LT((TCP_GET_SEQ(p)+p->payload_len),(StreamTcpReassembleGetRaBaseSeq(stream)+1)))
+ {
+ SCLogDebug("not inserting: SEQ+payload %"PRIu32", last_ack %"PRIu32", "
+ "ra_(app|raw)_base_seq %"PRIu32, (TCP_GET_SEQ(p)+p->payload_len),
+ stream->last_ack, StreamTcpReassembleGetRaBaseSeq(stream)+1);
+ return_seg = TRUE;
+ ret_value = -1;
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ);
+ goto end;
+ }
+
+ SCLogDebug("SEQ %"PRIu32", SEQ+payload %"PRIu32", last_ack %"PRIu32", "
+ "ra_app_base_seq %"PRIu32, TCP_GET_SEQ(p), (TCP_GET_SEQ(p)+p->payload_len),
+ stream->last_ack, stream->ra_app_base_seq);
+
+ if (seg == NULL) {
+ goto end;
+ }
+
+ /* fast track */
+ if (list_seg == NULL) {
+ SCLogDebug("empty list, inserting seg %p seq %" PRIu32 ", "
+ "len %" PRIu32 "", seg, seg->seq, seg->payload_len);
+ stream->seg_list = seg;
+ seg->prev = NULL;
+ stream->seg_list_tail = seg;
+ goto end;
+ }
+
+ /* insert the segment in the stream list using this fast track, if seg->seq
+ is equal or higher than stream->seg_list_tail.*/
+ if (SEQ_GEQ(seg->seq, (stream->seg_list_tail->seq +
+ stream->seg_list_tail->payload_len)))
+ {
+ stream->seg_list_tail->next = seg;
+ seg->prev = stream->seg_list_tail;
+ stream->seg_list_tail = seg;
+
+ goto end;
+ }
+
+ /* If the OS policy is not set then set the OS policy for this stream */
+ if (stream->os_policy == 0) {
+ StreamTcpSetOSPolicy(stream, p);
+ }
+
+ for (; list_seg != NULL; list_seg = next_list_seg) {
+ next_list_seg = list_seg->next;
+
+ SCLogDebug("seg %p, list_seg %p, list_prev %p list_seg->next %p, "
+ "segment length %" PRIu32 "", seg, list_seg, list_seg->prev,
+ list_seg->next, seg->payload_len);
+ SCLogDebug("seg->seq %"PRIu32", list_seg->seq %"PRIu32"",
+ seg->seq, list_seg->seq);
+
+ /* segment starts before list */
+ if (SEQ_LT(seg->seq, list_seg->seq)) {
+ /* seg is entirely before list_seg */
+ if (SEQ_LEQ((seg->seq + seg->payload_len), list_seg->seq)) {
+ SCLogDebug("before list seg: seg->seq %" PRIu32 ", list_seg->seq"
+ " %" PRIu32 ", list_seg->payload_len %" PRIu32 ", "
+ "list_seg->prev %p", seg->seq, list_seg->seq,
+ list_seg->payload_len, list_seg->prev);
+ seg->next = list_seg;
+ if (list_seg->prev == NULL) {
+ stream->seg_list = seg;
+ }
+ if (list_seg->prev != NULL) {
+ list_seg->prev->next = seg;
+ seg->prev = list_seg->prev;
+ }
+ list_seg->prev = seg;
+
+ goto end;
+
+ /* seg overlap with next seg(s) */
+ } else {
+ ret_value = HandleSegmentStartsBeforeListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsBeforeListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+ }
+
+ /* seg starts at same sequence number as list_seg */
+ } else if (SEQ_EQ(seg->seq, list_seg->seq)) {
+ ret_value = HandleSegmentStartsAtSameListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsAtSameListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+
+ /* seg starts at sequence number higher than list_seg */
+ } else if (SEQ_GT(seg->seq, list_seg->seq)) {
+ if (((SEQ_GEQ(seg->seq, (list_seg->seq + list_seg->payload_len))))
+ && SEQ_GT((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len)))
+ {
+ SCLogDebug("starts beyond list end, ends after list end: "
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " (%" PRIu32 ")",
+ seg->seq, list_seg->seq, list_seg->payload_len,
+ list_seg->seq + list_seg->payload_len);
+
+ if (list_seg->next == NULL) {
+ list_seg->next = seg;
+ seg->prev = list_seg;
+ stream->seg_list_tail = seg;
+ goto end;
+ }
+ } else {
+ ret_value = HandleSegmentStartsAfterListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsAfterListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+ }
+ }
+ }
+
+end:
+ if (return_seg == TRUE && seg != NULL) {
+ StreamTcpSegmentReturntoPool(seg);
+ }
+
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+#endif
+ SCReturnInt(ret_value);
+}
+
+/**
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the sequence number lower than the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+ * \param p Packet
+ *
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+
+ uint16_t overlap = 0;
+ uint16_t packet_length = 0;
+ uint32_t overlap_point = 0;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char return_after = FALSE;
+ uint8_t os_policy = stream->os_policy;
+#ifdef DEBUG
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 "", seg->seq,
+ seg->payload_len);
+ PrintList(stream->seg_list);
+#endif
+
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->seq) &&
+ SEQ_LT((seg->seq + seg->payload_len),(list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts before list seg, ends beyond it but before list end */
+ end_before = TRUE;
+
+ /* [aaaa[abab]bbbb] a = seg, b = list_seg, overlap is the part [abab]
+ * We know seg->seq + seg->payload_len is bigger than list_seg->seq */
+ overlap = (seg->seq + seg->payload_len) - list_seg->seq;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends before list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu16 " overlap is %" PRIu32 ", "
+ "overlap point %"PRIu32"", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap, overlap_point);
+ } else if (SEQ_EQ((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg fully overlaps list_seg, starts before, at end point
+ * [aaa[ababab]] where a = seg, b = list_seg
+ * overlap is [ababab], which is list_seg->payload_len */
+ overlap = list_seg->payload_len;
+ end_same = TRUE;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends at list end: list prev %p"
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ","
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32 "",
+ list_seg->prev, seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+ /* seg fully overlaps list_seg, starts before, ends after list endpoint */
+ } else if (SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg fully overlaps list_seg, starts before, ends after list endpoint
+ * [aaa[ababab]aaa] where a = seg, b = list_seg
+ * overlap is [ababab] which is list_seg->payload_len */
+ overlap = list_seg->payload_len;
+ end_after = TRUE;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends after list end: seg->seq "
+ "%" PRIu32 ", seg->payload_len %"PRIu32" list_seg->seq "
+ "%" PRIu32 ", list_seg->payload_len %" PRIu32 " overlap is"
+ " %" PRIu32 "", seg->seq, seg->payload_len,
+ list_seg->seq, list_seg->payload_len, overlap);
+ }
+
+ if (overlap > 0) {
+ /* handle the case where we need to fill a gap before list_seg first */
+ if (list_seg->prev != NULL && SEQ_LT((list_seg->prev->seq + list_seg->prev->payload_len), list_seg->seq)) {
+ SCLogDebug("GAP to fill before list segment, size %u", list_seg->seq - (list_seg->prev->seq + list_seg->prev->payload_len));
+
+ uint32_t new_seq = (list_seg->prev->seq + list_seg->prev->payload_len);
+ if (SEQ_GT(seg->seq, new_seq)) {
+ new_seq = seg->seq;
+ }
+
+ packet_length = list_seg->seq - new_seq;
+ if (packet_length > seg->payload_len) {
+ packet_length = seg->payload_len;
+ }
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+
+ new_seg->seq = new_seq;
+
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+
+ new_seg->next = list_seg;
+ new_seg->prev = list_seg->prev;
+ list_seg->prev->next = new_seg;
+ list_seg->prev = new_seg;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, seg);
+
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+#endif
+ }
+
+ /* Handling case when the segment starts before the first segment in
+ * the list */
+ if (list_seg->prev == NULL) {
+ if (end_after == TRUE && list_seg->next != NULL &&
+ SEQ_LT(list_seg->next->seq, (seg->seq + seg->payload_len)))
+ {
+ packet_length = (list_seg->seq - seg->seq) + list_seg->payload_len;
+ } else {
+ packet_length = seg->payload_len + (list_seg->payload_len - overlap);
+ return_after = TRUE;
+ }
+
+ SCLogDebug("entered here packet_length %" PRIu32 ", seg->payload_len"
+ " %" PRIu32 ", list->payload_len %" PRIu32 "",
+ packet_length, seg->payload_len, list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = seg->seq;
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* first the data before the list_seg->seq */
+ uint16_t replace = (uint16_t) (list_seg->seq - seg->seq);
+ SCLogDebug("copying %"PRIu16" bytes to new_seg", replace);
+ StreamTcpSegmentDataReplace(new_seg, seg, seg->seq, replace);
+
+ /* if any, data after list_seg->seq + list_seg->payload_len */
+ if (SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)) && return_after == TRUE)
+ {
+ replace = (uint16_t)(((seg->seq + seg->payload_len) -
+ (list_seg->seq +
+ list_seg->payload_len)));
+ SCLogDebug("replacing %"PRIu16"", replace);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), replace);
+ }
+
+ /* update the stream last_seg in case of removal of list_seg */
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+
+ stream->seg_list = new_seg;
+ SCLogDebug("list_seg now %p, stream->seg_list now %p", list_seg,
+ stream->seg_list);
+
+ } else if (end_before == TRUE || end_same == TRUE) {
+ /* Handling overlapping with more than one segment and filling gap */
+ if (SEQ_GT(list_seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ SCLogDebug("list_seg->prev %p list_seg->prev->seq %"PRIu32" "
+ "list_seg->prev->payload_len %"PRIu16"",
+ list_seg->prev, list_seg->prev->seq,
+ list_seg->prev->payload_len);
+ if (SEQ_LT(list_seg->prev->seq, seg->seq)) {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq + list_seg->prev->payload_len));
+ }
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+
+ new_seg->payload_len = packet_length;
+ if (SEQ_GT((list_seg->prev->seq + list_seg->prev->payload_len),
+ seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ uint16_t copy_len = (uint16_t) (list_seg->seq - seg->seq);
+ SCLogDebug("copy_len %" PRIu32 " (%" PRIu32 " - %" PRIu32 ")",
+ copy_len, list_seg->seq, seg->seq);
+ StreamTcpSegmentDataReplace(new_seg, seg, seg->seq, copy_len);
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+ }
+ } else if (end_after == TRUE) {
+ if (list_seg->next != NULL) {
+ if (SEQ_LEQ((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ if (SEQ_GT(seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq +
+ list_seg->prev->payload_len));
+ }
+
+ packet_length += (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ if (SEQ_GT((list_seg->prev->seq +
+ list_seg->prev->payload_len), seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* copy the part before list_seg */
+ uint16_t copy_len = list_seg->seq - new_seg->seq;
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ copy_len);
+
+ /* copy the part after list_seg */
+ copy_len = (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), copy_len);
+
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ return_after = TRUE;
+ }
+ /* Handle the case, when list_seg is the end of segment list, but
+ seg is ending after the list_seg. So we need to copy the data
+ from newly received segment. After copying return the newly
+ received seg to pool */
+ } else {
+ if (SEQ_GT(seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq +
+ list_seg->prev->payload_len));
+ }
+
+ packet_length += (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty",
+ segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+
+ if (SEQ_GT((list_seg->prev->seq +
+ list_seg->prev->payload_len), seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* copy the part before list_seg */
+ uint16_t copy_len = list_seg->seq - new_seg->seq;
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ copy_len);
+
+ /* copy the part after list_seg */
+ copy_len = (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), copy_len);
+
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ return_after = TRUE;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(seg, list_seg, list_seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(seg, list_seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE || end_same == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, overlap_point,
+ overlap);
+ } else {
+ SCLogDebug("using old data in starts before list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_VISTA:
+ case OS_POLICY_FIRST:
+ SCLogDebug("using old data in starts before list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_LAST:
+ default:
+ SCLogDebug("replacing old data in starts before list seg "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ StreamTcpSegmentDataReplace(list_seg, seg, overlap_point,
+ overlap);
+ break;
+ }
+ }
+ /* To return from for loop as seg is finished with current list_seg
+ no need to check further (improve performance) */
+ if (end_before == TRUE || end_same == TRUE || return_after == TRUE) {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the same sequence number as the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+ *
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ uint16_t overlap = 0;
+ uint16_t packet_length;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char handle_beyond = FALSE;
+ uint8_t os_policy = stream->os_policy;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg->seg == list_seg->seq and list_seg->payload_len > seg->payload_len
+ * [[ababab]bbbb] where a = seg, b = list_seg
+ * overlap is the [ababab] part, which equals seg->payload_len. */
+ overlap = seg->payload_len;
+ end_before = TRUE;
+ SCLogDebug("starts at list seq, ends before list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32,
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+
+ } else if (SEQ_EQ((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts at seq, ends at seq, retransmission.
+ * both segments are the same, so overlap is either
+ * seg->payload_len or list_seg->payload_len */
+
+ /* check csum, ack, other differences? */
+ overlap = seg->payload_len;
+ end_same = TRUE;
+ SCLogDebug("(retransmission) starts at list seq, ends at list end: "
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %"PRIu32"",
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+
+ } else if (SEQ_GT((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len))) {
+ /* seg starts at seq, ends beyond seq. */
+ /* seg->seg == list_seg->seq and seg->payload_len > list_seg->payload_len
+ * [[ababab]aaaa] where a = seg, b = list_seg
+ * overlap is the [ababab] part, which equals list_seg->payload_len. */
+ overlap = list_seg->payload_len;
+ end_after = TRUE;
+ SCLogDebug("starts at list seq, ends beyond list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32 "",
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+ }
+ if (overlap > 0) {
+ /*Handle the case when newly arrived segment ends after original
+ segment and original segment is the last segment in the list
+ or the next segment in the list starts after the end of new segment*/
+ if (end_after == TRUE) {
+ char fill_gap = FALSE;
+
+ if (list_seg->next != NULL) {
+ /* first see if we have space left to fill up */
+ if (SEQ_LT((list_seg->seq + list_seg->payload_len),
+ list_seg->next->seq))
+ {
+ fill_gap = TRUE;
+ }
+
+ /* then see if we overlap (partly) with the next seg */
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ handle_beyond = TRUE;
+ }
+ /* Handle the case, when list_seg is the end of segment list, but
+ seg is ending after the list_seg. So we need to copy the data
+ from newly received segment. After copying return the newly
+ received seg to pool */
+ } else {
+ fill_gap = TRUE;
+ }
+
+ SCLogDebug("fill_gap %s, handle_beyond %s", fill_gap?"TRUE":"FALSE",
+ handle_beyond?"TRUE":"FALSE");
+
+ if (fill_gap == TRUE) {
+ /* if there is a gap after this list_seg we fill it now with a
+ * new seg */
+ SCLogDebug("filling gap: list_seg->next->seq %"PRIu32"",
+ list_seg->next?list_seg->next->seq:0);
+ if (handle_beyond == TRUE) {
+ packet_length = list_seg->next->seq -
+ (list_seg->seq + list_seg->payload_len);
+ } else {
+ packet_length = seg->payload_len - list_seg->payload_len;
+ }
+
+ SCLogDebug("packet_length %"PRIu16"", packet_length);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("egment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ return -1;
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = list_seg->seq + list_seg->payload_len;
+ new_seg->next = list_seg->next;
+ if (new_seg->next != NULL)
+ new_seg->next->prev = new_seg;
+ new_seg->prev = list_seg;
+ list_seg->next = new_seg;
+ SCLogDebug("new_seg %p, new_seg->next %p, new_seg->prev %p, "
+ "list_seg->next %p", new_seg, new_seg->next,
+ new_seg->prev, list_seg->next);
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ new_seg->payload_len);
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(list_seg, seg, seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(list_seg, seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE || end_same == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts at list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_LAST:
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ break;
+ case OS_POLICY_LINUX:
+ if (end_after == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts at list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_FIRST:
+ default:
+ SCLogDebug("using old data in starts at list case, list_seg->seq"
+ " %" PRIu32 " policy %" PRIu32 " overlap %" PRIu32 "",
+ list_seg->seq, os_policy, overlap);
+ break;
+ }
+ }
+
+ /* return 1 if we're done */
+ if (end_before == TRUE || end_same == TRUE || handle_beyond == FALSE) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the sequence number higher than the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsAfterListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+ uint16_t overlap = 0;
+ uint16_t packet_length;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char handle_beyond = FALSE;
+ uint8_t os_policy = stream->os_policy;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts after list, ends before list end
+ * [bbbb[ababab]bbbb] where a = seg, b = list_seg
+ * overlap is the part [ababab] which is seg->payload_len */
+ overlap = seg->payload_len;
+ end_before = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends before list end: seg->seq"
+ " %" PRIu32 ", list_seg->seq %" PRIu32 ", list_seg->payload_len "
+ "%" PRIu32 " overlap is %" PRIu32 "", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+
+ } else if (SEQ_EQ((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len))) {
+ /* seg starts after seq, before end, ends at seq
+ * [bbbb[ababab]] where a = seg, b = list_seg
+ * overlapping part is [ababab], thus seg->payload_len */
+ overlap = seg->payload_len;
+ end_same = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends at list end: seg->seq"
+ " %" PRIu32 ", list_seg->seq %" PRIu32 ", list_seg->payload_len "
+ "%" PRIu32 " overlap is %" PRIu32 "", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+
+ } else if (SEQ_LT(seg->seq, list_seg->seq + list_seg->payload_len) &&
+ SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts after seq, before end, ends beyond seq.
+ *
+ * [bbb[ababab]aaa] where a = seg, b = list_seg.
+ * overlap is the [ababab] part, which can be get using:
+ * (list_seg->seq + list_seg->payload_len) - seg->seg */
+ overlap = (list_seg->seq + list_seg->payload_len) - seg->seq;
+ end_after = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends after list seq end: "
+ "seg->seq %" PRIu32 ", seg->payload_len %"PRIu16" (%"PRIu32") "
+ "list_seg->seq %" PRIu32 ", list_seg->payload_len %" PRIu32 " "
+ "(%"PRIu32") overlap is %" PRIu32 "", seg->seq, seg->payload_len,
+ seg->seq + seg->payload_len, list_seg->seq, list_seg->payload_len,
+ list_seg->seq + list_seg->payload_len, overlap);
+ }
+ if (overlap > 0) {
+ /*Handle the case when newly arrived segment ends after original
+ segment and original segment is the last segment in the list*/
+ if (end_after == TRUE) {
+ char fill_gap = FALSE;
+
+ if (list_seg->next != NULL) {
+ /* first see if we have space left to fill up */
+ if (SEQ_LT((list_seg->seq + list_seg->payload_len),
+ list_seg->next->seq))
+ {
+ fill_gap = TRUE;
+ }
+
+ /* then see if we overlap (partly) with the next seg */
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ handle_beyond = TRUE;
+ }
+ } else {
+ fill_gap = TRUE;
+ }
+
+ SCLogDebug("fill_gap %s, handle_beyond %s", fill_gap?"TRUE":"FALSE",
+ handle_beyond?"TRUE":"FALSE");
+
+ if (fill_gap == TRUE) {
+ /* if there is a gap after this list_seg we fill it now with a
+ * new seg */
+ if (list_seg->next != NULL) {
+ SCLogDebug("filling gap: list_seg->next->seq %"PRIu32"",
+ list_seg->next?list_seg->next->seq:0);
+
+ packet_length = list_seg->next->seq - (list_seg->seq +
+ list_seg->payload_len);
+ } else {
+ packet_length = seg->payload_len - overlap;
+ }
+ if (packet_length > (seg->payload_len - overlap)) {
+ packet_length = seg->payload_len - overlap;
+ }
+ SCLogDebug("packet_length %"PRIu16"", packet_length);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = list_seg->seq + list_seg->payload_len;
+ new_seg->next = list_seg->next;
+ if (new_seg->next != NULL)
+ new_seg->next->prev = new_seg;
+ new_seg->prev = list_seg;
+ list_seg->next = new_seg;
+
+ SCLogDebug("new_seg %p, new_seg->next %p, new_seg->prev %p, "
+ "list_seg->next %p new_seg->seq %"PRIu32"", new_seg,
+ new_seg->next, new_seg->prev, list_seg->next,
+ new_seg->seq);
+
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ new_seg->payload_len);
+
+ /* update the stream last_seg in case of removal of list_seg */
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(list_seg, seg, seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(list_seg, seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts beyond list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_LAST:
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_FIRST:
+ default: /* DEFAULT POLICY */
+ SCLogDebug("using old data in starts beyond list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ break;
+ }
+ }
+ if (end_before == TRUE || end_same == TRUE || handle_beyond == FALSE) {
+ SCReturnInt(1);
+ }
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief check if stream in pkt direction has depth reached
+ *
+ * \param p packet with *LOCKED* flow
+ *
+ * \retval 1 stream has depth reached
+ * \retval 0 stream does not have depth reached
+ */
+int StreamTcpReassembleDepthReached(Packet *p)
+{
+ if (p->flow != NULL && p->flow->protoctx != NULL) {
+ TcpSession *ssn = p->flow->protoctx;
+ TcpStream *stream;
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ stream = &ssn->client;
+ } else {
+ stream = &ssn->server;
+ }
+
+ return (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) ? 1 : 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Function to Check the reassembly depth valuer against the
+ * allowed max depth of the stream reassmbly for TCP streams.
+ *
+ * \param stream stream direction
+ * \param seq sequence number where "size" starts
+ * \param size size of the segment that is added
+ *
+ * \retval size Part of the size that fits in the depth, 0 if none
+ */
+static uint32_t StreamTcpReassembleCheckDepth(TcpStream *stream,
+ uint32_t seq, uint32_t size)
+{
+ SCEnter();
+
+ /* if the configured depth value is 0, it means there is no limit on
+ reassembly depth. Otherwise carry on my boy ;) */
+ if (stream_config.reassembly_depth == 0) {
+ SCReturnUInt(size);
+ }
+
+ /* if the final flag is set, we're not accepting anymore */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ SCReturnUInt(0);
+ }
+
+ /* if the ra_base_seq has moved passed the depth window we stop
+ * checking and just reject the rest of the packets including
+ * retransmissions. Saves us the hassle of dealing with sequence
+ * wraps as well */
+ if (SEQ_GEQ((StreamTcpReassembleGetRaBaseSeq(stream)+1),(stream->isn + stream_config.reassembly_depth))) {
+ stream->flags |= STREAMTCP_STREAM_FLAG_DEPTH_REACHED;
+ SCReturnUInt(0);
+ }
+
+ SCLogDebug("full Depth not yet reached: %"PRIu32" <= %"PRIu32,
+ (StreamTcpReassembleGetRaBaseSeq(stream)+1),
+ (stream->isn + stream_config.reassembly_depth));
+
+ if (SEQ_GEQ(seq, stream->isn) && SEQ_LT(seq, (stream->isn + stream_config.reassembly_depth))) {
+ /* packet (partly?) fits the depth window */
+
+ if (SEQ_LEQ((seq + size),(stream->isn + stream_config.reassembly_depth))) {
+ /* complete fit */
+ SCReturnUInt(size);
+ } else {
+ /* partial fit, return only what fits */
+ uint32_t part = (stream->isn + stream_config.reassembly_depth) - seq;
+#if DEBUG
+ BUG_ON(part > size);
+#else
+ if (part > size)
+ part = size;
+#endif
+ SCReturnUInt(part);
+ }
+ }
+
+ SCReturnUInt(0);
+}
+
+static void StreamTcpStoreStreamChunk(TcpSession *ssn, StreamMsg *smsg, const Packet *p, int streaminline)
+{
+ uint8_t direction = 0;
+
+ if ((!streaminline && (p->flowflags & FLOW_PKT_TOSERVER)) ||
+ ( streaminline && (p->flowflags & FLOW_PKT_TOCLIENT)))
+ {
+ direction = STREAM_TOCLIENT;
+ SCLogDebug("stream chunk is to_client");
+ } else {
+ direction = STREAM_TOSERVER;
+ SCLogDebug("stream chunk is to_server");
+ }
+
+ /* store the smsg in the tcp stream */
+ if (direction == STREAM_TOSERVER) {
+ SCLogDebug("storing smsg in the to_server");
+
+ /* put the smsg in the stream list */
+ if (ssn->toserver_smsg_head == NULL) {
+ ssn->toserver_smsg_head = smsg;
+ ssn->toserver_smsg_tail = smsg;
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ } else {
+ StreamMsg *cur = ssn->toserver_smsg_tail;
+ cur->next = smsg;
+ smsg->prev = cur;
+ smsg->next = NULL;
+ ssn->toserver_smsg_tail = smsg;
+ }
+ } else {
+ SCLogDebug("storing smsg in the to_client");
+
+ /* put the smsg in the stream list */
+ if (ssn->toclient_smsg_head == NULL) {
+ ssn->toclient_smsg_head = smsg;
+ ssn->toclient_smsg_tail = smsg;
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ } else {
+ StreamMsg *cur = ssn->toclient_smsg_tail;
+ cur->next = smsg;
+ smsg->prev = cur;
+ smsg->next = NULL;
+ ssn->toclient_smsg_tail = smsg;
+ }
+ }
+}
+
+/**
+ * \brief Insert a packets TCP data into the stream reassembly engine.
+ *
+ * \retval 0 good segment, as far as we checked.
+ * \retval -1 badness, reason to drop in inline mode
+ *
+ * If the retval is 0 the segment is inserted correctly, or overlap is handled,
+ * or it wasn't added because of reassembly depth.
+ *
+ */
+int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+
+ if (ssn->data_first_seen_dir == 0) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ ssn->data_first_seen_dir = STREAM_TOSERVER;
+ } else {
+ ssn->data_first_seen_dir = STREAM_TOCLIENT;
+ }
+ }
+
+ if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) &&
+ (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED)) {
+ SCLogDebug("ssn %p: both app and raw reassembly disabled, not reassembling", ssn);
+ SCReturnInt(0);
+ }
+
+ /* If we have reached the defined depth for either of the stream, then stop
+ reassembling the TCP session */
+ uint32_t size = StreamTcpReassembleCheckDepth(stream, TCP_GET_SEQ(p), p->payload_len);
+ SCLogDebug("ssn %p: check depth returned %"PRIu32, ssn, size);
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ /* increment stream depth counter */
+ StatsIncr(tv, ra_ctx->counter_tcp_stream_depth);
+
+ stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY;
+ SCLogDebug("ssn %p: reassembly depth reached, "
+ "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn);
+ }
+ if (size == 0) {
+ SCLogDebug("ssn %p: depth reached, not reassembling", ssn);
+ SCReturnInt(0);
+ }
+
+#if DEBUG
+ BUG_ON(size > p->payload_len);
+#else
+ if (size > p->payload_len)
+ size = p->payload_len;
+#endif
+
+ TcpSegment *seg = StreamTcpGetSegment(tv, ra_ctx, size);
+ if (seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[size]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+
+ memcpy(seg->payload, p->payload, size);
+ seg->payload_len = size;
+ seg->seq = TCP_GET_SEQ(p);
+
+ if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)
+ seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+
+ /* if raw reassembly is disabled for new segments, flag each
+ * segment as complete for raw before insert */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED) {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ SCLogDebug("segment %p flagged with SEGMENTTCP_FLAG_RAW_PROCESSED, "
+ "flags %02x", seg, seg->flags);
+ }
+
+ /* proto detection skipped, but now we do get data. Set event. */
+ if (stream->seg_list == NULL &&
+ stream->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_SKIPPED) {
+
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ }
+
+ if (StreamTcpReassembleInsertSegment(tv, ra_ctx, stream, seg, p) != 0) {
+ SCLogDebug("StreamTcpReassembleInsertSegment failed");
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+static uint8_t StreamGetAppLayerFlags(TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ uint8_t flag = 0;
+
+ if (!(stream->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED)) {
+ flag |= STREAM_START;
+ }
+
+ if (ssn->state == TCP_CLOSED) {
+ flag |= STREAM_EOF;
+ }
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ flag |= STREAM_EOF;
+ }
+
+ if (StreamTcpInlineMode() == 0) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag |= STREAM_TOCLIENT;
+ } else {
+ flag |= STREAM_TOSERVER;
+ }
+ } else {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag |= STREAM_TOSERVER;
+ } else {
+ flag |= STREAM_TOCLIENT;
+ }
+ }
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ flag |= STREAM_DEPTH;
+ }
+ return flag;
+}
+
+static void StreamTcpSetupMsg(TcpSession *ssn, TcpStream *stream, Packet *p,
+ StreamMsg *smsg)
+{
+ SCEnter();
+ smsg->data_len = 0;
+ SCLogDebug("smsg %p", smsg);
+ SCReturn;
+}
+
+/**
+ * \brief Check the minimum size limits for reassembly.
+ *
+ * \retval 0 don't reassemble yet
+ * \retval 1 do reassemble
+ */
+static int StreamTcpReassembleRawCheckLimit(TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ SCEnter();
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ SCLogDebug("reassembling now as STREAMTCP_STREAM_FLAG_NOREASSEMBLY is set, so not expecting any new packets");
+ SCReturnInt(1);
+ }
+
+ if (ssn->flags & STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY) {
+ SCLogDebug("reassembling now as STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY is set");
+ ssn->flags &= ~STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY;
+ SCReturnInt(1);
+ }
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED) {
+ SCLogDebug("reassembling now as STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED is set, "
+ "so no new segments will be considered");
+ SCReturnInt(1);
+ }
+
+ /* some states mean we reassemble no matter how much data we have */
+ if (ssn->state >= TCP_TIME_WAIT)
+ SCReturnInt(1);
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ SCReturnInt(1);
+
+ /* check if we have enough data to send to L7 */
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ SCLogDebug("StreamMsgQueueGetMinChunkLen(STREAM_TOSERVER) %"PRIu32,
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER));
+
+ if (StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER) >
+ (stream->last_ack - stream->ra_raw_base_seq)) {
+ SCLogDebug("toserver min chunk len not yet reached: "
+ "last_ack %"PRIu32", ra_raw_base_seq %"PRIu32", %"PRIu32" < "
+ "%"PRIu32"", stream->last_ack, stream->ra_raw_base_seq,
+ (stream->last_ack - stream->ra_raw_base_seq),
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER));
+ SCReturnInt(0);
+ }
+ } else {
+ SCLogDebug("StreamMsgQueueGetMinChunkLen(STREAM_TOCLIENT) %"PRIu32,
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT));
+
+ if (StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT) >
+ (stream->last_ack - stream->ra_raw_base_seq)) {
+ SCLogDebug("toclient min chunk len not yet reached: "
+ "last_ack %"PRIu32", ra_base_seq %"PRIu32", %"PRIu32" < "
+ "%"PRIu32"", stream->last_ack, stream->ra_raw_base_seq,
+ (stream->last_ack - stream->ra_raw_base_seq),
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT));
+ SCReturnInt(0);
+ }
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief see if app layer is done with a segment
+ *
+ * \retval 1 app layer is done with this segment
+ * \retval 0 not done yet
+ */
+#define StreamTcpAppLayerSegmentProcessed(ssn, stream, segment) \
+ (( ( (ssn)->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) || \
+ ( (stream)->flags & STREAMTCP_STREAM_FLAG_GAP ) || \
+ ( (segment)->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED ) ? 1 :0 ))
+
+/** \internal
+ * \brief check if we can remove a segment from our segment list
+ *
+ * If a segment is entirely before the oldest smsg, we can discard it. Otherwise
+ * we keep it around to be able to log it.
+ *
+ * \retval 1 yes
+ * \retval 0 no
+ */
+static inline int StreamTcpReturnSegmentCheck(const Flow *f, TcpSession *ssn, TcpStream *stream, TcpSegment *seg)
+{
+ if (stream == &ssn->client && ssn->toserver_smsg_head != NULL) {
+ /* not (seg is entirely before first smsg, skip) */
+ if (!(SEQ_LEQ(seg->seq + seg->payload_len, ssn->toserver_smsg_head->seq))) {
+ SCReturnInt(0);
+ }
+ } else if (stream == &ssn->server && ssn->toclient_smsg_head != NULL) {
+ /* not (seg is entirely before first smsg, skip) */
+ if (!(SEQ_LEQ(seg->seq + seg->payload_len, ssn->toclient_smsg_head->seq))) {
+ SCReturnInt(0);
+ }
+ }
+
+ /* if proto detect isn't done, we're not returning */
+ if (!(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream))) {
+ SCReturnInt(0);
+ }
+
+ /* check app layer conditions */
+ if (!(StreamTcpAppLayerSegmentProcessed(ssn, stream, seg))) {
+ SCReturnInt(0);
+ }
+
+ /* check raw reassembly conditions */
+ if (!(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg)
+{
+ if (seg->prev == NULL) {
+ stream->seg_list = seg->next;
+ if (stream->seg_list != NULL)
+ stream->seg_list->prev = NULL;
+ } else {
+ seg->prev->next = seg->next;
+ if (seg->next != NULL)
+ seg->next->prev = seg->prev;
+ }
+
+ if (stream->seg_list_tail == seg)
+ stream->seg_list_tail = seg->prev;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving a data segment
+ *
+ * | left edge | right edge based on sliding window size
+ * [aaa]
+ * [aaabbb]
+ * ...
+ * [aaabbbcccdddeeefff]
+ * [bbbcccdddeeefffggg] <- cut off aaa to adhere to the window size
+ *
+ * GAP situation: each chunk that is uninterrupted has it's own smsg
+ * [aaabbb].[dddeeefff]
+ * [aaa].[ccc].[eeefff]
+ *
+ * A flag will be set to indicate where the *NEW* payload starts. This
+ * is to aid the detection code for alert only sigs.
+ *
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+static int StreamTcpReassembleInlineRaw (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+ SCLogDebug("start p %p, seq %"PRIu32, p, TCP_GET_SEQ(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_DISABLE_RAW)
+ SCReturnInt(0);
+ if (stream->seg_list == NULL) {
+ SCReturnInt(0);
+ }
+
+ uint32_t ra_base_seq = stream->ra_raw_base_seq;
+ StreamMsg *smsg = NULL;
+ uint32_t smsg_offset = 0;
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+ TcpSegment *seg = stream->seg_list;
+ uint32_t next_seq = ra_base_seq + 1;
+ int gap = 0;
+
+ uint32_t chunk_size = PKT_IS_TOSERVER(p) ?
+ stream_config.reassembly_toserver_chunk_size :
+ stream_config.reassembly_toclient_chunk_size;
+
+ /* determine the left edge and right edge */
+ uint32_t right_edge = TCP_GET_SEQ(p) + p->payload_len;
+ uint32_t left_edge = right_edge - chunk_size;
+
+ /* shift the window to the right if the left edge doesn't cover segments */
+ if (SEQ_GT(seg->seq,left_edge)) {
+ right_edge += (seg->seq - left_edge);
+ left_edge = seg->seq;
+ }
+
+ SCLogDebug("left_edge %"PRIu32", right_edge %"PRIu32, left_edge, right_edge);
+
+ /* loop through the segments and fill one or more msgs */
+ for (; seg != NULL && SEQ_LT(seg->seq, right_edge); ) {
+ SCLogDebug("seg %p", seg);
+
+ /* If packets are fully before ra_base_seq, skip them. We do this
+ * because we've reassembled up to the ra_base_seq point already,
+ * so we won't do anything with segments before it anyway. */
+ SCLogDebug("checking for pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32""
+ " len %"PRIu16", combined %"PRIu32" and right_edge "
+ "%"PRIu32"", ra_base_seq, seg, seg->seq,
+ seg->payload_len, seg->seq+seg->payload_len, right_edge);
+
+ /* Remove the segments which are completely before the ra_base_seq */
+ if (SEQ_LT((seg->seq + seg->payload_len), (ra_base_seq - chunk_size)))
+ {
+ SCLogDebug("removing pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32""
+ " len %"PRIu16"", ra_base_seq, seg, seg->seq,
+ seg->payload_len);
+
+ /* only remove if app layer reassembly is ready too */
+ if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ /* otherwise, just flag it for removal */
+ } else {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ seg = seg->next;
+ }
+ continue;
+ }
+
+ /* if app layer protocol has been detected, then remove all the segments
+ * which has been previously processed and reassembled
+ *
+ * If the stream is in GAP state the app layer flag won't be set */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream) &&
+ (seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) &&
+ StreamTcpAppLayerSegmentProcessed(ssn, stream, seg))
+ {
+ SCLogDebug("segment(%p) of length %"PRIu16" has been processed,"
+ " so return it to pool", seg, seg->payload_len);
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ }
+
+ /* we've run into a sequence gap, wrap up any existing smsg and
+ * queue it so the next chunk (if any) is in a new smsg */
+ if (SEQ_GT(seg->seq, next_seq)) {
+ /* pass on pre existing smsg (if any) */
+ if (smsg != NULL && smsg->data_len > 0) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ gap = 1;
+ }
+
+ /* if the segment ends beyond left_edge we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), left_edge)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "left_edge %" PRIu32 "", seg->seq,
+ seg->payload_len, left_edge);
+
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(left_edge, seg->seq)) {
+ payload_offset = left_edge - seg->seq;
+
+ if (SEQ_LT(right_edge, (seg->seq + seg->payload_len))) {
+ payload_len = (right_edge - seg->seq) - payload_offset;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(right_edge, (seg->seq + seg->payload_len))) {
+ payload_len = right_edge - seg->seq;
+ } else {
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ break;
+ }
+
+ if (smsg == NULL) {
+ smsg = StreamMsgGetFromPool();
+ if (smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ return -1;
+ }
+
+ smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, smsg);
+ smsg->seq = ra_base_seq + 1;
+ }
+
+ /* copy the data into the smsg */
+ uint32_t copy_size = smsg->data_size - smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > smsg->data_size);
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(smsg->data + smsg_offset, seg->payload + payload_offset,
+ copy_size);
+ smsg_offset += copy_size;
+
+ SCLogDebug("seg total %u, seq %u off %u copy %u, ra_base_seq %u",
+ (seg->seq + payload_offset + copy_size), seg->seq,
+ payload_offset, copy_size, ra_base_seq);
+ if (gap == 0 && SEQ_GT((seg->seq + payload_offset + copy_size),ra_base_seq+1)) {
+ ra_base_seq += copy_size;
+ }
+ SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
+
+ smsg->data_len += copy_size;
+
+ /* queue the smsg if it's full */
+ if (smsg->data_len == smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+
+ /* get a new message
+ XXX we need a setup function */
+ smsg = StreamMsgGetFromPool();
+ if (smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ SCReturnInt(-1);
+ }
+ smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream,p,smsg);
+ smsg->seq = ra_base_seq + 1;
+
+ copy_size = smsg->data_size - smsg_offset;
+ if ((int32_t)copy_size > (seg->payload_len - payload_offset)) {
+ copy_size = (seg->payload_len - payload_offset);
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > smsg->data_size);
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", smsg_offset "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, smsg_offset, copy_size);
+ memcpy(smsg->data + smsg_offset, seg->payload +
+ payload_offset, copy_size);
+ smsg_offset += copy_size;
+ if (gap == 0 && SEQ_GT((seg->seq + payload_offset + copy_size),ra_base_seq+1)) {
+ ra_base_seq += copy_size;
+ }
+ SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
+ smsg->data_len += copy_size;
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "smsg_offset %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, smsg_offset, copy_size);
+ if (smsg->data_len == smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ /* see if we have segment payload left to process */
+ if ((copy_size + payload_offset) < seg->payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (ra_base_seq - chunk_size))) {
+ if (seg->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED) {
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ SCLogDebug("removing seg %p, seg->next %p", seg, seg->next);
+ StreamTcpSegmentReturntoPool(seg);
+ } else {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ }
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue */
+ if (smsg != NULL) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ smsg = NULL;
+ stream->ra_raw_base_seq = ra_base_seq;
+ }
+
+ /* see if we can clean up some segments */
+ left_edge = (ra_base_seq + 1) - chunk_size;
+ SCLogDebug("left_edge %"PRIu32", ra_base_seq %"PRIu32, left_edge, ra_base_seq);
+
+ /* loop through the segments to remove unneeded segments */
+ for (seg = stream->seg_list; seg != NULL && SEQ_LEQ((seg->seq + p->payload_len), left_edge); ) {
+ SCLogDebug("seg %p seq %"PRIu32", len %"PRIu16", sum %"PRIu32, seg, seg->seq, seg->payload_len, seg->seq+seg->payload_len);
+
+ /* only remove if app layer reassembly is ready too */
+ if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ } else {
+ break;
+ }
+ }
+ SCLogDebug("stream->ra_raw_base_seq %u", stream->ra_raw_base_seq);
+ SCReturnInt(0);
+}
+
+/** \brief Remove idle TcpSegments from TcpSession
+ *
+ * \param f flow
+ * \param flags direction flags
+ */
+void StreamTcpPruneSession(Flow *f, uint8_t flags)
+{
+ if (f == NULL || f->protoctx == NULL)
+ return;
+
+ TcpSession *ssn = f->protoctx;
+ TcpStream *stream = NULL;
+
+ if (flags & STREAM_TOSERVER) {
+ stream = &ssn->client;
+ } else if (flags & STREAM_TOCLIENT) {
+ stream = &ssn->server;
+ } else {
+ return;
+ }
+
+ /* loop through the segments and fill one or more msgs */
+ TcpSegment *seg = stream->seg_list;
+
+ for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
+ {
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32", FLAGS %02x",
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len), seg->flags);
+
+ if (StreamTcpReturnSegmentCheck(f, ssn, stream, seg) == 0) {
+ break;
+ }
+
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ }
+}
+
+#ifdef DEBUG
+static uint64_t GetStreamSize(TcpStream *stream)
+{
+ if (stream) {
+ uint64_t size = 0;
+ uint32_t cnt = 0;
+
+ TcpSegment *seg = stream->seg_list;
+ while (seg) {
+ cnt++;
+ size += (uint64_t)seg->payload_len;
+
+ seg = seg->next;
+ }
+
+ SCLogDebug("size %"PRIu64", cnt %"PRIu32, size, cnt);
+ return size;
+ }
+ return (uint64_t)0;
+}
+
+static void GetSessionSize(TcpSession *ssn, Packet *p)
+{
+ uint64_t size = 0;
+ if (ssn) {
+ size = GetStreamSize(&ssn->client);
+ size += GetStreamSize(&ssn->server);
+
+ //if (size > 900000)
+ // SCLogInfo("size %"PRIu64", packet %"PRIu64, size, p->pcap_cnt);
+ SCLogDebug("size %"PRIu64", packet %"PRIu64, size, p->pcap_cnt);
+ }
+}
+#endif
+
+typedef struct ReassembleData_ {
+ uint32_t ra_base_seq;
+ uint32_t data_len;
+ uint8_t data[4096];
+ int partial; /* last segment was processed only partially */
+ uint32_t data_sent; /* data passed on this run */
+} ReassembleData;
+
+/** \internal
+ * \brief test if segment follows a gap. If so, handle the gap
+ *
+ * If in inline mode, segment may be un-ack'd. In this case we
+ * consider it a gap, but it's not 'final' yet.
+ *
+ * \retval bool 1 gap 0 no gap
+ */
+int DoHandleGap(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
+ Packet *p, uint32_t next_seq)
+{
+ if (unlikely(SEQ_GT(seg->seq, next_seq))) {
+ /* we've run into a sequence gap */
+
+ if (StreamTcpInlineMode()) {
+ /* don't conclude it's a gap until we see that the data
+ * that is missing was acked. */
+ if (SEQ_GT(seg->seq,stream->last_ack) && ssn->state != TCP_CLOSED)
+ return 1;
+ }
+
+ /* first, pass on data before the gap */
+ if (rd->data_len > 0) {
+ SCLogDebug("pre GAP data");
+
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+ }
+
+#ifdef DEBUG
+ uint32_t gap_len = seg->seq - next_seq;
+ SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
+ "stream->last_ack %" PRIu32 ". Seq gap %" PRIu32"",
+ next_seq, seg->seq, stream->last_ack, gap_len);
+#endif
+ /* We have missed the packet and end host has ack'd it, so
+ * IDS should advance it's ra_base_seq and should not consider this
+ * packet any longer, even if it is retransmitted, as end host will
+ * drop it anyway */
+ rd->ra_base_seq = seg->seq - 1;
+
+ /* send gap "signal" */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0, StreamGetAppLayerFlags(ssn, stream, p)|STREAM_GAP);
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+
+ /* set a GAP flag and make sure not bothering this stream anymore */
+ SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
+ stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
+ StatsIncr(tv, ra_ctx->counter_tcp_reass_gap);
+#ifdef DEBUG
+ dbg_app_layer_gap++;
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+static inline int DoReassemble(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
+ Packet *p)
+{
+ /* fast path 1: segment is exactly what we need */
+ if (likely(rd->data_len == 0 &&
+ SEQ_EQ(seg->seq, rd->ra_base_seq+1) &&
+ SEQ_EQ(stream->last_ack, (seg->seq + seg->payload_len))))
+ {
+ /* process single segment directly */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ seg->payload, seg->payload_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += seg->payload_len;
+ rd->ra_base_seq += seg->payload_len;
+#ifdef DEBUG
+ ra_ctx->fp1++;
+#endif
+ /* if after the first data chunk we have no alproto yet,
+ * there is no point in continueing here. */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("no alproto after first data chunk");
+ return 0;
+ }
+ return 1;
+ /* fast path 2: segment acked completely, meets minimal size req for 0copy processing */
+ } else if (rd->data_len == 0 &&
+ SEQ_EQ(seg->seq, rd->ra_base_seq+1) &&
+ SEQ_GT(stream->last_ack, (seg->seq + seg->payload_len)) &&
+ seg->payload_len >= stream_config.zero_copy_size)
+ {
+ /* process single segment directly */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ seg->payload, seg->payload_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += seg->payload_len;
+ rd->ra_base_seq += seg->payload_len;
+#ifdef DEBUG
+ ra_ctx->fp2++;
+#endif
+ /* if after the first data chunk we have no alproto yet,
+ * there is no point in continueing here. */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("no alproto after first data chunk");
+ return 0;
+ }
+ return 1;
+ }
+#ifdef DEBUG
+ ra_ctx->sp++;
+#endif
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+
+ /* start clean */
+ rd->partial = FALSE;
+
+ /* if the segment ends beyond ra_base_seq we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), rd->ra_base_seq+1)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "ra_base_seq %" PRIu32 ", last_ack %"PRIu32, seg->seq,
+ seg->payload_len, rd->ra_base_seq, stream->last_ack);
+
+ if (StreamTcpInlineMode() == 0) {
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = (rd->ra_base_seq + 1) - seg->seq;
+ SCLogDebug("payload_offset %u", payload_offset);
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ if (SEQ_LT(stream->last_ack, (rd->ra_base_seq + 1))) {
+ payload_len = (stream->last_ack - seg->seq);
+ SCLogDebug("payload_len %u", payload_len);
+ } else {
+ payload_len = (stream->last_ack - seg->seq) - payload_offset;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ payload_len = stream->last_ack - seg->seq;
+ SCLogDebug("payload_len %u", payload_len);
+
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+ }
+ /* inline mode, don't consider last_ack as we process un-ACK'd segments */
+ } else {
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = rd->ra_base_seq - seg->seq - 1;
+ payload_len = seg->payload_len - payload_offset;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ return 0;
+ }
+
+ /* copy the data into the buffer */
+ uint16_t copy_size = sizeof(rd->data) - rd->data_len;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > sizeof(rd->data));
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(rd->data + rd->data_len, seg->payload + payload_offset, copy_size);
+ rd->data_len += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32", data_len %"PRIu32, rd->ra_base_seq, rd->data_len);
+
+ /* queue the smsg if it's full */
+ if (rd->data_len == sizeof(rd->data)) {
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+
+ /* if after the first data chunk we have no alproto yet,
+ * there is no point in continueing here. */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("no alproto after first data chunk");
+ return 0;
+ }
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+ rd->data_len = 0;
+
+ copy_size = sizeof(rd->data) - rd->data_len;
+ if (copy_size > (seg->payload_len - payload_offset)) {
+ copy_size = (seg->payload_len - payload_offset);
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > sizeof(rd->data));
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", data_len "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->data_len, copy_size);
+ memcpy(rd->data + rd->data_len, seg->payload +
+ payload_offset, copy_size);
+ rd->data_len += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "data_len %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->data_len, copy_size);
+
+ if (rd->data_len == sizeof(rd->data)) {
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+
+ /* if after the first data chunk we have no alproto yet,
+ * there is no point in continueing here. */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("no alproto after first data chunk");
+ return 0;
+ }
+ }
+
+ /* see if we have segment payload left to process */
+ if ((copy_size + payload_offset) < seg->payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving an ACK packet.
+ *
+ * Stream is in the opposite direction of the packet, as the ACK-packet
+ * is ACK'ing the stream.
+ *
+ * One of the utilities call by this function AppLayerHandleTCPData(),
+ * has a feature where it will call this very same function for the
+ * stream opposing the stream it is called with. This shouldn't cause
+ * any issues, since processing of each stream is independent of the
+ * other stream.
+ *
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ SCEnter();
+
+ /* this function can be directly called by app layer protocol
+ * detection. */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ SCLogDebug("stream no reassembly flag set. Mostly called via "
+ "app proto detection.");
+ SCReturnInt(0);
+ }
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+ GetSessionSize(ssn, p);
+#endif
+
+ /* if no segments are in the list or all are already processed,
+ * and state is beyond established, we send an empty msg */
+ TcpSegment *seg_tail = stream->seg_list_tail;
+ if (seg_tail == NULL ||
+ (seg_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
+ {
+ /* send an empty EOF msg if we have no segments but TCP state
+ * is beyond ESTABLISHED */
+ if (ssn->state >= TCP_CLOSING || (p->flags & PKT_PSEUDO_STREAM_END)) {
+ SCLogDebug("sending empty eof message");
+ /* send EOF to app layer */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+
+ SCReturnInt(0);
+ }
+ }
+
+ /* no segments, nothing to do */
+ if (stream->seg_list == NULL) {
+ SCLogDebug("no segments in the list to reassemble");
+ SCReturnInt(0);
+ }
+
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
+ SCReturnInt(0);
+ }
+
+ /* stream->ra_app_base_seq remains at stream->isn until protocol is
+ * detected. */
+ ReassembleData rd;
+ rd.ra_base_seq = stream->ra_app_base_seq;
+ rd.data_len = 0;
+ rd.data_sent = 0;
+ rd.partial = FALSE;
+ uint32_t next_seq = rd.ra_base_seq + 1;
+
+ SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
+ rd.ra_base_seq, stream->last_ack, next_seq);
+
+ /* loop through the segments and fill one or more msgs */
+ TcpSegment *seg = stream->seg_list;
+ SCLogDebug("pre-loop seg %p", seg);
+
+ /* Check if we have a gap at the start of the list. If last_ack is
+ * bigger than the list start and the list start is bigger than
+ * next_seq, we know we are missing data that has been ack'd. That
+ * won't get retransmitted, so it's a data gap.
+ */
+ if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) {
+ if (SEQ_GT(seg->seq, next_seq) && SEQ_LT(seg->seq, stream->last_ack)) {
+ /* send gap signal */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0,
+ StreamGetAppLayerFlags(ssn, stream, p)|STREAM_GAP);
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+
+ /* set a GAP flag and make sure not bothering this stream anymore */
+ SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
+ stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
+ StatsIncr(tv, ra_ctx->counter_tcp_reass_gap);
+#ifdef DEBUG
+ dbg_app_layer_gap++;
+#endif
+ SCReturnInt(0);
+ }
+ }
+
+ for (; seg != NULL; )
+ {
+ /* if in inline mode, we process all segments regardless of whether
+ * they are ack'd or not. In non-inline, we process only those that
+ * are at least partly ack'd. */
+ if (StreamTcpInlineMode() == 0 && SEQ_GEQ(seg->seq, stream->last_ack))
+ break;
+
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len));
+
+ if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
+ SCLogDebug("removing segment");
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ } else if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ seg = next_seg;
+ continue;
+ }
+
+ /* check if we have a sequence gap and if so, handle it */
+ if (DoHandleGap(tv, ra_ctx, ssn, stream, seg, &rd, p, next_seq) == 1)
+ break;
+
+ /* process this segment */
+ if (DoReassemble(tv, ra_ctx, ssn, stream, seg, &rd, p) == 0)
+ break;
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+ if (rd.partial == FALSE) {
+ SCLogDebug("fully done with segment in app layer reassembly (seg %p seq %"PRIu32")",
+ seg, seg->seq);
+ seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+ SCLogDebug("flags now %02x", seg->flags);
+ } else {
+ SCLogDebug("not yet fully done with segment in app layer reassembly");
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue to the l7 handler */
+ if (rd.data_len > 0) {
+ SCLogDebug("data_len > 0, %u", rd.data_len);
+ /* process what we have so far */
+ BUG_ON(rd.data_len > sizeof(rd.data));
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd.data, rd.data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ }
+
+ /* if no data was sent to the applayer, we send it a empty 'nudge'
+ * when in inline mode */
+ if (StreamTcpInlineMode() && rd.data_sent == 0 && ssn->state > TCP_ESTABLISHED) {
+ SCLogDebug("sending empty eof message");
+ /* send EOF to app layer */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0, StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ }
+
+ /* store ra_base_seq in the stream */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ stream->ra_app_base_seq = rd.ra_base_seq;
+ } else {
+ TcpSegment *tmp_seg = stream->seg_list;
+ while (tmp_seg != NULL) {
+ tmp_seg->flags &= ~SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+ tmp_seg = tmp_seg->next;
+ }
+ }
+ SCLogDebug("stream->ra_app_base_seq %u", stream->ra_app_base_seq);
+ SCReturnInt(0);
+}
+
+typedef struct ReassembleRawData_ {
+ uint32_t ra_base_seq;
+ int partial; /* last segment was processed only partially */
+ StreamMsg *smsg;
+ uint32_t smsg_offset; // TODO diff with smsg->data_len?
+} ReassembleRawData;
+
+static void DoHandleRawGap(TcpSession *ssn, TcpStream *stream, TcpSegment *seg, Packet *p,
+ ReassembleRawData *rd, uint32_t next_seq)
+{
+ /* we've run into a sequence gap */
+ if (SEQ_GT(seg->seq, next_seq)) {
+ /* pass on pre existing smsg (if any) */
+ if (rd->smsg != NULL && rd->smsg->data_len > 0) {
+ /* if app layer protocol has not been detected till yet,
+ then check did we have sent message to app layer already
+ or not. If not then sent the message and set flag that first
+ message has been sent. No more data till proto has not
+ been detected */
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* see what the length of the gap is, gap length is seg->seq -
+ * (ra_base_seq +1) */
+#ifdef DEBUG
+ uint32_t gap_len = seg->seq - next_seq;
+ SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
+ "stream->last_ack %" PRIu32 ". Seq gap %" PRIu32"",
+ next_seq, seg->seq, stream->last_ack, gap_len);
+#endif
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+
+ /* We have missed the packet and end host has ack'd it, so
+ * IDS should advance it's ra_base_seq and should not consider this
+ * packet any longer, even if it is retransmitted, as end host will
+ * drop it anyway */
+ rd->ra_base_seq = seg->seq - 1;
+ }
+}
+
+static int DoRawReassemble(TcpSession *ssn, TcpStream *stream, TcpSegment *seg, Packet *p,
+ ReassembleRawData *rd)
+{
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+
+ /* start clean */
+ rd->partial = FALSE;
+
+ /* if the segment ends beyond ra_base_seq we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), rd->ra_base_seq+1)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "ra_base_seq %" PRIu32 "", seg->seq,
+ seg->payload_len, rd->ra_base_seq);
+
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = rd->ra_base_seq - seg->seq;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+
+ if (SEQ_LT(stream->last_ack, rd->ra_base_seq)) {
+ payload_len = (stream->last_ack - seg->seq);
+ } else {
+ payload_len = (stream->last_ack - seg->seq) - payload_offset;
+ }
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ payload_len = stream->last_ack - seg->seq;
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ return 1; // TODO
+ }
+
+ if (rd->smsg == NULL) {
+ rd->smsg = StreamMsgGetFromPool();
+ if (rd->smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ return -1;
+ }
+
+ rd->smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, rd->smsg);
+ rd->smsg->seq = rd->ra_base_seq + 1;
+ SCLogDebug("smsg->seq %u", rd->smsg->seq);
+ }
+
+ /* copy the data into the smsg */
+ uint32_t copy_size = rd->smsg->data_size - rd->smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > rd->smsg->data_size);
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(rd->smsg->data + rd->smsg_offset, seg->payload + payload_offset,
+ copy_size);
+ rd->smsg_offset += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+
+ rd->smsg->data_len += copy_size;
+
+ /* queue the smsg if it's full */
+ if (rd->smsg->data_len == rd->smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+
+ /* get a new message
+ XXX we need a setup function */
+ rd->smsg = StreamMsgGetFromPool();
+ if (rd->smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ SCReturnInt(-1);
+ }
+ rd->smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, rd->smsg);
+ rd->smsg->seq = rd->ra_base_seq + 1;
+
+ copy_size = rd->smsg->data_size - rd->smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > rd->smsg->data_size);
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", smsg_offset "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->smsg_offset, copy_size);
+ memcpy(rd->smsg->data + rd->smsg_offset, seg->payload +
+ payload_offset, copy_size);
+ rd->smsg_offset += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+ rd->smsg->data_len += copy_size;
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "smsg_offset %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->smsg_offset, copy_size);
+ if (rd->smsg->data_len == rd->smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* see if we have segment payload left to process */
+ if (copy_size < payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving an ACK packet.
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+static int StreamTcpReassembleRaw (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+ SCLogDebug("start p %p", p);
+
+ if (ssn->flags & STREAMTCP_FLAG_DISABLE_RAW)
+ SCReturnInt(0);
+
+ if (stream->seg_list == NULL) {
+ SCLogDebug("no segments in the list to reassemble");
+ SCReturnInt(0);
+ }
+
+#if 0
+ if (ssn->state <= TCP_ESTABLISHED &&
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("only starting raw reassembly after app layer protocol "
+ "detection has completed.");
+ SCReturnInt(0);
+ }
+#endif
+ /* check if we have enough data */
+ if (StreamTcpReassembleRawCheckLimit(ssn,stream,p) == 0) {
+ SCLogDebug("not yet reassembling");
+ SCReturnInt(0);
+ }
+
+ TcpSegment *seg = stream->seg_list;
+ ReassembleRawData rd;
+ rd.smsg = NULL;
+ rd.ra_base_seq = stream->ra_raw_base_seq;
+ rd.smsg_offset = 0;
+ uint32_t next_seq = rd.ra_base_seq + 1;
+
+ SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
+ rd.ra_base_seq, stream->last_ack, next_seq);
+
+ /* loop through the segments and fill one or more msgs */
+ for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
+ {
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32", flags %02x",
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len), seg->flags);
+
+ if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
+ SCLogDebug("removing segment");
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ } else if(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) {
+ TcpSegment *next_seg = seg->next;
+ seg = next_seg;
+ continue;
+ }
+
+ DoHandleRawGap(ssn, stream, seg, p, &rd, next_seq);
+
+ if (DoRawReassemble(ssn, stream, seg, p, &rd) == 0)
+ break;
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+ if (rd.partial == FALSE) {
+ SCLogDebug("fully done with segment in raw reassembly (seg %p seq %"PRIu32")",
+ seg, seg->seq);
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ SCLogDebug("flags now %02x", seg->flags);
+ } else {
+ SCLogDebug("not yet fully done with segment in raw reassembly");
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue to the l7 handler */
+ if (rd.smsg != NULL) {
+ StreamTcpStoreStreamChunk(ssn, rd.smsg, p, 0);
+ rd.smsg = NULL;
+ stream->ra_raw_base_seq = rd.ra_base_seq;
+ }
+
+ SCReturnInt(0);
+}
+
+/** \brief update app layer and raw reassembly
+ *
+ * \retval r 0 on success, -1 on error
+ */
+int StreamTcpReassembleHandleSegmentUpdateACK (ThreadVars *tv,
+ TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+
+ int r = 0;
+ if (!(StreamTcpInlineMode())) {
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ if (StreamTcpReassembleRaw(ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ }
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+ SCReturnInt(r);
+}
+
+int StreamTcpReassembleHandleSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream,
+ Packet *p, PacketQueue *pq)
+{
+ SCEnter();
+ SCLogDebug("ssn %p, stream %p, p %p, p->payload_len %"PRIu16"",
+ ssn, stream, p, p->payload_len);
+
+ /* we need to update the opposing stream in
+ * StreamTcpReassembleHandleSegmentUpdateACK */
+ TcpStream *opposing_stream = NULL;
+ if (stream == &ssn->client) {
+ opposing_stream = &ssn->server;
+ } else {
+ opposing_stream = &ssn->client;
+ }
+
+ /* handle ack received */
+ if (StreamTcpReassembleHandleSegmentUpdateACK(tv, ra_ctx, ssn, opposing_stream, p) != 0)
+ {
+ SCLogDebug("StreamTcpReassembleHandleSegmentUpdateACK error");
+ SCReturnInt(-1);
+ }
+
+ /* If no stream reassembly/application layer protocol inspection, then
+ simple return */
+ if (p->payload_len > 0 && !(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
+
+ if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) {
+ SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error");
+ SCReturnInt(-1);
+ }
+
+ p->flags |= PKT_STREAM_ADD;
+ }
+
+ /* in stream inline mode even if we have no data we call the reassembly
+ * functions to handle EOF */
+ if (StreamTcpInlineMode()) {
+ int r = 0;
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ if (StreamTcpReassembleInlineRaw(ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+
+ if (r < 0) {
+ SCReturnInt(-1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to replace the data from a specific point up to given length.
+ *
+ * \param dst_seg Destination segment to replace the data
+ * \param src_seg Source segment of which data is to be written to destination
+ * \param start_point Starting point to replace the data onwards
+ * \param len Length up to which data is need to be replaced
+ *
+ * \todo VJ We can remove the abort()s later.
+ * \todo VJ Why not memcpy?
+ */
+void StreamTcpSegmentDataReplace(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len)
+{
+ uint32_t seq;
+ uint16_t src_pos = 0;
+ uint16_t dst_pos = 0;
+
+ SCLogDebug("start_point %u", start_point);
+
+ if (SEQ_GT(start_point, dst_seg->seq)) {
+ dst_pos = start_point - dst_seg->seq;
+ } else if (SEQ_LT(start_point, dst_seg->seq)) {
+ dst_pos = dst_seg->seq - start_point;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(((len + dst_pos) - 1) > dst_seg->payload_len);
+ } else {
+ if (((len + dst_pos) - 1) > dst_seg->payload_len)
+ return;
+ }
+
+ src_pos = (uint16_t)(start_point - src_seg->seq);
+
+ SCLogDebug("Replacing data from dst_pos %"PRIu16"", dst_pos);
+
+ for (seq = start_point; SEQ_LT(seq, (start_point + len)) &&
+ src_pos < src_seg->payload_len && dst_pos < dst_seg->payload_len;
+ seq++, dst_pos++, src_pos++)
+ {
+ dst_seg->payload[dst_pos] = src_seg->payload[src_pos];
+ }
+
+ SCLogDebug("Replaced data of size %"PRIu16" up to src_pos %"PRIu16
+ " dst_pos %"PRIu16, len, src_pos, dst_pos);
+}
+
+/**
+ * \brief Function to compare the data from a specific point up to given length.
+ *
+ * \param dst_seg Destination segment to compare the data
+ * \param src_seg Source segment of which data is to be compared to destination
+ * \param start_point Starting point to compare the data onwards
+ * \param len Length up to which data is need to be compared
+ *
+ * \retval 1 same
+ * \retval 0 different
+ */
+static int StreamTcpSegmentDataCompare(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len)
+{
+ uint32_t seq;
+ uint16_t src_pos = 0;
+ uint16_t dst_pos = 0;
+
+ SCLogDebug("start_point %u dst_seg %u src_seg %u", start_point, dst_seg->seq, src_seg->seq);
+
+ if (SEQ_GT(start_point, dst_seg->seq)) {
+ SCLogDebug("start_point %u > dst %u", start_point, dst_seg->seq);
+ dst_pos = start_point - dst_seg->seq;
+ } else if (SEQ_LT(start_point, dst_seg->seq)) {
+ SCLogDebug("start_point %u < dst %u", start_point, dst_seg->seq);
+ dst_pos = dst_seg->seq - start_point;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(((len + dst_pos) - 1) > dst_seg->payload_len);
+ } else {
+ if (((len + dst_pos) - 1) > dst_seg->payload_len)
+ return 1;
+ }
+
+ src_pos = (uint16_t)(start_point - src_seg->seq);
+
+ SCLogDebug("Comparing data from dst_pos %"PRIu16", src_pos %u", dst_pos, src_pos);
+
+ for (seq = start_point; SEQ_LT(seq, (start_point + len)) &&
+ src_pos < src_seg->payload_len && dst_pos < dst_seg->payload_len;
+ seq++, dst_pos++, src_pos++)
+ {
+ if (dst_seg->payload[dst_pos] != src_seg->payload[src_pos]) {
+ SCLogDebug("data is different %02x != %02x, dst_pos %u, src_pos %u", dst_seg->payload[dst_pos], src_seg->payload[src_pos], dst_pos, src_pos);
+ return 0;
+ }
+ }
+
+ SCLogDebug("Compared data of size %"PRIu16" up to src_pos %"PRIu16
+ " dst_pos %"PRIu16, len, src_pos, dst_pos);
+ return 1;
+}
+
+/**
+ * \brief Function to copy the data from src_seg to dst_seg.
+ *
+ * \param dst_seg Destination segment for copying the contents
+ * \param src_seg Source segment to copy its contents
+ *
+ * \todo VJ wouldn't a memcpy be more appropriate here?
+ *
+ * \warning Both segments need to be properly initialized.
+ */
+
+void StreamTcpSegmentDataCopy(TcpSegment *dst_seg, TcpSegment *src_seg)
+{
+ uint32_t u;
+ uint16_t dst_pos = 0;
+ uint16_t src_pos = 0;
+ uint32_t seq;
+
+ if (SEQ_GT(dst_seg->seq, src_seg->seq)) {
+ src_pos = dst_seg->seq - src_seg->seq;
+ seq = dst_seg->seq;
+ } else {
+ dst_pos = src_seg->seq - dst_seg->seq;
+ seq = src_seg->seq;
+ }
+
+ SCLogDebug("Copying data from seq %"PRIu32"", seq);
+ for (u = seq;
+ (SEQ_LT(u, (src_seg->seq + src_seg->payload_len)) &&
+ SEQ_LT(u, (dst_seg->seq + dst_seg->payload_len))); u++)
+ {
+ //SCLogDebug("u %"PRIu32, u);
+
+ dst_seg->payload[dst_pos] = src_seg->payload[src_pos];
+
+ dst_pos++;
+ src_pos++;
+ }
+ SCLogDebug("Copyied data of size %"PRIu16" up to dst_pos %"PRIu16"",
+ src_pos, dst_pos);
+}
+
+/**
+ * \brief Function to get the segment of required length from the pool.
+ *
+ * \param len Length which tells the required size of needed segment.
+ *
+ * \retval seg Segment from the pool or NULL
+ */
+TcpSegment* StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, uint16_t len)
+{
+ uint16_t idx = segment_pool_idx[len];
+ SCLogDebug("segment_pool_idx %" PRIu32 " for payload_len %" PRIu32 "",
+ idx, len);
+
+ SCMutexLock(&segment_pool_mutex[idx]);
+ TcpSegment *seg = (TcpSegment *) PoolGet(segment_pool[idx]);
+
+ SCLogDebug("segment_pool[%u]->empty_stack_size %u, segment_pool[%u]->alloc_"
+ "list_size %u, alloc %u", idx, segment_pool[idx]->empty_stack_size,
+ idx, segment_pool[idx]->alloc_stack_size,
+ segment_pool[idx]->allocated);
+ SCMutexUnlock(&segment_pool_mutex[idx]);
+
+ SCLogDebug("seg we return is %p", seg);
+ if (seg == NULL) {
+ SCLogDebug("segment_pool[%u]->empty_stack_size %u, "
+ "alloc %u", idx, segment_pool[idx]->empty_stack_size,
+ segment_pool[idx]->allocated);
+ /* Increment the counter to show that we are not able to serve the
+ segment request due to memcap limit */
+ StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap);
+ } else {
+ seg->flags = stream_config.segment_init_flags;
+ seg->next = NULL;
+ seg->prev = NULL;
+ }
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_cnt_mutex);
+ segment_pool_cnt++;
+ SCMutexUnlock(&segment_pool_cnt_mutex);
+#endif
+
+ return seg;
+}
+
+/**
+ * \brief Trigger RAW stream reassembly
+ *
+ * Used by AppLayerTriggerRawStreamReassembly to trigger RAW stream
+ * reassembly from the applayer, for example upon completion of a
+ * HTTP request.
+ *
+ * Works by setting a flag in the TcpSession that is unset as soon
+ * as it's checked. Since everything happens when operating under
+ * a single lock period, no side effects are expected.
+ *
+ * \param ssn TcpSession
+ */
+void StreamTcpReassembleTriggerRawReassembly(TcpSession *ssn)
+{
+#ifdef DEBUG
+ BUG_ON(ssn == NULL);
+#endif
+
+ if (ssn != NULL) {
+ SCLogDebug("flagged ssn %p for immediate raw reassembly", ssn);
+ ssn->flags |= STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY;
+ }
+}
+
+#ifdef UNITTESTS
+/** unit tests and it's support functions below */
+
+static int UtTestSmsg(StreamMsg *smsg, const uint8_t *buf, uint32_t buf_len)
+{
+ if (smsg == NULL)
+ return 0;
+
+ if (smsg->data_len != buf_len) {
+ return 0;
+ }
+
+ if (!(memcmp(buf, smsg->data, buf_len) == 0)) {
+ printf("data is not what we expected:\nExpected:\n");
+ PrintRawDataFp(stdout, (uint8_t *)buf, buf_len);
+ printf("Got:\n");
+ PrintRawDataFp(stdout, smsg->data, smsg->data_len);
+ return 0;
+ }
+ return 1;
+}
+
+static uint32_t UtSsnSmsgCnt(TcpSession *ssn, uint8_t direction)
+{
+ uint32_t cnt = 0;
+ StreamMsg *smsg = (direction == STREAM_TOSERVER) ?
+ ssn->toserver_smsg_head :
+ ssn->toclient_smsg_head;
+ while (smsg) {
+ cnt++;
+ smsg = smsg->next;
+ }
+ return cnt;
+}
+
+/** \brief The Function tests the reassembly engine working for different
+ * OSes supported. It includes all the OS cases and send
+ * crafted packets to test the reassembly.
+ *
+ * \param stream The stream which will contain the reassembled segments
+ */
+
+static int StreamTcpReassembleStreamTest(TcpStream *stream)
+{
+
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->tcph->th_seq = htonl(12);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ p->tcph->th_seq = htonl(16);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, 4); /*CCC*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x44, 1, 4); /*D*/
+ p->tcph->th_seq = htonl(22);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x45, 2, 4); /*EE*/
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x46, 3, 4); /*FFF*/
+ p->tcph->th_seq = htonl(27);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x47, 2, 4); /*GG*/
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x48, 2, 4); /*HH*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x49, 1, 4); /*I*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 4, 4); /*JJJJ*/
+ p->tcph->th_seq = htonl(13);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4b, 3, 4); /*KKK*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4c, 3, 4); /*LLL*/
+ p->tcph->th_seq = htonl(21);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4d, 3, 4); /*MMM*/
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4e, 1, 4); /*N*/
+ p->tcph->th_seq = htonl(28);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4f, 1, 4); /*O*/
+ p->tcph->th_seq = htonl(31);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x50, 1, 4); /*P*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x51, 2, 4); /*QQ*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x30, 1, 4); /*0*/
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+
+ SCFree(p);
+ return 1;
+}
+
+/** \brief The Function to create the packet with given payload, which is used
+ * to test the reassembly of the engine.
+ *
+ * \param payload The variable used to store the payload contents of the
+ * current packet.
+ * \param value The value which current payload will have for this packet
+ * \param payload_len The length of the filed payload for current packet.
+ * \param len Length of the payload array
+ */
+
+void StreamTcpCreateTestPacket(uint8_t *payload, uint8_t value,
+ uint8_t payload_len, uint8_t len)
+{
+ uint8_t i;
+ for (i = 0; i < payload_len; i++)
+ payload[i] = value;
+ for (; i < len; i++)
+ payload = NULL;
+}
+
+/** \brief The Function Checks the reassembled stream contents against predefined
+ * stream contents according to OS policy used.
+ *
+ * \param stream_policy Predefined value of stream for different OS policies
+ * \param stream Reassembled stream returned from the reassembly functions
+ */
+
+int StreamTcpCheckStreamContents(uint8_t *stream_policy, uint16_t sp_size, TcpStream *stream)
+{
+ TcpSegment *temp;
+ uint16_t i = 0;
+ uint8_t j;
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ TcpSegment *temp1;
+ for (temp1 = stream->seg_list; temp1 != NULL; temp1 = temp1->next)
+ PrintRawDataFp(stdout, temp1->payload, temp1->payload_len);
+
+ PrintRawDataFp(stdout, stream_policy, sp_size);
+ }
+#endif
+
+ for (temp = stream->seg_list; temp != NULL; temp = temp->next) {
+ j = 0;
+ for (; j < temp->payload_len; j++) {
+ SCLogDebug("i %"PRIu16", len %"PRIu32", stream %"PRIx32" and temp is %"PRIx8"",
+ i, temp->payload_len, stream_policy[i], temp->payload[j]);
+
+ if (stream_policy[i] == temp->payload[j]) {
+ i++;
+ continue;
+ } else
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/** \brief The Function Checks the Stream Queue contents against predefined
+ * stream contents.
+ *
+ * \param stream_contents Predefined value of stream contents
+ * \param stream Queue which has the stream contents
+ *
+ * \retval On success the function returns 1, on failure 0.
+ */
+static int StreamTcpCheckChunks (TcpSession *ssn, uint8_t *stream_contents)
+{
+ SCEnter();
+
+ StreamMsg *msg;
+ uint16_t i = 0;
+ uint8_t j;
+ uint8_t cnt = 0;
+
+ if (ssn == NULL) {
+ printf("ssn == NULL, ");
+ SCReturnInt(0);
+ }
+
+ if (ssn->toserver_smsg_head == NULL) {
+ printf("ssn->toserver_smsg_head == NULL, ");
+ SCReturnInt(0);
+ }
+
+ msg = ssn->toserver_smsg_head;
+ while(msg != NULL) {
+ cnt++;
+ j = 0;
+ for (; j < msg->data_len; j++) {
+ SCLogDebug("i is %" PRIu32 " and len is %" PRIu32 " and temp is %" PRIx32 "", i, msg->data_len, msg->data[j]);
+
+ if (stream_contents[i] == msg->data[j]) {
+ i++;
+ continue;
+ } else {
+ SCReturnInt(0);
+ }
+ }
+ msg = msg->next;
+ }
+ SCReturnInt(1);
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats before the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+static int StreamTcpTestStartsBeforeListSegment(TcpStream *stream) {
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ p->tcph->th_seq = htonl(16);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x44, 1, 4); /*D*/
+ p->tcph->th_seq = htonl(22);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x45, 2, 4); /*EE*/
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 4, 4); /*JJJJ*/
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCLogDebug("sending segment with SEQ 21, len 3");
+ StreamTcpCreateTestPacket(payload, 0x4c, 3, 4); /*LLL*/
+ p->tcph->th_seq = htonl(21);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4d, 3, 4); /*MMM*/
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats at the same seq no. as the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+static int StreamTcpTestStartsAtSameListSegment(TcpStream *stream)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, 4); /*CCC*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x48, 2, 4); /*HH*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x49, 1, 4); /*I*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4b, 3, 4); /*KKK*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4c, 4, 4); /*LLLL*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x50, 1, 4); /*P*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x51, 2, 4); /*QQ*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats after the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+
+static int StreamTcpTestStartsAfterListSegment(TcpStream *stream)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ p->tcph->th_seq = htonl(12);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x46, 3, 4); /*FFF*/
+ p->tcph->th_seq = htonl(27);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x47, 2, 4); /*GG*/
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 2, 4); /*JJ*/
+ p->tcph->th_seq = htonl(13);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4f, 1, 4); /*O*/
+ p->tcph->th_seq = htonl(31);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4e, 1, 4); /*N*/
+ p->tcph->th_seq = htonl(28);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest01(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_bsd[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_bsd,sizeof(stream_before_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and BSD policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest02(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_bsd[8] = {0x43, 0x43, 0x43, 0x4c, 0x48, 0x48,
+ 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_bsd, sizeof(stream_same_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest03(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_bsd[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_bsd, sizeof(stream_after_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and BSD policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest04(void)
+{
+ TcpStream stream;
+ uint8_t stream_bsd[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly: ");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_bsd, sizeof(stream_bsd), &stream) == 0) {
+ printf("failed in stream matching: ");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and VISTA policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest05(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_vista[10] = {0x4a, 0x41, 0x42, 0x4a, 0x4c, 0x44,
+ 0x4c, 0x4d, 0x45, 0x45};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_vista, sizeof(stream_before_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and VISTA policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest06(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_vista[8] = {0x43, 0x43, 0x43, 0x4c, 0x48, 0x48,
+ 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_vista, sizeof(stream_same_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest07(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_vista[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_vista, sizeof(stream_after_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and VISTA policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest08(void)
+{
+ TcpStream stream;
+ uint8_t stream_vista[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x42, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x44, 0x4c, 0x4d, 0x45, 0x45,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_vista, sizeof(stream_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest09(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_linux[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_linux, sizeof(stream_before_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and LINUX policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest10(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_linux[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_linux, sizeof(stream_same_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest11(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_linux[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_linux, sizeof(stream_after_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and LINUX policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest12(void)
+{
+ TcpStream stream;
+ uint8_t stream_linux[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_linux, sizeof(stream_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and OLD_LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest13(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_old_linux[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_old_linux, sizeof(stream_before_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and OLD_LINUX policy is
+ * used to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest14(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_old_linux[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_old_linux, sizeof(stream_same_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and OLD_LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest15(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_old_linux[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_old_linux, sizeof(stream_after_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and OLD_LINUX policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest16(void)
+{
+ TcpStream stream;
+ uint8_t stream_old_linux[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_old_linux, sizeof(stream_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and SOLARIS policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest17(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_solaris[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_solaris, sizeof(stream_before_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and SOLARIS policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest18(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_solaris[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_solaris, sizeof(stream_same_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and SOLARIS policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest19(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_solaris[8] = {0x41, 0x4a, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_solaris, sizeof(stream_after_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and SOLARIS policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest20(void)
+{
+ TcpStream stream;
+ uint8_t stream_solaris[25] = {0x30, 0x41, 0x4a, 0x4a, 0x4a, 0x42, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_solaris, sizeof(stream_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and LAST policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest21(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_last[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_last, sizeof(stream_before_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and LAST policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest22(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_last[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x50, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_last, sizeof(stream_same_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and LAST policy is used to reassemble
+ * segments.
+ */
+static int StreamTcpReassembleTest23(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_last[8] = {0x41, 0x4a, 0x4a, 0x46, 0x4e, 0x46, 0x47, 0x4f};
+ memset(&stream, 0, sizeof (TcpStream));
+
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_last, sizeof(stream_after_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and LAST policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest24(void)
+{
+ int ret = 0;
+ TcpStream stream;
+ uint8_t stream_last[25] = {0x30, 0x41, 0x4a, 0x4a, 0x4a, 0x4a, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x4e, 0x46, 0x47, 0x4f, 0x50, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+ if (StreamTcpCheckStreamContents(stream_last, sizeof(stream_last), &stream) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/** \brief The Function to test the missed packets handling with given payload,
+ * which is used to test the reassembly of the engine.
+ *
+ * \param stream Stream which contain the packets
+ * \param seq Sequence number of the packet
+ * \param ack Acknowledgment number of the packet
+ * \param payload The variable used to store the payload contents of the
+ * current packet.
+ * \param len The length of the payload for current packet.
+ * \param th_flag The TCP flags
+ * \param flowflags The packet flow direction
+ * \param state The TCP session state
+ *
+ * \retval On success it returns 0 and on failure it return -1.
+ */
+
+static int StreamTcpTestMissedPacket (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, uint32_t seq, uint32_t ack, uint8_t *payload,
+ uint16_t len, uint8_t th_flags, uint8_t flowflags, uint8_t state)
+{
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return -1;
+ Flow f;
+ TCPHdr tcph;
+ Port sp;
+ Port dp;
+ struct in_addr in;
+ ThreadVars tv;
+ PacketQueue pq;
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ sp = 200;
+ dp = 220;
+
+ FLOW_INITIALIZE(&f);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1) {
+ SCFree(p);
+ return -1;
+ }
+ f.src.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.5", &in) != 1) {
+ SCFree(p);
+ return -1;
+ }
+ f.dst.addr_data32[0] = in.s_addr;
+ f.flags |= FLOW_IPV4;
+ f.sp = sp;
+ f.dp = dp;
+ f.protoctx = ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(seq);
+ tcph.th_ack = htonl(ack);
+ tcph.th_flags = th_flags;
+ p->tcph = &tcph;
+ p->flowflags = flowflags;
+
+ p->payload = payload;
+ p->payload_len = len;
+ ssn->state = state;
+
+ TcpStream *s = NULL;
+ if (flowflags & FLOW_PKT_TOSERVER) {
+ s = &ssn->server;
+ } else {
+ s = &ssn->client;
+ }
+
+ SCMutexLock(&f.m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, ssn, s, p, &pq) == -1) {
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return -1;
+ }
+
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the starting of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest25 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+ ssn.server.next_seq = 14;
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 7;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the middle of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest26 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+ memset(&ssn, 0, sizeof (TcpSession));
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 13;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the end of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest27 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+ memset(&ssn, 0, sizeof (TcpSession));
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 13;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * in the starting of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest28 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpInitConfig(TRUE);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 6;
+ ssn.server.isn = 6;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (1): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (2): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (4): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly (5): ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching (6): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * in the middle of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest29 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = 9;
+ ssn.server.isn = 9;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 15;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * at the end of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest30 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[6] = {0x41, 0x41, 0x42, 0x42, 0x42, 0x00};
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+
+ StreamTcpInitConfig(TRUE);
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ th_flag = TH_FIN|TH_ACK;
+ seq = 18;
+ ack = 20;
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x00, 1, 4);
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flag, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test to reassemble the packets using the fast track method, as most
+ * packets arrives in order.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest31 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+
+ ssn.server.ra_raw_base_seq = 9;
+ ssn.server.isn = 9;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 15;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 16;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, 5, &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ if (ssn.server.seg_list_tail->seq != 16) {
+ printf("failed in fast track handling: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+static int StreamTcpReassembleTest32(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ uint8_t ret = 0;
+ uint8_t check_contents[35] = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x43, 0x43, 0x43};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t payload[20] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x41, 10, 20); /*AA*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x42, 10, 20); /*BB*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(40);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x43, 10, 20); /*CC*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 20;
+ StreamTcpCreateTestPacket(payload, 0x41, 20, 20); /*AA*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ if (StreamTcpCheckStreamContents(check_contents, 35, &stream) != 0) {
+ ret = 1;
+ } else {
+ printf("failed in stream matching: ");
+ }
+
+
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return ret;
+}
+
+static int StreamTcpReassembleTest33(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(40);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 30;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+static int StreamTcpReassembleTest34(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(857961230);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 304;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857961534);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857963582);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857960946);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 56 condition */
+static int StreamTcpReassembleTest35(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(2257022155UL);
+ p->tcph->th_ack = htonl(1374943142);
+ p->payload_len = 142;
+ stream.last_ack = 2257022285UL;
+ stream.ra_raw_base_seq = 2257022172UL;
+ stream.ra_app_base_seq = 2257022172UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(2257022285UL);
+ p->tcph->th_ack = htonl(1374943142);
+ p->payload_len = 34;
+ stream.last_ack = 2257022285UL;
+ stream.ra_raw_base_seq = 2257022172UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 57 condition */
+static int StreamTcpReassembleTest36(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(1549588966);
+ p->tcph->th_ack = htonl(4162241372UL);
+ p->payload_len = 204;
+ stream.last_ack = 1549589007;
+ stream.ra_raw_base_seq = 1549589101;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(1549589007);
+ p->tcph->th_ack = htonl(4162241372UL);
+ p->payload_len = 23;
+ stream.last_ack = 1549589007;
+ stream.ra_raw_base_seq = 1549589101;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 76 condition */
+static int StreamTcpReassembleTest37(void)
+{
+ TcpSession ssn;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ uint8_t packet[1460] = "";
+ PacketQueue pq;
+ ThreadVars tv;
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ memset(&stream, 0, sizeof (TcpStream));
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+ stream.os_policy = OS_POLICY_BSD;
+
+ p->tcph->th_seq = htonl(3061088537UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ /* pre base_seq, so should be rejected */
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) != -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(3061089928UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(3061091319UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/**
+ * \test Test to make sure we don't send the smsg from toclient to app layer
+ * until the app layer protocol has been detected and one smsg from
+ * toserver side has been sent to app layer.
+ *
+ * Unittest modified by commit -
+ *
+ * commit bab1636377bb4f1b7b889f4e3fd594795085eaa4
+ * Author: Anoop Saldanha <anoopsaldanha@gmail.com>
+ * Date: Fri Feb 15 18:58:33 2013 +0530
+ *
+ * Improved app protocol detection.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpReassembleTest38 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ Port sp;
+ Port dp;
+ struct in_addr in;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf2[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf1[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ FLOW_INITIALIZE(&f);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1)
+ goto end;
+ f.src.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.5", &in) != 1)
+ goto end;
+ f.dst.addr_data32[0] = in.s_addr;
+ sp = 200;
+ dp = 220;
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 60;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 60;
+ f.alproto = ALPROTO_UNKNOWN;
+
+ f.flags |= FLOW_IPV4;
+ f.sp = sp;
+ f.dp = dp;
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("there should one stream smsg in the queue (6): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that we don't return the segments until the app
+ * layer proto has been detected and after that remove the processed
+ * segments.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest39 (void)
+{
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* handshake */
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* partial request */
+ uint8_t request1[] = { 0x47, 0x45, };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+
+ /* response ack against partial request */
+ p->tcph->th_ack = htonl(3);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* complete partial request */
+ uint8_t request2[] = {
+ 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(3);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ /* response - acking */
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 9\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 10\n");
+ goto end;
+ }
+
+ /* response - acking the request again*/
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 11\n");
+ goto end;
+ }
+
+ /*** New Request ***/
+
+ /* partial request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 12\n");
+ goto end;
+ }
+
+
+ /* response ack against partial request */
+ p->tcph->th_ack = htonl(90);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 13\n");
+ goto end;
+ }
+
+ /* complete request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(90);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->client.seg_list->next->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 14\n");
+ goto end;
+ }
+
+ /* response ack against second partial request */
+ p->tcph->th_ack = htonl(175);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->client.seg_list->next->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+ if (ssn->toserver_smsg_head == NULL ||
+ ssn->toserver_smsg_head->next == NULL ||
+ ssn->toserver_smsg_head->next->next != NULL ||
+ ssn->toclient_smsg_head == NULL ||
+ ssn->toclient_smsg_head->next != NULL) {
+ printf("failure 16\n");
+ goto end;
+ }
+
+ StreamMsgReturnListToPool(ssn->toserver_smsg_head);
+ ssn->toserver_smsg_head = ssn->toserver_smsg_tail = NULL;
+ StreamMsgReturnListToPool(ssn->toclient_smsg_head);
+ ssn->toclient_smsg_head = ssn->toclient_smsg_tail = NULL;
+
+ /* response acking a request */
+ p->tcph->th_ack = htonl(175);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+ /* request acking a response */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(175);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+
+ ret = 1;
+end:
+ StreamTcpThreadDeinit(&tv, (void *)stt);
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f.m);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that we sent all the segments from the initial
+ * segments to app layer until we have detected the app layer proto.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest40 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 130);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "P";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "O";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "S";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "T \r\n";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 10;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 10;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(10);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.client;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " processed any smsg from toserver side till yet (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(11);
+ s = &ssn.server;
+ ssn.server.last_ack = 11;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf3;
+ p->payload_len = httplen3;
+ tcph.th_seq = htonl(11);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+ ssn.client.last_ack = 55;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (5): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(55);
+ tcph.th_ack = htonl(12);
+ s = &ssn.server;
+ ssn.server.last_ack = 12;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (6): ");
+ goto end;
+ }
+
+ /* check is have the segment in the list and flagged or not */
+ if (ssn.client.seg_list == NULL ||
+ (ssn.client.seg_list->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
+ {
+ printf("the list is NULL or the processed segment has not been flaged (7): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf4;
+ p->payload_len = httplen4;
+ tcph.th_seq = htonl(12);
+ tcph.th_ack = htonl(100);
+ s = &ssn.client;
+ ssn.client.last_ack = 100;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (10): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(100);
+ tcph.th_ack = htonl(13);
+ s = &ssn.server;
+ ssn.server.last_ack = 13;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (11): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf5;
+ p->payload_len = httplen5;
+ tcph.th_seq = htonl(13);
+ tcph.th_ack = htonl(145);
+ s = &ssn.client;
+ ssn.client.last_ack = 145;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (14): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(145);
+ tcph.th_ack = htonl(16);
+ s = &ssn.server;
+ ssn.server.last_ack = 16;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (15): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) == 0) {
+ printf("there should be a stream smsgs in the queue, as we have detected"
+ " the app layer protocol and one smsg from toserver side has "
+ "been sent (16): ");
+ goto end;
+ }
+
+ if (f->alproto != ALPROTO_HTTP) {
+ printf("app layer proto has not been detected (18): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest43 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
+ "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
+ "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
+ "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
+ "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
+ "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
+ "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
+ "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
+ "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
+ "aG9uZT\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 600;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 600;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(10);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " processed any smsg from toserver side till yet (4): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(55);
+ tcph.th_ack = htonl(44);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (5): ");
+ goto end;
+ }
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn.client)) {
+ printf("app layer detected flag isn't set, it should be (8): ");
+ goto end;
+ }
+
+ /* This packets induces a packet gap and also shows why we need to
+ process the current segment completely, even if it results in sending more
+ than one smsg to the app layer. If we don't send more than one smsg in
+ this case, then the first segment of lentgh 34 bytes will be sent to
+ app layer and protocol can not be detected in that message and moreover
+ the segment lentgh is less than the max. signature size for protocol
+ detection, so this will keep looping !! */
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf3;
+ p->payload_len = httplen3;
+ tcph.th_seq = htonl(54);
+ tcph.th_ack = htonl(100);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (9): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " detected the app layer protocol till yet (10): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(100);
+ tcph.th_ack = htonl(53);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (11): ");
+ goto end;
+ }
+ /* the flag should be set, as the smsg scanned size has crossed the max.
+ signature size for app proto detection */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn.client)) {
+ printf("app layer detected flag is not set, it should be (14): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test Test the memcap incrementing/decrementing and memcap check */
+static int StreamTcpReassembleTest44(void)
+{
+ uint8_t ret = 0;
+ StreamTcpInitConfig(TRUE);
+ uint32_t memuse = SC_ATOMIC_GET(ra_memuse);
+
+ StreamTcpReassembleIncrMemuse(500);
+ if (SC_ATOMIC_GET(ra_memuse) != (memuse+500)) {
+ printf("failed in incrementing the memory");
+ goto end;
+ }
+
+ StreamTcpReassembleDecrMemuse(500);
+ if (SC_ATOMIC_GET(ra_memuse) != memuse) {
+ printf("failed in decrementing the memory");
+ goto end;
+ }
+
+ if (StreamTcpReassembleCheckMemcap(500) != 1) {
+ printf("failed in validating the memcap");
+ goto end;
+ }
+
+ if (StreamTcpReassembleCheckMemcap((memuse + stream_config.reassembly_memcap)) != 0) {
+ printf("failed in validating the memcap");
+ goto end;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ if (SC_ATOMIC_GET(ra_memuse) != 0) {
+ printf("failed in clearing the memory");
+ goto end;
+ }
+
+ ret = 1;
+ return ret;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that reassembly_depth is enforced.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest45 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 60;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 60;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ /* set the default value of reassembly depth, as there is no config file */
+ stream_config.reassembly_depth = httplen1 + 1;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toclient packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ printf("there shouldn't be a noreassembly flag be set: ");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = httplen1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ printf("there shouldn't be a noreassembly flag be set: ");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = httplen1;
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (!(s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("the noreassembly flags should be set, "
+ "p.payload_len %"PRIu16" stream_config.reassembly_"
+ "depth %"PRIu32": ", p->payload_len,
+ stream_config.reassembly_depth);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \test Test the undefined config value of reassembly depth.
+ * the default value of 0 will be loaded and stream will be reassembled
+ * until the session ended
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest46 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ ThreadVars tv;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 60;
+ ssn.server.next_seq = ssn.server.isn;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 60;
+ ssn.client.next_seq = ssn.client.isn;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ stream_config.reassembly_depth = 0;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toclient packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("there shouldn't be any no reassembly flag be set \n");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = httplen1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("there shouldn't be any no reassembly flag be set \n");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10 + httplen1);
+ tcph.th_ack = htonl(20 + httplen1);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("the no_reassembly flags should not be set, "
+ "p->payload_len %"PRIu16" stream_config.reassembly_"
+ "depth %"PRIu32": ", p->payload_len,
+ stream_config.reassembly_depth);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \test Test to make sure we detect the sequence wrap around and continue
+ * stream reassembly properly.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest47 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ ThreadVars tv;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 0);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 0);
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "GET /EVILSUFF HTTP/1.1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 572799781UL;
+ ssn.server.isn = 572799781UL;
+ ssn.server.last_ack = 572799782UL;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 4294967289UL;
+ ssn.client.isn = 4294967289UL;
+ ssn.client.last_ack = 21;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ ssn.state = TCP_ESTABLISHED;
+ TcpStream *s = NULL;
+ uint8_t cnt = 0;
+
+ SCMutexLock(&f->m);
+ for (cnt=0; cnt < httplen1; cnt++) {
+ tcph.th_seq = htonl(ssn.client.isn + 1 + cnt);
+ tcph.th_ack = htonl(572799782UL);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = &httpbuf1[cnt];
+ p->payload_len = 1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver "
+ "packet\n");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = NULL;
+ p->payload_len = 0;
+ tcph.th_seq = htonl(572799782UL);
+ tcph.th_ack = htonl(ssn.client.isn + 1 + cnt);
+ tcph.th_flags = TH_ACK;
+ p->tcph = &tcph;
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver "
+ "packet\n");
+ goto end;
+ }
+ }
+
+ if (f->alproto != ALPROTO_HTTP) {
+ printf("App layer protocol (HTTP) should have been detected\n");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test 3 in order segments in inline reassembly */
+static int StreamTcpReassembleInlineTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload[] = "AAAAABBBBBCCCCC";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload, 15) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly.
+ */
+static int StreamTcpReassembleInlineTest02(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 20) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge)
+ */
+static int StreamTcpReassembleInlineTest03(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 15;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "BBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message 1: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected two stream messages: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 15) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge) with small packet overlap.
+ */
+static int StreamTcpReassembleInlineTest04(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 16;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "ABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 16) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs */
+static int StreamTcpReassembleInlineTest05(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs, with filling the GAP later */
+static int StreamTcpReassembleInlineTest06(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected two stream messages: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 20) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs, with filling the GAP later, small
+ * window */
+static int StreamTcpReassembleInlineTest07(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 16;
+
+ uint8_t stream_payload1[] = "ABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 6) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 16) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge). Test if the first segment is
+ * removed from the list.
+ */
+static int StreamTcpReassembleInlineTest08(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 15;
+ ssn.client.flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "BBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 16) {
+ printf("ra_raw_base_seq %"PRIu32", expected 16: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 15) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (ssn.client.seg_list->seq != 7) {
+ printf("expected segment 2 (seq 7) to be first in the list, got seq %"PRIu32": ", ssn.client.seg_list->seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge). Test if the first segment is
+ * removed from the list.
+ */
+static int StreamTcpReassembleInlineTest09(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 20;
+ ssn.client.flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(17);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected 2 stream message2, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 11) {
+ printf("ra_raw_base_seq %"PRIu32", expected 11: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ /* close the GAP and see if we properly reassemble and update ra_base_seq */
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected 3 stream messages: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 20) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (ssn.client.seg_list->seq != 2) {
+ printf("expected segment 1 (seq 2) to be first in the list, got seq %"PRIu32": ", ssn.client.seg_list->seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test App Layer reassembly.
+ */
+static int StreamTcpReassembleInlineTest10(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow *f = NULL;
+ Packet *p = NULL;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.server, 1);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ uint8_t stream_payload1[] = "GE";
+ uint8_t stream_payload2[] = "T /";
+ uint8_t stream_payload3[] = "HTTP/1.0\r\n\r\n";
+
+ p = UTHBuildPacketReal(stream_payload3, 12, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(7);
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 2, stream_payload1, 2) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ ssn.server.next_seq = 4;
+
+ int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleAppLayer failed: ");
+ goto end;
+ }
+
+ /* ssn.server.ra_app_base_seq should be isn here. */
+ if (ssn.server.ra_app_base_seq != 1 || ssn.server.ra_app_base_seq != ssn.server.isn) {
+ printf("expected ra_app_base_seq 1, got %u: ", ssn.server.ra_app_base_seq);
+ goto end;
+ }
+
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 4, stream_payload2, 3) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 7, stream_payload3, 12) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.server.next_seq = 19;
+
+ r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleAppLayer failed: ");
+ goto end;
+ }
+
+ if (ssn.server.ra_app_base_seq != 18) {
+ printf("expected ra_app_base_seq 18, got %u: ", ssn.server.ra_app_base_seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test test insert with overlap
+ */
+static int StreamTcpReassembleInsertTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 14, 'D', 2) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 16, 'D', 6) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 5: ");
+ goto end;
+ }
+ ssn.client.next_seq = 21;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 20) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test test insert with overlaps
+ */
+static int StreamTcpReassembleInsertTest02(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+
+ int i;
+ for (i = 2; i < 10; i++) {
+ int len;
+ len = i % 2;
+ if (len == 0)
+ len = 1;
+ int seq;
+ seq = i * 10;
+ if (seq < 2)
+ seq = 2;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, seq, 'A', len) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'B', 1024) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test test insert with overlaps
+ */
+static int StreamTcpReassembleInsertTest03(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 1024) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+
+ int i;
+ for (i = 2; i < 10; i++) {
+ int len;
+ len = i % 2;
+ if (len == 0)
+ len = 1;
+ int seq;
+ seq = i * 10;
+ if (seq < 2)
+ seq = 2;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, seq, 'B', len) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ }
+ ret = 1;
+end:
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+/** \brief The Function Register the Unit tests to test the reassembly engine
+ * for various OS policies.
+ */
+
+void StreamTcpReassembleRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpReassembleTest01 -- BSD OS Before Reassembly Test", StreamTcpReassembleTest01, 1);
+ UtRegisterTest("StreamTcpReassembleTest02 -- BSD OS At Same Reassembly Test", StreamTcpReassembleTest02, 1);
+ UtRegisterTest("StreamTcpReassembleTest03 -- BSD OS After Reassembly Test", StreamTcpReassembleTest03, 1);
+ UtRegisterTest("StreamTcpReassembleTest04 -- BSD OS Complete Reassembly Test", StreamTcpReassembleTest04, 1);
+ UtRegisterTest("StreamTcpReassembleTest05 -- VISTA OS Before Reassembly Test", StreamTcpReassembleTest05, 1);
+ UtRegisterTest("StreamTcpReassembleTest06 -- VISTA OS At Same Reassembly Test", StreamTcpReassembleTest06, 1);
+ UtRegisterTest("StreamTcpReassembleTest07 -- VISTA OS After Reassembly Test", StreamTcpReassembleTest07, 1);
+ UtRegisterTest("StreamTcpReassembleTest08 -- VISTA OS Complete Reassembly Test", StreamTcpReassembleTest08, 1);
+ UtRegisterTest("StreamTcpReassembleTest09 -- LINUX OS Before Reassembly Test", StreamTcpReassembleTest09, 1);
+ UtRegisterTest("StreamTcpReassembleTest10 -- LINUX OS At Same Reassembly Test", StreamTcpReassembleTest10, 1);
+ UtRegisterTest("StreamTcpReassembleTest11 -- LINUX OS After Reassembly Test", StreamTcpReassembleTest11, 1);
+ UtRegisterTest("StreamTcpReassembleTest12 -- LINUX OS Complete Reassembly Test", StreamTcpReassembleTest12, 1);
+ UtRegisterTest("StreamTcpReassembleTest13 -- LINUX_OLD OS Before Reassembly Test", StreamTcpReassembleTest13, 1);
+ UtRegisterTest("StreamTcpReassembleTest14 -- LINUX_OLD At Same Reassembly Test", StreamTcpReassembleTest14, 1);
+ UtRegisterTest("StreamTcpReassembleTest15 -- LINUX_OLD OS After Reassembly Test", StreamTcpReassembleTest15, 1);
+ UtRegisterTest("StreamTcpReassembleTest16 -- LINUX_OLD OS Complete Reassembly Test", StreamTcpReassembleTest16, 1);
+ UtRegisterTest("StreamTcpReassembleTest17 -- SOLARIS OS Before Reassembly Test", StreamTcpReassembleTest17, 1);
+ UtRegisterTest("StreamTcpReassembleTest18 -- SOLARIS At Same Reassembly Test", StreamTcpReassembleTest18, 1);
+ UtRegisterTest("StreamTcpReassembleTest19 -- SOLARIS OS After Reassembly Test", StreamTcpReassembleTest19, 1);
+ UtRegisterTest("StreamTcpReassembleTest20 -- SOLARIS OS Complete Reassembly Test", StreamTcpReassembleTest20, 1);
+ UtRegisterTest("StreamTcpReassembleTest21 -- LAST OS Before Reassembly Test", StreamTcpReassembleTest21, 1);
+ UtRegisterTest("StreamTcpReassembleTest22 -- LAST OS At Same Reassembly Test", StreamTcpReassembleTest22, 1);
+ UtRegisterTest("StreamTcpReassembleTest23 -- LAST OS After Reassembly Test", StreamTcpReassembleTest23, 1);
+ UtRegisterTest("StreamTcpReassembleTest24 -- LAST OS Complete Reassembly Test", StreamTcpReassembleTest24, 1);
+ UtRegisterTest("StreamTcpReassembleTest25 -- Gap at Start Reassembly Test", StreamTcpReassembleTest25, 1);
+ UtRegisterTest("StreamTcpReassembleTest26 -- Gap at middle Reassembly Test", StreamTcpReassembleTest26, 1);
+ UtRegisterTest("StreamTcpReassembleTest27 -- Gap at after Reassembly Test", StreamTcpReassembleTest27, 1);
+ UtRegisterTest("StreamTcpReassembleTest28 -- Gap at Start IDS missed packet Reassembly Test", StreamTcpReassembleTest28, 1);
+ UtRegisterTest("StreamTcpReassembleTest29 -- Gap at Middle IDS missed packet Reassembly Test", StreamTcpReassembleTest29, 1);
+ UtRegisterTest("StreamTcpReassembleTest30 -- Gap at End IDS missed packet Reassembly Test", StreamTcpReassembleTest30, 1);
+ UtRegisterTest("StreamTcpReassembleTest31 -- Fast Track Reassembly Test", StreamTcpReassembleTest31, 1);
+ UtRegisterTest("StreamTcpReassembleTest32 -- Bug test", StreamTcpReassembleTest32, 1);
+ UtRegisterTest("StreamTcpReassembleTest33 -- Bug test", StreamTcpReassembleTest33, 1);
+ UtRegisterTest("StreamTcpReassembleTest34 -- Bug test", StreamTcpReassembleTest34, 1);
+ UtRegisterTest("StreamTcpReassembleTest35 -- Bug56 test", StreamTcpReassembleTest35, 1);
+ UtRegisterTest("StreamTcpReassembleTest36 -- Bug57 test", StreamTcpReassembleTest36, 1);
+ UtRegisterTest("StreamTcpReassembleTest37 -- Bug76 test", StreamTcpReassembleTest37, 1);
+ UtRegisterTest("StreamTcpReassembleTest38 -- app proto test", StreamTcpReassembleTest38, 1);
+ UtRegisterTest("StreamTcpReassembleTest39 -- app proto test", StreamTcpReassembleTest39, 1);
+ UtRegisterTest("StreamTcpReassembleTest40 -- app proto test", StreamTcpReassembleTest40, 1);
+ UtRegisterTest("StreamTcpReassembleTest43 -- min smsg size test", StreamTcpReassembleTest43, 1);
+ UtRegisterTest("StreamTcpReassembleTest44 -- Memcap Test", StreamTcpReassembleTest44, 1);
+ UtRegisterTest("StreamTcpReassembleTest45 -- Depth Test", StreamTcpReassembleTest45, 1);
+ UtRegisterTest("StreamTcpReassembleTest46 -- Depth Test", StreamTcpReassembleTest46, 1);
+ UtRegisterTest("StreamTcpReassembleTest47 -- TCP Sequence Wraparound Test", StreamTcpReassembleTest47, 1);
+
+ UtRegisterTest("StreamTcpReassembleInlineTest01 -- inline RAW ra", StreamTcpReassembleInlineTest01, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest02 -- inline RAW ra 2", StreamTcpReassembleInlineTest02, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest03 -- inline RAW ra 3", StreamTcpReassembleInlineTest03, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest04 -- inline RAW ra 4", StreamTcpReassembleInlineTest04, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest05 -- inline RAW ra 5 GAP", StreamTcpReassembleInlineTest05, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest06 -- inline RAW ra 6 GAP", StreamTcpReassembleInlineTest06, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest07 -- inline RAW ra 7 GAP", StreamTcpReassembleInlineTest07, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest08 -- inline RAW ra 8 cleanup", StreamTcpReassembleInlineTest08, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest09 -- inline RAW ra 9 GAP cleanup", StreamTcpReassembleInlineTest09, 1);
+
+ UtRegisterTest("StreamTcpReassembleInlineTest10 -- inline APP ra 10", StreamTcpReassembleInlineTest10, 1);
+
+ UtRegisterTest("StreamTcpReassembleInsertTest01 -- insert with overlap", StreamTcpReassembleInsertTest01, 1);
+ UtRegisterTest("StreamTcpReassembleInsertTest02 -- insert with overlap", StreamTcpReassembleInsertTest02, 1);
+ UtRegisterTest("StreamTcpReassembleInsertTest03 -- insert with overlap", StreamTcpReassembleInsertTest03, 1);
+
+ StreamTcpInlineRegisterTests();
+ StreamTcpUtilRegisterTests();
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/stream-tcp-reassemble.h b/framework/src/suricata/src/stream-tcp-reassemble.h
new file mode 100644
index 00000000..b6e798ce
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-reassemble.h
@@ -0,0 +1,110 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef __STREAM_TCP_REASSEMBLE_H__
+#define __STREAM_TCP_REASSEMBLE_H__
+
+#include "stream-tcp-private.h"
+#include "stream.h"
+#include "app-layer-detect-proto.h"
+#include "stream-tcp-private.h"
+
+/** Supported OS list and default OS policy is BSD */
+enum
+{
+ OS_POLICY_NONE = 1,
+ OS_POLICY_BSD,
+ OS_POLICY_BSD_RIGHT,
+ OS_POLICY_OLD_LINUX,
+ OS_POLICY_LINUX,
+ OS_POLICY_OLD_SOLARIS,
+ OS_POLICY_SOLARIS,
+ OS_POLICY_HPUX10,
+ OS_POLICY_HPUX11,
+ OS_POLICY_IRIX,
+ OS_POLICY_MACOS,
+ OS_POLICY_WINDOWS,
+ OS_POLICY_VISTA,
+ OS_POLICY_WINDOWS2K3,
+ OS_POLICY_FIRST,
+ OS_POLICY_LAST
+};
+
+typedef struct TcpReassemblyThreadCtx_ {
+ void *app_tctx;
+ /** TCP segments which are not being reassembled due to memcap was reached */
+ uint16_t counter_tcp_segment_memcap;
+ /** number of streams that stop reassembly because their depth is reached */
+ uint16_t counter_tcp_stream_depth;
+ /** count number of streams with a unrecoverable stream gap (missing pkts) */
+ uint16_t counter_tcp_reass_gap;
+#ifdef DEBUG
+ uint64_t fp1;
+ uint64_t fp2;
+ uint64_t sp;
+#endif
+} TcpReassemblyThreadCtx;
+
+#define OS_POLICY_DEFAULT OS_POLICY_BSD
+
+int StreamTcpReassembleHandleSegment(ThreadVars *, TcpReassemblyThreadCtx *, TcpSession *, TcpStream *, Packet *, PacketQueue *);
+int StreamTcpReassembleInit(char);
+void StreamTcpReassembleFree(char);
+void StreamTcpReassembleRegisterTests(void);
+TcpReassemblyThreadCtx *StreamTcpReassembleInitThreadCtx(ThreadVars *tv);
+void StreamTcpReassembleFreeThreadCtx(TcpReassemblyThreadCtx *);
+int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream,
+ Packet *p);
+
+void StreamTcpCreateTestPacket(uint8_t *, uint8_t, uint8_t, uint8_t);
+
+void StreamTcpSetSessionNoReassemblyFlag (TcpSession *, char );
+void StreamTcpSetDisableRawReassemblyFlag (TcpSession *ssn, char direction);
+
+void StreamTcpSetOSPolicy(TcpStream *, Packet *);
+void StreamTcpReassemblePause (TcpSession *, char );
+void StreamTcpReassembleUnPause (TcpSession *, char );
+int StreamTcpCheckStreamContents(uint8_t *, uint16_t , TcpStream *);
+
+int StreamTcpReassembleInsertSegment(ThreadVars *, TcpReassemblyThreadCtx *, TcpStream *, TcpSegment *, Packet *);
+TcpSegment* StreamTcpGetSegment(ThreadVars *, TcpReassemblyThreadCtx *, uint16_t);
+
+void StreamTcpReturnStreamSegments(TcpStream *);
+void StreamTcpSegmentReturntoPool(TcpSegment *);
+
+void StreamTcpReassembleTriggerRawReassembly(TcpSession *);
+
+void StreamTcpPruneSession(Flow *, uint8_t);
+int StreamTcpReassembleDepthReached(Packet *p);
+
+void StreamTcpReassembleIncrMemuse(uint64_t size);
+void StreamTcpReassembleDecrMemuse(uint64_t size);
+int StreamTcpReassembleCheckMemcap(uint32_t size);
+
+void StreamTcpDisableAppLayer(Flow *f);
+int StreamTcpAppLayerIsDisabled(Flow *f);
+
+#endif /* __STREAM_TCP_REASSEMBLE_H__ */
+
diff --git a/framework/src/suricata/src/stream-tcp-sack.c b/framework/src/suricata/src/stream-tcp-sack.c
new file mode 100644
index 00000000..9e5503f7
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-sack.c
@@ -0,0 +1,960 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Stream engine TCP SACK handling.
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-sack.h"
+#include "util-unittest.h"
+
+#ifdef DEBUG
+void StreamTcpSackPrintList(TcpStream *stream)
+{
+ StreamTcpSackRecord *rec = stream->sack_head;
+ for (; rec != NULL; rec = rec->next) {
+ SCLogDebug("record %8u - %8u", rec->le, rec->re);
+ }
+}
+#endif /* DEBUG */
+
+static StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
+{
+ if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0)
+ return NULL;
+
+ StreamTcpSackRecord *rec = SCMalloc(sizeof(*rec));
+ if (unlikely(rec == NULL))
+ return NULL;
+
+ StreamTcpIncrMemuse((uint64_t)sizeof(*rec));
+ return rec;
+}
+
+static void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
+{
+ SCFree(rec);
+ StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
+}
+
+/**
+ * \brief insert a SACK range
+ *
+ * \param le left edge in host order
+ * \param re right edge in host order
+ *
+ * \retval 0 all is good
+ * \retval -1 error
+ */
+static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re)
+{
+ SCLogDebug("le %u, re %u", le, re);
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+
+ /* if to the left of last_ack then ignore */
+ if (SEQ_LT(re, stream->last_ack)) {
+ SCLogDebug("too far left. discarding");
+ goto end;
+ }
+ /* if to the right of the tcp window then ignore */
+ if (SEQ_GT(le, (stream->last_ack + stream->window))) {
+ SCLogDebug("too far right. discarding");
+ goto end;
+ }
+ if (stream->sack_head != NULL) {
+ StreamTcpSackRecord *rec;
+
+ for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
+ SCLogDebug("rec %p, le %u, re %u", rec, rec->le, rec->re);
+
+ if (SEQ_LT(le, rec->le)) {
+ SCLogDebug("SEQ_LT(le, rec->le)");
+ if (SEQ_LT(re, rec->le)) {
+ SCLogDebug("SEQ_LT(re, rec->le)");
+ // entirely before, prepend
+ StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
+ if (unlikely(stsr == NULL)) {
+ SCReturnInt(-1);
+ }
+ stsr->le = le;
+ stsr->re = re;
+
+ stsr->next = stream->sack_head;
+ stream->sack_head = stsr;
+ goto end;
+ } else if (SEQ_EQ(re, rec->le)) {
+ SCLogDebug("SEQ_EQ(re, rec->le)");
+ // starts before, ends on rec->le, expand
+ rec->le = le;
+ } else if (SEQ_GT(re, rec->le)) {
+ SCLogDebug("SEQ_GT(re, rec->le)");
+ // starts before, ends beyond rec->le
+ if (SEQ_LEQ(re, rec->re)) {
+ SCLogDebug("SEQ_LEQ(re, rec->re)");
+ // ends before rec->re, expand
+ rec->le = le;
+ } else { // implied if (re > rec->re)
+ SCLogDebug("implied if (re > rec->re), le set to %u", rec->re);
+ le = rec->re;
+ continue;
+ }
+ }
+ } else if (SEQ_EQ(le, rec->le)) {
+ SCLogDebug("SEQ_EQ(le, rec->le)");
+ if (SEQ_LEQ(re, rec->re)) {
+ SCLogDebug("SEQ_LEQ(re, rec->re)");
+ // new record fully overlapped
+ SCReturnInt(0);
+ } else { // implied re > rec->re
+ SCLogDebug("implied re > rec->re");
+ if (rec->next != NULL) {
+ if (SEQ_LEQ(re, rec->next->le)) {
+ rec->re = re;
+ goto end;
+ } else {
+ rec->re = rec->next->le;
+ le = rec->next->le;
+ SCLogDebug("le is now %u", le);
+ continue;
+ }
+ } else {
+ rec->re = re;
+ goto end;
+ }
+ }
+ } else { // implied (le > rec->le)
+ SCLogDebug("implied (le > rec->le)");
+ if (SEQ_LT(le, rec->re)) {
+ SCLogDebug("SEQ_LT(le, rec->re))");
+ // new record fully overlapped
+ if (SEQ_GT(re, rec->re)) {
+ SCLogDebug("SEQ_GT(re, rec->re)");
+
+ if (rec->next != NULL) {
+ if (SEQ_LEQ(re, rec->next->le)) {
+ rec->re = re;
+ goto end;
+ } else {
+ rec->re = rec->next->le;
+ le = rec->next->le;
+ continue;
+ }
+ } else {
+ rec->re = re;
+ goto end;
+ }
+ }
+
+ SCLogDebug("new range fully overlapped");
+ SCReturnInt(0);
+ } else if (SEQ_EQ(le, rec->re)) {
+ SCLogDebug("here");
+ // new record fully overlapped
+ //int r = StreamTcpSackInsertRange(stream, rec->re+1, re);
+ //SCReturnInt(r);
+ le = rec->re;
+ continue;
+ } else { /* implied le > rec->re */
+ SCLogDebug("implied le > rec->re");
+ if (rec->next == NULL) {
+ SCLogDebug("rec->next == NULL");
+ StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
+ if (unlikely(stsr == NULL)) {
+ SCReturnInt(-1);
+ }
+ stsr->le = le;
+ stsr->re = re;
+ stsr->next = NULL;
+
+ stream->sack_tail->next = stsr;
+ stream->sack_tail = stsr;
+ goto end;
+ } else {
+ SCLogDebug("implied rec->next != NULL");
+ if (SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)) {
+ SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)");
+ StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
+ if (unlikely(stsr == NULL)) {
+ SCReturnInt(-1);
+ }
+ stsr->le = le;
+ stsr->re = re;
+ stsr->next = rec->next;
+ rec->next = stsr;
+
+ } else if (SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)) {
+ SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)");
+ StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
+ if (unlikely(stsr == NULL)) {
+ SCReturnInt(-1);
+ }
+ stsr->le = le;
+ stsr->re = rec->next->le;
+ stsr->next = rec->next;
+ rec->next = stsr;
+
+ le = rec->next->le;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ SCLogDebug("implied empty list");
+ StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
+ if (unlikely(stsr == NULL)) {
+ SCReturnInt(-1);
+ }
+ stsr->le = le;
+ stsr->re = re;
+ stsr->next = NULL;
+
+ stream->sack_head = stsr;
+ stream->sack_tail = stsr;
+ }
+
+ StreamTcpSackPruneList(stream);
+end:
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Update stream with SACK records from a TCP packet.
+ *
+ * \param stream The stream to update.
+ * \param p packet to get the SACK records from
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
+{
+ int records = TCP_GET_SACK_CNT(p);
+ int record = 0;
+
+ TCPOptSackRecord *sack_rec = (TCPOptSackRecord *)(TCP_GET_SACK_PTR(p));
+
+ for (record = 0; record < records; record++) {
+ SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec,
+ stream->last_ack, ntohl(sack_rec->le), ntohl(sack_rec->re));
+
+ if (SEQ_LEQ(ntohl(sack_rec->re), stream->last_ack)) {
+ SCLogDebug("record before last_ack");
+ goto next;
+ }
+
+ /** \todo need a metric to a check for a right edge limit */
+/*
+ if (SEQ_GT(ntohl(sack_rec->re), stream->next_seq)) {
+ SCLogDebug("record beyond next_seq %u", stream->next_seq);
+ goto next;
+ }
+*/
+ if (SEQ_GEQ(ntohl(sack_rec->le), ntohl(sack_rec->re))) {
+ SCLogDebug("invalid record: le >= re");
+ goto next;
+ }
+
+ if (StreamTcpSackInsertRange(stream, ntohl(sack_rec->le),
+ ntohl(sack_rec->re)) == -1)
+ {
+ SCReturnInt(-1);
+ }
+
+ next:
+ sack_rec++;
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+ SCReturnInt(0);
+}
+
+void StreamTcpSackPruneList(TcpStream *stream)
+{
+ SCEnter();
+
+ StreamTcpSackRecord *rec = stream->sack_head;
+
+ while (rec != NULL) {
+ if (SEQ_LT(rec->re, stream->last_ack)) {
+ SCLogDebug("removing le %u re %u", rec->le, rec->re);
+
+ if (rec->next != NULL) {
+ stream->sack_head = rec->next;
+ StreamTcpSackRecordFree(rec);
+ rec = stream->sack_head;
+ continue;
+ } else {
+ stream->sack_head = NULL;
+ stream->sack_tail = NULL;
+ StreamTcpSackRecordFree(rec);
+ break;
+ }
+ } else if (SEQ_LT(rec->le, stream->last_ack)) {
+ SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re);
+ /* last ack inside this record, update */
+ rec->le = stream->last_ack;
+ break;
+ } else {
+ SCLogDebug("record beyond last_ack, nothing to do. Bailing out.");
+ break;
+ }
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+ SCReturn;
+}
+
+/**
+ * \brief Free SACK list from a stream
+ *
+ * \param stream Stream to cleanup
+ */
+void StreamTcpSackFreeList(TcpStream *stream)
+{
+ SCEnter();
+
+ StreamTcpSackRecord *rec = stream->sack_head;
+ StreamTcpSackRecord *next = NULL;
+
+ while (rec != NULL) {
+ next = rec->next;
+ StreamTcpSackRecordFree(rec);
+ rec = next;
+ }
+
+ stream->sack_head = NULL;
+ stream->sack_tail = NULL;
+ SCReturn;
+}
+
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest01 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 1, 10);
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 1, 20);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
+ printf("list in weird state, head le %u, re %u: ",
+ stream.sack_head->le, stream.sack_head->re);
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 19) {
+ printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest02 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 1, 20);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
+ printf("list in weird state, head le %u, re %u: ",
+ stream.sack_head->le, stream.sack_head->re);
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 19) {
+ printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest03 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 5, 15);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+ StreamTcpSackInsertRange(&stream, 15, 25);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 5) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 20) {
+ printf("size should be 20, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest04 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 20);
+ StreamTcpSackInsertRange(&stream, 30, 50);
+ StreamTcpSackInsertRange(&stream, 10, 25);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 45) {
+ printf("size should be 45, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest05 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 20);
+ StreamTcpSackInsertRange(&stream, 30, 50);
+ StreamTcpSackInsertRange(&stream, 10, 35);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 50) {
+ printf("size should be 50, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest06 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest07 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 10;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 30) {
+ printf("size should be 30, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest08 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 41;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("size should be 0, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest09 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 39;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 1) {
+ printf("size should be 1, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest10 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 1000;
+
+ StreamTcpSackInsertRange(&stream, 100, 119);
+ StreamTcpSackInsertRange(&stream, 111, 119);
+ StreamTcpSackInsertRange(&stream, 121, 129);
+ StreamTcpSackInsertRange(&stream, 131, 139);
+ StreamTcpSackInsertRange(&stream, 100, 140);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 99;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest11 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 1000;
+
+ StreamTcpSackInsertRange(&stream, 100, 119);
+ StreamTcpSackInsertRange(&stream, 111, 119);
+ StreamTcpSackInsertRange(&stream, 121, 129);
+ StreamTcpSackInsertRange(&stream, 131, 139);
+ StreamTcpSackInsertRange(&stream, 101, 140);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 99;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest12 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 2000;
+
+ StreamTcpSackInsertRange(&stream, 800, 1000);
+ StreamTcpSackInsertRange(&stream, 700, 900);
+ StreamTcpSackInsertRange(&stream, 600, 800);
+ StreamTcpSackInsertRange(&stream, 500, 700);
+ StreamTcpSackInsertRange(&stream, 100, 600);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 900) {
+ printf("size should be 900, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ StreamTcpSackInsertRange(&stream, 0, 1000);
+
+ if (StreamTcpSackedSize(&stream) != 1000) {
+ printf("size should be 1000, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 500;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 500) {
+ printf("size should be 500, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion on out of window condition.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest13 (void) {
+ TcpStream stream;
+ int retval = 0;
+ int i;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.last_ack = 10000;
+ stream.window = 2000;
+
+ for (i = 0; i < 10; i++) {
+ StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i));
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of out of window condition.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest14 (void) {
+ TcpStream stream;
+ int retval = 0;
+ int i;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.last_ack = 1000;
+ stream.window = 2000;
+
+ for (i = 0; i < 10; i++) {
+ StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i));
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+#endif /* UNITTESTS */
+
+void StreamTcpSackRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpSackTest01 -- Insertion",
+ StreamTcpSackTest01, 1);
+ UtRegisterTest("StreamTcpSackTest02 -- Insertion",
+ StreamTcpSackTest02, 1);
+ UtRegisterTest("StreamTcpSackTest03 -- Insertion",
+ StreamTcpSackTest03, 1);
+ UtRegisterTest("StreamTcpSackTest04 -- Insertion",
+ StreamTcpSackTest04, 1);
+ UtRegisterTest("StreamTcpSackTest05 -- Insertion",
+ StreamTcpSackTest05, 1);
+ UtRegisterTest("StreamTcpSackTest06 -- Insertion",
+ StreamTcpSackTest06, 1);
+ UtRegisterTest("StreamTcpSackTest07 -- Pruning",
+ StreamTcpSackTest07, 1);
+ UtRegisterTest("StreamTcpSackTest08 -- Pruning",
+ StreamTcpSackTest08, 1);
+ UtRegisterTest("StreamTcpSackTest09 -- Pruning",
+ StreamTcpSackTest09, 1);
+ UtRegisterTest("StreamTcpSackTest10 -- Pruning",
+ StreamTcpSackTest10, 1);
+ UtRegisterTest("StreamTcpSackTest11 -- Insertion && Pruning",
+ StreamTcpSackTest11, 1);
+ UtRegisterTest("StreamTcpSackTest12 -- Insertion && Pruning",
+ StreamTcpSackTest12, 1);
+ UtRegisterTest("StreamTcpSackTest13 -- Insertion out of window",
+ StreamTcpSackTest13, 1);
+ UtRegisterTest("StreamTcpSackTest14 -- Insertion out of window",
+ StreamTcpSackTest14, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/stream-tcp-sack.h b/framework/src/suricata/src/stream-tcp-sack.h
new file mode 100644
index 00000000..632fa14d
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-sack.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __STREAM_TCP_SACK_H__
+#define __STREAM_TCP_SACK_H__
+
+#include "suricata-common.h"
+#include "util-optimize.h"
+
+/**
+ * \brief Get the size of the SACKed ranges
+ *
+ * \param stream Stream to get the size for.
+ *
+ * \retval size the size
+ *
+ * Optimized for case where SACK is not in use in the
+ * stream, as it *should* only be used in case of packet
+ * loss.
+ */
+static inline uint32_t StreamTcpSackedSize(TcpStream *stream)
+{
+ if (likely(stream->sack_head == NULL)) {
+ SCReturnUInt(0U);
+ } else {
+ uint32_t size = 0;
+
+ StreamTcpSackRecord *rec = NULL;
+
+ for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
+ size += (rec->re - rec->le);
+ }
+
+ SCReturnUInt(size);
+ }
+}
+
+int StreamTcpSackUpdatePacket(TcpStream *, Packet *);
+void StreamTcpSackPruneList(TcpStream *);
+void StreamTcpSackFreeList(TcpStream *);
+void StreamTcpSackRegisterTests (void);
+
+#endif /* __STREAM_TCP_SACK_H__*/
diff --git a/framework/src/suricata/src/stream-tcp-util.c b/framework/src/suricata/src/stream-tcp-util.c
new file mode 100644
index 00000000..405684fd
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-util.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Helper functions for the stream engine.
+ */
+
+#include "suricata-common.h"
+
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp-inline.h"
+#include "stream-tcp.h"
+#include "stream-tcp-util.h"
+
+#include "util-memcmp.h"
+#include "util-print.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#ifdef UNITTESTS
+
+/* unittest helper functions */
+
+extern int stream_inline;
+
+void StreamTcpUTInit(TcpReassemblyThreadCtx **ra_ctx)
+{
+ StreamTcpInitConfig(TRUE);
+ *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+}
+
+void StreamTcpUTDeinit(TcpReassemblyThreadCtx *ra_ctx)
+{
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ stream_inline = 0;
+}
+
+void StreamTcpUTInitInline(void) {
+ stream_inline = 1;
+}
+
+void StreamTcpUTSetupSession(TcpSession *ssn)
+{
+ memset(ssn, 0x00, sizeof(TcpSession));
+}
+
+void StreamTcpUTClearSession(TcpSession *ssn)
+{
+ StreamTcpUTClearStream(&ssn->client);
+ StreamTcpUTClearStream(&ssn->server);
+}
+
+void StreamTcpUTSetupStream(TcpStream *s, uint32_t isn)
+{
+ memset(s, 0x00, sizeof(TcpStream));
+
+ s->isn = isn;
+ STREAMTCP_SET_RA_BASE_SEQ(s, isn);
+}
+
+void StreamTcpUTClearStream(TcpStream *s)
+{
+ StreamTcpReturnStreamSegments(s);
+}
+
+int StreamTcpUTAddSegmentWithPayload(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpStream *stream, uint32_t seq, uint8_t *payload, uint16_t len)
+{
+ TcpSegment *s = StreamTcpGetSegment(tv, ra_ctx, len);
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->seq = seq;
+ s->payload_len = len;
+ memcpy(s->payload, payload, len);
+
+ Packet *p = UTHBuildPacketReal(s->payload, s->payload_len, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ return -1;
+ }
+ p->tcph->th_seq = htonl(seq);
+
+ if (StreamTcpReassembleInsertSegment(tv, ra_ctx, stream, s, p) < 0)
+ return -1;
+
+ UTHFreePacket(p);
+ return 0;
+}
+
+int StreamTcpUTAddSegmentWithByte(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpStream *stream, uint32_t seq, uint8_t byte, uint16_t len)
+{
+ TcpSegment *s = StreamTcpGetSegment(tv, ra_ctx, len);
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->seq = seq;
+ s->payload_len = len;
+ memset(s->payload, byte, len);
+
+ Packet *p = UTHBuildPacketReal(s->payload, s->payload_len, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ return -1;
+ }
+ p->tcph->th_seq = htonl(seq);
+
+ if (StreamTcpReassembleInsertSegment(tv, ra_ctx, stream, s, p) < 0)
+ return -1;
+ UTHFreePacket(p);
+ return 0;
+}
+
+/* tests */
+
+int StreamTcpUtilTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+
+ StreamTcpUTInit(&ra_ctx);
+
+ if (ra_ctx == NULL) {
+ printf("ra_ctx is NULL: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+
+int StreamTcpUtilStreamTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpStream stream;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupStream(&stream, 1);
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+
+ TcpSegment *seg = stream.seg_list;
+ if (seg->seq != 2) {
+ printf("first seg in the list should have seq 2: ");
+ goto end;
+ }
+
+ seg = seg->next;
+ if (seg->seq != 7) {
+ printf("first seg in the list should have seq 7: ");
+ goto end;
+ }
+
+ seg = seg->next;
+ if (seg->seq != 12) {
+ printf("first seg in the list should have seq 12: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpUTClearStream(&stream);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+int StreamTcpUtilStreamTest02(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpStream stream;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupStream(&stream, 1);
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+
+ TcpSegment *seg = stream.seg_list;
+ if (seg->seq != 2) {
+ printf("first seg in the list should have seq 2: ");
+ goto end;
+ }
+
+ seg = seg->next;
+ if (seg->seq != 7) {
+ printf("first seg in the list should have seq 7: ");
+ goto end;
+ }
+
+ seg = seg->next;
+ if (seg->seq != 12) {
+ printf("first seg in the list should have seq 12: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpUTClearStream(&stream);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+#endif
+
+void StreamTcpUtilRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpUtilTest01", StreamTcpUtilTest01, 1);
+ UtRegisterTest("StreamTcpUtilStreamTest01", StreamTcpUtilStreamTest01, 1);
+ UtRegisterTest("StreamTcpUtilStreamTest02", StreamTcpUtilStreamTest02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/stream-tcp-util.h b/framework/src/suricata/src/stream-tcp-util.h
new file mode 100644
index 00000000..72f2b4fc
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-util.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __STREAM_TCP_UTIL_H__
+#define __STREAM_TCP_UTIL_H__
+
+#include "stream-tcp-private.h"
+
+void StreamTcpUTInit(TcpReassemblyThreadCtx **);
+void StreamTcpUTDeinit(TcpReassemblyThreadCtx *);
+
+void StreamTcpUTInitInline(void);
+
+void StreamTcpUTSetupSession(TcpSession *);
+void StreamTcpUTClearSession(TcpSession *);
+
+void StreamTcpUTSetupStream(TcpStream *, uint32_t isn);
+void StreamTcpUTClearStream(TcpStream *);
+
+int StreamTcpUTAddSegmentWithByte(ThreadVars *, TcpReassemblyThreadCtx *, TcpStream *, uint32_t, uint8_t, uint16_t);
+int StreamTcpUTAddSegmentWithPayload(ThreadVars *, TcpReassemblyThreadCtx *, TcpStream *, uint32_t, uint8_t *, uint16_t);
+
+
+void StreamTcpUtilRegisterTests(void);
+
+#endif /* __STREAM_TCP_UTIL_H__ */
+
diff --git a/framework/src/suricata/src/stream-tcp.c b/framework/src/suricata/src/stream-tcp.c
new file mode 100644
index 00000000..6cde8651
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp.c
@@ -0,0 +1,10772 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * TCP stream tracking and reassembly engine.
+ *
+ * \todo - 4WHS: what if after the 2nd SYN we turn out to be normal 3WHS anyway?
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "decode.h"
+#include "debug.h"
+#include "detect.h"
+
+#include "flow.h"
+#include "flow-util.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-pool.h"
+#include "util-pool-thread.h"
+#include "util-checksum.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-debug.h"
+#include "util-device.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream-tcp-inline.h"
+#include "stream-tcp-sack.h"
+#include "stream-tcp-util.h"
+#include "stream.h"
+
+#include "pkt-var.h"
+#include "host.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp-mem.h"
+
+#include "util-host-os-info.h"
+#include "util-privs.h"
+#include "util-profiling.h"
+#include "util-misc.h"
+#include "util-validate.h"
+#include "util-runmodes.h"
+
+#include "source-pcap-file.h"
+
+//#define DEBUG
+
+#define STREAMTCP_DEFAULT_PREALLOC 2048
+#define STREAMTCP_DEFAULT_MEMCAP (32 * 1024 * 1024) /* 32mb */
+#define STREAMTCP_DEFAULT_REASSEMBLY_MEMCAP (64 * 1024 * 1024) /* 64mb */
+#define STREAMTCP_DEFAULT_TOSERVER_CHUNK_SIZE 2560
+#define STREAMTCP_DEFAULT_TOCLIENT_CHUNK_SIZE 2560
+#define STREAMTCP_DEFAULT_MAX_SYNACK_QUEUED 5
+
+#define STREAMTCP_NEW_TIMEOUT 60
+#define STREAMTCP_EST_TIMEOUT 3600
+#define STREAMTCP_CLOSED_TIMEOUT 120
+
+#define STREAMTCP_EMERG_NEW_TIMEOUT 10
+#define STREAMTCP_EMERG_EST_TIMEOUT 300
+#define STREAMTCP_EMERG_CLOSED_TIMEOUT 20
+
+TmEcode StreamTcp (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode StreamTcpThreadInit(ThreadVars *, void *, void **);
+TmEcode StreamTcpThreadDeinit(ThreadVars *, void *);
+void StreamTcpExitPrintStats(ThreadVars *, void *);
+static int StreamTcpHandleFin(ThreadVars *tv, StreamTcpThread *, TcpSession *, Packet *, PacketQueue *);
+void StreamTcpRegisterTests (void);
+void StreamTcpReturnStreamSegments (TcpStream *);
+void StreamTcpInitConfig(char);
+int StreamTcpGetFlowState(void *);
+void StreamTcpSetOSPolicy(TcpStream*, Packet*);
+void StreamTcpPseudoPacketCreateStreamEndPacket(ThreadVars *tv, StreamTcpThread *stt, Packet *p, TcpSession *ssn, PacketQueue *pq);
+
+static int StreamTcpValidateTimestamp(TcpSession * , Packet *);
+static int StreamTcpHandleTimestamp(TcpSession * , Packet *);
+static int StreamTcpValidateRst(TcpSession * , Packet *);
+static inline int StreamTcpValidateAck(TcpSession *ssn, TcpStream *, Packet *);
+
+static PoolThread *ssn_pool = NULL;
+static SCMutex ssn_pool_mutex = SCMUTEX_INITIALIZER; /**< init only, protect initializing and growing pool */
+#ifdef DEBUG
+static uint64_t ssn_pool_cnt = 0; /** counts ssns, protected by ssn_pool_mutex */
+#endif
+
+uint64_t StreamTcpReassembleMemuseGlobalCounter(void);
+SC_ATOMIC_DECLARE(uint64_t, st_memuse);
+
+/* stream engine running in "inline" mode. */
+int stream_inline = 0;
+
+void TmModuleStreamTcpRegister (void)
+{
+ tmm_modules[TMM_STREAMTCP].name = "StreamTcp";
+ tmm_modules[TMM_STREAMTCP].ThreadInit = StreamTcpThreadInit;
+ tmm_modules[TMM_STREAMTCP].Func = StreamTcp;
+ tmm_modules[TMM_STREAMTCP].ThreadExitPrintStats = StreamTcpExitPrintStats;
+ tmm_modules[TMM_STREAMTCP].ThreadDeinit = StreamTcpThreadDeinit;
+ tmm_modules[TMM_STREAMTCP].RegisterTests = StreamTcpRegisterTests;
+ tmm_modules[TMM_STREAMTCP].cap_flags = 0;
+ tmm_modules[TMM_STREAMTCP].flags = TM_FLAG_STREAM_TM;
+}
+
+void StreamTcpIncrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_ADD(st_memuse, size);
+ return;
+}
+
+void StreamTcpDecrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_SUB(st_memuse, size);
+ return;
+}
+
+uint64_t StreamTcpMemuseCounter(void)
+{
+ uint64_t memusecopy = SC_ATOMIC_GET(st_memuse);
+ return memusecopy;
+}
+
+/**
+ * \brief Check if alloc'ing "size" would mean we're over memcap
+ *
+ * \retval 1 if in bounds
+ * \retval 0 if not in bounds
+ */
+int StreamTcpCheckMemcap(uint64_t size)
+{
+ if (stream_config.memcap == 0 || size + SC_ATOMIC_GET(st_memuse) <= stream_config.memcap)
+ return 1;
+ return 0;
+}
+
+/**
+ * \brief Function to return the stream back to the pool. It returns the
+ * segments in the stream to the segment pool.
+ *
+ * This function is called when the flow is destroyed, so it should free
+ * *everything* related to the tcp session. So including the app layer
+ * data. We are guaranteed to only get here when the flow's use_cnt is 0.
+ *
+ * \param ssn Void ptr to the ssn.
+ */
+void StreamTcpSessionClear(void *ssnptr)
+{
+ SCEnter();
+ StreamMsg *smsg = NULL;
+ TcpStateQueue *q, *q_next;
+
+ TcpSession *ssn = (TcpSession *)ssnptr;
+ if (ssn == NULL)
+ SCReturn;
+
+ StreamTcpReturnStreamSegments(&ssn->client);
+ StreamTcpReturnStreamSegments(&ssn->server);
+
+ //AppLayerParserCleanupState(ssn);
+
+ StreamTcpSackFreeList(&ssn->client);
+ StreamTcpSackFreeList(&ssn->server);
+
+ /* if we have (a) smsg(s), return to the pool */
+ smsg = ssn->toserver_smsg_head;
+ while(smsg != NULL) {
+ StreamMsg *smsg_next = smsg->next;
+ SCLogDebug("returning smsg %p to pool", smsg);
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ StreamMsgReturnToPool(smsg);
+ smsg = smsg_next;
+ }
+ ssn->toserver_smsg_head = NULL;
+
+ smsg = ssn->toclient_smsg_head;
+ while(smsg != NULL) {
+ StreamMsg *smsg_next = smsg->next;
+ SCLogDebug("returning smsg %p to pool", smsg);
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ StreamMsgReturnToPool(smsg);
+ smsg = smsg_next;
+ }
+ ssn->toclient_smsg_head = NULL;
+
+ q = ssn->queue;
+ while (q != NULL) {
+ q_next = q->next;
+ SCFree(q);
+ q = q_next;
+ StreamTcpDecrMemuse((uint64_t)sizeof(TcpStateQueue));
+ }
+ ssn->queue = NULL;
+ ssn->queue_len = 0;
+
+ memset(ssn, 0, sizeof(TcpSession));
+ PoolThreadReturn(ssn_pool, ssn);
+#ifdef DEBUG
+ SCMutexLock(&ssn_pool_mutex);
+ ssn_pool_cnt--;
+ SCMutexUnlock(&ssn_pool_mutex);
+#endif
+
+ SCReturn;
+}
+
+/**
+ * \brief Function to return the stream segments back to the pool.
+ *
+ * We don't clear out the app layer storage here as that is under protection
+ * of the "use_cnt" reference counter in the flow. This function is called
+ * when the use_cnt is always at least 1 (this pkt has incremented the flow
+ * use_cnt itself), so we don't bother.
+ *
+ * \param p Packet used to identify the stream.
+ */
+void StreamTcpSessionPktFree (Packet *p)
+{
+ SCEnter();
+
+ TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+ if (ssn == NULL)
+ SCReturn;
+
+ StreamTcpReturnStreamSegments(&ssn->client);
+ StreamTcpReturnStreamSegments(&ssn->server);
+
+ SCReturn;
+}
+
+/** \brief Stream alloc function for the Pool
+ * \retval ptr void ptr to TcpSession structure with all vars set to 0/NULL
+ */
+void *StreamTcpSessionPoolAlloc()
+{
+ void *ptr = NULL;
+
+ if (StreamTcpCheckMemcap((uint32_t)sizeof(TcpSession)) == 0)
+ return NULL;
+
+ ptr = SCMalloc(sizeof(TcpSession));
+ if (unlikely(ptr == NULL))
+ return NULL;
+
+ return ptr;
+}
+
+int StreamTcpSessionPoolInit(void *data, void* initdata)
+{
+ memset(data, 0, sizeof(TcpSession));
+ StreamTcpIncrMemuse((uint64_t)sizeof(TcpSession));
+
+ return 1;
+}
+
+/** \brief Pool free function
+ * \param s Void ptr to TcpSession memory */
+void StreamTcpSessionPoolCleanup(void *s)
+{
+ StreamMsg *smsg = NULL;
+ TcpStateQueue *q, *q_next;
+
+ if (s == NULL)
+ return;
+
+ TcpSession *ssn = (TcpSession *)s;
+
+ StreamTcpReturnStreamSegments(&ssn->client);
+ StreamTcpReturnStreamSegments(&ssn->server);
+
+ /* if we have (a) smsg(s), return to the pool */
+ smsg = ssn->toserver_smsg_head;
+ while(smsg != NULL) {
+ StreamMsg *smsg_next = smsg->next;
+ SCLogDebug("returning smsg %p to pool", smsg);
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ StreamMsgReturnToPool(smsg);
+ smsg = smsg_next;
+ }
+ ssn->toserver_smsg_head = NULL;
+
+ smsg = ssn->toclient_smsg_head;
+ while(smsg != NULL) {
+ StreamMsg *smsg_next = smsg->next;
+ SCLogDebug("returning smsg %p to pool", smsg);
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ StreamMsgReturnToPool(smsg);
+ smsg = smsg_next;
+ }
+ ssn->toclient_smsg_head = NULL;
+
+ q = ssn->queue;
+ while (q != NULL) {
+ q_next = q->next;
+ SCFree(q);
+ q = q_next;
+ StreamTcpDecrMemuse((uint64_t)sizeof(TcpStateQueue));
+ }
+ ssn->queue = NULL;
+ ssn->queue_len = 0;
+
+ StreamTcpDecrMemuse((uint64_t)sizeof(TcpSession));
+}
+
+/** \brief To initialize the stream global configuration data
+ *
+ * \param quiet It tells the mode of operation, if it is TRUE nothing will
+ * be get printed.
+ */
+
+void StreamTcpInitConfig(char quiet)
+{
+ intmax_t value = 0;
+ uint16_t rdrange = 10;
+
+ SCLogDebug("Initializing Stream");
+
+ memset(&stream_config, 0, sizeof(stream_config));
+
+ if ((ConfGetInt("stream.max-sessions", &value)) == 1) {
+ SCLogWarning(SC_WARN_OPTION_OBSOLETE, "max-sessions is obsolete. "
+ "Number of concurrent sessions is now only limited by Flow and "
+ "TCP stream engine memcaps.");
+ }
+
+ if ((ConfGetInt("stream.prealloc-sessions", &value)) == 1) {
+ stream_config.prealloc_sessions = (uint32_t)value;
+ } else {
+ if (RunmodeIsUnittests()) {
+ stream_config.prealloc_sessions = 128;
+ } else {
+ stream_config.prealloc_sessions = STREAMTCP_DEFAULT_PREALLOC;
+ if (ConfGetNode("stream.prealloc-sessions") != NULL) {
+ WarnInvalidConfEntry("stream.prealloc_sessions",
+ "%"PRIu32,
+ stream_config.prealloc_sessions);
+ }
+ }
+ }
+ if (!quiet) {
+ SCLogInfo("stream \"prealloc-sessions\": %"PRIu32" (per thread)",
+ stream_config.prealloc_sessions);
+ }
+
+ char *temp_stream_memcap_str;
+ if (ConfGet("stream.memcap", &temp_stream_memcap_str) == 1) {
+ if (ParseSizeStringU64(temp_stream_memcap_str, &stream_config.memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing stream.memcap "
+ "from conf file - %s. Killing engine",
+ temp_stream_memcap_str);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ stream_config.memcap = STREAMTCP_DEFAULT_MEMCAP;
+ }
+
+ if (!quiet) {
+ SCLogInfo("stream \"memcap\": %"PRIu64, stream_config.memcap);
+ }
+
+ ConfGetBool("stream.midstream", &stream_config.midstream);
+
+ if (!quiet) {
+ SCLogInfo("stream \"midstream\" session pickups: %s", stream_config.midstream ? "enabled" : "disabled");
+ }
+
+ ConfGetBool("stream.async-oneside", &stream_config.async_oneside);
+
+ if (!quiet) {
+ SCLogInfo("stream \"async-oneside\": %s", stream_config.async_oneside ? "enabled" : "disabled");
+ }
+
+ int csum = 0;
+
+ if ((ConfGetBool("stream.checksum-validation", &csum)) == 1) {
+ if (csum == 1) {
+ stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION;
+ }
+ /* Default is that we validate the checksum of all the packets */
+ } else {
+ stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION;
+ }
+
+ if (!quiet) {
+ SCLogInfo("stream \"checksum-validation\": %s",
+ stream_config.flags & STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION ?
+ "enabled" : "disabled");
+ }
+
+ int inl = 0;
+
+
+ char *temp_stream_inline_str;
+ if (ConfGet("stream.inline", &temp_stream_inline_str) == 1) {
+ /* checking for "auto" and falling back to boolean to provide
+ * backward compatibility */
+ if (strcmp(temp_stream_inline_str, "auto") == 0) {
+ if (EngineModeIsIPS()) {
+ stream_inline = 1;
+ } else {
+ stream_inline = 0;
+ }
+ } else if (ConfGetBool("stream.inline", &inl) == 1) {
+ stream_inline = inl;
+ }
+ }
+
+ if (!quiet) {
+ SCLogInfo("stream.\"inline\": %s", stream_inline ? "enabled" : "disabled");
+ }
+
+ if ((ConfGetInt("stream.max-synack-queued", &value)) == 1) {
+ if (value >= 0 && value <= 255) {
+ stream_config.max_synack_queued = (uint8_t)value;
+ } else {
+ stream_config.max_synack_queued = (uint8_t)STREAMTCP_DEFAULT_MAX_SYNACK_QUEUED;
+ }
+ } else {
+ stream_config.max_synack_queued = (uint8_t)STREAMTCP_DEFAULT_MAX_SYNACK_QUEUED;
+ }
+ if (!quiet) {
+ SCLogInfo("stream \"max-synack-queued\": %"PRIu8, stream_config.max_synack_queued);
+ }
+
+ char *temp_stream_reassembly_memcap_str;
+ if (ConfGet("stream.reassembly.memcap", &temp_stream_reassembly_memcap_str) == 1) {
+ if (ParseSizeStringU64(temp_stream_reassembly_memcap_str,
+ &stream_config.reassembly_memcap) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
+ "stream.reassembly.memcap "
+ "from conf file - %s. Killing engine",
+ temp_stream_reassembly_memcap_str);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ stream_config.reassembly_memcap = STREAMTCP_DEFAULT_REASSEMBLY_MEMCAP;
+ }
+
+ if (!quiet) {
+ SCLogInfo("stream.reassembly \"memcap\": %"PRIu64"", stream_config.reassembly_memcap);
+ }
+
+ char *temp_stream_reassembly_depth_str;
+ if (ConfGet("stream.reassembly.depth", &temp_stream_reassembly_depth_str) == 1) {
+ if (ParseSizeStringU32(temp_stream_reassembly_depth_str,
+ &stream_config.reassembly_depth) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
+ "stream.reassembly.depth "
+ "from conf file - %s. Killing engine",
+ temp_stream_reassembly_depth_str);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ stream_config.reassembly_depth = 0;
+ }
+
+ if (!quiet) {
+ SCLogInfo("stream.reassembly \"depth\": %"PRIu32"", stream_config.reassembly_depth);
+ }
+
+ int randomize = 0;
+ if ((ConfGetBool("stream.reassembly.randomize-chunk-size", &randomize)) == 0) {
+ /* randomize by default if value not set
+ * In ut mode we disable, to get predictible test results */
+ if (!(RunmodeIsUnittests()))
+ randomize = 1;
+ }
+
+ if (randomize) {
+ char *temp_rdrange;
+ if (ConfGet("stream.reassembly.randomize-chunk-range",
+ &temp_rdrange) == 1) {
+ if (ParseSizeStringU16(temp_rdrange, &rdrange) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
+ "stream.reassembly.randomize-chunk-range "
+ "from conf file - %s. Killing engine",
+ temp_rdrange);
+ exit(EXIT_FAILURE);
+ } else if (rdrange >= 100) {
+ SCLogError(SC_ERR_INVALID_VALUE,
+ "stream.reassembly.randomize-chunk-range "
+ "must be lower than 100");
+ exit(EXIT_FAILURE);
+ }
+ }
+ /* set a "random" seed */
+ srandom(time(0));
+ }
+
+ char *temp_stream_reassembly_toserver_chunk_size_str;
+ if (ConfGet("stream.reassembly.toserver-chunk-size",
+ &temp_stream_reassembly_toserver_chunk_size_str) == 1) {
+ if (ParseSizeStringU16(temp_stream_reassembly_toserver_chunk_size_str,
+ &stream_config.reassembly_toserver_chunk_size) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
+ "stream.reassembly.toserver-chunk-size "
+ "from conf file - %s. Killing engine",
+ temp_stream_reassembly_toserver_chunk_size_str);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ stream_config.reassembly_toserver_chunk_size =
+ STREAMTCP_DEFAULT_TOSERVER_CHUNK_SIZE;
+ }
+
+ if (randomize) {
+ stream_config.reassembly_toserver_chunk_size +=
+ (int) (stream_config.reassembly_toserver_chunk_size *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+ }
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER,
+ stream_config.reassembly_toserver_chunk_size);
+
+ char *temp_stream_reassembly_toclient_chunk_size_str;
+ if (ConfGet("stream.reassembly.toclient-chunk-size",
+ &temp_stream_reassembly_toclient_chunk_size_str) == 1) {
+ if (ParseSizeStringU16(temp_stream_reassembly_toclient_chunk_size_str,
+ &stream_config.reassembly_toclient_chunk_size) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
+ "stream.reassembly.toclient-chunk-size "
+ "from conf file - %s. Killing engine",
+ temp_stream_reassembly_toclient_chunk_size_str);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ stream_config.reassembly_toclient_chunk_size =
+ STREAMTCP_DEFAULT_TOCLIENT_CHUNK_SIZE;
+ }
+
+ if (randomize) {
+ stream_config.reassembly_toclient_chunk_size +=
+ (int) (stream_config.reassembly_toclient_chunk_size *
+ (random() * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
+ }
+
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT,
+ stream_config.reassembly_toclient_chunk_size);
+
+ if (!quiet) {
+ SCLogInfo("stream.reassembly \"toserver-chunk-size\": %"PRIu16,
+ stream_config.reassembly_toserver_chunk_size);
+ SCLogInfo("stream.reassembly \"toclient-chunk-size\": %"PRIu16,
+ stream_config.reassembly_toclient_chunk_size);
+ }
+
+ int enable_raw = 1;
+ if (ConfGetBool("stream.reassembly.raw", &enable_raw) == 1) {
+ if (!enable_raw) {
+ stream_config.ssn_init_flags = STREAMTCP_FLAG_DISABLE_RAW;
+ stream_config.segment_init_flags = SEGMENTTCP_FLAG_RAW_PROCESSED;
+ }
+ } else {
+ enable_raw = 1;
+ }
+ if (!quiet)
+ SCLogInfo("stream.reassembly.raw: %s", enable_raw ? "enabled" : "disabled");
+
+ /* init the memcap/use tracking */
+ SC_ATOMIC_INIT(st_memuse);
+ StatsRegisterGlobalCounter("tcp.memuse", StreamTcpMemuseCounter);
+
+ StreamTcpReassembleInit(quiet);
+
+ /* set the default free function and flow state function
+ * values. */
+ FlowSetProtoFreeFunc(IPPROTO_TCP, StreamTcpSessionClear);
+
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests()) {
+ SCMutexLock(&ssn_pool_mutex);
+ if (ssn_pool == NULL) {
+ ssn_pool = PoolThreadInit(1, /* thread */
+ 0, /* unlimited */
+ stream_config.prealloc_sessions,
+ sizeof(TcpSession),
+ StreamTcpSessionPoolAlloc,
+ StreamTcpSessionPoolInit, NULL,
+ StreamTcpSessionPoolCleanup, NULL);
+ }
+ SCMutexUnlock(&ssn_pool_mutex);
+ }
+#endif
+}
+
+void StreamTcpFreeConfig(char quiet)
+{
+ StreamTcpReassembleFree(quiet);
+
+ SCMutexLock(&ssn_pool_mutex);
+ if (ssn_pool != NULL) {
+ PoolThreadFree(ssn_pool);
+ ssn_pool = NULL;
+ }
+ SCMutexUnlock(&ssn_pool_mutex);
+ SCMutexDestroy(&ssn_pool_mutex);
+
+ SCLogDebug("ssn_pool_cnt %"PRIu64"", ssn_pool_cnt);
+}
+
+/** \brief The function is used to to fetch a TCP session from the
+ * ssn_pool, when a TCP SYN is received.
+ *
+ * \param quiet Packet P, which has been recieved for the new TCP session.
+ *
+ * \retval TcpSession A new TCP session with field initilaized to 0/NULL.
+ */
+TcpSession *StreamTcpNewSession (Packet *p, int id)
+{
+ TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+
+ if (ssn == NULL) {
+ p->flow->protoctx = PoolThreadGetById(ssn_pool, id);
+#ifdef DEBUG
+ SCMutexLock(&ssn_pool_mutex);
+ if (p->flow->protoctx != NULL)
+ ssn_pool_cnt++;
+ SCMutexUnlock(&ssn_pool_mutex);
+#endif
+
+ ssn = (TcpSession *)p->flow->protoctx;
+ if (ssn == NULL) {
+ SCLogDebug("ssn_pool is empty");
+ return NULL;
+ }
+
+ ssn->state = TCP_NONE;
+ ssn->flags = stream_config.ssn_init_flags;
+ ssn->tcp_packet_flags = p->tcph ? p->tcph->th_flags : 0;
+
+ if (PKT_IS_TOSERVER(p)) {
+ ssn->client.tcp_flags = p->tcph ? p->tcph->th_flags : 0;
+ ssn->server.tcp_flags = 0;
+ } else if (PKT_IS_TOCLIENT(p)) {
+ ssn->server.tcp_flags = p->tcph ? p->tcph->th_flags : 0;
+ ssn->client.tcp_flags = 0;
+ }
+ }
+
+ return ssn;
+}
+
+static void StreamTcpPacketSetState(Packet *p, TcpSession *ssn,
+ uint8_t state)
+{
+ if (state == ssn->state || PKT_IS_PSEUDOPKT(p))
+ return;
+
+ ssn->state = state;
+
+ /* update the flow state */
+ switch(ssn->state) {
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT1:
+ case TCP_FIN_WAIT2:
+ case TCP_CLOSING:
+ case TCP_CLOSE_WAIT:
+ SC_ATOMIC_SET(p->flow->flow_state, FLOW_STATE_ESTABLISHED);
+ break;
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ case TCP_CLOSED:
+ SC_ATOMIC_SET(p->flow->flow_state, FLOW_STATE_CLOSED);
+ break;
+ }
+}
+
+/**
+ * \brief Function to set the OS policy for the given stream based on the
+ * destination of the received packet.
+ *
+ * \param stream TcpStream of which os_policy needs to set
+ * \param p Packet which is used to set the os policy
+ */
+void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p)
+{
+ int ret = 0;
+
+ if (PKT_IS_IPV4(p)) {
+ /* Get the OS policy based on destination IP address, as destination
+ OS will decide how to react on the anomalies of newly received
+ packets */
+ ret = SCHInfoGetIPv4HostOSFlavour((uint8_t *)GET_IPV4_DST_ADDR_PTR(p));
+ if (ret > 0)
+ stream->os_policy = ret;
+ else
+ stream->os_policy = OS_POLICY_DEFAULT;
+
+ } else if (PKT_IS_IPV6(p)) {
+ /* Get the OS policy based on destination IP address, as destination
+ OS will decide how to react on the anomalies of newly received
+ packets */
+ ret = SCHInfoGetIPv6HostOSFlavour((uint8_t *)GET_IPV6_DST_ADDR(p));
+ if (ret > 0)
+ stream->os_policy = ret;
+ else
+ stream->os_policy = OS_POLICY_DEFAULT;
+ }
+
+ if (stream->os_policy == OS_POLICY_BSD_RIGHT)
+ stream->os_policy = OS_POLICY_BSD;
+ else if (stream->os_policy == OS_POLICY_OLD_SOLARIS)
+ stream->os_policy = OS_POLICY_SOLARIS;
+
+ SCLogDebug("Policy is %"PRIu8"", stream->os_policy);
+
+}
+
+/**
+ * \brief get the size of a stream
+ *
+ * \note this just calculates the diff between isn and last_ack
+ * and will not consider sequence wrap arounds (streams
+ * bigger than 4gb).
+ *
+ * \retval size stream size
+ */
+uint32_t StreamTcpGetStreamSize(TcpStream *stream)
+{
+ return (stream->last_ack - stream->isn - 1);
+}
+
+/**
+ * \brief macro to update last_ack only if the new value is higher
+ *
+ * \param ssn session
+ * \param stream stream to update
+ * \param ack ACK value to test and set
+ */
+#define StreamTcpUpdateLastAck(ssn, stream, ack) { \
+ if (SEQ_GT((ack), (stream)->last_ack)) \
+ { \
+ (stream)->last_ack = (ack); \
+ SCLogDebug("ssn %p: last_ack set to %"PRIu32, (ssn), (stream)->last_ack); \
+ StreamTcpSackPruneList((stream)); \
+ } else { \
+ SCLogDebug("ssn %p: no update: ack %u, last_ack %"PRIu32", next_seq %u (state %u)", \
+ (ssn), (ack), (stream)->last_ack, (stream)->next_seq, (ssn)->state); \
+ }\
+}
+
+/**
+ * \brief macro to update next_win only if the new value is higher
+ *
+ * \param ssn session
+ * \param stream stream to update
+ * \param win window value to test and set
+ */
+#define StreamTcpUpdateNextWin(ssn, stream, win) { \
+ uint32_t sacked_size__ = StreamTcpSackedSize((stream)); \
+ if (SEQ_GT(((win) + sacked_size__), (stream)->next_win)) { \
+ (stream)->next_win = ((win) + sacked_size__); \
+ SCLogDebug("ssn %p: next_win set to %"PRIu32, (ssn), (stream)->next_win); \
+ } \
+}
+
+static int StreamTcpPacketIsRetransmission(TcpStream *stream, Packet *p)
+{
+ if (p->payload_len == 0)
+ SCReturnInt(0);
+
+ /* retransmission of already ack'd data */
+ if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->last_ack)) {
+ StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION);
+ SCReturnInt(1);
+ }
+
+ /* retransmission of in flight data */
+ if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->next_seq)) {
+ StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION);
+ SCReturnInt(2);
+ }
+
+ SCLogDebug("seq %u payload_len %u => %u, last_ack %u", TCP_GET_SEQ(p),
+ p->payload_len, (TCP_GET_SEQ(p) + p->payload_len), stream->last_ack);
+ SCReturnInt(0);
+}
+
+/**
+ * \internal
+ * \brief Function to handle the TCP_CLOSED or NONE state. The function handles
+ * packets while the session state is None which means a newly
+ * initialized structure, or a fully closed session.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (p->tcph->th_flags & TH_RST) {
+ StreamTcpSetEvent(p, STREAM_RST_BUT_NO_SESSION);
+ SCLogDebug("RST packet received, no session setup");
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ StreamTcpSetEvent(p, STREAM_FIN_BUT_NO_SESSION);
+ SCLogDebug("FIN packet received, no session setup");
+ return -1;
+
+ /* SYN/ACK */
+ } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ if (stream_config.midstream == FALSE &&
+ stream_config.async_oneside == FALSE)
+ return 0;
+
+ if (ssn == NULL) {
+ ssn = StreamTcpNewSession(p, stt->ssn_pool_id);
+ if (ssn == NULL) {
+ StatsIncr(tv, stt->counter_tcp_ssn_memcap);
+ return -1;
+ }
+ StatsIncr(tv, stt->counter_tcp_sessions);
+ }
+ /* set the state */
+ StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
+ SCLogDebug("ssn %p: =~ midstream picked ssn state is now "
+ "TCP_SYN_RECV", ssn);
+ ssn->flags |= STREAMTCP_FLAG_MIDSTREAM;
+ /* Flag used to change the direct in the later stage in the session */
+ ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_SYNACK;
+
+ /* sequence number & window */
+ ssn->server.isn = TCP_GET_SEQ(p);
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
+ ssn->server.next_seq = ssn->server.isn + 1;
+ ssn->server.window = TCP_GET_WINDOW(p);
+ SCLogDebug("ssn %p: server window %u", ssn, ssn->server.window);
+
+ ssn->client.isn = TCP_GET_ACK(p) - 1;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn);
+ ssn->client.next_seq = ssn->client.isn + 1;
+
+ ssn->client.last_ack = TCP_GET_ACK(p);
+ ssn->server.last_ack = TCP_GET_SEQ(p);
+
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+
+ /** If the client has a wscale option the server had it too,
+ * so set the wscale for the server to max. Otherwise none
+ * will have the wscale opt just like it should. */
+ if (p->tcpvars.ws != NULL) {
+ ssn->client.wscale = TCP_GET_WSCALE(p);
+ ssn->server.wscale = TCP_WSCALE_MAX;
+ }
+
+ SCLogDebug("ssn %p: ssn->client.isn %"PRIu32", ssn->client.next_seq"
+ " %"PRIu32", ssn->client.last_ack %"PRIu32"", ssn,
+ ssn->client.isn, ssn->client.next_seq,
+ ssn->client.last_ack);
+ SCLogDebug("ssn %p: ssn->server.isn %"PRIu32", ssn->server.next_seq"
+ " %"PRIu32", ssn->server.last_ack %"PRIu32"", ssn,
+ ssn->server.isn, ssn->server.next_seq,
+ ssn->server.last_ack);
+
+ /* Set the timestamp value for both streams, if packet has timestamp
+ * option enabled.*/
+ if (p->tcpvars.ts != NULL) {
+ ssn->server.last_ts = TCP_GET_TSVAL(p);
+ ssn->client.last_ts = TCP_GET_TSECR(p);
+ SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" "
+ "ssn->client.last_ts %" PRIu32"", ssn,
+ ssn->server.last_ts, ssn->client.last_ts);
+
+ ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
+
+ ssn->server.last_pkt_ts = p->ts.tv_sec;
+ if (ssn->server.last_ts == 0)
+ ssn->server.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ if (ssn->client.last_ts == 0)
+ ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+
+ } else {
+ ssn->server.last_ts = 0;
+ ssn->client.last_ts = 0;
+ }
+
+ if (TCP_GET_SACKOK(p) == 1) {
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ SCLogDebug("ssn %p: SYN/ACK with SACK permitted, assuming "
+ "SACK permitted for both sides", ssn);
+ }
+
+ /* packet thinks it is in the wrong direction, flip it */
+ StreamTcpPacketSwitchDir(ssn, p);
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ if (ssn == NULL) {
+ ssn = StreamTcpNewSession(p, stt->ssn_pool_id);
+ if (ssn == NULL) {
+ StatsIncr(tv, stt->counter_tcp_ssn_memcap);
+ return -1;
+ }
+
+ StatsIncr(tv, stt->counter_tcp_sessions);
+ }
+
+ /* set the state */
+ StreamTcpPacketSetState(p, ssn, TCP_SYN_SENT);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_SENT", ssn);
+
+ /* set the sequence numbers and window */
+ ssn->client.isn = TCP_GET_SEQ(p);
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn);
+ ssn->client.next_seq = ssn->client.isn + 1;
+
+ /* Set the stream timestamp value, if packet has timestamp option
+ * enabled. */
+ if (p->tcpvars.ts != NULL) {
+ ssn->client.last_ts = TCP_GET_TSVAL(p);
+ SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, p->tcpvars.ts,
+ ssn->client.last_ts);
+
+ if (ssn->client.last_ts == 0)
+ ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+
+ ssn->client.last_pkt_ts = p->ts.tv_sec;
+ ssn->client.flags |= STREAMTCP_STREAM_FLAG_TIMESTAMP;
+ }
+
+ ssn->server.window = TCP_GET_WINDOW(p);
+ if (p->tcpvars.ws != NULL) {
+ ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE;
+ ssn->server.wscale = TCP_GET_WSCALE(p);
+ }
+
+ if (TCP_GET_SACKOK(p) == 1) {
+ ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK;
+ SCLogDebug("ssn %p: SACK permited on SYN packet", ssn);
+ }
+
+ SCLogDebug("ssn %p: ssn->client.isn %" PRIu32 ", "
+ "ssn->client.next_seq %" PRIu32 ", ssn->client.last_ack "
+ "%"PRIu32"", ssn, ssn->client.isn, ssn->client.next_seq,
+ ssn->client.last_ack);
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (stream_config.midstream == FALSE)
+ return 0;
+
+ if (ssn == NULL) {
+ ssn = StreamTcpNewSession(p, stt->ssn_pool_id);
+ if (ssn == NULL) {
+ StatsIncr(tv, stt->counter_tcp_ssn_memcap);
+ return -1;
+ }
+ StatsIncr(tv, stt->counter_tcp_sessions);
+ }
+ /* set the state */
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ midstream picked ssn state is now "
+ "TCP_ESTABLISHED", ssn);
+
+ ssn->flags = STREAMTCP_FLAG_MIDSTREAM;
+ ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED;
+
+ /** window scaling for midstream pickups, we can't do much other
+ * than assume that it's set to the max value: 14 */
+ ssn->client.wscale = TCP_WSCALE_MAX;
+ ssn->server.wscale = TCP_WSCALE_MAX;
+
+ /* set the sequence numbers and window */
+ ssn->client.isn = TCP_GET_SEQ(p) - 1;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn);
+ ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ ssn->client.last_ack = TCP_GET_SEQ(p);
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+ SCLogDebug("ssn %p: ssn->client.isn %u, ssn->client.next_seq %u",
+ ssn, ssn->client.isn, ssn->client.next_seq);
+
+ ssn->server.isn = TCP_GET_ACK(p) - 1;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
+ ssn->server.next_seq = ssn->server.isn + 1;
+ ssn->server.last_ack = TCP_GET_ACK(p);
+ ssn->server.next_win = ssn->server.last_ack;
+
+ SCLogDebug("ssn %p: ssn->client.next_win %"PRIu32", "
+ "ssn->server.next_win %"PRIu32"", ssn,
+ ssn->client.next_win, ssn->server.next_win);
+ SCLogDebug("ssn %p: ssn->client.last_ack %"PRIu32", "
+ "ssn->server.last_ack %"PRIu32"", ssn,
+ ssn->client.last_ack, ssn->server.last_ack);
+
+ /* Set the timestamp value for both streams, if packet has timestamp
+ * option enabled.*/
+ if (p->tcpvars.ts != NULL) {
+ ssn->client.last_ts = TCP_GET_TSVAL(p);
+ ssn->server.last_ts = TCP_GET_TSECR(p);
+ SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" "
+ "ssn->client.last_ts %" PRIu32"", ssn,
+ ssn->server.last_ts, ssn->client.last_ts);
+
+ ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
+
+ ssn->client.last_pkt_ts = p->ts.tv_sec;
+ if (ssn->server.last_ts == 0)
+ ssn->server.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ if (ssn->client.last_ts == 0)
+ ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+
+ } else {
+ ssn->server.last_ts = 0;
+ ssn->client.last_ts = 0;
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
+
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ SCLogDebug("ssn %p: assuming SACK permitted for both sides", ssn);
+
+ } else {
+ SCLogDebug("default case");
+ }
+
+ return 0;
+}
+
+/** \internal
+ * \brief Setup TcpStateQueue based on SYN/ACK packet
+ */
+static inline void StreamTcp3whsSynAckToStateQueue(Packet *p, TcpStateQueue *q)
+{
+ q->flags = 0;
+ q->wscale = 0;
+ q->ts = 0;
+ q->win = TCP_GET_WINDOW(p);
+ q->seq = TCP_GET_SEQ(p);
+ q->ack = TCP_GET_ACK(p);
+ q->pkt_ts = p->ts.tv_sec;
+
+ if (TCP_GET_SACKOK(p) == 1)
+ q->flags |= STREAMTCP_QUEUE_FLAG_SACK;
+
+ if (p->tcpvars.ws != NULL) {
+ q->flags |= STREAMTCP_QUEUE_FLAG_WS;
+ q->wscale = TCP_GET_WSCALE(p);
+ }
+ if (p->tcpvars.ts != NULL) {
+ q->flags |= STREAMTCP_QUEUE_FLAG_TS;
+ q->ts = TCP_GET_TSVAL(p);
+ }
+}
+
+/** \internal
+ * \brief Find the Queued SYN/ACK that is the same as this SYN/ACK
+ * \retval q or NULL */
+TcpStateQueue *StreamTcp3whsFindSynAckBySynAck(TcpSession *ssn, Packet *p)
+{
+ TcpStateQueue *q = ssn->queue;
+ TcpStateQueue search;
+
+ StreamTcp3whsSynAckToStateQueue(p, &search);
+
+ while (q != NULL) {
+ if (search.flags == q->flags &&
+ search.wscale == q->wscale &&
+ search.win == q->win &&
+ search.seq == q->seq &&
+ search.ack == q->ack &&
+ search.ts == q->ts) {
+ return q;
+ }
+
+ q = q->next;
+ }
+
+ return q;
+}
+
+int StreamTcp3whsQueueSynAck(TcpSession *ssn, Packet *p)
+{
+ /* first see if this is already in our list */
+ if (StreamTcp3whsFindSynAckBySynAck(ssn, p) != NULL)
+ return 0;
+
+ if (ssn->queue_len == stream_config.max_synack_queued) {
+ SCLogDebug("ssn %p: =~ SYN/ACK queue limit reached", ssn);
+ StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_FLOOD);
+ return -1;
+ }
+
+ if (StreamTcpCheckMemcap((uint32_t)sizeof(TcpStateQueue)) == 0) {
+ SCLogDebug("ssn %p: =~ SYN/ACK queue failed: stream memcap reached", ssn);
+ return -1;
+ }
+
+ TcpStateQueue *q = SCMalloc(sizeof(*q));
+ if (unlikely(q == NULL)) {
+ SCLogDebug("ssn %p: =~ SYN/ACK queue failed: alloc failed", ssn);
+ return -1;
+ }
+ memset(q, 0x00, sizeof(*q));
+ StreamTcpIncrMemuse((uint64_t)sizeof(TcpStateQueue));
+
+ StreamTcp3whsSynAckToStateQueue(p, q);
+
+ /* put in list */
+ q->next = ssn->queue;
+ ssn->queue = q;
+ ssn->queue_len++;
+ return 0;
+}
+
+/** \internal
+ * \brief Find the Queued SYN/ACK that goes with this ACK
+ * \retval q or NULL */
+TcpStateQueue *StreamTcp3whsFindSynAckByAck(TcpSession *ssn, Packet *p)
+{
+ uint32_t ack = TCP_GET_SEQ(p);
+ uint32_t seq = TCP_GET_ACK(p) - 1;
+ TcpStateQueue *q = ssn->queue;
+
+ while (q != NULL) {
+ if (seq == q->seq &&
+ ack == q->ack) {
+ return q;
+ }
+
+ q = q->next;
+ }
+
+ return NULL;
+}
+
+/** \internal
+ * \brief Update SSN after receiving a valid SYN/ACK
+ *
+ * Normally we update the SSN from the SYN/ACK packet. But in case
+ * of queued SYN/ACKs, we can use one of those.
+ *
+ * \param ssn TCP session
+ * \param p Packet
+ * \param q queued state if used, NULL otherwise
+ *
+ * To make sure all SYN/ACK based state updates are in one place,
+ * this function can updated based on Packet or TcpStateQueue, where
+ * the latter takes precedence.
+ */
+static void StreamTcp3whsSynAckUpdate(TcpSession *ssn, Packet *p, TcpStateQueue *q)
+{
+ TcpStateQueue update;
+ if (likely(q == NULL)) {
+ StreamTcp3whsSynAckToStateQueue(p, &update);
+ q = &update;
+ }
+
+ if (ssn->state != TCP_SYN_RECV) {
+ /* update state */
+ StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_RECV", ssn);
+ }
+ /* sequence number & window */
+ ssn->server.isn = q->seq;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
+ ssn->server.next_seq = ssn->server.isn + 1;
+
+ ssn->client.window = q->win;
+ SCLogDebug("ssn %p: window %" PRIu32 "", ssn, ssn->server.window);
+
+ /* Set the timestamp values used to validate the timestamp of
+ * received packets.*/
+ if ((q->flags & STREAMTCP_QUEUE_FLAG_TS) &&
+ (ssn->client.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP))
+ {
+ ssn->server.last_ts = q->ts;
+ SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" "
+ "ssn->client.last_ts %" PRIu32"", ssn,
+ ssn->server.last_ts, ssn->client.last_ts);
+ ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
+ ssn->server.last_pkt_ts = q->pkt_ts;
+ if (ssn->server.last_ts == 0)
+ ssn->server.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ } else {
+ ssn->client.last_ts = 0;
+ ssn->server.last_ts = 0;
+ ssn->client.flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ }
+
+ ssn->client.last_ack = q->ack;
+ ssn->server.last_ack = ssn->server.isn + 1;
+
+ /** check for the presense of the ws ptr to determine if we
+ * support wscale at all */
+ if ((ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) &&
+ (q->flags & STREAMTCP_QUEUE_FLAG_WS))
+ {
+ ssn->client.wscale = q->wscale;
+ } else {
+ ssn->client.wscale = 0;
+ }
+
+ if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) &&
+ (q->flags & STREAMTCP_QUEUE_FLAG_SACK)) {
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ SCLogDebug("ssn %p: SACK permitted for session", ssn);
+ } else {
+ ssn->flags &= ~STREAMTCP_FLAG_SACKOK;
+ }
+
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+ SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 "", ssn,
+ ssn->server.next_win);
+ SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 "", ssn,
+ ssn->client.next_win);
+ SCLogDebug("ssn %p: ssn->server.isn %" PRIu32 ", "
+ "ssn->server.next_seq %" PRIu32 ", "
+ "ssn->server.last_ack %" PRIu32 " "
+ "(ssn->client.last_ack %" PRIu32 ")", ssn,
+ ssn->server.isn, ssn->server.next_seq,
+ ssn->server.last_ack, ssn->client.last_ack);
+
+ /* unset the 4WHS flag as we received this SYN/ACK as part of a
+ * (so far) valid 3WHS */
+ if (ssn->flags & STREAMTCP_FLAG_4WHS)
+ SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS unset, normal SYN/ACK"
+ " so considering 3WHS", ssn);
+
+ ssn->flags &=~ STREAMTCP_FLAG_4WHS;
+}
+
+/**
+ * \brief Function to handle the TCP_SYN_SENT state. The function handles
+ * SYN, SYN/ACK, RST packets and correspondingly changes the connection
+ * state.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateSynSent(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ SCLogDebug("ssn %p: pkt received: %s", ssn, PKT_IS_TOCLIENT(p) ?
+ "toclient":"toserver");
+
+ /* RST */
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ if (PKT_IS_TOSERVER(p)) {
+ if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) &&
+ SEQ_EQ(TCP_GET_WINDOW(p), 0) &&
+ SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1)))
+ {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received and state changed to "
+ "TCP_CLOSED", ssn);
+ }
+ } else {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received and state changed to "
+ "TCP_CLOSED", ssn);
+ }
+
+ /* FIN */
+ } else if (p->tcph->th_flags & TH_FIN) {
+ /** \todo */
+
+ /* SYN/ACK */
+ } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ if ((ssn->flags & STREAMTCP_FLAG_4WHS) && PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: SYN/ACK received on 4WHS session", ssn);
+
+ /* Check if the SYN/ACK packet ack's the earlier
+ * received SYN packet. */
+ if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->server.isn + 1))) {
+ StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_ACK);
+
+ SCLogDebug("ssn %p: 4WHS ACK mismatch, packet ACK %"PRIu32""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_ACK(p), ssn->server.isn + 1);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packet SEQ's the *FIRST* received SYN
+ * packet. */
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
+ StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_SYN);
+
+ SCLogDebug("ssn %p: 4WHS SEQ mismatch, packet SEQ %"PRIu32""
+ " != %" PRIu32 " from *first* SYN pkt", ssn,
+ TCP_GET_SEQ(p), ssn->client.isn);
+ return -1;
+ }
+
+
+ /* update state */
+ StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
+ SCLogDebug("ssn %p: =~ 4WHS ssn state is now TCP_SYN_RECV", ssn);
+
+ /* sequence number & window */
+ ssn->client.isn = TCP_GET_SEQ(p);
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn);
+ ssn->client.next_seq = ssn->client.isn + 1;
+
+ ssn->server.window = TCP_GET_WINDOW(p);
+ SCLogDebug("ssn %p: 4WHS window %" PRIu32 "", ssn,
+ ssn->client.window);
+
+ /* Set the timestamp values used to validate the timestamp of
+ * received packets. */
+ if ((p->tcpvars.ts != NULL) &&
+ (ssn->server.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP))
+ {
+ ssn->client.last_ts = TCP_GET_TSVAL(p);
+ SCLogDebug("ssn %p: 4WHS ssn->client.last_ts %" PRIu32" "
+ "ssn->server.last_ts %" PRIu32"", ssn,
+ ssn->client.last_ts, ssn->server.last_ts);
+ ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
+ ssn->client.last_pkt_ts = p->ts.tv_sec;
+ if (ssn->client.last_ts == 0)
+ ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ } else {
+ ssn->server.last_ts = 0;
+ ssn->client.last_ts = 0;
+ ssn->server.flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ }
+
+ ssn->server.last_ack = TCP_GET_ACK(p);
+ ssn->client.last_ack = ssn->client.isn + 1;
+
+ /** check for the presense of the ws ptr to determine if we
+ * support wscale at all */
+ if ((ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) &&
+ (p->tcpvars.ws != NULL))
+ {
+ ssn->server.wscale = TCP_GET_WSCALE(p);
+ } else {
+ ssn->server.wscale = 0;
+ }
+
+ if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) &&
+ TCP_GET_SACKOK(p) == 1) {
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ SCLogDebug("ssn %p: SACK permitted for 4WHS session", ssn);
+ }
+
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+ SCLogDebug("ssn %p: 4WHS ssn->client.next_win %" PRIu32 "", ssn,
+ ssn->client.next_win);
+ SCLogDebug("ssn %p: 4WHS ssn->server.next_win %" PRIu32 "", ssn,
+ ssn->server.next_win);
+ SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", "
+ "ssn->client.next_seq %" PRIu32 ", "
+ "ssn->client.last_ack %" PRIu32 " "
+ "(ssn->server.last_ack %" PRIu32 ")", ssn,
+ ssn->client.isn, ssn->client.next_seq,
+ ssn->client.last_ack, ssn->server.last_ack);
+
+ /* done here */
+ return 0;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION);
+ SCLogDebug("ssn %p: SYN/ACK received in the wrong direction", ssn);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packet ack's the earlier
+ * received SYN packet. */
+ if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.isn + 1))) {
+ StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_WITH_WRONG_ACK);
+ SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
+ ssn->client.isn + 1);
+ return -1;
+ }
+
+ StreamTcp3whsSynAckUpdate(ssn, p, /* no queue override */NULL);
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn);
+ if (ssn->flags & STREAMTCP_FLAG_4WHS) {
+ SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent of "
+ "4WHS SYN", ssn);
+ }
+
+ if (PKT_IS_TOCLIENT(p)) {
+ /** a SYN only packet in the opposite direction could be:
+ * http://www.breakingpointsystems.com/community/blog/tcp-
+ * portals-the-three-way-handshake-is-a-lie
+ *
+ * \todo improve resetting the session */
+
+ /* indicate that we're dealing with 4WHS here */
+ ssn->flags |= STREAMTCP_FLAG_4WHS;
+ SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS flag set", ssn);
+
+ /* set the sequence numbers and window for server
+ * We leave the ssn->client.isn in place as we will
+ * check the SYN/ACK pkt with that.
+ */
+ ssn->server.isn = TCP_GET_SEQ(p);
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
+ ssn->server.next_seq = ssn->server.isn + 1;
+
+ /* Set the stream timestamp value, if packet has timestamp
+ * option enabled. */
+ if (p->tcpvars.ts != NULL) {
+ ssn->server.last_ts = TCP_GET_TSVAL(p);
+ SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn,
+ p->tcpvars.ts, ssn->server.last_ts);
+
+ if (ssn->server.last_ts == 0)
+ ssn->server.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ ssn->server.last_pkt_ts = p->ts.tv_sec;
+ ssn->server.flags |= STREAMTCP_STREAM_FLAG_TIMESTAMP;
+ }
+
+ ssn->server.window = TCP_GET_WINDOW(p);
+ if (p->tcpvars.ws != NULL) {
+ ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE;
+ ssn->server.wscale = TCP_GET_WSCALE(p);
+ } else {
+ ssn->flags &= ~STREAMTCP_FLAG_SERVER_WSCALE;
+ ssn->server.wscale = 0;
+ }
+
+ if (TCP_GET_SACKOK(p) == 1) {
+ ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK;
+ } else {
+ ssn->flags &= ~STREAMTCP_FLAG_CLIENT_SACKOK;
+ }
+
+ SCLogDebug("ssn %p: 4WHS ssn->server.isn %" PRIu32 ", "
+ "ssn->server.next_seq %" PRIu32 ", "
+ "ssn->server.last_ack %"PRIu32"", ssn,
+ ssn->server.isn, ssn->server.next_seq,
+ ssn->server.last_ack);
+ SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", "
+ "ssn->client.next_seq %" PRIu32 ", "
+ "ssn->client.last_ack %"PRIu32"", ssn,
+ ssn->client.isn, ssn->client.next_seq,
+ ssn->client.last_ack);
+ }
+
+ /** \todo check if it's correct or set event */
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ /* Handle the asynchronous stream, when we receive a SYN packet
+ and now istead of receving a SYN/ACK we receive a ACK from the
+ same host, which sent the SYN, this suggests the ASNYC streams.*/
+ if (stream_config.async_oneside == FALSE)
+ return 0;
+
+ /* we are in AYNC (one side) mode now. */
+
+ /* one side async means we won't see a SYN/ACK, so we can
+ * only check the SYN. */
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))) {
+ StreamTcpSetEvent(p, STREAM_3WHS_ASYNC_WRONG_SEQ);
+
+ SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
+ "%" PRIu32 " from stream",ssn, TCP_GET_SEQ(p),
+ ssn->client.next_seq);
+ return -1;
+ }
+
+ ssn->flags |= STREAMTCP_FLAG_ASYNC;
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p);
+ ssn->client.last_ack = TCP_GET_SEQ(p);
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+
+ /* Set the server side parameters */
+ ssn->server.isn = TCP_GET_ACK(p) - 1;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
+ ssn->server.next_seq = ssn->server.isn + 1;
+ ssn->server.last_ack = ssn->server.next_seq;
+ ssn->server.next_win = ssn->server.last_ack;
+
+ SCLogDebug("ssn %p: synsent => Asynchronous stream, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
+ "ssn->client.next_seq %" PRIu32 ""
+ ,ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p)
+ + p->payload_len, ssn->client.next_seq);
+
+ /* if SYN had wscale, assume it to be supported. Otherwise
+ * we know it not to be supported. */
+ if (ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) {
+ ssn->client.wscale = TCP_WSCALE_MAX;
+ }
+
+ /* Set the timestamp values used to validate the timestamp of
+ * received packets.*/
+ if (p->tcpvars.ts != NULL &&
+ (ssn->client.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP))
+ {
+ ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
+ ssn->client.flags &= ~STREAMTCP_STREAM_FLAG_TIMESTAMP;
+ ssn->client.last_pkt_ts = p->ts.tv_sec;
+ } else {
+ ssn->client.last_ts = 0;
+ ssn->client.flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ }
+
+ if (ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) {
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_SYN_RECV state. The function handles
+ * SYN, SYN/ACK, ACK, FIN, RST packets and correspondingly changes
+ * the connection state.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+
+static int StreamTcpPacketStateSynRecv(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ uint8_t reset = TRUE;
+ /* After receiveing the RST in SYN_RECV state and if detection
+ evasion flags has been set, then the following operating
+ systems will not closed the connection. As they consider the
+ packet as stray packet and not belonging to the current
+ session, for more information check
+ http://www.packetstan.com/2010/06/recently-ive-been-on-campaign-to-make.html */
+ if (ssn->flags & STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT) {
+ if (PKT_IS_TOSERVER(p)) {
+ if ((ssn->server.os_policy == OS_POLICY_LINUX) ||
+ (ssn->server.os_policy == OS_POLICY_OLD_LINUX) ||
+ (ssn->server.os_policy == OS_POLICY_SOLARIS))
+ {
+ reset = FALSE;
+ SCLogDebug("Detection evasion has been attempted, so"
+ " not resetting the connection !!");
+ }
+ } else {
+ if ((ssn->client.os_policy == OS_POLICY_LINUX) ||
+ (ssn->client.os_policy == OS_POLICY_OLD_LINUX) ||
+ (ssn->client.os_policy == OS_POLICY_SOLARIS))
+ {
+ reset = FALSE;
+ SCLogDebug("Detection evasion has been attempted, so"
+ " not resetting the connection !!");
+ }
+ }
+ }
+
+ if (reset == TRUE) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received and state changed to "
+ "TCP_CLOSED", ssn);
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ /* FIN is handled in the same way as in TCP_ESTABLISHED case */;
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if ((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1)
+ return -1;
+
+ /* SYN/ACK */
+ } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV. resent", ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: SYN/ACK-pkt to server in SYN_RECV state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packets ACK matches the earlier
+ * received SYN/ACK packet. */
+ if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) {
+ SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
+ ssn->client.isn + 1);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packet SEQ the earlier
+ * received SYN/ACK packet, server resend with different ISN. */
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) {
+ SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_SEQ(p),
+ ssn->client.isn);
+
+ if (StreamTcp3whsQueueSynAck(ssn, p) == -1)
+ return -1;
+ SCLogDebug("ssn %p: queued different SYN/ACK", ssn);
+ }
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn);
+
+ if (PKT_IS_TOCLIENT(p)) {
+ SCLogDebug("ssn %p: SYN-pkt to client in SYN_RECV state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV);
+ return -1;
+ }
+
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
+ SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV);
+ return -1;
+ }
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->queue_len) {
+ SCLogDebug("ssn %p: checking ACK against queued SYN/ACKs", ssn);
+ TcpStateQueue *q = StreamTcp3whsFindSynAckByAck(ssn, p);
+ if (q != NULL) {
+ SCLogDebug("ssn %p: here we update state against queued SYN/ACK", ssn);
+ StreamTcp3whsSynAckUpdate(ssn, p, /* using queue to update state */q);
+ } else {
+ SCLogDebug("ssn %p: none found, now checking ACK against original SYN/ACK (state)", ssn);
+ }
+ }
+
+
+ /* If the timestamp option is enabled for both the streams, then
+ * validate the received packet timestamp value against the
+ * stream->last_ts. If the timestamp is valid then process the
+ * packet normally otherwise the drop the packet (RFC 1323)*/
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!(StreamTcpValidateTimestamp(ssn, p))) {
+ return -1;
+ }
+ }
+
+ if ((ssn->flags & STREAMTCP_FLAG_4WHS) && PKT_IS_TOCLIENT(p)) {
+ SCLogDebug("ssn %p: ACK received on 4WHS session",ssn);
+
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) {
+ SCLogDebug("ssn %p: 4WHS wrong seq nr on packet", ssn);
+ StreamTcpSetEvent(p, STREAM_4WHS_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: 4WHS invalid ack nr on packet", ssn);
+ StreamTcpSetEvent(p, STREAM_4WHS_INVALID_ACK);
+ return -1;
+ }
+
+ SCLogDebug("4WHS normal pkt");
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+ ssn->server.next_seq += p->payload_len;
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 ", "
+ "ssn->client.last_ack %"PRIu32"", ssn,
+ ssn->client.next_win, ssn->client.last_ack);
+ return 0;
+ }
+
+ /* Check if the ACK received is in right direction. But when we have
+ * picked up a mid stream session after missing the initial SYN pkt,
+ * in this case the ACK packet can arrive from either client (normal
+ * case) or from server itself (asynchronous streams). Therefore
+ * the check has been avoided in this case */
+ if (PKT_IS_TOCLIENT(p)) {
+ /* special case, handle 4WHS, so SYN/ACK in the opposite
+ * direction */
+ if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK) {
+ SCLogDebug("ssn %p: ACK received on midstream SYN/ACK "
+ "pickup session",ssn);
+ /* fall through */
+ } else {
+ SCLogDebug("ssn %p: ACK received in the wrong direction",
+ ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_ACK_IN_WRONG_DIR);
+ return -1;
+ }
+ }
+
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ""
+ ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p),
+ TCP_GET_ACK(p));
+
+ /* Check both seq and ack number before accepting the packet and
+ changing to ESTABLISHED state */
+ if ((SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) &&
+ SEQ_EQ(TCP_GET_ACK(p), ssn->server.next_seq)) {
+ SCLogDebug("normal pkt");
+
+ /* process the packet normal, No Async streams :) */
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ ssn->client.next_seq += p->payload_len;
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+
+ if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
+ ssn->server.next_win = ssn->server.last_ack +
+ ssn->server.window;
+ if (!(ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)) {
+ /* window scaling for midstream pickups, we can't do much
+ * other than assume that it's set to the max value: 14 */
+ ssn->server.wscale = TCP_WSCALE_MAX;
+ ssn->client.wscale = TCP_WSCALE_MAX;
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ }
+ }
+
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ /* If asynchronous stream handling is allowed then set the session,
+ if packet's seq number is equal the expected seq no.*/
+ } else if (stream_config.async_oneside == TRUE &&
+ (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq)))
+ {
+ /*set the ASYNC flag used to indicate the session as async stream
+ and helps in relaxing the windows checks.*/
+ ssn->flags |= STREAMTCP_FLAG_ASYNC;
+ ssn->server.next_seq += p->payload_len;
+ ssn->server.last_ack = TCP_GET_SEQ(p);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ ssn->client.last_ack = TCP_GET_ACK(p);
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
+ ssn->server.window = TCP_GET_WINDOW(p);
+ ssn->client.next_win = ssn->server.last_ack +
+ ssn->server.window;
+ /* window scaling for midstream pickups, we can't do much
+ * other than assume that it's set to the max value: 14 */
+ ssn->server.wscale = TCP_WSCALE_MAX;
+ ssn->client.wscale = TCP_WSCALE_MAX;
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ }
+
+ SCLogDebug("ssn %p: synrecv => Asynchronous stream, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
+ "ssn->server.next_seq %" PRIu32 "\n"
+ , ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p)
+ + p->payload_len, ssn->server.next_seq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ /* Upon receiving the packet with correct seq number and wrong
+ ACK number, it causes the other end to send RST. But some target
+ system (Linux & solaris) does not RST the connection, so it is
+ likely to avoid the detection */
+ } else if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)){
+ ssn->flags |= STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT;
+ SCLogDebug("ssn %p: wrong ack nr on packet, possible evasion!!",
+ ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION);
+ return -1;
+
+ /* if we get a packet with a proper ack, but a seq that is beyond
+ * next_seq but in-window, we probably missed some packets */
+ } else if (SEQ_GT(TCP_GET_SEQ(p), ssn->client.next_seq) &&
+ SEQ_LEQ(TCP_GET_SEQ(p),ssn->client.next_win) &&
+ SEQ_EQ(TCP_GET_ACK(p), ssn->server.next_seq))
+ {
+ SCLogDebug("ssn %p: ACK for missing data", ssn);
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+ SCLogDebug("ssn %p: ACK for missing data: ssn->client.next_seq %u", ssn, ssn->client.next_seq);
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+
+ if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
+ ssn->client.window = TCP_GET_WINDOW(p);
+ ssn->server.next_win = ssn->server.last_ack +
+ ssn->server.window;
+ /* window scaling for midstream pickups, we can't do much
+ * other than assume that it's set to the max value: 14 */
+ ssn->server.wscale = TCP_WSCALE_MAX;
+ ssn->client.wscale = TCP_WSCALE_MAX;
+ ssn->flags |= STREAMTCP_FLAG_SACKOK;
+ }
+
+ StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
+ SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ SCLogDebug("ssn %p: wrong seq nr on packet", ssn);
+
+ StreamTcpSetEvent(p, STREAM_3WHS_WRONG_SEQ_WRONG_ACK);
+ return -1;
+ }
+
+ SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 ", "
+ "ssn->server.last_ack %"PRIu32"", ssn,
+ ssn->server.next_win, ssn->server.last_ack);
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_ESTABLISHED state packets, which are
+ * sent by the client to server. The function handles
+ * ACK packets and call StreamTcpReassembleHandleSegment() to handle
+ * the reassembly.
+ *
+ * Timestamp has already been checked at this point.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity etc.
+ * \param ssn Pointer to the current TCP session
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+static int HandleEstablishedPacketToServer(ThreadVars *tv, TcpSession *ssn, Packet *p,
+ StreamTcpThread *stt, PacketQueue *pq)
+{
+ SCLogDebug("ssn %p: =+ pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ","
+ "ACK %" PRIu32 ", WIN %"PRIu16"", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p), TCP_GET_WINDOW(p));
+
+ if (StreamTcpValidateAck(ssn, &(ssn->server), p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_EST_INVALID_ACK);
+ return -1;
+ }
+
+ /* check for Keep Alive */
+ if ((p->payload_len == 0 || p->payload_len == 1) &&
+ (TCP_GET_SEQ(p) == (ssn->client.next_seq - 1))) {
+ SCLogDebug("ssn %p: pkt is keep alive", ssn);
+
+ /* normal pkt */
+ } else if (!(SEQ_GEQ((TCP_GET_SEQ(p)+p->payload_len), ssn->client.last_ack))) {
+ if (ssn->flags & STREAMTCP_FLAG_ASYNC) {
+ SCLogDebug("ssn %p: server => Asynchrouns stream, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
+ " ssn->client.last_ack %" PRIu32 ", ssn->client.next_win"
+ "%" PRIu32"(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->client.last_ack, ssn->client.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win);
+
+ /* update the last_ack to current seq number as the session is
+ * async and other stream is not updating it anymore :( */
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
+
+ } else if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p)) &&
+ (stream_config.async_oneside == TRUE) &&
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM)) {
+ SCLogDebug("ssn %p: server => Asynchronous stream, packet SEQ."
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
+ "ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
+ "%" PRIu32 "(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->client.last_ack, ssn->client.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win);
+
+ /* it seems we missed SYN and SYN/ACK packets of this session.
+ * Update the last_ack to current seq number as the session
+ * is async and other stream is not updating it anymore :( */
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
+ ssn->flags |= STREAMTCP_FLAG_ASYNC;
+
+ } else if (SEQ_EQ(ssn->client.last_ack, (ssn->client.isn + 1)) &&
+ (stream_config.async_oneside == TRUE) &&
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM)) {
+ SCLogDebug("ssn %p: server => Asynchronous stream, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
+ "ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
+ "%" PRIu32 "(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->client.last_ack, ssn->client.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win);
+
+ /* it seems we missed SYN and SYN/ACK packets of this session.
+ * Update the last_ack to current seq number as the session
+ * is async and other stream is not updating it anymore :(*/
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
+ ssn->flags |= STREAMTCP_FLAG_ASYNC;
+
+ /* if last ack is beyond next_seq, we have accepted ack's for missing data.
+ * In this case we do accept the data before last_ack if it is (partly)
+ * beyond next seq */
+ } else if (SEQ_GT(ssn->client.last_ack, ssn->client.next_seq) &&
+ SEQ_GT((TCP_GET_SEQ(p)+p->payload_len),ssn->client.next_seq))
+ {
+ SCLogDebug("ssn %p: PKT SEQ %"PRIu32" payload_len %"PRIu16
+ " before last_ack %"PRIu32", after next_seq %"PRIu32":"
+ " acked data that we haven't seen before",
+ ssn, TCP_GET_SEQ(p), p->payload_len, ssn->client.last_ack, ssn->client.next_seq);
+ if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq)) {
+ ssn->client.next_seq += p->payload_len;
+ }
+ } else {
+ SCLogDebug("ssn %p: server => SEQ before last_ack, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
+ "ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
+ "%" PRIu32 "(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->client.last_ack, ssn->client.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win);
+
+ SCLogDebug("ssn %p: rejecting because pkt before last_ack", ssn);
+ StreamTcpSetEvent(p, STREAM_EST_PKT_BEFORE_LAST_ACK);
+ return -1;
+ }
+ }
+
+ int zerowindowprobe = 0;
+ /* zero window probe */
+ if (p->payload_len == 1 && TCP_GET_SEQ(p) == ssn->client.next_seq && ssn->client.window == 0) {
+ SCLogDebug("ssn %p: zero window probe", ssn);
+ zerowindowprobe = 1;
+
+ /* expected packet */
+ } else if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+ ssn->client.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+ ssn, ssn->client.next_seq);
+ /* not completely as expected, but valid */
+ } else if (SEQ_LT(TCP_GET_SEQ(p),ssn->client.next_seq) &&
+ SEQ_GT((TCP_GET_SEQ(p)+p->payload_len), ssn->client.next_seq))
+ {
+ ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %"PRIu32
+ " (started before next_seq, ended after)",
+ ssn, ssn->client.next_seq);
+ }
+
+ /* in window check */
+ if (zerowindowprobe) {
+ SCLogDebug("ssn %p: zero window probe, skipping oow check", ssn);
+ } else if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ (ssn->flags & STREAMTCP_FLAG_ASYNC))
+ {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ SCLogDebug("ssn %p: ssn->server.window %"PRIu32"", ssn,
+ ssn->server.window);
+
+ /* Check if the ACK value is sane and inside the window limit */
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+ SCLogDebug("ack %u last_ack %u next_seq %u", TCP_GET_ACK(p), ssn->server.last_ack, ssn->server.next_seq);
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->server, p);
+
+ /* update next_win */
+ StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
+
+ /* handle data (if any) */
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
+ } else {
+ SCLogDebug("ssn %p: toserver => SEQ out of window, packet SEQ "
+ "%" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
+ "ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
+ "%" PRIu32 "(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->client.last_ack, ssn->client.next_win,
+ (TCP_GET_SEQ(p) + p->payload_len) - ssn->client.next_win);
+ SCLogDebug("ssn %p: window %u sacked %u", ssn, ssn->client.window,
+ StreamTcpSackedSize(&ssn->client));
+ StreamTcpSetEvent(p, STREAM_EST_PACKET_OUT_OF_WINDOW);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_ESTABLISHED state packets, which are
+ * sent by the server to client. The function handles
+ * ACK packets and call StreamTcpReassembleHandleSegment() to handle
+ * the reassembly
+ *
+ * Timestamp has already been checked at this point.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity etc.
+ * \param ssn Pointer to the current TCP session
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+static int HandleEstablishedPacketToClient(ThreadVars *tv, TcpSession *ssn, Packet *p,
+ StreamTcpThread *stt, PacketQueue *pq)
+{
+ SCLogDebug("ssn %p: =+ pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ","
+ " ACK %" PRIu32 ", WIN %"PRIu16"", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p), TCP_GET_WINDOW(p));
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_EST_INVALID_ACK);
+ return -1;
+ }
+
+ /* To get the server window value from the servers packet, when connection
+ is picked up as midstream */
+ if ((ssn->flags & STREAMTCP_FLAG_MIDSTREAM) &&
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED))
+ {
+ ssn->server.window = TCP_GET_WINDOW(p);
+ ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
+ ssn->flags &= ~STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED;
+ SCLogDebug("ssn %p: adjusted midstream ssn->server.next_win to "
+ "%" PRIu32 "", ssn, ssn->server.next_win);
+ }
+
+ /* check for Keep Alive */
+ if ((p->payload_len == 0 || p->payload_len == 1) &&
+ (TCP_GET_SEQ(p) == (ssn->server.next_seq - 1))) {
+ SCLogDebug("ssn %p: pkt is keep alive", ssn);
+
+ /* normal pkt */
+ } else if (!(SEQ_GEQ((TCP_GET_SEQ(p)+p->payload_len), ssn->server.last_ack))) {
+ if (ssn->flags & STREAMTCP_FLAG_ASYNC) {
+
+ SCLogDebug("ssn %p: client => Asynchrouns stream, packet SEQ"
+ " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
+ " ssn->client.last_ack %" PRIu32 ", ssn->client.next_win"
+ " %"PRIu32"(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->server.last_ack, ssn->server.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->server.next_win);
+
+ ssn->server.last_ack = TCP_GET_SEQ(p);
+
+ /* if last ack is beyond next_seq, we have accepted ack's for missing data.
+ * In this case we do accept the data before last_ack if it is (partly)
+ * beyond next seq */
+ } else if (SEQ_GT(ssn->server.last_ack, ssn->server.next_seq) &&
+ SEQ_GT((TCP_GET_SEQ(p)+p->payload_len),ssn->server.next_seq))
+ {
+ SCLogDebug("ssn %p: PKT SEQ %"PRIu32" payload_len %"PRIu16
+ " before last_ack %"PRIu32", after next_seq %"PRIu32":"
+ " acked data that we haven't seen before",
+ ssn, TCP_GET_SEQ(p), p->payload_len, ssn->server.last_ack, ssn->server.next_seq);
+ if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq)) {
+ ssn->server.next_seq += p->payload_len;
+ }
+ } else {
+ SCLogDebug("ssn %p: PKT SEQ %"PRIu32" payload_len %"PRIu16
+ " before last_ack %"PRIu32". next_seq %"PRIu32,
+ ssn, TCP_GET_SEQ(p), p->payload_len, ssn->server.last_ack, ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_EST_PKT_BEFORE_LAST_ACK);
+ return -1;
+ }
+ }
+
+ int zerowindowprobe = 0;
+ /* zero window probe */
+ if (p->payload_len == 1 && TCP_GET_SEQ(p) == ssn->server.next_seq && ssn->server.window == 0) {
+ SCLogDebug("ssn %p: zero window probe", ssn);
+ zerowindowprobe = 1;
+
+ /* expected packet */
+ } else if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+ ssn->server.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+ ssn, ssn->server.next_seq);
+ /* not completely as expected, but valid */
+ } else if (SEQ_LT(TCP_GET_SEQ(p),ssn->server.next_seq) &&
+ SEQ_GT((TCP_GET_SEQ(p)+p->payload_len), ssn->server.next_seq))
+ {
+ ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32
+ " (started before next_seq, ended after)",
+ ssn, ssn->server.next_seq);
+ }
+
+ if (zerowindowprobe) {
+ SCLogDebug("ssn %p: zero window probe, skipping oow check", ssn);
+ } else if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ (ssn->flags & STREAMTCP_FLAG_ASYNC)) {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ SCLogDebug("ssn %p: ssn->client.window %"PRIu32"", ssn,
+ ssn->client.window);
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->client, p);
+
+ StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->server, p, pq);
+ } else {
+ SCLogDebug("ssn %p: client => SEQ out of window, packet SEQ"
+ "%" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
+ " ssn->server.last_ack %" PRIu32 ", ssn->server.next_win "
+ "%" PRIu32 "(%"PRIu32")", ssn, TCP_GET_SEQ(p),
+ p->payload_len, TCP_GET_SEQ(p) + p->payload_len,
+ ssn->server.last_ack, ssn->server.next_win,
+ TCP_GET_SEQ(p) + p->payload_len - ssn->server.next_win);
+ StreamTcpSetEvent(p, STREAM_EST_PACKET_OUT_OF_WINDOW);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * \internal
+ *
+ * \brief Find the highest sequence number needed to consider all segments as ACK'd
+ *
+ * Used to treat all segments as ACK'd upon receiving a valid RST.
+ *
+ * \param stream stream to inspect the segments from
+ * \param seq sequence number to check against
+ *
+ * \retval ack highest ack we need to set
+ */
+static inline uint32_t StreamTcpResetGetMaxAck(TcpStream *stream, uint32_t seq)
+{
+ uint32_t ack = seq;
+
+ if (stream->seg_list_tail != NULL) {
+ if (SEQ_GT((stream->seg_list_tail->seq + stream->seg_list_tail->payload_len), ack))
+ {
+ ack = stream->seg_list_tail->seq + stream->seg_list_tail->payload_len;
+ }
+ }
+
+ SCReturnUInt(ack);
+}
+
+/**
+ * \brief Function to handle the TCP_ESTABLISHED state. The function handles
+ * ACK, FIN, RST packets and correspondingly changes the connection
+ * state. The function handles the data inside packets and call
+ * StreamTcpReassembleHandleSegment(tv, ) to handle the reassembling.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity etc.
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateEstablished(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received and state changed to "
+ "TCP_CLOSED", ssn);
+
+ ssn->server.next_seq = TCP_GET_ACK(p);
+ ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn,
+ ssn->server.next_seq);
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+
+ /* don't return packets to pools here just yet, the pseudo
+ * packet will take care, otherwise the normal session
+ * cleanup. */
+ } else {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received and state changed to "
+ "TCP_CLOSED", ssn);
+
+ ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1;
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn,
+ ssn->server.next_seq);
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+
+ /* don't return packets to pools here just yet, the pseudo
+ * packet will take care, otherwise the normal session
+ * cleanup. */
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ SCLogDebug("ssn (%p: FIN received SEQ"
+ " %" PRIu32 ", last ACK %" PRIu32 ", next win %"PRIu32","
+ " win %" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack, ssn->server.next_win,
+ ssn->server.window);
+
+ if ((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1)
+ return -1;
+
+ /* SYN/ACK */
+ } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: SYN/ACK-pkt to server in ESTABLISHED state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_EST_SYNACK_TOSERVER);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packets ACK matches the earlier
+ * received SYN/ACK packet. */
+ if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) {
+ SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
+ ssn->client.isn + 1);
+
+ StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK);
+ return -1;
+ }
+
+ /* Check if the SYN/ACK packet SEQ the earlier
+ * received SYN packet. */
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) {
+ SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
+ ssn->client.isn + 1);
+
+ StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ);
+ return -1;
+ }
+
+ if (ssn->flags & STREAMTCP_FLAG_3WHS_CONFIRMED) {
+ /* a resend of a SYN while we are established already -- fishy */
+ StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND);
+ return -1;
+ }
+
+ SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent. "
+ "Likely due server not receiving final ACK in 3whs", ssn);
+
+ /* resetting state to TCP_SYN_RECV as we should get another ACK now */
+ StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
+ SCLogDebug("ssn %p: =~ ssn state is now reset to TCP_SYN_RECV", ssn);
+ return 0;
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn);
+ if (PKT_IS_TOCLIENT(p)) {
+ SCLogDebug("ssn %p: SYN-pkt to client in EST state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_EST_SYN_TOCLIENT);
+ return -1;
+ }
+
+ if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
+ SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn);
+
+ StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND_DIFF_SEQ);
+ return -1;
+ }
+
+ /* a resend of a SYN while we are established already -- fishy */
+ StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ /* Urgent pointer size can be more than the payload size, as it tells
+ * the future coming data from the sender will be handled urgently
+ * until data of size equal to urgent offset has been processed
+ * (RFC 2147) */
+
+ /* If the timestamp option is enabled for both the streams, then
+ * validate the received packet timestamp value against the
+ * stream->last_ts. If the timestamp is valid then process the
+ * packet normally otherwise the drop the packet (RFC 1323) */
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ /* Process the received packet to server */
+ HandleEstablishedPacketToServer(tv, ssn, p, stt, pq);
+
+ SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 ","
+ " next win %" PRIu32 ", win %" PRIu32 "", ssn,
+ ssn->client.next_seq, ssn->server.last_ack
+ ,ssn->client.next_win, ssn->client.window);
+
+ } else { /* implied to client */
+ if (!(ssn->flags & STREAMTCP_FLAG_3WHS_CONFIRMED)) {
+ ssn->flags |= STREAMTCP_FLAG_3WHS_CONFIRMED;
+ SCLogDebug("3whs is now confirmed by server");
+ }
+
+ /* Process the received packet to client */
+ HandleEstablishedPacketToClient(tv, ssn, p, stt, pq);
+
+ SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 ","
+ " next win %" PRIu32 ", win %" PRIu32 "", ssn,
+ ssn->server.next_seq, ssn->client.last_ack,
+ ssn->server.next_win, ssn->server.window);
+ }
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the FIN packets for states TCP_SYN_RECV and
+ * TCP_ESTABLISHED and changes to another TCP state as required.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ *
+ * \retval 0 success
+ * \retval -1 something wrong with the packet
+ */
+
+static int StreamTcpHandleFin(ThreadVars *tv, StreamTcpThread *stt,
+ TcpSession *ssn, Packet *p, PacketQueue *pq)
+{
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ","
+ " ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p),
+ TCP_GET_ACK(p));
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN_INVALID_ACK);
+ return -1;
+ }
+
+ if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_SEQ(p),
+ ssn->client.next_seq);
+
+ StreamTcpSetEvent(p, STREAM_FIN_OUT_OF_WINDOW);
+ return -1;
+ }
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSE_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSE_WAIT", ssn);
+
+ if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))
+ ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", ssn,
+ ssn->client.next_seq);
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client packet
+ and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
+ ssn, ssn->client.next_seq, ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", "
+ "ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p),
+ TCP_GET_ACK(p));
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN_INVALID_ACK);
+ return -1;
+ }
+
+ if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != "
+ "%" PRIu32 " from stream", ssn, TCP_GET_SEQ(p),
+ ssn->server.next_seq);
+
+ StreamTcpSetEvent(p, STREAM_FIN_OUT_OF_WINDOW);
+ return -1;
+ }
+
+ StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT1);
+ SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT1", ssn);
+
+ if (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))
+ ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len;
+
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn,
+ ssn->server.next_seq);
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client packet
+ and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
+ ssn, ssn->server.next_seq, ssn->client.last_ack);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_FIN_WAIT1 state. The function handles
+ * ACK, FIN, RST packets and correspondingly changes the connection
+ * state.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ *
+ * \retval 0 success
+ * \retval -1 something wrong with the packet
+ */
+
+static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if ((p->tcph->th_flags & (TH_FIN|TH_ACK)) == (TH_FIN|TH_ACK)) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+ ssn->client.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+ ssn, ssn->client.next_seq);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+ ssn->server.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+ ssn, ssn->server.next_seq);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+ ssn->client.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+ ssn, ssn->client.next_seq);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+ ssn->server.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+ ssn, ssn->server.next_seq);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on FinWait1", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ ssn->flags & STREAMTCP_FLAG_ASYNC)
+ {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
+
+ if (TCP_GET_SEQ(p) == ssn->client.next_seq) {
+ StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
+ SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
+ }
+ } else {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+
+ StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+ ssn->client.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+ ssn, ssn->client.next_seq);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->server, p);
+
+ /* update next_win */
+ StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+
+ } else { /* implied to client */
+
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ (ssn->flags & STREAMTCP_FLAG_ASYNC))
+ {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
+
+ if (TCP_GET_SEQ(p) == ssn->server.next_seq) {
+ StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
+ SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
+ }
+ } else {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+ ssn->server.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+ ssn, ssn->server.next_seq);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->client, p);
+
+ /* update next_win */
+ StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+ } else {
+ SCLogDebug("ssn (%p): default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_FIN_WAIT2 state. The function handles
+ * ACK, RST, FIN packets and correspondingly changes the connection
+ * state.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq - 1) &&
+ SEQ_EQ(TCP_GET_ACK(p), ssn->server.last_ack)) {
+ SCLogDebug("ssn %p: retransmission", ssn);
+ retransmission = 1;
+ } else if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ "
+ "%" PRIu32 " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq - 1) &&
+ SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack)) {
+ SCLogDebug("ssn %p: retransmission", ssn);
+ retransmission = 1;
+ } else if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ "
+ "%" PRIu32 " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on FinWait2", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ (ssn->flags & STREAMTCP_FLAG_ASYNC))
+ {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
+
+ } else {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+ ssn->client.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+ ssn, ssn->client.next_seq);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->server, p);
+
+ /* update next_win */
+ StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
+ (ssn->flags & STREAMTCP_FLAG_ASYNC))
+ {
+ SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win "
+ "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
+ } else {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+ ssn->server.next_seq += p->payload_len;
+ SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+ ssn, ssn->server.next_seq);
+ }
+
+ StreamTcpSackUpdatePacket(&ssn->client, p);
+
+ /* update next_win */
+ StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_CLOSING state. Upon arrival of ACK
+ * the connection goes to TCP_TIME_WAIT state. The state has been
+ * reached as both end application has been closed.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on Closing", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else { /* implied to client */
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (TCP_GET_SEQ(p) != ssn->server.next_seq) {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK);
+ return -1;
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+ SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("StreamTcpPacketStateClosing (%p): =+ next SEQ "
+ "%" PRIu32 ", last ACK %" PRIu32 "", ssn,
+ ssn->server.next_seq, ssn->client.last_ack);
+ }
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_CLOSE_WAIT state. Upon arrival of FIN
+ * packet from server the connection goes to TCP_LAST_ACK state.
+ * The state is possible only for server host.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ SCEnter();
+
+ if (ssn == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (PKT_IS_TOCLIENT(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ } else {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ }
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ SCReturnInt(-1);
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW);
+ SCReturnInt(-1);
+ }
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ /* don't update to LAST_ACK here as we want a toclient FIN for that */
+
+ if (!retransmission)
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (!retransmission) {
+ if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+ SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW);
+ SCReturnInt(-1);
+ }
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK);
+ SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on CloseWait", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ SCReturnInt(-1);
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ SCReturnInt(-1);
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->client.last_ack))) {
+ SCLogDebug("ssn %p: -> retransmission", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK);
+ SCReturnInt(-1);
+
+ } else if (SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW);
+ SCReturnInt(-1);
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq))
+ ssn->client.next_seq += p->payload_len;
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ } else {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack))) {
+ SCLogDebug("ssn %p: -> retransmission", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK);
+ SCReturnInt(-1);
+
+ } else if (SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+ {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW);
+ SCReturnInt(-1);
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq))
+ ssn->server.next_seq += p->payload_len;
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+ }
+
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to handle the TCP_LAST_ACK state. Upon arrival of ACK
+ * the connection goes to TCP_CLOSED state and stream memory is
+ * returned back to pool. The state is possible only for server host.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateLastAck(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ /** \todo */
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on LastAck", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ }
+
+ if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq + 1) {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_LASTACK_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_LASTACK_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+ }
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to handle the TCP_TIME_WAIT state. Upon arrival of ACK
+ * the connection goes to TCP_CLOSED state and stream memory is
+ * returned back to pool.
+ *
+ * \param tv Thread Variable containig input/output queue, cpu affinity
+ * \param p Packet which has to be handled in this TCP state.
+ * \param stt Strean Thread module registered to handle the stream handling
+ */
+
+static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p,
+ StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
+{
+ if (ssn == NULL)
+ return -1;
+
+ if (p->tcph->th_flags & TH_RST) {
+ if (!StreamTcpValidateRst(ssn, p))
+ return -1;
+
+ /* force both streams to reassemble, if necessary */
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
+ ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ } else {
+ StreamTcpUpdateLastAck(ssn, &ssn->client,
+ StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server,
+ StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ }
+
+ } else if (p->tcph->th_flags & TH_FIN) {
+ /** \todo */
+
+ } else if (p->tcph->th_flags & TH_SYN) {
+ SCLogDebug("ssn (%p): SYN pkt on TimeWait", ssn);
+ StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);
+ return -1;
+
+ } else if (p->tcph->th_flags & TH_ACK) {
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p))
+ return -1;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+
+ } else if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq+1) {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->client.next_seq);
+ StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ);
+ return -1;
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
+
+ ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+ ssn->server.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->client.next_seq,
+ ssn->server.last_ack);
+
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+ } else {
+ SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+ "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+ TCP_GET_SEQ(p), TCP_GET_ACK(p));
+ int retransmission = 0;
+ if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+ SCLogDebug("ssn %p: packet is retransmission", ssn);
+ retransmission = 1;
+ } else if (TCP_GET_SEQ(p) != ssn->server.next_seq && TCP_GET_SEQ(p) != ssn->server.next_seq+1) {
+ if (p->payload_len > 0 && TCP_GET_SEQ(p) == ssn->server.last_ack) {
+ SCLogDebug("ssn %p: -> retransmission", ssn);
+ SCReturnInt(0);
+ } else {
+ SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+ " != %" PRIu32 " from stream", ssn,
+ TCP_GET_SEQ(p), ssn->server.next_seq);
+ StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ);
+ return -1;
+ }
+ }
+
+ if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK);
+ SCReturnInt(-1);
+ }
+
+ if (!retransmission) {
+ StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+ SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
+
+ ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+ }
+
+ StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ StreamTcpHandleTimestamp(ssn, p);
+ }
+
+ /* Update the next_seq, in case if we have missed the client
+ packet and server has already received and acked it */
+ if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+ ssn->client.next_seq = TCP_GET_ACK(p);
+
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+ "%" PRIu32 "", ssn, ssn->server.next_seq,
+ ssn->client.last_ack);
+
+ StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
+ }
+
+ } else {
+ SCLogDebug("ssn %p: default case", ssn);
+ }
+
+ return 0;
+}
+
+/**
+ * \retval 1 packet is a keep alive pkt
+ * \retval 0 packet is not a keep alive pkt
+ */
+static int StreamTcpPacketIsKeepAlive(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL, *ostream = NULL;
+ uint32_t seq;
+ uint32_t ack;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return 0;
+
+ /*
+ rfc 1122:
+ An implementation SHOULD send a keep-alive segment with no
+ data; however, it MAY be configurable to send a keep-alive
+ segment containing one garbage octet, for compatibility with
+ erroneous TCP implementations.
+ */
+ if (p->payload_len > 1)
+ return 0;
+
+ if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0) {
+ return 0;
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ ostream = &ssn->server;
+ } else {
+ stream = &ssn->server;
+ ostream = &ssn->client;
+ }
+
+ seq = TCP_GET_SEQ(p);
+ ack = TCP_GET_ACK(p);
+
+ if (ack == ostream->last_ack && seq == (stream->next_seq - 1)) {
+ SCLogDebug("packet is TCP keep-alive: %"PRIu64, p->pcap_cnt);
+ stream->flags |= STREAMTCP_STREAM_FLAG_KEEPALIVE;
+ return 1;
+ }
+ SCLogDebug("seq %u (%u), ack %u (%u)", seq, (stream->next_seq - 1), ack, ostream->last_ack);
+ return 0;
+}
+
+/**
+ * \retval 1 packet is a keep alive ACK pkt
+ * \retval 0 packet is not a keep alive ACK pkt
+ */
+static int StreamTcpPacketIsKeepAliveACK(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL, *ostream = NULL;
+ uint32_t seq;
+ uint32_t ack;
+ uint32_t pkt_win;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return 0;
+ /* should get a normal ACK to a Keep Alive */
+ if (p->payload_len > 0)
+ return 0;
+
+ if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0)
+ return 0;
+
+ if (TCP_GET_WINDOW(p) == 0)
+ return 0;
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ ostream = &ssn->server;
+ } else {
+ stream = &ssn->server;
+ ostream = &ssn->client;
+ }
+
+ seq = TCP_GET_SEQ(p);
+ ack = TCP_GET_ACK(p);
+
+ pkt_win = TCP_GET_WINDOW(p) << ostream->wscale;
+ if (pkt_win != ostream->window)
+ return 0;
+
+ if ((ostream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE) && ack == ostream->last_ack && seq == stream->next_seq) {
+ SCLogDebug("packet is TCP keep-aliveACK: %"PRIu64, p->pcap_cnt);
+ ostream->flags &= ~STREAMTCP_STREAM_FLAG_KEEPALIVE;
+ return 1;
+ }
+ SCLogDebug("seq %u (%u), ack %u (%u) FLAG_KEEPALIVE: %s", seq, stream->next_seq, ack, ostream->last_ack,
+ ostream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE ? "set" : "not set");
+ return 0;
+}
+
+static void StreamTcpClearKeepAliveFlag(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return;
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ } else {
+ stream = &ssn->server;
+ }
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE) {
+ stream->flags &= ~STREAMTCP_STREAM_FLAG_KEEPALIVE;
+ SCLogDebug("FLAG_KEEPALIVE cleared");
+ }
+}
+
+/**
+ * \retval 1 packet is a window update pkt
+ * \retval 0 packet is not a window update pkt
+ */
+static int StreamTcpPacketIsWindowUpdate(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL, *ostream = NULL;
+ uint32_t seq;
+ uint32_t ack;
+ uint32_t pkt_win;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return 0;
+
+ if (ssn->state < TCP_ESTABLISHED)
+ return 0;
+
+ if (p->payload_len > 0)
+ return 0;
+
+ if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0)
+ return 0;
+
+ if (TCP_GET_WINDOW(p) == 0)
+ return 0;
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ ostream = &ssn->server;
+ } else {
+ stream = &ssn->server;
+ ostream = &ssn->client;
+ }
+
+ seq = TCP_GET_SEQ(p);
+ ack = TCP_GET_ACK(p);
+
+ pkt_win = TCP_GET_WINDOW(p) << ostream->wscale;
+ if (pkt_win == ostream->window)
+ return 0;
+
+ if (ack == ostream->last_ack && seq == stream->next_seq) {
+ SCLogDebug("packet is TCP window update: %"PRIu64, p->pcap_cnt);
+ return 1;
+ }
+ SCLogDebug("seq %u (%u), ack %u (%u)", seq, stream->next_seq, ack, ostream->last_ack);
+ return 0;
+}
+
+/**
+ * Try to detect whether a packet is a valid FIN 4whs final ack.
+ *
+ */
+static int StreamTcpPacketIsFinShutdownAck(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL, *ostream = NULL;
+ uint32_t seq;
+ uint32_t ack;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return 0;
+ if (!(ssn->state == TCP_TIME_WAIT || ssn->state == TCP_CLOSE_WAIT || ssn->state == TCP_LAST_ACK))
+ return 0;
+ if (p->tcph->th_flags != TH_ACK)
+ return 0;
+ if (p->payload_len != 0)
+ return 0;
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ ostream = &ssn->server;
+ } else {
+ stream = &ssn->server;
+ ostream = &ssn->client;
+ }
+
+ seq = TCP_GET_SEQ(p);
+ ack = TCP_GET_ACK(p);
+
+ SCLogDebug("%"PRIu64", seq %u ack %u stream->next_seq %u ostream->next_seq %u",
+ p->pcap_cnt, seq, ack, stream->next_seq, ostream->next_seq);
+
+ if (SEQ_EQ(stream->next_seq + 1, seq) && SEQ_EQ(ack, ostream->next_seq + 1)) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Try to detect packets doing bad window updates
+ *
+ * See bug 1238.
+ *
+ * Find packets that are unexpected, and shrink the window to the point
+ * where the packets we do expect are rejected for being out of window.
+ *
+ * The logic we use here is:
+ * - packet seq > next_seq
+ * - packet ack > next_seq (packet acks unseen data)
+ * - packet shrinks window more than it's own data size
+ * - packet shrinks window more than the diff between it's ack and the
+ * last_ack value
+ *
+ * Packets coming in after packet loss can look quite a bit like this.
+ */
+static int StreamTcpPacketIsBadWindowUpdate(TcpSession *ssn, Packet *p)
+{
+ TcpStream *stream = NULL, *ostream = NULL;
+ uint32_t seq;
+ uint32_t ack;
+ uint32_t pkt_win;
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ return 0;
+
+ if (ssn->state < TCP_ESTABLISHED || ssn->state == TCP_CLOSED)
+ return 0;
+
+ if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0)
+ return 0;
+
+ if (PKT_IS_TOSERVER(p)) {
+ stream = &ssn->client;
+ ostream = &ssn->server;
+ } else {
+ stream = &ssn->server;
+ ostream = &ssn->client;
+ }
+
+ seq = TCP_GET_SEQ(p);
+ ack = TCP_GET_ACK(p);
+
+ pkt_win = TCP_GET_WINDOW(p) << ostream->wscale;
+
+ if (pkt_win < ostream->window) {
+ uint32_t diff = ostream->window - pkt_win;
+ if (diff > p->payload_len &&
+ SEQ_GT(ack, ostream->next_seq) &&
+ SEQ_GT(seq, stream->next_seq))
+ {
+ SCLogDebug("%"PRIu64", pkt_win %u, stream win %u, diff %u, dsize %u",
+ p->pcap_cnt, pkt_win, ostream->window, diff, p->payload_len);
+ SCLogDebug("%"PRIu64", pkt_win %u, stream win %u",
+ p->pcap_cnt, pkt_win, ostream->window);
+ SCLogDebug("%"PRIu64", seq %u ack %u ostream->next_seq %u ostream->last_ack %u, ostream->next_win %u, diff %u (%u)",
+ p->pcap_cnt, seq, ack, ostream->next_seq, ostream->last_ack, ostream->next_win,
+ ostream->next_seq - ostream->last_ack, stream->next_seq - stream->last_ack);
+
+ /* get the expected window shrinking from looking at ack vs last_ack.
+ * Observed a lot of just a little overrunning that value. So added some
+ * margin that is still ok. To make sure this isn't a loophole to still
+ * close the window, this is limited to windows above 1024. Both values
+ * are rather arbitrary. */
+ uint32_t adiff = ack - ostream->last_ack;
+ if (((pkt_win > 1024) && (diff > (adiff + 32))) ||
+ ((pkt_win <= 1024) && (diff > adiff)))
+ {
+ SCLogDebug("pkt ACK %u is %u bytes beyond last_ack %u, shrinks window by %u "
+ "(allowing 32 bytes extra): pkt WIN %u", ack, adiff, ostream->last_ack, diff, pkt_win);
+ SCLogDebug("%u - %u = %u (state %u)", diff, adiff, diff - adiff, ssn->state);
+ StreamTcpSetEvent(p, STREAM_PKT_BAD_WINDOW_UPDATE);
+ return 1;
+ }
+ }
+
+ }
+ SCLogDebug("seq %u (%u), ack %u (%u)", seq, stream->next_seq, ack, ostream->last_ack);
+ return 0;
+}
+
+/* flow is and stays locked */
+int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
+ PacketQueue *pq)
+{
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(p->flow);
+
+ SCLogDebug("p->pcap_cnt %"PRIu64, p->pcap_cnt);
+
+ /* assign the thread id to the flow */
+ if (unlikely(p->flow->thread_id == 0)) {
+ p->flow->thread_id = (FlowThreadId)tv->id;
+#ifdef DEBUG
+ } else if (unlikely((FlowThreadId)tv->id != p->flow->thread_id)) {
+ SCLogDebug("wrong thread: flow has %u, we are %d", p->flow->thread_id, tv->id);
+#endif
+ }
+
+ TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+
+ /* track TCP flags */
+ if (ssn != NULL) {
+ ssn->tcp_packet_flags |= p->tcph->th_flags;
+ if (PKT_IS_TOSERVER(p))
+ ssn->client.tcp_flags |= p->tcph->th_flags;
+ else if (PKT_IS_TOCLIENT(p))
+ ssn->server.tcp_flags |= p->tcph->th_flags;
+ }
+
+ /* update counters */
+ if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ StatsIncr(tv, stt->counter_tcp_synack);
+ } else if (p->tcph->th_flags & (TH_SYN)) {
+ StatsIncr(tv, stt->counter_tcp_syn);
+ }
+ if (p->tcph->th_flags & (TH_RST)) {
+ StatsIncr(tv, stt->counter_tcp_rst);
+ }
+
+ /* broken TCP http://ask.wireshark.org/questions/3183/acknowledgment-number-broken-tcp-the-acknowledge-field-is-nonzero-while-the-ack-flag-is-not-set */
+ if (!(p->tcph->th_flags & TH_ACK) && TCP_GET_ACK(p) != 0) {
+ StreamTcpSetEvent(p, STREAM_PKT_BROKEN_ACK);
+ }
+
+ /* If we are on IPS mode, and got a drop action triggered from
+ * the IP only module, or from a reassembled msg and/or from an
+ * applayer detection, then drop the rest of the packets of the
+ * same stream and avoid inspecting it any further */
+ if (StreamTcpCheckFlowDrops(p) == 1) {
+ SCLogDebug("This flow/stream triggered a drop rule");
+ FlowSetNoPacketInspectionFlag(p->flow);
+ DecodeSetNoPacketInspectionFlag(p);
+ StreamTcpDisableAppLayer(p->flow);
+ PACKET_DROP(p);
+ /* return the segments to the pool */
+ StreamTcpSessionPktFree(p);
+ SCReturnInt(0);
+ }
+
+ if (ssn == NULL || ssn->state == TCP_NONE) {
+ if (StreamTcpPacketStateNone(tv, p, stt, ssn, &stt->pseudo_queue) == -1) {
+ goto error;
+ }
+
+ if (ssn != NULL)
+ SCLogDebug("ssn->alproto %"PRIu16"", p->flow->alproto);
+ } else {
+ /* special case for PKT_PSEUDO_STREAM_END packets:
+ * bypass the state handling and various packet checks,
+ * we care about reassembly here. */
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ if (PKT_IS_TOCLIENT(p)) {
+ ssn->client.last_ack = TCP_GET_ACK(p);
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, p, pq);
+ } else {
+ ssn->server.last_ack = TCP_GET_ACK(p);
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, p, pq);
+ }
+ /* straight to 'skip' as we already handled reassembly */
+ goto skip;
+ }
+
+ /* check if the packet is in right direction, when we missed the
+ SYN packet and picked up midstream session. */
+ if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)
+ StreamTcpPacketSwitchDir(ssn, p);
+
+ if (StreamTcpPacketIsKeepAlive(ssn, p) == 1) {
+ goto skip;
+ }
+ if (StreamTcpPacketIsKeepAliveACK(ssn, p) == 1) {
+ StreamTcpClearKeepAliveFlag(ssn, p);
+ goto skip;
+ }
+ StreamTcpClearKeepAliveFlag(ssn, p);
+
+ /* if packet is not a valid window update, check if it is perhaps
+ * a bad window update that we should ignore (and alert on) */
+ if (StreamTcpPacketIsFinShutdownAck(ssn, p) == 0)
+ if (StreamTcpPacketIsWindowUpdate(ssn, p) == 0)
+ if (StreamTcpPacketIsBadWindowUpdate(ssn,p))
+ goto skip;
+
+ switch (ssn->state) {
+ case TCP_SYN_SENT:
+ if(StreamTcpPacketStateSynSent(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_SYN_RECV:
+ if(StreamTcpPacketStateSynRecv(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_ESTABLISHED:
+ if(StreamTcpPacketStateEstablished(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_FIN_WAIT1:
+ if(StreamTcpPacketStateFinWait1(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_FIN_WAIT2:
+ if(StreamTcpPacketStateFinWait2(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_CLOSING:
+ if(StreamTcpPacketStateClosing(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_CLOSE_WAIT:
+ if(StreamTcpPacketStateCloseWait(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_LAST_ACK:
+ if(StreamTcpPacketStateLastAck(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_TIME_WAIT:
+ if(StreamTcpPacketStateTimeWait(tv, p, stt, ssn, &stt->pseudo_queue)) {
+ goto error;
+ }
+ break;
+ case TCP_CLOSED:
+ /* TCP session memory is not returned to pool until timeout. */
+ SCLogDebug("packet received on closed state");
+ break;
+ default:
+ SCLogDebug("packet received on default state");
+ break;
+ }
+ skip:
+
+ if (ssn->state >= TCP_ESTABLISHED) {
+ p->flags |= PKT_STREAM_EST;
+ }
+ }
+
+ /* deal with a pseudo packet that is created upon receiving a RST
+ * segment. To be sure we process both sides of the connection, we
+ * inject a fake packet into the system, forcing reassembly of the
+ * opposing direction.
+ * There should be only one, but to be sure we do a while loop. */
+ if (ssn != NULL) {
+ while (stt->pseudo_queue.len > 0) {
+ SCLogDebug("processing pseudo packet / stream end");
+ Packet *np = PacketDequeue(&stt->pseudo_queue);
+ if (np != NULL) {
+ /* process the opposing direction of the original packet */
+ if (PKT_IS_TOSERVER(np)) {
+ SCLogDebug("pseudo packet is to server");
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->client, np, NULL);
+ } else {
+ SCLogDebug("pseudo packet is to client");
+ StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+ &ssn->server, np, NULL);
+ }
+
+ /* enqueue this packet so we inspect it in detect etc */
+ PacketEnqueue(pq, np);
+ }
+ SCLogDebug("processing pseudo packet / stream end done");
+ }
+
+ /* recalc the csum on the packet if it was modified */
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ReCalculateChecksum(p);
+ }
+
+ /* check for conditions that may make us not want to log this packet */
+
+ /* streams that hit depth */
+ if ((ssn->client.flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) ||
+ (ssn->server.flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED))
+ {
+ p->flags |= PKT_STREAM_NOPCAPLOG;
+ }
+
+ /* encrypted packets */
+ if ((PKT_IS_TOSERVER(p) && (ssn->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) ||
+ (PKT_IS_TOCLIENT(p) && (ssn->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)))
+ {
+ p->flags |= PKT_STREAM_NOPCAPLOG;
+ }
+ }
+
+ SCReturnInt(0);
+
+error:
+ /* make sure we don't leave packets in our pseudo queue */
+ while (stt->pseudo_queue.len > 0) {
+ Packet *np = PacketDequeue(&stt->pseudo_queue);
+ if (np != NULL) {
+ PacketEnqueue(pq, np);
+ }
+ }
+
+ /* recalc the csum on the packet if it was modified */
+ if (p->flags & PKT_STREAM_MODIFIED) {
+ ReCalculateChecksum(p);
+ }
+
+ if (StreamTcpInlineMode()) {
+ PACKET_DROP(p);
+ }
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Function to validate the checksum of the received packet. If the
+ * checksum is invalid, packet will be dropped, as the end system will
+ * also drop the packet.
+ *
+ * \param p Packet of which checksum has to be validated
+ * \retval 1 if the checksum is valid, otherwise 0
+ */
+static inline int StreamTcpValidateChecksum(Packet *p)
+{
+ int ret = 1;
+
+ if (p->flags & PKT_IGNORE_CHECKSUM)
+ return ret;
+
+ if (p->level4_comp_csum == -1) {
+ if (PKT_IS_IPV4(p)) {
+ p->level4_comp_csum = TCPCalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->tcph,
+ (p->payload_len +
+ TCP_GET_HLEN(p)));
+ } else if (PKT_IS_IPV6(p)) {
+ p->level4_comp_csum = TCPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->tcph,
+ (p->payload_len +
+ TCP_GET_HLEN(p)));
+ }
+ }
+
+ if (p->level4_comp_csum != p->tcph->th_sum) {
+ ret = 0;
+ SCLogDebug("Checksum of received packet %p is invalid",p);
+ if (p->livedev) {
+ (void) SC_ATOMIC_ADD(p->livedev->invalid_checksums, 1);
+ } else if (p->pcap_cnt) {
+ PcapIncreaseInvalidChecksum();
+ }
+ }
+
+ return ret;
+}
+
+/** \internal
+ * \brief check if a packet is a valid stream started
+ * \retval bool true/false */
+static int TcpSessionPacketIsStreamStarter(const Packet *p)
+{
+ if (p->tcph->th_flags == TH_SYN) {
+ SCLogDebug("packet %"PRIu64" is a stream starter: %02x", p->pcap_cnt, p->tcph->th_flags);
+ return 1;
+ }
+
+ if (stream_config.midstream == TRUE || stream_config.async_oneside == TRUE) {
+ if (p->tcph->th_flags == (TH_SYN|TH_ACK)) {
+ SCLogDebug("packet %"PRIu64" is a midstream stream starter: %02x", p->pcap_cnt, p->tcph->th_flags);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** \internal
+ * \brief Check if Flow and TCP SSN allow this flow/tuple to be reused
+ * \retval bool true yes reuse, false no keep tracking old ssn */
+static int TcpSessionReuseDoneEnoughSyn(const Packet *p, const Flow *f, const TcpSession *ssn)
+{
+ if (FlowGetPacketDirection(f, p) == TOSERVER) {
+ if (ssn == NULL) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p null. No reuse.", p->pcap_cnt, ssn);
+ return 0;
+ }
+ if (SEQ_EQ(ssn->client.isn, TCP_GET_SEQ(p))) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p. Packet SEQ == Stream ISN. Retransmission. Don't reuse.", p->pcap_cnt, ssn);
+ return 0;
+ }
+ if (ssn->state >= TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state >= TCP_LAST_ACK (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state == TCP_NONE) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state == TCP_NONE (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state < TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state < TCP_LAST_ACK (%u). Don't reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 0;
+ }
+
+ } else {
+ if (ssn == NULL) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p null. Reuse.", p->pcap_cnt, ssn);
+ return 1;
+ }
+ if (ssn->state >= TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state >= TCP_LAST_ACK (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state == TCP_NONE) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state == TCP_NONE (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state < TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state < TCP_LAST_ACK (%u). Don't reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 0;
+ }
+ }
+
+ SCLogDebug("default: how did we get here?");
+ return 0;
+}
+
+/** \internal
+ * \brief check if ssn is done enough for reuse by syn/ack
+ * \note should only be called if midstream is enabled
+ */
+static int TcpSessionReuseDoneEnoughSynAck(const Packet *p, const Flow *f, const TcpSession *ssn)
+{
+ if (FlowGetPacketDirection(f, p) == TOCLIENT) {
+ if (ssn == NULL) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p null. No reuse.", p->pcap_cnt, ssn);
+ return 0;
+ }
+ if (SEQ_EQ(ssn->server.isn, TCP_GET_SEQ(p))) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p. Packet SEQ == Stream ISN. Retransmission. Don't reuse.", p->pcap_cnt, ssn);
+ return 0;
+ }
+ if (ssn->state >= TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state >= TCP_LAST_ACK (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state == TCP_NONE) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state == TCP_NONE (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state < TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state < TCP_LAST_ACK (%u). Don't reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 0;
+ }
+
+ } else {
+ if (ssn == NULL) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p null. Reuse.", p->pcap_cnt, ssn);
+ return 1;
+ }
+ if (ssn->state >= TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state >= TCP_LAST_ACK (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state == TCP_NONE) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state == TCP_NONE (%u). Reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 1;
+ }
+ if (ssn->state < TCP_LAST_ACK) {
+ SCLogDebug("steam starter packet %"PRIu64", ssn %p state < TCP_LAST_ACK (%u). Don't reuse.", p->pcap_cnt, ssn, ssn->state);
+ return 0;
+ }
+ }
+
+ SCLogDebug("default: how did we get here?");
+ return 0;
+}
+
+/** \brief Check if SSN is done enough for reuse
+ *
+ * Reuse means a new TCP session reuses the tuple (flow in suri)
+ *
+ * \retval bool true if ssn can be reused, false if not */
+int TcpSessionReuseDoneEnough(const Packet *p, const Flow *f, const TcpSession *ssn)
+{
+ if (p->tcph->th_flags == TH_SYN) {
+ return TcpSessionReuseDoneEnoughSyn(p, f, ssn);
+ }
+
+ if (stream_config.midstream == TRUE || stream_config.async_oneside == TRUE) {
+ if (p->tcph->th_flags == (TH_SYN|TH_ACK)) {
+ return TcpSessionReuseDoneEnoughSynAck(p, f, ssn);
+ }
+ }
+
+ return 0;
+}
+
+int TcpSessionPacketSsnReuse(const Packet *p, const Flow *f, const void *tcp_ssn)
+{
+ if (p->proto == IPPROTO_TCP && p->tcph != NULL) {
+ if (TcpSessionPacketIsStreamStarter(p) == 1) {
+ if (TcpSessionReuseDoneEnough(p, f, tcp_ssn) == 1) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/** \brief Handle TCP reuse of tuple
+ *
+ * Logic:
+ * 1. see if packet could trigger a new session
+ * 2. see if the flow/ssn is in a state where we want to support the reuse
+ * 3. disconnect packet from the old flow
+ * -> at this point new packets can still find the old flow
+ * -> as the flow's reference count != 0, it can't disappear
+ * 4. setup a new flow unconditionally
+ * 5. attach packet to new flow
+ * 6. tag old flow as FLOW_TCP_REUSED
+ * -> NEW packets won't find it
+ * -> existing packets in our queues may still reference it
+ * 7. dereference the old flow (reference cnt *may* now be 0,
+ * if no other packets reference it)
+ *
+ * The packets that still hold a reference to the old flow are updated
+ * by HandleFlowReuseApplyToPacket()
+ */
+static void TcpSessionReuseHandle(Packet *p) {
+ if (likely(TcpSessionPacketIsStreamStarter(p) == 0))
+ return;
+
+ int reuse = 0;
+ FLOWLOCK_RDLOCK(p->flow);
+ reuse = TcpSessionReuseDoneEnough(p, p->flow, p->flow->protoctx);
+ if (!reuse) {
+ SCLogDebug("steam starter packet %"PRIu64", but state not "
+ "ready to be reused", p->pcap_cnt);
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ SCLogDebug("steam starter packet %"PRIu64", and state "
+ "ready to be reused", p->pcap_cnt);
+
+ /* ok, this packet needs a new flow */
+
+ /* first, get a reference to the old flow */
+ Flow *old_f = NULL;
+ FlowReference(&old_f, p->flow);
+
+ /* get some settings that we move over to the new flow */
+ FlowThreadId thread_id = old_f->thread_id;
+ int autofp_tmqh_flow_qid = SC_ATOMIC_GET(old_f->autofp_tmqh_flow_qid);
+
+ /* disconnect the packet from the old flow */
+ FlowHandlePacketUpdateRemove(p->flow, p);
+ FLOWLOCK_UNLOCK(p->flow);
+ FlowDeReference(&p->flow); // < can't disappear while usecnt >0
+
+ /* Can't tag flow as reused yet, would be a race condition:
+ * new packets will not get old flow because of FLOW_TCP_REUSED,
+ * so new flow may be created. This new flow could be handled in
+ * a different thread. */
+
+ /* Get a flow. It will be either a locked flow or NULL */
+ Flow *new_f = FlowGetFlowFromHashByPacket(p);
+ if (new_f == NULL) {
+ FlowDeReference(&old_f); // < can't disappear while usecnt >0
+ return;
+ }
+
+ /* update flow and packet */
+ FlowHandlePacketUpdate(new_f, p);
+ BUG_ON(new_f != p->flow);
+
+ /* copy flow balancing settings */
+ new_f->thread_id = thread_id;
+ SC_ATOMIC_SET(new_f->autofp_tmqh_flow_qid, autofp_tmqh_flow_qid);
+
+ FLOWLOCK_UNLOCK(new_f);
+
+ /* tag original flow that it's now unused */
+ FLOWLOCK_WRLOCK(old_f);
+ SCLogDebug("old flow %p tagged with FLOW_TCP_REUSED by packet %"PRIu64"!", old_f, p->pcap_cnt);
+ old_f->flags |= FLOW_TCP_REUSED;
+ FLOWLOCK_UNLOCK(old_f);
+ FlowDeReference(&old_f); // < can't disappear while usecnt >0
+
+ SCLogDebug("new flow %p set up for packet %"PRIu64"!", p->flow, p->pcap_cnt);
+}
+
+/** \brief Handle packets that reference the wrong flow because of TCP reuse
+ *
+ * In the case of TCP reuse we can have many packets that were assigned
+ * a flow by the capture/decode threads before the stream engine decided
+ * that a new flow was needed for these packets.
+ * When HandleFlowReuse creates a new flow, the packets already processed
+ * by the flow engine will still reference the old flow.
+ *
+ * This function detects this case and replaces the flow for those packets.
+ * It's a fairly expensive operation, but it should be rare as it's only
+ * done for packets that were already in the engine when the TCP reuse
+ * case was handled. New packets are assigned the correct flow by the
+ * flow engine.
+ */
+static void TcpSessionReuseHandleApplyToPacket(Packet *p)
+{
+ int need_flow_replace = 0;
+
+ FLOWLOCK_WRLOCK(p->flow);
+ if (p->flow->flags & FLOW_TCP_REUSED) {
+ SCLogDebug("packet %"PRIu64" attached to outdated flow and ssn", p->pcap_cnt);
+ need_flow_replace = 1;
+ }
+
+ if (likely(need_flow_replace == 0)) {
+ /* Work around a race condition: if HandleFlowReuse has inserted a new flow,
+ * it will not have seen both sides of the session yet. The packet we have here
+ * may be the first that got the flow directly from the hash right after the
+ * flow was added. In this case it won't have FLOW_PKT_ESTABLISHED flag set. */
+ if ((p->flow->flags & FLOW_TO_DST_SEEN) && (p->flow->flags & FLOW_TO_SRC_SEEN)) {
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ SCLogDebug("packet %"PRIu64" / flow %p: p->flowflags |= FLOW_PKT_ESTABLISHED (%u/%u)", p->pcap_cnt, p->flow, p->flow->todstpktcnt, p->flow->tosrcpktcnt);
+ } else {
+ SCLogDebug("packet %"PRIu64" / flow %p: p->flowflags NOT FLOW_PKT_ESTABLISHED (%u/%u)", p->pcap_cnt, p->flow, p->flow->todstpktcnt, p->flow->tosrcpktcnt);
+ }
+ SCLogDebug("packet %"PRIu64" attached to regular flow %p and ssn", p->pcap_cnt, p->flow);
+ FLOWLOCK_UNLOCK(p->flow);
+ return;
+ }
+
+ /* disconnect packet from old flow */
+ FlowHandlePacketUpdateRemove(p->flow, p);
+ FLOWLOCK_UNLOCK(p->flow);
+ FlowDeReference(&p->flow); // < can't disappear while usecnt >0
+
+ /* find the new flow that does belong to this packet */
+ Flow *new_f = FlowLookupFlowFromHash(p);
+ if (new_f == NULL) {
+ // TODO reset packet flag wrt flow: direction, HAS_FLOW etc
+ p->flags &= ~PKT_HAS_FLOW;
+ return;
+ }
+ FlowHandlePacketUpdate(new_f, p);
+ BUG_ON(new_f != p->flow);
+ FLOWLOCK_UNLOCK(new_f);
+ SCLogDebug("packet %"PRIu64" switched over to new flow %p!", p->pcap_cnt, p->flow);
+}
+
+TmEcode StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ StreamTcpThread *stt = (StreamTcpThread *)data;
+ TmEcode ret = TM_ECODE_OK;
+
+ if (!(PKT_IS_TCP(p)))
+ return TM_ECODE_OK;
+
+ if (p->flow == NULL) {
+ StatsIncr(tv, stt->counter_tcp_no_flow);
+ return TM_ECODE_OK;
+ }
+
+ if (stream_config.flags & STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION) {
+ if (StreamTcpValidateChecksum(p) == 0) {
+ StatsIncr(tv, stt->counter_tcp_invalid_checksum);
+ return TM_ECODE_OK;
+ }
+ } else {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+
+ if (stt->runmode_flow_stream_async) {
+ /* "autofp" handling of TCP session/flow reuse */
+ if (!(p->flags & PKT_PSEUDO_STREAM_END)) {
+ /* apply previous reuses to this packet */
+ TcpSessionReuseHandleApplyToPacket(p);
+ if (p->flow == NULL)
+ return ret;
+
+ if (!(p->flowflags & FLOW_PKT_TOSERVER_FIRST)) {
+ /* after that, check for 'new' reuse */
+ TcpSessionReuseHandle(p);
+ if (p->flow == NULL)
+ return ret;
+ }
+ }
+ }
+ AppLayerProfilingReset(stt->ra_ctx->app_tctx);
+
+ FLOWLOCK_WRLOCK(p->flow);
+ ret = StreamTcpPacket(tv, p, stt, pq);
+ FLOWLOCK_UNLOCK(p->flow);
+
+ //if (ret)
+ // return TM_ECODE_FAILED;
+
+ stt->pkts++;
+ return ret;
+}
+
+TmEcode StreamTcpThreadInit(ThreadVars *tv, void *initdata, void **data)
+{
+ SCEnter();
+ StreamTcpThread *stt = SCMalloc(sizeof(StreamTcpThread));
+ if (unlikely(stt == NULL))
+ SCReturnInt(TM_ECODE_FAILED);
+ memset(stt, 0, sizeof(StreamTcpThread));
+ stt->ssn_pool_id = -1;
+
+ *data = (void *)stt;
+
+ stt->counter_tcp_sessions = StatsRegisterCounter("tcp.sessions", tv);
+ stt->counter_tcp_ssn_memcap = StatsRegisterCounter("tcp.ssn_memcap_drop", tv);
+ stt->counter_tcp_pseudo = StatsRegisterCounter("tcp.pseudo", tv);
+ stt->counter_tcp_pseudo_failed = StatsRegisterCounter("tcp.pseudo_failed", tv);
+ stt->counter_tcp_invalid_checksum = StatsRegisterCounter("tcp.invalid_checksum", tv);
+ stt->counter_tcp_no_flow = StatsRegisterCounter("tcp.no_flow", tv);
+ stt->counter_tcp_syn = StatsRegisterCounter("tcp.syn", tv);
+ stt->counter_tcp_synack = StatsRegisterCounter("tcp.synack", tv);
+ stt->counter_tcp_rst = StatsRegisterCounter("tcp.rst", tv);
+
+ /* init reassembly ctx */
+ stt->ra_ctx = StreamTcpReassembleInitThreadCtx(tv);
+ if (stt->ra_ctx == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ stt->ra_ctx->counter_tcp_segment_memcap = StatsRegisterCounter("tcp.segment_memcap_drop", tv);
+ stt->ra_ctx->counter_tcp_stream_depth = StatsRegisterCounter("tcp.stream_depth_reached", tv);
+ stt->ra_ctx->counter_tcp_reass_gap = StatsRegisterCounter("tcp.reassembly_gap", tv);
+
+ SCLogDebug("StreamTcp thread specific ctx online at %p, reassembly ctx %p",
+ stt, stt->ra_ctx);
+
+ SCMutexLock(&ssn_pool_mutex);
+ if (ssn_pool == NULL) {
+ ssn_pool = PoolThreadInit(1, /* thread */
+ 0, /* unlimited */
+ stream_config.prealloc_sessions,
+ sizeof(TcpSession),
+ StreamTcpSessionPoolAlloc,
+ StreamTcpSessionPoolInit, NULL,
+ StreamTcpSessionPoolCleanup, NULL);
+ stt->ssn_pool_id = 0;
+ SCLogDebug("pool size %d, thread ssn_pool_id %d", PoolThreadSize(ssn_pool), stt->ssn_pool_id);
+ } else {
+ /* grow ssn_pool until we have a element for our thread id */
+ stt->ssn_pool_id = PoolThreadGrow(ssn_pool,
+ 0, /* unlimited */
+ stream_config.prealloc_sessions,
+ sizeof(TcpSession),
+ StreamTcpSessionPoolAlloc,
+ StreamTcpSessionPoolInit, NULL,
+ StreamTcpSessionPoolCleanup, NULL);
+ SCLogDebug("pool size %d, thread ssn_pool_id %d", PoolThreadSize(ssn_pool), stt->ssn_pool_id);
+ }
+ SCMutexUnlock(&ssn_pool_mutex);
+ if (stt->ssn_pool_id < 0 || ssn_pool == NULL)
+ SCReturnInt(TM_ECODE_FAILED);
+
+ /* see if need to enable the TCP reuse handling in the stream engine */
+ stt->runmode_flow_stream_async = RunmodeGetFlowStreamAsync();
+ SCLogDebug("Flow and Stream engine run %s",
+ stt->runmode_flow_stream_async ? "asynchronous" : "synchronous");
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode StreamTcpThreadDeinit(ThreadVars *tv, void *data)
+{
+ SCEnter();
+ StreamTcpThread *stt = (StreamTcpThread *)data;
+ if (stt == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ /* XXX */
+
+ /* free reassembly ctx */
+ StreamTcpReassembleFreeThreadCtx(stt->ra_ctx);
+
+ /* clear memory */
+ memset(stt, 0, sizeof(StreamTcpThread));
+
+ SCFree(stt);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void StreamTcpExitPrintStats(ThreadVars *tv, void *data)
+{
+ StreamTcpThread *stt = (StreamTcpThread *)data;
+ if (stt == NULL) {
+ return;
+ }
+
+ SCLogInfo("Stream TCP processed %" PRIu64 " TCP packets", stt->pkts);
+}
+
+/**
+ * \brief Function to check the validity of the RST packets based on the
+ * target OS of the given packet.
+ *
+ * \param ssn TCP session to which the given packet belongs
+ * \param p Packet which has to be checked for its validity
+ *
+ * \retval 0 unacceptable RST
+ * \retval 1 acceptable RST
+ *
+ * WebSense sends RST packets that are:
+ * - RST flag, win 0, ack 0, seq = nextseq
+ *
+ */
+
+static int StreamTcpValidateRst(TcpSession *ssn, Packet *p)
+{
+
+ uint8_t os_policy;
+
+ if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+ if (!StreamTcpValidateTimestamp(ssn, p)) {
+ SCReturnInt(0);
+ }
+ }
+
+ /* Set up the os_policy to be used in validating the RST packets based on
+ target system */
+ if (PKT_IS_TOSERVER(p)) {
+ if (ssn->server.os_policy == 0)
+ StreamTcpSetOSPolicy(&ssn->server, p);
+
+ os_policy = ssn->server.os_policy;
+
+ if (p->tcph->th_flags & TH_ACK &&
+ TCP_GET_ACK(p) && StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_RST_INVALID_ACK);
+ SCReturnInt(0);
+ }
+
+ } else {
+ if (ssn->client.os_policy == 0)
+ StreamTcpSetOSPolicy(&ssn->client, p);
+
+ os_policy = ssn->client.os_policy;
+
+ if (p->tcph->th_flags & TH_ACK &&
+ TCP_GET_ACK(p) && StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+ SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+ StreamTcpSetEvent(p, STREAM_RST_INVALID_ACK);
+ SCReturnInt(0);
+ }
+ }
+
+ switch (os_policy) {
+ case OS_POLICY_HPUX11:
+ if(PKT_IS_TOSERVER(p)){
+ if(SEQ_GEQ(TCP_GET_SEQ(p), ssn->client.next_seq)) {
+ SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ } else {
+ SCLogDebug("reset is not Valid! Packet SEQ: %" PRIu32 " "
+ "and server SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->client.next_seq);
+ return 0;
+ }
+ } else { /* implied to client */
+ if(SEQ_GEQ(TCP_GET_SEQ(p), ssn->server.next_seq)) {
+ SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ } else {
+ SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " "
+ "and client SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->server.next_seq);
+ return 0;
+ }
+ }
+ break;
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ case OS_POLICY_SOLARIS:
+ if(PKT_IS_TOSERVER(p)){
+ if(SEQ_GEQ((TCP_GET_SEQ(p)+p->payload_len),
+ ssn->client.last_ack))
+ { /*window base is needed !!*/
+ if(SEQ_LT(TCP_GET_SEQ(p),
+ (ssn->client.next_seq + ssn->client.window)))
+ {
+ SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ }
+ } else {
+ SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and"
+ " server SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->client.next_seq);
+ return 0;
+ }
+ } else { /* implied to client */
+ if(SEQ_GEQ((TCP_GET_SEQ(p) + p->payload_len),
+ ssn->server.last_ack))
+ { /*window base is needed !!*/
+ if(SEQ_LT(TCP_GET_SEQ(p),
+ (ssn->server.next_seq + ssn->server.window)))
+ {
+ SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ }
+ } else {
+ SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and"
+ " client SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->server.next_seq);
+ return 0;
+ }
+ }
+ break;
+ default:
+ case OS_POLICY_BSD:
+ case OS_POLICY_FIRST:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_LAST:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ if(PKT_IS_TOSERVER(p)) {
+ if(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) {
+ SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ } else {
+ SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " "
+ "and server SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->client.next_seq);
+ return 0;
+ }
+ } else { /* implied to client */
+ if(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq)) {
+ SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "",
+ TCP_GET_SEQ(p));
+ return 1;
+ } else {
+ SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and"
+ " client SEQ: %" PRIu32 "", TCP_GET_SEQ(p),
+ ssn->server.next_seq);
+ return 0;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/**
+ * \brief Function to check the validity of the received timestamp based on
+ * the target OS of the given stream.
+ *
+ * It's passive except for:
+ * 1. it sets the os policy on the stream if necessary
+ * 2. it sets an event in the packet if necessary
+ *
+ * \param ssn TCP session to which the given packet belongs
+ * \param p Packet which has to be checked for its validity
+ *
+ * \retval 1 if the timestamp is valid
+ * \retval 0 if the timestamp is invalid
+ */
+static int StreamTcpValidateTimestamp (TcpSession *ssn, Packet *p)
+{
+ SCEnter();
+
+ TcpStream *sender_stream;
+ TcpStream *receiver_stream;
+ uint8_t ret = 1;
+ uint8_t check_ts = 1;
+
+ if (PKT_IS_TOSERVER(p)) {
+ sender_stream = &ssn->client;
+ receiver_stream = &ssn->server;
+ } else {
+ sender_stream = &ssn->server;
+ receiver_stream = &ssn->client;
+ }
+
+ /* Set up the os_policy to be used in validating the timestamps based on
+ the target system */
+ if (receiver_stream->os_policy == 0) {
+ StreamTcpSetOSPolicy(receiver_stream, p);
+ }
+
+ if (p->tcpvars.ts != NULL) {
+ uint32_t ts = TCP_GET_TSVAL(p);
+ uint32_t last_pkt_ts = sender_stream->last_pkt_ts;
+ uint32_t last_ts = sender_stream->last_ts;
+
+ if (sender_stream->flags & STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP) {
+ /* The 3whs used the timestamp with 0 value. */
+ switch (receiver_stream->os_policy) {
+ case OS_POLICY_LINUX:
+ case OS_POLICY_WINDOWS2K3:
+ /* Linux and windows 2003 does not allow the use of 0 as
+ * timestamp in the 3whs. */
+ check_ts = 0;
+ break;
+
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_VISTA:
+ if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) {
+ last_ts = ts;
+ check_ts = 0; /*next packet will be checked for validity
+ and stream TS has been updated with this
+ one.*/
+ }
+ break;
+ }
+ }
+
+ if (receiver_stream->os_policy == OS_POLICY_HPUX11) {
+ /* HPUX11 igoners the timestamp of out of order packets */
+ if (!SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
+ check_ts = 0;
+ }
+
+ if (ts == 0) {
+ switch (receiver_stream->os_policy) {
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_SOLARIS:
+ /* Old Linux and windows allowed packet with 0 timestamp. */
+ break;
+ default:
+ /* other OS simply drop the pakcet with 0 timestamp, when
+ * 3whs has valid timestamp*/
+ goto invalid;
+ }
+ }
+
+ if (check_ts) {
+ int32_t result = 0;
+
+ SCLogDebug("ts %"PRIu32", last_ts %"PRIu32"", ts, last_ts);
+
+ if (receiver_stream->os_policy == OS_POLICY_LINUX) {
+ /* Linux accepts TS which are off by one.*/
+ result = (int32_t) ((ts - last_ts) + 1);
+ } else {
+ result = (int32_t) (ts - last_ts);
+ }
+
+ SCLogDebug("result %"PRIi32", p->ts.tv_sec %"PRIuMAX"", result, (uintmax_t)p->ts.tv_sec);
+
+ if (last_pkt_ts == 0 &&
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM))
+ {
+ last_pkt_ts = p->ts.tv_sec;
+ }
+
+ if (result < 0) {
+ SCLogDebug("timestamp is not valid last_ts "
+ "%" PRIu32 " p->tcpvars->ts %" PRIu32 " result "
+ "%" PRId32 "", last_ts, ts, result);
+ /* candidate for rejection */
+ ret = 0;
+ } else if ((sender_stream->last_ts != 0) &&
+ (((uint32_t) p->ts.tv_sec) >
+ last_pkt_ts + PAWS_24DAYS))
+ {
+ SCLogDebug("packet is not valid last_pkt_ts "
+ "%" PRIu32 " p->ts.tv_sec %" PRIu32 "",
+ last_pkt_ts, (uint32_t) p->ts.tv_sec);
+ /* candidate for rejection */
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ /* if the timestamp of packet is not valid then, check if the
+ * current stream timestamp is not so old. if so then we need to
+ * accept the packet and update the stream->last_ts (RFC 1323)*/
+ if ((SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) &&
+ (((uint32_t) p->ts.tv_sec > (last_pkt_ts + PAWS_24DAYS))))
+ {
+ SCLogDebug("timestamp considered valid anyway");
+ } else {
+ goto invalid;
+ }
+ }
+ }
+ }
+
+ SCReturnInt(1);
+
+invalid:
+ StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP);
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to check the validity of the received timestamp based on
+ * the target OS of the given stream and update the session.
+ *
+ * \param ssn TCP session to which the given packet belongs
+ * \param p Packet which has to be checked for its validity
+ *
+ * \retval 1 if the timestamp is valid
+ * \retval 0 if the timestamp is invalid
+ */
+static int StreamTcpHandleTimestamp (TcpSession *ssn, Packet *p)
+{
+ SCEnter();
+
+ TcpStream *sender_stream;
+ TcpStream *receiver_stream;
+ uint8_t ret = 1;
+ uint8_t check_ts = 1;
+
+ if (PKT_IS_TOSERVER(p)) {
+ sender_stream = &ssn->client;
+ receiver_stream = &ssn->server;
+ } else {
+ sender_stream = &ssn->server;
+ receiver_stream = &ssn->client;
+ }
+
+ /* Set up the os_policy to be used in validating the timestamps based on
+ the target system */
+ if (receiver_stream->os_policy == 0) {
+ StreamTcpSetOSPolicy(receiver_stream, p);
+ }
+
+ if (p->tcpvars.ts != NULL) {
+ uint32_t ts = TCP_GET_TSVAL(p);
+
+ if (sender_stream->flags & STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP) {
+ /* The 3whs used the timestamp with 0 value. */
+ switch (receiver_stream->os_policy) {
+ case OS_POLICY_LINUX:
+ case OS_POLICY_WINDOWS2K3:
+ /* Linux and windows 2003 does not allow the use of 0 as
+ * timestamp in the 3whs. */
+ ssn->flags &= ~STREAMTCP_FLAG_TIMESTAMP;
+ check_ts = 0;
+ break;
+
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_VISTA:
+ sender_stream->flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP;
+ if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) {
+ sender_stream->last_ts = ts;
+ check_ts = 0; /*next packet will be checked for validity
+ and stream TS has been updated with this
+ one.*/
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (receiver_stream->os_policy == OS_POLICY_HPUX11) {
+ /*HPUX11 igoners the timestamp of out of order packets*/
+ if (!SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
+ check_ts = 0;
+ }
+
+ if (ts == 0) {
+ switch (receiver_stream->os_policy) {
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_SOLARIS:
+ /* Old Linux and windows allowed packet with 0 timestamp. */
+ break;
+ default:
+ /* other OS simply drop the pakcet with 0 timestamp, when
+ * 3whs has valid timestamp*/
+ goto invalid;
+ }
+ }
+
+ if (check_ts) {
+ int32_t result = 0;
+
+ SCLogDebug("ts %"PRIu32", last_ts %"PRIu32"", ts, sender_stream->last_ts);
+
+ if (receiver_stream->os_policy == OS_POLICY_LINUX) {
+ /* Linux accepts TS which are off by one.*/
+ result = (int32_t) ((ts - sender_stream->last_ts) + 1);
+ } else {
+ result = (int32_t) (ts - sender_stream->last_ts);
+ }
+
+ SCLogDebug("result %"PRIi32", p->ts.tv_sec %"PRIuMAX"", result, (uintmax_t)p->ts.tv_sec);
+
+ if (sender_stream->last_pkt_ts == 0 &&
+ (ssn->flags & STREAMTCP_FLAG_MIDSTREAM))
+ {
+ sender_stream->last_pkt_ts = p->ts.tv_sec;
+ }
+
+ if (result < 0) {
+ SCLogDebug("timestamp is not valid sender_stream->last_ts "
+ "%" PRIu32 " p->tcpvars->ts %" PRIu32 " result "
+ "%" PRId32 "", sender_stream->last_ts, ts, result);
+ /* candidate for rejection */
+ ret = 0;
+ } else if ((sender_stream->last_ts != 0) &&
+ (((uint32_t) p->ts.tv_sec) >
+ sender_stream->last_pkt_ts + PAWS_24DAYS))
+ {
+ SCLogDebug("packet is not valid sender_stream->last_pkt_ts "
+ "%" PRIu32 " p->ts.tv_sec %" PRIu32 "",
+ sender_stream->last_pkt_ts, (uint32_t) p->ts.tv_sec);
+ /* candidate for rejection */
+ ret = 0;
+ }
+
+ if (ret == 1) {
+ /* Update the timestamp and last seen packet time for this
+ * stream */
+ if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
+ sender_stream->last_ts = ts;
+
+ sender_stream->last_pkt_ts = p->ts.tv_sec;
+
+ } else if (ret == 0) {
+ /* if the timestamp of packet is not valid then, check if the
+ * current stream timestamp is not so old. if so then we need to
+ * accept the packet and update the stream->last_ts (RFC 1323)*/
+ if ((SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) &&
+ (((uint32_t) p->ts.tv_sec > (sender_stream->last_pkt_ts + PAWS_24DAYS))))
+ {
+ sender_stream->last_ts = ts;
+ sender_stream->last_pkt_ts = p->ts.tv_sec;
+
+ SCLogDebug("timestamp considered valid anyway");
+ } else {
+ goto invalid;
+ }
+ }
+ }
+ } else {
+ /* Solaris stops using timestamps if a packet is received
+ without a timestamp and timestamps were used on that stream. */
+ if (receiver_stream->os_policy == OS_POLICY_SOLARIS)
+ ssn->flags &= ~STREAMTCP_FLAG_TIMESTAMP;
+ }
+
+ SCReturnInt(1);
+
+invalid:
+ StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP);
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to test the received ACK values against the stream window
+ * and previous ack value. ACK values should be higher than previous
+ * ACK value and less than the next_win value.
+ *
+ * \param ssn TcpSession for state access
+ * \param stream TcpStream of which last_ack needs to be tested
+ * \param p Packet which is used to test the last_ack
+ *
+ * \retval 0 ACK is valid, last_ack is updated if ACK was higher
+ * \retval -1 ACK is invalid
+ */
+static inline int StreamTcpValidateAck(TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+
+ uint32_t ack = TCP_GET_ACK(p);
+
+ /* fast track */
+ if (SEQ_GT(ack, stream->last_ack) && SEQ_LEQ(ack, stream->next_win))
+ {
+ SCLogDebug("ACK in bounds");
+ SCReturnInt(0);
+ }
+ /* fast track */
+ else if (SEQ_EQ(ack, stream->last_ack)) {
+ SCLogDebug("pkt ACK %"PRIu32" == stream last ACK %"PRIu32, TCP_GET_ACK(p), stream->last_ack);
+ SCReturnInt(0);
+ }
+
+ /* exception handling */
+ if (SEQ_LT(ack, stream->last_ack)) {
+ SCLogDebug("pkt ACK %"PRIu32" < stream last ACK %"PRIu32, TCP_GET_ACK(p), stream->last_ack);
+
+ /* This is an attempt to get a 'left edge' value that we can check against.
+ * It doesn't work when the window is 0, need to think of a better way. */
+
+ if (stream->window != 0 && SEQ_LT(ack, (stream->last_ack - stream->window))) {
+ SCLogDebug("ACK %"PRIu32" is before last_ack %"PRIu32" - window "
+ "%"PRIu32" = %"PRIu32, ack, stream->last_ack,
+ stream->window, stream->last_ack - stream->window);
+ goto invalid;
+ }
+
+ SCReturnInt(0);
+ }
+
+ if (ssn->state > TCP_SYN_SENT && SEQ_GT(ack, stream->next_win)) {
+ SCLogDebug("ACK %"PRIu32" is after next_win %"PRIu32, ack, stream->next_win);
+ goto invalid;
+ /* a toclient RST as a reponse to SYN, next_win is 0, ack will be isn+1, just like
+ * the syn ack */
+ } else if (ssn->state == TCP_SYN_SENT && PKT_IS_TOCLIENT(p) &&
+ p->tcph->th_flags & TH_RST &&
+ SEQ_EQ(ack, stream->isn + 1)) {
+ SCReturnInt(0);
+ }
+
+ SCLogDebug("default path leading to invalid: ACK %"PRIu32", last_ack %"PRIu32
+ " next_win %"PRIu32, ack, stream->last_ack, stream->next_win);
+invalid:
+ StreamTcpSetEvent(p, STREAM_PKT_INVALID_ACK);
+ SCReturnInt(-1);
+}
+
+/** \brief Set the No reassembly flag for the given direction in given TCP
+ * session.
+ *
+ * \param ssn TCP Session to set the flag in
+ * \param direction direction to set the flag in: 0 toserver, 1 toclient
+ */
+void StreamTcpSetSessionNoReassemblyFlag (TcpSession *ssn, char direction)
+{
+ direction ? (ssn->server.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY) :
+ (ssn->client.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY);
+}
+
+/** \brief Set the No reassembly flag for the given direction in given TCP
+ * session.
+ *
+ * \param ssn TCP Session to set the flag in
+ * \param direction direction to set the flag in: 0 toserver, 1 toclient
+ */
+void StreamTcpSetDisableRawReassemblyFlag (TcpSession *ssn, char direction)
+{
+ direction ? (ssn->server.flags |= STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED) :
+ (ssn->client.flags |= STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED);
+}
+
+#define PSEUDO_PKT_SET_IPV4HDR(nipv4h,ipv4h) do { \
+ IPV4_SET_RAW_VER(nipv4h, IPV4_GET_RAW_VER(ipv4h)); \
+ IPV4_SET_RAW_HLEN(nipv4h, IPV4_GET_RAW_HLEN(ipv4h)); \
+ IPV4_SET_RAW_IPLEN(nipv4h, IPV4_GET_RAW_IPLEN(ipv4h)); \
+ IPV4_SET_RAW_IPTOS(nipv4h, IPV4_GET_RAW_IPTOS(ipv4h)); \
+ IPV4_SET_RAW_IPPROTO(nipv4h, IPV4_GET_RAW_IPPROTO(ipv4h)); \
+ (nipv4h)->s_ip_src = IPV4_GET_RAW_IPDST(ipv4h); \
+ (nipv4h)->s_ip_dst = IPV4_GET_RAW_IPSRC(ipv4h); \
+ } while (0)
+
+#define PSEUDO_PKT_SET_IPV6HDR(nipv6h,ipv6h) do { \
+ (nipv6h)->s_ip6_src[0] = (ipv6h)->s_ip6_dst[0]; \
+ (nipv6h)->s_ip6_src[1] = (ipv6h)->s_ip6_dst[1]; \
+ (nipv6h)->s_ip6_src[2] = (ipv6h)->s_ip6_dst[2]; \
+ (nipv6h)->s_ip6_src[3] = (ipv6h)->s_ip6_dst[3]; \
+ (nipv6h)->s_ip6_dst[0] = (ipv6h)->s_ip6_src[0]; \
+ (nipv6h)->s_ip6_dst[1] = (ipv6h)->s_ip6_src[1]; \
+ (nipv6h)->s_ip6_dst[2] = (ipv6h)->s_ip6_src[2]; \
+ (nipv6h)->s_ip6_dst[3] = (ipv6h)->s_ip6_src[3]; \
+ IPV6_SET_RAW_NH(nipv6h, IPV6_GET_RAW_NH(ipv6h)); \
+ } while (0)
+
+#define PSEUDO_PKT_SET_TCPHDR(ntcph,tcph) do { \
+ COPY_PORT((tcph)->th_dport, (ntcph)->th_sport); \
+ COPY_PORT((tcph)->th_sport, (ntcph)->th_dport); \
+ (ntcph)->th_seq = (tcph)->th_ack; \
+ (ntcph)->th_ack = (tcph)->th_seq; \
+ } while (0)
+
+/**
+ * \brief Function to fetch a packet from the packet allocation queue for
+ * creation of the pseudo packet from the reassembled stream.
+ *
+ * @param parent Pointer to the parent of the pseudo packet
+ * @param pkt pointer to the raw packet of the parent
+ * @param len length of the packet
+ * @return upon success returns the pointer to the new pseudo packet
+ * otherwise NULL
+ */
+Packet *StreamTcpPseudoSetup(Packet *parent, uint8_t *pkt, uint32_t len)
+{
+ SCEnter();
+
+ if (len == 0) {
+ SCReturnPtr(NULL, "Packet");
+ }
+
+ Packet *p = PacketGetFromQueueOrAlloc();
+ if (p == NULL) {
+ SCReturnPtr(NULL, "Packet");
+ }
+
+ /* set the root ptr to the lowest layer */
+ if (parent->root != NULL)
+ p->root = parent->root;
+ else
+ p->root = parent;
+
+ /* copy packet and set lenght, proto */
+ p->proto = parent->proto;
+ p->datalink = parent->datalink;
+
+ PacketCopyData(p, pkt, len);
+ p->recursion_level = parent->recursion_level + 1;
+ p->ts.tv_sec = parent->ts.tv_sec;
+ p->ts.tv_usec = parent->ts.tv_usec;
+
+ FlowReference(&p->flow, parent->flow);
+ /* set tunnel flags */
+
+ /* tell new packet it's part of a tunnel */
+ SET_TUNNEL_PKT(p);
+ /* tell parent packet it's part of a tunnel */
+ SET_TUNNEL_PKT(parent);
+
+ /* increment tunnel packet refcnt in the root packet */
+ TUNNEL_INCR_PKT_TPR(p);
+
+ return p;
+}
+
+/**
+ * \brief Function to setup the IP and TCP header of the pseudo packet from
+ * the newly copied raw packet contents of the parent.
+ *
+ * @param np pointer to the pseudo packet
+ * @param p pointer to the original packet
+ */
+static void StreamTcpPseudoPacketSetupHeader(Packet *np, Packet *p)
+{
+ /* Setup the IP header */
+ if (PKT_IS_IPV4(p)) {
+ np->ip4h = (IPV4Hdr *)((uint8_t *)GET_PKT_DATA(np) + (GET_PKT_LEN(np) - IPV4_GET_IPLEN(p)));
+ PSEUDO_PKT_SET_IPV4HDR(np->ip4h, p->ip4h);
+
+ /* Similarly setup the TCP header with ports in opposite direction */
+ np->tcph = (TCPHdr *)((uint8_t *)np->ip4h + IPV4_GET_HLEN(np));
+
+ PSEUDO_PKT_SET_TCPHDR(np->tcph, p->tcph);
+
+ /* Setup the adress and port details */
+ SET_IPV4_SRC_ADDR(p, &np->dst);
+ SET_IPV4_DST_ADDR(p, &np->src);
+ SET_TCP_SRC_PORT(p, &np->dp);
+ SET_TCP_DST_PORT(p, &np->sp);
+
+ } else if (PKT_IS_IPV6(p)) {
+ np->ip6h = (IPV6Hdr *)((uint8_t *)GET_PKT_DATA(np) + (GET_PKT_LEN(np) - IPV6_GET_PLEN(p) - IPV6_HEADER_LEN));
+ PSEUDO_PKT_SET_IPV6HDR(np->ip6h, p->ip6h);
+
+ /* Similarly setup the TCP header with ports in opposite direction */
+ np->tcph = (TCPHdr *)((uint8_t *)np->ip6h + IPV6_HEADER_LEN);
+ PSEUDO_PKT_SET_TCPHDR(np->tcph, p->tcph);
+
+ /* Setup the adress and port details */
+ SET_IPV6_SRC_ADDR(p, &np->dst);
+ SET_IPV6_DST_ADDR(p, &np->src);
+ SET_TCP_SRC_PORT(p, &np->dp);
+ SET_TCP_DST_PORT(p, &np->sp);
+ }
+
+ /* we don't need a payload (if any) */
+ np->payload = NULL;
+ np->payload_len = 0;
+}
+
+/** \brief Create a pseudo packet injected into the engine to signal the
+ * opposing direction of this stream to wrap up stream reassembly.
+ *
+ * \param p real packet
+ * \param pq packet queue to store the new pseudo packet in
+ */
+void StreamTcpPseudoPacketCreateStreamEndPacket(ThreadVars *tv, StreamTcpThread *stt, Packet *p, TcpSession *ssn, PacketQueue *pq)
+{
+ SCEnter();
+
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ SCReturn;
+ }
+
+ /* no need for a pseudo packet if there is nothing left to reassemble */
+ if (ssn->server.seg_list == NULL && ssn->client.seg_list == NULL) {
+ SCReturn;
+ }
+
+ Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
+ if (np == NULL) {
+ SCLogDebug("The packet received from packet allocation is NULL");
+ StatsIncr(tv, stt->counter_tcp_pseudo_failed);
+ SCReturn;
+ }
+ PKT_SET_SRC(np, PKT_SRC_STREAM_TCP_STREAM_END_PSEUDO);
+
+ /* Setup the IP and TCP headers */
+ StreamTcpPseudoPacketSetupHeader(np,p);
+
+ np->tenant_id = p->flow->tenant_id;
+
+ np->flowflags = p->flowflags;
+
+ np->flags |= PKT_STREAM_EST;
+ np->flags |= PKT_STREAM_EOF;
+ np->flags |= PKT_HAS_FLOW;
+ np->flags |= PKT_PSEUDO_STREAM_END;
+
+ if (p->flags & PKT_NOPACKET_INSPECTION) {
+ DecodeSetNoPacketInspectionFlag(np);
+ }
+ if (p->flags & PKT_NOPAYLOAD_INSPECTION) {
+ DecodeSetNoPayloadInspectionFlag(np);
+ }
+
+ if (PKT_IS_TOSERVER(p)) {
+ SCLogDebug("original is to_server, so pseudo is to_client");
+ np->flowflags &= ~FLOW_PKT_TOSERVER;
+ np->flowflags |= FLOW_PKT_TOCLIENT;
+#ifdef DEBUG
+ BUG_ON(!(PKT_IS_TOCLIENT(np)));
+ BUG_ON((PKT_IS_TOSERVER(np)));
+#endif
+ } else if (PKT_IS_TOCLIENT(p)) {
+ SCLogDebug("original is to_client, so pseudo is to_server");
+ np->flowflags &= ~FLOW_PKT_TOCLIENT;
+ np->flowflags |= FLOW_PKT_TOSERVER;
+#ifdef DEBUG
+ BUG_ON(!(PKT_IS_TOSERVER(np)));
+ BUG_ON((PKT_IS_TOCLIENT(np)));
+#endif
+ }
+
+ PacketEnqueue(pq, np);
+
+ StatsIncr(tv, stt->counter_tcp_pseudo);
+ SCReturn;
+}
+
+/**
+ * \brief Run callback function on each TCP segment
+ *
+ * This function is used by StreamMsgForEach() which
+ * should be used directly.
+ *
+ * \return -1 in case of error, the number of segment in case of success
+ *
+ */
+int StreamTcpSegmentForEach(const Packet *p, uint8_t flag, StreamSegmentCallback CallbackFunc, void *data)
+{
+ TcpSession *ssn = NULL;
+ TcpStream *stream = NULL;
+ int ret = 0;
+ int cnt = 0;
+
+ if (p->flow == NULL)
+ return 0;
+
+ FLOWLOCK_RDLOCK(p->flow);
+ ssn = (TcpSession *)p->flow->protoctx;
+
+ if (ssn == NULL) {
+ FLOWLOCK_UNLOCK(p->flow);
+ return 0;
+ }
+
+ if (flag & FLOW_PKT_TOSERVER) {
+ stream = &(ssn->server);
+ } else {
+ stream = &(ssn->client);
+ }
+ TcpSegment *seg = stream->seg_list;
+ for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);) {
+ ret = CallbackFunc(p, data, seg->payload, seg->payload_len);
+ if (ret != 1) {
+ SCLogDebug("Callback function has failed");
+ FLOWLOCK_UNLOCK(p->flow);
+ return -1;
+ }
+ seg = seg->next;
+ cnt++;
+ }
+ FLOWLOCK_UNLOCK(p->flow);
+ return cnt;
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the allocation of TCP session for a given packet from the
+ * ssn_pool.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest01 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ TcpSession *ssn = StreamTcpNewSession(p, 0);
+ if (ssn == NULL) {
+ printf("Session can not be allocated: ");
+ goto end;
+ }
+ f.protoctx = ssn;
+
+ if (f.alparser != NULL) {
+ printf("AppLayer field not set to NULL: ");
+ goto end;
+ }
+ if (ssn->state != 0) {
+ printf("TCP state field not set to 0: ");
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the deallocation of TCP session for a given packet and return
+ * the memory back to ssn_pool and corresponding segments to segment
+ * pool.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest02 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx ra_ctx;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ int ret = 0;
+ stt.ra_ctx = &ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we missed the intial
+ * SYN packet of the session. The session is setup only if midstream
+ * sessions are allowed to setup.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest03 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_SYN|TH_ACK;
+ p->tcph = &tcph;
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(19);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 20 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 11)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we missed the intial
+ * SYN/ACK packet of the session. The session is setup only if
+ * midstream sessions are allowed to setup.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest04 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK;
+ p->tcph = &tcph;
+
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(9);
+ p->tcph->th_ack = htonl(19);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 10 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 20)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we missed the intial
+ * 3WHS packet of the session. The session is setup only if
+ * midstream sessions are allowed to setup.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest05 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(13);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, 4); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(19);
+ p->tcph->th_ack = htonl(16);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, 4); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 16 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 23)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we have seen only the
+ * FIN, RST packets packet of the session. The session is setup only if
+ * midstream sessions are allowed to setup.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest06 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TcpSession ssn;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_flags = TH_FIN;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ /* StreamTcpPacket returns -1 on unsolicited FIN */
+ if (StreamTcpPacket(&tv, p, &stt, &pq) != -1) {
+ printf("StreamTcpPacket failed: ");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx)) != NULL) {
+ printf("we have a ssn while we shouldn't: ");
+ goto end;
+ }
+
+ p->tcph->th_flags = TH_RST;
+ /* StreamTcpPacket returns -1 on unsolicited RST */
+ if (StreamTcpPacket(&tv, p, &stt, &pq) != -1) {
+ printf("StreamTcpPacket failed (2): ");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx)) != NULL) {
+ printf("we have a ssn while we shouldn't (2): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the working on PAWS. The packet will be dropped by stream, as
+ * its timestamp is old, although the segment is in the window.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest07 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[1] = {0x42};
+ TCPVars tcpvars;
+ TCPOpt ts;
+ uint32_t data[2];
+ PacketQueue pq;
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof(StreamTcpThread));
+ memset(&tcph, 0, sizeof(TCPHdr));
+ memset(&tcpvars, 0, sizeof(TCPVars));
+ memset(&ts, 0, sizeof(TCPOpt));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+ stream_config.midstream = TRUE;
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+
+ data[0] = htonl(10);
+ data[1] = htonl(11);
+
+ ts.type = TCP_OPT_TS;
+ ts.len = 10;
+ ts.data = (uint8_t *)data;
+ tcpvars.ts = &ts;
+ p->tcpvars = tcpvars;
+
+ p->payload = payload;
+ p->payload_len = 1;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ data[0] = htonl(2);
+ p->tcpvars.ts->data = (uint8_t *)data;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ if (((TcpSession *) (p->flow->protoctx))->client.next_seq != 11) {
+ printf("the timestamp values are client %"PRIu32" server %" PRIu32""
+ " seq %" PRIu32 "\n", TCP_GET_TSVAL(p), TCP_GET_TSECR(p),
+ ((TcpSession *) (p->flow->protoctx))->client.next_seq);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+ ret = 1;
+ }
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the working on PAWS. The packet will be accpeted by engine as
+ * the timestamp is valid and it is in window.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest08 (void)
+{
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[1] = {0x42};
+ TCPVars tcpvars;
+ TCPOpt ts;
+ uint32_t data[2];
+
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof(StreamTcpThread));
+ memset(&tcph, 0, sizeof(TCPHdr));
+ memset(&tcpvars, 0, sizeof(TCPVars));
+ memset(&ts, 0, sizeof(TCPOpt));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+ stream_config.midstream = TRUE;
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+
+ data[0] = htonl(10);
+ data[1] = htonl(11);
+
+ ts.type = TCP_OPT_TS;
+ ts.len = 10;
+ ts.data = (uint8_t *)data;
+ tcpvars.ts = &ts;
+ p->tcpvars = tcpvars;
+
+ p->payload = payload;
+ p->payload_len = 1;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(20);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ data[0] = htonl(12);
+ p->tcpvars.ts->data = (uint8_t *)data;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *) (p->flow->protoctx))->client.next_seq != 12) {
+ printf("the timestamp values are client %"PRIu32" server %" PRIu32 " "
+ "seq %" PRIu32 "\n", TCP_GET_TSVAL(p), TCP_GET_TSECR(p),
+ ((TcpSession *) (p->flow->protoctx))->client.next_seq);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the working of No stream reassembly flag. The stream will not
+ * reassemble the segment if the flag is set.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest09 (void)
+{
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[1] = {0x42};
+
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof(StreamTcpThread));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+ stream_config.midstream = TRUE;
+
+ //prevent L7 from kicking in
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+
+ p->payload = payload;
+ p->payload_len = 1;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(12);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpSetSessionNoReassemblyFlag(((TcpSession *)(p->flow->protoctx)), 0);
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *) (p->flow->protoctx))->client.seg_list->next == NULL)
+ ret = 1;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we are seeing asynchronous
+ * stream, while we see all the packets in that stream from start.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest10 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(11);
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.async_oneside != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("failed in setting state\n");
+ goto end;
+ }
+
+ if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
+ printf("failed in setting asynchronous session\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 6 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 11) {
+ printf("failed in seq %"PRIu32" match\n",
+ ((TcpSession *)(p->flow->protoctx))->client.last_ack);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we are seeing asynchronous
+ * stream, while we missed the SYN packet of that stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest11 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(1);
+ tcph.th_flags = TH_SYN|TH_ACK;
+ p->tcph = &tcph;
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.async_oneside != TRUE) {
+ ret = 1;
+ goto end;
+ }
+
+ if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
+ printf("failed in setting asynchronous session\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("failed in setting state\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 2 &&
+ ((TcpSession *)(p->flow->protoctx))->client.next_seq != 1) {
+ printf("failed in seq %"PRIu32" match\n",
+ ((TcpSession *)(p->flow->protoctx))->server.last_ack);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we are seeing asynchronous
+ * stream, while we missed the SYN and SYN/ACK packets in that stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest12 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(11);
+ tcph.th_flags = TH_ACK;
+ p->tcph = &tcph;
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.async_oneside != TRUE) {
+ ret = 1;
+ goto end;
+ }
+
+ if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
+ printf("failed in setting asynchronous session\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("failed in setting state\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 6 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 11) {
+ printf("failed in seq %"PRIu32" match\n",
+ ((TcpSession *)(p->flow->protoctx))->client.last_ack);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session when we are seeing asynchronous
+ * stream, while we missed the SYN and SYN/ACK packets in that stream.
+ * Later, we start to receive the packet from other end stream too.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest13 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(11);
+ tcph.th_flags = TH_ACK;
+ p->tcph = &tcph;
+ int ret = 0;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.async_oneside != TRUE) {
+ ret = 1;
+ goto end;
+ }
+
+ if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
+ printf("failed in setting asynchronous session\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("failed in setting state\n");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(9);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 9 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 14) {
+ printf("failed in seq %"PRIu32" match\n",
+ ((TcpSession *)(p->flow->protoctx))->client.last_ack);
+ goto end;
+ }
+
+ StreamTcpSessionPktFree(p);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/* Dummy conf string to setup the OS policy for unit testing */
+static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "default-log-dir: /var/log/eidps\n"
+ "\n"
+ "logging:\n"
+ "\n"
+ " default-log-level: debug\n"
+ "\n"
+ " default-format: \"<%t> - <%l>\"\n"
+ "\n"
+ " default-startup-message: Your IDS has started.\n"
+ "\n"
+ " default-output-filter:\n"
+ "\n"
+ "host-os-policy:\n"
+ "\n"
+ " windows: 192.168.0.1\n"
+ "\n"
+ " linux: 192.168.0.2\n"
+ "\n";
+/* Dummy conf string to setup the OS policy for unit testing */
+static const char *dummy_conf_string1 =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "default-log-dir: /var/log/eidps\n"
+ "\n"
+ "logging:\n"
+ "\n"
+ " default-log-level: debug\n"
+ "\n"
+ " default-format: \"<%t> - <%l>\"\n"
+ "\n"
+ " default-startup-message: Your IDS has started.\n"
+ "\n"
+ " default-output-filter:\n"
+ "\n"
+ "host-os-policy:\n"
+ "\n"
+ " windows: 192.168.0.0/24," "192.168.1.1\n"
+ "\n"
+ " linux: 192.168.1.0/24," "192.168.0.1\n"
+ "\n";
+
+/**
+ * \brief Function to parse the dummy conf string and get the value of IP
+ * address for the corresponding OS policy type.
+ *
+ * \param conf_val_name Name of the OS policy type
+ * \retval returns IP address as string on success and NULL on failure
+ */
+char *StreamTcpParseOSPolicy (char *conf_var_name)
+{
+ SCEnter();
+ char conf_var_type_name[15] = "host-os-policy";
+ char *conf_var_full_name = NULL;
+ char *conf_var_value = NULL;
+
+ if (conf_var_name == NULL)
+ goto end;
+
+ /* the + 2 is for the '.' and the string termination character '\0' */
+ conf_var_full_name = (char *)SCMalloc(strlen(conf_var_type_name) +
+ strlen(conf_var_name) + 2);
+ if (conf_var_full_name == NULL)
+ goto end;
+
+ if (snprintf(conf_var_full_name,
+ strlen(conf_var_type_name) + strlen(conf_var_name) + 2, "%s.%s",
+ conf_var_type_name, conf_var_name) < 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Error in making the conf full name");
+ goto end;
+ }
+
+ if (ConfGet(conf_var_full_name, &conf_var_value) != 1) {
+ SCLogError(SC_ERR_UNKNOWN_VALUE, "Error in getting conf value for conf name %s",
+ conf_var_full_name);
+ goto end;
+ }
+
+ SCLogDebug("Value obtained from the yaml conf file, for the var "
+ "\"%s\" is \"%s\"", conf_var_name, conf_var_value);
+
+ end:
+ if (conf_var_full_name != NULL)
+ SCFree(conf_var_full_name);
+ SCReturnCharPtr(conf_var_value);
+
+
+}
+/**
+ * \test Test the setting up a OS policy. Te OS policy values are defined in
+ * the config string "dummy_conf_string"
+ *
+ * \retval On success it returns 1 and on failure 0
+ */
+
+static int StreamTcpTest14 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ struct in_addr addr;
+ IPV4Hdr ipv4h;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&addr, 0, sizeof(addr));
+ memset(&ipv4h, 0, sizeof(ipv4h));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+ strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ addr.s_addr = inet_addr("192.168.0.1");
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->dst.family = AF_INET;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ p->ip4h = &ipv4h;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, sizeof(payload)); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ addr.s_addr = inet_addr("192.168.0.2");
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 13 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
+ printf("failed in next_seq match client.next_seq %"PRIu32""
+ " server.next_seq %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->client.next_seq,
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
+ OS_POLICY_WINDOWS && ((TcpSession *)
+ (p->flow->protoctx))->server.os_policy != OS_POLICY_LINUX)
+ {
+ printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
+ " should be %"PRIu8" and server.os_policy: %"PRIu8""
+ " should be %"PRIu8"\n", ((TcpSession *)
+ (p->flow->protoctx))->client.os_policy, OS_POLICY_WINDOWS,
+ ((TcpSession *)(p->flow->protoctx))->server.os_policy,
+ OS_POLICY_LINUX);
+ goto end;
+ }
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a TCP session using the 4WHS:
+ * SYN, SYN, SYN/ACK, ACK
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcp4WHSTest01 (void)
+{
+ int ret = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = 0;
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = 0;
+ p->tcph->th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
+ printf("STREAMTCP_FLAG_4WHS flag not set: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(21); /* the SYN/ACK uses the SEQ from the first SYN pkt */
+ p->tcph->th_flags = TH_SYN|TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(21);
+ p->tcph->th_ack = htonl(10);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("state is not ESTABLISHED: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test set up a TCP session using the 4WHS:
+ * SYN, SYN, SYN/ACK, ACK, but the SYN/ACK does
+ * not have the right SEQ
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcp4WHSTest02 (void)
+{
+ int ret = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = 0;
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = 0;
+ p->tcph->th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
+ printf("STREAMTCP_FLAG_4WHS flag not set: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(21); /* the SYN/ACK uses the SEQ from the first SYN pkt */
+ p->tcph->th_flags = TH_SYN|TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) != -1) {
+ printf("SYN/ACK pkt not rejected but it should have: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test set up a TCP session using the 4WHS:
+ * SYN, SYN, SYN/ACK, ACK: however the SYN/ACK and ACK
+ * are part of a normal 3WHS
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcp4WHSTest03 (void)
+{
+ int ret = 0;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ memset(p, 0, SIZE_OF_PACKET);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+
+ StreamTcpInitConfig(TRUE);
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = 0;
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = 0;
+ p->tcph->th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
+ printf("STREAMTCP_FLAG_4WHS flag not set: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(11);
+ p->tcph->th_flags = TH_SYN|TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(31);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("state is not ESTABLISHED: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a OS policy. Te OS policy values are defined in
+ * the config string "dummy_conf_string1"
+ *
+ * \retval On success it returns 1 and on failure 0
+ */
+
+static int StreamTcpTest15 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ struct in_addr addr;
+ IPV4Hdr ipv4h;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&addr, 0, sizeof(addr));
+ memset(&ipv4h, 0, sizeof(ipv4h));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+ strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ addr.s_addr = inet_addr("192.168.0.20");
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->dst.family = AF_INET;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ p->ip4h = &ipv4h;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, sizeof(payload)); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ addr.s_addr = inet_addr("192.168.1.20");
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 13 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
+ printf("failed in next_seq match client.next_seq %"PRIu32""
+ " server.next_seq %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->client.next_seq,
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
+ OS_POLICY_WINDOWS && ((TcpSession *)
+ (p->flow->protoctx))->server.os_policy != OS_POLICY_LINUX)
+ {
+ printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
+ " should be %"PRIu8" and server.os_policy: %"PRIu8""
+ " should be %"PRIu8"\n", ((TcpSession *)
+ (p->flow->protoctx))->client.os_policy, OS_POLICY_WINDOWS,
+ ((TcpSession *)(p->flow->protoctx))->server.os_policy,
+ OS_POLICY_LINUX);
+ goto end;
+ }
+ StreamTcpSessionPktFree(p);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a OS policy. Te OS policy values are defined in
+ * the config string "dummy_conf_string1"
+ *
+ * \retval On success it returns 1 and on failure 0
+ */
+
+static int StreamTcpTest16 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ struct in_addr addr;
+ IPV4Hdr ipv4h;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&addr, 0, sizeof(addr));
+ memset(&ipv4h, 0, sizeof(ipv4h));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+ strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ addr.s_addr = inet_addr("192.168.0.1");
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->dst.family = AF_INET;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ p->ip4h = &ipv4h;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, sizeof(payload)); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ addr.s_addr = inet_addr("192.168.1.1");
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 13 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
+ printf("failed in next_seq match client.next_seq %"PRIu32""
+ " server.next_seq %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->client.next_seq,
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
+ OS_POLICY_LINUX && ((TcpSession *)
+ (p->flow->protoctx))->server.os_policy != OS_POLICY_WINDOWS)
+ {
+ printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
+ " should be %"PRIu8" and server.os_policy: %"PRIu8""
+ " should be %"PRIu8"\n", ((TcpSession *)
+ (p->flow->protoctx))->client.os_policy, OS_POLICY_LINUX,
+ ((TcpSession *)(p->flow->protoctx))->server.os_policy,
+ OS_POLICY_WINDOWS);
+ goto end;
+ }
+ StreamTcpSessionPktFree(p);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the setting up a OS policy. Te OS policy values are defined in
+ * the config string "dummy_conf_string1". To check the setting of
+ * Default os policy
+ *
+ * \retval On success it returns 1 and on failure 0
+ */
+
+static int StreamTcpTest17 (void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ uint8_t payload[4];
+ struct in_addr addr;
+ IPV4Hdr ipv4h;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&addr, 0, sizeof(addr));
+ memset(&ipv4h, 0, sizeof(ipv4h));
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+ strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ addr.s_addr = inet_addr("192.168.0.1");
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->dst.family = AF_INET;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ p->ip4h = &ipv4h;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, sizeof(payload)); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(23);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ addr.s_addr = inet_addr("10.1.1.1");
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(13);
+ p->tcph->th_flags = TH_ACK|TH_PUSH;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ if (stream_config.midstream != TRUE) {
+ ret = 1;
+ goto end;
+ }
+ if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED)
+ goto end;
+
+ if (((TcpSession *)(p->flow->protoctx))->client.next_seq != 13 &&
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
+ printf("failed in next_seq match client.next_seq %"PRIu32""
+ " server.next_seq %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->client.next_seq,
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
+ OS_POLICY_LINUX && ((TcpSession *)
+ (p->flow->protoctx))->server.os_policy != OS_POLICY_DEFAULT)
+ {
+ printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
+ " should be %"PRIu8" and server.os_policy: %"PRIu8""
+ " should be %"PRIu8"\n", ((TcpSession *)
+ (p->flow->protoctx))->client.os_policy, OS_POLICY_LINUX,
+ ((TcpSession *)(p->flow->protoctx))->server.os_policy,
+ OS_POLICY_DEFAULT);
+ goto end;
+ }
+ StreamTcpSessionPktFree(p);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/** \test Test the various OS policies based on different IP addresses from
+ confuguration defined in 'dummy_conf_string1' */
+static int StreamTcpTest18 (void)
+{
+
+ struct in_addr addr;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ TcpStream stream;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&stream, 0, sizeof(stream));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(ipv4h));
+
+ StreamTcpInitConfig(TRUE);
+ SCHInfoCleanResources();
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ p->dst.family = AF_INET;
+ p->ip4h = &ipv4h;
+ addr.s_addr = inet_addr("192.168.1.1");
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ StreamTcpSetOSPolicy(&stream, p);
+
+ if (stream.os_policy != OS_POLICY_WINDOWS)
+ goto end;
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return ret;
+}
+/** \test Test the various OS policies based on different IP addresses from
+ confuguration defined in 'dummy_conf_string1' */
+static int StreamTcpTest19 (void)
+{
+
+ struct in_addr addr;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ TcpStream stream;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&stream, 0, sizeof(stream));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(ipv4h));
+
+ StreamTcpInitConfig(TRUE);
+ SCHInfoCleanResources();
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ p->dst.family = AF_INET;
+ p->ip4h = &ipv4h;
+ addr.s_addr = inet_addr("192.168.0.30");
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ StreamTcpSetOSPolicy(&stream, p);
+
+ if (stream.os_policy != OS_POLICY_WINDOWS) {
+ printf("expected os_policy: %"PRIu8" but received %"PRIu8": ",
+ OS_POLICY_WINDOWS, stream.os_policy);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return ret;
+}
+/** \test Test the various OS policies based on different IP addresses from
+ confuguration defined in 'dummy_conf_string1' */
+static int StreamTcpTest20 (void)
+{
+
+ struct in_addr addr;
+ char os_policy_name[10] = "linux";
+ char *ip_addr;
+ TcpStream stream;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&stream, 0, sizeof(stream));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(ipv4h));
+
+ StreamTcpInitConfig(TRUE);
+ SCHInfoCleanResources();
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ p->dst.family = AF_INET;
+ p->ip4h = &ipv4h;
+ addr.s_addr = inet_addr("192.168.0.1");
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ StreamTcpSetOSPolicy(&stream, p);
+
+ if (stream.os_policy != OS_POLICY_LINUX) {
+ printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
+ OS_POLICY_LINUX, stream.os_policy);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return ret;
+}
+/** \test Test the various OS policies based on different IP addresses from
+ confuguration defined in 'dummy_conf_string1' */
+static int StreamTcpTest21 (void)
+{
+
+ struct in_addr addr;
+ char os_policy_name[10] = "linux";
+ char *ip_addr;
+ TcpStream stream;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&stream, 0, sizeof(stream));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(ipv4h));
+
+ StreamTcpInitConfig(TRUE);
+ SCHInfoCleanResources();
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ p->dst.family = AF_INET;
+ p->ip4h = &ipv4h;
+ addr.s_addr = inet_addr("192.168.1.30");
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ StreamTcpSetOSPolicy(&stream, p);
+
+ if (stream.os_policy != OS_POLICY_LINUX) {
+ printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
+ OS_POLICY_LINUX, stream.os_policy);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return ret;
+}
+/** \test Test the various OS policies based on different IP addresses from
+ confuguration defined in 'dummy_conf_string1' */
+static int StreamTcpTest22 (void)
+{
+
+ struct in_addr addr;
+ char os_policy_name[10] = "windows";
+ char *ip_addr;
+ TcpStream stream;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ IPV4Hdr ipv4h;
+ int ret = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&stream, 0, sizeof(stream));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&ipv4h, 0, sizeof(ipv4h));
+
+ StreamTcpInitConfig(TRUE);
+ SCHInfoCleanResources();
+
+ /* Load the config string in to parser */
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
+
+ /* Get the IP address as string and add it to Host info tree for lookups */
+ ip_addr = StreamTcpParseOSPolicy(os_policy_name);
+ SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
+
+ p->dst.family = AF_INET;
+ p->ip4h = &ipv4h;
+ addr.s_addr = inet_addr("123.231.2.1");
+ p->dst.address.address_un_data32[0] = addr.s_addr;
+ StreamTcpSetOSPolicy(&stream, p);
+
+ if (stream.os_policy != OS_POLICY_DEFAULT) {
+ printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
+ OS_POLICY_DEFAULT, stream.os_policy);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ ConfDeInit();
+ ConfRestoreContextBackup();
+ SCFree(p);
+ return ret;
+}
+
+/** \test Test the stream mem leaks conditions. */
+static int StreamTcpTest23(void)
+{
+ TcpSession ssn;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ uint8_t packet[1460] = "";
+ ThreadVars tv;
+ int result = 1;
+ PacketQueue pq;
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ FLOW_INITIALIZE(&f);
+ ssn.client.os_policy = OS_POLICY_BSD;
+ f.protoctx = &ssn;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+ ssn.client.ra_app_base_seq = ssn.client.ra_raw_base_seq = ssn.client.last_ack = 3184324453UL;
+
+ p->tcph->th_seq = htonl(3184324453UL);
+ p->tcph->th_ack = htonl(3373419609UL);
+ p->payload_len = 2;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling: ");
+ result &= 0;
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(3184324455UL);
+ p->tcph->th_ack = htonl(3373419621UL);
+ p->payload_len = 2;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling: ");
+ result &= 0;
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(3184324453UL);
+ p->tcph->th_ack = htonl(3373419621UL);
+ p->payload_len = 6;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling: ");
+ result &= 0;
+// goto end;
+ }
+
+ if(ssn.client.seg_list_tail != NULL && ssn.client.seg_list_tail->payload_len != 4) {
+ printf("failed in segment reassmebling: ");
+ result &= 0;
+ }
+
+end:
+ StreamTcpReturnStreamSegments(&ssn.client);
+ StreamTcpFreeConfig(TRUE);
+ if (SC_ATOMIC_GET(st_memuse) == 0) {
+ result &= 1;
+ } else {
+ printf("smemuse.stream_memuse %"PRIu64"\n", SC_ATOMIC_GET(st_memuse));
+ }
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/** \test Test the stream mem leaks conditions. */
+static int StreamTcpTest24(void)
+{
+ TcpSession ssn;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ uint8_t packet[1460] = "";
+ ThreadVars tv;
+ int result = 1;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(p, 0, SIZE_OF_PACKET);
+ memset(&f, 0, sizeof (Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ FLOW_INITIALIZE(&f);
+ ssn.client.os_policy = OS_POLICY_BSD;
+ f.protoctx = &ssn;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+ ssn.client.ra_app_base_seq = ssn.client.ra_raw_base_seq = ssn.client.last_ack = 3184324453UL;
+
+ p->tcph->th_seq = htonl(3184324455UL);
+ p->tcph->th_ack = htonl(3373419621UL);
+ p->payload_len = 4;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(3184324459UL);
+ p->tcph->th_ack = htonl(3373419633UL);
+ p->payload_len = 2;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(3184324459UL);
+ p->tcph->th_ack = htonl(3373419657UL);
+ p->payload_len = 4;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ if(ssn.client.seg_list_tail != NULL && ssn.client.seg_list_tail->payload_len != 2) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ }
+
+end:
+ StreamTcpReturnStreamSegments(&ssn.client);
+ StreamTcpFreeConfig(TRUE);
+ if (SC_ATOMIC_GET(st_memuse) == 0) {
+ result &= 1;
+ } else {
+ printf("smemuse.stream_memuse %"PRIu64"\n", SC_ATOMIC_GET(st_memuse));
+ }
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+/**
+ * \test Test the initialization of tcp streams with congestion flags
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest25(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ stt.ra_ctx = ra_ctx;
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN | TH_CWR;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the initialization of tcp streams with congestion flags
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest26(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ stt.ra_ctx = ra_ctx;
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN | TH_ECN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the initialization of tcp streams with congestion flags
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest27(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ stt.ra_ctx = ra_ctx;
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN | TH_CWR | TH_ECN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(6);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
+ goto end;
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/** \test Test the memcap incrementing/decrementing and memcap check */
+static int StreamTcpTest28(void)
+{
+ uint8_t ret = 0;
+ StreamTcpInitConfig(TRUE);
+ uint32_t memuse = SC_ATOMIC_GET(st_memuse);
+
+ StreamTcpIncrMemuse(500);
+ if (SC_ATOMIC_GET(st_memuse) != (memuse+500)) {
+ printf("failed in incrementing the memory");
+ goto end;
+ }
+
+ StreamTcpDecrMemuse(500);
+ if (SC_ATOMIC_GET(st_memuse) != memuse) {
+ printf("failed in decrementing the memory");
+ goto end;
+ }
+
+ if (StreamTcpCheckMemcap(500) != 1) {
+ printf("failed in validating the memcap");
+ goto end;
+ }
+
+ if (StreamTcpCheckMemcap((memuse + stream_config.memcap)) != 0) {
+ printf("failed in validating the overflowed memcap");
+ goto end;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ if (SC_ATOMIC_GET(st_memuse) != 0) {
+ printf("failed in clearing the memory");
+ goto end;
+ }
+
+ ret = 1;
+ return ret;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+#if 0
+/**
+ * \test Test the resetting of the sesison with bad checksum packet and later
+ * send the malicious contents on the session. Engine should drop the
+ * packet with the bad checksum.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest29(void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpSession ssn;
+ IPV4Hdr ipv4h;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ struct in_addr addr;
+ struct in_addr addr1;
+ TCPCache tcpc;
+ TCPVars tcpvars;
+ TcpStream server;
+ TcpStream client;
+
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset (&ipv4h, 0, sizeof(IPV4Hdr));
+ memset (&addr, 0, sizeof(addr));
+ memset (&addr1, 0, sizeof(addr1));
+ memset (&tcpc, 0, sizeof(tcpc));
+ memset (&tcpvars, 0, sizeof(tcpvars));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&server, 0, sizeof (TcpStream));
+ memset(&client, 0, sizeof (TcpStream));
+ uint8_t packet[1460] = "";
+ int result = 1;
+
+ FLOW_INITIALIZE(&f);
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+
+ ssn.client.os_policy = OS_POLICY_BSD;
+ p.src.family = AF_INET;
+ p.dst.family = AF_INET;
+ p.proto = IPPROTO_TCP;
+ p.flow = &f;
+ tcph.th_win = 5480;
+ p.tcph = &tcph;
+ p.payload = packet;
+ p.ip4h = &ipv4h;
+ p.tcpc = tcpc;
+ p.tcpc.level4_comp_csum = -1;
+ tcpvars.hlen = 20;
+ p.tcpvars = tcpvars;
+ ssn.state = TCP_ESTABLISHED;
+ addr.s_addr = inet_addr("10.1.3.53");
+ p.dst.address.address_un_data32[0] = addr.s_addr;
+ addr1.s_addr = inet_addr("10.1.3.7");
+ p.src.address.address_un_data32[0] = addr1.s_addr;
+ f.protoctx = &ssn;
+ stt.ra_ctx = ra_ctx;
+ ssn.server = server;
+ ssn.client = client;
+ ssn.client.isn = 10;
+ ssn.client.window = 5184;
+ ssn.client.last_ack = 10;
+ ssn.client.ra_base_seq = 10;
+ ssn.client.next_win = 5184;
+ ssn.server.isn = 119197101;
+ ssn.server.window = 5184;
+ ssn.server.next_win = 5184;
+ ssn.server.last_ack = 119197101;
+ ssn.server.ra_base_seq = 119197101;
+
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(11);
+ p.tcph->th_ack = htonl(119197102);
+ p.payload_len = 4;
+ p.ip4h->ip_src = addr1;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+ p.tcph->th_seq = htonl(119197102);
+ p.tcph->th_ack = htonl(15);
+ p.payload_len = 0;
+ p.ip4h->ip_src = addr;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_RST | TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(15);
+ p.tcph->th_ack = htonl(119197102);
+ p.payload_len = 0;
+ p.ip4h->ip_src = addr1;
+ p.tcph->th_sum = 12345;
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ if (ssn.state != TCP_ESTABLISHED) {
+ printf("the ssn.state should be TCP_ESTABLISHED(%"PRIu8"), not %"PRIu8""
+ "\n", TCP_ESTABLISHED, ssn.state);
+ result &= 0;
+ goto end;
+ }
+
+end:
+ StreamTcpReturnStreamSegments(&ssn.client);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test the overlapping of the packet with bad checksum packet and later
+ * send the malicious contents on the session. Engine should drop the
+ * packet with the bad checksum.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest30(void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpSession ssn;
+ IPV4Hdr ipv4h;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ struct in_addr addr;
+ struct in_addr addr1;
+ TCPCache tcpc;
+ TCPVars tcpvars;
+ TcpStream server;
+ TcpStream client;
+
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset (&ipv4h, 0, sizeof(IPV4Hdr));
+ memset (&addr, 0, sizeof(addr));
+ memset (&addr1, 0, sizeof(addr1));
+ memset (&tcpc, 0, sizeof(tcpc));
+ memset (&tcpvars, 0, sizeof(tcpvars));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&server, 0, sizeof (TcpStream));
+ memset(&client, 0, sizeof (TcpStream));
+ uint8_t payload[9] = "AAAAAAAAA";
+ uint8_t payload1[9] = "GET /EVIL";
+ uint8_t expected_content[9] = { 0x47, 0x45, 0x54, 0x20, 0x2f, 0x45, 0x56,
+ 0x49, 0x4c };
+ int result = 1;
+
+ FLOW_INITIALIZE(&f);
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+
+ ssn.client.os_policy = OS_POLICY_BSD;
+ p.src.family = AF_INET;
+ p.dst.family = AF_INET;
+ p.proto = IPPROTO_TCP;
+ p.flow = &f;
+ tcph.th_win = 5480;
+ p.tcph = &tcph;
+ p.payload = payload;
+ p.ip4h = &ipv4h;
+ p.tcpc = tcpc;
+ p.tcpc.level4_comp_csum = -1;
+ p.tcpvars = tcpvars;
+ ssn.state = TCP_ESTABLISHED;
+ addr.s_addr = inet_addr("10.1.3.53");
+ p.dst.address.address_un_data32[0] = addr.s_addr;
+ addr1.s_addr = inet_addr("10.1.3.7");
+ p.src.address.address_un_data32[0] = addr1.s_addr;
+ f.protoctx = &ssn;
+ stt.ra_ctx = ra_ctx;
+ ssn.server = server;
+ ssn.client = client;
+ ssn.client.isn = 10;
+ ssn.client.window = 5184;
+ ssn.client.last_ack = 10;
+ ssn.client.ra_base_seq = 10;
+ ssn.client.next_win = 5184;
+ ssn.server.isn = 1351079940;
+ ssn.server.window = 5184;
+ ssn.server.next_win = 1351088132;
+ ssn.server.last_ack = 1351079940;
+ ssn.server.ra_base_seq = 1351079940;
+
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(11);
+ p.tcph->th_ack = htonl(1351079940);
+ p.payload_len = 9;
+ p.ip4h->ip_src = addr1;
+ p.tcph->th_sum = 12345;
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(11);
+ p.tcph->th_ack = htonl(1351079940);
+ p.payload = payload1;
+ p.payload_len = 9;
+ p.ip4h->ip_src = addr1;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+ p.tcph->th_seq = htonl(1351079940);
+ p.tcph->th_ack = htonl(20);
+ p.payload_len = 0;
+ p.ip4h->ip_src = addr;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(expected_content, 9, &ssn.client) != 1) {
+ printf("the contents are not as expected(GET /EVIL), contents are: ");
+ PrintRawDataFp(stdout, ssn.client.seg_list->payload, 9);
+ result &= 0;
+ goto end;
+ }
+
+end:
+ StreamTcpReturnStreamSegments(&ssn.client);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test the multiple SYN packet handling with bad checksum and timestamp
+ * value. Engine should drop the bad checksum packet and establish
+ * TCP session correctly.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest31(void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpSession ssn;
+ IPV4Hdr ipv4h;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ struct in_addr addr;
+ struct in_addr addr1;
+ TCPCache tcpc;
+ TCPVars tcpvars;
+ TcpStream server;
+ TcpStream client;
+ TCPOpt tcpopt;
+
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset (&ipv4h, 0, sizeof(IPV4Hdr));
+ memset (&addr, 0, sizeof(addr));
+ memset (&addr1, 0, sizeof(addr1));
+ memset (&tcpc, 0, sizeof(tcpc));
+ memset (&tcpvars, 0, sizeof(tcpvars));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&server, 0, sizeof (TcpStream));
+ memset(&client, 0, sizeof (TcpStream));
+ memset(&tcpopt, 0, sizeof (TCPOpt));
+ int result = 1;
+
+ StreamTcpInitConfig(TRUE);
+
+ FLOW_INITIALIZE(&f);
+ /* prevent L7 from kicking in */
+
+ ssn.client.os_policy = OS_POLICY_LINUX;
+ p.src.family = AF_INET;
+ p.dst.family = AF_INET;
+ p.proto = IPPROTO_TCP;
+ p.flow = &f;
+ tcph.th_win = 5480;
+ p.tcph = &tcph;
+ p.ip4h = &ipv4h;
+ p.tcpc = tcpc;
+ p.tcpc.level4_comp_csum = -1;
+ p.tcpvars = tcpvars;
+ p.tcpvars.ts = &tcpopt;
+ addr.s_addr = inet_addr("10.1.3.53");
+ p.dst.address.address_un_data32[0] = addr.s_addr;
+ addr1.s_addr = inet_addr("10.1.3.7");
+ p.src.address.address_un_data32[0] = addr1.s_addr;
+ f.protoctx = &ssn;
+ stt.ra_ctx = ra_ctx;
+ ssn.server = server;
+ ssn.client = client;
+ ssn.client.isn = 10;
+ ssn.client.window = 5184;
+ ssn.client.last_ack = 10;
+ ssn.client.ra_base_seq = 10;
+ ssn.client.next_win = 5184;
+ ssn.server.isn = 1351079940;
+ ssn.server.window = 5184;
+ ssn.server.next_win = 1351088132;
+ ssn.server.last_ack = 1351079940;
+ ssn.server.ra_base_seq = 1351079940;
+
+ tcph.th_flags = TH_SYN;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(10);
+ p.payload_len = 0;
+ p.ip4h->ip_src = addr1;
+ p.tcpc.ts1 = 100;
+ p.tcph->th_sum = 12345;
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_SYN;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(10);
+ p.payload_len = 0;
+ p.ip4h->ip_src = addr1;
+ p.tcpc.ts1 = 10;
+ p.tcpc.level4_comp_csum = -1;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ ssn.flags |= STREAMTCP_FLAG_TIMESTAMP;
+ tcph.th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+ p.tcph->th_seq = htonl(1351079940);
+ p.tcph->th_ack = htonl(11);
+ p.payload_len = 0;
+ p.tcpc.ts1 = 10;
+ p.ip4h->ip_src = addr;
+ p.tcpc.level4_comp_csum = -1;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ tcph.th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ p.tcph->th_seq = htonl(11);
+ p.tcph->th_ack = htonl(1351079941);
+ p.payload_len = 0;
+ p.tcpc.ts1 = 10;
+ p.ip4h->ip_src = addr1;
+ p.tcpc.level4_comp_csum = -1;
+ p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
+ (uint16_t *)p.tcph,
+ (p.payload_len +
+ p.tcpvars.hlen) );
+
+ if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
+ printf("failed in segment reassmebling\n");
+ result &= 0;
+ goto end;
+ }
+
+ if (ssn.state != TCP_ESTABLISHED) {
+ printf("the should have been changed to TCP_ESTABLISHED!!\n ");
+ result &= 0;
+ goto end;
+ }
+
+end:
+ StreamTcpReturnStreamSegments(&ssn.client);
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+/**
+ * \test Test the initialization of tcp streams with ECN & CWR flags
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest32(void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ stt.ra_ctx = ra_ctx;
+ p.flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN | TH_CWR | TH_ECN;
+ p.tcph = &tcph;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_flags = TH_SYN | TH_ACK | TH_ECN;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_ACK | TH_ECN | TH_CWR;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(2);
+ p.tcph->th_flags = TH_PUSH | TH_ACK | TH_ECN | TH_CWR;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p.payload = payload;
+ p.payload_len = 3;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p.flowflags = FLOW_PKT_TOCLIENT;
+ p.tcph->th_flags = TH_ACK;
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) {
+ printf("the TCP state should be TCP_ESTABLISEHD\n");
+ goto end;
+ }
+ StreamTcpSessionClear(p.flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the allocation of TCP session for a given packet when the same
+ * ports have been used to start the new session after resetting the
+ * previous session.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest33 (void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx ra_ctx;
+ StreamMsgQueue stream_q;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&stream_q, 0, sizeof(StreamMsgQueue));
+ memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ p.flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p.tcph = &tcph;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ int ret = 0;
+ ra_ctx.stream_q = &stream_q;
+ stt.ra_ctx = &ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_RST | TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p.flow->protoctx))->state != TCP_CLOSED) {
+ printf("Tcp session should have been closed\n");
+ goto end;
+ }
+
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_SYN;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_ack = htonl(2);
+ p.tcph->th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(2);
+ p.tcph->th_seq = htonl(2);
+ p.tcph->th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("Tcp session should have been ESTABLISHED\n");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p.flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the allocation of TCP session for a given packet when the SYN
+ * packet is sent with the PUSH flag set.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest34 (void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx ra_ctx;
+ StreamMsgQueue stream_q;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&stream_q, 0, sizeof(StreamMsgQueue));
+ memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ p.flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN|TH_PUSH;
+ p.tcph = &tcph;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ int ret = 0;
+ ra_ctx.stream_q = &stream_q;
+ stt.ra_ctx = &ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("Tcp session should have been establisehd\n");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p.flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the allocation of TCP session for a given packet when the SYN
+ * packet is sent with the URG flag set.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest35 (void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx ra_ctx;
+ StreamMsgQueue stream_q;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&stream_q, 0, sizeof(StreamMsgQueue));
+ memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ p.flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN|TH_URG;
+ p.tcph = &tcph;
+ p.flowflags = FLOW_PKT_TOSERVER;
+ int ret = 0;
+ ra_ctx.stream_q = &stream_q;
+ stt.ra_ctx = &ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
+ goto end;
+
+ if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
+ printf("Tcp session should have been establisehd\n");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpSessionClear(p.flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the processing of PSH and URG flag in tcp session.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest36(void)
+{
+ Packet p;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset (&p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ stt.ra_ctx = ra_ctx;
+ p.flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p.tcph = &tcph;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_flags = TH_SYN | TH_ACK;
+ p.flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p.tcph->th_ack = htonl(1);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_ACK;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) {
+ printf("the TCP state should be TCP_ESTABLISEHD\n");
+ goto end;
+ }
+
+ p.tcph->th_ack = htonl(2);
+ p.tcph->th_seq = htonl(1);
+ p.tcph->th_flags = TH_PUSH | TH_ACK | TH_URG;
+ p.flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p.payload = payload;
+ p.payload_len = 3;
+
+ if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p.flow->protoctx)->client.next_seq != 4) {
+ printf("the ssn->client.next_seq should be 4, but it is %"PRIu32"\n",
+ ((TcpSession *)p.flow->protoctx)->client.next_seq);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p.flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+#endif
+
+/**
+ * \test Test the processing of out of order FIN packets in tcp session.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpTest37(void)
+{
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ int ret = 0;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(p, 0, SIZE_OF_PACKET);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+
+ stt.ra_ctx = ra_ctx;
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p->flow->protoctx)->state != TCP_ESTABLISHED) {
+ printf("the TCP state should be TCP_ESTABLISEHD\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(2);
+ p->tcph->th_seq = htonl(4);
+ p->tcph->th_flags = TH_ACK|TH_FIN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p->flow->protoctx)->state != TCP_CLOSE_WAIT) {
+ printf("the TCP state should be TCP_CLOSE_WAIT\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(4);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_ACK;
+ p->payload_len = 0;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
+ printf("failed in processing packet\n");
+ goto end;
+ }
+
+ if (((TcpSession *)p->flow->protoctx)->client.ra_raw_base_seq != 3) {
+ printf("the ssn->client.next_seq should be 3, but it is %"PRIu32"\n",
+ ((TcpSession *)p->flow->protoctx)->client.ra_raw_base_seq);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/**
+ * \test Test the validation of the ACK number before setting up the
+ * stream.last_ack.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest38 (void)
+{
+ int ret = 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[128];
+ TCPHdr tcph;
+ PacketQueue pq;
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&pq,0,sizeof(PacketQueue));
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ stt.ra_ctx = ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(29847);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ /* last_ack value should be 1 as the previous sent ACK value is out of
+ window */
+ if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 1) {
+ printf("the server.last_ack should be 1, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.last_ack);
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 127, 128); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 127;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->server.next_seq != 128) {
+ printf("the server.next_seq should be 128, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(256); // in window, but beyond next_seq
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ /* last_ack value should be 256, as the previous sent ACK value
+ is inside window */
+ if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 256) {
+ printf("the server.last_ack should be 1, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.last_ack);
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(128);
+ p->tcph->th_seq = htonl(8);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ /* last_ack value should be 256 as the previous sent ACK value is inside
+ window */
+ if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 256) {
+ printf("the server.last_ack should be 256, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.last_ack);
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ if (stt.ra_ctx != NULL)
+ StreamTcpReassembleFreeThreadCtx(stt.ra_ctx);
+ return ret;
+}
+
+/**
+ * \test Test the validation of the ACK number before setting up the
+ * stream.last_ack and update the next_seq after loosing the .
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpTest39 (void)
+{
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ PacketQueue pq;
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&pq,0,sizeof(PacketQueue));
+
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ FLOW_INITIALIZE(&f);
+ p->flow = &f;
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ int ret = 0;
+ stt.ra_ctx = ra_ctx;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ if (((TcpSession *)(p->flow->protoctx))->server.next_seq != 4) {
+ printf("the server.next_seq should be 4, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ p->tcph->th_ack = htonl(4);
+ p->tcph->th_seq = htonl(2);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ /* last_ack value should be 4 as the previous sent ACK value is inside
+ window */
+ if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 4) {
+ printf("the server.last_ack should be 4, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.last_ack);
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(4);
+ p->tcph->th_ack = htonl(5);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->payload = payload;
+ p->payload_len = 3;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
+ printf("failed in processing packet in StreamTcpPacket\n");
+ goto end;
+ }
+
+ /* next_seq value should be 2987 as the previous sent ACK value is inside
+ window */
+ if (((TcpSession *)(p->flow->protoctx))->server.next_seq != 7) {
+ printf("the server.next_seq should be 7, but it is %"PRIu32"\n",
+ ((TcpSession *)(p->flow->protoctx))->server.next_seq);
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ if (stt.ra_ctx != NULL)
+ StreamTcpReassembleFreeThreadCtx(stt.ra_ctx);
+ return ret;
+}
+
+static int StreamTcpTest40(void)
+{
+ uint8_t raw_vlan[] = {
+ 0x00, 0x20, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34,
+ 0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
+ 0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
+ 0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
+ 0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
+ 0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
+ 0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3
+ };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+
+ SET_PKT_LEN(p, sizeof(raw_vlan));
+ memcpy(GET_PKT_DATA(p), raw_vlan, sizeof(raw_vlan));
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeVLAN(&tv, &dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), NULL);
+
+ if(p->vlanh == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ if(p->tcph == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
+ if (np == NULL) {
+ printf("the packet received from packet allocation is NULL: ");
+ return 0;
+ }
+
+ StreamTcpPseudoPacketSetupHeader(np,p);
+
+ if (((uint8_t *)p->tcph - (uint8_t *)p->ip4h) != ((uint8_t *)np->tcph - (uint8_t *)np->ip4h)) {
+ return 0;
+ }
+
+ PACKET_RECYCLE(np);
+ PACKET_RECYCLE(p);
+ FlowShutdown();
+
+ return 1;
+}
+
+static int StreamTcpTest41(void)
+{
+ /* IPV6/TCP/no eth header */
+ uint8_t raw_ip[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x06, 0x40,
+ 0x20, 0x01, 0x06, 0x18, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x51, 0x99, 0xcc, 0x70,
+ 0x20, 0x01, 0x06, 0x18, 0x00, 0x01, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x8c, 0x9b, 0x00, 0x50, 0x6a, 0xe7, 0x07, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0x30,
+ 0x29, 0x9c, 0x00, 0x00, 0x02, 0x04, 0x05, 0x8c,
+ 0x04, 0x02, 0x08, 0x0a, 0x00, 0xdd, 0x1a, 0x39,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02 };
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ if (unlikely(p == NULL))
+ return 0;
+ ThreadVars tv;
+ DecodeThreadVars dtv;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(p, 0, SIZE_OF_PACKET);
+ PACKET_INITIALIZE(p);
+
+ if (PacketCopyData(p, raw_ip, sizeof(raw_ip)) == -1) {
+ PacketFree(p);
+ return 1;
+ }
+
+ FlowInitConfig(FLOW_QUIET);
+
+ DecodeRaw(&tv, &dtv, p, raw_ip, GET_PKT_LEN(p), NULL);
+
+ if (p->ip6h == NULL) {
+ printf("expected a valid ipv6 header but it was NULL: ");
+ FlowShutdown();
+ SCFree(p);
+ return 1;
+ }
+
+ if(p->tcph == NULL) {
+ SCFree(p);
+ return 0;
+ }
+
+ Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
+ if (np == NULL) {
+ printf("the packet received from packet allocation is NULL: ");
+ return 0;
+ }
+
+ StreamTcpPseudoPacketSetupHeader(np,p);
+
+ if (((uint8_t *)p->tcph - (uint8_t *)p->ip6h) != ((uint8_t *)np->tcph - (uint8_t *)np->ip6h)) {
+ return 0;
+ }
+
+ PACKET_RECYCLE(np);
+ PACKET_RECYCLE(p);
+ SCFree(p);
+ FlowShutdown();
+
+ return 1;
+}
+
+/** \test multiple different SYN/ACK, pick first */
+static int StreamTcpTest42 (void)
+{
+ int ret = 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ PacketQueue pq;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ TcpSession *ssn;
+
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ StreamTcpInitConfig(TRUE);
+
+ FLOW_INITIALIZE(&f);
+ p->tcph = &tcph;
+ tcph.th_win = htons(5480);
+ p->flow = &f;
+
+ /* SYN pkt */
+ tcph.th_flags = TH_SYN;
+ tcph.th_seq = htonl(100);
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(500);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(1000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* ACK */
+ p->tcph->th_ack = htonl(501);
+ p->tcph->th_seq = htonl(101);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ ssn = p->flow->protoctx;
+
+ if (ssn->state != TCP_ESTABLISHED) {
+ printf("state not TCP_ESTABLISHED: ");
+ goto end;
+ }
+
+ if (ssn->server.isn != 500) {
+ SCLogDebug("ssn->server.isn %"PRIu32" != %"PRIu32"",
+ ssn->server.isn, 500);
+ goto end;
+ }
+ if (ssn->client.isn != 100) {
+ SCLogDebug("ssn->client.isn %"PRIu32" != %"PRIu32"",
+ ssn->client.isn, 100);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/** \test multiple different SYN/ACK, pick second */
+static int StreamTcpTest43 (void)
+{
+ int ret = 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ PacketQueue pq;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ TcpSession *ssn;
+
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ StreamTcpInitConfig(TRUE);
+
+ FLOW_INITIALIZE(&f);
+ p->tcph = &tcph;
+ tcph.th_win = htons(5480);
+ p->flow = &f;
+
+ /* SYN pkt */
+ tcph.th_flags = TH_SYN;
+ tcph.th_seq = htonl(100);
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(500);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(1000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* ACK */
+ p->tcph->th_ack = htonl(1001);
+ p->tcph->th_seq = htonl(101);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ ssn = p->flow->protoctx;
+
+ if (ssn->state != TCP_ESTABLISHED) {
+ printf("state not TCP_ESTABLISHED: ");
+ goto end;
+ }
+
+ if (ssn->server.isn != 1000) {
+ SCLogDebug("ssn->server.isn %"PRIu32" != %"PRIu32"",
+ ssn->server.isn, 1000);
+ goto end;
+ }
+ if (ssn->client.isn != 100) {
+ SCLogDebug("ssn->client.isn %"PRIu32" != %"PRIu32"",
+ ssn->client.isn, 100);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/** \test multiple different SYN/ACK, pick neither */
+static int StreamTcpTest44 (void)
+{
+ int ret = 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ PacketQueue pq;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ TcpSession *ssn;
+
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ StreamTcpInitConfig(TRUE);
+
+ FLOW_INITIALIZE(&f);
+ p->tcph = &tcph;
+ tcph.th_win = htons(5480);
+ p->flow = &f;
+
+ /* SYN pkt */
+ tcph.th_flags = TH_SYN;
+ tcph.th_seq = htonl(100);
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(500);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(1000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* ACK */
+ p->tcph->th_ack = htonl(3001);
+ p->tcph->th_seq = htonl(101);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) != -1)
+ goto end;
+
+ ssn = p->flow->protoctx;
+
+ if (ssn->state != TCP_SYN_RECV) {
+ SCLogDebug("state not TCP_SYN_RECV");
+ goto end;
+ }
+
+ if (ssn->client.isn != 100) {
+ SCLogDebug("ssn->client.isn %"PRIu32" != %"PRIu32"",
+ ssn->client.isn, 100);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ FLOW_DESTROY(&f);
+ return ret;
+}
+
+/** \test multiple different SYN/ACK, over the limit */
+static int StreamTcpTest45 (void)
+{
+ int ret = 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread stt;
+ TCPHdr tcph;
+ PacketQueue pq;
+ Packet *p = SCMalloc(SIZE_OF_PACKET);
+ TcpSession *ssn;
+
+ if (unlikely(p == NULL))
+ return 0;
+ memset(p, 0, SIZE_OF_PACKET);
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ memset(&stt, 0, sizeof (StreamTcpThread));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ StreamTcpInitConfig(TRUE);
+ stream_config.max_synack_queued = 2;
+
+ FLOW_INITIALIZE(&f);
+ p->tcph = &tcph;
+ tcph.th_win = htons(5480);
+ p->flow = &f;
+
+ /* SYN pkt */
+ tcph.th_flags = TH_SYN;
+ tcph.th_seq = htonl(100);
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(500);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(1000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(2000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ /* SYN/ACK */
+ p->tcph->th_seq = htonl(3000);
+ p->tcph->th_ack = htonl(101);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) != -1)
+ goto end;
+
+ /* ACK */
+ p->tcph->th_ack = htonl(1001);
+ p->tcph->th_seq = htonl(101);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
+ goto end;
+
+ ssn = p->flow->protoctx;
+
+ if (ssn->state != TCP_ESTABLISHED) {
+ printf("state not TCP_ESTABLISHED: ");
+ goto end;
+ }
+
+ if (ssn->server.isn != 1000) {
+ SCLogDebug("ssn->server.isn %"PRIu32" != %"PRIu32"",
+ ssn->server.isn, 1000);
+ goto end;
+ }
+ if (ssn->client.isn != 100) {
+ SCLogDebug("ssn->client.isn %"PRIu32" != %"PRIu32"",
+ ssn->client.isn, 100);
+ goto end;
+ }
+
+ StreamTcpSessionClear(p->flow->protoctx);
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void StreamTcpRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpTest01 -- TCP session allocation",
+ StreamTcpTest01, 1);
+ UtRegisterTest("StreamTcpTest02 -- TCP session deallocation",
+ StreamTcpTest02, 1);
+ UtRegisterTest("StreamTcpTest03 -- SYN missed MidStream session",
+ StreamTcpTest03, 1);
+ UtRegisterTest("StreamTcpTest04 -- SYN/ACK missed MidStream session",
+ StreamTcpTest04, 1);
+ UtRegisterTest("StreamTcpTest05 -- 3WHS missed MidStream session",
+ StreamTcpTest05, 1);
+ UtRegisterTest("StreamTcpTest06 -- FIN, RST message MidStream session",
+ StreamTcpTest06, 1);
+ UtRegisterTest("StreamTcpTest07 -- PAWS invalid timestamp",
+ StreamTcpTest07, 1);
+ UtRegisterTest("StreamTcpTest08 -- PAWS valid timestamp",
+ StreamTcpTest08, 1);
+ UtRegisterTest("StreamTcpTest09 -- No Client Reassembly",
+ StreamTcpTest09, 1);
+ UtRegisterTest("StreamTcpTest10 -- No missed packet Async stream",
+ StreamTcpTest10, 1);
+ UtRegisterTest("StreamTcpTest11 -- SYN missed Async stream",
+ StreamTcpTest11, 1);
+ UtRegisterTest("StreamTcpTest12 -- SYN/ACK missed Async stream",
+ StreamTcpTest12, 1);
+ UtRegisterTest("StreamTcpTest13 -- opposite stream packets for Async "
+ "stream", StreamTcpTest13, 1);
+ UtRegisterTest("StreamTcp4WHSTest01", StreamTcp4WHSTest01, 1);
+ UtRegisterTest("StreamTcp4WHSTest02", StreamTcp4WHSTest02, 1);
+ UtRegisterTest("StreamTcp4WHSTest03", StreamTcp4WHSTest03, 1);
+ UtRegisterTest("StreamTcpTest14 -- setup OS policy", StreamTcpTest14, 1);
+ UtRegisterTest("StreamTcpTest15 -- setup OS policy", StreamTcpTest15, 1);
+ UtRegisterTest("StreamTcpTest16 -- setup OS policy", StreamTcpTest16, 1);
+ UtRegisterTest("StreamTcpTest17 -- setup OS policy", StreamTcpTest17, 1);
+ UtRegisterTest("StreamTcpTest18 -- setup OS policy", StreamTcpTest18, 1);
+ UtRegisterTest("StreamTcpTest19 -- setup OS policy", StreamTcpTest19, 1);
+ UtRegisterTest("StreamTcpTest20 -- setup OS policy", StreamTcpTest20, 1);
+ UtRegisterTest("StreamTcpTest21 -- setup OS policy", StreamTcpTest21, 1);
+ UtRegisterTest("StreamTcpTest22 -- setup OS policy", StreamTcpTest22, 1);
+ UtRegisterTest("StreamTcpTest23 -- stream memory leaks", StreamTcpTest23, 1);
+ UtRegisterTest("StreamTcpTest24 -- stream memory leaks", StreamTcpTest24, 1);
+ UtRegisterTest("StreamTcpTest25 -- test ecn/cwr sessions",
+ StreamTcpTest25, 1);
+ UtRegisterTest("StreamTcpTest26 -- test ecn/cwr sessions",
+ StreamTcpTest26, 1);
+ UtRegisterTest("StreamTcpTest27 -- test ecn/cwr sessions",
+ StreamTcpTest27, 1);
+ UtRegisterTest("StreamTcpTest28 -- Memcap Test", StreamTcpTest28, 1);
+
+#if 0 /* VJ 2010/09/01 disabled since they blow up on Fedora and Fedora is
+ * right about blowing up. The checksum functions are not used properly
+ * in the tests. */
+ UtRegisterTest("StreamTcpTest29 -- Badchecksum Reset Test", StreamTcpTest29, 1);
+ UtRegisterTest("StreamTcpTest30 -- Badchecksum Overlap Test", StreamTcpTest30, 1);
+ UtRegisterTest("StreamTcpTest31 -- MultipleSyns Test", StreamTcpTest31, 1);
+ UtRegisterTest("StreamTcpTest32 -- Bogus CWR Test", StreamTcpTest32, 1);
+ UtRegisterTest("StreamTcpTest33 -- RST-SYN Again Test", StreamTcpTest33, 1);
+ UtRegisterTest("StreamTcpTest34 -- SYN-PUSH Test", StreamTcpTest34, 1);
+ UtRegisterTest("StreamTcpTest35 -- SYN-URG Test", StreamTcpTest35, 1);
+ UtRegisterTest("StreamTcpTest36 -- PUSH-URG Test", StreamTcpTest36, 1);
+#endif
+ UtRegisterTest("StreamTcpTest37 -- Out of order FIN Test", StreamTcpTest37, 1);
+
+ UtRegisterTest("StreamTcpTest38 -- validate ACK", StreamTcpTest38, 1);
+ UtRegisterTest("StreamTcpTest39 -- update next_seq", StreamTcpTest39, 1);
+
+ UtRegisterTest("StreamTcpTest40 -- pseudo setup", StreamTcpTest40, 1);
+ UtRegisterTest("StreamTcpTest41 -- pseudo setup", StreamTcpTest41, 1);
+
+ UtRegisterTest("StreamTcpTest42 -- SYN/ACK queue", StreamTcpTest42, 1);
+ UtRegisterTest("StreamTcpTest43 -- SYN/ACK queue", StreamTcpTest43, 1);
+ UtRegisterTest("StreamTcpTest44 -- SYN/ACK queue", StreamTcpTest44, 1);
+ UtRegisterTest("StreamTcpTest45 -- SYN/ACK queue", StreamTcpTest45, 1);
+
+ /* set up the reassembly tests as well */
+ StreamTcpReassembleRegisterTests();
+
+ StreamTcpSackRegisterTests ();
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/stream-tcp.h b/framework/src/suricata/src/stream-tcp.h
new file mode 100644
index 00000000..f7c3ab10
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp.h
@@ -0,0 +1,232 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef __STREAM_TCP_H__
+#define __STREAM_TCP_H__
+
+#include "stream-tcp-private.h"
+
+#define COUNTER_STREAMTCP_STREAMS 1
+
+#include "app-layer-detect-proto.h"
+#include "util-mpm.h"
+#include "stream.h"
+#include "stream-tcp-reassemble.h"
+
+#define STREAM_VERBOSE FALSE
+/* Flag to indicate that the checksum validation for the stream engine
+ has been enabled */
+#define STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION 0x01
+
+/*global flow data*/
+typedef struct TcpStreamCnf_ {
+ /** stream tracking
+ *
+ * max stream mem usage
+ */
+ uint64_t memcap;
+ uint64_t reassembly_memcap; /**< max memory usage for stream reassembly */
+
+ uint32_t ssn_init_flags; /**< new ssn flags will be initialized to this */
+ uint8_t segment_init_flags; /**< new seg flags will be initialized to this */
+
+ uint16_t zero_copy_size; /**< use zero copy for app layer above segments
+ * of this size */
+
+ uint32_t prealloc_sessions; /**< ssns to prealloc per stream thread */
+ int midstream;
+ int async_oneside;
+ uint32_t reassembly_depth; /**< Depth until when we reassemble the stream */
+
+ uint16_t reassembly_toserver_chunk_size;
+ uint16_t reassembly_toclient_chunk_size;
+
+ int check_overlap_different_data;
+
+ /** reassembly -- inline mode
+ *
+ * sliding window size for raw stream reassembly
+ */
+ uint32_t reassembly_inline_window;
+ uint8_t flags;
+ uint8_t max_synack_queued;
+} TcpStreamCnf;
+
+typedef struct StreamTcpThread_ {
+ int ssn_pool_id;
+
+ /** if set to true, we activate the TCP tuple reuse code in the
+ * stream engine. */
+ int runmode_flow_stream_async;
+
+ uint64_t pkts;
+
+ /** queue for pseudo packet(s) that were created in the stream
+ * process and need further handling. Currently only used when
+ * receiving (valid) RST packets */
+ PacketQueue pseudo_queue;
+
+ uint16_t counter_tcp_sessions;
+ /** sessions not picked up because memcap was reached */
+ uint16_t counter_tcp_ssn_memcap;
+ /** pseudo packets processed */
+ uint16_t counter_tcp_pseudo;
+ /** pseudo packets failed to setup */
+ uint16_t counter_tcp_pseudo_failed;
+ /** packets rejected because their csum is invalid */
+ uint16_t counter_tcp_invalid_checksum;
+ /** TCP packets with no associated flow */
+ uint16_t counter_tcp_no_flow;
+ /** sessions reused */
+ uint16_t counter_tcp_reused_ssn;
+ /** syn pkts */
+ uint16_t counter_tcp_syn;
+ /** syn/ack pkts */
+ uint16_t counter_tcp_synack;
+ /** rst pkts */
+ uint16_t counter_tcp_rst;
+
+ /** tcp reassembly thread data */
+ TcpReassemblyThreadCtx *ra_ctx;
+} StreamTcpThread;
+
+TcpStreamCnf stream_config;
+void TmModuleStreamTcpRegister (void);
+void StreamTcpInitConfig (char);
+void StreamTcpFreeConfig(char);
+void StreamTcpRegisterTests (void);
+
+void StreamTcpSessionPktFree (Packet *);
+
+void StreamTcpIncrMemuse(uint64_t);
+void StreamTcpDecrMemuse(uint64_t);
+int StreamTcpCheckMemcap(uint64_t);
+
+Packet *StreamTcpPseudoSetup(Packet *, uint8_t *, uint32_t);
+
+int StreamTcpSegmentForEach(const Packet *p, uint8_t flag,
+ StreamSegmentCallback CallbackFunc,
+ void *data);
+void StreamTcpReassembleConfigEnableOverlapCheck(void);
+
+/** ------- Inline functions: ------ */
+
+/**
+ * \brief If we are on IPS mode, and got a drop action triggered from
+ * the IP only module, or from a reassembled msg and/or from an
+ * applayer detection, then drop the rest of the packets of the
+ * same stream and avoid inspecting it any further
+ * \param p pointer to the Packet to check
+ * \retval 1 if we must drop this stream
+ * \retval 0 if the stream still legal
+ */
+static inline int StreamTcpCheckFlowDrops(Packet *p)
+{
+ /* If we are on IPS mode, and got a drop action triggered from
+ * the IP only module, or from a reassembled msg and/or from an
+ * applayer detection, then drop the rest of the packets of the
+ * same stream and avoid inspecting it any further */
+ if (EngineModeIsIPS() && (p->flow->flags & FLOW_ACTION_DROP))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * \brief Function to flip the direction When we missed the SYN packet,
+ * SYN/ACK is considered as sent by server, but our engine flagged the
+ * packet as from client for the host whose packet is received first in
+ * the session.
+ *
+ * \param ssn TcpSession to whom this packet belongs
+ * \param p Packet whose flag has to be changed
+ */
+static inline void StreamTcpPacketSwitchDir(TcpSession *ssn, Packet *p)
+{
+ SCLogDebug("ssn %p: switching pkt direction", ssn);
+
+ if (PKT_IS_TOSERVER(p)) {
+ p->flowflags &= ~FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_TOCLIENT;
+
+ if (p->flowflags & FLOW_PKT_TOSERVER_FIRST) {
+ p->flowflags &= ~FLOW_PKT_TOSERVER_FIRST;
+ p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
+ }
+ } else {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ if (p->flowflags & FLOW_PKT_TOCLIENT_FIRST) {
+ p->flowflags &= ~FLOW_PKT_TOCLIENT_FIRST;
+ p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
+ }
+ }
+}
+
+enum {
+ /* stream has no segments for forced reassembly, nor for detection */
+ STREAM_HAS_UNPROCESSED_SEGMENTS_NONE = 0,
+ /* stream seems to have segments that need to be forced reassembled */
+ STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY = 1,
+ /* stream has no segments for forced reassembly, but only segments that
+ * have been sent for detection, but are stuck in the detection queues */
+ STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION = 2,
+};
+
+static inline int StreamNeedsReassembly(TcpSession *ssn, int direction)
+{
+ /* server tcp state */
+ if (direction) {
+ if (ssn->server.seg_list != NULL &&
+ (!(ssn->server.seg_list_tail->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) ||
+ !(ssn->server.seg_list_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED)) ) {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY;
+ } else if (ssn->toclient_smsg_head != NULL) {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ } else {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NONE;
+ }
+ } else {
+ if (ssn->client.seg_list != NULL &&
+ (!(ssn->client.seg_list_tail->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) ||
+ !(ssn->client.seg_list_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED)) ) {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY;
+ } else if (ssn->toserver_smsg_head != NULL) {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ } else {
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NONE;
+ }
+ }
+}
+
+TmEcode StreamTcpThreadInit(ThreadVars *, void *, void **);
+TmEcode StreamTcpThreadDeinit(ThreadVars *tv, void *data);
+int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
+ PacketQueue *pq);
+void StreamTcpSessionClear(void *ssnptr);
+uint32_t StreamTcpGetStreamSize(TcpStream *stream);
+
+#endif /* __STREAM_TCP_H__ */
+
diff --git a/framework/src/suricata/src/stream.c b/framework/src/suricata/src/stream.c
new file mode 100644
index 00000000..8aabd6dc
--- /dev/null
+++ b/framework/src/suricata/src/stream.c
@@ -0,0 +1,290 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Stream Chunk Handling API
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "threads.h"
+#include "stream.h"
+#include "util-pool.h"
+#include "util-debug.h"
+#include "stream-tcp.h"
+#include "flow-util.h"
+
+#ifdef DEBUG
+static SCMutex stream_pool_memuse_mutex;
+static uint64_t stream_pool_memuse = 0;
+static uint64_t stream_pool_memcnt = 0;
+#endif
+
+/* per queue setting */
+static uint16_t toserver_min_chunk_len = 2560;
+static uint16_t toclient_min_chunk_len = 2560;
+
+static Pool *stream_msg_pool = NULL;
+static SCMutex stream_msg_pool_mutex = SCMUTEX_INITIALIZER;
+
+static void StreamMsgEnqueue (StreamMsgQueue *q, StreamMsg *s)
+{
+ SCEnter();
+ SCLogDebug("s %p", s);
+ /* more packets in queue */
+ if (q->top != NULL) {
+ s->next = q->top;
+ q->top->prev = s;
+ q->top = s;
+ /* only packet */
+ } else {
+ q->top = s;
+ q->bot = s;
+ }
+ q->len++;
+#ifdef DBG_PERF
+ if (q->len > q->dbg_maxlen)
+ q->dbg_maxlen = q->len;
+#endif /* DBG_PERF */
+ SCReturn;
+}
+
+static StreamMsg *StreamMsgDequeue (StreamMsgQueue *q)
+{
+ SCEnter();
+
+ /* if the queue is empty there are no packets left.
+ * In that case we sleep and try again. */
+ if (q->len == 0) {
+ SCReturnPtr(NULL, "StreamMsg");
+ }
+
+ /* pull the bottom packet from the queue */
+ StreamMsg *s = q->bot;
+
+ /* more packets in queue */
+ if (q->bot->prev != NULL) {
+ q->bot = q->bot->prev;
+ q->bot->next = NULL;
+ /* just the one we remove, so now empty */
+ } else {
+ q->top = NULL;
+ q->bot = NULL;
+ }
+ q->len--;
+
+ s->next = NULL;
+ s->prev = NULL;
+ SCReturnPtr(s, "StreamMsg");
+}
+
+/* Used by stream reassembler to get msgs */
+StreamMsg *StreamMsgGetFromPool(void)
+{
+ SCMutexLock(&stream_msg_pool_mutex);
+ StreamMsg *s = (StreamMsg *)PoolGet(stream_msg_pool);
+ SCMutexUnlock(&stream_msg_pool_mutex);
+ return s;
+}
+
+/* Used by l7inspection to return msgs to pool */
+void StreamMsgReturnToPool(StreamMsg *s)
+{
+ SCLogDebug("s %p", s);
+ SCMutexLock(&stream_msg_pool_mutex);
+ PoolReturn(stream_msg_pool, (void *)s);
+ SCMutexUnlock(&stream_msg_pool_mutex);
+}
+
+/* Used by l7inspection to get msgs with data */
+StreamMsg *StreamMsgGetFromQueue(StreamMsgQueue *q)
+{
+ if (q->len > 0) {
+ StreamMsg *s = StreamMsgDequeue(q);
+ return s;
+ } else {
+ /* return NULL if we have no stream msg. Should only happen on signals. */
+ return NULL;
+ }
+}
+
+/* Used by stream reassembler to fill the queue for l7inspect reading */
+void StreamMsgPutInQueue(StreamMsgQueue *q, StreamMsg *s)
+{
+ StreamMsgEnqueue(q, s);
+ SCLogDebug("q->len %" PRIu32 "", q->len);
+}
+
+#define SIZE 4072
+void *StreamMsgPoolAlloc(void)
+{
+ if (StreamTcpReassembleCheckMemcap((uint32_t)(sizeof(StreamMsg)+SIZE)) == 0)
+ return NULL;
+
+ StreamMsg *m = SCCalloc(1, (sizeof(StreamMsg) + SIZE));
+ if (m != NULL) {
+ m->data = (uint8_t *)m + sizeof(StreamMsg);
+ m->data_size = SIZE;
+
+ StreamTcpReassembleIncrMemuse((uint32_t)(sizeof(StreamMsg)+SIZE));
+ }
+
+ return m;
+}
+
+int StreamMsgInit(void *data, void *initdata)
+{
+ StreamMsg *s = data;
+ memset(s->data, 0, s->data_size);
+
+#ifdef DEBUG
+ SCMutexLock(&stream_pool_memuse_mutex);
+ stream_pool_memuse += (sizeof(StreamMsg) + SIZE);
+ stream_pool_memcnt ++;
+ SCMutexUnlock(&stream_pool_memuse_mutex);
+#endif
+ return 1;
+}
+
+void StreamMsgPoolFree(void *ptr)
+{
+ if (ptr) {
+ SCFree(ptr);
+ StreamTcpReassembleDecrMemuse((uint32_t)(sizeof(StreamMsg)+SIZE));
+ }
+}
+
+void StreamMsgQueuesInit(uint32_t prealloc)
+{
+#ifdef DEBUG
+ SCMutexInit(&stream_pool_memuse_mutex, NULL);
+#endif
+ SCMutexLock(&stream_msg_pool_mutex);
+ stream_msg_pool = PoolInit(0, prealloc, 0,
+ StreamMsgPoolAlloc,StreamMsgInit,
+ NULL,NULL,StreamMsgPoolFree);
+ if (stream_msg_pool == NULL)
+ exit(EXIT_FAILURE); /* XXX */
+ SCMutexUnlock(&stream_msg_pool_mutex);
+}
+
+void StreamMsgQueuesDeinit(char quiet)
+{
+ if (quiet == FALSE) {
+ if (stream_msg_pool->max_outstanding > stream_msg_pool->allocated)
+ SCLogInfo("TCP segment chunk pool had a peak use of %u chunks, "
+ "more than the prealloc setting of %u",
+ stream_msg_pool->max_outstanding, stream_msg_pool->allocated);
+ }
+
+ SCMutexLock(&stream_msg_pool_mutex);
+ PoolFree(stream_msg_pool);
+ SCMutexUnlock(&stream_msg_pool_mutex);
+
+#ifdef DEBUG
+ SCMutexDestroy(&stream_pool_memuse_mutex);
+
+ if (quiet == FALSE)
+ SCLogDebug("stream_pool_memuse %"PRIu64", stream_pool_memcnt %"PRIu64"", stream_pool_memuse, stream_pool_memcnt);
+#endif
+}
+
+/** \brief alloc a stream msg queue
+ * \retval smq ptr to the queue or NULL */
+StreamMsgQueue *StreamMsgQueueGetNew(void)
+{
+ if (StreamTcpReassembleCheckMemcap((uint32_t)sizeof(StreamMsgQueue)) == 0)
+ return NULL;
+
+ StreamMsgQueue *smq = SCMalloc(sizeof(StreamMsgQueue));
+ if (unlikely(smq == NULL))
+ return NULL;
+
+ StreamTcpReassembleIncrMemuse((uint32_t)sizeof(StreamMsgQueue));
+
+ memset(smq, 0x00, sizeof(StreamMsgQueue));
+ return smq;
+}
+
+/** \brief Free a StreamMsgQueue
+ * \param q the queue to free
+ * \todo we may want to consider non empty queue's
+ */
+void StreamMsgQueueFree(StreamMsgQueue *q)
+{
+ SCFree(q);
+ StreamTcpReassembleDecrMemuse((uint32_t)sizeof(StreamMsgQueue));
+}
+
+void StreamMsgQueueSetMinChunkLen(uint8_t dir, uint16_t len)
+{
+ if (dir == FLOW_PKT_TOSERVER) {
+ toserver_min_chunk_len = len;
+ } else {
+ toclient_min_chunk_len = len;
+ }
+}
+
+uint16_t StreamMsgQueueGetMinChunkLen(uint8_t dir)
+{
+ if (dir == FLOW_PKT_TOSERVER) {
+ return toserver_min_chunk_len;
+ } else {
+ return toclient_min_chunk_len;
+ }
+}
+
+/** \brief Return a list of smsgs to the pool */
+void StreamMsgReturnListToPool(void *list)
+{
+ /* if we have (a) smsg(s), return to the pool */
+ StreamMsg *smsg = (StreamMsg *)list;
+ while (smsg != NULL) {
+ StreamMsg *smsg_next = smsg->next;
+ SCLogDebug("returning smsg %p to pool", smsg);
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ StreamMsgReturnToPool(smsg);
+ smsg = smsg_next;
+ }
+}
+
+/** \brief Run callback for all segments
+ *
+ * \return -1 in case of error, the number of segment in case of success
+ */
+int StreamSegmentForEach(const Packet *p, uint8_t flag, StreamSegmentCallback CallbackFunc, void *data)
+{
+ switch(p->proto) {
+ case IPPROTO_TCP:
+ return StreamTcpSegmentForEach(p, flag, CallbackFunc, data);
+ break;
+#ifdef DEBUG
+ case IPPROTO_UDP:
+ SCLogWarning(SC_ERR_UNKNOWN_PROTOCOL, "UDP is currently unsupported");
+ break;
+ default:
+ SCLogWarning(SC_ERR_UNKNOWN_PROTOCOL, "This protocol is currently unsupported");
+ break;
+#endif
+ }
+ return 0;
+}
diff --git a/framework/src/suricata/src/stream.h b/framework/src/suricata/src/stream.h
new file mode 100644
index 00000000..dfe5c342
--- /dev/null
+++ b/framework/src/suricata/src/stream.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __STREAM_H__
+#define __STREAM_H__
+
+#include "flow.h"
+
+#define STREAM_START 0x01
+#define STREAM_EOF 0x02
+#define STREAM_TOSERVER 0x04
+#define STREAM_TOCLIENT 0x08
+#define STREAM_GAP 0x10 /**< data gap encountered */
+#define STREAM_DEPTH 0x20 /**< depth reached */
+
+typedef struct StreamMsg_ {
+ struct StreamMsg_ *next;
+ struct StreamMsg_ *prev;
+
+ uint32_t seq; /**< sequence number */
+ uint32_t data_len; /**< length of the data */
+ uint32_t data_size;
+ uint8_t *data; /**< reassembled data: ptr to after this
+ * struct */
+} StreamMsg;
+
+typedef struct StreamMsgQueue_ {
+ StreamMsg *top;
+ StreamMsg *bot;
+ uint16_t len;
+#ifdef DBG_PERF
+ uint16_t dbg_maxlen;
+#endif /* DBG_PERF */
+} StreamMsgQueue;
+
+/* prototypes */
+void StreamMsgQueuesInit(uint32_t prealloc);
+void StreamMsgQueuesDeinit(char);
+
+StreamMsg *StreamMsgGetFromPool(void);
+void StreamMsgReturnToPool(StreamMsg *);
+StreamMsg *StreamMsgGetFromQueue(StreamMsgQueue *);
+void StreamMsgPutInQueue(StreamMsgQueue *, StreamMsg *);
+
+StreamMsgQueue *StreamMsgQueueGetNew(void);
+void StreamMsgQueueFree(StreamMsgQueue *);
+
+void StreamMsgQueueSetMinChunkLen(uint8_t dir, uint16_t len);
+uint16_t StreamMsgQueueGetMinChunkLen(uint8_t);
+
+void StreamMsgReturnListToPool(void *);
+
+typedef int (*StreamSegmentCallback)(const Packet *, void *, uint8_t *, uint32_t);
+int StreamSegmentForEach(const Packet *p, uint8_t flag,
+ StreamSegmentCallback CallbackFunc,
+ void *data);
+
+#endif /* __STREAM_H__ */
+
diff --git a/framework/src/suricata/src/suricata-common.h b/framework/src/suricata/src/suricata-common.h
new file mode 100644
index 00000000..388f1a42
--- /dev/null
+++ b/framework/src/suricata/src/suricata-common.h
@@ -0,0 +1,343 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Common includes, etc.
+ */
+
+#ifndef __SURICATA_COMMON_H__
+#define __SURICATA_COMMON_H__
+
+#ifdef DEBUG
+#define DBG_PERF
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define _GNU_SOURCE
+#define __USE_GNU
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef CLS
+#warning "L1 cache line size not detected during build. Assuming 64 bytes."
+#define CLS 64
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_STDINT_h
+#include <stdint.h>
+#endif
+
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#if HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h> /* for gettid(2) */
+#endif
+
+#if HAVE_SCHED_H
+#include <sched.h> /* for sched_setaffinity(2) */
+#endif
+
+#include <pcre.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#else
+#ifdef OS_WIN32
+#include "win32-syslog.h"
+#endif /* OS_WIN32 */
+#endif /* HAVE_SYSLOG_H */
+
+#ifdef OS_WIN32
+#include "win32-misc.h"
+#include "win32-service.h"
+#endif /* OS_WIN32 */
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#if HAVE_SYS_SIGNAL_H
+#include <sys/signal.h>
+#endif
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_PCAP_H
+#include <pcap.h>
+#endif
+
+#ifdef HAVE_PCAP_PCAP_H
+#include <pcap/pcap.h>
+#endif
+
+#ifdef HAVE_PCAP_BPF_H
+#include <pcap/bpf.h>
+#endif
+
+#if __CYGWIN__
+#if !defined _X86_ && !defined __x86_64
+#define _X86_
+#endif
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#include <windows.h>
+#endif
+
+#ifdef HAVE_W32API_WINBASE_H
+#include <w32api/winbase.h>
+#endif
+
+#ifdef HAVE_W32API_WTYPES_H
+#include <w32api/wtypes.h>
+#endif
+
+#if !__CYGWIN__
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+#endif /* !__CYGWIN__ */
+
+#if CPPCHECK==1
+#define BUG_ON(x) if (((x))) exit(1)
+#else
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#define BUG_ON(x) assert(!(x))
+#else
+#define BUG_ON(x)
+#endif
+#endif
+
+/* we need this to stringify the defines which are supplied at compiletime see:
+ http://gcc.gnu.org/onlinedocs/gcc-3.4.1/cpp/Stringification.html#Stringification */
+#define xstr(s) str(s)
+#define str(s) #s
+
+/** type for the internal signature id. Since it's used in the matching engine
+ * extensively keeping this as small as possible reduces the overall memory
+ * footprint of the engine. Set to uint32_t if the engine needs to support
+ * more than 64k sigs. */
+//#define SigIntId uint16_t
+#define SigIntId uint32_t
+
+/** same for pattern id's */
+#define PatIntId uint16_t
+
+/** FreeBSD does not define __WORDSIZE, but it uses __LONG_BIT */
+#ifndef __WORDSIZE
+ #ifdef __LONG_BIT
+ #define __WORDSIZE __LONG_BIT
+ #else
+ #ifdef LONG_BIT
+ #define __WORDSIZE LONG_BIT
+ #endif
+ #endif
+#endif
+
+/** Windows does not define __WORDSIZE, but it uses __X86__ */
+#ifndef __WORDSIZE
+ #if defined(__X86__) || defined(_X86_)
+ #define __WORDSIZE 32
+ #else
+ #if defined(__X86_64__) || defined(_X86_64_)
+ #define __WORDSIZE 64
+ #endif
+ #endif
+
+ #ifndef __WORDSIZE
+ #warning Defaulting to __WORDSIZE 32
+ #define __WORDSIZE 32
+ #endif
+#endif
+
+/** darwin doesn't defined __BYTE_ORDER and friends, but BYTE_ORDER */
+#ifndef __BYTE_ORDER
+#ifdef BYTE_ORDER
+#define __BYTE_ORDER BYTE_ORDER
+#endif
+#endif
+
+#ifndef __LITTLE_ENDIAN
+#ifdef LITTLE_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef __BIG_ENDIAN
+#ifdef BIG_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#endif
+#endif
+
+#ifndef HAVE_PCRE_FREE_STUDY
+#define pcre_free_study pcre_free
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x)<(y))?(x):(y))
+#endif
+
+typedef enum PacketProfileDetectId_ {
+ PROF_DETECT_MPM,
+ PROF_DETECT_MPM_PACKET, /* PKT MPM */
+ PROF_DETECT_MPM_PKT_STREAM, /* PKT inspected with stream MPM */
+ PROF_DETECT_MPM_STREAM, /* STREAM MPM */
+ PROF_DETECT_MPM_URI,
+ PROF_DETECT_MPM_HCBD,
+ PROF_DETECT_MPM_HSBD,
+ PROF_DETECT_MPM_HHD,
+ PROF_DETECT_MPM_HRHD,
+ PROF_DETECT_MPM_HMD,
+ PROF_DETECT_MPM_HCD,
+ PROF_DETECT_MPM_HRUD,
+ PROF_DETECT_MPM_HSMD,
+ PROF_DETECT_MPM_HSCD,
+ PROF_DETECT_MPM_HUAD,
+ PROF_DETECT_MPM_HHHD,
+ PROF_DETECT_MPM_HRHHD,
+ PROF_DETECT_MPM_DNSQUERY,
+ PROF_DETECT_IPONLY,
+ PROF_DETECT_RULES,
+ PROF_DETECT_STATEFUL,
+ PROF_DETECT_PREFILTER,
+ PROF_DETECT_NONMPMLIST,
+ PROF_DETECT_ALERT,
+ PROF_DETECT_CLEANUP,
+ PROF_DETECT_GETSGH,
+ PROF_DETECT_MPM_FD_SMTP,
+
+ PROF_DETECT_SIZE,
+} PacketProfileDetectId;
+
+#include <htp/htp.h>
+#include "threads.h"
+#include "tm-threads-common.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-mem.h"
+#include "detect-engine-alert.h"
+#include "util-optimize.h"
+#include "util-path.h"
+#include "util-conf.h"
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *, const char *src, size_t siz);
+#endif
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
+extern int coverage_unittests;
+extern int g_ut_modules;
+extern int g_ut_covered;
+#endif /* __SURICATA_COMMON_H__ */
+
diff --git a/framework/src/suricata/src/suricata.c b/framework/src/suricata/src/suricata.c
new file mode 100644
index 00000000..7c661e19
--- /dev/null
+++ b/framework/src/suricata/src/suricata.c
@@ -0,0 +1,2543 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_NSS
+#include <prinit.h>
+#include <nss.h>
+#endif
+
+#include "suricata.h"
+#include "decode.h"
+#include "detect.h"
+#include "packet-queue.h"
+#include "threads.h"
+#include "threadvars.h"
+
+#include "util-atomic.h"
+#include "util-spm.h"
+#include "util-cpu.h"
+#include "util-action.h"
+#include "util-pidfile.h"
+#include "util-ioctl.h"
+#include "util-device.h"
+#include "util-misc.h"
+#include "util-running-modes.h"
+
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "detect-fast-pattern.h"
+#include "detect-engine-tag.h"
+#include "detect-engine-threshold.h"
+#include "detect-engine-address.h"
+#include "detect-engine-port.h"
+#include "detect-engine-mpm.h"
+
+#include "tm-queuehandlers.h"
+#include "tm-queues.h"
+#include "tm-threads.h"
+
+#include "tmqh-flow.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "alert-fastlog.h"
+#include "alert-unified2-alert.h"
+#include "alert-debuglog.h"
+#include "alert-prelude.h"
+#include "alert-syslog.h"
+#include "output-json-alert.h"
+
+#include "output-json-flow.h"
+#include "output-json-netflow.h"
+#include "log-droplog.h"
+#include "output-json-drop.h"
+#include "log-httplog.h"
+#include "output-json-http.h"
+#include "log-dnslog.h"
+#include "output-json-dns.h"
+#include "log-tlslog.h"
+#include "log-tlsstore.h"
+#include "output-json-tls.h"
+#include "output-json-ssh.h"
+#include "log-pcap.h"
+#include "log-file.h"
+#include "output-json-file.h"
+#include "output-json-smtp.h"
+#include "output-json-stats.h"
+#include "log-filestore.h"
+#include "log-tcp-data.h"
+#include "log-stats.h"
+
+#include "output-json.h"
+
+#include "stream-tcp.h"
+
+#include "source-nfq.h"
+#include "source-nfq-prototypes.h"
+
+#include "source-nflog.h"
+
+#include "source-ipfw.h"
+
+#include "source-pcap.h"
+#include "source-pcap-file.h"
+
+#include "source-pfring.h"
+
+#include "source-erf-file.h"
+#include "source-erf-dag.h"
+#include "source-napatech.h"
+
+#include "source-af-packet.h"
+#include "source-netmap.h"
+#include "source-mpipe.h"
+
+#include "respond-reject.h"
+
+#include "flow.h"
+#include "flow-timeout.h"
+#include "flow-manager.h"
+#include "flow-var.h"
+#include "flow-bit.h"
+#include "pkt-var.h"
+#include "host-bit.h"
+
+#include "ippair.h"
+#include "ippair-bit.h"
+
+#include "host.h"
+#include "unix-manager.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+
+#include "util-radix-tree.h"
+#include "util-host-os-info.h"
+#include "util-cidr.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-time.h"
+#include "util-rule-vars.h"
+#include "util-classification-config.h"
+#include "util-threshold-config.h"
+#include "util-reference-config.h"
+#include "util-profiling.h"
+#include "util-magic.h"
+#include "util-signal.h"
+
+#include "util-coredump-config.h"
+
+#include "util-decode-mime.h"
+
+#include "defrag.h"
+
+#include "runmodes.h"
+#include "runmode-unittests.h"
+
+#include "util-cuda.h"
+#include "util-decode-asn1.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-daemon.h"
+#include "reputation.h"
+
+#include "output.h"
+#include "output-lua.h"
+
+#include "output-packet.h"
+#include "output-tx.h"
+#include "output-file.h"
+#include "output-filedata.h"
+#include "output-streaming.h"
+
+#include "util-privs.h"
+
+#include "tmqh-packetpool.h"
+
+#include "util-proto-name.h"
+#ifdef __SC_CUDA_SUPPORT__
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#endif
+#include "util-storage.h"
+#include "host-storage.h"
+
+/*
+ * we put this here, because we only use it here in main.
+ */
+volatile sig_atomic_t sigint_count = 0;
+volatile sig_atomic_t sighup_count = 0;
+volatile sig_atomic_t sigterm_count = 0;
+volatile sig_atomic_t sigusr2_count = 0;
+
+/*
+ * Flag to indicate if the engine is at the initialization
+ * or already processing packets. 3 stages: SURICATA_INIT,
+ * SURICATA_RUNTIME and SURICATA_FINALIZE
+ */
+SC_ATOMIC_DECLARE(unsigned int, engine_stage);
+
+/* Max packets processed simultaniously per thread. */
+#define DEFAULT_MAX_PENDING_PACKETS 1024
+
+/** suricata engine control flags */
+volatile uint8_t suricata_ctl_flags = 0;
+
+/** Run mode selected */
+int run_mode = RUNMODE_UNKNOWN;
+
+/** Engine mode: inline (ENGINE_MODE_IPS) or just
+ * detection mode (ENGINE_MODE_IDS by default) */
+static enum EngineMode g_engine_mode = ENGINE_MODE_IDS;
+
+/** Host mode: set if box is sniffing only
+ * or is a router */
+uint8_t host_mode = SURI_HOST_IS_SNIFFER_ONLY;
+
+/** Maximum packets to simultaneously process. */
+intmax_t max_pending_packets;
+
+/** global indicating if detection is enabled */
+int g_detect_disabled = 0;
+
+/** set caps or not */
+int sc_set_caps;
+
+char *conf_filename = NULL;
+
+int EngineModeIsIPS(void)
+{
+ return (g_engine_mode == ENGINE_MODE_IPS);
+}
+
+int EngineModeIsIDS(void)
+{
+ return (g_engine_mode == ENGINE_MODE_IDS);
+}
+
+void EngineModeSetIPS(void)
+{
+ g_engine_mode = ENGINE_MODE_IPS;
+}
+
+void EngineModeSetIDS(void)
+{
+ g_engine_mode = ENGINE_MODE_IDS;
+}
+
+int RunmodeIsUnittests(void)
+{
+ if (run_mode == RUNMODE_UNITTEST)
+ return 1;
+
+ return 0;
+}
+
+int RunmodeGetCurrent(void)
+{
+ return run_mode;
+}
+
+static void SignalHandlerSigint(/*@unused@*/ int sig)
+{
+ sigint_count = 1;
+ suricata_ctl_flags |= SURICATA_STOP;
+}
+static void SignalHandlerSigterm(/*@unused@*/ int sig)
+{
+ sigterm_count = 1;
+ suricata_ctl_flags |= SURICATA_KILL;
+}
+
+void SignalHandlerSigusr2StartingUp(int sig)
+{
+ SCLogInfo("Live rule reload only possible after engine completely started.");
+}
+
+void SignalHandlerSigusr2DelayedDetect(int sig)
+{
+ SCLogWarning(SC_ERR_LIVE_RULE_SWAP, "Live rule reload blocked while delayed detect is still loading.");
+}
+
+void SignalHandlerSigusr2SigFileStartup(int sig)
+{
+ SCLogInfo("Live rule reload not possible if -s or -S option used at runtime.");
+}
+
+/**
+ * SIGUSR2 handler. Just set sigusr2_count. The main loop will act on
+ * it.
+ */
+void SignalHandlerSigusr2(int sig)
+{
+ sigusr2_count = 1;
+}
+
+/**
+ * SIGHUP handler. Just set sighup_count. The main loop will act on
+ * it.
+ */
+static void SignalHandlerSigHup(/*@unused@*/ int sig)
+{
+ sighup_count = 1;
+}
+
+#ifdef DBG_MEM_ALLOC
+#ifndef _GLOBAL_MEM_
+#define _GLOBAL_MEM_
+/* This counter doesn't complain realloc's(), it's gives
+ * an aproximation for the startup */
+size_t global_mem = 0;
+#ifdef DBG_MEM_ALLOC_SKIP_STARTUP
+uint8_t print_mem_flag = 0;
+#else
+uint8_t print_mem_flag = 1;
+#endif
+#endif
+#endif
+
+void CreateLowercaseTable()
+{
+ /* create table for O(1) lowercase conversion lookup. It was removed, but
+ * we still need it for cuda. So resintalling it back into the codebase */
+ int c = 0;
+ memset(g_u8_lowercasetable, 0x00, sizeof(g_u8_lowercasetable));
+ for ( ; c < 256; c++) {
+ if (c >= 'A' && c <= 'Z')
+ g_u8_lowercasetable[c] = (c + ('a' - 'A'));
+ else
+ g_u8_lowercasetable[c] = c;
+ }
+}
+
+void GlobalInits()
+{
+ memset(trans_q, 0, sizeof(trans_q));
+ memset(data_queues, 0, sizeof(data_queues));
+
+ /* Initialize the trans_q mutex */
+ int blah;
+ int r = 0;
+ for(blah=0;blah<256;blah++) {
+ r |= SCMutexInit(&trans_q[blah].mutex_q, NULL);
+ r |= SCCondInit(&trans_q[blah].cond_q, NULL);
+
+ r |= SCMutexInit(&data_queues[blah].mutex_q, NULL);
+ r |= SCCondInit(&data_queues[blah].cond_q, NULL);
+ }
+
+ if (r != 0) {
+ SCLogInfo("Trans_Q Mutex not initialized correctly");
+ exit(EXIT_FAILURE);
+ }
+
+ CreateLowercaseTable();
+}
+
+/* XXX hack: make sure threads can stop the engine by calling this
+ function. Purpose: pcap file mode needs to be able to tell the
+ engine the file eof is reached. */
+void EngineStop(void)
+{
+ suricata_ctl_flags |= SURICATA_STOP;
+}
+
+void EngineKill(void)
+{
+ suricata_ctl_flags |= SURICATA_KILL;
+}
+
+/**
+ * \brief Used to indicate that the current task is done.
+ *
+ * This is mainly used by pcap-file to tell it has finished
+ * to treat a pcap files when running in unix-socket mode.
+ */
+void EngineDone(void)
+{
+ suricata_ctl_flags |= SURICATA_DONE;
+}
+
+static int SetBpfString(int optind, char *argv[])
+{
+ char *bpf_filter = NULL;
+ uint32_t bpf_len = 0;
+ int tmpindex = 0;
+
+ /* attempt to parse remaining args as bpf filter */
+ tmpindex = optind;
+ while(argv[tmpindex] != NULL) {
+ bpf_len+=strlen(argv[tmpindex]) + 1;
+ tmpindex++;
+ }
+
+ if (bpf_len == 0)
+ return TM_ECODE_OK;
+
+ if (EngineModeIsIPS()) {
+ SCLogError(SC_ERR_NOT_SUPPORTED,
+ "BPF filter not available in IPS mode."
+ " Use firewall filtering if possible.");
+ return TM_ECODE_FAILED;
+ }
+
+ bpf_filter = SCMalloc(bpf_len);
+ if (unlikely(bpf_filter == NULL))
+ return TM_ECODE_OK;
+ memset(bpf_filter, 0x00, bpf_len);
+
+ tmpindex = optind;
+ while(argv[tmpindex] != NULL) {
+ strlcat(bpf_filter, argv[tmpindex],bpf_len);
+ if(argv[tmpindex + 1] != NULL) {
+ strlcat(bpf_filter," ", bpf_len);
+ }
+ tmpindex++;
+ }
+
+ if(strlen(bpf_filter) > 0) {
+ if (ConfSetFinal("bpf-filter", bpf_filter) != 1) {
+ SCLogError(SC_ERR_FATAL, "Failed to set bpf filter.");
+ return TM_ECODE_FAILED;
+ }
+ }
+ SCFree(bpf_filter);
+
+ return TM_ECODE_OK;
+}
+
+static void SetBpfStringFromFile(char *filename)
+{
+ char *bpf_filter = NULL;
+ char *bpf_comment_tmp = NULL;
+ char *bpf_comment_start = NULL;
+ uint32_t bpf_len = 0;
+#ifdef OS_WIN32
+ struct _stat st;
+#else
+ struct stat st;
+#endif /* OS_WIN32 */
+ FILE *fp = NULL;
+ size_t nm = 0;
+
+ if (EngineModeIsIPS()) {
+ SCLogError(SC_ERR_NOT_SUPPORTED,
+ "BPF filter not available in IPS mode."
+ " Use firewall filtering if possible.");
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef OS_WIN32
+ if(_stat(filename, &st) != 0) {
+#else
+ if(stat(filename, &st) != 0) {
+#endif /* OS_WIN32 */
+ SCLogError(SC_ERR_FOPEN, "Failed to stat file %s", filename);
+ exit(EXIT_FAILURE);
+ }
+ bpf_len = st.st_size + 1;
+
+ fp = fopen(filename,"r");
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "Failed to open file %s", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ bpf_filter = SCMalloc(bpf_len * sizeof(char));
+ if (unlikely(bpf_filter == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate buffer for bpf filter in file %s", filename);
+ exit(EXIT_FAILURE);
+ }
+ memset(bpf_filter, 0x00, bpf_len);
+
+ nm = fread(bpf_filter, bpf_len - 1, 1, fp);
+ if((ferror(fp) != 0)||( nm != 1)) {
+ *bpf_filter='\0';
+ }
+ fclose(fp);
+
+ if(strlen(bpf_filter) > 0) {
+ /*replace comments with space*/
+ bpf_comment_start = bpf_filter;
+ while((bpf_comment_tmp = strchr(bpf_comment_start, '#')) != NULL) {
+ while((*bpf_comment_tmp !='\0') &&
+ (*bpf_comment_tmp != '\r') && (*bpf_comment_tmp != '\n'))
+ {
+ *bpf_comment_tmp++ = ' ';
+ }
+ bpf_comment_start = bpf_comment_tmp;
+ }
+ /*remove remaining '\r' and '\n' */
+ while((bpf_comment_tmp = strchr(bpf_filter, '\r')) != NULL) {
+ *bpf_comment_tmp = ' ';
+ }
+ while((bpf_comment_tmp = strchr(bpf_filter, '\n')) != NULL) {
+ *bpf_comment_tmp = ' ';
+ }
+ if(ConfSetFinal("bpf-filter", bpf_filter) != 1) {
+ SCLogError(SC_ERR_FOPEN, "ERROR: Failed to set bpf filter!");
+ SCFree(bpf_filter);
+ exit(EXIT_FAILURE);
+ }
+ }
+ SCFree(bpf_filter);
+}
+
+void usage(const char *progname)
+{
+#ifdef REVISION
+ printf("%s %s (rev %s)\n", PROG_NAME, PROG_VER, xstr(REVISION));
+#else
+ printf("%s %s\n", PROG_NAME, PROG_VER);
+#endif
+ printf("USAGE: %s [OPTIONS] [BPF FILTER]\n\n", progname);
+ printf("\t-c <path> : path to configuration file\n");
+ printf("\t-T : test configuration file (use with -c)\n");
+ printf("\t-i <dev or ip> : run in pcap live mode\n");
+ printf("\t-F <bpf filter file> : bpf filter file\n");
+ printf("\t-r <path> : run in pcap file/offline mode\n");
+#ifdef NFQ
+ printf("\t-q <qid> : run in inline nfqueue mode\n");
+#endif /* NFQ */
+#ifdef IPFW
+ printf("\t-d <divert port> : run in inline ipfw divert mode\n");
+#endif /* IPFW */
+ printf("\t-s <path> : path to signature file loaded in addition to suricata.yaml settings (optional)\n");
+ printf("\t-S <path> : path to signature file loaded exclusively (optional)\n");
+ printf("\t-l <dir> : default log directory\n");
+#ifndef OS_WIN32
+ printf("\t-D : run as daemon\n");
+#else
+ printf("\t--service-install : install as service\n");
+ printf("\t--service-remove : remove service\n");
+ printf("\t--service-change-params : change service startup parameters\n");
+#endif /* OS_WIN32 */
+ printf("\t-k [all|none] : force checksum check (all) or disabled it (none)\n");
+ printf("\t-V : display Suricata version\n");
+ printf("\t-v[v] : increase default Suricata verbosity\n");
+#ifdef UNITTESTS
+ printf("\t-u : run the unittests and exit\n");
+ printf("\t-U, --unittest-filter=REGEX : filter unittests with a regex\n");
+ printf("\t--list-unittests : list unit tests\n");
+ printf("\t--fatal-unittests : enable fatal failure on unittest error\n");
+ printf("\t--unittests-coverage : display unittest coverage report\n");
+#endif /* UNITTESTS */
+ printf("\t--list-app-layer-protos : list supported app layer protocols\n");
+ printf("\t--list-keywords[=all|csv|<kword>] : list keywords implemented by the engine\n");
+#ifdef __SC_CUDA_SUPPORT__
+ printf("\t--list-cuda-cards : list cuda supported cards\n");
+#endif
+ printf("\t--list-runmodes : list supported runmodes\n");
+ printf("\t--runmode <runmode_id> : specific runmode modification the engine should run. The argument\n"
+ "\t supplied should be the id for the runmode obtained by running\n"
+ "\t --list-runmodes\n");
+ printf("\t--engine-analysis : print reports on analysis of different sections in the engine and exit.\n"
+ "\t Please have a look at the conf parameter engine-analysis on what reports\n"
+ "\t can be printed\n");
+ printf("\t--pidfile <file> : write pid to this file\n");
+ printf("\t--init-errors-fatal : enable fatal failure on signature init error\n");
+ printf("\t--disable-detection : disable detection engine\n");
+ printf("\t--dump-config : show the running configuration\n");
+ printf("\t--build-info : display build information\n");
+ printf("\t--pcap[=<dev>] : run in pcap mode, no value select interfaces from suricata.yaml\n");
+#ifdef HAVE_PCAP_SET_BUFF
+ printf("\t--pcap-buffer-size : size of the pcap buffer value from 0 - %i\n",INT_MAX);
+#endif /* HAVE_SET_PCAP_BUFF */
+#ifdef HAVE_AF_PACKET
+ printf("\t--af-packet[=<dev>] : run in af-packet mode, no value select interfaces from suricata.yaml\n");
+#endif
+#ifdef HAVE_NETMAP
+ printf("\t--netmap[=<dev>] : run in netmap mode, no value select interfaces from suricata.yaml\n");
+#endif
+#ifdef HAVE_PFRING
+ printf("\t--pfring[=<dev>] : run in pfring mode, use interfaces from suricata.yaml\n");
+ printf("\t--pfring-int <dev> : run in pfring mode, use interface <dev>\n");
+ printf("\t--pfring-cluster-id <id> : pfring cluster id \n");
+ printf("\t--pfring-cluster-type <type> : pfring cluster type for PF_RING 4.1.2 and later cluster_round_robin|cluster_flow\n");
+#endif /* HAVE_PFRING */
+#ifdef HAVE_LIBCAP_NG
+ printf("\t--user <user> : run suricata as this user after init\n");
+ printf("\t--group <group> : run suricata as this group after init\n");
+#endif /* HAVE_LIBCAP_NG */
+ printf("\t--erf-in <path> : process an ERF file\n");
+#ifdef HAVE_DAG
+ printf("\t--dag <dagX:Y> : process ERF records from DAG interface X, stream Y\n");
+#endif
+#ifdef HAVE_NAPATECH
+ printf("\t--napatech : run Napatech Streams using the API\n");
+#endif
+#ifdef BUILD_UNIX_SOCKET
+ printf("\t--unix-socket[=<file>] : use unix socket to control suricata work\n");
+#endif
+#ifdef HAVE_MPIPE
+ printf("\t--mpipe : run with tilegx mpipe interface(s)\n");
+#endif
+ printf("\t--set name=value : set a configuration value\n");
+ printf("\n");
+ printf("\nTo run the engine with default configuration on "
+ "interface eth0 with signature file \"signatures.rules\", run the "
+ "command as:\n\n%s -c suricata.yaml -s signatures.rules -i eth0 \n\n",
+ progname);
+}
+
+void SCPrintBuildInfo(void)
+{
+ char *bits = "<unknown>-bits";
+ char *endian = "<unknown>-endian";
+ char features[2048] = "";
+ char *tls = "pthread key";
+
+#ifdef REVISION
+ printf("This is %s version %s (rev %s)\n", PROG_NAME, PROG_VER, xstr(REVISION));
+#elif defined RELEASE
+ printf("This is %s version %s RELEASE\n", PROG_NAME, PROG_VER);
+#else
+ printf("This is %s version %s\n", PROG_NAME, PROG_VER);
+#endif
+
+#ifdef DEBUG
+ strlcat(features, "DEBUG ", sizeof(features));
+#endif
+#ifdef DEBUG_VALIDATION
+ strlcat(features, "DEBUG_VALIDATION ", sizeof(features));
+#endif
+#ifdef UNITTESTS
+ strlcat(features, "UNITTESTS ", sizeof(features));
+#endif
+#ifdef NFQ
+ strlcat(features, "NFQ ", sizeof(features));
+#endif
+#ifdef IPFW
+ strlcat(features, "IPFW ", sizeof(features));
+#endif
+#ifdef HAVE_PCAP_SET_BUFF
+ strlcat(features, "PCAP_SET_BUFF ", sizeof(features));
+#endif
+#if LIBPCAP_VERSION_MAJOR == 1
+ strlcat(features, "LIBPCAP_VERSION_MAJOR=1 ", sizeof(features));
+#elif LIBPCAP_VERSION_MAJOR == 0
+ strlcat(features, "LIBPCAP_VERSION_MAJOR=0 ", sizeof(features));
+#endif
+#ifdef __SC_CUDA_SUPPORT__
+ strlcat(features, "CUDA ", sizeof(features));
+#endif
+#ifdef HAVE_PFRING
+ strlcat(features, "PF_RING ", sizeof(features));
+#endif
+#ifdef HAVE_AF_PACKET
+ strlcat(features, "AF_PACKET ", sizeof(features));
+#endif
+#ifdef HAVE_NETMAP
+ strlcat(features, "NETMAP ", sizeof(features));
+#endif
+#ifdef HAVE_PACKET_FANOUT
+ strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features));
+#endif
+#ifdef HAVE_DAG
+ strlcat(features, "DAG ", sizeof(features));
+#endif
+#ifdef HAVE_LIBCAP_NG
+ strlcat(features, "LIBCAP_NG ", sizeof(features));
+#endif
+#ifdef HAVE_LIBNET11
+ strlcat(features, "LIBNET1.1 ", sizeof(features));
+#endif
+#ifdef HAVE_HTP_URI_NORMALIZE_HOOK
+ strlcat(features, "HAVE_HTP_URI_NORMALIZE_HOOK ", sizeof(features));
+#endif
+#ifdef PCRE_HAVE_JIT
+ strlcat(features, "PCRE_JIT ", sizeof(features));
+#endif
+#ifdef HAVE_NSS
+ strlcat(features, "HAVE_NSS ", sizeof(features));
+#endif
+#ifdef HAVE_LUA
+ strlcat(features, "HAVE_LUA ", sizeof(features));
+#endif
+#ifdef HAVE_LUAJIT
+ strlcat(features, "HAVE_LUAJIT ", sizeof(features));
+#endif
+#ifdef HAVE_LIBJANSSON
+ strlcat(features, "HAVE_LIBJANSSON ", sizeof(features));
+#endif
+#ifdef PROFILING
+ strlcat(features, "PROFILING ", sizeof(features));
+#endif
+#ifdef PROFILE_LOCKING
+ strlcat(features, "PROFILE_LOCKING ", sizeof(features));
+#endif
+#ifdef TLS
+ strlcat(features, "TLS ", sizeof(features));
+#endif
+ if (strlen(features) == 0) {
+ strlcat(features, "none", sizeof(features));
+ }
+
+ printf("Features: %s\n", features);
+
+ /* SIMD stuff */
+ memset(features, 0x00, sizeof(features));
+#if defined(__SSE4_2__)
+ strlcat(features, "SSE_4_2 ", sizeof(features));
+#endif
+#if defined(__SSE4_1__)
+ strlcat(features, "SSE_4_1 ", sizeof(features));
+#endif
+#if defined(__SSE3__)
+ strlcat(features, "SSE_3 ", sizeof(features));
+#endif
+#if defined(__tile__)
+ strlcat(features, "Tilera ", sizeof(features));
+#endif
+ if (strlen(features) == 0) {
+ strlcat(features, "none", sizeof(features));
+ }
+ printf("SIMD support: %s\n", features);
+
+ /* atomics stuff */
+ memset(features, 0x00, sizeof(features));
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)
+ strlcat(features, "1 ", sizeof(features));
+#endif
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)
+ strlcat(features, "2 ", sizeof(features));
+#endif
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ strlcat(features, "4 ", sizeof(features));
+#endif
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
+ strlcat(features, "8 ", sizeof(features));
+#endif
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
+ strlcat(features, "16 ", sizeof(features));
+#endif
+ if (strlen(features) == 0) {
+ strlcat(features, "none", sizeof(features));
+ } else {
+ strlcat(features, "byte(s)", sizeof(features));
+ }
+ printf("Atomic intrisics: %s\n", features);
+
+#if __WORDSIZE == 64
+ bits = "64-bits";
+#elif __WORDSIZE == 32
+ bits = "32-bits";
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ endian = "Big-endian";
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ endian = "Little-endian";
+#endif
+
+ printf("%s, %s architecture\n", bits, endian);
+#ifdef __GNUC__
+ printf("GCC version %s, C version %"PRIiMAX"\n", __VERSION__, (intmax_t)__STDC_VERSION__);
+#else
+ printf("C version %"PRIiMAX"\n", (intmax_t)__STDC_VERSION__);
+#endif
+
+#if __SSP__ == 1
+ printf("compiled with -fstack-protector\n");
+#endif
+#if __SSP_ALL__ == 2
+ printf("compiled with -fstack-protector-all\n");
+#endif
+#ifdef _FORTIFY_SOURCE
+ printf("compiled with _FORTIFY_SOURCE=%d\n", _FORTIFY_SOURCE);
+#endif
+#ifdef CLS
+ printf("L1 cache line size (CLS)=%d\n", CLS);
+#endif
+#ifdef TLS
+ tls = "__thread";
+#endif
+ printf("thread local storage method: %s\n", tls);
+
+ printf("compiled with %s, linked against %s\n",
+ HTP_VERSION_STRING_FULL, htp_get_version());
+ printf("\n");
+#include "build-info.h"
+}
+
+int coverage_unittests;
+int g_ut_modules;
+int g_ut_covered;
+
+void RegisterAllModules()
+{
+ /* commanders */
+ TmModuleUnixManagerRegister();
+ /* managers */
+ TmModuleFlowManagerRegister();
+ TmModuleFlowRecyclerRegister();
+ /* nfq */
+ TmModuleReceiveNFQRegister();
+ TmModuleVerdictNFQRegister();
+ TmModuleDecodeNFQRegister();
+ /* ipfw */
+ TmModuleReceiveIPFWRegister();
+ TmModuleVerdictIPFWRegister();
+ TmModuleDecodeIPFWRegister();
+ /* pcap live */
+ TmModuleReceivePcapRegister();
+ TmModuleDecodePcapRegister();
+ /* pcap file */
+ TmModuleReceivePcapFileRegister();
+ TmModuleDecodePcapFileRegister();
+#ifdef HAVE_MPIPE
+ /* mpipe */
+ TmModuleReceiveMpipeRegister();
+ TmModuleDecodeMpipeRegister();
+#endif
+ /* af-packet */
+ TmModuleReceiveAFPRegister();
+ TmModuleDecodeAFPRegister();
+ /* netmap */
+ TmModuleReceiveNetmapRegister();
+ TmModuleDecodeNetmapRegister();
+ /* pfring */
+ TmModuleReceivePfringRegister();
+ TmModuleDecodePfringRegister();
+ /* dag file */
+ TmModuleReceiveErfFileRegister();
+ TmModuleDecodeErfFileRegister();
+ /* dag live */
+ TmModuleReceiveErfDagRegister();
+ TmModuleDecodeErfDagRegister();
+ /* napatech */
+ TmModuleNapatechStreamRegister();
+ TmModuleNapatechDecodeRegister();
+
+ /* stream engine */
+ TmModuleStreamTcpRegister();
+ /* detection */
+ TmModuleDetectRegister();
+ /* respond-reject */
+ TmModuleRespondRejectRegister();
+
+ TmModuleLuaLogRegister();
+ /* fast log */
+ TmModuleAlertFastLogRegister();
+ /* debug log */
+ TmModuleAlertDebugLogRegister();
+ /* prelue log */
+ TmModuleAlertPreludeRegister();
+ /* syslog log */
+ TmModuleAlertSyslogRegister();
+ /* unified2 log */
+ TmModuleUnified2AlertRegister();
+ /* drop log */
+ TmModuleLogDropLogRegister();
+ TmModuleJsonDropLogRegister();
+ /* json log */
+ TmModuleOutputJsonRegister();
+ /* email logs */
+ TmModuleJsonSmtpLogRegister();
+ /* http log */
+ TmModuleLogHttpLogRegister();
+ TmModuleJsonHttpLogRegister();
+ /* tls log */
+ TmModuleLogTlsLogRegister();
+ TmModuleJsonTlsLogRegister();
+ TmModuleLogTlsStoreRegister();
+ /* ssh */
+ TmModuleJsonSshLogRegister();
+ /* pcap log */
+ TmModulePcapLogRegister();
+ /* file log */
+ TmModuleLogFileLogRegister();
+ TmModuleJsonFileLogRegister();
+ TmModuleLogFilestoreRegister();
+ /* dns log */
+ TmModuleLogDnsLogRegister();
+ TmModuleJsonDnsLogRegister();
+ /* tcp streaming data */
+ TmModuleLogTcpDataLogRegister();
+ /* log stats */
+ TmModuleLogStatsLogRegister();
+
+ TmModuleJsonAlertLogRegister();
+ /* flow/netflow */
+ TmModuleJsonFlowLogRegister();
+ TmModuleJsonNetFlowLogRegister();
+ /* json stats */
+ TmModuleJsonStatsLogRegister();
+
+ /* log api */
+ TmModulePacketLoggerRegister();
+ TmModuleTxLoggerRegister();
+ TmModuleFileLoggerRegister();
+ TmModuleFiledataLoggerRegister();
+ TmModuleStreamingLoggerRegister();
+ TmModuleStatsLoggerRegister();
+ TmModuleDebugList();
+ /* nflog */
+ TmModuleReceiveNFLOGRegister();
+ TmModuleDecodeNFLOGRegister();
+
+}
+
+TmEcode LoadYamlConfig(char *conf_filename)
+{
+ SCEnter();
+
+ if (conf_filename == NULL)
+ SCReturnInt(TM_ECODE_OK);
+
+ if (ConfYamlLoadFile(conf_filename) != 0) {
+ /* Error already displayed. */
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static TmEcode ParseInterfacesList(int run_mode, char *pcap_dev)
+{
+ SCEnter();
+
+ /* run the selected runmode */
+ if (run_mode == RUNMODE_PCAP_DEV) {
+ if (strlen(pcap_dev) == 0) {
+ int ret = LiveBuildDeviceList("pcap");
+ if (ret == 0) {
+ SCLogError(SC_ERR_INITIALIZATION, "No interface found in config for pcap");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+#ifdef HAVE_MPIPE
+ } else if (run_mode == RUNMODE_TILERA_MPIPE) {
+ if (strlen(pcap_dev)) {
+ if (ConfSetFinal("mpipe.single_mpipe_dev", pcap_dev) != 1) {
+ fprintf(stderr, "ERROR: Failed to set mpipe.single_mpipe_dev\n");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ int ret = LiveBuildDeviceList("mpipe.inputs");
+ if (ret == 0) {
+ fprintf(stderr, "ERROR: No interface found in config for mpipe\n");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+#endif
+ } else if (run_mode == RUNMODE_PFRING) {
+ /* FIXME add backward compat support */
+ /* iface has been set on command line */
+ if (strlen(pcap_dev)) {
+ if (ConfSetFinal("pfring.live-interface", pcap_dev) != 1) {
+ SCLogError(SC_ERR_INITIALIZATION, "Failed to set pfring.live-interface");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ /* not an error condition if we have a 1.0 config */
+ LiveBuildDeviceList("pfring");
+ }
+ } else if (run_mode == RUNMODE_AFP_DEV) {
+ /* iface has been set on command line */
+ if (strlen(pcap_dev)) {
+ if (ConfSetFinal("af-packet.live-interface", pcap_dev) != 1) {
+ SCLogError(SC_ERR_INITIALIZATION, "Failed to set af-packet.live-interface");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ int ret = LiveBuildDeviceList("af-packet");
+ if (ret == 0) {
+ SCLogError(SC_ERR_INITIALIZATION, "No interface found in config for af-packet");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (AFPRunModeIsIPS()) {
+ SCLogInfo("AF_PACKET: Setting IPS mode");
+ EngineModeSetIPS();
+ }
+ }
+#ifdef HAVE_NETMAP
+ } else if (run_mode == RUNMODE_NETMAP) {
+ /* iface has been set on command line */
+ if (strlen(pcap_dev)) {
+ if (ConfSetFinal("netmap.live-interface", pcap_dev) != 1) {
+ SCLogError(SC_ERR_INITIALIZATION, "Failed to set netmap.live-interface");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ int ret = LiveBuildDeviceList("netmap");
+ if (ret == 0) {
+ SCLogError(SC_ERR_INITIALIZATION, "No interface found in config for netmap");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (NetmapRunModeIsIPS()) {
+ SCLogInfo("Netmap: Setting IPS mode");
+ EngineModeSetIPS();
+ }
+ }
+#endif
+#ifdef HAVE_NFLOG
+ } else if (run_mode == RUNMODE_NFLOG) {
+ int ret = LiveBuildDeviceListCustom("nflog", "group");
+ if (ret == 0) {
+ SCLogError(SC_ERR_INITIALIZATION, "No group found in config for nflog");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+#endif
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+static void SCInstanceInit(SCInstance *suri)
+{
+ suri->run_mode = RUNMODE_UNKNOWN;
+
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ suri->sig_file = NULL;
+ suri->sig_file_exclusive = FALSE;
+ suri->pid_filename = NULL;
+ suri->regex_arg = NULL;
+
+ suri->keyword_info = NULL;
+ suri->runmode_custom_mode = NULL;
+#ifndef OS_WIN32
+ suri->user_name = NULL;
+ suri->group_name = NULL;
+ suri->do_setuid = FALSE;
+ suri->do_setgid = FALSE;
+ suri->userid = 0;
+ suri->groupid = 0;
+#endif /* OS_WIN32 */
+ suri->delayed_detect = 0;
+ suri->daemon = 0;
+ suri->offline = 0;
+ suri->verbose = 0;
+ /* use -1 as unknown */
+ suri->checksum_validation = -1;
+#if HAVE_DETECT_DISABLED==1
+ g_detect_disabled = suri->disabled_detect = 1;
+#else
+ g_detect_disabled = suri->disabled_detect = 0;
+#endif
+}
+
+static TmEcode PrintVersion()
+{
+#ifdef REVISION
+ printf("This is %s version %s (rev %s)\n", PROG_NAME, PROG_VER, xstr(REVISION));
+#elif defined RELEASE
+ printf("This is %s version %s RELEASE\n", PROG_NAME, PROG_VER);
+#else
+ printf("This is %s version %s\n", PROG_NAME, PROG_VER);
+#endif
+ return TM_ECODE_OK;
+}
+
+static TmEcode SCPrintVersion()
+{
+#ifdef REVISION
+ SCLogNotice("This is %s version %s (rev %s)", PROG_NAME, PROG_VER, xstr(REVISION));
+#elif defined RELEASE
+ SCLogNotice("This is %s version %s RELEASE", PROG_NAME, PROG_VER);
+#else
+ SCLogNotice("This is %s version %s", PROG_NAME, PROG_VER);
+#endif
+ return TM_ECODE_OK;
+}
+
+static void SCSetStartTime(SCInstance *suri)
+{
+ memset(&suri->start_time, 0, sizeof(suri->start_time));
+ gettimeofday(&suri->start_time, NULL);
+}
+
+static void SCPrintElapsedTime(SCInstance *suri)
+{
+ struct timeval end_time;
+ memset(&end_time, 0, sizeof(end_time));
+ gettimeofday(&end_time, NULL);
+ uint64_t milliseconds = ((end_time.tv_sec - suri->start_time.tv_sec) * 1000) +
+ (((1000000 + end_time.tv_usec - suri->start_time.tv_usec) / 1000) - 1000);
+ SCLogInfo("time elapsed %.3fs", (float)milliseconds/(float)1000);
+}
+
+static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
+{
+ int opt;
+
+ int dump_config = 0;
+ int list_app_layer_protocols = 0;
+ int list_unittests = 0;
+ int list_cuda_cards = 0;
+ int list_runmodes = 0;
+ int list_keywords = 0;
+ int build_info = 0;
+ int conf_test = 0;
+ int engine_analysis = 0;
+ int ret = TM_ECODE_OK;
+
+#ifdef UNITTESTS
+ coverage_unittests = 0;
+ g_ut_modules = 0;
+ g_ut_covered = 0;
+#endif
+
+ struct option long_opts[] = {
+ {"dump-config", 0, &dump_config, 1},
+ {"pfring", optional_argument, 0, 0},
+ {"pfring-int", required_argument, 0, 0},
+ {"pfring-cluster-id", required_argument, 0, 0},
+ {"pfring-cluster-type", required_argument, 0, 0},
+ {"af-packet", optional_argument, 0, 0},
+ {"netmap", optional_argument, 0, 0},
+ {"pcap", optional_argument, 0, 0},
+#ifdef BUILD_UNIX_SOCKET
+ {"unix-socket", optional_argument, 0, 0},
+#endif
+ {"pcap-buffer-size", required_argument, 0, 0},
+ {"unittest-filter", required_argument, 0, 'U'},
+ {"list-app-layer-protos", 0, &list_app_layer_protocols, 1},
+ {"list-unittests", 0, &list_unittests, 1},
+ {"list-cuda-cards", 0, &list_cuda_cards, 1},
+ {"list-runmodes", 0, &list_runmodes, 1},
+ {"list-keywords", optional_argument, &list_keywords, 1},
+ {"runmode", required_argument, NULL, 0},
+ {"engine-analysis", 0, &engine_analysis, 1},
+#ifdef OS_WIN32
+ {"service-install", 0, 0, 0},
+ {"service-remove", 0, 0, 0},
+ {"service-change-params", 0, 0, 0},
+#endif /* OS_WIN32 */
+ {"pidfile", required_argument, 0, 0},
+ {"init-errors-fatal", 0, 0, 0},
+ {"disable-detection", 0, 0, 0},
+ {"fatal-unittests", 0, 0, 0},
+ {"unittests-coverage", 0, &coverage_unittests, 1},
+ {"user", required_argument, 0, 0},
+ {"group", required_argument, 0, 0},
+ {"erf-in", required_argument, 0, 0},
+ {"dag", required_argument, 0, 0},
+ {"napatech", 0, 0, 0},
+ {"build-info", 0, &build_info, 1},
+#ifdef HAVE_MPIPE
+ {"mpipe", optional_argument, 0, 0},
+#endif
+ {"set", required_argument, 0, 0},
+#ifdef HAVE_NFLOG
+ {"nflog", optional_argument, 0, 0},
+#endif
+ {NULL, 0, NULL, 0}
+ };
+
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ char short_opts[] = "c:TDhi:l:q:d:r:us:S:U:VF:vk:";
+
+ while ((opt = getopt_long(argc, argv, short_opts, long_opts, &option_index)) != -1) {
+ switch (opt) {
+ case 0:
+ if (strcmp((long_opts[option_index]).name , "pfring") == 0 ||
+ strcmp((long_opts[option_index]).name , "pfring-int") == 0) {
+#ifdef HAVE_PFRING
+ suri->run_mode = RUNMODE_PFRING;
+ if (optarg != NULL) {
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ strlcpy(suri->pcap_dev, optarg,
+ ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
+ (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
+ LiveRegisterDevice(optarg);
+ }
+#else
+ SCLogError(SC_ERR_NO_PF_RING,"PF_RING not enabled. Make sure "
+ "to pass --enable-pfring to configure when building.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_PFRING */
+ }
+ else if(strcmp((long_opts[option_index]).name , "pfring-cluster-id") == 0){
+#ifdef HAVE_PFRING
+ if (ConfSetFinal("pfring.cluster-id", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set pfring.cluster-id.\n");
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NO_PF_RING,"PF_RING not enabled. Make sure "
+ "to pass --enable-pfring to configure when building.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_PFRING */
+ }
+ else if(strcmp((long_opts[option_index]).name , "pfring-cluster-type") == 0){
+#ifdef HAVE_PFRING
+ if (ConfSetFinal("pfring.cluster-type", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set pfring.cluster-type.\n");
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NO_PF_RING,"PF_RING not enabled. Make sure "
+ "to pass --enable-pfring to configure when building.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_PFRING */
+ }
+ else if (strcmp((long_opts[option_index]).name , "af-packet") == 0){
+#ifdef HAVE_AF_PACKET
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_AFP_DEV;
+ if (optarg) {
+ LiveRegisterDevice(optarg);
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ strlcpy(suri->pcap_dev, optarg,
+ ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
+ (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
+ }
+ } else if (suri->run_mode == RUNMODE_AFP_DEV) {
+ SCLogWarning(SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL, "using "
+ "multiple devices to get packets is experimental.");
+ if (optarg) {
+ LiveRegisterDevice(optarg);
+ } else {
+ SCLogInfo("Multiple af-packet option without interface on each is useless");
+ break;
+ }
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NO_AF_PACKET,"AF_PACKET not enabled. On Linux "
+ "host, make sure to pass --enable-af-packet to "
+ "configure when building.");
+ return TM_ECODE_FAILED;
+#endif
+ } else if (strcmp((long_opts[option_index]).name , "netmap") == 0){
+#ifdef HAVE_NETMAP
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_NETMAP;
+ if (optarg) {
+ LiveRegisterDevice(optarg);
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ strlcpy(suri->pcap_dev, optarg,
+ ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
+ (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
+ }
+ } else if (suri->run_mode == RUNMODE_NETMAP) {
+ SCLogWarning(SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL, "using "
+ "multiple devices to get packets is experimental.");
+ if (optarg) {
+ LiveRegisterDevice(optarg);
+ } else {
+ SCLogInfo("Multiple netmap option without interface on each is useless");
+ break;
+ }
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NO_NETMAP, "NETMAP not enabled.");
+ return TM_ECODE_FAILED;
+#endif
+ } else if (strcmp((long_opts[option_index]).name, "nflog") == 0) {
+#ifdef HAVE_NFLOG
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_NFLOG;
+ LiveBuildDeviceListCustom("nflog", "group");
+ }
+#else
+ SCLogError(SC_ERR_NFLOG_NOSUPPORT, "NFLOG not enabled.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_NFLOG */
+ } else if (strcmp((long_opts[option_index]).name , "pcap") == 0) {
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_PCAP_DEV;
+ if (optarg) {
+ LiveRegisterDevice(optarg);
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ strlcpy(suri->pcap_dev, optarg,
+ ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
+ (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
+ }
+ } else if (suri->run_mode == RUNMODE_PCAP_DEV) {
+#ifdef OS_WIN32
+ SCLogError(SC_ERR_PCAP_MULTI_DEV_NO_SUPPORT, "pcap multi dev "
+ "support is not (yet) supported on Windows.");
+ return TM_ECODE_FAILED;
+#else
+ SCLogWarning(SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL, "using "
+ "multiple pcap devices to get packets is experimental.");
+ LiveRegisterDevice(optarg);
+#endif
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ } else if(strcmp((long_opts[option_index]).name, "init-errors-fatal") == 0) {
+ if (ConfSetFinal("engine.init-failure-fatal", "1") != 1) {
+ fprintf(stderr, "ERROR: Failed to set engine init-failure-fatal.\n");
+ return TM_ECODE_FAILED;
+ }
+#ifdef BUILD_UNIX_SOCKET
+ } else if (strcmp((long_opts[option_index]).name , "unix-socket") == 0) {
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_UNIX_SOCKET;
+ if (optarg) {
+ if (ConfSetFinal("unix-command.filename", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set unix-command.filename.\n");
+ return TM_ECODE_FAILED;
+ }
+
+ }
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#endif
+ }
+ else if(strcmp((long_opts[option_index]).name, "list-app-layer-protocols") == 0) {
+ /* listing all supported app layer protocols */
+ }
+ else if(strcmp((long_opts[option_index]).name, "list-unittests") == 0) {
+#ifdef UNITTESTS
+ suri->run_mode = RUNMODE_LIST_UNITTEST;
+#else
+ fprintf(stderr, "ERROR: Unit tests not enabled. Make sure to pass --enable-unittests to configure when building.\n");
+ return TM_ECODE_FAILED;
+#endif /* UNITTESTS */
+ } else if(strcmp((long_opts[option_index]).name, "list-cuda-cards") == 0) {
+#ifndef __SC_CUDA_SUPPORT__
+ fprintf(stderr, "ERROR: Cuda not enabled. Make sure to pass "
+ "--enable-cuda to configure when building.\n");
+ return TM_ECODE_FAILED;
+#endif /* UNITTESTS */
+ } else if (strcmp((long_opts[option_index]).name, "list-runmodes") == 0) {
+ suri->run_mode = RUNMODE_LIST_RUNMODES;
+ return TM_ECODE_OK;
+ } else if (strcmp((long_opts[option_index]).name, "list-keywords") == 0) {
+ if (optarg) {
+ if (strcmp("short",optarg)) {
+ suri->keyword_info = optarg;
+ }
+ }
+ } else if (strcmp((long_opts[option_index]).name, "runmode") == 0) {
+ suri->runmode_custom_mode = optarg;
+ } else if(strcmp((long_opts[option_index]).name, "engine-analysis") == 0) {
+ // do nothing for now
+ }
+#ifdef OS_WIN32
+ else if(strcmp((long_opts[option_index]).name, "service-install") == 0) {
+ suri->run_mode = RUNMODE_INSTALL_SERVICE;
+ return TM_ECODE_OK;
+ }
+ else if(strcmp((long_opts[option_index]).name, "service-remove") == 0) {
+ suri->run_mode = RUNMODE_REMOVE_SERVICE;
+ return TM_ECODE_OK;
+ }
+ else if(strcmp((long_opts[option_index]).name, "service-change-params") == 0) {
+ suri->run_mode = RUNMODE_CHANGE_SERVICE_PARAMS;
+ return TM_ECODE_OK;
+ }
+#endif /* OS_WIN32 */
+ else if(strcmp((long_opts[option_index]).name, "pidfile") == 0) {
+ suri->pid_filename = optarg;
+ }
+ else if(strcmp((long_opts[option_index]).name, "disable-detection") == 0) {
+ g_detect_disabled = suri->disabled_detect = 1;
+ SCLogInfo("detection engine disabled");
+ }
+ else if(strcmp((long_opts[option_index]).name, "fatal-unittests") == 0) {
+#ifdef UNITTESTS
+ if (ConfSetFinal("unittests.failure-fatal", "1") != 1) {
+ fprintf(stderr, "ERROR: Failed to set unittests failure-fatal.\n");
+ return TM_ECODE_FAILED;
+ }
+#else
+ fprintf(stderr, "ERROR: Unit tests not enabled. Make sure to pass --enable-unittests to configure when building.\n");
+ return TM_ECODE_FAILED;
+#endif /* UNITTESTS */
+ }
+ else if(strcmp((long_opts[option_index]).name, "user") == 0) {
+#ifndef HAVE_LIBCAP_NG
+ SCLogError(SC_ERR_LIBCAP_NG_REQUIRED, "libcap-ng is required to"
+ " drop privileges, but it was not compiled into Suricata.");
+ return TM_ECODE_FAILED;
+#else
+ suri->user_name = optarg;
+ suri->do_setuid = TRUE;
+#endif /* HAVE_LIBCAP_NG */
+ }
+ else if(strcmp((long_opts[option_index]).name, "group") == 0) {
+#ifndef HAVE_LIBCAP_NG
+ SCLogError(SC_ERR_LIBCAP_NG_REQUIRED, "libcap-ng is required to"
+ " drop privileges, but it was not compiled into Suricata.");
+ return TM_ECODE_FAILED;
+#else
+ suri->group_name = optarg;
+ suri->do_setgid = TRUE;
+#endif /* HAVE_LIBCAP_NG */
+ }
+ else if (strcmp((long_opts[option_index]).name, "erf-in") == 0) {
+ suri->run_mode = RUNMODE_ERF_FILE;
+ if (ConfSetFinal("erf-file.file", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set erf-file.file\n");
+ return TM_ECODE_FAILED;
+ }
+ }
+ else if (strcmp((long_opts[option_index]).name, "dag") == 0) {
+#ifdef HAVE_DAG
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_DAG;
+ }
+ else if (suri->run_mode != RUNMODE_DAG) {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE,
+ "more than one run mode has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ LiveRegisterDevice(optarg);
+#else
+ SCLogError(SC_ERR_DAG_REQUIRED, "libdag and a DAG card are required"
+ " to receieve packets using --dag.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_DAG */
+ }
+ else if (strcmp((long_opts[option_index]).name, "napatech") == 0) {
+#ifdef HAVE_NAPATECH
+ suri->run_mode = RUNMODE_NAPATECH;
+#else
+ SCLogError(SC_ERR_NAPATECH_REQUIRED, "libntapi and a Napatech adapter are required"
+ " to capture packets using --napatech.");
+ return TM_ECODE_FAILED;
+#endif /* HAVE_NAPATECH */
+ }
+ else if(strcmp((long_opts[option_index]).name, "pcap-buffer-size") == 0) {
+#ifdef HAVE_PCAP_SET_BUFF
+ if (ConfSetFinal("pcap.buffer-size", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set pcap-buffer-size.\n");
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NO_PCAP_SET_BUFFER_SIZE, "The version of libpcap you have"
+ " doesn't support setting buffer size.");
+#endif /* HAVE_PCAP_SET_BUFF */
+ }
+ else if(strcmp((long_opts[option_index]).name, "build-info") == 0) {
+ suri->run_mode = RUNMODE_PRINT_BUILDINFO;
+ return TM_ECODE_OK;
+ }
+#ifdef HAVE_MPIPE
+ else if(strcmp((long_opts[option_index]).name , "mpipe") == 0) {
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_TILERA_MPIPE;
+ if (optarg != NULL) {
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+ strlcpy(suri->pcap_dev, optarg,
+ ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
+ (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
+ LiveRegisterDevice(optarg);
+ }
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE,
+ "more than one run mode has been specified");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+ else if (strcmp((long_opts[option_index]).name, "set") == 0) {
+ if (optarg != NULL) {
+ /* Quick validation. */
+ char *val = strchr(optarg, '=');
+ if (val == NULL) {
+ SCLogError(SC_ERR_CMD_LINE,
+ "Invalid argument for --set, must be key=val.");
+ exit(EXIT_FAILURE);
+ }
+ if (!ConfSetFromString(optarg, 1)) {
+ fprintf(stderr, "Failed to set configuration value %s.",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case 'c':
+ conf_filename = optarg;
+ break;
+ case 'T':
+ SCLogInfo("Running suricata under test mode");
+ conf_test = 1;
+ if (ConfSetFinal("engine.init-failure-fatal", "1") != 1) {
+ fprintf(stderr, "ERROR: Failed to set engine init-failure-fatal.\n");
+ return TM_ECODE_FAILED;
+ }
+ break;
+#ifndef OS_WIN32
+ case 'D':
+ suri->daemon = 1;
+ break;
+#endif /* OS_WIN32 */
+ case 'h':
+ suri->run_mode = RUNMODE_PRINT_USAGE;
+ return TM_ECODE_OK;
+ case 'i':
+ memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
+
+ if (optarg == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "no option argument (optarg) for -i");
+ return TM_ECODE_FAILED;
+ }
+
+ /* some windows shells require escaping of the \ in \Device. Otherwise
+ * the backslashes are stripped. We put them back here. */
+ if (strlen(optarg) > 9 && strncmp(optarg, "DeviceNPF", 9) == 0) {
+ snprintf(suri->pcap_dev, sizeof(suri->pcap_dev), "\\Device\\NPF%s", optarg+9);
+ } else {
+ strlcpy(suri->pcap_dev, optarg, ((strlen(optarg) < sizeof(suri->pcap_dev)) ? (strlen(optarg)+1) : (sizeof(suri->pcap_dev))));
+ PcapTranslateIPToDevice(suri->pcap_dev, sizeof(suri->pcap_dev));
+ }
+
+ if (strcmp(suri->pcap_dev, optarg) != 0) {
+ SCLogInfo("translated %s to pcap device %s", optarg, suri->pcap_dev);
+ } else if (strlen(suri->pcap_dev) > 0 && isdigit((unsigned char)suri->pcap_dev[0])) {
+ SCLogError(SC_ERR_PCAP_TRANSLATE, "failed to find a pcap device for IP %s", optarg);
+ return TM_ECODE_FAILED;
+ }
+
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_PCAP_DEV;
+ LiveRegisterDevice(suri->pcap_dev);
+ } else if (suri->run_mode == RUNMODE_PCAP_DEV) {
+#ifdef OS_WIN32
+ SCLogError(SC_ERR_PCAP_MULTI_DEV_NO_SUPPORT, "pcap multi dev "
+ "support is not (yet) supported on Windows.");
+ return TM_ECODE_FAILED;
+#else
+ SCLogWarning(SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL, "using "
+ "multiple pcap devices to get packets is experimental.");
+ LiveRegisterDevice(suri->pcap_dev);
+#endif
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ break;
+ case 'l':
+ if (optarg == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "no option argument (optarg) for -l");
+ return TM_ECODE_FAILED;
+ }
+
+ if (ConfigSetLogDirectory(optarg) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_FATAL, "Failed to set log directory.\n");
+ return TM_ECODE_FAILED;
+ }
+ if (ConfigCheckLogDirectory(optarg) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_LOGDIR_CMDLINE, "The logging directory \"%s\""
+ " supplied at the commandline (-l %s) doesn't "
+ "exist. Shutting down the engine.", optarg, optarg);
+ return TM_ECODE_FAILED;
+ }
+ break;
+ case 'q':
+#ifdef NFQ
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_NFQ;
+ EngineModeSetIPS();
+ if (NFQRegisterQueue(optarg) == -1)
+ return TM_ECODE_FAILED;
+ } else if (suri->run_mode == RUNMODE_NFQ) {
+ if (NFQRegisterQueue(optarg) == -1)
+ return TM_ECODE_FAILED;
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_NFQ_NOSUPPORT,"NFQUEUE not enabled. Make sure to pass --enable-nfqueue to configure when building.");
+ return TM_ECODE_FAILED;
+#endif /* NFQ */
+ break;
+ case 'd':
+#ifdef IPFW
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_IPFW;
+ EngineModeSetIPS();
+ if (IPFWRegisterQueue(optarg) == -1)
+ return TM_ECODE_FAILED;
+ } else if (suri->run_mode == RUNMODE_IPFW) {
+ if (IPFWRegisterQueue(optarg) == -1)
+ return TM_ECODE_FAILED;
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#else
+ SCLogError(SC_ERR_IPFW_NOSUPPORT,"IPFW not enabled. Make sure to pass --enable-ipfw to configure when building.");
+ return TM_ECODE_FAILED;
+#endif /* IPFW */
+ break;
+ case 'r':
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_PCAP_FILE;
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+ "has been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ if (ConfSetFinal("pcap-file.file", optarg) != 1) {
+ fprintf(stderr, "ERROR: Failed to set pcap-file.file\n");
+ return TM_ECODE_FAILED;
+ }
+ break;
+ case 's':
+ if (suri->sig_file != NULL) {
+ SCLogError(SC_ERR_CMD_LINE, "can't have multiple -s options or mix -s and -S.");
+ return TM_ECODE_FAILED;
+ }
+ suri->sig_file = optarg;
+ break;
+ case 'S':
+ if (suri->sig_file != NULL) {
+ SCLogError(SC_ERR_CMD_LINE, "can't have multiple -S options or mix -s and -S.");
+ return TM_ECODE_FAILED;
+ }
+ suri->sig_file = optarg;
+ suri->sig_file_exclusive = TRUE;
+ break;
+ case 'u':
+#ifdef UNITTESTS
+ if (suri->run_mode == RUNMODE_UNKNOWN) {
+ suri->run_mode = RUNMODE_UNITTEST;
+ } else {
+ SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode has"
+ " been specified");
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+#else
+ fprintf(stderr, "ERROR: Unit tests not enabled. Make sure to pass --enable-unittests to configure when building.\n");
+ return TM_ECODE_FAILED;
+#endif /* UNITTESTS */
+ break;
+ case 'U':
+#ifdef UNITTESTS
+ suri->regex_arg = optarg;
+
+ if(strlen(suri->regex_arg) == 0)
+ suri->regex_arg = NULL;
+#endif
+ break;
+ case 'V':
+ suri->run_mode = RUNMODE_PRINT_VERSION;
+ return TM_ECODE_OK;
+ case 'F':
+ if (optarg == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "no option argument (optarg) for -F");
+ return TM_ECODE_FAILED;
+ }
+
+ SetBpfStringFromFile(optarg);
+ break;
+ case 'v':
+ suri->verbose++;
+ break;
+ case 'k':
+ if (optarg == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "no option argument (optarg) for -k");
+ return TM_ECODE_FAILED;
+ }
+ if (!strcmp("all", optarg))
+ suri->checksum_validation = 1;
+ else if (!strcmp("none", optarg))
+ suri->checksum_validation = 0;
+ else {
+ SCLogError(SC_ERR_INITIALIZATION, "option '%s' invalid for -k", optarg);
+ return TM_ECODE_FAILED;
+ }
+ break;
+ default:
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ if (suri->disabled_detect && suri->sig_file != NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "can't use -s/-S when detection is disabled");
+ return TM_ECODE_FAILED;
+ }
+
+ if (list_app_layer_protocols)
+ suri->run_mode = RUNMODE_LIST_APP_LAYERS;
+ if (list_cuda_cards)
+ suri->run_mode = RUNMODE_LIST_CUDA_CARDS;
+ if (list_keywords)
+ suri->run_mode = RUNMODE_LIST_KEYWORDS;
+ if (list_unittests)
+ suri->run_mode = RUNMODE_LIST_UNITTEST;
+ if (dump_config)
+ suri->run_mode = RUNMODE_DUMP_CONFIG;
+ if (conf_test)
+ suri->run_mode = RUNMODE_CONF_TEST;
+ if (engine_analysis)
+ suri->run_mode = RUNMODE_ENGINE_ANALYSIS;
+
+ ret = SetBpfString(optind, argv);
+ if (ret != TM_ECODE_OK)
+ return ret;
+
+ return TM_ECODE_OK;
+}
+
+#ifdef OS_WIN32
+static int WindowsInitService(int argc, char **argv)
+{
+ if (SCRunningAsService()) {
+ char path[MAX_PATH];
+ char *p = NULL;
+ strlcpy(path, argv[0], MAX_PATH);
+ if ((p = strrchr(path, '\\'))) {
+ *p = '\0';
+ }
+ if (!SetCurrentDirectory(path)) {
+ SCLogError(SC_ERR_FATAL, "Can't set current directory to: %s", path);
+ return -1;
+ }
+ SCLogInfo("Current directory is set to: %s", path);
+ daemon = 1;
+ SCServiceInit(argc, argv);
+ }
+
+ /* Windows socket subsystem initialization */
+ WSADATA wsaData;
+ if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) {
+ SCLogError(SC_ERR_FATAL, "Can't initialize Windows sockets: %d", WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* OS_WIN32 */
+
+static int MayDaemonize(SCInstance *suri)
+{
+ if (suri->daemon == 1 && suri->pid_filename == NULL) {
+ if (ConfGet("pid-file", &suri->pid_filename) == 1) {
+ SCLogInfo("Use pid file %s from config file.", suri->pid_filename);
+ } else {
+ suri->pid_filename = DEFAULT_PID_FILENAME;
+ }
+ }
+
+ if (suri->pid_filename != NULL && SCPidfileTestRunning(suri->pid_filename) != 0) {
+ suri->pid_filename = NULL;
+ return TM_ECODE_FAILED;
+ }
+
+ if (suri->daemon == 1) {
+ Daemonize();
+ }
+
+ if (suri->pid_filename != NULL) {
+ if (SCPidfileCreate(suri->pid_filename) != 0) {
+ suri->pid_filename = NULL;
+ SCLogError(SC_ERR_PIDFILE_DAEMON,
+ "Unable to create PID file, concurrent run of"
+ " Suricata can occur.");
+ SCLogError(SC_ERR_PIDFILE_DAEMON,
+ "PID file creation WILL be mandatory for daemon mode"
+ " in future version");
+ }
+ }
+
+ return TM_ECODE_OK;
+}
+
+static int InitSignalHandler(SCInstance *suri)
+{
+ /* registering signals we use */
+ UtilSignalHandlerSetup(SIGINT, SignalHandlerSigint);
+ UtilSignalHandlerSetup(SIGTERM, SignalHandlerSigterm);
+ UtilSignalHandlerSetup(SIGPIPE, SIG_IGN);
+ UtilSignalHandlerSetup(SIGSYS, SIG_IGN);
+
+#ifndef OS_WIN32
+ /* SIGHUP is not implemented on WIN32 */
+ UtilSignalHandlerSetup(SIGHUP, SignalHandlerSigHup);
+
+ /* Try to get user/group to run suricata as if
+ command line as not decide of that */
+ if (suri->do_setuid == FALSE && suri->do_setgid == FALSE) {
+ char *id;
+ if (ConfGet("run-as.user", &id) == 1) {
+ suri->do_setuid = TRUE;
+ suri->user_name = id;
+ }
+ if (ConfGet("run-as.group", &id) == 1) {
+ suri->do_setgid = TRUE;
+ suri->group_name = id;
+ }
+ }
+ /* Get the suricata user ID to given user ID */
+ if (suri->do_setuid == TRUE) {
+ if (SCGetUserID(suri->user_name, suri->group_name,
+ &suri->userid, &suri->groupid) != 0) {
+ SCLogError(SC_ERR_UID_FAILED, "failed in getting user ID");
+ return TM_ECODE_FAILED;
+ }
+
+ sc_set_caps = TRUE;
+ /* Get the suricata group ID to given group ID */
+ } else if (suri->do_setgid == TRUE) {
+ if (SCGetGroupID(suri->group_name, &suri->groupid) != 0) {
+ SCLogError(SC_ERR_GID_FAILED, "failed in getting group ID");
+ return TM_ECODE_FAILED;
+ }
+
+ sc_set_caps = TRUE;
+ }
+#endif /* OS_WIN32 */
+
+ return TM_ECODE_OK;
+}
+
+int StartInternalRunMode(SCInstance *suri, int argc, char **argv)
+{
+ /* Treat internal running mode */
+ switch(suri->run_mode) {
+ case RUNMODE_LIST_KEYWORDS:
+ ListKeywords(suri->keyword_info);
+ return TM_ECODE_DONE;
+ case RUNMODE_LIST_APP_LAYERS:
+ ListAppLayerProtocols();
+ return TM_ECODE_DONE;
+ case RUNMODE_PRINT_VERSION:
+ PrintVersion();
+ return TM_ECODE_DONE;
+ case RUNMODE_PRINT_BUILDINFO:
+ SCPrintBuildInfo();
+ return TM_ECODE_DONE;
+ case RUNMODE_PRINT_USAGE:
+ usage(argv[0]);
+ return TM_ECODE_DONE;
+#ifdef __SC_CUDA_SUPPORT__
+ case RUNMODE_LIST_CUDA_CARDS:
+ return ListCudaCards();
+#endif
+ case RUNMODE_LIST_RUNMODES:
+ RunModeListRunmodes();
+ return TM_ECODE_DONE;
+ case RUNMODE_LIST_UNITTEST:
+ RunUnittests(1, suri->regex_arg);
+#ifdef OS_WIN32
+ case RUNMODE_INSTALL_SERVICE:
+ if (SCServiceInstall(argc, argv)) {
+ return TM_ECODE_FAILED;
+ }
+ SCLogInfo("Suricata service has been successfuly installed.");
+ return TM_ECODE_DONE;
+ case RUNMODE_REMOVE_SERVICE:
+ if (SCServiceRemove(argc, argv)) {
+ return TM_ECODE_FAILED;
+ }
+ SCLogInfo("Suricata service has been successfuly removed.");
+ return TM_ECODE_DONE;
+ case RUNMODE_CHANGE_SERVICE_PARAMS:
+ if (SCServiceChangeParams(argc, argv)) {
+ return TM_ECODE_FAILED;
+ }
+ SCLogInfo("Suricata service startup parameters has been successfuly changed.");
+ return TM_ECODE_DONE;
+#endif /* OS_WIN32 */
+ default:
+ /* simply continue for other running mode */
+ break;
+ }
+ return TM_ECODE_OK;
+}
+
+static int FinalizeRunMode(SCInstance *suri, char **argv)
+{
+ switch (suri->run_mode) {
+ case RUNMODE_PCAP_FILE:
+ case RUNMODE_ERF_FILE:
+ case RUNMODE_ENGINE_ANALYSIS:
+ suri->offline = 1;
+ break;
+ case RUNMODE_UNKNOWN:
+ usage(argv[0]);
+ return TM_ECODE_FAILED;
+ }
+ /* Set the global run mode */
+ run_mode = suri->run_mode;
+
+
+ return TM_ECODE_OK;
+}
+
+static void SetupDelayedDetect(SCInstance *suri)
+{
+ /* In offline mode delayed init of detect is a bad idea */
+ if (suri->offline) {
+ suri->delayed_detect = 0;
+ } else {
+ ConfNode *denode = NULL;
+ ConfNode *decnf = ConfGetNode("detect-engine");
+ if (decnf != NULL) {
+ TAILQ_FOREACH(denode, &decnf->head, next) {
+ if (strcmp(denode->val, "delayed-detect") == 0) {
+ (void)ConfGetChildValueBool(denode, "delayed-detect", &suri->delayed_detect);
+ }
+ }
+ }
+ }
+
+ SCLogInfo("Delayed detect %s", suri->delayed_detect ? "enabled" : "disabled");
+ if (suri->delayed_detect) {
+ SCLogInfo("Packets will start being processed before signatures are active.");
+ }
+
+}
+
+static int LoadSignatures(DetectEngineCtx *de_ctx, SCInstance *suri)
+{
+ if (SigLoadSignatures(de_ctx, suri->sig_file, suri->sig_file_exclusive) < 0) {
+ SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
+ if (de_ctx->failure_fatal)
+ return TM_ECODE_FAILED;
+ }
+
+ SCThresholdConfInitContext(de_ctx, NULL);
+ return TM_ECODE_OK;
+}
+
+static int ConfigGetCaptureValue(SCInstance *suri)
+{
+ /* Pull the max pending packets from the config, if not found fall
+ * back on a sane default. */
+ if (ConfGetInt("max-pending-packets", &max_pending_packets) != 1)
+ max_pending_packets = DEFAULT_MAX_PENDING_PACKETS;
+ if (max_pending_packets >= 65535) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "Maximum max-pending-packets setting is 65534. "
+ "Please check %s for errors", conf_filename);
+ return TM_ECODE_FAILED;
+ }
+
+ SCLogDebug("Max pending packets set to %"PRIiMAX, max_pending_packets);
+
+ /* Pull the default packet size from the config, if not found fall
+ * back on a sane default. */
+ char *temp_default_packet_size;
+ if ((ConfGet("default-packet-size", &temp_default_packet_size)) != 1) {
+ switch (suri->run_mode) {
+ case RUNMODE_PCAP_DEV:
+ case RUNMODE_AFP_DEV:
+ case RUNMODE_NETMAP:
+ case RUNMODE_PFRING:
+ /* FIXME this don't work effficiently in multiinterface */
+ /* find payload for interface and use it */
+ default_packet_size = GetIfaceMaxPacketSize(suri->pcap_dev);
+ if (default_packet_size)
+ break;
+ /* fall through */
+ default:
+ default_packet_size = DEFAULT_PACKET_SIZE;
+ }
+ } else {
+ if (ParseSizeStringU32(temp_default_packet_size, &default_packet_size) < 0) {
+ SCLogError(SC_ERR_SIZE_PARSE, "Error parsing max-pending-packets "
+ "from conf file - %s. Killing engine",
+ temp_default_packet_size);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ SCLogDebug("Default packet size set to %"PRIu32, default_packet_size);
+
+ return TM_ECODE_OK;
+}
+/**
+ * This function is meant to contain code that needs
+ * to be run once the configuration has been loaded.
+ */
+static int PostConfLoadedSetup(SCInstance *suri)
+{
+ char *hostmode = NULL;
+
+ /* load the pattern matchers */
+ MpmTableSetup();
+#ifdef __SC_CUDA_SUPPORT__
+ MpmCudaEnvironmentSetup();
+#endif
+
+ switch (suri->checksum_validation) {
+ case 0:
+ ConfSet("stream.checksum-validation", "0");
+ break;
+ case 1:
+ ConfSet("stream.checksum-validation", "1");
+ break;
+ }
+
+ AppLayerSetup();
+
+ /* Check for the existance of the default logging directory which we pick
+ * from suricata.yaml. If not found, shut the engine down */
+ suri->log_dir = ConfigGetLogDirectory();
+
+ if (ConfigCheckLogDirectory(suri->log_dir) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_LOGDIR_CONFIG, "The logging directory \"%s\" "
+ "supplied by %s (default-log-dir) doesn't exist. "
+ "Shutting down the engine", suri->log_dir, conf_filename);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (ConfigGetCaptureValue(suri) != TM_ECODE_OK) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (ConfGet("host-mode", &hostmode) == 1) {
+ if (!strcmp(hostmode, "router")) {
+ host_mode = SURI_HOST_IS_ROUTER;
+ } else if (!strcmp(hostmode, "sniffer-only")) {
+ host_mode = SURI_HOST_IS_SNIFFER_ONLY;
+ } else {
+ if (strcmp(hostmode, "auto") != 0) {
+ WarnInvalidConfEntry("host-mode", "%s", "auto");
+ }
+ if (EngineModeIsIPS()) {
+ host_mode = SURI_HOST_IS_ROUTER;
+ } else {
+ host_mode = SURI_HOST_IS_SNIFFER_ONLY;
+ }
+ }
+ } else {
+ if (EngineModeIsIPS()) {
+ host_mode = SURI_HOST_IS_ROUTER;
+ SCLogInfo("No 'host-mode': suricata is in IPS mode, using "
+ "default setting 'router'");
+ } else {
+ host_mode = SURI_HOST_IS_SNIFFER_ONLY;
+ SCLogInfo("No 'host-mode': suricata is in IDS mode, using "
+ "default setting 'sniffer-only'");
+ }
+ }
+
+#ifdef NFQ
+ if (suri->run_mode == RUNMODE_NFQ)
+ NFQInitConfig(FALSE);
+#endif
+
+ /* Load the Host-OS lookup. */
+ SCHInfoLoadFromConfig();
+ if (suri->run_mode != RUNMODE_UNIX_SOCKET) {
+ DefragInit();
+ }
+
+ if (suri->run_mode == RUNMODE_ENGINE_ANALYSIS) {
+ SCLogInfo("== Carrying out Engine Analysis ==");
+ char *temp = NULL;
+ if (ConfGet("engine-analysis", &temp) == 0) {
+ SCLogInfo("no engine-analysis parameter(s) defined in conf file. "
+ "Please define/enable them in the conf to use this "
+ "feature.");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ /* hardcoded initialization code */
+ SigTableSetup(); /* load the rule keywords */
+ TmqhSetup();
+
+ StorageInit();
+ CIDRInit();
+ SigParsePrepare();
+#ifdef PROFILING
+ if (suri->run_mode != RUNMODE_UNIX_SOCKET) {
+ SCProfilingRulesGlobalInit();
+ SCProfilingKeywordsGlobalInit();
+ SCProfilingInit();
+ }
+#endif /* PROFILING */
+ SCReputationInitCtx();
+ SCProtoNameInit();
+
+ TagInitCtx();
+ ThresholdInit();
+ HostBitInitCtx();
+ IPPairBitInitCtx();
+
+ if (DetectAddressTestConfVars() < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "basic address vars test failed. Please check %s for errors", conf_filename);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (DetectPortTestConfVars() < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "basic port vars test failed. Please check %s for errors", conf_filename);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ RegisterAllModules();
+
+ AppLayerHtpNeedFileInspection();
+
+ DetectEngineRegisterAppInspectionEngines();
+
+ if (suri->sig_file != NULL)
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2SigFileStartup);
+ else
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2StartingUp);
+
+ StorageFinalize();
+
+ TmModuleRunInit();
+
+ PcapLogProfileSetup();
+ SCReturnInt(TM_ECODE_OK);
+}
+
+int main(int argc, char **argv)
+{
+ SCInstance suri;
+
+ SCInstanceInit(&suri);
+
+ sc_set_caps = FALSE;
+
+ SC_ATOMIC_INIT(engine_stage);
+
+ /* initialize the logging subsys */
+ SCLogInitLogModule(NULL);
+
+ if (SCSetThreadName("Suricata-Main") < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ ParseSizeInit();
+
+ RunModeRegisterRunModes();
+
+ /* By default use IDS mode, but if nfq or ipfw
+ * are specified, IPS mode will overwrite this */
+ EngineModeSetIDS();
+
+
+#ifdef OS_WIN32
+ /* service initialization */
+ if (WindowsInit(argc, argv) != 0) {
+ exit(EXIT_FAILURE);
+ }
+#endif /* OS_WIN32 */
+
+ /* Initialize the configuration module. */
+ ConfInit();
+
+ if (ParseCommandLine(argc, argv, &suri) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ switch (StartInternalRunMode(&suri, argc, argv)) {
+ case TM_ECODE_DONE:
+ exit(EXIT_SUCCESS);
+ case TM_ECODE_FAILED:
+ exit(EXIT_FAILURE);
+ }
+
+ if (FinalizeRunMode(&suri, argv) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (suri.run_mode == RUNMODE_UNITTEST)
+ RunUnittests(0, suri.regex_arg);
+
+#ifdef __SC_CUDA_SUPPORT__
+ /* Init the CUDA environment */
+ SCCudaInitCudaEnvironment();
+ CudaBufferInit();
+#endif
+
+ if (!CheckValidDaemonModes(suri.daemon, suri.run_mode)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initializations for global vars, queues, etc (memsets, mutex init..) */
+ GlobalInits();
+ TimeInit();
+ SupportFastPatternForSigMatchTypes();
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ StatsInit();
+ }
+
+ if (conf_filename == NULL)
+ conf_filename = DEFAULT_CONF_FILE;
+
+ /** \todo we need an api for these */
+ /* Load yaml configuration file if provided. */
+ if (LoadYamlConfig(conf_filename) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Since our config is now loaded we can finish configurating the
+ * logging module. */
+ SCLogLoadConfig(suri.daemon, suri.verbose);
+
+ SCPrintVersion();
+
+ UtilCpuPrintSummary();
+
+ if (suri.run_mode == RUNMODE_DUMP_CONFIG) {
+ ConfDump();
+ exit(EXIT_SUCCESS);
+ }
+
+ if (PostConfLoadedSetup(&suri) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (MayDaemonize(&suri) != TM_ECODE_OK)
+ exit(EXIT_FAILURE);
+
+ if (InitSignalHandler(&suri) != TM_ECODE_OK)
+ exit(EXIT_FAILURE);
+
+#ifdef HAVE_NSS
+ /* init NSS for md5 */
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ NSS_NoDB_Init(NULL);
+#endif
+
+ if (suri.disabled_detect) {
+ /* disable raw reassembly */
+ (void)ConfSetFinal("stream.reassembly.raw", "false");
+ }
+
+ HostInitConfig(HOST_VERBOSE);
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ FlowInitConfig(FLOW_VERBOSE);
+ StreamTcpInitConfig(STREAM_VERBOSE);
+ IPPairInitConfig(IPPAIR_VERBOSE);
+ AppLayerRegisterGlobalCounters();
+ }
+
+ if (MagicInit() != 0)
+ exit(EXIT_FAILURE);
+
+ DetectEngineCtx *de_ctx = NULL;
+ if (!suri.disabled_detect) {
+ SCClassConfInit();
+ SCReferenceConfInit();
+ DetectEngineMultiTenantSetup();
+ SetupDelayedDetect(&suri);
+ if (!suri.delayed_detect) {
+ de_ctx = DetectEngineCtxInit();
+ } else {
+ de_ctx = DetectEngineCtxInitMinimal();
+ }
+ if (de_ctx == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
+ "context failed.");
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA)
+ CudaVarsSetDeCtx(de_ctx);
+#endif /* __SC_CUDA_SUPPORT__ */
+
+ if (!suri.delayed_detect) {
+ if (LoadSignatures(de_ctx, &suri) != TM_ECODE_OK)
+ exit(EXIT_FAILURE);
+ if (suri.run_mode == RUNMODE_ENGINE_ANALYSIS) {
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ DetectEngineAddToMaster(de_ctx);
+ } else {
+ /* tell the app layer to consider only the log id */
+ RegisterAppLayerGetActiveTxIdFunc(AppLayerTransactionGetActiveLogOnly);
+ }
+
+ SCAsn1LoadConfig();
+
+ CoredumpLoadConfig();
+
+ SCSetStartTime(&suri);
+
+ SCDropMainThreadCaps(suri.userid, suri.groupid);
+
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ RunModeInitializeOutputs();
+ StatsSetupPostConfig();
+ }
+
+ if (ParseInterfacesList(suri.run_mode, suri.pcap_dev) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ if(suri.run_mode == RUNMODE_CONF_TEST){
+ SCLogNotice("Configuration provided was successfully loaded. Exiting.");
+ exit(EXIT_SUCCESS);
+ }
+
+ RunModeDispatch(suri.run_mode, suri.runmode_custom_mode);
+
+ /* In Unix socket runmode, Flow manager is started on demand */
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ /* Spawn the unix socket manager thread */
+ int unix_socket = 0;
+ if (ConfGetBool("unix-command.enabled", &unix_socket) != 1)
+ unix_socket = 0;
+ if (unix_socket == 1) {
+ UnixManagerThreadSpawn(0);
+#ifdef BUILD_UNIX_SOCKET
+ UnixManagerRegisterCommand("iface-stat", LiveDeviceIfaceStat, NULL,
+ UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("iface-list", LiveDeviceIfaceList, NULL, 0);
+#endif
+ }
+ /* Spawn the flow manager thread */
+ FlowManagerThreadSpawn();
+ FlowRecyclerThreadSpawn();
+ StatsSpawnThreads();
+ }
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA)
+ SCACCudaStartDispatcher();
+#endif
+
+ /* Check if the alloted queues have at least 1 reader and writer */
+ TmValidateQueueState();
+
+ /* Wait till all the threads have been initialized */
+ if (TmThreadWaitOnThreadInit() == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_INITIALIZATION, "Engine initialization failed, "
+ "aborting...");
+ exit(EXIT_FAILURE);
+ }
+
+ (void) SC_ATOMIC_CAS(&engine_stage, SURICATA_INIT, SURICATA_RUNTIME);
+
+ /* Un-pause all the paused threads */
+ TmThreadContinueThreads();
+ /* registering singal handlers we use. We register usr2 here, so that one
+ * can't call it during the first sig load phase or while threads are still
+ * starting up. */
+ if (DetectEngineEnabled() && suri.sig_file == NULL &&
+ suri.delayed_detect == 0)
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
+
+ if (suri.delayed_detect) {
+ /* force 'reload', this will load the rules and swap engines */
+ DetectEngineReload(NULL);
+
+ if (suri.sig_file != NULL)
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2SigFileStartup);
+ else
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
+ SCLogNotice("Signature(s) loaded, Detect thread(s) activated.");
+ }
+
+
+#ifdef DBG_MEM_ALLOC
+ SCLogInfo("Memory used at startup: %"PRIdMAX, (intmax_t)global_mem);
+#ifdef DBG_MEM_ALLOC_SKIP_STARTUP
+ print_mem_flag = 1;
+#endif
+#endif
+
+ int engine_retval = EXIT_SUCCESS;
+ while(1) {
+ if (suricata_ctl_flags & (SURICATA_KILL | SURICATA_STOP)) {
+ SCLogNotice("Signal Received. Stopping engine.");
+
+ break;
+ }
+
+ TmThreadCheckThreadState();
+
+ if (sighup_count > 0) {
+ OutputNotifyFileRotation();
+ sighup_count--;
+ }
+
+ if (sigusr2_count > 0) {
+ DetectEngineReload(conf_filename);
+ sigusr2_count--;
+ } else if (DetectEngineReloadIsStart()) {
+ DetectEngineReload(conf_filename);
+ DetectEngineReloadSetDone();
+ }
+
+ usleep(10* 1000);
+ }
+
+ /* Update the engine stage/status flag */
+ (void) SC_ATOMIC_CAS(&engine_stage, SURICATA_RUNTIME, SURICATA_DEINIT);
+
+ UnixSocketKillSocketThread();
+
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ /* First we need to disable the flow manager thread */
+ FlowDisableFlowManagerThread();
+ }
+
+
+ /* Disable packet acquisition first */
+ TmThreadDisableReceiveThreads();
+
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ /* we need a packet pool for FlowForceReassembly */
+ PacketPoolInit();
+
+ FlowForceReassembly();
+ /* kill receive threads when they have processed all
+ * flow timeout packets */
+ TmThreadDisablePacketThreads();
+ }
+
+ SCPrintElapsedTime(&suri);
+
+ /* before TmThreadKillThreads, as otherwise that kills it
+ * but more slowly */
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ FlowDisableFlowRecyclerThread();
+ }
+
+ /* kill remaining threads */
+ TmThreadKillThreads();
+
+
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ /* destroy the packet pool for flow reassembly after all
+ * the other threads are gone. */
+ PacketPoolDestroy();
+
+ StatsReleaseResources();
+ IPPairShutdown();
+ FlowShutdown();
+ StreamTcpFreeConfig(STREAM_VERBOSE);
+ }
+ HostShutdown();
+
+ HTPFreeConfig();
+ HTPAtExitPrintStats();
+
+#ifdef DBG_MEM_ALLOC
+ SCLogInfo("Total memory used (without SCFree()): %"PRIdMAX, (intmax_t)global_mem);
+#ifdef DBG_MEM_ALLOC_SKIP_STARTUP
+ print_mem_flag = 0;
+#endif
+#endif
+
+ SCPidfileRemove(suri.pid_filename);
+
+ AppLayerHtpPrintStats();
+
+ /** TODO this can do into it's own func */
+ de_ctx = DetectEngineGetCurrent();
+ if (de_ctx) {
+ DetectEngineMoveToFreeList(de_ctx);
+ DetectEngineDeReference(&de_ctx);
+ }
+ DetectEnginePruneFreeList();
+
+ AppLayerDeSetup();
+
+ TagDestroyCtx();
+
+ LiveDeviceListClean();
+ RunModeShutDown();
+ OutputDeregisterAll();
+ TimeDeinit();
+ SCProtoNameDeInit();
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ DefragDestroy();
+ }
+ if (!suri.disabled_detect) {
+ SCReferenceConfDeinit();
+ SCClassConfDeinit();
+ }
+ MagicDeinit();
+ TmqhCleanup();
+ TmModuleRunDeInit();
+ ParseSizeDeinit();
+#ifdef HAVE_NSS
+ NSS_Shutdown();
+ PR_Cleanup();
+#endif
+
+#ifdef HAVE_AF_PACKET
+ AFPPeersListClean();
+#endif
+
+#ifdef PROFILING
+ if (suri.run_mode != RUNMODE_UNIX_SOCKET) {
+ if (profiling_rules_enabled)
+ SCProfilingDump();
+ SCProfilingDestroy();
+ }
+#endif
+
+#ifdef OS_WIN32
+ if (daemon) {
+ return 0;
+ }
+#endif /* OS_WIN32 */
+
+ SC_ATOMIC_DESTROY(engine_stage);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (PatternMatchDefaultMatcher() == MPM_AC_CUDA)
+ MpmCudaBufferDeSetup();
+ CudaHandlerFreeProfiles();
+#endif
+ ConfDeInit();
+
+ exit(engine_retval);
+}
diff --git a/framework/src/suricata/src/suricata.h b/framework/src/suricata/src/suricata.h
new file mode 100644
index 00000000..12e72697
--- /dev/null
+++ b/framework/src/suricata/src/suricata.h
@@ -0,0 +1,197 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \mainpage Doxygen documentation
+ *
+ * \section intro_sec Introduction
+ *
+ * The Suricata Engine is an Open Source Next Generation Intrusion Detection
+ * and Prevention Engine. This engine is not intended to just replace or
+ * emulate the existing tools in the industry, but will bring new ideas and
+ * technologies to the field.
+ *
+ * \section dev_doc Developer documentation
+ *
+ * You've reach the automically generated documentation of Suricata. This
+ * document contains information about architecture and code structure. It
+ * is attended for developers wanting to understand or contribute to Suricata.
+ *
+ * \subsection modules Modules
+ *
+ * Documentation is generate from comments placed in all parts of the code.
+ * But you will also find some groups describing specific functional parts:
+ * - \ref decode
+ * - \ref httplayer
+ * - \ref sigstate
+ * - \ref threshold
+ *
+ * \section archi Architecture
+ *
+ * \subsection datastruct Data structures
+ *
+ * Regarding matching, there is three main data structures which are:
+ * - ::Packet: Data relative to an individual packet with information about
+ * linked structure such as the ::Flow the ::Packet belongs to.
+ * - ::Flow: Information about a flow for example a TCP session
+ * - ::StreamMsg: structure containing the reassembled data
+ *
+ * \subsection runmode Running mode
+ *
+ * Suricata is multithreaded and running modes define how the different
+ * threads are working together. You can see util-runmodes.c for example
+ * of running mode.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __SURICATA_H__
+#define __SURICATA_H__
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "data-queue.h"
+
+/* the name of our binary */
+#define PROG_NAME "Suricata"
+#define PROG_VER "2.1dev"
+
+/* workaround SPlint error (don't know __gnuc_va_list) */
+#ifdef S_SPLINT_S
+# include <err.h>
+# define CONFIG_DIR "/etc/suricata"
+#endif
+
+#define DEFAULT_CONF_FILE CONFIG_DIR "/suricata.yaml"
+
+#define DEFAULT_PID_DIR LOCAL_STATE_DIR "/run/"
+#define DEFAULT_PID_BASENAME "suricata.pid"
+#define DEFAULT_PID_FILENAME DEFAULT_PID_DIR DEFAULT_PID_BASENAME
+
+/* runtime engine control flags */
+#define SURICATA_STOP (1 << 0) /**< gracefully stop the engine: process all
+ outstanding packets first */
+#define SURICATA_KILL (1 << 1) /**< shut down asap, discarding outstanding
+ packets. */
+#define SURICATA_DONE (1 << 2) /**< packets capture ended */
+
+/* Engine stage/status*/
+enum {
+ SURICATA_INIT = 0,
+ SURICATA_RUNTIME,
+ SURICATA_DEINIT
+};
+
+/* Engine is acting as */
+enum EngineMode {
+ ENGINE_MODE_IDS,
+ ENGINE_MODE_IPS,
+};
+
+void EngineModeSetIPS(void);
+void EngineModeSetIDS(void);
+int EngineModeIsIPS(void);
+int EngineModeIsIDS(void);
+
+/* Box is acting as router */
+enum {
+ SURI_HOST_IS_SNIFFER_ONLY,
+ SURI_HOST_IS_ROUTER,
+};
+
+#define IS_SURI_HOST_MODE_SNIFFER_ONLY(host_mode) ((host_mode) == SURI_HOST_IS_SNIFFER_ONLY)
+#define IS_SURI_HOST_MODE_ROUTER(host_mode) ((host_mode) == SURI_HOST_IS_ROUTER)
+
+/* queue's between various other threads
+ * XXX move to the TmQueue structure later
+ */
+PacketQueue trans_q[256];
+
+SCDQDataQueue data_queues[256];
+
+typedef struct SCInstance_ {
+ int run_mode;
+
+ char pcap_dev[128];
+ char *sig_file;
+ int sig_file_exclusive;
+ char *pid_filename;
+ char *regex_arg;
+
+ char *keyword_info;
+ char *runmode_custom_mode;
+#ifndef OS_WIN32
+ char *user_name;
+ char *group_name;
+ uint8_t do_setuid;
+ uint8_t do_setgid;
+ uint32_t userid;
+ uint32_t groupid;
+#endif /* OS_WIN32 */
+ int delayed_detect;
+ int disabled_detect;
+ int daemon;
+ int offline;
+ int verbose;
+ int checksum_validation;
+
+ struct timeval start_time;
+
+ char *log_dir;
+} SCInstance;
+
+
+/* memset to zeros, and mutex init! */
+void GlobalInits();
+
+extern volatile uint8_t suricata_ctl_flags;
+
+/* uppercase to lowercase conversion lookup table */
+uint8_t g_u8_lowercasetable[256];
+
+extern char *conf_filename;
+
+/* marco to do the actual lookup */
+//#define u8_tolower(c) g_u8_lowercasetable[(c)]
+// these 2 are slower:
+//#define u8_tolower(c) ((c) >= 'A' && (c) <= 'Z') ? g_u8_lowercasetable[(c)] : (c)
+//#define u8_tolower(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c))
+
+/* this is faster than the table lookup */
+#include <ctype.h>
+#define u8_tolower(c) tolower((uint8_t)(c))
+
+void EngineStop(void);
+void EngineKill(void);
+void EngineDone(void);
+
+/* live rule swap required this to be made static */
+void SignalHandlerSigusr2(int);
+void SignalHandlerSigusr2EngineShutdown(int);
+void SignalHandlerSigusr2Idle(int sig);
+
+int RunmodeIsUnittests(void);
+int RunmodeGetCurrent(void);
+int IsRuleReloadSet(int quiet);
+
+extern int run_mode;
+
+#endif /* __SURICATA_H__ */
+
diff --git a/framework/src/suricata/src/threads-arch-tile.h b/framework/src/suricata/src/threads-arch-tile.h
new file mode 100644
index 00000000..d022d90e
--- /dev/null
+++ b/framework/src/suricata/src/threads-arch-tile.h
@@ -0,0 +1,114 @@
+/* Copyright (C) 2011-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ */
+
+#ifndef __THREADS_ARCH_TILE_H__
+#define __THREADS_ARCH_TILE_H__
+
+#include <tmc/spin.h>
+#include <arch/cycle.h>
+
+/* NOTE: On Tilera datapath threads use the TMC (Tilera Multicore
+ * Components) library spin mutexes while the control threads use
+ * pthread mutexes. So the pthread mutex types are split out so that
+ * their use can be differentiated.
+ */
+
+/* ctrl mutex */
+#define SCCtrlMutex pthread_mutex_t
+#define SCCtrlMutexAttr pthread_mutexattr_t
+#define SCCtrlMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCCtrlMutexLock(mut) pthread_mutex_lock(mut)
+#define SCCtrlMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCCtrlMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCCtrlMutexDestroy pthread_mutex_destroy
+
+/* ctrl cond */
+#define SCCtrlCondT pthread_cond_t
+#define SCCtrlCondInit pthread_cond_init
+#define SCCtrlCondSignal pthread_cond_signal
+#define SCCtrlCondTimedwait pthread_cond_timedwait
+#define SCCtrlCondWait pthread_cond_wait
+#define SCCtrlCondDestroy pthread_cond_destroy
+
+/* mutex */
+
+#define SCMutex tmc_spin_queued_mutex_t
+#define SCMutexAttr
+#define SCMutexDestroy(x) ({ (void)(x); 0; })
+#define SCMUTEX_INITIALIZER TMC_SPIN_QUEUED_MUTEX_INIT
+#define SCMutexInit(mut, mutattr) ({ \
+ int ret = 0; \
+ tmc_spin_queued_mutex_init(mut); \
+ ret; \
+})
+#define SCMutexLock(mut) ({ \
+ int ret = 0; \
+ tmc_spin_queued_mutex_lock(mut); \
+ ret; \
+})
+#define SCMutexTrylock(mut) ({ \
+ int ret = (tmc_spin_queued_mutex_trylock(mut) == 0) ? 0 : EBUSY; \
+ ret; \
+})
+#define SCMutexUnlock(mut) ({ \
+ int ret = 0; \
+ tmc_spin_queued_mutex_unlock(mut); \
+ ret; \
+})
+
+/* conditions */
+
+/* Ignore signals when using spin locks */
+#define SCCondT uint8_t
+#define SCCondInit(x,y) ({ 0; })
+#define SCCondSignal(x) ({ 0; })
+#define SCCondDestroy(x) ({ 0; })
+
+static inline void cycle_sleep(int cycles)
+{
+ uint64_t end = get_cycle_count() + cycles;
+ while (get_cycle_count() < end)
+ ;
+}
+#define SCCondWait(x,y) cycle_sleep(300)
+
+/* spinlocks */
+
+#define SCSpinlock tmc_spin_queued_mutex_t
+#define SCSpinLock(spin) ({ tmc_spin_queued_mutex_lock(spin); 0; })
+#define SCSpinTrylock(spin) (tmc_spin_queued_mutex_trylock(spin) ? EBUSY : 0)
+#define SCSpinUnlock(spin) ({ tmc_spin_queued_mutex_unlock(spin); 0; })
+#define SCSpinInit(spin, spin_attr) ({ tmc_spin_queued_mutex_init(spin); 0; })
+#define SCSpinDestroy(spin) ({ (void)(spin); 0; })
+
+/* rwlocks */
+
+#define SCRWLock tmc_spin_rwlock_t
+#define SCRWLockDestroy(x) ({ (void)(x); 0; })
+#define SCRWLockInit(rwl, rwlattr ) ({ tmc_spin_rwlock_init(rwl); 0; })
+#define SCRWLockWRLock(rwl) ({ tmc_spin_rwlock_wrlock(rwl); 0; })
+#define SCRWLockRDLock(rwl) ({ tmc_spin_rwlock_rdlock(rwl); 0; })
+#define SCRWLockTryWRLock(rwl) (tmc_spin_rwlock_trywrlock(rwl) ? EBUSY : 0)
+#define SCRWLockTryRDLock(rwl) (tmc_spin_rwlock_tryrdlock(rwl) ? EBUSY : 0)
+#define SCRWLockUnlock(rwl) ({ tmc_spin_rwlock_unlock(rwl); 0; })
+#endif
diff --git a/framework/src/suricata/src/threads-debug.h b/framework/src/suricata/src/threads-debug.h
new file mode 100644
index 00000000..050e3056
--- /dev/null
+++ b/framework/src/suricata/src/threads-debug.h
@@ -0,0 +1,389 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Threading functions defined as macros: debug variants
+ */
+
+#ifndef __THREADS_DEBUG_H__
+#define __THREADS_DEBUG_H__
+
+/* mutex */
+
+/** When dbg threads is defined, if a mutex fail to lock, it's
+ * initialized, logged, and does a second try; This is to prevent the system to freeze;
+ * It is for Mac OS X users;
+ * If you see a mutex, spinlock or condiion not initialized, report it please!
+ */
+#define SCMutexLock_dbg(mut) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locking mutex %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut); \
+ int retl = pthread_mutex_lock(mut); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locked mutex %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, retl); \
+ if (retl != 0) { \
+ switch (retl) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ retl = pthread_mutex_init(mut, NULL); \
+ if (retl != 0) \
+ exit(EXIT_FAILURE); \
+ retl = pthread_mutex_lock(mut); \
+ break; \
+ case EDEADLK: \
+ printf("A deadlock would occur if the thread blocked waiting for mutex\n"); \
+ break; \
+ } \
+ } \
+ retl; \
+})
+
+#define SCMutexTrylock_dbg(mut) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking mutex %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut); \
+ int rett = pthread_mutex_trylock(mut); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked mutex %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, rett); \
+ if (rett != 0) { \
+ switch (rett) { \
+ case EINVAL: \
+ printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \
+ break; \
+ case EBUSY: \
+ printf("Mutex is already locked\n"); \
+ break; \
+ } \
+ } \
+ rett; \
+})
+
+#define SCMutexInit_dbg(mut, mutattr) ({ \
+ int ret; \
+ ret = pthread_mutex_init(mut, mutattr); \
+ if (ret != 0) { \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") mutex %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, ret); \
+ break; \
+ case EAGAIN: \
+ printf("The system temporarily lacks the resources to create another mutex\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") mutex %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, ret); \
+ break; \
+ case ENOMEM: \
+ printf("The process cannot allocate enough memory to create another mutex\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") mutex %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, ret); \
+ break; \
+ } \
+ } \
+ ret; \
+})
+
+#define SCMutexUnlock_dbg(mut) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocking mutex %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut); \
+ int retu = pthread_mutex_unlock(mut); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocked mutex %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, retu); \
+ if (retu != 0) { \
+ switch (retu) { \
+ case EINVAL: \
+ printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \
+ break; \
+ case EPERM: \
+ printf("The current thread does not hold a lock on mutex\n"); \
+ break; \
+ } \
+ } \
+ retu; \
+})
+
+#define SCMutex pthread_mutex_t
+#define SCMutexAttr pthread_mutexattr_t
+#define SCMutexInit(mut, mutattrs) SCMutexInit_dbg(mut, mutattrs)
+#define SCMutexLock(mut) SCMutexLock_dbg(mut)
+#define SCMutexTrylock(mut) SCMutexTrylock_dbg(mut)
+#define SCMutexUnlock(mut) SCMutexUnlock_dbg(mut)
+#define SCMutexDestroy pthread_mutex_destroy
+#define SCMUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+/* conditions */
+
+#define SCCondWait_dbg(cond, mut) ({ \
+ int ret = pthread_cond_wait(cond, mut); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid (or a SCCondT not initialized!)\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") failed SCCondWait %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, ret); \
+ break; \
+ } \
+ ret; \
+})
+
+/* conditions */
+#define SCCondT pthread_cond_t
+#define SCCondInit pthread_cond_init
+#define SCCondSignal pthread_cond_signal
+#define SCCondDestroy pthread_cond_destroy
+#define SCCondWait SCCondWait_dbg
+
+/* spinlocks */
+
+#define SCSpinLock_dbg(spin) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locking spin %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin); \
+ int ret = pthread_spin_lock(spin); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocked spin %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin, ret); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ break; \
+ case EDEADLK: \
+ printf("A deadlock would occur if the thread blocked waiting for spin\n"); \
+ break; \
+ } \
+ ret; \
+})
+
+#define SCSpinTrylock_dbg(spin) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking spin %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin); \
+ int ret = pthread_spin_trylock(spin); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked spin %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin, ret); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ break; \
+ case EDEADLK: \
+ printf("A deadlock would occur if the thread blocked waiting for spin\n"); \
+ break; \
+ case EBUSY: \
+ printf("A thread currently holds the lock\n"); \
+ break; \
+ } \
+ ret; \
+})
+
+#define SCSpinUnlock_dbg(spin) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocking spin %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin); \
+ int ret = pthread_spin_unlock(spin); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlockedspin %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin, ret); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ break; \
+ case EPERM: \
+ printf("The calling thread does not hold the lock\n"); \
+ break; \
+ } \
+ ret; \
+})
+
+#define SCSpinInit_dbg(spin, spin_attr) ({ \
+ int ret = pthread_spin_init(spin, spin_attr); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") spinlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin, ret); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ break; \
+ case EBUSY: \
+ printf("A thread currently holds the lock\n"); \
+ break; \
+ case ENOMEM: \
+ printf("The process cannot allocate enough memory to create another spin\n"); \
+ break; \
+ case EAGAIN: \
+ printf("The system temporarily lacks the resources to create another spin\n"); \
+ break; \
+ } \
+ ret; \
+})
+
+#define SCSpinDestroy_dbg(spin) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") condition %p waiting\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin); \
+ int ret = pthread_spin_destroy(spin); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") condition %p passed %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), spin, ret); \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ break; \
+ case EBUSY: \
+ printf("A thread currently holds the lock\n"); \
+ break; \
+ case ENOMEM: \
+ printf("The process cannot allocate enough memory to create another spin\n"); \
+ break; \
+ case EAGAIN: \
+ printf("The system temporarily lacks the resources to create another spin\n"); \
+ break; \
+ } \
+ ret; \
+})
+
+#define SCSpinlock pthread_spinlock_t
+#define SCSpinLock SCSpinLock_dbg
+#define SCSpinTrylock SCSpinTrylock_dbg
+#define SCSpinUnlock SCSpinUnlock_dbg
+#define SCSpinInit SCSpinInit_dbg
+#define SCSpinDestroy SCSpinDestroy_dbg
+
+/* rwlocks */
+
+/** When dbg threads is defined, if a rwlock fail to lock, it's
+ * initialized, logged, and does a second try; This is to prevent the system to freeze;
+ * If you see a rwlock, spinlock or condiion not initialized, report it please!
+ */
+#define SCRWLockRDLock_dbg(rwl) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \
+ int retl = pthread_rwlock_rdlock(rwl); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retl); \
+ if (retl != 0) { \
+ switch (retl) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ retl = pthread_rwlock_init(rwl, NULL); \
+ if (retl != 0) \
+ exit(EXIT_FAILURE); \
+ retl = pthread_rwlock_rdlock(rwl); \
+ break; \
+ case EDEADLK: \
+ printf("A deadlock would occur if the thread blocked waiting for rwlock\n"); \
+ break; \
+ } \
+ } \
+ retl; \
+})
+
+#define SCRWLockWRLock_dbg(rwl) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \
+ int retl = pthread_rwlock_wrlock(rwl); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") locked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retl); \
+ if (retl != 0) { \
+ switch (retl) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ retl = pthread_rwlock_init(rwl, NULL); \
+ if (retl != 0) \
+ exit(EXIT_FAILURE); \
+ retl = pthread_rwlock_wrlock(rwl); \
+ break; \
+ case EDEADLK: \
+ printf("A deadlock would occur if the thread blocked waiting for rwlock\n"); \
+ break; \
+ } \
+ } \
+ retl; \
+})
+
+
+#define SCRWLockTryWRLock_dbg(rwl) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \
+ int rett = pthread_rwlock_trywrlock(rwl); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, rett); \
+ if (rett != 0) { \
+ switch (rett) { \
+ case EINVAL: \
+ printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \
+ break; \
+ case EBUSY: \
+ printf("RWLock is already locked\n"); \
+ break; \
+ } \
+ } \
+ rett; \
+})
+
+#define SCRWLockTryRDLock_dbg(rwl) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \
+ int rett = pthread_rwlock_tryrdlock(rwl); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, rett); \
+ if (rett != 0) { \
+ switch (rett) { \
+ case EINVAL: \
+ printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \
+ break; \
+ case EBUSY: \
+ printf("RWLock is already locked\n"); \
+ break; \
+ } \
+ } \
+ rett; \
+})
+
+#define SCRWLockInit_dbg(rwl, rwlattr) ({ \
+ int ret; \
+ ret = pthread_rwlock_init(rwl, rwlattr); \
+ if (ret != 0) { \
+ switch (ret) { \
+ case EINVAL: \
+ printf("The value specified by attr is invalid\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \
+ break; \
+ case EAGAIN: \
+ printf("The system temporarily lacks the resources to create another rwlock\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \
+ break; \
+ case ENOMEM: \
+ printf("The process cannot allocate enough memory to create another rwlock\n"); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \
+ break; \
+ } \
+ } \
+ ret; \
+})
+
+#define SCRWLockUnlock_dbg(rwl) ({ \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \
+ int retu = pthread_rwlock_unlock(rwl); \
+ printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retu); \
+ if (retu != 0) { \
+ switch (retu) { \
+ case EINVAL: \
+ printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \
+ break; \
+ case EPERM: \
+ printf("The current thread does not hold a lock on rwlock\n"); \
+ break; \
+ } \
+ } \
+ retu; \
+})
+
+#define SCRWLock pthread_rwlock_t
+#define SCRWLockInit(rwl, rwlattrs) SCRWLockInit_dbg(rwl, rwlattrs)
+#define SCRWLockRDLock(rwl) SCRWLockRDLock_dbg(rwl)
+#define SCRWLockWRLock(rwl) SCRWLockWRLock_dbg(rwl)
+#define SCRWLockTryWRLock(rwl) SCRWLockTryWRLock_dbg(rwl)
+#define SCRWLockTryRDLock(rwl) SCRWLockTryRDLock_dbg(rwl)
+#define SCRWLockUnlock(rwl) SCRWLockUnlock_dbg(rwl)
+#define SCRWLockDestroy pthread_rwlock_destroy
+
+/* ctrl mutex */
+#define SCCtrlMutex pthread_mutex_t
+#define SCCtrlMutexAttr pthread_mutexattr_t
+#define SCCtrlMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCCtrlMutexLock(mut) pthread_mutex_lock(mut)
+#define SCCtrlMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCCtrlMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCCtrlMutexDestroy pthread_mutex_destroy
+
+/* ctrl conditions */
+#define SCCtrlCondT pthread_cond_t
+#define SCCtrlCondInit pthread_cond_init
+#define SCCtrlCondSignal pthread_cond_signal
+#define SCCtrlCondTimedwait pthread_cond_timedwait
+#define SCCtrlCondWait pthread_cond_wait
+#define SCCtrlCondDestroy pthread_cond_destroy
+
+#endif
diff --git a/framework/src/suricata/src/threads-profile.h b/framework/src/suricata/src/threads-profile.h
new file mode 100644
index 00000000..6e19673b
--- /dev/null
+++ b/framework/src/suricata/src/threads-profile.h
@@ -0,0 +1,218 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Lock profiling wrappers
+ */
+
+#ifndef __THREADS_PROFILE_H__
+#define __THREADS_PROFILE_H__
+
+/* profiling */
+
+typedef struct ProfilingLock_ {
+ char *file;
+ char *func;
+ int line;
+ int type;
+ uint32_t cont;
+ uint64_t ticks;
+} ProfilingLock;
+
+extern __thread ProfilingLock locks[PROFILING_MAX_LOCKS];
+extern __thread int locks_idx;
+extern __thread int record_locks;
+
+extern __thread uint64_t mutex_lock_contention;
+extern __thread uint64_t mutex_lock_wait_ticks;
+extern __thread uint64_t mutex_lock_cnt;
+
+/* mutex */
+
+//printf("%16s(%s:%d): (thread:%"PRIuMAX") locked mutex %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, retl);
+#define SCMutexLock_profile(mut) ({ \
+ mutex_lock_cnt++; \
+ int retl = 0; \
+ int cont = 0; \
+ uint64_t mutex_lock_start = UtilCpuGetTicks(); \
+ if (pthread_mutex_trylock((mut)) != 0) { \
+ mutex_lock_contention++; \
+ cont = 1; \
+ retl = pthread_mutex_lock(mut); \
+ } \
+ uint64_t mutex_lock_end = UtilCpuGetTicks(); \
+ mutex_lock_wait_ticks += (uint64_t)(mutex_lock_end - mutex_lock_start); \
+ \
+ if (locks_idx < PROFILING_MAX_LOCKS && record_locks) { \
+ locks[locks_idx].file = (char *)__FILE__; \
+ locks[locks_idx].func = (char *)__func__; \
+ locks[locks_idx].line = (int)__LINE__; \
+ locks[locks_idx].type = LOCK_MUTEX; \
+ locks[locks_idx].cont = cont; \
+ locks[locks_idx].ticks = (uint64_t)(mutex_lock_end - mutex_lock_start); \
+ locks_idx++; \
+ } \
+ retl; \
+})
+
+#define SCMutex pthread_mutex_t
+#define SCMutexAttr pthread_mutexattr_t
+#define SCMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCMutexLock(mut) SCMutexLock_profile(mut)
+#define SCMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCMutexDestroy pthread_mutex_destroy
+#define SCMUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+/* conditions */
+
+#define SCCondT pthread_cond_t
+#define SCCondInit pthread_cond_init
+#define SCCondSignal pthread_cond_signal
+#define SCCondDestroy pthread_cond_destroy
+#define SCCondWait(cond, mut) pthread_cond_wait(cond, mut)
+
+/* spinlocks */
+
+extern __thread uint64_t spin_lock_contention;
+extern __thread uint64_t spin_lock_wait_ticks;
+extern __thread uint64_t spin_lock_cnt;
+
+//printf("%16s(%s:%d): (thread:%"PRIuMAX") locked mutex %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), mut, retl);
+#define SCSpinLock_profile(spin) ({ \
+ spin_lock_cnt++; \
+ int retl = 0; \
+ int cont = 0; \
+ uint64_t spin_lock_start = UtilCpuGetTicks(); \
+ if (pthread_spin_trylock((spin)) != 0) { \
+ spin_lock_contention++; \
+ cont = 1; \
+ retl = pthread_spin_lock((spin)); \
+ } \
+ uint64_t spin_lock_end = UtilCpuGetTicks(); \
+ spin_lock_wait_ticks += (uint64_t)(spin_lock_end - spin_lock_start); \
+ \
+ if (locks_idx < PROFILING_MAX_LOCKS && record_locks) { \
+ locks[locks_idx].file = (char *)__FILE__; \
+ locks[locks_idx].func = (char *)__func__; \
+ locks[locks_idx].line = (int)__LINE__; \
+ locks[locks_idx].type = LOCK_SPIN; \
+ locks[locks_idx].cont = cont; \
+ locks[locks_idx].ticks = (uint64_t)(spin_lock_end - spin_lock_start); \
+ locks_idx++; \
+ } \
+ retl; \
+})
+
+#define SCSpinlock pthread_spinlock_t
+#define SCSpinLock(mut) SCSpinLock_profile(mut)
+#define SCSpinTrylock(spin) pthread_spin_trylock(spin)
+#define SCSpinUnlock(spin) pthread_spin_unlock(spin)
+#define SCSpinInit(spin, spin_attr) pthread_spin_init(spin, spin_attr)
+#define SCSpinDestroy(spin) pthread_spin_destroy(spin)
+
+/* rwlocks */
+
+extern __thread uint64_t rww_lock_contention;
+extern __thread uint64_t rww_lock_wait_ticks;
+extern __thread uint64_t rww_lock_cnt;
+
+#define SCRWLockWRLock_profile(mut) ({ \
+ rww_lock_cnt++; \
+ int retl = 0; \
+ int cont = 0; \
+ uint64_t rww_lock_start = UtilCpuGetTicks(); \
+ if (pthread_rwlock_trywrlock((mut)) != 0) { \
+ rww_lock_contention++; \
+ cont = 1; \
+ retl = pthread_rwlock_wrlock(mut); \
+ } \
+ uint64_t rww_lock_end = UtilCpuGetTicks(); \
+ rww_lock_wait_ticks += (uint64_t)(rww_lock_end - rww_lock_start); \
+ \
+ if (locks_idx < PROFILING_MAX_LOCKS && record_locks) { \
+ locks[locks_idx].file = (char *)__FILE__; \
+ locks[locks_idx].func = (char *)__func__; \
+ locks[locks_idx].line = (int)__LINE__; \
+ locks[locks_idx].type = LOCK_RWW; \
+ locks[locks_idx].cont = cont; \
+ locks[locks_idx].ticks = (uint64_t)(rww_lock_end - rww_lock_start); \
+ locks_idx++; \
+ } \
+ retl; \
+})
+
+extern __thread uint64_t rwr_lock_contention;
+extern __thread uint64_t rwr_lock_wait_ticks;
+extern __thread uint64_t rwr_lock_cnt;
+
+#define SCRWLockRDLock_profile(mut) ({ \
+ rwr_lock_cnt++; \
+ int retl = 0; \
+ int cont = 0; \
+ uint64_t rwr_lock_start = UtilCpuGetTicks(); \
+ if (pthread_rwlock_tryrdlock((mut)) != 0) { \
+ rwr_lock_contention++; \
+ cont = 1; \
+ retl = pthread_rwlock_rdlock(mut); \
+ } \
+ uint64_t rwr_lock_end = UtilCpuGetTicks(); \
+ rwr_lock_wait_ticks += (uint64_t)(rwr_lock_end - rwr_lock_start); \
+ \
+ if (locks_idx < PROFILING_MAX_LOCKS && record_locks) { \
+ locks[locks_idx].file = (char *)__FILE__; \
+ locks[locks_idx].func = (char *)__func__; \
+ locks[locks_idx].line = (int)__LINE__; \
+ locks[locks_idx].type = LOCK_RWR; \
+ locks[locks_idx].cont = cont; \
+ locks[locks_idx].ticks = (uint64_t)(rwr_lock_end - rwr_lock_start); \
+ locks_idx++; \
+ } \
+ retl; \
+})
+
+#define SCRWLock pthread_rwlock_t
+#define SCRWLockInit(rwl, rwlattr ) pthread_rwlock_init(rwl, rwlattr)
+#define SCRWLockWRLock(mut) SCRWLockWRLock_profile(mut)
+#define SCRWLockRDLock(mut) SCRWLockRDLock_profile(mut)
+#define SCRWLockTryWRLock(rwl) pthread_rwlock_trywrlock(rwl)
+#define SCRWLockTryRDLock(rwl) pthread_rwlock_tryrdlock(rwl)
+#define SCRWLockUnlock(rwl) pthread_rwlock_unlock(rwl)
+#define SCRWLockDestroy pthread_rwlock_destroy
+
+/* ctrl mutex */
+#define SCCtrlMutex pthread_mutex_t
+#define SCCtrlMutexAttr pthread_mutexattr_t
+#define SCCtrlMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCCtrlMutexLock(mut) pthread_mutex_lock(mut)
+#define SCCtrlMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCCtrlMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCCtrlMutexDestroy pthread_mutex_destroy
+
+/* ctrl conditions */
+#define SCCtrlCondT pthread_cond_t
+#define SCCtrlCondInit pthread_cond_init
+#define SCCtrlCondSignal pthread_cond_signal
+#define SCCtrlCondTimedwait pthread_cond_timedwait
+#define SCCtrlCondWait pthread_cond_wait
+#define SCCtrlCondDestroy pthread_cond_destroy
+
+#endif
diff --git a/framework/src/suricata/src/threads.c b/framework/src/suricata/src/threads.c
new file mode 100644
index 00000000..6e585825
--- /dev/null
+++ b/framework/src/suricata/src/threads.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * This file now only contains unit tests see macros in threads.h
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+#include "debug.h"
+#include "util-debug.h"
+#include "threads.h"
+
+#ifdef UNITTESTS /* UNIT TESTS */
+
+/**
+ * \brief Test Mutex macros
+ */
+int ThreadMacrosTest01Mutex(void)
+{
+ SCMutex mut;
+ int r = 0;
+ r |= SCMutexInit(&mut, NULL);
+ r |= SCMutexLock(&mut);
+ r |= (SCMutexTrylock(&mut) == EBUSY)? 0 : 1;
+ r |= SCMutexUnlock(&mut);
+ r |= SCMutexDestroy(&mut);
+
+ return (r == 0)? 1 : 0;
+}
+
+/**
+ * \brief Test Spinlock Macros
+ *
+ * Valgrind's DRD tool (valgrind-3.5.0-Debian) reports:
+ *
+ * ==31156== Recursive locking not allowed: mutex 0x7fefff97c, recursion count 1, owner 1.
+ * ==31156== at 0x4C2C77E: pthread_spin_trylock (drd_pthread_intercepts.c:829)
+ * ==31156== by 0x40EB3E: ThreadMacrosTest02Spinlocks (threads.c:40)
+ * ==31156== by 0x532E8A: UtRunTests (util-unittest.c:182)
+ * ==31156== by 0x4065C3: main (suricata.c:789)
+ *
+ * To me this is a false positve, as the whole point of "trylock" is to see
+ * if a spinlock is actually locked.
+ *
+ */
+int ThreadMacrosTest02Spinlocks(void)
+{
+ SCSpinlock mut;
+ int r = 0;
+ r |= SCSpinInit(&mut, 0);
+ r |= SCSpinLock(&mut);
+#ifndef __OpenBSD__
+ r |= (SCSpinTrylock(&mut) == EBUSY)? 0 : 1;
+#else
+ r |= (SCSpinTrylock(&mut) == EDEADLK)? 0 : 1;
+#endif
+ r |= SCSpinUnlock(&mut);
+ r |= SCSpinDestroy(&mut);
+
+ return (r == 0)? 1 : 0;
+}
+
+/**
+ * \brief Test RWLock macros
+ */
+int ThreadMacrosTest03RWLocks(void)
+{
+ SCRWLock rwl_write;
+ int r = 0;
+ r |= SCRWLockInit(&rwl_write, NULL);
+ r |= SCRWLockWRLock(&rwl_write);
+/* work around OS X 10.10 Yosemite returning EDEADLK. All other
+ * OS' (and versions of OS X that I tested) seem to return EBUSY
+ * instead. */
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__==101000
+ r |= (SCRWLockTryWRLock(&rwl_write) == EDEADLK)? 0 : 1;
+#else
+ r |= (SCRWLockTryWRLock(&rwl_write) == EBUSY)? 0 : 1;
+#endif
+ r |= SCRWLockUnlock(&rwl_write);
+ r |= SCRWLockDestroy(&rwl_write);
+
+ return (r == 0)? 1 : 0;
+}
+
+/**
+ * \brief Test RWLock macros
+ */
+int ThreadMacrosTest04RWLocks(void)
+{
+ SCRWLock rwl_read;
+ int r = 0;
+ r |= SCRWLockInit(&rwl_read, NULL);
+ r |= SCRWLockRDLock(&rwl_read);
+ r |= (SCRWLockTryWRLock(&rwl_read) == EBUSY)? 0 : 1;
+ r |= SCRWLockUnlock(&rwl_read);
+ r |= SCRWLockDestroy(&rwl_read);
+
+ return (r == 0)? 1 : 0;
+}
+
+/**
+ * \brief Test RWLock macros
+ */
+int ThreadMacrosTest05RWLocks(void)
+{
+ SCRWLock rwl_read;
+ int r = 0;
+ r |= SCRWLockInit(&rwl_read, NULL);
+ r |= SCRWLockWRLock(&rwl_read);
+ r |= (SCRWLockTryRDLock(&rwl_read) == EBUSY)? 0 : 1;
+ r |= SCRWLockUnlock(&rwl_read);
+ r |= SCRWLockDestroy(&rwl_read);
+
+ return (r == 0)? 1 : 0;
+}
+
+#endif /* UNIT TESTS */
+
+/**
+ * \brief this function registers unit tests for DetectId
+ */
+void ThreadMacrosRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNIT TESTS */
+ UtRegisterTest("ThreadMacrosTest01Mutex", ThreadMacrosTest01Mutex, 1);
+ UtRegisterTest("ThreadMacrosTest02Spinlocks", ThreadMacrosTest02Spinlocks, 1);
+ UtRegisterTest("ThreadMacrosTest03RWLocks", ThreadMacrosTest03RWLocks, 1);
+ UtRegisterTest("ThreadMacrosTest04RWLocks", ThreadMacrosTest04RWLocks, 1);
+#endif /* UNIT TESTS */
+}
diff --git a/framework/src/suricata/src/threads.h b/framework/src/suricata/src/threads.h
new file mode 100644
index 00000000..0a843b7f
--- /dev/null
+++ b/framework/src/suricata/src/threads.h
@@ -0,0 +1,300 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Threading functions defined as macros
+ */
+
+#ifndef __THREADS_H__
+#define __THREADS_H__
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* need this for the _POSIX_SPIN_LOCKS define */
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef PROFILING
+#include "util-cpu.h"
+#ifdef PROFILE_LOCKING
+#include "util-profiling-locks.h"
+#endif /* PROFILE_LOCKING */
+#endif /* PROFILING */
+
+#if defined OS_FREEBSD || __OpenBSD__
+
+#if ! defined __OpenBSD__
+#include <sys/thr.h>
+#endif
+enum {
+ PRIO_LOW = 2,
+ PRIO_MEDIUM = 0,
+ PRIO_HIGH = -2,
+};
+
+#elif OS_DARWIN
+
+#include <mach/mach_init.h>
+enum {
+ PRIO_LOW = 2,
+ PRIO_MEDIUM = 0,
+ PRIO_HIGH = -2,
+};
+
+#elif OS_WIN32
+
+#include <windows.h>
+enum {
+ PRIO_LOW = THREAD_PRIORITY_LOWEST,
+ PRIO_MEDIUM = THREAD_PRIORITY_NORMAL,
+ PRIO_HIGH = THREAD_PRIORITY_HIGHEST,
+};
+
+#else /* LINUX */
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+#if HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#define THREAD_NAME_LEN 16
+#endif
+
+enum {
+ PRIO_LOW = 2,
+ PRIO_MEDIUM = 0,
+ PRIO_HIGH = -2,
+};
+
+#endif /* OS_FREEBSD */
+
+#include <pthread.h>
+
+/* The mutex/spinlock/condition definitions and functions are used
+ * in the same way as the POSIX definitions; Anyway we are centralizing
+ * them here to make an easier portability process and debugging process;
+ * Please, make sure you initialize mutex and spinlocks before using them
+ * because, some OS doesn't initialize them for you :)
+ */
+
+//#define DBG_THREADS
+
+#ifdef __tile__
+ #include "threads-arch-tile.h"
+#elif defined DBG_THREADS
+ #ifdef PROFILE_LOCKING
+ #error "Cannot mix DBG_THREADS and PROFILE_LOCKING"
+ #endif
+ #include "threads-debug.h"
+#elif defined PROFILE_LOCKING
+ #include "threads-profile.h"
+#else /* normal */
+
+/* mutex */
+#define SCMutex pthread_mutex_t
+#define SCMutexAttr pthread_mutexattr_t
+#define SCMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCMutexLock(mut) pthread_mutex_lock(mut)
+#define SCMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCMutexDestroy pthread_mutex_destroy
+#define SCMUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+/* rwlocks */
+#define SCRWLock pthread_rwlock_t
+#define SCRWLockInit(rwl, rwlattr ) pthread_rwlock_init(rwl, rwlattr)
+#define SCRWLockWRLock(rwl) pthread_rwlock_wrlock(rwl)
+#define SCRWLockRDLock(rwl) pthread_rwlock_rdlock(rwl)
+#define SCRWLockTryWRLock(rwl) pthread_rwlock_trywrlock(rwl)
+#define SCRWLockTryRDLock(rwl) pthread_rwlock_tryrdlock(rwl)
+#define SCRWLockUnlock(rwl) pthread_rwlock_unlock(rwl)
+#define SCRWLockDestroy pthread_rwlock_destroy
+
+/* conditions */
+#define SCCondT pthread_cond_t
+#define SCCondInit pthread_cond_init
+#define SCCondSignal pthread_cond_signal
+#define SCCondDestroy pthread_cond_destroy
+#define SCCondWait(cond, mut) pthread_cond_wait(cond, mut)
+
+/* ctrl mutex */
+#define SCCtrlMutex pthread_mutex_t
+#define SCCtrlMutexAttr pthread_mutexattr_t
+#define SCCtrlMutexInit(mut, mutattr ) pthread_mutex_init(mut, mutattr)
+#define SCCtrlMutexLock(mut) pthread_mutex_lock(mut)
+#define SCCtrlMutexTrylock(mut) pthread_mutex_trylock(mut)
+#define SCCtrlMutexUnlock(mut) pthread_mutex_unlock(mut)
+#define SCCtrlMutexDestroy pthread_mutex_destroy
+
+/* ctrl conditions */
+#define SCCtrlCondT pthread_cond_t
+#define SCCtrlCondInit pthread_cond_init
+#define SCCtrlCondSignal pthread_cond_signal
+#define SCCtrlCondTimedwait pthread_cond_timedwait
+#define SCCtrlCondWait pthread_cond_wait
+#define SCCtrlCondDestroy pthread_cond_destroy
+
+/* spinlocks */
+#if ((_POSIX_SPIN_LOCKS - 200112L) < 0L) || defined HELGRIND
+#define SCSpinlock SCMutex
+#define SCSpinLock(spin) SCMutexLock((spin))
+#define SCSpinTrylock(spin) SCMutexTrylock((spin))
+#define SCSpinUnlock(spin) SCMutexUnlock((spin))
+#define SCSpinInit(spin, spin_attr) SCMutexInit((spin), NULL)
+#define SCSpinDestroy(spin) SCMutexDestroy((spin))
+#else /* no spinlocks */
+#define SCSpinlock pthread_spinlock_t
+#define SCSpinLock(spin) pthread_spin_lock(spin)
+#define SCSpinTrylock(spin) pthread_spin_trylock(spin)
+#define SCSpinUnlock(spin) pthread_spin_unlock(spin)
+#define SCSpinInit(spin, spin_attr) pthread_spin_init(spin, spin_attr)
+#define SCSpinDestroy(spin) pthread_spin_destroy(spin)
+#endif /* no spinlocks */
+
+#endif /* __tile__ */
+
+#if (!defined SCMutex || !defined SCMutexAttr || !defined SCMutexInit || \
+ !defined SCMutexLock || !defined SCMutexTrylock || \
+ !defined SCMutexUnlock || !defined SCMutexDestroy || \
+ !defined SCMUTEX_INITIALIZER)
+#error "Mutex types and/or macro's not properly defined"
+#endif
+#if (!defined SCCtrlMutex || !defined SCCtrlMutexAttr || !defined SCCtrlMutexInit || \
+ !defined SCCtrlMutexLock || !defined SCCtrlMutexTrylock || \
+ !defined SCCtrlMutexUnlock || !defined SCCtrlMutexDestroy)
+#error "SCCtrlMutex types and/or macro's not properly defined"
+#endif
+
+#if (!defined SCSpinlock || !defined SCSpinLock || \
+ !defined SCSpinTrylock || !defined SCSpinUnlock || \
+ !defined SCSpinInit || !defined SCSpinDestroy)
+#error "Spinlock types and/or macro's not properly defined"
+#endif
+
+#if (!defined SCRWLock || !defined SCRWLockInit || !defined SCRWLockWRLock || \
+ !defined SCRWLockRDLock || !defined SCRWLockTryWRLock || \
+ !defined SCRWLockTryRDLock || !defined SCRWLockUnlock || !defined SCRWLockDestroy)
+#error "SCRWLock types and/or macro's not properly defined"
+#endif
+
+#if (!defined SCCondT || !defined SCCondInit || !defined SCCondSignal || \
+ !defined SCCondDestroy || !defined SCCondWait)
+#error "SCCond types and/or macro's not properly defined"
+#endif
+
+#if (!defined SCCtrlCondT || !defined SCCtrlCondInit || !defined SCCtrlCondSignal ||\
+ !defined SCCtrlCondDestroy || !defined SCCtrlCondTimedwait)
+#error "SCCtrlCond types and/or macro's not properly defined"
+#endif
+
+/** Get the Current Thread Id */
+#ifdef OS_FREEBSD
+#include <pthread_np.h>
+
+#define SCGetThreadIdLong(...) ({ \
+ long tmpthid; \
+ thr_self(&tmpthid); \
+ u_long tid = (u_long)tmpthid; \
+ tid; \
+})
+#elif __OpenBSD__
+#define SCGetThreadIdLong(...) ({ \
+ pid_t tpid; \
+ tpid = getpid(); \
+ u_long tid = (u_long)tpid; \
+ tid; \
+})
+#elif __CYGWIN__
+#define SCGetThreadIdLong(...) ({ \
+ u_long tid = (u_long)GetCurrentThreadId(); \
+ tid; \
+})
+#elif OS_WIN32
+#define SCGetThreadIdLong(...) ({ \
+ u_long tid = (u_long)GetCurrentThreadId(); \
+ tid; \
+})
+#elif OS_DARWIN
+#define SCGetThreadIdLong(...) ({ \
+ thread_port_t tpid; \
+ tpid = mach_thread_self(); \
+ u_long tid = (u_long)tpid; \
+ tid; \
+})
+#else
+#define SCGetThreadIdLong(...) ({ \
+ pid_t tmpthid; \
+ tmpthid = syscall(SYS_gettid); \
+ u_long tid = (u_long)tmpthid; \
+ tid; \
+})
+#endif /* OS FREEBSD */
+
+/*
+ * OS specific macro's for setting the thread name. "top" can display
+ * this name.
+ */
+#if defined OS_FREEBSD /* FreeBSD */
+/** \todo Add implementation for FreeBSD */
+#define SCSetThreadName(n) ({ \
+ char tname[16] = ""; \
+ if (strlen(n) > 16) \
+ SCLogDebug("Thread name is too long, truncating it..."); \
+ strlcpy(tname, n, 16); \
+ pthread_set_name_np(pthread_self(), tname); \
+ 0; \
+})
+#elif defined __OpenBSD__ /* OpenBSD */
+/** \todo Add implementation for OpenBSD */
+#define SCSetThreadName(n) (0)
+#elif defined OS_WIN32 /* Windows */
+/** \todo Add implementation for Windows */
+#define SCSetThreadName(n) (0)
+#elif defined OS_DARWIN /* Mac OS X */
+/** \todo Add implementation for MacOS */
+#define SCSetThreadName(n) (0)
+#elif defined PR_SET_NAME /* PR_SET_NAME */
+/**
+ * \brief Set the threads name
+ */
+#define SCSetThreadName(n) ({ \
+ char tname[THREAD_NAME_LEN + 1] = ""; \
+ if (strlen(n) > THREAD_NAME_LEN) \
+ SCLogDebug("Thread name is too long, truncating it..."); \
+ strlcpy(tname, n, THREAD_NAME_LEN); \
+ int ret = 0; \
+ if ((ret = prctl(PR_SET_NAME, tname, 0, 0, 0)) < 0) \
+ SCLogDebug("Error setting thread name \"%s\": %s", tname, strerror(errno)); \
+ ret; \
+})
+#else
+#define SCSetThreadName(n) (0)
+#endif
+
+
+void ThreadMacrosRegisterTests(void);
+
+#endif /* __THREADS_H__ */
+
diff --git a/framework/src/suricata/src/threadvars.h b/framework/src/suricata/src/threadvars.h
new file mode 100644
index 00000000..a632a23b
--- /dev/null
+++ b/framework/src/suricata/src/threadvars.h
@@ -0,0 +1,127 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __THREADVARS_H__
+#define __THREADVARS_H__
+
+#include "util-mpm.h"
+#include "util-affinity.h"
+#include "tm-queues.h"
+#include "counters.h"
+#include "threads.h"
+
+struct TmSlot_;
+
+/** Thread flags set and read by threads to control the threads */
+#define THV_USE 1 /** thread is in use */
+#define THV_INIT_DONE (1 << 1) /** thread initialization done */
+#define THV_PAUSE (1 << 2) /** signal thread to pause itself */
+#define THV_PAUSED (1 << 3) /** the thread is paused atm */
+#define THV_KILL (1 << 4) /** thread has been asked to cleanup and exit */
+#define THV_FAILED (1 << 5) /** thread has encountered an error and failed */
+#define THV_CLOSED (1 << 6) /** thread done, should be joinable */
+/* used to indicate the thread is going through de-init. Introduced as more
+ * of a hack for solving stream-timeout-shutdown. Is set by the main thread. */
+#define THV_DEINIT (1 << 7)
+#define THV_RUNNING_DONE (1 << 8) /** thread has completed running and is entering
+ * the de-init phase */
+#define THV_KILL_PKTACQ (1 << 9) /**< flag thread to stop packet acq */
+#define THV_FLOW_LOOP (1 << 10) /**< thread is in flow shutdown loop */
+
+/** Thread flags set and read by threads, to control the threads, when they
+ * encounter certain conditions like failure */
+#define THV_RESTART_THREAD 0x01 /** restart the thread */
+#define THV_ENGINE_EXIT 0x02 /** shut the engine down gracefully */
+
+/** Maximum no of times a thread can be restarted */
+#define THV_MAX_RESTARTS 50
+
+/** \brief Per thread variable structure */
+typedef struct ThreadVars_ {
+ pthread_t t;
+ char *name;
+ char *thread_group_name;
+
+ SC_ATOMIC_DECLARE(unsigned int, flags);
+
+ /** aof(action on failure) determines what should be done with the thread
+ when it encounters certain conditions like failures */
+ uint8_t aof;
+
+ /** no of times the thread has been restarted on failure */
+ uint8_t restarted;
+
+ /** local id */
+ int id;
+
+ /** queue's */
+ Tmq *inq;
+ Tmq *outq;
+ void *outctx;
+ char *outqh_name;
+
+ /** queue handlers */
+ struct Packet_ * (*tmqh_in)(struct ThreadVars_ *);
+ void (*InShutdownHandler)(struct ThreadVars_ *);
+ void (*tmqh_out)(struct ThreadVars_ *, struct Packet_ *);
+
+ /** slot functions */
+ void *(*tm_func)(void *);
+ struct TmSlot_ *tm_slots;
+
+ /** stream packet queue for flow time out injection */
+ struct PacketQueue_ *stream_pq;
+
+ uint8_t thread_setup_flags;
+
+ /** the type of thread as defined in tm-threads.h (TVT_PPT, TVT_MGMT) */
+ uint8_t type;
+
+ uint16_t cpu_affinity; /** cpu or core number to set affinity to */
+ uint16_t rank;
+ int thread_priority; /** priority (real time) for this thread. Look at threads.h */
+
+ /* counters */
+
+ /** public counter store: counter syncs update this */
+ StatsPublicThreadContext perf_public_ctx;
+
+ /** private counter store: counter updates modify this */
+ StatsPrivateThreadContext perf_private_ctx;
+
+ SCCtrlMutex *ctrl_mutex;
+ SCCtrlCondT *ctrl_cond;
+
+ uint8_t cap_flags; /**< Flags to indicate the capabilities of all the
+ TmModules resgitered under this thread */
+ struct ThreadVars_ *next;
+ struct ThreadVars_ *prev;
+} ThreadVars;
+
+/** Thread setup flags: */
+#define THREAD_SET_AFFINITY 0x01 /** CPU/Core affinity */
+#define THREAD_SET_PRIORITY 0x02 /** Real time priority */
+#define THREAD_SET_AFFTYPE 0x04 /** Priority and affinity */
+
+#endif /* __THREADVARS_H__ */
+
diff --git a/framework/src/suricata/src/tm-modules.c b/framework/src/suricata/src/tm-modules.c
new file mode 100644
index 00000000..73e9f235
--- /dev/null
+++ b/framework/src/suricata/src/tm-modules.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Thread Module functions
+ */
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "tm-threads.h"
+#include "util-debug.h"
+#include "threads.h"
+#include "util-logopenfile.h"
+
+void TmModuleDebugList(void)
+{
+ TmModule *t;
+ uint16_t i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ SCLogDebug("%s:%p", t->name, t->Func);
+ }
+}
+
+/** \brief get a tm module ptr by name
+ * \param name name string
+ * \retval ptr to the module or NULL */
+TmModule *TmModuleGetByName(const char *name)
+{
+ TmModule *t;
+ uint16_t i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ if (strcmp(t->name, name) == 0)
+ return t;
+ }
+
+ return NULL;
+}
+
+/** \brief get the id of a module from it's name
+ * \param name registered name of the module
+ * \retval id the id or -1 in case of error */
+int TmModuleGetIdByName(const char *name)
+{
+ TmModule *tm = TmModuleGetByName(name);
+ if (tm == NULL)
+ return -1;;
+ return TmModuleGetIDForTM(tm);
+}
+
+/**
+ * \brief Returns a TM Module by its id.
+ *
+ * \param id Id of the TM Module to return.
+ *
+ * \retval Pointer of the module to be returned if available;
+ * NULL if unavailable.
+ */
+TmModule *TmModuleGetById(int id)
+{
+
+ if (id < 0 || id >= TMM_SIZE) {
+ SCLogError(SC_ERR_TM_MODULES_ERROR, "Threading module with the id "
+ "\"%d\" doesn't exist", id);
+ return NULL;
+ }
+
+ return &tmm_modules[id];
+}
+
+/**
+ * \brief Given a TM Module, returns its id.
+ *
+ * \param tm Pointer to the TM Module.
+ *
+ * \retval id of the TM Module if available; -1 if unavailable.
+ */
+int TmModuleGetIDForTM(TmModule *tm)
+{
+ TmModule *t;
+ int i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ if (strcmp(t->name, tm->name) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
+void TmModuleRunInit(void)
+{
+ TmModule *t;
+ uint16_t i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ if (t->Init == NULL)
+ continue;
+
+ t->Init();
+ }
+}
+
+void TmModuleRunDeInit(void)
+{
+ TmModule *t;
+ uint16_t i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ if (t->DeInit == NULL)
+ continue;
+
+ t->DeInit();
+ }
+}
+
+/** \brief register all unittests for the tm modules */
+void TmModuleRegisterTests(void)
+{
+#ifdef UNITTESTS
+ TmModule *t;
+ uint16_t i;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ t = &tmm_modules[i];
+
+ if (t->name == NULL)
+ continue;
+
+ g_ut_modules++;
+
+
+ if (t->RegisterTests == NULL) {
+ if (coverage_unittests)
+ SCLogWarning(SC_WARN_NO_UNITTESTS, "threading module %s has no unittest "
+ "registration function.", t->name);
+ } else {
+ t->RegisterTests();
+ g_ut_covered++;
+ }
+ }
+#endif /* UNITTESTS */
+}
+
+#define CASE_CODE(E) case E: return #E
+
+/**
+ * \brief Maps the TmmId, to its string equivalent
+ *
+ * \param id tmm id
+ *
+ * \retval string equivalent for the tmm id
+ */
+const char * TmModuleTmmIdToString(TmmId id)
+{
+ switch (id) {
+ CASE_CODE (TMM_RECEIVENFLOG);
+ CASE_CODE (TMM_DECODENFLOG);
+ CASE_CODE (TMM_DECODENFQ);
+ CASE_CODE (TMM_VERDICTNFQ);
+ CASE_CODE (TMM_RECEIVENFQ);
+ CASE_CODE (TMM_RECEIVEPCAP);
+ CASE_CODE (TMM_RECEIVEPCAPFILE);
+ CASE_CODE (TMM_DECODEPCAP);
+ CASE_CODE (TMM_DECODEPCAPFILE);
+ CASE_CODE (TMM_RECEIVEPFRING);
+ CASE_CODE (TMM_DECODEPFRING);
+ CASE_CODE (TMM_DETECT);
+ CASE_CODE (TMM_ALERTFASTLOG);
+ CASE_CODE (TMM_ALERTFASTLOG4);
+ CASE_CODE (TMM_ALERTFASTLOG6);
+ CASE_CODE (TMM_ALERTUNIFIED2ALERT);
+ CASE_CODE (TMM_ALERTPRELUDE);
+ CASE_CODE (TMM_ALERTDEBUGLOG);
+ CASE_CODE (TMM_ALERTSYSLOG);
+ CASE_CODE (TMM_LOGDROPLOG);
+ CASE_CODE (TMM_ALERTSYSLOG4);
+ CASE_CODE (TMM_ALERTSYSLOG6);
+ CASE_CODE (TMM_RESPONDREJECT);
+ CASE_CODE (TMM_LOGDNSLOG);
+ CASE_CODE (TMM_LOGHTTPLOG);
+ CASE_CODE (TMM_LOGHTTPLOG4);
+ CASE_CODE (TMM_LOGHTTPLOG6);
+ CASE_CODE (TMM_LOGTLSLOG);
+ CASE_CODE (TMM_LOGTLSLOG4);
+ CASE_CODE (TMM_LOGTLSLOG6);
+ CASE_CODE (TMM_LOGTCPDATALOG);
+ CASE_CODE (TMM_PCAPLOG);
+ CASE_CODE (TMM_FILELOG);
+ CASE_CODE (TMM_FILESTORE);
+ CASE_CODE (TMM_STREAMTCP);
+ CASE_CODE (TMM_DECODEIPFW);
+ CASE_CODE (TMM_VERDICTIPFW);
+ CASE_CODE (TMM_RECEIVEIPFW);
+ CASE_CODE (TMM_RECEIVEERFFILE);
+ CASE_CODE (TMM_DECODEERFFILE);
+ CASE_CODE (TMM_RECEIVEERFDAG);
+ CASE_CODE (TMM_DECODEERFDAG);
+ CASE_CODE (TMM_RECEIVEMPIPE);
+ CASE_CODE (TMM_DECODEMPIPE);
+ CASE_CODE (TMM_RECEIVENAPATECH);
+ CASE_CODE (TMM_DECODENAPATECH);
+ CASE_CODE (TMM_RECEIVEAFP);
+ CASE_CODE (TMM_ALERTPCAPINFO);
+ CASE_CODE (TMM_DECODEAFP);
+ CASE_CODE (TMM_PACKETLOGGER);
+ CASE_CODE (TMM_TXLOGGER);
+ CASE_CODE (TMM_STATSLOGGER);
+ CASE_CODE (TMM_FILELOGGER);
+ CASE_CODE (TMM_FILEDATALOGGER);
+ CASE_CODE (TMM_STREAMINGLOGGER);
+ CASE_CODE (TMM_JSONALERTLOG);
+ CASE_CODE (TMM_JSONDROPLOG);
+ CASE_CODE (TMM_JSONDNSLOG);
+ CASE_CODE (TMM_JSONHTTPLOG);
+ CASE_CODE (TMM_JSONFILELOG);
+ CASE_CODE (TMM_JSONFLOWLOG);
+ CASE_CODE (TMM_JSONNETFLOWLOG);
+ CASE_CODE (TMM_JSONSMTPLOG);
+ CASE_CODE (TMM_JSONSSHLOG);
+ CASE_CODE (TMM_JSONSTATSLOG);
+ CASE_CODE (TMM_JSONTLSLOG);
+ CASE_CODE (TMM_OUTPUTJSON);
+ CASE_CODE (TMM_FLOWMANAGER);
+ CASE_CODE (TMM_FLOWRECYCLER);
+ CASE_CODE (TMM_UNIXMANAGER);
+ CASE_CODE (TMM_DETECTLOADER);
+ CASE_CODE (TMM_LUALOG);
+ CASE_CODE (TMM_LOGSTATSLOG);
+ CASE_CODE (TMM_RECEIVENETMAP);
+ CASE_CODE (TMM_DECODENETMAP);
+ CASE_CODE (TMM_TLSSTORE);
+
+ CASE_CODE (TMM_SIZE);
+ }
+ return "<unknown>";
+}
diff --git a/framework/src/suricata/src/tm-modules.h b/framework/src/suricata/src/tm-modules.h
new file mode 100644
index 00000000..c9d4e1f5
--- /dev/null
+++ b/framework/src/suricata/src/tm-modules.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TM_MODULES_H__
+#define __TM_MODULES_H__
+
+#include "tm-threads-common.h"
+#include "threadvars.h"
+
+/* thread flags */
+#define TM_FLAG_RECEIVE_TM 0x01
+#define TM_FLAG_DECODE_TM 0x02
+#define TM_FLAG_STREAM_TM 0x04
+#define TM_FLAG_DETECT_TM 0x08
+#define TM_FLAG_LOGAPI_TM 0x10 /**< TM is run by Log API */
+#define TM_FLAG_MANAGEMENT_TM 0x20
+#define TM_FLAG_COMMAND_TM 0x40
+
+typedef struct TmModule_ {
+ char *name;
+
+ /** thread handling */
+ TmEcode (*ThreadInit)(ThreadVars *, void *, void **);
+ void (*ThreadExitPrintStats)(ThreadVars *, void *);
+ TmEcode (*ThreadDeinit)(ThreadVars *, void *);
+
+ /** the packet processing function */
+ TmEcode (*Func)(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+
+ TmEcode (*PktAcqLoop)(ThreadVars *, void *, void *);
+
+ TmEcode (*Management)(ThreadVars *, void *);
+
+ /** global Init/DeInit */
+ TmEcode (*Init)(void);
+ TmEcode (*DeInit)(void);
+
+ void (*RegisterTests)(void);
+
+ uint8_t cap_flags; /**< Flags to indicate the capability requierment of
+ the given TmModule */
+ /* Other flags used by the module */
+ uint8_t flags;
+ /* priority in the logging order, higher priority is runned first */
+ uint8_t priority;
+} TmModule;
+
+TmModule tmm_modules[TMM_SIZE];
+
+/**
+ * Structure that output modules use to maintain private data.
+ */
+typedef struct OutputCtx_ {
+
+ /** Pointer to data private to the output. */
+ void *data;
+
+ /** Pointer to a cleanup function. */
+ void (*DeInit)(struct OutputCtx_ *);
+
+ TAILQ_HEAD(, OutputModule_) submodules;
+} OutputCtx;
+
+TmModule *TmModuleGetByName(const char *name);
+TmModule *TmModuleGetById(int id);
+int TmModuleGetIdByName(const char *name);
+int TmModuleGetIDForTM(TmModule *tm);
+TmEcode TmModuleRegister(char *name, int (*module_func)(ThreadVars *, Packet *, void *));
+void TmModuleDebugList(void);
+void TmModuleRegisterTests(void);
+const char * TmModuleTmmIdToString(TmmId id);
+
+void TmModuleRunInit(void);
+void TmModuleRunDeInit(void);
+
+#endif /* __TM_MODULES_H__ */
+
diff --git a/framework/src/suricata/src/tm-queuehandlers.c b/framework/src/suricata/src/tm-queuehandlers.c
new file mode 100644
index 00000000..007a8ab3
--- /dev/null
+++ b/framework/src/suricata/src/tm-queuehandlers.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Master Queue Handler
+ */
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+
+#include "tm-queuehandlers.h"
+#include "tmqh-simple.h"
+#include "tmqh-nfq.h"
+#include "tmqh-packetpool.h"
+#include "tmqh-flow.h"
+#include "tmqh-ringbuffer.h"
+
+void TmqhSetup (void)
+{
+ memset(&tmqh_table, 0, sizeof(tmqh_table));
+
+ TmqhSimpleRegister();
+ TmqhNfqRegister();
+ TmqhPacketpoolRegister();
+ TmqhFlowRegister();
+ TmqhRingBufferRegister();
+}
+
+/** \brief Clean up registration time allocs */
+void TmqhCleanup(void)
+{
+ TmqhRingBufferDestroy();
+}
+
+Tmqh* TmqhGetQueueHandlerByName(char *name)
+{
+ int i;
+
+ for (i = 0; i < TMQH_SIZE; i++) {
+ if (strcmp(name, tmqh_table[i].name) == 0)
+ return &tmqh_table[i];
+ }
+
+ return NULL;
+}
+
diff --git a/framework/src/suricata/src/tm-queuehandlers.h b/framework/src/suricata/src/tm-queuehandlers.h
new file mode 100644
index 00000000..63249799
--- /dev/null
+++ b/framework/src/suricata/src/tm-queuehandlers.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TM_QUEUEHANDLERS_H__
+#define __TM_QUEUEHANDLERS_H__
+
+enum {
+ TMQH_SIMPLE,
+ TMQH_NFQ,
+ TMQH_PACKETPOOL,
+ TMQH_FLOW,
+ TMQH_RINGBUFFER_MRSW,
+ TMQH_RINGBUFFER_SRSW,
+ TMQH_RINGBUFFER_SRMW,
+
+ TMQH_SIZE,
+};
+
+typedef struct Tmqh_ {
+ char *name;
+ Packet *(*InHandler)(ThreadVars *);
+ void (*InShutdownHandler)(ThreadVars *);
+ void (*OutHandler)(ThreadVars *, Packet *);
+ void *(*OutHandlerCtxSetup)(char *);
+ void (*OutHandlerCtxFree)(void *);
+ void (*RegisterTests)(void);
+} Tmqh;
+
+Tmqh tmqh_table[TMQH_SIZE];
+
+void TmqhSetup (void);
+void TmqhCleanup(void);
+Tmqh* TmqhGetQueueHandlerByName(char *name);
+
+#endif /* __TM_QUEUEHANDLERS_H__ */
+
diff --git a/framework/src/suricata/src/tm-queues.c b/framework/src/suricata/src/tm-queues.c
new file mode 100644
index 00000000..e549a975
--- /dev/null
+++ b/framework/src/suricata/src/tm-queues.c
@@ -0,0 +1,126 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Thread module management functions
+ */
+
+#include "suricata.h"
+#include "threads.h"
+#include "tm-queues.h"
+#include "util-debug.h"
+
+#define TMQ_MAX_QUEUES 256
+
+static uint16_t tmq_id = 0;
+static Tmq tmqs[TMQ_MAX_QUEUES];
+
+Tmq* TmqAlloc(void)
+{
+ Tmq *q = SCMalloc(sizeof(Tmq));
+ if (unlikely(q == NULL))
+ goto error;
+
+ memset(q, 0, sizeof(Tmq));
+ return q;
+
+error:
+ return NULL;
+}
+
+Tmq* TmqCreateQueue(char *name)
+{
+ if (tmq_id >= TMQ_MAX_QUEUES)
+ goto error;
+
+ Tmq *q = &tmqs[tmq_id];
+ q->name = name;
+ q->id = tmq_id++;
+ /* for cuda purposes */
+ q->q_type = 0;
+
+ SCLogDebug("created queue \'%s\', %p", name, q);
+ return q;
+
+error:
+ SCLogError(SC_ERR_THREAD_QUEUE, "too many thread queues %u, max is %u", tmq_id+1, TMQ_MAX_QUEUES);
+ return NULL;
+}
+
+Tmq* TmqGetQueueByName(char *name)
+{
+ uint16_t i;
+
+ for (i = 0; i < tmq_id; i++) {
+ if (strcmp(tmqs[i].name, name) == 0)
+ return &tmqs[i];
+ }
+
+ return NULL;
+}
+
+void TmqDebugList(void)
+{
+ uint16_t i = 0;
+ for (i = 0; i < tmq_id; i++) {
+ /* get a lock accessing the len */
+ SCMutexLock(&trans_q[tmqs[i].id].mutex_q);
+ printf("TmqDebugList: id %" PRIu32 ", name \'%s\', len %" PRIu32 "\n", tmqs[i].id, tmqs[i].name, trans_q[tmqs[i].id].len);
+ SCMutexUnlock(&trans_q[tmqs[i].id].mutex_q);
+ }
+}
+
+void TmqResetQueues(void)
+{
+ memset(&tmqs, 0x00, sizeof(tmqs));
+ tmq_id = 0;
+}
+
+/**
+ * \brief Checks if all the queues allocated so far have at least one reader
+ * and writer.
+ */
+void TmValidateQueueState(void)
+{
+ int i = 0;
+ char err = FALSE;
+
+ for (i = 0; i < tmq_id; i++) {
+ SCMutexLock(&trans_q[tmqs[i].id].mutex_q);
+ if (tmqs[i].reader_cnt == 0) {
+ SCLogError(SC_ERR_THREAD_QUEUE, "queue \"%s\" doesn't have a reader (id %d, max %u)", tmqs[i].name, i, tmq_id);
+ err = TRUE;
+ } else if (tmqs[i].writer_cnt == 0) {
+ SCLogError(SC_ERR_THREAD_QUEUE, "queue \"%s\" doesn't have a writer (id %d, max %u)", tmqs[i].name, i, tmq_id);
+ err = TRUE;
+ }
+ SCMutexUnlock(&trans_q[tmqs[i].id].mutex_q);
+
+ if (err == TRUE)
+ goto error;
+ }
+
+ return;
+
+error:
+ SCLogError(SC_ERR_FATAL, "fatal error during threading setup");
+ exit(EXIT_FAILURE);
+}
diff --git a/framework/src/suricata/src/tm-queues.h b/framework/src/suricata/src/tm-queues.h
new file mode 100644
index 00000000..01dbb6e7
--- /dev/null
+++ b/framework/src/suricata/src/tm-queues.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TM_QUEUES_H__
+#define __TM_QUEUES_H__
+
+typedef struct Tmq_ {
+ char *name;
+ uint16_t id;
+ uint16_t reader_cnt;
+ uint16_t writer_cnt;
+ /* 0 for packet-queue and 1 for data-queue */
+ uint8_t q_type;
+} Tmq;
+
+Tmq* TmqCreateQueue(char *name);
+Tmq* TmqGetQueueByName(char *name);
+
+void TmqDebugList(void);
+void TmqResetQueues(void);
+void TmValidateQueueState(void);
+
+#endif /* __TM_QUEUES_H__ */
+
diff --git a/framework/src/suricata/src/tm-threads-common.h b/framework/src/suricata/src/tm-threads-common.h
new file mode 100644
index 00000000..f6629eaa
--- /dev/null
+++ b/framework/src/suricata/src/tm-threads-common.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __TM_THREADS_COMMON_H__
+#define __TM_THREADS_COMMON_H__
+
+/** \brief Thread Model Module id's.
+ *
+ * \note anything added here should also be added to TmModuleTmmIdToString
+ * in tm-modules.c
+ */
+typedef enum {
+ TMM_DECODENFQ,
+ TMM_VERDICTNFQ,
+ TMM_RECEIVENFQ,
+ TMM_RECEIVEPCAP,
+ TMM_RECEIVEPCAPFILE,
+ TMM_DECODEPCAP,
+ TMM_DECODEPCAPFILE,
+ TMM_RECEIVEPFRING,
+ TMM_DECODEPFRING,
+ TMM_DETECT,
+ TMM_ALERTFASTLOG,
+ TMM_ALERTFASTLOG4,
+ TMM_ALERTFASTLOG6,
+ TMM_ALERTUNIFIED2ALERT,
+ TMM_ALERTPRELUDE,
+ TMM_ALERTDEBUGLOG,
+ TMM_ALERTSYSLOG,
+ TMM_LOGDROPLOG,
+ TMM_ALERTSYSLOG4,
+ TMM_ALERTSYSLOG6,
+ TMM_RESPONDREJECT,
+ TMM_LOGDNSLOG,
+ TMM_LOGHTTPLOG,
+ TMM_LOGHTTPLOG4,
+ TMM_LOGHTTPLOG6,
+ TMM_LOGTLSLOG,
+ TMM_LOGTLSLOG4,
+ TMM_LOGTLSLOG6,
+ TMM_LOGTCPDATALOG,
+ TMM_OUTPUTJSON,
+ TMM_PCAPLOG,
+ TMM_FILELOG,
+ TMM_FILESTORE,
+ TMM_STREAMTCP,
+ TMM_DECODEIPFW,
+ TMM_VERDICTIPFW,
+ TMM_RECEIVEIPFW,
+ TMM_RECEIVEERFFILE,
+ TMM_DECODEERFFILE,
+ TMM_RECEIVEERFDAG,
+ TMM_DECODEERFDAG,
+ TMM_RECEIVEAFP,
+ TMM_DECODEAFP,
+ TMM_RECEIVENETMAP,
+ TMM_DECODENETMAP,
+ TMM_ALERTPCAPINFO,
+ TMM_RECEIVEMPIPE,
+ TMM_DECODEMPIPE,
+ TMM_RECEIVENAPATECH,
+ TMM_DECODENAPATECH,
+ TMM_PACKETLOGGER,
+ TMM_TXLOGGER,
+ TMM_STATSLOGGER,
+ TMM_FILELOGGER,
+ TMM_FILEDATALOGGER,
+ TMM_STREAMINGLOGGER,
+ TMM_JSONALERTLOG,
+ TMM_JSONDROPLOG,
+ TMM_JSONHTTPLOG,
+ TMM_JSONDNSLOG,
+ TMM_JSONSMTPLOG,
+ TMM_JSONSSHLOG,
+ TMM_JSONSTATSLOG,
+ TMM_JSONTLSLOG,
+ TMM_JSONFILELOG,
+ TMM_RECEIVENFLOG,
+ TMM_DECODENFLOG,
+ TMM_JSONFLOWLOG,
+ TMM_JSONNETFLOWLOG,
+ TMM_LOGSTATSLOG,
+
+ TMM_FLOWMANAGER,
+ TMM_FLOWRECYCLER,
+ TMM_DETECTLOADER,
+
+ TMM_UNIXMANAGER,
+
+ TMM_LUALOG,
+ TMM_TLSSTORE,
+ TMM_SIZE,
+} TmmId;
+
+/*Error codes for the thread modules*/
+typedef enum {
+ TM_ECODE_OK = 0, /**< Thread module exits OK*/
+ TM_ECODE_FAILED, /**< Thread module exits due to failure*/
+ TM_ECODE_DONE, /**< Thread module task is finished*/
+} TmEcode;
+
+/* ThreadVars type */
+enum {
+ TVT_PPT,
+ TVT_MGMT,
+ TVT_CMD,
+ TVT_MAX,
+};
+
+#endif /* __TM_THREADS_COMMON_H__ */
+
diff --git a/framework/src/suricata/src/tm-threads.c b/framework/src/suricata/src/tm-threads.c
new file mode 100644
index 00000000..c6828b40
--- /dev/null
+++ b/framework/src/suricata/src/tm-threads.c
@@ -0,0 +1,2179 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Thread management functions.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "stream.h"
+#include "runmodes.h"
+#include "threadvars.h"
+#include "tm-queues.h"
+#include "tm-queuehandlers.h"
+#include "tm-threads.h"
+#include "tmqh-packetpool.h"
+#include "threads.h"
+#include "util-debug.h"
+#include "util-privs.h"
+#include "util-cpu.h"
+#include "util-optimize.h"
+#include "util-profiling.h"
+#include "util-signal.h"
+#include "queue.h"
+
+#ifdef PROFILE_LOCKING
+__thread uint64_t mutex_lock_contention;
+__thread uint64_t mutex_lock_wait_ticks;
+__thread uint64_t mutex_lock_cnt;
+
+__thread uint64_t spin_lock_contention;
+__thread uint64_t spin_lock_wait_ticks;
+__thread uint64_t spin_lock_cnt;
+
+__thread uint64_t rww_lock_contention;
+__thread uint64_t rww_lock_wait_ticks;
+__thread uint64_t rww_lock_cnt;
+
+__thread uint64_t rwr_lock_contention;
+__thread uint64_t rwr_lock_wait_ticks;
+__thread uint64_t rwr_lock_cnt;
+#endif
+
+#ifdef OS_FREEBSD
+#include <sched.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/cpuset.h>
+#include <sys/thr.h>
+#define cpu_set_t cpuset_t
+#endif /* OS_FREEBSD */
+
+/* prototypes */
+static int SetCPUAffinity(uint16_t cpu);
+
+/* root of the threadvars list */
+ThreadVars *tv_root[TVT_MAX] = { NULL };
+
+/* lock to protect tv_root */
+SCMutex tv_root_lock = SCMUTEX_INITIALIZER;
+
+/* Action On Failure(AOF). Determines how the engine should behave when a
+ * thread encounters a failure. Defaults to restart the failed thread */
+uint8_t tv_aof = THV_RESTART_THREAD;
+
+/**
+ * \brief Check if a thread flag is set.
+ *
+ * \retval 1 flag is set.
+ * \retval 0 flag is not set.
+ */
+int TmThreadsCheckFlag(ThreadVars *tv, uint16_t flag)
+{
+ return (SC_ATOMIC_GET(tv->flags) & flag) ? 1 : 0;
+}
+
+/**
+ * \brief Set a thread flag.
+ */
+void TmThreadsSetFlag(ThreadVars *tv, uint16_t flag)
+{
+ SC_ATOMIC_OR(tv->flags, flag);
+}
+
+/**
+ * \brief Unset a thread flag.
+ */
+void TmThreadsUnsetFlag(ThreadVars *tv, uint16_t flag)
+{
+ SC_ATOMIC_AND(tv->flags, ~flag);
+}
+
+/**
+ * \brief Separate run function so we can call it recursively.
+ *
+ * \todo Deal with post_pq for slots beyond the first.
+ */
+TmEcode TmThreadsSlotVarRun(ThreadVars *tv, Packet *p,
+ TmSlot *slot)
+{
+ TmEcode r;
+ TmSlot *s;
+ Packet *extra_p;
+
+ for (s = slot; s != NULL; s = s->slot_next) {
+ TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
+ PACKET_PROFILING_TMM_START(p, s->tm_id);
+
+ if (unlikely(s->id == 0)) {
+ r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq, &s->slot_post_pq);
+ } else {
+ r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq, NULL);
+ }
+
+ PACKET_PROFILING_TMM_END(p, s->tm_id);
+
+ /* handle error */
+ if (unlikely(r == TM_ECODE_FAILED)) {
+ /* Encountered error. Return packets to packetpool and return */
+ TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
+
+ SCMutexLock(&s->slot_post_pq.mutex_q);
+ TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
+ SCMutexUnlock(&s->slot_post_pq.mutex_q);
+
+ TmThreadsSetFlag(tv, THV_FAILED);
+ return TM_ECODE_FAILED;
+ }
+
+ /* handle new packets */
+ while (s->slot_pre_pq.top != NULL) {
+ extra_p = PacketDequeue(&s->slot_pre_pq);
+ if (unlikely(extra_p == NULL))
+ continue;
+
+ /* see if we need to process the packet */
+ if (s->slot_next != NULL) {
+ r = TmThreadsSlotVarRun(tv, extra_p, s->slot_next);
+ if (unlikely(r == TM_ECODE_FAILED)) {
+ TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
+
+ SCMutexLock(&s->slot_post_pq.mutex_q);
+ TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
+ SCMutexUnlock(&s->slot_post_pq.mutex_q);
+
+ TmqhOutputPacketpool(tv, extra_p);
+ TmThreadsSetFlag(tv, THV_FAILED);
+ return TM_ECODE_FAILED;
+ }
+ }
+ tv->tmqh_out(tv, extra_p);
+ }
+ }
+
+ return TM_ECODE_OK;
+}
+
+/** \internal
+ *
+ * \brief Process flow timeout packets
+ *
+ * Process flow timeout pseudo packets. During shutdown this loop
+ * is run until the flow engine kills the thread and the queue is
+ * empty.
+ */
+static int TmThreadTimeoutLoop(ThreadVars *tv, TmSlot *s)
+{
+ TmSlot *stream_slot = NULL, *slot = NULL;
+ int run = 1;
+ int r = TM_ECODE_OK;
+
+ for (slot = s; slot != NULL; slot = slot->slot_next) {
+ if (slot->tm_id == TMM_STREAMTCP) {
+ stream_slot = slot;
+ break;
+ }
+ }
+
+ if (tv->stream_pq == NULL || stream_slot == NULL)
+ return r;
+
+ SCLogDebug("flow end loop starting");
+ while(run) {
+ Packet *p;
+ if (tv->stream_pq->len != 0) {
+ SCMutexLock(&tv->stream_pq->mutex_q);
+ p = PacketDequeue(tv->stream_pq);
+ SCMutexUnlock(&tv->stream_pq->mutex_q);
+ BUG_ON(p == NULL);
+
+ if ((r = TmThreadsSlotProcessPkt(tv, stream_slot, p) != TM_ECODE_OK)) {
+ if (r == TM_ECODE_FAILED)
+ run = 0;
+ }
+ } else {
+ usleep(1);
+ }
+
+ if (tv->stream_pq->len == 0 && TmThreadsCheckFlag(tv, THV_KILL)) {
+ run = 0;
+ }
+ }
+ SCLogDebug("flow end loop complete");
+
+ return r;
+}
+
+/*
+
+ pcap/nfq
+
+ pkt read
+ callback
+ process_pkt
+
+ pfring
+
+ pkt read
+ process_pkt
+
+ slot:
+ setup
+
+ pkt_ack_loop(tv, slot_data)
+
+ deinit
+
+ process_pkt:
+ while(s)
+ run s;
+ queue;
+
+ */
+
+void *TmThreadsSlotPktAcqLoop(void *td)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ ThreadVars *tv = (ThreadVars *)td;
+ TmSlot *s = tv->tm_slots;
+ char run = 1;
+ TmEcode r = TM_ECODE_OK;
+ TmSlot *slot = NULL;
+
+ /* Set the thread name */
+ if (SCSetThreadName(tv->name) < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ if (tv->thread_setup_flags != 0)
+ TmThreadSetupOptions(tv);
+
+ /* Drop the capabilities for this thread */
+ SCDropCaps(tv);
+
+ PacketPoolInit();
+
+ /* check if we are setup properly */
+ if (s == NULL || s->PktAcqLoop == NULL || tv->tmqh_in == NULL || tv->tmqh_out == NULL) {
+ SCLogError(SC_ERR_FATAL, "TmSlot or ThreadVars badly setup: s=%p,"
+ " PktAcqLoop=%p, tmqh_in=%p,"
+ " tmqh_out=%p",
+ s, s ? s->PktAcqLoop : NULL, tv->tmqh_in, tv->tmqh_out);
+ EngineKill();
+
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
+ pthread_exit((void *) -1);
+ return NULL;
+ }
+
+ for (slot = s; slot != NULL; slot = slot->slot_next) {
+ if (slot->SlotThreadInit != NULL) {
+ void *slot_data = NULL;
+ r = slot->SlotThreadInit(tv, slot->slot_initdata, &slot_data);
+ if (r != TM_ECODE_OK) {
+ if (r == TM_ECODE_DONE) {
+ EngineDone();
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_INIT_DONE | THV_RUNNING_DONE);
+ goto error;
+ } else {
+ EngineKill();
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
+ goto error;
+ }
+ }
+ (void)SC_ATOMIC_SET(slot->slot_data, slot_data);
+ }
+ memset(&slot->slot_pre_pq, 0, sizeof(PacketQueue));
+ SCMutexInit(&slot->slot_pre_pq.mutex_q, NULL);
+ memset(&slot->slot_post_pq, 0, sizeof(PacketQueue));
+ SCMutexInit(&slot->slot_post_pq.mutex_q, NULL);
+
+ /* get the 'pre qeueue' from module before the stream module */
+ if (slot->slot_next != NULL && slot->slot_next->tm_id == TMM_STREAMTCP) {
+ SCLogDebug("pre-stream packetqueue %p (postq)", &s->slot_post_pq);
+ tv->stream_pq = &slot->slot_post_pq;
+ /* if the stream module is the first, get the threads input queue */
+ } else if (slot == (TmSlot *)tv->tm_slots && slot->tm_id == TMM_STREAMTCP) {
+ tv->stream_pq = &trans_q[tv->inq->id];
+ SCLogDebug("pre-stream packetqueue %p (inq)", &slot->slot_pre_pq);
+ }
+ }
+
+ StatsSetupPrivate(tv);
+
+ TmThreadsSetFlag(tv, THV_INIT_DONE);
+
+ while(run) {
+ if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
+ TmThreadsSetFlag(tv, THV_PAUSED);
+ TmThreadTestThreadUnPaused(tv);
+ TmThreadsUnsetFlag(tv, THV_PAUSED);
+ }
+
+ r = s->PktAcqLoop(tv, SC_ATOMIC_GET(s->slot_data), s);
+
+ if (r == TM_ECODE_FAILED || TmThreadsCheckFlag(tv, THV_KILL_PKTACQ)
+ || suricata_ctl_flags) {
+ run = 0;
+ }
+ if (r == TM_ECODE_DONE) {
+ run = 0;
+ }
+ }
+ StatsSyncCounters(tv);
+
+ TmThreadsSetFlag(tv, THV_FLOW_LOOP);
+
+ /* process all pseudo packets the flow timeout may throw at us */
+ TmThreadTimeoutLoop(tv, s);
+
+ TmThreadsSetFlag(tv, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv, THV_DEINIT);
+
+ PacketPoolDestroy();
+
+ for (slot = s; slot != NULL; slot = slot->slot_next) {
+ if (slot->SlotThreadExitPrintStats != NULL) {
+ slot->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(slot->slot_data));
+ }
+
+ if (slot->SlotThreadDeinit != NULL) {
+ r = slot->SlotThreadDeinit(tv, SC_ATOMIC_GET(slot->slot_data));
+ if (r != TM_ECODE_OK) {
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ goto error;
+ }
+ }
+
+ BUG_ON(slot->slot_pre_pq.len);
+ BUG_ON(slot->slot_post_pq.len);
+ }
+
+ tv->stream_pq = NULL;
+ SCLogDebug("%s ending", tv->name);
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ pthread_exit((void *) 0);
+ return NULL;
+
+error:
+ tv->stream_pq = NULL;
+ pthread_exit((void *) -1);
+ return NULL;
+}
+
+
+/**
+ * \todo Only the first "slot" currently makes the "post_pq" available
+ * to the thread module.
+ */
+void *TmThreadsSlotVar(void *td)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ ThreadVars *tv = (ThreadVars *)td;
+ TmSlot *s = (TmSlot *)tv->tm_slots;
+ Packet *p = NULL;
+ char run = 1;
+ TmEcode r = TM_ECODE_OK;
+
+ PacketPoolInitEmpty();
+
+ /* Set the thread name */
+ if (SCSetThreadName(tv->name) < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ if (tv->thread_setup_flags != 0)
+ TmThreadSetupOptions(tv);
+
+ /* Drop the capabilities for this thread */
+ SCDropCaps(tv);
+
+ /* check if we are setup properly */
+ if (s == NULL || tv->tmqh_in == NULL || tv->tmqh_out == NULL) {
+ EngineKill();
+
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
+ pthread_exit((void *) -1);
+ return NULL;
+ }
+
+ for (; s != NULL; s = s->slot_next) {
+ if (s->SlotThreadInit != NULL) {
+ void *slot_data = NULL;
+ r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
+ if (r != TM_ECODE_OK) {
+ EngineKill();
+
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
+ goto error;
+ }
+ (void)SC_ATOMIC_SET(s->slot_data, slot_data);
+ }
+ memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
+ SCMutexInit(&s->slot_pre_pq.mutex_q, NULL);
+ memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
+ SCMutexInit(&s->slot_post_pq.mutex_q, NULL);
+
+ /* special case: we need to access the stream queue
+ * from the flow timeout code */
+
+ /* get the 'pre qeueue' from module before the stream module */
+ if (s->slot_next != NULL && s->slot_next->tm_id == TMM_STREAMTCP) {
+ SCLogDebug("pre-stream packetqueue %p (preq)", &s->slot_pre_pq);
+ tv->stream_pq = &s->slot_pre_pq;
+ /* if the stream module is the first, get the threads input queue */
+ } else if (s == (TmSlot *)tv->tm_slots && s->tm_id == TMM_STREAMTCP) {
+ tv->stream_pq = &trans_q[tv->inq->id];
+ SCLogDebug("pre-stream packetqueue %p (inq)", &s->slot_pre_pq);
+ }
+ }
+
+ StatsSetupPrivate(tv);
+
+ TmThreadsSetFlag(tv, THV_INIT_DONE);
+
+ s = (TmSlot *)tv->tm_slots;
+
+ while (run) {
+ if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
+ TmThreadsSetFlag(tv, THV_PAUSED);
+ TmThreadTestThreadUnPaused(tv);
+ TmThreadsUnsetFlag(tv, THV_PAUSED);
+ }
+
+ /* input a packet */
+ p = tv->tmqh_in(tv);
+
+ if (p != NULL) {
+ /* run the thread module(s) */
+ r = TmThreadsSlotVarRun(tv, p, s);
+ if (r == TM_ECODE_FAILED) {
+ TmqhOutputPacketpool(tv, p);
+ TmThreadsSetFlag(tv, THV_FAILED);
+ break;
+ }
+
+ /* output the packet */
+ tv->tmqh_out(tv, p);
+
+ } /* if (p != NULL) */
+
+ /* now handle the post_pq packets */
+ TmSlot *slot;
+ for (slot = s; slot != NULL; slot = slot->slot_next) {
+ if (slot->slot_post_pq.top != NULL) {
+ while (1) {
+ SCMutexLock(&slot->slot_post_pq.mutex_q);
+ Packet *extra_p = PacketDequeue(&slot->slot_post_pq);
+ SCMutexUnlock(&slot->slot_post_pq.mutex_q);
+
+ if (extra_p == NULL)
+ break;
+
+ if (slot->slot_next != NULL) {
+ r = TmThreadsSlotVarRun(tv, extra_p, slot->slot_next);
+ if (r == TM_ECODE_FAILED) {
+ SCMutexLock(&slot->slot_post_pq.mutex_q);
+ TmqhReleasePacketsToPacketPool(&slot->slot_post_pq);
+ SCMutexUnlock(&slot->slot_post_pq.mutex_q);
+
+ TmqhOutputPacketpool(tv, extra_p);
+ TmThreadsSetFlag(tv, THV_FAILED);
+ break;
+ }
+ }
+ /* output the packet */
+ tv->tmqh_out(tv, extra_p);
+ } /* while */
+ } /* if */
+ } /* for */
+
+ if (TmThreadsCheckFlag(tv, THV_KILL)) {
+ run = 0;
+ }
+ } /* while (run) */
+ StatsSyncCounters(tv);
+
+ TmThreadsSetFlag(tv, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv, THV_DEINIT);
+
+ PacketPoolDestroy();
+
+ s = (TmSlot *)tv->tm_slots;
+
+ for ( ; s != NULL; s = s->slot_next) {
+ if (s->SlotThreadExitPrintStats != NULL) {
+ s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
+ }
+
+ if (s->SlotThreadDeinit != NULL) {
+ r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
+ if (r != TM_ECODE_OK) {
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ goto error;
+ }
+ }
+ BUG_ON(s->slot_pre_pq.len);
+ BUG_ON(s->slot_post_pq.len);
+ }
+
+ SCLogDebug("%s ending", tv->name);
+ tv->stream_pq = NULL;
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ pthread_exit((void *) 0);
+ return NULL;
+
+error:
+ tv->stream_pq = NULL;
+ pthread_exit((void *) -1);
+ return NULL;
+}
+
+static void *TmThreadsManagement(void *td)
+{
+ /* block usr2. usr2 to be handled by the main thread only */
+ UtilSignalBlock(SIGUSR2);
+
+ ThreadVars *tv = (ThreadVars *)td;
+ TmSlot *s = (TmSlot *)tv->tm_slots;
+ TmEcode r = TM_ECODE_OK;
+
+ BUG_ON(s == NULL);
+
+ /* Set the thread name */
+ if (SCSetThreadName(tv->name) < 0) {
+ SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
+ }
+
+ if (tv->thread_setup_flags != 0)
+ TmThreadSetupOptions(tv);
+
+ /* Drop the capabilities for this thread */
+ SCDropCaps(tv);
+
+ SCLogDebug("%s starting", tv->name);
+
+ if (s->SlotThreadInit != NULL) {
+ void *slot_data = NULL;
+ r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
+ if (r != TM_ECODE_OK) {
+ EngineKill();
+
+ TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
+ pthread_exit((void *) -1);
+ return NULL;
+ }
+ (void)SC_ATOMIC_SET(s->slot_data, slot_data);
+ }
+ memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
+ memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
+
+ StatsSetupPrivate(tv);
+
+ TmThreadsSetFlag(tv, THV_INIT_DONE);
+
+ r = s->Management(tv, SC_ATOMIC_GET(s->slot_data));
+ /* handle error */
+ if (r == TM_ECODE_FAILED) {
+ TmThreadsSetFlag(tv, THV_FAILED);
+ }
+
+ if (TmThreadsCheckFlag(tv, THV_KILL)) {
+ StatsSyncCounters(tv);
+ }
+
+ TmThreadsSetFlag(tv, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv, THV_DEINIT);
+
+ if (s->SlotThreadExitPrintStats != NULL) {
+ s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
+ }
+
+ if (s->SlotThreadDeinit != NULL) {
+ r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
+ if (r != TM_ECODE_OK) {
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ pthread_exit((void *) -1);
+ return NULL;
+ }
+ }
+
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ pthread_exit((void *) 0);
+ return NULL;
+}
+
+/**
+ * \brief We set the slot functions.
+ *
+ * \param tv Pointer to the TV to set the slot function for.
+ * \param name Name of the slot variant.
+ * \param fn_p Pointer to a custom slot function. Used only if slot variant
+ * "name" is "custom".
+ *
+ * \retval TmEcode TM_ECODE_OK on success; TM_ECODE_FAILED on failure.
+ */
+TmEcode TmThreadSetSlots(ThreadVars *tv, char *name, void *(*fn_p)(void *))
+{
+ if (name == NULL) {
+ if (fn_p == NULL) {
+ printf("Both slot name and function pointer can't be NULL inside "
+ "TmThreadSetSlots\n");
+ goto error;
+ } else {
+ name = "custom";
+ }
+ }
+
+ if (strcmp(name, "varslot") == 0) {
+ tv->tm_func = TmThreadsSlotVar;
+ } else if (strcmp(name, "pktacqloop") == 0) {
+ tv->tm_func = TmThreadsSlotPktAcqLoop;
+ } else if (strcmp(name, "management") == 0) {
+ tv->tm_func = TmThreadsManagement;
+ } else if (strcmp(name, "command") == 0) {
+ tv->tm_func = TmThreadsManagement;
+ } else if (strcmp(name, "custom") == 0) {
+ if (fn_p == NULL)
+ goto error;
+ tv->tm_func = fn_p;
+ } else {
+ printf("Error: Slot \"%s\" not supported\n", name);
+ goto error;
+ }
+
+ return TM_ECODE_OK;
+
+error:
+ return TM_ECODE_FAILED;
+}
+
+ThreadVars *TmThreadsGetTVContainingSlot(TmSlot *tm_slot)
+{
+ ThreadVars *tv;
+ int i;
+
+ SCMutexLock(&tv_root_lock);
+
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+
+ while (tv) {
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ if (slots == tm_slot) {
+ SCMutexUnlock(&tv_root_lock);
+ return tv;
+ }
+ slots = slots->slot_next;
+ }
+ tv = tv->next;
+ }
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return NULL;
+}
+
+/**
+ * \brief Appends a new entry to the slots.
+ *
+ * \param tv TV the slot is attached to.
+ * \param tm TM to append.
+ * \param data Data to be passed on to the slot init function.
+ *
+ * \retval The allocated TmSlot or NULL if there is an error
+ */
+static inline TmSlot * _TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data)
+{
+ TmSlot *slot = SCMalloc(sizeof(TmSlot));
+ if (unlikely(slot == NULL))
+ return NULL;
+ memset(slot, 0, sizeof(TmSlot));
+ SC_ATOMIC_INIT(slot->slot_data);
+ slot->tv = tv;
+ slot->SlotThreadInit = tm->ThreadInit;
+ slot->slot_initdata = data;
+ SC_ATOMIC_INIT(slot->SlotFunc);
+ (void)SC_ATOMIC_SET(slot->SlotFunc, tm->Func);
+ slot->PktAcqLoop = tm->PktAcqLoop;
+ slot->Management = tm->Management;
+ slot->SlotThreadExitPrintStats = tm->ThreadExitPrintStats;
+ slot->SlotThreadDeinit = tm->ThreadDeinit;
+ /* we don't have to check for the return value "-1". We wouldn't have
+ * received a TM as arg, if it didn't exist */
+ slot->tm_id = TmModuleGetIDForTM(tm);
+
+ tv->cap_flags |= tm->cap_flags;
+
+ if (tv->tm_slots == NULL) {
+ tv->tm_slots = slot;
+ slot->id = 0;
+ } else {
+ TmSlot *a = (TmSlot *)tv->tm_slots, *b = NULL;
+
+ /* get the last slot */
+ for ( ; a != NULL; a = a->slot_next) {
+ b = a;
+ }
+ /* append the new slot */
+ if (b != NULL) {
+ b->slot_next = slot;
+ slot->id = b->id + 1;
+ }
+ }
+
+ return slot;
+}
+
+void TmSlotFree(TmSlot *tms)
+{
+ SC_ATOMIC_DESTROY(tms->slot_data);
+ SCFree(tms);
+}
+
+/**
+ * \brief Appends a new entry to the slots.
+ *
+ * \param tv TV the slot is attached to.
+ * \param tm TM to append.
+ * \param data Data to be passed on to the slot init function.
+ */
+void TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data)
+{
+ _TmSlotSetFuncAppend(tv, tm, data);
+}
+
+/**
+ * \brief Returns the slot holding a TM with the particular tm_id.
+ *
+ * \param tm_id TM id of the TM whose slot has to be returned.
+ *
+ * \retval slots Pointer to the slot.
+ */
+TmSlot *TmSlotGetSlotForTM(int tm_id)
+{
+ ThreadVars *tv = NULL;
+ TmSlot *slots;
+ int i;
+
+ SCMutexLock(&tv_root_lock);
+
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv) {
+ slots = tv->tm_slots;
+ while (slots != NULL) {
+ if (slots->tm_id == tm_id) {
+ SCMutexUnlock(&tv_root_lock);
+ return slots;
+ }
+ slots = slots->slot_next;
+ }
+ tv = tv->next;
+ }
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return NULL;
+}
+
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+static int SetCPUAffinitySet(cpu_set_t *cs)
+{
+#if defined OS_FREEBSD
+ int r = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ SCGetThreadIdLong(), sizeof(cpu_set_t),cs);
+#elif OS_DARWIN
+ int r = thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
+ (void*)cs, THREAD_AFFINITY_POLICY_COUNT);
+#else
+ pid_t tid = syscall(SYS_gettid);
+ int r = sched_setaffinity(tid, sizeof(cpu_set_t), cs);
+#endif /* OS_FREEBSD */
+
+ if (r != 0) {
+ printf("Warning: sched_setaffinity failed (%" PRId32 "): %s\n", r,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+
+/**
+ * \brief Set the thread affinity on the calling thread.
+ *
+ * \param cpuid Id of the core/cpu to setup the affinity.
+ *
+ * \retval 0 If all goes well; -1 if something is wrong.
+ */
+static int SetCPUAffinity(uint16_t cpuid)
+{
+#if defined __OpenBSD__
+ return 0;
+#else
+ int cpu = (int)cpuid;
+
+#if defined OS_WIN32 || defined __CYGWIN__
+ DWORD cs = 1 << cpu;
+
+ int r = (0 == SetThreadAffinityMask(GetCurrentThread(), cs));
+ if (r != 0) {
+ printf("Warning: sched_setaffinity failed (%" PRId32 "): %s\n", r,
+ strerror(errno));
+ return -1;
+ }
+ SCLogDebug("CPU Affinity for thread %lu set to CPU %" PRId32,
+ SCGetThreadIdLong(), cpu);
+
+ return 0;
+
+#else
+ cpu_set_t cs;
+
+ CPU_ZERO(&cs);
+ CPU_SET(cpu, &cs);
+ return SetCPUAffinitySet(&cs);
+#endif /* windows */
+#endif /* not supported */
+}
+
+
+/**
+ * \brief Set the thread options (thread priority).
+ *
+ * \param tv Pointer to the ThreadVars to setup the thread priority.
+ *
+ * \retval TM_ECODE_OK.
+ */
+TmEcode TmThreadSetThreadPriority(ThreadVars *tv, int prio)
+{
+ tv->thread_setup_flags |= THREAD_SET_PRIORITY;
+ tv->thread_priority = prio;
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Adjusting nice value for threads.
+ */
+void TmThreadSetPrio(ThreadVars *tv)
+{
+ SCEnter();
+#ifndef __CYGWIN__
+#ifdef OS_WIN32
+ if (0 == SetThreadPriority(GetCurrentThread(), tv->thread_priority)) {
+ SCLogError(SC_ERR_THREAD_NICE_PRIO, "Error setting priority for "
+ "thread %s: %s", tv->name, strerror(errno));
+ } else {
+ SCLogDebug("Priority set to %"PRId32" for thread %s",
+ tv->thread_priority, tv->name);
+ }
+#else
+ int ret = nice(tv->thread_priority);
+ if (ret == -1) {
+ SCLogError(SC_ERR_THREAD_NICE_PRIO, "Error setting nice value "
+ "for thread %s: %s", tv->name, strerror(errno));
+ } else {
+ SCLogDebug("Nice value set to %"PRId32" for thread %s",
+ tv->thread_priority, tv->name);
+ }
+#endif /* OS_WIN32 */
+#endif
+ SCReturn;
+}
+
+
+/**
+ * \brief Set the thread options (cpu affinity).
+ *
+ * \param tv pointer to the ThreadVars to setup the affinity.
+ * \param cpu cpu on which affinity is set.
+ *
+ * \retval TM_ECODE_OK
+ */
+TmEcode TmThreadSetCPUAffinity(ThreadVars *tv, uint16_t cpu)
+{
+ tv->thread_setup_flags |= THREAD_SET_AFFINITY;
+ tv->cpu_affinity = cpu;
+
+ return TM_ECODE_OK;
+}
+
+
+TmEcode TmThreadSetCPU(ThreadVars *tv, uint8_t type)
+{
+ if (!threading_set_cpu_affinity)
+ return TM_ECODE_OK;
+
+ if (type > MAX_CPU_SET) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid cpu type family");
+ return TM_ECODE_FAILED;
+ }
+
+ tv->thread_setup_flags |= THREAD_SET_AFFTYPE;
+ tv->cpu_affinity = type;
+
+ return TM_ECODE_OK;
+}
+
+int TmThreadGetNbThreads(uint8_t type)
+{
+ if (type >= MAX_CPU_SET) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid cpu type family");
+ return 0;
+ }
+
+ return thread_affinity[type].nb_threads;
+}
+
+/**
+ * \brief Set the thread options (cpu affinitythread).
+ * Priority should be already set by pthread_create.
+ *
+ * \param tv pointer to the ThreadVars of the calling thread.
+ */
+TmEcode TmThreadSetupOptions(ThreadVars *tv)
+{
+ if (tv->thread_setup_flags & THREAD_SET_AFFINITY) {
+ SCLogInfo("Setting affinity for \"%s\" Module to cpu/core "
+ "%"PRIu16", thread id %lu", tv->name, tv->cpu_affinity,
+ SCGetThreadIdLong());
+ SetCPUAffinity(tv->cpu_affinity);
+ }
+
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+ if (tv->thread_setup_flags & THREAD_SET_PRIORITY)
+ TmThreadSetPrio(tv);
+ if (tv->thread_setup_flags & THREAD_SET_AFFTYPE) {
+ ThreadsAffinityType *taf = &thread_affinity[tv->cpu_affinity];
+ if (taf->mode_flag == EXCLUSIVE_AFFINITY) {
+ int cpu = AffinityGetNextCPU(taf);
+ SetCPUAffinity(cpu);
+ /* If CPU is in a set overwrite the default thread prio */
+ if (CPU_ISSET(cpu, &taf->lowprio_cpu)) {
+ tv->thread_priority = PRIO_LOW;
+ } else if (CPU_ISSET(cpu, &taf->medprio_cpu)) {
+ tv->thread_priority = PRIO_MEDIUM;
+ } else if (CPU_ISSET(cpu, &taf->hiprio_cpu)) {
+ tv->thread_priority = PRIO_HIGH;
+ } else {
+ tv->thread_priority = taf->prio;
+ }
+ SCLogInfo("Setting prio %d for \"%s\" Module to cpu/core "
+ "%d, thread id %lu", tv->thread_priority,
+ tv->name, cpu, SCGetThreadIdLong());
+ } else {
+ SetCPUAffinitySet(&taf->cpu_set);
+ tv->thread_priority = taf->prio;
+ SCLogInfo("Setting prio %d for \"%s\" thread "
+ ", thread id %lu", tv->thread_priority,
+ tv->name, SCGetThreadIdLong());
+ }
+ TmThreadSetPrio(tv);
+ }
+#endif
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Creates and returns the TV instance for a new thread.
+ *
+ * \param name Name of this TV instance
+ * \param inq_name Incoming queue name
+ * \param inqh_name Incoming queue handler name as set by TmqhSetup()
+ * \param outq_name Outgoing queue name
+ * \param outqh_name Outgoing queue handler as set by TmqhSetup()
+ * \param slots String representation for the slot function to be used
+ * \param fn_p Pointer to function when \"slots\" is of type \"custom\"
+ * \param mucond Flag to indicate whether to initialize the condition
+ * and the mutex variables for this newly created TV.
+ *
+ * \retval the newly created TV instance, or NULL on error
+ */
+ThreadVars *TmThreadCreate(char *name, char *inq_name, char *inqh_name,
+ char *outq_name, char *outqh_name, char *slots,
+ void * (*fn_p)(void *), int mucond)
+{
+ ThreadVars *tv = NULL;
+ Tmq *tmq = NULL;
+ Tmqh *tmqh = NULL;
+
+ SCLogDebug("creating thread \"%s\"...", name);
+
+ /* XXX create separate function for this: allocate a thread container */
+ tv = SCMalloc(sizeof(ThreadVars));
+ if (unlikely(tv == NULL))
+ goto error;
+ memset(tv, 0, sizeof(ThreadVars));
+
+ SC_ATOMIC_INIT(tv->flags);
+ SCMutexInit(&tv->perf_public_ctx.m, NULL);
+
+ tv->name = name;
+ /* default state for every newly created thread */
+ TmThreadsSetFlag(tv, THV_PAUSE);
+ TmThreadsSetFlag(tv, THV_USE);
+ /* default aof for every newly created thread */
+ tv->aof = THV_RESTART_THREAD;
+
+ /* set the incoming queue */
+ if (inq_name != NULL && strcmp(inq_name, "packetpool") != 0) {
+ SCLogDebug("inq_name \"%s\"", inq_name);
+
+ tmq = TmqGetQueueByName(inq_name);
+ if (tmq == NULL) {
+ tmq = TmqCreateQueue(inq_name);
+ if (tmq == NULL)
+ goto error;
+ }
+ SCLogDebug("tmq %p", tmq);
+
+ tv->inq = tmq;
+ tv->inq->reader_cnt++;
+ SCLogDebug("tv->inq %p", tv->inq);
+ }
+ if (inqh_name != NULL) {
+ SCLogDebug("inqh_name \"%s\"", inqh_name);
+
+ tmqh = TmqhGetQueueHandlerByName(inqh_name);
+ if (tmqh == NULL)
+ goto error;
+
+ tv->tmqh_in = tmqh->InHandler;
+ tv->InShutdownHandler = tmqh->InShutdownHandler;
+ SCLogDebug("tv->tmqh_in %p", tv->tmqh_in);
+ }
+
+ /* set the outgoing queue */
+ if (outqh_name != NULL) {
+ SCLogDebug("outqh_name \"%s\"", outqh_name);
+
+ tmqh = TmqhGetQueueHandlerByName(outqh_name);
+ if (tmqh == NULL)
+ goto error;
+
+ tv->tmqh_out = tmqh->OutHandler;
+ tv->outqh_name = tmqh->name;
+
+ if (outq_name != NULL && strcmp(outq_name, "packetpool") != 0) {
+ SCLogDebug("outq_name \"%s\"", outq_name);
+
+ if (tmqh->OutHandlerCtxSetup != NULL) {
+ tv->outctx = tmqh->OutHandlerCtxSetup(outq_name);
+ if (tv->outctx == NULL)
+ goto error;
+ tv->outq = NULL;
+ } else {
+ tmq = TmqGetQueueByName(outq_name);
+ if (tmq == NULL) {
+ tmq = TmqCreateQueue(outq_name);
+ if (tmq == NULL)
+ goto error;
+ }
+ SCLogDebug("tmq %p", tmq);
+
+ tv->outq = tmq;
+ tv->outctx = NULL;
+ tv->outq->writer_cnt++;
+ }
+ }
+ }
+
+ if (TmThreadSetSlots(tv, slots, fn_p) != TM_ECODE_OK) {
+ goto error;
+ }
+
+ if (mucond != 0)
+ TmThreadInitMC(tv);
+
+ return tv;
+
+error:
+ SCLogError(SC_ERR_THREAD_CREATE, "failed to setup a thread");
+
+ if (tv != NULL)
+ SCFree(tv);
+ return NULL;
+}
+
+/**
+ * \brief Creates and returns a TV instance for a Packet Processing Thread.
+ * This function doesn't support custom slots, and hence shouldn't be
+ * supplied \"custom\" as its slot type. All PPT threads are created
+ * with a mucond(see TmThreadCreate declaration) of 0. Hence the tv
+ * conditional variables are not used to kill the thread.
+ *
+ * \param name Name of this TV instance
+ * \param inq_name Incoming queue name
+ * \param inqh_name Incoming queue handler name as set by TmqhSetup()
+ * \param outq_name Outgoing queue name
+ * \param outqh_name Outgoing queue handler as set by TmqhSetup()
+ * \param slots String representation for the slot function to be used
+ *
+ * \retval the newly created TV instance, or NULL on error
+ */
+ThreadVars *TmThreadCreatePacketHandler(char *name, char *inq_name,
+ char *inqh_name, char *outq_name,
+ char *outqh_name, char *slots)
+{
+ ThreadVars *tv = NULL;
+
+ tv = TmThreadCreate(name, inq_name, inqh_name, outq_name, outqh_name,
+ slots, NULL, 0);
+
+ if (tv != NULL) {
+ tv->type = TVT_PPT;
+ tv->id = TmThreadsRegisterThread(tv, tv->type);
+ }
+
+
+ return tv;
+}
+
+/**
+ * \brief Creates and returns the TV instance for a Management thread(MGMT).
+ * This function supports only custom slot functions and hence a
+ * function pointer should be sent as an argument.
+ *
+ * \param name Name of this TV instance
+ * \param fn_p Pointer to function when \"slots\" is of type \"custom\"
+ * \param mucond Flag to indicate whether to initialize the condition
+ * and the mutex variables for this newly created TV.
+ *
+ * \retval the newly created TV instance, or NULL on error
+ */
+ThreadVars *TmThreadCreateMgmtThread(char *name, void *(fn_p)(void *),
+ int mucond)
+{
+ ThreadVars *tv = NULL;
+
+ tv = TmThreadCreate(name, NULL, NULL, NULL, NULL, "custom", fn_p, mucond);
+
+ if (tv != NULL) {
+ tv->type = TVT_MGMT;
+ tv->id = TmThreadsRegisterThread(tv, tv->type);
+ TmThreadSetCPU(tv, MANAGEMENT_CPU_SET);
+ }
+
+ return tv;
+}
+
+/**
+ * \brief Creates and returns the TV instance for a Management thread(MGMT).
+ * This function supports only custom slot functions and hence a
+ * function pointer should be sent as an argument.
+ *
+ * \param name Name of this TV instance
+ * \param module Name of TmModule with MANAGEMENT flag set.
+ * \param mucond Flag to indicate whether to initialize the condition
+ * and the mutex variables for this newly created TV.
+ *
+ * \retval the newly created TV instance, or NULL on error
+ */
+ThreadVars *TmThreadCreateMgmtThreadByName(char *name, char *module,
+ int mucond)
+{
+ ThreadVars *tv = NULL;
+
+ tv = TmThreadCreate(name, NULL, NULL, NULL, NULL, "management", NULL, mucond);
+
+ if (tv != NULL) {
+ tv->type = TVT_MGMT;
+ tv->id = TmThreadsRegisterThread(tv, tv->type);
+ TmThreadSetCPU(tv, MANAGEMENT_CPU_SET);
+
+ TmModule *m = TmModuleGetByName(module);
+ if (m) {
+ TmSlotSetFuncAppend(tv, m, NULL);
+ }
+ }
+
+ return tv;
+}
+
+/**
+ * \brief Creates and returns the TV instance for a Command thread (CMD).
+ * This function supports only custom slot functions and hence a
+ * function pointer should be sent as an argument.
+ *
+ * \param name Name of this TV instance
+ * \param module Name of TmModule with COMMAND flag set.
+ * \param mucond Flag to indicate whether to initialize the condition
+ * and the mutex variables for this newly created TV.
+ *
+ * \retval the newly created TV instance, or NULL on error
+ */
+ThreadVars *TmThreadCreateCmdThreadByName(char *name, char *module,
+ int mucond)
+{
+ ThreadVars *tv = NULL;
+
+ tv = TmThreadCreate(name, NULL, NULL, NULL, NULL, "command", NULL, mucond);
+
+ if (tv != NULL) {
+ tv->type = TVT_CMD;
+ tv->id = TmThreadsRegisterThread(tv, tv->type);
+ TmThreadSetCPU(tv, MANAGEMENT_CPU_SET);
+
+ TmModule *m = TmModuleGetByName(module);
+ if (m) {
+ TmSlotSetFuncAppend(tv, m, NULL);
+ }
+ }
+
+ return tv;
+}
+
+/**
+ * \brief Appends this TV to tv_root based on its type
+ *
+ * \param type holds the type this TV belongs to.
+ */
+void TmThreadAppend(ThreadVars *tv, int type)
+{
+ SCMutexLock(&tv_root_lock);
+
+ if (tv_root[type] == NULL) {
+ tv_root[type] = tv;
+ tv->next = NULL;
+ tv->prev = NULL;
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+ }
+
+ ThreadVars *t = tv_root[type];
+
+ while (t) {
+ if (t->next == NULL) {
+ t->next = tv;
+ tv->prev = t;
+ tv->next = NULL;
+ break;
+ }
+
+ t = t->next;
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Removes this TV from tv_root based on its type
+ *
+ * \param tv The tv instance to remove from the global tv list.
+ * \param type Holds the type this TV belongs to.
+ */
+void TmThreadRemove(ThreadVars *tv, int type)
+{
+ SCMutexLock(&tv_root_lock);
+
+ if (tv_root[type] == NULL) {
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+ }
+
+ ThreadVars *t = tv_root[type];
+ while (t != tv) {
+ t = t->next;
+ }
+
+ if (t != NULL) {
+ if (t->prev != NULL)
+ t->prev->next = t->next;
+ if (t->next != NULL)
+ t->next->prev = t->prev;
+
+ if (t == tv_root[type])
+ tv_root[type] = t->next;;
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Kill a thread.
+ *
+ * \param tv A ThreadVars instance corresponding to the thread that has to be
+ * killed.
+ */
+void TmThreadKillThread(ThreadVars *tv)
+{
+ int i = 0;
+
+ if (tv == NULL)
+ return;
+
+ if (tv->inq != NULL) {
+ /* we wait till we dry out all the inq packets, before we
+ * kill this thread. Do note that you should have disabled
+ * packet acquire by now using TmThreadDisableReceiveThreads()*/
+ if (!(strlen(tv->inq->name) == strlen("packetpool") &&
+ strcasecmp(tv->inq->name, "packetpool") == 0)) {
+ PacketQueue *q = &trans_q[tv->inq->id];
+ while (q->len != 0) {
+ usleep(1000);
+ }
+ }
+ }
+
+ /* set the thread flag informing the thread that it needs to be
+ * terminated */
+ TmThreadsSetFlag(tv, THV_KILL);
+ TmThreadsSetFlag(tv, THV_DEINIT);
+
+ /* to be sure, signal more */
+ int cnt = 0;
+ while (1) {
+ if (TmThreadsCheckFlag(tv, THV_CLOSED)) {
+ SCLogDebug("signalled the thread %" PRId32 " times", cnt);
+ break;
+ }
+
+ cnt++;
+
+ if (tv->InShutdownHandler != NULL) {
+ tv->InShutdownHandler(tv);
+ }
+ if (tv->inq != NULL) {
+ for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++) {
+ if (tv->inq->q_type == 0)
+ SCCondSignal(&trans_q[tv->inq->id].cond_q);
+ else
+ SCCondSignal(&data_queues[tv->inq->id].cond_q);
+ }
+ SCLogDebug("signalled tv->inq->id %" PRIu32 "", tv->inq->id);
+ }
+
+ if (tv->ctrl_cond != NULL ) {
+ pthread_cond_broadcast(tv->ctrl_cond);
+ }
+
+ usleep(100);
+ }
+
+ if (tv->outctx != NULL) {
+ Tmqh *tmqh = TmqhGetQueueHandlerByName(tv->outqh_name);
+ if (tmqh == NULL)
+ BUG_ON(1);
+
+ if (tmqh->OutHandlerCtxFree != NULL) {
+ tmqh->OutHandlerCtxFree(tv->outctx);
+ }
+ }
+
+ /* join it */
+ pthread_join(tv->t, NULL);
+ SCLogDebug("thread %s stopped", tv->name);
+
+ return;
+}
+
+/**
+ * \brief Disable all threads having the specified TMs.
+ *
+ * Breaks out of the packet acquisition loop, and bumps
+ * into the 'flow loop', where it will process packets
+ * from the flow engine's shutdown handling.
+ */
+void TmThreadDisableReceiveThreads(void)
+{
+ /* value in seconds */
+#define THREAD_KILL_MAX_WAIT_TIME 60
+ /* value in microseconds */
+#define WAIT_TIME 100
+
+ double total_wait_time = 0;
+
+ ThreadVars *tv = NULL;
+
+again:
+ SCMutexLock(&tv_root_lock);
+
+ /* all receive threads are part of packet processing threads */
+ tv = tv_root[TVT_PPT];
+
+ /* we do have to keep in mind that TVs are arranged in the order
+ * right from receive to log. The moment we fail to find a
+ * receive TM amongst the slots in a tv, it indicates we are done
+ * with all receive threads */
+ while (tv) {
+ int disable = 0;
+ /* obtain the slots for this TV */
+ TmSlot *slots = tv->tm_slots;
+ while (slots != NULL) {
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+
+ if (tm->flags & TM_FLAG_RECEIVE_TM) {
+ disable = 1;
+ break;
+ }
+
+ slots = slots->slot_next;
+ continue;
+ }
+
+ if (disable) {
+ if (tv->inq != NULL) {
+ /* we wait till we dry out all the inq packets, before we
+ * kill this thread. Do note that you should have disabled
+ * packet acquire by now using TmThreadDisableReceiveThreads()*/
+ if (!(strlen(tv->inq->name) == strlen("packetpool") &&
+ strcasecmp(tv->inq->name, "packetpool") == 0)) {
+ PacketQueue *q = &trans_q[tv->inq->id];
+ if (q->len != 0) {
+ SCMutexUnlock(&tv_root_lock);
+ /* don't sleep while holding a lock */
+ usleep(1000);
+ goto again;
+ }
+ }
+ }
+
+ /* we found a receive TV. Send it a KILL_PKTACQ signal. */
+ TmThreadsSetFlag(tv, THV_KILL_PKTACQ);
+
+ if (tv->inq != NULL) {
+ int i;
+ for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++) {
+ if (tv->inq->q_type == 0)
+ SCCondSignal(&trans_q[tv->inq->id].cond_q);
+ else
+ SCCondSignal(&data_queues[tv->inq->id].cond_q);
+ }
+ SCLogDebug("signalled tv->inq->id %" PRIu32 "", tv->inq->id);
+ }
+
+ /* wait for it to enter the 'flow loop' stage */
+ while (!TmThreadsCheckFlag(tv, THV_FLOW_LOOP)) {
+ usleep(WAIT_TIME);
+ total_wait_time += WAIT_TIME / 1000000.0;
+ if (total_wait_time > THREAD_KILL_MAX_WAIT_TIME) {
+ SCLogError(SC_ERR_FATAL, "Engine unable to "
+ "disable detect thread - \"%s\". "
+ "Killing engine", tv->name);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ tv = tv->next;
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Disable all threads having the specified TMs.
+ */
+void TmThreadDisablePacketThreads(void)
+{
+ /* value in seconds */
+#define THREAD_KILL_MAX_WAIT_TIME 60
+ /* value in microseconds */
+#define WAIT_TIME 100
+
+ double total_wait_time = 0;
+
+ ThreadVars *tv = NULL;
+
+again:
+ SCMutexLock(&tv_root_lock);
+
+ /* all receive threads are part of packet processing threads */
+ tv = tv_root[TVT_PPT];
+
+ /* we do have to keep in mind that TVs are arranged in the order
+ * right from receive to log. The moment we fail to find a
+ * receive TM amongst the slots in a tv, it indicates we are done
+ * with all receive threads */
+ while (tv) {
+ if (tv->inq != NULL) {
+ /* we wait till we dry out all the inq packets, before we
+ * kill this thread. Do note that you should have disabled
+ * packet acquire by now using TmThreadDisableReceiveThreads()*/
+ if (!(strlen(tv->inq->name) == strlen("packetpool") &&
+ strcasecmp(tv->inq->name, "packetpool") == 0)) {
+ PacketQueue *q = &trans_q[tv->inq->id];
+ if (q->len != 0) {
+ SCMutexUnlock(&tv_root_lock);
+ /* don't sleep while holding a lock */
+ usleep(1000);
+ goto again;
+ }
+ }
+ }
+
+ /* we found our receive TV. Send it a KILL signal. This is all
+ * we need to do to kill receive threads */
+ TmThreadsSetFlag(tv, THV_KILL);
+
+ if (tv->inq != NULL) {
+ int i;
+ for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++) {
+ if (tv->inq->q_type == 0)
+ SCCondSignal(&trans_q[tv->inq->id].cond_q);
+ else
+ SCCondSignal(&data_queues[tv->inq->id].cond_q);
+ }
+ SCLogDebug("signalled tv->inq->id %" PRIu32 "", tv->inq->id);
+ }
+
+ while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
+ usleep(WAIT_TIME);
+ total_wait_time += WAIT_TIME / 1000000.0;
+ if (total_wait_time > THREAD_KILL_MAX_WAIT_TIME) {
+ SCLogError(SC_ERR_FATAL, "Engine unable to "
+ "disable detect thread - \"%s\". "
+ "Killing engine", tv->name);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ tv = tv->next;
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+TmSlot *TmThreadGetFirstTmSlotForPartialPattern(const char *tm_name)
+{
+ ThreadVars *tv = NULL;
+ TmSlot *slots = NULL;
+
+ SCMutexLock(&tv_root_lock);
+
+ /* all receive threads are part of packet processing threads */
+ tv = tv_root[TVT_PPT];
+
+ while (tv) {
+ slots = tv->tm_slots;
+
+ while (slots != NULL) {
+ TmModule *tm = TmModuleGetById(slots->tm_id);
+
+ char *found = strstr(tm->name, tm_name);
+ if (found != NULL)
+ goto end;
+
+ slots = slots->slot_next;
+ }
+
+ tv = tv->next;
+ }
+
+ end:
+ SCMutexUnlock(&tv_root_lock);
+ return slots;
+}
+
+void TmThreadKillThreadsFamily(int family)
+{
+ ThreadVars *tv = NULL;
+
+ if ((family < 0) || (family >= TVT_MAX))
+ return;
+
+ SCMutexLock(&tv_root_lock);
+ tv = tv_root[family];
+
+ while (tv) {
+ TmThreadKillThread(tv);
+
+ tv = tv->next;
+ }
+ SCMutexUnlock(&tv_root_lock);
+}
+
+void TmThreadKillThreads(void)
+{
+ int i = 0;
+
+ for (i = 0; i < TVT_MAX; i++) {
+ TmThreadKillThreadsFamily(i);
+ }
+
+ return;
+}
+
+void TmThreadFree(ThreadVars *tv)
+{
+ TmSlot *s;
+ TmSlot *ps;
+ if (tv == NULL)
+ return;
+
+ SCLogDebug("Freeing thread '%s'.", tv->name);
+
+ StatsThreadCleanup(tv);
+
+ s = (TmSlot *)tv->tm_slots;
+ while (s) {
+ ps = s;
+ s = s->slot_next;
+ SCFree(ps);
+ }
+
+ TmThreadsUnregisterThread(tv->id);
+ SCFree(tv);
+}
+
+void TmThreadClearThreadsFamily(int family)
+{
+ ThreadVars *tv = NULL;
+ ThreadVars *ptv = NULL;
+
+ if ((family < 0) || (family >= TVT_MAX))
+ return;
+
+ SCMutexLock(&tv_root_lock);
+ tv = tv_root[family];
+
+ while (tv) {
+ ptv = tv;
+ tv = tv->next;
+ TmThreadFree(ptv);
+ }
+ tv_root[family] = NULL;
+ SCMutexUnlock(&tv_root_lock);
+}
+
+/**
+ * \brief Spawns a thread associated with the ThreadVars instance tv
+ *
+ * \retval TM_ECODE_OK on success and TM_ECODE_FAILED on failure
+ */
+TmEcode TmThreadSpawn(ThreadVars *tv)
+{
+ pthread_attr_t attr;
+ if (tv->tm_func == NULL) {
+ printf("ERROR: no thread function set\n");
+ return TM_ECODE_FAILED;
+ }
+
+ /* Initialize and set thread detached attribute */
+ pthread_attr_init(&attr);
+
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int rc = pthread_create(&tv->t, &attr, tv->tm_func, (void *)tv);
+ if (rc) {
+ printf("ERROR; return code from pthread_create() is %" PRId32 "\n", rc);
+ return TM_ECODE_FAILED;
+ }
+
+ TmThreadWaitForFlag(tv, THV_INIT_DONE | THV_RUNNING_DONE);
+
+ TmThreadAppend(tv, tv->type);
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Sets the thread flags for a thread instance(tv)
+ *
+ * \param tv Pointer to the thread instance for which the flag has to be set
+ * \param flags Holds the thread state this thread instance has to be set to
+ */
+#if 0
+void TmThreadSetFlags(ThreadVars *tv, uint8_t flags)
+{
+ if (tv != NULL)
+ tv->flags = flags;
+
+ return;
+}
+#endif
+/**
+ * \brief Sets the aof(Action on failure) for a thread instance(tv)
+ *
+ * \param tv Pointer to the thread instance for which the aof has to be set
+ * \param aof Holds the aof this thread instance has to be set to
+ */
+void TmThreadSetAOF(ThreadVars *tv, uint8_t aof)
+{
+ if (tv != NULL)
+ tv->aof = aof;
+
+ return;
+}
+
+/**
+ * \brief Initializes the mutex and condition variables for this TV
+ *
+ * It can be used by a thread to control a wait loop that can also be
+ * influenced by other threads.
+ *
+ * \param tv Pointer to a TV instance
+ */
+void TmThreadInitMC(ThreadVars *tv)
+{
+ if ( (tv->ctrl_mutex = SCMalloc(sizeof(*tv->ctrl_mutex))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in TmThreadInitMC. "
+ "Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ if (SCCtrlMutexInit(tv->ctrl_mutex, NULL) != 0) {
+ printf("Error initializing the tv->m mutex\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (tv->ctrl_cond = SCMalloc(sizeof(*tv->ctrl_cond))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in TmThreadInitMC. "
+ "Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ if (SCCtrlCondInit(tv->ctrl_cond, NULL) != 0) {
+ SCLogError(SC_ERR_FATAL, "Error initializing the tv->cond condition "
+ "variable");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+/**
+ * \brief Tests if the thread represented in the arg has been unpaused or not.
+ *
+ * The function would return if the thread tv has been unpaused or if the
+ * kill flag for the thread has been set.
+ *
+ * \param tv Pointer to the TV instance.
+ */
+void TmThreadTestThreadUnPaused(ThreadVars *tv)
+{
+ while (TmThreadsCheckFlag(tv, THV_PAUSE)) {
+ usleep(100);
+
+ if (TmThreadsCheckFlag(tv, THV_KILL))
+ break;
+ }
+
+ return;
+}
+
+/**
+ * \brief Waits till the specified flag(s) is(are) set. We don't bother if
+ * the kill flag has been set or not on the thread.
+ *
+ * \param tv Pointer to the TV instance.
+ */
+void TmThreadWaitForFlag(ThreadVars *tv, uint16_t flags)
+{
+ while (!TmThreadsCheckFlag(tv, flags)) {
+ usleep(100);
+ }
+
+ return;
+}
+
+/**
+ * \brief Unpauses a thread
+ *
+ * \param tv Pointer to a TV instance that has to be unpaused
+ */
+void TmThreadContinue(ThreadVars *tv)
+{
+ TmThreadsUnsetFlag(tv, THV_PAUSE);
+
+ return;
+}
+
+/**
+ * \brief Unpauses all threads present in tv_root
+ */
+void TmThreadContinueThreads()
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv != NULL) {
+ TmThreadContinue(tv);
+ tv = tv->next;
+ }
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Pauses a thread
+ *
+ * \param tv Pointer to a TV instance that has to be paused
+ */
+void TmThreadPause(ThreadVars *tv)
+{
+ TmThreadsSetFlag(tv, THV_PAUSE);
+
+ return;
+}
+
+/**
+ * \brief Pauses all threads present in tv_root
+ */
+void TmThreadPauseThreads()
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ TmThreadsListThreads();
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv != NULL) {
+ TmThreadPause(tv);
+ tv = tv->next;
+ }
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ return;
+}
+
+/**
+ * \brief Restarts the thread sent as the argument
+ *
+ * \param tv Pointer to the thread instance(tv) to be restarted
+ */
+static void TmThreadRestartThread(ThreadVars *tv)
+{
+ if (tv->restarted >= THV_MAX_RESTARTS) {
+ SCLogError(SC_ERR_TM_THREADS_ERROR,"thread restarts exceeded "
+ "threshold limit for thread \"%s\"", tv->name);
+ exit(EXIT_FAILURE);
+ }
+
+ TmThreadsUnsetFlag(tv, THV_CLOSED);
+ TmThreadsUnsetFlag(tv, THV_FAILED);
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_THREAD_SPAWN, "thread \"%s\" failed to spawn", tv->name);
+ exit(EXIT_FAILURE);
+ }
+
+ tv->restarted++;
+ SCLogInfo("thread \"%s\" restarted", tv->name);
+
+ return;
+}
+
+/**
+ * \brief Used to check the thread for certain conditions of failure. If the
+ * thread has been specified to restart on failure, the thread is
+ * restarted. If the thread has been specified to gracefully shutdown
+ * the engine on failure, it does so. The global aof flag, tv_aof
+ * overrides the thread aof flag, if it holds a THV_ENGINE_EXIT;
+ */
+void TmThreadCheckThreadState(void)
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+
+ while (tv) {
+ if (TmThreadsCheckFlag(tv, THV_FAILED)) {
+ TmThreadsSetFlag(tv, THV_DEINIT);
+ pthread_join(tv->t, NULL);
+ if ((tv_aof & THV_ENGINE_EXIT) || (tv->aof & THV_ENGINE_EXIT)) {
+ EngineKill();
+ goto end;
+ } else {
+ /* if the engine kill-stop has been received by now, chuck
+ * restarting and return to kill the engine */
+ if ((suricata_ctl_flags & SURICATA_KILL) ||
+ (suricata_ctl_flags & SURICATA_STOP)) {
+ goto end;
+ }
+ TmThreadRestartThread(tv);
+ }
+ }
+ tv = tv->next;
+ }
+ }
+end:
+ SCMutexUnlock(&tv_root_lock);
+ return;
+}
+
+/**
+ * \brief Used to check if all threads have finished their initialization. On
+ * finding an un-initialized thread, it waits till that thread completes
+ * its initialization, before proceeding to the next thread.
+ *
+ * \retval TM_ECODE_OK all initialized properly
+ * \retval TM_ECODE_FAILED failure
+ */
+TmEcode TmThreadWaitOnThreadInit(void)
+{
+ ThreadVars *tv = NULL;
+ int i = 0;
+ uint16_t mgt_num = 0;
+ uint16_t ppt_num = 0;
+
+ SCMutexLock(&tv_root_lock);
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv != NULL) {
+ char started = FALSE;
+ while (started == FALSE) {
+ if (TmThreadsCheckFlag(tv, THV_INIT_DONE)) {
+ started = TRUE;
+ } else {
+ /* sleep a little to give the thread some
+ * time to finish initialization */
+ usleep(100);
+ }
+
+ if (TmThreadsCheckFlag(tv, THV_FAILED)) {
+ SCMutexUnlock(&tv_root_lock);
+ SCLogError(SC_ERR_THREAD_INIT, "thread \"%s\" failed to "
+ "initialize.", tv->name);
+ return TM_ECODE_FAILED;
+ }
+ if (TmThreadsCheckFlag(tv, THV_CLOSED)) {
+ SCMutexUnlock(&tv_root_lock);
+ SCLogError(SC_ERR_THREAD_INIT, "thread \"%s\" closed on "
+ "initialization.", tv->name);
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ if (i == TVT_MGMT) mgt_num++;
+ else if (i == TVT_PPT) ppt_num++;
+
+ tv = tv->next;
+ }
+ }
+ SCMutexUnlock(&tv_root_lock);
+
+ SCLogNotice("all %"PRIu16" packet processing threads, %"PRIu16" management "
+ "threads initialized, engine started.", ppt_num, mgt_num);
+
+ return TM_ECODE_OK;
+}
+
+/**
+ * \brief Returns the TV for the calling thread.
+ *
+ * \retval tv Pointer to the ThreadVars instance for the calling thread;
+ * NULL on no match
+ */
+ThreadVars *TmThreadsGetCallingThread(void)
+{
+ pthread_t self = pthread_self();
+ ThreadVars *tv = NULL;
+ int i = 0;
+
+ SCMutexLock(&tv_root_lock);
+
+ for (i = 0; i < TVT_MAX; i++) {
+ tv = tv_root[i];
+ while (tv) {
+ if (pthread_equal(self, tv->t)) {
+ SCMutexUnlock(&tv_root_lock);
+ return tv;
+ }
+ tv = tv->next;
+ }
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+
+ return NULL;
+}
+
+typedef struct Thread_ {
+ ThreadVars *tv; /**< threadvars structure */
+ const char *name;
+ int type;
+ int in_use; /**< bool to indicate this is in use */
+} Thread;
+
+typedef struct Threads_ {
+ Thread *threads;
+ size_t threads_size;
+ int threads_cnt;
+} Threads;
+
+static Threads thread_store = { NULL, 0, 0 };
+static SCMutex thread_store_lock = SCMUTEX_INITIALIZER;
+
+void TmThreadsListThreads(void)
+{
+ Thread *t;
+ size_t s;
+
+ SCMutexLock(&thread_store_lock);
+
+ for (s = 0; s < thread_store.threads_size; s++) {
+ t = &thread_store.threads[s];
+ if (t == NULL || t->in_use == 0)
+ continue;
+ SCLogInfo("Thread %"PRIuMAX", %s type %d, tv %p", (uintmax_t)s+1, t->name, t->type, t->tv);
+ }
+
+ SCMutexUnlock(&thread_store_lock);
+}
+
+#define STEP 32
+/**
+ * \retval id thread id, or 0 if not found
+ */
+int TmThreadsRegisterThread(ThreadVars *tv, const int type)
+{
+ SCMutexLock(&thread_store_lock);
+ if (thread_store.threads == NULL) {
+ thread_store.threads = SCCalloc(STEP, sizeof(Thread));
+ BUG_ON(thread_store.threads == NULL);
+ thread_store.threads_size = STEP;
+ }
+
+ size_t s;
+ for (s = 0; s < thread_store.threads_size; s++) {
+ if (thread_store.threads[s].in_use == 0) {
+ Thread *t = &thread_store.threads[s];
+ t->name = tv->name;
+ t->type = type;
+ t->tv = tv;
+ t->in_use = 1;
+
+ SCMutexUnlock(&thread_store_lock);
+ return (int)(s+1);
+ }
+ }
+
+ /* if we get here the array is completely filled */
+ void *newmem = SCRealloc(thread_store.threads, ((thread_store.threads_size + STEP) * sizeof(Thread)));
+ BUG_ON(newmem == NULL);
+ thread_store.threads = newmem;
+ memset((uint8_t *)thread_store.threads + (thread_store.threads_size * sizeof(Thread)), 0x00, STEP);
+
+ Thread *t = &thread_store.threads[thread_store.threads_size];
+ t->name = tv->name;
+ t->type = type;
+ t->tv = tv;
+ t->in_use = 1;
+
+ s = thread_store.threads_size;
+ thread_store.threads_size += STEP;
+
+ SCMutexUnlock(&thread_store_lock);
+ return (int)(s+1);
+}
+#undef STEP
+
+void TmThreadsUnregisterThread(const int id)
+{
+ SCMutexLock(&thread_store_lock);
+ if (id <= 0 || id > (int)thread_store.threads_size) {
+ SCMutexUnlock(&thread_store_lock);
+ return;
+ }
+
+ /* id is one higher than index */
+ int idx = id - 1;
+
+ /* reset thread_id, which serves as clearing the record */
+ thread_store.threads[idx].in_use = 0;
+
+ /* check if we have at least one registered thread left */
+ size_t s;
+ for (s = 0; s < thread_store.threads_size; s++) {
+ Thread *t = &thread_store.threads[s];
+ if (t->in_use == 1) {
+ goto end;
+ }
+ }
+
+ /* if we get here no threads are registered */
+ SCFree(thread_store.threads);
+ thread_store.threads = NULL;
+ thread_store.threads_size = 0;
+ thread_store.threads_cnt = 0;
+
+end:
+ SCMutexUnlock(&thread_store_lock);
+}
+
+/**
+ * \retval r 1 if packet was accepted, 0 otherwise
+ * \note if packet was not accepted, it's still the responsibility
+ * of the caller.
+ */
+int TmThreadsInjectPacketsById(Packet **packets, const int id)
+{
+ if (id <= 0 || id > (int)thread_store.threads_size)
+ return 0;
+
+ int idx = id - 1;
+
+ Thread *t = &thread_store.threads[idx];
+ ThreadVars *tv = t->tv;
+
+ if (tv == NULL || tv->stream_pq == NULL)
+ return 0;
+
+ SCMutexLock(&tv->stream_pq->mutex_q);
+ while (*packets != NULL) {
+ PacketEnqueue(tv->stream_pq, *packets);
+ packets++;
+ }
+ SCMutexUnlock(&tv->stream_pq->mutex_q);
+
+ /* wake up listening thread(s) if necessary */
+ if (tv->inq != NULL) {
+ SCCondSignal(&trans_q[tv->inq->id].cond_q);
+ }
+ return 1;
+}
diff --git a/framework/src/suricata/src/tm-threads.h b/framework/src/suricata/src/tm-threads.h
new file mode 100644
index 00000000..9b9fd620
--- /dev/null
+++ b/framework/src/suricata/src/tm-threads.h
@@ -0,0 +1,203 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __TM_THREADS_H__
+#define __TM_THREADS_H__
+
+#include "tmqh-packetpool.h"
+#include "tm-threads-common.h"
+#include "tm-modules.h"
+
+#define TM_QUEUE_NAME_MAX 16
+#define TM_THREAD_NAME_MAX 16
+
+typedef TmEcode (*TmSlotFunc)(ThreadVars *, Packet *, void *, PacketQueue *,
+ PacketQueue *);
+
+typedef struct TmSlot_ {
+ /* the TV holding this slot */
+ ThreadVars *tv;
+
+ /* function pointers */
+ SC_ATOMIC_DECLARE(TmSlotFunc, SlotFunc);
+
+ TmEcode (*PktAcqLoop)(ThreadVars *, void *, void *);
+
+ TmEcode (*SlotThreadInit)(ThreadVars *, void *, void **);
+ void (*SlotThreadExitPrintStats)(ThreadVars *, void *);
+ TmEcode (*SlotThreadDeinit)(ThreadVars *, void *);
+
+ /* data storage */
+ void *slot_initdata;
+ SC_ATOMIC_DECLARE(void *, slot_data);
+
+ /* queue filled by the SlotFunc with packets that will
+ * be processed futher _before_ the current packet.
+ * The locks in the queue are NOT used */
+ PacketQueue slot_pre_pq;
+
+ /* queue filled by the SlotFunc with packets that will
+ * be processed futher _after_ the current packet. The
+ * locks in the queue are NOT used */
+ PacketQueue slot_post_pq;
+
+ /* store the thread module id */
+ int tm_id;
+
+ /* slot id, only used my TmVarSlot to know what the first slot is */
+ int id;
+
+ /* linked list, only used when you have multiple slots(used by TmVarSlot) */
+ struct TmSlot_ *slot_next;
+
+ /* just called once, so not perf critical */
+ TmEcode (*Management)(ThreadVars *, void *);
+
+} TmSlot;
+
+extern ThreadVars *tv_root[TVT_MAX];
+
+extern SCMutex tv_root_lock;
+
+void TmSlotSetFuncAppend(ThreadVars *, TmModule *, void *);
+void TmSlotSetFuncAppendDelayed(ThreadVars *, TmModule *, void *, int delayed);
+TmSlot *TmSlotGetSlotForTM(int);
+
+ThreadVars *TmThreadCreate(char *, char *, char *, char *, char *, char *,
+ void *(fn_p)(void *), int);
+ThreadVars *TmThreadCreatePacketHandler(char *, char *, char *, char *, char *,
+ char *);
+ThreadVars *TmThreadCreateMgmtThread(char *name, void *(fn_p)(void *), int);
+ThreadVars *TmThreadCreateMgmtThreadByName(char *name, char *module,
+ int mucond);
+ThreadVars *TmThreadCreateCmdThreadByName(char *name, char *module,
+ int mucond);
+TmEcode TmThreadSpawn(ThreadVars *);
+void TmThreadSetFlags(ThreadVars *, uint8_t);
+void TmThreadSetAOF(ThreadVars *, uint8_t);
+void TmThreadKillThread(ThreadVars *);
+void TmThreadKillThreadsFamily(int family);
+void TmThreadKillThreads(void);
+void TmThreadClearThreadsFamily(int family);
+void TmThreadAppend(ThreadVars *, int);
+void TmThreadRemove(ThreadVars *, int);
+
+TmEcode TmThreadSetCPUAffinity(ThreadVars *, uint16_t);
+TmEcode TmThreadSetThreadPriority(ThreadVars *, int);
+TmEcode TmThreadSetCPU(ThreadVars *, uint8_t);
+TmEcode TmThreadSetupOptions(ThreadVars *);
+void TmThreadSetPrio(ThreadVars *);
+int TmThreadGetNbThreads(uint8_t type);
+
+void TmThreadInitMC(ThreadVars *);
+void TmThreadTestThreadUnPaused(ThreadVars *);
+void TmThreadContinue(ThreadVars *);
+void TmThreadContinueThreads(void);
+void TmThreadPause(ThreadVars *);
+void TmThreadPauseThreads(void);
+void TmThreadCheckThreadState(void);
+TmEcode TmThreadWaitOnThreadInit(void);
+ThreadVars *TmThreadsGetCallingThread(void);
+
+int TmThreadsCheckFlag(ThreadVars *, uint16_t);
+void TmThreadsSetFlag(ThreadVars *, uint16_t);
+void TmThreadsUnsetFlag(ThreadVars *, uint16_t);
+void TmThreadWaitForFlag(ThreadVars *, uint16_t);
+
+TmEcode TmThreadsSlotVarRun (ThreadVars *tv, Packet *p, TmSlot *slot);
+
+ThreadVars *TmThreadsGetTVContainingSlot(TmSlot *);
+void TmThreadDisablePacketThreads(void);
+void TmThreadDisableReceiveThreads(void);
+TmSlot *TmThreadGetFirstTmSlotForPartialPattern(const char *);
+
+/**
+ * \brief Process the rest of the functions (if any) and queue.
+ */
+static inline TmEcode TmThreadsSlotProcessPkt(ThreadVars *tv, TmSlot *s, Packet *p)
+{
+ TmEcode r = TM_ECODE_OK;
+
+ if (s == NULL) {
+ tv->tmqh_out(tv, p);
+ return r;
+ }
+
+ if (TmThreadsSlotVarRun(tv, p, s) == TM_ECODE_FAILED) {
+ TmqhOutputPacketpool(tv, p);
+ TmSlot *slot = s;
+ while (slot != NULL) {
+ SCMutexLock(&slot->slot_post_pq.mutex_q);
+ TmqhReleasePacketsToPacketPool(&slot->slot_post_pq);
+ SCMutexUnlock(&slot->slot_post_pq.mutex_q);
+
+ slot = slot->slot_next;
+ }
+ TmThreadsSetFlag(tv, THV_FAILED);
+ r = TM_ECODE_FAILED;
+
+ } else {
+ tv->tmqh_out(tv, p);
+
+ /* post process pq */
+ TmSlot *slot = s;
+ while (slot != NULL) {
+ if (slot->slot_post_pq.top != NULL) {
+ while (1) {
+ SCMutexLock(&slot->slot_post_pq.mutex_q);
+ Packet *extra_p = PacketDequeue(&slot->slot_post_pq);
+ SCMutexUnlock(&slot->slot_post_pq.mutex_q);
+
+ if (extra_p == NULL)
+ break;
+
+ if (slot->slot_next != NULL) {
+ r = TmThreadsSlotVarRun(tv, extra_p, slot->slot_next);
+ if (r == TM_ECODE_FAILED) {
+ SCMutexLock(&slot->slot_post_pq.mutex_q);
+ TmqhReleasePacketsToPacketPool(&slot->slot_post_pq);
+ SCMutexUnlock(&slot->slot_post_pq.mutex_q);
+
+ TmqhOutputPacketpool(tv, extra_p);
+ TmThreadsSetFlag(tv, THV_FAILED);
+ break;
+ }
+ }
+ tv->tmqh_out(tv, extra_p);
+ }
+ } /* if (slot->slot_post_pq.top != NULL) */
+ slot = slot->slot_next;
+ } /* while (slot != NULL) */
+ }
+
+ return r;
+}
+
+
+void TmThreadsListThreads(void);
+int TmThreadsRegisterThread(ThreadVars *tv, const int type);
+void TmThreadsUnregisterThread(const int id);
+int TmThreadsInjectPacketsById(Packet **, int id);
+
+#endif /* __TM_THREADS_H__ */
diff --git a/framework/src/suricata/src/tmqh-flow.c b/framework/src/suricata/src/tmqh-flow.c
new file mode 100644
index 00000000..c0898ef0
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-flow.c
@@ -0,0 +1,510 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Simple output queue handler that makes sure all packets of the same flow
+ * are sent to the same queue. We support different kind of q handlers. Have
+ * a look at "autofp-scheduler" conf to further undertsand the various q
+ * handlers we provide.
+ */
+
+#include "suricata.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "tmqh-flow.h"
+
+#include "tm-queuehandlers.h"
+
+#include "conf.h"
+#include "util-unittest.h"
+
+Packet *TmqhInputFlow(ThreadVars *t);
+void TmqhOutputFlowHash(ThreadVars *t, Packet *p);
+void TmqhOutputFlowActivePackets(ThreadVars *t, Packet *p);
+void TmqhOutputFlowRoundRobin(ThreadVars *t, Packet *p);
+void *TmqhOutputFlowSetupCtx(char *queue_str);
+void TmqhOutputFlowFreeCtx(void *ctx);
+void TmqhFlowRegisterTests(void);
+
+void TmqhFlowRegister(void)
+{
+ tmqh_table[TMQH_FLOW].name = "flow";
+ tmqh_table[TMQH_FLOW].InHandler = TmqhInputFlow;
+ tmqh_table[TMQH_FLOW].OutHandlerCtxSetup = TmqhOutputFlowSetupCtx;
+ tmqh_table[TMQH_FLOW].OutHandlerCtxFree = TmqhOutputFlowFreeCtx;
+ tmqh_table[TMQH_FLOW].RegisterTests = TmqhFlowRegisterTests;
+
+ char *scheduler = NULL;
+ if (ConfGet("autofp-scheduler", &scheduler) == 1) {
+ if (strcasecmp(scheduler, "round-robin") == 0) {
+ SCLogInfo("AutoFP mode using \"Round Robin\" flow load balancer");
+ tmqh_table[TMQH_FLOW].OutHandler = TmqhOutputFlowRoundRobin;
+ } else if (strcasecmp(scheduler, "active-packets") == 0) {
+ SCLogInfo("AutoFP mode using \"Active Packets\" flow load balancer");
+ tmqh_table[TMQH_FLOW].OutHandler = TmqhOutputFlowActivePackets;
+ } else if (strcasecmp(scheduler, "hash") == 0) {
+ SCLogInfo("AutoFP mode using \"Hash\" flow load balancer");
+ tmqh_table[TMQH_FLOW].OutHandler = TmqhOutputFlowHash;
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry \"%s\" "
+ "for autofp-scheduler in conf. Killing engine.",
+ scheduler);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ SCLogInfo("AutoFP mode using default \"Active Packets\" flow load balancer");
+ tmqh_table[TMQH_FLOW].OutHandler = TmqhOutputFlowActivePackets;
+ }
+
+ return;
+}
+
+/* same as 'simple' */
+Packet *TmqhInputFlow(ThreadVars *tv)
+{
+ PacketQueue *q = &trans_q[tv->inq->id];
+
+ StatsSyncCountersIfSignalled(tv);
+
+ SCMutexLock(&q->mutex_q);
+ if (q->len == 0) {
+ /* if we have no packets in queue, wait... */
+ SCCondWait(&q->cond_q, &q->mutex_q);
+ }
+
+ if (q->len > 0) {
+ Packet *p = PacketDequeue(q);
+ SCMutexUnlock(&q->mutex_q);
+ return p;
+ } else {
+ /* return NULL if we have no pkt. Should only happen on signals. */
+ SCMutexUnlock(&q->mutex_q);
+ return NULL;
+ }
+}
+
+static int StoreQueueId(TmqhFlowCtx *ctx, char *name)
+{
+ void *ptmp;
+ Tmq *tmq = TmqGetQueueByName(name);
+ if (tmq == NULL) {
+ tmq = TmqCreateQueue(SCStrdup(name));
+ if (tmq == NULL)
+ return -1;
+ }
+ tmq->writer_cnt++;
+
+ uint16_t id = tmq->id;
+
+ if (ctx->queues == NULL) {
+ ctx->size = 1;
+ ctx->queues = SCMalloc(ctx->size * sizeof(TmqhFlowMode));
+ if (ctx->queues == NULL) {
+ return -1;
+ }
+ memset(ctx->queues, 0, ctx->size * sizeof(TmqhFlowMode));
+ } else {
+ ctx->size++;
+ ptmp = SCRealloc(ctx->queues, ctx->size * sizeof(TmqhFlowMode));
+ if (ptmp == NULL) {
+ SCFree(ctx->queues);
+ ctx->queues = NULL;
+ return -1;
+ }
+ ctx->queues = ptmp;
+
+ memset(ctx->queues + (ctx->size - 1), 0, sizeof(TmqhFlowMode));
+ }
+ ctx->queues[ctx->size - 1].q = &trans_q[id];
+ SC_ATOMIC_INIT(ctx->queues[ctx->size - 1].total_packets);
+ SC_ATOMIC_INIT(ctx->queues[ctx->size - 1].total_flows);
+
+ return 0;
+}
+
+/**
+ * \brief setup the queue handlers ctx
+ *
+ * Parses a comma separated string "queuename1,queuename2,etc"
+ * and sets the ctx up to devide flows over these queue's.
+ *
+ * \param queue_str comma separated string with output queue names
+ *
+ * \retval ctx queues handlers ctx or NULL in error
+ */
+void *TmqhOutputFlowSetupCtx(char *queue_str)
+{
+ if (queue_str == NULL || strlen(queue_str) == 0)
+ return NULL;
+
+ SCLogDebug("queue_str %s", queue_str);
+
+ TmqhFlowCtx *ctx = SCMalloc(sizeof(TmqhFlowCtx));
+ if (unlikely(ctx == NULL))
+ return NULL;
+ memset(ctx,0x00,sizeof(TmqhFlowCtx));
+
+ char *str = SCStrdup(queue_str);
+ if (unlikely(str == NULL)) {
+ goto error;
+ }
+ char *tstr = str;
+
+ /* parse the comma separated string */
+ do {
+ char *comma = strchr(tstr,',');
+ if (comma != NULL) {
+ *comma = '\0';
+ char *qname = tstr;
+ int r = StoreQueueId(ctx,qname);
+ if (r < 0)
+ goto error;
+ } else {
+ char *qname = tstr;
+ int r = StoreQueueId(ctx,qname);
+ if (r < 0)
+ goto error;
+ }
+ tstr = comma ? (comma + 1) : comma;
+ } while (tstr != NULL);
+
+ SC_ATOMIC_INIT(ctx->round_robin_idx);
+
+ SCFree(str);
+ return (void *)ctx;
+
+error:
+ SCFree(ctx);
+ if (str != NULL)
+ SCFree(str);
+ return NULL;
+}
+
+void TmqhOutputFlowFreeCtx(void *ctx)
+{
+ int i;
+ TmqhFlowCtx *fctx = (TmqhFlowCtx *)ctx;
+
+ SCLogInfo("AutoFP - Total flow handler queues - %" PRIu16,
+ fctx->size);
+ for (i = 0; i < fctx->size; i++) {
+ SCLogInfo("AutoFP - Queue %-2"PRIu32 " - pkts: %-12"PRIu64" flows: %-12"PRIu64, i,
+ SC_ATOMIC_GET(fctx->queues[i].total_packets),
+ SC_ATOMIC_GET(fctx->queues[i].total_flows));
+ SC_ATOMIC_DESTROY(fctx->queues[i].total_packets);
+ SC_ATOMIC_DESTROY(fctx->queues[i].total_flows);
+ }
+
+ SCFree(fctx->queues);
+
+ return;
+}
+
+/**
+ * \brief select the queue to output in a round robin fashion.
+ *
+ * \param tv thread vars
+ * \param p packet
+ */
+void TmqhOutputFlowRoundRobin(ThreadVars *tv, Packet *p)
+{
+ int16_t qid = 0;
+
+ TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx;
+
+ /* if no flow we use the first queue,
+ * should be rare */
+ if (p->flow != NULL) {
+ qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid);
+ if (qid == -1) {
+ qid = SC_ATOMIC_ADD(ctx->round_robin_idx, 1);
+ if (qid >= ctx->size) {
+ SC_ATOMIC_RESET(ctx->round_robin_idx);
+ qid = 0;
+ }
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1);
+ (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, qid);
+ }
+ } else {
+ qid = ctx->last++;
+
+ if (ctx->last == ctx->size)
+ ctx->last = 0;
+ }
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_packets, 1);
+
+ PacketQueue *q = ctx->queues[qid].q;
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+
+ return;
+}
+
+/**
+ * \brief select the queue to output to based on queue lengths.
+ *
+ * \param tv thread vars
+ * \param p packet
+ */
+void TmqhOutputFlowActivePackets(ThreadVars *tv, Packet *p)
+{
+ int16_t qid = 0;
+
+ TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx;
+
+ /* if no flow we use the first queue,
+ * should be rare */
+ if (p->flow != NULL) {
+ qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid);
+ if (qid == -1) {
+ uint16_t i = 0;
+ int lowest_id = 0;
+ TmqhFlowMode *queues = ctx->queues;
+ uint32_t lowest = queues[i].q->len;
+ for (i = 1; i < ctx->size; i++) {
+ if (queues[i].q->len < lowest) {
+ lowest = queues[i].q->len;
+ lowest_id = i;
+ }
+ }
+ qid = lowest_id;
+ (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, lowest_id);
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1);
+ }
+ } else {
+ qid = ctx->last++;
+
+ if (ctx->last == ctx->size)
+ ctx->last = 0;
+ }
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_packets, 1);
+
+ PacketQueue *q = ctx->queues[qid].q;
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+
+ return;
+}
+
+/**
+ * \brief select the queue to output based on address hash.
+ *
+ * \param tv thread vars.
+ * \param p packet.
+ */
+void TmqhOutputFlowHash(ThreadVars *tv, Packet *p)
+{
+ int16_t qid = 0;
+
+ TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx;
+
+ /* if no flow we use the first queue,
+ * should be rare */
+ if (p->flow != NULL) {
+ qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid);
+ if (qid == -1) {
+#if __WORDSIZE == 64
+ uint64_t addr = (uint64_t)p->flow;
+#else
+ uint32_t addr = (uint32_t)p->flow;
+#endif
+ addr >>= 7;
+
+ /* we don't have to worry about possible overflow, since
+ * ctx->size will be lesser than 2 ** 31 for sure */
+ qid = addr % ctx->size;
+ (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, qid);
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1);
+ }
+ } else {
+ qid = ctx->last++;
+
+ if (ctx->last == ctx->size)
+ ctx->last = 0;
+ }
+ (void) SC_ATOMIC_ADD(ctx->queues[qid].total_packets, 1);
+
+ PacketQueue *q = ctx->queues[qid].q;
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+
+ return;
+}
+
+#ifdef UNITTESTS
+
+static int TmqhOutputFlowSetupCtxTest01(void)
+{
+ int retval = 0;
+ Tmq *tmq = NULL;
+ TmqhFlowCtx *fctx = NULL;
+
+ TmqResetQueues();
+
+ tmq = TmqCreateQueue("queue1");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("queue2");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("another");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("yetanother");
+ if (tmq == NULL)
+ goto end;
+
+ char *str = "queue1,queue2,another,yetanother";
+ void *ctx = TmqhOutputFlowSetupCtx(str);
+
+ if (ctx == NULL)
+ goto end;
+
+ fctx = (TmqhFlowCtx *)ctx;
+
+ if (fctx->size != 4)
+ goto end;
+
+ if (fctx->queues == NULL)
+ goto end;
+
+ if (fctx->queues[0].q != &trans_q[0])
+ goto end;
+ if (fctx->queues[1].q != &trans_q[1])
+ goto end;
+ if (fctx->queues[2].q != &trans_q[2])
+ goto end;
+ if (fctx->queues[3].q != &trans_q[3])
+ goto end;
+
+ retval = 1;
+end:
+ if (fctx != NULL)
+ TmqhOutputFlowFreeCtx(fctx);
+ TmqResetQueues();
+ return retval;
+}
+
+static int TmqhOutputFlowSetupCtxTest02(void)
+{
+ int retval = 0;
+ Tmq *tmq = NULL;
+ TmqhFlowCtx *fctx = NULL;
+
+ TmqResetQueues();
+
+ tmq = TmqCreateQueue("queue1");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("queue2");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("another");
+ if (tmq == NULL)
+ goto end;
+ tmq = TmqCreateQueue("yetanother");
+ if (tmq == NULL)
+ goto end;
+
+ char *str = "queue1";
+ void *ctx = TmqhOutputFlowSetupCtx(str);
+
+ if (ctx == NULL)
+ goto end;
+
+ fctx = (TmqhFlowCtx *)ctx;
+
+ if (fctx->size != 1)
+ goto end;
+
+ if (fctx->queues == NULL)
+ goto end;
+
+ if (fctx->queues[0].q != &trans_q[0])
+ goto end;
+
+ retval = 1;
+end:
+ if (fctx != NULL)
+ TmqhOutputFlowFreeCtx(fctx);
+ TmqResetQueues();
+ return retval;
+}
+
+static int TmqhOutputFlowSetupCtxTest03(void)
+{
+ int retval = 0;
+ TmqhFlowCtx *fctx = NULL;
+
+ TmqResetQueues();
+
+ char *str = "queue1,queue2,another,yetanother";
+ void *ctx = TmqhOutputFlowSetupCtx(str);
+
+ if (ctx == NULL)
+ goto end;
+
+ fctx = (TmqhFlowCtx *)ctx;
+
+ if (fctx->size != 4)
+ goto end;
+
+ if (fctx->queues == NULL)
+ goto end;
+
+ if (fctx->queues[0].q != &trans_q[0])
+ goto end;
+ if (fctx->queues[1].q != &trans_q[1])
+ goto end;
+ if (fctx->queues[2].q != &trans_q[2])
+ goto end;
+ if (fctx->queues[3].q != &trans_q[3])
+ goto end;
+
+ retval = 1;
+end:
+ if (fctx != NULL)
+ TmqhOutputFlowFreeCtx(fctx);
+ TmqResetQueues();
+ return retval;
+}
+
+#endif /* UNITTESTS */
+
+void TmqhFlowRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("TmqhOutputFlowSetupCtxTest01", TmqhOutputFlowSetupCtxTest01, 1);
+ UtRegisterTest("TmqhOutputFlowSetupCtxTest02", TmqhOutputFlowSetupCtxTest02, 1);
+ UtRegisterTest("TmqhOutputFlowSetupCtxTest03", TmqhOutputFlowSetupCtxTest03, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/tmqh-flow.h b/framework/src/suricata/src/tmqh-flow.h
new file mode 100644
index 00000000..ccf92633
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-flow.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TMQH_FLOW_H__
+#define __TMQH_FLOW_H__
+
+typedef struct TmqhFlowMode_ {
+ PacketQueue *q;
+ SC_ATOMIC_DECLARE(uint64_t, total_packets);
+ SC_ATOMIC_DECLARE(uint64_t, total_flows);
+} TmqhFlowMode;
+
+/** \brief Ctx for the flow queue handler
+ * \param size number of queues to output to
+ * \param queues array of queue id's this flow handler outputs to */
+typedef struct TmqhFlowCtx_ {
+ uint16_t size;
+ uint16_t last;
+
+ TmqhFlowMode *queues;
+
+ SC_ATOMIC_DECLARE(uint16_t, round_robin_idx);
+} TmqhFlowCtx;
+
+void TmqhFlowRegister (void);
+void TmqhFlowRegisterTests(void);
+
+#endif /* __TMQH_FLOW_H__ */
diff --git a/framework/src/suricata/src/tmqh-nfq.c b/framework/src/suricata/src/tmqh-nfq.c
new file mode 100644
index 00000000..90b9e77a
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-nfq.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * NFQ Verdict Handler
+ */
+
+#include "suricata-common.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+
+#include "tm-queuehandlers.h"
+
+void TmqhOutputVerdictNfq(ThreadVars *t, Packet *p);
+
+void TmqhNfqRegister (void)
+{
+ tmqh_table[TMQH_NFQ].name = "nfq";
+ tmqh_table[TMQH_NFQ].InHandler = NULL;
+ tmqh_table[TMQH_NFQ].OutHandler = TmqhOutputVerdictNfq;
+}
+
+void TmqhOutputVerdictNfq(ThreadVars *t, Packet *p)
+{
+/* XXX not scaling */
+#if 0
+ PacketQueue *q = &trans_q[p->verdict_q_id];
+
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+#endif
+}
+
diff --git a/framework/src/suricata/src/tmqh-nfq.h b/framework/src/suricata/src/tmqh-nfq.h
new file mode 100644
index 00000000..50884c88
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-nfq.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TMQH_NFQ_H__
+#define __TMQH_NFQ_H__
+
+void TmqhNfqRegister (void);
+
+#endif /* __TMQH_NFQ_H__ */
diff --git a/framework/src/suricata/src/tmqh-packetpool.c b/framework/src/suricata/src/tmqh-packetpool.c
new file mode 100644
index 00000000..a1f19dca
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-packetpool.c
@@ -0,0 +1,565 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Packetpool queue handlers. Packet pool is implemented as a stack.
+ */
+
+#include "suricata.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-uricontent.h"
+#include "threads.h"
+#include "threadvars.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "host.h"
+
+#include "stream.h"
+#include "stream-tcp-reassemble.h"
+
+#include "tm-queuehandlers.h"
+
+#include "pkt-var.h"
+
+#include "tmqh-packetpool.h"
+
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-profiling.h"
+#include "util-device.h"
+
+/* Number of freed packet to save for one pool before freeing them. */
+#define MAX_PENDING_RETURN_PACKETS 32
+
+#ifdef TLS
+__thread PktPool thread_pkt_pool;
+
+static inline PktPool *GetThreadPacketPool(void)
+{
+ return &thread_pkt_pool;
+}
+#else
+/* __thread not supported. */
+static pthread_key_t pkt_pool_thread_key;
+static SCMutex pkt_pool_thread_key_mutex = SCMUTEX_INITIALIZER;
+static int pkt_pool_thread_key_initialized = 0;
+
+static void PktPoolThreadDestroy(void * buf)
+{
+ SCFreeAligned(buf);
+}
+
+static void TmqhPacketPoolInit(void)
+{
+ SCMutexLock(&pkt_pool_thread_key_mutex);
+ if (pkt_pool_thread_key_initialized) {
+ /* Key has already been created. */
+ SCMutexUnlock(&pkt_pool_thread_key_mutex);
+ return;
+ }
+
+ /* Create the pthread Key that is used to look up thread specific
+ * data buffer. Needs to be created only once.
+ */
+ int r = pthread_key_create(&pkt_pool_thread_key, PktPoolThreadDestroy);
+ if (r != 0) {
+ SCLogError(SC_ERR_MEM_ALLOC, "pthread_key_create failed with %d", r);
+ exit(EXIT_FAILURE);
+ }
+
+ pkt_pool_thread_key_initialized = 1;
+ SCMutexUnlock(&pkt_pool_thread_key_mutex);
+}
+
+static PktPool *ThreadPacketPoolCreate(void)
+{
+ TmqhPacketPoolInit();
+
+ /* Create a new pool for this thread. */
+ PktPool* pool = (PktPool*)SCMallocAligned(sizeof(PktPool), CLS);
+ if (pool == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+ exit(EXIT_FAILURE);
+ }
+ memset(pool,0x0,sizeof(*pool));
+
+ int r = pthread_setspecific(pkt_pool_thread_key, pool);
+ if (r != 0) {
+ SCLogError(SC_ERR_MEM_ALLOC, "pthread_setspecific failed with %d", r);
+ exit(EXIT_FAILURE);
+ }
+
+ return pool;
+}
+
+static inline PktPool *GetThreadPacketPool(void)
+{
+ PktPool* pool = (PktPool*)pthread_getspecific(pkt_pool_thread_key);
+ if (pool == NULL)
+ pool = ThreadPacketPoolCreate();
+
+ return pool;
+}
+#endif
+
+/**
+ * \brief TmqhPacketpoolRegister
+ * \initonly
+ */
+void TmqhPacketpoolRegister (void)
+{
+ tmqh_table[TMQH_PACKETPOOL].name = "packetpool";
+ tmqh_table[TMQH_PACKETPOOL].InHandler = TmqhInputPacketpool;
+ tmqh_table[TMQH_PACKETPOOL].OutHandler = TmqhOutputPacketpool;
+}
+
+static int PacketPoolIsEmpty(PktPool *pool)
+{
+ /* Check local stack first. */
+ if (pool->head || pool->return_stack.head)
+ return 0;
+
+ return 1;
+}
+
+void PacketPoolWait(void)
+{
+ PktPool *my_pool = GetThreadPacketPool();
+
+ if (PacketPoolIsEmpty(my_pool)) {
+ SCMutexLock(&my_pool->return_stack.mutex);
+ SC_ATOMIC_ADD(my_pool->return_stack.sync_now, 1);
+ SCCondWait(&my_pool->return_stack.cond, &my_pool->return_stack.mutex);
+ SCMutexUnlock(&my_pool->return_stack.mutex);
+ }
+
+ while(PacketPoolIsEmpty(my_pool))
+ cc_barrier();
+}
+
+/** \brief Wait until we have the requested ammount of packets in the pool
+ *
+ * In some cases waiting for packets is undesirable. Especially when
+ * a wait would happen under a lock of some kind, other parts of the
+ * engine could have to wait.
+ *
+ * This function only returns when at least N packets are in our pool.
+ *
+ * \param n number of packets needed
+ */
+void PacketPoolWaitForN(int n)
+{
+ PktPool *my_pool = GetThreadPacketPool();
+ Packet *p = NULL;
+
+ while (1) {
+ int i = 0;
+ PacketPoolWait();
+
+ /* count packets in our stack */
+ p = my_pool->head;
+ while (p != NULL) {
+ if (++i == n)
+ return;
+
+ p = p->next;
+ }
+
+ /* continue counting in the return stack */
+ if (my_pool->return_stack.head != NULL) {
+ SCMutexLock(&my_pool->return_stack.mutex);
+ p = my_pool->return_stack.head;
+ while (p != NULL) {
+ if (++i == n) {
+ SCMutexUnlock(&my_pool->return_stack.mutex);
+ return;
+ }
+ p = p->next;
+ }
+ SCMutexUnlock(&my_pool->return_stack.mutex);
+
+ /* or signal that we need packets and wait */
+ } else {
+ SCMutexLock(&my_pool->return_stack.mutex);
+ SC_ATOMIC_ADD(my_pool->return_stack.sync_now, 1);
+ SCCondWait(&my_pool->return_stack.cond, &my_pool->return_stack.mutex);
+ SCMutexUnlock(&my_pool->return_stack.mutex);
+ }
+ }
+}
+
+/** \brief a initialized packet
+ *
+ * \warning Use *only* at init, not at packet runtime
+ */
+static void PacketPoolStorePacket(Packet *p)
+{
+ /* Clear the PKT_ALLOC flag, since that indicates to push back
+ * onto the ring buffer. */
+ p->flags &= ~PKT_ALLOC;
+ p->pool = GetThreadPacketPool();
+ p->ReleasePacket = PacketPoolReturnPacket;
+ PacketPoolReturnPacket(p);
+}
+
+static void PacketPoolGetReturnedPackets(PktPool *pool)
+{
+ SCMutexLock(&pool->return_stack.mutex);
+ /* Move all the packets from the locked return stack to the local stack. */
+ pool->head = pool->return_stack.head;
+ pool->return_stack.head = NULL;
+ SCMutexUnlock(&pool->return_stack.mutex);
+}
+
+/** \brief Get a new packet from the packet pool
+ *
+ * Only allocates from the thread's local stack, or mallocs new packets.
+ * If the local stack is empty, first move all the return stack packets to
+ * the local stack.
+ * \retval Packet pointer, or NULL on failure.
+ */
+Packet *PacketPoolGetPacket(void)
+{
+ PktPool *pool = GetThreadPacketPool();
+#ifdef DEBUG_VALIDATION
+ BUG_ON(pool->initialized == 0);
+ BUG_ON(pool->destroyed == 1);
+#endif /* DEBUG_VALIDATION */
+ if (pool->head) {
+ /* Stack is not empty. */
+ Packet *p = pool->head;
+ pool->head = p->next;
+ p->pool = pool;
+ PACKET_REINIT(p);
+ return p;
+ }
+
+ /* Local Stack is empty, so check the return stack, which requires
+ * locking. */
+ PacketPoolGetReturnedPackets(pool);
+
+ /* Try to allocate again. Need to check for not empty again, since the
+ * return stack might have been empty too.
+ */
+ if (pool->head) {
+ /* Stack is not empty. */
+ Packet *p = pool->head;
+ pool->head = p->next;
+ p->pool = pool;
+ PACKET_REINIT(p);
+ return p;
+ }
+
+ /* Failed to allocate a packet, so return NULL. */
+ /* Optionally, could allocate a new packet here. */
+ return NULL;
+}
+
+/** \brief Return packet to Packet pool
+ *
+ */
+void PacketPoolReturnPacket(Packet *p)
+{
+ PktPool *my_pool = GetThreadPacketPool();
+
+ PACKET_RELEASE_REFS(p);
+
+ PktPool *pool = p->pool;
+ if (pool == NULL) {
+ PacketFree(p);
+ return;
+ }
+#ifdef DEBUG_VALIDATION
+ BUG_ON(pool->initialized == 0);
+ BUG_ON(pool->destroyed == 1);
+ BUG_ON(my_pool->initialized == 0);
+ BUG_ON(my_pool->destroyed == 1);
+#endif /* DEBUG_VALIDATION */
+
+ if (pool == my_pool) {
+ /* Push back onto this thread's own stack, so no locking. */
+ p->next = my_pool->head;
+ my_pool->head = p;
+ } else {
+ PktPool *pending_pool = my_pool->pending_pool;
+ if (pending_pool == NULL) {
+ /* No pending packet, so store the current packet. */
+ my_pool->pending_pool = pool;
+ my_pool->pending_head = p;
+ my_pool->pending_tail = p;
+ my_pool->pending_count = 1;
+ } else if (pending_pool == pool) {
+ /* Another packet for the pending pool list. */
+ p->next = my_pool->pending_head;
+ my_pool->pending_head = p;
+ my_pool->pending_count++;
+ if (SC_ATOMIC_GET(pool->return_stack.sync_now) || my_pool->pending_count > MAX_PENDING_RETURN_PACKETS) {
+ /* Return the entire list of pending packets. */
+ SCMutexLock(&pool->return_stack.mutex);
+ my_pool->pending_tail->next = pool->return_stack.head;
+ pool->return_stack.head = my_pool->pending_head;
+ SC_ATOMIC_RESET(pool->return_stack.sync_now);
+ SCMutexUnlock(&pool->return_stack.mutex);
+ SCCondSignal(&pool->return_stack.cond);
+ /* Clear the list of pending packets to return. */
+ my_pool->pending_pool = NULL;
+ my_pool->pending_head = NULL;
+ my_pool->pending_tail = NULL;
+ my_pool->pending_count = 0;
+ }
+ } else {
+ /* Push onto return stack for this pool */
+ SCMutexLock(&pool->return_stack.mutex);
+ p->next = pool->return_stack.head;
+ pool->return_stack.head = p;
+ SC_ATOMIC_RESET(pool->return_stack.sync_now);
+ SCMutexUnlock(&pool->return_stack.mutex);
+ SCCondSignal(&pool->return_stack.cond);
+ }
+ }
+}
+
+void PacketPoolInitEmpty(void)
+{
+#ifndef TLS
+ TmqhPacketPoolInit();
+#endif
+
+ PktPool *my_pool = GetThreadPacketPool();
+
+#ifdef DEBUG_VALIDATION
+ BUG_ON(my_pool->initialized);
+ my_pool->initialized = 1;
+ my_pool->destroyed = 0;
+#endif /* DEBUG_VALIDATION */
+
+ SCMutexInit(&my_pool->return_stack.mutex, NULL);
+ SCCondInit(&my_pool->return_stack.cond, NULL);
+ SC_ATOMIC_INIT(my_pool->return_stack.sync_now);
+}
+
+void PacketPoolInit(void)
+{
+ extern intmax_t max_pending_packets;
+
+#ifndef TLS
+ TmqhPacketPoolInit();
+#endif
+
+ PktPool *my_pool = GetThreadPacketPool();
+
+#ifdef DEBUG_VALIDATION
+ BUG_ON(my_pool->initialized);
+ my_pool->initialized = 1;
+ my_pool->destroyed = 0;
+#endif /* DEBUG_VALIDATION */
+
+ SCMutexInit(&my_pool->return_stack.mutex, NULL);
+ SCCondInit(&my_pool->return_stack.cond, NULL);
+ SC_ATOMIC_INIT(my_pool->return_stack.sync_now);
+
+ /* pre allocate packets */
+ SCLogDebug("preallocating packets... packet size %" PRIuMAX "",
+ (uintmax_t)SIZE_OF_PACKET);
+ int i = 0;
+ for (i = 0; i < max_pending_packets; i++) {
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered while allocating a packet. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ PacketPoolStorePacket(p);
+ }
+ SCLogInfo("preallocated %"PRIiMAX" packets. Total memory %"PRIuMAX"",
+ max_pending_packets, (uintmax_t)(max_pending_packets*SIZE_OF_PACKET));
+
+}
+
+void PacketPoolDestroy(void)
+{
+ Packet *p = NULL;
+ PktPool *my_pool = GetThreadPacketPool();
+
+#ifdef DEBUG_VALIDATION
+ BUG_ON(my_pool->destroyed);
+#endif /* DEBUG_VALIDATION */
+
+ if (my_pool && my_pool->pending_pool != NULL) {
+ p = my_pool->pending_head;
+ while (p) {
+ Packet *next_p = p->next;
+ PacketFree(p);
+ p = next_p;
+ my_pool->pending_count--;
+ }
+#ifdef DEBUG_VALIDATION
+ BUG_ON(my_pool->pending_count);
+#endif /* DEBUG_VALIDATION */
+ my_pool->pending_pool = NULL;
+ my_pool->pending_head = NULL;
+ my_pool->pending_tail = NULL;
+ }
+
+ while ((p = PacketPoolGetPacket()) != NULL) {
+ PacketFree(p);
+ }
+
+ SC_ATOMIC_DESTROY(my_pool->return_stack.sync_now);
+
+#ifdef DEBUG_VALIDATION
+ my_pool->initialized = 0;
+ my_pool->destroyed = 1;
+#endif /* DEBUG_VALIDATION */
+}
+
+Packet *TmqhInputPacketpool(ThreadVars *tv)
+{
+ return PacketPoolGetPacket();
+}
+
+void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
+{
+ int proot = 0;
+
+ SCEnter();
+ SCLogDebug("Packet %p, p->root %p, alloced %s", p, p->root, p->flags & PKT_ALLOC ? "true" : "false");
+
+ /** \todo make this a callback
+ * Release tcp segments. Done here after alerting can use them. */
+ if (p->flow != NULL && p->proto == IPPROTO_TCP) {
+ SCMutexLock(&p->flow->m);
+ StreamTcpPruneSession(p->flow, p->flowflags & FLOW_PKT_TOSERVER ?
+ STREAM_TOSERVER : STREAM_TOCLIENT);
+ SCMutexUnlock(&p->flow->m);
+ }
+
+ if (IS_TUNNEL_PKT(p)) {
+ SCLogDebug("Packet %p is a tunnel packet: %s",
+ p,p->root ? "upper layer" : "tunnel root");
+
+ /* get a lock to access root packet fields */
+ SCMutex *m = p->root ? &p->root->tunnel_mutex : &p->tunnel_mutex;
+ SCMutexLock(m);
+
+ if (IS_TUNNEL_ROOT_PKT(p)) {
+ SCLogDebug("IS_TUNNEL_ROOT_PKT == TRUE");
+ if (TUNNEL_PKT_TPR(p) == 0) {
+ SCLogDebug("TUNNEL_PKT_TPR(p) == 0, no more tunnel packet "
+ "depending on this root");
+ /* if this packet is the root and there are no
+ * more tunnel packets, return it to the pool */
+
+ /* fall through */
+ } else {
+ SCLogDebug("tunnel root Packet %p: TUNNEL_PKT_TPR(p) > 0, so "
+ "packets are still depending on this root, setting "
+ "p->tunnel_verdicted == 1", p);
+ /* if this is the root and there are more tunnel
+ * packets, return this to the pool. It's still referenced
+ * by the tunnel packets, and we will return it
+ * when we handle them */
+ SET_TUNNEL_PKT_VERDICTED(p);
+
+ PACKET_PROFILING_END(p);
+ SCMutexUnlock(m);
+ SCReturn;
+ }
+ } else {
+ SCLogDebug("NOT IS_TUNNEL_ROOT_PKT, so tunnel pkt");
+
+ /* the p->root != NULL here seems unnecessary: IS_TUNNEL_PKT checks
+ * that p->tunnel_pkt == 1, IS_TUNNEL_ROOT_PKT checks that +
+ * p->root == NULL. So when we are here p->root can only be
+ * non-NULL, right? CLANG thinks differently. May be a FP, but
+ * better safe than sorry. VJ */
+ if (p->root != NULL && IS_TUNNEL_PKT_VERDICTED(p->root) &&
+ TUNNEL_PKT_TPR(p) == 1)
+ {
+ SCLogDebug("p->root->tunnel_verdicted == 1 && TUNNEL_PKT_TPR(p) == 1");
+ /* the root is ready and we are the last tunnel packet,
+ * lets enqueue them both. */
+ TUNNEL_DECR_PKT_TPR_NOLOCK(p);
+
+ /* handle the root */
+ SCLogDebug("setting proot = 1 for root pkt, p->root %p "
+ "(tunnel packet %p)", p->root, p);
+ proot = 1;
+
+ /* fall through */
+ } else {
+ /* root not ready yet, so get rid of the tunnel pkt only */
+
+ SCLogDebug("NOT p->root->tunnel_verdicted == 1 && "
+ "TUNNEL_PKT_TPR(p) == 1 (%" PRIu32 ")", TUNNEL_PKT_TPR(p));
+
+ TUNNEL_DECR_PKT_TPR_NOLOCK(p);
+
+ /* fall through */
+ }
+ }
+ SCMutexUnlock(m);
+
+ SCLogDebug("tunnel stuff done, move on (proot %d)", proot);
+ }
+
+ FlowDeReference(&p->flow);
+
+ /* we're done with the tunnel root now as well */
+ if (proot == 1) {
+ SCLogDebug("getting rid of root pkt... alloc'd %s", p->root->flags & PKT_ALLOC ? "true" : "false");
+
+ FlowDeReference(&p->root->flow);
+
+ p->root->ReleasePacket(p->root);
+ p->root = NULL;
+ }
+
+ PACKET_PROFILING_END(p);
+
+ p->ReleasePacket(p);
+
+ SCReturn;
+}
+
+/**
+ * \brief Release all the packets in the queue back to the packetpool. Mainly
+ * used by threads that have failed, and wants to return the packets back
+ * to the packetpool.
+ *
+ * \param pq Pointer to the packetqueue from which the packets have to be
+ * returned back to the packetpool
+ *
+ * \warning this function assumes that the pq does not use locking
+ */
+void TmqhReleasePacketsToPacketPool(PacketQueue *pq)
+{
+ Packet *p = NULL;
+
+ if (pq == NULL)
+ return;
+
+ while ( (p = PacketDequeue(pq)) != NULL)
+ TmqhOutputPacketpool(NULL, p);
+
+ return;
+}
diff --git a/framework/src/suricata/src/tmqh-packetpool.h b/framework/src/suricata/src/tmqh-packetpool.h
new file mode 100644
index 00000000..ab45184c
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-packetpool.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TMQH_PACKETPOOL_H__
+#define __TMQH_PACKETPOOL_H__
+
+#include "decode.h"
+#include "threads.h"
+#include "util-atomic.h"
+
+ /* Return stack, onto which other threads free packets. */
+typedef struct PktPoolLockedStack_{
+ /* linked list of free packets. */
+ SCMutex mutex;
+ SCCondT cond;
+ SC_ATOMIC_DECLARE(int, sync_now);
+ Packet *head;
+} __attribute__((aligned(CLS))) PktPoolLockedStack;
+
+typedef struct PktPool_ {
+ /* link listed of free packets local to this thread.
+ * No mutex is needed.
+ */
+ Packet *head;
+ /* Packets waiting (pending) to be returned to the given Packet
+ * Pool. Accumulate packets for the same pool until a theshold is
+ * reached, then return them all at once. Keep the head and tail
+ * to fast insertion of the entire list onto a return stack.
+ */
+ struct PktPool_ *pending_pool;
+ Packet *pending_head;
+ Packet *pending_tail;
+ uint32_t pending_count;
+
+#ifdef DEBUG_VALIDATION
+ int initialized;
+ int destroyed;
+#endif /* DEBUG_VALIDATION */
+
+ /* All members above this point are accessed locally by only one thread, so
+ * these should live on their own cache line.
+ */
+
+ /* Return stack, where other threads put packets that they free that belong
+ * to this thread.
+ */
+ PktPoolLockedStack return_stack;
+} PktPool;
+
+Packet *TmqhInputPacketpool(ThreadVars *);
+void TmqhOutputPacketpool(ThreadVars *, Packet *);
+void TmqhReleasePacketsToPacketPool(PacketQueue *);
+void TmqhPacketpoolRegister(void);
+Packet *PacketPoolGetPacket(void);
+void PacketPoolWait(void);
+void PacketPoolWaitForN(int n);
+void PacketPoolReturnPacket(Packet *p);
+void PacketPoolInit(void);
+void PacketPoolInitEmpty(void);
+void PacketPoolDestroy(void);
+
+#endif /* __TMQH_PACKETPOOL_H__ */
diff --git a/framework/src/suricata/src/tmqh-ringbuffer.c b/framework/src/suricata/src/tmqh-ringbuffer.c
new file mode 100644
index 00000000..29228e1a
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-ringbuffer.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * RingBuffer queue handler
+ */
+
+#include "suricata.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+
+#include "tm-queuehandlers.h"
+
+#include "util-ringbuffer.h"
+
+static RingBuffer8 *ringbuffers[256];
+
+Packet *TmqhInputRingBufferMrSw(ThreadVars *t);
+void TmqhOutputRingBufferMrSw(ThreadVars *t, Packet *p);
+Packet *TmqhInputRingBufferSrSw(ThreadVars *t);
+void TmqhOutputRingBufferSrSw(ThreadVars *t, Packet *p);
+Packet *TmqhInputRingBufferSrMw(ThreadVars *t);
+void TmqhOutputRingBufferSrMw(ThreadVars *t, Packet *p);
+void TmqhInputRingBufferShutdownHandler(ThreadVars *);
+
+/**
+ * \brief TmqhRingBufferRegister
+ * \initonly
+ */
+void TmqhRingBufferRegister (void)
+{
+ tmqh_table[TMQH_RINGBUFFER_MRSW].name = "ringbuffer_mrsw";
+ tmqh_table[TMQH_RINGBUFFER_MRSW].InHandler = TmqhInputRingBufferMrSw;
+ tmqh_table[TMQH_RINGBUFFER_MRSW].InShutdownHandler = TmqhInputRingBufferShutdownHandler;
+ tmqh_table[TMQH_RINGBUFFER_MRSW].OutHandler = TmqhOutputRingBufferMrSw;
+
+ tmqh_table[TMQH_RINGBUFFER_SRSW].name = "ringbuffer_srsw";
+ tmqh_table[TMQH_RINGBUFFER_SRSW].InHandler = TmqhInputRingBufferSrSw;
+ tmqh_table[TMQH_RINGBUFFER_SRSW].InShutdownHandler = TmqhInputRingBufferShutdownHandler;
+ tmqh_table[TMQH_RINGBUFFER_SRSW].OutHandler = TmqhOutputRingBufferSrSw;
+
+ tmqh_table[TMQH_RINGBUFFER_SRMW].name = "ringbuffer_srmw";
+ tmqh_table[TMQH_RINGBUFFER_SRMW].InHandler = TmqhInputRingBufferSrMw;
+ tmqh_table[TMQH_RINGBUFFER_SRMW].InShutdownHandler = TmqhInputRingBufferShutdownHandler;
+ tmqh_table[TMQH_RINGBUFFER_SRMW].OutHandler = TmqhOutputRingBufferSrMw;
+
+ memset(ringbuffers, 0, sizeof(ringbuffers));
+
+ int i = 0;
+ for (i = 0; i < 256; i++) {
+ ringbuffers[i] = RingBuffer8Init();
+ if (ringbuffers[i] == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error allocating memory to register Ringbuffers. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+void TmqhRingBufferDestroy (void)
+{
+ int i = 0;
+ for (i = 0; i < 256; i++) {
+ RingBuffer8Destroy(ringbuffers[i]);
+ }
+}
+
+void TmqhInputRingBufferShutdownHandler(ThreadVars *tv)
+{
+ if (tv == NULL || tv->inq == NULL) {
+ return;
+ }
+
+ RingBuffer8 *rb = ringbuffers[tv->inq->id];
+ if (rb == NULL) {
+ return;
+ }
+
+ RingBuffer8Shutdown(rb);
+}
+
+Packet *TmqhInputRingBufferMrSw(ThreadVars *t)
+{
+ RingBuffer8 *rb = ringbuffers[t->inq->id];
+
+ Packet *p = (Packet *)RingBufferMrSw8Get(rb);
+
+ StatsSyncCountersIfSignalled(t);
+
+ return p;
+}
+
+void TmqhOutputRingBufferMrSw(ThreadVars *t, Packet *p)
+{
+ RingBuffer8 *rb = ringbuffers[t->outq->id];
+ RingBufferMrSw8Put(rb, (void *)p);
+}
+
+Packet *TmqhInputRingBufferSrSw(ThreadVars *t)
+{
+ RingBuffer8 *rb = ringbuffers[t->inq->id];
+
+ Packet *p = (Packet *)RingBufferSrSw8Get(rb);
+
+ StatsSyncCountersIfSignalled(t);
+
+ return p;
+}
+
+void TmqhOutputRingBufferSrSw(ThreadVars *t, Packet *p)
+{
+ RingBuffer8 *rb = ringbuffers[t->outq->id];
+ RingBufferSrSw8Put(rb, (void *)p);
+}
+
+Packet *TmqhInputRingBufferSrMw(ThreadVars *t)
+{
+ RingBuffer8 *rb = ringbuffers[t->inq->id];
+
+ Packet *p = (Packet *)RingBufferSrMw8Get(rb);
+
+ StatsSyncCountersIfSignalled(t);
+
+ return p;
+}
+
+void TmqhOutputRingBufferSrMw(ThreadVars *t, Packet *p)
+{
+ RingBuffer8 *rb = ringbuffers[t->outq->id];
+ RingBufferSrMw8Put(rb, (void *)p);
+}
+
diff --git a/framework/src/suricata/src/tmqh-ringbuffer.h b/framework/src/suricata/src/tmqh-ringbuffer.h
new file mode 100644
index 00000000..f38fce99
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-ringbuffer.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TMQH_RINGBUFFER_H__
+#define __TMQH_RINGBUFFER_H__
+
+void TmqhRingBufferRegister (void);
+void TmqhRingBufferDestroy (void);
+
+#endif /* __TMQH_RINGBUFFER_H__ */
diff --git a/framework/src/suricata/src/tmqh-simple.c b/framework/src/suricata/src/tmqh-simple.c
new file mode 100644
index 00000000..bc09dff3
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-simple.c
@@ -0,0 +1,155 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Simple queue handler
+ */
+
+#include "suricata.h"
+#include "packet-queue.h"
+#include "decode.h"
+#include "threads.h"
+#include "threadvars.h"
+
+#include "tm-queuehandlers.h"
+
+Packet *TmqhInputSimple(ThreadVars *t);
+void TmqhOutputSimple(ThreadVars *t, Packet *p);
+void TmqhInputSimpleShutdownHandler(ThreadVars *);
+
+void TmqhSimpleRegister (void)
+{
+ tmqh_table[TMQH_SIMPLE].name = "simple";
+ tmqh_table[TMQH_SIMPLE].InHandler = TmqhInputSimple;
+ tmqh_table[TMQH_SIMPLE].InShutdownHandler = TmqhInputSimpleShutdownHandler;
+ tmqh_table[TMQH_SIMPLE].OutHandler = TmqhOutputSimple;
+}
+
+Packet *TmqhInputSimple(ThreadVars *t)
+{
+ PacketQueue *q = &trans_q[t->inq->id];
+
+ StatsSyncCountersIfSignalled(t);
+
+ SCMutexLock(&q->mutex_q);
+
+ if (q->len == 0) {
+ /* if we have no packets in queue, wait... */
+ SCCondWait(&q->cond_q, &q->mutex_q);
+ }
+
+ if (q->len > 0) {
+ Packet *p = PacketDequeue(q);
+ SCMutexUnlock(&q->mutex_q);
+ return p;
+ } else {
+ /* return NULL if we have no pkt. Should only happen on signals. */
+ SCMutexUnlock(&q->mutex_q);
+ return NULL;
+ }
+}
+
+void TmqhInputSimpleShutdownHandler(ThreadVars *tv)
+{
+ int i;
+
+ if (tv == NULL || tv->inq == NULL) {
+ return;
+ }
+
+ for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++)
+ SCCondSignal(&trans_q[tv->inq->id].cond_q);
+}
+
+void TmqhOutputSimple(ThreadVars *t, Packet *p)
+{
+ SCLogDebug("Packet %p, p->root %p, alloced %s", p, p->root, p->flags & PKT_ALLOC ? "true":"false");
+
+ PacketQueue *q = &trans_q[t->outq->id];
+
+ SCMutexLock(&q->mutex_q);
+ PacketEnqueue(q, p);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+}
+
+/*******************************Generic-Q-Handlers*****************************/
+
+/**
+ * \brief Public version of TmqhInputSimple from the tmqh-simple queue
+ * handler, except that it is a generic version that is directly
+ * tied to a "SCDQDataQueue" instance(sent as an arg).
+ *
+ * Retrieves a data_instance from the queue. If the queue is empty, it
+ * waits on the queue, till a data_instance is enqueued into the queue
+ * by some other module.
+ *
+ * All references to "data_instance" means a reference to a data structure
+ * instance that implements the template "struct SCDQGenericQData_".
+ *
+ * \param q The SCDQDataQueue instance to wait on.
+ *
+ * \retval p The returned packet from the queue.
+ * \retval data The returned data_instance from the queue.
+ */
+SCDQGenericQData *TmqhInputSimpleOnQ(SCDQDataQueue *q)
+{
+ SCMutexLock(&q->mutex_q);
+ if (q->len == 0) {
+ /* if we have no packets in queue, wait... */
+ SCCondWait(&q->cond_q, &q->mutex_q);
+ }
+
+ if (q->len > 0) {
+ SCDQGenericQData *data = SCDQDataDequeue(q);
+ SCMutexUnlock(&q->mutex_q);
+ return data;
+ } else {
+ /* return NULL if we have no data in the queue. Should only happen
+ * on signals. */
+ SCMutexUnlock(&q->mutex_q);
+ return NULL;
+ }
+}
+
+/**
+ * \brief Public version of TmqhOutputSimple from the tmqh-simple queue
+ * handler, except that it is a generic version that is directly
+ * tied to a SCDQDataQueue instance(sent as an arg).
+ *
+ * Pumps out a data_instance into the queue. If the queue is empty, it
+ * waits on the queue, till a data_instance is enqueued into the queue.
+ *
+ * All references to "data_instance" means a reference to a data structure
+ * instance that implements the template "struct SCDQGenericQData_".
+ *
+ * \param q The SCDQDataQueue instance to pump the data into.
+ * \param data The data instance to be enqueued.
+ */
+void TmqhOutputSimpleOnQ(SCDQDataQueue *q, SCDQGenericQData *data)
+{
+ SCMutexLock(&q->mutex_q);
+ SCDQDataEnqueue(q, data);
+ SCCondSignal(&q->cond_q);
+ SCMutexUnlock(&q->mutex_q);
+
+ return;
+}
diff --git a/framework/src/suricata/src/tmqh-simple.h b/framework/src/suricata/src/tmqh-simple.h
new file mode 100644
index 00000000..1d4417b4
--- /dev/null
+++ b/framework/src/suricata/src/tmqh-simple.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __TMQH_SIMPLE_H__
+#define __TMQH_SIMPLE_H__
+
+#include "data-queue.h"
+
+SCDQGenericQData *TmqhInputSimpleOnQ(SCDQDataQueue *);
+void TmqhOutputSimpleOnQ(SCDQDataQueue *, SCDQGenericQData *);
+
+void TmqhSimpleRegister (void);
+
+#endif /* __TMQH_SIMPLE_H__ */
diff --git a/framework/src/suricata/src/unix-manager.c b/framework/src/suricata/src/unix-manager.c
new file mode 100644
index 00000000..1960df56
--- /dev/null
+++ b/framework/src/suricata/src/unix-manager.c
@@ -0,0 +1,1030 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "unix-manager.h"
+#include "detect-engine.h"
+#include "tm-threads.h"
+#include "runmodes.h"
+#include "conf.h"
+
+#include "util-privs.h"
+#include "util-debug.h"
+#include "util-signal.h"
+
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef BUILD_UNIX_SOCKET
+#include <jansson.h>
+
+// MSG_NOSIGNAL does not exists on OS X
+#ifdef OS_DARWIN
+# ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL SO_NOSIGPIPE
+# endif
+#endif
+
+#define SOCKET_PATH LOCAL_STATE_DIR "/run/suricata/"
+#define SOCKET_FILENAME "suricata-command.socket"
+#define SOCKET_TARGET SOCKET_PATH SOCKET_FILENAME
+
+typedef struct Command_ {
+ char *name;
+ TmEcode (*Func)(json_t *, json_t *, void *);
+ void *data;
+ int flags;
+ TAILQ_ENTRY(Command_) next;
+} Command;
+
+typedef struct Task_ {
+ TmEcode (*Func)(void *);
+ void *data;
+ TAILQ_ENTRY(Task_) next;
+} Task;
+
+typedef struct UnixClient_ {
+ int fd;
+ TAILQ_ENTRY(UnixClient_) next;
+} UnixClient;
+
+typedef struct UnixCommand_ {
+ time_t start_timestamp;
+ int socket;
+ struct sockaddr_un client_addr;
+ int select_max;
+ TAILQ_HEAD(, Command_) commands;
+ TAILQ_HEAD(, Task_) tasks;
+ TAILQ_HEAD(, UnixClient_) clients;
+} UnixCommand;
+
+/**
+ * \brief Create a command unix socket on system
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+int UnixNew(UnixCommand * this)
+{
+ struct sockaddr_un addr;
+ int len;
+ int ret;
+ int on = 1;
+ char *sockettarget = NULL;
+ char *socketname;
+
+ this->start_timestamp = time(NULL);
+ this->socket = -1;
+ this->select_max = 0;
+
+ TAILQ_INIT(&this->commands);
+ TAILQ_INIT(&this->tasks);
+ TAILQ_INIT(&this->clients);
+
+ if (ConfGet("unix-command.filename", &socketname) == 1) {
+ if (PathIsAbsolute(socketname)) {
+ sockettarget = SCStrdup(socketname);
+ if (unlikely(sockettarget == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate socket name");
+ return 0;
+ }
+ } else {
+ int socketlen = strlen(SOCKET_PATH) + strlen(socketname) + 2;
+ sockettarget = SCMalloc(socketlen);
+ if (unlikely(sockettarget == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate socket name");
+ return 0;
+ }
+ snprintf(sockettarget, socketlen, "%s/%s", SOCKET_PATH, socketname);
+
+ /* Create socket dir */
+ ret = mkdir(SOCKET_PATH, S_IRWXU|S_IXGRP|S_IRGRP);
+ if ( ret != 0 ) {
+ int err = errno;
+ if (err != EEXIST) {
+ SCFree(sockettarget);
+ SCLogError(SC_ERR_OPENING_FILE,
+ "Cannot create socket directory %s: %s", SOCKET_PATH, strerror(err));
+ return 0;
+ }
+ }
+
+ }
+ SCLogInfo("Using unix socket file '%s'", sockettarget);
+ }
+ if (sockettarget == NULL) {
+ sockettarget = SCStrdup(SOCKET_TARGET);
+ if (unlikely(sockettarget == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate socket name");
+ return 0;
+ }
+ }
+
+ /* Remove socket file */
+ (void) unlink(sockettarget);
+
+ /* set address */
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, sockettarget, sizeof(addr.sun_path));
+ addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
+ len = strlen(addr.sun_path) + sizeof(addr.sun_family);
+
+ /* create socket */
+ this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (this->socket == -1) {
+ SCLogWarning(SC_ERR_OPENING_FILE,
+ "Unix Socket: unable to create UNIX socket %s: %s",
+ addr.sun_path, strerror(errno));
+ SCFree(sockettarget);
+ return 0;
+ }
+ this->select_max = this->socket + 1;
+
+ /* Set file mode: will not fully work on most system, the group
+ * permission is not changed on some Linux and *BSD won't do the
+ * chmod. */
+ ret = fchmod(this->socket, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+ if (ret == -1) {
+ int err = errno;
+ SCLogWarning(SC_ERR_INITIALIZATION,
+ "Unable to change permission on socket: %s (%d)",
+ strerror(err),
+ err);
+ }
+ /* set reuse option */
+ ret = setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof(on));
+ if ( ret != 0 ) {
+ SCLogWarning(SC_ERR_INITIALIZATION,
+ "Cannot set sockets options: %s.", strerror(errno));
+ }
+
+ /* bind socket */
+ ret = bind(this->socket, (struct sockaddr *) &addr, len);
+ if (ret == -1) {
+ SCLogWarning(SC_ERR_INITIALIZATION,
+ "Unix socket: UNIX socket bind(%s) error: %s",
+ sockettarget, strerror(errno));
+ SCFree(sockettarget);
+ return 0;
+ }
+
+ /* listen */
+ if (listen(this->socket, 1) == -1) {
+ SCLogWarning(SC_ERR_INITIALIZATION,
+ "Command server: UNIX socket listen() error: %s",
+ strerror(errno));
+ SCFree(sockettarget);
+ return 0;
+ }
+ SCFree(sockettarget);
+ return 1;
+}
+
+void UnixCommandSetMaxFD(UnixCommand *this)
+{
+ UnixClient *item;
+
+ if (this == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Unix command is NULL, warn devel");
+ return;
+ }
+
+ this->select_max = this->socket + 1;
+ TAILQ_FOREACH(item, &this->clients, next) {
+ if (item->fd >= this->select_max) {
+ this->select_max = item->fd + 1;
+ }
+ }
+}
+
+/**
+ * \brief Close the unix socket
+ */
+void UnixCommandClose(UnixCommand *this, int fd)
+{
+ UnixClient *item;
+ int found = 0;
+
+ TAILQ_FOREACH(item, &this->clients, next) {
+ if (item->fd == fd) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "No fd found in client list");
+ return;
+ }
+
+ TAILQ_REMOVE(&this->clients, item, next);
+
+ close(item->fd);
+ UnixCommandSetMaxFD(this);
+ SCFree(item);
+}
+
+/**
+ * \brief Callback function used to send message to socket
+ */
+int UnixCommandSendCallback(const char *buffer, size_t size, void *data)
+{
+ int fd = *(int *) data;
+
+ if (send(fd, buffer, size, MSG_NOSIGNAL) == -1) {
+ SCLogInfo("Unable to send block: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+#define UNIX_PROTO_VERSION_LENGTH 200
+#define UNIX_PROTO_VERSION "0.1"
+
+/**
+ * \brief Accept a new client on unix socket
+ *
+ * The function is called when a new user is detected
+ * in UnixMain(). It does the initial protocol negotiation
+ * with client.
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+int UnixCommandAccept(UnixCommand *this)
+{
+ char buffer[UNIX_PROTO_VERSION_LENGTH + 1];
+ json_t *client_msg;
+ json_t *server_msg;
+ json_t *version;
+ json_error_t jerror;
+ int client;
+ int ret;
+ UnixClient *uclient = NULL;
+
+ /* accept client socket */
+ socklen_t len = sizeof(this->client_addr);
+ client = accept(this->socket, (struct sockaddr *) &this->client_addr,
+ &len);
+ if (client < 0) {
+ SCLogInfo("Unix socket: accept() error: %s",
+ strerror(errno));
+ return 0;
+ }
+ SCLogDebug("Unix socket: client connection");
+
+ /* read client version */
+ buffer[sizeof(buffer)-1] = 0;
+ ret = recv(client, buffer, sizeof(buffer)-1, 0);
+ if (ret < 0) {
+ SCLogInfo("Command server: client doesn't send version");
+ close(client);
+ return 0;
+ }
+ if (ret >= (int)(sizeof(buffer)-1)) {
+ SCLogInfo("Command server: client message is too long, "
+ "disconnect him.");
+ close(client);
+ return 0;
+ }
+ buffer[ret] = 0;
+
+ client_msg = json_loads(buffer, 0, &jerror);
+ if (client_msg == NULL) {
+ SCLogInfo("Invalid command, error on line %d: %s\n", jerror.line, jerror.text);
+ close(client);
+ return 0;
+ }
+
+ version = json_object_get(client_msg, "version");
+ if (!json_is_string(version)) {
+ SCLogInfo("error: version is not a string");
+ close(client);
+ json_decref(client_msg);
+ return 0;
+ }
+
+ /* check client version */
+ if (strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0) {
+ SCLogInfo("Unix socket: invalid client version: \"%s\"",
+ json_string_value(version));
+ json_decref(client_msg);
+ close(client);
+ return 0;
+ } else {
+ SCLogInfo("Unix socket: client version: \"%s\"",
+ json_string_value(version));
+ }
+
+ json_decref(client_msg);
+ /* send answer */
+ server_msg = json_object();
+ if (server_msg == NULL) {
+ close(client);
+ return 0;
+ }
+ json_object_set_new(server_msg, "return", json_string("OK"));
+
+ if (json_dump_callback(server_msg, UnixCommandSendCallback, &client, 0) == -1) {
+ SCLogWarning(SC_ERR_SOCKET, "Unable to send command");
+ json_decref(server_msg);
+ close(client);
+ return 0;
+ }
+ json_decref(server_msg);
+
+ /* client connected */
+ SCLogInfo("Unix socket: client connected");
+
+ uclient = SCMalloc(sizeof(UnixClient));
+ if (unlikely(uclient == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate new cient");
+ return 0;
+ }
+ uclient->fd = client;
+ TAILQ_INSERT_TAIL(&this->clients, uclient, next);
+ UnixCommandSetMaxFD(this);
+ return 1;
+}
+
+int UnixCommandBackgroundTasks(UnixCommand* this)
+{
+ int ret = 1;
+ Task *ltask;
+
+ TAILQ_FOREACH(ltask, &this->tasks, next) {
+ int fret = ltask->Func(ltask->data);
+ if (fret != TM_ECODE_OK) {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+/**
+ * \brief Command dispatcher
+ *
+ * \param this a UnixCommand:: structure
+ * \param command a string containing a json formatted
+ * command
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+int UnixCommandExecute(UnixCommand * this, char *command, UnixClient *client)
+{
+ int ret = 1;
+ json_error_t error;
+ json_t *jsoncmd = NULL;
+ json_t *cmd = NULL;
+ json_t *server_msg = json_object();
+ const char * value;
+ int found = 0;
+ Command *lcmd;
+
+ if (server_msg == NULL) {
+ return 0;
+ }
+
+ jsoncmd = json_loads(command, 0, &error);
+ if (jsoncmd == NULL) {
+ SCLogInfo("Invalid command, error on line %d: %s\n", error.line, error.text);
+ goto error;
+ }
+
+ cmd = json_object_get(jsoncmd, "command");
+ if(!json_is_string(cmd)) {
+ SCLogInfo("error: command is not a string");
+ goto error_cmd;
+ }
+ value = json_string_value(cmd);
+
+ TAILQ_FOREACH(lcmd, &this->commands, next) {
+ if (!strcmp(value, lcmd->name)) {
+ int fret = TM_ECODE_OK;
+ found = 1;
+ if (lcmd->flags & UNIX_CMD_TAKE_ARGS) {
+ cmd = json_object_get(jsoncmd, "arguments");
+ if(!json_is_object(cmd)) {
+ SCLogInfo("error: argument is not an object");
+ goto error_cmd;
+ }
+ }
+ fret = lcmd->Func(cmd, server_msg, lcmd->data);
+ if (fret != TM_ECODE_OK) {
+ ret = 0;
+ }
+ break;
+ }
+ }
+
+ if (found == 0) {
+ json_object_set_new(server_msg, "message", json_string("Unknown command"));
+ ret = 0;
+ }
+
+ switch (ret) {
+ case 0:
+ json_object_set_new(server_msg, "return", json_string("NOK"));
+ break;
+ case 1:
+ json_object_set_new(server_msg, "return", json_string("OK"));
+ break;
+ }
+
+ /* send answer */
+ if (json_dump_callback(server_msg, UnixCommandSendCallback, &client->fd, 0) == -1) {
+ SCLogWarning(SC_ERR_SOCKET, "Unable to send command");
+ goto error_cmd;
+ }
+
+ json_decref(jsoncmd);
+ json_decref(server_msg);
+ return ret;
+
+error_cmd:
+ json_decref(jsoncmd);
+error:
+ json_decref(server_msg);
+ UnixCommandClose(this, client->fd);
+ return 0;
+}
+
+void UnixCommandRun(UnixCommand * this, UnixClient *client)
+{
+ char buffer[4096];
+ int ret;
+ ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0);
+ if (ret <= 0) {
+ if (ret == 0) {
+ SCLogInfo("Unix socket: lost connection with client");
+ } else {
+ SCLogInfo("Unix socket: error on recv() from client: %s",
+ strerror(errno));
+ }
+ UnixCommandClose(this, client->fd);
+ return;
+ }
+ if (ret >= (int)(sizeof(buffer)-1)) {
+ SCLogInfo("Command server: client command is too long, "
+ "disconnect him.");
+ UnixCommandClose(this, client->fd);
+ }
+ buffer[ret] = 0;
+ UnixCommandExecute(this, buffer, client);
+}
+
+/**
+ * \brief Select function
+ *
+ * \retval 0 in case of error, 1 in case of success
+ */
+int UnixMain(UnixCommand * this)
+{
+ struct timeval tv;
+ int ret;
+ fd_set select_set;
+ UnixClient *uclient;
+ UnixClient *tclient;
+
+ /* Wait activity on the socket */
+ FD_ZERO(&select_set);
+ FD_SET(this->socket, &select_set);
+ TAILQ_FOREACH(uclient, &this->clients, next) {
+ FD_SET(uclient->fd, &select_set);
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 200 * 1000;
+ ret = select(this->select_max, &select_set, NULL, NULL, &tv);
+
+ /* catch select() error */
+ if (ret == -1) {
+ /* Signal was caught: just ignore it */
+ if (errno == EINTR) {
+ return 1;
+ }
+ SCLogInfo("Command server: select() fatal error: %s", strerror(errno));
+ return 0;
+ }
+
+ if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
+ return 1;
+ }
+
+ /* timeout: continue */
+ if (ret == 0) {
+ return 1;
+ }
+
+ TAILQ_FOREACH_SAFE(uclient, &this->clients, next, tclient) {
+ if (FD_ISSET(uclient->fd, &select_set)) {
+ UnixCommandRun(this, uclient);
+ }
+ }
+ if (FD_ISSET(this->socket, &select_set)) {
+ if (!UnixCommandAccept(this))
+ return 1;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Used to kill unix manager thread(s).
+ *
+ * \todo Kinda hackish since it uses the tv name to identify unix manager
+ * thread. We need an all weather identification scheme.
+ */
+void UnixKillUnixManagerThread(void)
+{
+ ThreadVars *tv = NULL;
+ int cnt = 0;
+
+ SCCtrlCondSignal(&unix_manager_ctrl_cond);
+
+ SCMutexLock(&tv_root_lock);
+
+ /* flow manager thread(s) is/are a part of mgmt threads */
+ tv = tv_root[TVT_CMD];
+
+ while (tv != NULL) {
+ if (strcasecmp(tv->name, "UnixManagerThread") == 0) {
+ TmThreadsSetFlag(tv, THV_KILL);
+ TmThreadsSetFlag(tv, THV_DEINIT);
+
+ /* be sure it has shut down */
+ while (!TmThreadsCheckFlag(tv, THV_CLOSED)) {
+ usleep(100);
+ }
+ cnt++;
+ }
+ tv = tv->next;
+ }
+
+ /* not possible, unless someone decides to rename UnixManagerThread */
+ if (cnt == 0) {
+ SCMutexUnlock(&tv_root_lock);
+ abort();
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+ return;
+}
+
+
+TmEcode UnixManagerShutdownCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ json_object_set_new(server_msg, "message", json_string("Closing Suricata"));
+ EngineStop();
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerVersionCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ json_object_set_new(server_msg, "message", json_string(
+#ifdef REVISION
+ PROG_VER xstr(REVISION)
+#elif defined RELEASE
+ PROG_VER " RELEASE"
+#else
+ PROG_VER
+#endif
+ ));
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerUptimeCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ int uptime;
+ UnixCommand *ucmd = (UnixCommand *)data;
+
+ uptime = time(NULL) - ucmd->start_timestamp;
+ json_object_set_new(server_msg, "message", json_integer(uptime));
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerRunningModeCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ json_object_set_new(server_msg, "message", json_string(RunmodeGetActive()));
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerCaptureModeCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ json_object_set_new(server_msg, "message", json_string(RunModeGetMainMode()));
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerReloadRules(json_t *cmd, json_t *server_msg, void *data)
+{
+ SCEnter();
+ DetectEngineReloadStart();
+
+ while (DetectEngineReloadIsDone() == 0)
+ usleep(100);
+
+ json_object_set_new(server_msg, "message", json_string("done"));
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode UnixManagerConfGetCommand(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+
+ char *confval = NULL;
+ char *variable = NULL;
+
+ json_t *jarg = json_object_get(cmd, "variable");
+ if(!json_is_string(jarg)) {
+ SCLogInfo("error: variable is not a string");
+ json_object_set_new(server_msg, "message", json_string("variable is not a string"));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ variable = (char *)json_string_value(jarg);
+ if (ConfGet(variable, &confval) != 1) {
+ json_object_set_new(server_msg, "message", json_string("Unable to get value"));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (confval) {
+ json_object_set_new(server_msg, "message", json_string(confval));
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ json_object_set_new(server_msg, "message", json_string("No string value"));
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+TmEcode UnixManagerListCommand(json_t *cmd,
+ json_t *answer, void *data)
+{
+ SCEnter();
+ json_t *jdata;
+ json_t *jarray;
+ Command *lcmd = NULL;
+ UnixCommand *gcmd = (UnixCommand *) data;
+ int i = 0;
+
+ jdata = json_object();
+ if (jdata == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+ jarray = json_array();
+ if (jarray == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+
+ TAILQ_FOREACH(lcmd, &gcmd->commands, next) {
+ json_array_append(jarray, json_string(lcmd->name));
+ i++;
+ }
+
+ json_object_set_new(jdata, "count", json_integer(i));
+ json_object_set_new(jdata, "commands", jarray);
+ json_object_set_new(answer, "message", jdata);
+ SCReturnInt(TM_ECODE_OK);
+}
+
+
+#if 0
+TmEcode UnixManagerReloadRules(json_t *cmd,
+ json_t *server_msg, void *data)
+{
+ SCEnter();
+ if (suricata_ctl_flags != 0) {
+ json_object_set_new(server_msg, "message",
+ json_string("Live rule swap no longer possible."
+ " Engine in shutdown mode."));
+ SCReturn(TM_ECODE_FAILED);
+ } else {
+ /* FIXME : need to check option value */
+ UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2Idle);
+ DetectEngineSpawnLiveRuleSwapMgmtThread();
+ json_object_set_new(server_msg, "message", json_string("Reloading rules"));
+ }
+ SCReturn(TM_ECODE_OK);
+}
+#endif
+
+static UnixCommand command;
+
+/**
+ * \brief Add a command to the list of commands
+ *
+ * This function adds a command to the list of commands available
+ * through the unix socket.
+ *
+ * When a command is received from user through the unix socket, the content
+ * of 'Command' field in the JSON message is match against keyword, then the
+ * Func is called. See UnixSocketAddPcapFile() for an example.
+ *
+ * \param keyword name of the command
+ * \param Func function to run when command is received
+ * \param data a pointer to data that are passed to Func when it is run
+ * \param flags a flag now used to tune the command type
+ * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure
+ */
+TmEcode UnixManagerRegisterCommand(const char * keyword,
+ TmEcode (*Func)(json_t *, json_t *, void *),
+ void *data, int flags)
+{
+ SCEnter();
+ Command *cmd = NULL;
+ Command *lcmd = NULL;
+
+ if (Func == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Null function");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (keyword == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Null keyword");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ TAILQ_FOREACH(lcmd, &command.commands, next) {
+ if (!strcmp(keyword, lcmd->name)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "%s already registered", keyword);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ cmd = SCMalloc(sizeof(Command));
+ if (unlikely(cmd == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't alloc cmd");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ cmd->name = SCStrdup(keyword);
+ if (unlikely(cmd->name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't alloc cmd name");
+ SCFree(cmd);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ cmd->Func = Func;
+ cmd->data = data;
+ cmd->flags = flags;
+ /* Add it to the list */
+ TAILQ_INSERT_TAIL(&command.commands, cmd, next);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Add a task to the list of tasks
+ *
+ * This function adds a task to run in the background. The task is run
+ * each time the UnixMain() function exits from select.
+ *
+ * \param Func function to run when a command is received
+ * \param data a pointer to data that are passed to Func when it is run
+ * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure
+ */
+TmEcode UnixManagerRegisterBackgroundTask(TmEcode (*Func)(void *),
+ void *data)
+{
+ SCEnter();
+ Task *task = NULL;
+
+ if (Func == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Null function");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ task = SCMalloc(sizeof(Task));
+ if (unlikely(task == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't alloc task");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ task->Func = Func;
+ task->data = data;
+ /* Add it to the list */
+ TAILQ_INSERT_TAIL(&command.tasks, task, next);
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+typedef struct UnixManagerThreadData_ {
+ int padding;
+} UnixManagerThreadData;
+
+static TmEcode UnixManagerThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ UnixManagerThreadData *utd = SCCalloc(1, sizeof(*utd));
+ if (utd == NULL)
+ return TM_ECODE_FAILED;
+
+ *data = utd;
+ return TM_ECODE_OK;
+}
+
+static TmEcode UnixManagerThreadDeinit(ThreadVars *t, void *data)
+{
+ SCFree(data);
+ return TM_ECODE_OK;
+}
+
+static TmEcode UnixManager(ThreadVars *th_v, void *thread_data)
+{
+ int ret;
+
+ /* set the thread name */
+ SCLogDebug("%s started...", th_v->name);
+
+ StatsSetupPrivate(th_v);
+
+ if (UnixNew(&command) == 0) {
+ int failure_fatal = 0;
+ SCLogError(SC_ERR_INITIALIZATION,
+ "Unable to create unix command socket");
+ if (ConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) {
+ SCLogDebug("ConfGetBool could not load the value.");
+ }
+ if (failure_fatal) {
+ exit(EXIT_FAILURE);
+ } else {
+ return TM_ECODE_FAILED;
+ }
+ }
+
+ /* Set the threads capability */
+ th_v->cap_flags = 0;
+ SCDropCaps(th_v);
+
+ /* Init Unix socket */
+ UnixManagerRegisterCommand("shutdown", UnixManagerShutdownCommand, NULL, 0);
+ UnixManagerRegisterCommand("command-list", UnixManagerListCommand, &command, 0);
+ UnixManagerRegisterCommand("help", UnixManagerListCommand, &command, 0);
+ UnixManagerRegisterCommand("version", UnixManagerVersionCommand, &command, 0);
+ UnixManagerRegisterCommand("uptime", UnixManagerUptimeCommand, &command, 0);
+ UnixManagerRegisterCommand("running-mode", UnixManagerRunningModeCommand, &command, 0);
+ UnixManagerRegisterCommand("capture-mode", UnixManagerCaptureModeCommand, &command, 0);
+ UnixManagerRegisterCommand("conf-get", UnixManagerConfGetCommand, &command, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("dump-counters", StatsOutputCounterSocket, NULL, 0);
+ UnixManagerRegisterCommand("reload-rules", UnixManagerReloadRules, NULL, 0);
+ UnixManagerRegisterCommand("register-tenant-handler", UnixSocketRegisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("unregister-tenant-handler", UnixSocketUnregisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("register-tenant", UnixSocketRegisterTenant, &command, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("reload-tenant", UnixSocketReloadTenant, &command, UNIX_CMD_TAKE_ARGS);
+ UnixManagerRegisterCommand("unregister-tenant", UnixSocketUnregisterTenant, &command, UNIX_CMD_TAKE_ARGS);
+
+
+ TmThreadsSetFlag(th_v, THV_INIT_DONE);
+ while (1) {
+ ret = UnixMain(&command);
+ if (ret == 0) {
+ SCLogError(SC_ERR_FATAL, "Fatal error on unix socket");
+ }
+
+ if ((ret == 0) || (TmThreadsCheckFlag(th_v, THV_KILL))) {
+ UnixClient *item;
+ UnixClient *titem;
+ TAILQ_FOREACH_SAFE(item, &(&command)->clients, next, titem) {
+ close(item->fd);
+ SCFree(item);
+ }
+ StatsSyncCounters(th_v);
+ break;
+ }
+
+ UnixCommandBackgroundTasks(&command);
+ }
+ return TM_ECODE_OK;
+}
+
+
+/** \brief Spawn the unix socket manager thread
+ *
+ * \param mode if set to 1, init failure cause suricata exit
+ * */
+void UnixManagerThreadSpawn(int mode)
+{
+ ThreadVars *tv_unixmgr = NULL;
+
+ SCCtrlCondInit(&unix_manager_ctrl_cond, NULL);
+ SCCtrlMutexInit(&unix_manager_ctrl_mutex, NULL);
+
+ tv_unixmgr = TmThreadCreateCmdThreadByName("UnixManagerThread",
+ "UnixManager", 0);
+
+ if (tv_unixmgr == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ if (TmThreadSpawn(tv_unixmgr) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_INITIALIZATION, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ if (mode == 1) {
+ if (TmThreadsCheckFlag(tv_unixmgr, THV_RUNNING_DONE)) {
+ SCLogError(SC_ERR_INITIALIZATION, "Unix socket init failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ return;
+}
+
+/**
+ * \brief Used to kill unix manager thread(s).
+ *
+ * \todo Kinda hackish since it uses the tv name to identify unix manager
+ * thread. We need an all weather identification scheme.
+ */
+void UnixSocketKillSocketThread(void)
+{
+ ThreadVars *tv = NULL;
+
+ SCMutexLock(&tv_root_lock);
+
+ /* unix manager thread(s) is/are a part of command threads */
+ tv = tv_root[TVT_CMD];
+
+ while (tv != NULL) {
+ if (strcasecmp(tv->name, "UnixManagerThread") == 0) {
+ /* If the thread dies during init it will have
+ * THV_RUNNING_DONE set, so we can set the correct flag
+ * and exit.
+ */
+ if (TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
+ TmThreadsSetFlag(tv, THV_KILL);
+ TmThreadsSetFlag(tv, THV_DEINIT);
+ TmThreadsSetFlag(tv, THV_CLOSED);
+ break;
+ }
+ TmThreadsSetFlag(tv, THV_KILL);
+ TmThreadsSetFlag(tv, THV_DEINIT);
+ /* Be sure it has shut down */
+ while (!TmThreadsCheckFlag(tv, THV_CLOSED)) {
+ usleep(100);
+ }
+ }
+ tv = tv->next;
+ }
+
+ SCMutexUnlock(&tv_root_lock);
+ return;
+}
+
+#else /* BUILD_UNIX_SOCKET */
+
+void UnixManagerThreadSpawn(int mode)
+{
+ SCLogError(SC_ERR_UNIMPLEMENTED, "Unix socket is not compiled");
+ return;
+}
+
+void UnixSocketKillSocketThread(void)
+{
+ return;
+}
+
+#endif /* BUILD_UNIX_SOCKET */
+
+void TmModuleUnixManagerRegister (void)
+{
+#ifdef BUILD_UNIX_SOCKET
+ tmm_modules[TMM_UNIXMANAGER].name = "UnixManager";
+ tmm_modules[TMM_UNIXMANAGER].ThreadInit = UnixManagerThreadInit;
+ tmm_modules[TMM_UNIXMANAGER].ThreadDeinit = UnixManagerThreadDeinit;
+ tmm_modules[TMM_UNIXMANAGER].Management = UnixManager;
+ tmm_modules[TMM_UNIXMANAGER].cap_flags = 0;
+ tmm_modules[TMM_UNIXMANAGER].flags = TM_FLAG_COMMAND_TM;
+#endif /* BUILD_UNIX_SOCKET */
+}
diff --git a/framework/src/suricata/src/unix-manager.h b/framework/src/suricata/src/unix-manager.h
new file mode 100644
index 00000000..848da761
--- /dev/null
+++ b/framework/src/suricata/src/unix-manager.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef UNIX_MANAGER_H
+#define UNIX_MANAGER_H
+
+#ifdef BUILD_UNIX_SOCKET
+#include <jansson.h>
+#endif
+
+#define UNIX_CMD_TAKE_ARGS 1
+
+SCCtrlCondT unix_manager_ctrl_cond;
+SCCtrlMutex unix_manager_ctrl_mutex;
+
+void UnixManagerThreadSpawn(int mode);
+void UnixSocketKillSocketThread(void);
+
+
+#ifdef BUILD_UNIX_SOCKET
+TmEcode UnixManagerRegisterCommand(const char * keyword,
+ TmEcode (*Func)(json_t *, json_t *, void *),
+ void *data, int flags);
+TmEcode UnixManagerRegisterBackgroundTask(
+ TmEcode (*Func)(void *),
+ void *data);
+#endif
+
+void TmModuleUnixManagerRegister(void);
+
+#endif /* UNIX_MANAGER_H */
diff --git a/framework/src/suricata/src/util-action.c b/framework/src/suricata/src/util-action.c
new file mode 100644
index 00000000..2b349748
--- /dev/null
+++ b/framework/src/suricata/src/util-action.c
@@ -0,0 +1,1627 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#include "suricata-common.h"
+
+#include "action-globals.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-sigorder.h"
+
+#include "util-unittest.h"
+#include "util-action.h"
+#include "util-unittest-helper.h"
+#include "util-debug.h"
+
+/* Default order: */
+uint8_t action_order_sigs[4] = {ACTION_PASS, ACTION_DROP, ACTION_REJECT, ACTION_ALERT};
+/* This order can be changed from config */
+
+/**
+ * \brief Return the priority associated to an action (to order sigs
+ * as specified at config)
+ * action_order_sigs has this priority by index val
+ * so action_order_sigs[0] has to be inspected first.
+ * This function is called from detect-engine-sigorder
+ * \param action can be one of ACTION_PASS, ACTION_DROP,
+ * ACTION_REJECT or ACTION_ALERT
+ * \retval uint8_t the priority (order of this actions)
+ */
+uint8_t ActionOrderVal(uint8_t action)
+{
+ /* reject_both and reject_dst have the same prio as reject */
+ if( (action & ACTION_REJECT) ||
+ (action & ACTION_REJECT_BOTH) ||
+ (action & ACTION_REJECT_DST)) {
+ action = ACTION_REJECT;
+ }
+ uint8_t i = 0;
+ for (; i < 4; i++) {
+ if (action_order_sigs[i] == action)
+ return i;
+ }
+ /* Unknown action, set just a low prio (high val) */
+ return 10;
+}
+
+/**
+ * \brief Return the ACTION_* bit from their ascii value
+ * \param action can be one of "pass", "drop",
+ * "reject" or "alert"
+ * \retval uint8_t can be one of ACTION_PASS, ACTION_DROP,
+ * ACTION_REJECT or ACTION_ALERT
+ */
+uint8_t ActionAsciiToFlag(char *action)
+{
+ if (strcmp(action,"pass") == 0)
+ return ACTION_PASS;
+ if (strcmp(action,"drop") == 0)
+ return ACTION_DROP;
+ if (strcmp(action,"reject") == 0)
+ return ACTION_REJECT;
+ if (strcmp(action,"alert") == 0)
+ return ACTION_ALERT;
+
+ return 0;
+}
+
+/**
+ * \brief Load the action order from config. If none is provided,
+ * it will be default to ACTION_PASS, ACTION_DROP,
+ * ACTION_REJECT, ACTION_ALERT (pass has the highest prio)
+ *
+ * \retval 0 on success; -1 on fatal error;
+ */
+int ActionInitConfig()
+{
+ uint8_t actions_used = 0;
+ uint8_t action_flag = 0;
+ uint8_t actions_config[4] = {0, 0, 0, 0};
+ int order = 0;
+
+ ConfNode *action_order;
+ ConfNode *action = NULL;
+
+ /* Let's load the order of actions from the general config */
+ action_order = ConfGetNode("action-order");
+ if (action_order == NULL) {
+ /* No configuration, use defaults. */
+ return 0;
+ }
+ else {
+ TAILQ_FOREACH(action, &action_order->head, next) {
+ SCLogDebug("Loading action order : %s", action->val);
+ action_flag = ActionAsciiToFlag(action->val);
+ if (action_flag == 0) {
+ SCLogError(SC_ERR_ACTION_ORDER, "action-order, invalid action: \"%s\". Please, use"
+ " \"pass\",\"drop\",\"alert\",\"reject\". You have"
+ " to specify all of them, without quotes and without"
+ " capital letters", action->val);
+ goto error;
+ }
+
+ if (actions_used & action_flag) {
+ SCLogError(SC_ERR_ACTION_ORDER, "action-order, action already set: \"%s\". Please,"
+ " use \"pass\",\"drop\",\"alert\",\"reject\". You"
+ " have to specify all of them, without quotes and"
+ " without capital letters", action->val);
+ goto error;
+ }
+
+ if (order >= 4) {
+ SCLogError(SC_ERR_ACTION_ORDER, "action-order, you have already specified all the "
+ "possible actions plus \"%s\". Please, use \"pass\","
+ "\"drop\",\"alert\",\"reject\". You have to specify"
+ " all of them, without quotes and without capital"
+ " letters", action->val);
+ goto error;
+ }
+ actions_used |= action_flag;
+ actions_config[order++] = action_flag;
+ }
+ }
+ if (order < 4) {
+ SCLogError(SC_ERR_ACTION_ORDER, "action-order, the config didn't specify all of the "
+ "actions. Please, use \"pass\",\"drop\",\"alert\","
+ "\"reject\". You have to specify all of them, without"
+ " quotes and without capital letters");
+ goto error;
+ }
+
+ /* Now, it's a valid config. Override the default preset */
+ for (order = 0; order < 4; order++) {
+ action_order_sigs[order] = actions_config[order];
+ }
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+#ifdef UNITTESTS
+#include "util-unittest.h"
+
+/**
+ * \test Check that we invalidate duplicated actions
+ * (It should default to pass, drop, reject, alert)
+ */
+int UtilActionTest01(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - alert\n\
+ - drop\n\
+ - reject\n\
+ - alert\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we invalidate with unknown keywords
+ * (It should default to pass, drop, reject, alert)
+ */
+int UtilActionTest02(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - alert\n\
+ - drop\n\
+ - reject\n\
+ - ftw\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we invalidate if any action is missing
+ * (It should default to pass, drop, reject, alert)
+ */
+int UtilActionTest03(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - alert\n\
+ - drop\n\
+ - reject\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we invalidate if any action is missing
+ * (It should default to pass, drop, reject, alert)
+ */
+int UtilActionTest04(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we invalidate with unknown keywords
+ * and/or more than the expected
+ * (It should default to pass, drop, reject, alert)
+ */
+int UtilActionTest05(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - alert\n\
+ - drop\n\
+ - reject\n\
+ - pass\n\
+ - whatever\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we load a valid config
+ */
+int UtilActionTest06(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - alert\n\
+ - drop\n\
+ - reject\n\
+ - pass\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_ALERT ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_PASS)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we load a valid config
+ */
+int UtilActionTest07(void)
+{
+ int res = 1;
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+action-order:\n\
+ - pass\n\
+ - alert\n\
+ - drop\n\
+ - reject\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ActionInitConfig();
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_ALERT ||
+ action_order_sigs[2] != ACTION_DROP ||
+ action_order_sigs[3] != ACTION_REJECT)
+ {
+ res = 0;
+ }
+ ConfRestoreContextBackup();
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the IP Only engine in the default case
+ */
+int UtilActionTest08(void)
+{
+ int res = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass ip 192.168.1.1 80 -> any any (msg:\"sig 2\"; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {0, 0, 0},
+ {1, 0, 1} };
+ /* This means that with the second packet, the results will be
+ * all ({0,0,0}) since, we should match the "pass" rule first
+ */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the IP Only engine with more
+ * prio to drop
+ */
+int UtilActionTest09(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass ip 192.168.1.1 80 -> any any (msg:\"sig 2\"; sid:2;)";
+ sigs[2]= "drop ip any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {0, 0, 1},
+ {1, 0, 1} };
+ /* This means that with the second packet, the results will be
+ * all ({0,0,1}) since, we should match the "drop" rule first.
+ * Later the "pass" rule will avoid the "alert" rule match
+ */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the detection engine in the default case
+ */
+int UtilActionTest10(void)
+{
+ int res = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ uint8_t *buf2 = (uint8_t *)"wo!";
+ uint16_t buflen2 = strlen((char *)buf2);
+ Packet *p[3];
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf2, buflen2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)";
+ sigs[1]= "pass ip any any -> any any (msg:\"sig 2\"; content:\"wo\"; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {0, 0, 0},
+ {1, 0, 1} };
+ /* This means that with the second packet, the results will be
+ * all ({0,0,0}) since, we should match the "pass" rule first
+ */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the detection engine with more
+ * prio to drop
+ */
+int UtilActionTest11(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ uint8_t *buf2 = (uint8_t *)"Hi all wo!";
+ uint16_t buflen2 = strlen((char *)buf2);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf2, buflen2, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"wo\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {0, 0, 1},
+ {1, 0, 1} };
+ /* This means that with the second packet, the results will be
+ * all ({0,0,1}) since, we should match the "drop" rule first.
+ * Later the "pass" rule will avoid the "alert" rule match
+ */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the detection engine in the default case
+ */
+int UtilActionTest12(void)
+{
+ int res = 0;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass ip any any -> any any (msg:\"Testing normal 2\"; sid:2;)";
+ sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 0},
+ {0, 0, 0},
+ {0, 0, 0} };
+ /* All should match the 3 sigs, but the action pass has prio */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the detection engine with more
+ * prio to drop
+ */
+int UtilActionTest13(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 1},
+ {0, 0, 1},
+ {0, 0, 1} };
+ /* All the patckets should match the 3 sigs. As drop has more
+ * priority than pass, it should alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check that we handle the "pass" action
+ * correctly at the detection engine with more
+ * prio to drop and alert
+ */
+int UtilActionTest14(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_ALERT;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {1, 0, 1},
+ {1, 0, 1} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal)
+ */
+int UtilActionTest15(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 0},
+ {0, 0, 0},
+ {0, 0, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal)
+ */
+int UtilActionTest16(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "drop tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "pass tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 0},
+ {0, 0, 0},
+ {0, 0, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal)
+ */
+int UtilActionTest17(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "pass tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "drop tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 0},
+ {0, 0, 0},
+ {0, 0, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for drop
+ */
+int UtilActionTest18(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 0, 1},
+ {0, 0, 1},
+ {0, 0, 1} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for drop
+ */
+int UtilActionTest19(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "drop tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "pass tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 0},
+ {1, 0, 0},
+ {1, 0, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for drop
+ */
+int UtilActionTest20(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_PASS;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "pass tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "drop tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 1, 0},
+ {0, 1, 0},
+ {0, 1, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for alert and drop
+ */
+int UtilActionTest21(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_ALERT;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 0, 1},
+ {1, 0, 1},
+ {1, 0, 1} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for alert and drop
+ */
+int UtilActionTest22(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_ALERT;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "drop tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "alert tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "pass tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {1, 1, 0},
+ {1, 1, 0},
+ {1, 1, 0} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+end:
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+ return res;
+}
+
+/**
+ * \test Check mixed sigs (iponly and normal) with more prio for alert and drop
+ */
+int UtilActionTest23(void)
+{
+ int res = 1;
+ uint8_t *buf = (uint8_t *)"Hi all!";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p[3];
+
+ action_order_sigs[0] = ACTION_DROP;
+ action_order_sigs[1] = ACTION_ALERT;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_PASS;
+
+ p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+ p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.1", "192.168.1.5",
+ 80, 41424);
+ p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+
+ if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
+ goto end;
+
+ char *sigs[3];
+ sigs[0]= "pass tcp any any -> any any (msg:\"sig 1\"; sid:1;)";
+ sigs[1]= "drop tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)";
+ sigs[2]= "alert tcp any any -> any any (msg:\"sig 3\"; sid:3;)";
+
+ uint32_t sid[3] = {1, 2, 3};
+
+ uint32_t results[3][3] = {
+ {0, 1, 1},
+ {0, 1, 1},
+ {0, 1, 1} };
+ /* All the patckets should match the 3 sigs. As drop
+ * and alert have more priority than pass, both should
+ * alert on each packet */
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto cleanup;
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, 3) == 0)
+ goto cleanup;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+
+ res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3);
+
+cleanup:
+ UTHFreePackets(p, 3);
+
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ /* Restore default values */
+ action_order_sigs[0] = ACTION_PASS;
+ action_order_sigs[1] = ACTION_DROP;
+ action_order_sigs[2] = ACTION_REJECT;
+ action_order_sigs[3] = ACTION_ALERT;
+
+end:
+ return res;
+}
+
+/**
+ * \test Check that the expected defaults are loaded if the
+ * action-order configuration is not present.
+ */
+int UtilActionTest24(void)
+{
+ int res = 1;
+ char config[] = "%YAML 1.1\n"
+ "---\n";
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ if (ActionInitConfig() != 0) {
+ res = 0;
+ goto done;
+ }
+ if (action_order_sigs[0] != ACTION_PASS ||
+ action_order_sigs[1] != ACTION_DROP ||
+ action_order_sigs[2] != ACTION_REJECT ||
+ action_order_sigs[3] != ACTION_ALERT) {
+ res = 0;
+ }
+
+done:
+ ConfRestoreContextBackup();
+ return res;
+}
+
+#endif
+
+/* Register unittests */
+void UtilActionRegisterTests(void)
+{
+#ifdef UNITTESTS
+ /* Generic tests */
+ UtRegisterTest("UtilActionTest01", UtilActionTest01, 1);
+ UtRegisterTest("UtilActionTest02", UtilActionTest02, 1);
+ UtRegisterTest("UtilActionTest02", UtilActionTest02, 1);
+ UtRegisterTest("UtilActionTest03", UtilActionTest03, 1);
+ UtRegisterTest("UtilActionTest04", UtilActionTest04, 1);
+ UtRegisterTest("UtilActionTest05", UtilActionTest05, 1);
+ UtRegisterTest("UtilActionTest06", UtilActionTest06, 1);
+ UtRegisterTest("UtilActionTest07", UtilActionTest07, 1);
+ UtRegisterTest("UtilActionTest08", UtilActionTest08, 1);
+ UtRegisterTest("UtilActionTest09", UtilActionTest09, 1);
+ UtRegisterTest("UtilActionTest10", UtilActionTest10, 1);
+ UtRegisterTest("UtilActionTest11", UtilActionTest11, 1);
+ UtRegisterTest("UtilActionTest12", UtilActionTest12, 1);
+ UtRegisterTest("UtilActionTest13", UtilActionTest13, 1);
+ UtRegisterTest("UtilActionTest14", UtilActionTest14, 1);
+ UtRegisterTest("UtilActionTest15", UtilActionTest15, 1);
+ UtRegisterTest("UtilActionTest16", UtilActionTest16, 1);
+ UtRegisterTest("UtilActionTest17", UtilActionTest17, 1);
+ UtRegisterTest("UtilActionTest18", UtilActionTest18, 1);
+ UtRegisterTest("UtilActionTest19", UtilActionTest19, 1);
+ UtRegisterTest("UtilActionTest20", UtilActionTest20, 1);
+ UtRegisterTest("UtilActionTest21", UtilActionTest21, 1);
+ UtRegisterTest("UtilActionTest22", UtilActionTest22, 1);
+ UtRegisterTest("UtilActionTest23", UtilActionTest23, 1);
+ UtRegisterTest("UtilActionTest24", UtilActionTest24, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/util-action.h b/framework/src/suricata/src/util-action.h
new file mode 100644
index 00000000..bc711314
--- /dev/null
+++ b/framework/src/suricata/src/util-action.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __ACTION_ORDER_H__
+#define __ACTION_ORDER_H__
+#include "suricata-common.h"
+int ActionInitConfig();
+uint8_t ActionOrderVal(uint8_t);
+void UtilActionRegisterTests(void);
+
+#endif /* __ACTION_ORDER_H__ */
diff --git a/framework/src/suricata/src/util-affinity.c b/framework/src/suricata/src/util-affinity.c
new file mode 100644
index 00000000..82456c20
--- /dev/null
+++ b/framework/src/suricata/src/util-affinity.c
@@ -0,0 +1,323 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * CPU affinity related code and helper.
+ */
+
+#include "suricata-common.h"
+#define _THREAD_AFFINITY
+#include "util-affinity.h"
+#include "util-cpu.h"
+#include "conf.h"
+#include "threads.h"
+#include "queue.h"
+#include "runmodes.h"
+
+ThreadsAffinityType thread_affinity[MAX_CPU_SET] = {
+ {
+ .name = "receive-cpu-set",
+ .mode_flag = EXCLUSIVE_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "decode-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "stream-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "detect-cpu-set",
+ .mode_flag = EXCLUSIVE_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "verdict-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "reject-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "output-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+ {
+ .name = "management-cpu-set",
+ .mode_flag = BALANCED_AFFINITY,
+ .prio = PRIO_MEDIUM,
+ .lcpu = 0,
+ },
+
+};
+
+int thread_affinity_init_done = 0;
+
+/**
+ * \brief find affinity by its name
+ * \retval a pointer to the affinity or NULL if not found
+ */
+ThreadsAffinityType * GetAffinityTypeFromName(const char *name)
+{
+ int i;
+ for (i = 0; i < MAX_CPU_SET; i++) {
+ if (!strcmp(thread_affinity[i].name, name)) {
+ return &thread_affinity[i];
+ }
+ }
+ return NULL;
+}
+
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+static void AffinitySetupInit()
+{
+ int i, j;
+ int ncpu = UtilCpuGetNumProcessorsConfigured();
+
+ SCLogDebug("Initialize affinity setup\n");
+ /* be conservative relatively to OS: use all cpus by default */
+ for (i = 0; i < MAX_CPU_SET; i++) {
+ cpu_set_t *cs = &thread_affinity[i].cpu_set;
+ CPU_ZERO(cs);
+ for (j = 0; j < ncpu; j++) {
+ CPU_SET(j, cs);
+ }
+ SCMutexInit(&thread_affinity[i].taf_mutex, NULL);
+ }
+ return;
+}
+
+static void build_cpuset(char *name, ConfNode *node, cpu_set_t *cpu)
+{
+ ConfNode *lnode;
+ TAILQ_FOREACH(lnode, &node->head, next) {
+ int i;
+ long int a,b;
+ int stop = 0;
+ int max = UtilCpuGetNumProcessorsOnline() - 1;
+ if (!strcmp(lnode->val, "all")) {
+ a = 0;
+ b = max;
+ stop = 1;
+ } else if (index(lnode->val, '-') != NULL) {
+ char *sep = index(lnode->val, '-');
+ char *end;
+ a = strtoul(lnode->val, &end, 10);
+ if (end != sep) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "%s: invalid cpu range (start invalid): \"%s\"",
+ name,
+ lnode->val);
+ exit(EXIT_FAILURE);
+ }
+ b = strtol(sep + 1, &end, 10);
+ if (end != sep + strlen(sep)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "%s: invalid cpu range (end invalid): \"%s\"",
+ name,
+ lnode->val);
+ exit(EXIT_FAILURE);
+ }
+ if (a > b) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "%s: invalid cpu range (bad order): \"%s\"",
+ name,
+ lnode->val);
+ exit(EXIT_FAILURE);
+ }
+ if (b > max) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "%s: upper bound (%ld) of cpu set is too high, only %d cpu(s)",
+ name,
+ b, max + 1);
+ }
+ } else {
+ char *end;
+ a = strtoul(lnode->val, &end, 10);
+ if (end != lnode->val + strlen(lnode->val)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "%s: invalid cpu range (not an integer): \"%s\"",
+ name,
+ lnode->val);
+ exit(EXIT_FAILURE);
+ }
+ b = a;
+ }
+ for (i = a; i<= b; i++) {
+ CPU_SET(i, cpu);
+ }
+ if (stop)
+ break;
+ }
+}
+#endif /* OS_WIN32 and __OpenBSD__ */
+
+/**
+ * \brief Extract cpu affinity configuration from current config file
+ */
+
+void AffinitySetupLoadFromConfig()
+{
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+ ConfNode *root = ConfGetNode("threading.cpu-affinity");
+ ConfNode *affinity;
+
+ if (thread_affinity_init_done == 0) {
+ AffinitySetupInit();
+ thread_affinity_init_done = 1;
+ }
+
+ SCLogDebug("Load affinity from config\n");
+ if (root == NULL) {
+ SCLogInfo("can't get cpu-affinity node");
+ return;
+ }
+
+ TAILQ_FOREACH(affinity, &root->head, next) {
+ ThreadsAffinityType *taf = GetAffinityTypeFromName(affinity->val);
+ ConfNode *node = NULL;
+ ConfNode *nprio = NULL;
+
+ if (taf == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu-affinity type");
+ exit(EXIT_FAILURE);
+ } else {
+ SCLogInfo("Found affinity definition for \"%s\"",
+ affinity->val);
+ }
+
+ CPU_ZERO(&taf->cpu_set);
+ node = ConfNodeLookupChild(affinity->head.tqh_first, "cpu");
+ if (node == NULL) {
+ SCLogInfo("unable to find 'cpu'");
+ } else {
+ build_cpuset(affinity->val, node, &taf->cpu_set);
+ }
+
+ CPU_ZERO(&taf->lowprio_cpu);
+ CPU_ZERO(&taf->medprio_cpu);
+ CPU_ZERO(&taf->hiprio_cpu);
+ nprio = ConfNodeLookupChild(affinity->head.tqh_first, "prio");
+ if (nprio != NULL) {
+ node = ConfNodeLookupChild(nprio, "low");
+ if (node == NULL) {
+ SCLogDebug("unable to find 'low' prio using default value");
+ } else {
+ build_cpuset(affinity->val, node, &taf->lowprio_cpu);
+ }
+
+ node = ConfNodeLookupChild(nprio, "medium");
+ if (node == NULL) {
+ SCLogDebug("unable to find 'medium' prio using default value");
+ } else {
+ build_cpuset(affinity->val, node, &taf->medprio_cpu);
+ }
+
+ node = ConfNodeLookupChild(nprio, "high");
+ if (node == NULL) {
+ SCLogDebug("unable to find 'high' prio using default value");
+ } else {
+ build_cpuset(affinity->val, node, &taf->hiprio_cpu);
+ }
+ node = ConfNodeLookupChild(nprio, "default");
+ if (node != NULL) {
+ if (!strcmp(node->val, "low")) {
+ taf->prio = PRIO_LOW;
+ } else if (!strcmp(node->val, "medium")) {
+ taf->prio = PRIO_MEDIUM;
+ } else if (!strcmp(node->val, "high")) {
+ taf->prio = PRIO_HIGH;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu_affinity prio");
+ exit(EXIT_FAILURE);
+ }
+ SCLogInfo("Using default prio '%s'", node->val);
+ }
+ }
+
+ node = ConfNodeLookupChild(affinity->head.tqh_first, "mode");
+ if (node != NULL) {
+ if (!strcmp(node->val, "exclusive")) {
+ taf->mode_flag = EXCLUSIVE_AFFINITY;
+ } else if (!strcmp(node->val, "balanced")) {
+ taf->mode_flag = BALANCED_AFFINITY;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu_affinity node");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ node = ConfNodeLookupChild(affinity->head.tqh_first, "threads");
+ if (node != NULL) {
+ taf->nb_threads = atoi(node->val);
+ if (! taf->nb_threads) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "bad value for threads count");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+#endif /* OS_WIN32 and __OpenBSD__ */
+}
+
+/**
+ * \brief Return next cpu to use for a given thread family
+ * \retval the cpu to used given by its id
+ */
+int AffinityGetNextCPU(ThreadsAffinityType *taf)
+{
+ int ncpu = 0;
+
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+ int iter = 0;
+ SCMutexLock(&taf->taf_mutex);
+ ncpu = taf->lcpu;
+ while (!CPU_ISSET(ncpu, &taf->cpu_set) && iter < 2) {
+ ncpu++;
+ if (ncpu >= UtilCpuGetNumProcessorsOnline()) {
+ ncpu = 0;
+ iter++;
+ }
+ }
+ if (iter == 2) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "cpu_set does not contains available cpus, cpu afinity conf is invalid");
+ }
+ taf->lcpu = ncpu + 1;
+ if (taf->lcpu >= UtilCpuGetNumProcessorsOnline())
+ taf->lcpu = 0;
+ SCMutexUnlock(&taf->taf_mutex);
+ SCLogInfo("Setting affinity on CPU %d", ncpu);
+#endif /* OS_WIN32 and __OpenBSD__ */
+ return ncpu;
+}
diff --git a/framework/src/suricata/src/util-affinity.h b/framework/src/suricata/src/util-affinity.h
new file mode 100644
index 00000000..9ca30fcf
--- /dev/null
+++ b/framework/src/suricata/src/util-affinity.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_AFFINITY_H__
+#define __UTIL_AFFINITY_H__
+#include "suricata-common.h"
+
+#if defined OS_FREEBSD
+#include <sched.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/cpuset.h>
+#include <sys/thr.h>
+#define cpu_set_t cpuset_t
+#elif defined __OpenBSD__
+#include <sched.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#elif defined OS_DARWIN
+#include <mach/mach.h>
+#include <mach/mach_init.h>
+#include <mach/thread_policy.h>
+#define cpu_set_t thread_affinity_policy_data_t
+#define CPU_SET(cpu_id, new_mask) (*(new_mask)).affinity_tag = (cpu_id + 1)
+#define CPU_ISSET(cpu_id, new_mask) ((*(new_mask)).affinity_tag == (cpu_id + 1))
+#define CPU_ZERO(new_mask) (*(new_mask)).affinity_tag = THREAD_AFFINITY_TAG_NULL
+#endif
+
+enum {
+ RECEIVE_CPU_SET,
+ DECODE_CPU_SET,
+ STREAM_CPU_SET,
+ DETECT_CPU_SET,
+ VERDICT_CPU_SET,
+ REJECT_CPU_SET,
+ OUTPUT_CPU_SET,
+ MANAGEMENT_CPU_SET,
+ MAX_CPU_SET
+};
+
+enum {
+ BALANCED_AFFINITY,
+ EXCLUSIVE_AFFINITY,
+ MAX_AFFINITY
+};
+
+typedef struct ThreadsAffinityType_ {
+ char *name;
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+ cpu_set_t cpu_set;
+#endif
+ uint8_t mode_flag;
+ int prio;
+ int nb_threads;
+#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
+ cpu_set_t lowprio_cpu;
+ cpu_set_t medprio_cpu;
+ cpu_set_t hiprio_cpu;
+#endif
+ SCMutex taf_mutex;
+ uint16_t lcpu; /* use by exclusive mode */
+} ThreadsAffinityType;
+
+/** store thread affinity mode for all type of threads */
+#ifndef _THREAD_AFFINITY
+extern ThreadsAffinityType thread_affinity[MAX_CPU_SET];
+#endif
+
+void AffinitySetupLoadFromConfig();
+ThreadsAffinityType * GetAffinityTypeFromName(const char *name);
+
+int AffinityGetNextCPU(ThreadsAffinityType *taf);
+
+#endif /* __UTIL_AFFINITY_H__ */
diff --git a/framework/src/suricata/src/util-atomic.c b/framework/src/suricata/src/util-atomic.c
new file mode 100644
index 00000000..9ad448e6
--- /dev/null
+++ b/framework/src/suricata/src/util-atomic.c
@@ -0,0 +1,73 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-atomic.h"
+#include "util-unittest.h"
+
+#ifdef UNITTESTS
+
+static int SCAtomicTest01(void)
+{
+ int result = 0;
+ int a = 10;
+ int b = 20;
+ int *temp_int = NULL;
+
+ SC_ATOMIC_DECL_AND_INIT(void *, temp);
+
+ temp_int = SC_ATOMIC_GET(temp);
+ if (temp_int != NULL)
+ goto end;
+
+ (void)SC_ATOMIC_SET(temp, &a);
+ temp_int = SC_ATOMIC_GET(temp);
+ if (temp_int == NULL)
+ goto end;
+ if (*temp_int != a)
+ goto end;
+
+ (void)SC_ATOMIC_SET(temp, &b);
+ temp_int = SC_ATOMIC_GET(temp);
+ if (temp_int == NULL)
+ goto end;
+ if (*temp_int != b)
+ goto end;
+
+ result = 1;
+
+ end:
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCAtomicRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SCAtomicTest01", SCAtomicTest01, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-atomic.h b/framework/src/suricata/src/util-atomic.h
new file mode 100644
index 00000000..cbcfc868
--- /dev/null
+++ b/framework/src/suricata/src/util-atomic.h
@@ -0,0 +1,476 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * API for atomic operations. Uses atomic instructions (GCC only at this time)
+ * where available, falls back to (spin)locked* operations otherwise.
+ *
+ * To prevent developers from accidentally working with the atomic variables
+ * directly instead of through the proper macro's, a marco trick is performed
+ * that exposes different variable names than the developer uses. So if the dev
+ * uses "somevar", internally "somevar_sc_atomic__" is used.
+ *
+ * Where available, we use __sync_fetch_and_add and
+ * __sync_bool_compare_and_swap. If those are unavailable, the API
+ * transparently created a matching (spin)lock for each atomic variable. The
+ * lock will be named "somevar_sc_lock__"
+ *
+ * (*) where spinlocks are unavailable, the threading api falls back to mutex
+ */
+
+
+#ifndef __UTIL_ATOMIC_H__
+#define __UTIL_ATOMIC_H__
+
+/* test if we have atomic operations support */
+#if (!defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || \
+ !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)) && \
+ !defined(__tile__)
+
+/* Do not have atomic operations support, so implement them with locks. */
+
+/**
+ * \brief wrapper to declare an atomic variable including a (spin) lock
+ * to protect it.
+ *
+ * \warning Variable and lock are _not_ initialized.
+ */
+#define SC_ATOMIC_DECLARE(type, name) \
+ type name ## _sc_atomic__; \
+ SCSpinlock name ## _sc_lock__
+
+/**
+ * \brief wrapper to reference an atomic variable already declared on another file (including the spin lock)
+ *
+ */
+#define SC_ATOMIC_EXTERN(type, name) \
+ extern type name ## _sc_atomic__; \
+ extern SCSpinlock name ## _sc_lock__
+
+/**
+ * \brief wrapper to declare an atomic variable including a (spin) lock
+ * to protect it and initialize them.
+ */
+#define SC_ATOMIC_DECL_AND_INIT(type, name) \
+ type name ## _sc_atomic__ = 0; \
+ SCSpinlock name ## _sc_lock__; \
+ SCSpinInit(&(name ## _sc_lock__), 0)
+
+/**
+ * \brief Initialize the previously declared atomic variable and it's
+ * lock.
+ */
+#define SC_ATOMIC_INIT(name) do { \
+ SCSpinInit(&(name ## _sc_lock__), 0); \
+ (name ## _sc_atomic__) = 0; \
+ } while(0)
+
+/**
+ * \brief Initialize the previously declared atomic variable and it's
+ * lock.
+ */
+#define SC_ATOMIC_RESET(name) do { \
+ (name ## _sc_atomic__) = 0; \
+ } while(0)
+
+/**
+ * \brief Destroy the lock used to protect this variable
+ */
+#define SC_ATOMIC_DESTROY(name) do { \
+ SCSpinDestroy(&(name ## _sc_lock__)); \
+ } while (0)
+
+/**
+ * \brief add a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to add to the variable
+ */
+#define SC_ATOMIC_ADD(name, val) ({\
+ typeof(name ## _sc_atomic__) var; \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) += (val); \
+ var = (name ## _sc_atomic__); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0); \
+ var ; \
+})
+
+/**
+ * \brief sub a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_SUB(name, val) ({ \
+ typeof(name ## _sc_atomic__) var; \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) -= (val); \
+ var = (name ## _sc_atomic__); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0); \
+ var ; \
+})
+
+/**
+ * \brief Bitwise AND a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_AND(name, val) \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) &= (val); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0)
+
+/**
+ * \brief Bitwise OR a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_OR(name, val) \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) |= (val); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0)
+
+/**
+ * \brief Bitwise NAND a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_NAND(name, val) \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) = ~(name ## _sc_atomic__) & (val); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0)
+
+/**
+ * \brief Bitwise XOR a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_XOR(name, val) \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ (name ## _sc_atomic__) ^= (val); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while(0)
+
+/**
+ * \brief Get the value from the atomic variable.
+ *
+ * \retval var value
+ */
+#define SC_ATOMIC_GET(name) ({ \
+ typeof(name ## _sc_atomic__) var; \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ var = (name ## _sc_atomic__); \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while (0); \
+ var; \
+})
+
+/**
+ * \brief Set the value for the atomic variable.
+ *
+ * \retval var value
+ */
+#define SC_ATOMIC_SET(name, val) ({ \
+ typeof(name ## _sc_atomic__) var; \
+ do { \
+ SCSpinLock(&(name ## _sc_lock__)); \
+ var = (name ## _sc_atomic__) = val; \
+ SCSpinUnlock(&(name ## _sc_lock__)); \
+ } while (0); \
+ var; \
+})
+
+/**
+ * \brief atomic Compare and Switch
+ *
+ * \warning "name" is passed to us as "&var"
+ */
+#define SC_ATOMIC_CAS(name, cmpval, newval) ({ \
+ char r = 0; \
+ do { \
+ SCSpinLock((name ## _sc_lock__)); \
+ if (*(name ## _sc_atomic__) == (cmpval)) { \
+ *(name ## _sc_atomic__) = (newval); \
+ r = 1; \
+ } \
+ SCSpinUnlock((name ## _sc_lock__)); \
+ } while(0); \
+ r; \
+})
+
+#else /* we do have support for CAS */
+
+/**
+ * \brief wrapper for OS/compiler specific atomic compare and swap (CAS)
+ * function.
+ *
+ * \param addr Address of the variable to CAS
+ * \param tv Test value to compare the value at address against
+ * \param nv New value to set the variable at addr to
+ *
+ * \retval 0 CAS failed
+ * \retval 1 CAS succeeded
+ */
+#define SCAtomicCompareAndSwap(addr, tv, nv) \
+ __sync_bool_compare_and_swap((addr), (tv), (nv))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and add
+ * function.
+ *
+ * \param addr Address of the variable to add to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicFetchAndAdd(addr, value) \
+ __sync_fetch_and_add((addr), (value))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and sub
+ * function.
+ *
+ * \param addr Address of the variable to add to
+ * \param value Value to sub from the variable at addr
+ */
+#define SCAtomicFetchAndSub(addr, value) \
+ __sync_fetch_and_sub((addr), (value))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and add
+ * function.
+ *
+ * \param addr Address of the variable to add to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicAddAndFetch(addr, value) \
+ __sync_add_and_fetch((addr), (value))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and sub
+ * function.
+ *
+ * \param addr Address of the variable to add to
+ * \param value Value to sub from the variable at addr
+ */
+#define SCAtomicSubAndFetch(addr, value) \
+ __sync_sub_and_fetch((addr), (value))
+
+
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and "AND"
+ * function.
+ *
+ * \param addr Address of the variable to AND to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicFetchAndAnd(addr, value) \
+ __sync_fetch_and_and((addr), (value))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and "NAND"
+ * function.
+ *
+ * \param addr Address of the variable to NAND to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicFetchAndNand(addr, value) \
+ __sync_fetch_and_nand((addr), (value))
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and "XOR"
+ * function.
+ *
+ * \param addr Address of the variable to XOR to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicFetchAndXor(addr, value) \
+ __sync_fetch_and_xor((addr), (value))
+
+
+/**
+ * \brief wrapper for OS/compiler specific atomic fetch and or
+ * function.
+ *
+ * \param addr Address of the variable to or to
+ * \param value Value to add to the variable at addr
+ */
+#define SCAtomicFetchAndOr(addr, value) \
+ __sync_fetch_and_or((addr), (value))
+
+/**
+ * \brief wrapper for declaring atomic variables.
+ *
+ * \warning Only char, short, int, long, long long and their unsigned
+ * versions are supported.
+ *
+ * \param type Type of the variable (char, short, int, long, long long)
+ * \param name Name of the variable.
+ *
+ * We just declare the variable here as we rely on atomic operations
+ * to modify it, so no need for locks.
+ *
+ * \warning variable is not initialized
+ */
+#define SC_ATOMIC_DECLARE(type, name) \
+ type name ## _sc_atomic__
+
+/**
+ * \brief wrapper for referencing an atomic variable declared on another file.
+ *
+ * \warning Only char, short, int, long, long long and their unsigned
+ * versions are supported.
+ *
+ * \param type Type of the variable (char, short, int, long, long long)
+ * \param name Name of the variable.
+ *
+ * We just declare the variable here as we rely on atomic operations
+ * to modify it, so no need for locks.
+ *
+ */
+#define SC_ATOMIC_EXTERN(type, name) \
+ extern type name ## _sc_atomic__
+
+/**
+ * \brief wrapper for declaring an atomic variable and initializing it.
+ **/
+#define SC_ATOMIC_DECL_AND_INIT(type, name) \
+ type name ## _sc_atomic__ = 0
+
+/**
+ * \brief wrapper for initializing an atomic variable.
+ **/
+#define SC_ATOMIC_INIT(name) \
+ (name ## _sc_atomic__) = 0
+
+/**
+ * \brief wrapper for reinitializing an atomic variable.
+ **/
+#define SC_ATOMIC_RESET(name) \
+ (name ## _sc_atomic__) = 0
+
+/**
+ * \brief No-op.
+ */
+#define SC_ATOMIC_DESTROY(name)
+
+/**
+ * \brief add a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to add to the variable
+ */
+#define SC_ATOMIC_ADD(name, val) \
+ SCAtomicAddAndFetch(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief sub a value from our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to sub from the variable
+ */
+#define SC_ATOMIC_SUB(name, val) \
+ SCAtomicSubAndFetch(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief Bitwise OR a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to OR to the variable
+ */
+#define SC_ATOMIC_OR(name, val) \
+ SCAtomicFetchAndOr(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief Bitwise AND a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to AND to the variable
+ */
+#define SC_ATOMIC_AND(name, val) \
+ SCAtomicFetchAndAnd(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief Bitwise NAND a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to NAND to the variable
+ */
+#define SC_ATOMIC_NAND(name, val) \
+ SCAtomicFetchAndNand(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief Bitwise XOR a value to our atomic variable
+ *
+ * \param name the atomic variable
+ * \param val the value to XOR to the variable
+ */
+#define SC_ATOMIC_XOR(name, val) \
+ SCAtomicFetchAndXor(&(name ## _sc_atomic__), (val))
+
+/**
+ * \brief atomic Compare and Switch
+ *
+ * \warning "name" is passed to us as "&var"
+ */
+#define SC_ATOMIC_CAS(name, cmpval, newval) \
+ SCAtomicCompareAndSwap((name ## _sc_atomic__), cmpval, newval)
+
+/**
+ * \brief Get the value from the atomic variable.
+ *
+ * \retval var value
+ */
+#define SC_ATOMIC_GET(name) \
+ (name ## _sc_atomic__)
+
+/**
+ * \brief Set the value for the atomic variable.
+ *
+ * \retval var value
+ */
+#define SC_ATOMIC_SET(name, val) ({ \
+ while (SC_ATOMIC_CAS(&name, SC_ATOMIC_GET(name), val) == 0) \
+ ; \
+ })
+
+#endif /* !no atomic operations */
+
+void SCAtomicRegisterTests(void);
+
+#endif /* __UTIL_ATOMIC_H__ */
+
diff --git a/framework/src/suricata/src/util-base64.c b/framework/src/suricata/src/util-base64.c
new file mode 100644
index 00000000..f4b508a0
--- /dev/null
+++ b/framework/src/suricata/src/util-base64.c
@@ -0,0 +1,146 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author David Abarbanel <david.abarbanel@baesystems.com>
+ *
+ */
+
+#include "util-base64.h"
+
+/* Constants */
+#define BASE64_TABLE_MAX 122
+
+/* Base64 character to index conversion table */
+/* Characters are mapped as "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
+static const int b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
+ -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51 };
+
+/**
+ * \brief Gets a base64-decoded value from an encoded character
+ *
+ * \param c The encoded character
+ *
+ * \return The decoded value (0 or above), or -1 if the parameter is invalid
+ */
+static inline int GetBase64Value(uint8_t c) {
+
+ int val = -1;
+
+ /* Pull from conversion table */
+ if (c <= BASE64_TABLE_MAX) {
+ val = b64table[(int) c];
+ }
+
+ return val;
+}
+
+/**
+ * \brief Decodes a 4-byte base64-encoded block into a 3-byte ascii-encoded block
+ *
+ * \param ascii the 3-byte ascii output block
+ * \param b64 the 4-byte base64 input block
+ *
+ * \return none
+ */
+static inline void DecodeBase64Block(uint8_t ascii[ASCII_BLOCK], uint8_t b64[B64_BLOCK]) {
+
+ ascii[0] = (uint8_t) (b64[0] << 2) | (b64[1] >> 4);
+ ascii[1] = (uint8_t) (b64[1] << 4) | (b64[2] >> 2);
+ ascii[2] = (uint8_t) (b64[2] << 6) | (b64[3]);
+}
+
+/**
+ * \brief Decodes a base64-encoded string buffer into an ascii-encoded byte buffer
+ *
+ * \param dest The destination byte buffer
+ * \param src The source string
+ * \param len The length of the source string
+ *
+ * \return Number of bytes decoded, or 0 if no data is decoded or it fails
+ */
+uint32_t DecodeBase64(uint8_t *dest, const uint8_t *src, uint32_t len) {
+
+ int val;
+ uint32_t padding = 0, numDecoded = 0, bbidx = 0, valid = 1, i;
+ uint8_t *dptr = dest;
+ uint8_t b64[B64_BLOCK] = { 0,0,0,0 };
+
+ /* Traverse through each alpha-numeric letter in the source array */
+ for(i = 0; i < len && src[i] != 0; i++) {
+
+ /* Get decimal representation */
+ val = GetBase64Value(src[i]);
+ if (val < 0) {
+
+ /* Invalid character found, so decoding fails */
+ if (src[i] != '=') {
+ valid = 0;
+ numDecoded = 0;
+ break;
+ }
+ padding++;
+ }
+
+ /* For each alpha-numeric letter in the source array, find the numeric
+ * value */
+ b64[bbidx++] = (val > 0 ? val : 0);
+
+ /* Decode every 4 base64 bytes into 3 ascii bytes */
+ if (bbidx == B64_BLOCK) {
+
+ /* For every 4 bytes, add 3 bytes but deduct the '=' padded blocks */
+ numDecoded += ASCII_BLOCK - (padding < B64_BLOCK ?
+ padding : ASCII_BLOCK);
+
+ /* Decode base-64 block into ascii block and move pointer */
+ DecodeBase64Block(dptr, b64);
+ dptr += ASCII_BLOCK;
+
+ /* Reset base-64 block and index */
+ bbidx = 0;
+ padding = 0;
+ }
+ }
+
+ /* Finish remaining b64 bytes by padding */
+ if (valid && bbidx > 0) {
+
+ /* Decode remaining */
+ numDecoded += ASCII_BLOCK - (B64_BLOCK - bbidx);
+ DecodeBase64Block(dptr, b64);
+ }
+
+ if (numDecoded == 0) {
+ SCLogDebug("base64 decoding failed");
+ }
+
+ return numDecoded;
+}
diff --git a/framework/src/suricata/src/util-base64.h b/framework/src/suricata/src/util-base64.h
new file mode 100644
index 00000000..fb1a90a3
--- /dev/null
+++ b/framework/src/suricata/src/util-base64.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author David Abarbanel <david.abarbanel@baesystems.com>
+ *
+ */
+
+#ifndef __UTIL_BASE64_H_
+#define __UTIL_BASE64_H_
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-spm-bm.h"
+
+/* Constants */
+#define ASCII_BLOCK 3
+#define B64_BLOCK 4
+
+/* Function prototypes */
+uint32_t DecodeBase64(uint8_t *dest, const uint8_t *src, uint32_t len);
+
+#endif
diff --git a/framework/src/suricata/src/util-binsearch.c b/framework/src/suricata/src/util-binsearch.c
new file mode 100644
index 00000000..59627759
--- /dev/null
+++ b/framework/src/suricata/src/util-binsearch.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Binary search utility functions
+ *
+ * \todo replace this by a better algo
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+void BinSearchInit (void)
+{
+ /* nothing no more */
+}
+
+/* Binary search.
+ *
+ * Returns:
+ * - ptr to start of the match
+ * - null if no match
+ */
+/* simple bin search modelled loosely after strstr */
+uint8_t *
+BinSearch(const uint8_t *haystack, size_t haystack_len,
+ const uint8_t *needle, size_t needle_len)
+{
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + (needle_len - 1);
+
+ if (needle_len == 0)
+ return NULL;
+
+ for (n = needle; haystack != hmax; haystack++) {
+ if (*haystack != *n) {
+ continue;
+ }
+ /* one byte needles */
+ if (needle_len == 1)
+ return (uint8_t *)haystack;
+
+ for (h = haystack+1, n++; h != hmax; h++, n++) {
+ //printf("h %c n %c\n", isprint(*h) ? *h : 'X', *n);
+ if (*h != *n) {
+ break;
+ }
+ /* if we run out of needle we fully matched */
+ if (n == nmax) {
+ return (uint8_t *)haystack;
+ }
+ }
+ n = needle;
+ }
+ return NULL;
+}
+
+/* Caseless binary search. More expensive that the one that
+ * respects case.
+ *
+ * Returns:
+ * - ptr to start of the match
+ * - null if no match
+ */
+uint8_t *
+BinSearchNocase(const uint8_t *haystack, size_t haystack_len,
+ const uint8_t *needle, size_t needle_len)
+{
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + (needle_len - 1);
+
+ if (needle_len == 0)
+ return NULL;
+
+ for (n = needle; haystack != hmax; haystack++) {
+ if (*haystack != *n && *haystack != u8_tolower(*n)) {
+ continue;
+ }
+ for (h = haystack+1, n++; h != hmax; h++, n++) {
+ if (*h != *n && *h != u8_tolower(*n)) {
+ break;
+ }
+ /* if we run out of needle we fully matched */
+ if (n == nmax) {
+ return (uint8_t *)haystack;
+ }
+ }
+ n = needle;
+ }
+ return NULL;
+}
+
diff --git a/framework/src/suricata/src/util-binsearch.h b/framework/src/suricata/src/util-binsearch.h
new file mode 100644
index 00000000..857e0838
--- /dev/null
+++ b/framework/src/suricata/src/util-binsearch.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_BINSEARCH_H__
+#define __UTIL_BINSEARCH_H__
+
+void BinSearchInit (void);
+uint8_t *BinSearch(const uint8_t *, size_t, const uint8_t *, size_t);
+uint8_t *BinSearchNocase(const uint8_t *, size_t, const uint8_t *, size_t);
+
+#endif /* __UTIL_BINSEARCH_H__ */
+
diff --git a/framework/src/suricata/src/util-bloomfilter-counting.c b/framework/src/suricata/src/util-bloomfilter-counting.c
new file mode 100644
index 00000000..443b8d79
--- /dev/null
+++ b/framework/src/suricata/src/util-bloomfilter-counting.c
@@ -0,0 +1,410 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Counting Bloom Filter implementation. Can be used with 8, 16, 32 bits
+ * counters.
+ */
+
+#include "suricata-common.h"
+#include "util-bloomfilter-counting.h"
+#include "util-unittest.h"
+
+/* type: 1, 2 or 4 for 8, 16, or 32 bit counters
+ *
+ */
+BloomFilterCounting *BloomFilterCountingInit(uint32_t size, uint8_t type, uint8_t iter, uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t)) {
+ BloomFilterCounting *bf = NULL;
+
+ if (iter == 0)
+ goto error;
+
+ if (Hash == NULL || size == 0) {
+ //printf("ERROR: BloomFilterCountingInit no Hash function\n");
+ goto error;
+ }
+
+ if (type != 1 && type != 2 && type != 4) {
+ //printf("ERROR: BloomFilterCountingInit only 1, 2 and 4 bytes are supported\n");
+ goto error;
+ }
+
+ /* setup the filter */
+ bf = SCMalloc(sizeof(BloomFilterCounting));
+ if (unlikely(bf == NULL))
+ goto error;
+ memset(bf,0,sizeof(BloomFilterCounting));
+ bf->type = type; /* size of the type: 1, 2, 4 */
+ bf->array_size = size;
+ bf->hash_iterations = iter;
+ bf->Hash = Hash;
+
+ /* setup the bitarray */
+ bf->array = SCMalloc(bf->array_size * bf->type);
+ if (bf->array == NULL)
+ goto error;
+ memset(bf->array,0,bf->array_size * bf->type);
+
+ return bf;
+
+error:
+ if (bf != NULL) {
+ if (bf->array != NULL)
+ SCFree(bf->array);
+
+ SCFree(bf);
+ }
+ return NULL;
+}
+
+void BloomFilterCountingFree(BloomFilterCounting *bf)
+{
+ if (bf != NULL) {
+ if (bf->array != NULL)
+ SCFree(bf->array);
+
+ SCFree(bf);
+ }
+}
+
+void BloomFilterCountingPrint(BloomFilterCounting *bf)
+{
+ printf("\n------ Counting Bloom Filter Stats ------\n");
+ printf("Buckets: %" PRIu32 "\n", bf->array_size);
+ printf("Counter size: %" PRIu32 "\n", bf->type);
+ printf("Memory size: %" PRIu32 " bytes\n", bf->array_size * bf->type);
+ printf("Hash function pointer: %p\n", bf->Hash);
+ printf("Hash functions: %" PRIu32 "\n", bf->hash_iterations);
+ printf("-----------------------------------------\n");
+}
+
+int BloomFilterCountingAdd(BloomFilterCounting *bf, void *data, uint16_t datalen)
+{
+ uint8_t iter = 0;
+ uint32_t hash = 0;
+
+ if (bf == NULL || data == NULL || datalen == 0)
+ return -1;
+
+ for (iter = 0; iter < bf->hash_iterations; iter++) {
+ hash = bf->Hash(data, datalen, iter, bf->array_size) * bf->type;
+ if (bf->type == 1) {
+ uint8_t *u8 = (uint8_t *)&bf->array[hash];
+ if ((*u8) != 255)
+ (*u8)++;
+ } else if (bf->type == 2) {
+ uint16_t *u16 = (uint16_t *)&bf->array[hash];
+ if ((*u16) != 65535)
+ (*u16)++;
+ } else if (bf->type == 4) {
+ uint32_t *u32 = (uint32_t *)&bf->array[hash];
+ if ((*u32) != 4294967295UL)
+ (*u32)++;
+ }
+ }
+
+ return 0;
+}
+
+int BloomFilterCountingRemove(BloomFilterCounting *bf, void *data, uint16_t datalen)
+{
+ uint8_t iter = 0;
+ uint32_t hash = 0;
+
+ if (bf == NULL || data == NULL || datalen == 0)
+ return -1;
+
+ /* only remove data that was actually added */
+ if (BloomFilterCountingTest(bf, data, datalen) == 0) {
+ printf("ERROR: BloomFilterCountingRemove tried to remove data "
+ "that was never added to the set or was already removed.\n");
+ return -1;
+ }
+
+ /* decrease counters for every iteration */
+ for (iter = 0; iter < bf->hash_iterations; iter++) {
+ hash = bf->Hash(data, datalen, iter, bf->array_size) * bf->type;
+ if (bf->type == 1) {
+ uint8_t *u8 = (uint8_t *)&bf->array[hash];
+ if ((*u8) > 0)
+ (*u8)--;
+ else {
+ printf("ERROR: BloomFilterCountingRemove tried to decrease a "
+ "counter below zero.\n");
+ return -1;
+ }
+ } else if (bf->type == 2) {
+ uint16_t *u16 = (uint16_t *)&bf->array[hash];
+ if ((*u16) > 0)
+ (*u16)--;
+ else {
+ printf("ERROR: BloomFilterCountingRemove tried to decrease a "
+ "counter below zero.\n");
+ return -1;
+ }
+ } else if (bf->type == 4) {
+ uint32_t *u32 = (uint32_t *)&bf->array[hash];
+ if ((*u32) > 0)
+ (*u32)--;
+ else {
+ printf("ERROR: BloomFilterCountingRemove tried to decrease a "
+ "counter below zero.\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Test if data matches our filter and is likely to be in the set
+ *
+ * returns 0: for no match
+ * 1: match
+ */
+int BloomFilterCountingTest(BloomFilterCounting *bf, void *data, uint16_t datalen)
+{
+ uint8_t iter = 0;
+ uint32_t hash = 0;
+ int hit = 1;
+
+ /* check each hash iteration */
+ for (iter = 0; iter < bf->hash_iterations; iter++) {
+ hash = bf->Hash(data, datalen, iter, bf->array_size) * bf->type;
+ if (bf->type == 1) {
+ uint8_t *u8 = (uint8_t *)&bf->array[hash];
+ if ((*u8) == 0x00) {
+ hit = 0;
+ break;
+ }
+ } else if (bf->type == 2) {
+ uint16_t *u16 = (uint16_t *)&bf->array[hash];
+ if ((*u16) == 0x0000) {
+ hit = 0;
+ break;
+ }
+ } else if (bf->type == 4) {
+ uint32_t *u32 = (uint32_t *)&bf->array[hash];
+ if ((*u32) == 0x00000000) {
+ hit = 0;
+ break;
+ }
+ }
+ }
+
+ return hit;
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+static uint32_t BloomHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint32_t i;
+ uint32_t hash = 0;
+
+ for (i = 0; i < datalen; i++) {
+ if (i == 0) hash += (((uint32_t)*d++));
+ else if (i == 1) hash += (((uint32_t)*d++) * datalen);
+ else hash *= (((uint32_t)*d++) * i);
+ }
+
+ hash *= (iter + datalen);
+ hash %= hash_size;
+ return hash;
+}
+
+static int BloomFilterCountingTestInit01 (void)
+{
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 4, 4, BloomHash);
+ if (bf == NULL)
+ return 0;
+
+ BloomFilterCountingFree(bf);
+ return 1;
+}
+
+/* no hash function, so it should fail */
+static int BloomFilterCountingTestInit02 (void)
+{
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 4, 4, NULL);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterCountingFree(bf);
+ return 0;
+}
+
+static int BloomFilterCountingTestInit03 (void)
+{
+ int result = 0;
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 4, 4, BloomHash);
+ if (bf == NULL)
+ return 0;
+
+ if (bf->Hash == BloomHash)
+ result = 1;
+
+ BloomFilterCountingFree(bf);
+ return result;
+}
+
+static int BloomFilterCountingTestInit04 (void)
+{
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 0, 4, BloomHash);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterCountingFree(bf);
+ return 0;
+}
+
+static int BloomFilterCountingTestInit05 (void)
+{
+ BloomFilterCounting *bf = BloomFilterCountingInit(0, 4, 4, BloomHash);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterCountingFree(bf);
+ return 0;
+}
+
+static int BloomFilterCountingTestInit06 (void)
+{
+ BloomFilterCounting *bf = BloomFilterCountingInit(32, 3, 4, BloomHash);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterCountingFree(bf);
+ return 0;
+}
+
+static int BloomFilterCountingTestAdd01 (void)
+{
+ int result = 0;
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 4, 4, BloomHash);
+ if (bf == NULL)
+ return 0;
+
+ int r = BloomFilterCountingAdd(bf, "test", 0);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterCountingFree(bf);
+ return result;
+}
+
+static int BloomFilterCountingTestAdd02 (void)
+{
+ int result = 0;
+ BloomFilterCounting *bf = BloomFilterCountingInit(1024, 4, 4, BloomHash);
+ if (bf == NULL)
+ return 0;
+
+ int r = BloomFilterCountingAdd(bf, NULL, 4);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterCountingFree(bf);
+ return result;
+}
+
+static int BloomFilterCountingTestFull01 (void)
+{
+ int result = 0;
+ BloomFilterCounting *bf = BloomFilterCountingInit(32, 4, 4, BloomHash);
+ if (bf == NULL) {
+ printf("init failed: ");
+ goto end;
+ }
+
+ int r = BloomFilterCountingAdd(bf, "test", 4);
+ if (r != 0) {
+ printf("first add: ");
+ goto end;
+ }
+
+ r = BloomFilterCountingTest(bf, "test", 4);
+ if (r != 1) {
+ printf("2nd add: ");
+ goto end;
+ }
+
+ r = BloomFilterCountingRemove(bf, "test", 4);
+ if (r != 0) {
+ printf("3rd add: ");
+ goto end;
+ }
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL)
+ BloomFilterCountingFree(bf);
+ return result;
+}
+
+static int BloomFilterCountingTestFull02 (void)
+{
+ int result = 0;
+ BloomFilterCounting *bf = BloomFilterCountingInit(32, 4, 4, BloomHash);
+ if (bf == NULL)
+ goto end;
+
+ int r = BloomFilterCountingTest(bf, "test", 4);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterCountingFree(bf);
+ return result;
+}
+#endif
+
+void BloomFilterCountingRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("BloomFilterCountingTestInit01", BloomFilterCountingTestInit01, 1);
+ UtRegisterTest("BloomFilterCountingTestInit02", BloomFilterCountingTestInit02, 1);
+ UtRegisterTest("BloomFilterCountingTestInit03", BloomFilterCountingTestInit03, 1);
+ UtRegisterTest("BloomFilterCountingTestInit04", BloomFilterCountingTestInit04, 1);
+ UtRegisterTest("BloomFilterCountingTestInit05", BloomFilterCountingTestInit05, 1);
+ UtRegisterTest("BloomFilterCountingTestInit06", BloomFilterCountingTestInit06, 1);
+
+ UtRegisterTest("BloomFilterCountingTestAdd01", BloomFilterCountingTestAdd01, 1);
+ UtRegisterTest("BloomFilterCountingTestAdd02", BloomFilterCountingTestAdd02, 1);
+
+ UtRegisterTest("BloomFilterCountingTestFull01", BloomFilterCountingTestFull01, 1);
+ UtRegisterTest("BloomFilterCountingTestFull02", BloomFilterCountingTestFull02, 1);
+#endif
+}
+
diff --git a/framework/src/suricata/src/util-bloomfilter-counting.h b/framework/src/suricata/src/util-bloomfilter-counting.h
new file mode 100644
index 00000000..80d790be
--- /dev/null
+++ b/framework/src/suricata/src/util-bloomfilter-counting.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __BLOOMFILTERCOUNTING_H__
+#define __BLOOMFILTERCOUNTING_H__
+
+/* Bloom filter structure */
+typedef struct BloomFilterCounting_ {
+ uint8_t *array;
+ uint32_t array_size; /* size in buckets */
+ uint8_t type; /* 1, 2 or 4 byte counters */
+ uint8_t hash_iterations;
+ uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t);
+} BloomFilterCounting;
+
+/* prototypes */
+BloomFilterCounting *BloomFilterCountingInit(uint32_t, uint8_t, uint8_t, uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t));
+void BloomFilterCountingFree(BloomFilterCounting *);
+void BloomFilterCountingPrint(BloomFilterCounting *);
+int BloomFilterCountingAdd(BloomFilterCounting *, void *, uint16_t);
+int BloomFilterCountingRemove(BloomFilterCounting *, void *, uint16_t);
+int BloomFilterCountingTest(BloomFilterCounting *, void *, uint16_t);
+
+void BloomFilterCountingRegisterTests(void);
+
+#endif /* __BLOOMFILTERCOUNTING_H__ */
+
diff --git a/framework/src/suricata/src/util-bloomfilter.c b/framework/src/suricata/src/util-bloomfilter.c
new file mode 100644
index 00000000..5718fb10
--- /dev/null
+++ b/framework/src/suricata/src/util-bloomfilter.c
@@ -0,0 +1,290 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Bitwise bloom filter implementation
+ */
+
+#include "suricata-common.h"
+#include "util-bloomfilter.h"
+#include "util-unittest.h"
+
+BloomFilter *BloomFilterInit(uint32_t size, uint8_t iter, uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t)) {
+ BloomFilter *bf = NULL;
+
+ if (size == 0 || iter == 0)
+ goto error;
+
+ if (Hash == NULL) {
+ //printf("ERROR: BloomFilterInit no Hash function\n");
+ goto error;
+ }
+
+ /* setup the filter */
+ bf = SCMalloc(sizeof(BloomFilter));
+ if (unlikely(bf == NULL))
+ goto error;
+ memset(bf,0,sizeof(BloomFilter));
+ bf->bitarray_size = size;
+ bf->hash_iterations = iter;
+ bf->Hash = Hash;
+
+ /* setup the bitarray */
+ bf->bitarray = SCMalloc((bf->bitarray_size/8)+1);
+ if (bf->bitarray == NULL)
+ goto error;
+ memset(bf->bitarray,0,(bf->bitarray_size/8)+1);
+
+ return bf;
+
+error:
+ if (bf != NULL) {
+ if (bf->bitarray != NULL)
+ SCFree(bf->bitarray);
+
+ SCFree(bf);
+ }
+ return NULL;
+}
+
+void BloomFilterFree(BloomFilter *bf)
+{
+ if (bf != NULL) {
+ if (bf->bitarray != NULL)
+ SCFree(bf->bitarray);
+
+ SCFree(bf);
+ }
+}
+
+void BloomFilterPrint(BloomFilter *bf)
+{
+ printf("\n---------- Bloom Filter Stats -----------\n");
+ printf("Buckets: %" PRIu32 "\n", bf->bitarray_size);
+ printf("Memory size: %" PRIu32 " bytes\n", bf->bitarray_size/8 + 1);
+ printf("Hash function pointer: %p\n", bf->Hash);
+ printf("Hash functions: %" PRIu32 "\n", bf->hash_iterations);
+ printf("-----------------------------------------\n");
+}
+
+int BloomFilterAdd(BloomFilter *bf, void *data, uint16_t datalen)
+{
+ uint8_t iter = 0;
+ uint32_t hash = 0;
+
+ if (bf == NULL || data == NULL || datalen == 0)
+ return -1;
+
+ for (iter = 0; iter < bf->hash_iterations; iter++) {
+ hash = bf->Hash(data, datalen, iter, bf->bitarray_size);
+ bf->bitarray[hash/8] |= (1<<hash%8);
+ }
+
+ return 0;
+}
+
+uint32_t BloomFilterMemoryCnt(BloomFilter *bf)
+{
+ if (bf == NULL)
+ return 0;
+
+ return 2;
+}
+
+uint32_t BloomFilterMemorySize(BloomFilter *bf)
+{
+ if (bf == NULL)
+ return 0;
+
+ return (sizeof(BloomFilter) + (bf->bitarray_size/8) + 1);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+static uint32_t BloomFilterTestHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint32_t i;
+ uint32_t hash = 0;
+
+ for (i = 0; i < datalen; i++) {
+ if (i == 0) hash += (((uint32_t)*d++));
+ else if (i == 1) hash += (((uint32_t)*d++) * datalen);
+ else hash *= (((uint32_t)*d++) * i);
+ }
+
+ hash *= (iter + datalen);
+ hash %= hash_size;
+ return hash;
+}
+
+static int BloomFilterTestInit01 (void)
+{
+ BloomFilter *bf = BloomFilterInit(1024, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ return 0;
+
+ BloomFilterFree(bf);
+ return 1;
+}
+
+/* no hash function, so it should fail */
+static int BloomFilterTestInit02 (void)
+{
+ BloomFilter *bf = BloomFilterInit(1024, 4, NULL);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterFree(bf);
+ return 0;
+}
+
+static int BloomFilterTestInit03 (void)
+{
+ int result = 0;
+ BloomFilter *bf = BloomFilterInit(1024, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ return 0;
+
+ if (bf->Hash == BloomFilterTestHash)
+ result = 1;
+
+ BloomFilterFree(bf);
+ return result;
+}
+
+static int BloomFilterTestInit04 (void)
+{
+ BloomFilter *bf = BloomFilterInit(1024, 0, BloomFilterTestHash);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterFree(bf);
+ return 0;
+}
+
+static int BloomFilterTestInit05 (void)
+{
+ BloomFilter *bf = BloomFilterInit(0, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ return 1;
+
+ BloomFilterFree(bf);
+ return 0;
+}
+
+static int BloomFilterTestAdd01 (void)
+{
+ int result = 0;
+ BloomFilter *bf = BloomFilterInit(1024, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ return 0;
+
+ int r = BloomFilterAdd(bf, "test", 0);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterFree(bf);
+ return result;
+}
+
+static int BloomFilterTestAdd02 (void)
+{
+ int result = 0;
+ BloomFilter *bf = BloomFilterInit(1024, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ return 0;
+
+ int r = BloomFilterAdd(bf, NULL, 4);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterFree(bf);
+ return result;
+}
+
+static int BloomFilterTestFull01 (void)
+{
+ int result = 0;
+ BloomFilter *bf = BloomFilterInit(32, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ goto end;
+
+ int r = BloomFilterAdd(bf, "test", 4);
+ if (r != 0)
+ goto end;
+
+ r = BloomFilterTest(bf, "test", 4);
+ if (r != 1)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterFree(bf);
+ return result;
+}
+
+static int BloomFilterTestFull02 (void)
+{
+ int result = 0;
+ BloomFilter *bf = BloomFilterInit(32, 4, BloomFilterTestHash);
+ if (bf == NULL)
+ goto end;
+
+ int r = BloomFilterTest(bf, "test", 4);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (bf != NULL) BloomFilterFree(bf);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void BloomFilterRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("BloomFilterTestInit01", BloomFilterTestInit01, 1);
+ UtRegisterTest("BloomFilterTestInit02", BloomFilterTestInit02, 1);
+ UtRegisterTest("BloomFilterTestInit03", BloomFilterTestInit03, 1);
+ UtRegisterTest("BloomFilterTestInit04", BloomFilterTestInit04, 1);
+ UtRegisterTest("BloomFilterTestInit05", BloomFilterTestInit05, 1);
+
+ UtRegisterTest("BloomFilterTestAdd01", BloomFilterTestAdd01, 1);
+ UtRegisterTest("BloomFilterTestAdd02", BloomFilterTestAdd02, 1);
+
+ UtRegisterTest("BloomFilterTestFull01", BloomFilterTestFull01, 1);
+ UtRegisterTest("BloomFilterTestFull02", BloomFilterTestFull02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-bloomfilter.h b/framework/src/suricata/src/util-bloomfilter.h
new file mode 100644
index 00000000..e7a5874f
--- /dev/null
+++ b/framework/src/suricata/src/util-bloomfilter.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __BLOOMFILTER_H__
+#define __BLOOMFILTER_H__
+
+/* Bloom Filter structure */
+typedef struct BloomFilter_ {
+ uint8_t hash_iterations;
+ uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t);
+ uint32_t bitarray_size;
+ uint8_t *bitarray;
+} BloomFilter;
+
+/* prototypes */
+BloomFilter *BloomFilterInit(uint32_t, uint8_t, uint32_t (*Hash)(void *, uint16_t, uint8_t, uint32_t));
+void BloomFilterFree(BloomFilter *);
+void BloomFilterPrint(BloomFilter *);
+int BloomFilterAdd(BloomFilter *, void *, uint16_t);
+uint32_t BloomFilterMemoryCnt(BloomFilter *);
+uint32_t BloomFilterMemorySize(BloomFilter *);
+
+void BloomFilterRegisterTests(void);
+
+/** ----- Inline functions ---- */
+
+static inline int BloomFilterTest(BloomFilter *, void *, uint16_t);
+
+static inline int BloomFilterTest(BloomFilter *bf, void *data, uint16_t datalen)
+{
+ uint8_t iter = 0;
+ uint32_t hash = 0;
+ int hit = 1;
+
+ for (iter = 0; iter < bf->hash_iterations; iter++) {
+ hash = bf->Hash(data, datalen, iter, bf->bitarray_size);
+ if (!(bf->bitarray[hash/8] & (1<<hash%8))) {
+ hit = 0;
+ break;
+ }
+ }
+
+ return hit;
+}
+
+#endif /* __BLOOMFILTER_H__ */
+
diff --git a/framework/src/suricata/src/util-buffer.c b/framework/src/suricata/src/util-buffer.c
new file mode 100644
index 00000000..d4f50b06
--- /dev/null
+++ b/framework/src/suricata/src/util-buffer.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-debug.h"
+#include "util-buffer.h"
+
+/* 10 mb */
+#define MAX_LIMIT 10485760
+
+MemBuffer *MemBufferCreateNew(uint32_t size)
+{
+ if (size > MAX_LIMIT) {
+ SCLogWarning(SC_ERR_MEM_BUFFER_API, "Mem buffer asked to create "
+ "buffer with size greater than API limit - %d", MAX_LIMIT);
+ return NULL;
+ }
+
+ uint32_t total_size = size + sizeof(MemBuffer);
+
+ MemBuffer *buffer = SCMalloc(total_size);
+ if (unlikely(buffer == NULL)) {
+ return NULL;
+ }
+ memset(buffer, 0, total_size);
+
+ buffer->size = size;
+ buffer->buffer = (uint8_t *)buffer + sizeof(MemBuffer);
+
+ return buffer;
+}
+
+/** \brief expand membuffer by size of 'expand_by'
+ *
+ * If expansion failed, buffer will still be valid.
+ *
+ * \retval result 0 ok, -1 expansion failed
+ */
+int MemBufferExpand(MemBuffer **buffer, uint32_t expand_by) {
+ if (((*buffer)->size + expand_by) > MAX_LIMIT) {
+ SCLogWarning(SC_ERR_MEM_BUFFER_API, "Mem buffer asked to create "
+ "buffer with size greater than API limit - %d", MAX_LIMIT);
+ return -1;
+ }
+
+ uint32_t total_size = (*buffer)->size + sizeof(MemBuffer) + expand_by;
+
+ MemBuffer *tbuffer = SCRealloc(*buffer, total_size);
+ if (unlikely(tbuffer == NULL)) {
+ return -1;
+ }
+
+ *buffer = tbuffer;
+ (*buffer)->size += expand_by;
+ (*buffer)->buffer = (uint8_t *)tbuffer + sizeof(MemBuffer);
+
+ SCLogDebug("expanded buffer by %u, size is now %u", expand_by, (*buffer)->size);
+ return 0;
+}
+
+void MemBufferFree(MemBuffer *buffer)
+{
+ SCFree(buffer);
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-buffer.h b/framework/src/suricata/src/util-buffer.h
new file mode 100644
index 00000000..58d5098a
--- /dev/null
+++ b/framework/src/suricata/src/util-buffer.h
@@ -0,0 +1,177 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_BUFFER_H__
+#define __UTIL_BUFFER_H__
+
+typedef struct MemBuffer_ {
+ uint8_t *buffer;
+ uint32_t size;
+ uint32_t offset;
+} MemBuffer;
+
+MemBuffer *MemBufferCreateNew(uint32_t size);
+int MemBufferExpand(MemBuffer **buffer, uint32_t expand_by);
+void MemBufferFree(MemBuffer *buffer);
+
+/**
+ * \brief Reset the mem buffer.
+ *
+ * \param mem_buffer Pointer to the mem buffer instance.
+ */
+#define MemBufferReset(mem_buffer) do { \
+ (mem_buffer)->buffer[0] = 0; \
+ (mem_buffer)->offset = 0; \
+ } while (0)
+
+/**
+ * \brief Get the MemBuffers underlying buffer.
+ */
+#define MEMBUFFER_BUFFER(mem_buffer) (mem_buffer)->buffer
+
+/**
+ * \brief Get the MemBuffers current offset.
+ */
+#define MEMBUFFER_OFFSET(mem_buffer) (mem_buffer)->offset
+
+/**
+ * \brief Get the MemBuffers current size.
+ */
+#define MEMBUFFER_SIZE(mem_buffer) (mem_buffer)->size
+
+/**
+ * \brief Write a buffer to the file pointer.
+ *
+ * Accepted buffers can contain both printable and non-printable
+ * characters. Printable characters are written in the printable
+ * format and the non-printable chars are written in hex codes
+ * using the |XX| format.
+ *
+ * For example this would be the kind of output in the file -
+ * onetwo|EF|three|ED|five
+ *
+ * \param buffer Pointer to the src MemBuffer instance to write.
+ * \param fp Pointer to the file file instance to write to.
+ */
+#define MemBufferPrintToFP(buffer, fp) do { \
+ uint32_t i; \
+ \
+ for (i = 0; i < (buffer)->offset; i++) { \
+ if (isprint(buffer->buffer[i])) \
+ fprintf(fp, "%c", (buffer)->buffer[i]); \
+ else \
+ fprintf(fp, "|%02X|", (buffer)->buffer[i]); \
+ } \
+ } while (0)
+
+/**
+ * \brief Write a buffer to the file pointer as a printable char string.
+ *
+ * \param buffer Pointer to the src MemBuffer instance to write.
+ * \param fp Pointer to the file file instance to write to.
+ */
+#define MemBufferPrintToFPAsString(mem_buffer, fp) ({ \
+ fwrite((mem_buffer)->buffer, sizeof(uint8_t), (mem_buffer)->offset, fp); \
+})
+
+/**
+ * \brief Write a buffer in hex format.
+ *
+ * \param buffer Pointer to the src MemBuffer instance to write.
+ * \param fp Pointer to the file file instance to write to.
+ */
+#define MemBufferPrintToFPAsHex(buffer, fp) do { \
+ uint32_t i; \
+ \
+ for (i = 0; i < (buffer)->offset; i++) { \
+ if (((buffer)->offset % 8) == 0) \
+ fprintf(fp, "\n"); \
+ fprintf(fp, " %02X", (buffer)->buffer[i]); \
+ } \
+ } while (0)
+
+
+/**
+ * \brief Write a raw buffer to the MemBuffer dst.
+ *
+ * When we say raw buffer it indicates a buffer that need not be
+ * purely a string buffer. It can be a pure string buffer or not or
+ * a mixture of both. Hence we don't accept any format strings.
+ *
+ * If the remaining space on the buffer is lesser than the length of
+ * the buffer to write, it is truncated to fit into the empty space.
+ *
+ * Also after every write a '\0' is appended. This would indicate
+ * that the total available space to write in the buffer is
+ * MemBuffer->size - 1 and not Membuffer->size. The reason we
+ * append the '\0' is for supporting writing pure string buffers
+ * as well, that can later be used by other string handling funcs.
+ *
+ * \param raw_buffer The buffer to write.
+ * \param raw_buffer_len Length of the above buffer.
+ */
+#define MemBufferWriteRaw(dst, raw_buffer, raw_buffer_len) do { \
+ uint32_t write_len; \
+ \
+ if (((raw_buffer_len) >= (dst)->size - (dst)->offset)) { \
+ SCLogDebug("Truncating data write since it exceeded buffer limit of " \
+ "- %"PRIu32"\n", (dst)->size); \
+ write_len = ((dst)->size - (dst)->offset) - 1; \
+ } else { \
+ write_len = (raw_buffer_len); \
+ } \
+ \
+ memcpy((dst)->buffer + (dst)->offset, (raw_buffer), write_len); \
+ (dst)->offset += write_len; \
+ dst->buffer[dst->offset] = '\0'; \
+ } while (0)
+
+/**
+ * \brief Write a string buffer to the Membuffer dst.
+ *
+ * This function takes a format string and arguments for the format
+ * string like sprintf.
+ *
+ * An example usage of this is -
+ * MemBufferWriteString(mem_buffer_instance, \"%d - %s\", 10, \"one\");
+ *
+ * \param dst The dst MemBuffer instance.
+ * \param format The format string.
+ * \param ... Variable arguments.
+ */
+#define MemBufferWriteString(dst, ...) do { \
+ int cw = snprintf((char *)(dst)->buffer + (dst)->offset, \
+ (dst)->size - (dst)->offset, \
+ __VA_ARGS__); \
+ if (cw >= 0) { \
+ if ( ((dst)->offset + cw) >= (dst)->size) { \
+ SCLogDebug("Truncating data write since it exceeded buffer " \
+ "limit of - %"PRIu32"\n", (dst)->size); \
+ (dst)->offset = (dst)->size - 1; \
+ } else { \
+ (dst->offset) += cw; \
+ } \
+ } \
+ } while (0)
+
+#endif /* __UTIL_BUFFER_H__ */
diff --git a/framework/src/suricata/src/util-byte.c b/framework/src/suricata/src/util-byte.c
new file mode 100644
index 00000000..1d8e088a
--- /dev/null
+++ b/framework/src/suricata/src/util-byte.c
@@ -0,0 +1,629 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ *
+ * Byte utility functions
+ */
+
+#include "suricata-common.h"
+#include "util-byte.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+/** \brief Turn byte array into string.
+ *
+ * All non-printables are copied over, except for '\0', which is
+ * turned into literal \0 in the string.
+ *
+ * \param bytes byte array
+ * \param nbytes number of bytes
+ * \return string nul-terminated string or NULL on error
+ */
+char *BytesToString(const uint8_t *bytes, size_t nbytes)
+{
+ size_t n = nbytes + 1;
+ size_t nulls = 0;
+
+ size_t u;
+ for (u = 0; u < nbytes; u++) {
+ if (bytes[u] == '\0')
+ nulls++;
+ }
+ n += nulls;
+
+ char *string = SCCalloc(1, n);
+ if (string == NULL)
+ return NULL;
+
+ if (nulls == 0) {
+ /* no nulls */
+ memcpy(string, bytes, nbytes);
+ } else {
+ /* nulls present */
+ char *dst = string;
+ for (u = 0; u < nbytes; u++) {
+ if (bytes[u] == '\0') {
+ *dst++ = '\\';
+ *dst++ = '0';
+ } else {
+ *dst++ = bytes[u];
+ }
+ }
+ }
+ return string;
+}
+
+int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes)
+{
+ uint64_t i64;
+ int ret;
+
+ /* Uint64 is limited to 8 bytes */
+ if (len > 8) {
+ /** \todo Need standard return values */
+ return -1;
+ }
+
+ ret = ByteExtract(&i64, e, len, bytes);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint64_t)i64;
+
+ return ret;
+}
+
+int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes)
+{
+ uint64_t i64;
+ int ret;
+
+ /* Uint32 is limited to 4 bytes */
+ if (len > 4) {
+ /** \todo Need standard return values */
+ return -1;
+ }
+
+ ret = ByteExtract(&i64, e, len, bytes);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint32_t)i64;
+
+ return ret;
+}
+
+int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes)
+{
+ uint64_t i64;
+ int ret;
+
+ /* Uint16 is limited to 2 bytes */
+ if (len > 2) {
+ /** \todo Need standard return values */
+ return -1;
+ }
+
+ ret = ByteExtract(&i64, e, len, bytes);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint16_t)i64;
+
+ return ret;
+}
+
+int ByteExtractString(uint64_t *res, int base, uint16_t len, const char *str)
+{
+ const char *ptr = str;
+ char *endptr = NULL;
+
+ /* 23 - This is the largest string (octal, with a zero prefix) that
+ * will not overflow uint64_t. The only way this length
+ * could be over 23 and still not overflow is if it were zero
+ * prefixed and we only support 1 byte of zero prefix for octal.
+ *
+ * "01777777777777777777777" = 0xffffffffffffffff
+ */
+ char strbuf[24];
+
+ if (len > 23) {
+ SCLogError(SC_ERR_ARG_LEN_LONG, "len too large (23 max)");
+ return -1;
+ }
+
+ if (len) {
+ /* Extract out the string so it can be null terminated */
+ memcpy(strbuf, str, len);
+ strbuf[len] = '\0';
+ ptr = strbuf;
+ }
+
+ errno = 0;
+ *res = strtoull(ptr, &endptr, base);
+
+ if (errno == ERANGE) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
+ return -1;
+ /* If there is no numeric value in the given string then strtoull(), makes
+ endptr equals to ptr and return 0 as result */
+ } else if (endptr == ptr && *res == 0) {
+ SCLogDebug("No numeric value");
+ return -1;
+ } else if (endptr == ptr) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
+ return -1;
+ }
+ /* This will interfere with some rules that do not know the length
+ * in advance and instead are just using the max.
+ */
+#if 0
+ else if (len && *endptr != '\0') {
+ fprintf(stderr, "ByteExtractString: Extra characters following numeric value\n");
+ return -1;
+ }
+#endif
+
+ return (endptr - ptr);
+}
+
+int ByteExtractStringUint64(uint64_t *res, int base, uint16_t len, const char *str)
+{
+ return ByteExtractString(res, base, len, str);
+}
+
+int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str)
+{
+ uint64_t i64;
+ int ret;
+
+ ret = ByteExtractString(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint32_t)i64;
+
+ if ((uint64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIu64 " > %" PRIuMAX ")", i64, (uintmax_t)UINT_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str)
+{
+ uint64_t i64;
+ int ret;
+
+ ret = ByteExtractString(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint16_t)i64;
+
+ if ((uint64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIu64 " > %" PRIuMAX ")", i64, (uintmax_t)USHRT_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str)
+{
+ uint64_t i64;
+ int ret;
+
+ ret = ByteExtractString(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (uint8_t)i64;
+
+ if ((uint64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIu64 " > %" PRIuMAX ")", i64, (uintmax_t)UCHAR_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ByteExtractStringSigned(int64_t *res, int base, uint16_t len, const char *str)
+{
+ const char *ptr = str;
+ char *endptr;
+
+ /* 23 - This is the largest string (octal, with a zero prefix) that
+ * will not overflow int64_t. The only way this length
+ * could be over 23 and still not overflow is if it were zero
+ * prefixed and we only support 1 byte of zero prefix for octal.
+ *
+ * "-0777777777777777777777" = 0xffffffffffffffff
+ */
+ char strbuf[24];
+
+ if (len > 23) {
+ SCLogError(SC_ERR_ARG_LEN_LONG, "len too large (23 max)");
+ return -1;
+ }
+
+ if (len) {
+ /* Extract out the string so it can be null terminated */
+ memcpy(strbuf, str, len);
+ strbuf[len] = '\0';
+ ptr = strbuf;
+ }
+
+ errno = 0;
+ *res = strtoll(ptr, &endptr, base);
+
+ if (errno == ERANGE) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
+ return -1;
+ } else if (endptr == str) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
+ return -1;
+ }
+ /* This will interfere with some rules that do not know the length
+ * in advance and instead are just using the max.
+ */
+#if 0
+ else if (len && *endptr != '\0') {
+ fprintf(stderr, "ByteExtractStringSigned: Extra characters following numeric value\n");
+ return -1;
+ }
+#endif
+
+ //fprintf(stderr, "ByteExtractStringSigned: Extracted base %d: 0x%" PRIx64 "\n", base, *res);
+
+ return (endptr - ptr);
+}
+
+int ByteExtractStringInt64(int64_t *res, int base, uint16_t len, const char *str)
+{
+ return ByteExtractStringSigned(res, base, len, str);
+}
+
+int ByteExtractStringInt32(int32_t *res, int base, uint16_t len, const char *str)
+{
+ int64_t i64;
+ int ret;
+
+ ret = ByteExtractStringSigned(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (int32_t)i64;
+
+ if ((int64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIi64 " > %" PRIiMAX ")\n", i64, (intmax_t)INT_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ByteExtractStringInt16(int16_t *res, int base, uint16_t len, const char *str)
+{
+ int64_t i64;
+ int ret;
+
+ ret = ByteExtractStringSigned(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (int16_t)i64;
+
+ if ((int64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIi64 " > %" PRIiMAX ")\n", i64, (intmax_t)SHRT_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ByteExtractStringInt8(int8_t *res, int base, uint16_t len, const char *str)
+{
+ int64_t i64;
+ int ret;
+
+ ret = ByteExtractStringSigned(&i64, base, len, str);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ *res = (int8_t)i64;
+
+ if ((int64_t)(*res) != i64) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range "
+ "(%" PRIi64 " > %" PRIiMAX ")\n", i64, (intmax_t)CHAR_MAX);
+ return -1;
+ }
+
+ return ret;
+}
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+static int ByteTest01 (void)
+{
+ uint16_t val = 0x0102;
+ uint16_t i16 = 0xbfbf;
+ uint8_t bytes[2] = { 0x02, 0x01 };
+ int ret = ByteExtractUint16(&i16, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 2) && (i16 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest02 (void)
+{
+ uint16_t val = 0x0102;
+ uint16_t i16 = 0xbfbf;
+ uint8_t bytes[2] = { 0x01, 0x02 };
+ int ret = ByteExtractUint16(&i16, BYTE_BIG_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 2) && (i16 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest03 (void)
+{
+ uint32_t val = 0x01020304;
+ uint32_t i32 = 0xbfbfbfbf;
+ uint8_t bytes[4] = { 0x04, 0x03, 0x02, 0x01 };
+ int ret = ByteExtractUint32(&i32, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 4) && (i32 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest04 (void)
+{
+ uint32_t val = 0x01020304;
+ uint32_t i32 = 0xbfbfbfbf;
+ uint8_t bytes[4] = { 0x01, 0x02, 0x03, 0x04 };
+ int ret = ByteExtractUint32(&i32, BYTE_BIG_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 4) && (i32 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest05 (void)
+{
+ uint64_t val = 0x0102030405060708ULL;
+ uint64_t i64 = 0xbfbfbfbfbfbfbfbfULL;
+ uint8_t bytes[8] = { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
+ int ret = ByteExtractUint64(&i64, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 8) && (i64 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest06 (void)
+{
+ uint64_t val = 0x0102030405060708ULL;
+ uint64_t i64 = 0xbfbfbfbfbfbfbfbfULL;
+ uint8_t bytes[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+ int ret = ByteExtractUint64(&i64, BYTE_BIG_ENDIAN, sizeof(bytes), bytes);
+
+ if ((ret == 8) && (i64 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest07 (void)
+{
+ const char *str = "1234567890";
+ uint64_t val = 1234567890;
+ uint64_t i64 = 0xbfbfbfbfbfbfbfbfULL;
+ int ret = ByteExtractStringUint64(&i64, 10, strlen(str), str);
+
+ if ((ret == 10) && (i64 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest08 (void)
+{
+ const char *str = "1234567890";
+ uint32_t val = 1234567890;
+ uint32_t i32 = 0xbfbfbfbf;
+ int ret = ByteExtractStringUint32(&i32, 10, strlen(str), str);
+
+ if ((ret == 10) && (i32 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest09 (void)
+{
+ const char *str = "12345";
+ uint16_t val = 12345;
+ uint16_t i16 = 0xbfbf;
+ int ret = ByteExtractStringUint16(&i16, 10, strlen(str), str);
+
+ if ((ret == 5) && (i16 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest10 (void)
+{
+ const char *str = "123";
+ uint8_t val = 123;
+ uint8_t i8 = 0xbf;
+ int ret = ByteExtractStringUint8(&i8, 10, strlen(str), str);
+
+ if ((ret == 3) && (i8 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest11 (void)
+{
+ const char *str = "-1234567890";
+ int64_t val = -1234567890;
+ int64_t i64 = 0xbfbfbfbfbfbfbfbfULL;
+ int ret = ByteExtractStringInt64(&i64, 10, strlen(str), str);
+
+ if ((ret == 11) && (i64 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest12 (void)
+{
+ const char *str = "-1234567890";
+ int32_t val = -1234567890;
+ int32_t i32 = 0xbfbfbfbf;
+ int ret = ByteExtractStringInt32(&i32, 10, strlen(str), str);
+
+ if ((ret == 11) && (i32 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest13 (void)
+{
+ const char *str = "-12345";
+ int16_t val = -12345;
+ int16_t i16 = 0xbfbf;
+ int ret = ByteExtractStringInt16(&i16, 10, strlen(str), str);
+
+ if ((ret == 6) && (i16 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ByteTest14 (void)
+{
+ const char *str = "-123";
+ int8_t val = -123;
+ int8_t i8 = 0xbf;
+ int ret = ByteExtractStringInt8(&i8, 10, strlen(str), str);
+
+ if ((ret == 4) && (i8 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \test max u32 value */
+static int ByteTest15 (void)
+{
+ const char *str = "4294967295";
+ uint32_t val = 4294967295UL;
+ uint32_t u32 = 0xffffffff;
+
+ int ret = ByteExtractStringUint32(&u32, 10, strlen(str), str);
+ if ((ret == 10) && (u32 == val)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \test max u32 value + 1 */
+static int ByteTest16 (void)
+{
+ const char *str = "4294967296";
+ uint32_t u32 = 0;
+
+ int ret = ByteExtractStringUint32(&u32, 10, strlen(str), str);
+ if (ret != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* UNITTESTS */
+
+void ByteRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ByteTest01", ByteTest01, 1);
+ UtRegisterTest("ByteTest02", ByteTest02, 1);
+ UtRegisterTest("ByteTest03", ByteTest03, 1);
+ UtRegisterTest("ByteTest04", ByteTest04, 1);
+ UtRegisterTest("ByteTest05", ByteTest05, 1);
+ UtRegisterTest("ByteTest06", ByteTest06, 1);
+ UtRegisterTest("ByteTest07", ByteTest07, 1);
+ UtRegisterTest("ByteTest08", ByteTest08, 1);
+ UtRegisterTest("ByteTest09", ByteTest09, 1);
+ UtRegisterTest("ByteTest10", ByteTest10, 1);
+ UtRegisterTest("ByteTest11", ByteTest11, 1);
+ UtRegisterTest("ByteTest12", ByteTest12, 1);
+ UtRegisterTest("ByteTest13", ByteTest13, 1);
+ UtRegisterTest("ByteTest14", ByteTest14, 1);
+ UtRegisterTest("ByteTest15", ByteTest15, 1);
+ UtRegisterTest("ByteTest16", ByteTest16, 1);
+#endif /* UNITTESTS */
+}
+
+
diff --git a/framework/src/suricata/src/util-byte.h b/framework/src/suricata/src/util-byte.h
new file mode 100644
index 00000000..82c16a4d
--- /dev/null
+++ b/framework/src/suricata/src/util-byte.h
@@ -0,0 +1,292 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Brian Rectanus <brectanu@gmail.com>
+ */
+
+#ifndef __UTIL_BYTE_H__
+#define __UTIL_BYTE_H__
+
+#include <stdint.h>
+
+#define BYTE_BIG_ENDIAN 0
+#define BYTE_LITTLE_ENDIAN 1
+
+/** Wrappers for OS dependent byte swapping functions */
+#ifdef OS_FREEBSD
+#include <sys/endian.h>
+#define SCByteSwap16(x) bswap16(x)
+#define SCByteSwap32(x) bswap32(x)
+#define SCByteSwap64(x) bswap64(x)
+#elif defined __OpenBSD__
+#include <sys/types.h>
+#define SCByteSwap16(x) swap16(x)
+#define SCByteSwap32(x) swap32(x)
+#define SCByteSwap64(x) swap64(x)
+#elif OS_DARWIN
+#include <libkern/OSByteOrder.h>
+#define SCByteSwap16(x) OSSwapInt16(x)
+#define SCByteSwap32(x) OSSwapInt32(x)
+#define SCByteSwap64(x) OSSwapInt64(x)
+#elif defined(__WIN32) || defined(_WIN32)
+/* Quick & dirty solution, nothing seems to exist for this in Win32 API */
+#define SCByteSwap16(x) \
+ ((((x) & 0xff00) >> 8) \
+ | (((x) & 0x00ff) << 8))
+#define SCByteSwap32(x) \
+ ((((x) & 0xff000000) >> 24) \
+ | (((x) & 0x00ff0000) >> 8) \
+ | (((x) & 0x0000ff00) << 8) \
+ | (((x) & 0x000000ff) << 24))
+#define SCByteSwap64(x) \
+ ((((x) & 0xff00000000000000ull) >> 56) \
+ | (((x) & 0x00ff000000000000ull) >> 40) \
+ | (((x) & 0x0000ff0000000000ull) >> 24) \
+ | (((x) & 0x000000ff00000000ull) >> 8) \
+ | (((x) & 0x00000000ff000000ull) << 8) \
+ | (((x) & 0x0000000000ff0000ull) << 24) \
+ | (((x) & 0x000000000000ff00ull) << 40) \
+ | (((x) & 0x00000000000000ffull) << 56))
+#else
+#include <byteswap.h>
+#define SCByteSwap16(x) bswap_16(x)
+#define SCByteSwap32(x) bswap_32(x)
+#define SCByteSwap64(x) bswap_64(x)
+#endif /* OS_FREEBSD */
+
+/** \brief Turn byte array into string.
+ *
+ * All non-printables are copied over, except for '\0', which is
+ * turned into literal \0 in the string.
+ *
+ * \param bytes byte array
+ * \param nbytes number of bytes
+ * \return string nul-terminated string or NULL on error
+ */
+char *BytesToString(const uint8_t *bytes, size_t nbytes);
+
+/**
+ * Extract bytes from a byte string and convert to a unint64_t.
+ *
+ * \param res Stores result
+ * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN)
+ * \param len Number of bytes to extract (8 max)
+ * \param bytes Data to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes);
+
+/**
+ * Extract bytes from a byte string and convert to a unint32_t.
+ *
+ * \param res Stores result
+ * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN)
+ * \param len Number of bytes to extract (8 max)
+ * \param bytes Data to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes);
+
+/**
+ * Extract bytes from a byte string and convert to a unint16_t.
+ *
+ * \param res Stores result
+ * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN)
+ * \param len Number of bytes to extract (8 max)
+ * \param bytes Data to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes);
+
+/**
+ * Extract unsigned integer value from a string.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractString(uint64_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract unsigned integer value from a string as uint64_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param len Number of bytes to extract (23 max)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringUint64(uint64_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract unsigned integer value from a string as uint32_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract unsigned integer value from a string as uint16_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract unsigned integer value from a string as uint8_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract signed integer value from a string.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringSigned(int64_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract signed integer value from a string as uint64_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringInt64(int64_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract signed integer value from a string as uint32_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringInt32(int32_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract signed integer value from a string as uint16_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringInt16(int16_t *res, int base, uint16_t len, const char *str);
+
+/**
+ * Extract signed integer value from a string as uint8_t.
+ *
+ * \param res Stores result
+ * \param base Base of the number to extract
+ * \param len Number of bytes to extract (23 max or 0 for unbounded)
+ * \param str String to extract from
+ *
+ * \return n Number of bytes extracted on success
+ * \return -1 On error
+ */
+int ByteExtractStringInt8(int8_t *res, int base, uint16_t len, const char *str);
+
+#ifdef UNITTESTS
+void ByteRegisterTests(void);
+#endif /* UNITTESTS */
+
+/** ------ Inline functions ----- */
+static inline int ByteExtract(uint64_t *res, int e, uint16_t len, const uint8_t *bytes)
+{
+ uint64_t b = 0;
+ int i;
+
+ if ((e != BYTE_BIG_ENDIAN) && (e != BYTE_LITTLE_ENDIAN)) {
+ /** \todo Need standard return values */
+ return -1;
+ }
+
+ *res = 0;
+
+ /* Go through each byte and merge it into the result in the correct order */
+ /** \todo Probably a more efficient way to do this. */
+ for (i = 0; i < len; i++) {
+
+ if (e == BYTE_LITTLE_ENDIAN) {
+ b = bytes[i];
+ }
+ else {
+ b = bytes[len - i - 1];
+ }
+
+ *res |= (b << ((i & 7) << 3));
+
+ }
+
+ return len;
+}
+
+
+#endif /* __UTIL_BYTE_H__ */
+
diff --git a/framework/src/suricata/src/util-checksum.c b/framework/src/suricata/src/util-checksum.c
new file mode 100644
index 00000000..455e2f52
--- /dev/null
+++ b/framework/src/suricata/src/util-checksum.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2011-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Util functions for checskum.
+ */
+
+#include "suricata-common.h"
+
+#include "util-checksum.h"
+
+int ReCalculateChecksum(Packet *p)
+{
+ if (PKT_IS_IPV4(p)) {
+ if (PKT_IS_TCP(p)) {
+ /* TCP */
+ p->tcph->th_sum = 0;
+ p->tcph->th_sum = TCPCalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->tcph, (p->payload_len + TCP_GET_HLEN(p)));
+ } else if (PKT_IS_UDP(p)) {
+ p->udph->uh_sum = 0;
+ p->udph->uh_sum = UDPV4CalculateChecksum(p->ip4h->s_ip_addrs,
+ (uint16_t *)p->udph, (p->payload_len + UDP_HEADER_LEN));
+ }
+ /* IPV4 */
+ p->ip4h->ip_csum = 0;
+ p->ip4h->ip_csum = IPV4CalculateChecksum((uint16_t *)p->ip4h,
+ IPV4_GET_RAW_HLEN(p->ip4h));
+ } else if (PKT_IS_IPV6(p)) {
+ /* just TCP for IPV6 */
+ if (PKT_IS_TCP(p)) {
+ p->tcph->th_sum = 0;
+ p->tcph->th_sum = TCPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->tcph, (p->payload_len + TCP_GET_HLEN(p)));
+ } else if (PKT_IS_UDP(p)) {
+ p->udph->uh_sum = 0;
+ p->udph->uh_sum = UDPV6CalculateChecksum(p->ip6h->s_ip6_addrs,
+ (uint16_t *)p->udph, (p->payload_len + UDP_HEADER_LEN));
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Check if the number of invalid checksums indicate checksum
+ * offloading in place.
+ *
+ * \retval 1 yes, offloading in place
+ * \retval 0 no, no offloading used
+ */
+int ChecksumAutoModeCheck(uint32_t thread_count,
+ unsigned int iface_count, unsigned int iface_fail)
+{
+ if (thread_count == CHECKSUM_SAMPLE_COUNT) {
+ if (iface_fail != 0) {
+ if ((iface_count / iface_fail) < CHECKSUM_INVALID_RATIO) {
+ SCLogInfo("More than 1/%dth of packets have an invalid "
+ "checksum, assuming checksum offloading is used (%u/%u)",
+ CHECKSUM_INVALID_RATIO, iface_fail, iface_count);
+ return 1;
+ } else {
+ SCLogInfo("Less than 1/%dth of packets have an invalid "
+ "checksum, assuming checksum offloading is NOT used (%u/%u)",
+ CHECKSUM_INVALID_RATIO, iface_fail, iface_count);
+ }
+ } else {
+ SCLogInfo("No packets with invalid checksum, assuming checksum offloading is NOT used");
+ }
+ }
+ return 0;
+}
diff --git a/framework/src/suricata/src/util-checksum.h b/framework/src/suricata/src/util-checksum.h
new file mode 100644
index 00000000..dc14334c
--- /dev/null
+++ b/framework/src/suricata/src/util-checksum.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2011-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_CHECKSUM_H__
+#define __UTIL_CHECKSUM_H__
+
+int ReCalculateChecksum(Packet *p);
+int ChecksumAutoModeCheck(uint32_t thread_count,
+ unsigned int iface_count, unsigned int iface_fail);
+
+/* constant linked with detection of interface with
+ * invalid checksums */
+#define CHECKSUM_SAMPLE_COUNT 1000
+#define CHECKSUM_INVALID_RATIO 10
+
+#endif
diff --git a/framework/src/suricata/src/util-cidr.c b/framework/src/suricata/src/util-cidr.c
new file mode 100644
index 00000000..300a4455
--- /dev/null
+++ b/framework/src/suricata/src/util-cidr.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * CIDR utility functions
+ */
+
+#include "suricata-common.h"
+
+static uint32_t cidrs[33];
+
+void CIDRInit(void)
+{
+ int i = 0;
+
+ /* skip 0 as it will result in 0xffffffff */
+ cidrs[0] = 0;
+ for (i = 1; i < 33; i++) {
+ cidrs[i] = htonl(0xFFFFFFFF << (32 - i));
+ //printf("CIDRInit: cidrs[%02d] = 0x%08X\n", i, cidrs[i]);
+ }
+}
+
+uint32_t CIDRGet(int cidr)
+{
+ if (cidr < 0 || cidr > 32)
+ return 0;
+ return cidrs[cidr];
+}
+
diff --git a/framework/src/suricata/src/util-cidr.h b/framework/src/suricata/src/util-cidr.h
new file mode 100644
index 00000000..ee275b4c
--- /dev/null
+++ b/framework/src/suricata/src/util-cidr.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_NETMASK_H__
+#define __UTIL_NETMASK_H__
+
+void CIDRInit(void);
+uint32_t CIDRGet(int);
+
+#endif /* __UTIL_NETMASK_H__ */
+
diff --git a/framework/src/suricata/src/util-classification-config.c b/framework/src/suricata/src/util-classification-config.c
new file mode 100644
index 00000000..e88d4f20
--- /dev/null
+++ b/framework/src/suricata/src/util-classification-config.c
@@ -0,0 +1,839 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Used for parsing a classification.config file
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "util-hash.h"
+
+#include "conf.h"
+#include "util-classification-config.h"
+#include "util-unittest.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-fmemopen.h"
+
+/* Regex to parse the classtype argument from a Signature. The first substring
+ * holds the classtype name, the second substring holds the classtype the
+ * classtype description, and the third argument holds the priority */
+#define DETECT_CLASSCONFIG_REGEX "^\\s*config\\s*classification\\s*:\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*,\\s*(.+)\\s*,\\s*(\\d+)\\s*$"
+
+/* Default path for the classification.config file */
+#if defined OS_WIN32 || defined __CYGWIN__
+#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\classification.config"
+#else
+#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/classification.config"
+#endif
+
+static pcre *regex = NULL;
+static pcre_extra *regex_study = NULL;
+
+uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen);
+char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2);
+void SCClassConfClasstypeHashFree(void *ch);
+static char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx);
+
+void SCClassConfInit(void)
+{
+ const char *eb = NULL;
+ int eo;
+ int opts = 0;
+
+ regex = pcre_compile(DETECT_CLASSCONFIG_REGEX, opts, &eb, &eo, NULL);
+ if (regex == NULL) {
+ SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ DETECT_CLASSCONFIG_REGEX, eo, eb);
+ return;
+ }
+
+ regex_study = pcre_study(regex, 0, &eb);
+ if (eb != NULL) {
+ pcre_free(regex);
+ regex = NULL;
+ SCLogDebug("pcre study failed: %s", eb);
+ return;
+ }
+ return;
+}
+
+void SCClassConfDeinit(void)
+{
+ if (regex != NULL) {
+ pcre_free(regex);
+ regex = NULL;
+ }
+ if (regex_study != NULL) {
+ pcre_free(regex_study);
+ regex_study = NULL;
+ }
+}
+
+
+/**
+ * \brief Inits the context to be used by the Classification Config parsing API.
+ *
+ * This function initializes the hash table to be used by the Detection
+ * Engine Context to hold the data from the classification.config file,
+ * obtains the file desc to parse the classification.config file, and
+ * inits the regex used to parse the lines from classification.config
+ * file.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval fp NULL on error
+ */
+FILE *SCClassConfInitContextAndLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ char *filename = NULL;
+
+ /* init the hash table to be used by the classification config Classtypes */
+ de_ctx->class_conf_ht = HashTableInit(128, SCClassConfClasstypeHashFunc,
+ SCClassConfClasstypeHashCompareFunc,
+ SCClassConfClasstypeHashFree);
+ if (de_ctx->class_conf_ht == NULL) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "Error initializing the hash "
+ "table");
+ goto error;
+ }
+
+ /* if it is not NULL, use the file descriptor. The hack so that we can
+ * avoid using a dummy classification file for testing purposes and
+ * instead use an input stream against a buffer containing the
+ * classification strings */
+ if (fd == NULL) {
+ filename = SCClassConfGetConfFilename(de_ctx);
+ if ( (fd = fopen(filename, "r")) == NULL) {
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests())
+ goto error; // silently fail
+#endif
+ SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
+ goto error;
+ }
+ }
+
+ return fd;
+
+ error:
+ if (de_ctx->class_conf_ht != NULL) {
+ HashTableFree(de_ctx->class_conf_ht);
+ de_ctx->class_conf_ht = NULL;
+ }
+ if (fd != NULL) {
+ fclose(fd);
+ fd = NULL;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * \brief Returns the path for the Classification Config file. We check if we
+ * can retrieve the path from the yaml conf file. If it is not present,
+ * return the default path for the classification file which is
+ * "./classification.config".
+ *
+ * \retval log_filename Pointer to a string containing the path for the
+ * Classification Config file.
+ */
+static char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx)
+{
+ char *log_filename = NULL;
+ char config_value[256] = "";
+
+ if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
+ snprintf(config_value, sizeof(config_value),
+ "%s.classification-file", de_ctx->config_prefix);
+
+ /* try loading prefix setting, fall back to global if that
+ * fails. */
+ if (ConfGet(config_value, &log_filename) != 1) {
+ if (ConfGet("classification-file", &log_filename) != 1) {
+ log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
+ }
+ }
+ } else {
+ if (ConfGet("classification-file", &log_filename) != 1) {
+ log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
+ }
+ }
+
+ return log_filename;
+}
+
+/**
+ * \brief Releases resources used by the Classification Config API.
+ */
+static void SCClassConfDeInitLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ if (fd != NULL) {
+ fclose(fd);
+ fd = NULL;
+ }
+}
+
+/**
+ * \brief Releases resources used by the Classification Config API.
+ */
+void SCClassConfDeInitContext(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->class_conf_ht != NULL)
+ HashTableFree(de_ctx->class_conf_ht);
+
+ de_ctx->class_conf_ht = NULL;
+
+ return;
+}
+
+/**
+ * \brief Converts a string to lowercase.
+ *
+ * \param str Pointer to the string to be converted.
+ */
+static char *SCClassConfStringToLowercase(const char *str)
+{
+ char *new_str = NULL;
+ char *temp_str = NULL;
+
+ if ( (new_str = SCStrdup(str)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ return NULL;
+ }
+
+ temp_str = new_str;
+ while (*temp_str != '\0') {
+ *temp_str = tolower((unsigned char)*temp_str);
+ temp_str++;
+ }
+
+ return new_str;
+}
+
+/**
+ * \brief Parses a line from the classification file and adds it to Classtype
+ * hash table in DetectEngineCtx, i.e. DetectEngineCtx->class_conf_ht.
+ *
+ * \param rawstr Pointer to the string to be parsed.
+ * \param index Relative index of the string to be parsed.
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCClassConfAddClasstype(char *rawstr, uint8_t index, DetectEngineCtx *de_ctx)
+{
+ char ct_name[64];
+ char ct_desc[512];
+ char ct_priority_str[16];
+ int ct_priority = 0;
+ uint8_t ct_id = index;
+
+ SCClassConfClasstype *ct_new = NULL;
+ SCClassConfClasstype *ct_lookup = NULL;
+
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
+ if (ret < 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid Classtype in "
+ "classification.config file");
+ goto error;
+ }
+
+ /* retrieve the classtype name */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, ct_name, sizeof(ct_name));
+ if (ret < 0) {
+ SCLogInfo("pcre_copy_substring() failed");
+ goto error;
+ }
+
+ /* retrieve the classtype description */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 2, ct_desc, sizeof(ct_desc));
+ if (ret < 0) {
+ SCLogInfo("pcre_copy_substring() failed");
+ goto error;
+ }
+
+ /* retrieve the classtype priority */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 3, ct_priority_str, sizeof(ct_priority_str));
+ if (ret < 0) {
+ SCLogInfo("pcre_copy_substring() failed");
+ goto error;
+ }
+ if (strlen(ct_priority_str) == 0) {
+ goto error;
+ }
+
+ ct_priority = atoi(ct_priority_str);
+
+ /* Create a new instance of the parsed Classtype string */
+ ct_new = SCClassConfAllocClasstype(ct_id, ct_name, ct_desc, ct_priority);
+ if (ct_new == NULL)
+ goto error;
+
+ /* Check if the Classtype is present in the HashTable. In case it's present
+ * ignore it, as it is a duplicate. If not present, add it to the table */
+ ct_lookup = HashTableLookup(de_ctx->class_conf_ht, ct_new, 0);
+ if (ct_lookup == NULL) {
+ if (HashTableAdd(de_ctx->class_conf_ht, ct_new, 0) < 0)
+ SCLogDebug("HashTable Add failed");
+ } else {
+ SCLogDebug("Duplicate classtype found inside classification.config");
+ if (ct_new->classtype_desc) SCFree(ct_new->classtype_desc);
+ if (ct_new->classtype) SCFree(ct_new->classtype);
+ SCFree(ct_new);
+ }
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Checks if a string is a comment or a blank line.
+ *
+ * Comments lines are lines of the following format -
+ * "# This is a comment string" or
+ * " # This is a comment string".
+ *
+ * \param line String that has to be checked
+ *
+ * \retval 1 On the argument string being a comment or blank line
+ * \retval 0 Otherwise
+ */
+static int SCClassConfIsLineBlankOrComment(char *line)
+{
+ while (*line != '\0') {
+ /* we have a comment */
+ if (*line == '#')
+ return 1;
+
+ /* this line is neither a comment line, nor a blank line */
+ if (!isspace((unsigned char)*line))
+ return 0;
+
+ line++;
+ }
+
+ /* we have a blank line */
+ return 1;
+}
+
+/**
+ * \brief Parses the Classification Config file and updates the
+ * DetectionEngineCtx->class_conf_ht with the Classtype information.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ */
+void SCClassConfParseFile(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ char line[1024];
+ uint8_t i = 1;
+
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if (SCClassConfIsLineBlankOrComment(line))
+ continue;
+
+ SCClassConfAddClasstype(line, i, de_ctx);
+ i++;
+ }
+
+#ifdef UNITTESTS
+ SCLogInfo("Added \"%d\" classification types from the classification file",
+ de_ctx->class_conf_ht->count);
+#endif
+
+ return;
+}
+
+/**
+ * \brief Returns a new SCClassConfClasstype instance. The classtype string
+ * is converted into lowercase, before being assigned to the instance.
+ *
+ * \param classtype Pointer to the classification type.
+ * \param classtype_desc Pointer to the classification type description.
+ * \param priority Holds the priority for the classification type.
+ *
+ * \retval ct Pointer to the new instance of SCClassConfClasstype on success;
+ * NULL on failure.
+ */
+SCClassConfClasstype *SCClassConfAllocClasstype(uint8_t classtype_id,
+ const char *classtype,
+ const char *classtype_desc,
+ int priority)
+{
+ SCClassConfClasstype *ct = NULL;
+
+ if (classtype == NULL)
+ return NULL;
+
+ if ( (ct = SCMalloc(sizeof(SCClassConfClasstype))) == NULL)
+ return NULL;
+ memset(ct, 0, sizeof(SCClassConfClasstype));
+
+ if ( (ct->classtype = SCClassConfStringToLowercase(classtype)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+
+ SCClassConfDeAllocClasstype(ct);
+ return NULL;
+ }
+
+ if (classtype_desc != NULL &&
+ (ct->classtype_desc = SCStrdup(classtype_desc)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+
+ SCClassConfDeAllocClasstype(ct);
+ return NULL;
+ }
+
+ ct->classtype_id = classtype_id;
+ ct->priority = priority;
+
+ return ct;
+}
+
+/**
+ * \brief Frees a SCClassConfClasstype instance
+ *
+ * \param Pointer to the SCClassConfClasstype instance that has to be freed
+ */
+void SCClassConfDeAllocClasstype(SCClassConfClasstype *ct)
+{
+ if (ct != NULL) {
+ if (ct->classtype != NULL)
+ SCFree(ct->classtype);
+
+ if (ct->classtype_desc != NULL)
+ SCFree(ct->classtype_desc);
+
+ SCFree(ct);
+ }
+
+ return;
+}
+
+/**
+ * \brief Hashing function to be used to hash the Classtype name. Would be
+ * supplied as an argument to the HashTableInit function for
+ * DetectEngineCtx->class_conf_ht.
+ *
+ * \param ht Pointer to the HashTable.
+ * \param data Pointer to the data to be hashed. In this case, the data
+ * would be a pointer to a SCClassConfClasstype instance.
+ * \param datalen Not used by this function.
+ */
+uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ SCClassConfClasstype *ct = (SCClassConfClasstype *)data;
+ uint32_t hash = 0;
+ int i = 0;
+
+ int len = strlen(ct->classtype);
+
+ for (i = 0; i < len; i++)
+ hash += tolower((unsigned char)(ct->classtype)[i]);
+
+ hash = hash % ht->array_size;
+
+ return hash;
+}
+
+/**
+ * \brief Used to compare two Classtypes that have been stored in the HashTable.
+ * This function is supplied as an argument to the HashTableInit function
+ * for DetectionEngineCtx->class_conf_ct.
+ *
+ * \param data1 Pointer to the first SCClassConfClasstype to be compared.
+ * \param len1 Not used by this function.
+ * \param data2 Pointer to the second SCClassConfClasstype to be compared.
+ * \param len2 Not used by this function.
+ *
+ * \retval 1 On data1 and data2 being equal.
+ * \retval 0 On data1 and data2 not being equal.
+ */
+char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2)
+{
+ SCClassConfClasstype *ct1 = (SCClassConfClasstype *)data1;
+ SCClassConfClasstype *ct2 = (SCClassConfClasstype *)data2;
+ int len1 = 0;
+ int len2 = 0;
+
+ if (ct1 == NULL || ct2 == NULL)
+ return 0;
+
+ if (ct1->classtype == NULL || ct2->classtype == NULL)
+ return 0;
+
+ len1 = strlen(ct1->classtype);
+ len2 = strlen(ct2->classtype);
+
+ if (len1 == len2 && memcmp(ct1->classtype, ct2->classtype, len1) == 0) {
+ SCLogDebug("Match found inside Classification-Config hash function");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Used to free the Classification Config Hash Data that was stored in
+ * DetectEngineCtx->class_conf_ht Hashtable.
+ *
+ * \param ch Pointer to the data that has to be freed.
+ */
+void SCClassConfClasstypeHashFree(void *ch)
+{
+ SCClassConfDeAllocClasstype(ch);
+
+ return;
+}
+
+/**
+ * \brief Loads the Classtype info from the classification.config file.
+ *
+ * The classification.config file contains the different classtypes,
+ * that can be used to label Signatures. Each line of the file should
+ * have the following format -
+ * classtype_name, classtype_description, priority
+ * None of the above parameters should hold a quote inside the file.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context that should be updated
+ * with Classtype information.
+ */
+void SCClassConfLoadClassficationConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ fd = SCClassConfInitContextAndLocalResources(de_ctx, fd);
+ if (fd == NULL) {
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests() && fd == NULL) {
+ return;
+ }
+#endif
+ SCLogError(SC_ERR_OPENING_FILE, "please check the \"classification-file\" "
+ "option in your suricata.yaml file");
+ return;
+ }
+
+ SCClassConfParseFile(de_ctx, fd);
+ SCClassConfDeInitLocalResources(de_ctx, fd);
+
+ return;
+}
+
+/**
+ * \brief Gets the classtype from the corresponding hash table stored
+ * in the Detection Engine Context's class conf ht, given the
+ * classtype name.
+ *
+ * \param ct_name Pointer to the classtype name that has to be looked up.
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval lookup_ct_info Pointer to the SCClassConfClasstype instance from
+ * the hash table on success; NULL on failure.
+ */
+SCClassConfClasstype *SCClassConfGetClasstype(const char *ct_name,
+ DetectEngineCtx *de_ctx)
+{
+ char name[strlen(ct_name) + 1];
+ size_t s;
+ for (s = 0; s < strlen(ct_name); s++)
+ name[s] = tolower((unsigned char)ct_name[s]);
+ name[s] = '\0';
+
+ SCClassConfClasstype ct_lookup = {0, name, NULL, 0 };
+ SCClassConfClasstype *lookup_ct_info = HashTableLookup(de_ctx->class_conf_ht,
+ &ct_lookup, 0);
+ return lookup_ct_info;
+}
+
+/*----------------------------------Unittests---------------------------------*/
+
+
+#ifdef UNITTESTS
+
+/**
+ * \brief Creates a dummy classification file, with all valid Classtypes, for
+ * testing purposes.
+ *
+ * \file_path Pointer to the file_path for the dummy classification file.
+ */
+FILE *SCClassConfGenerateValidDummyClassConfigFD01(void)
+{
+ const char *buffer =
+ "config classification: nothing-wrong,Nothing Wrong With Us,3\n"
+ "config classification: unknown,Unknown are we,3\n"
+ "config classification: bad-unknown,We think it's bad, 2\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy classification file, with some valid Classtypes and a
+ * couple of invalid Classtypes, for testing purposes.
+ *
+ * \file_path Pointer to the file_path for the dummy classification file.
+ */
+FILE *SCClassConfGenerateInValidDummyClassConfigFD02(void)
+{
+ const char *buffer =
+ "config classification: not-suspicious,Not Suspicious Traffic,3\n"
+ "onfig classification: unknown,Unknown Traffic,3\n"
+ "config classification: _badunknown,Potentially Bad Traffic, 2\n"
+ "config classification: bamboola1,Unknown Traffic,3\n"
+ "config classification: misc-activity,Misc activity,-1\n"
+ "config classification: policy-violation,Potential Corporate "
+ "config classification: bamboola,Unknown Traffic,3\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy classification file, with all invalid Classtypes, for
+ * testing purposes.
+ *
+ * \file_path Pointer to the file_path for the dummy classification file.
+ */
+FILE *SCClassConfGenerateInValidDummyClassConfigFD03(void)
+{
+ const char *buffer =
+ "conig classification: not-suspicious,Not Suspicious Traffic,3\n"
+ "onfig classification: unknown,Unknown Traffic,3\n"
+ "config classification: _badunknown,Potentially Bad Traffic, 2\n"
+ "config classification: misc-activity,Misc activity,-1\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check that the classification file is loaded and the detection engine
+ * content class_conf_hash_table loaded with the classtype data.
+ */
+int SCClassConfTest01(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return result;
+
+ result = (de_ctx->class_conf_ht->count == 3);
+ if (result == 0) printf("de_ctx->class_conf_ht->count %u: ", de_ctx->class_conf_ht->count);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Check that invalid classtypes present in the classification config file
+ * aren't loaded.
+ */
+int SCClassConfTest02(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD03();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return result;
+
+ result = (de_ctx->class_conf_ht->count == 0);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Check that only valid classtypes are loaded into the hash table from
+ * the classfication.config file.
+ */
+int SCClassConfTest03(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD02();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return result;
+
+ result = (de_ctx->class_conf_ht->count == 3);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Check if the classtype info from the classification.config file have
+ * been loaded into the hash table.
+ */
+int SCClassConfTest04(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return 0;
+
+ result = (de_ctx->class_conf_ht->count == 3);
+
+ result &= (SCClassConfGetClasstype("unknown", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Check if the classtype info from the invalid classification.config file
+ * have not been loaded into the hash table, and cross verify to check
+ * that the hash table contains no classtype data.
+ */
+int SCClassConfTest05(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD03();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return 0;
+
+ result = (de_ctx->class_conf_ht->count == 0);
+
+ result &= (SCClassConfGetClasstype("unknown", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Check if the classtype info from the classification.config file have
+ * been loaded into the hash table.
+ */
+int SCClassConfTest06(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD02();
+ SCClassConfLoadClassficationConfigFile(de_ctx, fd);
+
+ if (de_ctx->class_conf_ht == NULL)
+ return 0;
+
+ result = (de_ctx->class_conf_ht->count == 3);
+
+ result &= (SCClassConfGetClasstype("unknown", de_ctx) == NULL);
+ result &= (SCClassConfGetClasstype("not-suspicious", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("bamboola1", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("bamboola1", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("BAMBOolA1", de_ctx) != NULL);
+ result &= (SCClassConfGetClasstype("unkNOwn", de_ctx) == NULL);
+
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for Classification Config API.
+ */
+void SCClassConfRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("SCClassConfTest01", SCClassConfTest01, 1);
+ UtRegisterTest("SCClassConfTest02", SCClassConfTest02, 1);
+ UtRegisterTest("SCClassConfTest03", SCClassConfTest03, 1);
+ UtRegisterTest("SCClassConfTest04", SCClassConfTest04, 1);
+ UtRegisterTest("SCClassConfTest05", SCClassConfTest05, 1);
+ UtRegisterTest("SCClassConfTest06", SCClassConfTest06, 1);
+
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/util-classification-config.h b/framework/src/suricata/src/util-classification-config.h
new file mode 100644
index 00000000..7e916c2c
--- /dev/null
+++ b/framework/src/suricata/src/util-classification-config.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_CLASSIFICATION_CONFIG_H__
+#define __UTIL_CLASSIFICATION_CONFIG_H__
+
+/**
+ * \brief Container for a Classtype from the Classification.config file.
+ */
+typedef struct SCClassConfClasstype_ {
+ /* The index of the classification within classification.confg */
+ uint8_t classtype_id;
+
+ /* The classtype name. This is the primary key for a Classification. */
+ char *classtype;
+
+ /* Description for a classification. Would be used while printing out
+ * the classification info for a Signature, by the fast-log module. */
+ char *classtype_desc;
+
+ /* The priority this classification type carries */
+ int priority;
+} SCClassConfClasstype;
+
+SCClassConfClasstype *SCClassConfAllocClasstype(uint8_t, const char *,
+ const char *, int);
+void SCClassConfDeAllocClasstype(SCClassConfClasstype *);
+void SCClassConfLoadClassficationConfigFile(DetectEngineCtx *, FILE *fd);
+SCClassConfClasstype *SCClassConfGetClasstype(const char *,
+ DetectEngineCtx *);
+void SCClassConfDeInitContext(DetectEngineCtx *);
+void SCClassConfRegisterTests(void);
+
+/* for unittests */
+FILE *SCClassConfGenerateValidDummyClassConfigFD01(void);
+FILE *SCClassConfGenerateInValidDummyClassConfigFD02(void);
+FILE *SCClassConfGenerateInValidDummyClassConfigFD03(void);
+
+void SCClassConfInit(void);
+void SCClassConfDeinit(void);
+
+#endif /* __UTIL_CLASSIFICATION_CONFIG_H__ */
diff --git a/framework/src/suricata/src/util-clock.h b/framework/src/suricata/src/util-clock.h
new file mode 100644
index 00000000..e6e3517b
--- /dev/null
+++ b/framework/src/suricata/src/util-clock.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_CLOCK_H__
+#define __UTIL_CLOCK_H__
+
+#include <time.h>
+
+/* Feel free to add more macros */
+
+#define CLOCK_INIT clock_t clo1, clo2; clo1 = clo2 = 0;
+#define CLOCK_START clo1 = clock()
+
+#define CLOCK_END clo2 = clock()
+
+#define CLOCK_PRINT_SEC printf("Seconds spent: %.4fs\n", ((clo2 - clo1)/(double)CLOCKS_PER_SEC))
+
+#define GET_CLOCK_END_SECS ((clo1 - clo2)/(double)CLOCKS_PER_SEC)
+
+#endif /*__UTIL_CLOCK_H__ */
diff --git a/framework/src/suricata/src/util-conf.c b/framework/src/suricata/src/util-conf.c
new file mode 100644
index 00000000..e0f25d1d
--- /dev/null
+++ b/framework/src/suricata/src/util-conf.c
@@ -0,0 +1,65 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "conf.h"
+
+TmEcode ConfigSetLogDirectory(char *name)
+{
+ return ConfSetFinal("default-log-dir", name) ? TM_ECODE_OK : TM_ECODE_FAILED;
+}
+
+char *ConfigGetLogDirectory()
+{
+ char *log_dir = NULL;
+
+ if (ConfGet("default-log-dir", &log_dir) != 1) {
+#ifdef OS_WIN32
+ log_dir = _getcwd(NULL, 0);
+ if (log_dir == NULL) {
+ log_dir = DEFAULT_LOG_DIR;
+ }
+#else
+ log_dir = DEFAULT_LOG_DIR;
+#endif /* OS_WIN32 */
+ }
+
+ return log_dir;
+}
+
+TmEcode ConfigCheckLogDirectory(char *log_dir)
+{
+ SCEnter();
+#ifdef OS_WIN32
+ struct _stat buf;
+ if (_stat(log_dir, &buf) != 0) {
+#else
+ struct stat buf;
+ if (stat(log_dir, &buf) != 0) {
+#endif /* OS_WIN32 */
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
diff --git a/framework/src/suricata/src/util-conf.h b/framework/src/suricata/src/util-conf.h
new file mode 100644
index 00000000..74d87e30
--- /dev/null
+++ b/framework/src/suricata/src/util-conf.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ */
+
+#ifndef __UTIL_UTIL_CONF_H__
+#define __UTIL_UTIL_CONF_H__
+
+TmEcode ConfigSetLogDirectory(char *name);
+char *ConfigGetLogDirectory();
+TmEcode ConfigCheckLogDirectory(char *log_dir);
+
+#endif /* __UTIL_UTIL_CONF_H__ */
diff --git a/framework/src/suricata/src/util-coredump-config.c b/framework/src/suricata/src/util-coredump-config.c
new file mode 100644
index 00000000..2115f930
--- /dev/null
+++ b/framework/src/suricata/src/util-coredump-config.c
@@ -0,0 +1,206 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ *
+ * Coredump configuration
+ */
+
+#define _FILE_OFFSET_BITS 64
+#include "util-coredump-config.h"
+#include "conf.h"
+#include <sys/resource.h>
+
+/**
+ * \brief Configures the core dump size.
+ *
+ * \retval Returns 1 on success and 0 on failure.
+ *
+ */
+int32_t CoredumpLoadConfig (void)
+{
+ /* get core dump configuration settings for suricata */
+ char* dump_size_config = NULL;
+ rlim_t max_dump = 0;
+ uint32_t unlimited = 0;
+ size_t rlim_size = sizeof(rlim_t);
+
+ if (ConfGet ("coredump.max-dump", &dump_size_config) == 0) {
+ SCLogDebug ("core dump size not specified");
+ return 1;
+ }
+ if (strcasecmp (dump_size_config, "unlimited") == 0) {
+ unlimited = 1;
+ }
+ else {
+ /* disallow negative values */
+ if (strchr (dump_size_config, '-') != NULL) {
+ SCLogInfo ("Negative value for core dump size; ignored.");
+ return 0;
+ }
+ /* the size of rlim_t is platform dependent */
+ if (rlim_size > 8) {
+ SCLogInfo ("Unexpected type for rlim_t");
+ return 0;
+ }
+ errno = 0;
+ if (rlim_size == 8) {
+ max_dump = (rlim_t) strtoull (dump_size_config, NULL, 10);
+ }
+ else if (rlim_size == 4) {
+ max_dump = (rlim_t) strtoul (dump_size_config, NULL, 10);
+ }
+ if ((errno == ERANGE) || (errno != 0 && max_dump == 0)) {
+ SCLogInfo ("Illegal core dump size: %s.", dump_size_config);
+ return 0;
+ }
+ SCLogInfo ("Max dump is %llu", (unsigned long long) max_dump);
+ }
+
+#if defined OS_WIN32
+ /* todo: use the registry to get/set dump configuration */
+ SCLogInfo("Configuring core dump is not yet supported on Windows.");
+ return 0;
+#endif
+
+#ifdef HAVE_SYS_PRCTL_H
+ /* Linux specific core dump configuration; set dumpable flag if needed */
+ int dumpable = 0;
+ dumpable = prctl (PR_GET_DUMPABLE, 0, 0, 0, 0);
+ if (dumpable == -1) {
+ SCLogInfo ("Can't get core dump configuration of process.");
+ }
+ else if (unlimited == 1 || max_dump > 0) {
+ /* try to enable core dump for this process */
+ if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ SCLogInfo ("Unable to make this process dumpable.");
+ return 0;
+ } else {
+ SCLogDebug ("Process is dumpable.");
+ }
+ }
+ /* don't clear dumpable flag since this will have other effects;
+ * just set dump size to 0 below */
+#endif /* Linux specific */
+
+ struct rlimit lim; /*existing limit*/
+ struct rlimit new_lim; /*desired limit*/
+
+ /* get the current core dump file configuration */
+ if (getrlimit (RLIMIT_CORE, &lim) == -1) {
+ SCLogInfo ("Can't read coredump limit for this process.");
+ return 0;
+ }
+
+ if (unlimited) {
+ /* we want no limit on coredump size */
+ if (lim.rlim_max == RLIM_INFINITY && lim.rlim_cur == RLIM_INFINITY) {
+ SCLogInfo ("Core dump size is unlimited.");
+ return 1;
+ }
+ else {
+ new_lim.rlim_max = RLIM_INFINITY;
+ new_lim.rlim_cur = RLIM_INFINITY;
+ if (setrlimit (RLIMIT_CORE, &new_lim) == 0) {
+ SCLogInfo ("Core dump size set to unlimited.");
+ return 1;
+ }
+ if (errno == EPERM) {
+ /* couldn't raise the hard limit to unlimited;
+ * try increasing the soft limit to the hard limit instead */
+ if (lim.rlim_cur < lim.rlim_max) {
+ new_lim.rlim_cur = lim.rlim_max;
+ if (setrlimit (RLIMIT_CORE, & new_lim) == 0) {
+ SCLogInfo ("Could not set core dump size to unlimited; core dump size set to the hard limit.");
+ return 0;
+ }
+ else {
+ SCLogInfo ("Failed to set core dump size to unlimited or to the hard limit.");
+ return 0;
+ }
+ }
+ SCLogInfo ("Could not set core dump size to unlimited; it's set to the hard limit.");
+ return 0;
+ }
+ }
+ }
+ else {
+ /* we want a non-infinite soft limit on coredump size */
+ new_lim.rlim_cur = max_dump;
+
+ /* check whether the hard limit needs to be adjusted */
+ if (lim.rlim_max == RLIM_INFINITY) {
+ /* keep the current value (unlimited) for the hard limit */
+ new_lim.rlim_max = lim.rlim_max;
+ }
+#ifdef RLIM_SAVED_MAX
+ else if (lim.rlim_max == RLIM_SAVED_MAX) {
+ /* keep the current value (unknown) for the hard limit */
+ new_lim.rlim_max = lim.rlim_max;
+ }
+#endif
+ else if (lim.rlim_max < max_dump) {
+ /* need to raise the hard coredump size limit */
+ new_lim.rlim_max = max_dump;
+ }
+ else {
+ /* hard limit is ample */
+ new_lim.rlim_max = lim.rlim_max;
+ }
+ if (setrlimit (RLIMIT_CORE, &new_lim) == 0) {
+ SCLogInfo ("Core dump setting attempted is %llu", (unsigned long long) new_lim.rlim_cur);
+ struct rlimit actual_lim;
+ if (getrlimit (RLIMIT_CORE, &actual_lim) == 0) {
+ if (actual_lim.rlim_cur == RLIM_INFINITY) {
+ SCLogInfo ("Core dump size set to unlimited.");
+ }
+#ifdef RLIM_SAVED_CUR
+ else if (actual_lim.rlim_cur == RLIM_SAVED_CUR) {
+ SCLogInfo ("Core dump size set to soft limit.");
+ }
+#endif
+ else {
+ SCLogInfo ("Core dump size set to %llu", (unsigned long long) actual_lim.rlim_cur);
+ }
+ }
+ return 1;
+ }
+
+ if (errno == EINVAL || errno == EPERM) {
+ /* could't increase the hard limit, or the soft limit exceeded the hard
+ * limit; try to raise the soft limit to the hard limit */
+ if ((lim.rlim_cur < max_dump && lim.rlim_cur < lim.rlim_max)
+#ifdef RLIM_SAVED_CUR
+ || (lim.rlim_cur == RLIM_SAVED_CUR)
+#endif
+ ){
+ new_lim.rlim_max = lim.rlim_max;
+ new_lim.rlim_cur = lim.rlim_max;
+ if (setrlimit (RLIMIT_CORE, &new_lim) == 0) {
+ SCLogInfo("Core dump size set to the hard limit.");
+ return 0;
+ }
+ }
+ }
+ }
+ /* failed to set the coredump limit */
+ SCLogInfo ("Could't set coredump size to %s.", dump_size_config);
+ return 0;
+}
diff --git a/framework/src/suricata/src/util-coredump-config.h b/framework/src/suricata/src/util-coredump-config.h
new file mode 100644
index 00000000..7994421b
--- /dev/null
+++ b/framework/src/suricata/src/util-coredump-config.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ */
+
+#ifndef __COREDUMP_CONFIG_H__
+#define __COREDUMP_CONFIG_H__
+
+#include "suricata-common.h"
+
+int32_t CoredumpLoadConfig (void);
+
+#endif /* __COREDUMP_CONFIG_H__ */
diff --git a/framework/src/suricata/src/util-cpu.c b/framework/src/suricata/src/util-cpu.c
new file mode 100644
index 00000000..f8f748bc
--- /dev/null
+++ b/framework/src/suricata/src/util-cpu.c
@@ -0,0 +1,231 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Retrieve CPU information (configured CPUs, online CPUs)
+ */
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "suricata-common.h"
+
+/**
+ * Ok, if they should use sysconf, check that they have the macro's
+ * (syscalls) defined;
+ *
+ * Note: For windows it's different; Check the following:
+ * SYSTEM_INFO info;
+ * GetSystemInfo(&info);
+ * -> info.dwNumberOfProcessors;
+ */
+#ifdef _SC_NPROCESSORS_CONF
+#define SYSCONF_NPROCESSORS_CONF_COMPAT
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+#define SYSCONF_NPROCESSORS_ONLN_COMPAT
+#endif
+
+/* This one is available on Solaris 10 */
+#ifdef _SC_NPROCESSORS_MAX
+#define SYSCONF_NPROCESSORS_MAX_COMPAT
+#endif
+
+/**
+ * \brief Get the number of cpus configured in the system
+ * \retval 0 if the syscall is not available or we have an error;
+ * otherwise it will return the number of cpus configured
+ */
+uint16_t UtilCpuGetNumProcessorsConfigured()
+{
+#ifdef SYSCONF_NPROCESSORS_CONF_COMPAT
+ long nprocs = -1;
+ nprocs = sysconf(_SC_NPROCESSORS_CONF);
+ if (nprocs < 1) {
+ SCLogError(SC_ERR_SYSCALL, "Couldn't retrieve the number of cpus "
+ "configured (%s)", strerror(errno));
+ return 0;
+ }
+
+ if (nprocs > UINT16_MAX) {
+ SCLogDebug("It seems that there are more than %"PRIu16" CPUs "
+ "configured on this system. You can modify util-cpu.{c,h} "
+ "to use uint32_t to support it", UINT16_MAX);
+ return UINT16_MAX;
+ }
+
+ return (uint16_t)nprocs;
+#elif OS_WIN32
+ long nprocs = -1;
+ const char* envvar = getenv("NUMBER_OF_PROCESSORS");
+ nprocs = (NULL != envvar) ? atoi(envvar) : 0;
+ if (nprocs < 1) {
+ SCLogError(SC_ERR_SYSCALL, "Couldn't retrieve the number of cpus "
+ "configured from the NUMBER_OF_PROCESSORS environment variable");
+ return 0;
+ }
+ return (uint16_t)nprocs;
+#else
+ SCLogError(SC_ERR_SYSCONF, "Couldn't retrieve the number of cpus "
+ "configured, sysconf macro unavailable");
+ return 0;
+#endif
+}
+
+/**
+ * \brief Get the number of cpus online in the system
+ * \retval 0 if the syscall is not available or we have an error;
+ * otherwise it will return the number of cpus online
+ */
+uint16_t UtilCpuGetNumProcessorsOnline()
+{
+#ifdef SYSCONF_NPROCESSORS_ONLN_COMPAT
+ long nprocs = -1;
+ nprocs = sysconf(_SC_NPROCESSORS_ONLN);
+ if (nprocs < 1) {
+ SCLogError(SC_ERR_SYSCALL, "Couldn't retrieve the number of cpus "
+ "online (%s)", strerror(errno));
+ return 0;
+ }
+
+ if (nprocs > UINT16_MAX) {
+ SCLogDebug("It seems that there are more than %"PRIu16" CPUs online. "
+ "You can modify util-cpu.{c,h} to use uint32_t to "
+ "support it", UINT16_MAX);
+ return UINT16_MAX;
+ }
+
+ return nprocs;
+#elif OS_WIN32
+ return UtilCpuGetNumProcessorsConfigured();
+#else
+ SCLogError(SC_ERR_SYSCONF, "Couldn't retrieve the number of cpus online, "
+ "synconf macro unavailable");
+ return 0;
+#endif
+}
+
+/**
+ * \brief Get the maximum number of cpus allowed in the system
+ * This syscall is present on Solaris, but it's not on linux
+ * or macosx. Maybe you should look at UtilCpuGetNumProcessorsConfigured()
+ * \retval 0 if the syscall is not available or we have an error;
+ * otherwise it will return the number of cpus allowed
+ */
+uint16_t UtilCpuGetNumProcessorsMax()
+{
+#ifdef SYSCONF_NPROCESSORS_MAX_COMPAT
+ long nprocs = -1;
+ nprocs = sysconf(_SC_NPROCESSORS_MAX);
+ if (nprocs < 1) {
+ SCLogError(SC_ERR_SYSCALL, "Couldn't retrieve the maximum number of cpus "
+ "allowed by the system (%s)", strerror(errno));
+ return 0;
+ }
+
+ if (nprocs > UINT16_MAX) {
+ SCLogDebug("It seems that the system support more that %"PRIu16" CPUs. You "
+ "can modify util-cpu.{c,h} to use uint32_t to support it", UINT16_MAX);
+ return UINT16_MAX;
+ }
+
+ return (uint16_t)nprocs;
+#else
+ SCLogError(SC_ERR_SYSCONF, "Couldn't retrieve the maximum number of cpus allowed by "
+ "the system, synconf macro unavailable");
+ return 0;
+#endif
+}
+
+/**
+ * \brief Print a summary of CPUs detected (configured and online)
+ */
+void UtilCpuPrintSummary()
+{
+ uint16_t cpus_conf = UtilCpuGetNumProcessorsConfigured();
+ uint16_t cpus_online = UtilCpuGetNumProcessorsOnline();
+
+ SCLogDebug("CPUs Summary: ");
+ if (cpus_conf > 0)
+ SCLogDebug("CPUs configured: %"PRIu16, cpus_conf);
+ if (cpus_online > 0)
+ SCLogInfo("CPUs/cores online: %"PRIu16, cpus_online);
+ if (cpus_online == 0 && cpus_conf == 0)
+ SCLogInfo("Couldn't retireve any information of CPU's, please, send your operating "
+ "system info and check util-cpu.{c,h}");
+}
+
+/**
+ * Get the current number of ticks from the CPU.
+ *
+ * \todo We'll have to deal with removig ticks from the extra cpuids inbetween
+ * 2 calls.
+ */
+#if defined(__tile__)
+#include <arch/cycle.h>
+uint64_t UtilCpuGetTicks(void)
+{
+ return get_cycle_count();
+}
+#else
+uint64_t UtilCpuGetTicks(void)
+{
+ uint64_t val;
+#if defined(__GNUC__) && (defined(__x86_64) || defined(_X86_64_) || defined(ia_64) || defined(__i386__))
+#if defined(__x86_64) || defined(_X86_64_) || defined(ia_64)
+ __asm__ __volatile__ (
+ "xorl %%eax,%%eax\n\t"
+ "cpuid\n\t"
+ ::: "%rax", "%rbx", "%rcx", "%rdx");
+#else
+ __asm__ __volatile__ (
+ "xorl %%eax,%%eax\n\t"
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ ::: "%eax", "%ecx", "%edx");
+#endif
+ uint32_t a, d;
+ __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
+ val = ((uint64_t)a) | (((uint64_t)d) << 32);
+#if defined(__x86_64) || defined(_X86_64_) || defined(ia_64)
+ __asm__ __volatile__ (
+ "xorl %%eax,%%eax\n\t"
+ "cpuid\n\t"
+ ::: "%rax", "%rbx", "%rcx", "%rdx");
+#else
+ __asm__ __volatile__ (
+ "xorl %%eax,%%eax\n\t"
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ ::: "%eax", "%ecx", "%edx");
+#endif
+
+#else /* #if defined(__GNU__) */
+#warning Using inferior version of UtilCpuGetTicks
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ val = (now.tv_sec * 1000000) + now.tv_usec;
+#endif
+ return val;
+}
+#endif /* __tile__ */
diff --git a/framework/src/suricata/src/util-cpu.h b/framework/src/suricata/src/util-cpu.h
new file mode 100644
index 00000000..c7af0359
--- /dev/null
+++ b/framework/src/suricata/src/util-cpu.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_CPU_H__
+#define __UTIL_CPU_H__
+
+/* Processors configured: */
+uint16_t UtilCpuGetNumProcessorsConfigured();
+/* Processors online: */
+uint16_t UtilCpuGetNumProcessorsOnline();
+
+/* Only on Solaris */
+uint16_t UtilCpuGetNumProcessorsMax();
+
+void UtilCpuPrintSummary();
+
+uint64_t UtilCpuGetTicks(void);
+
+#endif /* __UTIL_CPU_H__ */
diff --git a/framework/src/suricata/src/util-crypt.c b/framework/src/suricata/src/util-crypt.c
new file mode 100644
index 00000000..a9d82013
--- /dev/null
+++ b/framework/src/suricata/src/util-crypt.c
@@ -0,0 +1,306 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Roliers Jean-Paul <popof.fpn@gmail.co>
+ *
+ * Implements cryptographic functions.
+ * Based on the libtomcrypt library ( http://libtom.org/?page=features&newsitems=5&whatfile=crypt )
+ *
+ * Implementation of function using NSS is not linked with libtomcrypt.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-crypt.h"
+#ifdef HAVE_NSS
+#include <sechash.h>
+#endif
+
+#ifndef HAVE_NSS
+
+#define F0(x,y,z) (z ^ (x & (y ^ z)))
+#define F1(x,y,z) (x ^ y ^ z)
+#define F2(x,y,z) ((x & y) | (z & (x | y)))
+#define F3(x,y,z) (x ^ y ^ z)
+
+
+static int Sha1Compress(HashState *md, unsigned char *buf)
+{
+ uint32_t a,b,c,d,e,W[80],i;
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32H(W[i], buf + (4*i));
+ }
+
+ /* copy state */
+ a = md->sha1.state[0];
+ b = md->sha1.state[1];
+ c = md->sha1.state[2];
+ d = md->sha1.state[3];
+ e = md->sha1.state[4];
+
+ /* expand it */
+ for (i = 16; i < 80; i++) {
+ W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
+ }
+
+ /* compress */
+ /* round one */
+ #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
+ #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
+ #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
+ #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
+
+ for (i = 0; i < 20; ) {
+ FF0(a,b,c,d,e,i++);
+ FF0(e,a,b,c,d,i++);
+ FF0(d,e,a,b,c,i++);
+ FF0(c,d,e,a,b,i++);
+ FF0(b,c,d,e,a,i++);
+ }
+
+ /* round two */
+ for (; i < 40; ) {
+ FF1(a,b,c,d,e,i++);
+ FF1(e,a,b,c,d,i++);
+ FF1(d,e,a,b,c,i++);
+ FF1(c,d,e,a,b,i++);
+ FF1(b,c,d,e,a,i++);
+ }
+
+ /* round three */
+ for (; i < 60; ) {
+ FF2(a,b,c,d,e,i++);
+ FF2(e,a,b,c,d,i++);
+ FF2(d,e,a,b,c,i++);
+ FF2(c,d,e,a,b,i++);
+ FF2(b,c,d,e,a,i++);
+ }
+
+ /* round four */
+ for (; i < 80; ) {
+ FF3(a,b,c,d,e,i++);
+ FF3(e,a,b,c,d,i++);
+ FF3(d,e,a,b,c,i++);
+ FF3(c,d,e,a,b,i++);
+ FF3(b,c,d,e,a,i++);
+ }
+
+ #undef FF0
+ #undef FF1
+ #undef FF2
+ #undef FF3
+
+ /* store */
+ md->sha1.state[0] = md->sha1.state[0] + a;
+ md->sha1.state[1] = md->sha1.state[1] + b;
+ md->sha1.state[2] = md->sha1.state[2] + c;
+ md->sha1.state[3] = md->sha1.state[3] + d;
+ md->sha1.state[4] = md->sha1.state[4] + e;
+
+ return SC_SHA_1_OK;
+}
+
+static int Sha1Init(HashState * md)
+{
+ if(md == NULL)
+ {
+ return SC_SHA_1_NOK;
+ }
+ md->sha1.state[0] = 0x67452301UL;
+ md->sha1.state[1] = 0xefcdab89UL;
+ md->sha1.state[2] = 0x98badcfeUL;
+ md->sha1.state[3] = 0x10325476UL;
+ md->sha1.state[4] = 0xc3d2e1f0UL;
+ md->sha1.curlen = 0;
+ md->sha1.length = 0;
+ return SC_SHA_1_OK;
+}
+
+static int Sha1Process (HashState * md, const unsigned char *in, unsigned long inlen)
+{
+ if(md == NULL || in == NULL) {
+ return SC_SHA_1_INVALID_ARG;
+ }
+
+ unsigned long n;
+ int err;
+
+ if (md->sha1.curlen > sizeof(md->sha1.buf)) {
+ return SC_SHA_1_INVALID_ARG;
+ }
+ while (inlen > 0) {
+ if (md-> sha1.curlen == 0 && inlen >= 64) {
+ if ((err = Sha1Compress(md, (unsigned char *)in)) != SC_SHA_1_OK) {
+ return err;
+ }
+ md-> sha1 .length += 64 * 8;
+ in += 64;
+ inlen -= 64;
+ } else {
+ n = MIN(inlen, (64 - md-> sha1 .curlen));
+ memcpy(md-> sha1 .buf + md-> sha1.curlen, in, (size_t)n);
+ md-> sha1 .curlen += n;
+ in += n;
+ inlen -= n;
+ if (md-> sha1 .curlen == 64) {
+ if ((err = Sha1Compress(md, md-> sha1 .buf)) != SC_SHA_1_OK) {
+ return err;
+ }
+ md-> sha1 .length += 8*64;
+ md-> sha1 .curlen = 0;
+ }
+ }
+ }
+ return SC_SHA_1_OK;
+}
+
+
+
+static int Sha1Done(HashState * md, unsigned char *out)
+{
+ int i;
+
+ if (md == NULL || out == NULL)
+ {
+ return SC_SHA_1_NOK;
+ }
+
+ if (md->sha1.curlen >= sizeof(md->sha1.buf)) {
+ return SC_SHA_1_INVALID_ARG;
+ }
+
+ /* increase the length of the message */
+ md->sha1.length += md->sha1.curlen * 8;
+
+ /* append the '1' bit */
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->sha1.curlen > 56) {
+ while (md->sha1.curlen < 64) {
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
+ }
+ Sha1Compress(md, md->sha1.buf);
+ md->sha1.curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (md->sha1.curlen < 56) {
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha1.length, md->sha1.buf+56);
+ Sha1Compress(md, md->sha1.buf);
+
+ /* copy output */
+ for (i = 0; i < 5; i++) {
+ STORE32H(md->sha1.state[i], out+(4*i));
+ }
+
+ memset(md, 0, sizeof(HashState));
+
+ return SC_SHA_1_OK;
+}
+
+unsigned char* ComputeSHA1(unsigned char* buff, int bufflen)
+{
+ HashState md;
+ unsigned char* lResult = (unsigned char*) SCMalloc((sizeof(unsigned char) * 20));
+ if (lResult == NULL)
+ return NULL;
+ Sha1Init(&md);
+ Sha1Process(&md, buff, bufflen);
+ Sha1Done(&md, lResult);
+ return lResult;
+}
+
+#else /* HAVE_NSS */
+
+unsigned char* ComputeSHA1(unsigned char* buff, int bufflen)
+{
+ HASHContext *sha1_ctx = HASH_Create(HASH_AlgSHA1);
+ unsigned char* lResult = NULL;
+ unsigned int rlen;
+ if (sha1_ctx == NULL) {
+ return NULL;
+ }
+
+ lResult = (unsigned char*) SCMalloc((sizeof(unsigned char) * 20));
+ if (lResult == NULL) {
+ HASH_Destroy(sha1_ctx);
+ return NULL;
+ }
+ HASH_Begin(sha1_ctx);
+ HASH_Update(sha1_ctx, buff, bufflen);
+ HASH_End(sha1_ctx, lResult, &rlen, (sizeof(unsigned char) * 20));
+ HASH_Destroy(sha1_ctx);
+
+ return lResult;
+}
+
+#endif /* HAVE_NSS */
+
+static const char *b64codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int Base64Encode(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen)
+{
+ unsigned long i, len2, leven;
+ unsigned char *p;
+ if(in == NULL || out == NULL || outlen == NULL)
+ {
+ return SC_BASE64_INVALID_ARG;
+ }
+ /* valid output size ? */
+ len2 = 4 * ((inlen + 2) / 3);
+ if (*outlen < len2 + 1) {
+ *outlen = len2 + 1;
+ return SC_BASE64_OVERFLOW;
+ }
+ p = out;
+ leven = 3*(inlen / 3);
+ for (i = 0; i < leven; i += 3) {
+ *p++ = b64codes[(in[0] >> 2) & 0x3F];
+ *p++ = b64codes[(((in[0] & 3) << 4) + (in[1] >> 4)) & 0x3F];
+ *p++ = b64codes[(((in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F];
+ *p++ = b64codes[in[2] & 0x3F];
+ in += 3;
+ }
+ /* Pad it if necessary... */
+ if (i < inlen) {
+ unsigned a = in[0];
+ unsigned b = (i+1 < inlen) ? in[1] : 0;
+
+ *p++ = b64codes[(a >> 2) & 0x3F];
+ *p++ = b64codes[(((a & 3) << 4) + (b >> 4)) & 0x3F];
+ *p++ = (i+1 < inlen) ? b64codes[(((b & 0xf) << 2)) & 0x3F] : '=';
+ *p++ = '=';
+ }
+ /* append a NULL byte */
+ *p = '\0';
+ /* return ok */
+ *outlen = p - out;
+ return SC_BASE64_OK;
+}
diff --git a/framework/src/suricata/src/util-crypt.h b/framework/src/suricata/src/util-crypt.h
new file mode 100644
index 00000000..7a3540b2
--- /dev/null
+++ b/framework/src/suricata/src/util-crypt.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Roliers Jean-Paul <popof.fpn@gmail.co>
+ *
+ * Implements cryptographic functions.
+ * Based on the libtomcrypt library ( http://libtom.org/?page=features&newsitems=5&whatfile=crypt )
+ */
+
+#ifndef UTIL_CRYPT_H_
+#define UTIL_CRYPT_H_
+
+#include "suricata-common.h"
+
+typedef enum {
+ SC_SHA_1_OK,
+ SC_SHA_1_NOK,
+ SC_SHA_1_INVALID_ARG,
+
+ SC_BASE64_OK,
+ SC_BASE64_INVALID_ARG,
+ SC_BASE64_OVERFLOW,
+
+} CryptId;
+
+#ifndef HAVE_NSS
+
+#define LOAD32H(x, y) \
+ { x = ((unsigned long)((y)[0] & 255)<<24) | \
+ ((unsigned long)((y)[1] & 255)<<16) | \
+ ((unsigned long)((y)[2] & 255)<<8) | \
+ ((unsigned long)((y)[3] & 255)); }
+
+#define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
+ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
+ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
+ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+#define STORE32H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
+ (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
+
+#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#ifndef MIN
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#endif
+
+typedef struct Sha1State_ {
+ uint64_t length;
+ uint32_t state[5], curlen;
+ unsigned char buf[64];
+} Sha1State;
+
+typedef union HashState_ {
+ char dummy[1];
+ Sha1State sha1;
+ void *data;
+} HashState;
+
+#endif /* don't HAVE_NSS */
+
+unsigned char* ComputeSHA1(unsigned char* buff, int bufflen);
+int Base64Encode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen);
+
+#endif /* UTIL_CRYPT_H_ */
diff --git a/framework/src/suricata/src/util-cuda-buffer.c b/framework/src/suricata/src/util-cuda-buffer.c
new file mode 100644
index 00000000..efaef9ff
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-buffer.c
@@ -0,0 +1,1358 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * API has be introduced to allow buffering of data by multiple writers
+ * asynronously. The current version only allows sequential reads.
+ *
+ * The API works by first registering a couple of buffers, which would
+ * be sliced and allocated for use by the API to potential writers.
+ *
+ * The registration API requires 3 buffers to be registered. The data
+ * buffer(d_buffer), into which the API buffers data, the pointer buffer
+ * (p_buffer), which would hold the pointer var instance corresponding to
+ * its entry in the d_buffer, and the offset buffer(o_buffer), which
+ * holds an offset entry for the data corresponding to the pointer buffer
+ * entry.
+ *
+ * A writer wishing to write data would be required to obtain a slice
+ * using CudaBufferGetSlice. Once data has been written to the slice,
+ * it can report back saying the slice has been written to by setting
+ * a flag in the slice - SC_ATOMIC_SET(slice->done, 1).
+ *
+ * A reader wishing to retrieve the data written by writers, will do
+ * so using the API call - CudaBufferCullCompletedSlices(). Once data
+ * has been consumed, the reader would report back using
+ * CudaBufferReportCulledConsumption() so that resources can be freed
+ * to be reallocated to other writers.
+ */
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "util-atomic.h"
+#include "util-pool.h"
+#include "util-misc.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-cuda-buffer.h"
+
+/* rotation limit for the buffers. This basically decides at what position
+ * inside alloced buffer should the API rotate and start using the buffer
+ * from the start - The right value's from 0.1-1.0. Do note that the
+ * rotation decision is taken when the culling process takes place.
+ * Have a look at - CudaBufferCullCompletedSlices */
+#define CUDA_BUFFER_BUFFER_ROTATION_LIMIT 0.75
+
+/* The max buffer size that be registered to CudaBufferRegisterNew */
+#define CUDA_BUFFER_BUFFER_LIMIT (1 * 1024 * 1024 * 1024)
+
+/* 100,000 * 5 = 500,000 */
+#define CUDA_BUFFER_ITEM_LIMIT (100000 * 5)
+
+/* a million slices to be prealloced = 100,000 * 10 */
+#define CUDA_BUFFER_SLICE_POOL_PREALLOC (100000 * 10)
+
+/* we store all our slices here */
+static Pool *slice_pool = NULL;
+/* mutex for the above slice pool */
+static SCMutex slice_pool_mutex;
+
+/**
+ * \brief Used by a consumer to report back(and thus have it freed),
+ * once it has consumed data returned in the CudaBufferCulledInfo
+ * instance(obtained from the call to CudaBufferCullCompletedSlices).
+ */
+void CudaBufferReportCulledConsumption(CudaBufferData *cb_data,
+ CudaBufferCulledInfo *culled_info)
+{
+ SCMutexLock(&cb_data->m);
+
+ if (culled_info->d_buffer_reset) {
+ cb_data->d_buffer_read = 0;
+ } else {
+ if (culled_info->no_of_items != 0) {
+ cb_data->d_buffer_read = culled_info->d_buffer_start_offset +
+ culled_info->d_buffer_len;
+ }
+ }
+
+ if (culled_info->op_buffer_reset) {
+ cb_data->op_buffer_read = 0;
+ } else {
+ if (culled_info->no_of_items != 0) {
+ cb_data->op_buffer_read += culled_info->no_of_items;
+ }
+ }
+
+ SCMutexUnlock(&cb_data->m);
+}
+
+/**
+ * \brief Remove slices that are done. "Done" as in worker threads are done
+ * writing data to it.
+ *
+ * \param cb_data Pointer to the CudaBufferData instance.
+ */
+void CudaBufferCullCompletedSlices(CudaBufferData *cb_data,
+ CudaBufferCulledInfo *culled_info,
+ uint32_t size_limit)
+{
+ culled_info->no_of_items = 0;
+ culled_info->d_buffer_reset = 0;
+ culled_info->op_buffer_reset = 0;
+
+ SCMutexLock(&cb_data->m);
+
+ int buffer_reset = 0;
+ uint32_t d_buffer_write_temp = 0;
+ uint32_t op_buffer_write_temp = 0;
+
+ if ((cb_data->d_buffer_write >=
+ (cb_data->d_buffer_len * CUDA_BUFFER_BUFFER_ROTATION_LIMIT)) &&
+ (cb_data->d_buffer_read != 0))
+ {
+ SCLogDebug("d_buffer reset");
+ d_buffer_write_temp = cb_data->d_buffer_write;
+ cb_data->d_buffer_write = 0;
+ buffer_reset = 1;
+ culled_info->d_buffer_reset = 1;
+ }
+
+ /* reset op_buffer */
+ if ((cb_data->op_buffer_write >=
+ (cb_data->op_buffer_len * CUDA_BUFFER_BUFFER_ROTATION_LIMIT)) &&
+ (cb_data->op_buffer_read != 0))
+ {
+ SCLogDebug("op_buffer reset");
+ op_buffer_write_temp = cb_data->op_buffer_write;
+ cb_data->op_buffer_write = 0;
+ buffer_reset = 1;
+ culled_info->op_buffer_reset = 1;
+ }
+
+ CudaBufferSlice *slice_temp = cb_data->slice_head;
+ CudaBufferSlice *max_culled_slice = NULL;
+ uint32_t curr_size = 0;
+
+ while (slice_temp != NULL) {
+ if (!SC_ATOMIC_GET(slice_temp->done)) {
+ SCLogDebug("CudaBuffer waiting on an item to finish");
+ if (buffer_reset) {
+ while (!SC_ATOMIC_GET(slice_temp->done))
+ usleep(1);
+ } else {
+ break;
+ }
+ }
+
+ if (curr_size + (slice_temp->end_offset - slice_temp->start_offset + 1) > size_limit) {
+ if (buffer_reset) {
+ cb_data->op_buffer_write = op_buffer_write_temp;
+ cb_data->d_buffer_write = d_buffer_write_temp;
+ culled_info->d_buffer_reset = 0;
+ culled_info->op_buffer_reset = 0;
+ }
+ break;
+ }
+
+ max_culled_slice = slice_temp;
+ curr_size += (slice_temp->end_offset - slice_temp->start_offset + 1);
+
+ slice_temp = slice_temp->next;
+ }
+
+ CudaBufferSlice *slice_head = cb_data->slice_head;
+
+ if (max_culled_slice != NULL) {
+ cb_data->slice_head = max_culled_slice->next;
+ if (max_culled_slice->next == NULL) {
+ cb_data->slice_tail = NULL;
+ }
+ max_culled_slice->next = NULL;
+ } else {
+ SCMutexUnlock(&cb_data->m);
+ return;
+ }
+
+ culled_info->d_buffer_start_offset = slice_head->start_offset;
+ culled_info->d_buffer_len = (max_culled_slice->end_offset -
+ slice_head->start_offset + 1);
+ culled_info->op_buffer_start_offset = cb_data->op_buffer_read;
+ SCMutexUnlock(&cb_data->m);
+
+ /* push out the used slices to the the slice_pool */
+ SCMutexLock(&slice_pool_mutex);
+ slice_temp = slice_head;
+ while (slice_temp != max_culled_slice) {
+ CudaBufferSlice *tmp = slice_temp->next;
+
+ PoolReturn(slice_pool, slice_temp);
+ culled_info->no_of_items++;
+
+ slice_temp = tmp;
+ }
+ PoolReturn(slice_pool, slice_temp);
+ culled_info->no_of_items++;
+ SCMutexUnlock(&slice_pool_mutex);
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Adds a slice to the CudaBufferData slice list.
+ *
+ * We expect the CudaBufferData instance to be locked.
+ *
+ * \param cb_data Pointer to the CudaBufferdata instance.
+ * \param slice Pointer to the slice to be pushed.
+ */
+static inline void CudaBufferAppendSlice(CudaBufferData *cb_data, CudaBufferSlice *slice)
+{
+ slice->next = NULL;
+
+ if (cb_data->slice_head == NULL) {
+ cb_data->slice_head = slice;
+ cb_data->slice_tail = slice;
+ } else {
+ cb_data->slice_tail->next = slice;
+ cb_data->slice_tail = slice;
+ }
+
+ return;
+}
+
+/**
+ * \brief Gets a new buffer slice for a consumer to write to.
+ *
+ * All slices returned are aligned to the next 8 byte boundary.
+ *
+ * \param cb_data Pointer to the CudaBufferdata instance.
+ * \param len Length of the slice required.
+ * \param p Pointer to the var corresponding to the data to store.
+ *
+ * \retval slice Pointer to the slice if successful; NULL if unsuccessful.
+ */
+CudaBufferSlice *CudaBufferGetSlice(CudaBufferData *cb_data, uint32_t len, void *p)
+{
+#define ALIGN_UP(offset, alignment) (offset) = ((offset) + (alignment) - 1) & ~((alignment) - 1)
+
+ SCMutexLock(&slice_pool_mutex);
+ CudaBufferSlice *slice = PoolGet(slice_pool);
+ SCMutexUnlock(&slice_pool_mutex);
+ if (slice == NULL) {
+ return NULL;
+ }
+
+ SCMutexLock(&cb_data->m);
+
+ if (cb_data->d_buffer_write < cb_data->d_buffer_read) {
+ if (cb_data->d_buffer_write + len >= cb_data->d_buffer_read) {
+ SCLogDebug("d_buffer full");
+ SCMutexUnlock(&cb_data->m);
+
+ SCMutexLock(&slice_pool_mutex);
+ PoolReturn(slice_pool, slice);
+ SCMutexUnlock(&slice_pool_mutex);
+ return NULL;
+ }
+ } else {
+ if (cb_data->d_buffer_write + len > cb_data->d_buffer_len) {
+ SCLogDebug("d_buffer limit hit - buffer_len - %"PRIu32,
+ cb_data->d_buffer_len);
+ SCMutexUnlock(&cb_data->m);
+
+ SCMutexLock(&slice_pool_mutex);
+ PoolReturn(slice_pool, slice);
+ SCMutexUnlock(&slice_pool_mutex);
+ return NULL;
+ }
+ }
+
+ if (cb_data->op_buffer_write < cb_data->op_buffer_read) {
+ if (cb_data->op_buffer_write + 1 >= cb_data->op_buffer_read) {
+ SCLogDebug("op_buffer full");
+ SCMutexUnlock(&cb_data->m);
+
+ SCMutexLock(&slice_pool_mutex);
+ PoolReturn(slice_pool, slice);
+ SCMutexUnlock(&slice_pool_mutex);
+ return NULL;
+ }
+ } else {
+ if (cb_data->op_buffer_write + 1 > cb_data->op_buffer_len) {
+ SCLogDebug("op_buffer limit hit - buffer_len - %"PRIu32,
+ cb_data->op_buffer_len);
+ SCMutexUnlock(&cb_data->m);
+
+ SCMutexLock(&slice_pool_mutex);
+ PoolReturn(slice_pool, slice);
+ SCMutexUnlock(&slice_pool_mutex);
+ return NULL;
+ }
+ }
+
+ slice->start_offset = cb_data->d_buffer_write;
+ cb_data->d_buffer_write = slice->start_offset + len;
+ ALIGN_UP(cb_data->d_buffer_write, 8);
+ slice->end_offset = cb_data->d_buffer_write - 1;
+ slice->buffer = cb_data->d_buffer;
+ SC_ATOMIC_SET(slice->done, 0);
+
+ CudaBufferAppendSlice(cb_data, slice);
+ cb_data->no_of_items++;
+
+ cb_data->o_buffer[cb_data->op_buffer_write] = slice->start_offset;
+ cb_data->p_buffer[cb_data->op_buffer_write] = p;
+ cb_data->op_buffer_write++;
+
+ SCMutexUnlock(&cb_data->m);
+
+ return slice;
+}
+
+void CudaBufferDeRegister(CudaBufferData *cb_data)
+{
+ CudaBufferSlice *slice_temp = cb_data->slice_head;
+ SCMutexLock(&slice_pool_mutex);
+ while (slice_temp != NULL) {
+ CudaBufferSlice *slice_temp_next = slice_temp->next;
+ PoolReturn(slice_pool, slice_temp);
+ slice_temp = slice_temp_next;
+ }
+ SCMutexUnlock(&slice_pool_mutex);
+
+ SCMutexDestroy(&cb_data->m);
+ SCFree(cb_data);
+
+ return;
+}
+
+/**
+ * \brief Registers a new buffer to be handled by the CudaBuffer API.
+ *
+ * More on what this API does can be understood from the API
+ * docs at the start of this file.
+ *
+ * \param d_buffer The data buffer to work with.
+ * \param d_buffer_len Length of d_buffer.
+ * \param o_buffer The offset buffer.
+ * \param p_buffer The pointer buffer.
+ * \param op_buffer_no_of_items Length of o_buffer and p_buffer. Please
+ * note that both o_buffer and p_buffer
+ * should be of the same length.
+ * \param len Length of the buffer to be assigned.
+ */
+CudaBufferData *CudaBufferRegisterNew(uint8_t *d_buffer, uint32_t d_buffer_len,
+ uint32_t *o_buffer, void **p_buffer,
+ uint32_t op_buffer_no_of_items)
+{
+ if (d_buffer_len > CUDA_BUFFER_BUFFER_LIMIT) {
+ SCLogError(SC_ERR_CUDA_BUFFER_ERROR, "Buffer max limit exceeded. We "
+ "accept a max limit of %u bytes", CUDA_BUFFER_BUFFER_LIMIT);
+ return NULL;
+ }
+
+ if ((d_buffer_len % 8) != 0) {
+ SCLogError(SC_ERR_CUDA_BUFFER_ERROR, "Please specify a buffer length which "
+ "is a multiple of 8");
+ return NULL;
+ }
+
+ CudaBufferData *new = SCMalloc(sizeof(CudaBufferData));
+ if (unlikely(new == NULL)) {
+ return NULL;
+ }
+ memset(new, 0, sizeof(CudaBufferData));
+
+ /* payload/data buffer and set its size */
+ new->d_buffer = d_buffer;
+ new->d_buffer_len = d_buffer_len;
+
+ /* offset buffer and set its size */
+ new->o_buffer = o_buffer;
+ new->p_buffer = p_buffer;
+ /* common to the above 2 malloc'ed buffers */
+ new->op_buffer_len = op_buffer_no_of_items;
+
+ /* used to lock this new instance when it's used */
+ SCMutexInit(&new->m, NULL);
+
+ return new;
+}
+
+static void *CudaBufferSlicePoolAlloc(void *null)
+{
+ void *ptr = SCMalloc(sizeof(CudaBufferSlice));
+ if (unlikely(ptr == NULL))
+ return NULL;
+ memset(ptr, 0, sizeof(CudaBufferSlice));
+
+ SC_ATOMIC_INIT(((CudaBufferSlice *)ptr)->done);
+
+ return ptr;
+}
+
+static int CudaBufferSlicePoolInit(void *data, void *init_data)
+{
+ SC_ATOMIC_INIT(((CudaBufferSlice *)data)->done);
+
+ return 1;
+}
+
+/* disabled to reflect the changes made in PoolInit */
+#if 0
+static void CudaBufferSlicePoolFree(void *data)
+{
+ SC_ATOMIC_DESTROY(((CudaBufferSlice *)data)->done);
+ SCFree(data);
+
+ return;
+}
+#endif
+
+static void CudaBufferSlicePoolCleanup(void *data)
+{
+ SC_ATOMIC_DESTROY(((CudaBufferSlice *)data)->done);
+
+ return;
+}
+
+/**
+ * \brief Init the API. To be called only once at startup time.
+ */
+void CudaBufferInit(void)
+{
+ SCMutexInit(&slice_pool_mutex, NULL);
+
+ slice_pool = PoolInit(CUDA_BUFFER_SLICE_POOL_PREALLOC,
+ CUDA_BUFFER_SLICE_POOL_PREALLOC,
+ sizeof(CudaBufferSlice),
+ CudaBufferSlicePoolAlloc,
+ CudaBufferSlicePoolInit,
+ NULL,
+ CudaBufferSlicePoolCleanup,
+ NULL);
+ if (slice_pool == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "CudaBuffer slice_pool is not initialized");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+/****************************Unittests***************************/
+
+#ifdef UNITTESTS
+
+int CudaBufferTest01(void)
+{
+ CudaBufferSlice *slice1, *slice2, *slice3, *slice4, *slice_temp;
+ int result = 0;
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ /* new slice */
+ slice1 = CudaBufferGetSlice(data, 8, NULL);
+ if (slice1->start_offset != 0 || slice1->end_offset != 7 ||
+ SC_ATOMIC_GET(slice1->done) != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 8 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 1 || data->op_buffer_read != 0 ||
+ data->no_of_items != 1) {
+ printf("failure 2\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* new slice */
+ slice2 = CudaBufferGetSlice(data, 16, NULL);
+ if (slice2->start_offset != 8 || slice2->end_offset != 23 ||
+ SC_ATOMIC_GET(slice2->done) != 0) {
+ printf("failure 5\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 24 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 2 || data->op_buffer_read != 0 ||
+ data->no_of_items != 2) {
+ printf("failure 6\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 7\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 8 || slice_temp->end_offset != 23 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 8\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 9\n");
+ goto end;
+ }
+
+ /* new slice */
+ slice3 = CudaBufferGetSlice(data, 36, NULL);
+ if (slice3->start_offset != 24 || slice3->end_offset != 63 ||
+ SC_ATOMIC_GET(slice3->done) != 0) {
+ printf("failure 10\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 64 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 0 ||
+ data->no_of_items != 3) {
+ printf("failure 11\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 12\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 8 || slice_temp->end_offset != 23 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 13\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 24 || slice_temp->end_offset != 63 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 14\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+ slice4 = CudaBufferGetSlice(data, 10, NULL);
+ if (slice4 != NULL) {
+ printf("failure 16\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 17\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+int CudaBufferTest02(void)
+{
+ CudaBufferSlice *slice1, *slice2, *slice3, *slice_temp;
+ int result = 0;
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ slice1 = CudaBufferGetSlice(data, 8, NULL);
+ slice2 = CudaBufferGetSlice(data, 16, NULL);
+ if (data->d_buffer_write != 24 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 2 || data->op_buffer_read != 0 ||
+ data->no_of_items != 2) {
+ printf("failure 1\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 8 || slice_temp->end_offset != 23 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* culling */
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 0) {
+ printf("failure 5\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 6\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 8 || slice_temp->end_offset != 23 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 7\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ SC_ATOMIC_SET(slice2->done, 1);
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 0) {
+ printf("failure 9\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 7 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 10\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 8 || slice_temp->end_offset != 23 ||
+ SC_ATOMIC_GET(slice_temp->done) != 1) {
+ printf("failure 11\n");
+ goto end;
+ }
+ if (slice_temp->next != NULL) {
+ printf("failure 12\n");
+ goto end;
+ }
+
+ SC_ATOMIC_SET(slice1->done, 1);
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 2) {
+ printf("failure 13\n");
+ goto end;
+ }
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 14\n");
+ goto end;
+ }
+ if (culled_info.d_buffer_start_offset != 0 ||
+ culled_info.d_buffer_len != 24 ||
+ culled_info.op_buffer_start_offset != 0 ||
+ culled_info.d_buffer_reset != 0 || culled_info.op_buffer_reset != 0) {
+ printf("failure 15\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 24 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 2 || data->op_buffer_read != 0 ||
+ data->no_of_items != 2) {
+ printf("failure 16\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ if (data->d_buffer_write != 24 || data->d_buffer_read != 24 ||
+ data->op_buffer_write != 2 || data->op_buffer_read != 2 ||
+ data->no_of_items != 2) {
+ printf("failure 17\n");
+ goto end;
+ }
+
+ /* new slice */
+ slice3 = CudaBufferGetSlice(data, 8, NULL);
+ if (slice3->start_offset != 24 || slice3->end_offset != 31 ||
+ SC_ATOMIC_GET(slice3->done) != 0) {
+ printf("failure 18\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 32 || data->d_buffer_read != 24 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 2 ||
+ data->no_of_items != 3) {
+ printf("failure 19\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 24 || slice_temp->end_offset != 31 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 20\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp != NULL) {
+ printf("failure 21\n");
+ goto end;
+ }
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 0) {
+ printf("failure 22\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 32 || data->d_buffer_read != 24 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 2 ||
+ data->no_of_items != 3) {
+ printf("failure 23\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 24 || slice_temp->end_offset != 31 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 24\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp != NULL) {
+ printf("failure 25\n");
+ goto end;
+ }
+
+ /* set done flag */
+ SC_ATOMIC_SET(slice3->done, 1);
+ if (slice3->start_offset != 24 || slice3->end_offset != 31 ||
+ SC_ATOMIC_GET(slice3->done) != 1) {
+ printf("failure 26\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 32 || data->d_buffer_read != 24 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 2 ||
+ data->no_of_items != 3) {
+ printf("failure 27\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 24 || slice_temp->end_offset != 31 ||
+ SC_ATOMIC_GET(slice_temp->done) != 1) {
+ printf("failure 28\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp != NULL) {
+ printf("failure 29\n");
+ goto end;
+ }
+
+ /* culling */
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 1) {
+ printf("failure 30\n");
+ goto end;
+ }
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 31\n");
+ goto end;
+ }
+ if (culled_info.d_buffer_start_offset != 24 ||
+ culled_info.d_buffer_len != 8 ||
+ culled_info.op_buffer_start_offset != 2 ||
+ culled_info.d_buffer_reset != 0 || culled_info.op_buffer_reset != 0) {
+ printf("failure 32\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 32 || data->d_buffer_read != 24 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 2 ||
+ data->no_of_items != 3) {
+ printf("failure 33\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ if (data->d_buffer_write != 32 || data->d_buffer_read != 32 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 3 ||
+ data->no_of_items != 3) {
+ printf("failure 34\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 35\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+int CudaBufferTest03(void)
+{
+ CudaBufferSlice *slice, *slice_temp;
+ int result = 0;
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ slice = CudaBufferGetSlice(data, 16, NULL);
+ BUG_ON(slice == NULL);
+ slice = CudaBufferGetSlice(data, 16, NULL);
+ BUG_ON(slice == NULL);
+ slice = CudaBufferGetSlice(data, 24, NULL);
+ BUG_ON(slice == NULL);
+
+ /* culling */
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 0 ||
+ data->no_of_items != 3) {
+ printf("failure 2\n");
+ goto end;
+ }
+ slice_temp = data->slice_head;
+ if (slice_temp->start_offset != 0 || slice_temp->end_offset != 15 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 16 || slice_temp->end_offset != 31 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 4\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp->start_offset != 32 || slice_temp->end_offset != 55 ||
+ SC_ATOMIC_GET(slice_temp->done) != 0) {
+ printf("failure 5\n");
+ goto end;
+ }
+ slice_temp = slice_temp->next;
+ if (slice_temp != NULL) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 7\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+int CudaBufferTest04(void)
+{
+ CudaBufferSlice *slice1, *slice2, *slice3, *slice_temp;
+ int result = 0;
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ slice1 = CudaBufferGetSlice(data, 16, NULL);
+ slice2 = CudaBufferGetSlice(data, 16, NULL);
+ slice3 = CudaBufferGetSlice(data, 24, NULL);
+
+ SC_ATOMIC_SET(slice1->done, 1);
+
+ /* culling */
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 0 ||
+ data->no_of_items != 3) {
+ printf("failure 1\n");
+ goto end;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 1) {
+ printf("failure 2\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 0 ||
+ data->no_of_items != 3) {
+ printf("failure 3\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 16 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 1 ||
+ data->no_of_items != 3) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ SC_ATOMIC_SET(slice2->done, 1);
+ SC_ATOMIC_SET(slice3->done, 1);
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (culled_info.no_of_items != 2) {
+ printf("failure 5\n");
+ goto end;
+ }
+ if (data->d_buffer_write != 0 || data->d_buffer_read != 16 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 1 ||
+ data->no_of_items != 3) {
+ printf("failure 6\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ if (data->d_buffer_write != 0 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 3 || data->op_buffer_read != 3 ||
+ data->no_of_items != 3) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 9\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+int CudaBufferTest05(void)
+{
+ CudaBufferSlice *slice1, *slice2, *slice3, *slice_temp;
+ int result = 0;
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ slice1 = CudaBufferGetSlice(data, 16, NULL);
+ slice2 = CudaBufferGetSlice(data, 16, NULL);
+ slice3 = CudaBufferGetSlice(data, 24, NULL);
+
+ SC_ATOMIC_SET(slice1->done, 1);
+
+ /* culling */
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ CudaBufferReportCulledConsumption(data, &culled_info);
+
+ SC_ATOMIC_SET(slice2->done, 1);
+ SC_ATOMIC_SET(slice3->done, 1);
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ slice1 = CudaBufferGetSlice(data, 16, NULL);
+ if (slice1 == NULL) {
+ printf("failure 1\n");
+ goto end;
+ }
+ slice2 = CudaBufferGetSlice(data, 16, NULL);
+ if (slice2 == NULL) {
+ printf("failure 2\n");
+ goto end;
+ }
+ slice3 = CudaBufferGetSlice(data, 24, NULL);
+ if (slice2 == NULL) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 4\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+int CudaBufferTest06(void)
+{
+ CudaBufferSlice *slice, *slice_temp;
+ int result = 0;
+ CudaBufferCulledInfo culled_info;
+ memset(&culled_info, 0, sizeof(CudaBufferCulledInfo));
+
+ uint8_t *d_buffer = SCMalloc(sizeof(uint8_t) * 64);
+ uint32_t *o_buffer = SCMalloc(sizeof(uint32_t) * 64);
+ void **p_buffer = SCMalloc(sizeof(void *) * 64);
+ if (d_buffer == NULL || o_buffer == NULL || p_buffer == NULL) {
+ printf("failure 0\n");
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+ return 0;
+ }
+
+ CudaBufferData *data = CudaBufferRegisterNew(d_buffer, 64,
+ o_buffer, p_buffer, 64);
+ if (data == NULL) {
+ goto end;
+ }
+
+ slice = CudaBufferGetSlice(data, 3, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "one", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 3, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "two", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ if (data->d_buffer_write != 16 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 2 || data->op_buffer_read != 0 ||
+ data->no_of_items != 2) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ slice = CudaBufferGetSlice(data, 5, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "three", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 4, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "four", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 4, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "five", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ if (data->d_buffer_write != 40 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 5 || data->op_buffer_read != 0 ||
+ data->no_of_items != 5) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ slice = CudaBufferGetSlice(data, 3, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "six", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 5, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "seven", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ if (memcmp(data->d_buffer, "one", 3) != 0 ||
+ memcmp(data->d_buffer + 8, "two", 3) != 0 ||
+ memcmp(data->d_buffer + 16, "three", 5) != 0 ||
+ memcmp(data->d_buffer + 24, "four", 4) != 0 ||
+ memcmp(data->d_buffer + 32, "five", 4) != 0 ||
+ memcmp(data->d_buffer + 40, "six", 3) != 0 ||
+ memcmp(data->d_buffer + 48, "seven", 5) != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 7 || data->op_buffer_read != 0 ||
+ data->no_of_items != 7) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+ /* culling */
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 7 || data->op_buffer_read != 0 ||
+ data->no_of_items != 7) {
+ printf("failure 5\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+ if (data->d_buffer_write != 56 || data->d_buffer_read != 56 ||
+ data->op_buffer_write != 7 || data->op_buffer_read != 7 ||
+ data->no_of_items != 7) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->d_buffer_write != 0 || data->d_buffer_read != 56 ||
+ data->op_buffer_write != 7 || data->op_buffer_read != 7 ||
+ data->no_of_items != 7) {
+ printf("failure 7\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+
+ if (data->d_buffer_write != 0 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 7 || data->op_buffer_read != 7 ||
+ data->no_of_items != 7) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ slice = CudaBufferGetSlice(data, 5, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "eight", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 4, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "nine", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 3, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "ten", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 6, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "eleven", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ slice = CudaBufferGetSlice(data, 6, NULL);
+ memcpy(slice->buffer + slice->start_offset,
+ "twelve", slice->end_offset - slice->start_offset + 1);
+ SC_ATOMIC_SET(slice->done, 1);
+
+ if (data->d_buffer_write != 40 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 12 || data->op_buffer_read != 7 ||
+ data->no_of_items != 12) {
+ printf("failure 9\n");
+ goto end;
+ }
+
+ if (memcmp(data->d_buffer, "eight", 5) != 0 ||
+ memcmp(data->d_buffer + 8, "nine", 4) != 0 ||
+ memcmp(data->d_buffer + 16, "ten", 3) != 0 ||
+ memcmp(data->d_buffer + 24, "eleven", 6) != 0 ||
+ memcmp(data->d_buffer + 32, "twelve", 6) != 0) {
+ printf("failure 10\n");
+ goto end;
+ }
+
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->d_buffer_write != 40 || data->d_buffer_read != 0 ||
+ data->op_buffer_write != 12 || data->op_buffer_read != 7 ||
+ data->no_of_items != 12) {
+ printf("failure 11\n");
+ goto end;
+ }
+ CudaBufferReportCulledConsumption(data, &culled_info);
+
+ if (data->d_buffer_write != 40 || data->d_buffer_read != 40 ||
+ data->op_buffer_write != 12 || data->op_buffer_read != 12 ||
+ data->no_of_items != 12) {
+ printf("failure 12\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ slice_temp = data->slice_head;
+ while (slice_temp != NULL) {
+ SC_ATOMIC_SET(slice_temp->done, 1);
+ slice_temp = slice_temp->next;
+ }
+ CudaBufferCullCompletedSlices(data, &culled_info, UTIL_MPM_CUDA_GPU_TRANSFER_SIZE);
+ if (data->slice_head != NULL || data->slice_tail != NULL) {
+ printf("failure 13\n");
+ result = 0;
+ }
+
+ CudaBufferDeRegister(data);
+ SCFree(d_buffer);
+ SCFree(o_buffer);
+ SCFree(p_buffer);
+
+ return result;
+}
+
+#endif /* #ifdef UNITTESTS */
+
+void CudaBufferRegisterUnittests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("CudaBufferTest01", CudaBufferTest01, 1);
+ UtRegisterTest("CudaBufferTest02", CudaBufferTest02, 1);
+ UtRegisterTest("CudaBufferTest03", CudaBufferTest03, 1);
+ UtRegisterTest("CudaBufferTest04", CudaBufferTest04, 1);
+ UtRegisterTest("CudaBufferTest05", CudaBufferTest05, 1);
+ UtRegisterTest("CudaBufferTest06", CudaBufferTest06, 1);
+#endif
+
+ return;
+}
+
+#endif /* __SC_CUDA_SUPPORT__ */
diff --git a/framework/src/suricata/src/util-cuda-buffer.h b/framework/src/suricata/src/util-cuda-buffer.h
new file mode 100644
index 00000000..ab494e67
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-buffer.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file API to allow buffering of data.
+ *
+ * Introduced with cuda as the primary objective. Allows multiple
+ * threads to simultaneously access a single buffer and write to it.
+ *
+ * Current version allows only serial reads from the buffer.
+ * When the need arises, the API will be updated to allow multiple
+ * non-sequential reads.
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#ifndef __UTIL_CUDA_BUFFER_H__
+#define __UTIL_CUDA_BUFFER_H__
+
+#include "util-atomic.h"
+
+/**
+ * \brief Used by consumers to retrieve the data buffered.
+ */
+typedef struct CudaBufferCulledInfo_ {
+ uint32_t no_of_items;
+
+ uint32_t d_buffer_start_offset;
+ uint32_t d_buffer_len;
+
+ /* we use no_of_items to determine the no of items here */
+ uint32_t op_buffer_start_offset;
+
+ uint8_t d_buffer_reset;
+ uint8_t op_buffer_reset;
+} CudaBufferCulledInfo;
+
+/**
+ * /brief A slice which contains details on where to buffer data by a
+ * writer.
+ */
+typedef struct CudaBufferSlice_ {
+ uint32_t start_offset;
+ uint32_t end_offset;
+ uint8_t *buffer;
+ SC_ATOMIC_DECLARE(uint8_t, done);
+
+ struct CudaBufferSlice_ *next;
+} CudaBufferSlice;
+
+typedef struct CudaBufferData_ {
+ /* the data buffer */
+ uint8_t *d_buffer;
+ uint32_t d_buffer_len;
+ uint32_t d_buffer_write;
+ uint32_t d_buffer_read;
+
+ /* debug only. Can be removed */
+ uint32_t no_of_items;
+
+ /* these 2 buffers below - o_buffer and p_buffer should be
+ * used/updated in tandem
+ * p_buffer is the ptr buffer that points to a data instance that
+ * represents it's corresponding data stored in d_buffer.
+ * o_buffer is the corresponding entry to the one in p_buffer, which
+ * holds the offset to the corresponding entry in d_buffer. */
+ uint32_t *o_buffer;
+ void **p_buffer;
+ uint32_t op_buffer_len;
+ uint32_t op_buffer_write;
+ uint32_t op_buffer_read;
+
+ /* slice lists used by writers */
+ CudaBufferSlice *slice_head;
+ CudaBufferSlice *slice_tail;
+
+ /* mutex used by the entire struct */
+ SCMutex m;
+} CudaBufferData;
+
+void CudaBufferReportCulledConsumption(CudaBufferData *cb_data,
+ CudaBufferCulledInfo *culled_info);
+void CudaBufferCullCompletedSlices(CudaBufferData *cb_data,
+ CudaBufferCulledInfo *culled_info, uint32_t size_limit);
+CudaBufferSlice *CudaBufferGetSlice(CudaBufferData *data, uint32_t len, void *p);
+void CudaBufferDeRegister(CudaBufferData *cb_data);
+CudaBufferData *CudaBufferRegisterNew(uint8_t *d_buffer, uint32_t d_buffer_len,
+ uint32_t *o_buffer, void **p_buffer,
+ uint32_t op_buffer_no_of_items);
+void CudaBufferInit(void);
+void CudaBufferRegisterUnittests(void);
+
+#endif /* __UTIL_CUDA_BUFFER_H__ */
+
+#endif /* __SC_CUDA_SUPPORT__ */
diff --git a/framework/src/suricata/src/util-cuda-handlers.c b/framework/src/suricata/src/util-cuda-handlers.c
new file mode 100644
index 00000000..a6cbeec9
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-handlers.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+/* compile in, only if we have a CUDA enabled device on the machine, with the
+ * toolkit and the driver installed */
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "suricata-common.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "conf.h"
+#include "util-cuda.h"
+#include "util-cuda-handlers.h"
+
+/* file only exists if cuda is enabled */
+#include "cuda-ptxdump.h"
+
+/************************conf file profile section**********************/
+
+typedef struct CudaHandlerConfProfile_ {
+ char *name;
+ void *ctx;
+ void (*Free)(void *);
+
+ struct CudaHandlerConfProfile_ *next;
+} CudaHandlerConfProfile;
+
+static CudaHandlerConfProfile *conf_profiles = NULL;
+/* protects above var */
+static SCMutex mutex = SCMUTEX_INITIALIZER;
+
+void CudaHandlerAddCudaProfileFromConf(const char *name,
+ void *(*Callback)(ConfNode *node),
+ void (*Free)(void *))
+{
+ /* we don't do data validation */
+ SCMutexLock(&mutex);
+
+ CudaHandlerConfProfile *tmp_cp = conf_profiles;
+ while (tmp_cp != NULL && strcasecmp(name, tmp_cp->name) != 0)
+ tmp_cp = tmp_cp->next;
+
+ if (tmp_cp != NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "We already have a cuda conf "
+ "profile by the name \"%s\" registered.", name);
+ exit(EXIT_FAILURE);
+ }
+
+ char tmp[200];
+ int r = snprintf(tmp, sizeof(tmp), "%s%s", "cuda.", name);
+ if (r < 0) {
+ SCLogError(SC_ERR_FATAL, "snprintf failure.");
+ exit(EXIT_FAILURE);
+ } else if (r > (int)sizeof(tmp)) {
+ SCLogError(SC_ERR_FATAL, "buffer not big enough to write param.");
+ exit(EXIT_FAILURE);
+ }
+ void *ctx = Callback(ConfGetNode(tmp));
+ if (ctx == NULL) {
+ SCMutexUnlock(&mutex);
+ return;
+ }
+
+ CudaHandlerConfProfile *new_cp = SCMalloc(sizeof(CudaHandlerConfProfile));
+ if (unlikely(new_cp == NULL))
+ exit(EXIT_FAILURE);
+ memset(new_cp, 0, sizeof(CudaHandlerConfProfile));
+ new_cp->name = SCStrdup(name);
+ if (new_cp->name == NULL)
+ exit(EXIT_FAILURE);
+ new_cp->ctx = ctx;
+ new_cp->Free = Free;
+
+ if (conf_profiles == NULL) {
+ conf_profiles = new_cp;
+ } else {
+ new_cp->next = conf_profiles;
+ conf_profiles = new_cp;
+ }
+
+ SCMutexUnlock(&mutex);
+ return;
+}
+
+void *CudaHandlerGetCudaProfile(const char *name)
+{
+ SCMutexLock(&mutex);
+
+ CudaHandlerConfProfile *tmp_cp = conf_profiles;
+ while (tmp_cp != NULL && strcasecmp(name, tmp_cp->name) != 0)
+ tmp_cp = tmp_cp->next;
+
+ if (tmp_cp == NULL) {
+ SCMutexUnlock(&mutex);
+ return NULL;
+ }
+
+ SCMutexUnlock(&mutex);
+ return tmp_cp->ctx;
+}
+
+void CudaHandlerFreeProfiles(void)
+{
+ SCMutexLock(&mutex);
+
+ CudaHandlerConfProfile *tmp = conf_profiles;
+ while (tmp != NULL) {
+ CudaHandlerConfProfile *curr = tmp;
+ tmp = tmp->next;
+ SCFree(curr->name);
+ if (curr->Free != NULL)
+ curr->Free(curr->ctx);
+ SCFree(curr);
+ }
+
+ SCMutexUnlock(&mutex);
+ return;
+}
+
+/*******************cuda context related data section*******************/
+
+/* we use a concept where every device on the gpu has only 1 context. If
+ * a section in the engine wants to use a device and tries to open a context
+ * on it, we first check if a context is already created for the device and if
+ * so we return it. If not we create a new one and update with the entry */
+
+static CUcontext *cuda_contexts = NULL;
+static int no_of_cuda_contexts = 0;
+
+typedef struct CudaHandlerModuleData_ {
+ char *name;
+ void *data;
+
+ struct CudaHandlerModuleData_ *next;
+} CudaHandlerModuleData;
+
+typedef struct CudaHandlerModule_ {
+ char *name;
+
+ /* the context used by this module */
+ CUcontext context;
+ /* the device on which the above context was created */
+ int device_id;
+ CudaHandlerModuleData *module_data;
+
+ struct CudaHandlerModule_ *next;
+} CudaHandlerModule;
+
+static CudaHandlerModule *cudahl_modules = NULL;
+
+CUcontext CudaHandlerModuleGetContext(const char *name, int device_id)
+{
+ void *ptmp;
+ SCMutexLock(&mutex);
+
+ CudaHandlerModule *module = cudahl_modules;
+ while (module != NULL && strcasecmp(module->name, name) != 0)
+ module = module->next;
+ if (module != NULL) {
+ if (module->device_id != device_id) {
+ SCLogError(SC_ERR_CUDA_HANDLER_ERROR, "Module already "
+ "registered, but the new device_id is different "
+ "from the already registered device_id.");
+ exit(EXIT_FAILURE);
+ }
+ SCMutexUnlock(&mutex);
+ return module->context;
+ }
+
+ CudaHandlerModule *new_module = SCMalloc(sizeof(CudaHandlerModule));
+ if (unlikely(new_module == NULL))
+ exit(EXIT_FAILURE);
+ memset(new_module, 0, sizeof(CudaHandlerModule));
+ new_module->device_id = device_id;
+ new_module->name = SCStrdup(name);
+ if (new_module->name == NULL)
+ exit(EXIT_FAILURE);
+ if (cudahl_modules == NULL) {
+ cudahl_modules = new_module;
+ } else {
+ new_module->next = cudahl_modules;
+ cudahl_modules = new_module;
+ }
+
+ if (no_of_cuda_contexts <= device_id) {
+ ptmp = SCRealloc(cuda_contexts, sizeof(CUcontext) * (device_id + 1));
+ if (unlikely(ptmp == NULL)) {
+ SCFree(cuda_contexts);
+ cuda_contexts = NULL;
+ exit(EXIT_FAILURE);
+ }
+ cuda_contexts = ptmp;
+
+ memset(cuda_contexts + no_of_cuda_contexts, 0,
+ sizeof(CUcontext) * ((device_id + 1) - no_of_cuda_contexts));
+ no_of_cuda_contexts = device_id + 1;
+ }
+
+ if (cuda_contexts[device_id] == 0) {
+ SCCudaDevices *devices = SCCudaGetDeviceList();
+ if (SCCudaCtxCreate(&cuda_contexts[device_id], CU_CTX_SCHED_BLOCKING_SYNC,
+ devices->devices[device_id]->device) == -1) {
+ SCLogDebug("ctxcreate failure.");
+ exit(EXIT_FAILURE);
+ }
+ }
+ new_module->context = cuda_contexts[device_id];
+
+ SCMutexUnlock(&mutex);
+ return cuda_contexts[device_id];
+}
+
+void CudaHandlerModuleStoreData(const char *module_name,
+ const char *data_name, void *data_ptr)
+{
+ SCMutexLock(&mutex);
+
+ CudaHandlerModule *module = cudahl_modules;
+ while (module != NULL && strcasecmp(module->name, module_name) != 0)
+ module = module->next;
+ if (module == NULL) {
+ SCLogError(SC_ERR_CUDA_HANDLER_ERROR, "Trying to retrieve data "
+ "\"%s\" from module \"%s\" that hasn't been registered "
+ "yet.", module_name, data_name);
+ exit(EXIT_FAILURE);
+ }
+
+ CudaHandlerModuleData *data = module->module_data;
+ while (data != NULL && (strcasecmp(data_name, data->name) != 0)) {
+ data = data->next;
+ }
+ if (data != NULL) {
+ SCLogWarning(SC_ERR_CUDA_HANDLER_ERROR, "Data \"%s\" already "
+ "registered for this module \"%s\".", data_name,
+ module_name);
+ SCMutexUnlock(&mutex);
+ goto end;
+ }
+
+ CudaHandlerModuleData *new_data = SCMalloc(sizeof(CudaHandlerModuleData));
+ if (unlikely(new_data == NULL))
+ exit(EXIT_FAILURE);
+ memset(new_data, 0, sizeof(CudaHandlerModuleData));
+ new_data->name = SCStrdup(data_name);
+ if (new_data->name == NULL)
+ exit(EXIT_FAILURE);
+ new_data->data = data_ptr;
+
+ if (module->module_data == NULL) {
+ module->module_data = new_data;
+ } else {
+ new_data->next = module->module_data;
+ module->module_data = new_data;
+ }
+
+ SCMutexUnlock(&mutex);
+
+ end:
+ return;
+}
+
+void *CudaHandlerModuleGetData(const char *module_name, const char *data_name)
+{
+ SCMutexLock(&mutex);
+
+ CudaHandlerModule *module = cudahl_modules;
+ while (module != NULL && strcasecmp(module->name, module_name) != 0)
+ module = module->next;
+ if (module == NULL) {
+ SCLogError(SC_ERR_CUDA_HANDLER_ERROR, "Trying to retrieve data "
+ "\"%s\" from module \"%s\" that hasn't been registered "
+ "yet.", module_name, data_name);
+ SCMutexUnlock(&mutex);
+ return NULL;
+ }
+
+ CudaHandlerModuleData *data = module->module_data;
+ while (data != NULL && (strcasecmp(data_name, data->name) != 0)) {
+ data = data->next;
+ }
+ if (data == NULL) {
+ SCLogInfo("Data \"%s\" already registered for this module \"%s\". "
+ "Returning it.", data_name, module_name);
+ SCMutexUnlock(&mutex);
+ return NULL;
+ }
+
+ SCMutexUnlock(&mutex);
+ return data->data;
+}
+
+int CudaHandlerGetCudaModule(CUmodule *p_module, const char *ptx_image)
+{
+#define CUDA_HANDLER_GET_CUDA_MODULE_BUFFER_EXTRA_SPACE 15
+
+ int i = 0;
+
+ /* select the ptx image based on the compute capability supported by all
+ * devices (i.e. the lowest) */
+ char *image = SCMalloc(strlen(ptx_image) + CUDA_HANDLER_GET_CUDA_MODULE_BUFFER_EXTRA_SPACE);
+ if (unlikely(image == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(image, 0x00, strlen(ptx_image) + CUDA_HANDLER_GET_CUDA_MODULE_BUFFER_EXTRA_SPACE);
+
+ int major = INT_MAX;
+ int minor = INT_MAX;
+ SCCudaDevices *devices = SCCudaGetDeviceList();
+ for (i = 0; i < devices->count; i++){
+ if (devices->devices[i]->major_rev < major){
+ major = devices->devices[i]->major_rev;
+ minor = devices->devices[i]->minor_rev;
+ }
+ if (devices->devices[i]->major_rev == major &&
+ devices->devices[i]->minor_rev < minor){
+ minor = devices->devices[i]->minor_rev;
+ }
+ }
+ snprintf(image,
+ strlen(ptx_image) + CUDA_HANDLER_GET_CUDA_MODULE_BUFFER_EXTRA_SPACE,
+ "%s_sm_%u%u",
+ ptx_image, major, minor);
+
+ /* we don't have a cuda module associated with this module. Create a
+ * cuda module, update the module with this cuda module reference and
+ * then return the module refernce back to the calling function using
+ * the argument */
+ SCLogDebug("Loading kernel module: %s\n",image);
+ if (SCCudaModuleLoadData(p_module, (void *)SCCudaPtxDumpGetModule(image)) == -1)
+ goto error;
+ SCFree(image);
+
+ return 0;
+ error:
+ SCFree(image);
+ return -1;
+
+#undef CUDA_HANDLER_GET_CUDA_MODULE_BUFFER_EXTRA_SPACE
+}
+
+
+#endif /* __SC_CUDA_SUPPORT__ */
diff --git a/framework/src/suricata/src/util-cuda-handlers.h b/framework/src/suricata/src/util-cuda-handlers.h
new file mode 100644
index 00000000..eee227df
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-handlers.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_CUDA_HANDLERS__H__
+#define __UTIL_CUDA_HANDLERS__H__
+
+#include "conf.h"
+#include "util-cuda.h"
+
+/************************conf file profile section**********************/
+
+void CudaHandlerAddCudaProfileFromConf(const char *name,
+ void *(*Callback)(ConfNode *node),
+ void (*Free)(void *));
+void *CudaHandlerGetCudaProfile(const char *name);
+void CudaHandlerFreeProfiles(void);
+
+/*******************cuda context related data section*******************/
+
+#define CUDA_HANDLER_MODULE_DATA_TYPE_MEMORY_HOST 0
+#define CUDA_HANDLER_MODULE_DATA_TYPE_MEMORY_DEVICE 1
+#define CUDA_HANDLER_MODULE_DATA_TYPE_CUDA_BUFFER 2
+
+CUcontext CudaHandlerModuleGetContext(const char *module_name, int device_id);
+void CudaHandlerModuleStoreData(const char *module_name,
+ const char *data_name, void *data_ptr);
+void *CudaHandlerModuleGetData(const char *module_name, const char *data_name);
+int CudaHandlerGetCudaModule(CUmodule *p_module, const char *ptx_image);
+
+#endif /* __UTIL_CUDA_HANDLERS__H__ */
diff --git a/framework/src/suricata/src/util-cuda-vars.c b/framework/src/suricata/src/util-cuda-vars.c
new file mode 100644
index 00000000..624c09f8
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-vars.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "suricata.h"
+#include "util-mpm.h"
+#include "util-cuda-handlers.h"
+#include "util-cuda-vars.h"
+#include "detect-engine-mpm.h"
+#include "util-debug.h"
+#include "util-mpm-ac.h"
+
+static DetectEngineCtx *cuda_de_ctx = NULL;
+
+void CudaVarsSetDeCtx(DetectEngineCtx *de_ctx)
+{
+ if (cuda_de_ctx != NULL) {
+ SCLogError(SC_ERR_FATAL, "CudaVarsSetDeCtx() called more than once. "
+ "This function should be called only once during the "
+ "lifetime of the engine.");
+ exit(EXIT_FAILURE);
+ }
+
+ cuda_de_ctx = de_ctx;
+
+ return;
+}
+
+int CudaThreadVarsInit(CudaThreadVars *ctv)
+{
+ if (PatternMatchDefaultMatcher() != MPM_AC_CUDA)
+ return 0;
+
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ if (conf == NULL) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error obtaining cuda mpm profile.");
+ return -1;
+ }
+
+ ctv->mpm_is_cuda = 1;
+ ctv->cuda_ac_cb = CudaHandlerModuleGetData(MPM_AC_CUDA_MODULE_NAME, MPM_AC_CUDA_MODULE_CUDA_BUFFER_NAME);
+ ctv->data_buffer_size_max_limit = conf->data_buffer_size_max_limit;
+ ctv->data_buffer_size_min_limit = conf->data_buffer_size_min_limit;
+ ctv->mpm_proto_tcp_ctx_ts = MpmFactoryGetMpmCtxForProfile(cuda_de_ctx, cuda_de_ctx->sgh_mpm_context_proto_tcp_packet, 0);
+ ctv->mpm_proto_tcp_ctx_tc = MpmFactoryGetMpmCtxForProfile(cuda_de_ctx, cuda_de_ctx->sgh_mpm_context_proto_tcp_packet, 1);
+ ctv->mpm_proto_udp_ctx_ts = MpmFactoryGetMpmCtxForProfile(cuda_de_ctx, cuda_de_ctx->sgh_mpm_context_proto_udp_packet, 0);
+ ctv->mpm_proto_udp_ctx_tc = MpmFactoryGetMpmCtxForProfile(cuda_de_ctx, cuda_de_ctx->sgh_mpm_context_proto_udp_packet, 1);
+ ctv->mpm_proto_other_ctx = MpmFactoryGetMpmCtxForProfile(cuda_de_ctx, cuda_de_ctx->sgh_mpm_context_proto_other_packet, 0);
+
+ return 0;
+}
+
+#endif
diff --git a/framework/src/suricata/src/util-cuda-vars.h b/framework/src/suricata/src/util-cuda-vars.h
new file mode 100644
index 00000000..9c24a915
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda-vars.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#ifndef __UTIL_CUDA_VARS__H__
+#define __UTIL_CUDA_VARS__H__
+
+#include "util-cuda-buffer.h"
+#include "util-mpm.h"
+#include "threads.h"
+
+typedef struct CudaThreadVars_ {
+ /* cb - CudaBuffer */
+ CudaBufferData *cuda_ac_cb;
+
+ MpmCtx *mpm_proto_other_ctx;
+
+ MpmCtx *mpm_proto_tcp_ctx_ts;
+ MpmCtx *mpm_proto_udp_ctx_ts;
+
+ MpmCtx *mpm_proto_tcp_ctx_tc;
+ MpmCtx *mpm_proto_udp_ctx_tc;
+
+ uint16_t data_buffer_size_max_limit;
+ uint16_t data_buffer_size_min_limit;
+
+ uint8_t mpm_is_cuda;
+} CudaThreadVars;
+
+typedef struct CudaPacketVars_ {
+ uint8_t cuda_mpm_enabled;
+ uint8_t cuda_done;
+ uint16_t cuda_gpu_matches;
+ SCMutex cuda_mutex;
+ SCCondT cuda_cond;
+ uint32_t cuda_results[(UTIL_MPM_CUDA_DATA_BUFFER_SIZE_MAX_LIMIT_DEFAULT * 2) + 1];
+} CudaPacketVars;
+
+void CudaVarsSetDeCtx(struct DetectEngineCtx_ *de_ctx);
+int CudaThreadVarsInit(CudaThreadVars *ctv);
+
+#endif /* __UTIL_CUDA_VARS__H__ */
+
+#endif /* __SC_CUDA_SUPPORT__ */
diff --git a/framework/src/suricata/src/util-cuda.c b/framework/src/suricata/src/util-cuda.c
new file mode 100644
index 00000000..3ada56b2
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda.c
@@ -0,0 +1,5455 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * NVIDIA CUDA utility functions - last referenced Cuda Toolkit 4.2
+ */
+
+/* compile in, only if we have a CUDA enabled device on the machine, with the
+ * toolkit and the driver installed */
+#ifdef __SC_CUDA_SUPPORT__
+
+#include <cuda.h>
+#include "util-cuda.h"
+#include "suricata-common.h"
+
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#define CASE_CODE(E) case E: return #E
+
+typedef enum SCCudaAPIS_ {
+ /* init api */
+ SC_CUDA_CU_INIT,
+
+ /* version management api */
+ SC_CUDA_CU_DRIVER_GET_VERSION,
+
+ /* device management api */
+ SC_CUDA_CU_DEVICE_COMPUTE_CAPABILITY,
+ SC_CUDA_CU_DEVICE_GET,
+ SC_CUDA_CU_DEVICE_GET_ATTRIBUTE,
+ SC_CUDA_CU_DEVICE_GET_COUNT,
+ SC_CUDA_CU_DEVICE_GET_NAME,
+ SC_CUDA_CU_DEVICE_GET_PROPERTIES,
+ SC_CUDA_CU_DEVICE_TOTAL_MEM,
+
+ /* context management api */
+ SC_CUDA_CU_CTX_CREATE,
+ SC_CUDA_CU_CTX_DESTROY,
+ SC_CUDA_CU_CTX_GET_API_VERSION,
+ SC_CUDA_CU_CTX_GET_CACHE_CONFIG,
+ SC_CUDA_CU_CTX_GET_CURRENT,
+ SC_CUDA_CU_CTX_GET_DEVICE,
+ SC_CUDA_CU_CTX_GET_LIMIT,
+ SC_CUDA_CU_CTX_POP_CURRENT,
+ SC_CUDA_CU_CTX_PUSH_CURRENT,
+ SC_CUDA_CU_CTX_SET_CACHE_CONFIG,
+ SC_CUDA_CU_CTX_SET_CURRENT,
+ SC_CUDA_CU_CTX_SET_LIMIT,
+ SC_CUDA_CU_CTX_SYNCHRONIZE,
+ SC_CUDA_CU_CTX_ATTACH,
+ SC_CUDA_CU_CTX_DETACH,
+
+ /* module management api */
+ SC_CUDA_CU_MODULE_GET_FUNCTION,
+ SC_CUDA_CU_MODULE_GET_GLOBAL,
+ SC_CUDA_CU_MODULE_GET_SURF_REF,
+ SC_CUDA_CU_MODULE_GET_TEX_REF,
+ SC_CUDA_CU_MODULE_LOAD,
+ SC_CUDA_CU_MODULE_LOAD_DATA,
+ SC_CUDA_CU_MODULE_LOAD_DATA_EX,
+ SC_CUDA_CU_MODULE_LOAD_FAT_BINARY,
+ SC_CUDA_CU_MODULE_UNLOAD,
+
+ /* memory management api */
+ SC_CUDA_CU_ARRAY_3D_CREATE,
+ SC_CUDA_CU_ARRAY_3D_GET_DESCRIPTOR,
+ SC_CUDA_CU_ARRAY_CREATE,
+ SC_CUDA_CU_ARRAY_DESTROY,
+ SC_CUDA_CU_ARRAY_GET_DESCRIPTOR,
+ SC_CUDA_CU_DEVICE_GET_BY_PCI_BUS_ID,
+ SC_CUDA_CU_DEVICE_GET_PCI_BUS_ID,
+ SC_CUDA_CU_IPC_CLOSE_MEM_HANDLE,
+ SC_CUDA_CU_IPC_GET_EVENT_HANDLE,
+ SC_CUDA_CU_IPC_GET_MEM_HANDLE,
+ SC_CUDA_CU_IPC_OPEN_EVENT_HANDLE,
+ SC_CUDA_CU_IPC_OPEN_MEM_HANDLE,
+ SC_CUDA_CU_MEM_ALLOC,
+ SC_CUDA_CU_MEM_ALLOC_HOST,
+ SC_CUDA_CU_MEM_ALLOC_PITCH,
+ SC_CUDA_CU_MEMCPY,
+ SC_CUDA_CU_MEMCPY_2D,
+ SC_CUDA_CU_MEMCPY_2D_ASYNC,
+ SC_CUDA_CU_MEMCPY_2D_UNALIGNED,
+ SC_CUDA_CU_MEMCPY_3D,
+ SC_CUDA_CU_MEMCPY_3D_ASYNC,
+ SC_CUDA_CU_MEMCPY_3D_PEER,
+ SC_CUDA_CU_MEMCPY_3D_PEER_ASYNC,
+ SC_CUDA_CU_MEMCPY_ASYNC,
+ SC_CUDA_CU_MEMCPY_A_TO_A,
+ SC_CUDA_CU_MEMCPY_A_TO_D,
+ SC_CUDA_CU_MEMCPY_A_TO_H,
+ SC_CUDA_CU_MEMCPY_A_TO_H_ASYNC,
+ SC_CUDA_CU_MEMCPY_D_TO_A,
+ SC_CUDA_CU_MEMCPY_D_TO_D,
+ SC_CUDA_CU_MEMCPY_D_TO_D_ASYNC,
+ SC_CUDA_CU_MEMCPY_D_TO_H,
+ SC_CUDA_CU_MEMCPY_D_TO_H_ASYNC,
+ SC_CUDA_CU_MEMCPY_H_TO_A,
+ SC_CUDA_CU_MEMCPY_H_TO_A_ASYNC,
+ SC_CUDA_CU_MEMCPY_H_TO_D,
+ SC_CUDA_CU_MEMCPY_H_TO_D_ASYNC,
+ SC_CUDA_CU_MEMCPY_PEER,
+ SC_CUDA_CU_MEMCPY_PEER_ASYNC,
+ SC_CUDA_CU_MEM_FREE,
+ SC_CUDA_CU_MEM_FREE_HOST,
+ SC_CUDA_CU_MEM_GET_ADDRESS_RANGE,
+ SC_CUDA_CU_MEM_GET_INFO,
+ SC_CUDA_CU_MEM_HOST_ALLOC,
+ SC_CUDA_CU_MEM_HOST_GET_DEVICE_POINTER,
+ SC_CUDA_CU_MEM_HOST_GET_FLAGS,
+ SC_CUDA_CU_MEM_HOST_REGISTER,
+ SC_CUDA_CU_MEM_HOST_UNREGISTER,
+ SC_CUDA_CU_MEMSET_D16,
+ SC_CUDA_CU_MEMSET_D16_ASYNC,
+ SC_CUDA_CU_MEMSET_D2_D16,
+ SC_CUDA_CU_MEMSET_D2_D16_ASYNC,
+ SC_CUDA_CU_MEMSET_D2_D32,
+ SC_CUDA_CU_MEMSET_D2_D32_ASYNC,
+ SC_CUDA_CU_MEMSET_D2_D8,
+ SC_CUDA_CU_MEMSET_D2_D8_ASYNC,
+ SC_CUDA_CU_MEMSET_D32,
+ SC_CUDA_CU_MEMSET_D32_ASYNC,
+ SC_CUDA_CU_MEMSET_D8,
+ SC_CUDA_CU_MEMSET_D8_ASYNC,
+
+ /* unified addresssing */
+ SC_CUDA_CU_POINTER_GET_ATTRIBUTE,
+
+ /* stream management api */
+ SC_CUDA_CU_STREAM_CREATE,
+ SC_CUDA_CU_STREAM_DESTROY,
+ SC_CUDA_CU_STREAM_QUERY,
+ SC_CUDA_CU_STREAM_SYNCHRONIZE,
+ SC_CUDA_CU_STREAM_WAIT_EVENT,
+
+ /* event management api */
+ SC_CUDA_CU_EVENT_CREATE,
+ SC_CUDA_CU_EVENT_DESTROY,
+ SC_CUDA_CU_EVENT_ELAPSED_TIME,
+ SC_CUDA_CU_EVENT_QUERY,
+ SC_CUDA_CU_EVENT_RECORD,
+ SC_CUDA_CU_EVENT_SYNCHRONIZE,
+
+ /* execution control api */
+ SC_CUDA_CU_FUNC_GET_ATTRIBUTE,
+ SC_CUDA_CU_FUNC_SET_CACHE_CONFIG,
+ SC_CUDA_CU_LAUNCH_KERNEL,
+ SC_CUDA_CU_FUNC_SET_BLOCK_SHAPE,
+ SC_CUDA_CU_FUNC_SET_SHARED_SIZE,
+ SC_CUDA_CU_LAUNCH,
+ SC_CUDA_CU_LAUNCH_GRID,
+ SC_CUDA_CU_LAUNCH_GRID_ASYNC,
+ SC_CUDA_CU_PARAM_SETF,
+ SC_CUDA_CU_PARAM_SETI,
+ SC_CUDA_CU_PARAM_SET_SIZE,
+ SC_CUDA_CU_PARAM_SET_TEX_REF,
+ SC_CUDA_CU_PARAM_SETV,
+
+ /* texture reference api */
+ SC_CUDA_CU_TEX_REF_CREATE,
+ SC_CUDA_CU_TEX_REF_DESTROY,
+ SC_CUDA_CU_TEX_REF_GET_ADDRESS,
+ SC_CUDA_CU_TEX_REF_GET_ADDRESS_MODE,
+ SC_CUDA_CU_TEX_REF_GET_ARRAY,
+ SC_CUDA_CU_TEX_REF_GET_FILTER_MODE,
+ SC_CUDA_CU_TEX_REF_GET_FLAGS,
+ SC_CUDA_CU_TEX_REF_GET_FORMAT,
+ SC_CUDA_CU_TEX_REF_SET_ADDRESS,
+ SC_CUDA_CU_TEX_REF_SET_ADDRESS_2D,
+ SC_CUDA_CU_TEX_REF_SET_ADDRESS_MODE,
+ SC_CUDA_CU_TEX_REF_SET_ARRAY,
+ SC_CUDA_CU_TEX_REF_SET_FILTER_MODE,
+ SC_CUDA_CU_TEX_REF_SET_FLAGS,
+ SC_CUDA_CU_TEX_REF_SET_FORMAT,
+} SCCudaAPIS;
+
+SCEnumCharMap sc_cuda_api_names_string_map[] = {
+ /* init api */
+ { "cuInit", SC_CUDA_CU_INIT },
+
+ /* version management api */
+ { "cuDriverGetVersion", SC_CUDA_CU_DRIVER_GET_VERSION },
+
+ /* device management api */
+ { "cuDeviceComputeCapability", SC_CUDA_CU_DEVICE_COMPUTE_CAPABILITY },
+ { "cuDeviceGet", SC_CUDA_CU_DEVICE_GET },
+ { "cuDeviceGetAttribute", SC_CUDA_CU_DEVICE_GET_ATTRIBUTE },
+ { "cuDeviceGetCount", SC_CUDA_CU_DEVICE_GET_COUNT },
+ { "cuDeviceGetName", SC_CUDA_CU_DEVICE_GET_NAME },
+ { "cuDeviceGetProperties", SC_CUDA_CU_DEVICE_GET_PROPERTIES },
+ { "cuDeviceTotalMem", SC_CUDA_CU_DEVICE_TOTAL_MEM },
+
+ /* context management api */
+ { "cuCtxCreate", SC_CUDA_CU_CTX_CREATE },
+ { "cuCtxDestroy", SC_CUDA_CU_CTX_DESTROY },
+ { "cuCtxGetApiVersion", SC_CUDA_CU_CTX_GET_API_VERSION },
+ { "cuCtxGetCacheConfig", SC_CUDA_CU_CTX_GET_CACHE_CONFIG },
+ { "cuCtxGetCurrent", SC_CUDA_CU_CTX_GET_CURRENT },
+ { "cuCtxGetDevice", SC_CUDA_CU_CTX_GET_DEVICE },
+ { "cuCtxGetLimit", SC_CUDA_CU_CTX_GET_LIMIT },
+ { "cuCtxPopCurrent", SC_CUDA_CU_CTX_POP_CURRENT },
+ { "cuCtxPushCurrent", SC_CUDA_CU_CTX_PUSH_CURRENT },
+ { "cuCtxSetCacheConfig", SC_CUDA_CU_CTX_SET_CACHE_CONFIG },
+ { "cuCtxSetCurrent", SC_CUDA_CU_CTX_SET_CURRENT },
+ { "cuCtxSetLimit", SC_CUDA_CU_CTX_SET_LIMIT },
+ { "cuCtxSynchronize", SC_CUDA_CU_CTX_SYNCHRONIZE },
+ { "cuCtxAttach", SC_CUDA_CU_CTX_ATTACH },
+ { "cuCtxDetach", SC_CUDA_CU_CTX_DETACH },
+
+ /* module management api */
+ { "cuModuleGetFunction", SC_CUDA_CU_MODULE_GET_FUNCTION },
+ { "cuModuleGetGlobal", SC_CUDA_CU_MODULE_GET_GLOBAL },
+ { "cuModuleGetSurfRef", SC_CUDA_CU_MODULE_GET_SURF_REF },
+ { "cuModuleGetTexRef", SC_CUDA_CU_MODULE_GET_TEX_REF },
+ { "cuModuleLoad", SC_CUDA_CU_MODULE_LOAD },
+ { "cuModuleLoadData", SC_CUDA_CU_MODULE_LOAD_DATA },
+ { "cuModuleLoadDataEx", SC_CUDA_CU_MODULE_LOAD_DATA_EX },
+ { "cuModuleLoadFatBinary", SC_CUDA_CU_MODULE_LOAD_FAT_BINARY },
+ { "cuModuleUnload", SC_CUDA_CU_MODULE_UNLOAD },
+
+ /* memory management api */
+ { "cuArray3DCreate", SC_CUDA_CU_ARRAY_3D_CREATE },
+ { "cuArray3DGetDescriptor", SC_CUDA_CU_ARRAY_3D_GET_DESCRIPTOR },
+ { "cuArrayCreate", SC_CUDA_CU_ARRAY_CREATE },
+ { "cuArrayDestroy", SC_CUDA_CU_ARRAY_DESTROY },
+ { "cuArrayGetDescriptor", SC_CUDA_CU_ARRAY_GET_DESCRIPTOR },
+ { "cuDeviceGetByPCIBusId", SC_CUDA_CU_DEVICE_GET_BY_PCI_BUS_ID },
+ { "cuDeviceGetPCIBusId", SC_CUDA_CU_DEVICE_GET_PCI_BUS_ID },
+ { "cuIpcCloseMemHandle", SC_CUDA_CU_IPC_CLOSE_MEM_HANDLE },
+ { "cuIpcGetEventHandle", SC_CUDA_CU_IPC_GET_MEM_HANDLE },
+ { "cuIpcGetMemHandle", SC_CUDA_CU_IPC_GET_MEM_HANDLE },
+ { "cuIpcOpenEventHandle", SC_CUDA_CU_IPC_OPEN_EVENT_HANDLE },
+ { "cuIpcOpenMemHandle", SC_CUDA_CU_IPC_OPEN_MEM_HANDLE },
+ { "cuMemAlloc", SC_CUDA_CU_MEM_ALLOC },
+ { "cuMemAllocHost", SC_CUDA_CU_MEM_ALLOC_HOST },
+ { "cuMemAllocPitch", SC_CUDA_CU_MEM_ALLOC_PITCH },
+ { "cuMemcpy", SC_CUDA_CU_MEMCPY },
+ { "cuMemcpy2D", SC_CUDA_CU_MEMCPY_2D },
+ { "cuMemcpy2DAsync", SC_CUDA_CU_MEMCPY_2D_ASYNC },
+ { "cuMemcpy2DUnaligned", SC_CUDA_CU_MEMCPY_2D_UNALIGNED },
+ { "cuMemcpy3D", SC_CUDA_CU_MEMCPY_3D },
+ { "cuMemcpy3DAsync", SC_CUDA_CU_MEMCPY_3D_ASYNC },
+ { "cuMemcpy3DPeer", SC_CUDA_CU_MEMCPY_3D_PEER },
+ { "cuMemcpy3DPeerAsync", SC_CUDA_CU_MEMCPY_3D_PEER_ASYNC },
+ { "cuMemcpyAsync", SC_CUDA_CU_MEMCPY_ASYNC },
+ { "cuMemcpyAtoA", SC_CUDA_CU_MEMCPY_A_TO_A },
+ { "cuMemcpyAtoD", SC_CUDA_CU_MEMCPY_A_TO_D },
+ { "cuMemcpyAtoH", SC_CUDA_CU_MEMCPY_A_TO_H },
+ { "cuMemcpyAtoHAsync", SC_CUDA_CU_MEMCPY_A_TO_H_ASYNC },
+ { "cuMemcpyDtoA", SC_CUDA_CU_MEMCPY_D_TO_A },
+ { "cuMemcpyDtoD", SC_CUDA_CU_MEMCPY_D_TO_D },
+ { "cuMemcpyDtoDAsync", SC_CUDA_CU_MEMCPY_D_TO_D_ASYNC },
+ { "cuMemcpyDtoH", SC_CUDA_CU_MEMCPY_D_TO_H },
+ { "cuMemcpyDtoHAsync", SC_CUDA_CU_MEMCPY_D_TO_H_ASYNC },
+ { "cuMemcpyHtoA", SC_CUDA_CU_MEMCPY_H_TO_A },
+ { "cuMemcpyHtoAAsync", SC_CUDA_CU_MEMCPY_H_TO_A_ASYNC },
+ { "cuMemcpyHtoD", SC_CUDA_CU_MEMCPY_H_TO_D },
+ { "cuMemcpyHtoDAsync", SC_CUDA_CU_MEMCPY_H_TO_D_ASYNC },
+ { "cuMemcpyPeer", SC_CUDA_CU_MEMCPY_PEER },
+ { "cuMemcpyPeerAsync", SC_CUDA_CU_MEMCPY_PEER_ASYNC },
+ { "cuMemFree", SC_CUDA_CU_MEM_FREE },
+ { "cuMemFreeHost", SC_CUDA_CU_MEM_FREE_HOST },
+ { "cuMemGetAddressRange", SC_CUDA_CU_MEM_GET_ADDRESS_RANGE },
+ { "cuMemGetInfo", SC_CUDA_CU_MEM_GET_INFO },
+ { "cuMemHostAlloc", SC_CUDA_CU_MEM_HOST_ALLOC },
+ { "cuMemHostGetDevicePointer", SC_CUDA_CU_MEM_HOST_GET_DEVICE_POINTER },
+ { "cuMemHostGetFlags", SC_CUDA_CU_MEM_HOST_GET_FLAGS },
+ { "cuMemHostRegister", SC_CUDA_CU_MEM_HOST_REGISTER },
+ { "cuMemHostUnregister", SC_CUDA_CU_MEM_HOST_UNREGISTER },
+ { "cuMemsetD16", SC_CUDA_CU_MEMSET_D16 },
+ { "cuMemsetD16Async", SC_CUDA_CU_MEMSET_D16_ASYNC },
+ { "cuMemsetD2D16", SC_CUDA_CU_MEMSET_D2_D16 },
+ { "cuMemsetD2D16Async", SC_CUDA_CU_MEMSET_D2_D16_ASYNC },
+ { "cuMemsetD2D32", SC_CUDA_CU_MEMSET_D2_D32 },
+ { "cuMemsetD2D32Async", SC_CUDA_CU_MEMSET_D2_D32_ASYNC },
+ { "cuMemsetD2D8", SC_CUDA_CU_MEMSET_D2_D8 },
+ { "cuMemsetD2D8Async", SC_CUDA_CU_MEMSET_D2_D8_ASYNC },
+ { "cuMemsetD32", SC_CUDA_CU_MEMSET_D32 },
+ { "cuMemsetD32Async", SC_CUDA_CU_MEMSET_D32_ASYNC },
+ { "cuMemsetD8", SC_CUDA_CU_MEMSET_D8 },
+ { "cuMemsetD8Async", SC_CUDA_CU_MEMSET_D8_ASYNC },
+
+ /* unified addressing */
+ { "cuPointerGetAttribute", SC_CUDA_CU_POINTER_GET_ATTRIBUTE },
+
+ /* stream management api */
+ { "cuStreamCreate", SC_CUDA_CU_STREAM_CREATE },
+ { "cuStreamDestroy", SC_CUDA_CU_STREAM_DESTROY },
+ { "cuStreamQuery", SC_CUDA_CU_STREAM_QUERY },
+ { "cuStreamSynchronize", SC_CUDA_CU_STREAM_SYNCHRONIZE },
+ { "cuStreamWaitEvent", SC_CUDA_CU_STREAM_WAIT_EVENT },
+
+ /* event management api */
+ { "cuEventCreate", SC_CUDA_CU_EVENT_CREATE },
+ { "cuEventDestroy", SC_CUDA_CU_EVENT_DESTROY },
+ { "cuEventElapseTime", SC_CUDA_CU_EVENT_ELAPSED_TIME },
+ { "cuEventQuery", SC_CUDA_CU_EVENT_QUERY },
+ { "cuEventRecord", SC_CUDA_CU_EVENT_RECORD },
+ { "cuEventSynchronize", SC_CUDA_CU_EVENT_SYNCHRONIZE },
+
+ /* execution control api */
+ { "cuFuncGetAttribute", SC_CUDA_CU_FUNC_GET_ATTRIBUTE },
+ { "cuFuncSetCacheConfig", SC_CUDA_CU_FUNC_SET_CACHE_CONFIG },
+ { "cuLaunchKernel", SC_CUDA_CU_LAUNCH_KERNEL },
+ { "cuFuncSetBlockShape", SC_CUDA_CU_FUNC_SET_BLOCK_SHAPE },
+ { "cuFuncSetSharedSize", SC_CUDA_CU_FUNC_SET_SHARED_SIZE },
+ { "cuLaunch", SC_CUDA_CU_LAUNCH },
+ { "cuLaunchGrid", SC_CUDA_CU_LAUNCH_GRID },
+ { "cuLaunchGridAsync", SC_CUDA_CU_LAUNCH_GRID_ASYNC },
+ { "cuParamSetf", SC_CUDA_CU_PARAM_SETF },
+ { "cuParamSeti", SC_CUDA_CU_PARAM_SETI },
+ { "cuParamSetSize", SC_CUDA_CU_PARAM_SET_SIZE },
+ { "cuSetTexRef", SC_CUDA_CU_PARAM_SET_TEX_REF },
+ { "cuSetv", SC_CUDA_CU_PARAM_SETV },
+
+ /* texture reference api */
+ { "cuTexRefCreate", SC_CUDA_CU_TEX_REF_CREATE},
+ { "cuTexRefDestroy", SC_CUDA_CU_TEX_REF_DESTROY},
+ { "cuTexRefGetAddress", SC_CUDA_CU_TEX_REF_GET_ADDRESS},
+ { "cuTexRefGetAddressMode", SC_CUDA_CU_TEX_REF_GET_ADDRESS_MODE},
+ { "cuTexRefGetArray", SC_CUDA_CU_TEX_REF_GET_ARRAY},
+ { "cuTexRefGetFilterMode", SC_CUDA_CU_TEX_REF_GET_FILTER_MODE},
+ { "cuTexRefGetFlags", SC_CUDA_CU_TEX_REF_GET_FLAGS},
+ { "cuTexRefGetFormat", SC_CUDA_CU_TEX_REF_GET_FORMAT},
+ { "cuTexRefSetAddress", SC_CUDA_CU_TEX_REF_SET_ADDRESS},
+ { "cuTexRefSetAddress2D", SC_CUDA_CU_TEX_REF_SET_ADDRESS_2D},
+ { "cuTexRefSetAddressMode", SC_CUDA_CU_TEX_REF_SET_ADDRESS_MODE},
+ { "cuTexRefSetArray", SC_CUDA_CU_TEX_REF_SET_ARRAY},
+ { "cuTexRefSetFilterMode", SC_CUDA_CU_TEX_REF_SET_FILTER_MODE},
+ { "cuTexRefSetFlags", SC_CUDA_CU_TEX_REF_SET_FLAGS},
+ { "cuTexRefSetFormat", SC_CUDA_CU_TEX_REF_SET_FORMAT},
+
+ { NULL, -1 },
+};
+
+static SCCudaDevices *devices = NULL;
+
+/*****************************Error_Handling_API*******************************/
+
+/**
+ * \internal
+ * \brief Maps the error enums from SCCudaAPIS to strings using the preprocessor
+ * #ENUM_VALUE. This is mainly needed for logging purposes to log the
+ * error codes.
+ *
+ * \param err The error_code for which the string has to be returned.
+ *
+ * \retval The string equivalent of the error code.
+ */
+static const char *SCCudaGetErrorCodeInString(int err)
+{
+ switch (err) {
+ CASE_CODE(CUDA_SUCCESS);
+ CASE_CODE(CUDA_ERROR_INVALID_VALUE);
+ CASE_CODE(CUDA_ERROR_OUT_OF_MEMORY);
+ CASE_CODE(CUDA_ERROR_NOT_INITIALIZED);
+ CASE_CODE(CUDA_ERROR_DEINITIALIZED);
+ CASE_CODE(CUDA_ERROR_PROFILER_DISABLED);
+ CASE_CODE(CUDA_ERROR_PROFILER_NOT_INITIALIZED);
+ CASE_CODE(CUDA_ERROR_PROFILER_ALREADY_STARTED);
+ CASE_CODE(CUDA_ERROR_PROFILER_ALREADY_STOPPED);
+ CASE_CODE(CUDA_ERROR_NO_DEVICE);
+ CASE_CODE(CUDA_ERROR_INVALID_DEVICE);
+ CASE_CODE(CUDA_ERROR_INVALID_IMAGE);
+ CASE_CODE(CUDA_ERROR_INVALID_CONTEXT);
+ /* deprecated error code as of 3.2 */
+ CASE_CODE(CUDA_ERROR_CONTEXT_ALREADY_CURRENT);
+ CASE_CODE(CUDA_ERROR_MAP_FAILED);
+ CASE_CODE(CUDA_ERROR_UNMAP_FAILED);
+ CASE_CODE(CUDA_ERROR_ARRAY_IS_MAPPED);
+ CASE_CODE(CUDA_ERROR_ALREADY_MAPPED);
+ CASE_CODE(CUDA_ERROR_NO_BINARY_FOR_GPU);
+ CASE_CODE(CUDA_ERROR_ALREADY_ACQUIRED);
+ CASE_CODE(CUDA_ERROR_NOT_MAPPED);
+ CASE_CODE(CUDA_ERROR_NOT_MAPPED_AS_ARRAY);
+ CASE_CODE(CUDA_ERROR_NOT_MAPPED_AS_POINTER);
+ CASE_CODE(CUDA_ERROR_ECC_UNCORRECTABLE);
+ CASE_CODE(CUDA_ERROR_UNSUPPORTED_LIMIT);
+ CASE_CODE(CUDA_ERROR_CONTEXT_ALREADY_IN_USE);
+ CASE_CODE(CUDA_ERROR_INVALID_SOURCE);
+ CASE_CODE(CUDA_ERROR_FILE_NOT_FOUND);
+ CASE_CODE(CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND);
+ CASE_CODE(CUDA_ERROR_SHARED_OBJECT_INIT_FAILED);
+ CASE_CODE(CUDA_ERROR_OPERATING_SYSTEM);
+ CASE_CODE(CUDA_ERROR_INVALID_HANDLE);
+ CASE_CODE(CUDA_ERROR_NOT_FOUND);
+ CASE_CODE(CUDA_ERROR_NOT_READY);
+ CASE_CODE(CUDA_ERROR_LAUNCH_FAILED);
+ CASE_CODE(CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES);
+ CASE_CODE(CUDA_ERROR_LAUNCH_TIMEOUT);
+ CASE_CODE(CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING);
+ CASE_CODE(CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED);
+ CASE_CODE(CUDA_ERROR_PEER_ACCESS_NOT_ENABLED);
+ CASE_CODE(CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE);
+ CASE_CODE(CUDA_ERROR_CONTEXT_IS_DESTROYED);
+ CASE_CODE(CUDA_ERROR_ASSERT);
+ CASE_CODE(CUDA_ERROR_TOO_MANY_PEERS);
+ CASE_CODE(CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED);
+ CASE_CODE(CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED);
+ CASE_CODE(CUDA_ERROR_UNKNOWN);
+ default:
+ return "CUDA_UNKNOWN_ERROR_CODE";
+ }
+}
+
+/**
+ * \internal
+ * \brief A generic function that handles the return values from the CUDA driver
+ * API.
+ *
+ * \param result The result from the CUDA driver API call.
+ * \param api_type An enum value SCCudaAPIS corresponing to the API for which the
+ * result was returned. The enum is needed to map the api type to
+ * a string for logging purposes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCCudaHandleRetValue(CUresult result, SCCudaAPIS api_type)
+{
+ if (result == CUDA_SUCCESS) {
+ SCLogDebug("%s executed successfully",
+ SCMapEnumValueToName(api_type, sc_cuda_api_names_string_map));
+ return 0;
+ } else {
+ SCLogError(SC_ERR_CUDA_ERROR, "%s failed. Returned errocode - %s",
+ SCMapEnumValueToName(api_type, sc_cuda_api_names_string_map),
+ SCCudaGetErrorCodeInString(result));
+ return -1;
+ }
+}
+
+/*****************************Cuda_Initialization_API**************************/
+
+/**
+ * \internal
+ * \brief Inits the cuda driver API.
+ *
+ * \param flags Currently should be 0.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaInit(unsigned int flags)
+{
+ CUresult result = cuInit(flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_INIT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/*****************************Version_Management_API***************************/
+
+/**
+ * \brief Returns in *driver_version the version number of the installed CUDA
+ * driver. This function automatically returns CUDA_ERROR_INVALID_VALUE
+ * if the driver_version argument is NULL.
+ *
+ * \param driver_version Returns the CUDA driver version.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDriverGetVersion(int *driver_version)
+{
+ CUresult result = 0;
+
+ if (driver_version == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "driver_version NULL");
+ goto error;
+ }
+
+ result = cuDriverGetVersion(driver_version);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DRIVER_GET_VERSION) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/*****************************Device_Management_API****************************/
+
+/**
+ * \internal
+ * \brief Returns the major and the minor revision numbers that define the
+ * compute capability for the device that is sent as the argument.
+ *
+ * \param major Pointer to an integer, that will be updated with the major revision.
+ * \param minor Pointer to an integer, that will be updated with the minor revision.
+ * \param dev The device handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceComputeCapability(int *major, int *minor, CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (major == NULL || minor == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "major is NULL or minor is NULL");
+ goto error;
+ }
+
+ result = cuDeviceComputeCapability(major, minor, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_COMPUTE_CAPABILITY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Returns a device handle given an ordinal in the range
+ * [0, cuDeviceGetCount() - 1].
+ *
+ * \param device Pointer to a CUDevice instance that will be updated with the
+ * device handle.
+ * \param ordinal An index in the range [0, cuDeviceGetCount() - 1].
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceGet(CUdevice *device, int ordinal)
+{
+ CUresult result = 0;
+
+ if (device == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "device NULL");
+ goto error;
+ }
+
+ result = cuDeviceGet(device, ordinal);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Returns the various attributes for the device that is sent as the arg.
+ *
+ * The supported attributes are:
+ *
+ * CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK: Maximum number of threads
+ * per block;
+ * CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X: Maximum x-dimension of a block;
+ * CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y: Maximum y-dimension of a block;
+ * CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z: Maximum z-dimension of a block;
+ * CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X: Maximum x-dimension of a grid;
+ * CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y: Maximum y-dimension of a grid;
+ * CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z: Maximum z-dimension of a grid;
+ * CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK: Maximum amount of
+ * shared mem-ory available to a thread block in bytes; this amount
+ * is shared by all thread blocks simultaneously resident on a
+ * multiprocessor;
+ * CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY: Memory available on device
+ * for __constant_-_ variables in a CUDA C kernel in bytes;
+ * CU_DEVICE_ATTRIBUTE_WARP_SIZE: Warp size in threads;
+ * CU_DEVICE_ATTRIBUTE_MAX_PITCH: Maximum pitch in bytes allowed by the
+ * memory copy functions that involve memory regions allocated
+ * through cuMemAllocPitch();
+ * CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK: Maximum number of 32-bit
+ * registers avail-able to a thread block; this number is shared by
+ * all thread blocks simultaneously resident on a multiprocessor;
+ * CU_DEVICE_ATTRIBUTE_CLOCK_RATE: Peak clock frequency in kilohertz;
+ * CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT: Alignment requirement; texture
+ * base addresses aligned to textureAlign bytes do not need an offset
+ * applied to texture fetches;
+ * CU_DEVICE_ATTRIBUTE_GPU_OVERLAP: 1 if the device can concurrently copy
+ * memory between host and device while executing a kernel, or 0 if not;
+ * CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT: Number of multiprocessors on
+ * the device;
+ * CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT: 1 if there is a run time limit
+ * for kernels executed on the device, or 0 if not;
+ * CU_DEVICE_ATTRIBUTE_INTEGRATED: 1 if the device is integrated with the
+ * memory subsystem, or 0 if not;
+ * CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY: 1 if the device can map host
+ * memory into the CUDA address space, or 0 if not;
+ * CU_DEVICE_ATTRIBUTE_COMPUTE_MODE: Compute mode that device is currently
+ * in. Available modes are as follows:
+ * - CU_COMPUTEMODE_DEFAULT: Default mode - Device is not restricted
+ * and can have multiple CUDA contexts present at a single time.
+ * - CU_COMPUTEMODE_EXCLUSIVE: Compute-exclusive mode - Device can have
+ * only one CUDA con-text present on it at a time.
+ * - CU_COMPUTEMODE_PROHIBITED: Compute-prohibited mode - Device is
+ * prohibited from creating new CUDA contexts.
+ *
+ * \param pi Pointer to an interger instance that will be updated with the
+ * attribute value.
+ * \param attrib Device attribute to query.
+ * \param dev The device handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceGetAttribute(int *pi, CUdevice_attribute attrib,
+ CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (pi == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "prop is NULL");
+ goto error;
+ }
+
+ result = cuDeviceGetAttribute(pi, attrib, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_ATTRIBUTE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Gets the total no of devices with compute capability greater than or
+ * equal to 1.0 that are available for execution.
+ *
+ * \param count Pointer to an integer that will be updated with the device count.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceGetCount(int *count)
+{
+ CUresult result = 0;
+
+ if (count == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "count NULL");
+ goto error;
+ }
+
+ result = cuDeviceGetCount(count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_COUNT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Returns the device name, given the device handle.
+ *
+ * \param name Pointer to a char buffer which will be updated with the device name.
+ * \param len Length of the above buffer.
+ * \param dev The device handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceGetName(char *name, int len, CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (name == NULL || len == 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "name is NULL or len is 0");
+ goto error;
+ }
+
+ result = cuDeviceGetName(name, len, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_NAME) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Returns the properties of the device. The CUdevprop structure is
+ * defined as
+ *
+ * typedef struct CUdevprop_st {
+ * int maxThreadsPerBlock;
+ * int maxThreadsDim[3];
+ * int maxGridSize[3];
+ * int sharedMemPerBlock;
+ * int totalConstantMemory;
+ * int SIMDWidth;
+ * int memPitch;
+ * int regsPerBlock;
+ * int clockRate;
+ * int textureAlign
+ * } CUdevprop;
+ *
+ * \param prop Pointer to a CUdevprop instance that holds the device properties.
+ * \param dev The device handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceGetProperties(CUdevprop *prop, CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (prop == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "prop is NULL");
+ goto error;
+ }
+
+ result = cuDeviceGetProperties(prop, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_PROPERTIES) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Returns the total amount of memory availabe on the device which
+ * is sent as the argument.
+ *
+ * \param bytes Pointer to an unsigned int instance, that will be updated with
+ * total memory for the device.
+ * \param dev The device handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaDeviceTotalMem(size_t *bytes, CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (bytes == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "bytes is NULL");
+ goto error;
+ }
+
+ result = cuDeviceTotalMem(bytes, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_TOTAL_MEM) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Creates and returns a new instance of SCCudaDevice.
+ *
+ * \retval device Pointer to the new instance of SCCudaDevice.
+ */
+static SCCudaDevice *SCCudaAllocSCCudaDevice(void)
+{
+ SCCudaDevice *device = SCMalloc(sizeof(SCCudaDevice));
+ if (unlikely(device == NULL))
+ return NULL;
+ memset(device, 0 , sizeof(SCCudaDevice));
+
+ return device;
+}
+
+/**
+ * \internal
+ * \brief Frees an instance of SCCudaDevice.
+ *
+ * \param device Pointer to the an instance of SCCudaDevice to be freed.
+ */
+static void SCCudaDeAllocSCCudaDevice(SCCudaDevice *device)
+{
+ SCFree(device);
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates and returns a new instance of SCCudaDevices.
+ *
+ * \retval devices Pointer to the new instance of SCCudaDevices.
+ */
+static SCCudaDevices *SCCudaAllocSCCudaDevices(void)
+{
+ SCCudaDevices *devices = SCMalloc(sizeof(SCCudaDevices));
+ if (unlikely(devices == NULL))
+ return NULL;
+ memset(devices, 0 , sizeof(SCCudaDevices));
+
+ return devices;
+}
+
+/**
+ * \internal
+ * \brief Frees an instance of SCCudaDevices.
+ *
+ * \param device Pointer to the an instance of SCCudaDevices to be freed.
+ */
+static void SCCudaDeAllocSCCudaDevices(SCCudaDevices *devices)
+{
+ int i = 0;
+
+ if (devices == NULL)
+ return;
+
+ if (devices->devices != NULL) {
+ for (i = 0; i < devices->count; i++)
+ SCCudaDeAllocSCCudaDevice(devices->devices[i]);
+
+ SCFree(devices->devices);
+ }
+
+ SCFree(devices);
+
+ return;
+}
+
+/**
+ * \brief Retrieves all the devices and all the information corresponding to
+ * the devices on the CUDA device available on this system and returns
+ * a SCCudaDevices instances which holds all this information.
+ *
+ * \retval devices Pointer to a SCCudaDevices instance that holds information
+ * for all the CUDA devices on the system.
+ */
+static SCCudaDevices *SCCudaGetDevices(void)
+{
+ SCCudaDevices *devices = SCCudaAllocSCCudaDevices();
+ int i = 0;
+
+ if (SCCudaDeviceGetCount(&devices->count) == -1)
+ goto error;
+
+ devices->devices = SCMalloc(devices->count * sizeof(SCCudaDevice *));
+ if (devices->devices == NULL)
+ goto error;
+
+ /* update the device properties */
+ for (i = 0; i < devices->count; i++) {
+ devices->devices[i] = SCCudaAllocSCCudaDevice();
+
+ if (SCCudaDeviceGet(&devices->devices[i]->device, i) == -1)
+ goto error;
+
+ if (SCCudaDeviceComputeCapability(&devices->devices[i]->major_rev,
+ &devices->devices[i]->minor_rev,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetName(devices->devices[i]->name,
+ SC_CUDA_DEVICE_NAME_MAX_LEN,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceTotalMem(&devices->devices[i]->bytes,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetProperties(&devices->devices[i]->prop,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ /* retrieve the attributes */
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_threads_per_block,
+ CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_block_dim_x,
+ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_block_dim_y,
+ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_block_dim_z,
+ CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_grid_dim_x,
+ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_grid_dim_y,
+ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_grid_dim_z,
+ CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_shared_memory_per_block,
+ CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_total_constant_memory,
+ CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_warp_size,
+ CU_DEVICE_ATTRIBUTE_WARP_SIZE,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_pitch,
+ CU_DEVICE_ATTRIBUTE_MAX_PITCH,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_max_registers_per_block,
+ CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_clock_rate,
+ CU_DEVICE_ATTRIBUTE_CLOCK_RATE,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_texture_alignment,
+ CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_gpu_overlap,
+ CU_DEVICE_ATTRIBUTE_GPU_OVERLAP,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_multiprocessor_count,
+ CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_kernel_exec_timeout,
+ CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_integrated,
+ CU_DEVICE_ATTRIBUTE_INTEGRATED,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_can_map_host_memory,
+ CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+
+ if (SCCudaDeviceGetAttribute(&devices->devices[i]->attr_compute_mode,
+ CU_DEVICE_ATTRIBUTE_COMPUTE_MODE,
+ devices->devices[i]->device) == -1) {
+ goto error;
+ }
+ }
+
+#ifdef DEBUG
+ SCCudaPrintDeviceList(devices);
+#endif
+
+ return devices;
+
+ error:
+ SCCudaDeAllocSCCudaDevices(devices);
+ return NULL;
+}
+
+/**
+ * \brief Prints the information for all the devices for this CUDA platform,
+ * supplied inside the argument.
+ *
+ * \param devices Pointer to a SCCudaDevices instance that holds information on
+ * the devices.
+ */
+void SCCudaPrintDeviceList(SCCudaDevices *devices)
+{
+ int i = 0;
+
+ if (devices == NULL) {
+ SCLogError(SC_ERR_CUDA_ERROR, "CUDA environment not initialized. "
+ "Please initialized the CUDA environment by calling "
+ "SCCudaInitCudaEnvironment() before making any calls "
+ "to the CUDA API.");
+ return;
+ }
+
+ SCLogDebug("Printing device info for this CUDA context");
+ SCLogDebug("No of devices: %d", devices->count);
+
+ for (i = 0; i < devices->count; i++) {
+ SCLogDebug("Device ID: %d", devices->devices[i]->device);
+ SCLogDebug("Device Name: %s", devices->devices[i]->name);
+ SCLogDebug("Device Major Revision: %d", devices->devices[i]->major_rev);
+ SCLogDebug("Device Minor Revision: %d", devices->devices[i]->minor_rev);
+
+ /* Cudevprop */
+ SCLogDebug("Device Max Threads Per Block: %d",
+ devices->devices[i]->prop.maxThreadsPerBlock);
+ SCLogDebug("Device Max Threads Dim: [%d, %d, %d]",
+ devices->devices[i]->prop.maxThreadsDim[0],
+ devices->devices[i]->prop.maxThreadsDim[1],
+ devices->devices[i]->prop.maxThreadsDim[2]);
+ SCLogDebug("Device Max Grid Size: [%d, %d, %d]",
+ devices->devices[i]->prop.maxGridSize[0],
+ devices->devices[i]->prop.maxGridSize[1],
+ devices->devices[i]->prop.maxGridSize[2]);
+ SCLogDebug("Device Shared Memory Per Block: %d",
+ devices->devices[i]->prop.sharedMemPerBlock);
+ SCLogDebug("Device Total Constant Memory: %d",
+ devices->devices[i]->prop.totalConstantMemory);
+ SCLogDebug("Device SIMD Width(Warp Size): %d",
+ devices->devices[i]->prop.SIMDWidth);
+ SCLogDebug("Device Maximum Mem Pitch: %d", devices->devices[i]->prop.memPitch);
+ SCLogDebug("Device Total Registers Available Per Block: %d",
+ devices->devices[i]->prop.regsPerBlock);
+ SCLogDebug("Device Clock Frequency: %d", devices->devices[i]->prop.clockRate);
+ SCLogDebug("Device Texture Alignment Requirement: %d",
+ devices->devices[i]->prop.textureAlign);
+
+
+ /* device attributes */
+ SCLogDebug("Device Max Threads Per Block: %d",
+ devices->devices[i]->attr_max_threads_per_block);
+ SCLogDebug("Device Max Block Dim X: %d",
+ devices->devices[i]->attr_max_block_dim_x);
+ SCLogDebug("Device Max Block Dim Y: %d",
+ devices->devices[i]->attr_max_block_dim_y);
+ SCLogDebug("Device Max Block Dim Z: %d",
+ devices->devices[i]->attr_max_block_dim_z);
+ SCLogDebug("Device Max Grid Dim X: %d",
+ devices->devices[i]->attr_max_grid_dim_x);
+ SCLogDebug("Device Max Grid Dim Y: %d",
+ devices->devices[i]->attr_max_grid_dim_y);
+ SCLogDebug("Device Max Grid Dim Z: %d",
+ devices->devices[i]->attr_max_grid_dim_z);
+ SCLogDebug("Device Max Shared Memory Per Block: %d",
+ devices->devices[i]->attr_max_shared_memory_per_block);
+ SCLogDebug("Device Total Constant Memory: %d",
+ devices->devices[i]->attr_total_constant_memory);
+ SCLogDebug("Device Warp Size: %d", devices->devices[i]->attr_warp_size);
+ SCLogDebug("Device Max Pitch: %d", devices->devices[i]->attr_max_pitch);
+ SCLogDebug("Device Max Registers Per Block: %d",
+ devices->devices[i]->attr_max_registers_per_block);
+ SCLogDebug("Device Clock Rate: %d", devices->devices[i]->attr_clock_rate);
+ SCLogDebug("Device Texture Alignement: %d",
+ devices->devices[i]->attr_texture_alignment);
+ SCLogDebug("Device GPU Overlap: %s",
+ (devices->devices[i]->attr_gpu_overlap == 1) ? "Yes": "No");
+ SCLogDebug("Device Multiprocessor Count: %d",
+ devices->devices[i]->attr_multiprocessor_count);
+ SCLogDebug("Device Kernel Exec Timeout: %s",
+ (devices->devices[i]->attr_kernel_exec_timeout) ? "Yes": "No");
+ SCLogDebug("Device Integrated With Memory Subsystem: %s",
+ (devices->devices[i]->attr_integrated) ? "Yes": "No");
+ SCLogDebug("Device Can Map Host Memory: %s",
+ (devices->devices[i]->attr_can_map_host_memory) ? "Yes": "No");
+ if (devices->devices[i]->attr_compute_mode == CU_COMPUTEMODE_DEFAULT)
+ SCLogDebug("Device Compute Mode: CU_COMPUTEMODE_DEFAULT");
+ else if (devices->devices[i]->attr_compute_mode == CU_COMPUTEMODE_EXCLUSIVE)
+ SCLogDebug("Device Compute Mode: CU_COMPUTEMODE_EXCLUSIVE");
+ else if (devices->devices[i]->attr_compute_mode == CU_COMPUTEMODE_PROHIBITED)
+ SCLogDebug("Device Compute Mode: CU_COMPUTEMODE_PROHIBITED");
+ }
+
+ return;
+}
+
+/**
+ * \brief Prints some basic information for the default device(the first devie)
+ * we will be using on this cuda platform for use by our engine. This
+ * function is basically to be used to print some minimal information to
+ * the user at engine startup.
+ *
+ * \param devices Pointer to a SCCudaDevices instance that holds information on
+ * the devices.
+ */
+void SCCudaPrintBasicDeviceInfo(SCCudaDevices *devices)
+{
+ int i = 0;
+
+ if (devices == NULL) {
+ SCLogError(SC_ERR_CUDA_ERROR, "CUDA environment not initialized. "
+ "Please initialized the CUDA environment by calling "
+ "SCCudaInitCudaEnvironment() before making any calls "
+ "to the CUDA API.");
+ return;
+ }
+
+ for (i = 0; i < devices->count; i++) {
+ SCLogInfo("GPU Device %d: %s, %d Multiprocessors, %dMHz, CUDA Compute "
+ "Capability %d.%d", i + 1,
+ devices->devices[i]->name,
+ devices->devices[i]->attr_multiprocessor_count,
+ devices->devices[i]->attr_clock_rate/1000,
+ devices->devices[i]->major_rev,
+ devices->devices[i]->minor_rev);
+ }
+
+ return;
+}
+
+/**
+ * \brief Gets the device list, for the CUDA platform environment initialized by
+ * the engine.
+ *
+ * \retval devices Pointer to the CUDA device list on success; NULL on failure.
+ */
+SCCudaDevices *SCCudaGetDeviceList(void)
+{
+ if (devices == NULL) {
+ SCLogError(SC_ERR_CUDA_ERROR, "CUDA environment not initialized. "
+ "Please initialized the CUDA environment by calling "
+ "SCCudaInitCudaEnvironment() before making any calls "
+ "to the CUDA API.");
+ return NULL;
+ }
+
+ return devices;
+}
+
+/*****************************Context_Management_API***************************/
+
+/**
+ * \brief Creates a new CUDA context and associates it with the calling thread.
+ * The flags parameter is described below. The context is created with
+ * a usage count of 1 and the caller of cuCtxCreate() must call
+ * cuCtxDestroy() or cuCtxDetach() when done using the context. If a
+ * context is already current to the thread, it is supplanted by the
+ * newly created context and may be restored by a subsequent call to
+ * cuCtxPopCurrent(). The two LSBs of the flags parameter can be used
+ * to control how the OS thread, which owns the CUDA context at the
+ * time of an API call, interacts with the OS scheduler when waiting for
+ * results from the GPU.
+ *
+ * - CU_CTX_SCHED_AUTO: The default value if the flags parameter is zero,
+ * uses a heuristic based on the number of active CUDA contexts in
+ * the process C and the number of logical processors in the system
+ * P. If C > P, then CUDA will yield to other OS threads when
+ * waiting for the GPU, otherwise CUDA will not yield while waiting
+ * for results and actively spin on the processor.
+ * - CU_CTX_SCHED_SPIN: Instruct CUDA to actively spin when waiting for
+ * results from the GPU. This can de-crease latency when waiting for
+ * the GPU, but may lower the performance of CPU threads if they are
+ * performing work in parallel with the CUDA thread.
+ * - CU_CTX_SCHED_YIELD: Instruct CUDA to yield its thread when waiting
+ * for results from the GPU. This can increase latency when waiting
+ * for the GPU, but can increase the performance of CPU threads
+ * performing work in parallel with the GPU.
+ * - CU_CTX_BLOCKING_SYNC: Instruct CUDA to block the CPU thread on a
+ * synchronization primitive when waiting for the GPU to finish work.
+ * - CU_CTX_MAP_HOST: Instruct CUDA to support mapped pinned allocations.
+ * This flag must be set in order to allocate pinned host memory
+ * that is accessible to the GPU.
+ *
+ * Note to Linux users:
+ * Context creation will fail with CUDA_ERROR_UNKNOWN if the compute mode
+ * of the device is CU_COMPUTEMODE_PROHIBITED. Similarly, context creation
+ * will also fail with CUDA_ERROR_UNKNOWN if the compute mode for the
+ * device is set to CU_COMPUTEMODE_EXCLUSIVE and there is already an
+ * active context on the device. The function cuDeviceGetAttribute() can
+ * be used with CU_DEVICE_ATTRIBUTE_COMPUTE_MODE to determine the compute
+ * mode of the device. The nvidia-smi tool can be used to set the compute
+ * mode for devices. Documentation for nvidia-smi can be obtained by
+ * passing a -h option to it.
+ *
+ * \param pctx Returned context handle of the current context.
+ * \param flags Context creation flags.
+ * \param dev Device to create context on.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxCreate(CUcontext *pctx, unsigned int flags, CUdevice dev)
+{
+ CUresult result = 0;
+
+ if (pctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pctx NULL");
+ goto error;
+ }
+
+ result = cuCtxCreate(pctx, flags, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Destroys the CUDA context specified by ctx. If the context usage count
+ * is not equal to 1, or the context is current to any CPU thread other
+ * than the current one, this function fails. Floating contexts (detached
+ * from a CPU thread via cuCtxPopCurrent()) may be destroyed by this
+ * function.
+ *
+ * \param ctx Context to destroy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxDestroy(CUcontext ctx)
+{
+ CUresult result = 0;
+
+ result = cuCtxDestroy(ctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_DESTROY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxGetApiVersion(CUcontext ctx, unsigned int *version)
+{
+ CUresult result = 0;
+
+ if (version == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "version NULL");
+ goto error;
+ }
+
+ result = cuCtxGetApiVersion(ctx, version);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_GET_API_VERSION) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxGetCacheConfig(CUfunc_cache *pconfig)
+{
+ CUresult result = 0;
+
+ if (pconfig == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pconfig NULL");
+ goto error;
+ }
+
+ result = cuCtxGetCacheConfig(pconfig);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_GET_CACHE_CONFIG) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxGetCurrent(CUcontext *pctx)
+{
+ CUresult result = 0;
+
+ if (pctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pctx NULL");
+ goto error;
+ }
+
+ result = cuCtxGetCurrent(pctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_GET_CURRENT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *device the ordinal of the current context's device.
+ *
+ * \param device Returned device id for the current context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxGetDevice(CUdevice *device)
+{
+ CUresult result = 0;
+
+ if (device == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "device NULL");
+ goto error;
+ }
+
+ result = cuCtxGetDevice(device);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_GET_DEVICE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxGetLimit(size_t *pvalue, CUlimit limit)
+{
+ CUresult result = 0;
+
+ result = cuCtxGetLimit(pvalue, limit);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_GET_LIMIT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Pops the current CUDA context from the CPU thread. The CUDA context
+ * must have a usage count of 1. CUDA contexts have a usage count of 1
+ * upon creation; the usage count may be incremented with cuCtxAttach()
+ * and decremented with cuCtxDetach().
+ *
+ * If successful, cuCtxPopCurrent() passes back the new context handle
+ * in *pctx. The old context may then be made current to a different CPU
+ * thread by calling cuCtxPushCurrent().
+ *
+ * Floating contexts may be destroyed by calling cuCtxDestroy().
+ *
+ * If a context was current to the CPU thread before cuCtxCreate() or
+ * cuCtxPushCurrent() was called, this function makes that context
+ * current to the CPU thread again.
+ *
+ * \param pctx Returned new context handle.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxPopCurrent(CUcontext *pctx)
+{
+ CUresult result = 0;
+
+ result = cuCtxPopCurrent(pctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_POP_CURRENT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Pushes the given context ctx onto the CPU thread's stack of current
+ * contexts. The speci?ed context becomes the CPU thread's current
+ * context, so all CUDA functions that operate on the current context
+ * are affected.
+ *
+ * The previous current context may be made current again by calling
+ * cuCtxDestroy() or cuCtxPopCurrent().
+ *
+ * The context must be "floating," i.e. not attached to any thread.
+ * Contexts are made to float by calling cuCtxPopCurrent().
+ *
+ * \param ctx Floating context to attach.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxPushCurrent(CUcontext ctx)
+{
+ CUresult result = 0;
+
+ result = cuCtxPushCurrent(ctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_PUSH_CURRENT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxSetCacheConfig(CUfunc_cache config)
+{
+ CUresult result = 0;
+
+ result = cuCtxSetCacheConfig(config);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_SET_CACHE_CONFIG) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxSetCurrent(CUcontext ctx)
+{
+ CUresult result = 0;
+
+ result = cuCtxSetCurrent(ctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_SET_CURRENT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaCtxSetLimit(CUlimit limit, size_t value)
+{
+ CUresult result = 0;
+
+ result = cuCtxSetLimit(value, limit);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_SET_LIMIT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Blocks until the device has completed all preceding requested tasks.
+ * cuCtxSynchronize() returns an error if one of the preceding tasks failed.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxSynchronize(void)
+{
+ CUresult result = 0;
+
+ result = cuCtxSynchronize();
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_SYNCHRONIZE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Increments the usage count of the context and passes back a context
+ * handle in *pctx that must be passed to cuCtxDetach() when the
+ * application is done with the context. cuCtxAttach() fails if there is
+ * no context current to the thread. Currently, the flags parameter must
+ * be 0.
+ *
+ * \param pctx Returned context handle of the current context.
+ * \param flags Context attach flags (must be 0).
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxAttach(CUcontext *pctx, unsigned int flags)
+{
+ CUresult result = 0;
+
+ SCLogInfo("Cuda API - %s deprecated",
+ SCMapEnumValueToName(SC_CUDA_CU_CTX_ATTACH,
+ sc_cuda_api_names_string_map));
+
+ if (pctx == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pctx NULL");
+ goto error;
+ }
+
+ result = cuCtxAttach(pctx, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_ATTACH) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Decrements the usage count of the context ctx, and destroys the
+ * context if the usage count goes to 0. The context must be a handle
+ * that was passed back by cuCtxCreate() or cuCtxAttach(), and must be
+ * current to the calling thread.
+ *
+ * \param ctx Context to destroy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaCtxDetach(CUcontext ctx)
+{
+ CUresult result = 0;
+
+ SCLogInfo("Cuda API - %s deprecated",
+ SCMapEnumValueToName(SC_CUDA_CU_CTX_DETACH,
+ sc_cuda_api_names_string_map));
+
+ result = cuCtxDetach(ctx);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_CTX_DETACH) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/*****************************Module_Management_API****************************/
+
+/**
+ * \brief Returns in *hfunc the handle of the function of name \"name\" located
+ * in module hmod. If no function of that name exists,
+ * cuModuleGetFunction() returns CUDA_ERROR_NOT_FOUND.
+ *
+ * \param hfunc Returned function handle.
+ * \param hmod Module to return function from.
+ * \param name Name of function to retrieve.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleGetFunction(CUfunction *hfunc, CUmodule hmod, const char *name)
+{
+ CUresult result = 0;
+
+ if (hfunc == NULL || name == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "hfunc is NULL or name is NULL");
+ goto error;
+ }
+
+ result = cuModuleGetFunction(hfunc, hmod, name);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_GET_FUNCTION) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *dptr and *bytes the base pointer and size of the global
+ * name \"name\" located in module hmod. If no variable of that name
+ * exists, cuModuleGetGlobal() returns CUDA_ERROR_NOT_FOUND. Both
+ * parameters dptr and bytes are optional. If one of them is NULL,
+ * it is ignored.
+ *
+ * \param dptr Returned global device pointer.
+ * \param bytes Returned global size in bytes.
+ * \param hmod Module to return function from.
+ * \param name Name of global to retrieve.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleGetGlobal(CUdeviceptr *dptr, size_t *bytes, CUmodule hmod,
+ const char *name)
+{
+ CUresult result = 0;
+
+ if (name == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "name is NULL");
+ goto error;
+ }
+
+ result = cuModuleGetGlobal(dptr, bytes, hmod, name);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_GET_GLOBAL) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaModuleGetSurfRef(CUsurfref *p_surf_ref, CUmodule hmod, const char *name)
+{
+ CUresult result = 0;
+
+ if (p_surf_ref == NULL || name == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_surf_ref is NULL or name is NULL");
+ goto error;
+ }
+
+ result = cuModuleGetSurfRef(p_surf_ref, hmod, name);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_GET_SURF_REF) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *p_tex_ref the handle of the texture reference of name
+ * \"name\" in the module hmod. If no texture reference of that name
+ * exists, cuModuleGetTexRef() returns CUDA_ERROR_NOT_FOUND. This texture
+ * reference handle should not be destroyed, since it will be destroyed
+ * when the module is unloaded.
+ *
+ * \param p_tex_ref Returned global device pointer.
+ * \param hmod Module to retrieve texture reference from.
+ * \param name Name of the texture reference to retrieve.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleGetTexRef(CUtexref *p_tex_ref, CUmodule hmod, const char *name)
+{
+ CUresult result = 0;
+
+ if (p_tex_ref == NULL || name == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_tex_ref is NULL or name is NULL");
+ goto error;
+ }
+
+ result = cuModuleGetTexRef(p_tex_ref, hmod, name);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_GET_TEX_REF) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Takes a filename fname and loads the corresponding module \"module\"
+ * into the current context. The CUDA driver API does not attempt to
+ * lazily allocate the resources needed by a module; if the memory for
+ * functions and data (constant and global) needed by the module cannot
+ * be allocated, cuModuleLoad() fails. The file should be a cubin file
+ * as output by nvcc or a PTX file, either as output by nvcc or handwrtten.
+ *
+ * \param module Returned module.
+ * \param fname Filename of module to load.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleLoad(CUmodule *module, const char *fname)
+{
+ CUresult result = 0;
+
+ if (module == NULL || fname == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "module is NULL or fname is NULL");
+ goto error;
+ }
+
+ result = cuModuleLoad(module, fname);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_LOAD) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Takes a pointer image and loads the corresponding module \"module\"
+ * into the current context. The pointer may be obtained by mapping a
+ * cubin or PTX file, passing a cubin or PTX ?le as a NULL-terminated
+ * text string, or incorporating a cubin object into the executable
+ * resources and using operating system calls such as Windows
+ * FindResource() to obtain the pointer.
+ *
+ * \param module Returned module.
+ * \param image Module data to load
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleLoadData(CUmodule *module, const void *image)
+{
+ CUresult result = 0;
+
+ if (module == NULL || image == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "module is NULL or image is NULL");
+ goto error;
+ }
+
+ result = cuModuleLoadData(module, image);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_LOAD_DATA) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Takes a pointer image and loads the corresponding module module into
+ * the current context. The pointer may be obtained by mapping a cubin or
+ * PTX file, passing a cubin or PTX file as a NULL-terminated text
+ * string, or incorporating a cubin object into the executable resources
+ * and using operating system calls such as Windows FindResource() to
+ * obtain the pointer. Options are passed as an array via options and any
+ * corresponding parameters are passed in optionValues. The number of
+ * total options is supplied via numOptions. Any outputs will be returned
+ * via optionValues. Supported options are:
+ *
+ * - CU_JIT_MAX_REGISTERS: input specifies the maximum number of registers
+ * per thread;
+ * - CU_JIT_THREADS_PER_BLOCK: input specifies number of threads per block
+ * to target compilation for; output returns the number of threads
+ * the compiler actually targeted;
+ * - CU_JIT_WALL_TIME: output returns the float value of wall clock time,
+ * in milliseconds, spent compiling the PTX code;
+ * - CU_JIT_INFO_LOG_BUFFER: input is a pointer to a buffer in which to
+ * print any informational log messages from PTX assembly;
+ * - CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES: input is the size in bytes of the
+ * buffer; output is the number of bytes filled with messages;
+ * - CU_JIT_ERROR_LOG_BUFFER: input is a pointer to a buffer in which to
+ * print any error log messages from PTX assembly;
+ * - CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES: input is the size in bytes of the
+ * buffer; output is the number of bytes filled with messages;
+ * - CU_JIT_OPTIMIZATION_LEVEL: input is the level of optimization to apply
+ * to generated code (0 - 4), with 4 being the default and highest
+ * level;
+ * - CU_JIT_TARGET_FROM_CUCONTEXT: causes compilation target to be
+ * determined based on current attached context (default);
+ * - CU_JIT_TARGET: input is the compilation target based on supplied
+ * CUjit_target_enum; possible values are:
+ * -- CU_TARGET_COMPUTE_10
+ * -- CU_TARGET_COMPUTE_11
+ * -- CU_TARGET_COMPUTE_12
+ * -- CU_TARGET_COMPUTE_13
+ *
+ * \param module Returned module.
+ * \param image Module data to load.
+ * \param numOptions Number of options.
+ * \param options Options for JIT.
+ * \param optionValues Option values for JIT.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleLoadDataEx(CUmodule *module, const void *image,
+ unsigned int num_options, CUjit_option *options,
+ void **option_values)
+{
+ CUresult result = 0;
+
+ if (module == NULL || image == NULL || options == NULL ||
+ option_values == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "module is NULL or image is NULL or options is NULL or "
+ "option_values is NULL");
+ goto error;
+ }
+
+ result = cuModuleLoadDataEx(module, image, num_options, options, option_values);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_LOAD_DATA_EX) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Takes a pointer fat_cubin and loads the corresponding module \"module\"
+ * into the current context. The pointer represents a fat binary object,
+ * which is a collection of different cubin files, all representing the
+ * same device code, but compiled and optimized for different
+ * architectures. There is currently no documented API for constructing
+ * and using fat binary objects by programmers, and therefore this
+ * function is an internal function in this version of CUDA. More
+ * information can be found in the nvcc document.
+ *
+ * \param module Returned module.
+ * \param fatCubin Fat binary to load.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleLoadFatBinary(CUmodule *module, const void *fat_cubin)
+{
+ CUresult result = 0;
+
+ if (module == NULL || fat_cubin == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "module is NULL or fatCubin is NULL");
+ goto error;
+ }
+
+ result = cuModuleLoadFatBinary(module, fat_cubin);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_LOAD_FAT_BINARY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Unloads a module hmod from the current context.
+ *
+ * \param module Module to unload
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaModuleUnload(CUmodule hmod)
+{
+ CUresult result = 0;
+
+ result = cuModuleUnload(hmod);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MODULE_UNLOAD) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/****************************Memory_Management_API*****************************/
+
+/**
+ * \brief Creates a CUDA array according to the CUDA_ARRAY3D_DESCRIPTOR
+ * structure pAllocateArray and returns a handle to the new CUDA
+ * array in *p_handle. The CUDA_ARRAY3D_DESCRIPTOR is defined as:
+ *
+ * typedef struct {
+ * unsigned int Width;
+ * unsigned int Height;
+ * unsigned int Depth;
+ * CUarray_format Format;
+ * unsigned int NumChannels;
+ * unsigned int Flags;
+ * } CUDA_ARRAY3D_DESCRIPTOR;
+ *
+ * where:
+ *
+ * - Width, Height, and Depth are the width, height, and depth of the
+ * CUDA array (in elements); the CUDA array is one-dimensional if
+v * height and depth are 0, two-dimensional if depth is 0, and
+ * three-dimensional otherwise;
+ * - Format speci?es the format of the elements; CUarray_format is
+ * defined as:
+ *
+ * typedef enum CUarray_format_enum {
+ * CU_AD_FORMAT_UNSIGNED_INT8 = 0x01,
+ * CU_AD_FORMAT_UNSIGNED_INT16 = 0x02,
+ * CU_AD_FORMAT_UNSIGNED_INT32 = 0x03,
+ * CU_AD_FORMAT_SIGNED_INT8 = 0x08,
+ * CU_AD_FORMAT_SIGNED_INT16 = 0x09,
+ * CU_AD_FORMAT_SIGNED_INT32 = 0x0a,
+ * CU_AD_FORMAT_HALF = 0x10,
+ * CU_AD_FORMAT_FLOAT = 0x20
+ * } CUarray_format;
+ *
+ * - NumChannels speci?es the number of packed components per CUDA array
+ * element; it may be 1, 2, or 4;
+ * - Flags provides for future features. For now, it must be set to 0.
+ *
+ * Here are examples of CUDA array descriptions:
+ *
+ * Description for a CUDA array of 2048 floats:
+ *
+ * CUDA_ARRAY3D_DESCRIPTOR desc;
+ * desc.Format = CU_AD_FORMAT_FLOAT;
+ * desc.NumChannels = 1;
+ * desc.Width = 2048;
+ * desc.Height = 0;
+ * desc.Depth = 0;
+ *
+ * Description for a 64 x 64 CUDA array of floats:
+ *
+ * CUDA_ARRAY3D_DESCRIPTOR desc;
+ * desc.Format = CU_AD_FORMAT_FLOAT;
+ * desc.NumChannels = 1;
+ * desc.Width = 64;
+ * desc.Height = 64;
+ * desc.Depth = 0;
+ *
+ * Description for a width x height x depth CUDA array of 64-bit,
+ * 4x16-bit float16's:
+ *
+ * CUDA_ARRAY3D_DESCRIPTOR desc;
+ * desc.FormatFlags = CU_AD_FORMAT_HALF;
+ * desc.NumChannels = 4;
+ * desc.Width = width;
+ * desc.Height = height;
+ * desc.Depth = depth;
+ *
+ * \param p_handle Returned Handle.
+ * \param p_allocate_array 3D array descriptor.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaArray3DCreate(CUarray *p_handle,
+ const CUDA_ARRAY3D_DESCRIPTOR *p_allocate_array)
+{
+ CUresult result = 0;
+
+ if (p_handle == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_handle is NULL");
+ goto error;
+ }
+
+ result = cuArray3DCreate(p_handle, p_allocate_array);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_ARRAY_3D_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *p_rray_descriptor a descriptor containing information on
+ * the format and dimensions of the CUDA array h_array. It is useful for
+ * subroutines that have been passed a CUDA array, but need to know the
+ * CUDA array parameters for validation or other purposes.
+ *
+ * This function may be called on 1D and 2D arrays, in which case the
+ * Height and/or Depth members of the descriptor struct will be set to 0.
+ *
+ * \param p_array_descriptor Returned 3D array descriptor.
+ * \param h_array 3D array to get descriptor of.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaArray3DGetDescriptor(CUDA_ARRAY3D_DESCRIPTOR *p_array_descriptor,
+ CUarray h_array)
+{
+ CUresult result = 0;
+
+ if (p_array_descriptor == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_array_descriptor is NULL");
+ goto error;
+ }
+
+ result = cuArray3DGetDescriptor(p_array_descriptor, h_array);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_ARRAY_3D_GET_DESCRIPTOR) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Creates a CUDA array according to the CUDA_ARRAY_DESCRIPTOR structure
+ * p_allocate_array and returns a handle to the new CUDA array in
+ * p_handle. The CUDA_ARRAY_DESCRIPTOR is defined as:
+ *
+ * typedef struct {
+ * unsigned int Width;
+ * unsigned int Height;
+ * CUarray_format Format;
+ * unsigned int NumChannels;
+ * } CUDA_ARRAY_DESCRIPTOR;
+ *
+ * where:
+ *
+ * - Width, and Height are the width, and height of the CUDA array
+ * (in elements); the CUDA array is one-dimensional if height is 0,
+ * two-dimensional otherwise;
+ * - Format speci?es the format of the elements; CUarray_format is
+ * defined as:
+ *
+ * typedef enum CUarray_format_enum {
+ * CU_AD_FORMAT_UNSIGNED_INT8 = 0x01,
+ * CU_AD_FORMAT_UNSIGNED_INT16 = 0x02,
+ * CU_AD_FORMAT_UNSIGNED_INT32 = 0x03,
+ * CU_AD_FORMAT_SIGNED_INT8 = 0x08,
+ * CU_AD_FORMAT_SIGNED_INT16 = 0x09,
+ * CU_AD_FORMAT_SIGNED_INT32 = 0x0a,
+ * CU_AD_FORMAT_HALF = 0x10,
+ * CU_AD_FORMAT_FLOAT = 0x20
+ * } CUarray_format;
+ *
+ * - NumChannels specifies the number of packed components per CUDA
+ * array element; it may be 1, 2, or 4;
+ *
+ * Here are examples of CUDA array descriptions:
+ *
+ * Description for a CUDA array of 2048 floats:
+ *
+ * CUDA_ARRAY_DESCRIPTOR desc;
+ * desc.Format = CU_AD_FORMAT_FLOAT;
+ * desc.NumChannels = 1;
+ * desc.Width = 2048;
+ * desc.Height = 1;
+ *
+ * Description for a 64 x 64 CUDA array of floats:
+ *
+ * CUDA_ARRAY_DESCRIPTOR desc;
+ * desc.Format = CU_AD_FORMAT_FLOAT;
+ * desc.NumChannels = 1;
+ * desc.Width = 64;
+ * desc.Height = 64;
+ *
+ * Description for a width x height CUDA array of 64-bit, 4x16-bit
+ * float16's:
+ *
+ * CUDA_ARRAY_DESCRIPTOR desc;
+ * desc.FormatFlags = CU_AD_FORMAT_HALF;
+ * desc.NumChannels = 4;
+ * desc.Width = width;
+ * desc.Height = height;
+ *
+ * Description for a width x height CUDA array of 16-bit elements, each
+ * of which is two 8-bit unsigned chars:
+ *
+ * CUDA_ARRAY_DESCRIPTOR arrayDesc;
+ * desc.FormatFlags = CU_AD_FORMAT_UNSIGNED_INT8;
+ * desc.NumChannels = 2;
+ * desc.Width = width;
+ * desc.Height = height;
+ *
+ * \param p_handle Returned array.
+ * \param p_allocate_array Array descriptor.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaArrayCreate(CUarray *p_handle,
+ const CUDA_ARRAY_DESCRIPTOR *p_allocate_array)
+{
+ CUresult result = 0;
+
+ if (p_handle == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_handle is NULL");
+ goto error;
+ }
+
+ result = cuArrayCreate(p_handle, p_allocate_array);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_ARRAY_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+/**
+ * \brief Destroys the CUDA array h_array.
+ *
+ * \param h_array Array to destroy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaArrayDestroy(CUarray h_array)
+{
+ int result = cuArrayDestroy(h_array);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_ARRAY_DESTROY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *p_array_descriptor a descriptor containing information on
+ * the format and dimensions of the CUDA array h_array. It is useful for
+ * subroutines that have been passed a CUDA array, but need to know the
+ * CUDA array parameters for validation or other purposes.
+ *
+ * \param p_array_descriptor Returned array descriptor.
+ * \param h_array Array to get descriptor of.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaArrayGetDescriptor(CUDA_ARRAY_DESCRIPTOR *p_array_descriptor,
+ CUarray h_array)
+{
+ CUresult result = 0;
+
+ if (p_array_descriptor == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_array_descriptor is NULL");
+ goto error;
+ }
+
+ result = cuArrayGetDescriptor(p_array_descriptor, h_array);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_ARRAY_GET_DESCRIPTOR) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaDeviceGetByPCIBusId(CUdevice *dev, char *pci_bus_id)
+{
+ CUresult result = 0;
+
+ result = cuDeviceGetByPCIBusId(dev, pci_bus_id);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_BY_PCI_BUS_ID) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaDeviceGetPCIBusId(char *pci_bus_id, int len, CUdevice dev)
+{
+ CUresult result = 0;
+
+ result = cuDeviceGetPCIBusId(pci_bus_id, len, dev);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_DEVICE_GET_PCI_BUS_ID) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaIpcCloseMemHandle(CUdeviceptr dptr)
+{
+ CUresult result = 0;
+
+ result = cuIpcCloseMemHandle(dptr);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_IPC_CLOSE_MEM_HANDLE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaIpcGetEventHandle(CUipcEventHandle *p_handle, CUevent event)
+{
+ CUresult result = 0;
+
+ result = cuIpcGetEventHandle(p_handle, event);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_IPC_GET_MEM_HANDLE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaIpcGetMemHandle(CUipcMemHandle *p_handle, CUdeviceptr dptr)
+{
+ CUresult result = 0;
+
+ result = cuIpcGetMemHandle(p_handle, dptr);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_IPC_GET_MEM_HANDLE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaIpcOpenEventHandle(CUevent *ph_event, CUipcEventHandle handle)
+{
+ CUresult result = 0;
+
+ result = cuIpcOpenEventHandle(ph_event, handle);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_IPC_GET_MEM_HANDLE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaIpcOpenMemHandle(CUdeviceptr *pdptr, CUipcMemHandle handle,
+ unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuIpcOpenMemHandle(pdptr, handle, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_IPC_OPEN_EVENT_HANDLE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *p_array_descriptor a descriptor containing information on
+ * the format and dimensions of the CUDA array h_array. It is useful for
+ * subroutines that have been passed a CUDA array, but need to know the
+ * CUDA array parameters for validation or other purposes.
+ *
+ * \param p_array_descriptor Returned array descriptor.
+ * \param h_array Array to get descriptor of.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemAlloc(CUdeviceptr *dptr, size_t byte_size)
+{
+ CUresult result = 0;
+
+ if (dptr == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "dptr is NULL");
+ goto error;
+ }
+
+ result = cuMemAlloc(dptr, byte_size);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_ALLOC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Allocates bytesize bytes of host memory that is page-locked and
+ * accessible to the device. The driver tracks the vir-tual memory
+ * ranges allocated with this function and automatically accelerates
+ * calls to functions such as cuMemcpy(). Since the memory can be
+ * accessed directly by the device, it can be read or written with
+ * much higher bandwidth than pageable memory obtained with functions
+ * such as SCMalloc(). Allocating excessive amounts of memory with
+ * cuMemAllocHost() may degrade system performance, since it reduces
+ * the amount of memory available to the system for paging. As a result,
+ * this function is best used sparingly to allocate staging areas for
+ * data exchange between host and device.
+ *
+ * \param pp Returned host pointer to page-locked memory.
+ * \param byte_size Requested allocation size in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemAllocHost(void **pp, size_t byte_size)
+{
+ CUresult result = 0;
+
+ if (pp == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pp is NULL");
+ goto error;
+ }
+
+ result = cuMemAllocHost(pp, byte_size);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_ALLOC_HOST) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Allocates at least width_in_bytes * height bytes of linear memory on the
+ * device and returns in *dptr a pointer to the allocated memory. The
+ * function may pad the allocation to ensure that corresponding pointers in
+ * any given row will continue to meet the alignment requirements for
+ * coalescing as the address is updated from row to row. ElementSizeBytes
+ * specifies the size of the largest reads and writes that will be
+ * performed on the memory range.
+ *
+ * element_size_bytes may be 4, 8 or 16 (since coalesced memory
+ * transactions are not possible on other data sizes). If element_size_bytes
+ * is smaller than the actual read/write size of a kernel, the kernel will
+ * run correctly, but possibly at reduced speed. The pitch returned in
+ * *p_itch by cuMemAllocPitch() is the width in bytes of the allocation.
+ * The intended usage of pitch is as a separate parameter of the allocation,
+ * used to compute addresses within the 2D array. Given the row and column
+ * of an array element of type T, the address is computed as:
+ *
+ * T * p_element = (T*)((char*)base_address + row * pitch) + column;
+ *
+ * The pitch returned by cuMemAllocPitch() is guaranteed to work with
+ * cuMemcpy2D() under all circumstances. For allocations of 2D arrays, it
+ * is recommended that programmers consider performing pitch allocations
+ * using cuMemAllocPitch(). Due to alignment restrictions in the hardware,
+ * this is especially true if the application will be performing 2D memory
+ * copies between different regions of device memory (whether linear memory
+ * or CUDA arrays).
+ *
+ * \param dptr Returned device pointer.
+ * \param p_pitch Returned pitch of allocation in bytes.
+ * \param width_in_bytes Requested allocation width in bytes.
+ * \param height Requested allocation width in rows.
+ * \param element_size_bytes Size of largest reads/writes for range.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemAllocPitch(CUdeviceptr *dptr, size_t *p_pitch,
+ size_t width_in_bytes,
+ size_t height,
+ unsigned int element_size_bytes)
+{
+ CUresult result = 0;
+
+ if (dptr == NULL || p_pitch == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "dptr is NULL or p_pitch is NULL");
+ goto error;
+ }
+
+ result = cuMemAllocPitch(dptr, p_pitch, width_in_bytes, height,
+ element_size_bytes);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_ALLOC_PITCH) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemcpy(CUdeviceptr dst, CUdeviceptr src, size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpy(dst, src, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+
+/**
+ * \brief Perform a 2D memory copy according to the parameters specified in
+ * p_copy. The CUDA_MEMCPY2D structure is defined as:
+ *
+ * typedef struct CUDA_MEMCPY2D_st {
+ * unsigned int srcXInBytes, srcY;
+ * CUmemorytype srcMemoryType;
+ * const void *srcHost;
+ * CUdeviceptr srcDevice;
+ * CUarray srcArray;
+ * unsigned int srcPitch;
+ * unsigned int dstXInBytes, dstY;
+ * CUmemorytype dstMemoryType;
+ * void *dstHost;
+ * CUdeviceptr dstDevice;
+ * CUarray dstArray;
+ * unsigned int dstPitch;
+ * unsigned int WidthInBytes;
+ * unsigned int Height;
+ * } CUDA_MEMCPY2D;
+ *
+ * where:
+ *
+ * - srcMemoryType and dstMemoryType specify the type of memory of the
+ * source and destination, respectively;
+ *
+ * CUmemorytype_enum is de?ned as:
+ *
+ * typedef enum CUmemorytype_enum {
+ * CU_MEMORYTYPE_HOST = 0x01,
+ * CU_MEMORYTYPE_DEVICE = 0x02,
+ * CU_MEMORYTYPE_ARRAY = 0x03
+ * } CUmemorytype;
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_HOST, srcHost and srcPitch specify
+ * the (host) base address of the source data and the bytes per row to
+ * apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_DEVICE, srcDevice and srcPitch
+ * specify the (device) base address of the source data and the bytes per
+ * row to apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_ARRAY, srcArray speci?es the handle
+ * of the source data. srcHost, srcDevice and srcPitch are ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_HOST, dstHost and dstPitch specify
+ * the (host) base address of the destination data and the bytes per row
+ * to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_DEVICE, dstDevice and dstPitch
+ * specify the (device) base address of the destination data and the
+ * bytes per row to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_ARRAY, dstArray specifies the handle
+ * of the destination data dstHost, dstDevice and dstPitch are ignored.
+ *
+ * - srcXInBytes and srcY specify the base address of the source data for
+ * the copy.
+ *
+ * For host pointers, the starting address is
+ *
+ * void* Start = (void*)((char*)srcHost+srcY*srcPitch + srcXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr Start = srcDevice+srcY*srcPitch+srcXInBytes;
+ *
+ * For CUDA arrays, srcXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - dstXInBytes and dstY specify the base address of the destination data
+ * for the copy.
+ *
+ * For host pointers, the base address is
+ *
+ * void* dstStart = (void*)((char*)dstHost+dstY*dstPitch + dstXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr dstStart = dstDevice+dstY*dstPitch+dstXInBytes;
+ *
+ * For CUDA arrays, dstXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - WidthInBytes and Height specify the width (in bytes) and height of
+ * the 2D copy being performed. Any pitches must be greater than or
+ * equal to WidthInBytes.
+ *
+ * cuMemcpy2D() returns an error if any pitch is greater than the
+ * maximum allowed (CU_DEVICE_ATTRIBUTE_MAX_PITCH). cuMemAllocPitch()
+ * passes back pitches that always work with cuMemcpy2D(). On intra-device
+ * memory copies (device ? device, CUDA array ? device, CUDA array ?
+ * CUDA array), cuMemcpy2D() may fail for pitches not computed by
+ * cuMemAllocPitch(). cuMemcpy2DUnaligned() does not have this restriction,
+ * but may run signi?cantly slower in the cases where cuMemcpy2D() would
+ * have returned an error code.
+ *
+ * \param p_copy Parameters for the memory copy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpy2D(const CUDA_MEMCPY2D *p_copy)
+{
+ CUresult result = 0;
+
+ if (p_copy == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_copy is NULL");
+ goto error;
+ }
+
+ result = cuMemcpy2D(p_copy);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_2D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Perform a 2D memory copy according to the parameters specified in
+ * p_copy. The CUDA_MEMCPY2D structure is defined as:
+ *
+ * typedef struct CUDA_MEMCPY2D_st {
+ * unsigned int srcXInBytes, srcY;
+ * CUmemorytype srcMemoryType;
+ * const void *srcHost;
+ * CUdeviceptr srcDevice;
+ * CUarray srcArray;
+ * unsigned int srcPitch;
+ * unsigned int dstXInBytes, dstY;
+ * CUmemorytype dstMemoryType;
+ * void *dstHost;
+ * CUdeviceptr dstDevice;
+ * CUarray dstArray;
+ * unsigned int dstPitch;
+ * unsigned int WidthInBytes;
+ * unsigned int Height;
+ * } CUDA_MEMCPY2D;
+ *
+ * where:
+ *
+ * - srcMemoryType and dstMemoryType specify the type of memory of the
+ * source and destination, respectively;
+ *
+ * CUmemorytype_enum is de?ned as:
+ *
+ * typedef enum CUmemorytype_enum {
+ * CU_MEMORYTYPE_HOST = 0x01,
+ * CU_MEMORYTYPE_DEVICE = 0x02,
+ * CU_MEMORYTYPE_ARRAY = 0x03
+ * } CUmemorytype;
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_HOST, srcHost and srcPitch specify
+ * the (host) base address of the source data and the bytes per row to
+ * apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_DEVICE, srcDevice and srcPitch
+ * specify the (device) base address of the source data and the bytes per
+ * row to apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_ARRAY, srcArray speci?es the handle
+ * of the source data. srcHost, srcDevice and srcPitch are ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_HOST, dstHost and dstPitch specify
+ * the (host) base address of the destination data and the bytes per row
+ * to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_DEVICE, dstDevice and dstPitch
+ * specify the (device) base address of the destination data and the
+ * bytes per row to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_ARRAY, dstArray specifies the handle
+ * of the destination data dstHost, dstDevice and dstPitch are ignored.
+ *
+ * - srcXInBytes and srcY specify the base address of the source data for
+ * the copy.
+ *
+ * For host pointers, the starting address is
+ *
+ * void* Start = (void*)((char*)srcHost+srcY*srcPitch + srcXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr Start = srcDevice+srcY*srcPitch+srcXInBytes;
+ *
+ * For CUDA arrays, srcXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - dstXInBytes and dstY specify the base address of the destination data
+ * for the copy.
+ *
+ * For host pointers, the base address is
+ *
+ * void* dstStart = (void*)((char*)dstHost+dstY*dstPitch + dstXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr dstStart = dstDevice+dstY*dstPitch+dstXInBytes;
+ *
+ * For CUDA arrays, dstXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - WidthInBytes and Height specify the width (in bytes) and height of
+ * the 2D copy being performed. Any pitches must be greater than or
+ * equal to WidthInBytes.
+ *
+ * cuMemcpy2D() returns an error if any pitch is greater than the
+ * maximum allowed (CU_DEVICE_ATTRIBUTE_MAX_PITCH). cuMemAllocPitch()
+ * passes back pitches that always work with cuMemcpy2D(). On intra-device
+ * memory copies (device ? device, CUDA array ? device, CUDA array ?
+ * CUDA array), cuMemcpy2D() may fail for pitches not computed by
+ * cuMemAllocPitch(). cuMemcpy2DUnaligned() does not have this restriction,
+ * but may run signi?cantly slower in the cases where cuMemcpy2D() would
+ * have returned an error code.
+ *
+ * cuMemcpy2DAsync() is asynchronous and can optionally be associated to a
+ * stream by passing a non-zero hStream argument. It only works on
+ * page-locked host memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * \param p_copy Parameters for the memory copy.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpy2DAsync(const CUDA_MEMCPY2D *p_copy, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ if (p_copy == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_copy is NULL");
+ goto error;
+ }
+
+ result = cuMemcpy2DAsync(p_copy, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_2D_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Perform a 2D memory copy according to the parameters specified in
+ * p_copy. The CUDA_MEMCPY2D structure is defined as:
+ *
+ * typedef struct CUDA_MEMCPY2D_st {
+ * unsigned int srcXInBytes, srcY;
+ * CUmemorytype srcMemoryType;
+ * const void *srcHost;
+ * CUdeviceptr srcDevice;
+ * CUarray srcArray;
+ * unsigned int srcPitch;
+ * unsigned int dstXInBytes, dstY;
+ * CUmemorytype dstMemoryType;
+ * void *dstHost;
+ * CUdeviceptr dstDevice;
+ * CUarray dstArray;
+ * unsigned int dstPitch;
+ * unsigned int WidthInBytes;
+ * unsigned int Height;
+ * } CUDA_MEMCPY2D;
+ *
+ * where:
+ *
+ * - srcMemoryType and dstMemoryType specify the type of memory of the
+ * source and destination, respectively;
+ *
+ * CUmemorytype_enum is de?ned as:
+ *
+ * typedef enum CUmemorytype_enum {
+ * CU_MEMORYTYPE_HOST = 0x01,
+ * CU_MEMORYTYPE_DEVICE = 0x02,
+ * CU_MEMORYTYPE_ARRAY = 0x03
+ * } CUmemorytype;
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_HOST, srcHost and srcPitch specify
+ * the (host) base address of the source data and the bytes per row to
+ * apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_DEVICE, srcDevice and srcPitch
+ * specify the (device) base address of the source data and the bytes per
+ * row to apply. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_ARRAY, srcArray speci?es the handle
+ * of the source data. srcHost, srcDevice and srcPitch are ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_HOST, dstHost and dstPitch specify
+ * the (host) base address of the destination data and the bytes per row
+ * to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_DEVICE, dstDevice and dstPitch
+ * specify the (device) base address of the destination data and the
+ * bytes per row to apply. dstArray is ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_ARRAY, dstArray specifies the handle
+ * of the destination data dstHost, dstDevice and dstPitch are ignored.
+ *
+ * - srcXInBytes and srcY specify the base address of the source data for
+ * the copy.
+ *
+ * For host pointers, the starting address is
+ *
+ * void* Start = (void*)((char*)srcHost+srcY*srcPitch + srcXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr Start = srcDevice+srcY*srcPitch+srcXInBytes;
+ *
+ * For CUDA arrays, srcXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - dstXInBytes and dstY specify the base address of the destination data
+ * for the copy.
+ *
+ * For host pointers, the base address is
+ *
+ * void* dstStart = (void*)((char*)dstHost+dstY*dstPitch + dstXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr dstStart = dstDevice+dstY*dstPitch+dstXInBytes;
+ *
+ * For CUDA arrays, dstXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - WidthInBytes and Height specify the width (in bytes) and height of
+ * the 2D copy being performed. Any pitches must be greater than or
+ * equal to WidthInBytes.
+ *
+ * cuMemcpy2D() returns an error if any pitch is greater than the
+ * maximum allowed (CU_DEVICE_ATTRIBUTE_MAX_PITCH). cuMemAllocPitch()
+ * passes back pitches that always work with cuMemcpy2D(). On intra-device
+ * memory copies (device ? device, CUDA array ? device, CUDA array ?
+ * CUDA array), cuMemcpy2D() may fail for pitches not computed by
+ * cuMemAllocPitch(). cuMemcpy2DUnaligned() does not have this restriction,
+ * but may run signi?cantly slower in the cases where cuMemcpy2D() would
+ * have returned an error code.
+ *
+ * cuMemcpy2DAsync() is asynchronous and can optionally be associated to a
+ * stream by passing a non-zero hStream argument. It only works on
+ * page-locked host memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * \param p_copy Parameters for the memory copy.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpy2DUnaligned(const CUDA_MEMCPY2D *p_copy)
+{
+ CUresult result = 0;
+
+ if (p_copy == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_copy is NULL");
+ goto error;
+ }
+
+ result = cuMemcpy2DUnaligned(p_copy);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_2D_UNALIGNED) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Perform a 3D memory copy according to the parameters specified in
+ * p_copy. The CUDA_MEMCPY3D structure is defined as:
+ *
+ * typedef struct CUDA_MEMCPY3D_st {
+ * unsigned int srcXInBytes, srcY, srcZ;
+ * unsigned int srcLOD;
+ * CUmemorytype srcMemoryType;
+ * const void *srcHost;
+ * CUdeviceptr srcDevice;
+ * CUarray srcArray;
+ * unsigned int srcPitch; // ignored when src is array
+ * unsigned int srcHeight; // ignored when src is array; may be 0 if Depth==1
+ * unsigned int dstXInBytes, dstY, dstZ;
+ * unsigned int dstLOD;
+ * CUmemorytype dstMemoryType;
+ * void *dstHost;
+ * CUdeviceptr dstDevice;
+ * CUarray dstArray;
+ * unsigned int dstPitch; // ignored when dst is array
+ * unsigned int dstHeight; // ignored when dst is array; may be 0 if Depth==1
+ * unsigned int WidthInBytes;
+ * unsigned int Height;
+ * unsigned int Depth;
+ * } CUDA_MEMCPY3D;
+ *
+ * where:
+ *
+ * - srcMemoryType and dstMemoryType specify the type of memory of the
+ * source and destination, respectively;
+ * CUmemorytype_enum is defined as:
+ *
+ * typedef enum CUmemorytype_enum {
+ * CU_MEMORYTYPE_HOST = 0x01,
+ * CU_MEMORYTYPE_DEVICE = 0x02,
+ * CU_MEMORYTYPE_ARRAY = 0x03
+ * } CUmemorytype;
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_HOST, srcHost, srcPitch and srcHeight
+ * specify the (host) base address of the source data, the bytes per row,
+ * and the height of each 2D slice of the 3D array. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_DEVICE, srcDevice, srcPitch and
+ * srcHeight specify the (device) base address of the source data, the
+ * bytes per row, and the height of each 2D slice of the 3D array.
+ * srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_ARRAY, srcArray specifies the handle
+ * of the source data. srcHost, srcDevice, srcPitch and srcHeight are
+ * ignored. If dstMemoryType is CU_MEMORYTYPE_HOST, dstHost and dstPitch
+ * specify the (host) base address of the destination data, the bytes per
+ * row, and the height of each 2D slice of the 3D array. dstArray is
+ * ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_DEVICE, dstDevice and dstPitch
+ * specify the (device) base address of the destination data, the bytes
+ * per row, and the height of each 2D slice of the 3D array. dstArray is
+ * ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_ARRAY, dstArray specifies the
+ * handle of the destination data. dstHost, dstDevice, dstPitch and
+ * dstHeight are ignored.
+ *
+ * - srcXInBytes, srcY and srcZ specify the base address of the source
+ * data for the copy.
+ *
+ * For host pointers, the starting address is
+ *
+ * void* Start = (void*)((char*)srcHost+(srcZ*srcHeight+srcY)*srcPitch + srcXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr Start = srcDevice+(srcZ*srcHeight+srcY)*srcPitch+srcXInBytes;
+ *
+ * For CUDA arrays, srcXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - dstXInBytes, dstY and dstZ specify the base address of the destination
+ * data for the copy.
+ *
+ * For host pointers, the base address is
+ *
+ * void* dstStart = (void*)((char*)dstHost+(dstZ*dstHeight+dstY)*dstPitch + dstXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr dstStart = dstDevice+(dstZ*dstHeight+dstY)*dstPitch+dstXInBytes;
+ *
+ * For CUDA arrays, dstXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - WidthInBytes, Height and Depth specify the width (in bytes), height
+ * and depth of the 3D copy being performed. Any pitches must be greater
+ * than or equal to WidthInBytes.
+ *
+ * cuMemcpy3D() returns an error if any pitch is greater than the maximum
+ * allowed (CU_DEVICE_ATTRIBUTE_MAX_PITCH).
+ *
+ * The srcLOD and dstLOD members of the CUDA_MEMCPY3D structure must be
+ * set to 0.
+ *
+ * \param p_copy Parameters for the memory copy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpy3D(const CUDA_MEMCPY3D *p_copy)
+{
+ CUresult result = 0;
+
+ if (p_copy == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_copy is NULL");
+ goto error;
+ }
+
+ result = cuMemcpy3D(p_copy);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_3D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Perform a 3D memory copy according to the parameters specified in
+ * p_copy. The CUDA_MEMCPY3D structure is defined as:
+ *
+ * typedef struct CUDA_MEMCPY3D_st {
+ * unsigned int srcXInBytes, srcY, srcZ;
+ * unsigned int srcLOD;
+ * CUmemorytype srcMemoryType;
+ * const void *srcHost;
+ * CUdeviceptr srcDevice;
+ * CUarray srcArray;
+ * unsigned int srcPitch; // ignored when src is array
+ * unsigned int srcHeight; // ignored when src is array; may be 0 if Depth==1
+ * unsigned int dstXInBytes, dstY, dstZ;
+ * unsigned int dstLOD;
+ * CUmemorytype dstMemoryType;
+ * void *dstHost;
+ * CUdeviceptr dstDevice;
+ * CUarray dstArray;
+ * unsigned int dstPitch; // ignored when dst is array
+ * unsigned int dstHeight; // ignored when dst is array; may be 0 if Depth==1
+ * unsigned int WidthInBytes;
+ * unsigned int Height;
+ * unsigned int Depth;
+ * } CUDA_MEMCPY3D;
+ *
+ * where:
+ *
+ * - srcMemoryType and dstMemoryType specify the type of memory of the
+ * source and destination, respectively;
+ * CUmemorytype_enum is defined as:
+ *
+ * typedef enum CUmemorytype_enum {
+ * CU_MEMORYTYPE_HOST = 0x01,
+ * CU_MEMORYTYPE_DEVICE = 0x02,
+ * CU_MEMORYTYPE_ARRAY = 0x03
+ * } CUmemorytype;
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_HOST, srcHost, srcPitch and srcHeight
+ * specify the (host) base address of the source data, the bytes per row,
+ * and the height of each 2D slice of the 3D array. srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_DEVICE, srcDevice, srcPitch and
+ * srcHeight specify the (device) base address of the source data, the
+ * bytes per row, and the height of each 2D slice of the 3D array.
+ * srcArray is ignored.
+ *
+ * If srcMemoryType is CU_MEMORYTYPE_ARRAY, srcArray specifies the handle
+ * of the source data. srcHost, srcDevice, srcPitch and srcHeight are
+ * ignored. If dstMemoryType is CU_MEMORYTYPE_HOST, dstHost and dstPitch
+ * specify the (host) base address of the destination data, the bytes per
+ * row, and the height of each 2D slice of the 3D array. dstArray is
+ * ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_DEVICE, dstDevice and dstPitch
+ * specify the (device) base address of the destination data, the bytes
+ * per row, and the height of each 2D slice of the 3D array. dstArray is
+ * ignored.
+ *
+ * If dstMemoryType is CU_MEMORYTYPE_ARRAY, dstArray specifies the
+ * handle of the destination data. dstHost, dstDevice, dstPitch and
+ * dstHeight are ignored.
+ *
+ * - srcXInBytes, srcY and srcZ specify the base address of the source
+ * data for the copy.
+ *
+ * For host pointers, the starting address is
+ *
+ * void* Start = (void*)((char*)srcHost+(srcZ*srcHeight+srcY)*srcPitch + srcXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr Start = srcDevice+(srcZ*srcHeight+srcY)*srcPitch+srcXInBytes;
+ *
+ * For CUDA arrays, srcXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - dstXInBytes, dstY and dstZ specify the base address of the destination
+ * data for the copy.
+ *
+ * For host pointers, the base address is
+ *
+ * void* dstStart = (void*)((char*)dstHost+(dstZ*dstHeight+dstY)*dstPitch + dstXInBytes);
+ *
+ * For device pointers, the starting address is
+ *
+ * CUdeviceptr dstStart = dstDevice+(dstZ*dstHeight+dstY)*dstPitch+dstXInBytes;
+ *
+ * For CUDA arrays, dstXInBytes must be evenly divisible by the array
+ * element size.
+ *
+ * - WidthInBytes, Height and Depth specify the width (in bytes), height
+ * and depth of the 3D copy being performed. Any pitches must be greater
+ * than or equal to WidthInBytes.
+ *
+ * cuMemcpy3D() returns an error if any pitch is greater than the maximum
+ * allowed (CU_DEVICE_ATTRIBUTE_MAX_PITCH).
+ *
+ * cuMemcpy3DAsync() is asynchronous and can optionally be associated
+ * to a stream by passing a non-zero hStream argument. It only works on
+ * page-locked host memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * The srcLOD and dstLOD members of the CUDA_MEMCPY3D structure must be
+ * set to 0.
+ *
+ * \param p_copy Parameters for the memory copy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpy3DAsync(const CUDA_MEMCPY3D *p_copy, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ if (p_copy == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_copy is NULL");
+ goto error;
+ }
+
+ result = cuMemcpy3DAsync(p_copy, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_3D_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemcpy3DPeer(const CUDA_MEMCPY3D_PEER *p_copy)
+{
+ CUresult result = 0;
+
+ result = cuMemcpy3DPeer(p_copy);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_3D_PEER) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaMemcpy3DPeerAsync(const CUDA_MEMCPY3D_PEER *p_copy,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpy3DPeerAsync(p_copy, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_3D_PEER_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaMemcpyAsync(CUdeviceptr dst, CUdeviceptr src, size_t byte_count,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyAsync(dst, src, byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from one 1D CUDA array to another. dstArray and srcArray
+ * specify the handles of the destination and source CUDA arrays for the
+ * copy, respectively. dstIndex and srcIndex specify the destination and
+ * source indices into the CUDA array. These values are in the range
+ * [0, Width-1] for the CUDA array; they are not byte offsets. ByteCount
+ * is the number of bytes to be copied. The size of the elements in the
+ * CUDA arrays need not be the same format, but the elements must be the
+ * same size; and count must be evenly divisible by that size.
+ *
+ * \param dst_array Destination array.
+ * \param dst_index Offset of destination array.
+ * \param src_array Source array.
+ * \param src_index Offset of source array.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyAtoA(CUarray dst_array, size_t dst_offset,
+ CUarray src_array, size_t src_offset,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyAtoA(dst_array, dst_offset, src_array, src_offset,
+ byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_A_TO_A) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \param Copies from one 1D CUDA array to device memory. dstDevice specifies the
+ * base pointer of the destination and must be naturally aligned with the
+ * CUDA array elements. hSrc and SrcIndex specify the CUDA array handle and
+ * the index (in array elements) of the array element where the copy is
+ * to begin. ByteCount speci?es the number of bytes to copy and must be
+ * evenly divisible by the array element size.
+ *
+ * \param dst_device Destination device pointer.
+ * \param h_src Source array.
+ * \param src_index Offset of source array.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyAtoD(CUdeviceptr dst_device, CUarray src_array,
+ size_t src_offset, size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyAtoD(dst_device, src_array, src_offset, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_A_TO_D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \param Copies from one 1D CUDA array to host memory. dstHost specifies the
+ * base pointer of the destination. srcArray and srcIndex specify the
+ * CUDA array handle and starting index of the source data. ByteCount
+ * specifies the number of bytes to copy.
+ *
+ * \param dst_device Destination device pointer.
+ * \param h_src Source array.
+ * \param src_index Offset of source array.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyAtoH(void *dst_host, CUarray src_array, size_t src_offset,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyAtoH(dst_host, src_array, src_offset, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_A_TO_H) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \param Copies from one 1D CUDA array to host memory. dstHost specifies the
+ * base pointer of the destination. srcArray and srcIndex specify the
+ * CUDA array handle and starting index of the source data. ByteCount
+ * specifies the number of bytes to copy.
+ *
+ * cuMemcpyAtoHAsync() is asynchronous and can optionally be associated
+ * to a stream by passing a non-zero stream argument. It only works on
+ * page-locked host memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * \param dst_device Destination device pointer.
+ * \param src_array Source array.
+ * \param src_index Offset of source array.
+ * \param byte_count Size of memory copy in bytes.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyAtoHAsync(void *dst_host, CUarray src_array,
+ size_t src_offset, size_t byte_count,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyAtoHAsync(dst_host, src_array, src_offset, byte_count,
+ h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_A_TO_H_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from device memory to a 1D CUDA array. dstArray and dstIndex
+ * specify the CUDA array handle and starting index of the destination
+ * data. srcDevice speci?es the base pointer of the source. ByteCount
+ * specifies the number of bytes to copy.
+ *
+ * \param dst_array Destination array.
+ * \param dst_index Offset of destination array.
+ * \param src_device Source device pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyDtoA(CUarray dst_array, size_t dst_offset,
+ CUdeviceptr src_device, size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyDtoA(dst_array, dst_offset, src_device, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_D_TO_A) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from device memory to device memory. dstDevice and srcDevice are
+ * the base pointers of the destination and source, respectively.
+ * byte_count specifies the number of bytes to copy. Note that this
+ * function is asynchronous.
+ *
+ * \param dst_device Destination device pointer.
+ * \param src_device Source device pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyDtoD(CUdeviceptr dst_device, CUdeviceptr src_device,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyDtoD(dst_device, src_device, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_D_TO_D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemcpyDtoDAsync(CUdeviceptr dst_device, CUdeviceptr src_device,
+ size_t byte_count, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyDtoDAsync(dst_device, src_device, byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_D_TO_D_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+
+/**
+ * \brief Copies from device to host memory. dst_host and src_device specify
+ * the base pointers of the destination and source, respectively.
+ * byte_count specifies the number of bytes to copy. Note that this
+ * function is synchronous.
+ *
+ * \param dst_host Destination device pointer.
+ * \param src_device Source device pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyDtoH(void *dst_host, CUdeviceptr src_device,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyDtoH(dst_host, src_device, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_D_TO_H) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from device to host memory. dst_host and src_device specify
+ * the base pointers of the destination and source, respectively.
+ * byte_count specifies the number of bytes to copy.
+ *
+ * cuMemcpyDtoHAsync() is asynchronous and can optionally be associated
+ * to a stream by passing a non-zero h_stream argument. It only works
+ * on page-locked memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * \param dst_host Destination device pointer.
+ * \param src_device Source device pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyDtoHAsync(void *dst_host, CUdeviceptr src_device,
+ size_t byte_count, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyDtoHAsync(dst_host, src_device, byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_D_TO_H_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from host memory to a 1D CUDA array. dst_array and dst_index
+ * specify the CUDA array handle and starting index of the destination
+ * data. p_src specifies the base address of the source. byte_count
+ * specifies the number of bytes to copy.
+ *
+ * \param dst_array Destination array.
+ * \param dst_index Offset of destination array.
+ * \param p_src Source host pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyHtoA(CUarray dst_array, size_t dst_offset,
+ const void *src_host, size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyHtoA(dst_array, dst_offset, src_host, byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_H_TO_A) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from host memory to a 1D CUDA array. dst_array and dst_index
+ * specify the CUDA array handle and starting index of the destination
+ * data. p_src specifies the base address of the source. byte_count
+ * specfies the number of bytes to copy.
+ *
+ * cuMemcpyHtoAAsync() is asynchronous and can optionally be associated
+ * to a stream by passing a non-zero h_stream argument. It only works on
+ * page-locked memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ * \param dst_array Destination array.
+ * \param dst_index Offset of destination array.
+ * \param p_src Source host pointer.
+ * \param byte_count Size of memory copy in bytes.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyHtoAAsync(CUarray dst_array, size_t dst_offset,
+ const void *src_host, size_t byte_count,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyHtoAAsync(dst_array, dst_offset, src_host, byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_H_TO_A_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from host memory to device memory. dst_device and src_host
+ * are the base addresses of the destination and source, respectively.
+ * byte_count specifies the number of bytes to copy. Note that this
+ * function is synchronous.
+ *
+ * \param dst_device Destination device pointer.
+ * \param src_host Source host pointer.
+ * \param byte_count Size of memory copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyHtoD(CUdeviceptr dst_device, const void *src_host,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyHtoD(dst_device, src_host,byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_H_TO_D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies from host memory to device memory. dst_device and src_host are
+ * the base addresses of the destination and source, respectively.
+ * byte_count specifies the number of bytes to copy.
+ *
+ * cuMemcpyHtoDAsync() is asynchronous and can optionally be associated
+ * to a stream by passing a non-zero h_stream argument. It only works on
+ * page-locked memory and returns an error if a pointer to pageable
+ * memory is passed as input.
+ *
+ *
+ * \param dst_device Destination device pointer.
+ * \param src_host Source host pointer.
+ * \param byte_count Size of memory copy in bytes.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemcpyHtoDAsync(CUdeviceptr dst_device, const void *src_host,
+ size_t byte_count, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyHtoDAsync(dst_device, src_host, byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_H_TO_D_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemcpyPeer(CUdeviceptr dst_device, CUcontext dst_context,
+ CUdeviceptr src_device, CUcontext src_context,
+ size_t byte_count)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyPeer(dst_device, dst_context, src_device, src_context,
+ byte_count);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_PEER) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaMemcpyPeerAsync(CUdeviceptr dst_device, CUcontext dst_context,
+ CUdeviceptr src_device, CUcontext src_context,
+ size_t byte_count, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemcpyPeerAsync(dst_device, dst_context, src_device, src_context,
+ byte_count, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMCPY_PEER_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Frees the memory space pointed to by dptr, which must have been
+ * returned by a previous call to cuMemAlloc() or cuMemAllocPitch().
+ *
+ * \param dptr Pointer to the memory to free.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemFree(CUdeviceptr dptr)
+{
+ CUresult result = 0;
+
+ result = cuMemFree(dptr);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_FREE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Frees the memory space pointed to by p, which must have been returned
+ * by a previous call to cuMemAllocHost().
+ *
+ * \param p Pointer to the memory to free.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemFreeHost(void *p)
+{
+ CUresult result = 0;
+
+ if (p == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p is NULL");
+ goto error;
+ }
+
+ result = cuMemFreeHost(p);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_FREE_HOST) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns the base address in *pbase and size in *psize of the allocation
+ * by cuMemAlloc() or cuMemAllocPitch() that contains the input pointer
+ * dptr. Both parameters pbase and psize are optional. If one of them is
+ * NULL, it is ignored.
+ *
+ * \param pbase Returned base address.
+ * \param psize Returned size of device memory allocation.
+ * \param dptr Device pointer to query
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemGetAddressRange(CUdeviceptr *pbase, size_t *psize,
+ CUdeviceptr dptr)
+{
+ CUresult result = 0;
+
+ result = cuMemGetAddressRange(pbase, psize, dptr);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_GET_ADDRESS_RANGE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *free and *total respectively, the free and total amount
+ * of memory available for allocation by the CUDA context, in bytes.
+ *
+ * \param free Returned free memory in bytes.
+ * \param total Returned total memory in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemGetInfo(size_t *free, size_t *total)
+{
+ CUresult result = 0;
+
+ if (free == NULL || total == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "free is NULL || total is NULL");
+ goto error;
+ }
+
+ result = cuMemGetInfo(free, total);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_GET_INFO) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Allocates bytesize bytes of host memory that is page-locked and
+ * accessible to the device. The driver tracks the virtual memory ranges
+ * allocated with this function and automatically accelerates calls to
+ * functions such as cuMemcpyHtoD(). Since the memory can be accessed
+ * directly by the device, it can be read or written with much higher
+ * bandwidth than pageable memory obtained with functions such as
+ * SCMalloc(). Allocating excessive amounts of pinned memory may degrade
+ * system performance, since it reduces the amount of memory available
+ * to the system for paging. As a result, this function is best used
+ * sparingly to allocate staging areas for data exchange between host
+ * and device.
+ *
+ * The Flags parameter enables different options to be specified that
+ * affect the allocation, as follows.
+ *
+ * - CU_MEMHOSTALLOC_PORTABLE: The memory returned by this call will be
+ * considered as pinned memory by all CUDA contexts, not just the one
+ * that performed the allocation.
+ * - CU_MEMHOSTALLOC_DEVICEMAP: Maps the allocation into the CUDA
+ * address space. The device pointer to the memory may be obtained by
+ * calling cuMemHostGetDevicePointer(). This feature is available only
+ * on GPUs with compute capability greater than or equal to 1.1.
+ * - CU_MEMHOSTALLOC_WRITECOMBINED: Allocates the memory as write-combined
+ * (WC). WC memory can be transferred across the PCI Express bus more
+ * quickly on some system con?gurations, but cannot be read efficiently
+ * by most CPUs. WC memory is a good option for buffers that will be
+ * written by the CPU and read by the GPU via mapped pinned memory or
+ * host->device transfers. All of these fags are orthogonal to one
+ * another: a developer may allocate memory that is portable, mapped
+ * and/or write-combined with no restrictions.
+ *
+ * The CUDA context must have been created with the CU_CTX_MAP_HOST flag
+ * in order for the CU_MEMHOSTALLOC_MAPPED flag to have any effect.
+ *
+ * The CU_MEMHOSTALLOC_MAPPED flag may be specified on CUDA contexts for
+ * devices that do not support mapped pinned memory. The failure is
+ * deferred to cuMemHostGetDevicePointer() because the memory may be
+ * mapped into other CUDA contexts via the CU_MEMHOSTALLOC_PORTABLE flag.
+ *
+ * The memory allocated by this function must be freed with cuMemFreeHost().
+ *
+ * \param pp Returned host pointer to page-locked memory.
+ * \param byte_size Requested allocation size in bytes.
+ * \param flags Flags for allocation request.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemHostAlloc(void **pp, size_t byte_size, unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuMemHostAlloc(pp, byte_size, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_HOST_ALLOC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Passes back the device pointer pdptr corresponding to the mapped,
+ * pinned host buffer p allocated by cuMemHostAlloc.
+ *
+ * cuMemHostGetDevicePointer() will fail if the CU_MEMALLOCHOST_DEVICEMAP
+ * flag was not speci?ed at the time the memory was allocated, or if the
+ * function is called on a GPU that does not support mapped pinned memory.
+ *
+ * Flags provides for future releases. For now, it must be set to 0.
+ *
+ * \param pdptr Returned device pointer.
+ * \param p Host pointer.
+ * \param flags Options(must be 0).
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemHostGetDevicePointer(CUdeviceptr *pdptr, void *p, unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuMemHostGetDevicePointer(pdptr, p, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_HOST_GET_DEVICE_POINTER) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Passes back the flags p_flags that were specified when allocating the
+ * pinned host buffer p allocated by cuMemHostAlloc.
+ *
+ * cuMemHostGetFlags() will fail if the pointer does not reside in an
+ * allocation performed by cuMemAllocHost() or cuMemHostAlloc().
+ *
+ * \param p_flags Returned flags word.
+ * \param p Host pointer.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemHostGetFlags(unsigned int *p_flags, void *p)
+{
+ CUresult result = 0;
+
+ result = cuMemHostGetFlags(p_flags, p);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_HOST_GET_FLAGS) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemHostRegister(void *p, size_t byte_size, unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuMemHostRegister(p, byte_size, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_HOST_REGISTER) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaMemHostUnregister(void *p)
+{
+ CUresult result = 0;
+
+ result = cuMemHostUnregister(p);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEM_HOST_UNREGISTER) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the memory range of N 16-bit values to the speci?ed value us.
+ *
+ * \param dst_device Destination device pointer.
+ * \param us Value to set.
+ * \param n Number of elements.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD16(CUdeviceptr dst_device, unsigned short us, size_t n)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD16(dst_device, us, n);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D16) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD16Async(CUdeviceptr dst_device, unsigned short us,
+ size_t n, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD16Async(dst_device, us, n, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D16_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the 2D memory range of Width 16-bit values to the specified
+ * value us. Height specifies the number of rows to set, and dst_pitch
+ * specifies the number of bytes between each row. This function
+ * performs fastest when the pitch is one that has been passed back
+ * by cuMemAllocPitch().
+ *
+ * \param dst_device Destination device pointer.
+ * \param dst_pitch Pitch of destination device pointer.
+ * \param us Value to set
+ * \param width Width of row.
+ * \param height Number of rows
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD2D16(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned short us, size_t width,
+ size_t height)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D16(dst_device, dst_pitch, us, width, height);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D16) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD2D16Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned short us, size_t width,
+ size_t height, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D16Async(dst_device, dst_pitch, us, width, height,
+ h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D16_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the 2D memory range of Width 32-bit values to the specified value
+ * ui. Height speci?es the number of rows to set, and dstPitch specifies
+ * the number of bytes between each row. This function performs fastest
+ * when the pitch is one that has been passed back by cuMemAllocPitch().
+ *
+ * \param dst_device Destination device pointer.
+ * \param dst_pitch Pitch of destination device pointer.
+ * \param ui Value to set
+ * \param width Width of row.
+ * \param height Number of rows
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD2D32(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned int ui, size_t width, size_t height)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D32(dst_device, dst_pitch, ui, width, height);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D32) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD2D32Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned int ui, size_t width, size_t height,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D32Async(dst_device, dst_pitch, ui, width, height,
+ h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D32_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the 2D memory range of Width 8-bit values to the specified value
+ * uc. Height speci?es the number of rows to set, and dstPitch specifies
+ * the number of bytes between each row. This function performs fastest
+ * when the pitch is one that has been passed back by cuMemAllocPitch().
+ *
+ * \param dst_device Destination device pointer.
+ * \param dst_pitch Pitch of destination device pointer.
+ * \param uc Value to set
+ * \param width Width of row.
+ * \param height Number of rows
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD2D8(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned char uc, size_t width, size_t height)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D8(dst_device, dst_pitch, uc, width, height);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D8) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD2D8Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned char uc, size_t width, size_t height,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD2D8Async(dst_device, dst_pitch, uc, width, height,
+ h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D2_D8_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the memory range of N 32-bit values to the specified value ui.
+ *
+ * \param dst_device Destination device pointer.
+ * \param ui Value to set.
+ * \param n Number of elements.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD32(CUdeviceptr dst_device, unsigned int ui, size_t n)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD32(dst_device, ui, n);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D32) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD32Async(CUdeviceptr dst_device, unsigned int ui,
+ size_t n, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD32Async(dst_device, ui, n, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D32_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets the memory range of N 8-bit values to the specified value ui.
+ *
+ * \param dst_device Destination device pointer.
+ * \param uc Value to set.
+ * \param n Number of elements.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaMemsetD8(CUdeviceptr dst_device, unsigned char uc, size_t n)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD8(dst_device, uc, n);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D8) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaMemsetD8Async(CUdeviceptr dst_device, unsigned char uc,
+ size_t n, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuMemsetD8Async(dst_device, uc, n, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_MEMSET_D8_ASYNC) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/*****************************Unified_Addressing_API****************************/
+
+int SCCudaPointerGetAttribute(void *data, CUpointer_attribute attribute,
+ CUdeviceptr ptr)
+{
+ CUresult result = 0;
+
+ result = cuPointerGetAttribute(data, attribute, ptr);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_POINTER_GET_ATTRIBUTE) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/*****************************Stream_Management_API****************************/
+
+/**
+ * \brief Creates a stream and returns a handle in ph_stream. Flags is
+ * required to be 0.
+ *
+ * \param ph_stream Returned newly created stream.
+ * \param flags Parameters for stream creation(must be 0).
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaStreamCreate(CUstream *ph_stream, unsigned int flags)
+{
+ CUresult result = 0;
+
+ if (ph_stream == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "phStream is NULL");
+ goto error;
+ }
+
+ result = cuStreamCreate(ph_stream, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_STREAM_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Destroys the stream specified by h_stream.
+ *
+ * \param h_stream Stream to destroy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaStreamDestroy(CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuStreamDestroy(h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_STREAM_DESTROY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns CUDA_SUCCESS if all operations in the stream specifed by
+ * h_stream have completed, or CUDA_ERROR_NOT_READY if not.
+ *
+ * \param h_stream Stream to query status of.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaStreamQuery(CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuStreamQuery(h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_STREAM_QUERY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Waits until the device has completed all operations in the stream
+ * specified by h_stream.
+ *
+ * \param h_stream Stream to wait for.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaStreamSynchronize(CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuStreamSynchronize(h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_STREAM_SYNCHRONIZE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaStreamWaitEvent(CUstream h_stream, CUevent h_event,
+ unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuStreamWaitEvent(h_stream, h_event, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_STREAM_WAIT_EVENT) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/*****************************Event_Management_API*****************************/
+
+/**
+ * \brief Creates an event *ph_event with the flags specified via flags. Valid
+ * flags include:
+ *
+ * CU_EVENT_DEFAULT: Default event creation flag.
+ * CU_EVENT_BLOCKING_SYNC: Specifies that event should use blocking
+ * synchronization.
+ *
+ * \param ph_event Returns newly created event.
+ * \param flags Event creation flags.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventCreate(CUevent *ph_event, unsigned int flags)
+{
+ CUresult result = 0;
+
+ if (ph_event == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "ph_event is NULL");
+ goto error;
+ }
+
+ result = cuEventCreate(ph_event, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Destroys the event specified by h_event.
+ *
+ * \param h_event Event to destroy.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventDestroy(CUevent h_event)
+{
+ CUresult result = 0;
+
+ result = cuEventDestroy(h_event);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_DESTROY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Computes the elapsed time between two events (in milliseconds with
+ * a resolution of around 0.5 microseconds). If either event has not
+ * been recorded yet, this function returns CUDA_ERROR_NOT_READY. If
+ * either event has been recorded with a non-zero stream, the result
+ * is undefined.
+ *
+ * \param p_milli_seconds Returned elapsed time in milliseconds.
+ * \param h_start Starting event.
+ * \param h_end Ending event.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventElapsedTime(float *p_milli_seconds, CUevent h_start, CUevent h_end)
+{
+ CUresult result = 0;
+
+ if (p_milli_seconds == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_milli_seconds is NULL");
+ goto error;
+ }
+
+ result = cuEventElapsedTime(p_milli_seconds, h_start, h_end);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_ELAPSED_TIME) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns CUDA_SUCCESS if the event has actually been recorded, or
+ * CUDA_ERROR_NOT_READY if not. If cuEventRecord() has not been called
+ * on this event, the function returns CUDA_ERROR_INVALID_VALUE.
+ *
+ * \param h_event Event to query.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventQuery(CUevent h_event)
+{
+ CUresult result = 0;
+
+ result = cuEventQuery(h_event);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_QUERY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Records an event. If stream is non-zero, the event is recorded after
+ * all preceding operations in the stream have been completed; otherwise,
+ * it is recorded after all preceding operations in the CUDA context have
+ * been completed. Since operation is asynchronous, cuEventQuery() and/or
+ * cuEventSynchronize() must be used to determine when the event has
+ * actually been recorded.
+ *
+ * If cuEventRecord() has previously been called and the event has not
+ * been recorded yet, this function returns CUDA_ERROR_INVALID_VALUE.
+ *
+ * \param h_event Event to record.
+ * \param h_stream Stream to record event for.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventRecord(CUevent h_event, CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuEventRecord(h_event, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_RECORD) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Waits until the event has actually been recorded. If cuEventRecord()
+ * has been called on this event, the function returns
+ * CUDA_ERROR_INVALID_VALUE.
+ *
+ * If cuEventRecord() has previously been called and the event has not
+ * been recorded yet, this function returns CUDA_ERROR_INVALID_VALUE.
+ *
+ * \param h_event Event to wait for.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaEventSynchronize(CUevent h_event)
+{
+ CUresult result = 0;
+
+ result = cuEventSynchronize(h_event);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_EVENT_SYNCHRONIZE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/***********************Execution_Control_Management_API***********************/
+
+/**
+ * \brief Returns in *pi the integer value of the attribute attrib on the
+ * kernel given by hfunc. The supported attributes are:
+ *
+ * - CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK: The number of threads
+ * beyond which a launch of the function would fail. This number
+ * depends on both the function and the device on which the
+ * function is currently loaded.
+ * - CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES: The size in bytes of
+ * statically-allocated shared memory required by this function.
+ * This does not include dynamically-allocated shared memory
+ * requested by the user at runtime.
+ * - CU_FUNC_ATTRIBUTE_CONST_SIZE_BYTES: The size in bytes of
+ * user-allocated constant memory required by this function.
+ * - CU_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES: The size in bytes of thread
+ * local memory used by this function.
+ * - CU_FUNC_ATTRIBUTE_NUM_REGS: The number of registers used by each
+ * thread of this function.
+ *
+ * \param pi Pointer to an integer which would be updated with the returned
+ * attribute value.
+ * \param attrib Attribute requested.
+ * \param hfunc Function to query attribute of.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaFuncGetAttribute(int *pi, CUfunction_attribute attrib, CUfunction hfunc)
+{
+ CUresult result = 0;
+
+ if (pi == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pi is NULL");
+ goto error;
+ }
+
+ result = cuFuncGetAttribute(pi, attrib, hfunc);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_FUNC_GET_ATTRIBUTE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+int SCCudaFuncSetCacheConfig(CUfunction hfunc, CUfunc_cache config)
+{
+ CUresult result = 0;
+
+ result = cuFuncSetCacheConfig(hfunc, config);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_FUNC_SET_CACHE_CONFIG) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+int SCCudaLaunchKernel(CUfunction f, unsigned int grid_dim_x,
+ unsigned int grid_dim_y, unsigned int grid_dim_z,
+ unsigned int block_dim_x, unsigned int block_dim_y,
+ unsigned int block_dim_z, unsigned int shared_mem_bytes,
+ CUstream h_stream, void **kernel_params, void **extra)
+{
+ CUresult result = 0;
+
+ result = cuLaunchKernel(f, grid_dim_x, grid_dim_y, grid_dim_z,
+ block_dim_x, block_dim_y, block_dim_z,
+ shared_mem_bytes, h_stream, kernel_params, extra);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_LAUNCH_KERNEL) == -1)
+ goto error;
+
+ return 0;
+ error:
+ return -1;
+}
+
+/**
+ * \brief Specifies the x, y, and z dimensions of the thread blocks that are
+ * created when the kernel given by hfunc is launched.
+ *
+ * \param hfunc Kernel to specify dimensions of.
+ * \param x X dimension.
+ * \param y Y dimension.
+ * \param z Z dimension.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaFuncSetBlockShape(CUfunction hfunc, int x, int y, int z)
+{
+ CUresult result = 0;
+
+ result = cuFuncSetBlockShape(hfunc, x, y, z);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_FUNC_SET_BLOCK_SHAPE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets through bytes the amount of dynamic shared memory that will be
+ * available to each thread block when the kernel given by hfunc is
+ * launched.
+ *
+ * \param hfunc Kernel to specify dynamic shared memory for.
+ * \param bytes Dynamic shared memory size per thread in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaFuncSetSharedSize(CUfunction hfunc, unsigned int bytes)
+{
+ CUresult result = 0;
+
+ result = cuFuncSetSharedSize(hfunc, bytes);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_FUNC_SET_SHARED_SIZE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Invokes the kernel f on a 1 x 1 x 1 grid of blocks. The block contains
+ * the number of threads specified by a previous call to
+ * cuFuncSetBlockShape().
+ *
+ * \param f Kernel to launch.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaLaunch(CUfunction f)
+{
+ CUresult result = 0;
+
+ result = cuLaunch(f);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_LAUNCH) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Invokes the kernel f on a grid_width x grid_height grid of blocks.
+ * Each block contains the number of threads specified by a previous call
+ * to cuFuncSetBlockShape().
+ *
+ * \param f Kernel to launch.
+ * \param grid_width Width of grid in blocks.
+ * \param grib_height Height of grid in blocks.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaLaunchGrid(CUfunction f, int grid_width, int grid_height)
+{
+ CUresult result = 0;
+
+ result = cuLaunchGrid(f, grid_width, grid_height);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_LAUNCH_GRID) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Invokes the kernel f on a grid_width x grid_height grid of blocks.
+ * Each block contains the number of threads specified by a previous call
+ * to cuFuncSetBlockShape(). cuLaunchGridAsync() can optionally be
+ * associated to a stream by passing a non-zero hStream argument.
+ *
+ * \param f Kernel to launch.
+ * \param grid_width Width of grid in blocks.
+ * \param grib_height Height of grid in blocks.
+ * \param h_stream Stream identifier.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaLaunchGridAsync(CUfunction f, int grid_width, int grid_height,
+ CUstream h_stream)
+{
+ CUresult result = 0;
+
+ result = cuLaunchGridAsync(f, grid_width, grid_height, h_stream);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_LAUNCH_GRID_ASYNC) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets a foating-point parameter that will be specified the next time
+ * the kernel corresponding to hfunc will be invoked. offset is a byte
+ * offset.
+ *
+ * \param h_func Kernel to add parameter to.
+ * \param offset Offset to add parameter to argument list.
+ * \param value Value of parameter.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaParamSetf(CUfunction h_func, int offset, float value)
+{
+ CUresult result = 0;
+
+ result = cuParamSetf(h_func, offset, value);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_PARAM_SETF) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets an integer parameter that will be specified the next time
+ * the kernel corresponding to hfunc will be invoked. offset is a byte
+ * offset.
+ *
+ * \param h_func Kernel to add parameter to.
+ * \param offset Offset to add parameter to argument list.
+ * \param value Value of parameter.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaParamSeti(CUfunction h_func, int offset, unsigned int value)
+{
+ CUresult result = 0;
+
+ result = cuParamSeti(h_func, offset, value);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_PARAM_SETI) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Sets through numbytes the total size in bytes needed by the function
+ * parameters of the kernel corresponding to hfunc.
+ *
+ * \param h_func Kernel to set parameter size for.
+ * \param num_bytes Size of paramter list in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaParamSetSize(CUfunction h_func, unsigned int num_bytes)
+{
+ CUresult result = 0;
+
+ result = cuParamSetSize(h_func, num_bytes);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_PARAM_SET_SIZE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Makes the CUDA array or linear memory bound to the texture reference
+ * h_tex_ref available to a device program as a texture. In this version
+ * of CUDA, the texture-reference must be obtained via cuModuleGetTexRef()
+ * and the tex_unit parameter must be set to CU_PARAM_TR_DEFAULT.
+ *
+ * \param h_func Kernel to add texture-reference to.
+ * \param tex_unit Texture unit (must be CU_PARAM_TR_DEFAULT).
+ * \param h_tex_ref Texture-reference to add to argument list.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaParamSetTexRef(CUfunction h_func, int tex_unit, CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ result = cuParamSetTexRef(h_func, tex_unit, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_PARAM_SET_TEX_REF) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Copies an arbitrary amount of data (specified in numbytes) from ptr
+ * into the parameter space of the kernel corresponding to hfunc.
+ * offset is a byte offset.
+ *
+ * \param h_func Kernel to add data to.
+ * \param offset Offset to add data to argument list.
+ * \param ptr Pointer to arbitrary data.
+ * \param num_bytes Size of data to copy in bytes.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaParamSetv(CUfunction h_func, int offset, void *ptr,
+ unsigned int num_bytes)
+{
+ CUresult result = 0;
+
+ if (ptr == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "ptr is NULL");
+ goto error;
+ }
+
+ result = cuParamSetv(h_func, offset, ptr, num_bytes);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_PARAM_SETV) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/***********************Texture_Reference_Management_API***********************/
+
+/**
+ * \brief Creates a texture reference and returns its handle in *pTexRef. Once
+ * created, the application must call cuTexRefSetArray() or cuTexRefSetAddress()
+ * to associate the reference with allocated memory. Other texture reference
+ * functions are used to specify the format and interpretation (addressing,
+ * filtering, etc.) to be used when the memory is read through this texture
+ * reference. To associate the texture reference with a texture ordinal for
+ * a given function, the application should call cuParamSetTexRef().
+ *
+ * \param p_tex_ref Returned texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefCreate(CUtexref *p_tex_ref)
+{
+ CUresult result = 0;
+
+ if (p_tex_ref == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_tex_ref is NULL");
+ goto error;
+ }
+
+ result = cuTexRefCreate(p_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_CREATE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Destroys the texture reference specified by hTexRef.
+ *
+ * \param h_tex_ref Texture reference to destroy
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefDestroy(CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ result = cuTexRefDestroy(h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_DESTROY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *pdptr the base address bound to the texture reference
+ * hTexRef, or returns CUDA_ERROR_INVALID_VALUE if the texture reference
+ * is not bound to any device memory range.
+ *
+ * \param pdptr Returned device address
+ * \param h_tex_ref Texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetAddress(CUdeviceptr *pdptr, CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ if (pdptr == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pdptr is NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetAddress(pdptr, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_ADDRESS) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *pam the addressing mode corresponding to the dimension
+ * dim of the texture reference hTexRef. Currently, the only valid value
+ * for dim are 0 and 1.
+ *
+ * \param pam Returned addressing mode
+ * \param h_tex_ref Texture reference
+ * \param dim Dimension
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetAddressMode(CUaddress_mode *pam, CUtexref h_tex_ref, int dim)
+{
+ CUresult result = 0;
+
+ if (pam == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pam is NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetAddressMode(pam, h_tex_ref, dim);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_ADDRESS_MODE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *phArray the CUDA array bound to the texture reference
+ * hTexRef, or returns CUDA_ERROR_INVALID_VALUE if the texture reference
+ * is not bound to any CUDA array.
+ *
+ * \param ph_array Returned array
+ * \param h_tex_ref Texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetArray(CUarray *ph_array, CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ if (ph_array == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "ph_array is NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetArray(ph_array, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_ARRAY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *pfm the filtering mode of the texture reference hTexRef.
+ *
+ * \param pfm Returned filtering mode
+ * \param h_tex_ref Texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetFilterMode(CUfilter_mode *pfm, CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ if (pfm == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "pfm is NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetFilterMode(pfm, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_FILTER_MODE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *pFlags the flags of the texture reference hTexRef.
+ *
+ * \param p_flags Returned flags
+ * \param h_tex_ref Texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetFlags(unsigned int *p_flags, CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ if (p_flags == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_flags is NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetFlags(p_flags, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_FLAGS) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Returns in *pFormat and *pNumChannels the format and number of
+ * components of the CUDA array bound to the texture reference hTexRef.
+ * If pFormat or pNumChannels is NULL, it will be ignored.
+ *
+ * \param p_format Returned format
+ * \param p_num_channels Returned number of components
+ * \param h_tex_ref Texture reference
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefGetFormat(CUarray_format *p_format, int *p_num_channels,
+ CUtexref h_tex_ref)
+{
+ CUresult result = 0;
+
+ if (p_format == NULL || p_num_channels == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "p_format == NULL || p_num_channels == NULL");
+ goto error;
+ }
+
+ result = cuTexRefGetFormat(p_format, p_num_channels, h_tex_ref);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_GET_FORMAT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Binds a linear address range to the texture reference hTexRef. Any
+ * previous address or CUDA array state associated with the texture
+ * reference is superseded by this function. Any memory previously
+ * bound to hTexRef is unbound.
+ *
+ * Since the hardware enforces an alignment requirement on texture
+ * base addresses, cuTexRefSetAddress() passes back a byte offset in
+ * *ByteOffset that must be applied to texture fetches in order to read
+ * from the desired memory. This offset must be divided by the texel
+ * size and passed to kernels that read from the texture so they can be
+ * applied to the tex1Dfetch() function.
+ *
+ * If the device memory pointer was returned from cuMemAlloc(), the
+ * offset is guaranteed to be 0 and NULL may be passed as the
+ * ByteOffset parameter.
+ *
+ * \param byte_offset Returned byte offset
+ * \param h_tex_ref Texture reference to bind
+ * \param dptr Device pointer to bind
+ * \param bytes Size of memory to bind in bytes
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetAddress(size_t *byte_offset, CUtexref h_tex_ref,
+ CUdeviceptr dptr, unsigned int bytes)
+{
+ CUresult result = 0;
+
+ if (byte_offset == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument supplied. "
+ "byte_offset is NULL");
+ goto error;
+ }
+
+ result = cuTexRefSetAddress(byte_offset, h_tex_ref, dptr, bytes);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_ADDRESS) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Binds a linear address range to the texture reference hTexRef. Any
+ * previous address or CUDA array state associated with the texture
+ * reference is superseded by this function. Any memory previously bound
+ * to hTexRef is unbound.
+ *
+ * Using a tex2D() function inside a kernel requires a call to either
+ * cuTexRefSetArray() to bind the corresponding texture reference to an
+ * array, or cuTexRefSetAddress2D() to bind the texture reference to
+ * linear memory.
+ *
+ * Function calls to cuTexRefSetFormat() cannot follow calls to
+ * cuTexRefSetAddress2D() for the same texture reference.
+ *
+ * It is required that dptr be aligned to the appropriate hardware-
+ * specific texture alignment. You can query this value using the device
+ * attribute CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT. If an unaligned dptr
+ * is supplied, CUDA_ERROR_INVALID_VALUE is returned.
+ *
+ * \param h_tex_ref Texture reference to bind
+ * \param desc Descriptor of CUDA array
+ * \param dptr Device pointer to bind
+ * \param pitch Line pitch in bytes
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetAddress2D(CUtexref h_tex_ref, const CUDA_ARRAY_DESCRIPTOR *desc,
+ CUdeviceptr dptr, unsigned int pitch)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetAddress2D(h_tex_ref, desc, dptr, pitch);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_ADDRESS_2D) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Specifies the addressing mode am for the given dimension dim of the
+ * texture reference hTexRef. If dim is zero, the addressing mode is
+ * applied to the first parameter of the functions used to fetch from
+ * the texture; if dim is 1, the second, and so on. CUaddress_mode is
+ * defined as:
+ *
+ * typedef enum CUaddress_mode_enum {
+ * CU_TR_ADDRESS_MODE_WRAP = 0,
+ * CU_TR_ADDRESS_MODE_CLAMP = 1,
+ * CU_TR_ADDRESS_MODE_MIRROR = 2,
+ * } CUaddress_mode;
+ *
+ * \param h_tex_ref Texture reference
+ * \param dim Dimension
+ * \param am Addressing mode to set
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetAddressMode(CUtexref h_tex_ref, int dim, CUaddress_mode am)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetAddressMode(h_tex_ref, dim, am);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_ADDRESS_MODE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Binds the CUDA array hArray to the texture reference hTexRef. Any
+ * previous address or CUDA array state associated with the texture
+ * reference is superseded by this function. Flags must be set to
+ * CU_TRSA_OVERRIDE_FORMAT. Any CUDA array previously bound to hTexRef
+ * is unbound.
+ *
+ * \param h_tex_ref Texture reference to bind
+ * \param h_array Array to bind
+ * \param flags Options (must be CU_TRSA_OVERRIDE_FORMAT)
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetArray(CUtexref h_tex_ref, CUarray h_array, unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetArray(h_tex_ref, h_array, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_ARRAY) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Specifies the filtering mode fm to be used when reading memory through
+ * the texture reference hTexRef. CUfilter_mode_enum is defined as:
+ *
+ * typedef enum CUfilter_mode_enum {
+ * CU_TR_FILTER_MODE_POINT = 0,
+ * CU_TR_FILTER_MODE_LINEAR = 1
+ * } CUfilter_mode;
+ *
+ * \param h_tex_ref Texture reference
+ * \param fm Filtering mode to set
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetFilterMode(CUtexref h_tex_ref, CUfilter_mode fm)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetFilterMode(h_tex_ref, fm);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_FILTER_MODE) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Specifies optional flags via Flags to specify the behavior of data
+ * returned through the texture reference hTexRef. The valid flags are:
+ *
+ * * CU_TRSF_READ_AS_INTEGER, which suppresses the default behavior of
+ * having the texture promote integer data to floating point data in
+ * the range [0, 1];
+ * * CU_TRSF_NORMALIZED_COORDINATES, which suppresses the default
+ * behavior of having the texture coordinates range from [0, Dim) where
+ * Dim is the width or height of the CUDA array. Instead, the texture
+ * coordinates [0, 1.0) reference the entire breadth of the array
+ * dimension;
+ *
+ * \param h_tex_ref Texture reference
+ * \param flags Optional flags to set
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetFlags(CUtexref h_tex_ref, unsigned int flags)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetFlags(h_tex_ref, flags);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_FLAGS) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Specifies the format of the data to be read by the texture reference
+ * hTexRef. fmt and NumPackedComponents are exactly analogous to the
+ * Format and NumChannels members of the CUDA_ARRAY_DESCRIPTOR structure:
+ * They specify the format of each component and the number of components
+ * per array element.
+ *
+ * \param h_tex_ref Texture reference
+ * \param fmt Format to set
+ * \param num_packed_components Number of components per array element
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCCudaTexRefSetFormat(CUtexref h_tex_ref, CUarray_format fmt,
+ int num_packed_components)
+{
+ CUresult result = 0;
+
+ result = cuTexRefSetFormat(h_tex_ref, fmt, num_packed_components);
+ if (SCCudaHandleRetValue(result, SC_CUDA_CU_TEX_REF_SET_FORMAT) == -1)
+ goto error;
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**************************Cuda_Env_Initialization_API*************************/
+
+/**
+ * \brief Initialize the CUDA Environment for the engine.
+ *
+ * \retval 0 On successfully initializing the CUDA environment for the engine.
+ * \retval -1 On failure.
+ */
+int SCCudaInitCudaEnvironment(void)
+{
+ if (devices != NULL) {
+ SCLogWarning(SC_ERR_CUDA_ERROR, "CUDA engine already initalized!!!!");
+ return 0;
+ }
+
+ if (SCCudaInit(0) == -1) {
+ SCLogError(SC_ERR_CUDA_ERROR, "Error initializing CUDA API. SCCudaInit() "
+ "returned -1");
+ goto error;
+ }
+
+ if ( (devices = SCCudaGetDevices()) == NULL) {
+ SCLogError(SC_ERR_CUDA_ERROR, "Error getting CUDA device list. "
+ "SCCudaGetDevices() returned NULL");
+ goto error;
+ }
+
+ SCCudaPrintBasicDeviceInfo(devices);
+
+ return 0;
+
+ error:
+ SCCudaDeAllocSCCudaDevices(devices);
+ return -1;
+}
+
+/**********************************Cuda_Utility********************************/
+
+/**
+ * \brief List the cuda cards on the system.
+ *
+ */
+void SCCudaListCards(void)
+{
+ int i = 0;
+
+ if (devices == NULL) {
+ SCLogWarning(SC_ERR_CUDA_ERROR, "CUDA engine not initalized! Please "
+ "initialize the cuda environment using "
+ "SCCudaInitCudaEnvironment().");
+ return;
+ }
+
+ printf("CUDA Cards recognized by the suricata CUDA module - \n");
+ printf("|-----------------------------------------------------------------------------|\n");
+ printf("| %-10s | %-20s | %-10s | %-10s | %-13s |\n",
+ "Device Id", " Device Name", " Multi-", "Clock Rate", "Cuda Compute");
+ printf("| %-10s | %-20s | %-10s | %-10s | %-13s |\n",
+ "", "", "Processors", " (MHz)", "Capability");
+ printf("|-----------------------------------------------------------------------------|\n");
+ for (i = 0; i < devices->count; i++) {
+ printf("| %-10d | %-20s | %-10d | %-10d | %d.%-11d |\n",
+ i,
+ devices->devices[i]->name,
+ devices->devices[i]->attr_multiprocessor_count,
+ devices->devices[i]->attr_clock_rate/1000,
+ devices->devices[i]->major_rev,
+ devices->devices[i]->minor_rev);
+ }
+ printf("|-----------------------------------------------------------------------------|\n");
+
+ return;
+}
+
+int SCCudaIsCudaDeviceIdValid(int cuda_device_id)
+{
+ if (devices == NULL) {
+ SCLogWarning(SC_ERR_CUDA_ERROR, "CUDA engine not initalized! Please "
+ "initialize the cuda environment using "
+ "SCCudaInitCudaEnvironment().");
+ return 0;
+ }
+
+ return (cuda_device_id < devices->count);
+}
+
+/**********************************Unittests***********************************/
+
+int SCCudaTest01(void)
+{
+ SCCudaDevices *devices = SCCudaGetDeviceList();
+
+ if (devices == NULL)
+ return 0;
+
+ return (devices->count != 0);
+}
+
+#if defined(__x86_64__) || defined(__ia64__)
+/**
+ * extern "C" __global__ void SCCudaSuricataTest(int *input, int *output)
+ * {
+ * output[threadIdx.x] = input[threadIdx.x] * 2;
+ * }
+ */
+static const char *sc_cuda_test_kernel_64_bit =
+ " .version 1.4\n"
+ " .target sm_10, map_f64_to_f32\n"
+ " .entry SCCudaSuricataTest (\n"
+ " .param .u64 __cudaparm_SCCudaSuricataTest_input,\n"
+ " .param .u64 __cudaparm_SCCudaSuricataTest_output)\n"
+ "{\n"
+ " .reg .u32 %r<5>;\n"
+ " .reg .u64 %rd<8>;\n"
+ " .loc 15 1 0\n"
+ " $LBB1_SCCudaSuricataTest:\n"
+ " .loc 15 3 0\n"
+ " cvt.u32.u16 %r1, %tid.x;\n"
+ " cvt.u64.u32 %rd1, %r1;\n"
+ " mul.lo.u64 %rd2, %rd1, 4;\n"
+ " ld.param.u64 %rd3, [__cudaparm_SCCudaSuricataTest_input];\n"
+ " add.u64 %rd4, %rd3, %rd2;\n"
+ " ld.global.s32 %r2, [%rd4+0];\n"
+ " mul.lo.s32 %r3, %r2, 2;\n"
+ " ld.param.u64 %rd5, [__cudaparm_SCCudaSuricataTest_output];\n"
+ " add.u64 %rd6, %rd5, %rd2;\n"
+ " st.global.s32 [%rd6+0], %r3;\n"
+ " .loc 15 4 0\n"
+ " exit;\n"
+ " $LDWend_SCCudaSuricataTest:\n"
+ "} // SCCudaSuricataTest\n"
+ "\n";
+#else
+/**
+ * extern "C" __global__ void SCCudaSuricataTest(int *input, int *output)
+ * {
+ * output[threadIdx.x] = input[threadIdx.x] * 2;
+ * }
+ */
+static const char *sc_cuda_test_kernel_32_bit =
+ " .version 1.4\n"
+ " .target sm_10, map_f64_to_f32\n"
+ " .entry SCCudaSuricataTest (\n"
+ " .param .u32 __cudaparm_SCCudaSuricataTest_input,\n"
+ " .param .u32 __cudaparm_SCCudaSuricataTest_output)\n"
+ " {\n"
+ " .reg .u16 %rh<3>;\n"
+ " .reg .u32 %r<9>;\n"
+ " .loc 15 2 0\n"
+ "$LBB1_SCCudaSuricataTest:\n"
+ " .loc 15 4 0\n"
+ " mov.u16 %rh1, %tid.x;\n"
+ " mul.wide.u16 %r1, %rh1, 4;\n"
+ " ld.param.u32 %r2, [__cudaparm_SCCudaSuricataTest_input];\n"
+ " add.u32 %r3, %r2, %r1;\n"
+ " ld.global.s32 %r4, [%r3+0];\n"
+ " mul.lo.s32 %r5, %r4, 2;\n"
+ " ld.param.u32 %r6, [__cudaparm_SCCudaSuricataTest_output];\n"
+ " add.u32 %r7, %r6, %r1;\n"
+ " st.global.s32 [%r7+0], %r5;\n"
+ " .loc 15 5 0\n"
+ " exit;\n"
+ "$LDWend_SCCudaSuricataTest:\n"
+ " } // SCCudaSuricataTest\n"
+ "";
+#endif
+
+int SCCudaTest02(void)
+{
+#define ALIGN_UP(offset, alignment) do { \
+ (offset) = ((offset) + (alignment) - 1) & ~((alignment) - 1); \
+ } while (0)
+#define N 256
+ CUcontext context;
+ CUmodule module;
+ CUfunction kernel;
+ CUdeviceptr d_input, d_output;
+ int h_input[N];
+ int h_result[N];
+ SCCudaDevices *devices = SCCudaGetDeviceList();
+ int result = 0;
+ int offset = 0;
+ int i = 0;
+
+ if (devices == NULL)
+ goto end;
+
+ if (devices->count == 0)
+ goto end;
+
+ if (SCCudaCtxCreate(&context, 0, devices->devices[0]->device) == -1)
+ goto end;
+
+#if defined(__x86_64__) || defined(__ia64__)
+ if (SCCudaModuleLoadData(&module, (void *)sc_cuda_test_kernel_64_bit) == -1)
+ goto end;
+#else
+ if (SCCudaModuleLoadData(&module, (void *)sc_cuda_test_kernel_32_bit) == -1)
+ goto end;
+#endif
+
+ if (SCCudaModuleGetFunction(&kernel, module, "SCCudaSuricataTest") == -1)
+ goto end;
+
+ for (i = 0; i < N; i++)
+ h_input[i] = i * 2;
+
+ if (SCCudaMemAlloc(&d_input, N * sizeof(int)) == -1)
+ goto end;
+
+ if (SCCudaMemcpyHtoD(d_input, h_input, N * sizeof(int)) == -1)
+ goto end;
+
+ if (SCCudaMemAlloc(&d_output, N * sizeof(int)) == -1)
+ goto end;
+
+ offset = 0;
+ ALIGN_UP(offset, __alignof(void *));
+ if (SCCudaParamSetv(kernel, offset, (void *)&d_input, sizeof(void *)) == -1)
+ goto end;
+ offset += sizeof(void *);
+
+ ALIGN_UP(offset, __alignof(void *));
+ if (SCCudaParamSetv(kernel, offset, (void *)&d_output, sizeof(void *)) == -1)
+ goto end;
+ offset += sizeof(void *);
+
+ if (SCCudaParamSetSize(kernel, offset) == -1)
+ goto end;
+
+ if (SCCudaFuncSetBlockShape(kernel, N, 1, 1) == -1)
+ goto end;
+
+ if (SCCudaLaunchGrid(kernel, 1, 1) == -1)
+ goto end;
+
+ if (SCCudaMemcpyDtoH(h_result, d_output, N * sizeof(int)) == -1)
+ goto end;
+
+ for (i = 0; i < N; i++)
+ h_input[i] = i * 4;
+
+ for (i = 0; i < N; i++) {
+ if (h_result[i] != h_input[i])
+ goto end;
+ }
+
+ if (SCCudaMemFree(d_input) == -1)
+ goto end;
+
+ if (SCCudaMemFree(d_output) == -1)
+ goto end;
+
+ if (SCCudaModuleUnload(module) == -1)
+ goto end;
+
+ if (SCCudaCtxDestroy(context) == -1)
+ goto end;
+
+ result = 1;
+
+ end:
+ return result;
+}
+
+void SCCudaRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SCCudaTest01", SCCudaTest01, 1);
+ UtRegisterTest("SCCudaTest02", SCCudaTest02, 1);
+#endif
+
+ return;
+}
+
+#endif /* __SC_CUDA_SUPPORT__ */
diff --git a/framework/src/suricata/src/util-cuda.h b/framework/src/suricata/src/util-cuda.h
new file mode 100644
index 00000000..8e544fd0
--- /dev/null
+++ b/framework/src/suricata/src/util-cuda.h
@@ -0,0 +1,323 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_CUDA__H__
+#define __UTIL_CUDA__H__
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include <cuda.h>
+
+#define SC_CUDA_DEFAULT_DEVICE 0
+#define SC_CUDA_DEVICE_NAME_MAX_LEN 128
+
+typedef struct SCCudaDevice_ {
+ /* device id */
+ CUdevice device;
+
+ /* device name */
+ char name[SC_CUDA_DEVICE_NAME_MAX_LEN];
+
+ /* device compute capability */
+ int major_rev;
+ int minor_rev;
+
+ /* device properties */
+ CUdevprop prop;
+
+ /* device total memory */
+ size_t bytes;
+
+ /* device attributes. We could have used a fixed int array table to hold
+ * the attributes, but it is better we specify it exclusively this way,
+ * since the usage would be less error prone */
+ int attr_max_threads_per_block;
+ int attr_max_block_dim_x;
+ int attr_max_block_dim_y;
+ int attr_max_block_dim_z;
+ int attr_max_grid_dim_x;
+ int attr_max_grid_dim_y;
+ int attr_max_grid_dim_z;
+ int attr_max_shared_memory_per_block;
+ int attr_total_constant_memory;
+ int attr_warp_size;
+ int attr_max_pitch;
+ int attr_max_registers_per_block;
+ int attr_clock_rate;
+ int attr_texture_alignment;
+ int attr_gpu_overlap;
+ int attr_multiprocessor_count;
+ int attr_kernel_exec_timeout;
+ int attr_integrated;
+ int attr_can_map_host_memory;
+ int attr_compute_mode;
+} SCCudaDevice;
+
+
+typedef struct SCCudaDevices_ {
+ int count;
+ SCCudaDevice **devices;
+} SCCudaDevices;
+
+
+/**************************Cuda_Initialization_API**************************/
+int SCCudaInit(unsigned int flags);
+
+/***************************Version_Management_API***************************/
+int SCCudaDriverGetVersion(int *driver_version);
+
+/***************************Device_Management_API****************************/
+int SCCudaDeviceComputeCapability(int *major, int *minor, CUdevice dev);
+int SCCudaDeviceGet(CUdevice *device, int ordinal);
+int SCCudaDeviceGetAttribute(int *pi, CUdevice_attribute attrib,
+ CUdevice dev);
+int SCCudaDeviceGetCount(int *count);
+int SCCudaDeviceGetName(char *name, int len, CUdevice dev);
+int SCCudaDeviceGetProperties(CUdevprop *prop, CUdevice dev);
+int SCCudaDeviceTotalMem(size_t *bytes, CUdevice dev);
+
+void SCCudaPrintDeviceList(SCCudaDevices *);
+void SCCudaPrintBasicDeviceInfo(SCCudaDevices *);
+SCCudaDevices *SCCudaGetDeviceList(void);
+
+/***************************Context_Management_API***************************/
+int SCCudaCtxCreate(CUcontext *pctx, unsigned int flags, CUdevice dev);
+int SCCudaCtxDestroy(CUcontext ctx);
+int SCCudaCtxGetApiVersion(CUcontext ctx, unsigned int *version);
+int SCCudaCtxGetCacheConfig(CUfunc_cache *pconfig);
+int SCCudaCtxGetCurrent(CUcontext *pctx);
+int SCCudaCtxGetDevice(CUdevice *device);
+int SCCudaCtxGetLimit(size_t *pvalue, CUlimit limit);
+int SCCudaCtxPopCurrent(CUcontext *pctx);
+int SCCudaCtxPushCurrent(CUcontext ctx);
+int SCCudaCtxSetCacheConfig(CUfunc_cache config);
+int SCCudaCtxSetCurrent(CUcontext ctx);
+int SCCudaCtxSetLimit(CUlimit limit, size_t value);
+int SCCudaCtxSynchronize(void);
+int SCCudaCtxAttach(CUcontext *pctx, unsigned int flags);
+int SCCudaCtxDetach(CUcontext ctx);
+
+/***************************Module_Management_API****************************/
+int SCCudaModuleGetFunction(CUfunction *hfunc, CUmodule hmod,
+ const char *name);
+int SCCudaModuleGetGlobal(CUdeviceptr *dptr, size_t *bytes, CUmodule hmod,
+ const char *name);
+int SCCudaModuleGetSurfRef(CUsurfref *p_surf_ref, CUmodule hmod,
+ const char *name);
+int SCCudaModuleGetTexRef(CUtexref *p_tex_ref, CUmodule hmod,
+ const char *name);
+int SCCudaModuleLoad(CUmodule *module, const char *fname);
+int SCCudaModuleLoadData(CUmodule *module, const void *image);
+int SCCudaModuleLoadDataEx(CUmodule *module, const void *image,
+ unsigned int num_options, CUjit_option *options,
+ void **option_values);
+int SCCudaModuleLoadFatBinary(CUmodule *module, const void *fat_cubin);
+int SCCudaModuleUnload(CUmodule hmod);
+
+/**************************Memory_Management_API*****************************/
+int SCCudaArray3DCreate(CUarray *p_handle,
+ const CUDA_ARRAY3D_DESCRIPTOR *p_allocate_array);
+int SCCudaArray3DGetDescriptor(CUDA_ARRAY3D_DESCRIPTOR *p_array_descriptor,
+ CUarray h_array);
+int SCCudaArrayCreate(CUarray *p_handle,
+ const CUDA_ARRAY_DESCRIPTOR *p_allocate_array);
+int SCCudaArrayDestroy(CUarray h_array);
+int SCCudaArrayGetDescriptor(CUDA_ARRAY_DESCRIPTOR *p_array_descriptor,
+ CUarray h_array);
+int SCCudaDeviceGetByPCIBusId(CUdevice *dev, char *pci_bus_id);
+int SCCudaDeviceGetPCIBusId(char *pci_bus_id, int len, CUdevice dev);
+int SCCudaIpcCloseMemHandle(CUdeviceptr dptr);
+int SCCudaIpcGetEventHandle(CUipcEventHandle *p_handle, CUevent event);
+int SCCudaIpcGetMemHandle(CUipcMemHandle *p_handle, CUdeviceptr dptr);
+int SCCudaIpcOpenEventHandle(CUevent *ph_event, CUipcEventHandle handle);
+int SCCudaIpcOpenMemHandle(CUdeviceptr *pdptr, CUipcMemHandle handle,
+ unsigned int flags);
+int SCCudaMemAlloc(CUdeviceptr *dptr, size_t byte_size);
+int SCCudaMemAllocHost(void **pp, size_t byte_size);
+int SCCudaMemAllocPitch(CUdeviceptr *dptr, size_t *p_pitch,
+ size_t width_in_bytes,
+ size_t height,
+ unsigned int element_size_bytes);
+int SCCudaMemcpy(CUdeviceptr dst, CUdeviceptr src, size_t byte_count);
+int SCCudaMemcpy2D(const CUDA_MEMCPY2D *p_copy);
+int SCCudaMemcpy2DAsync(const CUDA_MEMCPY2D *p_copy, CUstream h_stream);
+int SCCudaMemcpy2DUnaligned(const CUDA_MEMCPY2D *p_copy);
+int SCCudaMemcpy3D(const CUDA_MEMCPY3D *p_copy);
+int SCCudaMemcpy3DAsync(const CUDA_MEMCPY3D *p_copy, CUstream h_stream);
+int SCCudaMemcpy3DPeer(const CUDA_MEMCPY3D_PEER *p_copy);
+int SCCudaMemcpy3DPeerAsync(const CUDA_MEMCPY3D_PEER *p_copy,
+ CUstream h_stream);
+int SCCudaMemcpyAsync(CUdeviceptr dst, CUdeviceptr src, size_t byte_count,
+ CUstream h_stream);
+int SCCudaMemcpyAtoA(CUarray dst_array, size_t dst_offset,
+ CUarray src_array, size_t src_offset,
+ size_t byte_count);
+int SCCudaMemcpyAtoD(CUdeviceptr dst_device, CUarray src_array,
+ size_t src_offset, size_t byte_count);
+int SCCudaMemcpyAtoH(void *dst_host, CUarray src_array, size_t src_offset,
+ size_t byte_count);
+int SCCudaMemcpyAtoHAsync(void *dst_host, CUarray src_array,
+ size_t src_offset, size_t byte_count,
+ CUstream h_stream);
+int SCCudaMemcpyDtoA(CUarray dst_array, size_t dst_offset,
+ CUdeviceptr src_device, size_t byte_count);
+int SCCudaMemcpyDtoD(CUdeviceptr dst_device, CUdeviceptr src_device,
+ size_t byte_count);
+int SCCudaMemcpyDtoDAsync(CUdeviceptr dst_device, CUdeviceptr src_device,
+ size_t byte_count, CUstream h_stream);
+int SCCudaMemcpyDtoH(void *dst_host, CUdeviceptr src_device,
+ size_t byte_count);
+int SCCudaMemcpyDtoHAsync(void *dst_host, CUdeviceptr src_device,
+ size_t byte_count, CUstream h_stream);
+int SCCudaMemcpyHtoA(CUarray dst_array, size_t dst_offset,
+ const void *src_host, size_t byte_count);
+int SCCudaMemcpyHtoAAsync(CUarray dst_array, size_t dst_offset,
+ const void *src_host, size_t byte_count,
+ CUstream h_stream);
+int SCCudaMemcpyHtoD(CUdeviceptr dst_device, const void *src_host,
+ size_t byte_count);
+int SCCudaMemcpyHtoDAsync(CUdeviceptr dst_device, const void *src_host,
+ size_t byte_count, CUstream h_stream);
+int SCCudaMemcpyPeer(CUdeviceptr dst_device, CUcontext dst_context,
+ CUdeviceptr src_device, CUcontext src_context,
+ size_t byte_count);
+int SCCudaMemcpyPeerAsync(CUdeviceptr dst_device, CUcontext dst_context,
+ CUdeviceptr src_device, CUcontext src_context,
+ size_t byte_count, CUstream h_stream);
+int SCCudaMemFree(CUdeviceptr dptr);
+int SCCudaMemFreeHost(void *p);
+int SCCudaMemGetAddressRange(CUdeviceptr *pbase, size_t *psize,
+ CUdeviceptr dptr);
+int SCCudaMemGetInfo(size_t *free, size_t *total);
+int SCCudaMemHostAlloc(void **pp, size_t byte_size, unsigned int flags);
+int SCCudaMemHostGetDevicePointer(CUdeviceptr *pdptr, void *p,
+ unsigned int flags);
+int SCCudaMemHostGetFlags(unsigned int *p_flags, void *p);
+int SCCudaMemHostRegister(void *p, size_t byte_size, unsigned int flags);
+int SCCudaMemHostUnregister(void *p);
+int SCCudaMemsetD16(CUdeviceptr dst_device, unsigned short us, size_t n);
+int SCCudaMemsetD16Async(CUdeviceptr dst_device, unsigned short us,
+ size_t n, CUstream h_stream);
+int SCCudaMemsetD2D16(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned short us, size_t width,
+ size_t height);
+int SCCudaMemsetD2D16Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned short us, size_t width,
+ size_t height, CUstream h_stream);
+int SCCudaMemsetD2D32(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned int ui, size_t width, size_t height);
+int SCCudaMemsetD2D32Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned int ui, size_t width, size_t height,
+ CUstream h_stream);
+int SCCudaMemsetD2D8(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned char uc, size_t width, size_t height);
+int SCCudaMemsetD2D8Async(CUdeviceptr dst_device, size_t dst_pitch,
+ unsigned char uc, size_t width, size_t height,
+ CUstream h_stream);
+int SCCudaMemsetD32(CUdeviceptr dst_device, unsigned int ui, size_t n);
+int SCCudaMemsetD32Async(CUdeviceptr dst_device, unsigned int ui,
+ size_t n, CUstream h_stream);
+int SCCudaMemsetD8(CUdeviceptr dst_device, unsigned char uc, size_t n);
+int SCCudaMemsetD8Async(CUdeviceptr dst_device, unsigned char uc,
+ size_t n, CUstream h_stream);
+
+/***************************Unified_Addressing_API****************************/
+
+int SCCudaPointerGetAttribute(void *data, CUpointer_attribute attribute,
+ CUdeviceptr ptr);
+
+/***************************Stream_Management_API****************************/
+int SCCudaStreamCreate(CUstream *ph_stream, unsigned int flags);
+int SCCudaStreamDestroy(CUstream h_stream);
+int SCCudaStreamQuery(CUstream h_stream);
+int SCCudaStreamSynchronize(CUstream h_stream);
+int SCCudaStreamWaitEvent(CUstream h_stream, CUevent h_event,
+ unsigned int flags);
+
+/***************************Event_Management_API*****************************/
+int SCCudaEventCreate(CUevent *ph_event, unsigned int flags);
+int SCCudaEventDestroy(CUevent h_event);
+int SCCudaEventElapsedTime(float *p_milli_seconds, CUevent h_start,
+ CUevent h_end);
+int SCCudaEventQuery(CUevent h_event);
+int SCCudaEventRecord(CUevent h_event, CUstream h_stream);
+int SCCudaEventSynchronize(CUevent h_event);
+
+/***********************Execution_Control_Management_API***********************/
+int SCCudaFuncGetAttribute(int *pi, CUfunction_attribute attrib,
+ CUfunction hfunc);
+int SCCudaFuncSetCacheConfig(CUfunction hfunc, CUfunc_cache config);
+int SCCudaLaunchKernel(CUfunction f, unsigned int grid_dim_x,
+ unsigned int grid_dim_y, unsigned int grid_dim_z,
+ unsigned int block_dim_x, unsigned int block_dim_y,
+ unsigned int block_dim_z, unsigned int shared_mem_bytes,
+ CUstream h_stream, void **kernel_params, void **extra);
+int SCCudaFuncSetBlockShape(CUfunction hfunc, int x, int y, int z);
+int SCCudaFuncSetSharedSize(CUfunction hfunc, unsigned int bytes);
+int SCCudaLaunch(CUfunction f);
+int SCCudaLaunchGrid(CUfunction f, int grid_width, int grid_height);
+int SCCudaLaunchGridAsync(CUfunction f, int grid_width, int grid_height,
+ CUstream h_stream);
+int SCCudaParamSetf(CUfunction h_func, int offset, float value);
+int SCCudaParamSeti(CUfunction h_func, int offset, unsigned int value);
+int SCCudaParamSetSize(CUfunction h_func, unsigned int num_bytes);
+int SCCudaParamSetTexRef(CUfunction h_func, int tex_unit, CUtexref h_tex_ref);
+int SCCudaParamSetv(CUfunction h_func, int offset, void *ptr,
+ unsigned int num_bytes);
+
+/*********************Texture_Reference_Management_API***********************/
+int SCCudaTexRefCreate(CUtexref *p_tex_ref);
+int SCCudaTexRefDestroy(CUtexref h_tex_ref);
+int SCCudaTexRefGetAddress(CUdeviceptr *pdptr, CUtexref h_tex_ref);
+int SCCudaTexRefGetAddressMode(CUaddress_mode *pam, CUtexref h_tex_ref,
+ int dim);
+int SCCudaTexRefGetArray(CUarray *ph_array, CUtexref h_tex_ref);
+int SCCudaTexRefGetFilterMode(CUfilter_mode *pfm, CUtexref h_tex_ref);
+int SCCudaTexRefGetFlags(unsigned int *p_flags, CUtexref h_tex_ref);
+int SCCudaTexRefGetFormat(CUarray_format *p_format, int *p_num_channels,
+ CUtexref h_tex_ref);
+int SCCudaTexRefSetAddress(size_t *byte_offset, CUtexref h_tex_ref,
+ CUdeviceptr dptr, unsigned int bytes);
+int SCCudaTexRefSetAddress2D(CUtexref h_tex_ref,
+ const CUDA_ARRAY_DESCRIPTOR *desc,
+ CUdeviceptr dptr, unsigned int pitch);
+int SCCudaTexRefSetAddressMode(CUtexref h_tex_ref, int dim, CUaddress_mode am);
+int SCCudaTexRefSetArray(CUtexref h_tex_ref, CUarray h_array,
+ unsigned int flags);
+int SCCudaTexRefSetFilterMode(CUtexref h_tex_ref, CUfilter_mode fm);
+int SCCudaTexRefSetFlags(CUtexref h_tex_ref, unsigned int flags);
+int SCCudaTexRefSetFormat(CUtexref h_tex_ref, CUarray_format fmt,
+ int num_packed_components);
+
+/************************Cuda_Env_Initialization_API*************************/
+int SCCudaInitCudaEnvironment(void);
+
+/********************************Cuda_Utility********************************/
+void SCCudaListCards(void);
+int SCCudaIsCudaDeviceIdValid(int cuda_device_id);
+
+/********************************Unittests***********************************/
+void SCCudaRegisterTests(void);
+
+#endif /* __SC_CUDA_SUPPORT__ */
+#endif /* __UTIL_CUDA_H__ */
diff --git a/framework/src/suricata/src/util-daemon.c b/framework/src/suricata/src/util-daemon.c
new file mode 100644
index 00000000..2eec7af1
--- /dev/null
+++ b/framework/src/suricata/src/util-daemon.c
@@ -0,0 +1,195 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias Galvan <iglesiasg@gmail.com>
+ *
+ * Daemonization process
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "runmodes.h"
+#include "util-daemon.h"
+#include "util-debug.h"
+#include "conf.h"
+
+#ifndef OS_WIN32
+
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static volatile sig_atomic_t sigflag = 0;
+
+/**
+ * \brief Signal handler used to take the parent process out of stand-by
+ */
+static void SignalHandlerSigusr1 (int signo)
+{
+ sigflag = 1;
+}
+
+/**
+ * \brief Tell the parent process the child is ready
+ *
+ * \param pid pid of the parent process to signal
+ */
+static void TellWaitingParent (pid_t pid)
+{
+ kill(pid, SIGUSR1);
+}
+
+/**
+ * \brief Set the parent on stand-by until the child is ready
+ *
+ * \param pid pid of the child process to wait
+ */
+static void WaitForChild (pid_t pid)
+{
+ int status;
+ SCLogDebug("Daemon: Parent waiting for child to be ready...");
+ /* Wait until child signals is ready */
+ while (sigflag == 0) {
+ if (waitpid(pid, &status, WNOHANG)) {
+ /* Check if the child is still there, otherwise the parent should exit */
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ SCLogError(SC_ERR_DAEMON, "Child died unexpectedly");
+ exit(EXIT_FAILURE);
+ }
+ }
+ /* sigsuspend(); */
+ sleep(1);
+ }
+}
+
+/**
+ * \brief Close stdin, stdout, stderr.Redirect logging info to syslog
+ *
+ */
+static void SetupLogging (void)
+{
+ /* Redirect stdin, stdout, stderr to /dev/null */
+ int fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ return;
+ if (dup2(fd, 0) < 0)
+ return;
+ if (dup2(fd, 1) < 0)
+ return;
+ if (dup2(fd, 2) < 0)
+ return;
+ close(fd);
+}
+
+/**
+ * \brief Daemonize the process
+ *
+ */
+void Daemonize (void)
+{
+ pid_t pid, sid;
+
+ /* Register the signal handler */
+ signal(SIGUSR1, SignalHandlerSigusr1);
+
+ /** \todo We should check if wie allow more than 1 instance
+ to run simultaneously. Maybe change the behaviour
+ through conf file */
+
+ /* Creates a new process */
+ pid = fork();
+
+ if (pid < 0) {
+ /* Fork error */
+ SCLogError(SC_ERR_DAEMON, "Error forking the process");
+ exit(EXIT_FAILURE);
+ } else if (pid == 0) {
+ /* Child continues here */
+ char *daemondir;
+
+ umask(027);
+
+ sid = setsid();
+ if (sid < 0) {
+ SCLogError(SC_ERR_DAEMON, "Error creating new session");
+ exit(EXIT_FAILURE);
+ }
+
+ if (ConfGet("daemon-directory", &daemondir) == 1) {
+ if ((chdir(daemondir)) < 0) {
+ SCLogError(SC_ERR_DAEMON, "Error changing to working directory");
+ exit(EXIT_FAILURE);
+ }
+ }
+#ifndef OS_WIN32
+ else {
+ if (chdir("/") < 0) {
+ SCLogError(SC_ERR_DAEMON, "Error changing to working directory '/'");
+ }
+ }
+#endif
+
+ SetupLogging();
+
+ /* Child is ready, tell its parent */
+ TellWaitingParent(getppid());
+
+ /* Daemon is up and running */
+ SCLogDebug("Daemon is running");
+ return;
+ }
+ /* Parent continues here, waiting for child to be ready */
+ SCLogDebug("Parent is waiting for child to be ready");
+ WaitForChild(pid);
+
+ /* Parent exits */
+ SCLogDebug("Child is ready, parent exiting");
+ exit(EXIT_SUCCESS);
+
+}
+
+#endif /* ifndef OS_WIN32 */
+
+/**
+ * \brief Check for a valid combination daemon/mode
+ *
+ * \param daemon daemon on or off
+ * \param mode selected mode
+ *
+ * \retval 1 valid combination
+ * \retval 0 invalid combination
+ */
+int CheckValidDaemonModes (int daemon, int mode)
+{
+ if (daemon) {
+ switch (mode) {
+ case RUNMODE_PCAP_FILE:
+ SCLogError(SC_ERR_INVALID_RUNMODE, "ERROR: pcap offline mode cannot run as daemon");
+ return 0;
+ case RUNMODE_UNITTEST:
+ SCLogError(SC_ERR_INVALID_RUNMODE, "ERROR: unittests cannot run as daemon");
+ return 0;
+ default:
+ SCLogDebug("Allowed mode");
+ break;
+ }
+ }
+ return 1;
+}
diff --git a/framework/src/suricata/src/util-daemon.h b/framework/src/suricata/src/util-daemon.h
new file mode 100644
index 00000000..ef8dc725
--- /dev/null
+++ b/framework/src/suricata/src/util-daemon.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gerardo Iglesias Galvan <iglesiasg@gmail.com>
+ */
+
+#ifndef __UTIL_DAEMON_H__
+#define __UTIL_DAEMON_H__
+
+/** \todo Adjust path */
+#define DAEMON_WORKING_DIRECTORY "/"
+
+#ifdef OS_WIN32
+#define Daemonize()
+#else
+void Daemonize (void);
+#endif
+
+int CheckValidDaemonModes (int, int);
+
+#endif /* __UTIL_DAEMON_H__ */
diff --git a/framework/src/suricata/src/util-debug-filters.c b/framework/src/suricata/src/util-debug-filters.c
new file mode 100644
index 00000000..6c06b0af
--- /dev/null
+++ b/framework/src/suricata/src/util-debug-filters.c
@@ -0,0 +1,1009 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Debug filter utility functions
+ */
+
+#include "suricata-common.h"
+
+/* both of these are defined in util-debug.c */
+extern int sc_log_module_initialized;
+extern int sc_log_module_cleaned;
+
+/* used to indicate if any FG filters are registered */
+int sc_log_fg_filters_present = 0;
+
+/* used to indicate if any FD filters are registered */
+int sc_log_fd_filters_present = 0;
+
+/**
+ * \brief Holds the fine-grained filters
+ */
+SCLogFGFilterFile *sc_log_fg_filters[SC_LOG_FILTER_MAX] = { NULL, NULL };
+
+/**
+ * \brief Mutex for accessing the fine-grained fiters sc_log_fg_filters
+ */
+static SCMutex sc_log_fg_filters_m[SC_LOG_FILTER_MAX] = { SCMUTEX_INITIALIZER,
+ SCMUTEX_INITIALIZER };
+
+/**
+ * \brief Holds the function-dependent filters
+ */
+static SCLogFDFilter *sc_log_fd_filters = NULL;
+
+/**
+ * \brief Mutex for accessing the function-dependent filters sc_log_fd_filters
+ */
+static SCMutex sc_log_fd_filters_m = SCMUTEX_INITIALIZER;
+
+/**
+ * \brief Holds the thread_list required by function-dependent filters
+ */
+static SCLogFDFilterThreadList *sc_log_fd_filters_tl = NULL;
+
+/**
+ * \brief Mutex for accessing the FD thread_list sc_log_fd_filters_tl
+ */
+static SCMutex sc_log_fd_filters_tl_m = SCMUTEX_INITIALIZER;
+
+/**
+ * \brief Helper function used internally to add a FG filter
+ *
+ * \param file File_name of the filter
+ * \param function Function_name of the filter
+ * \param line Line number of the filter
+ * \param listtype The filter listtype. Can be either a blacklist or whitelist
+ * filter listtype(SC_LOG_FILTER_BL or SC_LOG_FILTER_WL)
+ *
+ * \retval 0 on successfully adding the filter;
+ * \retval -1 on failure
+ */
+int SCLogAddFGFilter(const char *file, const char *function,
+ int line, int listtype)
+{
+ SCLogFGFilterFile *fgf_file = NULL;
+ SCLogFGFilterFile *prev_fgf_file = NULL;
+
+ SCLogFGFilterFunc *fgf_func = NULL;
+ SCLogFGFilterFunc *prev_fgf_func = NULL;
+
+ SCLogFGFilterLine *fgf_line = NULL;
+ SCLogFGFilterLine *prev_fgf_line = NULL;
+
+ int found = 0;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return -1 ;
+ }
+
+ if (file == NULL && function == NULL && line < 0) {
+ printf("Error: Invalid arguments supplied to SCLogAddFGFilter\n");
+ return -1;
+ }
+
+ SCMutex *m = &sc_log_fg_filters_m[listtype];
+
+ SCMutexLock(m);
+
+ fgf_file = sc_log_fg_filters[listtype];
+
+ prev_fgf_file = fgf_file;
+ while (fgf_file != NULL) {
+ prev_fgf_file = fgf_file;
+ if (file == NULL && fgf_file->file == NULL)
+ found = 1;
+ else if (file != NULL && fgf_file->file != NULL)
+ found = (strcmp(file, fgf_file->file) == 0);
+ else
+ found = 0;
+
+ if (found == 1)
+ break;
+
+ fgf_file = fgf_file->next;
+ }
+
+ if (found == 0) {
+ SCLogAddToFGFFileList(prev_fgf_file, file, function, line, listtype);
+ goto done;
+ }
+
+ found = 0;
+ fgf_func = fgf_file->func;
+ prev_fgf_func = fgf_func;
+ while (fgf_func != NULL) {
+ prev_fgf_func = fgf_func;
+ if (function == NULL && fgf_func->func == NULL)
+ found = 1;
+ else if (function != NULL && fgf_func->func != NULL)
+ found = (strcmp(function, fgf_func->func) == 0);
+ else
+ found = 0;
+
+ if (found == 1)
+ break;
+
+ fgf_func = fgf_func->next;
+ }
+
+ if (found == 0) {
+ SCLogAddToFGFFuncList(fgf_file, prev_fgf_func, function, line);
+ goto done;
+ }
+
+ found = 0;
+ fgf_line = fgf_func->line;
+ prev_fgf_line = fgf_line;
+ while(fgf_line != NULL) {
+ prev_fgf_line = fgf_line;
+ if (line == fgf_line->line) {
+ found = 1;
+ break;
+ }
+
+ fgf_line = fgf_line->next;
+ }
+
+ if (found == 0) {
+ SCLogAddToFGFLineList(fgf_func, prev_fgf_line, line);
+ goto done;
+ }
+
+ done:
+ SCMutexUnlock(&sc_log_fg_filters_m[listtype]);
+ sc_log_fg_filters_present = 1;
+
+ return 0;
+}
+
+/**
+ * \brief Internal function used to check for matches against registered FG
+ * filters. Checks if there is a match for the incoming log_message with
+ * any of the FG filters. Based on whether the filter type is whitelist
+ * or blacklist, the function allows the message to be logged or not.
+ *
+ * \param file File_name from where the log_message originated
+ * \param function Function_name from where the log_message originated
+ * \param line Line number from where the log_message originated
+ * \param listtype The filter listtype. Can be either a blacklist or whitelist
+ * filter listtype(SC_LOG_FILTER_BL or SC_LOG_FILTER_WL)
+ *
+ * \retval 1 if there is a match
+ * \retval 0 on no match
+ * \retval -1 on failure
+ */
+static int SCLogMatchFGFilter(const char *file, const char *function, int line,
+ int listtype)
+{
+ SCLogFGFilterFile *fgf_file = NULL;
+ SCLogFGFilterFunc *fgf_func = NULL;
+ SCLogFGFilterLine *fgf_line = NULL;
+ int match = 1;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return -1;
+ }
+
+ SCMutexLock(&sc_log_fg_filters_m[listtype]);
+
+ fgf_file = sc_log_fg_filters[listtype];
+
+ if (fgf_file == NULL) {
+ SCMutexUnlock(&sc_log_fg_filters_m[listtype]);
+ return 1;
+ }
+
+ while(fgf_file != NULL) {
+ match = 1;
+
+ match &= (fgf_file->file != NULL)? !strcmp(file, fgf_file->file): 1;
+
+ if (match == 0) {
+ fgf_file = fgf_file->next;
+ continue;
+ }
+
+ fgf_func = fgf_file->func;
+ while (fgf_func != NULL) {
+ match = 1;
+
+ match &= (fgf_func->func != NULL)? !strcmp(function, fgf_func->func): 1;
+
+ if (match == 0) {
+ fgf_func = fgf_func->next;
+ continue;
+ }
+
+ fgf_line = fgf_func->line;
+ while (fgf_line != NULL) {
+ match = 1;
+
+ match &= (fgf_line->line != -1)? (line == fgf_line->line): 1;
+
+ if (match == 1)
+ break;
+
+ fgf_line = fgf_line->next;
+ }
+
+ if (match == 1)
+ break;
+
+ fgf_func = fgf_func->next;
+ }
+
+ if (match == 1) {
+ SCMutexUnlock(&sc_log_fg_filters_m[listtype]);
+ if (listtype == SC_LOG_FILTER_WL)
+ return 1;
+ else
+ return 0;
+ }
+
+ fgf_file = fgf_file->next;
+ }
+
+ SCMutexUnlock(&sc_log_fg_filters_m[listtype]);
+
+ if (listtype == SC_LOG_FILTER_WL)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * \brief Checks if there is a match for the incoming log_message with any
+ * of the FG filters. If there is a match, it allows the message
+ * to be logged, else it rejects that message.
+ *
+ * \param file File_name from where the log_message originated
+ * \param function Function_name from where the log_message originated
+ * \param line Line number from where the log_message originated
+ *
+ * \retval 1 if there is a match
+ * \retval 0 on no match
+ * \retval -1 on failure
+ */
+int SCLogMatchFGFilterWL(const char *file, const char *function, int line)
+{
+ return SCLogMatchFGFilter(file, function, line, SC_LOG_FILTER_WL);
+}
+
+/**
+ * \brief Checks if there is a match for the incoming log_message with any
+ * of the FG filters. If there is a match it rejects the logging
+ * for that messages, else it allows that message to be logged
+ *
+ * \praram file File_name from where the log_message originated
+ * \param function Function_name from where the log_message originated
+ * \param line Line number from where the log_message originated
+ *
+ * \retval 1 if there is a match
+ * \retval 0 on no match
+ * \retval -1 on failure
+ */
+int SCLogMatchFGFilterBL(const char *file, const char *function, int line)
+{
+ return SCLogMatchFGFilter(file, function, line, SC_LOG_FILTER_BL);
+}
+
+/**
+ * \brief Adds a Whitelist(WL) fine-grained(FG) filter. A FG filter WL filter
+ * allows messages that match this filter, to be logged, while the filter
+ * is defined using a file_name, function_name and line_number.
+ *
+ * If a particular paramter in the fg-filter(file, function and line),
+ * shouldn't be considered while logging the message, one can supply
+ * NULL for the file_name or function_name and a negative line_no.
+ *
+ * \param file File_name of the filter
+ * \param function Function_name of the filter
+ * \param line Line number of the filter
+ *
+ * \retval 0 on successfully adding the filter;
+ * \retval -1 on failure
+ */
+int SCLogAddFGFilterWL(const char *file, const char *function, int line)
+{
+ return SCLogAddFGFilter(file, function, line, SC_LOG_FILTER_WL);
+}
+
+/**
+ * \brief Adds a Blacklist(BL) fine-grained(FG) filter. A FG filter BL filter
+ * allows messages that don't match this filter, to be logged, while the
+ * filter is defined using a file_name, function_name and line_number
+ *
+ * If a particular paramter in the fg-filter(file, function and line),
+ * shouldn't be considered while logging the message, one can supply
+ * NULL for the file_name or function_name and a negative line_no.
+ *
+ * \param file File_name of the filter
+ * \param function Function_name of the filter
+ * \param line Line number of the filter
+ *
+ * \retval 0 on successfully adding the filter
+ * \retval -1 on failure
+ */
+int SCLogAddFGFilterBL(const char *file, const char *function, int line)
+{
+ return SCLogAddFGFilter(file, function, line, SC_LOG_FILTER_BL);
+}
+
+void SCLogReleaseFGFilters(void)
+{
+ SCLogFGFilterFile *fgf_file = NULL;
+ SCLogFGFilterFunc *fgf_func = NULL;
+ SCLogFGFilterLine *fgf_line = NULL;
+
+ void *temp = NULL;
+
+ int i = 0;
+
+ for (i = 0; i < SC_LOG_FILTER_MAX; i++) {
+ SCMutexLock(&sc_log_fg_filters_m[i]);
+
+ fgf_file = sc_log_fg_filters[i];
+ while (fgf_file != NULL) {
+
+ fgf_func = fgf_file->func;
+ while (fgf_func != NULL) {
+
+ fgf_line = fgf_func->line;
+ while(fgf_line != NULL) {
+ temp = fgf_line;
+ fgf_line = fgf_line->next;
+ SCFree(temp);
+ }
+
+ if (fgf_func->func != NULL)
+ SCFree(fgf_func->func);
+ temp = fgf_func;
+ fgf_func = fgf_func->next;
+ SCFree(temp);
+ }
+
+ if (fgf_file->file != NULL)
+ SCFree(fgf_file->file);
+ temp = fgf_file;
+ fgf_file = fgf_file->next;
+ SCFree(temp);
+ }
+
+ SCMutexUnlock(&sc_log_fg_filters_m[i]);
+ sc_log_fg_filters[i] = NULL;
+ }
+
+ return;
+}
+
+/**
+ * \brief Prints the FG filters(both WL and BL). Used for debugging purposes.
+ *
+ * \retval count The no of FG filters
+ */
+int SCLogPrintFGFilters()
+{
+ SCLogFGFilterFile *fgf_file = NULL;
+ SCLogFGFilterFunc *fgf_func = NULL;
+ SCLogFGFilterLine *fgf_line = NULL;
+
+ int count = 0;
+ int i = 0;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return 0;
+ }
+
+#ifdef DEBUG
+ printf("Fine grained filters:\n");
+#endif
+
+ for (i = 0; i < SC_LOG_FILTER_MAX; i++) {
+ SCMutexLock(&sc_log_fg_filters_m[i]);
+
+ fgf_file = sc_log_fg_filters[i];
+ while (fgf_file != NULL) {
+
+ fgf_func = fgf_file->func;
+ while (fgf_func != NULL) {
+
+ fgf_line = fgf_func->line;
+ while(fgf_line != NULL) {
+#ifdef DEBUG
+ printf("%s - ", fgf_file->file);
+ printf("%s - ", fgf_func->func);
+ printf("%d\n", fgf_line->line);
+#endif
+
+ count++;
+
+ fgf_line = fgf_line->next;
+ }
+
+ fgf_func = fgf_func->next;
+ }
+
+ fgf_file = fgf_file->next;
+ }
+ SCMutexUnlock(&sc_log_fg_filters_m[i]);
+ }
+
+ return count;
+}
+
+
+
+/* --------------------------------------------------|--------------------------
+ * -------------------------- Code for the FD Filter |--------------------------
+ * --------------------------------------------------V--------------------------
+ */
+
+/**
+ * \brief Checks if there is a match for the incoming log_message with any
+ * of the FD filters
+ *
+ * \param function Function_name from where the log_message originated
+ *
+ * \retval 1 if there is a match
+ * \retval 0 on no match;
+ */
+int SCLogMatchFDFilter(const char *function)
+{
+#ifndef DEBUG
+ return 1;
+#else
+ SCLogFDFilterThreadList *thread_list = NULL;
+
+ pthread_t self = pthread_self();
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return 0;
+ }
+
+ SCMutexLock(&sc_log_fd_filters_tl_m);
+
+ if (sc_log_fd_filters_tl == NULL) {
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+ if (sc_log_fd_filters != NULL)
+ return 0;
+ return 1;
+ }
+
+ thread_list = sc_log_fd_filters_tl;
+ while (thread_list != NULL) {
+ if (pthread_equal(self, thread_list->t)) {
+ if (thread_list->entered > 0) {
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+ return 1;
+ }
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+ return 0;
+ }
+
+ thread_list = thread_list->next;
+ }
+
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+
+ return 0;
+#endif
+}
+
+/**
+ * \brief Updates a FD filter, based on whether the function that calls this
+ * function, is registered as a FD filter or not. This is called by
+ * a function only on its entry
+ *
+ * \param function Function_name from where the log_message originated
+ *
+ * \retval 1 Since it is a hack to get things working inside the macros
+ */
+int SCLogCheckFDFilterEntry(const char *function)
+{
+ SCLogFDFilter *curr = NULL;
+
+ SCLogFDFilterThreadList *thread_list = NULL;
+ SCLogFDFilterThreadList *thread_list_temp = NULL;
+
+ //pid_t self = syscall(SYS_gettid);
+ pthread_t self = pthread_self();
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return 0;
+ }
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ curr = sc_log_fd_filters;
+
+ while (curr != NULL) {
+ if (strcmp(function, curr->func) == 0)
+ break;
+
+ curr = curr->next;
+ }
+
+ if (curr == NULL) {
+ SCMutexUnlock(&sc_log_fd_filters_m);
+ return 1;
+ }
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+
+ SCMutexLock(&sc_log_fd_filters_tl_m);
+
+ thread_list = sc_log_fd_filters_tl;
+ while (thread_list != NULL) {
+ if (pthread_equal(self, thread_list->t))
+ break;
+
+ thread_list = thread_list->next;
+ }
+
+ if (thread_list != NULL) {
+ thread_list->entered++;
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+ return 1;
+ }
+
+ if ( (thread_list_temp = SCMalloc(sizeof(SCLogFDFilterThreadList))) == NULL) {
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+ return 0;
+ }
+ memset(thread_list_temp, 0, sizeof(SCLogFDFilterThreadList));
+
+ thread_list_temp->t = self;
+ thread_list_temp->entered++;
+
+ sc_log_fd_filters_tl = thread_list_temp;
+
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+
+ return 1;
+}
+
+/**
+ * \brief Updates a FD filter, based on whether the function that calls this
+ * function, is registered as a FD filter or not. This is called by
+ * a function only before its exit.
+ *
+ * \param function Function_name from where the log_message originated
+ *
+ */
+void SCLogCheckFDFilterExit(const char *function)
+{
+ SCLogFDFilter *curr = NULL;
+
+ SCLogFDFilterThreadList *thread_list = NULL;
+
+ //pid_t self = syscall(SYS_gettid);
+ pthread_t self = pthread_self();
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return;
+ }
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ curr = sc_log_fd_filters;
+
+ while (curr != NULL) {
+ if (strcmp(function, curr->func) == 0)
+ break;
+
+ curr = curr->next;
+ }
+
+ if (curr == NULL) {
+ SCMutexUnlock(&sc_log_fd_filters_m);
+ return;
+ }
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+
+ SCMutexLock(&sc_log_fd_filters_tl_m);
+
+ thread_list = sc_log_fd_filters_tl;
+ while (thread_list != NULL) {
+ if (pthread_equal(self, thread_list->t))
+ break;
+
+ thread_list = thread_list->next;
+ }
+
+ SCMutexUnlock(&sc_log_fd_filters_tl_m);
+
+ if (thread_list != NULL)
+ thread_list->entered--;
+
+ return;
+}
+
+/**
+ * \brief Adds a Function-Dependent(FD) filter
+ *
+ * \param Name of the function for which a FD filter has to be registered
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int SCLogAddFDFilter(const char *function)
+{
+ SCLogFDFilter *curr = NULL;
+ SCLogFDFilter *prev = NULL;
+ SCLogFDFilter *temp = NULL;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return -1;
+ }
+
+ if (function == NULL) {
+ printf("Invalid argument supplied to SCLogAddFDFilter\n");
+ return -1;
+ }
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ curr = sc_log_fd_filters;
+ while (curr != NULL) {
+ prev = curr;
+
+ if (strcmp(function, curr->func) == 0) {
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+ return 0;
+ }
+
+ curr = curr->next;
+ }
+
+ if ( (temp = SCMalloc(sizeof(SCLogFDFilter))) == NULL) {
+ printf("Error Allocating memory (SCMalloc)\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(temp, 0, sizeof(SCLogFDFilter));
+
+ if ( (temp->func = SCStrdup(function)) == NULL) {
+ printf("Error Allocating memory (SCStrdup)\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sc_log_fd_filters == NULL)
+ sc_log_fd_filters = temp;
+ /* clang thinks prev can be NULL, but it can't be unless
+ * sc_log_fd_filters is also NULL which is handled here.
+ * Doing this "fix" to shut clang up. */
+ else if (prev != NULL)
+ prev->next = temp;
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+ sc_log_fd_filters_present = 1;
+
+ return 0;
+}
+
+/**
+ * \brief Releases all the FD filters added to the logging module
+ */
+void SCLogReleaseFDFilters(void)
+{
+ SCLogFDFilter *fdf = NULL;
+ SCLogFDFilter *temp = NULL;
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ fdf = sc_log_fd_filters;
+ while (fdf != NULL) {
+ temp = fdf;
+ fdf = fdf->next;
+ SCLogReleaseFDFilter(temp);
+ }
+
+ sc_log_fd_filters = NULL;
+
+ SCMutexUnlock( &sc_log_fd_filters_m );
+
+ return;
+}
+
+/**
+ * \brief Removes a Function-Dependent(FD) filter
+ *
+ * \param Name of the function for which a FD filter has to be unregistered
+ *
+ * \retval 0 on success(the filter was removed or the filter was not present)
+ * \retval -1 on failure/error
+ */
+int SCLogRemoveFDFilter(const char *function)
+{
+ SCLogFDFilter *curr = NULL;
+ SCLogFDFilter *prev = NULL;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return -1 ;
+ }
+
+ if (function == NULL) {
+ printf("Invalid argument(s) supplied to SCLogRemoveFDFilter\n");
+ return -1;
+ }
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ if (sc_log_fd_filters == NULL) {
+ SCMutexUnlock(&sc_log_fd_filters_m);
+ return 0;
+ }
+
+ curr = sc_log_fd_filters;
+ prev = curr;
+ while (curr != NULL) {
+ if (strcmp(function, curr->func) == 0)
+ break;
+
+ prev = curr;
+ curr = curr->next;
+ }
+
+ if (curr == NULL) {
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+
+ return 0;
+ }
+
+ if (sc_log_fd_filters == curr)
+ sc_log_fd_filters = curr->next;
+ else
+ prev->next = curr->next;
+
+ SCLogReleaseFDFilter(curr);
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+
+ if (sc_log_fd_filters == NULL)
+ sc_log_fd_filters_present = 0;
+
+ return 0;
+}
+
+/**
+ * \brief Prints the FG filters(both WL and BL). Used for debugging purposes.
+ *
+ * \retval count The no of FG filters
+ */
+int SCLogPrintFDFilters(void)
+{
+ SCLogFDFilter *fdf = NULL;
+ int count = 0;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return 0;
+ }
+
+#ifdef DEBUG
+ printf("FD filters:\n");
+#endif
+
+ SCMutexLock(&sc_log_fd_filters_m);
+
+ fdf = sc_log_fd_filters;
+ while (fdf != NULL) {
+#ifdef DEBUG
+ printf("%s \n", fdf->func);
+#endif
+ fdf = fdf->next;
+ count++;
+ }
+
+ SCMutexUnlock(&sc_log_fd_filters_m);
+
+ return count;
+}
+
+/**
+ * \brief Helper function used internally to add a FG filter. This function is
+ * called when the file component of the incoming filter has no entry
+ * in the filter list.
+ *
+ * \param fgf_file The file component(basically the position in the list) from
+ * the filter list, after which the new filter has to be added
+ * \param file File_name of the filter
+ * \param function Function_name of the filter
+ * \param line Line number of the filter
+ * \param listtype The filter listtype. Can be either a blacklist or whitelist
+ * filter listtype(SC_LOG_FILTER_BL or SC_LOG_FILTER_WL)
+ */
+void SCLogAddToFGFFileList(SCLogFGFilterFile *fgf_file,
+ const char *file,
+ const char *function, int line,
+ int listtype)
+{
+ SCLogFGFilterFile *fgf_file_temp = NULL;
+ SCLogFGFilterFunc *fgf_func_temp = NULL;
+ SCLogFGFilterLine *fgf_line_temp = NULL;
+
+ if ( (fgf_file_temp = SCMalloc(sizeof(SCLogFGFilterFile))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFFileList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_file_temp, 0, sizeof(SCLogFGFilterFile));
+
+ if ( file != NULL && (fgf_file_temp->file = SCStrdup(file)) == NULL) {
+ printf("Error Allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (fgf_func_temp = SCMalloc(sizeof(SCLogFGFilterFunc))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFFileList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_func_temp, 0, sizeof(SCLogFGFilterFunc));
+
+ if ( function != NULL && (fgf_func_temp->func = SCStrdup(function)) == NULL) {
+ printf("Error Allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (fgf_line_temp = SCMalloc(sizeof(SCLogFGFilterLine))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFFileList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_line_temp, 0, sizeof(SCLogFGFilterLine));
+
+ fgf_line_temp->line = line;
+
+ /* add to the lists */
+ fgf_func_temp->line = fgf_line_temp;
+
+ fgf_file_temp->func = fgf_func_temp;
+
+ if (fgf_file == NULL)
+ sc_log_fg_filters[listtype] = fgf_file_temp;
+ else
+ fgf_file->next = fgf_file_temp;
+
+ return;
+}
+
+/**
+ * \brief Helper function used internally to add a FG filter. This function is
+ * called when the file component of the incoming filter has an entry
+ * in the filter list, but the function component doesn't have an entry
+ * for the corresponding file component
+ *
+ * \param fgf_file The file component from the filter list to which the new
+ * filter has to be added
+ * \param fgf_func The function component(basically the position in the list),
+ * from the filter list, after which the new filter has to be
+ * added
+ * \param function Function_name of the filter
+ * \param line Line number of the filter
+ */
+void SCLogAddToFGFFuncList(SCLogFGFilterFile *fgf_file,
+ SCLogFGFilterFunc *fgf_func,
+ const char *function, int line)
+{
+ SCLogFGFilterFunc *fgf_func_temp = NULL;
+ SCLogFGFilterLine *fgf_line_temp = NULL;
+
+ if ( (fgf_func_temp = SCMalloc(sizeof(SCLogFGFilterFunc))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFFuncList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_func_temp, 0, sizeof(SCLogFGFilterFunc));
+
+ if ( function != NULL && (fgf_func_temp->func = SCStrdup(function)) == NULL) {
+ printf("Error Allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (fgf_line_temp = SCMalloc(sizeof(SCLogFGFilterLine))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFFuncList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_line_temp, 0, sizeof(SCLogFGFilterLine));
+
+ fgf_line_temp->line = line;
+
+ /* add to the lists */
+ fgf_func_temp->line = fgf_line_temp;
+
+ if (fgf_func == NULL)
+ fgf_file->func = fgf_func_temp;
+ else
+ fgf_func->next = fgf_func_temp;
+
+ return;
+}
+
+/**
+ * \brief Helper function used internally to add a FG filter. This function is
+ * called when the file and function components of the incoming filter
+ * have an entry in the filter list, but the line component doesn't have
+ * an entry for the corresponding function component
+ *
+ * \param fgf_func The function component from the filter list to which the new
+ * filter has to be added
+ * \param fgf_line The function component(basically the position in the list),
+ * from the filter list, after which the new filter has to be
+ * added
+ * \param line Line number of the filter
+ */
+void SCLogAddToFGFLineList(SCLogFGFilterFunc *fgf_func,
+ SCLogFGFilterLine *fgf_line,
+ int line)
+{
+ SCLogFGFilterLine *fgf_line_temp = NULL;
+
+ if ( (fgf_line_temp = SCMalloc(sizeof(SCLogFGFilterLine))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAddToFGFLineList. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(fgf_line_temp, 0, sizeof(SCLogFGFilterLine));
+
+ fgf_line_temp->line = line;
+
+ /* add to the lists */
+ if (fgf_line == NULL)
+ fgf_func->line = fgf_line_temp;
+ else
+ fgf_line->next = fgf_line_temp;
+
+ return;
+}
+
+/**
+ * \brief Releases the memory alloted to a FD filter
+ *
+ * \param Pointer to the FD filter that has to be freed
+ */
+void SCLogReleaseFDFilter(SCLogFDFilter *fdf)
+{
+ if (fdf != NULL) {
+ if (fdf->func != NULL)
+ SCFree(fdf->func);
+ SCFree(fdf);
+ }
+
+ return;
+}
+
diff --git a/framework/src/suricata/src/util-debug-filters.h b/framework/src/suricata/src/util-debug-filters.h
new file mode 100644
index 00000000..bbbcf9af
--- /dev/null
+++ b/framework/src/suricata/src/util-debug-filters.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __DEBUG_FILTERS_H__
+#define __DEBUG_FILTERS_H__
+
+#include <pthread.h>
+#include "threads.h"
+#include "util-mem.h"
+
+/**
+ * \brief Enum that holds the different kinds of filters available
+ */
+enum {
+ SC_LOG_FILTER_BL = 0,
+ SC_LOG_FILTER_WL = 1,
+ SC_LOG_FILTER_MAX = 2,
+};
+
+/**
+ * \brief Structure used to hold the line_no details of a FG filter
+ */
+typedef struct SCLogFGFilterLine_ {
+ int line;
+
+ struct SCLogFGFilterLine_ *next;
+} SCLogFGFilterLine;
+
+/**
+ * \brief structure used to hold the function details of a FG filter
+ */
+typedef struct SCLogFGFilterFunc_ {
+ char *func;
+ SCLogFGFilterLine *line;
+
+ struct SCLogFGFilterFunc_ *next;
+} SCLogFGFilterFunc;
+
+/**
+ * \brief Structure used to hold FG filters. Encapsulates filename details,
+ * func details, which inturn encapsulates the line_no details
+ */
+typedef struct SCLogFGFilterFile_ {
+ char *file;
+ SCLogFGFilterFunc *func;
+
+ struct SCLogFGFilterFile_ *next;
+} SCLogFGFilterFile;
+
+/**
+ * \brief Structure used to hold the thread_list used by FD filters
+ */
+typedef struct SCLogFDFilterThreadList_ {
+ int entered;
+ pthread_t t;
+// pid_t t;
+
+ struct SCLogFDFilterThreadList_ *next;
+} SCLogFDFilterThreadList;
+
+/**
+ * \brief Structure that holds the FD filters
+ */
+typedef struct SCLogFDFilter_ {
+ char *func;
+
+ struct SCLogFDFilter_ *next;
+} SCLogFDFilter;
+
+
+extern int sc_log_fg_filters_present;
+
+extern int sc_log_fd_filters_present;
+
+
+int SCLogAddFGFilterWL(const char *, const char *, int);
+
+int SCLogAddFGFilterBL(const char *, const char *, int);
+
+int SCLogMatchFGFilterBL(const char *, const char *, int);
+
+int SCLogMatchFGFilterWL(const char *, const char *, int);
+
+void SCLogReleaseFGFilters(void);
+
+int SCLogAddFDFilter(const char *);
+
+int SCLogPrintFDFilters(void);
+
+void SCLogReleaseFDFilters(void);
+
+int SCLogRemoveFDFilter(const char *);
+
+int SCLogCheckFDFilterEntry(const char *);
+
+void SCLogCheckFDFilterExit(const char *);
+
+int SCLogMatchFDFilter(const char *);
+
+int SCLogPrintFGFilters(void);
+
+void SCLogAddToFGFFileList(SCLogFGFilterFile *,
+ const char *,
+ const char *, int,
+ int);
+
+void SCLogAddToFGFFuncList(SCLogFGFilterFile *,
+ SCLogFGFilterFunc *,
+ const char *, int);
+
+void SCLogAddToFGFLineList(SCLogFGFilterFunc *,
+ SCLogFGFilterLine *,
+ int);
+
+void SCLogReleaseFDFilter(SCLogFDFilter *);
+#endif /* __DEBUG_H__ */
diff --git a/framework/src/suricata/src/util-debug.c b/framework/src/suricata/src/util-debug.c
new file mode 100644
index 00000000..f1ac0463
--- /dev/null
+++ b/framework/src/suricata/src/util-debug.c
@@ -0,0 +1,1653 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Debug utility functions
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-enum.h"
+#include "util-debug-filters.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "packet-queue.h"
+#include "threadvars.h"
+
+#include "tm-queuehandlers.h"
+#include "tm-queues.h"
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-syslog.h"
+
+#include "conf.h"
+
+/* holds the string-enum mapping for the enums held in the table SCLogLevel */
+SCEnumCharMap sc_log_level_map[ ] = {
+ { "Not set", SC_LOG_NOTSET},
+ { "None", SC_LOG_NONE },
+ { "Emergency", SC_LOG_EMERGENCY },
+ { "Alert", SC_LOG_ALERT },
+ { "Critical", SC_LOG_CRITICAL },
+ { "Error", SC_LOG_ERROR },
+ { "Warning", SC_LOG_WARNING },
+ { "Notice", SC_LOG_NOTICE },
+ { "Info", SC_LOG_INFO },
+ { "Debug", SC_LOG_DEBUG },
+ { NULL, -1 }
+};
+
+/* holds the string-enum mapping for the enums held in the table SCLogOPIface */
+SCEnumCharMap sc_log_op_iface_map[ ] = {
+ { "Console", SC_LOG_OP_IFACE_CONSOLE },
+ { "File", SC_LOG_OP_IFACE_FILE },
+ { "Syslog", SC_LOG_OP_IFACE_SYSLOG },
+ { NULL, -1 }
+};
+
+#if defined (OS_WIN32)
+/**
+ * \brief Used for synchronous output on WIN32
+ */
+static SCMutex sc_log_stream_lock = NULL;
+#endif /* OS_WIN32 */
+
+/**
+ * \brief Holds the config state for the logging module
+ */
+static SCLogConfig *sc_log_config = NULL;
+
+/**
+ * \brief Returns the full path given a file and configured log dir
+ */
+static char *SCLogGetLogFilename(char *);
+
+/**
+ * \brief Holds the global log level. Is the same as sc_log_config->log_level
+ */
+SCLogLevel sc_log_global_log_level;
+
+/**
+ * \brief Used to indicate whether the logging module has been init or not
+ */
+int sc_log_module_initialized = 0;
+
+/**
+ * \brief Used to indicate whether the logging module has been cleaned or not
+ */
+int sc_log_module_cleaned = 0;
+
+/**
+ * \brief Maps the SC logging level to the syslog logging level
+ *
+ * \param The SC logging level that has to be mapped to the syslog_log_level
+ *
+ * \retval syslog_log_level The mapped syslog_api_log_level, for the logging
+ * module api's internal log_level
+ */
+static inline int SCLogMapLogLevelToSyslogLevel(int log_level)
+{
+ int syslog_log_level = 0;
+
+ switch (log_level) {
+ case SC_LOG_EMERGENCY:
+ syslog_log_level = LOG_EMERG;
+ break;
+ case SC_LOG_ALERT:
+ syslog_log_level = LOG_ALERT;
+ break;
+ case SC_LOG_CRITICAL:
+ syslog_log_level = LOG_CRIT;
+ break;
+ case SC_LOG_ERROR:
+ syslog_log_level = LOG_ERR;
+ break;
+ case SC_LOG_WARNING:
+ syslog_log_level = LOG_WARNING;
+ break;
+ case SC_LOG_NOTICE:
+ syslog_log_level = LOG_NOTICE;
+ break;
+ case SC_LOG_INFO:
+ syslog_log_level = LOG_INFO;
+ break;
+ case SC_LOG_DEBUG:
+ syslog_log_level = LOG_DEBUG;
+ break;
+ default:
+ syslog_log_level = LOG_EMERG;
+ break;
+ }
+
+ return syslog_log_level;
+}
+
+/**
+ * \brief Output function that logs a character string out to a file descriptor
+ *
+ * \param fd Pointer to the file descriptor
+ * \param msg Pointer to the character string that should be logged
+ */
+static inline void SCLogPrintToStream(FILE *fd, char *msg)
+{
+#if defined (OS_WIN32)
+ SCMutexLock(&sc_log_stream_lock);
+#endif /* OS_WIN32 */
+
+ if (fprintf(fd, "%s\n", msg) < 0)
+ printf("Error writing to stream using fprintf\n");
+
+ fflush(fd);
+
+#if defined (OS_WIN32)
+ SCMutexUnlock(&sc_log_stream_lock);
+#endif /* OS_WIN32 */
+
+ return;
+}
+
+/**
+ * \brief Output function that logs a character string throught the syslog iface
+ *
+ * \param syslog_log_level Holds the syslog_log_level that the message should be
+ * logged as
+ * \param msg Pointer to the char string, that should be logged
+ *
+ * \todo syslog is thread-safe according to POSIX manual and glibc code, but we
+ * we will have to look into non POSIX compliant boxes like freeBSD
+ */
+static inline void SCLogPrintToSyslog(int syslog_log_level, const char *msg)
+{
+ //static struct syslog_data data = SYSLOG_DATA_INIT;
+ //syslog_r(syslog_log_level, NULL, "%s", msg);
+
+ syslog(syslog_log_level, "%s", msg);
+
+ return;
+}
+
+#ifdef HAVE_LIBJANSSON
+/**
+ */
+int SCLogMessageJSON(struct timeval *tval, char *buffer, size_t buffer_size,
+ SCLogLevel log_level, const char *file,
+ unsigned line, const char *function, SCError error_code,
+ const char *message)
+{
+ json_t *js = json_object();
+ if (unlikely(js == NULL))
+ goto error;
+ json_t *ejs = json_object();
+ if (unlikely(ejs == NULL))
+ goto error;
+
+ char timebuf[64];
+ CreateIsoTimeString(tval, timebuf, sizeof(timebuf));
+ json_object_set_new(js, "timestamp", json_string(timebuf));
+
+ json_object_set_new(js, "event_type", json_string("engine"));
+
+ if (error_code > 0) {
+ json_object_set_new(ejs, "error_code", json_integer(error_code));
+ json_object_set_new(ejs, "error", json_string(SCErrorToString(error_code)));
+ }
+
+ if (message)
+ json_object_set_new(ejs, "message", json_string(message));
+
+ if (log_level >= SC_LOG_DEBUG) {
+ if (function)
+ json_object_set_new(ejs, "function", json_string(function));
+
+ if (file)
+ json_object_set_new(ejs, "file", json_string(file));
+
+ if (line > 0)
+ json_object_set_new(ejs, "line", json_integer(line));
+ }
+
+ json_object_set_new(js, "engine", ejs);
+
+ char *js_s = json_dumps(js,
+ JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
+#ifdef JSON_ESCAPE_SLASH
+ JSON_ESCAPE_SLASH
+#else
+ 0
+#endif
+ );
+ snprintf(buffer, buffer_size, "%s", js_s);
+ free(js_s);
+
+ json_object_del(js, "engine");
+ json_object_clear(js);
+ json_decref(js);
+
+ return 0;
+error:
+ return -1;
+}
+#endif /* HAVE_LIBJANSSON */
+
+/**
+ * \brief Adds the global log_format to the outgoing buffer
+ *
+ * \param log_level log_level of the message that has to be logged
+ * \param msg Buffer containing the outgoing message
+ * \param file File_name from where the message originated
+ * \param function Function_name from where the message originated
+ * \param line Line_no from where the messaged originated
+ *
+ * \retval SC_OK on success; else an error code
+ */
+static SCError SCLogMessageGetBuffer(
+ struct timeval *tval, int color, SCLogOPType type,
+ char *buffer, size_t buffer_size,
+ const char *log_format,
+
+ const SCLogLevel log_level, const char *file,
+ const unsigned int line, const char *function,
+ const SCError error_code, const char *message)
+{
+#ifdef HAVE_LIBJANSSON
+ if (type == SC_LOG_OP_TYPE_JSON)
+ return SCLogMessageJSON(tval, buffer, buffer_size, log_level, file, line, function, error_code, message);
+#endif
+
+ char *temp = buffer;
+ const char *s = NULL;
+ struct tm *tms = NULL;
+
+ char *redb = "";
+ char *red = "";
+ char *yellowb = "";
+ char *yellow = "";
+ char *green = "";
+ char *blue = "";
+ char *reset = "";
+ if (color) {
+ redb = "\x1b[1;31m";
+ red = "\x1b[31m";
+ yellowb = "\x1b[1;33m";
+ yellow = "\x1b[33m";
+ green = "\x1b[32m";
+ blue = "\x1b[34m";
+ reset = "\x1b[0m";
+ }
+
+ /* no of characters_written(cw) by snprintf */
+ int cw = 0;
+
+ if (sc_log_module_initialized != 1) {
+#ifdef DEBUG
+ printf("Logging module not initialized. Call SCLogInitLogModule(), "
+ "before using the logging API\n");
+#endif
+ return SC_ERR_LOG_MODULE_NOT_INIT;
+ }
+
+ char *temp_fmt = strdup(log_format);
+ if (unlikely(temp_fmt == NULL)) {
+ return SC_ERR_MEM_ALLOC;
+ }
+ char *temp_fmt_h = temp_fmt;
+ char *substr = temp_fmt;
+
+ while ( (temp_fmt = index(temp_fmt, SC_LOG_FMT_PREFIX)) ) {
+ if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_OK;
+ }
+ switch(temp_fmt[1]) {
+ case SC_LOG_FMT_TIME:
+ temp_fmt[0] = '\0';
+
+ struct tm local_tm;
+ tms = SCLocalTime(tval->tv_sec, &local_tm);
+
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%d/%d/%04d -- %02d:%02d:%02d%s",
+ substr, green, tms->tm_mday, tms->tm_mon + 1,
+ tms->tm_year + 1900, tms->tm_hour, tms->tm_min,
+ tms->tm_sec, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ case SC_LOG_FMT_PID:
+ temp_fmt[0] = '\0';
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%u%s", substr, yellow, getpid(), reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ case SC_LOG_FMT_TID:
+ temp_fmt[0] = '\0';
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%lu%s", substr, yellow, SCGetThreadIdLong(), reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+ case SC_LOG_FMT_TM:
+ temp_fmt[0] = '\0';
+/* disabled to prevent dead lock:
+ * log or alloc (which calls log on error) can call TmThreadsGetCallingThread
+ * which will lock tv_root_lock. This can happen while we already hold this
+ * lock. */
+#if 0
+ ThreadVars *tv = TmThreadsGetCallingThread();
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - *msg),
+ "%s%s", substr, ((tv != NULL)? tv->name: "UNKNOWN TM"));
+#endif
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s", substr, "N/A");
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+ case SC_LOG_FMT_LOG_LEVEL:
+ temp_fmt[0] = '\0';
+ s = SCMapEnumValueToName(log_level, sc_log_level_map);
+ if (s != NULL) {
+ if (log_level <= SC_LOG_ERROR)
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, redb, s, reset);
+ else if (log_level == SC_LOG_WARNING)
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, red, s, reset);
+ else if (log_level == SC_LOG_NOTICE)
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, yellowb, s, reset);
+ else
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, yellow, s, reset);
+ } else {
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s", substr, "INVALID");
+ }
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ case SC_LOG_FMT_FILE_NAME:
+ temp_fmt[0] = '\0';
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, blue, file, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ case SC_LOG_FMT_LINE:
+ temp_fmt[0] = '\0';
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%u%s", substr, green, line, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ case SC_LOG_FMT_FUNCTION:
+ temp_fmt[0] = '\0';
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "%s%s%s%s", substr, green, function, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ temp_fmt++;
+ substr = temp_fmt;
+ substr++;
+ break;
+
+ }
+ temp_fmt++;
+ }
+ if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_OK;
+ }
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s", substr);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_OK;
+ }
+
+ if (error_code != SC_OK) {
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
+ "[%sERRCODE%s: %s%s%s(%s%d%s)] - ", yellow, reset, red, SCErrorToString(error_code), reset, yellow, error_code, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_OK;
+ }
+ }
+
+ char *xyellow = error_code > SC_OK ? yellow : "";
+ cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s%s%s", xyellow, message, reset);
+ if (cw < 0)
+ goto error;
+ temp += cw;
+ if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_OK;
+ }
+
+ SCFree(temp_fmt_h);
+
+ if (sc_log_config->op_filter_regex != NULL) {
+#define MAX_SUBSTRINGS 30
+ int ov[MAX_SUBSTRINGS];
+
+ if (pcre_exec(sc_log_config->op_filter_regex,
+ sc_log_config->op_filter_regex_study,
+ buffer, strlen(buffer), 0, 0, ov, MAX_SUBSTRINGS) < 0)
+ {
+ return SC_ERR_LOG_FG_FILTER_MATCH; // bit hacky, but just return !0
+ }
+#undef MAX_SUBSTRINGS
+ }
+
+ return SC_OK;
+
+ error:
+ if (temp_fmt_h != NULL)
+ SCFree(temp_fmt_h);
+ return SC_ERR_SPRINTF;
+}
+
+/**
+ * \brief Adds the global log_format to the outgoing buffer
+ *
+ * \param log_level log_level of the message that has to be logged
+ * \param msg Buffer containing the outgoing message
+ * \param file File_name from where the message originated
+ * \param function Function_name from where the message originated
+ * \param line Line_no from where the messaged originated
+ *
+ * \retval SC_OK on success; else an error code
+ */
+SCError SCLogMessage(const SCLogLevel log_level, const char *file,
+ const unsigned int line, const char *function,
+ const SCError error_code, const char *message)
+{
+ char buffer[SC_LOG_MAX_LOG_MSG_LEN] = "";
+ SCLogOPIfaceCtx *op_iface_ctx = NULL;
+
+ if (sc_log_module_initialized != 1) {
+ printf("Logging module not initialized. Call SCLogInitLogModule() "
+ "first before using the debug API\n");
+ return SC_OK;
+ }
+
+ /* get ts here so we log the same ts to each output */
+ struct timeval tval;
+ gettimeofday(&tval, NULL);
+
+ op_iface_ctx = sc_log_config->op_ifaces;
+ while (op_iface_ctx != NULL) {
+ if (log_level != SC_LOG_NOTSET && log_level > op_iface_ctx->log_level) {
+ op_iface_ctx = op_iface_ctx->next;
+ continue;
+ }
+
+ switch (op_iface_ctx->iface) {
+ case SC_LOG_OP_IFACE_CONSOLE:
+ if (SCLogMessageGetBuffer(&tval, op_iface_ctx->use_color, op_iface_ctx->type,
+ buffer, sizeof(buffer),
+ op_iface_ctx->log_format ?
+ op_iface_ctx->log_format : sc_log_config->log_format,
+ log_level, file, line, function,
+ error_code, message) == 0)
+ {
+ SCLogPrintToStream((log_level == SC_LOG_ERROR)? stderr: stdout, buffer);
+ }
+ break;
+ case SC_LOG_OP_IFACE_FILE:
+ if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
+ op_iface_ctx->log_format ?
+ op_iface_ctx->log_format : sc_log_config->log_format,
+ log_level, file, line, function,
+ error_code, message) == 0)
+ {
+ SCLogPrintToStream(op_iface_ctx->file_d, buffer);
+ }
+ break;
+ case SC_LOG_OP_IFACE_SYSLOG:
+ if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
+ op_iface_ctx->log_format ?
+ op_iface_ctx->log_format : sc_log_config->log_format,
+ log_level, file, line, function,
+ error_code, message) == 0)
+ {
+ SCLogPrintToSyslog(SCLogMapLogLevelToSyslogLevel(log_level), buffer);
+ }
+ break;
+ default:
+ break;
+ }
+ op_iface_ctx = op_iface_ctx->next;
+ }
+ return SC_OK;
+}
+
+/**
+ * \brief Returns whether debug messages are enabled to be logged or not
+ *
+ * \retval 1 if debug messages are enabled to be logged
+ * \retval 0 if debug messages are not enabled to be logged
+ */
+int SCLogDebugEnabled(void)
+{
+#ifdef DEBUG
+ if (sc_log_global_log_level == SC_LOG_DEBUG)
+ return 1;
+ else
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * \brief Allocates an output buffer for an output interface. Used when we
+ * want the op_interface log_format to override the global_log_format.
+ * Currently not used.
+ *
+ * \retval buffer Pointer to the newly created output_buffer
+ */
+SCLogOPBuffer *SCLogAllocLogOPBuffer(void)
+{
+ SCLogOPBuffer *buffer = NULL;
+ SCLogOPIfaceCtx *op_iface_ctx = NULL;
+ int i = 0;
+
+ if ( (buffer = SCMalloc(sc_log_config->op_ifaces_cnt *
+ sizeof(SCLogOPBuffer))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogAllocLogOPBuffer. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ op_iface_ctx = sc_log_config->op_ifaces;
+ for (i = 0;
+ i < sc_log_config->op_ifaces_cnt;
+ i++, op_iface_ctx = op_iface_ctx->next) {
+ buffer[i].log_format = op_iface_ctx->log_format;
+ buffer[i].temp = buffer[i].msg;
+ }
+
+ return buffer;
+}
+
+/*----------------------The logging module initialization code--------------- */
+
+/**
+ * \brief Returns a new output_interface_context
+ *
+ * \retval iface_ctx Pointer to a newly allocated output_interface_context
+ * \initonly
+ */
+static inline SCLogOPIfaceCtx *SCLogAllocLogOPIfaceCtx()
+{
+ SCLogOPIfaceCtx *iface_ctx = NULL;
+
+ if ( (iface_ctx = SCMalloc(sizeof(SCLogOPIfaceCtx))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogallocLogOPIfaceCtx. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(iface_ctx, 0, sizeof(SCLogOPIfaceCtx));
+
+ return iface_ctx;
+}
+
+/**
+ * \brief Initializes the file output interface
+ *
+ * \param file Path to the file used for logging purposes
+ * \param log_format Pointer to the log_format for this op interface, that
+ * overrides the global_log_format
+ * \param log_level Override of the global_log_level by this interface
+ *
+ * \retval iface_ctx Pointer to the file output interface context created
+ * \initonly
+ */
+static inline SCLogOPIfaceCtx *SCLogInitFileOPIface(const char *file,
+ const char *log_format,
+ int log_level,
+ SCLogOPType type)
+{
+ SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
+
+ if (iface_ctx == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogInitFileOPIface. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ if (file == NULL || log_format == NULL) {
+ goto error;
+ }
+
+ iface_ctx->iface = SC_LOG_OP_IFACE_FILE;
+ iface_ctx->type = type;
+
+ if ( (iface_ctx->file_d = fopen(file, "w+")) == NULL) {
+ printf("Error opening file %s\n", file);
+ goto error;
+ }
+
+ if ((iface_ctx->file = SCStrdup(file)) == NULL) {
+ goto error;
+ }
+
+ if ((iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
+ goto error;
+ }
+
+ iface_ctx->log_level = log_level;
+
+ return iface_ctx;
+
+error:
+ if (iface_ctx->file != NULL) {
+ SCFree((char *)iface_ctx->file);
+ iface_ctx->file = NULL;
+ }
+ if (iface_ctx->log_format != NULL) {
+ SCFree((char *)iface_ctx->log_format);
+ iface_ctx->log_format = NULL;
+ }
+ if (iface_ctx->file_d != NULL) {
+ fclose(iface_ctx->file_d);
+ iface_ctx->file_d = NULL;
+ }
+ return NULL;
+}
+
+/**
+ * \brief Initializes the console output interface and deals with possible
+ * env var overrides.
+ *
+ * \param log_format Pointer to the log_format for this op interface, that
+ * overrides the global_log_format
+ * \param log_level Override of the global_log_level by this interface
+ *
+ * \retval iface_ctx Pointer to the console output interface context created
+ * \initonly
+ */
+static inline SCLogOPIfaceCtx *SCLogInitConsoleOPIface(const char *log_format,
+ SCLogLevel log_level, SCLogOPType type)
+{
+ SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
+
+ if (iface_ctx == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogInitConsoleOPIface. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ iface_ctx->iface = SC_LOG_OP_IFACE_CONSOLE;
+ iface_ctx->type = type;
+
+ /* console log format is overridden by envvars */
+ const char *tmp_log_format = log_format;
+ const char *s = getenv(SC_LOG_ENV_LOG_FORMAT);
+ if (s != NULL) {
+#if 0
+ printf("Overriding setting for \"console.format\" because of env "
+ "var SC_LOG_FORMAT=\"%s\".\n", s);
+#endif
+ tmp_log_format = s;
+ }
+
+ if (tmp_log_format != NULL &&
+ (iface_ctx->log_format = SCStrdup(tmp_log_format)) == NULL) {
+ printf("Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* console log level is overridden by envvars */
+ SCLogLevel tmp_log_level = log_level;
+ s = getenv(SC_LOG_ENV_LOG_LEVEL);
+ if (s != NULL) {
+ SCLogLevel l = SCMapEnumNameToValue(s, sc_log_level_map);
+ if (l > SC_LOG_NOTSET && l < SC_LOG_LEVEL_MAX) {
+#if 0
+ printf("Overriding setting for \"console.level\" because of env "
+ "var SC_LOG_LEVEL=\"%s\".\n", s);
+#endif
+ tmp_log_level = l;
+ }
+ }
+ iface_ctx->log_level = tmp_log_level;
+
+ if (isatty(fileno(stdout))) {
+ iface_ctx->use_color = TRUE;
+ }
+
+ return iface_ctx;
+}
+
+/**
+ * \brief Initializes the syslog output interface
+ *
+ * \param facility The facility code for syslog
+ * \param log_format Pointer to the log_format for this op interface, that
+ * overrides the global_log_format
+ * \param log_level Override of the global_log_level by this interface
+ *
+ * \retval iface_ctx Pointer to the syslog output interface context created
+ */
+static inline SCLogOPIfaceCtx *SCLogInitSyslogOPIface(int facility,
+ const char *log_format,
+ SCLogLevel log_level,
+ SCLogOPType type)
+{
+ SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
+
+ if ( iface_ctx == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogInitSyslogOPIface. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ iface_ctx->iface = SC_LOG_OP_IFACE_SYSLOG;
+ iface_ctx->type = type;
+
+ if (facility == -1)
+ facility = SC_LOG_DEF_SYSLOG_FACILITY;
+ iface_ctx->facility = facility;
+
+ if (log_format != NULL &&
+ (iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
+ printf("Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ iface_ctx->log_level = log_level;
+
+ openlog(NULL, LOG_NDELAY, iface_ctx->facility);
+
+ return iface_ctx;
+}
+
+/**
+ * \brief Frees the output_interface context supplied as an argument
+ *
+ * \param iface_ctx Pointer to the op_interface_context to be freed
+ */
+static inline void SCLogFreeLogOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx)
+{
+ SCLogOPIfaceCtx *temp = NULL;
+
+ while (iface_ctx != NULL) {
+ temp = iface_ctx;
+
+ if (iface_ctx->file_d != NULL)
+ fclose(iface_ctx->file_d);
+
+ if (iface_ctx->file != NULL)
+ SCFree((void *)iface_ctx->file);
+
+ if (iface_ctx->log_format != NULL)
+ SCFree((void *)iface_ctx->log_format);
+
+ if (iface_ctx->iface == SC_LOG_OP_IFACE_SYSLOG) {
+ closelog();
+ }
+
+ iface_ctx = iface_ctx->next;
+
+ SCFree(temp);
+ }
+
+ return;
+}
+
+/**
+ * \brief Internal function used to set the logging module global_log_level
+ * during the initialization phase
+ *
+ * \param sc_lid The initialization data supplied.
+ * \param sc_lc The logging module context which has to be updated.
+ */
+static inline void SCLogSetLogLevel(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
+{
+ SCLogLevel log_level = SC_LOG_NOTSET;
+ const char *s = NULL;
+
+ /* envvar overrides config */
+ s = getenv(SC_LOG_ENV_LOG_LEVEL);
+ if (s != NULL) {
+ log_level = SCMapEnumNameToValue(s, sc_log_level_map);
+ } else if (sc_lid != NULL) {
+ log_level = sc_lid->global_log_level;
+ }
+
+ /* deal with the global_log_level to be used */
+ if (log_level > SC_LOG_NOTSET && log_level < SC_LOG_LEVEL_MAX)
+ sc_lc->log_level = log_level;
+ else {
+ sc_lc->log_level = SC_LOG_DEF_LOG_LEVEL;
+#ifndef UNITTESTS
+ if (sc_lid != NULL) {
+ printf("Warning: Invalid/No global_log_level assigned by user. Falling "
+ "back on the default_log_level \"%s\"\n",
+ SCMapEnumValueToName(sc_lc->log_level, sc_log_level_map));
+ }
+#endif
+ }
+
+ /* we also set it to a global var, as it is easier to access it */
+ sc_log_global_log_level = sc_lc->log_level;
+
+ return;
+}
+
+/**
+ * \brief Internal function used to set the logging module global_log_format
+ * during the initialization phase
+ *
+ * \param sc_lid The initialization data supplied.
+ * \param sc_lc The logging module context which has to be updated.
+ */
+static inline void SCLogSetLogFormat(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
+{
+ char *format = NULL;
+
+ /* envvar overrides config */
+ format = getenv(SC_LOG_ENV_LOG_FORMAT);
+ if (format == NULL) {
+ if (sc_lid != NULL) {
+ format = sc_lid->global_log_format;
+ }
+ }
+
+ /* deal with the global log format to be used */
+ if (format == NULL || strlen(format) > SC_LOG_MAX_LOG_FORMAT_LEN) {
+ format = SC_LOG_DEF_LOG_FORMAT;
+#ifndef UNITTESTS
+ if (sc_lid != NULL) {
+ printf("Warning: Invalid/No global_log_format supplied by user or format "
+ "length exceeded limit of \"%d\" characters. Falling back on "
+ "default log_format \"%s\"\n", SC_LOG_MAX_LOG_FORMAT_LEN,
+ format);
+ }
+#endif
+ }
+
+ if (format != NULL && (sc_lc->log_format = SCStrdup(format)) == NULL) {
+ printf("Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+/**
+ * \brief Internal function used to set the logging module global_op_ifaces
+ * during the initialization phase
+ *
+ * \param sc_lid The initialization data supplied.
+ * \param sc_lc The logging module context which has to be updated.
+ */
+static inline void SCLogSetOPIface(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
+{
+ SCLogOPIfaceCtx *op_ifaces_ctx = NULL;
+ int op_iface = 0;
+ const char *s = NULL;
+
+ if (sc_lid != NULL && sc_lid->op_ifaces != NULL) {
+ sc_lc->op_ifaces = sc_lid->op_ifaces;
+ sc_lid->op_ifaces = NULL;
+ sc_lc->op_ifaces_cnt = sc_lid->op_ifaces_cnt;
+ } else {
+ s = getenv(SC_LOG_ENV_LOG_OP_IFACE);
+ if (s != NULL) {
+ op_iface = SCMapEnumNameToValue(s, sc_log_op_iface_map);
+
+ if(op_iface < 0 || op_iface >= SC_LOG_OP_IFACE_MAX) {
+ op_iface = SC_LOG_DEF_LOG_OP_IFACE;
+#ifndef UNITTESTS
+ printf("Warning: Invalid output interface supplied by user. "
+ "Falling back on default_output_interface \"%s\"\n",
+ SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
+#endif
+ }
+ }
+ else {
+ op_iface = SC_LOG_DEF_LOG_OP_IFACE;
+#ifndef UNITTESTS
+ if (sc_lid != NULL) {
+ printf("Warning: Output_interface not supplied by user. Falling "
+ "back on default_output_interface \"%s\"\n",
+ SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
+ }
+#endif
+ }
+
+ switch (op_iface) {
+ case SC_LOG_OP_IFACE_CONSOLE:
+ op_ifaces_ctx = SCLogInitConsoleOPIface(NULL, SC_LOG_LEVEL_MAX,0);
+ break;
+ case SC_LOG_OP_IFACE_FILE:
+ s = getenv(SC_LOG_ENV_LOG_FILE);
+ if (s == NULL) {
+ char *str = SCLogGetLogFilename(SC_LOG_DEF_LOG_FILE);
+ if (str != NULL) {
+ op_ifaces_ctx = SCLogInitFileOPIface(str, NULL, SC_LOG_LEVEL_MAX,0);
+ SCFree(str);
+ }
+ } else {
+ op_ifaces_ctx = SCLogInitFileOPIface(s, NULL, SC_LOG_LEVEL_MAX,0);
+ }
+ break;
+ case SC_LOG_OP_IFACE_SYSLOG:
+ s = getenv(SC_LOG_ENV_LOG_FACILITY);
+ if (s == NULL)
+ s = SC_LOG_DEF_SYSLOG_FACILITY_STR;
+
+ op_ifaces_ctx = SCLogInitSyslogOPIface(SCMapEnumNameToValue(s, SCSyslogGetFacilityMap()), NULL, -1,0);
+ break;
+ }
+ sc_lc->op_ifaces = op_ifaces_ctx;
+ sc_lc->op_ifaces_cnt++;
+ }
+ return;
+}
+
+/**
+ * \brief Internal function used to set the logging module op_filter
+ * during the initialization phase
+ *
+ * \param sc_lid The initialization data supplied.
+ * \param sc_lc The logging module context which has to be updated.
+ */
+static inline void SCLogSetOPFilter(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
+{
+ const char *filter = NULL;
+
+ int opts = 0;
+ const char *ep;
+ int eo = 0;
+
+ /* envvar overrides */
+ filter = getenv(SC_LOG_ENV_LOG_OP_FILTER);
+ if (filter == NULL) {
+ if (sc_lid != NULL) {
+ filter = sc_lid->op_filter;
+ }
+ }
+
+ if (filter != NULL && strcmp(filter, "") != 0) {
+ sc_lc->op_filter = SCStrdup(filter);
+ if (sc_lc->op_filter == NULL) {
+ printf("pcre filter alloc failed\n");
+ return;
+ }
+ sc_lc->op_filter_regex = pcre_compile(filter, opts, &ep, &eo, NULL);
+ if (sc_lc->op_filter_regex == NULL) {
+ SCFree(sc_lc->op_filter);
+ printf("pcre compile of \"%s\" failed at offset %d : %s\n", filter,
+ eo, ep);
+ return;
+ }
+
+ sc_lc->op_filter_regex_study = pcre_study(sc_lc->op_filter_regex, 0,
+ &ep);
+ if (ep != NULL) {
+ printf("pcre study failed: %s\n", ep);
+ return;
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Returns a pointer to a new SCLogInitData. This is a public interface
+ * intended to be used after the logging paramters are read from the
+ * conf file
+ *
+ * \retval sc_lid Pointer to the newly created SCLogInitData
+ * \initonly
+ */
+SCLogInitData *SCLogAllocLogInitData(void)
+{
+ SCLogInitData *sc_lid = NULL;
+
+ /* not using SCMalloc here because if it fails we can't log */
+ if ( (sc_lid = SCMalloc(sizeof(SCLogInitData))) == NULL)
+ return NULL;
+
+ memset(sc_lid, 0, sizeof(SCLogInitData));
+
+ return sc_lid;
+}
+
+/**
+ * \brief Frees a SCLogInitData
+ *
+ * \param sc_lid Pointer to the SCLogInitData to be freed
+ */
+void SCLogFreeLogInitData(SCLogInitData *sc_lid)
+{
+ if (sc_lid != NULL) {
+ if (sc_lid->startup_message != NULL)
+ SCFree(sc_lid->startup_message);
+ if (sc_lid->global_log_format != NULL)
+ SCFree(sc_lid->global_log_format);
+ if (sc_lid->op_filter != NULL)
+ SCFree(sc_lid->op_filter);
+
+ SCLogFreeLogOPIfaceCtx(sc_lid->op_ifaces);
+ }
+
+ return;
+}
+
+/**
+ * \brief Frees the logging module context
+ */
+static inline void SCLogFreeLogConfig(SCLogConfig *sc_lc)
+{
+ if (sc_lc != NULL) {
+ if (sc_lc->startup_message != NULL)
+ SCFree(sc_lc->startup_message);
+ if (sc_lc->log_format != NULL)
+ SCFree(sc_lc->log_format);
+ if (sc_lc->op_filter != NULL)
+ SCFree(sc_lc->op_filter);
+
+ SCLogFreeLogOPIfaceCtx(sc_lc->op_ifaces);
+ SCFree(sc_lc);
+ }
+
+ return;
+}
+
+/**
+ * \brief Appends an output_interface to the output_interface list sent in head
+ *
+ * \param iface_ctx Pointer to the output_interface that has to be added to head
+ * \param head Pointer to the output_interface list
+ */
+void SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx, SCLogInitData *sc_lid)
+{
+ SCLogOPIfaceCtx *temp = NULL, *prev = NULL;
+ SCLogOPIfaceCtx **head = &sc_lid->op_ifaces;
+
+ if (iface_ctx == NULL) {
+#ifdef DEBUG
+ printf("Argument(s) to SCLogAppendOPIfaceCtx() NULL\n");
+#endif
+ return;
+ }
+
+ temp = *head;
+ while (temp != NULL) {
+ prev = temp;
+ temp = temp->next;
+ }
+
+ if (prev == NULL)
+ *head = iface_ctx;
+ else
+ prev->next = iface_ctx;
+
+ sc_lid->op_ifaces_cnt++;
+
+ return;
+}
+
+
+/**
+ * \brief Creates a new output interface based on the arguments sent. The kind
+ * of output interface to be created is decided by the iface_name arg.
+ * If iface_name is "file", the arg argument will hold the filename to be
+ * used for logging purposes. If iface_name is "syslog", the arg
+ * argument holds the facility code. If iface_name is "console", arg is
+ * NULL.
+ *
+ * \param iface_name Interface name. Can be "console", "file" or "syslog"
+ * \param log_format Override for the global_log_format
+ * \param log_level Override for the global_log_level
+ * \param log_level Parameter required by a particular interface. Explained in
+ * the function description
+ *
+ * \retval iface_ctx Pointer to the newly created output interface
+ */
+SCLogOPIfaceCtx *SCLogInitOPIfaceCtx(const char *iface_name,
+ const char *log_format,
+ int log_level, const char *arg)
+{
+ int iface = SCMapEnumNameToValue(iface_name, sc_log_op_iface_map);
+
+ if (log_level < SC_LOG_NONE || log_level > SC_LOG_DEBUG) {
+#ifndef UNITTESTS
+ printf("Warning: Supplied log_level_override for op_interface \"%s\" "
+ "is invalid. Defaulting to not specifying an override\n",
+ iface_name);
+#endif
+ log_level = SC_LOG_NOTSET;
+ }
+
+ switch (iface) {
+ case SC_LOG_OP_IFACE_CONSOLE:
+ return SCLogInitConsoleOPIface(log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
+ case SC_LOG_OP_IFACE_FILE:
+ return SCLogInitFileOPIface(arg, log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
+ case SC_LOG_OP_IFACE_SYSLOG:
+ return SCLogInitSyslogOPIface(SCMapEnumNameToValue(arg, SCSyslogGetFacilityMap()),
+ log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
+ default:
+#ifdef DEBUG
+ printf("Output Interface \"%s\" not supported by the logging module",
+ iface_name);
+#endif
+ return NULL;
+ }
+}
+
+/**
+ * \brief Initializes the logging module.
+ *
+ * \param sc_lid The initialization data for the logging module. If sc_lid is
+ * NULL, we would stick to the default configuration for the
+ * logging subsystem.
+ * \initonly
+ */
+void SCLogInitLogModule(SCLogInitData *sc_lid)
+{
+ /* De-initialize the logging context, if it has already init by the
+ * environment variables at the start of the engine */
+ SCLogDeInitLogModule();
+
+#if defined (OS_WIN32)
+ if (SCMutexInit(&sc_log_stream_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX, "Failed to initialize log mutex.");
+ exit(EXIT_FAILURE);
+ }
+#endif /* OS_WIN32 */
+
+ /* sc_log_config is a global variable */
+ if ( (sc_log_config = SCMalloc(sizeof(SCLogConfig))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCLogInitLogModule. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(sc_log_config, 0, sizeof(SCLogConfig));
+
+ SCLogSetLogLevel(sc_lid, sc_log_config);
+ SCLogSetLogFormat(sc_lid, sc_log_config);
+ SCLogSetOPIface(sc_lid, sc_log_config);
+ SCLogSetOPFilter(sc_lid, sc_log_config);
+
+ sc_log_module_initialized = 1;
+ sc_log_module_cleaned = 0;
+
+ //SCOutputPrint(sc_did->startup_message);
+
+ return;
+}
+
+void SCLogLoadConfig(int daemon, int verbose)
+{
+ ConfNode *outputs;
+ SCLogInitData *sc_lid;
+ int have_logging = 0;
+
+ outputs = ConfGetNode("logging.outputs");
+ if (outputs == NULL) {
+ SCLogDebug("No logging.output configuration section found.");
+ return;
+ }
+
+ sc_lid = SCLogAllocLogInitData();
+ if (sc_lid == NULL) {
+ SCLogDebug("Could not allocate memory for log init data");
+ return;
+ }
+
+ /* Get default log level and format. */
+ char *default_log_level_s = NULL;
+ if (ConfGet("logging.default-log-level", &default_log_level_s) == 1) {
+ sc_lid->global_log_level =
+ SCMapEnumNameToValue(default_log_level_s, sc_log_level_map);
+ if (sc_lid->global_log_level == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid default log level: %s",
+ default_log_level_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else {
+ SCLogWarning(SC_ERR_MISSING_CONFIG_PARAM,
+ "No default log level set, will use notice.");
+ sc_lid->global_log_level = SC_LOG_NOTICE;
+ }
+
+ if (verbose) {
+ sc_lid->global_log_level += verbose;
+ if (sc_lid->global_log_level > SC_LOG_LEVEL_MAX)
+ sc_lid->global_log_level = SC_LOG_LEVEL_MAX;
+ }
+
+ if (ConfGet("logging.default-log-format", &sc_lid->global_log_format) != 1)
+ sc_lid->global_log_format = SC_LOG_DEF_LOG_FORMAT;
+
+ ConfGet("logging.default-output-filter", &sc_lid->op_filter);
+
+ ConfNode *seq_node, *output;
+ TAILQ_FOREACH(seq_node, &outputs->head, next) {
+ SCLogLevel level = sc_lid->global_log_level;
+ SCLogOPIfaceCtx *op_iface_ctx = NULL;
+ const char *format;
+ const char *level_s;
+
+ output = ConfNodeLookupChild(seq_node, seq_node->val);
+ if (output == NULL)
+ continue;
+
+ /* By default an output is enabled. */
+ const char *enabled = ConfNodeLookupChildValue(output, "enabled");
+ if (enabled != NULL && ConfValIsFalse(enabled))
+ continue;
+
+ SCLogOPType type = SC_LOG_OP_TYPE_REGULAR;
+ const char *type_s = ConfNodeLookupChildValue(output, "type");
+ if (type_s != NULL) {
+ if (strcmp(type_s, "regular") == 0)
+ type = SC_LOG_OP_TYPE_REGULAR;
+ else if (strcmp(type_s, "json") == 0) {
+#ifdef HAVE_LIBJANSSON
+ type = SC_LOG_OP_TYPE_JSON;
+#else
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "libjansson support not "
+ "compiled in, can't use 'json' logging");
+ exit(EXIT_FAILURE);
+#endif /* HAVE_LIBJANSSON */
+ }
+ }
+
+ /* if available use the log format setting for this output,
+ * otherwise fall back to the global setting. */
+ format = ConfNodeLookupChildValue(output, "format");
+ if (format == NULL)
+ format = sc_lid->global_log_format;
+
+ level_s = ConfNodeLookupChildValue(output, "level");
+ if (level_s != NULL) {
+ level = SCMapEnumNameToValue(level_s, sc_log_level_map);
+ if (level == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid log level: %s",
+ level_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (strcmp(output->name, "console") == 0) {
+ op_iface_ctx = SCLogInitConsoleOPIface(format, level, type);
+ }
+ else if (strcmp(output->name, "file") == 0) {
+ const char *filename = ConfNodeLookupChildValue(output, "filename");
+ if (filename == NULL) {
+ SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
+ "Logging to file requires a filename");
+ exit(EXIT_FAILURE);
+ }
+ have_logging = 1;
+ op_iface_ctx = SCLogInitFileOPIface(filename, format, level, type);
+ }
+ else if (strcmp(output->name, "syslog") == 0) {
+ int facility = SC_LOG_DEF_SYSLOG_FACILITY;
+ const char *facility_s = ConfNodeLookupChildValue(output,
+ "facility");
+ if (facility_s != NULL) {
+ facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
+ if (facility == -1) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog "
+ "facility: \"%s\", now using \"%s\" as syslog "
+ "facility", facility_s, SC_LOG_DEF_SYSLOG_FACILITY_STR);
+ facility = SC_LOG_DEF_SYSLOG_FACILITY;
+ }
+ }
+ printf("Initialization syslog logging with format \"%s\".\n",
+ format);
+ have_logging = 1;
+ op_iface_ctx = SCLogInitSyslogOPIface(facility, format, level, type);
+ }
+ else {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid logging method: %s, "
+ "ignoring", output->name);
+ }
+ if (op_iface_ctx != NULL) {
+ SCLogAppendOPIfaceCtx(op_iface_ctx, sc_lid);
+ }
+ }
+
+ if (daemon && (have_logging == 0)) {
+ SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
+ "NO logging compatible with daemon mode selected,"
+ " suricata won't be able to log. Please update "
+ " 'logging.outputs' in the YAML.");
+ }
+
+ SCLogInitLogModule(sc_lid);
+
+ SCLogDebug("sc_log_global_log_level: %d", sc_log_global_log_level);
+ SCLogDebug("sc_lc->log_format: %s", sc_log_config->log_format);
+ SCLogDebug("SCLogSetOPFilter: filter: %s", sc_log_config->op_filter);
+
+ if (sc_lid != NULL)
+ SCFree(sc_lid);
+}
+
+/**
+ * \brief Returns a full file path given a filename uses log dir specified in
+ * conf or DEFAULT_LOG_DIR
+ *
+ * \param filearg The relative filename for which we want a full path include
+ * log directory
+ *
+ * \retval log_filename The fullpath of the logfile to open
+ */
+static char *SCLogGetLogFilename(char *filearg)
+{
+ char *log_dir;
+ char *log_filename;
+
+ log_dir = ConfigGetLogDirectory();
+
+ log_filename = SCMalloc(PATH_MAX);
+ if (unlikely(log_filename == NULL))
+ return NULL;
+ snprintf(log_filename, PATH_MAX, "%s/%s", log_dir, filearg);
+
+ return log_filename;
+}
+
+/**
+ * \brief De-Initializes the logging module
+ */
+void SCLogDeInitLogModule(void)
+{
+ SCLogFreeLogConfig(sc_log_config);
+
+ /* reset the global logging_module variables */
+ sc_log_global_log_level = 0;
+ sc_log_module_initialized = 0;
+ sc_log_module_cleaned = 1;
+ sc_log_config = NULL;
+
+ /* de-init the FD filters */
+ SCLogReleaseFDFilters();
+ /* de-init the FG filters */
+ SCLogReleaseFGFilters();
+
+#if defined (OS_WIN32)
+ if (sc_log_stream_lock != NULL) {
+ SCMutexDestroy(&sc_log_stream_lock);
+ sc_log_stream_lock = NULL;
+ }
+#endif /* OS_WIN32 */
+
+ return;
+}
+
+//------------------------------------Unit_Tests--------------------------------
+
+/* The logging engine should be tested to the maximum extent possible, since
+ * logging code would be used throughout the codebase, and hence we can't afford
+ * to have a single bug here(not that you can afford to have a bug
+ * elsewhere ;) ). Please report a bug, if you get a slightest hint of a bug
+ * from the logging module.
+ */
+
+#ifdef UNITTESTS
+
+int SCLogTestInit01()
+{
+ int result = 1;
+
+ /* unset any environment variables set for the logging module */
+ unsetenv(SC_LOG_ENV_LOG_LEVEL);
+ unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
+ unsetenv(SC_LOG_ENV_LOG_FORMAT);
+
+ SCLogInitLogModule(NULL);
+
+ if (sc_log_config == NULL)
+ return 0;
+
+ result &= (SC_LOG_DEF_LOG_LEVEL == sc_log_config->log_level);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ SC_LOG_DEF_LOG_OP_IFACE == sc_log_config->op_ifaces->iface);
+ result &= (sc_log_config->log_format != NULL &&
+ strcmp(SC_LOG_DEF_LOG_FORMAT, sc_log_config->log_format) == 0);
+
+ SCLogDeInitLogModule();
+
+ setenv(SC_LOG_ENV_LOG_LEVEL, "Debug", 1);
+ setenv(SC_LOG_ENV_LOG_OP_IFACE, "Console", 1);
+ setenv(SC_LOG_ENV_LOG_FORMAT, "%n- %l", 1);
+
+ SCLogInitLogModule(NULL);
+
+ result &= (SC_LOG_DEBUG == sc_log_config->log_level);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
+ result &= (sc_log_config->log_format != NULL &&
+ !strcmp("%n- %l", sc_log_config->log_format));
+
+ unsetenv(SC_LOG_ENV_LOG_LEVEL);
+ unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
+ unsetenv(SC_LOG_ENV_LOG_FORMAT);
+
+ SCLogDeInitLogModule();
+
+ return result;
+}
+
+int SCLogTestInit02()
+{
+ SCLogInitData *sc_lid = NULL;
+ SCLogOPIfaceCtx *sc_iface_ctx = NULL;
+ int result = 1;
+ char *logfile = SCLogGetLogFilename("boo.txt");
+ sc_lid = SCLogAllocLogInitData();
+ if (sc_lid == NULL)
+ return 0;
+ sc_lid->startup_message = "Test02";
+ sc_lid->global_log_level = SC_LOG_DEBUG;
+ sc_lid->op_filter = "boo";
+ sc_iface_ctx = SCLogInitOPIfaceCtx("file", "%m - %d", SC_LOG_ALERT,
+ logfile);
+ SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
+ sc_iface_ctx = SCLogInitOPIfaceCtx("console", NULL, SC_LOG_ERROR,
+ NULL);
+ SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
+
+ SCLogInitLogModule(sc_lid);
+
+ if (sc_log_config == NULL)
+ return 0;
+
+ result &= (SC_LOG_DEBUG == sc_log_config->log_level);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ SC_LOG_OP_IFACE_FILE == sc_log_config->op_ifaces->iface);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->next != NULL &&
+ SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->next->iface);
+ result &= (sc_log_config->log_format != NULL &&
+ strcmp(SC_LOG_DEF_LOG_FORMAT, sc_log_config->log_format) == 0);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->log_format != NULL &&
+ strcmp("%m - %d", sc_log_config->op_ifaces->log_format) == 0);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->next != NULL &&
+ sc_log_config->op_ifaces->next->log_format == NULL);
+
+ SCLogDeInitLogModule();
+
+ sc_lid = SCLogAllocLogInitData();
+ if (sc_lid == NULL)
+ return 0;
+ sc_lid->startup_message = "Test02";
+ sc_lid->global_log_level = SC_LOG_DEBUG;
+ sc_lid->op_filter = "boo";
+ sc_lid->global_log_format = "kaboo";
+
+ SCLogInitLogModule(sc_lid);
+
+ if (sc_log_config == NULL)
+ return 0;
+
+ result &= (SC_LOG_DEBUG == sc_log_config->log_level);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->next == NULL);
+ result &= (sc_log_config->log_format != NULL &&
+ strcmp("kaboo", sc_log_config->log_format) == 0);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->log_format == NULL);
+ result &= (sc_log_config->op_ifaces != NULL &&
+ sc_log_config->op_ifaces->next == NULL);
+
+ SCLogDeInitLogModule();
+
+ return result;
+}
+
+int SCLogTestInit03()
+{
+ int result = 1;
+
+ SCLogInitLogModule(NULL);
+
+ SCLogAddFGFilterBL(NULL, "bamboo", -1);
+ SCLogAddFGFilterBL(NULL, "soo", -1);
+ SCLogAddFGFilterBL(NULL, "dummy", -1);
+
+ result &= (SCLogPrintFGFilters() == 3);
+
+ SCLogAddFGFilterBL(NULL, "dummy1", -1);
+ SCLogAddFGFilterBL(NULL, "dummy2", -1);
+
+ result &= (SCLogPrintFGFilters() == 5);
+
+ SCLogDeInitLogModule();
+
+ return result;
+}
+
+int SCLogTestInit04()
+{
+ int result = 1;
+
+ SCLogInitLogModule(NULL);
+
+ SCLogAddFDFilter("bamboo");
+ SCLogAddFDFilter("soo");
+ SCLogAddFDFilter("foo");
+ SCLogAddFDFilter("roo");
+
+ result &= (SCLogPrintFDFilters() == 4);
+
+ SCLogAddFDFilter("loo");
+ SCLogAddFDFilter("soo");
+
+ result &= (SCLogPrintFDFilters() == 5);
+
+ SCLogRemoveFDFilter("bamboo");
+ SCLogRemoveFDFilter("soo");
+ SCLogRemoveFDFilter("foo");
+ SCLogRemoveFDFilter("noo");
+
+ result &= (SCLogPrintFDFilters() == 2);
+
+ SCLogDeInitLogModule();
+
+ return result;
+}
+
+int SCLogTestInit05()
+{
+ int result = 1;
+
+ SCLogInfo("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+
+ return result;
+}
+
+
+#endif /* UNITTESTS */
+
+void SCLogRegisterTests()
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("SCLogTestInit01", SCLogTestInit01, 1);
+ UtRegisterTest("SCLogTestInit02", SCLogTestInit02, 1);
+ UtRegisterTest("SCLogTestInit03", SCLogTestInit03, 1);
+ UtRegisterTest("SCLogTestInit04", SCLogTestInit04, 1);
+ UtRegisterTest("SCLogTestInit05", SCLogTestInit05, 1);
+
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-debug.h b/framework/src/suricata/src/util-debug.h
new file mode 100644
index 00000000..9c9d2e23
--- /dev/null
+++ b/framework/src/suricata/src/util-debug.h
@@ -0,0 +1,551 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_DEBUG_H__
+#define __UTIL_DEBUG_H__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <pcre.h>
+
+#include "threads.h"
+#include "util-enum.h"
+#include "util-error.h"
+#include "util-debug-filters.h"
+
+/**
+ * \brief ENV vars that can be used to set the properties for the logging module
+ */
+#define SC_LOG_ENV_LOG_LEVEL "SC_LOG_LEVEL"
+#define SC_LOG_ENV_LOG_OP_IFACE "SC_LOG_OP_IFACE"
+#define SC_LOG_ENV_LOG_FILE "SC_LOG_FILE"
+#define SC_LOG_ENV_LOG_FACILITY "SC_LOG_FACILITY"
+#define SC_LOG_ENV_LOG_FORMAT "SC_LOG_FORMAT"
+#define SC_LOG_ENV_LOG_OP_FILTER "SC_LOG_OP_FILTER"
+
+/**
+ * \brief The various log levels
+ */
+typedef enum {
+ SC_LOG_NOTSET = -1,
+ SC_LOG_NONE = 0,
+ SC_LOG_EMERGENCY,
+ SC_LOG_ALERT,
+ SC_LOG_CRITICAL,
+ SC_LOG_ERROR,
+ SC_LOG_WARNING,
+ SC_LOG_NOTICE,
+ SC_LOG_INFO,
+ SC_LOG_DEBUG,
+ SC_LOG_LEVEL_MAX,
+} SCLogLevel;
+
+/**
+ * \brief The various output interfaces supported
+ */
+typedef enum {
+ SC_LOG_OP_IFACE_CONSOLE,
+ SC_LOG_OP_IFACE_FILE,
+ SC_LOG_OP_IFACE_SYSLOG,
+ SC_LOG_OP_IFACE_MAX,
+} SCLogOPIface;
+
+typedef enum {
+ SC_LOG_OP_TYPE_REGULAR = 0,
+ SC_LOG_OP_TYPE_JSON,
+} SCLogOPType;
+
+/* The default log_format, if it is not supplied by the user */
+#ifdef RELEASE
+#define SC_LOG_DEF_LOG_FORMAT "%t - <%d> - "
+#else
+#define SC_LOG_DEF_LOG_FORMAT "[%i] %t - (%f:%l) <%d> (%n) -- "
+#endif
+
+/* The maximum length of the log message */
+#define SC_LOG_MAX_LOG_MSG_LEN 2048
+
+/* The maximum length of the log format */
+#define SC_LOG_MAX_LOG_FORMAT_LEN 128
+
+/* The default log level, if it is not supplied by the user */
+#define SC_LOG_DEF_LOG_LEVEL SC_LOG_INFO
+
+/* The default output interface to be used */
+#define SC_LOG_DEF_LOG_OP_IFACE SC_LOG_OP_IFACE_CONSOLE
+
+/* The default log file to be used */
+#define SC_LOG_DEF_LOG_FILE "sc_ids_log.log"
+
+/* The default syslog facility to be used */
+#define SC_LOG_DEF_SYSLOG_FACILITY_STR "local0"
+#define SC_LOG_DEF_SYSLOG_FACILITY LOG_LOCAL0
+
+/**
+ * \brief Structure to be used when log_level override support would be provided
+ * by the logging module
+ */
+typedef struct SCLogOPBuffer_ {
+ char msg[SC_LOG_MAX_LOG_MSG_LEN];
+ char *temp;
+ const char *log_format;
+} SCLogOPBuffer;
+
+/**
+ * \brief The output interface context for the logging module
+ */
+typedef struct SCLogOPIfaceCtx_ {
+ SCLogOPIface iface;
+
+ int16_t use_color;
+ int16_t type;
+
+ /* the output file to be used if the interface is SC_LOG_IFACE_FILE */
+ const char *file;
+ /* the output file descriptor for the above file */
+ FILE * file_d;
+
+ /* the facility code if the interface is SC_LOG_IFACE_SYSLOG */
+ int facility;
+
+ /* override for the global_log_level */
+ SCLogLevel log_level;
+
+ /* override for the global_log_format(currently not used) */
+ const char *log_format;
+
+ struct SCLogOPIfaceCtx_ *next;
+} SCLogOPIfaceCtx;
+
+/**
+ * \brief Structure containing init data, that would be passed to
+ * SCInitDebugModule()
+ */
+typedef struct SCLogInitData_ {
+ /* startup message */
+ char *startup_message;
+
+ /* the log level */
+ SCLogLevel global_log_level;
+
+ /* the log format */
+ char *global_log_format;
+
+ /* output filter */
+ char *op_filter;
+
+ /* list of output interfaces to be used */
+ SCLogOPIfaceCtx *op_ifaces;
+ /* no of op ifaces */
+ uint8_t op_ifaces_cnt;
+} SCLogInitData;
+
+/**
+ * \brief Holds the config state used by the logging api
+ */
+typedef struct SCLogConfig_ {
+ char *startup_message;
+ SCLogLevel log_level;
+ char *log_format;
+
+ char *op_filter;
+ /* compiled pcre filter expression */
+ pcre *op_filter_regex;
+ pcre_extra *op_filter_regex_study;
+
+ /* op ifaces used */
+ SCLogOPIfaceCtx *op_ifaces;
+ /* no of op ifaces */
+ uint8_t op_ifaces_cnt;
+} SCLogConfig;
+
+/* The different log format specifiers supported by the API */
+#define SC_LOG_FMT_TIME 't' /* Timestamp in standard format */
+#define SC_LOG_FMT_PID 'p' /* PID */
+#define SC_LOG_FMT_TID 'i' /* Thread ID */
+#define SC_LOG_FMT_TM 'm' /* Thread module name */
+#define SC_LOG_FMT_LOG_LEVEL 'd' /* Log level */
+#define SC_LOG_FMT_FILE_NAME 'f' /* File name */
+#define SC_LOG_FMT_LINE 'l' /* Line number */
+#define SC_LOG_FMT_FUNCTION 'n' /* Function */
+
+/* The log format prefix for the format specifiers */
+#define SC_LOG_FMT_PREFIX '%'
+
+extern SCLogLevel sc_log_global_log_level;
+
+extern int sc_log_module_initialized;
+
+extern int sc_log_module_cleaned;
+
+
+#define SCLog(x, ...) \
+ do { \
+ if (sc_log_global_log_level >= x && \
+ (sc_log_fg_filters_present == 0 || \
+ SCLogMatchFGFilterWL(__FILE__, __FUNCTION__, __LINE__) == 1 || \
+ SCLogMatchFGFilterBL(__FILE__, __FUNCTION__, __LINE__) == 1) && \
+ (sc_log_fd_filters_present == 0 || \
+ SCLogMatchFDFilter(__FUNCTION__) == 1)) \
+ { \
+ char _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN] = ""; \
+ \
+ snprintf(_sc_log_msg, SC_LOG_MAX_LOG_MSG_LEN, __VA_ARGS__); \
+ \
+ SCLogMessage(x, \
+ __FILE__, \
+ __LINE__, \
+ __FUNCTION__, SC_OK, _sc_log_msg); \
+ } \
+ } while(0)
+
+#define SCLogErr(x, err, ...) \
+ do { \
+ if (sc_log_global_log_level >= x && \
+ (sc_log_fg_filters_present == 0 || \
+ SCLogMatchFGFilterWL(__FILE__, __FUNCTION__, __LINE__) == 1 || \
+ SCLogMatchFGFilterBL(__FILE__, __FUNCTION__, __LINE__) == 1) && \
+ (sc_log_fd_filters_present == 0 || \
+ SCLogMatchFDFilter(__FUNCTION__) == 1)) \
+ { \
+ char _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN] = ""; \
+ \
+ snprintf(_sc_log_msg, SC_LOG_MAX_LOG_MSG_LEN, __VA_ARGS__); \
+ \
+ SCLogMessage(x, \
+ __FILE__, \
+ __LINE__, \
+ __FUNCTION__, err, _sc_log_msg); \
+ } \
+ } while(0)
+
+/**
+ * \brief Macro used to log INFORMATIONAL messages.
+ *
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogInfo(...) SCLog(SC_LOG_INFO, __VA_ARGS__)
+
+/**
+ * \brief Macro used to log NOTICE messages.
+ *
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogNotice(...) SCLog(SC_LOG_NOTICE, __VA_ARGS__)
+
+/**
+ * \brief Macro used to log WARNING messages.
+ *
+ * \retval err_code Error code that has to be logged along with the
+ * warning message
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogWarning(err_code, ...) SCLogErr(SC_LOG_WARNING, err_code, \
+ __VA_ARGS__)
+/**
+ * \brief Macro used to log ERROR messages.
+ *
+ * \retval err_code Error code that has to be logged along with the
+ * error message
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogError(err_code, ...) SCLogErr(SC_LOG_ERROR, err_code, \
+ __VA_ARGS__)
+/**
+ * \brief Macro used to log CRITICAL messages.
+ *
+ * \retval err_code Error code that has to be logged along with the
+ * critical message
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogCritical(err_code, ...) SCLogErr(SC_LOG_CRITICAL, err_code, \
+ __VA_ARGS__)
+/**
+ * \brief Macro used to log ALERT messages.
+ *
+ * \retval err_code Error code that has to be logged along with the
+ * alert message
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogAlert(err_code, ...) SCLogErr(SC_LOG_ALERT, err_code, \
+ __VA_ARGS__)
+/**
+ * \brief Macro used to log EMERGENCY messages.
+ *
+ * \retval err_code Error code that has to be logged along with the
+ * emergency message
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogEmerg(err_code, ...) SCLogErr(SC_LOG_EMERGENCY, err_code, \
+ __VA_ARGS__)
+
+
+/* Avoid the overhead of using the debugging subsystem, in production mode */
+#ifndef DEBUG
+
+#define SCLogDebug(...) do { } while (0)
+
+#define SCEnter(...)
+
+#define SCReturn return
+
+#define SCReturnInt(x) return x
+
+#define SCReturnUInt(x) return x
+
+#define SCReturnDbl(x) return x
+
+#define SCReturnChar(x) return x
+
+#define SCReturnCharPtr(x) return x
+
+#define SCReturnCT(x, type) return x
+
+#define SCReturnPtr(x, type) return x
+
+/* Please use it only for debugging purposes */
+#else
+
+
+/**
+ * \brief Macro used to log DEBUG messages. Comes under the debugging subsystem,
+ * and hence will be enabled only in the presence of the DEBUG macro.
+ *
+ * \retval ... Takes as argument(s), a printf style format message
+ */
+#define SCLogDebug(...) SCLog(SC_LOG_DEBUG, __VA_ARGS__)
+
+/**
+ * \brief Macro used to log debug messages on function entry. Comes under the
+ * debugging subsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_entry logs, it also
+ * processes the FD filters, if any FD filters are registered.
+ *
+ * \retval f An argument can be supplied, although it is not used
+ */
+#define SCEnter(f) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG &&\
+ SCLogCheckFDFilterEntry(__FUNCTION__)) \
+ { \
+ SCLogDebug("Entering ... >>"); \
+ } \
+ } while(0)
+
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that don't return
+ * a value.
+ */
+#define SCReturn do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning ... <<" ); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns an
+ * integer value.
+ *
+ * \retval x Variable of type 'integer' that has to be returned
+ */
+#define SCReturnInt(x) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning: %"PRIdMAX" ... <<", (intmax_t)x); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns an
+ * unsigned integer value.
+ *
+ * \retval x Variable of type 'unsigned integer' that has to be returned
+ */
+#define SCReturnUInt(x) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning: %"PRIuMAX" ... <<", (uintmax_t)x); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns a
+ * float/double value.
+ *
+ * \retval x Variable of type 'float/double' that has to be returned
+ */
+#define SCReturnDbl(x) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning: %f ... <<", x); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns a var
+ * of character type.
+ *
+ * \retval x Variable of type 'char' that has to be returned
+ */
+#define SCReturnChar(x) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning: %c ... <<", x); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns a
+ * character string.
+ *
+ * \retval x Pointer to the char string that has to be returned
+ */
+#define SCReturnCharPtr(x) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ if ((x) != NULL) { \
+ SCLogDebug("Returning: %s ... <<", x); \
+ } else { \
+ SCLogDebug("Returning: NULL ... <<"); \
+ } SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns a var
+ * of custom type
+ *
+ * \retval x Variable instance of a custom type that has to be returned
+ * \retval type Pointer to a character string holding the name of the custom
+ * type(the argument x) that has to be returned
+ */
+#define SCReturnCT(x, type) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning var of " \
+ "type %s ... <<", type); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+/**
+ * \brief Macro used to log debug messages on function exit. Comes under the
+ * debugging sybsystem, and hence will be enabled only in the presence
+ * of the DEBUG macro. Apart from logging function_exit logs, it also
+ * processes the FD filters, if any FD filters are registered. This
+ * function_exit macro should be used for functions that returns a
+ * pointer to a custom type
+ *
+ * \retval x Pointer to a variable instance of a custom type that has to be
+ * returned
+ * \retval type Pointer to a character string holding the name of the custom
+ * type(the argument x) that has to be returned
+ */
+#define SCReturnPtr(x, type) do { \
+ if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
+ SCLogDebug("Returning pointer %p of " \
+ "type %s ... <<", x, type); \
+ SCLogCheckFDFilterExit(__FUNCTION__); \
+ } \
+ return x; \
+ } while(0)
+
+#endif /* DEBUG */
+
+#define FatalError(x, ...) do { \
+ SCLogError(x, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+} while(0)
+
+/** \brief Fatal error IF we're starting up, and configured to consider
+ * errors to be fatal errors */
+#define FatalErrorOnInit(x, ...) do { \
+ int init_errors_fatal = 0; \
+ ConfGetBool("engine.init-failure-fatal", &init_errors_fatal); \
+ if (init_errors_fatal && (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT))\
+ { \
+ SCLogError(x, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } \
+ SCLogWarning(x, __VA_ARGS__); \
+} while(0)
+
+
+SCLogInitData *SCLogAllocLogInitData(void);
+
+SCLogOPIfaceCtx *SCLogInitOPIfaceCtx(const char *, const char *, int,
+ const char *);
+
+void SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx *, SCLogInitData *);
+
+void SCLogInitLogModule(SCLogInitData *);
+
+void SCLogDeInitLogModule(void);
+
+SCError SCLogMessage(const SCLogLevel, const char *, const unsigned int,
+ const char *, const SCError, const char *message);
+
+SCLogOPBuffer *SCLogAllocLogOPBuffer(void);
+
+int SCLogDebugEnabled(void);
+
+void SCLogRegisterTests(void);
+
+void SCLogLoadConfig(int daemon, int verbose);
+
+#endif /* __UTIL_DEBUG_H__ */
diff --git a/framework/src/suricata/src/util-decode-asn1.c b/framework/src/suricata/src/util-decode-asn1.c
new file mode 100644
index 00000000..3372b517
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-asn1.c
@@ -0,0 +1,904 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements ASN1 decoding (needed for the asn1 keyword, BER, CER & DER)
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+#include "decode.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-print.h"
+
+#include "util-decode-asn1.h"
+#include "conf.h"
+
+uint16_t asn1_max_frames_config = ASN1_MAX_FRAMES;
+
+void SCAsn1LoadConfig()
+{
+ intmax_t value = 0;
+
+ /** set config defaults */
+ if ((ConfGetInt("asn1-max-frames", &value)) == 1) {
+ asn1_max_frames_config = (uint16_t)value;
+ SCLogDebug("Max stack frame set to %"PRIu16, asn1_max_frames_config);
+ }
+
+}
+
+/**
+ * \brief Decode and check the identifier information of the
+ * current node that is in extended format
+ *
+ * \param ac pointer to the ASN1 Context data
+ *
+ * \retval byte of the status of the parser
+ */
+uint8_t SCAsn1GetHighTagNumber(Asn1Ctx *ac)
+{
+ uint8_t ret = 0;
+ uint32_t tag_num = 0;
+
+ /* If we have a high tag num, skip the id octet */
+ ac->iter++;
+
+ Asn1Node *node = ASN1CTX_CUR_NODE(ac);
+
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB;
+ return ret;
+ }
+
+ uint8_t raw_id = *ac->iter;
+
+ tag_num += ASN1_BER_GET_HIGH_TAG_NUM(raw_id);
+
+ if (ASN1_BER_GET_HIGH_TAG_NUM(raw_id) == 0) {
+ /* Set event, invalid id */
+ node->flags |= ASN1_BER_EVENT_INVALID_ID;
+ ac->parser_status |= ASN1_STATUS_INVALID;
+ return ASN1_PARSER_ERR;
+ }
+
+ ac->iter++;
+ if (!ASN1_BER_IS_HIGH_TAG_END(raw_id)) {
+ do {
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB;
+ return ret;
+ }
+
+ raw_id = *ac->iter;
+
+ if ((uint64_t) ((uint64_t)tag_num +
+ (uint64_t)ASN1_BER_GET_HIGH_TAG_NUM(raw_id)) > UINT32_MAX)
+ {
+ node->flags |= ASN1_BER_EVENT_ID_TOO_LONG;
+ ac->parser_status |= ASN1_STATUS_INVALID;
+ return ASN1_PARSER_ERR;
+ }
+
+ tag_num += ASN1_BER_GET_HIGH_TAG_NUM(raw_id);
+ ac->iter++;
+ } while (!ASN1_BER_IS_HIGH_TAG_END(raw_id));
+ }
+ node->id.tag_num = tag_num;
+
+ return ASN1_PARSER_OK;
+}
+
+/**
+ * \brief Decode and check the length, of the current node
+ * in definite but extended format, that we are parsing,
+ * checking invalid opts
+ *
+ * \param ac pointer to the ASN1 Context data
+ *
+ * \retval byte of the status of the parser
+ */
+uint32_t SCAsn1GetLengthLongForm(Asn1Ctx *ac)
+{
+ uint8_t raw_len = *ac->iter;
+ uint8_t ret = 0;
+ uint32_t content_len = 0;
+ uint8_t oct_len = ASN1_BER_GET_LONG_LEN_OCTETS(raw_len);
+ uint8_t i = 0;
+
+ Asn1Node *node = ASN1CTX_CUR_NODE(ac);
+
+ for (; i < oct_len; i++) {
+ ac->iter++;
+
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB;
+ return ASN1_PARSER_ERR;
+ }
+
+ raw_len = *ac->iter;
+ if (raw_len == 0xFF && ac->iter == node->len.ptr + 1) {
+ /* 8.1.3.5, 0xFF shall not be used */
+ node->flags |= ASN1_BER_EVENT_INVALID_LEN;
+ ac->parser_status = ASN1_STATUS_INVALID;
+ return ASN1_PARSER_ERR;
+ }
+
+ if ((uint64_t) ((uint64_t)content_len +
+ (uint64_t) ASN1_BER_GET_HIGH_TAG_NUM(raw_len)) > UINT32_MAX)
+ {
+ node->flags |= ASN1_BER_EVENT_LEN_TOO_LONG;
+ ac->parser_status = ASN1_STATUS_INVALID;
+ return ASN1_PARSER_ERR;
+ }
+
+ content_len += raw_len;
+ }
+
+ ac->iter++;
+
+ node->len.len = content_len;
+ return ASN1_PARSER_OK;
+}
+
+
+/**
+ * \brief Check the content length and perform other inspections
+ * and decodings if necessary
+ *
+ * \param ac pointer to the ASN1 Context data
+ *
+ * \retval byte of the status of the parser
+ */
+uint8_t SCAsn1DecodeContent(Asn1Ctx *ac)
+{
+
+ Asn1Node *node = ASN1CTX_CUR_NODE(ac);
+
+ /* Uops, if we are done, we break here */
+ if (node->flags & ASN1_NODE_IS_EOC)
+ return ASN1_PARSER_OK;
+
+ /* First check the form of length (BER, DER, CER)
+ * and if we are on a zero length */
+ if (node->len.form != ASN1_BER_LEN_INDEFINITE &&
+ node->len.len == 0)
+ {
+ node->data.len = 0;
+ return ASN1_PARSER_OK;
+ }
+
+ node->data.ptr = ac->iter;
+ /* If we have a complete length, check that
+ * it is in bounds */
+ if (ac->iter + node->len.len > ac->end) {
+ /* We do not have all the content octets! */
+ node->data.len = ac->end - ac->iter;
+ } else {
+ /* We have all the content octets */
+ node->data.len = node->len.len;
+ }
+
+ return ASN1_PARSER_OK;
+}
+
+/**
+ * \brief Decode and check the length, of the current node
+ * that we are parsing, also check invalid opts
+ *
+ * \param ac pointer to the ASN1 Context data
+ *
+ * \retval byte of the status of the parser
+ */
+uint8_t SCAsn1DecodeLength(Asn1Ctx *ac)
+{
+ uint8_t ret = 0;
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB;
+ return ASN1_PARSER_ERR;
+ }
+
+ Asn1Node *node = ASN1CTX_CUR_NODE(ac);
+ /* Store the position */
+ node->len.ptr = ac->iter;
+
+ uint8_t len_byte = *ac->iter;
+
+ //SCPrintByteBin(len_byte);
+
+ if (*node->id.ptr == 0 && len_byte == 0) {
+ node->flags |= ASN1_NODE_IS_EOC;
+ ac->iter++;
+ return ASN1_PARSER_OK;
+ }
+
+ if (ASN1_BER_IS_INDEFINITE_LEN(len_byte)) {
+ node->len.form = ASN1_BER_LEN_INDEFINITE;
+ node->len.len = 0;
+ ac->iter++;
+
+ uint8_t *tmp_iter = ac->iter;
+
+ /* Check that e-o-c is in bounds */
+ for (; tmp_iter < ac->end - 1; tmp_iter++) {
+ if (ASN1_BER_IS_EOC(tmp_iter)) {
+ node->data.len = tmp_iter - ac->iter;
+ node->len.len = tmp_iter - ac->iter;
+ return ASN1_PARSER_OK;
+ }
+ }
+
+ /* EOC Not found */
+ ac->parser_status |= ASN1_STATUS_INVALID;
+ node->flags |= ASN1_BER_EVENT_EOC_NOT_FOUND;
+
+ return ASN1_PARSER_ERR;
+
+ } else {
+ /* Look which form we get (and if it apply to the id type) */
+ if (ASN1_BER_IS_SHORT_LEN(len_byte)) {
+ node->len.form = ASN1_BER_LEN_SHORT;
+ node->len.len = ASN1_BER_GET_SHORT_LEN(len_byte);
+ ac->iter++;
+ } else {
+ node->len.form = ASN1_BER_LEN_LONG;
+
+ /* Ok, let's parse the long form */
+ return SCAsn1GetLengthLongForm(ac);
+ }
+
+ }
+ return ASN1_PARSER_OK;
+}
+
+/**
+ * \brief Decode and check the identifier information of the
+ * current node that we are parsing, also check invalid opts
+ *
+ * \param ac pointer to the ASN1 Context data
+ *
+ * \retval byte of the status of the parser
+ */
+uint8_t SCAsn1DecodeIdentifier(Asn1Ctx *ac)
+{
+ uint8_t ret = 0;
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB;
+ return ret;
+ }
+
+ Asn1Node *node = ASN1CTX_CUR_NODE(ac);
+ /* Store the position */
+ node->id.ptr = ac->iter;
+
+ //SCPrintByteBin(*ac->iter);
+
+ node->id.class_tag = ASN1_BER_GET_CLASS_TAG(*ac->iter);
+ node->id.tag_type = ASN1_BER_IS_CONSTRUCTED(*ac->iter);
+
+ if (ASN1_BER_IS_HIGH_TAG(*ac->iter)) {
+ return SCAsn1GetHighTagNumber(ac);
+ } else {
+ node->id.tag_num = ASN1_BER_GET_LOW_TAG_NUM(*ac->iter);
+ ac->iter++;
+ }
+
+ return ASN1_PARSER_OK;
+}
+
+/**
+ * \brief Helper function that print the bits of a byte
+ * to check encoding internals
+ * \param byte value of the byte
+ */
+void SCPrintByteBin(uint8_t byte)
+{
+ uint8_t i = 0;
+ for (i = 8; i > 0; i--) {
+ printf("%"PRIu8, (uint8_t)((byte >> (i - 1)) & 0x01));
+ if (i == 5)
+ printf(" ");
+ }
+ printf("\n");
+}
+
+/**
+ * \brief check if we have remaining data available,
+ * otherwise the parser should stop
+ * \param ac Asn1Ctx pointer initialized
+ * \retval 1 if we are out of bounds, 0 if not
+ */
+uint8_t SCAsn1CheckBounds(Asn1Ctx *ac)
+{
+ return (ac->iter < ac->end && ac->iter >= ac->data)? ASN1_PARSER_OK : ASN1_PARSER_ERR;
+}
+
+
+/**
+ * \brief Create a new ASN1 Parsing context
+ *
+ * \retval Asn1Ctx pointer to the new ctx
+ */
+Asn1Ctx *SCAsn1CtxNew(void)
+{
+ Asn1Ctx *ac = SCMalloc(sizeof(Asn1Ctx));
+
+ if (unlikely(ac == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ return NULL;
+ }
+ memset(ac, 0, sizeof(Asn1Ctx));
+
+ ac->asn1_stack = SCMalloc(sizeof(Asn1Node *) * asn1_max_frames_config);
+ if (ac->asn1_stack == NULL) {
+ SCFree(ac);
+ return NULL;
+ }
+ memset(ac->asn1_stack, 0, sizeof(Asn1Node *) * asn1_max_frames_config);
+
+ return ac;
+}
+
+/**
+ * \brief Destroy an ASN1 Parsing context
+ *
+ * \param Asn1Ctx pointer to the new ctx
+ */
+void SCAsn1CtxDestroy(Asn1Ctx *ac)
+{
+ if (ac == NULL)
+ return;
+
+ uint16_t i = 0;
+ for (; i < ac->cur_frame; i++) {
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, i);
+ if (node != NULL) {
+ SCFree(node);
+ }
+ }
+ SCFree(ac);
+}
+
+/**
+ * \brief Create a new node at the array stack of frames in the ctx
+ *
+ * \param ac pointer to the ASN1 ctx
+ * \param node index of the frame that we are going to allocate
+ * at the asn1 stack in the parser
+ *
+ * \retval Asn1Node pointer to the new node allocated
+ */
+Asn1Node *SCAsn1CtxNewFrame(Asn1Ctx *ac, uint16_t node)
+{
+ if (node >= asn1_max_frames_config) {
+ return NULL;
+ }
+
+ if (ac->asn1_stack[node] == NULL)
+ ac->asn1_stack[node] = SCMalloc(sizeof(Asn1Node));
+
+ if (ac->asn1_stack[node] == NULL)
+ return NULL;
+
+ memset(ac->asn1_stack[node], 0, sizeof(Asn1Node));
+ return ac->asn1_stack[node];
+}
+
+/**
+ * \brief Initialize the data of the ASN1 parser ctx with the asn1 raw buffer
+ *
+ * \param ac pointer to the ASN1 ctx
+ * \param data pointer to the data to process (binary raw of asn1)
+ * \param length length of the asn1 raw buffer
+ *
+ * \retval void
+ */
+void SCAsn1CtxInit(Asn1Ctx *ac, uint8_t *data, uint16_t length)
+{
+ ac->data = data;
+ ac->iter = data;
+ ac->len = length;
+ ac->end = data + length;
+ ac->parser_status = ASN1_STATUS_OK;
+}
+
+/**
+ * \brief Decode the nodes/frames located at certain position/level
+ *
+ * \param ac pointer to the ASN1 ctx
+ * \param node_id node index at the asn1 stack of the ctx
+ *
+ * \retval byte of parser status
+ */
+uint8_t SCAsn1Decode(Asn1Ctx *ac, uint16_t node_id)
+{
+ Asn1Node *node = NULL;
+ uint8_t ret = 0;
+
+ /* while remaining data, and no fatal error, or end, or max stack frames */
+ while (ac->iter < ac->end
+ && !(ac->parser_status & ASN1_STATUS_DONE)
+ && ac->cur_frame < asn1_max_frames_config)
+ {
+ /* Prepare a new frame */
+ if (SCAsn1CtxNewFrame(ac, node_id) == NULL)
+ break;
+
+ ac->cur_frame = node_id;
+ node = ASN1CTX_GET_NODE(ac, node_id);
+
+ SCLogDebug("ASN1 Getting ID, cur:%x remaining %"PRIu32, (uint8_t)*ac->iter, (uint32_t)(ac->end - ac->iter));
+
+ /* Get identifier/tag */
+ ret = SCAsn1DecodeIdentifier(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ SCLogDebug("Error parsing identifier");
+
+ node->flags |= ASN1_BER_EVENT_INVALID_ID;
+ ac->ctx_flags |= node->flags;
+
+ break;
+ }
+
+ SCLogDebug("ASN1 Getting LEN");
+
+ /* Get length of content */
+ ret = SCAsn1DecodeLength(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ SCLogDebug("Error parsing length");
+
+ node->flags |= ASN1_BER_EVENT_INVALID_LEN;
+ ac->ctx_flags |= node->flags;
+
+ break;
+ }
+
+ if ( !(node->flags & ASN1_NODE_IS_EOC)) {
+ SCLogDebug("ASN1 Getting CONTENT");
+
+ /* Inspect content */
+ ret = SCAsn1DecodeContent(ac);
+ if (ret == ASN1_PARSER_ERR) {
+ SCLogDebug("Error parsing content");
+
+ break;
+ }
+
+ /* Skip to the next record (if any) */
+ if (node->id.tag_type != ASN1_TAG_TYPE_CONSTRUCTED)
+ /* Is primitive, skip it all (no need to decode it)*/
+ ac->iter += node->data.len;
+ }
+
+ /* Check if we are done with data */
+ ret = SCAsn1CheckBounds(ac);
+ if (ret == ASN1_PARSER_ERR) {
+
+ ac->parser_status |= ASN1_STATUS_DONE;
+ /* There's no more data available */
+ ret = ASN1_PARSER_OK;
+
+ break;
+ }
+#if 0
+ printf("Tag Num: %"PRIu32", Tag Type: %"PRIu8", Class:%"PRIu8", Length: %"PRIu32"\n", node->id.tag_num, node->id.tag_type, node->id.class_tag, node->len.len);
+ printf("Data: \n");
+ PrintRawDataFp(stdout, node->data.ptr, node->len.len);
+ printf(" -- EOD --\n");
+#endif
+
+ /* Stack flags/events here, so we have the resume at the ctx flags */
+ ac->ctx_flags |= node->flags;
+
+ /* Check if it's not a primitive type,
+ * then we need to decode contents */
+ if (node->id.tag_type == ASN1_TAG_TYPE_CONSTRUCTED) {
+ ret = SCAsn1Decode(ac, node_id + 1);
+ } /* Else we have reached a primitive type and stop the recursion,
+ * look if we have other branches at the same level */
+
+ /* But first check if it's a constructed node, and the sum of child
+ * lengths was more than the length of this frame
+ * this would mean that we have an overflow at the attributes */
+ if (ac->iter > node->data.ptr + node->data.len + 1) {
+ /* We decoded more length on this frame */
+ }
+
+ node_id = ac->cur_frame + 1;
+ }
+
+ return ret;
+}
+
+/* ----------------------- Unit tests ------------------------ */
+#ifdef UNITTESTS
+
+/**
+ * \test Check we handle extended identifiers correctly
+ */
+int DecodeAsn1Test01(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 3;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->id.tag_num != 10) {
+ ret = 0;
+ printf("Error, expected tag_num 10, got %"PRIu32" :", node->id.tag_num);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle extended identifiers correctly
+ */
+int DecodeAsn1Test02(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x81\x81\x81\x81\x06";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 6;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->id.tag_num != 10) {
+ ret = 0;
+ printf("Error, expected tag_num 10, got %"PRIu32": ", node->id.tag_num);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle short identifiers correctly
+ */
+int DecodeAsn1Test03(void)
+{
+ uint8_t *str = (uint8_t *) "\x28";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 1;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->id.tag_num != 8) {
+ ret = 0;
+ printf("Error, expected tag_num 10, got %"PRIu32": ", node->id.tag_num);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle extended lengths correctly with indefinite form
+ */
+int DecodeAsn1Test04(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06\x80\x12\x12\x12\x00\x00";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 9;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->len.len != 3) {
+ ret = 0;
+ printf("Error, expected length 3, got %"PRIu32": ", node->len.len);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle extended lengths correctly
+ * in the definite form
+ */
+int DecodeAsn1Test05(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06\x82\x10\x10";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 6;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->len.len!= 32) {
+ ret = 0;
+ printf("Error, expected length 10, got %"PRIu32": ", node->len.len);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle short lengths correctly
+ */
+int DecodeAsn1Test06(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06\x26";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 4;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->len.len != 38) {
+ ret = 0;
+ printf("Error, expected length 10, got %"PRIu32": ", node->len.len);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle events correctly
+ */
+int DecodeAsn1Test07(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x00\x84\x06";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 4;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if ( !(ac->ctx_flags & ASN1_BER_EVENT_INVALID_ID)
+ || !(node->flags & ASN1_BER_EVENT_INVALID_ID))
+ {
+ ret = 0;
+ printf("Error, expected invalid id, got flags %"PRIu8": ", ac->ctx_flags);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle events correctly
+ */
+int DecodeAsn1Test08(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06\x81\xFF";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 5;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if ( !(ac->ctx_flags & ASN1_BER_EVENT_INVALID_LEN)
+ || !(node->flags & ASN1_BER_EVENT_INVALID_LEN))
+ {
+ ret = 0;
+ printf("Error, expected invalid length, got flags %"PRIu8": ", ac->ctx_flags);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Check we handle events correctly
+ */
+int DecodeAsn1Test09(void)
+{
+ uint8_t *str = (uint8_t *) "\x3F\x84\x06\x80\xAB\xCD\xEF";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = 7;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ SCAsn1Decode(ac, ac->cur_frame);
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if ( !(ac->ctx_flags & ASN1_BER_EVENT_EOC_NOT_FOUND)
+ || !(node->flags & ASN1_BER_EVENT_EOC_NOT_FOUND))
+ {
+ ret = 0;
+ printf("Error, expected eoc not found, got flags %"PRIu8": ", ac->ctx_flags);
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+/**
+ * \test Decode a big chunk of data
+ */
+int DecodeAsn1Test10(void)
+{
+ // Example from the specification X.690-0207 Appendix A.3
+ uint8_t *str = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
+ "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
+ "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
+ "Jones""\xA0\x0A\x43\x08""19590717"
+ "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
+ "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
+ "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
+ "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
+ "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
+ "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
+ "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
+ "\xA0\x0A\x43\x08""19590717";
+
+ Asn1Ctx *ac = SCAsn1CtxNew();
+ if (ac == NULL)
+ return 0;
+ uint8_t ret = 1;
+
+ uint16_t len = strlen((char *)str)-1;
+
+ SCAsn1CtxInit(ac, str, len);
+
+ ret = SCAsn1Decode(ac, ac->cur_frame);
+
+ /* General checks */
+ if (ret != ASN1_PARSER_OK) {
+ printf("Error decoding asn1 data: ");
+ ret = 0;
+ goto end;
+ }
+
+ if (ac->cur_frame != 59) {
+ printf("Error decoding asn1 data, not all the nodes"
+ "were correctly decoded: ");
+ ret = 0;
+ goto end;
+ }
+
+ if (ac->iter != ac->end) {
+ printf("Error decoding asn1 data, not all the nodes"
+ "were correctly decoded: ");
+ ret = 0;
+ goto end;
+ }
+
+ Asn1Node *node = ASN1CTX_GET_NODE(ac, 0);
+ if (node->len.len != 133) {
+ printf("Error decoding asn1 data, not all the nodes"
+ "were correctly decoded: ");
+ ret = 0;
+ goto end;
+ }
+
+ node = ASN1CTX_GET_NODE(ac, 30);
+ if (node->len.len != 133) {
+ printf("Error decoding asn1 data, not all the nodes"
+ "were correctly decoded: ");
+ ret = 0;
+ goto end;
+ }
+
+end:
+ SCAsn1CtxDestroy(ac);
+ return ret;
+}
+
+#endif
+
+void DecodeAsn1RegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DecodeAsn1Test01", DecodeAsn1Test01, 1);
+ UtRegisterTest("DecodeAsn1Test02", DecodeAsn1Test02, 1);
+ UtRegisterTest("DecodeAsn1Test03", DecodeAsn1Test03, 1);
+
+ UtRegisterTest("DecodeAsn1Test04", DecodeAsn1Test04, 1);
+ UtRegisterTest("DecodeAsn1Test05", DecodeAsn1Test05, 1);
+ UtRegisterTest("DecodeAsn1Test06", DecodeAsn1Test06, 1);
+
+ UtRegisterTest("DecodeAsn1Test07", DecodeAsn1Test07, 1);
+ UtRegisterTest("DecodeAsn1Test08", DecodeAsn1Test08, 1);
+ UtRegisterTest("DecodeAsn1Test09", DecodeAsn1Test09, 1);
+
+ UtRegisterTest("DecodeAsn1Test10", DecodeAsn1Test10, 1);
+#endif
+}
+
diff --git a/framework/src/suricata/src/util-decode-asn1.h b/framework/src/suricata/src/util-decode-asn1.h
new file mode 100644
index 00000000..d3ff9a3e
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-asn1.h
@@ -0,0 +1,220 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Implements ASN1 decoding (needed for the asn1 keyword)
+ */
+
+#ifndef __DECODE_ASN1_H__
+#define __DECODE_ASN1_H__
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+
+#define ASN1_MAX_FRAMES 128
+
+/* For future enconding type implementations */
+enum {
+ ASN1_BER_ENC,
+ ASN1_ENC_UNKNOWN
+};
+
+/* Class of tag */
+#define ASN1_BER_CLASS_UNIV 0
+#define ASN1_BER_CLASS_APP 1
+#define ASN1_BER_CLASS_CTX_SPECIFIC 2
+#define ASN1_BER_CLASS_PRIV 3
+
+/* For low tag numbers */
+#define ASN1_BER_GET_CLASS_TAG(id_octet) \
+ ((id_octet >> 6) & 0x03) /* (8.1.2.2a) */
+#define ASN1_BER_IS_CONSTRUCTED(id_octet) \
+ ((id_octet >> 5) & 0x01) /* (8.1.2.5) Constructed Tag */
+#define ASN1_BER_IS_PRIMITIVE(id_octet) \
+ (((id_octet >> 5) & 0x01)?0:1) /* (8.1.2.5) Primitive Tag */
+#define ASN1_BER_IS_LOW_TAG(id_octet) \
+ ASN1_BER_IS_PRIMITIVE(id_octet) /* (8.1.2.5) Is Low Tag
+ Number */
+#define ASN1_BER_GET_LOW_TAG_NUM(id_octet) \
+ (id_octet & 0x1F) /* (8.1.2.2c) Get LowTag Number */
+
+/* For high tag numbers */
+#define ASN1_BER_IS_HIGH_TAG(id_octet) \
+ ((ASN1_BER_GET_LOW_TAG_NUM(id_octet) == 0x1F) && \
+ ASN1_BER_IS_CONSTRUCTED(id_octet)) /* (8.1.2.4) High Tag Number */
+#define ASN1_BER_IS_HIGH_TAG_END(id_octet) \
+ ( !((id_octet >> 7) & 0x01)) /* (8.1.2.4) Is End of Tag Num */
+#define ASN1_BER_GET_HIGH_TAG_NUM(id_octet) \
+ (id_octet & 0x7F) /* (8.1.2.4) Part of High Tag
+ Number */
+
+
+#define ASN1_BER_IS_SHORT_LEN(id_octet) \
+ ( !((id_octet >> 7) & 0x01)) /* (8.1.3.3) Is short form */
+#define ASN1_BER_GET_SHORT_LEN(id_octet) \
+ (id_octet & 0x7F) /* (8.1.3.3) length value */
+#define ASN1_BER_GET_LONG_LEN_OCTETS(id_octet) \
+ (id_octet & 0x7F) /* (8.1.3.5) the number of
+ bytes */
+#define ASN1_BER_GET_LONG_LEN(id_octet) \
+ (id_octet) /* (8.1.3.5) the byte itself*/
+#define ASN1_BER_LONG_LEN_HAS_NEXT(id_octet) \
+ ( !((id_octet >> 7) & 0x01)) /* (8.1.3.5) Has next octets
+ lenght */
+#define ASN1_BER_IS_INDEFINITE_LEN(id_octet) \
+ (id_octet == 0x80) /* (8.1.3.6) Need end-of-ccontent */
+#define ASN1_BER_IS_EOC(tmp_iter) (*tmp_iter == 0 && *(tmp_iter + 1) == 0)
+
+/* Return the current node/frame that we are filling */
+#define ASN1CTX_CUR_NODE(ac) (ac->asn1_stack[ac->cur_frame])
+#define ASN1CTX_GET_NODE(ac, node) (ac->asn1_stack[node])
+
+/* BER Universal tags */
+#define ASN1_UNITAG_EOC 0 /* EOC */
+#define ASN1_UNITAG_BOOLEAN 1
+#define ASN1_UNITAG_INTEGER 2
+#define ASN1_UNITAG_BIT_STRING 3
+#define ASN1_UNITAG_OCTET_STRING 4
+#define ASN1_UNITAG_NULL 5
+#define ASN1_UNITAG_OID 6
+#define ASN1_UNITAG_OBJECT_DESCRIPTOR 7
+#define ASN1_UNITAG_EXTERNAL 8
+#define ASN1_UNITAG_REAL 9
+#define ASN1_UNITAG_ENUMERATED 10
+#define ASN1_UNITAG_EMBEDDED_PDV 11
+#define ASN1_UNITAG_UTF8_STRING 12
+#define ASN1_UNITAG_RELATIVE_OID 13
+#define ASN1_UNITAG_SEQUENCE 16
+#define ASN1_UNITAG_SET 17
+#define ASN1_UNITAG_NUMERIC_STRING 18
+#define ASN1_UNITAG_PRINTABLE_STRING 19
+#define ASN1_UNITAG_TELETEX_STRING 20
+#define ASN1_UNITAG_VIDEOTEX_STRING 21
+#define ASN1_UNITAG_IA5_STRING 22
+#define ASN1_UNITAG_UTCTIME 23
+#define ASN1_UNITAG_GENERALIZED_TIME 24
+#define ASN1_UNITAG_GRAPHIC_STRING 25
+#define ASN1_UNITAG_VISIBLE_STRING 26
+#define ASN1_UNITAG_GENERAL_STRING 27
+#define ASN1_UNITAG_UNIVERSAL_STRING 28
+#define ASN1_UNITAG_CHARACTER_STRING 29
+#define ASN1_UNITAG_BMP_STRING 30
+
+/* Length form */
+#define ASN1_BER_LEN_SHORT 0
+#define ASN1_BER_LEN_LONG 1
+#define ASN1_BER_LEN_INDEFINITE 2
+
+
+/* Error events/flags */
+#define ASN1_BER_EVENT_ID_TOO_LONG 0x01
+#define ASN1_BER_EVENT_INVALID_ID 0x02 /* (8.1.2.4.2c) First subsequent
+ id val (from bit 7 to 0) Shall
+ not be 0 */
+#define ASN1_BER_EVENT_INVALID_LEN 0x04 /* (8.1.3.2a) we expect a simple
+ form, or (8.1.3.5c) we got
+ 0xFF, or not enough data */
+#define ASN1_BER_EVENT_LEN_TOO_LONG 0x08
+#define ASN1_BER_EVENT_EOC_NOT_FOUND 0x10 /* EOC not found */
+
+
+/* Helper flags */
+#define ASN1_NODE_IS_EOC 1
+#define ASN1_TAG_TYPE_PRIMITIVE 0
+#define ASN1_TAG_TYPE_CONSTRUCTED 1
+
+typedef struct Asn1Len_ {
+ uint8_t form;
+ uint32_t len;
+ uint8_t *ptr;
+} Asn1Len;
+
+typedef struct Asn1Id_ {
+ uint8_t *ptr;
+ uint8_t class_tag;
+ uint8_t tag_type;
+ uint32_t tag_num;
+} Asn1Id;
+
+typedef struct Asn1Data_ {
+ uint8_t *ptr;
+ uint32_t len;
+ uint8_t type;
+} Asn1Data;
+
+typedef struct Asn1Node_ {
+ uint8_t *raw_str;
+ uint8_t data_len;
+ Asn1Len len;
+ Asn1Id id;
+ Asn1Data data;
+ uint8_t flags;
+} Asn1Node;
+
+typedef struct Asn1Ctx_ {
+ uint8_t *data;
+ uint8_t *end;
+ uint16_t len;
+
+ uint8_t *iter;
+
+ uint16_t cur_frame;
+ Asn1Node *asn1_stack2[ASN1_MAX_FRAMES];
+ Asn1Node **asn1_stack;
+
+ uint8_t parser_status;
+
+ uint8_t ctx_flags;
+} Asn1Ctx;
+
+/* Return codes of the decoder */
+#define ASN1_PARSER_OK 0x01 /* Everything ok */
+#define ASN1_PARSER_ERR 0x02 /* Internal error, fatal error, we can't continue decoding */
+
+/* Status of the parser */
+#define ASN1_STATUS_OK 0x00 /* On the road */
+#define ASN1_STATUS_INVALID 0x01 /* We found something weird/invalid by the specification, but we can try to continue parsing */
+#define ASN1_STATUS_OOB 0x02 /* We don't have enough data or ran out of bounds */
+#define ASN1_STATUS_DONE 0x04 /* We have finished cleanly */
+
+void SCPrintByteBin(uint8_t);
+
+Asn1Ctx *SCAsn1CtxNew(void);
+void SCAsn1CtxInit(Asn1Ctx *, uint8_t *, uint16_t);
+void SCAsn1CtxDestroy(Asn1Ctx *);
+
+uint8_t SCAsn1Decode(Asn1Ctx *, uint16_t);
+uint8_t SCAsn1DecodeIdentifier(Asn1Ctx *);
+uint8_t SCAsn1DecodeLength(Asn1Ctx *);
+uint8_t SCAsn1DecodeContent(Asn1Ctx *);
+
+uint8_t SCAsn1CheckBounds(Asn1Ctx *);
+
+void DecodeAsn1RegisterTests(void);
+void SCAsn1LoadConfig();
+
+#endif /* __DECODE_ASN1_H__ */
+
diff --git a/framework/src/suricata/src/util-decode-der-get.c b/framework/src/suricata/src/util-decode-der-get.c
new file mode 100644
index 00000000..4323eb39
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-der-get.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#include "suricata-common.h"
+
+#include "util-decode-der.h"
+#include "util-decode-der-get.h"
+
+static const uint8_t SEQ_IDX_ISSUER[] = { 0, 2 };
+static const uint8_t SEQ_IDX_SUBJECT[] = { 0, 4 };
+
+static const char *Oid2ShortStr(const char *oid)
+{
+ if (strcmp(oid, "1.2.840.113549.1.9.1")==0)
+ return "emailAddress";
+
+ if (strcmp(oid, "2.5.4.3")==0)
+ return "CN";
+
+ if (strcmp(oid, "2.5.4.5")==0)
+ return "serialNumber";
+
+ if (strcmp(oid, "2.5.4.6")==0)
+ return "C";
+
+ if (strcmp(oid, "2.5.4.7")==0)
+ return "L";
+
+ if (strcmp(oid, "2.5.4.8")==0)
+ return "ST";
+
+ if (strcmp(oid, "2.5.4.10")==0)
+ return "O";
+
+ if (strcmp(oid, "2.5.4.11")==0)
+ return "OU";
+
+ if (strcmp(oid, "0.9.2342.19200300.100.1.25")==0)
+ return "DC";
+
+ return "unknown";
+}
+
+/**
+ * \brief Iterate through an ASN.1 structure, following the index sequence.
+ * Context specific elements are skipped.
+ *
+ * \retval The matching node, or NULL
+ */
+const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz, uint32_t *errcode)
+{
+ const Asn1Generic * node;
+ uint8_t idx, i;
+ uint8_t offset = 0;
+
+ if (errcode)
+ *errcode = ERR_DER_MISSING_ELEMENT;
+
+ node = top;
+ if (node == NULL || seq_index == NULL)
+ return NULL;
+
+ for (offset=0; offset<seqsz; offset++) {
+
+ idx = seq_index[offset];
+ for (i=0; i<idx; i++) {
+ if (node == NULL || node->data == NULL)
+ return NULL;
+
+ /* skip context-specific elements */
+ while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) {
+ node = node->next;
+ if (node == NULL || node->data == NULL)
+ return NULL;
+ }
+
+ node = node->next;
+ if (node == NULL || node->data == NULL)
+ return NULL;
+ }
+
+ /* skip context-specific elements */
+ if (node == NULL || node->data == NULL)
+ return NULL;
+ while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) {
+ node = node->next;
+ if (node == NULL || node->data == NULL)
+ return NULL;
+ }
+
+ node = node->data;
+ }
+
+ if (errcode)
+ *errcode = 0;
+
+ return node;
+}
+
+int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode)
+{
+ const Asn1Generic *node_oid;
+ const Asn1Generic *node, *it;
+ const Asn1Generic *node_set;
+ const Asn1Generic *node_str;
+ const char *shortname;
+ int rc = -1;
+ const char *separator = ", ";
+
+ if (errcode)
+ *errcode = ERR_DER_MISSING_ELEMENT;
+
+ if (length < 10)
+ goto issuer_dn_error;
+ buffer[0] = '\0';
+
+ node = Asn1DerGet(cert, SEQ_IDX_ISSUER, sizeof(SEQ_IDX_ISSUER), errcode);
+ if ((node == NULL) || node->type != ASN1_SEQUENCE)
+ goto issuer_dn_error;
+
+ it = node;
+ while (it != NULL) {
+ if (it->data == NULL)
+ goto issuer_dn_error;
+ node_set = it->data;
+ if (node_set->type != ASN1_SET || node_set->data == NULL)
+ goto issuer_dn_error;
+ node = node_set->data;
+ if (node->type != ASN1_SEQUENCE || node->data == NULL)
+ goto issuer_dn_error;
+ node_oid = node->data;
+ if (node_oid->str == NULL || node_oid->type != ASN1_OID)
+ goto issuer_dn_error;
+ shortname = Oid2ShortStr(node_oid->str);
+ if (node->next == NULL)
+ goto issuer_dn_error;
+ node = node->next;
+ node_str = node->data;
+ if (node_str == NULL || node_str->str == NULL)
+ goto issuer_dn_error;
+
+ switch (node_str->type) {
+ case ASN1_PRINTSTRING:
+ case ASN1_IA5STRING:
+ case ASN1_T61STRING:
+ case ASN1_UTF8STRING:
+ case ASN1_OCTETSTRING:
+ strlcat(buffer, shortname, length);
+ strlcat(buffer, "=", length);
+ strlcat(buffer, node_str->str, length);
+ break;
+ default:
+ if (errcode)
+ *errcode = ERR_DER_UNSUPPORTED_STRING;
+ goto issuer_dn_error;
+ }
+
+ if (strcmp(shortname,"CN")==0)
+ separator = "/";
+ if (it->next != NULL)
+ strlcat(buffer, separator, length);
+ it = it->next;
+ }
+
+ if (errcode)
+ *errcode = 0;
+
+ rc = 0;
+issuer_dn_error:
+ return rc;
+}
+
+int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode)
+{
+ const Asn1Generic *node_oid;
+ const Asn1Generic *node, *it;
+ const Asn1Generic *node_set;
+ const Asn1Generic *node_str;
+ const char *shortname;
+ int rc = -1;
+ const char *separator = ", ";
+
+ if (errcode)
+ *errcode = ERR_DER_MISSING_ELEMENT;
+
+ if (length < 10)
+ goto subject_dn_error;
+ buffer[0] = '\0';
+
+ node = Asn1DerGet(cert, SEQ_IDX_SUBJECT, sizeof(SEQ_IDX_SUBJECT), errcode);
+
+ if ((node == NULL) || node->type != ASN1_SEQUENCE)
+ goto subject_dn_error;
+
+ it = node;
+ while (it != NULL) {
+ if (it == NULL || it->data == NULL)
+ goto subject_dn_error;
+ node_set = it->data;
+ if (node_set->type != ASN1_SET || node_set->data == NULL)
+ goto subject_dn_error;
+ node = node_set->data;
+ if (node->type != ASN1_SEQUENCE || node->data == NULL)
+ goto subject_dn_error;
+ node_oid = node->data;
+ if (node_oid->str == NULL || node_oid->type != ASN1_OID)
+ goto subject_dn_error;
+ shortname = Oid2ShortStr(node_oid->str);
+ if (node->next == NULL)
+ goto subject_dn_error;
+ node = node->next;
+ node_str = node->data;
+ if (node_str == NULL || node_str->str == NULL)
+ goto subject_dn_error;
+
+ switch (node_str->type) {
+ case ASN1_PRINTSTRING:
+ case ASN1_IA5STRING:
+ case ASN1_T61STRING:
+ case ASN1_UTF8STRING:
+ case ASN1_OCTETSTRING:
+ strlcat(buffer, shortname, length);
+ strlcat(buffer, "=", length);
+ strlcat(buffer, node_str->str, length);
+ break;
+ default:
+ if (errcode)
+ *errcode = ERR_DER_UNSUPPORTED_STRING;
+ goto subject_dn_error;
+ }
+
+ if (strcmp(shortname,"CN")==0)
+ separator = "/";
+ if (it->next != NULL)
+ strlcat(buffer, separator, length);
+ it = it->next;
+ }
+
+ if (errcode)
+ *errcode = 0;
+
+ rc = 0;
+subject_dn_error:
+ return rc;
+}
+
+/* vim: set et ts=4 sw=4: */
diff --git a/framework/src/suricata/src/util-decode-der-get.h b/framework/src/suricata/src/util-decode-der-get.h
new file mode 100644
index 00000000..79ecd787
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-der-get.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __UTIL_DECODE_DER_GET_H__
+#define __UTIL_DECODE_DER_GET_H__
+
+const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz, uint32_t *errcode);
+
+int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode);
+int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode);
+
+#endif /* __UTIL_DECODE_DER_GET_H__ */
diff --git a/framework/src/suricata/src/util-decode-der.c b/framework/src/suricata/src/util-decode-der.c
new file mode 100644
index 00000000..1687668b
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-der.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2011-2015 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+/*
+ * An ASN.1 Parser for DER-encoded structures.
+ * This parser is not written to be complete or fast, but is rather
+ * focused on stability and security.
+ * It does not support all ASN.1 structure, only a meaningful subset
+ * to decode x509v3 certificates (See RFC 3280).
+ *
+ * References (like 8.19.4) are relative to the ISO/IEC 8825-1:2003 document
+ *
+ */
+
+#include "suricata-common.h"
+
+#include "util-decode-der.h"
+
+#define MAX_OID_LENGTH 256
+
+static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerUTF8String(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode);
+
+static Asn1Generic * Asn1GenericNew(void)
+{
+ Asn1Generic *obj;
+
+ obj = SCMalloc(sizeof(Asn1Generic));
+ if (obj != NULL)
+ memset(obj, 0, sizeof(Asn1Generic));
+
+ return obj;
+}
+
+/**
+ * \retval r 0 ok, -1 error
+ */
+static int Asn1SequenceAppend(Asn1Generic *seq, Asn1Generic *node)
+{
+ Asn1Generic *it, *new_container;
+
+ if (seq->data == NULL) {
+ seq->data = node;
+ return 0;
+ }
+
+ new_container = Asn1GenericNew();
+ if (new_container == NULL)
+ return -1;
+ new_container->data = node;
+
+ for (it=seq; it->next != NULL; it=it->next)
+ ;
+
+ it->next = new_container;
+ return 0;
+}
+
+static Asn1Generic * DecodeAsn1DerGeneric(const unsigned char *buffer, uint32_t max_size, uint8_t depth, int seq_index, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t numbytes, el_max_size;
+ Asn1ElementType el;
+ uint8_t c;
+ uint32_t i;
+ Asn1Generic *child;
+ uint8_t el_type;
+
+ el.cls = (d_ptr[0] & 0xc0) >> 6;
+ el.pc = (d_ptr[0] & 0x20) >> 5;
+ el.tag = (d_ptr[0] & 0x1f);
+
+ el_type = el.tag;
+
+ if (el.tag == 0x1f)
+ return NULL;
+
+ switch (el.cls) {
+ case ASN1_CLASS_CONTEXTSPEC:
+ /* get element type from definition
+ * see http://www.ietf.org/rfc/rfc3280.txt)
+ */
+ if (depth == 2 && el.tag == 0) {
+ el_type = ASN1_SEQUENCE; /* TBSCertificate */
+ break;
+ }
+ if (depth == 2 && el.tag == 1) {
+ el_type = ASN1_BITSTRING; /* issuerUniqueID */
+ break;
+ }
+ if (depth == 2 && el.tag == 2) {
+ el_type = ASN1_BITSTRING; /* subjectUniqueID */
+ break;
+ }
+ if (depth == 2 && el.tag == 3) {
+ el_type = ASN1_SEQUENCE; /* extensions */
+ break;
+ }
+ /* unknown context specific value - do not decode */
+ break;
+ };
+
+ el_max_size = max_size - (d_ptr-buffer);
+ switch (el_type) {
+ case ASN1_INTEGER:
+ child = DecodeAsn1DerInteger(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_BOOLEAN:
+ child = DecodeAsn1DerBoolean(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_NULL:
+ child = DecodeAsn1DerNull(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_BITSTRING:
+ child = DecodeAsn1DerBitstring(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_OID:
+ child = DecodeAsn1DerOid(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_IA5STRING:
+ child = DecodeAsn1DerIA5String(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_OCTETSTRING:
+ child = DecodeAsn1DerOctetString(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_UTF8STRING:
+ child = DecodeAsn1DerUTF8String(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_PRINTSTRING:
+ child = DecodeAsn1DerPrintableString(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_SEQUENCE:
+ child = DecodeAsn1DerSequence(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_SET:
+ child = DecodeAsn1DerSet(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_T61STRING:
+ child = DecodeAsn1DerT61String(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ case ASN1_UTCTIME:
+ child = DecodeAsn1DerUTCTime(d_ptr, el_max_size, depth+1, errcode);
+ break;
+ default:
+ /* unknown ASN.1 type */
+ child = NULL;
+ child = Asn1GenericNew();
+ if (child == NULL)
+ break;
+ child->type = el.tag;
+ /* total sequence length */
+ const unsigned char * save_d_ptr = d_ptr;
+ d_ptr++;
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ child->length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ if (numbytes > el_max_size) {
+ SCFree(child);
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ return NULL;
+ }
+ child->length = 0;
+ d_ptr++;
+ for (i=0; i<numbytes; i++) {
+ child->length = child->length<<8 | d_ptr[0];
+ d_ptr++;
+ }
+ }
+ /* fix the length for unknown objects, else
+ * sequence parsing will fail
+ */
+ child->length += (d_ptr - save_d_ptr);
+ break;
+ };
+ if (child == NULL)
+ return NULL;
+
+ child->header = el;
+ return child;
+}
+
+static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint8_t numbytes;
+ uint32_t value;
+ uint32_t i;
+ Asn1Generic *a;
+
+ numbytes = d_ptr[1];
+
+ if (numbytes > size) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ return NULL;
+ }
+
+ d_ptr += 2;
+
+ value = 0;
+ /* Here we need to ensure that numbytes is less than 4
+ so integer affectation is possible. We set the value
+ to 0xffffffff which is by convention the unknown value.
+ In this case, the hexadecimal value must be used. */
+ if (numbytes > 4) {
+ value = 0xffffffff;
+ } else {
+ for (i=0; i<numbytes; i++) {
+ value = value<<8 | d_ptr[i];
+ }
+ }
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_INTEGER;
+ a->length = (d_ptr - buffer) + numbytes;
+ a->value = value;
+
+ a->str = SCMalloc(2*numbytes + 1);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+ for (i=0; i<numbytes; i++) {
+ snprintf(a->str + 2*i, 2*(numbytes-i)+1, "%02X", d_ptr[i]);
+ }
+ a->str[2*numbytes]='\0';
+
+ return a;
+}
+
+static int DecodeAsn1BuildValue(const unsigned char **d_ptr, uint32_t *val, uint8_t numbytes, uint32_t *errcode)
+{
+ int i;
+ uint32_t value = 0;
+ if (numbytes > 4) {
+ if (errcode)
+ *errcode = ERR_DER_INVALID_SIZE;
+ /* too big won't fit: set it to 0xffffffff by convention */
+ value = 0xffffffff;
+ *val = value;
+ return -1;
+ } else {
+ for (i=0; i<numbytes; i++) {
+ value = value<<8 | (*d_ptr)[0];
+ (*d_ptr)++;
+ }
+ }
+ *val = value;
+ return 0;
+}
+
+static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint8_t numbytes;
+ uint32_t value;
+ Asn1Generic *a;
+
+ numbytes = d_ptr[1];
+ d_ptr += 2;
+
+ if (DecodeAsn1BuildValue(&d_ptr, &value, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_BOOLEAN;
+ a->length = (d_ptr - buffer);
+ a->value = value;
+
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer, uint32_t size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint8_t numbytes;
+ uint32_t value;
+ Asn1Generic *a;
+
+ numbytes = d_ptr[1];
+ d_ptr += 2;
+ if (DecodeAsn1BuildValue(&d_ptr, &value, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_NULL;
+ a->length = (d_ptr - buffer);
+ a->value = 0;
+
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t length;
+ uint8_t numbytes, c;
+ Asn1Generic *a;
+
+ d_ptr++;
+
+ /* size */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ }
+ if (length > max_size)
+ return NULL;
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_BITSTRING;
+ a->strlen = length;
+ a->str = SCMalloc(length);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+ memcpy(a->str, (const char*)d_ptr, length);
+
+ d_ptr += length;
+
+ a->length = (d_ptr - buffer);
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t oid_length, oid_value;
+ uint8_t numbytes, c;
+ Asn1Generic *a;
+ uint32_t i;
+
+ d_ptr++;
+
+ /* size */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ oid_length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &oid_length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ }
+ if (oid_length > max_size)
+ return NULL;
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_OID;
+ a->str = SCMalloc(MAX_OID_LENGTH);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+
+ /* first element = X*40 + Y (See 8.19.4) */
+ snprintf(a->str, MAX_OID_LENGTH, "%d.%d", (d_ptr[0]/40), (d_ptr[0]%40));
+ d_ptr++;
+
+ /* sub-identifiers are multi valued, coded and 7 bits, first bit of the 8bits is used
+ to indicate, if a new value is starting */
+ for (i=1; i<oid_length; ) {
+ int s = strlen(a->str);
+ c = d_ptr[0];
+ oid_value = 0;
+ while ( i<oid_length && (c & (1<<7)) == 1<<7 ) {
+ oid_value = oid_value<<7 | (c & ~(1<<7));
+ d_ptr++;
+ c = d_ptr[0];
+ i++;
+ }
+ oid_value = oid_value<<7 | c;
+ d_ptr++;
+ i++;
+ snprintf(a->str + s, MAX_OID_LENGTH - s, ".%d", oid_value);
+ }
+
+ a->length = (d_ptr - buffer);
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t length, numbytes;
+ Asn1Generic *a;
+ unsigned char c;
+
+ d_ptr++;
+
+ /* total sequence length */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ }
+ if (length == UINT32_MAX || length > max_size) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ return NULL;
+ }
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_IA5STRING;
+ a->strlen = length;
+ a->str = SCMalloc(length+1);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+ strlcpy(a->str, (const char*)d_ptr, length+1);
+
+ d_ptr += length;
+
+ a->length = (d_ptr - buffer);
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t length, numbytes;
+ Asn1Generic *a;
+ unsigned char c;
+
+ d_ptr++;
+
+ /* total sequence length */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ }
+ if (length == UINT32_MAX || length > max_size) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ return NULL;
+ }
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_OCTETSTRING;
+ a->strlen = length;
+ /* Add one to the octet string for the 0. This will then
+ * allow us to use the string in printf */
+ a->str = SCMalloc(length + 1);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+ memcpy(a->str, (const char*)d_ptr, length);
+ a->str[length] = 0;
+
+ d_ptr += length;
+
+ a->length = (d_ptr - buffer);
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerUTF8String(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ Asn1Generic *a = DecodeAsn1DerOctetString(buffer, max_size, depth, errcode);
+ if (a != NULL)
+ a->type = ASN1_UTF8STRING;
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t length, numbytes;
+ Asn1Generic *a;
+ unsigned char c;
+
+ d_ptr++;
+
+ /* total sequence length */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ }
+ if (length == UINT32_MAX || length > max_size) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ return NULL;
+ }
+
+ a = Asn1GenericNew();
+ if (a == NULL)
+ return NULL;
+ a->type = ASN1_PRINTSTRING;
+ a->strlen = length;
+ a->str = SCMalloc(length+1);
+ if (a->str == NULL) {
+ SCFree(a);
+ return NULL;
+ }
+ strlcpy(a->str, (const char*)d_ptr, length+1);
+ a->str[length] = '\0';
+
+ d_ptr += length;
+
+ a->length = (d_ptr - buffer);
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t d_length, parsed_bytes, numbytes, el_max_size;
+ uint8_t c;
+ uint32_t seq_index;
+ Asn1Generic *node;
+
+ d_ptr++;
+
+ node = Asn1GenericNew();
+ if (node == NULL)
+ return NULL;
+ node->type = ASN1_SEQUENCE;
+
+ /* total sequence length */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ d_length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
+ SCFree(node);
+ return NULL;
+ }
+ }
+ node->length = d_length + (d_ptr - buffer);
+ if (node->length > max_size || node->length < d_length /* wrap */) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ SCFree(node);
+ return NULL;
+ }
+
+ parsed_bytes = 0;
+ seq_index = 0;
+
+ /* decode child elements */
+ while (parsed_bytes < d_length) {
+ el_max_size = max_size - (d_ptr-buffer);
+
+ Asn1Generic *child = DecodeAsn1DerGeneric(d_ptr, el_max_size, depth, seq_index, errcode);
+ if (child == NULL) {
+ if (errcode && *errcode != 0) {
+ DerFree(node);
+ return NULL;
+ }
+ break;
+ }
+
+ int ret = Asn1SequenceAppend(node, child);
+ if (ret == -1) {
+ DerFree(child);
+ break;
+ }
+
+ parsed_bytes += child->length;
+ d_ptr += child->length;
+ seq_index++;
+
+ }
+
+ return (Asn1Generic *)node;
+}
+
+static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t d_length, numbytes, el_max_size;
+ uint8_t c;
+ uint32_t seq_index;
+ Asn1Generic *node;
+ Asn1Generic *child;
+
+ d_ptr++;
+
+ node = Asn1GenericNew();
+ if (node == NULL)
+ return NULL;
+ node->type = ASN1_SET;
+ node->data = NULL;
+
+ /* total sequence length */
+ c = d_ptr[0];
+ if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */
+ d_length = c;
+ d_ptr++;
+ } else { /* long form 8.1.3.5 */
+ numbytes = c & 0x7f;
+ d_ptr++;
+ if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
+ SCFree(node);
+ return NULL;
+ }
+ }
+ node->length = d_length + (d_ptr - buffer);
+
+ if (node->length > max_size || node->length < d_length /* wrap */) {
+ if (errcode)
+ *errcode = ERR_DER_ELEMENT_SIZE_TOO_BIG;
+ SCFree(node);
+ return NULL;
+ }
+
+ seq_index = 0;
+
+ el_max_size = max_size - (d_ptr-buffer);
+ child = DecodeAsn1DerGeneric(d_ptr, el_max_size, depth, seq_index, errcode);
+ if (child == NULL) {
+ DerFree(node);
+ return NULL;
+ }
+
+ node->data = child;
+
+ return (Asn1Generic *)node;
+}
+
+static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ Asn1Generic *a;
+
+ a = DecodeAsn1DerIA5String(buffer, max_size, depth, errcode);
+ if (a != NULL)
+ a->type = ASN1_T61STRING;
+
+ return a;
+}
+
+static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer, uint32_t max_size, uint8_t depth, uint32_t *errcode)
+{
+ Asn1Generic *a;
+
+ a = DecodeAsn1DerIA5String(buffer, max_size, depth, errcode);
+ if (a != NULL)
+ a->type = ASN1_UTCTIME;
+
+ return a;
+}
+
+Asn1Generic * DecodeDer(const unsigned char *buffer, uint32_t size, uint32_t *errcode)
+{
+ const unsigned char *d_ptr = buffer;
+ uint32_t d_length, numbytes;
+ Asn1Generic *cert;
+ uint8_t c;
+
+ /* Check that buffer is an ASN.1 structure (basic checks) */
+ if (d_ptr[0] != 0x30 && d_ptr[1] != 0x82) /* Sequence */
+ return NULL;
+
+ c = d_ptr[1];
+ if ((c & (1<<7))>>7 != 1)
+ return NULL;
+
+ numbytes = c & 0x7f;
+ d_ptr += 2;
+ if (DecodeAsn1BuildValue(&d_ptr, &d_length, numbytes, errcode) == -1) {
+ return NULL;
+ }
+ if (d_length+(d_ptr-buffer) != size)
+ return NULL;
+
+ if (errcode)
+ *errcode = 0;
+
+ cert = DecodeAsn1DerGeneric(buffer, size, 0 /* depth */, 0, errcode);
+
+ return cert;
+}
+
+void DerFree(Asn1Generic *a)
+{
+ Asn1Generic *it, *n;
+
+ if (a == NULL)
+ return;
+
+ it = a;
+ while (it) {
+ n = it->next;
+ if (it->data) {
+ DerFree(it->data);
+ }
+ if (it->str)
+ SCFree(it->str);
+ memset(it, 0xff, sizeof(Asn1Generic));
+ SCFree(it);
+ it = n;
+ }
+}
diff --git a/framework/src/suricata/src/util-decode-der.h b/framework/src/suricata/src/util-decode-der.h
new file mode 100644
index 00000000..4c4b1aaf
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-der.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011-2012 ANSSI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __UTIL_DECODE_DER_H__
+#define __UTIL_DECODE_DER_H__
+
+#define ASN1_CLASS_UNIVERSAL 0
+#define ASN1_CLASS_APPLICATION 1
+#define ASN1_CLASS_CONTEXTSPEC 2
+#define ASN1_CLASS_PRIVATE 3
+
+#define ASN1_UNKNOWN 0
+#define ASN1_BOOLEAN 0x01
+#define ASN1_INTEGER 0x02
+#define ASN1_BITSTRING 0x03
+#define ASN1_OCTETSTRING 0x04
+#define ASN1_NULL 0x05
+#define ASN1_OID 0x06
+#define ASN1_UTF8STRING 0x0c
+#define ASN1_SEQUENCE 0x10
+#define ASN1_SET 0x11
+#define ASN1_PRINTSTRING 0x13
+#define ASN1_T61STRING 0x14
+#define ASN1_IA5STRING 0x16
+#define ASN1_UTCTIME 0x17
+
+typedef struct Asn1ElementType_ {
+ uint8_t cls:2;
+ uint8_t pc:1;
+ uint8_t tag:5;
+} __attribute__((packed)) Asn1ElementType;
+
+/* Generic ASN.1 element
+ * Presence and meaning of fields depends on the header and type values.
+ */
+typedef struct Asn1Generic_ {
+ Asn1ElementType header;
+ uint8_t type;
+ uint32_t length; /* length of node, including header */
+
+ struct Asn1Generic_ *data; /* only if type is structured */
+
+ char *str;
+ uint32_t strlen;
+ uint64_t value;
+ struct Asn1Generic_ *next; /* only if type is sequence */
+} Asn1Generic;
+
+/* Generic error */
+#define ERR_DER_GENERIC 0x01
+/* Unknown ASN.1 element type */
+#define ERR_DER_UNKNOWN_ELEMENT 0x02
+/* One element requires to read more bytes than available */
+#define ERR_DER_ELEMENT_SIZE_TOO_BIG 0x03
+/* One element size is invalid (more than 4 bytes long) */
+#define ERR_DER_INVALID_SIZE 0x04
+/* Unsupported string type */
+#define ERR_DER_UNSUPPORTED_STRING 0x05
+/* Missing field or element */
+#define ERR_DER_MISSING_ELEMENT 0x06
+
+Asn1Generic * DecodeDer(const unsigned char *buffer, uint32_t size, uint32_t *errcode);
+void DerFree(Asn1Generic *a);
+
+#endif /* __UTIL_DECODE_DER_H__ */
diff --git a/framework/src/suricata/src/util-decode-mime.c b/framework/src/suricata/src/util-decode-mime.c
new file mode 100644
index 00000000..3f4affcc
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-mime.c
@@ -0,0 +1,2887 @@
+/* Copyright (C) 2012 BAE Systems
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author David Abarbanel <david.abarbanel@baesystems.com>
+ *
+ */
+
+#include "suricata-common.h"
+
+#include "util-decode-mime.h"
+
+#include "util-spm-bs.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+#include "util-print.h"
+
+/* Character constants */
+#ifndef CR
+#define CR 13
+#define LF 10
+#endif
+
+#define CRLF "\r\n"
+#define COLON 58
+#define DASH 45
+#define PRINTABLE_START 33
+#define PRINTABLE_END 126
+#define UC_START 65
+#define UC_END 90
+#define LC_START 97
+#define LC_END 122
+#define UC_LC_DIFF 32
+#define EOL_LEN 2
+
+/* Base-64 constants */
+#define BASE64_STR "Base64"
+
+/* Mime Constants */
+#define MAX_LINE_LEN 998 /* Def in RFC 2045, excluding CRLF sequence */
+#define MAX_ENC_LINE_LEN 76 /* Def in RFC 2045, excluding CRLF sequence */
+#define MAX_HEADER_NAME 75 /* 75 + ":" = 76 */
+#define MAX_HEADER_VALUE 2000 /* Default - arbitrary limit */
+#define BOUNDARY_BUF 256
+#define CTNT_TYPE_STR "content-type"
+#define CTNT_DISP_STR "content-disposition"
+#define CTNT_TRAN_STR "content-transfer-encoding"
+#define MSG_ID_STR "message-id"
+#define BND_START_STR "boundary=\""
+#define TOK_END_STR "\""
+#define MSG_STR "message/"
+#define MULTIPART_STR "multipart/"
+#define QP_STR "quoted-printable"
+#define TXT_STR "text/plain"
+#define HTML_STR "text/html"
+#define URL_STR "http://"
+
+/* Memory Usage Constants */
+#define STACK_FREE_NODES 10
+
+/* Other Constants */
+#define MAX_IP4_CHARS 15
+#define MAX_IP6_CHARS 39
+
+/* Globally hold configuration data */
+static MimeDecConfig mime_dec_config = { 1, 1, 1, MAX_HEADER_VALUE };
+
+#ifdef DEBUG
+/* Mime Parser String translation */
+static const char *StateFlags[] = { "NONE",
+ "HEADER_READY",
+ "HEADER_STARTED",
+ "HEADER_DONE",
+ "BODY_STARTED",
+ "BODY_DONE",
+ "BODY_END_BOUND",
+ "PARSE_DONE",
+ "PARSE_ERROR",
+ NULL };
+#endif
+
+/* URL executable file extensions */
+static const char *UrlExeExts[] = { ".exe",
+ ".vbs",
+ ".bin",
+ ".cmd",
+ ".bat",
+ ".jar",
+ ".js",
+ NULL };
+
+/**
+ * \brief Function used to print character strings that are not null-terminated
+ *
+ * \param log_level The logging level in which to print
+ * \param label A label for the string to print
+ * \param src The source string
+ * \param len The length of the string
+ *
+ * \return none
+ */
+static void PrintChars(int log_level, char *label, const uint8_t *src, uint32_t len)
+{
+#ifdef DEBUG
+ if (log_level <= sc_log_global_log_level) {
+ printf("[%s]\n", label);
+ PrintRawDataFp(stdout, (uint8_t *)src, len);
+ }
+#endif
+}
+
+/**
+ * \brief Set global config policy
+ *
+ * \param config Config policy to set
+ * \return none
+ */
+void MimeDecSetConfig(MimeDecConfig *config)
+{
+ if (config != NULL) {
+ mime_dec_config = *config;
+
+ /* Set to default */
+ if (mime_dec_config.header_value_depth == 0) {
+ mime_dec_config.header_value_depth = MAX_HEADER_VALUE;
+ }
+ } else {
+ SCLogWarning(SC_ERR_MISSING_CONFIG_PARAM, "Invalid null configuration parameters");
+ }
+}
+
+/**
+ * \brief Get global config policy
+ *
+ * \return config data structure
+ */
+MimeDecConfig * MimeDecGetConfig(void)
+{
+ return &mime_dec_config;
+}
+
+/**
+ * \brief Follow the 'next' pointers to the leaf
+ *
+ * \param node The root entity
+ *
+ * \return Pointer to leaf on 'next' side
+ *
+ */
+static MimeDecEntity *findLastSibling(MimeDecEntity *node)
+{
+ if (node == NULL)
+ return NULL;
+ while(node->next != NULL)
+ node = node->next;
+ return node;
+}
+
+/**
+ * \brief Frees a mime entity tree
+ *
+ * \param entity The root entity
+ *
+ * \return none
+ *
+ */
+void MimeDecFreeEntity (MimeDecEntity *entity)
+{
+ if (entity == NULL)
+ return;
+ MimeDecEntity *lastSibling = findLastSibling(entity);
+ while (entity != NULL)
+ {
+ /**
+ * Move child to next
+ * Transform tree into list
+ */
+ if (entity->child != NULL)
+ {
+ lastSibling->next = entity->child;
+ lastSibling = findLastSibling(lastSibling);
+ }
+
+ /**
+ * Move to next element
+ */
+ MimeDecEntity *old = entity;
+ entity = entity->next;
+
+ MimeDecFreeField(old->field_list);
+ MimeDecFreeUrl(old->url_list);
+ SCFree(old->filename);
+
+ SCFree(old);
+ }
+}
+
+/**
+ * \brief Iteratively frees a header field entry list
+ *
+ * \param field The header field
+ *
+ * \return none
+ *
+ */
+void MimeDecFreeField(MimeDecField *field)
+{
+ MimeDecField *temp, *curr;
+
+ if (field != NULL) {
+
+ curr = field;
+ while (curr != NULL) {
+ temp = curr;
+ curr = curr->next;
+
+ /* Free contents of node */
+ SCFree(temp->name);
+ SCFree(temp->value);
+
+ /* Now free node data */
+ SCFree(temp);
+ }
+ }
+}
+
+/**
+ * \brief Iteratively frees a URL entry list
+ *
+ * \param url The url entry
+ *
+ * \return none
+ *
+ */
+void MimeDecFreeUrl(MimeDecUrl *url)
+{
+ MimeDecUrl *temp, *curr;
+
+ if (url != NULL) {
+
+ curr = url;
+ while (curr != NULL) {
+ temp = curr;
+ curr = curr->next;
+
+ /* Now free node data */
+ SCFree(temp->url);
+ SCFree(temp);
+ }
+ }
+}
+
+/**
+ * \brief Creates and adds a header field entry to an entity
+ *
+ * The entity is optional. If NULL is specified, than a new stand-alone field
+ * is created.
+ *
+ * \param entity The parent entity
+ *
+ * \return The field object, or NULL if the operation fails
+ *
+ */
+MimeDecField * MimeDecAddField(MimeDecEntity *entity)
+{
+ MimeDecField *node = SCMalloc(sizeof(MimeDecField));
+ if (unlikely(node == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ memset(node, 0x00, sizeof(MimeDecField));
+
+ /* If list is empty, then set as head of list */
+ if (entity->field_list == NULL) {
+ entity->field_list = node;
+ } else {
+ /* Otherwise add to beginning of list since these are out-of-order in
+ * the message */
+ node->next = entity->field_list;
+ entity->field_list = node;
+ }
+
+ return node;
+}
+
+/**
+ * \brief Searches for a header field with the specified name
+ *
+ * \param entity The entity to search
+ * \param name The header name (lowercase)
+ *
+ * \return The field object, or NULL if not found
+ *
+ */
+MimeDecField * MimeDecFindField(const MimeDecEntity *entity, const char *name) {
+ MimeDecField *curr = entity->field_list;
+
+ while (curr != NULL) {
+ /* name is stored lowercase */
+ if (strlen(name) == curr->name_len) {
+ if (SCMemcmp(curr->name, name, curr->name_len) == 0) {
+ break;
+ }
+ }
+ curr = curr->next;
+ }
+
+ return curr;
+}
+
+/**
+ * \brief Creates and adds a URL entry to the specified entity
+ *
+ * The entity is optional and if NULL is specified, then a new list will be created.
+ *
+ * \param entity The entity
+ *
+ * \return URL entry or NULL if the operation fails
+ *
+ */
+static MimeDecUrl * MimeDecAddUrl(MimeDecEntity *entity, uint8_t *url, uint32_t url_len, uint8_t flags)
+{
+ MimeDecUrl *node = SCMalloc(sizeof(MimeDecUrl));
+ if (unlikely(node == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ memset(node, 0x00, sizeof(MimeDecUrl));
+
+ node->url = url;
+ node->url_len = url_len;
+ node->url_flags = flags;
+
+ /* If list is empty, then set as head of list */
+ if (entity->url_list == NULL) {
+ entity->url_list = node;
+ } else {
+ /* Otherwise add to beginning of list since these are out-of-order in
+ * the message */
+ node->next = entity->url_list;
+ entity->url_list = node;
+ }
+
+ return node;
+}
+
+/**
+ * \brief Creates and adds a child entity to the specified parent entity
+ *
+ * \param parent The parent entity
+ *
+ * \return The child entity, or NULL if the operation fails
+ *
+ */
+MimeDecEntity * MimeDecAddEntity(MimeDecEntity *parent)
+{
+ MimeDecEntity *curr, *node = SCMalloc(sizeof(MimeDecEntity));
+ if (unlikely(node == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ memset(node, 0x00, sizeof(MimeDecEntity));
+
+ /* If parent is NULL then just return the new pointer */
+ if (parent != NULL) {
+ if (parent->child == NULL) {
+ parent->child = node;
+ } else {
+ curr = parent->child;
+ while (curr->next != NULL) {
+ curr = curr->next;
+ }
+ curr->next = node;
+ }
+ }
+
+ return node;
+}
+
+/**
+ * \brief Creates a mime header field and fills in its values and adds it to the
+ * specified entity
+ *
+ * \param entity Entity in which to add the field
+ * \param name String containing the name
+ * \param nlen Length of the name
+ * \param value String containing the value
+ * \param vlen Length of the value
+ *
+ * \return The field or NULL if the operation fails
+ */
+static MimeDecField * MimeDecFillField(MimeDecEntity *entity, uint8_t *name,
+ uint32_t nlen, const uint8_t *value, uint32_t vlen)
+{
+ if (nlen == 0 && vlen == 0)
+ return NULL;
+
+ MimeDecField *field = MimeDecAddField(entity);
+ if (unlikely(field == NULL)) {
+ return NULL;
+ }
+
+ if (nlen > 0) {
+ /* convert to lowercase and store */
+ uint32_t u;
+ for (u = 0; u < nlen; u++)
+ name[u] = tolower(name[u]);
+
+ field->name = (uint8_t *)name;
+ field->name_len = nlen;
+ }
+
+ if (vlen > 0) {
+ field->value = (uint8_t *)value;
+ field->value_len = vlen;
+ }
+
+ return field;
+}
+
+/**
+ * \brief Pushes a node onto a stack and returns the new node.
+ *
+ * \param stack The top of the stack
+ *
+ * \return pointer to a new node, otherwise NULL if it fails
+ */
+static MimeDecStackNode * PushStack(MimeDecStack *stack)
+{
+ /* Attempt to pull from free nodes list */
+ MimeDecStackNode *node = stack->free_nodes;
+ if (node == NULL) {
+ node = SCMalloc(sizeof(MimeDecStackNode));
+ if (unlikely(node == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ } else {
+ /* Move free nodes pointer over */
+ stack->free_nodes = stack->free_nodes->next;
+ stack->free_nodes_cnt--;
+ }
+ memset(node, 0x00, sizeof(MimeDecStackNode));
+
+ /* Push to top of stack */
+ node->next = stack->top;
+ stack->top = node;
+
+ /* Return a pointer to the top of the stack */
+ return node;
+}
+
+/**
+ * \brief Pops the top node from the stack and returns the next node.
+ *
+ * \param stack The top of the stack
+ *
+ * \return pointer to the next node, otherwise NULL if no nodes remain
+ */
+static MimeDecStackNode * PopStack(MimeDecStack *stack)
+{
+ /* Move stack pointer to next item */
+ MimeDecStackNode *curr = stack->top;
+ if (curr != NULL) {
+ curr = curr->next;
+ }
+
+ /* Always free alloc'd memory */
+ SCFree(stack->top->bdef);
+
+ /* Now move head to free nodes list */
+ if (stack->free_nodes_cnt < STACK_FREE_NODES) {
+ stack->top->next = stack->free_nodes;
+ stack->free_nodes = stack->top;
+ stack->free_nodes_cnt++;
+ } else {
+ SCFree(stack->top);
+ }
+ stack->top = curr;
+
+ /* Return a pointer to the top of the stack */
+ return curr;
+}
+
+/**
+ * \brief Frees the stack along with the free-nodes list
+ *
+ * \param stack The stack pointer
+ *
+ * \return none
+ */
+static void FreeMimeDecStack(MimeDecStack *stack)
+{
+ MimeDecStackNode *temp, *curr;
+
+ if (stack != NULL) {
+ /* Top of stack */
+ curr = stack->top;
+ while (curr != NULL) {
+ temp = curr;
+ curr = curr->next;
+
+ /* Now free node */
+ SCFree(temp->bdef);
+ SCFree(temp);
+ }
+
+ /* Free nodes */
+ curr = stack->free_nodes;
+ while (curr != NULL) {
+ temp = curr;
+ curr = curr->next;
+
+ /* Now free node */
+ SCFree(temp);
+ }
+
+ SCFree(stack);
+ }
+}
+
+/**
+ * \brief Adds a data value to the data values linked list
+ *
+ * \param dv The head of the linked list (NULL if new list)
+ *
+ * \return pointer to a new node, otherwise NULL if it fails
+ */
+static DataValue * AddDataValue(DataValue *dv)
+{
+ DataValue *curr, *node = SCMalloc(sizeof(DataValue));
+ if (unlikely(node == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ memset(node, 0x00, sizeof(DataValue));
+
+ if (dv != NULL) {
+ curr = dv;
+ while (curr->next != NULL) {
+ curr = curr->next;
+ }
+
+ curr->next = node;
+ }
+
+ return node;
+}
+
+/**
+ * \brief Frees a linked list of data values starting at the head
+ *
+ * \param dv The head of the linked list
+ *
+ * \return none
+ */
+static void FreeDataValue(DataValue *dv)
+{
+ DataValue *temp, *curr;
+
+ if (dv != NULL) {
+ curr = dv;
+ while (curr != NULL) {
+ temp = curr;
+ curr = curr->next;
+
+ /* Now free node */
+ SCFree(temp->value);
+ SCFree(temp);
+ }
+ }
+}
+
+/**
+ * \brief Converts a list of data values into a single value (returns dynamically
+ * allocated memory)
+ *
+ * \param dv The head of the linked list (NULL if new list)
+ * \param len The output length of the single value
+ *
+ * \return pointer to a single value, otherwise NULL if it fails or is zero-length
+ */
+static uint8_t * GetFullValue(DataValue *dv, uint32_t *len)
+{
+ DataValue *curr;
+ uint32_t offset = 0;
+ uint8_t *val = NULL;
+
+ /* First calculate total length */
+ *len = 0;
+ curr = dv;
+ while (curr != NULL) {
+ *len += curr->value_len;
+
+#if 0
+ /* Add CRLF except on last one */
+ if (curr->next != NULL) {
+ *len += 2;
+ }
+#endif
+ curr = curr->next;
+ }
+
+ /* Must have at least one character in the value */
+ if (*len > 0) {
+ val = SCCalloc(1, *len);
+ if (unlikely(val == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ *len = 0;
+ return NULL;
+ }
+
+ curr = dv;
+ while (curr != NULL) {
+ memcpy(val + offset, curr->value, curr->value_len);
+ offset += curr->value_len;
+
+#if 0 /* VJ unclear why this is needed ? */
+ /* Add CRLF except on last one */
+ if (curr->next != NULL) {
+ memcpy(val + offset, CRLF, 2);
+ offset += 2;
+ }
+#endif
+ curr = curr->next;
+ }
+ }
+
+ return val;
+}
+
+/**
+ * \brief Find a string while searching up to N characters within a source
+ * buffer
+ *
+ * \param src The source string (not null-terminated)
+ * \param len The length of the source string
+ * \param find The string to find (null-terminated)
+ * \param find_len length of the 'find' string
+ *
+ * \return Pointer to the position it was found, otherwise NULL if not found
+ */
+static inline uint8_t * FindBuffer(const uint8_t *src, uint32_t len, const uint8_t *find, uint32_t find_len)
+{
+ /* Use utility search function */
+ return BasicSearchNocase(src, len, find, find_len);
+}
+
+/**
+ * \brief Get a line (CRLF or just CR or LF) from a buffer (similar to GetToken)
+ *
+ * \param buf The input buffer (not null-terminated)
+ * \param blen The length of the input buffer
+ * \param remainPtr Pointer to remaining after tokenizing iteration
+ * \param tokLen Output token length (if non-null line)
+ *
+ * \return Pointer to line
+ */
+static uint8_t * GetLine(uint8_t *buf, uint32_t blen, uint8_t **remainPtr,
+ uint32_t *tokLen)
+{
+ uint32_t i;
+ uint8_t *tok;
+
+ /* So that it can be used just like strtok_r */
+ if (buf == NULL) {
+ buf = *remainPtr;
+ } else {
+ *remainPtr = buf;
+ }
+ if (buf == NULL)
+ return NULL;
+
+ tok = buf;
+
+ /* length must be specified */
+ for (i = 0; i < blen && buf[i] != 0; i++) {
+
+ /* Found delimiter */
+ if (buf[i] == CR || buf[i] == LF) {
+
+ /* Add another if we find either CRLF or LFCR */
+ *remainPtr += (i + 1);
+ if ((i + 1 < blen) && buf[i] != buf[i + 1] &&
+ (buf[i + 1] == CR || buf[i + 1] == LF)) {
+ (*remainPtr)++;
+ }
+ break;
+ }
+ }
+
+ /* If no delimiter found, then point to end of buffer */
+ if (buf == *remainPtr) {
+ (*remainPtr) += i;
+ }
+
+ /* Calculate token length */
+ *tokLen = (buf + i) - tok;
+
+ return tok;
+}
+
+/**
+ * \brief Get token from buffer and return pointer to it
+ *
+ * \param buf The input buffer (not null-terminated)
+ * \param blen The length of the input buffer
+ * \param delims Character delimiters (null-terminated)
+ * \param remainPtr Pointer to remaining after tokenizing iteration
+ * \param tokLen Output token length (if non-null line)
+ *
+ * \return Pointer to token, or NULL if not found
+ */
+static uint8_t * GetToken(uint8_t *buf, uint32_t blen, const char *delims,
+ uint8_t **remainPtr, uint32_t *tokenLen)
+{
+ uint32_t i, j, delimFound = 0;
+ uint8_t *tok = NULL;
+
+ /* So that it can be used just like strtok_r */
+ if (buf == NULL) {
+ buf = *remainPtr;
+ } else {
+ *remainPtr = buf;
+ }
+ if (buf == NULL)
+ return NULL;
+
+ /* Must specify length */
+ for (i = 0; i < blen && buf[i] != 0; i++) {
+
+ /* Look for delimiters */
+ for (j = 0; delims[j] != 0; j++) {
+ if (buf[i] == delims[j]) {
+ /* Data must be found before delimiter matters */
+ if (tok != NULL) {
+ (*remainPtr) += (i + 1);
+ }
+ delimFound = 1;
+ break;
+ }
+ }
+
+ /* If at least one non-delimiter found, then a token is found */
+ if (tok == NULL && !delimFound) {
+ tok = buf + i;
+ } else {
+ /* Reset delimiter */
+ delimFound = 0;
+ }
+
+ /* If delimiter found, then break out of loop */
+ if (buf != *remainPtr) {
+ break;
+ }
+ }
+
+ /* Make sure remaining points to end of buffer if delimiters not found */
+ if (tok != NULL) {
+ if (buf == *remainPtr) {
+ (*remainPtr) += i;
+ }
+
+ /* Calculate token length */
+ *tokenLen = (buf + i) - tok;
+ }
+
+ return tok;
+}
+
+/**
+ * \brief Stores the final MIME header value into the current entity on the
+ * stack.
+ *
+ * \param state The parser state
+ *
+ * \return MIME_DEC_OK if stored, otherwise a negative number indicating error
+ */
+static int StoreMimeHeader(MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK, stored = 0;
+ uint8_t *val;
+ uint32_t vlen;
+
+ /* Lets save the most recent header */
+ if (state->hname != NULL || state->hvalue != NULL) {
+ SCLogDebug("Storing last header");
+ val = GetFullValue(state->hvalue, &vlen);
+ if (val != NULL) {
+ if (state->hname == NULL) {
+ SCLogDebug("Error: Invalid parser state - header value without"
+ " name");
+ ret = MIME_DEC_ERR_PARSE;
+ } else if (state->stack->top != NULL) {
+ /* Store each header name and value */
+ if (MimeDecFillField(state->stack->top->data, state->hname,
+ state->hlen, val, vlen) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "MimeDecFillField() function failed");
+ ret = MIME_DEC_ERR_MEM;
+ } else {
+ stored = 1;
+ }
+ } else {
+ SCLogDebug("Error: Stack pointer missing");
+ ret = MIME_DEC_ERR_DATA;
+ }
+ } else if (state->hvalue != NULL) {
+ /* Memory allocation must have failed since val is NULL */
+ SCLogError(SC_ERR_MEM_ALLOC, "GetFullValue() function failed");
+ ret = MIME_DEC_ERR_MEM;
+ }
+
+ /* Do cleanup here */
+ if (!stored) {
+ SCFree(state->hname);
+ SCFree(val);
+ }
+ state->hname = NULL;
+ FreeDataValue(state->hvalue);
+ state->hvalue = NULL;
+ state->hvlen = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Function determines whether a url string points to an executable
+ * based on file extension only.
+ *
+ * \param url The url string
+ * \param len The url string length
+ *
+ * \retval 1 The url points to an EXE
+ * \retval 0 The url does NOT point to an EXE
+ */
+static int IsExeUrl(const uint8_t *url, uint32_t len)
+{
+ int isExeUrl = 0;
+ uint32_t i, extLen;
+ uint8_t *ext;
+
+ /* Now check for executable extensions and if not found, cut off at first '/' */
+ for (i = 0; UrlExeExts[i] != NULL; i++) {
+ extLen = strlen(UrlExeExts[i]);
+ ext = FindBuffer(url, len, (uint8_t *)UrlExeExts[i], strlen(UrlExeExts[i]));
+ if (ext != NULL && (ext + extLen - url == (int)len || ext[extLen] == '?')) {
+ isExeUrl = 1;
+ break;
+ }
+ }
+
+ return isExeUrl;
+}
+
+/**
+ * \brief Function determines whether a host string is a numeric IP v4 address
+ *
+ * \param urlhost The host string
+ * \param len The host string length
+ *
+ * \retval 1 The host is a numeric IP
+ * \retval 0 The host is NOT a numeric IP
+ */
+static int IsIpv4Host(const uint8_t *urlhost, uint32_t len)
+{
+ struct sockaddr_in sa;
+ char tempIp[MAX_IP4_CHARS + 1];
+
+ /* Cut off at '/' */
+ uint32_t i = 0;
+ for (i = 0; i < len && urlhost[i] != 0; i++) {
+
+ if (urlhost[i] == '/') {
+ break;
+ }
+ }
+
+ /* Too many chars */
+ if (i > MAX_IP4_CHARS) {
+ return 0;
+ }
+
+ /* Create null-terminated string */
+ memcpy(tempIp, urlhost, i);
+ tempIp[i] = '\0';
+
+ return inet_pton(AF_INET, tempIp, &(sa.sin_addr));
+}
+
+/**
+ * \brief Function determines whether a host string is a numeric IP v6 address
+ *
+ * \param urlhost The host string
+ * \param len The host string length
+ *
+ * \retval 1 The host is a numeric IP
+ * \retval 0 The host is NOT a numeric IP
+ */
+static int IsIpv6Host(const uint8_t *urlhost, uint32_t len)
+{
+ struct sockaddr_in sa;
+ char tempIp[MAX_IP6_CHARS + 1];
+
+ /* Cut off at '/' */
+ uint32_t i = 0;
+ for (i = 0; i < len && urlhost[i] != 0; i++) {
+
+ if (urlhost[i] == '/') {
+ break;
+ }
+ }
+
+ /* Too many chars */
+ if (i > MAX_IP6_CHARS) {
+ return 0;
+ }
+
+ /* Create null-terminated string */
+ memcpy(tempIp, urlhost, i);
+ tempIp[i] = '\0';
+
+ return inet_pton(AF_INET6, tempIp, &(sa.sin_addr));
+}
+
+/**
+ * \brief Traverses through the list of URLs for an exact match of the specified
+ * string
+ *
+ * \param entity The MIME entity
+ * \param url The matching URL string (lowercase)
+ * \param url_len The matching URL string length
+ *
+ * \return URL object or NULL if not found
+ */
+static MimeDecUrl *FindExistingUrl(MimeDecEntity *entity, uint8_t *url, uint32_t url_len)
+{
+ MimeDecUrl *curr = entity->url_list;
+
+ while (curr != NULL) {
+ if (url_len == curr->url_len) {
+ /* search url and stored url are both in
+ * lowercase, so we can do an exact match */
+ if (SCMemcmp(curr->url, url, url_len) == 0) {
+ break;
+ }
+ }
+ curr = curr->next;
+ }
+
+ return curr;
+}
+
+/**
+ * \brief This function searches a text or html line for a URL string
+ *
+ * URLS are generally truncated to the 'host.domain' format because
+ * some email messages contain dozens or even hundreds of URLs with
+ * the same host, but with only small variations in path.
+ *
+ * The exception is that URLs with executable file extensions are stored
+ * with the full path. They are stored in lowercase.
+ *
+ * Numeric IPs, malformed numeric IPs, and URLs pointing to executables are
+ * also flagged as URLs of interest.
+ *
+ * \param line the line
+ * \param len the line length
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int FindUrlStrings(const uint8_t *line, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+ uint8_t *fptr, *remptr, *tok = NULL, *tempUrl;
+ uint32_t tokLen = 0, i, tempUrlLen;
+ uint8_t urlStrLen = 0, flags = 0;
+
+ remptr = (uint8_t *)line;
+ do {
+ SCLogDebug("Looking for URL String starting with: %s", URL_STR);
+
+ /* Check for token definition */
+ fptr = FindBuffer(remptr, len - (remptr - line), (uint8_t *)URL_STR, strlen(URL_STR));
+ if (fptr != NULL) {
+
+ urlStrLen = strlen(URL_STR);
+ fptr += urlStrLen; /* Start at end of start string */
+ tok = GetToken(fptr, len - (fptr - line), " \"\'<>]\t", &remptr,
+ &tokLen);
+ if (tok == fptr) {
+ SCLogDebug("Found url string");
+
+ /* First copy to temp URL string */
+ tempUrl = SCMalloc(urlStrLen + tokLen);
+ if (unlikely(tempUrl == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+
+ PrintChars(SC_LOG_DEBUG, "RAW URL", tok, tokLen);
+
+ /* Copy over to temp URL while decoding */
+ tempUrlLen = 0;
+ for (i = 0; i < tokLen && tok[i] != 0; i++) {
+
+ // URL decoding would probably go here
+
+ /* url is all lowercase */
+ tempUrl[tempUrlLen] = tolower(tok[i]);
+ tempUrlLen++;
+ }
+
+ /* Determine if URL points to an EXE */
+ if (IsExeUrl(tempUrl, tempUrlLen)) {
+ flags |= URL_IS_EXE;
+
+ PrintChars(SC_LOG_DEBUG, "EXE URL", tempUrl, tempUrlLen);
+ } else {
+ /* Not an EXE URL */
+ /* Cut off length at first '/' */
+ /* If seems that BAESystems had done the following
+ in support of PEScan. We don't want it for logging.
+ Therefore its been removed.
+ tok = FindString(tempUrl, tempUrlLen, "/");
+ if (tok != NULL) {
+ tempUrlLen = tok - tempUrl;
+ }
+ */
+ }
+
+ /* Make sure remaining URL exists */
+ if (tempUrlLen > 0) {
+ if (!(FindExistingUrl(entity, tempUrl, tempUrlLen))) {
+ /* Now look for numeric IP */
+ if (IsIpv4Host(tempUrl, tempUrlLen)) {
+ flags |= URL_IS_IP4;
+
+ PrintChars(SC_LOG_DEBUG, "IP URL4", tempUrl, tempUrlLen);
+ } else if (IsIpv6Host(tempUrl, tempUrlLen)) {
+ flags |= URL_IS_IP6;
+
+ PrintChars(SC_LOG_DEBUG, "IP URL6", tempUrl, tempUrlLen);
+ }
+
+ /* Add URL list item */
+ MimeDecAddUrl(entity, tempUrl, tempUrlLen, flags);
+ } else {
+ SCFree(tempUrl);
+ }
+ } else {
+ SCFree(tempUrl);
+ }
+ }
+ }
+ } while (fptr != NULL);
+
+ return ret;
+}
+
+/**
+ * \brief This function is a pre-processor for handling decoded data chunks that
+ * then invokes the caller's callback function for further processing
+ *
+ * \param chunk The decoded chunk
+ * \param len The decoded chunk length (varies)
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessDecodedDataChunk(const uint8_t *chunk, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint8_t *remainPtr, *tok;
+ uint32_t tokLen;
+
+ if ((state->stack != NULL) && (state->stack->top != NULL) &&
+ (state->stack->top->data != NULL)) {
+ MimeDecConfig *mdcfg = MimeDecGetConfig();
+ if (mdcfg != NULL && mdcfg->extract_urls) {
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+ /* If plain text or html, then look for URLs */
+ if (((entity->ctnt_flags & CTNT_IS_TEXT) ||
+ (entity->ctnt_flags & CTNT_IS_MSG) ||
+ (entity->ctnt_flags & CTNT_IS_HTML)) &&
+ ((entity->ctnt_flags & CTNT_IS_ATTACHMENT) == 0)) {
+
+ /* Remainder from previous line */
+ if (state->linerem_len > 0) {
+ // TODO
+ } else {
+ /* No remainder from previous line */
+ /* Parse each line one by one */
+ remainPtr = (uint8_t *)chunk;
+ do {
+ tok = GetLine(remainPtr, len - (remainPtr - (uint8_t *)chunk),
+ &remainPtr, &tokLen);
+ if (tok != remainPtr) {
+ // DEBUG - ADDED
+ /* If last token found without CR/LF delimiter, then save
+ * and reconstruct with next chunk
+ */
+ if (tok + tokLen - (uint8_t *) chunk == (int)len) {
+ PrintChars(SC_LOG_DEBUG, "LAST CHUNK LINE - CUTOFF",
+ tok, tokLen);
+ SCLogDebug("\nCHUNK CUTOFF CHARS: %d delim %ld\n", tokLen, len - (tok + tokLen - (uint8_t *) chunk));
+ } else {
+ /* Search line for URL */
+ ret = FindUrlStrings(tok, tokLen, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: FindUrlStrings() function"
+ " failed: %d", ret);
+ break;
+ }
+ }
+ }
+ } while (tok != remainPtr && remainPtr - (uint8_t *) chunk < (int)len);
+ }
+ }
+ }
+
+ /* Now invoke callback */
+ if (state->DataChunkProcessorFunc != NULL) {
+ ret = state->DataChunkProcessorFunc(chunk, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: state->dataChunkProcessor() callback function"
+ " failed");
+ }
+ }
+ } else {
+ SCLogDebug("Error: Stack pointer missing");
+ ret = MIME_DEC_ERR_DATA;
+ }
+
+ /* Reset data chunk buffer */
+ state->data_chunk_len = 0;
+
+ /* Mark body / file as no longer at beginning */
+ state->body_begin = 0;
+
+ return ret;
+}
+
+/**
+ * \brief Processes a remainder (line % 4 = remainder) from the previous line
+ * such that all base64 decoding attempts are divisible by 4
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param state The current parser state
+ * \param force Flag indicating whether decoding should always occur
+ *
+ * \return Number of bytes pulled from the current buffer
+ */
+static uint8_t ProcessBase64Remainder(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state, int force)
+{
+ uint32_t ret;
+ uint8_t remainder = 0, remdec = 0;
+
+ SCLogDebug("Base64 line remainder found: %u", state->bvr_len);
+
+ /* Fill in block with first few bytes of current line */
+ remainder = B64_BLOCK - state->bvr_len;
+ remainder = remainder < len ? remainder : len;
+ memcpy(state->bvremain + state->bvr_len, buf, remainder);
+ state->bvr_len += remainder;
+
+ /* If data chunk buffer will be full, then clear it now */
+ if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
+
+ /* Invoke pre-processor and callback */
+ ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len,
+ state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() function failed");
+ }
+ }
+
+ /* Only decode if divisible by 4 */
+ if (state->bvr_len == B64_BLOCK || force) {
+ remdec = DecodeBase64(state->data_chunk + state->data_chunk_len,
+ state->bvremain, state->bvr_len);
+ if (remdec > 0) {
+
+ /* Track decoded length */
+ state->stack->top->data->decoded_body_len += remdec;
+
+ /* Update length */
+ state->data_chunk_len += remdec;
+
+ /* If data chunk buffer is now full, then clear */
+ if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
+
+ /* Invoke pre-processor and callback */
+ ret = ProcessDecodedDataChunk(state->data_chunk,
+ state->data_chunk_len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() function "
+ "failed");
+ }
+ }
+ } else {
+ /* Track failed base64 */
+ state->stack->top->data->anomaly_flags |= ANOM_INVALID_BASE64;
+ state->msg->anomaly_flags |= ANOM_INVALID_BASE64;
+ SCLogDebug("Error: DecodeBase64() function failed");
+ PrintChars(SC_LOG_DEBUG, "Base64 failed string", state->bvremain, state->bvr_len);
+ }
+
+ /* Reset remaining */
+ state->bvr_len = 0;
+ }
+
+ return remainder;
+}
+
+/**
+ * \brief Processes a body line by base64-decoding and passing to the data chunk
+ * processing callback function when the buffer is read
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessBase64BodyLine(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint8_t rem1 = 0, rem2 = 0;
+ uint32_t numDecoded, remaining, offset, avail, tobuf;
+
+ /* Track long line */
+ if (len > MAX_ENC_LINE_LEN) {
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_ENC_LINE;
+ state->msg->anomaly_flags |= ANOM_LONG_ENC_LINE;
+ SCLogDebug("Error: Max encoded input line length exceeded %u > %u",
+ len, MAX_ENC_LINE_LEN);
+ }
+
+ /* First process remaining from previous line */
+ if (state->bvr_len > 0) {
+
+ SCLogDebug("Base64 line remainder found: %u", state->bvr_len);
+
+ /* Process remainder and return number of bytes pulled from current buffer */
+ rem1 = ProcessBase64Remainder(buf, len, state, 0);
+ }
+
+ /* No error and at least some more data needs to be decoded */
+ if ((int) (len - rem1) > 0) {
+
+ /* Determine whether we need to save a remainder if not divisible by 4 */
+ rem2 = (len - rem1) % B64_BLOCK;
+ if (rem2 > 0) {
+
+ SCLogDebug("Base64 saving remainder: %u", rem2);
+
+ memcpy(state->bvremain, buf + (len - rem2), rem2);
+ state->bvr_len = rem2;
+ }
+
+ /* Process remaining in loop in case buffer fills up */
+ remaining = len - rem1 - rem2;
+ offset = rem1;
+ while (remaining > 0) {
+
+ /* Determine amount to add to buffer */
+ avail = (DATA_CHUNK_SIZE - state->data_chunk_len) * B64_BLOCK / ASCII_BLOCK;
+ tobuf = avail > remaining ? remaining : avail;
+ while (tobuf % 4 != 0) {
+ tobuf--;
+ }
+
+ if (tobuf < B64_BLOCK) {
+ SCLogDebug("Error: Invalid state for decoding base-64 block");
+ return MIME_DEC_ERR_PARSE;
+ }
+
+ SCLogDebug("Decoding: %u", len - rem1 - rem2);
+
+ numDecoded = DecodeBase64(state->data_chunk + state->data_chunk_len,
+ buf + offset, tobuf);
+ if (numDecoded > 0) {
+
+ /* Track decoded length */
+ state->stack->top->data->decoded_body_len += numDecoded;
+
+ /* Update length */
+ state->data_chunk_len += numDecoded;
+
+ if ((int) (DATA_CHUNK_SIZE - state->data_chunk_len) < 0) {
+ SCLogDebug("Error: Invalid Chunk length: %u",
+ state->data_chunk_len);
+ ret = MIME_DEC_ERR_PARSE;
+ break;
+ }
+
+ /* If buffer full, then invoke callback */
+ if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
+
+ /* Invoke pre-processor and callback */
+ ret = ProcessDecodedDataChunk(state->data_chunk,
+ state->data_chunk_len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() "
+ "function failed");
+ }
+ }
+ } else {
+ /* Track failed base64 */
+ state->stack->top->data->anomaly_flags |= ANOM_INVALID_BASE64;
+ state->msg->anomaly_flags |= ANOM_INVALID_BASE64;
+ SCLogDebug("Error: DecodeBase64() function failed");
+ PrintChars(SC_LOG_DEBUG, "Base64 failed string", buf + offset, tobuf);
+ }
+
+ /* Update counts */
+ remaining -= tobuf;
+ offset += tobuf;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Decoded a hex character into its equivalent byte value for
+ * quoted-printable decoding
+ *
+ * \param h The hex char
+ *
+ * \return byte value on success, -1 if failed
+ **/
+static int16_t DecodeQPChar(char h)
+{
+ uint16_t res = 0;
+
+ /* 0-9 */
+ if (h >= 48 && h <= 57) {
+ res = h - 48;
+ } else if (h >= 65 && h <= 70) {
+ /* A-F */
+ res = h - 55;
+ } else {
+ /* Invalid */
+ res = -1;
+ }
+
+ return res;
+
+}
+
+/**
+ * \brief Processes a quoted-printable encoded body line by decoding and passing
+ * to the data chunk processing callback function when the buffer is read
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessQuotedPrintableBodyLine(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint32_t remaining, offset;
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+ uint8_t c, h1, h2, val;
+ int16_t res;
+
+ /* Track long line */
+ if (len > MAX_ENC_LINE_LEN) {
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_ENC_LINE;
+ state->msg->anomaly_flags |= ANOM_LONG_ENC_LINE;
+ SCLogDebug("Error: Max encoded input line length exceeded %u > %u",
+ len, MAX_ENC_LINE_LEN);
+ }
+
+ remaining = len;
+ offset = 0;
+ while (remaining > 0) {
+
+ c = *(buf + offset);
+
+ /* Copy over normal character */
+ if (c != '=') {
+ state->data_chunk[state->data_chunk_len] = c;
+ state->data_chunk_len++;
+ entity->decoded_body_len += 1;
+
+ /* Add CRLF sequence if end of line */
+ if (remaining == 1) {
+ memcpy(state->data_chunk + state->data_chunk_len, CRLF, EOL_LEN);
+ state->data_chunk_len += EOL_LEN;
+ entity->decoded_body_len += EOL_LEN;
+ }
+ } else if (remaining > 1) {
+ /* If last character handle as soft line break by ignoring,
+ otherwise process as escaped '=' character */
+
+ /* Not enough characters */
+ if (remaining < 3) {
+ entity->anomaly_flags |= ANOM_INVALID_QP;
+ state->msg->anomaly_flags |= ANOM_INVALID_QP;
+ SCLogDebug("Error: Quoted-printable decoding failed");
+ } else {
+ h1 = *(buf + offset + 1);
+ res = DecodeQPChar(h1);
+ if (res < 0) {
+ entity->anomaly_flags |= ANOM_INVALID_QP;
+ state->msg->anomaly_flags |= ANOM_INVALID_QP;
+ SCLogDebug("Error: Quoted-printable decoding failed");
+ } else {
+ val = (res << 4); /* Shift result left */
+ h2 = *(buf + offset + 2);
+ res = DecodeQPChar(h2);
+ if (res < 0) {
+ entity->anomaly_flags |= ANOM_INVALID_QP;
+ state->msg->anomaly_flags |= ANOM_INVALID_QP;
+ SCLogDebug("Error: Quoted-printable decoding failed");
+ } else {
+ /* Decoding sequence succeeded */
+ val += res;
+
+ state->data_chunk[state->data_chunk_len] = val;
+ state->data_chunk_len++;
+ entity->decoded_body_len++;
+
+ /* Add CRLF sequence if end of line */
+ if (remaining == 3) {
+ memcpy(state->data_chunk + state->data_chunk_len,
+ CRLF, EOL_LEN);
+ state->data_chunk_len += EOL_LEN;
+ entity->decoded_body_len += EOL_LEN;
+ }
+
+ /* Account for extra 2 characters in 3-characted QP
+ * sequence */
+ remaining -= 2;
+ offset += 2;
+ }
+ }
+ }
+ }
+
+ /* Change by 1 */
+ remaining--;
+ offset++;
+
+ /* If buffer full, then invoke callback */
+ if (DATA_CHUNK_SIZE - state->data_chunk_len < EOL_LEN + 1) {
+
+ /* Invoke pre-processor and callback */
+ ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len,
+ state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() function "
+ "failed");
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Processes a body line by base64-decoding (if applicable) and passing to
+ * the data chunk processing callback function
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessBodyLine(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint32_t remaining, offset, avail, tobuf;
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+
+ SCLogDebug("Processing body line");
+
+ /* Track length */
+ entity->body_len += len + 2; /* With CRLF */
+
+ /* Process base-64 content if enabled */
+ MimeDecConfig *mdcfg = MimeDecGetConfig();
+ if (mdcfg != NULL && mdcfg->decode_base64 &&
+ (entity->ctnt_flags & CTNT_IS_BASE64)) {
+
+ ret = ProcessBase64BodyLine(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBase64BodyLine() function failed");
+ }
+ } else if (mdcfg != NULL && mdcfg->decode_quoted_printable &&
+ (entity->ctnt_flags & CTNT_IS_QP)) {
+ /* Process quoted-printable content if enabled */
+ ret = ProcessQuotedPrintableBodyLine(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessQuotedPrintableBodyLine() function "
+ "failed");
+ }
+ } else {
+ /* Process non-decoded content */
+ remaining = len;
+ offset = 0;
+ while (remaining > 0) {
+
+ /* Plan to add CRLF to the end of each line */
+ avail = DATA_CHUNK_SIZE - state->data_chunk_len;
+ tobuf = avail > remaining + EOL_LEN ? remaining : avail - EOL_LEN;
+
+ /* Copy over to buffer */
+ memcpy(state->data_chunk + state->data_chunk_len, buf + offset, tobuf);
+ state->data_chunk_len += tobuf;
+
+ /* Now always add a CRLF to the end */
+ if (tobuf == remaining) {
+ memcpy(state->data_chunk + state->data_chunk_len, CRLF, EOL_LEN);
+ state->data_chunk_len += EOL_LEN;
+ }
+
+ if ((int) (DATA_CHUNK_SIZE - state->data_chunk_len) < 0) {
+ SCLogDebug("Error: Invalid Chunk length: %u",
+ state->data_chunk_len);
+ ret = MIME_DEC_ERR_PARSE;
+ break;
+ }
+
+ /* If buffer full, then invoke callback */
+ if (DATA_CHUNK_SIZE - state->data_chunk_len < EOL_LEN + 1) {
+
+ /* Invoke pre-processor and callback */
+ ret = ProcessDecodedDataChunk(state->data_chunk,
+ state->data_chunk_len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() function "
+ "failed");
+ }
+ }
+
+ remaining -= tobuf;
+ offset += tobuf;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Find the start of a header name on the current line
+ *
+ * \param buf The input line (not null-terminated)
+ * \param blen The length of the input line
+ * \param glen The output length of the header name
+ *
+ * \return Pointer to header name, or NULL if not found
+ */
+static uint8_t * FindMimeHeaderStart(const uint8_t *buf, uint32_t blen, uint32_t *hlen)
+{
+ uint32_t i, valid = 0;
+ uint8_t *hname = NULL;
+
+ /* Init */
+ *hlen = 0;
+
+ /* Look for sequence of printable characters followed by ':', or
+ CRLF then printable characters followed by ':' */
+ for (i = 0; i < blen && buf[i] != 0; i++) {
+
+ /* If ready for printable characters and found one, then increment */
+ if (buf[i] != COLON && buf[i] >= PRINTABLE_START &&
+ buf[i] <= PRINTABLE_END) {
+ valid++;
+ } else if (valid > 0 && buf[i] == COLON) {
+ /* If ready for printable characters, found some, and found colon
+ * delimiter, then a match is found */
+ hname = (uint8_t *) buf + i - valid;
+ *hlen = valid;
+ break;
+ } else {
+ /* Otherwise reset and quit */
+ break;
+ }
+ }
+
+ return hname;
+}
+
+/**
+ * \brief Find full header name and value on the current line based on the
+ * current state
+ *
+ * \param buf The current line (no CRLF)
+ * \param blen The length of the current line
+ * \param state The current state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int FindMimeHeader(const uint8_t *buf, uint32_t blen,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint8_t *hname, *hval = NULL;
+ DataValue *dv;
+ uint32_t hlen, vlen;
+ int finish_header = 0, new_header = 0;
+ MimeDecConfig *mdcfg = MimeDecGetConfig();
+
+ /* Find first header */
+ hname = FindMimeHeaderStart(buf, blen, &hlen);
+ if (hname != NULL) {
+
+ /* Warn and track but don't do anything yet */
+ if (hlen > MAX_HEADER_NAME) {
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_NAME;
+ state->msg->anomaly_flags |= ANOM_LONG_HEADER_NAME;
+ SCLogDebug("Error: Header name exceeds limit (%u > %u)",
+ hlen, MAX_HEADER_NAME);
+ }
+
+ /* Value starts after 'header:' (normalize spaces) */
+ hval = hname + hlen + 1;
+ if (hval - buf >= (int)blen) {
+ SCLogDebug("No Header value found");
+ hval = NULL;
+ } else {
+ while (hval[0] == ' ') {
+
+ /* If last character before end of bounds, set to NULL */
+ if (hval - buf >= (int)blen - 1) {
+ SCLogDebug("No Header value found");
+ hval = NULL;
+ break;
+ }
+
+ hval++;
+ }
+ }
+
+ /* If new header found, then previous header is finished */
+ if (state->state_flag == HEADER_STARTED) {
+ finish_header = 1;
+ }
+
+ /* Now process new header */
+ new_header = 1;
+
+ /* Must wait for next line to determine if finished */
+ state->state_flag = HEADER_STARTED;
+ } else if (blen == 0) {
+ /* Found body */
+ /* No more headers */
+ state->state_flag = HEADER_DONE;
+
+ finish_header = 1;
+
+ SCLogDebug("All Header processing finished");
+ } else if (state->state_flag == HEADER_STARTED) {
+ /* Found multi-line value (ie. Received header) */
+ /* If max header value exceeded, flag it */
+ vlen = blen;
+ if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
+ SCLogDebug("Error: Header value of length (%u) is too long",
+ state->hvlen + vlen);
+ vlen = mdcfg->header_value_depth - state->hvlen;
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
+ state->msg->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
+ }
+ if (vlen > 0) {
+ dv = AddDataValue(state->hvalue);
+ if (dv == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "AddDataValue() function failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ if (state->hvalue == NULL) {
+ state->hvalue = dv;
+ }
+
+ dv->value = SCMalloc(vlen);
+ if (unlikely(dv->value == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(dv->value, buf, vlen);
+ dv->value_len = vlen;
+ state->hvlen += vlen;
+ }
+ } else {
+ /* Likely a body without headers */
+ SCLogDebug("No headers found");
+
+ state->state_flag = BODY_STARTED;
+
+ /* Flag beginning of body */
+ state->body_begin = 1;
+ state->body_end = 0;
+
+ ret = ProcessBodyLine(buf, blen, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBodyLine() function failed");
+ return ret;
+ }
+ }
+
+ /* If we need to finish a header, then do so below and then cleanup */
+ if (finish_header) {
+ /* Store the header value */
+ ret = StoreMimeHeader(state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: StoreMimeHeader() function failed");
+ return ret;
+ }
+ }
+
+ /* When next header is found, we always create a new one */
+ if (new_header) {
+ /* Copy name and value to state */
+ state->hname = SCMalloc(hlen);
+ if (unlikely(state->hname == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(state->hname, hname, hlen);
+ state->hlen = hlen;
+
+ if (state->hvalue != NULL) {
+ SCLogDebug("Error: Parser failed due to unexpected header "
+ "value");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ if (hval != NULL) {
+ /* If max header value exceeded, flag it */
+ vlen = blen - (hval - buf);
+ if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
+ SCLogDebug("Error: Header value of length (%u) is too long",
+ state->hvlen + vlen);
+ vlen = mdcfg->header_value_depth - state->hvlen;
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
+ state->msg->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
+ }
+
+ if (vlen > 0) {
+ state->hvalue = AddDataValue(NULL);
+ if (state->hvalue == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "AddDataValue() function failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ state->hvalue->value = SCMalloc(vlen);
+ if (unlikely(state->hvalue->value == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(state->hvalue->value, hval, vlen);
+ state->hvalue->value_len = vlen;
+ state->hvlen += vlen;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Finds a mime header token within the specified field
+ *
+ * \param field The current field
+ * \param search_start The start of the search (ie. boundary=\")
+ * \param search_end The end of the search (ie. \")
+ * \param tlen The output length of the token (if found)
+ *
+ * \return A pointer to the token if found, otherwise NULL if not found
+ */
+static uint8_t * FindMimeHeaderToken(MimeDecField *field, char *search_start,
+ char *search_end, uint32_t *tlen)
+{
+ uint8_t *fptr, *tptr = NULL, *tok = NULL;
+
+ SCLogDebug("Looking for token: %s", search_start);
+
+ /* Check for token definition */
+ fptr = FindBuffer(field->value, field->value_len, (const uint8_t *)search_start, strlen(search_start));
+ if (fptr != NULL) {
+ fptr += strlen(search_start); /* Start at end of start string */
+ tok = GetToken(fptr, field->value_len - (fptr - field->value), search_end,
+ &tptr, tlen);
+ if (tok != NULL) {
+ SCLogDebug("Found mime token");
+ }
+ }
+
+ return tok;
+}
+
+/**
+ * \brief Processes the current line for mime headers and also does post-processing
+ * when all headers found
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessMimeHeaders(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ MimeDecField *field;
+ uint8_t *bptr = NULL, *rptr = NULL;
+ uint32_t blen = 0;
+ MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
+
+ /* Look for mime header in current line */
+ ret = FindMimeHeader(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: FindMimeHeader() function failed: %d", ret);
+ return ret;
+ }
+
+ /* Post-processing after all headers done */
+ if (state->state_flag == HEADER_DONE) {
+ /* First determine encoding by looking at Content-Transfer-Encoding */
+ field = MimeDecFindField(entity, CTNT_TRAN_STR);
+ if (field != NULL) {
+ /* Look for base64 */
+ if (FindBuffer(field->value, field->value_len, (const uint8_t *)BASE64_STR, strlen(BASE64_STR))) {
+ SCLogDebug("Base64 encoding found");
+ entity->ctnt_flags |= CTNT_IS_BASE64;
+ } else if (FindBuffer(field->value, field->value_len, (const uint8_t *)QP_STR, strlen(QP_STR))) {
+ /* Look for quoted-printable */
+ SCLogDebug("quoted-printable encoding found");
+ entity->ctnt_flags |= CTNT_IS_QP;
+ }
+ }
+
+ /* Check for file attachment in content disposition */
+ field = MimeDecFindField(entity, CTNT_DISP_STR);
+ if (field != NULL) {
+ bptr = FindMimeHeaderToken(field, "filename=\"", TOK_END_STR, &blen);
+ if (bptr != NULL) {
+ SCLogDebug("File attachment found in disposition");
+ entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
+
+ /* Copy over using dynamic memory */
+ entity->filename = SCMalloc(blen);
+ if (unlikely(entity->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(entity->filename, bptr, blen);
+ entity->filename_len = blen;
+ }
+ }
+
+ /* Check for boundary, encapsulated message, and file name in Content-Type */
+ field = MimeDecFindField(entity, CTNT_TYPE_STR);
+ if (field != NULL) {
+ /* Check if child entity boundary definition found */
+ bptr = FindMimeHeaderToken(field, BND_START_STR, TOK_END_STR, &blen);
+ if (bptr != NULL) {
+ state->found_child = 1;
+ entity->ctnt_flags |= CTNT_IS_MULTIPART;
+
+ if (blen > (BOUNDARY_BUF - 2)) {
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_BOUNDARY;
+ return MIME_DEC_ERR_PARSE;
+ }
+
+ /* Store boundary in parent node */
+ state->stack->top->bdef = SCMalloc(blen);
+ if (unlikely(state->stack->top->bdef == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(state->stack->top->bdef, bptr, blen);
+ state->stack->top->bdef_len = blen;
+ }
+
+ /* Look for file name (if not already found) */
+ if (!(entity->ctnt_flags & CTNT_IS_ATTACHMENT)) {
+ bptr = FindMimeHeaderToken(field, "name=\"", TOK_END_STR, &blen);
+ if (bptr != NULL) {
+ SCLogDebug("File attachment found");
+ entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
+
+ /* Copy over using dynamic memory */
+ entity->filename = SCMalloc(blen);
+ if (unlikely(entity->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return MIME_DEC_ERR_MEM;
+ }
+ memcpy(entity->filename, bptr, blen);
+ entity->filename_len = blen;
+ }
+ }
+
+ /* Pull out short-hand content type */
+ entity->ctnt_type = GetToken(field->value, field->value_len, " \r\n;",
+ &rptr, &entity->ctnt_type_len);
+ if (entity->ctnt_type != NULL) {
+ /* Check for encapsulated message */
+ if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
+ (const uint8_t *)MSG_STR, strlen(MSG_STR)))
+ {
+ SCLogDebug("Found encapsulated message entity");
+
+ entity->ctnt_flags |= CTNT_IS_ENV;
+
+ /* Create and push child to stack */
+ MimeDecEntity *child = MimeDecAddEntity(entity);
+ if (child == NULL)
+ return MIME_DEC_ERR_MEM;
+ child->ctnt_flags |= (CTNT_IS_ENCAP | CTNT_IS_MSG);
+ PushStack(state->stack);
+ state->stack->top->data = child;
+
+ /* Mark as encapsulated child */
+ state->stack->top->is_encap = 1;
+
+ /* Ready to parse headers */
+ state->state_flag = HEADER_READY;
+ } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
+ (const uint8_t *)MULTIPART_STR, strlen(MULTIPART_STR)))
+ {
+ /* Check for multipart */
+ SCLogDebug("Found multipart entity");
+ entity->ctnt_flags |= CTNT_IS_MULTIPART;
+ } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
+ (const uint8_t *)TXT_STR, strlen(TXT_STR)))
+ {
+ /* Check for plain text */
+ SCLogDebug("Found plain text entity");
+ entity->ctnt_flags |= CTNT_IS_TEXT;
+ } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
+ (const uint8_t *)HTML_STR, strlen(HTML_STR)))
+ {
+ /* Check for html */
+ SCLogDebug("Found html entity");
+ entity->ctnt_flags |= CTNT_IS_HTML;
+ }
+ }
+ }
+
+ /* Store pointer to Message-ID */
+ field = MimeDecFindField(entity, MSG_ID_STR);
+ if (field != NULL) {
+ entity->msg_id = field->value;
+ entity->msg_id_len = field->value_len;
+ }
+
+ /* Flag beginning of body */
+ state->body_begin = 1;
+ state->body_end = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Indicates to the parser that the body of an entity has completed
+ * processing on the previous line
+ *
+ * \param state The current parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessBodyComplete(MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+
+ SCLogDebug("Process body complete called");
+
+ /* Mark the file as hitting the end */
+ state->body_end = 1;
+
+ if (state->bvr_len > 0) {
+ SCLogDebug("Found (%u) remaining base64 bytes not processed",
+ state->bvr_len);
+
+ /* Process the remainder */
+ ret = ProcessBase64Remainder(NULL, 0, state, 1);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBase64BodyLine() function failed");
+ }
+ }
+
+ /* Invoke pre-processor and callback with remaining data */
+ ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessDecodedDataChunk() function failed");
+ }
+
+ /* Now reset */
+ state->body_begin = 0;
+ state->body_end = 0;
+
+ return ret;
+}
+
+/**
+ * \brief When a mime boundary is found, look for end boundary and also do stack
+ * management
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ * \param bdef_len The length of the current boundary
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessMimeBoundary(const uint8_t *buf, uint32_t len, uint32_t bdef_len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint8_t *rptr;
+ MimeDecEntity *child;
+
+ SCLogDebug("PROCESSING BOUNDARY - START: %d",
+ state->state_flag);
+
+ /* If previous line was not an end boundary, then we process the body as
+ * completed */
+ if (state->state_flag != BODY_END_BOUND) {
+
+ /* First lets complete the body */
+ ret = ProcessBodyComplete(state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBodyComplete() function failed");
+ return ret;
+ }
+ } else {
+ /* If last line was an end boundary, then now we are ready to parse
+ * headers again */
+ state->state_flag = HEADER_READY;
+ }
+
+ /* Update remaining buffer */
+ rptr = (uint8_t *) buf + bdef_len + 2;
+
+ /* If entity is encapsulated and current and parent didn't define the boundary,
+ * then pop out */
+ if (state->stack->top->is_encap && state->stack->top->bdef_len == 0) {
+
+ if (state->stack->top->next == NULL) {
+ SCLogDebug("Error: Missing parent entity from stack");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ if (state->stack->top->next->bdef_len == 0) {
+
+ SCLogDebug("POPPED ENCAPSULATED CHILD FROM STACK: %p=%p",
+ state->stack->top, state->stack->top->data);
+
+ /* If end of boundary found, pop the child off the stack */
+ PopStack(state->stack);
+ if (state->stack->top == NULL) {
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+ }
+ }
+
+ /* Now check for end of nested boundary */
+ if (len - (rptr - buf) > 1 && rptr[0] == DASH && rptr[1] == DASH) {
+ SCLogDebug("FOUND END BOUNDARY, POPPING: %p=%p",
+ state->stack->top, state->stack->top->data);
+
+ /* If end of boundary found, pop the child off the stack */
+ PopStack(state->stack);
+ if (state->stack->top == NULL) {
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ /* If current is an encapsulated message with a boundary definition,
+ * then pop him as well */
+ if (state->stack->top->is_encap && state->stack->top->bdef_len != 0) {
+ SCLogDebug("FOUND END BOUNDARY AND ENCAP, POPPING: %p=%p",
+ state->stack->top, state->stack->top->data);
+
+ PopStack(state->stack);
+ if (state->stack->top == NULL) {
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+ }
+
+ state->state_flag = BODY_END_BOUND;
+ } else if (state->found_child) {
+ /* Otherwise process new child */
+ SCLogDebug("Child entity created");
+
+ /* Create and push child to stack */
+ child = MimeDecAddEntity(state->stack->top->data);
+ if (child == NULL)
+ return MIME_DEC_ERR_MEM;
+ child->ctnt_flags |= CTNT_IS_BODYPART;
+ PushStack(state->stack);
+ state->stack->top->data = child;
+
+ /* Reset flag */
+ state->found_child = 0;
+ } else {
+ /* Otherwise process sibling */
+ if (state->stack->top->next == NULL) {
+ SCLogDebug("Error: Missing parent entity from stack");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ SCLogDebug("SIBLING CREATED, POPPING PARENT: %p=%p",
+ state->stack->top, state->stack->top->data);
+
+ /* First pop current to get access to parent */
+ PopStack(state->stack);
+ if (state->stack->top == NULL) {
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ /* Create and push child to stack */
+ child = MimeDecAddEntity(state->stack->top->data);
+ if (child == NULL)
+ return MIME_DEC_ERR_MEM;
+ child->ctnt_flags |= CTNT_IS_BODYPART;
+ PushStack(state->stack);
+ state->stack->top->data = child;
+ }
+
+ /* After boundary look for headers */
+ if (state->state_flag != BODY_END_BOUND) {
+ state->state_flag = HEADER_READY;
+ }
+
+ SCLogDebug("PROCESSING BOUNDARY - END: %d", state->state_flag);
+ return ret;
+}
+
+/**
+ * \brief Processes the MIME Entity body based on the input line and current
+ * state of the parser
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessMimeBody(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+ uint8_t temp[BOUNDARY_BUF];
+ uint8_t *bstart;
+ int body_found = 0;
+ uint32_t tlen;
+
+ /* Ignore empty lines */
+ if (len == 0) {
+ return ret;
+ }
+
+ /* First look for boundary */
+ MimeDecStackNode *node = state->stack->top;
+ if (node == NULL) {
+ SCLogDebug("Error: Invalid stack state");
+ return MIME_DEC_ERR_PARSE;
+ }
+
+ /* Traverse through stack to find a boundary definition */
+ if (state->state_flag == BODY_END_BOUND || node->bdef == NULL) {
+
+ /* If not found, then use parent's boundary */
+ node = node->next;
+ while (node != NULL && node->bdef == NULL) {
+ SCLogDebug("Traversing through stack for node with boundary");
+ node = node->next;
+ }
+ }
+
+ /* This means no boundary / parent w/boundary was found so we are in the body */
+ if (node == NULL) {
+ body_found = 1;
+ } else {
+
+ /* Now look for start of boundary */
+ if (len > 1 && buf[0] == '-' && buf[1] == '-') {
+
+ tlen = node->bdef_len + 2;
+ if (tlen > BOUNDARY_BUF) {
+ if (state->stack->top->data)
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_BOUNDARY;
+ return MIME_DEC_ERR_PARSE;
+ }
+
+ memcpy(temp, "--", 2);
+ memcpy(temp + 2, node->bdef, node->bdef_len);
+
+ /* Find either next boundary or end boundary */
+ bstart = FindBuffer((const uint8_t *)buf, len, temp, tlen);
+ if (bstart != NULL) {
+ ret = ProcessMimeBoundary(buf, len, node->bdef_len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessMimeBoundary() function "
+ "failed");
+ return ret;
+ }
+ } else {
+ /* Otherwise add value to body */
+ body_found = 1;
+ }
+ } else {
+ /* Otherwise add value to body */
+ body_found = 1;
+ }
+ }
+
+ /* Process body line */
+ if (body_found) {
+ state->state_flag = BODY_STARTED;
+
+ ret = ProcessBodyLine(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBodyLine() function failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Processes the MIME Entity based on the input line and current state of
+ * the parser
+ *
+ * \param buf The current line
+ * \param len The length of the line
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+static int ProcessMimeEntity(const uint8_t *buf, uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+
+ SCLogDebug("START FLAG: %s", StateFlags[state->state_flag]);
+
+ /* Track long line */
+ if (len > MAX_LINE_LEN) {
+ state->stack->top->data->anomaly_flags |= ANOM_LONG_LINE;
+ state->msg->anomaly_flags |= ANOM_LONG_LINE;
+ SCLogDebug("Error: Max input line length exceeded %u > %u", len,
+ MAX_LINE_LEN);
+ }
+
+ /* Looking for headers */
+ if (state->state_flag == HEADER_READY ||
+ state->state_flag == HEADER_STARTED) {
+
+ SCLogDebug("Processing Headers");
+
+ /* Process message headers */
+ ret = ProcessMimeHeaders(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessMimeHeaders() function failed: %d",
+ ret);
+ return ret;
+ }
+ } else {
+ /* Processing body */
+ SCLogDebug("Processing Body of: %p", state->stack->top);
+
+ ret = ProcessMimeBody(buf, len, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessMimeBody() function failed: %d",
+ ret);
+ return ret;
+ }
+ }
+
+ SCLogDebug("END FLAG: %s", StateFlags[state->state_flag]);
+
+ return ret;
+}
+
+/**
+ * \brief Init the parser by allocating memory for the state and top-level entity
+ *
+ * \param data A caller-specified pointer to data for access within the data chunk
+ * processor callback function
+ * \param dcpfunc The data chunk processor callback function
+ *
+ * \return A pointer to the state object, or NULL if the operation fails
+ */
+MimeDecParseState * MimeDecInitParser(void *data,
+ int (*DataChunkProcessorFunc)(const uint8_t *chunk, uint32_t len,
+ MimeDecParseState *state))
+{
+ MimeDecParseState *state;
+ MimeDecEntity *mimeMsg;
+
+ state = SCMalloc(sizeof(MimeDecParseState));
+ if (unlikely(state == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ return NULL;
+ }
+ memset(state, 0x00, sizeof(MimeDecParseState));
+
+ state->stack = SCMalloc(sizeof(MimeDecStack));
+ if (unlikely(state->stack == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ SCFree(state);
+ return NULL;
+ }
+ memset(state->stack, 0x00, sizeof(MimeDecStack));
+
+ mimeMsg = SCMalloc(sizeof(MimeDecEntity));
+ if (unlikely(mimeMsg == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ SCFree(state->stack);
+ SCFree(state);
+ return NULL;
+ }
+ memset(mimeMsg, 0x00, sizeof(MimeDecEntity));
+ mimeMsg->ctnt_flags |= CTNT_IS_MSG;
+
+ /* Init state */
+ state->msg = mimeMsg;
+ PushStack(state->stack);
+ if (state->stack->top == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
+ SCFree(state->stack);
+ SCFree(state);
+ return NULL;
+ }
+ state->stack->top->data = mimeMsg;
+ state->state_flag = HEADER_READY;
+ state->data = data;
+ state->DataChunkProcessorFunc = DataChunkProcessorFunc;
+
+ return state;
+}
+
+/**
+ * \brief De-Init parser by freeing up any residual memory
+ *
+ * \param state The parser state
+ *
+ * \return none
+ */
+void MimeDecDeInitParser(MimeDecParseState *state)
+{
+ uint32_t cnt = 0;
+
+ while (state->stack->top != NULL) {
+ SCLogDebug("Remaining on stack: [%p]=>[%p]",
+ state->stack->top, state->stack->top->data);
+
+ PopStack(state->stack);
+ cnt++;
+ }
+
+ if (cnt > 1) {
+ state->msg->anomaly_flags |= ANOM_MALFORMED_MSG;
+ SCLogDebug("Warning: Stack is not empty upon completion of "
+ "processing (%u items remaining)", cnt);
+ }
+
+ SCFree(state->hname);
+ FreeDataValue(state->hvalue);
+ FreeMimeDecStack(state->stack);
+ SCFree(state);
+}
+
+/**
+ * \brief Called to indicate that the last message line has been processed and
+ * the parsing operation is complete
+ *
+ * This function should be called directly by the caller.
+ *
+ * \param state The parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+int MimeDecParseComplete(MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+
+ SCLogDebug("Parsing flagged as completed");
+
+ /* Store the header value */
+ ret = StoreMimeHeader(state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: StoreMimeHeader() function failed");
+ return ret;
+ }
+
+ /* Lets complete the body */
+ ret = ProcessBodyComplete(state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: ProcessBodyComplete() function failed");
+ return ret;
+ }
+
+ if (state->stack->top == NULL) {
+ state->msg->anomaly_flags |= ANOM_MALFORMED_MSG;
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+
+ /* If encapsulated, pop off the stack */
+ if (state->stack->top->is_encap) {
+ PopStack(state->stack);
+ if (state->stack->top == NULL) {
+ state->msg->anomaly_flags |= ANOM_MALFORMED_MSG;
+ SCLogDebug("Error: Message is malformed");
+ return MIME_DEC_ERR_DATA;
+ }
+ }
+
+ /* Look extra stack items remaining */
+ if (state->stack->top->next != NULL) {
+ state->msg->anomaly_flags |= ANOM_MALFORMED_MSG;
+ SCLogDebug("Warning: Message has unclosed message part boundary");
+ }
+
+ state->state_flag = PARSE_DONE;
+
+ return ret;
+}
+
+/**
+ * \brief Parse a line of a MIME message and update the parser state
+ *
+ * \param line A string representing the line (w/out CRLF)
+ * \param len The length of the line
+ * \param state The parser state
+ *
+ * \return MIME_DEC_OK on success, otherwise < 0 on failure
+ */
+int MimeDecParseLine(const uint8_t *line, const uint32_t len,
+ MimeDecParseState *state)
+{
+ int ret = MIME_DEC_OK;
+
+ /* For debugging purposes */
+ if (len > 0) {
+ PrintChars(SC_LOG_DEBUG, "SMTP LINE", line, len);
+ } else {
+ SCLogDebug("SMTP LINE - EMPTY");
+ }
+
+ /* Process the entity */
+ ret = ProcessMimeEntity(line, len, state);
+ if (ret != MIME_DEC_OK) {
+ state->state_flag = PARSE_ERROR;
+ SCLogDebug("Error: ProcessMimeEntity() function failed: %d", ret);
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Parses an entire message when available in its entirety (wraps the
+ * line-based parsing functions)
+ *
+ * \param buf Buffer pointing to the full message
+ * \param blen Length of the buffer
+ * \param data Caller data to be available in callback
+ * \param dcpfunc Callback for processing each decoded body data chunk
+ *
+ * \return A pointer to the decoded MIME message, or NULL if the operation fails
+ */
+MimeDecEntity * MimeDecParseFullMsg(const uint8_t *buf, uint32_t blen, void *data,
+ int (*dcpfunc)(const uint8_t *chunk, uint32_t len,
+ MimeDecParseState *state))
+{
+ int ret = MIME_DEC_OK;
+ uint8_t *remainPtr, *tok;
+ uint32_t tokLen;
+
+ MimeDecParseState *state = MimeDecInitParser(data, dcpfunc);
+ if (state == NULL) {
+ SCLogDebug("Error: MimeDecInitParser() function failed to create "
+ "state");
+ return NULL;
+ }
+
+ MimeDecEntity *msg = state->msg;
+
+ /* Parse each line one by one */
+ remainPtr = (uint8_t *) buf;
+ uint8_t *line = NULL;
+ do {
+ tok = GetLine(remainPtr, blen - (remainPtr - buf), &remainPtr, &tokLen);
+ if (tok != remainPtr) {
+
+ line = tok;
+
+ /* Parse the line */
+ ret = MimeDecParseLine(line, tokLen, state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: MimeDecParseLine() function failed: %d",
+ ret);
+ break;
+ }
+ }
+
+ } while (tok != remainPtr && remainPtr - buf < (int)blen);
+
+ if (ret == MIME_DEC_OK) {
+ SCLogDebug("Message parser was successful");
+
+ /* Now complete message */
+ ret = MimeDecParseComplete(state);
+ if (ret != MIME_DEC_OK) {
+ SCLogDebug("Error: MimeDecParseComplete() function failed");
+ }
+ }
+
+ /* De-allocate memory for parser */
+ MimeDecDeInitParser(state);
+
+ if (ret != MIME_DEC_OK) {
+ MimeDecFreeEntity(msg);
+ msg = NULL;
+ }
+
+ return msg;
+}
+
+#ifdef UNITTESTS
+
+/* Helper body chunk callback function */
+static int TestDataChunkCallback(const uint8_t *chunk, uint32_t len,
+ MimeDecParseState *state)
+{
+ uint32_t *line_count = (uint32_t *) state->data;
+
+ if (state->body_begin) {
+ SCLogDebug("Body begin (len=%u)", len);
+ }
+
+ /* Add up the line counts */
+ if (len > 0) {
+
+ uint8_t *remainPtr;
+ uint8_t *tok;
+ uint32_t tokLen;
+
+ PrintChars(SC_LOG_DEBUG, "CHUNK", chunk, len);
+
+ /* Parse each line one by one */
+ remainPtr = (uint8_t *) chunk;
+ do {
+ tok = GetLine(remainPtr, len - (remainPtr - (uint8_t *) chunk),
+ &remainPtr, &tokLen);
+ if (tok != NULL && tok != remainPtr) {
+ (*line_count)++;
+ }
+
+ } while (tok != NULL && tok != remainPtr &&
+ (uint32_t)(remainPtr - (uint8_t *) chunk) < len);
+
+ SCLogDebug("line count (len=%u): %u", len, *line_count);
+ }
+
+ if (state->body_end) {
+ SCLogDebug("Body end (len=%u)", len);
+ }
+
+ return MIME_DEC_OK;
+}
+
+/* Test simple case of line counts */
+static int MimeDecParseLineTest01(void)
+{
+ int ret = MIME_DEC_OK;
+
+ uint32_t expected_count = 3;
+ uint32_t line_count = 0;
+
+ /* Init parser */
+ MimeDecParseState *state = MimeDecInitParser(&line_count,
+ TestDataChunkCallback);
+
+ char *str = "From: Sender1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "To: Recipient1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "Content-Type: text/plain";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "A simple message line 1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "A simple message line 2";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "A simple message line 3";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ if (ret != MIME_DEC_OK) {
+ return ret;
+ }
+ /* Completed */
+ ret = MimeDecParseComplete(state);
+ if (ret != MIME_DEC_OK) {
+ return ret;
+ }
+
+ MimeDecEntity *msg = state->msg;
+ if (msg->next != NULL || msg->child != NULL) {
+ SCLogInfo("Error: Invalid sibling or child message");
+ return -1;
+ }
+
+ MimeDecFreeEntity(msg);
+
+ /* De Init parser */
+ MimeDecDeInitParser(state);
+
+ SCLogInfo("LINE COUNT FINISHED: %d", line_count);
+
+ if (expected_count != line_count) {
+ SCLogInfo("Error: Line count is invalid: expected - %d actual - %d",
+ expected_count, line_count);
+ return -1;
+ }
+
+ return ret;
+}
+
+/* Test simple case of EXE URL extraction */
+static int MimeDecParseLineTest02(void)
+{
+ int ret = MIME_DEC_OK;
+
+ uint32_t expected_count = 2;
+ uint32_t line_count = 0;
+
+ MimeDecGetConfig()->decode_base64 = 1;
+ MimeDecGetConfig()->decode_quoted_printable = 1;
+ MimeDecGetConfig()->extract_urls = 1;
+
+ /* Init parser */
+ MimeDecParseState *state = MimeDecInitParser(&line_count,
+ TestDataChunkCallback);
+
+ char *str = "From: Sender1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "To: Recipient1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "Content-Type: text/plain";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "A simple message line 1";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ str = "A simple message line 2 click on http://www.test.com/malware.exe?"
+ "hahah hopefully you click this link";
+ ret |= MimeDecParseLine((uint8_t *)str, strlen(str), state);
+
+ if (ret != MIME_DEC_OK) {
+ return ret;
+ }
+ /* Completed */
+ ret = MimeDecParseComplete(state);
+ if (ret != MIME_DEC_OK) {
+ return ret;
+ }
+
+ MimeDecEntity *msg = state->msg;
+ if (msg->url_list == NULL || (msg->url_list != NULL &&
+ !(msg->url_list->url_flags & URL_IS_EXE))) {
+ SCLogInfo("Warning: Expected EXE URL not found");
+ return -1;
+ }
+
+ MimeDecFreeEntity(msg);
+
+ /* De Init parser */
+ MimeDecDeInitParser(state);
+
+ SCLogInfo("LINE COUNT FINISHED: %d", line_count);
+
+ if (expected_count != line_count) {
+ SCLogInfo("Warning: Line count is invalid: expected - %d actual - %d",
+ expected_count, line_count);
+ return -1;
+ }
+
+ return ret;
+}
+
+/* Test full message with linebreaks */
+static int MimeDecParseFullMsgTest01(void)
+{
+ int ret = MIME_DEC_OK;
+
+ uint32_t expected_count = 3;
+ uint32_t line_count = 0;
+
+ char msg[] = "From: Sender1\r\n"
+ "To: Recipient1\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "Line 1\r\n"
+ "Line 2\r\n"
+ "Line 3\r\n";
+
+ MimeDecEntity *entity = MimeDecParseFullMsg((uint8_t *)msg, strlen(msg), &line_count,
+ TestDataChunkCallback);
+ if (entity == NULL) {
+ SCLogInfo("Warning: Message failed to parse");
+ return -1;
+ }
+
+ MimeDecFreeEntity(entity);
+
+ if (expected_count != line_count) {
+ SCLogInfo("Warning: Line count is invalid: expected - %d actual - %d",
+ expected_count, line_count);
+ return -1;
+ }
+
+ return ret;
+}
+
+static int MimeBase64DecodeTest01(void)
+{
+ int ret = -1;
+
+ char *msg = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890@"
+ "#$%^&*()-=_+,./;'[]<>?:";
+ char *base64msg = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QU"
+ "VJTVFVWV1hZWjEyMzQ1Njc4OTBAIyQlXiYqKCktPV8rLC4vOydbXTw+Pzo=";
+
+ uint8_t *dst = SCMalloc(strlen(msg)-1);
+ if (dst == NULL)
+ return 0;
+
+ ret = DecodeBase64(dst, (const uint8_t *)base64msg, strlen(base64msg));
+
+ if (memcmp(dst, msg, strlen(msg)) == 0) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int MimeIsExeURLTest01(void)
+{
+ int ret = -1;
+ char *url1 = "http://www.google.com/";
+ char *url2 = "http://www.google.com/test.exe";
+
+ if(IsExeUrl((const uint8_t *)url1, strlen(url1)) != 0){
+ SCLogDebug("Debug: URL1 error");
+ goto end;
+ }
+ if(IsExeUrl((const uint8_t *)url2, strlen(url2)) != 1){
+ SCLogDebug("Debug: URL2 error");
+ goto end;
+ }
+ ret = 0;
+
+ end:
+
+ return ret;
+}
+
+static int MimeIsIpv4HostTest01(void)
+{
+ if(IsIpv4Host((const uint8_t *)"192.168.1.1", 11) != 1) {
+ return 1;
+ }
+
+ if(IsIpv4Host((const uint8_t *)"999.oogle.com", 14) != 0) {
+ return 1;
+ }
+
+ if(IsIpv4Host((const uint8_t *)"0:0:0:0:0:0:0:0", 15) != 0) {
+ return 1;
+ }
+
+ if (IsIpv4Host((const uint8_t *)"192.168.255.255", 15) != 1) {
+ return 1;
+ }
+
+ if (IsIpv4Host((const uint8_t *)"192.168.255.255/testurl.html", 28) != 1) {
+ return 1;
+ }
+
+ if (IsIpv4Host((const uint8_t *)"www.google.com", 14) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int MimeIsIpv6HostTest01(void)
+{
+ if(IsIpv6Host((const uint8_t *)"0:0:0:0:0:0:0:0", 19) != 1) {
+ return 1;
+ }
+
+ if(IsIpv6Host((const uint8_t *)"0000:0000:0000:0000:0000:0000:0000:0000", 39) != 1) {
+ return 1;
+ }
+
+ if(IsIpv6Host((const uint8_t *)"0:0:0:0:0:0:0:0", 19) != 1) {
+ return 1;
+ }
+
+ if(IsIpv6Host((const uint8_t *)"192:168:1:1:0:0:0:0", 19) != 1) {
+ return 1;
+ }
+
+ if(IsIpv6Host((const uint8_t *)"999.oogle.com", 14) != 0) {
+ return 1;
+ }
+
+ if (IsIpv6Host((const uint8_t *)"192.168.255.255", 15) != 0) {
+ return 1;
+ }
+
+ if (IsIpv6Host((const uint8_t *)"192.168.255.255/testurl.html", 28) != 0) {
+ return 1;
+ }
+
+ if (IsIpv6Host((const uint8_t *)"www.google.com", 14) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+void MimeDecRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("MimeDecParseLineTest01", MimeDecParseLineTest01, 0);
+ UtRegisterTest("MimeDecParseLineTest02", MimeDecParseLineTest02, 0);
+ UtRegisterTest("MimeDecParseFullMsgTest01", MimeDecParseFullMsgTest01, 0);
+ UtRegisterTest("MimeBase64DecodeTest01", MimeBase64DecodeTest01, 0);
+ UtRegisterTest("MimeIsExeURLTest01", MimeIsExeURLTest01, 0);
+ UtRegisterTest("MimeIsIpv4HostTest01", MimeIsIpv4HostTest01, 0);
+ UtRegisterTest("MimeIsIpv6HostTest01", MimeIsIpv6HostTest01, 0);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/util-decode-mime.h b/framework/src/suricata/src/util-decode-mime.h
new file mode 100644
index 00000000..792fbcc1
--- /dev/null
+++ b/framework/src/suricata/src/util-decode-mime.h
@@ -0,0 +1,240 @@
+/* Copyright (C) 2012 BAE Systems
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author David Abarbanel <david.abarbanel@baesystems.com>
+ *
+ */
+
+#ifndef MIME_DECODE_H_
+#define MIME_DECODE_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "suricata.h"
+#include "util-base64.h"
+#include "util-debug.h"
+
+/* Header Flags */
+#define HDR_IS_LOGGED 1
+
+/* Content Flags */
+#define CTNT_IS_MSG 1
+#define CTNT_IS_ENV 2
+#define CTNT_IS_ENCAP 4
+#define CTNT_IS_BODYPART 8
+#define CTNT_IS_MULTIPART 16
+#define CTNT_IS_ATTACHMENT 32
+#define CTNT_IS_BASE64 64
+#define CTNT_IS_QP 128
+#define CTNT_IS_TEXT 256
+#define CTNT_IS_HTML 512
+
+/* URL Flags */
+#define URL_IS_IP4 1
+#define URL_IS_IP6 2
+#define URL_IS_EXE 4
+
+/* Anomaly Flags */
+#define ANOM_INVALID_BASE64 1 /* invalid base64 chars */
+#define ANOM_INVALID_QP 2 /* invalid qouted-printable chars */
+#define ANOM_LONG_HEADER_NAME 4 /* header is abnormally long */
+#define ANOM_LONG_HEADER_VALUE 8 /* header value is abnormally long
+ * (includes multi-line) */
+#define ANOM_LONG_LINE 16 /* Lines that exceed 998 octets */
+#define ANOM_LONG_ENC_LINE 32 /* Lines that exceed 76 octets */
+#define ANOM_MALFORMED_MSG 64 /* Misc msg format errors found */
+#define ANOM_LONG_BOUNDARY 128 /* Boundary too long */
+
+/* Pubicly exposed size constants */
+#define DATA_CHUNK_SIZE 3072 /* Should be divisible by 3 */
+#define LINEREM_SIZE 256
+
+/* Mime Parser Constants */
+#define HEADER_READY 0x01
+#define HEADER_STARTED 0x02
+#define HEADER_DONE 0x03
+#define BODY_STARTED 0x04
+#define BODY_DONE 0x05
+#define BODY_END_BOUND 0x06
+#define PARSE_DONE 0x07
+#define PARSE_ERROR 0x08
+
+/**
+ * \brief Mime Decoder Error Codes
+ */
+typedef enum MimeDecRetCode {
+ MIME_DEC_OK = 0,
+ MIME_DEC_MORE = 1,
+ MIME_DEC_ERR_DATA = -1,
+ MIME_DEC_ERR_MEM = -2,
+ MIME_DEC_ERR_PARSE = -3
+} MimeDecRetCode;
+
+/**
+ * \brief Structure for containing configuration options
+ *
+ */
+typedef struct MimeDecConfig {
+ int decode_base64; /**< Decode base64 bodies */
+ int decode_quoted_printable; /**< Decode quoted-printable bodies */
+ int extract_urls; /**< Extract and store URLs in data structure */
+ uint32_t header_value_depth; /**< Depth of which to store header values
+ (Default is 2000) */
+} MimeDecConfig;
+
+/**
+ * \brief This represents a header field name and associated value
+ */
+typedef struct MimeDecField {
+ uint8_t *name; /**< Name of the header field */
+ uint32_t name_len; /**< Length of the name */
+ uint32_t value_len; /**< Length of the value */
+ uint8_t *value; /**< Value of the header field */
+ struct MimeDecField *next; /**< Pointer to next field */
+} MimeDecField;
+
+/**
+ * \brief This represents a URL value node in a linked list
+ *
+ * Since HTML can sometimes contain a high number of URLs, this
+ * structure only features the URL host name/IP or those that are
+ * pointing to an executable file (see url_flags to determine which).
+ */
+typedef struct MimeDecUrl {
+ uint8_t *url; /**< String representation of full or partial URL (lowercase) */
+ uint32_t url_len; /**< Length of the URL string */
+ uint32_t url_flags; /**< Flags indicating type of URL */
+ struct MimeDecUrl *next; /**< Pointer to next URL */
+} MimeDecUrl;
+
+/**
+ * \brief This represents the MIME Entity (or also top level message) in a
+ * child-sibling tree
+ */
+typedef struct MimeDecEntity {
+ MimeDecField *field_list; /**< Pointer to list of header fields */
+ MimeDecUrl *url_list; /**< Pointer to list of URLs */
+ uint32_t body_len; /**< Length of body (prior to any decoding) */
+ uint32_t decoded_body_len; /**< Length of body after decoding */
+ uint32_t header_flags; /**< Flags indicating header characteristics */
+ uint32_t ctnt_flags; /**< Flags indicating type of content */
+ uint32_t anomaly_flags; /**< Flags indicating an anomaly in the message */
+ uint32_t filename_len; /**< Length of file attachment name */
+ uint8_t *filename; /**< Name of file attachment */
+ uint8_t *ctnt_type; /**< Quick access pointer to short-hand content type field */
+ uint32_t ctnt_type_len; /**< Length of content type field value */
+ uint32_t msg_id_len; /**< Quick access pointer to message Id */
+ uint8_t *msg_id; /**< Quick access pointer to message Id */
+ struct MimeDecEntity *next; /**< Pointer to list of sibling entities */
+ struct MimeDecEntity *child; /**< Pointer to list of child entities */
+} MimeDecEntity;
+
+/**
+ * \brief Structure contains boundary and entity for the current node (entity)
+ * in the stack
+ *
+ */
+typedef struct MimeDecStackNode {
+ MimeDecEntity *data; /**< Pointer to the entity data structure */
+ uint8_t *bdef; /**< Copy of boundary definition for child entity */
+ uint32_t bdef_len; /**< Boundary length for child entity */
+ int is_encap; /**< Flag indicating entity is encapsulated in message */
+ struct MimeDecStackNode *next; /**< Pointer to next item on the stack */
+} MimeDecStackNode;
+
+/**
+ * \brief Structure holds the top of the stack along with some free reusable nodes
+ *
+ */
+typedef struct MimeDecStack {
+ MimeDecStackNode *top; /**< Pointer to the top of the stack */
+ MimeDecStackNode *free_nodes; /**< Pointer to the list of free nodes */
+ uint32_t free_nodes_cnt; /**< Count of free nodes in the list */
+} MimeDecStack;
+
+/**
+ * \brief Structure contains a list of value and lengths for robust data processing
+ *
+ */
+typedef struct DataValue {
+ uint8_t *value; /**< Copy of data value */
+ uint32_t value_len; /**< Length of data value */
+ struct DataValue *next; /**< Pointer to next value in the list */
+} DataValue;
+
+/**
+ * \brief Structure contains the current state of the MIME parser
+ *
+ */
+typedef struct MimeDecParseState {
+ MimeDecEntity *msg; /**< Pointer to the top-level message entity */
+ MimeDecStack *stack; /**< Pointer to the top of the entity stack */
+ uint8_t *hname; /**< Copy of the last known header name */
+ uint32_t hlen; /**< Length of the last known header name */
+ uint32_t hvlen; /**< Total length of value list */
+ DataValue *hvalue; /**< Pointer to the incomplete header value list */
+ uint8_t linerem[LINEREM_SIZE]; /**< Remainder from previous line (for URL extraction) */
+ uint16_t linerem_len; /**< Length of remainder from previous line */
+ uint8_t bvremain[B64_BLOCK]; /**< Remainder from base64-decoded line */
+ uint8_t bvr_len; /**< Length of remainder from base64-decoded line */
+ uint8_t data_chunk[DATA_CHUNK_SIZE]; /**< Buffer holding data chunk */
+ uint8_t state_flag; /**< Flag representing current state of parser */
+ uint32_t data_chunk_len; /**< Length of data chunk */
+ int found_child; /**< Flag indicating a child entity was found */
+ int body_begin; /**< Currently at beginning of body */
+ int body_end; /**< Currently at end of body */
+ void *data; /**< Pointer to data specific to the caller */
+ int (*DataChunkProcessorFunc) (const uint8_t *chunk, uint32_t len,
+ struct MimeDecParseState *state); /**< Data chunk processing function callback */
+} MimeDecParseState;
+
+/* Config functions */
+void MimeDecSetConfig(MimeDecConfig *config);
+MimeDecConfig * MimeDecGetConfig(void);
+
+/* Memory functions */
+void MimeDecFreeEntity(MimeDecEntity *entity);
+void MimeDecFreeField(MimeDecField *field);
+void MimeDecFreeUrl(MimeDecUrl *url);
+
+/* List functions */
+MimeDecField * MimeDecAddField(MimeDecEntity *entity);
+MimeDecField * MimeDecFindField(const MimeDecEntity *entity, const char *name);
+MimeDecEntity * MimeDecAddEntity(MimeDecEntity *parent);
+
+/* Helper functions */
+//MimeDecField * MimeDecFillField(MimeDecEntity *entity, const char *name,
+// uint32_t nlen, const char *value, uint32_t vlen, int copy_name_value);
+
+/* Parser functions */
+MimeDecParseState * MimeDecInitParser(void *data, int (*dcpfunc)(const uint8_t *chunk,
+ uint32_t len, MimeDecParseState *state));
+void MimeDecDeInitParser(MimeDecParseState *state);
+int MimeDecParseComplete(MimeDecParseState *state);
+int MimeDecParseLine(const uint8_t *line, const uint32_t len, MimeDecParseState *state);
+MimeDecEntity * MimeDecParseFullMsg(const uint8_t *buf, uint32_t blen, void *data,
+ int (*DataChunkProcessorFunc)(const uint8_t *chunk, uint32_t len, MimeDecParseState *state));
+
+/* Test functions */
+void MimeDecRegisterTests(void);
+
+#endif
diff --git a/framework/src/suricata/src/util-device.c b/framework/src/suricata/src/util-device.c
new file mode 100644
index 00000000..c08e30f0
--- /dev/null
+++ b/framework/src/suricata/src/util-device.c
@@ -0,0 +1,273 @@
+/* Copyright (C) 2011-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "util-device.h"
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * \brief Utility functions to handle device list
+ */
+
+/** private device list */
+static TAILQ_HEAD(, LiveDevice_) live_devices =
+ TAILQ_HEAD_INITIALIZER(live_devices);
+
+/** if set to 0 when we don't have real devices */
+static int live_devices_stats = 1;
+
+/**
+ * \brief Add a pcap device for monitoring
+ *
+ * \param dev string with the device name
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int LiveRegisterDevice(char *dev)
+{
+ LiveDevice *pd = SCMalloc(sizeof(LiveDevice));
+ if (unlikely(pd == NULL)) {
+ return -1;
+ }
+
+ pd->dev = SCStrdup(dev);
+ if (unlikely(pd->dev == NULL)) {
+ SCFree(pd);
+ return -1;
+ }
+ SC_ATOMIC_INIT(pd->pkts);
+ SC_ATOMIC_INIT(pd->drop);
+ SC_ATOMIC_INIT(pd->invalid_checksums);
+ pd->ignore_checksum = 0;
+ TAILQ_INSERT_TAIL(&live_devices, pd, next);
+
+ SCLogDebug("Device \"%s\" registered.", dev);
+ return 0;
+}
+
+/**
+ * \brief Get the number of registered devices
+ *
+ * \retval cnt the number of registered devices
+ */
+int LiveGetDeviceCount(void)
+{
+ int i = 0;
+ LiveDevice *pd;
+
+ TAILQ_FOREACH(pd, &live_devices, next) {
+ i++;
+ }
+
+ return i;
+}
+
+/**
+ * \brief Get a pointer to the device name at idx
+ *
+ * \param number idx of the device in our list
+ *
+ * \retval ptr pointer to the string containing the device
+ * \retval NULL on error
+ */
+char *LiveGetDeviceName(int number)
+{
+ int i = 0;
+ LiveDevice *pd;
+
+ TAILQ_FOREACH(pd, &live_devices, next) {
+ if (i == number) {
+ return pd->dev;
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Get a pointer to the device at idx
+ *
+ * \param number idx of the device in our list
+ *
+ * \retval ptr pointer to the string containing the device
+ * \retval NULL on error
+ */
+LiveDevice *LiveGetDevice(char *name)
+{
+ int i = 0;
+ LiveDevice *pd;
+
+ if (name == NULL) {
+ SCLogWarning(SC_ERR_INVALID_VALUE, "Name of device should not be null");
+ return NULL;
+ }
+
+ TAILQ_FOREACH(pd, &live_devices, next) {
+ if (!strcmp(name, pd->dev)) {
+ return pd;
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+
+
+int LiveBuildDeviceList(char * runmode)
+{
+ return LiveBuildDeviceListCustom(runmode, "interface");
+}
+
+int LiveBuildDeviceListCustom(char * runmode, char * itemname)
+{
+ ConfNode *base = ConfGetNode(runmode);
+ ConfNode *child;
+ int i = 0;
+
+ if (base == NULL)
+ return 0;
+
+ TAILQ_FOREACH(child, &base->head, next) {
+ ConfNode *subchild;
+ TAILQ_FOREACH(subchild, &child->head, next) {
+ if ((!strcmp(subchild->name, itemname))) {
+ if (!strcmp(subchild->val, "default"))
+ break;
+ SCLogInfo("Adding %s %s from config file",
+ itemname, subchild->val);
+ LiveRegisterDevice(subchild->val);
+ i++;
+ }
+ }
+ }
+
+ return i;
+}
+
+/** Call this function to disable stat on live devices
+ *
+ * This can be useful in the case, this is not a real interface.
+ */
+void LiveDeviceHasNoStats()
+{
+ live_devices_stats = 0;
+}
+
+int LiveDeviceListClean()
+{
+ SCEnter();
+ LiveDevice *pd, *tpd;
+
+ TAILQ_FOREACH_SAFE(pd, &live_devices, next, tpd) {
+ if (live_devices_stats) {
+ SCLogNotice("Stats for '%s': pkts: %" PRIu64", drop: %" PRIu64 " (%.2f%%), invalid chksum: %" PRIu64,
+ pd->dev,
+ SC_ATOMIC_GET(pd->pkts),
+ SC_ATOMIC_GET(pd->drop),
+ 100 * (SC_ATOMIC_GET(pd->drop) * 1.0) / SC_ATOMIC_GET(pd->pkts),
+ SC_ATOMIC_GET(pd->invalid_checksums));
+ }
+ if (pd->dev)
+ SCFree(pd->dev);
+ SC_ATOMIC_DESTROY(pd->pkts);
+ SC_ATOMIC_DESTROY(pd->drop);
+ SC_ATOMIC_DESTROY(pd->invalid_checksums);
+ SCFree(pd);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+#ifdef BUILD_UNIX_SOCKET
+TmEcode LiveDeviceIfaceStat(json_t *cmd, json_t *answer, void *data)
+{
+ SCEnter();
+ LiveDevice *pd;
+ const char * name = NULL;
+ json_t *jarg = json_object_get(cmd, "iface");
+ if(!json_is_string(jarg)) {
+ json_object_set_new(answer, "message", json_string("Iface is not a string"));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ name = json_string_value(jarg);
+ if (name == NULL) {
+ json_object_set_new(answer, "message", json_string("Iface name is NULL"));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ TAILQ_FOREACH(pd, &live_devices, next) {
+ if (!strcmp(name, pd->dev)) {
+ json_t *jdata = json_object();
+ if (jdata == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ json_object_set_new(jdata, "pkts",
+ json_integer(SC_ATOMIC_GET(pd->pkts)));
+ json_object_set_new(jdata, "invalid-checksums",
+ json_integer(SC_ATOMIC_GET(pd->invalid_checksums)));
+ json_object_set_new(jdata, "drop",
+ json_integer(SC_ATOMIC_GET(pd->drop)));
+ json_object_set_new(answer, "message", jdata);
+ SCReturnInt(TM_ECODE_OK);
+ }
+ }
+ json_object_set_new(answer, "message", json_string("Iface does not exist"));
+ SCReturnInt(TM_ECODE_FAILED);
+}
+
+TmEcode LiveDeviceIfaceList(json_t *cmd, json_t *answer, void *data)
+{
+ SCEnter();
+ json_t *jdata;
+ json_t *jarray;
+ LiveDevice *pd;
+ int i = 0;
+
+ jdata = json_object();
+ if (jdata == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+ jarray = json_array();
+ if (jarray == NULL) {
+ json_object_set_new(answer, "message",
+ json_string("internal error at json object creation"));
+ return TM_ECODE_FAILED;
+ }
+ TAILQ_FOREACH(pd, &live_devices, next) {
+ json_array_append(jarray, json_string(pd->dev));
+ i++;
+ }
+
+ json_object_set_new(jdata, "count", json_integer(i));
+ json_object_set_new(jdata, "ifaces", jarray);
+ json_object_set_new(answer, "message", jdata);
+ SCReturnInt(TM_ECODE_OK);
+}
+#endif /* BUILD_UNIX_SOCKET */
diff --git a/framework/src/suricata/src/util-device.h b/framework/src/suricata/src/util-device.h
new file mode 100644
index 00000000..fd6a8213
--- /dev/null
+++ b/framework/src/suricata/src/util-device.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2011-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __UTIL_DEVICE_H__
+#define __UTIL_DEVICE_H__
+
+#include "queue.h"
+#include "unix-manager.h"
+
+/** storage for live device names */
+typedef struct LiveDevice_ {
+ char *dev; /**< the device (e.g. "eth0") */
+ int ignore_checksum;
+ SC_ATOMIC_DECLARE(uint64_t, pkts);
+ SC_ATOMIC_DECLARE(uint64_t, drop);
+ SC_ATOMIC_DECLARE(uint64_t, invalid_checksums);
+ TAILQ_ENTRY(LiveDevice_) next;
+} LiveDevice;
+
+
+int LiveRegisterDevice(char *dev);
+int LiveGetDeviceCount(void);
+char *LiveGetDeviceName(int number);
+LiveDevice *LiveGetDevice(char *dev);
+int LiveBuildDeviceList(char * base);
+void LiveDeviceHasNoStats(void);
+int LiveDeviceListClean(void);
+int LiveBuildDeviceListCustom(char * base, char * itemname);
+
+#ifdef BUILD_UNIX_SOCKET
+TmEcode LiveDeviceIfaceStat(json_t *cmd, json_t *server_msg, void *data);
+TmEcode LiveDeviceIfaceList(json_t *cmd, json_t *server_msg, void *data);
+#endif
+
+#endif /* __UTIL_DEVICE_H__ */
diff --git a/framework/src/suricata/src/util-enum.c b/framework/src/suricata/src/util-enum.c
new file mode 100644
index 00000000..97ee86f9
--- /dev/null
+++ b/framework/src/suricata/src/util-enum.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "util-enum.h"
+
+/**
+ * \brief Maps a string name to an enum value from the supplied table. Please
+ * specify the last element of any map table with a {NULL, -1}. If
+ * missing, you will be welcomed with a segfault :)
+ *
+ * \param enum_name Character string that has to be mapped to an enum value
+ * from the table
+ * \param table Enum-Char table, from which the mapping is retrieved
+ *
+ * \retval result The enum_value for the enum_name string or -1 on failure
+ */
+int SCMapEnumNameToValue(const char *enum_name, SCEnumCharMap *table)
+{
+ int result = -1;
+
+ if (enum_name == NULL || table == NULL) {
+ printf("Invalid argument(s) passed into SCMapEnumNameToValue\n");
+ return -1;
+ }
+
+ for (; table->enum_name != NULL; table++) {
+ if (strcasecmp(table->enum_name, enum_name) == 0) {
+ result = table->enum_value;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * \brief Maps an enum value to a string name, from the supplied table
+ *
+ * \param enum_value Enum_value that has to be mapped to a string_value
+ * from the table
+ * \param table Enum-Char table, from which the mapping is retrieved
+ *
+ * \retval result The enum_name for the enum_value supplied or NULL on failure
+ */
+const char * SCMapEnumValueToName(int enum_value, SCEnumCharMap *table)
+{
+ if (table == NULL) {
+ printf("Invalid argument(s) passed into SCMapEnumValueToName\n");
+ return NULL;
+ }
+
+ for (; table->enum_name != NULL; table++) {
+ if (table->enum_value == enum_value) {
+ return table->enum_name;
+ }
+ }
+
+ printf("A enum by the value %d doesn't exist in this table\n", enum_value);
+
+ return NULL;
+}
diff --git a/framework/src/suricata/src/util-enum.h b/framework/src/suricata/src/util-enum.h
new file mode 100644
index 00000000..ee555e99
--- /dev/null
+++ b/framework/src/suricata/src/util-enum.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_ENUM_H__
+#define __UTIL_ENUM_H__
+
+typedef struct SCEnumCharMap_ {
+ char *enum_name;
+ int enum_value;
+} SCEnumCharMap;
+
+int SCMapEnumNameToValue(const char *, SCEnumCharMap *);
+
+const char * SCMapEnumValueToName(int, SCEnumCharMap *);
+
+#endif /* __UTIL_ENUM_H__ */
diff --git a/framework/src/suricata/src/util-error.c b/framework/src/suricata/src/util-error.c
new file mode 100644
index 00000000..6e783f42
--- /dev/null
+++ b/framework/src/suricata/src/util-error.c
@@ -0,0 +1,315 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Error utility functions
+ *
+ * \todo Needs refining of the error codes. Renaming with a prefix of SC_ERR,
+ * removal of duplicates and entries have to be made in util-error.c
+ */
+
+#include "util-error.h"
+
+#define CASE_CODE(E) case E: return #E
+
+/**
+ * \brief Maps the error code, to its string equivalent
+ *
+ * \param The error code
+ *
+ * \retval The string equivalent for the error code
+ */
+const char * SCErrorToString(SCError err)
+{
+ switch (err) {
+ CASE_CODE (SC_OK);
+ CASE_CODE (SC_ERR_MEM_ALLOC);
+ CASE_CODE (SC_ERR_ACTION_ORDER);
+ CASE_CODE (SC_ERR_PCRE_MATCH);
+ CASE_CODE (SC_ERR_PCRE_GET_SUBSTRING);
+ CASE_CODE (SC_ERR_PCRE_COMPILE);
+ CASE_CODE (SC_ERR_PCRE_STUDY);
+ CASE_CODE (SC_ERR_PCRE_PARSE);
+ CASE_CODE (SC_ERR_LOG_MODULE_NOT_INIT);
+ CASE_CODE (SC_ERR_LOG_FG_FILTER_MATCH);
+ CASE_CODE (SC_ERR_PCAP_DISPATCH);
+ CASE_CODE (SC_ERR_PCAP_CREATE);
+ CASE_CODE (SC_ERR_PCAP_SET_SNAPLEN);
+ CASE_CODE (SC_ERR_PCAP_SET_PROMISC);
+ CASE_CODE (SC_ERR_PCAP_SET_TIMEOUT);
+ CASE_CODE (SC_ERR_PCAP_OPEN_LIVE);
+ CASE_CODE (SC_ERR_PCAP_OPEN_OFFLINE);
+ CASE_CODE (SC_ERR_PCAP_ACTIVATE_HANDLE);
+ CASE_CODE (SC_ERR_PCAP_SET_BUFF_SIZE);
+ CASE_CODE (SC_ERR_NO_PCAP_SET_BUFFER_SIZE);
+ CASE_CODE (SC_ERR_NO_PF_RING);
+ CASE_CODE (SC_ERR_PF_RING_RECV);
+ CASE_CODE (SC_ERR_PF_RING_GET_CLUSTERID_FAILED);
+ CASE_CODE (SC_ERR_PF_RING_GET_INTERFACE_FAILED);
+ CASE_CODE (SC_ERR_PF_RING_OPEN);
+ CASE_CODE (SC_ERR_GET_CLUSTER_TYPE_FAILED);
+ CASE_CODE (SC_ERR_INVALID_CLUSTER_TYPE);
+ CASE_CODE (SC_ERR_PF_RING_SET_CLUSTER_FAILED);
+ CASE_CODE (SC_ERR_DATALINK_UNIMPLEMENTED);
+ CASE_CODE (SC_ERR_INVALID_SIGNATURE);
+ CASE_CODE (SC_ERR_OPENING_FILE);
+ CASE_CODE (SC_ERR_OPENING_RULE_FILE);
+ CASE_CODE (SC_ERR_NO_RULES);
+ CASE_CODE (SC_ERR_NO_RULES_LOADED);
+ CASE_CODE (SC_ERR_COUNTER_EXCEEDED);
+ CASE_CODE (SC_ERR_INVALID_CHECKSUM);
+ CASE_CODE (SC_ERR_SPRINTF);
+ CASE_CODE (SC_ERR_FATAL);
+ CASE_CODE (SC_ERR_INVALID_ARGUMENT);
+ CASE_CODE (SC_ERR_SPINLOCK);
+ CASE_CODE (SC_ERR_INVALID_ENUM_MAP);
+ CASE_CODE (SC_ERR_INVALID_IP_NETBLOCK);
+ CASE_CODE (SC_ERR_INVALID_IPV4_ADDR);
+ CASE_CODE (SC_ERR_INVALID_IPV6_ADDR);
+ CASE_CODE (SC_ERR_INVALID_RUNMODE);
+ CASE_CODE (SC_ERR_COMPLETE_PORT_SPACE_NEGATED);
+ CASE_CODE (SC_ERR_NO_PORTS_LEFT_AFTER_MERGE);
+ CASE_CODE (SC_ERR_NEGATED_VALUE_IN_PORT_RANGE);
+ CASE_CODE (SC_ERR_PORT_PARSE_INSERT_STRING);
+ CASE_CODE (SC_ERR_UNREACHABLE_CODE_REACHED);
+ CASE_CODE (SC_ERR_INVALID_NUMERIC_VALUE);
+ CASE_CODE (SC_ERR_NUMERIC_VALUE_ERANGE);
+ CASE_CODE (SC_ERR_INVALID_NUM_BYTES);
+ CASE_CODE (SC_ERR_ARG_LEN_LONG);
+ CASE_CODE (SC_ERR_ALPARSER);
+ CASE_CODE (SC_ERR_POOL_EMPTY);
+ CASE_CODE (SC_ERR_REASSEMBLY);
+ CASE_CODE (SC_ERR_POOL_INIT);
+ CASE_CODE (SC_ERR_UNIMPLEMENTED);
+ CASE_CODE (SC_ERR_ADDRESS_ENGINE_GENERIC);
+ CASE_CODE (SC_ERR_PORT_ENGINE_GENERIC);
+ CASE_CODE (SC_ERR_FAST_LOG_GENERIC);
+ CASE_CODE (SC_ERR_IPONLY_RADIX);
+ CASE_CODE (SC_ERR_DEBUG_LOG_GENERIC);
+ CASE_CODE (SC_ERR_UNIFIED_LOG_GENERIC);
+ CASE_CODE (SC_ERR_HTTP_LOG_GENERIC);
+ CASE_CODE (SC_ERR_UNIFIED_ALERT_GENERIC);
+ CASE_CODE (SC_ERR_UNIFIED2_ALERT_GENERIC);
+ CASE_CODE (SC_ERR_FWRITE);
+ CASE_CODE (SC_ERR_FOPEN);
+ CASE_CODE (SC_ERR_THREAD_NICE_PRIO);
+ CASE_CODE (SC_ERR_THREAD_SPAWN);
+ CASE_CODE (SC_ERR_THREAD_CREATE);
+ CASE_CODE (SC_ERR_THREAD_INIT);
+ CASE_CODE (SC_ERR_THREAD_DEINIT);
+ CASE_CODE (SC_ERR_THRESHOLD_HASH_ADD);
+ CASE_CODE (SC_ERR_UNDEFINED_VAR);
+ CASE_CODE (SC_ERR_RULE_KEYWORD_UNKNOWN);
+ CASE_CODE (SC_ERR_FLAGS_MODIFIER);
+ CASE_CODE (SC_ERR_DISTANCE_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_BYTETEST_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_BYTEJUMP_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_WITHIN_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_WITHIN_INVALID);
+ CASE_CODE (SC_ERR_DEPTH_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_OFFSET_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_NOCASE_MISSING_PATTERN);
+ CASE_CODE (SC_ERR_RAWBYTES_MISSING_CONTENT);
+ CASE_CODE (SC_ERR_NO_URICONTENT_NEGATION);
+ CASE_CODE (SC_ERR_HASH_TABLE_INIT);
+ CASE_CODE (SC_ERR_STAT);
+ CASE_CODE (SC_ERR_LOGDIR_CONFIG);
+ CASE_CODE (SC_ERR_LOGDIR_CMDLINE);
+ CASE_CODE (SC_ERR_RADIX_TREE_GENERIC);
+ CASE_CODE (SC_ERR_MISSING_QUOTE);
+ CASE_CODE (SC_ERR_UNKNOWN_PROTOCOL);
+ CASE_CODE (SC_ERR_UNKNOWN_RUN_MODE);
+ CASE_CODE (SC_ERR_IPFW_NOSUPPORT);
+ CASE_CODE (SC_ERR_IPFW_BIND);
+ CASE_CODE (SC_ERR_IPFW_SOCK);
+ CASE_CODE (SC_ERR_IPFW_SETSOCKOPT);
+ CASE_CODE (SC_ERR_IPFW_NOPORT);
+ CASE_CODE (SC_WARN_IPFW_RECV);
+ CASE_CODE (SC_WARN_IPFW_XMIT);
+ CASE_CODE (SC_WARN_IPFW_SETSOCKOPT);
+ CASE_CODE (SC_WARN_IPFW_UNBIND);
+ CASE_CODE (SC_ERR_MULTIPLE_RUN_MODE);
+ CASE_CODE (SC_ERR_BPF);
+ CASE_CODE (SC_ERR_MISSING_CONFIG_PARAM);
+ CASE_CODE (SC_ERR_UNKNOWN_VALUE);
+ CASE_CODE (SC_ERR_INVALID_VALUE);
+ CASE_CODE (SC_ERR_UNKNOWN_REGEX_MOD);
+ CASE_CODE (SC_ERR_INVALID_OPERATOR);
+ CASE_CODE (SC_ERR_PCAP_RECV_INIT);
+ CASE_CODE (SC_ERR_NFQ_NOSUPPORT);
+ CASE_CODE (SC_ERR_NFQ_UNBIND);
+ CASE_CODE (SC_ERR_NFQ_BIND);
+ CASE_CODE (SC_ERR_NFQ_HANDLE_PKT);
+ CASE_CODE (SC_ERR_NFLOG_NOSUPPORT);
+ CASE_CODE (SC_ERR_NFLOG_OPEN);
+ CASE_CODE (SC_ERR_NFLOG_BIND);
+ CASE_CODE (SC_ERR_NFLOG_UNBIND);
+ CASE_CODE (SC_ERR_NFLOG_MAX_BUFSIZ);
+ CASE_CODE (SC_ERR_NFLOG_SET_MODE);
+ CASE_CODE (SC_ERR_NFLOG_HANDLE_PKT);
+ CASE_CODE (SC_ERR_NFLOG_GROUP);
+ CASE_CODE (SC_ERR_NFLOG_FD);
+ CASE_CODE (SC_WARN_NFLOG_SETSOCKOPT);
+ CASE_CODE (SC_WARN_NFLOG_RECV);
+ CASE_CODE (SC_WARN_NFLOG_LOSING_EVENTS);
+ CASE_CODE (SC_WARN_NFLOG_MAXBUFSIZ_REACHED);
+ CASE_CODE (SC_ERR_CUDA_ERROR);
+ CASE_CODE (SC_ERR_CUDA_HANDLER_ERROR);
+ CASE_CODE (SC_ERR_TM_THREADS_ERROR);
+ CASE_CODE (SC_ERR_TM_MODULES_ERROR);
+ CASE_CODE (SC_ERR_AC_CUDA_ERROR);
+ CASE_CODE (SC_ERR_INVALID_YAML_CONF_ENTRY);
+ CASE_CODE (SC_ERR_TMQ_ALREADY_REGISTERED);
+ CASE_CODE (SC_ERR_CONFLICTING_RULE_KEYWORDS);
+ CASE_CODE (SC_ERR_INITIALIZATION);
+ CASE_CODE (SC_ERR_INVALID_ACTION);
+ CASE_CODE (SC_ERR_LIBNET_REQUIRED_FOR_ACTION);
+ CASE_CODE (SC_ERR_LIBNET_INIT);
+ CASE_CODE (SC_ERR_LIBNET_INVALID_DIR);
+ CASE_CODE (SC_ERR_LIBNET_BUILD_FAILED);
+ CASE_CODE (SC_ERR_LIBNET_WRITE_FAILED);
+ CASE_CODE (SC_ERR_LIBNET_NOT_ENABLED);
+ CASE_CODE (SC_ERR_UNIFIED_LOG_FILE_HEADER);
+ CASE_CODE (SC_ERR_REFERENCE_UNKNOWN);
+ CASE_CODE (SC_ERR_PIDFILE_SNPRINTF);
+ CASE_CODE (SC_ERR_PIDFILE_OPEN);
+ CASE_CODE (SC_ERR_PIDFILE_WRITE);
+ CASE_CODE (SC_ERR_PIDFILE_DAEMON);
+ CASE_CODE (SC_ERR_UID_FAILED);
+ CASE_CODE (SC_ERR_GID_FAILED);
+ CASE_CODE (SC_ERR_CHANGING_CAPS_FAILED);
+ CASE_CODE (SC_ERR_LIBCAP_NG_REQUIRED);
+ CASE_CODE (SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG);
+ CASE_CODE (SC_WARN_FLOW_EMERGENCY);
+ CASE_CODE (SC_ERR_SVC);
+ CASE_CODE (SC_ERR_ERF_DAG_OPEN_FAILED);
+ CASE_CODE (SC_ERR_ERF_DAG_STREAM_OPEN_FAILED);
+ CASE_CODE (SC_ERR_ERF_DAG_STREAM_START_FAILED);
+ CASE_CODE (SC_ERR_ERF_DAG_STREAM_SET_FAILED);
+ CASE_CODE (SC_ERR_ERF_DAG_STREAM_READ_FAILED);
+ CASE_CODE (SC_WARN_ERF_DAG_REC_LEN_CHANGED);
+ CASE_CODE (SC_ERR_NAPATECH_OPEN_FAILED);
+ CASE_CODE (SC_ERR_NAPATECH_STREAM_NEXT_FAILED);
+ CASE_CODE (SC_ERR_NAPATECH_NOSUPPORT);
+ CASE_CODE (SC_ERR_NAPATECH_REQUIRED);
+ CASE_CODE (SC_ERR_NAPATECH_TIMESTAMP_TYPE_NOT_SUPPORTED);
+ CASE_CODE (SC_ERR_NAPATECH_INIT_FAILED);
+ CASE_CODE (SC_ERR_NAPATECH_CONFIG_STREAM);
+ CASE_CODE (SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED);
+ CASE_CODE (SC_ERR_NAPATECH_STAT_DROPS_FAILED);
+ CASE_CODE (SC_ERR_NAPATECH_PARSE_CONFIG);
+ CASE_CODE (SC_WARN_COMPATIBILITY);
+ CASE_CODE (SC_ERR_DCERPC);
+ CASE_CODE (SC_ERR_DETECT_PREPARE);
+ CASE_CODE (SC_ERR_AHO_CORASICK);
+ CASE_CODE (SC_ERR_REFERENCE_CONFIG);
+ CASE_CODE (SC_ERR_DUPLICATE_SIG);
+ CASE_CODE (SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL);
+ CASE_CODE (SC_ERR_PCAP_MULTI_DEV_NO_SUPPORT);
+ CASE_CODE (SC_ERR_HTTP_METHOD_NEEDS_PRECEEDING_CONTENT);
+ CASE_CODE (SC_ERR_HTTP_METHOD_INCOMPATIBLE_WITH_RAWBYTES);
+ CASE_CODE (SC_ERR_HTTP_METHOD_RELATIVE_MISSING);
+ CASE_CODE (SC_ERR_HTTP_COOKIE_NEEDS_PRECEEDING_CONTENT);
+ CASE_CODE (SC_ERR_HTTP_COOKIE_INCOMPATIBLE_WITH_RAWBYTES);
+ CASE_CODE (SC_ERR_HTTP_COOKIE_RELATIVE_MISSING);
+ CASE_CODE (SC_ERR_LOGPCAP_SGUIL_BASE_DIR_MISSING);
+ CASE_CODE (SC_ERR_UNKNOWN_DECODE_EVENT);
+ CASE_CODE (SC_ERR_RUNMODE);
+ CASE_CODE (SC_ERR_SHUTDOWN);
+ CASE_CODE (SC_ERR_INVALID_DIRECTION);
+ CASE_CODE (SC_ERR_AFP_CREATE);
+ CASE_CODE (SC_ERR_AFP_READ);
+ CASE_CODE (SC_ERR_AFP_DISPATCH);
+ CASE_CODE (SC_ERR_CMD_LINE);
+ CASE_CODE (SC_ERR_SIZE_PARSE);
+ CASE_CODE (SC_ERR_RAWBYTES_FILE_DATA);
+ CASE_CODE (SC_ERR_SOCKET);
+ CASE_CODE (SC_ERR_PCAP_TRANSLATE);
+ CASE_CODE (SC_WARN_OUTDATED_LIBHTP);
+ CASE_CODE (SC_WARN_DEPRECATED);
+ CASE_CODE (SC_WARN_PROFILE);
+ CASE_CODE (SC_ERR_FLOW_INIT);
+ CASE_CODE (SC_ERR_HOST_INIT);
+ CASE_CODE (SC_ERR_MEM_BUFFER_API);
+ CASE_CODE (SC_ERR_INVALID_MD5);
+ CASE_CODE (SC_ERR_NO_MD5_SUPPORT);
+ CASE_CODE (SC_ERR_EVENT_ENGINE);
+ CASE_CODE (SC_ERR_NO_LUA_SUPPORT);
+ CASE_CODE (SC_ERR_LUA_ERROR);
+ CASE_CODE (SC_ERR_NO_GEOIP_SUPPORT);
+ CASE_CODE (SC_ERR_GEOIP_ERROR);
+ CASE_CODE (SC_ERR_DEFRAG_INIT);
+ CASE_CODE (SC_ERR_NO_REPUTATION);
+ CASE_CODE (SC_ERR_NOT_SUPPORTED);
+ CASE_CODE (SC_ERR_LIVE_RULE_SWAP);
+ CASE_CODE (SC_WARN_UNCOMMON);
+ CASE_CODE (SC_ERR_SYSCALL);
+ CASE_CODE (SC_ERR_SYSCONF);
+ CASE_CODE (SC_ERR_INVALID_ARGUMENTS);
+ CASE_CODE (SC_ERR_STATS_NOT_INIT);
+ CASE_CODE (SC_ERR_NFQ_OPEN);
+ CASE_CODE (SC_ERR_NFQ_MAXLEN);
+ CASE_CODE (SC_ERR_NFQ_CREATE_QUEUE);
+ CASE_CODE (SC_ERR_NFQ_SET_MODE);
+ CASE_CODE (SC_ERR_NFQ_SETSOCKOPT);
+ CASE_CODE (SC_ERR_NFQ_RECV);
+ CASE_CODE (SC_ERR_NFQ_SET_VERDICT);
+ CASE_CODE (SC_ERR_NFQ_THREAD_INIT);
+ CASE_CODE (SC_ERR_DAEMON);
+ CASE_CODE (SC_ERR_TLS_LOG_GENERIC);
+ CASE_CODE (SC_ERR_MUTEX);
+ CASE_CODE (SC_ERR_REPUTATION_INVALID_OPERATION);
+ CASE_CODE (SC_ERR_REPUTATION_INVALID_TYPE);
+ CASE_CODE (SC_ERR_BYTE_EXTRACT_FAILED);
+ CASE_CODE (SC_ERR_DAG_REQUIRED);
+ CASE_CODE (SC_ERR_DAG_NOSUPPORT);
+ CASE_CODE (SC_ERR_NO_AF_PACKET);
+ CASE_CODE (SC_ERR_PCAP_FILE_DELETE_FAILED);
+ CASE_CODE (SC_ERR_MAGIC_OPEN);
+ CASE_CODE (SC_ERR_MAGIC_LOAD);
+ CASE_CODE (SC_ERR_CUDA_BUFFER_ERROR);
+ CASE_CODE (SC_ERR_DNS_LOG_GENERIC);
+ CASE_CODE (SC_WARN_OPTION_OBSOLETE);
+ CASE_CODE (SC_WARN_NO_UNITTESTS);
+ CASE_CODE (SC_ERR_THREAD_QUEUE);
+ CASE_CODE (SC_WARN_XFF_INVALID_MODE);
+ CASE_CODE (SC_WARN_XFF_INVALID_HEADER);
+ CASE_CODE (SC_WARN_XFF_INVALID_DEPLOYMENT);
+ CASE_CODE (SC_ERR_THRESHOLD_SETUP);
+ CASE_CODE (SC_ERR_DNS_CONFIG);
+ CASE_CODE (SC_ERR_MODBUS_CONFIG);
+ CASE_CODE (SC_ERR_CONF_YAML_ERROR);
+ CASE_CODE (SC_ERR_CONF_NAME_TOO_LONG);
+ CASE_CODE (SC_ERR_APP_LAYER_PROTOCOL_DETECTION);
+ CASE_CODE (SC_ERR_PCIE_INIT_FAILED);
+ CASE_CODE (SC_WARN_LUA_SCRIPT);
+ CASE_CODE (SC_ERR_LUA_SCRIPT);
+ CASE_CODE (SC_WARN_NO_STATS_LOGGERS);
+ CASE_CODE (SC_ERR_NO_NETMAP);
+ CASE_CODE (SC_ERR_NETMAP_CREATE);
+ CASE_CODE (SC_ERR_NETMAP_READ);
+ CASE_CODE (SC_ERR_IPPAIR_INIT);
+ CASE_CODE (SC_ERR_MT_NO_SELECTOR);
+ CASE_CODE (SC_ERR_MT_DUPLICATE_TENANT);
+ }
+
+ return "UNKNOWN_ERROR";
+}
diff --git a/framework/src/suricata/src/util-error.h b/framework/src/suricata/src/util-error.h
new file mode 100644
index 00000000..d1e42a8c
--- /dev/null
+++ b/framework/src/suricata/src/util-error.h
@@ -0,0 +1,306 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+
+/* different error types */
+typedef enum {
+ SC_OK,
+ SC_ERR_MEM_ALLOC,
+ SC_ERR_PCRE_MATCH,
+ SC_ERR_ACTION_ORDER,
+ SC_ERR_PCRE_GET_SUBSTRING,
+ SC_ERR_PCRE_COMPILE,
+ SC_ERR_PCRE_STUDY,
+ SC_ERR_PCRE_PARSE,
+ SC_ERR_LOG_MODULE_NOT_INIT,
+ SC_ERR_LOG_FG_FILTER_MATCH,
+ SC_ERR_COUNTER_EXCEEDED,
+ SC_ERR_INVALID_CHECKSUM,
+ SC_ERR_SPRINTF,
+ SC_ERR_INVALID_ARGUMENT,
+ SC_ERR_SPINLOCK,
+ SC_ERR_INVALID_ENUM_MAP,
+ SC_ERR_INVALID_IP_NETBLOCK,
+ SC_ERR_INVALID_IPV4_ADDR,
+ SC_ERR_INVALID_IPV6_ADDR,
+ SC_ERR_INVALID_RUNMODE,
+ SC_ERR_PCAP_DISPATCH,
+ SC_ERR_PCAP_CREATE,
+ SC_ERR_PCAP_SET_SNAPLEN,
+ SC_ERR_PCAP_SET_PROMISC,
+ SC_ERR_PCAP_SET_TIMEOUT,
+ SC_ERR_PCAP_OPEN_LIVE,
+ SC_ERR_PCAP_OPEN_OFFLINE,
+ SC_ERR_PCAP_ACTIVATE_HANDLE,
+ SC_ERR_PCAP_SET_BUFF_SIZE,
+ SC_ERR_NO_PCAP_SET_BUFFER_SIZE,
+ SC_ERR_NO_PF_RING,
+ SC_ERR_PF_RING_RECV,
+ SC_ERR_PF_RING_GET_CLUSTERID_FAILED,
+ SC_ERR_PF_RING_GET_INTERFACE_FAILED,
+ SC_ERR_PF_RING_OPEN,
+ SC_ERR_GET_CLUSTER_TYPE_FAILED,
+ SC_ERR_INVALID_CLUSTER_TYPE,
+ SC_ERR_PF_RING_SET_CLUSTER_FAILED,
+ SC_ERR_DATALINK_UNIMPLEMENTED,
+ SC_ERR_INVALID_SIGNATURE,
+ SC_ERR_OPENING_FILE,
+ SC_ERR_OPENING_RULE_FILE,
+ SC_ERR_NO_RULES,
+ SC_ERR_NO_RULES_LOADED,
+ SC_ERR_FOPEN,
+ SC_ERR_INITIALIZATION,
+ SC_ERR_THREAD_SPAWN,
+ SC_ERR_THREAD_NICE_PRIO,
+ SC_ERR_THREAD_CREATE,
+ SC_ERR_THREAD_INIT, /**< thread's initialization function failed */
+ SC_ERR_SYSCALL,
+ SC_ERR_SYSCONF,
+ SC_ERR_INVALID_ARGUMENTS,
+ SC_ERR_STATS_NOT_INIT,
+ SC_ERR_COMPLETE_PORT_SPACE_NEGATED,
+ SC_ERR_NO_PORTS_LEFT_AFTER_MERGE,
+ SC_ERR_NEGATED_VALUE_IN_PORT_RANGE,
+ SC_ERR_PORT_PARSE_INSERT_STRING,
+ SC_ERR_UNREACHABLE_CODE_REACHED,
+ SC_ERR_ALPARSER,
+ SC_ERR_INVALID_NUMERIC_VALUE,
+ SC_ERR_NUMERIC_VALUE_ERANGE,
+ SC_ERR_INVALID_NUM_BYTES,
+ SC_ERR_ARG_LEN_LONG,
+ SC_ERR_POOL_EMPTY,
+ SC_ERR_REASSEMBLY,
+ SC_ERR_POOL_INIT,
+ SC_ERR_NFQ_NOSUPPORT,
+ SC_ERR_NFQ_OPEN,
+ SC_ERR_NFQ_BIND,
+ SC_ERR_NFQ_UNBIND,
+ SC_ERR_NFQ_MAXLEN,
+ SC_ERR_NFQ_CREATE_QUEUE,
+ SC_ERR_NFQ_SET_MODE,
+ SC_ERR_NFQ_SETSOCKOPT,
+ SC_ERR_NFQ_RECV,
+ SC_ERR_NFQ_HANDLE_PKT,
+ SC_ERR_NFQ_SET_VERDICT,
+ SC_ERR_NFQ_THREAD_INIT,
+ SC_ERR_IPFW_NOSUPPORT,
+ SC_ERR_IPFW_BIND,
+ SC_ERR_IPFW_SOCK,
+ SC_ERR_IPFW_NOPORT,
+ SC_WARN_IPFW_RECV,
+ SC_WARN_IPFW_XMIT,
+ SC_WARN_IPFW_SETSOCKOPT,
+ SC_WARN_IPFW_UNBIND,
+ SC_ERR_DAEMON,
+ SC_ERR_UNIMPLEMENTED,
+ SC_ERR_ADDRESS_ENGINE_GENERIC,
+ SC_ERR_PORT_ENGINE_GENERIC,
+ SC_ERR_IPONLY_RADIX,
+ SC_ERR_FAST_LOG_GENERIC,
+ SC_ERR_DEBUG_LOG_GENERIC,
+ SC_ERR_UNIFIED_LOG_GENERIC,
+ SC_ERR_HTTP_LOG_GENERIC,
+ SC_ERR_TLS_LOG_GENERIC,
+ SC_ERR_UNIFIED_ALERT_GENERIC,
+ SC_ERR_UNIFIED2_ALERT_GENERIC,
+ SC_ERR_FWRITE,
+ SC_ERR_THRESHOLD_HASH_ADD,
+ SC_ERR_UNDEFINED_VAR,
+ SC_ERR_RULE_KEYWORD_UNKNOWN,
+ SC_ERR_FLAGS_MODIFIER,
+ SC_ERR_DISTANCE_MISSING_CONTENT,
+ SC_ERR_WITHIN_MISSING_CONTENT,
+ SC_ERR_WITHIN_INVALID,
+ SC_ERR_OFFSET_MISSING_CONTENT,
+ SC_ERR_DEPTH_MISSING_CONTENT,
+ SC_ERR_BYTETEST_MISSING_CONTENT,
+ SC_ERR_BYTEJUMP_MISSING_CONTENT,
+ SC_ERR_NOCASE_MISSING_PATTERN,
+ SC_ERR_RAWBYTES_MISSING_CONTENT,
+ SC_ERR_NO_URICONTENT_NEGATION,
+ SC_ERR_HASH_TABLE_INIT,
+ SC_ERR_STAT,
+ SC_ERR_LOGDIR_CONFIG,
+ SC_ERR_LOGDIR_CMDLINE,
+ SC_ERR_MISSING_CONFIG_PARAM,
+ SC_ERR_RADIX_TREE_GENERIC,
+ SC_ERR_MISSING_QUOTE,
+ SC_ERR_MUTEX,
+ SC_ERR_REPUTATION_INVALID_OPERATION,
+ SC_ERR_REPUTATION_INVALID_TYPE,
+ SC_ERR_UNKNOWN_PROTOCOL, /**< signature contains invalid protocol */
+ SC_ERR_UNKNOWN_RUN_MODE,
+ SC_ERR_MULTIPLE_RUN_MODE,
+ SC_ERR_BPF,
+ SC_ERR_BYTE_EXTRACT_FAILED,
+ SC_ERR_UNKNOWN_VALUE,
+ SC_ERR_INVALID_VALUE,
+ SC_ERR_UNKNOWN_REGEX_MOD,
+ SC_ERR_INVALID_OPERATOR,
+ SC_ERR_PCAP_RECV_INIT,
+ SC_ERR_CUDA_ERROR,
+ SC_ERR_CUDA_HANDLER_ERROR,
+ SC_ERR_TM_THREADS_ERROR,
+ SC_ERR_TM_MODULES_ERROR,
+ SC_ERR_AC_CUDA_ERROR,
+ SC_ERR_INVALID_YAML_CONF_ENTRY,
+ SC_ERR_TMQ_ALREADY_REGISTERED,
+ SC_ERR_CONFLICTING_RULE_KEYWORDS,
+ SC_ERR_INVALID_ACTION,
+ SC_ERR_LIBNET_REQUIRED_FOR_ACTION,
+ SC_ERR_LIBNET_INIT,
+ SC_ERR_LIBNET_INVALID_DIR,
+ SC_ERR_LIBNET_BUILD_FAILED,
+ SC_ERR_LIBNET_WRITE_FAILED,
+ SC_ERR_LIBNET_NOT_ENABLED,
+ SC_ERR_UNIFIED_LOG_FILE_HEADER, /**< Error to indicate the unified file
+ header writing function has been
+ failed */
+ SC_ERR_REFERENCE_UNKNOWN, /**< unknown reference key (cve, url, etc) */
+ SC_ERR_PIDFILE_SNPRINTF,
+ SC_ERR_PIDFILE_OPEN,
+ SC_ERR_PIDFILE_WRITE,
+ SC_ERR_PIDFILE_DAEMON,
+ SC_ERR_UID_FAILED,
+ SC_ERR_GID_FAILED,
+ SC_ERR_CHANGING_CAPS_FAILED,
+ SC_ERR_LIBCAP_NG_REQUIRED,
+ SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG,
+ SC_WARN_FLOW_EMERGENCY,
+ SC_WARN_COMPATIBILITY,
+ SC_ERR_SVC,
+ SC_ERR_ERF_DAG_OPEN_FAILED,
+ SC_ERR_ERF_DAG_STREAM_OPEN_FAILED,
+ SC_ERR_ERF_DAG_STREAM_START_FAILED,
+ SC_ERR_ERF_DAG_STREAM_SET_FAILED,
+ SC_ERR_ERF_DAG_STREAM_READ_FAILED,
+ SC_WARN_ERF_DAG_REC_LEN_CHANGED,
+ SC_ERR_DAG_REQUIRED,
+ SC_ERR_DAG_NOSUPPORT, /**< no ERF/DAG support compiled in */
+ SC_ERR_FATAL,
+ SC_ERR_DCERPC,
+ SC_ERR_DETECT_PREPARE, /**< preparing the detection engine failed */
+ SC_ERR_AHO_CORASICK,
+ SC_ERR_REFERENCE_CONFIG,
+ SC_ERR_DUPLICATE_SIG, /**< Error to indicate that signature is duplicate */
+ SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL,
+ SC_ERR_PCAP_MULTI_DEV_NO_SUPPORT,
+ SC_ERR_HTTP_METHOD_NEEDS_PRECEEDING_CONTENT,
+ SC_ERR_HTTP_METHOD_INCOMPATIBLE_WITH_RAWBYTES,
+ SC_ERR_HTTP_METHOD_RELATIVE_MISSING,
+ SC_ERR_HTTP_COOKIE_NEEDS_PRECEEDING_CONTENT,
+ SC_ERR_HTTP_COOKIE_INCOMPATIBLE_WITH_RAWBYTES,
+ SC_ERR_HTTP_COOKIE_RELATIVE_MISSING,
+ SC_ERR_LOGPCAP_SGUIL_BASE_DIR_MISSING,
+ SC_ERR_UNKNOWN_DECODE_EVENT,
+ SC_ERR_RUNMODE,
+ SC_ERR_SHUTDOWN,
+ SC_ERR_INVALID_DIRECTION,
+ SC_ERR_AFP_CREATE,
+ SC_ERR_AFP_READ,
+ SC_ERR_AFP_DISPATCH,
+ SC_ERR_NO_AF_PACKET,
+ SC_ERR_PCAP_FILE_DELETE_FAILED,
+ SC_ERR_CMD_LINE,
+ SC_ERR_MAGIC_OPEN,
+ SC_ERR_MAGIC_LOAD,
+ SC_ERR_SIZE_PARSE,
+ SC_ERR_RAWBYTES_FILE_DATA,
+ SC_ERR_SOCKET,
+ SC_ERR_PCAP_TRANSLATE, /* failed to translate ip to dev */
+ SC_WARN_OUTDATED_LIBHTP,
+ SC_WARN_DEPRECATED,
+ SC_WARN_PROFILE,
+ SC_ERR_FLOW_INIT,
+ SC_ERR_HOST_INIT,
+ SC_ERR_MEM_BUFFER_API,
+ SC_ERR_INVALID_MD5,
+ SC_ERR_NO_MD5_SUPPORT,
+ SC_ERR_EVENT_ENGINE,
+ SC_ERR_NO_LUA_SUPPORT,
+ SC_ERR_LUA_ERROR,
+ SC_ERR_DEFRAG_INIT,
+ SC_ERR_NAPATECH_OPEN_FAILED,
+ SC_ERR_NAPATECH_STREAM_NEXT_FAILED,
+ SC_ERR_NAPATECH_NOSUPPORT,
+ SC_ERR_NAPATECH_REQUIRED,
+ SC_ERR_NAPATECH_TIMESTAMP_TYPE_NOT_SUPPORTED,
+ SC_ERR_NAPATECH_INIT_FAILED,
+ SC_ERR_NAPATECH_CONFIG_STREAM,
+ SC_ERR_NAPATECH_STREAMS_REGISTER_FAILED,
+ SC_ERR_NAPATECH_STAT_DROPS_FAILED,
+ SC_ERR_NAPATECH_PARSE_CONFIG,
+ SC_ERR_NO_REPUTATION,
+ SC_ERR_NOT_SUPPORTED,
+ SC_ERR_IPFW_SETSOCKOPT,
+ SC_ERR_NO_GEOIP_SUPPORT,
+ SC_ERR_GEOIP_ERROR,
+ SC_ERR_LIVE_RULE_SWAP,
+ SC_WARN_UNCOMMON,
+ SC_ERR_CUDA_BUFFER_ERROR,
+ SC_ERR_DNS_LOG_GENERIC,
+ SC_WARN_OPTION_OBSOLETE,
+ SC_WARN_NO_UNITTESTS,
+ SC_ERR_THREAD_QUEUE,
+ SC_WARN_XFF_INVALID_MODE,
+ SC_WARN_XFF_INVALID_HEADER,
+ SC_WARN_XFF_INVALID_DEPLOYMENT,
+ SC_ERR_THRESHOLD_SETUP,
+ SC_ERR_DNS_CONFIG,
+ SC_ERR_MODBUS_CONFIG,
+ SC_ERR_CONF_YAML_ERROR,
+ SC_ERR_CONF_NAME_TOO_LONG,
+ SC_ERR_APP_LAYER_PROTOCOL_DETECTION,
+ SC_ERR_PCIE_INIT_FAILED,
+ SC_ERR_NFLOG_NOSUPPORT,
+ SC_ERR_NFLOG_OPEN,
+ SC_ERR_NFLOG_BIND,
+ SC_ERR_NFLOG_UNBIND,
+ SC_ERR_NFLOG_MAX_BUFSIZ,
+ SC_ERR_NFLOG_SET_MODE,
+ SC_ERR_NFLOG_HANDLE_PKT,
+ SC_ERR_NFLOG_GROUP,
+ SC_ERR_NFLOG_FD,
+ SC_WARN_NFLOG_RECV,
+ SC_WARN_NFLOG_LOSING_EVENTS,
+ SC_WARN_NFLOG_MAXBUFSIZ_REACHED,
+ SC_WARN_NFLOG_SETSOCKOPT,
+ SC_WARN_LUA_SCRIPT,
+ SC_ERR_LUA_SCRIPT,
+ SC_WARN_NO_STATS_LOGGERS,
+ SC_ERR_NO_NETMAP,
+ SC_ERR_NETMAP_CREATE,
+ SC_ERR_NETMAP_READ,
+ SC_ERR_THREAD_DEINIT, /**< thread's deinit function failed */
+ SC_ERR_IPPAIR_INIT,
+ SC_ERR_MT_NO_SELECTOR,
+ SC_ERR_MT_DUPLICATE_TENANT,
+} SCError;
+
+const char *SCErrorToString(SCError);
+
+
+#endif /* __ERROR_H__ */
diff --git a/framework/src/suricata/src/util-file.c b/framework/src/suricata/src/util-file.c
new file mode 100644
index 00000000..bfb68e1e
--- /dev/null
+++ b/framework/src/suricata/src/util-file.c
@@ -0,0 +1,932 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "flow.h"
+#include "stream.h"
+#include "runmodes.h"
+#include "util-hash.h"
+#include "util-debug.h"
+#include "util-memcmp.h"
+#include "util-print.h"
+#include "app-layer-parser.h"
+#include "util-validate.h"
+
+/** \brief switch to force magic checks on all files
+ * regardless of the rules.
+ */
+static int g_file_force_magic = 0;
+
+/** \brief switch to force md5 calculation on all files
+ * regardless of the rules.
+ */
+static int g_file_force_md5 = 0;
+
+/** \brief switch to force tracking off all files
+ * regardless of the rules.
+ */
+static int g_file_force_tracking = 0;
+
+/* prototypes */
+static void FileFree(File *);
+static void FileDataFree(FileData *);
+
+void FileForceMagicEnable(void)
+{
+ g_file_force_magic = 1;
+}
+
+void FileForceMd5Enable(void)
+{
+ g_file_force_md5 = 1;
+}
+
+int FileForceMagic(void)
+{
+ return g_file_force_magic;
+}
+
+int FileForceMd5(void)
+{
+ return g_file_force_md5;
+}
+
+void FileForceTrackingEnable(void)
+{
+ g_file_force_tracking = 1;
+}
+
+int FileMagicSize(void)
+{
+ /** \todo make this size configurable */
+ return 512;
+}
+
+static int FileAppendFileDataFilePtr(File *ff, FileData *ffd)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (ff->chunks_tail == NULL) {
+ ff->chunks_head = ffd;
+ ff->chunks_tail = ffd;
+ ff->content_len_so_far = ffd->len;
+ } else {
+ ff->chunks_tail->next = ffd;
+ ff->chunks_tail = ffd;
+ ff->content_len_so_far += ffd->len;
+ }
+
+#ifdef DEBUG
+ ff->chunks_cnt++;
+ if (ff->chunks_cnt > ff->chunks_cnt_max)
+ ff->chunks_cnt_max = ff->chunks_cnt;
+#endif
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx)
+ HASH_Update(ff->md5_ctx, ffd->data, ffd->len);
+#endif
+ SCReturnInt(0);
+}
+
+static int FileAppendFileData(FileContainer *ffc, FileData *ffd)
+{
+ SCEnter();
+
+ if (ffc == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (FileAppendFileDataFilePtr(ffc->tail, ffd) == -1)
+ {
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+
+
+static int FilePruneFile(File *file)
+{
+ SCEnter();
+
+ SCLogDebug("file %p, file->chunks_cnt %"PRIu64, file, file->chunks_cnt);
+
+ if (!(file->flags & FILE_NOMAGIC)) {
+ /* need magic but haven't set it yet, bail out */
+ if (file->magic == NULL)
+ SCReturnInt(0);
+ else
+ SCLogDebug("file->magic %s", file->magic);
+ } else {
+ SCLogDebug("file->flags & FILE_NOMAGIC == true");
+ }
+
+ /* okay, we now know we can prune */
+ FileData *fd = file->chunks_head;
+
+ while (fd != NULL) {
+ SCLogDebug("fd %p", fd);
+
+ if (file->flags & FILE_NOSTORE || fd->stored == 1) {
+ file->chunks_head = fd->next;
+ if (file->chunks_tail == fd)
+ file->chunks_tail = fd->next;
+
+ FileDataFree(fd);
+
+ fd = file->chunks_head;
+#ifdef DEBUG
+ file->chunks_cnt--;
+ SCLogDebug("file->chunks_cnt %"PRIu64, file->chunks_cnt);
+#endif
+ } else if (fd->stored == 0) {
+ fd = NULL;
+ SCReturnInt(0);
+ break;
+ }
+ }
+
+ /* file is done when state is closed+, logging/storing is done (if any) */
+ if (file->state >= FILE_STATE_CLOSED &&
+ (!RunModeOutputFileEnabled() || (file->flags & FILE_LOGGED)) &&
+ (!RunModeOutputFiledataEnabled() || (file->flags & FILE_STORED)))
+ {
+ SCReturnInt(1);
+ } else {
+ SCReturnInt(0);
+ }
+}
+
+void FilePrune(FileContainer *ffc)
+{
+ File *file = ffc->head;
+
+ while (file) {
+ if (FilePruneFile(file) == 0)
+ break;
+
+ BUG_ON(file != ffc->head);
+
+ File *file_next = file->next;
+
+ /* update head and tail */
+ ffc->head = file_next;
+ if (file == ffc->tail)
+ ffc->tail = NULL;
+
+ FileFree(file);
+ file = file_next;
+ }
+}
+
+/**
+ * \brief allocate a FileContainer
+ *
+ * \retval new newly allocated FileContainer
+ * \retval NULL error
+ */
+FileContainer *FileContainerAlloc(void)
+{
+ FileContainer *new = SCMalloc(sizeof(FileContainer));
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
+ return NULL;
+ }
+ memset(new, 0, sizeof(FileContainer));
+ new->head = new->tail = NULL;
+ return new;
+}
+
+/**
+ * \brief Recycle a FileContainer
+ *
+ * \param ffc FileContainer
+ */
+void FileContainerRecycle(FileContainer *ffc)
+{
+ if (ffc == NULL)
+ return;
+
+ File *cur = ffc->head;
+ File *next = NULL;
+ for (;cur != NULL; cur = next) {
+ next = cur->next;
+ FileFree(cur);
+ }
+ ffc->head = ffc->tail = NULL;
+}
+
+/**
+ * \brief Free a FileContainer
+ *
+ * \param ffc FileContainer
+ */
+void FileContainerFree(FileContainer *ffc)
+{
+ if (ffc == NULL)
+ return;
+
+ File *ptr = ffc->head;
+ File *next = NULL;
+ for (;ptr != NULL; ptr = next) {
+ next = ptr->next;
+ FileFree(ptr);
+ }
+ ffc->head = ffc->tail = NULL;
+ SCFree(ffc);
+}
+
+/**
+ * \internal
+ *
+ * \brief allocate a FileData chunk and set it up
+ *
+ * \param data data chunk to store in the FileData
+ * \param data_len lenght of the data
+ *
+ * \retval new FileData object
+ */
+static FileData *FileDataAlloc(uint8_t *data, uint32_t data_len)
+{
+ FileData *new = SCMalloc(sizeof(FileData));
+ if (unlikely(new == NULL)) {
+ return NULL;
+ }
+ memset(new, 0, sizeof(FileData));
+
+ new->data = SCMalloc(data_len);
+ if (new->data == NULL) {
+ SCFree(new);
+ return NULL;
+ }
+
+ new->len = data_len;
+ memcpy(new->data, data, data_len);
+
+ new->next = NULL;
+ return new;
+}
+
+/**
+ * \internal
+ *
+ * \brief free a FileData object
+ *
+ * \param ffd the flow file data object to free
+ */
+static void FileDataFree(FileData *ffd)
+{
+ if (ffd == NULL)
+ return;
+
+ if (ffd->data != NULL) {
+ SCFree(ffd->data);
+ }
+
+ SCFree(ffd);
+}
+
+/**
+ * \brief Alloc a new File
+ *
+ * \param name character array containing the name (not a string)
+ * \param name_len length in bytes of the name
+ *
+ * \retval new File object or NULL on error
+ */
+static File *FileAlloc(uint8_t *name, uint16_t name_len)
+{
+ File *new = SCMalloc(sizeof(File));
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
+ return NULL;
+ }
+ memset(new, 0, sizeof(File));
+
+ new->name = SCMalloc(name_len);
+ if (new->name == NULL) {
+ SCFree(new);
+ return NULL;
+ }
+
+ new->name_len = name_len;
+ memcpy(new->name, name, name_len);
+
+ return new;
+}
+
+static void FileFree(File *ff)
+{
+ if (ff == NULL)
+ return;
+
+ if (ff->name != NULL)
+ SCFree(ff->name);
+
+ /* magic returned by libmagic is strdup'd by MagicLookup. */
+ if (ff->magic != NULL)
+ SCFree(ff->magic);
+
+ if (ff->chunks_head != NULL) {
+ FileData *ffd = ff->chunks_head;
+
+ while (ffd != NULL) {
+ FileData *next_ffd = ffd->next;
+ FileDataFree(ffd);
+ ffd = next_ffd;
+ }
+ }
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx)
+ HASH_Destroy(ff->md5_ctx);
+#endif
+ SCLogDebug("ff chunks_cnt %"PRIu64", chunks_cnt_max %"PRIu64,
+ ff->chunks_cnt, ff->chunks_cnt_max);
+ SCFree(ff);
+}
+
+void FileContainerAdd(FileContainer *ffc, File *ff)
+{
+ if (ffc->head == NULL || ffc->tail == NULL) {
+ ffc->head = ffc->tail = ff;
+ } else {
+ ffc->tail->next = ff;
+ ffc->tail = ff;
+ }
+}
+
+/**
+ * \brief Tag a file for storing
+ *
+ * \param ff The file to store
+ */
+int FileStore(File *ff)
+{
+ ff->flags |= FILE_STORE;
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Set the TX id for a file
+ *
+ * \param ff The file to store
+ * \param txid the tx id
+ */
+int FileSetTx(File *ff, uint64_t txid)
+{
+ SCLogDebug("ff %p txid %"PRIu64, ff, txid);
+ if (ff != NULL)
+ ff->txid = txid;
+ SCReturnInt(0);
+}
+
+/**
+ * \brief check if we have stored enough
+ *
+ * \param ff file
+ *
+ * \retval 0 limit not reached yet
+ * \retval 1 limit reached
+ */
+static int FileStoreNoStoreCheck(File *ff)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (ff->flags & FILE_NOSTORE) {
+ if (ff->state == FILE_STATE_OPENED &&
+ ff->size >= (uint64_t)FileMagicSize())
+ {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Store a chunk of file data in the flow. The open "flowfile"
+ * will be used.
+ *
+ * \param ffc the container
+ * \param data data chunk
+ * \param data_len data chunk len
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * \retval -2 no store for this file
+ */
+int FileAppendData(FileContainer *ffc, uint8_t *data, uint32_t data_len)
+{
+ SCEnter();
+
+ if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ if (ffc->tail->state != FILE_STATE_OPENED) {
+ if (ffc->tail->flags & FILE_NOSTORE) {
+ SCReturnInt(-2);
+ }
+ SCReturnInt(-1);
+ }
+
+ ffc->tail->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ffc->tail->size);
+
+ if (FileStoreNoStoreCheck(ffc->tail) == 1) {
+#ifdef HAVE_NSS
+ /* no storage but forced md5 */
+ if (ffc->tail->md5_ctx) {
+ if (ffc->tail->md5_ctx)
+ HASH_Update(ffc->tail->md5_ctx, data, data_len);
+
+ SCReturnInt(0);
+ }
+#endif
+ if (g_file_force_tracking || (!(ffc->tail->flags & FILE_NOTRACK)))
+ SCReturnInt(0);
+
+ ffc->tail->state = FILE_STATE_TRUNCATED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
+ SCReturnInt(-2);
+ }
+
+ SCLogDebug("appending %"PRIu32" bytes", data_len);
+
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ffc->tail->state = FILE_STATE_ERROR;
+ SCReturnInt(-1);
+ }
+
+ if (ffc->tail->chunks_head == NULL)
+ ffd->stream_offset = 0;
+ else
+ ffd->stream_offset = ffc->tail->size;
+
+ /* append the data */
+ if (FileAppendFileData(ffc, ffd) < 0) {
+ ffc->tail->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnInt(-1);
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Open a new File
+ *
+ * \param ffc flow container
+ * \param name filename character array
+ * \param name_len filename len
+ * \param data initial data
+ * \param data_len initial data len
+ * \param flags open flags
+ *
+ * \retval ff flowfile object
+ *
+ * \note filename is not a string, so it's not nul terminated.
+ */
+File *FileOpenFile(FileContainer *ffc, uint8_t *name,
+ uint16_t name_len, uint8_t *data, uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ //PrintRawDataFp(stdout, name, name_len);
+
+ File *ff = FileAlloc(name, name_len);
+ if (ff == NULL) {
+ SCReturnPtr(NULL, "File");
+ }
+
+ if (flags & FILE_STORE) {
+ ff->flags |= FILE_STORE;
+ } else if (flags & FILE_NOSTORE) {
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+ }
+ if (flags & FILE_NOMAGIC) {
+ SCLogDebug("not doing magic for this file");
+ ff->flags |= FILE_NOMAGIC;
+ }
+ if (flags & FILE_NOMD5) {
+ SCLogDebug("not doing md5 for this file");
+ ff->flags |= FILE_NOMD5;
+ }
+
+#ifdef HAVE_NSS
+ if (!(ff->flags & FILE_NOMD5) || g_file_force_md5) {
+ ff->md5_ctx = HASH_Create(HASH_AlgMD5);
+ if (ff->md5_ctx != NULL) {
+ HASH_Begin(ff->md5_ctx);
+ }
+ }
+#endif
+
+ ff->state = FILE_STATE_OPENED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_OPENED");
+
+ FileContainerAdd(ffc, ff);
+
+ if (data != NULL) {
+ //PrintRawDataFp(stdout, data, data_len);
+ ff->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ff->size);
+
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ff->state = FILE_STATE_ERROR;
+ SCReturnPtr(NULL, "File");
+ }
+
+ /* append the data */
+ if (FileAppendFileData(ffc, ffd) < 0) {
+ ff->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnPtr(NULL, "File");
+ }
+ }
+
+ SCReturnPtr(ff, "File");
+}
+
+static int FileCloseFilePtr(File *ff, uint8_t *data,
+ uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (ff->state != FILE_STATE_OPENED) {
+ SCReturnInt(-1);
+ }
+
+ ff->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ff->size);
+
+ if (data != NULL) {
+ //PrintRawDataFp(stdout, data, data_len);
+
+ if (ff->flags & FILE_NOSTORE) {
+#ifdef HAVE_NSS
+ /* no storage but md5 */
+ if (ff->md5_ctx)
+ HASH_Update(ff->md5_ctx, data, data_len);
+#endif
+ } else {
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ff->state = FILE_STATE_ERROR;
+ SCReturnInt(-1);
+ }
+
+ /* append the data */
+ if (FileAppendFileDataFilePtr(ff, ffd) < 0) {
+ ff->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnInt(-1);
+ }
+ }
+ }
+
+ if (flags & FILE_TRUNCATED) {
+ ff->state = FILE_STATE_TRUNCATED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
+
+ if (flags & FILE_NOSTORE) {
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+ }
+ } else {
+ ff->state = FILE_STATE_CLOSED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_CLOSED");
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx) {
+ unsigned int len = 0;
+ HASH_End(ff->md5_ctx, ff->md5, &len, sizeof(ff->md5));
+ ff->flags |= FILE_MD5;
+ }
+#endif
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Close a File
+ *
+ * \param ffc the container
+ * \param data final data if any
+ * \param data_len data len if any
+ * \param flags flags
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int FileCloseFile(FileContainer *ffc, uint8_t *data,
+ uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ if (ffc == NULL || ffc->tail == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (FileCloseFilePtr(ffc->tail, data, data_len, flags) == -1) {
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief disable file storage for a flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableStoring(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_STORE_TS;
+ else
+ f->flags |= FLOW_FILE_NO_STORE_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ /* if we're already storing, we'll continue */
+ if (!(ptr->flags & FILE_STORE)) {
+ SCLogDebug("not storing this file");
+ ptr->flags |= FILE_NOSTORE;
+ }
+ }
+ }
+ SCReturn;
+}
+
+/**
+ * \brief disable file magic lookups for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableMagic(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_MAGIC_TS;
+ else
+ f->flags |= FLOW_FILE_NO_MAGIC_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling magic for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOMAGIC;
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief disable file md5 calc for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableMd5(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_MD5_TS;
+ else
+ f->flags |= FLOW_FILE_NO_MD5_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling md5 for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOMD5;
+
+#ifdef HAVE_NSS
+ /* destroy any ctx we may have so far */
+ if (ptr->md5_ctx != NULL) {
+ HASH_Destroy(ptr->md5_ctx);
+ ptr->md5_ctx = NULL;
+ }
+#endif
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief disable file size tracking for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableFilesize(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_SIZE_TS;
+ else
+ f->flags |= FLOW_FILE_NO_SIZE_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling size tracking for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOTRACK;
+ }
+ }
+
+ SCReturn;
+}
+
+
+/**
+ * \brief set no store flag, close file if needed
+ *
+ * \param ff file
+ */
+void FileDisableStoringForFile(File *ff)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturn;
+ }
+
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+
+ if (ff->state == FILE_STATE_OPENED && ff->size >= (uint64_t)FileMagicSize()) {
+ if (g_file_force_md5 == 0 && g_file_force_tracking == 0) {
+ (void)FileCloseFilePtr(ff, NULL, 0,
+ (FILE_TRUNCATED|FILE_NOSTORE));
+ }
+ }
+}
+
+/**
+ * \brief disable file storing for files in a transaction
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ * \param tx_id transaction id
+ */
+void FileDisableStoringForTransaction(Flow *f, uint8_t direction, uint64_t tx_id)
+{
+ File *ptr = NULL;
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ SCEnter();
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->txid == tx_id) {
+ if (ptr->flags & FILE_STORE) {
+ /* weird, already storing -- let it continue*/
+ SCLogDebug("file is already being stored");
+ } else {
+ FileDisableStoringForFile(ptr);
+ }
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief flag a file with id "file_id" to be stored.
+ *
+ * \param fc file store
+ * \param file_id the file's id
+ */
+void FileStoreFileById(FileContainer *fc, uint16_t file_id)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->file_id == file_id) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+ }
+}
+
+void FileStoreAllFilesForTx(FileContainer *fc, uint16_t tx_id)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->txid == tx_id) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+ }
+}
+
+void FileStoreAllFiles(FileContainer *fc)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+}
+
+void FileTruncateAllOpenFiles(FileContainer *fc)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->state == FILE_STATE_OPENED) {
+ FileCloseFilePtr(ptr, NULL, 0, FILE_TRUNCATED);
+ }
+ }
+ }
+}
diff --git a/framework/src/suricata/src/util-file.h b/framework/src/suricata/src/util-file.h
new file mode 100644
index 00000000..9cfc3a85
--- /dev/null
+++ b/framework/src/suricata/src/util-file.h
@@ -0,0 +1,192 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#ifndef __UTIL_FILE_H__
+#define __UTIL_FILE_H__
+
+#ifdef HAVE_NSS
+#include <sechash.h>
+#endif
+
+#define FILE_TRUNCATED 0x0001
+#define FILE_NOMAGIC 0x0002
+#define FILE_NOMD5 0x0004
+#define FILE_MD5 0x0008
+#define FILE_LOGGED 0x0010
+#define FILE_NOSTORE 0x0020
+#define FILE_STORE 0x0040
+#define FILE_STORED 0x0080
+#define FILE_NOTRACK 0x0100 /**< track size of file */
+
+typedef enum FileState_ {
+ FILE_STATE_NONE = 0, /**< no state */
+ FILE_STATE_OPENED, /**< flow file is opened */
+ FILE_STATE_CLOSED, /**< flow file is completed,
+ there will be no more data. */
+ FILE_STATE_TRUNCATED, /**< flow file is not complete, but
+ there will be no more data. */
+ FILE_STATE_ERROR, /**< file is in an error state */
+ FILE_STATE_MAX
+} FileState;
+
+typedef struct FileData_ {
+ uint8_t *data;
+ uint32_t len;
+ uint64_t stream_offset;
+ int stored; /* true if this chunk has been stored already
+ * false otherwise */
+ struct FileData_ *next;
+} FileData;
+
+typedef struct File_ {
+ uint16_t flags;
+ uint64_t txid; /**< tx this file is part of */
+ unsigned int file_id;
+ uint8_t *name;
+ uint16_t name_len;
+ int16_t state;
+ uint64_t size; /**< size tracked so far */
+ char *magic;
+ FileData *chunks_head;
+ FileData *chunks_tail;
+ struct File_ *next;
+#ifdef HAVE_NSS
+ HASHContext *md5_ctx;
+ uint8_t md5[MD5_LENGTH];
+#endif
+#ifdef DEBUG
+ uint64_t chunks_cnt;
+ uint64_t chunks_cnt_max;
+#endif
+ uint64_t content_len_so_far;
+ uint64_t content_inspected;
+} File;
+
+typedef struct FileContainer_ {
+ File *head;
+ File *tail;
+} FileContainer;
+
+FileContainer *FileContainerAlloc();
+void FileContainerFree(FileContainer *);
+
+void FileContainerRecycle(FileContainer *);
+
+void FileContainerAdd(FileContainer *, File *);
+
+/**
+ * \brief Open a new File
+ *
+ * \param ffc flow container
+ * \param name filename character array
+ * \param name_len filename len
+ * \param data initial data
+ * \param data_len initial data len
+ * \param flags open flags
+ *
+ * \retval ff flowfile object
+ *
+ * \note filename is not a string, so it's not nul terminated.
+ */
+File *FileOpenFile(FileContainer *, uint8_t *name, uint16_t name_len,
+ uint8_t *data, uint32_t data_len, uint8_t flags);
+/**
+ * \brief Close a File
+ *
+ * \param ffc the container
+ * \param data final data if any
+ * \param data_len data len if any
+ * \param flags flags
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int FileCloseFile(FileContainer *, uint8_t *data, uint32_t data_len, uint8_t flags);
+
+/**
+ * \brief Store a chunk of file data in the flow. The open "flowfile"
+ * will be used.
+ *
+ * \param ffc the container
+ * \param data data chunk
+ * \param data_len data chunk len
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int FileAppendData(FileContainer *, uint8_t *data, uint32_t data_len);
+
+/**
+ * \brief Tag a file for storing
+ *
+ * \param ff The file to store
+ */
+int FileStore(File *);
+
+/**
+ * \brief Set the TX id for a file
+ *
+ * \param ff The file to store
+ * \param txid the tx id
+ */
+int FileSetTx(File *, uint64_t txid);
+
+/**
+ * \brief disable file storage for a flow
+ *
+ * \param f *LOCKED* flow
+ */
+void FileDisableStoring(struct Flow_ *, uint8_t);
+
+void FileDisableFilesize(Flow *f, uint8_t direction);
+
+/**
+ * \brief disable file storing for a transaction
+ *
+ * \param f flow
+ * \param tx_id transaction id
+ */
+void FileDisableStoringForTransaction(Flow *f, uint8_t direction, uint64_t tx_id);
+
+void FlowFileDisableStoringForTransaction(struct Flow_ *f, uint16_t tx_id);
+void FilePrune(FileContainer *ffc);
+
+
+void FileDisableMagic(Flow *f, uint8_t);
+void FileForceMagicEnable(void);
+int FileForceMagic(void);
+
+void FileDisableMd5(Flow *f, uint8_t);
+void FileForceMd5Enable(void);
+int FileForceMd5(void);
+
+void FileForceTrackingEnable(void);
+
+void FileStoreAllFiles(FileContainer *);
+void FileStoreAllFilesForTx(FileContainer *, uint16_t);
+void FileStoreFileById(FileContainer *fc, uint16_t);
+
+void FileTruncateAllOpenFiles(FileContainer *);
+
+#endif /* __UTIL_FILE_H__ */
diff --git a/framework/src/suricata/src/util-fix_checksum.c b/framework/src/suricata/src/util-fix_checksum.c
new file mode 100644
index 00000000..f2bf881e
--- /dev/null
+++ b/framework/src/suricata/src/util-fix_checksum.c
@@ -0,0 +1,58 @@
+/*
+ * Reference: OpenBSD's pf.c.
+ *
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002 - 2008 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ */
+
+#include <stdint.h>
+
+/**
+ * \brief Fix-up an IP checksum.
+ *
+ * \param sum The current checksum.
+ * \param old Value of old header parameter.
+ * \param new Value of new header parameter.
+ *
+ * \retval New checksum.
+ */
+uint16_t
+FixChecksum(uint16_t sum, uint16_t old, uint16_t new)
+{
+ uint32_t l;
+
+ l = sum + old - new;
+ l = (l >> 16) + (l & 65535);
+ l = l & 65535;
+
+ return l;
+}
diff --git a/framework/src/suricata/src/util-fix_checksum.h b/framework/src/suricata/src/util-fix_checksum.h
new file mode 100644
index 00000000..d5e53dd6
--- /dev/null
+++ b/framework/src/suricata/src/util-fix_checksum.h
@@ -0,0 +1,37 @@
+/*
+ * Reference: OpenBSD's pf.c.
+ *
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002 - 2008 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ */
+
+uint16_t FixChecksum(uint16_t sum, uint16_t old, uint16_t new);
diff --git a/framework/src/suricata/src/util-fmemopen.c b/framework/src/suricata/src/util-fmemopen.c
new file mode 100644
index 00000000..0a20b80b
--- /dev/null
+++ b/framework/src/suricata/src/util-fmemopen.c
@@ -0,0 +1,198 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * Based on FMem.c of Alexandre Flori (2008/10/17 AF)
+ */
+
+#include "suricata-common.h"
+#include "util-fmemopen.h"
+
+#ifdef OS_DARWIN
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef OS_FREEBSD
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef __OpenBSD__
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef USE_FMEM_WRAPPER
+
+#ifdef OS_WIN32
+
+/**
+ * \brief portable version of SCFmemopen for Windows works on top of real temp files
+ * \param buffer that holds the file content
+ * \param size of the file buffer
+ * \param mode mode of the file to open
+ * \retval pointer to the file; NULL if something is wrong
+ */
+FILE *SCFmemopen(void *buf, size_t size, const char *mode)
+{
+ char temppath[MAX_PATH - 13];
+ if (0 == GetTempPath(sizeof(temppath), temppath))
+ return NULL;
+
+ char filename[MAX_PATH + 1];
+ if (0 == GetTempFileName(temppath, "SC", 0, filename))
+ return NULL;
+
+ FILE *f = fopen(filename, "wb");
+ if (NULL == f)
+ return NULL;
+
+ fwrite(buf, size, 1, f);
+ fclose(f);
+
+ return fopen(filename, mode);
+}
+
+#else
+
+typedef struct SCFmem_ {
+ size_t pos;
+ size_t size;
+ char *buffer;
+} SCFmem;
+
+/**
+ * \brief Seek the mem file from offset and whence
+ * \param handler pointer to the memfile
+ * \param osffset number of bytes to move from whence
+ * \param whence SEEK_SET, SEEK_CUR, SEEK_END
+ * \retval pos the position by the last operation, -1 if sizes are out of bounds
+ */
+static fpos_t SeekFn(void *handler, fpos_t offset, int whence)
+{
+ size_t pos = 0;
+ SCFmem *mem = handler;
+
+ switch (whence) {
+ case SEEK_SET:
+ if (offset >= 0 && (size_t)offset <= mem->size) {
+ return mem->pos = offset;
+ }
+ break;
+ case SEEK_CUR:
+ if (mem->pos + offset <= mem->size)
+ return mem->pos += offset;
+ break;
+ case SEEK_END:
+ /* must be negative */
+ if (mem->size + offset <= mem->size)
+ return pos = mem->size + offset;
+ break;
+ }
+
+ return -1;
+}
+
+/**
+ * \brief Read from the buffer looking for the available memory limits
+ * \param handler pointer to the memfile
+ * \param buf buffer to read from the handler
+ * \param number of bytes to read
+ * \retval count , the number of bytes read
+ */
+static int ReadFn(void *handler, char *buf, int size)
+{
+ size_t count = 0;
+ SCFmem *mem = handler;
+ size_t available = mem->size - mem->pos;
+ int is_eof = 0;
+
+ if (size < 0) return - 1;
+
+ if ((size_t)size > available) {
+ size = available;
+ } else {
+ is_eof = 1;
+ }
+
+ while (count < (size_t)size)
+ buf[count++] = mem->buffer[mem->pos++];
+
+ if (is_eof == 1)
+ return 0;
+
+ return count;
+}
+
+/**
+ * \brief Write into the buffer looking for the available memory limits
+ * \param handler pointer to the memfile
+ * \param buf buffer to write in the handler
+ * \param number of bytes to write
+ * \retval count , the number of bytes writen
+ */
+static int WriteFn(void *handler, const char *buf, int size)
+{
+ size_t count = 0;
+ SCFmem *mem = handler;
+ size_t available = mem->size - mem->pos;
+
+ if (size < 0) return - 1;
+
+ if ((size_t)size > available)
+ size = available;
+
+ while (count < (size_t)size)
+ mem->buffer[mem->pos++] = buf[count++];
+
+ return count;
+}
+
+/**
+ * \brief close the mem file handler
+ * \param handler pointer to the memfile
+ * \retval 0 on succesful
+ */
+static int CloseFn(void *handler)
+{
+ SCFree(handler);
+ return 0;
+}
+
+/**
+ * \brief portable version of SCFmemopen for OS X / BSD built on top of funopen()
+ * \param buffer that holds the file content
+ * \param size of the file buffer
+ * \param mode mode of the file to open
+ * \retval pointer to the file; NULL if something is wrong
+ */
+FILE *SCFmemopen(void *buf, size_t size, const char *mode)
+{
+ SCFmem *mem = (SCFmem *) SCMalloc(sizeof(SCFmem));
+ if (mem == NULL)
+ return NULL;
+
+ memset(mem, 0, sizeof(SCFmem));
+ mem->size = size, mem->buffer = buf;
+
+ return funopen(mem, ReadFn, WriteFn, SeekFn, CloseFn);
+}
+
+#endif /* OS_WIN32 */
+
+#endif /* USE_FMEM_WRAPPER */
diff --git a/framework/src/suricata/src/util-fmemopen.h b/framework/src/suricata/src/util-fmemopen.h
new file mode 100644
index 00000000..4b558eaf
--- /dev/null
+++ b/framework/src/suricata/src/util-fmemopen.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * Based on FMem.c of Alexandre Flori (2008/10/17 AF)
+ */
+
+#ifndef __FMEMOPEN_H__
+#define __FMEMOPEN_H__
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Include this file only for OSX / BSD compilations */
+#ifdef OS_DARWIN
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef OS_FREEBSD
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef __OpenBSD__
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef OS_WIN32
+#define USE_FMEM_WRAPPER 1
+#endif
+
+#ifdef USE_FMEM_WRAPPER
+FILE *SCFmemopen(void *, size_t, const char *);
+#else
+/* Else use the normal fmemopen */
+#define SCFmemopen fmemopen
+#endif
+
+#endif /* __FMEMOPEN_H__ */
diff --git a/framework/src/suricata/src/util-hash-lookup3.c b/framework/src/suricata/src/util-hash-lookup3.c
new file mode 100644
index 00000000..f765521d
--- /dev/null
+++ b/framework/src/suricata/src/util-hash-lookup3.c
@@ -0,0 +1,999 @@
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+//#define SELF_TEST 1
+
+#include <stdio.h> /* defines printf for tests */
+#include <time.h> /* defines time_t for timings in the test */
+#include <stdint.h> /* defines uint32_t etc */
+#include <sys/param.h> /* attempt to define endianness */
+#ifdef linux
+# include <endian.h> /* attempt to define endianness */
+#endif
+
+/*
+ * My best guess at if you are big-endian or little-endian. This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN) || \
+ (defined(i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN) || \
+ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines. To be useful, it requires
+ -- that the key be an array of uint32_t's, and
+ -- that the length be the number of uint32_t's in the key
+
+ The function hashword() is identical to hashlittle() on little-endian
+ machines, and identical to hashbig() on big-endian machines,
+ except that the length has to be measured in uint32_ts rather than in
+ bytes. hashlittle() is more complicated than hashword() only because
+ hashlittle() has to dance around fitting the key bytes into registers.
+--------------------------------------------------------------------
+*/
+uint32_t hashword(
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t initval) /* the previous hash, or an arbitrary value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ return c;
+}
+
+
+/*
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values. pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds. If you pass in (*pb)==0, the output
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+void hashword2 (
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t *pc, /* IN: seed OUT: primary hash value */
+uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
+ c += *pb;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ *pc=c; *pb=b;
+}
+
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ const uint8_t *k8 = (const uint8_t *)k;
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+/*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+void hashlittle2(
+ const void *key, /* the key to hash */
+ size_t length, /* length of the key */
+ uint32_t *pc, /* IN: primary initval, OUT: primary hash */
+ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
+ c += *pb;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ const uint8_t *k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+ }
+
+ final(a,b,c);
+ *pc=c; *pb=b;
+}
+
+
+
+/*
+ * hashbig():
+ * This is the same as hashword() on big-endian machines. It is different
+ * from hashlittle() on all machines. hashbig() takes advantage of
+ * big-endian byte ordering.
+ */
+uint32_t hashbig( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c;
+ union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff00; break;
+ case 2 : a+=k[0]&0xffff0000; break;
+ case 1 : a+=k[0]&0xff000000; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ const uint8_t *k8 = (const uint8_t *)k;
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
+ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
+ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
+ case 1 : a+=((uint32_t)k8[0])<<24; break;
+ case 0 : return c;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[11];
+ case 11: c+=((uint32_t)k[10])<<8;
+ case 10: c+=((uint32_t)k[9])<<16;
+ case 9 : c+=((uint32_t)k[8])<<24;
+ case 8 : b+=k[7];
+ case 7 : b+=((uint32_t)k[6])<<8;
+ case 6 : b+=((uint32_t)k[5])<<16;
+ case 5 : b+=((uint32_t)k[4])<<24;
+ case 4 : a+=k[3];
+ case 3 : a+=((uint32_t)k[2])<<8;
+ case 2 : a+=((uint32_t)k[1])<<16;
+ case 1 : a+=((uint32_t)k[0])<<24;
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+#ifdef SELF_TEST
+
+/* used for timings */
+void driver1()
+{
+ uint8_t buf[256];
+ uint32_t i;
+ uint32_t h=0;
+ time_t a,z;
+
+ time(&a);
+ for (i=0; i<256; ++i) buf[i] = 'x';
+ for (i=0; i<1; ++i)
+ {
+ h = hashlittle(&buf[0],1,h);
+ }
+ time(&z);
+ if (z-a > 0) printf("time %d %.8x\n", z-a, h);
+}
+
+/* check that every input bit changes every output bit half the time */
+#define HASHSTATE 1
+#define HASHLEN 1
+#define MAXPAIR 60
+#define MAXLEN 70
+void driver2()
+{
+ uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
+ uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
+ uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
+ uint32_t x[HASHSTATE],y[HASHSTATE];
+ uint32_t hlen;
+
+ printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
+ for (hlen=0; hlen < MAXLEN; ++hlen)
+ {
+ z=0;
+ for (i=0; i<hlen; ++i) /*----------------------- for each input byte, */
+ {
+ for (j=0; j<8; ++j) /*------------------------ for each input bit, */
+ {
+ for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */
+ {
+ for (l=0; l<HASHSTATE; ++l)
+ e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
+
+ /*---- check that every output bit is affected by that input bit */
+ for (k=0; k<MAXPAIR; k+=2)
+ {
+ uint32_t finished=1;
+ /* keys have one bit different */
+ for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
+ /* have a and b be two keys differing in only one bit */
+ a[i] ^= (k<<j);
+ a[i] ^= (k>>(8-j));
+ c[0] = hashlittle(a, hlen, m);
+ b[i] ^= ((k+1)<<j);
+ b[i] ^= ((k+1)>>(8-j));
+ d[0] = hashlittle(b, hlen, m);
+ /* check every bit is 1, 0, set, and not set at least once */
+ for (l=0; l<HASHSTATE; ++l)
+ {
+ e[l] &= (c[l]^d[l]);
+ f[l] &= ~(c[l]^d[l]);
+ g[l] &= c[l];
+ h[l] &= ~c[l];
+ x[l] &= d[l];
+ y[l] &= ~d[l];
+ if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
+ }
+ if (finished) break;
+ }
+ if (k>z) z=k;
+ if (k==MAXPAIR)
+ {
+ printf("Some bit didn't change: ");
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
+ e[0],f[0],g[0],h[0],x[0],y[0]);
+ printf("i %d j %d m %d len %d\n", i, j, m, hlen);
+ }
+ if (z==MAXPAIR) goto done;
+ }
+ }
+ }
+ done:
+ if (z < MAXPAIR)
+ {
+ printf("Mix success %2d bytes %2d initvals ",i,m);
+ printf("required %d trials\n", z/2);
+ }
+ }
+ printf("\n");
+}
+
+/* Check for reading beyond the end of the buffer and alignment problems */
+void driver3()
+{
+ uint8_t buf[MAXLEN+20], *b;
+ uint32_t len;
+ uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
+ uint32_t h;
+ uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
+ uint32_t i;
+ uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
+ uint32_t j;
+ uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
+ uint32_t ref,x,y;
+ uint8_t *p;
+
+ printf("Endianness. These lines should all be the same (for values filled in):\n");
+ printf("%.8x %.8x %.8x\n",
+ hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13));
+ p = q;
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qq[1];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqq[2];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqqq[3];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ printf("\n");
+
+ /* check that hashlittle2 and hashlittle produce the same results */
+ i=47; j=0;
+ hashlittle2(q, sizeof(q), &i, &j);
+ if (hashlittle(q, sizeof(q), 47) != i)
+ printf("hashlittle2 and hashlittle mismatch\n");
+
+ /* check that hashword2 and hashword produce the same results */
+ len = 0xdeadbeef;
+ i=47, j=0;
+ hashword2(&len, 1, &i, &j);
+ if (hashword(&len, 1, 47) != i)
+ printf("hashword2 and hashword mismatch %x %x\n",
+ i, hashword(&len, 1, 47));
+
+ /* check hashlittle doesn't read before or after the ends of the string */
+ for (h=0, b=buf+1; h<8; ++h, ++b)
+ {
+ for (i=0; i<MAXLEN; ++i)
+ {
+ len = i;
+ for (j=0; j<i; ++j) *(b+j)=0;
+
+ /* these should all be equal */
+ ref = hashlittle(b, len, (uint32_t)1);
+ *(b+i)=(uint8_t)~0;
+ *(b-1)=(uint8_t)~0;
+ x = hashlittle(b, len, (uint32_t)1);
+ y = hashlittle(b, len, (uint32_t)1);
+ if ((ref != x) || (ref != y))
+ {
+ printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y,
+ h, i);
+ }
+ }
+ }
+}
+
+/* check for problems with nulls */
+ void driver4()
+{
+ uint8_t buf[1];
+ uint32_t h,i,state[HASHSTATE];
+
+
+ buf[0] = ~0;
+ for (i=0; i<HASHSTATE; ++i) state[i] = 1;
+ printf("These should all be different\n");
+ for (i=0, h=0; i<8; ++i)
+ {
+ h = hashlittle(buf, 0, h);
+ printf("%2ld 0-byte strings, hash is %.8x\n", i, h);
+ }
+}
+
+void driver5()
+{
+ uint32_t b,c;
+ b=0, c=0, hashlittle2("", 0, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* deadbeef deadbeef */
+ b=0xdeadbeef, c=0, hashlittle2("", 0, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* bd5b7dde deadbeef */
+ b=0xdeadbeef, c=0xdeadbeef, hashlittle2("", 0, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* 9c093ccd bd5b7dde */
+ b=0, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* 17770551 ce7226e6 */
+ b=1, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* e3607cae bd371de4 */
+ b=0, c=1, hashlittle2("Four score and seven years ago", 30, &c, &b);
+ printf("hash is %.8lx %.8lx\n", c, b); /* cd628161 6cbea4b3 */
+ c = hashlittle("Four score and seven years ago", 30, 0);
+ printf("hash is %.8lx\n", c); /* 17770551 */
+ c = hashlittle("Four score and seven years ago", 30, 1);
+ printf("hash is %.8lx\n", c); /* cd628161 */
+}
+
+
+int main()
+{
+ driver1(); /* test that the key is hashed: used for timings */
+ driver2(); /* test that whole key is hashed thoroughly */
+ driver3(); /* test that nothing but the key is hashed */
+ driver4(); /* test hashing multiple buffers (all buffers are null) */
+ driver5(); /* test the hash against known vectors */
+ return 1;
+}
+
+#endif /* SELF_TEST */
diff --git a/framework/src/suricata/src/util-hash-lookup3.h b/framework/src/suricata/src/util-hash-lookup3.h
new file mode 100644
index 00000000..1e37e910
--- /dev/null
+++ b/framework/src/suricata/src/util-hash-lookup3.h
@@ -0,0 +1,63 @@
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+
+#ifndef __UTIL_HASH_LOOKUP3_H__
+#define __UTIL_HASH_LOOKUP3_H__
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+uint32_t hashword(const uint32_t *k, /* the key, an array of uint32_t values */
+ size_t length, /* the length of the key, in uint32_ts */
+ uint32_t initval); /* the previous hash, or an arbitrary value */
+
+
+void hashword2 (const uint32_t *k, /* the key, an array of uint32_t values */
+ size_t length, /* the length of the key, in uint32_ts */
+ uint32_t *pc, /* IN: seed OUT: primary hash value */
+ uint32_t *pb); /* IN: more seed OUT: secondary hash value */
+
+uint32_t hashlittle( const void *key, size_t length, uint32_t initval);
+
+void hashlittle2(const void *key, /* the key to hash */
+ size_t length, /* length of the key */
+ uint32_t *pc, /* IN: primary initval, OUT: primary hash */
+ uint32_t *pb); /* IN: secondary initval, OUT: secondary hash */
+
+uint32_t hashbig( const void *key, size_t length, uint32_t initval);
+
+#endif /* __UTIL_HASH_LOOKUP3_H__ */
+
diff --git a/framework/src/suricata/src/util-hash.c b/framework/src/suricata/src/util-hash.c
new file mode 100644
index 00000000..7552867f
--- /dev/null
+++ b/framework/src/suricata/src/util-hash.c
@@ -0,0 +1,432 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Chained hash table implementation
+ *
+ * The 'Free' pointer can be used to have the API free your
+ * hashed data. If it's NULL it's the callers responsebility
+ */
+
+#include "suricata-common.h"
+#include "util-hash.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+
+HashTable* HashTableInit(uint32_t size, uint32_t (*Hash)(struct HashTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *)) {
+
+ HashTable *ht = NULL;
+
+ if (size == 0) {
+ goto error;
+ }
+
+ if (Hash == NULL) {
+ //printf("ERROR: HashTableInit no Hash function\n");
+ goto error;
+ }
+
+ /* setup the filter */
+ ht = SCMalloc(sizeof(HashTable));
+ if (unlikely(ht == NULL))
+ goto error;
+ memset(ht,0,sizeof(HashTable));
+ ht->array_size = size;
+ ht->Hash = Hash;
+ ht->Free = Free;
+
+ if (Compare != NULL)
+ ht->Compare = Compare;
+ else
+ ht->Compare = HashTableDefaultCompare;
+
+ /* setup the bitarray */
+ ht->array = SCMalloc(ht->array_size * sizeof(HashTableBucket *));
+ if (ht->array == NULL)
+ goto error;
+ memset(ht->array,0,ht->array_size * sizeof(HashTableBucket *));
+
+ return ht;
+
+error:
+ if (ht != NULL) {
+ if (ht->array != NULL)
+ SCFree(ht->array);
+
+ SCFree(ht);
+ }
+ return NULL;
+}
+
+void HashTableFree(HashTable *ht)
+{
+ uint32_t i = 0;
+
+ if (ht == NULL)
+ return;
+
+ /* free the buckets */
+ for (i = 0; i < ht->array_size; i++) {
+ HashTableBucket *hashbucket = ht->array[i];
+ while (hashbucket != NULL) {
+ HashTableBucket *next_hashbucket = hashbucket->next;
+ if (ht->Free != NULL)
+ ht->Free(hashbucket->data);
+ SCFree(hashbucket);
+ hashbucket = next_hashbucket;
+ }
+ }
+
+ /* free the arrray */
+ if (ht->array != NULL)
+ SCFree(ht->array);
+
+ SCFree(ht);
+}
+
+void HashTablePrint(HashTable *ht)
+{
+ printf("\n----------- Hash Table Stats ------------\n");
+ printf("Buckets: %" PRIu32 "\n", ht->array_size);
+ printf("Hash function pointer: %p\n", ht->Hash);
+ printf("-----------------------------------------\n");
+}
+
+int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
+{
+ if (ht == NULL || data == NULL)
+ return -1;
+
+ uint32_t hash = ht->Hash(ht, data, datalen);
+
+ HashTableBucket *hb = SCMalloc(sizeof(HashTableBucket));
+ if (unlikely(hb == NULL))
+ goto error;
+ memset(hb, 0, sizeof(HashTableBucket));
+ hb->data = data;
+ hb->size = datalen;
+ hb->next = NULL;
+
+ if (ht->array[hash] == NULL) {
+ ht->array[hash] = hb;
+ } else {
+ hb->next = ht->array[hash];
+ ht->array[hash] = hb;
+ }
+
+#ifdef UNITTESTS
+ ht->count++;
+#endif
+
+ return 0;
+
+error:
+ return -1;
+}
+
+int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
+{
+ uint32_t hash = ht->Hash(ht, data, datalen);
+
+ if (ht->array[hash] == NULL) {
+ return -1;
+ }
+
+ if (ht->array[hash]->next == NULL) {
+ if (ht->Free != NULL)
+ ht->Free(ht->array[hash]->data);
+ SCFree(ht->array[hash]);
+ ht->array[hash] = NULL;
+ return 0;
+ }
+
+ HashTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
+ do {
+ if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
+ if (prev_hashbucket == NULL) {
+ /* root bucket */
+ ht->array[hash] = hashbucket->next;
+ } else {
+ /* child bucket */
+ prev_hashbucket->next = hashbucket->next;
+ }
+
+ /* remove this */
+ if (ht->Free != NULL)
+ ht->Free(hashbucket->data);
+ SCFree(hashbucket);
+ return 0;
+ }
+
+ prev_hashbucket = hashbucket;
+ hashbucket = hashbucket->next;
+ } while (hashbucket != NULL);
+
+ return -1;
+}
+
+void *HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
+{
+ uint32_t hash = 0;
+
+ if (ht == NULL)
+ return NULL;
+
+ hash = ht->Hash(ht, data, datalen);
+
+ if (ht->array[hash] == NULL)
+ return NULL;
+
+ HashTableBucket *hashbucket = ht->array[hash];
+ do {
+ if (ht->Compare(hashbucket->data, hashbucket->size, data, datalen) == 1)
+ return hashbucket->data;
+
+ hashbucket = hashbucket->next;
+ } while (hashbucket != NULL);
+
+ return NULL;
+}
+
+uint32_t HashTableGenericHash(HashTable *ht, void *data, uint16_t datalen)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint32_t i;
+ uint32_t hash = 0;
+
+ for (i = 0; i < datalen; i++) {
+ if (i == 0) hash += (((uint32_t)*d++));
+ else if (i == 1) hash += (((uint32_t)*d++) * datalen);
+ else hash *= (((uint32_t)*d++) * i) + datalen + i;
+ }
+
+ hash *= datalen;
+ hash %= ht->array_size;
+ return hash;
+}
+
+char HashTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ if (len1 != len2)
+ return 0;
+
+ if (SCMemcmp(data1,data2,len1) != 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+static int HashTableTestInit01 (void)
+{
+ HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 0;
+
+ HashTableFree(ht);
+ return 1;
+}
+
+/* no hash function, so it should fail */
+static int HashTableTestInit02 (void)
+{
+ HashTable *ht = HashTableInit(1024, NULL, NULL, NULL);
+ if (ht == NULL)
+ return 1;
+
+ HashTableFree(ht);
+ return 0;
+}
+
+static int HashTableTestInit03 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 0;
+
+ if (ht->Hash == HashTableGenericHash)
+ result = 1;
+
+ HashTableFree(ht);
+ return result;
+}
+
+static int HashTableTestInit04 (void)
+{
+ HashTable *ht = HashTableInit(0, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 1;
+
+ HashTableFree(ht);
+ return 0;
+}
+
+static int HashTableTestInit05 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 0;
+
+ if (ht->Compare == HashTableDefaultCompare)
+ result = 1;
+
+ HashTableFree(ht);
+ return result;
+}
+
+static char HashTableDefaultCompareTest(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ if (len1 != len2)
+ return 0;
+
+ if (SCMemcmp(data1,data2,len1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int HashTableTestInit06 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(1024, HashTableGenericHash, HashTableDefaultCompareTest, NULL);
+ if (ht == NULL)
+ return 0;
+
+ if (ht->Compare == HashTableDefaultCompareTest)
+ result = 1;
+
+ HashTableFree(ht);
+ return result;
+}
+
+static int HashTableTestAdd01 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashTableAdd(ht, "test", 0);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashTableFree(ht);
+ return result;
+}
+
+static int HashTableTestAdd02 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashTableAdd(ht, NULL, 4);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashTableFree(ht);
+ return result;
+}
+
+static int HashTableTestFull01 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashTableAdd(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ char *rp = HashTableLookup(ht, "test", 4);
+ if (rp == NULL)
+ goto end;
+
+ r = HashTableRemove(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashTableFree(ht);
+ return result;
+}
+
+static int HashTableTestFull02 (void)
+{
+ int result = 0;
+ HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashTableAdd(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ char *rp = HashTableLookup(ht, "test", 4);
+ if (rp == NULL)
+ goto end;
+
+ r = HashTableRemove(ht, "test2", 5);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashTableFree(ht);
+ return result;
+}
+#endif
+
+void HashTableRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HashTableTestInit01", HashTableTestInit01, 1);
+ UtRegisterTest("HashTableTestInit02", HashTableTestInit02, 1);
+ UtRegisterTest("HashTableTestInit03", HashTableTestInit03, 1);
+ UtRegisterTest("HashTableTestInit04", HashTableTestInit04, 1);
+ UtRegisterTest("HashTableTestInit05", HashTableTestInit05, 1);
+ UtRegisterTest("HashTableTestInit06", HashTableTestInit06, 1);
+
+ UtRegisterTest("HashTableTestAdd01", HashTableTestAdd01, 1);
+ UtRegisterTest("HashTableTestAdd02", HashTableTestAdd02, 1);
+
+ UtRegisterTest("HashTableTestFull01", HashTableTestFull01, 1);
+ UtRegisterTest("HashTableTestFull02", HashTableTestFull02, 1);
+#endif
+}
+
diff --git a/framework/src/suricata/src/util-hash.h b/framework/src/suricata/src/util-hash.h
new file mode 100644
index 00000000..f1611188
--- /dev/null
+++ b/framework/src/suricata/src/util-hash.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HASH_H__
+#define __HASH_H__
+
+/* hash bucket structure */
+typedef struct HashTableBucket_ {
+ void *data;
+ uint16_t size;
+ struct HashTableBucket_ *next;
+} HashTableBucket;
+
+/* hash table structure */
+typedef struct HashTable_ {
+ HashTableBucket **array;
+ uint32_t array_size;
+#ifdef UNITTESTS
+ uint32_t count;
+#endif
+ uint32_t (*Hash)(struct HashTable_ *, void *, uint16_t);
+ char (*Compare)(void *, uint16_t, void *, uint16_t);
+ void (*Free)(void *);
+} HashTable;
+
+#define HASH_NO_SIZE 0
+
+/* prototypes */
+HashTable* HashTableInit(uint32_t, uint32_t (*Hash)(struct HashTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *));
+void HashTableFree(HashTable *);
+void HashTablePrint(HashTable *);
+int HashTableAdd(HashTable *, void *, uint16_t);
+int HashTableRemove(HashTable *, void *, uint16_t);
+void *HashTableLookup(HashTable *, void *, uint16_t);
+uint32_t HashTableGenericHash(HashTable *, void *, uint16_t);
+char HashTableDefaultCompare(void *, uint16_t, void *, uint16_t);
+
+void HashTableRegisterTests(void);
+
+#endif /* __HASH_H__ */
+
diff --git a/framework/src/suricata/src/util-hashlist.c b/framework/src/suricata/src/util-hashlist.c
new file mode 100644
index 00000000..db8ba905
--- /dev/null
+++ b/framework/src/suricata/src/util-hashlist.c
@@ -0,0 +1,518 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Chained hash table implementation
+ *
+ * The 'Free' pointer can be used to have the API free your
+ * hashed data. If it's NULL it's the callers responsebility
+ */
+
+#include "suricata-common.h"
+#include "util-hashlist.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-memcmp.h"
+
+HashListTable* HashListTableInit(uint32_t size, uint32_t (*Hash)(struct HashListTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *)) {
+
+ HashListTable *ht = NULL;
+
+ if (size == 0) {
+ goto error;
+ }
+
+ if (Hash == NULL) {
+ //printf("ERROR: HashListTableInit no Hash function\n");
+ goto error;
+ }
+
+ /* setup the filter */
+ ht = SCMalloc(sizeof(HashListTable));
+ if (unlikely(ht == NULL))
+ goto error;
+ memset(ht,0,sizeof(HashListTable));
+ ht->array_size = size;
+ ht->Hash = Hash;
+ ht->Free = Free;
+
+ if (Compare != NULL)
+ ht->Compare = Compare;
+ else
+ ht->Compare = HashListTableDefaultCompare;
+
+ /* setup the bitarray */
+ ht->array = SCMalloc(ht->array_size * sizeof(HashListTableBucket *));
+ if (ht->array == NULL)
+ goto error;
+ memset(ht->array,0,ht->array_size * sizeof(HashListTableBucket *));
+
+ ht->listhead = NULL;
+ ht->listtail = NULL;
+ return ht;
+
+error:
+ if (ht != NULL) {
+ if (ht->array != NULL)
+ SCFree(ht->array);
+
+ SCFree(ht);
+ }
+ return NULL;
+}
+
+void HashListTableFree(HashListTable *ht)
+{
+ uint32_t i = 0;
+
+ if (ht == NULL)
+ return;
+
+ /* free the buckets */
+ for (i = 0; i < ht->array_size; i++) {
+ HashListTableBucket *hashbucket = ht->array[i];
+ while (hashbucket != NULL) {
+ HashListTableBucket *next_hashbucket = hashbucket->bucknext;
+ if (ht->Free != NULL)
+ ht->Free(hashbucket->data);
+ SCFree(hashbucket);
+ hashbucket = next_hashbucket;
+ }
+ }
+
+ /* free the array */
+ if (ht->array != NULL)
+ SCFree(ht->array);
+
+ SCFree(ht);
+}
+
+void HashListTablePrint(HashListTable *ht)
+{
+ printf("\n----------- Hash Table Stats ------------\n");
+ printf("Buckets: %" PRIu32 "\n", ht->array_size);
+ printf("Hash function pointer: %p\n", ht->Hash);
+ printf("-----------------------------------------\n");
+}
+
+int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
+{
+ if (ht == NULL || data == NULL)
+ return -1;
+
+ uint32_t hash = ht->Hash(ht, data, datalen);
+
+ SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
+
+ HashListTableBucket *hb = SCMalloc(sizeof(HashListTableBucket));
+ if (unlikely(hb == NULL))
+ goto error;
+ memset(hb, 0, sizeof(HashListTableBucket));
+ hb->data = data;
+ hb->size = datalen;
+ hb->bucknext = NULL;
+ hb->listnext = NULL;
+ hb->listprev = NULL;
+
+ if (ht->array[hash] == NULL) {
+ ht->array[hash] = hb;
+ } else {
+ hb->bucknext = ht->array[hash];
+ ht->array[hash] = hb;
+ }
+
+ if (ht->listtail == NULL) {
+ ht->listhead = hb;
+ ht->listtail = hb;
+ } else {
+ hb->listprev = ht->listtail;
+ ht->listtail->listnext = hb;
+ ht->listtail = hb;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+int HashListTableRemove(HashListTable *ht, void *data, uint16_t datalen)
+{
+ uint32_t hash = ht->Hash(ht, data, datalen);
+
+ SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
+
+ if (ht->array[hash] == NULL) {
+ SCLogDebug("ht->array[hash] NULL");
+ return -1;
+ }
+
+ /* fast track for just one data part */
+ if (ht->array[hash]->bucknext == NULL) {
+ HashListTableBucket *hb = ht->array[hash];
+
+ if (ht->Compare(hb->data,hb->size,data,datalen) == 1) {
+ /* remove from the list */
+ if (hb->listprev == NULL) {
+ ht->listhead = hb->listnext;
+ } else {
+ hb->listprev->listnext = hb->listnext;
+ }
+ if (hb->listnext == NULL) {
+ ht->listtail = hb->listprev;
+ } else {
+ hb->listnext->listprev = hb->listprev;
+ }
+
+ if (ht->Free != NULL)
+ ht->Free(hb->data);
+
+ SCFree(ht->array[hash]);
+ ht->array[hash] = NULL;
+ return 0;
+ }
+
+ SCLogDebug("fast track default case");
+ return -1;
+ }
+
+ /* more data in this bucket */
+ HashListTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
+ do {
+ if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
+
+ /* remove from the list */
+ if (hashbucket->listprev == NULL) {
+ ht->listhead = hashbucket->listnext;
+ } else {
+ hashbucket->listprev->listnext = hashbucket->listnext;
+ }
+ if (hashbucket->listnext == NULL) {
+ ht->listtail = hashbucket->listprev;
+ } else {
+ hashbucket->listnext->listprev = hashbucket->listprev;
+ }
+
+ if (prev_hashbucket == NULL) {
+ /* root bucket */
+ ht->array[hash] = hashbucket->bucknext;
+ } else {
+ /* child bucket */
+ prev_hashbucket->bucknext = hashbucket->bucknext;
+ }
+
+ /* remove this */
+ if (ht->Free != NULL)
+ ht->Free(hashbucket->data);
+ SCFree(hashbucket);
+ return 0;
+ }
+
+ prev_hashbucket = hashbucket;
+ hashbucket = hashbucket->bucknext;
+ } while (hashbucket != NULL);
+
+ SCLogDebug("slow track default case");
+ return -1;
+}
+
+char HashListTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ if (len1 != len2)
+ return 0;
+
+ if (SCMemcmp(data1,data2,len1) != 0)
+ return 0;
+
+ return 1;
+}
+
+void *HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
+{
+
+ if (ht == NULL) {
+ SCLogDebug("Hash List table is NULL");
+ return NULL;
+ }
+
+ uint32_t hash = ht->Hash(ht, data, datalen);
+
+ if (ht->array[hash] == NULL) {
+ return NULL;
+ }
+
+ HashListTableBucket *hashbucket = ht->array[hash];
+ do {
+ if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1)
+ return hashbucket->data;
+
+ hashbucket = hashbucket->bucknext;
+ } while (hashbucket != NULL);
+
+ return NULL;
+}
+
+uint32_t HashListTableGenericHash(HashListTable *ht, void *data, uint16_t datalen)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint32_t i;
+ uint32_t hash = 0;
+
+ for (i = 0; i < datalen; i++) {
+ if (i == 0) hash += (((uint32_t)*d++));
+ else if (i == 1) hash += (((uint32_t)*d++) * datalen);
+ else hash *= (((uint32_t)*d++) * i) + datalen + i;
+ }
+
+ hash *= datalen;
+ hash %= ht->array_size;
+ return hash;
+}
+
+HashListTableBucket *HashListTableGetListHead(HashListTable *ht)
+{
+ return ht->listhead;
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+static int HashListTableTestInit01 (void)
+{
+ HashListTable *ht = HashListTableInit(1024, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 0;
+
+ HashListTableFree(ht);
+ return 1;
+}
+
+/* no hash function, so it should fail */
+static int HashListTableTestInit02 (void)
+{
+ HashListTable *ht = HashListTableInit(1024, NULL, NULL, NULL);
+ if (ht == NULL)
+ return 1;
+
+ HashListTableFree(ht);
+ return 0;
+}
+
+static int HashListTableTestInit03 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(1024, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 0;
+
+ if (ht->Hash == HashListTableGenericHash)
+ result = 1;
+
+ HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestInit04 (void)
+{
+ HashListTable *ht = HashListTableInit(0, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ return 1;
+
+ HashListTableFree(ht);
+ return 0;
+}
+
+static int HashListTableTestAdd01 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, "test", 0);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestAdd02 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, NULL, 4);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestAdd03 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, "test", 0);
+ if (r != 0)
+ goto end;
+
+ if (ht->listhead == NULL) {
+ printf("ht->listhead == NULL: ");
+ goto end;
+ }
+
+ if (ht->listtail == NULL) {
+ printf("ht->listtail == NULL: ");
+ goto end;
+ }
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestAdd04 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ char *rp = HashListTableLookup(ht, "test", 4);
+ if (rp == NULL)
+ goto end;
+
+ HashListTableBucket *htb = HashListTableGetListHead(ht);
+ if (htb == NULL) {
+ printf("htb == NULL: ");
+ goto end;
+ }
+
+ char *rp2 = HashListTableGetListData(htb);
+ if (rp2 == NULL) {
+ printf("rp2 == NULL: ");
+ goto end;
+ }
+
+ if (rp != rp2) {
+ printf("rp != rp2: ");
+ goto end;
+ }
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestFull01 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ char *rp = HashListTableLookup(ht, "test", 4);
+ if (rp == NULL)
+ goto end;
+
+ r = HashListTableRemove(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+
+static int HashListTableTestFull02 (void)
+{
+ int result = 0;
+ HashListTable *ht = HashListTableInit(32, HashListTableGenericHash, NULL, NULL);
+ if (ht == NULL)
+ goto end;
+
+ int r = HashListTableAdd(ht, "test", 4);
+ if (r != 0)
+ goto end;
+
+ char *rp = HashListTableLookup(ht, "test", 4);
+ if (rp == NULL)
+ goto end;
+
+ r = HashListTableRemove(ht, "test2", 5);
+ if (r == 0)
+ goto end;
+
+ /* all is good! */
+ result = 1;
+end:
+ if (ht != NULL) HashListTableFree(ht);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void HashListTableRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("HashListTableTestInit01", HashListTableTestInit01, 1);
+ UtRegisterTest("HashListTableTestInit02", HashListTableTestInit02, 1);
+ UtRegisterTest("HashListTableTestInit03", HashListTableTestInit03, 1);
+ UtRegisterTest("HashListTableTestInit04", HashListTableTestInit04, 1);
+
+ UtRegisterTest("HashListTableTestAdd01", HashListTableTestAdd01, 1);
+ UtRegisterTest("HashListTableTestAdd02", HashListTableTestAdd02, 1);
+ UtRegisterTest("HashListTableTestAdd03", HashListTableTestAdd03, 1);
+ UtRegisterTest("HashListTableTestAdd04", HashListTableTestAdd04, 1);
+
+ UtRegisterTest("HashListTableTestFull01", HashListTableTestFull01, 1);
+ UtRegisterTest("HashListTableTestFull02", HashListTableTestFull02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-hashlist.h b/framework/src/suricata/src/util-hashlist.h
new file mode 100644
index 00000000..f75fee36
--- /dev/null
+++ b/framework/src/suricata/src/util-hashlist.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __HASHLIST_H__
+#define __HASHLIST_H__
+
+/* hash bucket structure */
+typedef struct HashListTableBucket_ {
+ void *data;
+ uint16_t size;
+ struct HashListTableBucket_ *bucknext;
+ struct HashListTableBucket_ *listnext;
+ struct HashListTableBucket_ *listprev;
+} HashListTableBucket;
+
+/* hash table structure */
+typedef struct HashListTable_ {
+ HashListTableBucket **array;
+ HashListTableBucket *listhead;
+ HashListTableBucket *listtail;
+ uint32_t array_size;
+ uint32_t (*Hash)(struct HashListTable_ *, void *, uint16_t);
+ char (*Compare)(void *, uint16_t, void *, uint16_t);
+ void (*Free)(void *);
+} HashListTable;
+
+#define HASHLIST_NO_SIZE 0
+
+/* prototypes */
+HashListTable* HashListTableInit(uint32_t, uint32_t (*Hash)(struct HashListTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *));
+void HashListTableFree(HashListTable *);
+void HashListTablePrint(HashListTable *);
+int HashListTableAdd(HashListTable *, void *, uint16_t);
+int HashListTableRemove(HashListTable *, void *, uint16_t);
+void *HashListTableLookup(HashListTable *, void *, uint16_t);
+uint32_t HashListTableGenericHash(HashListTable *, void *, uint16_t);
+HashListTableBucket *HashListTableGetListHead(HashListTable *);
+#define HashListTableGetListNext(hb) (hb)->listnext
+#define HashListTableGetListData(hb) (hb)->data
+char HashListTableDefaultCompare(void *, uint16_t, void *, uint16_t);
+
+void HashListTableRegisterTests(void);
+
+#endif /* __HASHLIST_H__ */
+
diff --git a/framework/src/suricata/src/util-host-info.c b/framework/src/suricata/src/util-host-info.c
new file mode 100644
index 00000000..c7ea8adf
--- /dev/null
+++ b/framework/src/suricata/src/util-host-info.c
@@ -0,0 +1,106 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Get information on running host
+ *
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+
+#ifndef OS_WIN32
+#include <sys/utsname.h>
+
+#define VERSION_REGEX "^([0-9]+)\\.([0-9]+)"
+
+int SCKernelVersionIsAtLeast(int major, int minor)
+{
+ struct utsname kuname;
+ pcre *version_regex;
+ pcre_extra *version_regex_study;
+ const char *eb;
+ int opts = 0;
+ int eo;
+#define MAX_SUBSTRINGS 3 * 6
+ int ov[MAX_SUBSTRINGS];
+ int ret;
+ int kmajor, kminor;
+ const char **list;
+
+ /* get local version */
+ if (uname(&kuname) != 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid uname return: %s",
+ strerror(errno));
+ return 0;
+ }
+
+ SCLogDebug("Kernel release is '%s'", kuname.release);
+
+ version_regex = pcre_compile(VERSION_REGEX, opts, &eb, &eo, NULL);
+ if (version_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", VERSION_REGEX, eo, eb);
+ goto error;
+ }
+
+ version_regex_study = pcre_study(version_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ ret = pcre_exec(version_regex, version_regex_study, kuname.release,
+ strlen(kuname.release), 0, 0, ov, MAX_SUBSTRINGS);
+
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Version did not cut");
+ goto error;
+ }
+
+ if (ret < 3) {
+ SCLogError(SC_ERR_PCRE_MATCH, "Version major and minor not found (ret %d)", ret);
+ goto error;
+ }
+
+ pcre_get_substring_list(kuname.release, ov, ret, &list);
+
+ kmajor = atoi(list[1]);
+ kminor = atoi(list[2]);
+
+ pcre_free_substring_list(list);
+
+ if (kmajor > major)
+ return 1;
+ if (kmajor == major && kminor >= minor)
+ return 1;
+error:
+ return 0;
+}
+
+#else /* OS_WIN32 */
+
+int SCKernelVersionIsAtLeast(int major, int minor)
+{
+ SCLogError(SC_ERR_NOT_SUPPORTED, "OS compare is not supported on Windows");
+ return 0;
+}
+
+#endif /* OS_WIN32 */
diff --git a/framework/src/suricata/src/util-host-info.h b/framework/src/suricata/src/util-host-info.h
new file mode 100644
index 00000000..b3a5e47a
--- /dev/null
+++ b/framework/src/suricata/src/util-host-info.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_HOST_INFO_H__
+#define __UTIL_HOST_INFO_H__
+
+int SCKernelVersionIsAtLeast(int major, int minor);
+
+#endif /* __UTIL_HOST_INFO_H__ */
diff --git a/framework/src/suricata/src/util-host-os-info.c b/framework/src/suricata/src/util-host-os-info.c
new file mode 100644
index 00000000..ad0de7e2
--- /dev/null
+++ b/framework/src/suricata/src/util-host-os-info.c
@@ -0,0 +1,1656 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Host info utility functions
+ */
+
+#include "suricata-common.h"
+#include "util-host-os-info.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-ip.h"
+#include "util-radix-tree.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "util-enum.h"
+#include "util-unittest.h"
+
+/** Enum map for the various OS flavours */
+SCEnumCharMap sc_hinfo_os_policy_map[ ] = {
+ { "none", OS_POLICY_NONE },
+ { "bsd", OS_POLICY_BSD },
+ { "bsd-right", OS_POLICY_BSD_RIGHT },
+ { "old-linux", OS_POLICY_OLD_LINUX },
+ { "linux", OS_POLICY_LINUX },
+ { "old-solaris", OS_POLICY_OLD_SOLARIS },
+ { "solaris", OS_POLICY_SOLARIS },
+ { "hpux10", OS_POLICY_HPUX10 },
+ { "hpux11", OS_POLICY_HPUX11 },
+ { "irix", OS_POLICY_IRIX },
+ { "macos", OS_POLICY_MACOS },
+ { "windows", OS_POLICY_WINDOWS },
+ { "vista", OS_POLICY_VISTA },
+ { "windows2k3", OS_POLICY_WINDOWS2K3 },
+ { NULL, -1 },
+};
+
+/** Radix tree that holds the host OS information */
+static SCRadixTree *sc_hinfo_tree = NULL;
+
+
+/**
+ * \brief Allocates the host_os flavour wrapped in user_data variable to be sent
+ * along with the key to the radix tree
+ *
+ * \param host_os Pointer to a character string containing the host_os flavour
+ *
+ * \retval user_data On success, pointer to the user_data that has to be sent
+ * along with the key, to be added to the Radix tree; NULL on
+ * failure
+ * \initonly
+ */
+static void *SCHInfoAllocUserDataOSPolicy(const char *host_os)
+{
+ int *user_data = NULL;
+
+ if ( (user_data = SCMalloc(sizeof(int))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error allocating memory. Exiting");
+ exit(EXIT_FAILURE);
+ }
+
+ /* the host os flavour that has to be sent as user data */
+ if ( (*user_data = SCMapEnumNameToValue(host_os, sc_hinfo_os_policy_map)) == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "Invalid enum map inside "
+ "SCHInfoAddHostOSInfo()");
+ SCFree(user_data);
+ return NULL;
+ }
+
+ return (void *)user_data;
+}
+
+/**
+ * \brief Used to free the user data that is allocated by host_os_info API
+ *
+ * \param Pointer to the data that has to be freed
+ */
+static void SCHInfoFreeUserDataOSPolicy(void *data)
+{
+ if (data != NULL)
+ SCFree(data);
+
+ return;
+}
+
+/**
+ * \brief Used to add the host-os-info data obtained from the conf
+ *
+ * \param host_os The host_os name/flavour from the conf file
+ * \param host_os_ip_range Pointer to a char string holding the ip/ip_netblock
+ * for the host_os specified in the first argument
+ * \param is_ipv4 Indicates if the ip address to be considered for the
+ * default configuration is IPV4; if not it is IPV6.
+ * Specified using SC_HINFO_IS_IPV6 or SC_HINFO_IS_IPV4
+ *
+ * \retval 0 On successfully adding the host os info to the Radix tree
+ * \retval -1 On failure
+ * \initonly (only specified from config, at the startup)
+ */
+int SCHInfoAddHostOSInfo(char *host_os, char *host_os_ip_range, int is_ipv4)
+{
+ char *ip_str = NULL;
+ char *ip_str_rem = NULL;
+ struct in_addr *ipv4_addr = NULL;
+ struct in6_addr *ipv6_addr = NULL;
+ char *netmask_str = NULL;
+ int netmask_value = 0;
+ int *user_data = NULL;
+ char recursive = FALSE;
+
+ if (host_os == NULL || host_os_ip_range == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid arguments");
+ return -1;
+ }
+
+ /* create the radix tree that would hold all the host os info */
+ if (sc_hinfo_tree == NULL)
+ sc_hinfo_tree = SCRadixCreateRadixTree(SCHInfoFreeUserDataOSPolicy, NULL);
+
+ /* the host os flavour that has to be sent as user data */
+ if ( (user_data = SCHInfoAllocUserDataOSPolicy(host_os)) == NULL) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "Invalid enum map inside");
+ return -1;
+ }
+
+ /* if we have a default configuration set the appropriate values for the
+ * netblocks */
+ if ( (strcasecmp(host_os_ip_range, "default")) == 0) {
+ if (is_ipv4)
+ host_os_ip_range = "0.0.0.0/0";
+ else
+ host_os_ip_range = "::/0";
+ }
+
+ if ( (ip_str = SCStrdup(host_os_ip_range)) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+
+ /* check if we have more addresses in the host_os_ip_range */
+ if ((ip_str_rem = index(ip_str, ',')) != NULL) {
+ ip_str_rem[0] = '\0';
+ ip_str_rem++;
+ recursive = TRUE;
+ }
+
+ /* check if we have received a netblock */
+ if ( (netmask_str = index(ip_str, '/')) != NULL) {
+ netmask_str[0] = '\0';
+ netmask_str++;
+ }
+
+ if (index(ip_str, ':') == NULL) {
+ /* if we are here, we have an IPV4 address */
+ if ( (ipv4_addr = ValidateIPV4Address(ip_str)) == NULL) {
+ SCLogError(SC_ERR_INVALID_IPV4_ADDR, "Invalid IPV4 address");
+ SCHInfoFreeUserDataOSPolicy(user_data);
+ SCFree(ip_str);
+ return -1;
+ }
+
+ if (netmask_str == NULL) {
+ SCRadixAddKeyIPV4((uint8_t *)ipv4_addr, sc_hinfo_tree,
+ (void *)user_data);
+ } else {
+ netmask_value = atoi(netmask_str);
+ if (netmask_value < 0 || netmask_value > 32) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Invalid IPV4 Netblock");
+ SCHInfoFreeUserDataOSPolicy(user_data);
+ SCFree(ipv4_addr);
+ SCFree(ip_str);
+ return -1;
+ }
+
+ MaskIPNetblock((uint8_t *)ipv4_addr, netmask_value, 32);
+ SCRadixAddKeyIPV4Netblock((uint8_t *)ipv4_addr, sc_hinfo_tree,
+ (void *)user_data, netmask_value);
+ }
+ } else {
+ /* if we are here, we have an IPV6 address */
+ if ( (ipv6_addr = ValidateIPV6Address(ip_str)) == NULL) {
+ SCLogError(SC_ERR_INVALID_IPV6_ADDR, "Invalid IPV6 address inside");
+ SCHInfoFreeUserDataOSPolicy(user_data);
+ SCFree(ip_str);
+ return -1;
+ }
+
+ if (netmask_str == NULL) {
+ SCRadixAddKeyIPV6((uint8_t *)ipv6_addr, sc_hinfo_tree,
+ (void *)user_data);
+ } else {
+ netmask_value = atoi(netmask_str);
+ if (netmask_value < 0 || netmask_value > 128) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Invalid IPV6 Netblock");
+ SCHInfoFreeUserDataOSPolicy(user_data);
+ SCFree(ipv6_addr);
+ SCFree(ip_str);
+ return -1;
+ }
+
+ MaskIPNetblock((uint8_t *)ipv6_addr, netmask_value, 128);
+ SCRadixAddKeyIPV6Netblock((uint8_t *)ipv6_addr, sc_hinfo_tree,
+ (void *)user_data, netmask_value);
+ }
+ }
+
+ if (recursive == TRUE) {
+ SCHInfoAddHostOSInfo(host_os, ip_str_rem, is_ipv4);
+ }
+
+ if (ip_str != NULL)
+ SCFree(ip_str);
+ if (ipv4_addr != NULL)
+ SCFree(ipv4_addr);
+ if (ipv6_addr != NULL)
+ SCFree(ipv6_addr);
+ return *user_data;
+}
+
+/**
+ * \brief Retrieves the host os flavour, given an ipv4/ipv6 address as a string.
+ *
+ * \param Pointer to a string containing an IP address
+ *
+ * \retval The OS flavour on success; -1 on failure, or on not finding the key
+ */
+int SCHInfoGetHostOSFlavour(char *ip_addr_str)
+{
+ struct in_addr *ipv4_addr = NULL;
+ struct in6_addr *ipv6_addr = NULL;
+ void *user_data = NULL;
+
+ if (ip_addr_str == NULL || index(ip_addr_str, '/') != NULL)
+ return -1;
+
+ if (index(ip_addr_str, ':') != NULL) {
+ if ( (ipv6_addr = ValidateIPV6Address(ip_addr_str)) == NULL) {
+ SCLogError(SC_ERR_INVALID_IPV4_ADDR, "Invalid IPV4 address");
+ return -1;
+ }
+
+ (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)ipv6_addr, sc_hinfo_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+ else
+ return *((int *)user_data);
+ } else {
+ if ( (ipv4_addr = ValidateIPV4Address(ip_addr_str)) == NULL) {
+ SCLogError(SC_ERR_INVALID_IPV4_ADDR, "Invalid IPV4 address");
+ return -1;
+ }
+
+ (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)ipv4_addr, sc_hinfo_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+ else
+ return *((int *)user_data);
+ }
+}
+
+/**
+ * \brief Retrieves the host os flavour, given an ipv4 address in the raw
+ * address format.
+ *
+ * \param Pointer to a raw ipv4 address.
+ *
+ * \retval The OS flavour on success; -1 on failure, or on not finding the key
+ */
+int SCHInfoGetIPv4HostOSFlavour(uint8_t *ipv4_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV4BestMatch(ipv4_addr, sc_hinfo_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+ else
+ return *((int *)user_data);
+}
+
+/**
+ * \brief Retrieves the host os flavour, given an ipv6 address in the raw
+ * address format.
+ *
+ * \param Pointer to a raw ipv6 address.
+ *
+ * \retval The OS flavour on success; -1 on failure, or on not finding the key
+ */
+int SCHInfoGetIPv6HostOSFlavour(uint8_t *ipv6_addr)
+{
+ void *user_data = NULL;
+ (void)SCRadixFindKeyIPV6BestMatch(ipv6_addr, sc_hinfo_tree, &user_data);
+ if (user_data == NULL)
+ return -1;
+ else
+ return *((int *)user_data);
+}
+
+void SCHInfoCleanResources(void)
+{
+ if (sc_hinfo_tree != NULL) {
+ SCRadixReleaseRadixTree(sc_hinfo_tree);
+ sc_hinfo_tree = NULL;
+ }
+
+ return;
+}
+
+/**
+ * \brief Load the host os policy information from the configuration.
+ *
+ * \initonly (A mem alloc error should cause an exit failure)
+ */
+void SCHInfoLoadFromConfig(void)
+{
+ ConfNode *root = ConfGetNode("host-os-policy");
+ if (root == NULL)
+ return;
+
+ ConfNode *policy;
+ TAILQ_FOREACH(policy, &root->head, next) {
+ ConfNode *host;
+ TAILQ_FOREACH(host, &policy->head, next) {
+ int is_ipv4 = 1;
+ if (host->val != NULL && index(host->val, ':') != NULL)
+ is_ipv4 = 0;
+ if (SCHInfoAddHostOSInfo(policy->name, host->val, is_ipv4) == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to add host \"%s\" with policy \"%s\" to host "
+ "info database", host->val, policy->name);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+}
+
+/*------------------------------------Unit_Tests------------------------------*/
+
+#ifdef UNITTESTS
+static SCRadixTree *sc_hinfo_tree_backup = NULL;
+
+static void SCHInfoCreateContextBackup(void)
+{
+ sc_hinfo_tree_backup = sc_hinfo_tree;
+ sc_hinfo_tree = NULL;
+
+ return;
+}
+
+static void SCHInfoRestoreContextBackup(void)
+{
+ sc_hinfo_tree = sc_hinfo_tree_backup;
+ sc_hinfo_tree_backup = NULL;
+
+ return;
+}
+
+/**
+ * \test Check if we the IPs with the right OS flavours are added to the host OS
+ * radix tree, and the IPS with invalid flavours returns an error(-1)
+ */
+int SCHInfoTestInvalidOSFlavour01(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 0;
+
+ if (SCHInfoAddHostOSInfo("bamboo", "192.168.1.1", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("irix", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("bsd", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("bsd", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("old_linux", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("old_linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("macos", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("macos", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows2k3", "192.168.1.1", SC_HINFO_IS_IPV4) !=
+ SCMapEnumNameToValue("windows2k3", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that invalid ipv4 addresses and ipv4 netblocks are rejected by
+ * the host os info API
+ */
+int SCHInfoTestInvalidIPV4Address02(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.566", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1", SC_HINFO_IS_IPV4 != -1)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.168", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.1/33", SC_HINFO_IS_IPV4) != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that invalid ipv4 addresses and ipv4 netblocks are rejected by
+ * the host os info API
+ */
+int SCHInfoTestInvalidIPV6Address03(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux", "2362:7322", SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "19YW:", SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "1235", SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "1922:236115:", SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "", SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "1921.6311:6241:6422:7352:ABBB:DDDD:EEEE/129",
+ SC_HINFO_IS_IPV6) != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv4 addresses are inserted into the host os radix
+ * tree, and the host os api retrieves the right value for the host os
+ * flavour, on supplying as arg an ipv4 addresses that has been added to
+ * the host os radix tree.
+ */
+int SCHInfoTestValidIPV4Address04(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.1", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "192.192.1.2", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "192.168.1.100", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10", "192.168.2.4", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.192.1.5", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "192.168.10.20", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "111.163.151.62", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "11.1.120.210", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "19.18.110.210", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "19.18.120.110", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11", "191.168.11.128", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "191.168.11.192", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.1") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.2") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.192.2.4") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.2.4") !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.192.1.5") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.10.20") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.163.151.62") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("11.1.120.210") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("19.18.110.210") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("19.18.120.110") !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("191.168.11.128") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("191.168.11.192") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("191.168.11.224") != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv4 addresses/netblocks are inserted into the host os
+ * radix tree, and the host os api retrieves the right value for the host
+ * os flavour, on supplying as arg an ipv4 addresses that has been added
+ * to the host os radix tree.
+ */
+int SCHInfoTestValidIPV4Address05(void)
+{
+ SCHInfoCreateContextBackup();
+
+ struct in_addr in;
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.1", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "192.192.1.2", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "192.168.1.100", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10", "192.168.2.4", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "192.192.1.5", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "192.168.10.20", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "111.163.151.62", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11", "111.162.208.124/20", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "111.162.240.1", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "111.162.214.100", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "111.162.208.100", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux", "111.162.194.112", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.1") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.2") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.192.2.4") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.2.4") !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.192.1.5") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.10.20") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.163.151.62") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.208.0") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.210.1") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.214.1") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.0.0") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.240.112") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.240.1") !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.214.100") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (inet_pton(AF_INET, "111.162.208.100", &in) < 0) {
+ goto end;
+ }
+ if (SCHInfoGetIPv4HostOSFlavour((uint8_t *)&in) !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.194.112") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.208.200") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (inet_pton(AF_INET, "111.162.208.200", &in) < 0) {
+ goto end;
+ }
+ if (SCHInfoGetIPv4HostOSFlavour((uint8_t *)&in) !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("111.162.200.201") != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv6 addresses are inserted into the host os radix
+ * tree, and the host os api retrieves the right value for the host os
+ * flavour, on supplying as arg an ipv6 address that has been added to
+ * the host os radix tree.
+ */
+int SCHInfoTestValidIPV6Address06(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux",
+ "2351:2512:6211:6246:235A:6242:2352:62AD",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "6961:6121:2132:6241:423A:2135:2461:621D",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "DD13:613D:F312:62DD:6213:421A:6212:2652",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10",
+ "9891:2131:2151:6426:1342:674D:622F:2342",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "3525:2351:4223:6211:2311:2667:6242:2154",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "1511:6211:6726:7777:1212:2333:6222:7722",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "2666:6222:7222:2335:6223:7722:3425:2362",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "8762:2352:6241:7245:EE23:21AD:2312:622C",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "6422:EE1A:2621:34AD:2462:432D:642E:E13A",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "3521:7622:6241:6242:7277:1234:2352:6234",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11",
+ "2141:6232:6252:2223:7734:2345:6245:6222",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "5222:6432:6432:2322:6662:3423:4322:3245",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:62AD") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:6FFFE") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2652") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2222") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("9891:2131:2151:6426:1342:674D:622F:2342") !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("3525:2351:4223:6211:2311:2667:6242:2154") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("1511:6211:6726:7777:1212:2333:6222:7722") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2666:6222:7222:2335:6223:7722:3425:2362") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:622C") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("6422:EE1A:2621:34AD:2462:432D:642E:E13A") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("3521:7622:6241:6242:7277:1234:2352:6234") !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2141:6232:6252:2223:7734:2345:6245:6222") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("5222:6432:6432:2322:6662:3423:4322:3245") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("5222:6432:6432:2322:6662:3423:4322:DDDD") != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv6 addresses/netblocks are inserted into the host os
+ * radix tree, and the host os api retrieves the right value for the host
+ * os flavour, on supplying as arg an ipv6 address that has been added to
+ * the host os radix tree.
+ */
+int SCHInfoTestValidIPV6Address07(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux",
+ "2351:2512:6211:6246:235A:6242:2352:62AD",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "6961:6121:2132:6241:423A:2135:2461:621D",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "DD13:613D:F312:62DD:6213:421A:6212:2652",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10",
+ "9891:2131:2151:6426:1342:674D:622F:2342",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "3525:2351:4223:6211:2311:2667:6242:2154",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "1511:6211:6726:7777:1212:2333:6222:7722",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "2666:6222:7222:2335:6223:7722:3425:2362",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "8762:2352:6241:7245:EE23:21AD:2312:622C/68",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "8762:2352:6241:7245:EE23:21AD:2412:622C",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "8762:2352:6241:7245:EE23:21AD:FFFF:622C",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11",
+ "8762:2352:6241:7245:EE23:21AD:2312:62FF",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "8762:2352:6241:7245:EE23:21AD:2121:1212",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:62AD") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:6FFFE") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2652") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2222") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("9891:2131:2151:6426:1342:674D:622F:2342") !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("3525:2351:4223:6211:2311:2667:6242:2154") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("1511:6211:6726:7777:1212:2333:6222:7722") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2666:6222:7222:2335:6223:7722:3425:2362") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:622C") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2412:622C") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:FFFF:622C") !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:62FF") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2121:1212") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("5222:6432:6432:2322:6662:3423:4322:DDDD") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2121:1DDD") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:FFFF:2121:1DDD") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:622C") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE00:0000:0000:0000") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:E000:0000:0000:0000") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv6 addresses/netblocks are inserted into the host os
+ * radix tree, and the host os api retrieves the right value for the host
+ * os flavour, on supplying as arg an ipv6 address that has been added to
+ * the host os radix tree.
+ */
+int SCHInfoTestValidIPV6Address08(void)
+{
+ SCHInfoCreateContextBackup();
+
+ struct in6_addr in6;
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux",
+ "2351:2512:6211:6246:235A:6242:2352:62AD",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "6961:6121:2132:6241:423A:2135:2461:621D",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "DD13:613D:F312:62DD:6213:421A:6212:2652",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux10",
+ "9891:2131:2151:6426:1342:674D:622F:2342",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "3525:2351:4223:6211:2311:2667:6242:2154",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "1511:6211:6726:7777:1212:2333:6222:7722",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "2666:6222:7222:2335:6223:7722:3425:2362",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris",
+ "8762:2352:6241:7245:EE23:21AD:2312:622C/68",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("linux",
+ "8762:2352:6241:7245:EE23:21AD:2412:622C",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows",
+ "8762:2352:6241:7245:EE23:21AD:FFFF:622C",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("hpux11",
+ "8762:2352:6241:7245:EE23:21AD:2312:62FF",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista",
+ "8762:2352:6241:7245:EE23:21AD:2121:1212",
+ SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("irix", "default", SC_HINFO_IS_IPV6) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:62AD") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2351:2512:6211:6246:235A:6242:2352:6FFF") !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2652") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("DD13:613D:F312:62DD:6213:421A:6212:2222") !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("9891:2131:2151:6426:1342:674D:622F:2342") !=
+ SCMapEnumNameToValue("hpux10", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("3525:2351:4223:6211:2311:2667:6242:2154") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("1511:6211:6726:7777:1212:2333:6222:7722") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("2666:6222:7222:2335:6223:7722:3425:2362") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:622C") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2412:622C") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:FFFF:622C") !=
+ SCMapEnumNameToValue("windows", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:62FF") !=
+ SCMapEnumNameToValue("hpux11", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2121:1212") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("5222:6432:6432:2322:6662:3423:4322:DDDD") !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2121:1DDD") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:FFFF:2121:1DDD") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE23:21AD:2312:622C") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("8762:2352:6241:7245:EE00:0000:0000:0000") !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (inet_pton(AF_INET6, "8762:2352:6241:7245:E000:0000:0000:0000", &in6) < 0) {
+ goto end;
+ }
+ if (SCHInfoGetIPv6HostOSFlavour((uint8_t *)&in6) !=
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (inet_pton(AF_INET6, "AD23:2DDA:6D1D:A223:E235:0232:1241:1666", &in6) < 0) {
+ goto end;
+ }
+ if (SCHInfoGetIPv6HostOSFlavour((uint8_t *)&in6) !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that valid ipv4 addresses are inserted into the host os radix
+ * tree, and the host os api retrieves the right value for the host os
+ * flavour, on supplying as arg an ipv4 addresses that has been added to
+ * the host os radix tree.
+ */
+int SCHInfoTestValidIPV4Address09(void)
+{
+ SCHInfoCreateContextBackup();
+
+ int result = 1;
+
+ if (SCHInfoAddHostOSInfo("linux", "192.168.1.0", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("windows", "192.192.1.2", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.0") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "192.168.1.0/16", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("macos", "192.168.1.0/20", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.1.0") !=
+ SCMapEnumNameToValue("linux", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("vista", "192.168.50.128/25", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.50.128") !=
+ SCMapEnumNameToValue("vista", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("irix", "192.168.50.128", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("192.168.50.128") !=
+ SCMapEnumNameToValue("irix", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") !=
+ SCMapEnumNameToValue("macos", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ struct sockaddr_in servaddr;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0) {
+ goto end;
+ }
+
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, sc_hinfo_tree, 16);
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") !=
+ SCMapEnumNameToValue("macos", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0) {
+ goto end;
+ }
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, sc_hinfo_tree, 20);
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") != -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("solaris", "192.168.1.0/16", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+ if (SCHInfoAddHostOSInfo("macos", "192.168.1.0/20", SC_HINFO_IS_IPV4) == -1) {
+ goto end;
+ }
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") ==
+ SCMapEnumNameToValue("macos", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0) {
+ goto end;
+ }
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, sc_hinfo_tree, 20);
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") ==
+ SCMapEnumNameToValue("solaris", sc_hinfo_os_policy_map)) {
+ goto end;
+ }
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0) {
+ goto end;
+ }
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, sc_hinfo_tree, 16);
+
+ if (SCHInfoGetHostOSFlavour("192.168.1.100") != -1) {
+ goto end;
+ }
+
+ result = 1;
+
+ end:
+ SCHInfoCleanResources();
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check the loading of host info from a configuration file.
+ */
+int SCHInfoTestLoadFromConfig01(void)
+{
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+host-os-policy:\n\
+ bsd: [0.0.0.0/0]\n\
+ windows: [10.0.0.0/8, 192.168.1.0/24]\n\
+ linux: [10.0.0.5/32]\n\
+\n";
+
+ int result = 0;
+
+ SCHInfoCreateContextBackup();
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ SCHInfoLoadFromConfig();
+ if (SCHInfoGetHostOSFlavour("10.0.0.4") != OS_POLICY_WINDOWS)
+ goto end;
+ if (SCHInfoGetHostOSFlavour("10.0.0.5") != OS_POLICY_LINUX)
+ goto end;
+ if (SCHInfoGetHostOSFlavour("192.168.1.1") != OS_POLICY_WINDOWS)
+ goto end;
+ if (SCHInfoGetHostOSFlavour("172.168.1.1") != OS_POLICY_BSD)
+ goto end;
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check the loading of host info from a configuration file.
+ */
+int SCHInfoTestLoadFromConfig02(void)
+{
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+host-os-policy:\n\
+ one-two: [0.0.0.0/0]\n\
+ one-two-three:\n\
+ four_five:\n\
+ six-seven_eight: [10.0.0.0/8, 192.168.1.0/24]\n\
+ nine_ten_eleven: [10.0.0.5/32]\n\
+\n";
+
+ int result = 0;
+
+ SCHInfoCreateContextBackup();
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ConfNode *root = ConfGetNode("host-os-policy");
+ if (root == NULL)
+ goto end;
+
+ int count = 0;
+
+ ConfNode *policy;
+ TAILQ_FOREACH(policy, &root->head, next) {
+ switch (count) {
+ case 0:
+ if (strcmp("one-two", policy->name) != 0)
+ goto end;
+ break;
+ case 1:
+ if (strcmp("one-two-three", policy->name) != 0)
+ goto end;
+ break;
+ case 2:
+ if (strcmp("four-five", policy->name) != 0)
+ goto end;
+ break;
+ case 3:
+ if (strcmp("six-seven-eight", policy->name) != 0)
+ goto end;
+ break;
+ case 4:
+ if (strcmp("nine-ten-eleven", policy->name) != 0)
+ goto end;
+ break;
+ }
+ count++;
+ }
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check the loading of host info from a configuration file.
+ */
+int SCHInfoTestLoadFromConfig03(void)
+{
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+host-os-policy:\n\
+ bsd-right: [0.0.0.1]\n\
+ old-linux: [0.0.0.2]\n\
+ old-solaris: [0.0.0.3]\n\
+ windows: [0.0.0.4]\n\
+ vista: [0.0.0.5]\n\
+\n";
+
+ int result = 1;
+
+ SCHInfoCreateContextBackup();
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ConfNode *root = ConfGetNode("host-os-policy");
+ if (root == NULL)
+ goto end;
+
+ ConfNode *policy;
+ TAILQ_FOREACH(policy, &root->head, next) {
+ if (SCMapEnumNameToValue(policy->name, sc_hinfo_os_policy_map) == -1) {
+ printf("Invalid enum map inside\n");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ SCHInfoRestoreContextBackup();
+ return result;
+}
+
+/**
+ * \test Check the loading of host info from a configuration file.
+ */
+int SCHInfoTestLoadFromConfig04(void)
+{
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+host-os-policy:\n\
+ bsd_right: [0.0.0.1]\n\
+ old_linux: [0.0.0.2]\n\
+ old_solaris: [0.0.0.3]\n\
+ windows: [0.0.0.4]\n\
+ vista: [0.0.0.5]\n\
+\n";
+
+ int result = 1;
+
+ SCHInfoCreateContextBackup();
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ ConfNode *root = ConfGetNode("host-os-policy");
+ if (root == NULL)
+ goto end;
+
+ ConfNode *policy;
+ TAILQ_FOREACH(policy, &root->head, next) {
+ if (SCMapEnumNameToValue(policy->name, sc_hinfo_os_policy_map) == -1) {
+ printf("Invalid enum map inside\n");
+ goto end;
+ }
+ }
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ SCHInfoRestoreContextBackup();
+ return result;
+}
+
+/**
+ * \test Check the loading of host info from a configuration file.
+ */
+int SCHInfoTestLoadFromConfig05(void)
+{
+ char config[] = "\
+%YAML 1.1\n\
+---\n\
+host-os-policy:\n\
+ bsd_right: [0.0.0.1]\n\
+ old_linux: [0.0.0.2]\n\
+ old-solaris: [0.0.0.3]\n\
+ windows: [0.0.0.4]\n\
+ linux: [0.0.0.5]\n\
+\n";
+
+ int result = 0;
+
+ SCHInfoCreateContextBackup();
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(config, strlen(config));
+
+ SCHInfoLoadFromConfig();
+
+ if (SCHInfoGetHostOSFlavour("0.0.0.1") != OS_POLICY_BSD_RIGHT) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.2") != OS_POLICY_OLD_LINUX) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.3") != OS_POLICY_OLD_SOLARIS) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.4") != OS_POLICY_WINDOWS) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.5") != OS_POLICY_VISTA) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.0") != -1) {
+ goto end;
+ }
+ if (SCHInfoGetHostOSFlavour("0.0.0.6") != -1) {
+ goto end;
+ }
+
+
+ result = 1;
+
+ end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ SCHInfoRestoreContextBackup();
+
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCHInfoRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+ UtRegisterTest("SCHInfoTesInvalidOSFlavour01",
+ SCHInfoTestInvalidOSFlavour01, 1);
+ UtRegisterTest("SCHInfoTestInvalidIPV4Address02",
+ SCHInfoTestInvalidIPV4Address02, 1);
+ UtRegisterTest("SCHInfoTestInvalidIPV6Address03",
+ SCHInfoTestInvalidIPV6Address03, 1);
+ UtRegisterTest("SCHInfoTestValidIPV4Address04",
+ SCHInfoTestValidIPV4Address04, 1);
+ UtRegisterTest("SCHInfoTestValidIPV4Address05",
+ SCHInfoTestValidIPV4Address05, 1);
+ UtRegisterTest("SCHInfoTestValidIPV6Address06",
+ SCHInfoTestValidIPV6Address06, 1);
+ UtRegisterTest("SCHInfoTestValidIPV6Address07",
+ SCHInfoTestValidIPV6Address07, 1);
+ UtRegisterTest("SCHInfoTestValidIPV6Address08",
+ SCHInfoTestValidIPV6Address08, 1);
+ UtRegisterTest("SCHInfoTestValidIPV4Address09",
+ SCHInfoTestValidIPV4Address09, 1);
+
+ UtRegisterTest("SCHInfoTestLoadFromConfig01",
+ SCHInfoTestLoadFromConfig01, 1);
+ UtRegisterTest("SCHInfoTestLoadFromConfig02",
+ SCHInfoTestLoadFromConfig02, 1);
+ UtRegisterTest("SCHInfoTestLoadFromConfig03",
+ SCHInfoTestLoadFromConfig03, 1);
+ UtRegisterTest("SCHInfoTestLoadFromConfig04",
+ SCHInfoTestLoadFromConfig04, 1);
+#endif /* UNITTESTS */
+
+}
diff --git a/framework/src/suricata/src/util-host-os-info.h b/framework/src/suricata/src/util-host-os-info.h
new file mode 100644
index 00000000..2dbcbc20
--- /dev/null
+++ b/framework/src/suricata/src/util-host-os-info.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_HOST_OS_INFO_H__
+#define __UTIL_HOST_OS_INFO_H__
+
+#define SC_HINFO_IS_IPV6 0
+#define SC_HINFO_IS_IPV4 1
+
+int SCHInfoAddHostOSInfo(char *, char *, int);
+int SCHInfoGetHostOSFlavour(char *);
+int SCHInfoGetIPv4HostOSFlavour(uint8_t *);
+int SCHInfoGetIPv6HostOSFlavour(uint8_t *);
+void SCHInfoCleanResources(void);
+void SCHInfoLoadFromConfig(void);
+void SCHInfoRegisterTests(void);
+
+#endif /* __UTIL_HOST_OS_INFO_H__ */
diff --git a/framework/src/suricata/src/util-ioctl.c b/framework/src/suricata/src/util-ioctl.c
new file mode 100644
index 00000000..f08dea71
--- /dev/null
+++ b/framework/src/suricata/src/util-ioctl.c
@@ -0,0 +1,248 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_LINUX_ETHTOOL_H
+#include <linux/types.h>
+#include <linux/ethtool.h>
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#else
+#error "ethtool.h present but sockios.h is missing"
+#endif /* HAVE_LINUX_SOCKIOS_H */
+#endif /* HAVE_LINUX_ETHTOOL_H */
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+/**
+ * \brief output a majorant of hardware header length
+ *
+ * \param Name of a network interface
+ */
+int GetIfaceMaxHWHeaderLength(const char *pcap_dev)
+{
+ if ((!strcmp("eth", pcap_dev))
+ ||
+ (!strcmp("br", pcap_dev))
+ ||
+ (!strcmp("bond", pcap_dev))
+ ||
+ (!strcmp("wlan", pcap_dev))
+ ||
+ (!strcmp("tun", pcap_dev))
+ ||
+ (!strcmp("tap", pcap_dev))
+ ||
+ (!strcmp("lo", pcap_dev)))
+ return ETHERNET_HEADER_LEN;
+
+ if (!strcmp("ppp", pcap_dev))
+ return SLL_HEADER_LEN;
+ /* SLL_HEADER_LEN is the biggest one */
+ return SLL_HEADER_LEN;
+}
+
+/**
+ * \brief output the link MTU
+ *
+ * \param Name of link
+ * \retval -1 in case of error, 0 if MTU can not be found
+ */
+int GetIfaceMTU(const char *pcap_dev)
+{
+#ifdef SIOCGIFMTU
+ struct ifreq ifr;
+ int fd;
+
+ (void)strlcpy(ifr.ifr_name, pcap_dev, sizeof(ifr.ifr_name));
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (ioctl(fd, SIOCGIFMTU, (char *)&ifr) < 0) {
+ SCLogWarning(SC_ERR_SYSCALL,
+ "Failure when trying to get MTU via ioctl: %d",
+ errno);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ SCLogInfo("Found an MTU of %d for '%s'", ifr.ifr_mtu,
+ pcap_dev);
+ return ifr.ifr_mtu;
+#else
+ /* ioctl is not defined, let's pretend returning 0 is ok */
+ return 0;
+#endif
+}
+
+/**
+ * \brief output max packet size for a link
+ *
+ * This does a best effort to find the maximum packet size
+ * for the link. In case of uncertainty, it will output a
+ * majorant to be sure avoid the cost of dynamic allocation.
+ *
+ * \param Name of a network interface
+ * \retval 0 in case of error
+ */
+int GetIfaceMaxPacketSize(const char *pcap_dev)
+{
+ int ll_header = GetIfaceMaxHWHeaderLength(pcap_dev);
+ int mtu = 0;
+
+ if ((pcap_dev == NULL) || strlen(pcap_dev) == 0)
+ return 0;
+
+ mtu = GetIfaceMTU(pcap_dev);
+ switch (mtu) {
+ case 0:
+ case -1:
+ return 0;
+ }
+ if (ll_header == -1) {
+ /* be conservative, choose a big one */
+ ll_header = 16;
+ }
+ return ll_header + mtu;
+}
+
+/**
+ * \brief output offloading status of the link
+ *
+ * Test interface for GRO and LRO features. If one of them is
+ * activated then suricata mays received packets merge at reception.
+ * The result is oversized packets and this may cause some serious
+ * problem in some capture mode where the size of the packet is
+ * limited (AF_PACKET in V2 more for example).
+ *
+ * ETHTOOL_GGRO ETH_FLAG_LRO
+ *
+ * \param Name of link
+ * \retval -1 in case of error, 0 if none, 1 if some
+ */
+int GetIfaceOffloading(const char *pcap_dev)
+{
+#if defined (ETHTOOL_GGRO) && defined (ETHTOOL_GFLAGS)
+ struct ifreq ifr;
+ int fd;
+ struct ethtool_value ethv;
+ int ret = 0;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ (void)strlcpy(ifr.ifr_name, pcap_dev, sizeof(ifr.ifr_name));
+
+ /* First get GRO */
+ ethv.cmd = ETHTOOL_GGRO;
+ ifr.ifr_data = (void *) &ethv;
+ if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) {
+ SCLogWarning(SC_ERR_SYSCALL,
+ "Failure when trying to get feature via ioctl: %s (%d)",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ } else {
+ if (ethv.data) {
+ SCLogInfo("Generic Receive Offload is set on %s", pcap_dev);
+ ret = 1;
+ } else {
+ SCLogInfo("Generic Receive Offload is unset on %s", pcap_dev);
+ }
+ }
+
+ /* Then get LRO which is set in a flag */
+ ethv.data = 0;
+ ethv.cmd = ETHTOOL_GFLAGS;
+ ifr.ifr_data = (void *) &ethv;
+ if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) {
+ SCLogWarning(SC_ERR_SYSCALL,
+ "Failure when trying to get feature via ioctl: %s (%d)",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ } else {
+ if (ethv.data & ETH_FLAG_LRO) {
+ SCLogInfo("Large Receive Offload is set on %s", pcap_dev);
+ ret = 1;
+ } else {
+ SCLogInfo("Large Receive Offload is unset on %s", pcap_dev);
+ }
+ }
+
+ close(fd);
+
+ return ret;
+#else
+ /* ioctl is not defined, let's pretend returning 0 is ok */
+ return 0;
+#endif
+}
+
+int GetIfaceRSSQueuesNum(const char *pcap_dev)
+{
+#if defined HAVE_LINUX_ETHTOOL_H && defined ETHTOOL_GRXRINGS
+ struct ifreq ifr;
+ struct ethtool_rxnfc nfccmd;
+ int fd;
+
+ (void)strlcpy(ifr.ifr_name, pcap_dev, sizeof(ifr.ifr_name));
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ SCLogWarning(SC_ERR_SYSCALL,
+ "Failure when opening socket for ioctl: %d",
+ errno);
+ return -1;
+ }
+
+ nfccmd.cmd = ETHTOOL_GRXRINGS;
+ ifr.ifr_data = (void*) &nfccmd;
+
+ if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) {
+ if (errno != ENOTSUP) {
+ SCLogWarning(SC_ERR_SYSCALL,
+ "Failure when trying to get number of RSS queue ioctl: %d",
+ errno);
+ }
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ SCLogInfo("Found %d RX RSS queues for '%s'", (int)nfccmd.data,
+ pcap_dev);
+ return (int)nfccmd.data;
+#else
+ return 0;
+#endif
+}
diff --git a/framework/src/suricata/src/util-ioctl.h b/framework/src/suricata/src/util-ioctl.h
new file mode 100644
index 00000000..3931c1fe
--- /dev/null
+++ b/framework/src/suricata/src/util-ioctl.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eleblond@edenwall.com>
+ */
+
+int GetIfaceMTU(const char *pcap_dev);
+int GetIfaceMaxPacketSize(const char *pcap_dev);
+int GetIfaceOffloading(const char *pcap_dev);
+int GetIfaceRSSQueuesNum(const char *pcap_dev);
diff --git a/framework/src/suricata/src/util-ip.c b/framework/src/suricata/src/util-ip.c
new file mode 100644
index 00000000..1f22d864
--- /dev/null
+++ b/framework/src/suricata/src/util-ip.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ *
+ * IP addresses related utility functions
+ */
+
+#include "suricata-common.h"
+
+/**
+ * \brief Validates an IPV4 address and returns the network endian arranged
+ * version of the IPV4 address
+ *
+ * \param addr Pointer to a character string containing an IPV4 address. A
+ * valid IPV4 address is a character string containing a dotted
+ * format of "ddd.ddd.ddd.ddd"
+ *
+ * \retval Pointer to an in_addr instance containing the network endian format
+ * of the IPV4 address
+ * \retval NULL if the IPV4 address is invalid
+ */
+struct in_addr *ValidateIPV4Address(const char *addr_str)
+{
+ struct in_addr *addr = NULL;
+
+ if ( (addr = SCMalloc(sizeof(struct in_addr))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in ValidateIPV4Address. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ if (inet_pton(AF_INET, addr_str, addr) <= 0) {
+ SCFree(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+/**
+ * \brief Validates an IPV6 address and returns the network endian arranged
+ * version of the IPV6 addresss
+ *
+ * \param addr Pointer to a character string containing an IPV6 address
+ *
+ * \retval Pointer to a in6_addr instance containing the network endian format
+ * of the IPV6 address
+ * \retval NULL if the IPV6 address is invalid
+ */
+struct in6_addr *ValidateIPV6Address(const char *addr_str)
+{
+ struct in6_addr *addr = NULL;
+
+ if ( (addr = SCMalloc(sizeof(struct in6_addr))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in ValidateIPV6Address. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ if (inet_pton(AF_INET6, addr_str, addr) <= 0) {
+ SCFree(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+/**
+ * \brief Culls the non-netmask portion of the IP address. For example an IP
+ * address 192.168.240.1 would be chopped to 192.168.224.0 against a
+ * netmask value of 19.
+ *
+ * \param stream Pointer the IP address that has to be masked
+ * \param netmask The netmask value (cidr) to which the IP address has to be culled
+ * \param key_bitlen The bitlen of the stream
+ */
+void MaskIPNetblock(uint8_t *stream, uint8_t netmask, uint16_t key_bitlen)
+{
+ int mask = 0;
+ int i = 0;
+ int bytes = key_bitlen / 8;
+
+ for (i = 0; i < bytes; i++) {
+ mask = -1;
+ if ( ((i + 1) * 8) > netmask) {
+ if ( ((i + 1) * 8 - netmask) < 8)
+ mask = -1 << ((i + 1) * 8 - netmask);
+ else
+ mask = 0;
+ }
+ stream[i] &= mask;
+ }
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-ip.h b/framework/src/suricata/src/util-ip.h
new file mode 100644
index 00000000..25ffe9ec
--- /dev/null
+++ b/framework/src/suricata/src/util-ip.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ */
+
+#ifndef __UTIL_IP_H__
+#define __UTIL_IP_H__
+
+struct in_addr *ValidateIPV4Address(const char *);
+struct in6_addr *ValidateIPV6Address(const char *);
+void MaskIPNetblock(uint8_t *, int, int);
+
+#endif /* __UTIL_IP_H__ */
diff --git a/framework/src/suricata/src/util-logopenfile-tile.c b/framework/src/suricata/src/util-logopenfile-tile.c
new file mode 100644
index 00000000..c2414fc4
--- /dev/null
+++ b/framework/src/suricata/src/util-logopenfile-tile.c
@@ -0,0 +1,370 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio <decanio.tom@gmail.com>
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ *
+ * File-like output for logging on Tilera PCIe cards (TILEncore-Gx)
+ * add the option to send logs across PCIe and then write the output
+ * files on the host system.
+ *
+ */
+#include <sys/types.h>
+
+#include "suricata-common.h" /* errno.h, string.h, etc. */
+#include "tm-modules.h" /* LogFileCtx */
+#include "conf.h" /* ConfNode, etc. */
+#include "util-atomic.h"
+#include "util-logopenfile-tile.h"
+
+#ifdef __tile__
+#include <gxio/trio.h>
+#include <mde-version.h>
+
+#if MDE_VERSION_CODE >= MDE_VERSION(4,1,0)
+#include <gxpci/gxpci.h>
+#else
+#include <gxpci.h>
+#endif
+
+/*
+ * Tilera trio (PCIe) configuration.
+ */
+static gxio_trio_context_t trio_context_body;
+static gxio_trio_context_t* trio_context = &trio_context_body;
+/*
+ * gxpci contexts used for log relay
+ */
+static gxpci_context_t gxpci_context_body;
+static gxpci_context_t *gxpci_context = &gxpci_context_body;
+/* The TRIO index. */
+static int trio_index = 0;
+
+/* The queue index of a packet queue. */
+static unsigned int queue_index = 0;
+
+/* The local PCIe MAC index. */
+static int loc_mac;
+
+/*
+ * Code for writing files over PCIe to host on Tilera TILEncore PCIe cards.
+ */
+
+#define OP_OPEN 1
+#define OP_WRITE 2
+#define OP_CLOSE 3
+
+/** Maximum number of commands in one PCIe function call */
+#define MAX_CMDS_BATCH 64
+
+typedef struct {
+ uint32_t magic;
+ uint32_t fileno;
+ uint32_t op;
+ uint32_t seq;
+ uint32_t len;
+ uint32_t next_offset;
+ char buf[];
+} __attribute__((__packed__)) PcieMsg;
+
+static int gxpci_fileno = 0;
+static int pcie_initialized = 0;
+/* Allocate a Huge page of memory, registered with Trio, into which
+ data to be sent over PCIe is written. Each write starts at wc_pos.
+*/
+static char *log_mem = NULL;
+static uint64_t wr_pos; /* write position within log_mem */
+
+static SCMutex raw_mutex __attribute__((aligned(64)));
+static SCMutex pcie_mutex __attribute__((aligned(64)));
+#define CHECK_SEQ_NUM 1
+#ifdef CHECK_SEQ_NUM
+static uint32_t raw_seq = 0;
+#endif
+static uint32_t comps_rcvd = 0;
+/* Block of memory registered with PCIe DMA engine as a source for
+ * PCIe data transfers. Must be <= Huge Page size (16 MB).
+ * Must be large enough that it can't wrap before first PCIe transfer
+ * has completed.
+ */
+#define PCIE_MEMORY_BLOCK_SIZE (4 * 1024 * 1024)
+
+/* Send a buffer over PCIe to Host memory.
+ * len must be smaller than one Packet Queue transfer block.
+ * TODO: Check errors
+ */
+static void TilePcieDMABuf(void *buf, uint32_t len)
+{
+ gxpci_comp_t comp[MAX_CMDS_BATCH];
+ gxpci_cmd_t cmd;
+ int result;
+ int credits;
+
+ SCMutexLock(&pcie_mutex);
+
+#ifdef CHECK_SEQ_NUM
+ ((PcieMsg *)buf)->seq = ++raw_seq;
+ __insn_mf();
+#endif
+
+ /* Wait for credits to be available for more PCIe writes. */
+ do {
+ result = gxpci_get_comps(gxpci_context, comp, 0, MAX_CMDS_BATCH);
+ if (result) {
+ if (unlikely(result == GXPCI_ERESET)) {
+ SCLogInfo("gxpci channel is reset");
+ return;
+ } else {
+ __sync_fetch_and_add(&comps_rcvd, result);
+ }
+ }
+
+ credits = gxpci_get_cmd_credits(gxpci_context);
+ if (unlikely(credits == GXPCI_ERESET)) {
+ SCLogInfo("gxpci channel is reset");
+ return;
+ }
+ } while (credits == 0);
+
+ cmd.buffer = buf;
+ /* Round transfer size up to next host cache-line. This will
+ * transfer more data, but is more efficient.
+ */
+ cmd.size = (len + (CLS - 1)) & ~(CLS - 1);
+
+ __insn_mf();
+
+ /* Loop until the command is sent. */
+ do {
+ /* Send PCIe command to packet queue from tile to host. */
+ result = gxpci_pq_t2h_cmd(gxpci_context, &cmd);
+ if (result == 0)
+ break;
+ if (result == GXPCI_ERESET) {
+ SCLogInfo("gxpci channel is reset");
+ break;
+ }
+ /* Not enough credits to send command? */
+ if (result == GXPCI_ECREDITS)
+ continue;
+ } while (1);
+
+ SCMutexUnlock(&pcie_mutex);
+}
+
+/* Allocate a buffer for data that can be sent over PCIe. Reserves
+ * space at the beginning for the Pcie msg. The buffer is allocated
+ * from a 4MB pool on one huge page. The allocation simply walks
+ * throught the buffer sequentially. This removes the need to free
+ * the buffers, as they simply age out.
+ */
+static PcieMsg *TilePcieAllocateBuffer(size_t size)
+{
+ size += sizeof(PcieMsg);
+ /* Round up to cache-line size */
+ size = (size + (CLS - 1)) & ~(CLS - 1);
+
+ PcieMsg *pmsg;
+ SCMutexLock(&raw_mutex);
+ pmsg = (PcieMsg *)&log_mem[wr_pos];
+ wr_pos += size;
+ if (wr_pos > PCIE_MEMORY_BLOCK_SIZE) {
+ /* Don't have enough space at the end of the memory block, so
+ * wrap to the start.
+ */
+ pmsg = (PcieMsg *)&log_mem[0];
+ wr_pos = size;
+
+ }
+ SCMutexUnlock(&raw_mutex);
+
+ return pmsg;
+}
+
+static void PcieWriteOpen(PcieFile *fp, const char *path, const char append)
+{
+ /* Need space for file name, file mode character and string termination */
+ const int buffer_size = strlen(path) + 2;
+
+ /* Allocate space in the PCIe output buffer */
+ PcieMsg *p = TilePcieAllocateBuffer(buffer_size);
+
+ p->magic = 5555;
+ p->fileno = fp->fileno;
+ p->op = OP_OPEN;
+ p->len = offsetof(PcieMsg, buf);
+ /* Format is one character Mode, followed by file path. */
+ p->len += snprintf(p->buf, buffer_size, "%c%s", append, path);
+
+ TilePcieDMABuf(p, p->len);
+}
+
+static int TilePcieWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
+{
+ PcieFile *fp = log_ctx->pcie_fp;
+ /* Allocate space in the PCIe output buffer */
+ PcieMsg *p = TilePcieAllocateBuffer(buffer_len);
+
+ p->magic = 5555;
+ p->fileno = fp->fileno;
+ p->op = OP_WRITE;
+ p->len = offsetof(PcieMsg, buf);
+ p->len += buffer_len;
+ p->next_offset = 0;
+
+ /* Can remove the need for this memcpy later. */
+ memcpy(p->buf, buffer, buffer_len);
+
+ TilePcieDMABuf(p, p->len);
+
+ return buffer_len;
+}
+
+static PcieFile *TileOpenPcieFpInternal(const char *path, const char append_char)
+{
+ int result;
+ PcieFile *fp;
+
+ /* Only initialize once */
+ if (SCAtomicCompareAndSwap(&pcie_initialized, 0, 1)) {
+ SCMutexInit(&raw_mutex, NULL);
+ SCMutexInit(&pcie_mutex, NULL);
+
+ SCLogInfo("Initializing Tile-Gx PCIe index %d / %d, queue: %d",
+ trio_index, loc_mac, queue_index);
+
+ result = gxio_trio_init(trio_context, trio_index);
+ if (result < 0) {
+ pcie_initialized = 0;
+ SCLogError(SC_ERR_PCIE_INIT_FAILED,
+ "gxio_trio_init() failed: %d: %s",
+ result, gxio_strerror(result));
+ return NULL;
+ }
+
+ result = gxpci_init(trio_context, gxpci_context, trio_index, loc_mac);
+ if (result < 0) {
+ pcie_initialized = 0;
+ SCLogError(SC_ERR_PCIE_INIT_FAILED,
+ "gxpci_init() failed: %d: %s",
+ result, gxpci_strerror(result));
+ return NULL;
+ }
+
+ /*
+ * This indicates that we need to allocate an ASID ourselves,
+ * instead of using one that is allocated somewhere else.
+ */
+ int asid = GXIO_ASID_NULL;
+
+ result = gxpci_open_queue(gxpci_context, asid, GXPCI_PQ_T2H, 0,
+ queue_index, 0, 0);
+ if (result < 0) {
+ pcie_initialized = 0;
+ SCLogError(SC_ERR_PCIE_INIT_FAILED,
+ "gxpci_open_queue() failed: %d: %s",
+ result, gxpci_strerror(result));
+ return NULL;
+ }
+
+ /*
+ * Allocate and register data buffer
+ */
+ size_t hugepagesz = tmc_alloc_get_huge_pagesize();
+ tmc_alloc_t alloc = TMC_ALLOC_INIT;
+ tmc_alloc_set_huge(&alloc);
+ tmc_alloc_set_home(&alloc, TMC_ALLOC_HOME_HASH);
+ tmc_alloc_set_pagesize_exact(&alloc, hugepagesz);
+ log_mem = tmc_alloc_map(&alloc, hugepagesz);
+ BUG_ON(PCIE_MEMORY_BLOCK_SIZE > hugepagesz);
+
+ result = gxpci_iomem_register(gxpci_context, log_mem, hugepagesz);
+ if (result < 0) {
+ pcie_initialized = 0;
+ SCLogError(SC_ERR_PCIE_INIT_FAILED,
+ "gxpci_iomem_register() failed: %d: %s",
+ result, gxpci_strerror(result));
+ return NULL;
+ }
+ }
+ fp = SCMalloc(sizeof(PcieFile));
+ if (fp == NULL) {
+ SCLogError(SC_ERR_PCIE_INIT_FAILED,
+ "Failed to Allocate memory for PCIe file pointer");
+
+ return NULL;
+ }
+
+ /* Sequentially allocate File descriptor numbers. Not currently ever freed */
+ fp->fileno = SCAtomicFetchAndAdd(&gxpci_fileno, 1);
+ PcieWriteOpen(fp, path, append_char);
+
+ return fp;
+}
+
+/** \brief Close a PCIe file
+ * \param PCIe file desriptor
+ */
+static void TileClosePcieFp(LogFileCtx *log_ctx)
+{
+ SCLogInfo("Closing Tile-Gx PCIe: %s", log_ctx->filename);
+
+ /* TODO: Need to count open files and close when reaches zero. */
+ SCMutexLock(&pcie_mutex);
+
+ if (gxpci_context) {
+ gxpci_destroy(gxpci_context);
+ gxpci_context = NULL;
+ }
+
+ SCMutexUnlock(&pcie_mutex);
+
+ free(log_ctx->pcie_fp);
+}
+
+/** \brief open the indicated file remotely over PCIe to a host
+ * \param path filesystem path to open
+ * \param append_setting open file with O_APPEND: "yes" or "no"
+ * \retval FILE* on success
+ * \retval NULL on error
+ */
+PcieFile *TileOpenPcieFp(LogFileCtx *log_ctx, const char *path,
+ const char *append_setting)
+{
+ PcieFile *ret = NULL;
+ if (strcasecmp(append_setting, "yes") == 0) {
+ ret = TileOpenPcieFpInternal(path, 'a');
+ } else {
+ ret = TileOpenPcieFpInternal(path, 'w');
+ }
+
+ /* Override the default Write and Close functions
+ * with PCIe Write and Close functions.
+ */
+ log_ctx->Write = TilePcieWrite;
+ log_ctx->Close = TileClosePcieFp;
+
+ if (ret == NULL)
+ SCLogError(SC_ERR_FOPEN, "Error opening PCIe file: \"%s\": %s",
+ path, strerror(errno));
+ return ret;
+}
+
+#endif /* __tilegx__ */
diff --git a/framework/src/suricata/src/util-logopenfile-tile.h b/framework/src/suricata/src/util-logopenfile-tile.h
new file mode 100644
index 00000000..4d1ac657
--- /dev/null
+++ b/framework/src/suricata/src/util-logopenfile-tile.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
+ */
+
+#ifndef __UTIL_LOGOPENFILE_TILE_H__
+#define __UTIL_LOGOPENFILE_TILE_H__
+
+#include "util-logopenfile.h" /* LogFileCtx */
+
+PcieFile *TileOpenPcieFp(LogFileCtx *log_ctx, const char *path,
+ const char *append_setting);
+
+#endif /* __UTIL_LOGOPENFILE_TILE_H__ */
diff --git a/framework/src/suricata/src/util-logopenfile.c b/framework/src/suricata/src/util-logopenfile.c
new file mode 100644
index 00000000..b25c4a82
--- /dev/null
+++ b/framework/src/suricata/src/util-logopenfile.c
@@ -0,0 +1,383 @@
+/* vi: set et ts=4: */
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Mike Pomraning <mpomraning@qualys.com>
+ *
+ * File-like output for logging: regular files and sockets.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "suricata-common.h" /* errno.h, string.h, etc. */
+#include "tm-modules.h" /* LogFileCtx */
+#include "conf.h" /* ConfNode, etc. */
+#include "output.h" /* DEFAULT_LOG_* */
+#include "util-logopenfile.h"
+#include "util-logopenfile-tile.h"
+
+/** \brief connect to the indicated local stream socket, logging any errors
+ * \param path filesystem path to connect to
+ * \param log_err, non-zero if connect failure should be logged.
+ * \retval FILE* on success (fdopen'd wrapper of underlying socket)
+ * \retval NULL on error
+ */
+static FILE *
+SCLogOpenUnixSocketFp(const char *path, int sock_type, int log_err)
+{
+ struct sockaddr_un sun;
+ int s = -1;
+ FILE * ret = NULL;
+
+ memset(&sun, 0x00, sizeof(sun));
+
+ s = socket(PF_UNIX, sock_type, 0);
+ if (s < 0) goto err;
+
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+
+ if (connect(s, (const struct sockaddr *)&sun, sizeof(sun)) < 0)
+ goto err;
+
+ ret = fdopen(s, "w");
+ if (ret == NULL)
+ goto err;
+
+ return ret;
+
+err:
+ if (log_err)
+ SCLogWarning(SC_ERR_SOCKET,
+ "Error connecting to socket \"%s\": %s (will keep trying)",
+ path, strerror(errno));
+
+ if (s >= 0)
+ close(s);
+
+ return NULL;
+}
+
+/**
+ * \brief Attempt to reconnect a disconnected (or never-connected) Unix domain socket.
+ * \retval 1 if it is now connected; otherwise 0
+ */
+static int SCLogUnixSocketReconnect(LogFileCtx *log_ctx)
+{
+ int disconnected = 0;
+ if (log_ctx->fp) {
+ SCLogWarning(SC_ERR_SOCKET,
+ "Write error on Unix socket \"%s\": %s; reconnecting...",
+ log_ctx->filename, strerror(errno));
+ fclose(log_ctx->fp);
+ log_ctx->fp = NULL;
+ log_ctx->reconn_timer = 0;
+ disconnected = 1;
+ }
+
+ struct timeval tv;
+ uint64_t now;
+ gettimeofday(&tv, NULL);
+ now = (uint64_t)tv.tv_sec * 1000;
+ now += tv.tv_usec / 1000; /* msec resolution */
+ if (log_ctx->reconn_timer != 0 &&
+ (now - log_ctx->reconn_timer) < LOGFILE_RECONN_MIN_TIME) {
+ /* Don't bother to try reconnecting too often. */
+ return 0;
+ }
+ log_ctx->reconn_timer = now;
+
+ log_ctx->fp = SCLogOpenUnixSocketFp(log_ctx->filename, log_ctx->sock_type, 0);
+ if (log_ctx->fp) {
+ /* Connected at last (or reconnected) */
+ SCLogNotice("Reconnected socket \"%s\"", log_ctx->filename);
+ } else if (disconnected) {
+ SCLogWarning(SC_ERR_SOCKET, "Reconnect failed: %s (will keep trying)",
+ strerror(errno));
+ }
+
+ return log_ctx->fp ? 1 : 0;
+}
+
+/**
+ * \brief Write buffer to log file.
+ * \retval 0 on failure; otherwise, the return value of fwrite (number of
+ * characters successfully written).
+ */
+static int SCLogFileWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
+{
+ /* Check for rotation. */
+ if (log_ctx->rotation_flag) {
+ log_ctx->rotation_flag = 0;
+ SCConfLogReopen(log_ctx);
+ }
+
+ int ret = 0;
+
+ if (log_ctx->fp == NULL && log_ctx->is_sock)
+ SCLogUnixSocketReconnect(log_ctx);
+
+ if (log_ctx->fp) {
+ clearerr(log_ctx->fp);
+ ret = fwrite(buffer, buffer_len, 1, log_ctx->fp);
+ fflush(log_ctx->fp);
+
+ if (ferror(log_ctx->fp) && log_ctx->is_sock) {
+ /* Error on Unix socket, maybe needs reconnect */
+ if (SCLogUnixSocketReconnect(log_ctx)) {
+ ret = fwrite(buffer, buffer_len, 1, log_ctx->fp);
+ fflush(log_ctx->fp);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void SCLogFileClose(LogFileCtx *log_ctx)
+{
+ if (log_ctx->fp)
+ fclose(log_ctx->fp);
+}
+
+/** \brief open the indicated file, logging any errors
+ * \param path filesystem path to open
+ * \param append_setting open file with O_APPEND: "yes" or "no"
+ * \retval FILE* on success
+ * \retval NULL on error
+ */
+static FILE *
+SCLogOpenFileFp(const char *path, const char *append_setting)
+{
+ FILE *ret = NULL;
+
+ if (strcasecmp(append_setting, "yes") == 0) {
+ ret = fopen(path, "a");
+ } else {
+ ret = fopen(path, "w");
+ }
+
+ if (ret == NULL)
+ SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s",
+ path, strerror(errno));
+ return ret;
+}
+
+/** \brief open the indicated file remotely over PCIe to a host
+ * \param path filesystem path to open
+ * \param append_setting open file with O_APPEND: "yes" or "no"
+ * \retval FILE* on success
+ * \retval NULL on error
+ */
+static PcieFile *SCLogOpenPcieFp(LogFileCtx *log_ctx, const char *path,
+ const char *append_setting)
+{
+#ifndef __tile__
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
+ "PCIe logging only supported on Tile-Gx Architecture.");
+ return NULL;
+#else
+ return TileOpenPcieFp(log_ctx, path, append_setting);
+#endif
+}
+
+/** \brief open a generic output "log file", which may be a regular file or a socket
+ * \param conf ConfNode structure for the output section in question
+ * \param log_ctx Log file context allocated by caller
+ * \param default_filename Default name of file to open, if not specified in ConfNode
+ * \param rotate Register the file for rotation in HUP.
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int
+SCConfLogOpenGeneric(ConfNode *conf,
+ LogFileCtx *log_ctx,
+ const char *default_filename,
+ int rotate)
+{
+ char log_path[PATH_MAX];
+ char *log_dir;
+ const char *filename, *filetype;
+
+ // Arg check
+ if (conf == NULL || log_ctx == NULL || default_filename == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "SCConfLogOpenGeneric(conf %p, ctx %p, default %p) "
+ "missing an argument",
+ conf, log_ctx, default_filename);
+ return -1;
+ }
+ if (log_ctx->fp != NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "SCConfLogOpenGeneric: previously initialized Log CTX "
+ "encountered");
+ return -1;
+ }
+
+ // Resolve the given config
+ filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename == NULL)
+ filename = default_filename;
+
+ log_dir = ConfigGetLogDirectory();
+
+ if (PathIsAbsolute(filename)) {
+ snprintf(log_path, PATH_MAX, "%s", filename);
+ } else {
+ snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename);
+ }
+
+ filetype = ConfNodeLookupChildValue(conf, "filetype");
+ if (filetype == NULL)
+ filetype = DEFAULT_LOG_FILETYPE;
+
+ const char *append = ConfNodeLookupChildValue(conf, "append");
+ if (append == NULL)
+ append = DEFAULT_LOG_MODE_APPEND;
+
+ // Now, what have we been asked to open?
+ if (strcasecmp(filetype, "unix_stream") == 0) {
+ /* Don't bail. May be able to connect later. */
+ log_ctx->is_sock = 1;
+ log_ctx->sock_type = SOCK_STREAM;
+ log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1);
+ } else if (strcasecmp(filetype, "unix_dgram") == 0) {
+ /* Don't bail. May be able to connect later. */
+ log_ctx->is_sock = 1;
+ log_ctx->sock_type = SOCK_DGRAM;
+ log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1);
+ } else if (strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 ||
+ strcasecmp(filetype, "file") == 0) {
+ log_ctx->fp = SCLogOpenFileFp(log_path, append);
+ if (log_ctx->fp == NULL)
+ return -1; // Error already logged by Open...Fp routine
+ log_ctx->is_regular = 1;
+ if (rotate) {
+ OutputRegisterFileRotationFlag(&log_ctx->rotation_flag);
+ }
+ } else if (strcasecmp(filetype, "pcie") == 0) {
+ log_ctx->pcie_fp = SCLogOpenPcieFp(log_ctx, log_path, append);
+ if (log_ctx->pcie_fp == NULL)
+ return -1; // Error already logged by Open...Fp routine
+ } else {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for "
+ "%s.filetype. Expected \"regular\" (default), \"unix_stream\", "
+ "\"pcie\" "
+ "or \"unix_dgram\"",
+ conf->name);
+ }
+ log_ctx->filename = SCStrdup(log_path);
+ if (unlikely(log_ctx->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC,
+ "Failed to allocate memory for filename");
+ return -1;
+ }
+
+ SCLogInfo("%s output device (%s) initialized: %s", conf->name, filetype,
+ filename);
+
+ return 0;
+}
+
+/**
+ * \brief Reopen a regular log file with the side-affect of truncating it.
+ *
+ * This is useful to clear the log file and start a new one, or to
+ * re-open the file after its been moved by something external
+ * (eg. logrotate).
+ */
+int SCConfLogReopen(LogFileCtx *log_ctx)
+{
+ if (!log_ctx->is_regular) {
+ /* Not supported and not needed on non-regular files. */
+ return 0;
+ }
+
+ if (log_ctx->filename == NULL) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT,
+ "Can't re-open LogFileCtx without a filename.");
+ return -1;
+ }
+
+ fclose(log_ctx->fp);
+
+ /* Reopen the file. Append is forced in case the file was not
+ * moved as part of a rotation process. */
+ SCLogDebug("Reopening log file %s.", log_ctx->filename);
+ log_ctx->fp = SCLogOpenFileFp(log_ctx->filename, "yes");
+ if (log_ctx->fp == NULL) {
+ return -1; // Already logged by Open..Fp routine.
+ }
+
+ return 0;
+}
+
+/** \brief LogFileNewCtx() Get a new LogFileCtx
+ * \retval LogFileCtx * pointer if succesful, NULL if error
+ * */
+LogFileCtx *LogFileNewCtx(void)
+{
+ LogFileCtx* lf_ctx;
+ lf_ctx = (LogFileCtx*)SCMalloc(sizeof(LogFileCtx));
+
+ if (lf_ctx == NULL)
+ return NULL;
+ memset(lf_ctx, 0, sizeof(LogFileCtx));
+
+ SCMutexInit(&lf_ctx->fp_mutex,NULL);
+
+ // Default Write and Close functions
+ lf_ctx->Write = SCLogFileWrite;
+ lf_ctx->Close = SCLogFileClose;
+
+ return lf_ctx;
+}
+
+/** \brief LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
+ * \param motcx pointer to the OutputCtx
+ * \retval int 1 if succesful, 0 if error
+ * */
+int LogFileFreeCtx(LogFileCtx *lf_ctx)
+{
+ if (lf_ctx == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (lf_ctx->fp != NULL) {
+ SCMutexLock(&lf_ctx->fp_mutex);
+ lf_ctx->Close(lf_ctx);
+ SCMutexUnlock(&lf_ctx->fp_mutex);
+ }
+
+ SCMutexDestroy(&lf_ctx->fp_mutex);
+
+ if (lf_ctx->prefix != NULL)
+ SCFree(lf_ctx->prefix);
+
+ if(lf_ctx->filename != NULL)
+ SCFree(lf_ctx->filename);
+
+ OutputUnregisterFileRotationFlag(&lf_ctx->rotation_flag);
+
+ SCFree(lf_ctx);
+
+ SCReturnInt(1);
+}
diff --git a/framework/src/suricata/src/util-logopenfile.h b/framework/src/suricata/src/util-logopenfile.h
new file mode 100644
index 00000000..d345475d
--- /dev/null
+++ b/framework/src/suricata/src/util-logopenfile.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Mike Pomraning <mpomraning@qualys.com>
+ */
+
+#ifndef __UTIL_LOGOPENFILE_H__
+#define __UTIL_LOGOPENFILE_H__
+
+#include "conf.h" /* ConfNode */
+#include "tm-modules.h" /* LogFileCtx */
+
+typedef struct {
+ uint16_t fileno;
+} PcieFile;
+
+enum LogFileType { LOGFILE_TYPE_FILE,
+ LOGFILE_TYPE_SYSLOG,
+ LOGFILE_TYPE_UNIX_DGRAM,
+ LOGFILE_TYPE_UNIX_STREAM };
+
+/** Global structure for Output Context */
+typedef struct LogFileCtx_ {
+ union {
+ FILE *fp;
+ PcieFile *pcie_fp;
+ };
+
+ int (*Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp);
+ void (*Close)(struct LogFileCtx_ *fp);
+
+ /** It will be locked if the log/alert
+ * record cannot be written to the file in one call */
+ SCMutex fp_mutex;
+
+ /** the type of file */
+ enum LogFileType type;
+
+ /** The name of the file */
+ char *filename;
+
+ /** Handle auto-connecting / reconnecting sockets */
+ int is_sock;
+ int sock_type;
+ uint64_t reconn_timer;
+
+ /**< Used by some alert loggers like the unified ones that append
+ * the date onto the end of files. */
+ char *prefix;
+
+ /** Generic size_limit and size_current
+ * They must be common to the threads accesing the same file */
+ uint64_t size_limit; /**< file size limit */
+ uint64_t size_current; /**< file current size */
+
+ /* Alerts on the module (not on the file) */
+ uint64_t alerts;
+ /* flag to avoid multiple threads printing the same stats */
+ uint8_t flags;
+
+ /* Flag if file is a regular file or not. Only regular files
+ * allow for rotataion. */
+ uint8_t is_regular;
+
+ /* Flag set when file rotation notification is received. */
+ int rotation_flag;
+} LogFileCtx;
+
+/* Min time (msecs) before trying to reconnect a Unix domain socket */
+#define LOGFILE_RECONN_MIN_TIME 500
+
+/* flags for LogFileCtx */
+#define LOGFILE_HEADER_WRITTEN 0x01
+#define LOGFILE_ALERTS_PRINTED 0x02
+
+LogFileCtx *LogFileNewCtx(void);
+int LogFileFreeCtx(LogFileCtx *);
+
+int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *, const char *, int);
+int SCConfLogReopen(LogFileCtx *);
+
+#endif /* __UTIL_LOGOPENFILE_H__ */
diff --git a/framework/src/suricata/src/util-lua-common.c b/framework/src/suricata/src/util-lua-common.c
new file mode 100644
index 00000000..82013485
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-common.c
@@ -0,0 +1,772 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Common function for Lua Output
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+
+int LuaCallbackError(lua_State *luastate, const char *msg)
+{
+ lua_pushnil(luastate);
+ lua_pushstring(luastate, msg);
+ return 2;
+}
+
+const char *LuaGetStringArgument(lua_State *luastate, int argc)
+{
+ /* get argument */
+ if (!lua_isstring(luastate, argc))
+ return NULL;
+ const char *str = lua_tostring(luastate, argc);
+ if (str == NULL)
+ return NULL;
+ if (strlen(str) == 0)
+ return NULL;
+ return str;
+}
+
+void LuaPushTableKeyValueInt(lua_State *luastate, const char *key, int value)
+{
+ lua_pushstring(luastate, key);
+ lua_pushnumber(luastate, value);
+ lua_settable(luastate, -3);
+}
+
+/** \brief Push a key plus string value to the stack
+ *
+ * If value is NULL, string "(null")" will be put on the stack.
+ */
+void LuaPushTableKeyValueString(lua_State *luastate, const char *key, const char *value)
+{
+ lua_pushstring(luastate, key);
+ lua_pushstring(luastate, value ? value : "(null)");
+ lua_settable(luastate, -3);
+}
+
+void LuaPushTableKeyValueArray(lua_State *luastate, const char *key, const uint8_t *value, size_t len)
+{
+ lua_pushstring(luastate, key);
+ LuaPushStringBuffer(luastate, value, len);
+ lua_settable(luastate, -3);
+}
+
+/** \internal
+ * \brief fill lua stack with payload
+ * \param luastate the lua state
+ * \param p packet
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: payload (string)
+ */
+static int LuaCallbackStreamingBufferPushToStack(lua_State *luastate, const LuaStreamingBuffer *b)
+{
+ //PrintRawDataFp(stdout, (uint8_t *)b->data, b->data_len);
+ lua_pushlstring (luastate, (const char *)b->data, b->data_len);
+ lua_pushboolean (luastate, (b->flags & OUTPUT_STREAMING_FLAG_OPEN));
+ lua_pushboolean (luastate, (b->flags & OUTPUT_STREAMING_FLAG_CLOSE));
+ return 3;
+}
+
+/** \internal
+ * \brief Wrapper for getting payload into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackStreamingBuffer(lua_State *luastate)
+{
+ const LuaStreamingBuffer *b = LuaStateGetStreamingBuffer(luastate);
+ if (b == NULL)
+ return LuaCallbackError(luastate, "internal error: no buffer");
+
+ return LuaCallbackStreamingBufferPushToStack(luastate, b);
+}
+
+/** \internal
+ * \brief fill lua stack with payload
+ * \param luastate the lua state
+ * \param p packet
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: payload (string)
+ */
+static int LuaCallbackPacketPayloadPushToStackFromPacket(lua_State *luastate, const Packet *p)
+{
+ lua_pushlstring (luastate, (const char *)p->payload, p->payload_len);
+ return 1;
+}
+
+/** \internal
+ * \brief Wrapper for getting payload into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackPacketPayload(lua_State *luastate)
+{
+ const Packet *p = LuaStateGetPacket(luastate);
+ if (p == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackPacketPayloadPushToStackFromPacket(luastate, p);
+}
+
+/** \internal
+ * \brief fill lua stack with header info
+ * \param luastate the lua state
+ * \param p packet
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: ts (string)
+ */
+static int LuaCallbackTimeStringPushToStackFromPacket(lua_State *luastate, const Packet *p)
+{
+ char timebuf[64];
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+ lua_pushstring (luastate, timebuf);
+ return 1;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackPacketTimeString(lua_State *luastate)
+{
+ const Packet *p = LuaStateGetPacket(luastate);
+ if (p == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackTimeStringPushToStackFromPacket(luastate, p);
+}
+
+/** \internal
+ * \brief fill lua stack with time string
+ * \param luastate the lua state
+ * \param flow flow
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: ts (string)
+ */
+static int LuaCallbackTimeStringPushToStackFromFlow(lua_State *luastate, const Flow *flow)
+{
+ char timebuf[64];
+ CreateTimeString(&flow->startts, timebuf, sizeof(timebuf));
+ lua_pushstring (luastate, timebuf);
+ return 1;
+}
+
+/** \internal
+ * \brief Wrapper for getting ts info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackFlowTimeString(lua_State *luastate)
+{
+ int r = 0;
+ int locked = 0;
+ Flow *flow = LuaStateGetFlow(luastate, &locked);
+ if (flow == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (locked == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(flow);
+ r = LuaCallbackTimeStringPushToStackFromFlow(luastate, flow);
+ FLOWLOCK_UNLOCK(flow);
+ } else {
+ r = LuaCallbackTimeStringPushToStackFromFlow(luastate, flow);
+ }
+ return r;
+}
+
+/** \internal
+ * \brief fill lua stack with header info
+ * \param luastate the lua state
+ * \param p packet
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: ipver (number), src ip (string), dst ip (string), protocol (number),
+ * sp or icmp type (number), dp or icmp code (number).
+ */
+static int LuaCallbackTuplePushToStackFromPacket(lua_State *luastate, const Packet *p)
+{
+ int ipver = 0;
+ if (PKT_IS_IPV4(p)) {
+ ipver = 4;
+ } else if (PKT_IS_IPV6(p)) {
+ ipver = 6;
+ }
+ lua_pushnumber (luastate, ipver);
+ if (ipver == 0)
+ return 1;
+
+ char srcip[46] = "", dstip[46] = "";
+ if (PKT_IS_IPV4(p)) {
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ } else if (PKT_IS_IPV6(p)) {
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ }
+
+ lua_pushstring (luastate, srcip);
+ lua_pushstring (luastate, dstip);
+
+ /* proto and ports (or type/code) */
+ lua_pushnumber (luastate, p->proto);
+ if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP) {
+ lua_pushnumber (luastate, p->sp);
+ lua_pushnumber (luastate, p->dp);
+
+ } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) {
+ lua_pushnumber (luastate, p->type);
+ lua_pushnumber (luastate, p->code);
+ } else {
+ lua_pushnumber (luastate, 0);
+ lua_pushnumber (luastate, 0);
+ }
+
+ return 6;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackTuple(lua_State *luastate)
+{
+ const Packet *p = LuaStateGetPacket(luastate);
+ if (p == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackTuplePushToStackFromPacket(luastate, p);
+}
+
+/** \internal
+ * \brief fill lua stack with header info
+ * \param luastate the lua state
+ * \param f flow, locked
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: ipver (number), src ip (string), dst ip (string), protocol (number),
+ * sp or icmp type (number), dp or icmp code (number).
+ */
+static int LuaCallbackTuplePushToStackFromFlow(lua_State *luastate, const Flow *f)
+{
+ int ipver = 0;
+ if (FLOW_IS_IPV4(f)) {
+ ipver = 4;
+ } else if (FLOW_IS_IPV6(f)) {
+ ipver = 6;
+ }
+ lua_pushnumber (luastate, ipver);
+ if (ipver == 0)
+ return 1;
+
+ char srcip[46] = "", dstip[46] = "";
+ if (FLOW_IS_IPV4(f)) {
+ PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
+ } else if (FLOW_IS_IPV6(f)) {
+ PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
+ }
+
+ lua_pushstring (luastate, srcip);
+ lua_pushstring (luastate, dstip);
+
+ /* proto and ports (or type/code) */
+ lua_pushnumber (luastate, f->proto);
+ if (f->proto == IPPROTO_TCP || f->proto == IPPROTO_UDP) {
+ lua_pushnumber (luastate, f->sp);
+ lua_pushnumber (luastate, f->dp);
+
+ } else if (f->proto == IPPROTO_ICMP || f->proto == IPPROTO_ICMPV6) {
+ lua_pushnumber (luastate, f->type);
+ lua_pushnumber (luastate, f->code);
+ } else {
+ lua_pushnumber (luastate, 0);
+ lua_pushnumber (luastate, 0);
+ }
+
+ return 6;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackTupleFlow(lua_State *luastate)
+{
+ int r = 0;
+ int lock_hint = 0;
+ Flow *f = LuaStateGetFlow(luastate, &lock_hint);
+ if (f == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(f);
+ r = LuaCallbackTuplePushToStackFromFlow(luastate, f);
+ FLOWLOCK_UNLOCK(f);
+ } else {
+ r = LuaCallbackTuplePushToStackFromFlow(luastate, f);
+ }
+ return r;
+}
+
+/** \internal
+ * \brief fill lua stack with AppLayerProto
+ * \param luastate the lua state
+ * \param f flow, locked
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: alproto as string (string)
+ */
+static int LuaCallbackAppLayerProtoPushToStackFromFlow(lua_State *luastate, const Flow *f)
+{
+ const char *string = AppProtoToString(f->alproto);
+ if (string == NULL)
+ string = "unknown";
+ lua_pushstring(luastate, string);
+ return 1;
+}
+
+/** \internal
+ * \brief Wrapper for getting AppLayerProto info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackAppLayerProtoFlow(lua_State *luastate)
+{
+ int r = 0;
+ int lock_hint = 0;
+ Flow *f = LuaStateGetFlow(luastate, &lock_hint);
+ if (f == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(f);
+ r = LuaCallbackAppLayerProtoPushToStackFromFlow(luastate, f);
+ FLOWLOCK_UNLOCK(f);
+ } else {
+ r = LuaCallbackAppLayerProtoPushToStackFromFlow(luastate, f);
+ }
+ return r;
+}
+
+/** \internal
+ * \brief fill lua stack with flow stats
+ * \param luastate the lua state
+ * \param f flow, locked
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: ts pkts (number), ts bytes (number), tc pkts (number), tc bytes (number)
+ */
+static int LuaCallbackStatsPushToStackFromFlow(lua_State *luastate, const Flow *f)
+{
+ lua_pushnumber(luastate, f->todstpktcnt);
+ lua_pushnumber(luastate, f->todstbytecnt);
+ lua_pushnumber(luastate, f->tosrcpktcnt);
+ lua_pushnumber(luastate, f->tosrcbytecnt);
+ return 4;
+}
+
+/** \internal
+ * \brief Wrapper for getting AppLayerProto info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackStatsFlow(lua_State *luastate)
+{
+ int r = 0;
+ int lock_hint = 0;
+ Flow *f = LuaStateGetFlow(luastate, &lock_hint);
+ if (f == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(f);
+ r = LuaCallbackStatsPushToStackFromFlow(luastate, f);
+ FLOWLOCK_UNLOCK(f);
+ } else {
+ r = LuaCallbackStatsPushToStackFromFlow(luastate, f);
+ }
+ return r;
+}
+
+/** \internal
+ * \brief fill lua stack with alert info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: sid (number), rev (number), gid (number)
+ */
+static int LuaCallbackRuleIdsPushToStackFromPacketAlert(lua_State *luastate, const PacketAlert *pa)
+{
+ lua_pushnumber (luastate, pa->s->id);
+ lua_pushnumber (luastate, pa->s->rev);
+ lua_pushnumber (luastate, pa->s->gid);
+ return 3;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackRuleIds(lua_State *luastate)
+{
+ const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
+ if (pa == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackRuleIdsPushToStackFromPacketAlert(luastate, pa);
+}
+
+/** \internal
+ * \brief fill lua stack with alert info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: msg (string)
+ */
+static int LuaCallbackRuleMsgPushToStackFromPacketAlert(lua_State *luastate, const PacketAlert *pa)
+{
+ lua_pushstring (luastate, pa->s->msg);
+ return 1;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackRuleMsg(lua_State *luastate)
+{
+ const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
+ if (pa == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackRuleMsgPushToStackFromPacketAlert(luastate, pa);
+}
+
+/** \internal
+ * \brief fill lua stack with alert info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: class (string), prio (number)
+ */
+static int LuaCallbackRuleClassPushToStackFromPacketAlert(lua_State *luastate, const PacketAlert *pa)
+{
+ lua_pushstring (luastate, pa->s->class_msg);
+ lua_pushnumber (luastate, pa->s->prio);
+ return 2;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackRuleClass(lua_State *luastate)
+{
+ const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
+ if (pa == NULL)
+ return LuaCallbackError(luastate, "internal error: no packet");
+
+ return LuaCallbackRuleClassPushToStackFromPacketAlert(luastate, pa);
+}
+
+static int LuaCallbackLogPath(lua_State *luastate)
+{
+ const char *ld = ConfigGetLogDirectory();
+ if (ld == NULL)
+ return LuaCallbackError(luastate, "internal error: no log dir");
+
+ return LuaPushStringBuffer(luastate, (const uint8_t *)ld, strlen(ld));
+}
+
+static int LuaCallbackLogDebug(lua_State *luastate)
+{
+ const char *msg = LuaGetStringArgument(luastate, 1);
+ if (msg == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+ SCLogDebug("%s", msg);
+ return 0;
+}
+
+static int LuaCallbackLogInfo(lua_State *luastate)
+{
+ const char *msg = LuaGetStringArgument(luastate, 1);
+ if (msg == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+ SCLogInfo("%s", msg);
+ return 0;
+}
+
+static int LuaCallbackLogNotice(lua_State *luastate)
+{
+ const char *msg = LuaGetStringArgument(luastate, 1);
+ if (msg == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+ SCLogNotice("%s", msg);
+ return 0;
+}
+
+static int LuaCallbackLogWarning(lua_State *luastate)
+{
+ const char *msg = LuaGetStringArgument(luastate, 1);
+ if (msg == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+ SCLogWarning(SC_WARN_LUA_SCRIPT, "%s", msg);
+ return 0;
+}
+
+static int LuaCallbackLogError(lua_State *luastate)
+{
+ const char *msg = LuaGetStringArgument(luastate, 1);
+ if (msg == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+ SCLogError(SC_ERR_LUA_SCRIPT, "%s", msg);
+ return 0;
+}
+
+/** \internal
+ * \brief fill lua stack with file info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: fileid (number), txid (number), name (string),
+ * size (number), magic (string), md5 in hex (string)
+ */
+static int LuaCallbackFileInfoPushToStackFromFile(lua_State *luastate, const File *file)
+{
+#ifdef HAVE_NSS
+ char md5[33] = "";
+ char *md5ptr = md5;
+ if (file->flags & FILE_MD5) {
+ size_t x;
+ for (x = 0; x < sizeof(file->md5); x++) {
+ char one[3] = "";
+ snprintf(one, sizeof(one), "%02x", file->md5[x]);
+ strlcat(md5, one, sizeof(md5));
+ }
+ }
+#else
+ char *md5ptr = NULL;
+#endif
+
+ lua_pushnumber(luastate, file->file_id);
+ lua_pushnumber(luastate, file->txid);
+ lua_pushlstring(luastate, (char *)file->name, file->name_len);
+ lua_pushnumber(luastate, file->size);
+ lua_pushstring (luastate, file->magic);
+ lua_pushstring(luastate, md5ptr);
+ return 6;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackFileInfo(lua_State *luastate)
+{
+ const File *file = LuaStateGetFile(luastate);
+ if (file == NULL)
+ return LuaCallbackError(luastate, "internal error: no file");
+
+ return LuaCallbackFileInfoPushToStackFromFile(luastate, file);
+}
+
+/** \internal
+ * \brief fill lua stack with file info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: state (string), stored (bool)
+ */
+static int LuaCallbackFileStatePushToStackFromFile(lua_State *luastate, const File *file)
+{
+ const char *state = "UNKNOWN";
+ switch (file->state) {
+ case FILE_STATE_CLOSED:
+ state = "CLOSED";
+ break;
+ case FILE_STATE_TRUNCATED:
+ state = "TRUNCATED";
+ break;
+ case FILE_STATE_ERROR:
+ state = "ERROR";
+ break;
+ }
+
+ lua_pushstring (luastate, state);
+ lua_pushboolean (luastate, file->flags & FILE_STORED);
+ return 2;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackFileState(lua_State *luastate)
+{
+ const File *file = LuaStateGetFile(luastate);
+ if (file == NULL)
+ return LuaCallbackError(luastate, "internal error: no file");
+
+ return LuaCallbackFileStatePushToStackFromFile(luastate, file);
+}
+
+/** \internal
+ * \brief fill lua stack with thread info
+ * \param luastate the lua state
+ * \param pa pointer to packet alert struct
+ * \retval cnt number of data items placed on the stack
+ *
+ * Places: thread id (number), thread name (string, thread group name (string)
+ */
+static int LuaCallbackThreadInfoPushToStackFromThreadVars(lua_State *luastate, const ThreadVars *tv)
+{
+ u_long tid = SCGetThreadIdLong();
+ lua_pushinteger (luastate, (lua_Integer)tid);
+ lua_pushstring (luastate, tv->name);
+ lua_pushstring (luastate, tv->thread_group_name);
+ return 3;
+}
+
+/** \internal
+ * \brief Wrapper for getting tuple info into a lua script
+ * \retval cnt number of items placed on the stack
+ */
+static int LuaCallbackThreadInfo(lua_State *luastate)
+{
+ const ThreadVars *tv = LuaStateGetThreadVars(luastate);
+ if (tv == NULL)
+ return LuaCallbackError(luastate, "internal error: no tv");
+
+ return LuaCallbackThreadInfoPushToStackFromThreadVars(luastate, tv);
+}
+
+int LuaRegisterFunctions(lua_State *luastate)
+{
+ /* registration of the callbacks */
+ lua_pushcfunction(luastate, LuaCallbackPacketPayload);
+ lua_setglobal(luastate, "SCPacketPayload");
+ lua_pushcfunction(luastate, LuaCallbackPacketTimeString);
+ lua_setglobal(luastate, "SCPacketTimeString");
+ lua_pushcfunction(luastate, LuaCallbackTuple);
+ lua_setglobal(luastate, "SCPacketTuple");
+
+ lua_pushcfunction(luastate, LuaCallbackFlowTimeString);
+ lua_setglobal(luastate, "SCFlowTimeString");
+ lua_pushcfunction(luastate, LuaCallbackTupleFlow);
+ lua_setglobal(luastate, "SCFlowTuple");
+ lua_pushcfunction(luastate, LuaCallbackAppLayerProtoFlow);
+ lua_setglobal(luastate, "SCFlowAppLayerProto");
+ lua_pushcfunction(luastate, LuaCallbackStatsFlow);
+ lua_setglobal(luastate, "SCFlowStats");
+
+ lua_pushcfunction(luastate, LuaCallbackStreamingBuffer);
+ lua_setglobal(luastate, "SCStreamingBuffer");
+
+ lua_pushcfunction(luastate, LuaCallbackLogPath);
+ lua_setglobal(luastate, "SCLogPath");
+
+ lua_pushcfunction(luastate, LuaCallbackLogDebug);
+ lua_setglobal(luastate, "SCLogDebug");
+ lua_pushcfunction(luastate, LuaCallbackLogInfo);
+ lua_setglobal(luastate, "SCLogInfo");
+ lua_pushcfunction(luastate, LuaCallbackLogNotice);
+ lua_setglobal(luastate, "SCLogNotice");
+ lua_pushcfunction(luastate, LuaCallbackLogWarning);
+ lua_setglobal(luastate, "SCLogWarning");
+ lua_pushcfunction(luastate, LuaCallbackLogError);
+ lua_setglobal(luastate, "SCLogError");
+
+
+ lua_pushcfunction(luastate, LuaCallbackRuleIds);
+ lua_setglobal(luastate, "SCRuleIds");
+ lua_pushcfunction(luastate, LuaCallbackRuleMsg);
+ lua_setglobal(luastate, "SCRuleMsg");
+ lua_pushcfunction(luastate, LuaCallbackRuleClass);
+ lua_setglobal(luastate, "SCRuleClass");
+
+ lua_pushcfunction(luastate, LuaCallbackFileInfo);
+ lua_setglobal(luastate, "SCFileInfo");
+ lua_pushcfunction(luastate, LuaCallbackFileState);
+ lua_setglobal(luastate, "SCFileState");
+
+ lua_pushcfunction(luastate, LuaCallbackThreadInfo);
+ lua_setglobal(luastate, "SCThreadInfo");
+ return 0;
+}
+
+int LuaStateNeedProto(lua_State *luastate, AppProto alproto)
+{
+ AppProto flow_alproto = 0;
+ int locked = 0;
+ Flow *flow = LuaStateGetFlow(luastate, &locked);
+ if (flow == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (locked == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(flow);
+ flow_alproto = flow->alproto;
+ FLOWLOCK_UNLOCK(flow);
+ } else {
+ flow_alproto = flow->alproto;
+ }
+
+ return (alproto == flow_alproto);
+
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/util-lua-common.h b/framework/src/suricata/src/util-lua-common.h
new file mode 100644
index 00000000..2e0df287
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-common.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_LUA_COMMON_H__
+#define __UTIL_LUA_COMMON_H__
+
+#ifdef HAVE_LUA
+
+int LuaCallbackError(lua_State *luastate, const char *msg);
+const char *LuaGetStringArgument(lua_State *luastate, int argc);
+
+void LuaPushTableKeyValueInt(lua_State *luastate, const char *key, int value);
+void LuaPushTableKeyValueString(lua_State *luastate, const char *key, const char *value);
+void LuaPushTableKeyValueArray(lua_State *luastate, const char *key, const uint8_t *value, size_t len);
+
+int LuaRegisterFunctions(lua_State *luastate);
+
+int LuaStateNeedProto(lua_State *luastate, AppProto alproto);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_COMMON_H__ */
diff --git a/framework/src/suricata/src/util-lua-dns.c b/framework/src/suricata/src/util-lua-dns.c
new file mode 100644
index 00000000..dcebc6a1
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-dns.c
@@ -0,0 +1,321 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-dns-common.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+
+static int DnsGetDnsRrname(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ char *c;
+ size_t input_len;
+ c = BytesToString((uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry)), query->len);
+ if (c != NULL) {
+ int ret;
+ input_len = strlen(c);
+ /* sanity check */
+ if (input_len > (size_t)(2 * query->len)) {
+ SCFree(c);
+ return LuaCallbackError(luastate, "invalid length");
+ }
+ ret = LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
+ SCFree(c);
+ return ret;
+ }
+ }
+
+ return LuaCallbackError(luastate, "no query");
+}
+
+static int DnsGetTxid(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ lua_pushinteger(luastate, tx->tx_id);
+ return 1;
+}
+
+static int DnsGetRcode(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ if (tx->rcode) {
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ return LuaPushStringBuffer(luastate, (const uint8_t *)rcode, strlen(rcode));
+ } else {
+ return 0;
+ }
+}
+
+static int DnsGetRecursionDesired(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ lua_pushboolean(luastate, tx->recursion_desired);
+ return 1;
+}
+
+static int DnsGetQueryTable(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ uint32_t u = 0;
+ lua_newtable(luastate);
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ lua_pushinteger(luastate, u++);
+
+ lua_newtable(luastate);
+ char record[16] = "";
+ DNSCreateTypeString(query->type, record, sizeof(record));
+ lua_pushstring(luastate, "type");
+ lua_pushstring(luastate, record);
+ lua_settable(luastate, -3);
+
+ {
+ char *c;
+ size_t input_len;
+ c = BytesToString((uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry)), query->len);
+ if (c != NULL) {
+ input_len = strlen(c);
+ /* sanity check */
+ if (input_len > (size_t)(2 * query->len)) {
+ SCFree(c);
+ return LuaCallbackError(luastate, "invalid length");
+ }
+ lua_pushstring(luastate, "rrname");
+ LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
+ lua_settable(luastate, -3);
+ SCFree(c);
+ }
+ }
+
+
+ lua_settable(luastate, -3);
+ }
+
+ return 1;
+}
+
+static int DnsGetAnswerTable(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ uint32_t u = 0;
+ lua_newtable(luastate);
+ DNSAnswerEntry *answer = NULL;
+ TAILQ_FOREACH(answer, &tx->answer_list, next) {
+ lua_pushinteger(luastate, u++);
+
+ lua_newtable(luastate);
+ char record[16] = "";
+ DNSCreateTypeString(answer->type, record, sizeof(record));
+ lua_pushstring(luastate, "type");
+ lua_pushstring(luastate, record);
+ lua_settable(luastate, -3);
+
+ lua_pushstring(luastate, "ttl");
+ lua_pushinteger(luastate, answer->ttl);
+ lua_settable(luastate, -3);
+
+ {
+ uint8_t *ptr = (uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry));
+ lua_pushstring(luastate, "rrname");
+ LuaPushStringBuffer(luastate, ptr, answer->fqdn_len);
+ lua_settable(luastate, -3);
+
+ ptr = (uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry) + answer->fqdn_len);
+ if (answer->type == DNS_RECORD_TYPE_A) {
+ char a[16] = "";
+ PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
+ lua_pushstring(luastate, "addr");
+ LuaPushStringBuffer(luastate, (uint8_t *)a, strlen(a));
+ lua_settable(luastate, -3);
+ } else if (answer->type == DNS_RECORD_TYPE_AAAA) {
+ char a[46];
+ PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
+ lua_pushstring(luastate, "addr");
+ LuaPushStringBuffer(luastate, (uint8_t *)a, strlen(a));
+ lua_settable(luastate, -3);
+ } else if (answer->data_len == 0) {
+ /* not setting 'addr' */
+ } else {
+ lua_pushstring(luastate, "addr");
+ LuaPushStringBuffer(luastate, (uint8_t *)ptr, answer->data_len);
+ lua_settable(luastate, -3);
+ }
+ }
+
+ lua_settable(luastate, -3);
+ }
+
+ return 1;
+}
+
+static int DnsGetAuthorityTable(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+ return LuaCallbackError(luastate, "error: protocol not dns");
+
+ DNSTransaction *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ uint32_t u = 0;
+ lua_newtable(luastate);
+ DNSAnswerEntry *answer = NULL;
+ TAILQ_FOREACH(answer, &tx->authority_list, next) {
+ lua_pushinteger(luastate, u++);
+
+ lua_newtable(luastate);
+ char record[16] = "";
+ DNSCreateTypeString(answer->type, record, sizeof(record));
+ lua_pushstring(luastate, "type");
+ lua_pushstring(luastate, record);
+ lua_settable(luastate, -3);
+
+ lua_pushstring(luastate, "ttl");
+ lua_pushinteger(luastate, answer->ttl);
+ lua_settable(luastate, -3);
+
+ {
+ char *c;
+ size_t input_len;
+ c = BytesToString((uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry)), answer->fqdn_len);
+ if (c != NULL) {
+ input_len = strlen(c);
+ /* sanity check */
+ if (input_len > (size_t)(2 * answer->fqdn_len)) {
+ SCFree(c);
+ return LuaCallbackError(luastate, "invalid length");
+ }
+ lua_pushstring(luastate, "rrname");
+ LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
+ lua_settable(luastate, -3);
+ SCFree(c);
+ }
+ }
+
+
+ lua_settable(luastate, -3);
+ }
+
+ return 1;
+}
+
+
+/** \brief register http lua extensions in a luastate */
+int LuaRegisterDnsFunctions(lua_State *luastate)
+{
+ /* registration of the callbacks */
+ lua_pushcfunction(luastate, DnsGetDnsRrname);
+ lua_setglobal(luastate, "DnsGetDnsRrname");
+
+ lua_pushcfunction(luastate, DnsGetQueryTable);
+ lua_setglobal(luastate, "DnsGetQueries");
+
+ lua_pushcfunction(luastate, DnsGetAnswerTable);
+ lua_setglobal(luastate, "DnsGetAnswers");
+
+ lua_pushcfunction(luastate, DnsGetAuthorityTable);
+ lua_setglobal(luastate, "DnsGetAuthorities");
+
+ lua_pushcfunction(luastate, DnsGetTxid);
+ lua_setglobal(luastate, "DnsGetTxid");
+
+ lua_pushcfunction(luastate, DnsGetRcode);
+ lua_setglobal(luastate, "DnsGetRcode");
+
+ lua_pushcfunction(luastate, DnsGetRecursionDesired);
+ lua_setglobal(luastate, "DnsGetRecursionDesired");
+ return 0;
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/util-lua-dns.h b/framework/src/suricata/src/util-lua-dns.h
new file mode 100644
index 00000000..582fdea7
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-dns.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_LUA_DNS_H__
+#define __UTIL_LUA_DNS_H__
+
+#ifdef HAVE_LUA
+
+int LuaRegisterDnsFunctions(lua_State *luastate);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_HTTP_H__ */
diff --git a/framework/src/suricata/src/util-lua-http.c b/framework/src/suricata/src/util-lua-http.c
new file mode 100644
index 00000000..3d97b0f6
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-http.c
@@ -0,0 +1,348 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+
+static int HttpGetRequestHost(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ if (tx->request_hostname == NULL)
+ return LuaCallbackError(luastate, "no request hostname");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(tx->request_hostname), bstr_len(tx->request_hostname));
+}
+
+static int HttpGetRequestUriRaw(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ if (tx->request_uri == NULL)
+ return LuaCallbackError(luastate, "no request uri");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(tx->request_uri), bstr_len(tx->request_uri));
+}
+
+static int HttpGetRequestUriNormalized(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud == NULL)
+ return LuaCallbackError(luastate, "no htud in tx");
+
+ if (htud->request_uri_normalized == NULL ||
+ bstr_ptr(htud->request_uri_normalized) == NULL ||
+ bstr_len(htud->request_uri_normalized) == 0)
+ return LuaCallbackError(luastate, "no normalized uri");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(htud->request_uri_normalized),
+ bstr_len(htud->request_uri_normalized));
+}
+
+static int HttpGetRequestLine(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ if (tx->request_line == NULL)
+ return LuaCallbackError(luastate, "no request_line");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(tx->request_line), bstr_len(tx->request_line));
+}
+
+static int HttpGetResponseLine(lua_State *luastate)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ if (tx->response_line == NULL)
+ return LuaCallbackError(luastate, "no response_line");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(tx->response_line), bstr_len(tx->response_line));
+}
+
+static int HttpGetHeader(lua_State *luastate, int dir)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ const char *name = LuaGetStringArgument(luastate, 1);
+ if (name == NULL)
+ return LuaCallbackError(luastate, "1st argument missing, empty or wrong type");
+
+ htp_table_t *headers = tx->request_headers;
+ if (dir == 1)
+ headers = tx->response_headers;
+ if (headers == NULL)
+ return LuaCallbackError(luastate, "tx has no headers");
+
+ htp_header_t *h = (htp_header_t *)htp_table_get_c(headers, name);
+ if (h == NULL || bstr_len(h->value) == 0)
+ return LuaCallbackError(luastate, "header not found");
+
+ return LuaPushStringBuffer(luastate,
+ bstr_ptr(h->value), bstr_len(h->value));
+}
+
+static int HttpGetRequestHeader(lua_State *luastate)
+{
+ return HttpGetHeader(luastate, 0 /* request */);
+}
+
+static int HttpGetResponseHeader(lua_State *luastate)
+{
+ return HttpGetHeader(luastate, 1 /* response */);
+}
+
+static int HttpGetRawHeaders(lua_State *luastate, int dir)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud == NULL)
+ return LuaCallbackError(luastate, "no htud in tx");
+
+ uint8_t *raw = htud->request_headers_raw;
+ uint32_t raw_len = htud->request_headers_raw_len;
+ if (dir == 1) {
+ raw = htud->response_headers_raw;
+ raw_len = htud->response_headers_raw_len;
+ }
+
+ if (raw == NULL || raw_len == 0)
+ return LuaCallbackError(luastate, "no raw headers");
+
+ return LuaPushStringBuffer(luastate, raw, raw_len);
+}
+
+static int HttpGetRawRequestHeaders(lua_State *luastate)
+{
+ return HttpGetRawHeaders(luastate, 0);
+}
+
+static int HttpGetRawResponseHeaders(lua_State *luastate)
+{
+ return HttpGetRawHeaders(luastate, 1);
+}
+
+
+static int HttpGetHeaders(lua_State *luastate, int dir)
+{
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ htp_table_t *table = tx->request_headers;
+ if (dir == 1)
+ table = tx->response_headers;
+ if (tx->request_headers == NULL)
+ return LuaCallbackError(luastate, "no headers");
+
+ lua_newtable(luastate);
+ htp_header_t *h = NULL;
+ size_t i = 0;
+ size_t no_of_headers = htp_table_size(table);
+ for (; i < no_of_headers; i++) {
+ h = htp_table_get_index(table, i, NULL);
+ LuaPushStringBuffer(luastate, bstr_ptr(h->name), bstr_len(h->name));
+ LuaPushStringBuffer(luastate, bstr_ptr(h->value), bstr_len(h->value));
+ lua_settable(luastate, -3);
+ }
+ return 1;
+}
+
+/** \brief return request headers as lua table */
+static int HttpGetRequestHeaders(lua_State *luastate)
+{
+ return HttpGetHeaders(luastate, 0);
+}
+
+/** \brief return response headers as lua table */
+static int HttpGetResponseHeaders(lua_State *luastate)
+{
+ return HttpGetHeaders(luastate, 1);
+}
+
+static int HttpGetBody(lua_State *luastate, int dir)
+{
+ HtpBody *body = NULL;
+
+ if (!(LuaStateNeedProto(luastate, ALPROTO_HTTP)))
+ return LuaCallbackError(luastate, "error: protocol not http");
+
+ htp_tx_t *tx = LuaStateGetTX(luastate);
+ if (tx == NULL)
+ return LuaCallbackError(luastate, "internal error: no tx");
+
+ HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
+ if (htud == NULL)
+ return LuaCallbackError(luastate, "no htud in tx");
+
+ if (dir == 0)
+ body = &htud->request_body;
+ else
+ body = &htud->response_body;
+
+ if (body->first == NULL)
+ return LuaCallbackError(luastate, "no body");
+
+ int index = 1;
+ HtpBodyChunk *chunk = body->first;
+ lua_newtable(luastate);
+ while (chunk != NULL) {
+ lua_pushinteger(luastate, index);
+ LuaPushStringBuffer(luastate, chunk->data, chunk->len);
+ lua_settable(luastate, -3);
+
+ chunk = chunk->next;
+ index++;
+ }
+
+ if (body->first && body->last) {
+ lua_pushinteger(luastate, body->first->stream_offset);
+ lua_pushinteger(luastate, body->last->stream_offset + body->last->len);
+ return 3;
+ } else {
+ return 1;
+ }
+}
+
+static int HttpGetRequestBody(lua_State *luastate)
+{
+ return HttpGetBody(luastate, 0);
+}
+
+static int HttpGetResponseBody(lua_State *luastate)
+{
+ return HttpGetBody(luastate, 1);
+}
+
+/** \brief register http lua extensions in a luastate */
+int LuaRegisterHttpFunctions(lua_State *luastate)
+{
+ /* registration of the callbacks */
+ lua_pushcfunction(luastate, HttpGetRequestHeader);
+ lua_setglobal(luastate, "HttpGetRequestHeader");
+ lua_pushcfunction(luastate, HttpGetResponseHeader);
+ lua_setglobal(luastate, "HttpGetResponseHeader");
+ lua_pushcfunction(luastate, HttpGetRequestLine);
+ lua_setglobal(luastate, "HttpGetRequestLine");
+ lua_pushcfunction(luastate, HttpGetResponseLine);
+ lua_setglobal(luastate, "HttpGetResponseLine");
+ lua_pushcfunction(luastate, HttpGetRawRequestHeaders);
+ lua_setglobal(luastate, "HttpGetRawRequestHeaders");
+ lua_pushcfunction(luastate, HttpGetRawResponseHeaders);
+ lua_setglobal(luastate, "HttpGetRawResponseHeaders");
+ lua_pushcfunction(luastate, HttpGetRequestUriRaw);
+ lua_setglobal(luastate, "HttpGetRequestUriRaw");
+ lua_pushcfunction(luastate, HttpGetRequestUriNormalized);
+ lua_setglobal(luastate, "HttpGetRequestUriNormalized");
+ lua_pushcfunction(luastate, HttpGetRequestHeaders);
+ lua_setglobal(luastate, "HttpGetRequestHeaders");
+ lua_pushcfunction(luastate, HttpGetResponseHeaders);
+ lua_setglobal(luastate, "HttpGetResponseHeaders");
+ lua_pushcfunction(luastate, HttpGetRequestHost);
+ lua_setglobal(luastate, "HttpGetRequestHost");
+
+ lua_pushcfunction(luastate, HttpGetRequestBody);
+ lua_setglobal(luastate, "HttpGetRequestBody");
+ lua_pushcfunction(luastate, HttpGetResponseBody);
+ lua_setglobal(luastate, "HttpGetResponseBody");
+ return 0;
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/util-lua-http.h b/framework/src/suricata/src/util-lua-http.h
new file mode 100644
index 00000000..8a75ec53
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-http.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_LUA_HTTP_H__
+#define __UTIL_LUA_HTTP_H__
+
+#ifdef HAVE_LUA
+
+int LuaRegisterHttpFunctions(lua_State *luastate);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_HTTP_H__ */
diff --git a/framework/src/suricata/src/util-lua-tls.c b/framework/src/suricata/src/util-lua-tls.c
new file mode 100644
index 00000000..8816d5d5
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-tls.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-ssl.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+
+static int GetCertInfo(lua_State *luastate, const Flow *f, int direction)
+{
+ void *state = FlowGetAppState(f);
+ if (state == NULL)
+ return LuaCallbackError(luastate, "error: no app layer state");
+
+ SSLState *ssl_state = (SSLState *)state;
+ SSLStateConnp *connp = NULL;
+
+ if (direction) {
+ connp = &ssl_state->client_connp;
+ } else {
+ connp = &ssl_state->server_connp;
+ }
+
+ if (connp->cert0_subject == NULL)
+ return LuaCallbackError(luastate, "error: no cert");
+
+ /* tls.version */
+ char ssl_version[32] = "";
+ switch (ssl_state->server_connp.version) {
+ case TLS_VERSION_UNKNOWN:
+ snprintf(ssl_version, sizeof(ssl_version), "UNDETERMINED");
+ break;
+ case SSL_VERSION_2:
+ snprintf(ssl_version, sizeof(ssl_version), "SSLv2");
+ break;
+ case SSL_VERSION_3:
+ snprintf(ssl_version, sizeof(ssl_version), "SSLv3");
+ break;
+ case TLS_VERSION_10:
+ snprintf(ssl_version, sizeof(ssl_version), "TLSv1");
+ break;
+ case TLS_VERSION_11:
+ snprintf(ssl_version, sizeof(ssl_version), "TLS 1.1");
+ break;
+ case TLS_VERSION_12:
+ snprintf(ssl_version, sizeof(ssl_version), "TLS 1.2");
+ break;
+ default:
+ snprintf(ssl_version, sizeof(ssl_version), "0x%04x",
+ ssl_state->server_connp.version);
+ break;
+ }
+
+ int r = LuaPushStringBuffer(luastate, (uint8_t *)ssl_version, strlen(ssl_version));
+ r += LuaPushStringBuffer(luastate, (uint8_t *)connp->cert0_subject, strlen(connp->cert0_subject));
+ r += LuaPushStringBuffer(luastate, (uint8_t *)connp->cert0_issuerdn, strlen(connp->cert0_issuerdn));
+ r += LuaPushStringBuffer(luastate, (uint8_t *)connp->cert0_fingerprint, strlen(connp->cert0_fingerprint));
+ return r;
+}
+
+static int TlsGetCertInfo(lua_State *luastate)
+{
+ int r;
+
+ if (!(LuaStateNeedProto(luastate, ALPROTO_TLS)))
+ return LuaCallbackError(luastate, "error: protocol not tls");
+
+ int direction = LuaStateGetDirection(luastate);
+
+ int lock_hint = 0;
+ Flow *f = LuaStateGetFlow(luastate, &lock_hint);
+ if (f == NULL)
+ return LuaCallbackError(luastate, "internal error: no flow");
+
+ if (lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+ FLOWLOCK_RDLOCK(f);
+ r = GetCertInfo(luastate, f, direction);
+ FLOWLOCK_UNLOCK(f);
+ } else {
+ r = GetCertInfo(luastate, f, direction);
+ }
+ return r;
+}
+
+/** \brief register tls lua extensions in a luastate */
+int LuaRegisterTlsFunctions(lua_State *luastate)
+{
+ /* registration of the callbacks */
+ lua_pushcfunction(luastate, TlsGetCertInfo);
+ lua_setglobal(luastate, "TlsGetCertInfo");
+ return 0;
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/util-lua-tls.h b/framework/src/suricata/src/util-lua-tls.h
new file mode 100644
index 00000000..57a27b55
--- /dev/null
+++ b/framework/src/suricata/src/util-lua-tls.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_LUA_TLS_H__
+#define __UTIL_LUA_TLS_H__
+
+#ifdef HAVE_LUA
+
+int LuaRegisterTlsFunctions(lua_State *luastate);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_TLS_H__ */
diff --git a/framework/src/suricata/src/util-lua.c b/framework/src/suricata/src/util-lua.c
new file mode 100644
index 00000000..32d206c3
--- /dev/null
+++ b/framework/src/suricata/src/util-lua.c
@@ -0,0 +1,276 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Common function for Lua
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+
+#include "output.h"
+#include "app-layer-htp.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+#include "util-proto-name.h"
+#include "util-logopenfile.h"
+#include "util-time.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "util-lua.h"
+
+/* key for tv (threadvars) pointer */
+const char lua_ext_key_tv[] = "suricata:lua:tv:ptr";
+/* key for tx pointer */
+const char lua_ext_key_tx[] = "suricata:lua:tx:ptr";
+/* key for p (packet) pointer */
+const char lua_ext_key_p[] = "suricata:lua:pkt:ptr";
+/* key for f (flow) pointer */
+const char lua_ext_key_flow[] = "suricata:lua:flow:ptr";
+/* key for flow lock hint bool */
+const char lua_ext_key_flow_lock_hint[] = "suricata:lua:flow:lock_hint";
+/* key for direction */
+const char lua_ext_key_direction[] = "suricata:lua:direction";
+
+/* key for pa (packet alert) pointer */
+const char lua_ext_key_pa[] = "suricata:lua:pkt:alert:ptr";
+/* key for file pointer */
+const char lua_ext_key_file[] = "suricata:lua:file:ptr";
+/* key for streaming buffer pointer */
+const char lua_ext_key_streaming_buffer[] = "suricata:lua:streaming_buffer:ptr";
+
+/** \brief get tv pointer from the lua state */
+ThreadVars *LuaStateGetThreadVars(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tv);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *tv = lua_touserdata(luastate, -1);
+ return (ThreadVars *)tv;
+}
+
+void LuaStateSetThreadVars(lua_State *luastate, ThreadVars *tv)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tv);
+ lua_pushlightuserdata(luastate, (void *)tv);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief get packet pointer from the lua state */
+Packet *LuaStateGetPacket(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_p);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *p = lua_touserdata(luastate, -1);
+ return (Packet *)p;
+}
+
+void LuaStateSetPacket(lua_State *luastate, Packet *p)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_p);
+ lua_pushlightuserdata(luastate, (void *)p);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief get tx pointer from the lua state */
+void *LuaStateGetTX(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *tx = lua_touserdata(luastate, -1);
+ return tx;
+}
+
+void LuaStateSetTX(lua_State *luastate, void *txptr)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx);
+ lua_pushlightuserdata(luastate, (void *)txptr);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+Flow *LuaStateGetFlow(lua_State *luastate, int *lock_hint)
+{
+ Flow *f = NULL;
+ int need_flow_lock = 0;
+
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_flow);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ f = lua_touserdata(luastate, -1);
+
+ /* need flow lock hint */
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_flow_lock_hint);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ need_flow_lock = lua_toboolean(luastate, -1);
+
+ *lock_hint = need_flow_lock;
+ return f;
+}
+
+void LuaStateSetFlow(lua_State *luastate, Flow *f, int need_flow_lock)
+{
+ /* flow */
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_flow);
+ lua_pushlightuserdata(luastate, (void *)f);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+
+ /* flow lock status hint */
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_flow_lock_hint);
+ lua_pushboolean(luastate, need_flow_lock);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief get packet alert pointer from the lua state */
+PacketAlert *LuaStateGetPacketAlert(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_pa);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *pa = lua_touserdata(luastate, -1);
+ return (PacketAlert *)pa;
+}
+
+void LuaStateSetPacketAlert(lua_State *luastate, PacketAlert *pa)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_pa);
+ lua_pushlightuserdata(luastate, (void *)pa);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief get file pointer from the lua state */
+File *LuaStateGetFile(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_file);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *file = lua_touserdata(luastate, -1);
+ return (File *)file;
+}
+
+void LuaStateSetFile(lua_State *luastate, File *file)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_file);
+ lua_pushlightuserdata(luastate, (void *)file);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+LuaStreamingBuffer *LuaStateGetStreamingBuffer(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_streaming_buffer);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ void *b = lua_touserdata(luastate, -1);
+ return (LuaStreamingBuffer *)b;
+}
+
+void LuaStateSetStreamingBuffer(lua_State *luastate, LuaStreamingBuffer *b)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_streaming_buffer);
+ lua_pushlightuserdata(luastate, (void *)b);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief get packet pointer from the lua state */
+int LuaStateGetDirection(lua_State *luastate)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction);
+ lua_gettable(luastate, LUA_REGISTRYINDEX);
+ int dir = lua_toboolean(luastate, -1);
+ return dir;
+}
+
+void LuaStateSetDirection(lua_State *luastate, int direction)
+{
+ lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction);
+ lua_pushboolean(luastate, direction);
+ lua_settable(luastate, LUA_REGISTRYINDEX);
+}
+
+/** \brief dump stack from lua state to screen */
+void LuaPrintStack(lua_State *state) {
+ int size = lua_gettop(state);
+ int i;
+
+ for (i = 1; i <= size; i++) {
+ int type = lua_type(state, i);
+ printf("Stack size=%d, level=%d, type=%d, ", size, i, type);
+
+ switch (type) {
+ case LUA_TFUNCTION:
+ printf("function %s", lua_tostring(state, i) ? "true" : "false");
+ break;
+ case LUA_TBOOLEAN:
+ printf("bool %s", lua_toboolean(state, i) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf("number %g", lua_tonumber(state, i));
+ break;
+ case LUA_TSTRING:
+ printf("string `%s'", lua_tostring(state, i));
+ break;
+ case LUA_TTABLE:
+ printf("table `%s'", lua_tostring(state, i));
+ break;
+ default:
+ printf("other %s", lua_typename(state, type));
+ break;
+
+ }
+ printf("\n");
+ }
+}
+
+int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
+{
+ if (input_len % 4 != 0) {
+ /* we're using a buffer sized at a multiple of 4 as lua_pushlstring generates
+ * invalid read errors in valgrind otherwise. Adding in a nul to be sure.
+ *
+ * Buffer size = len + 1 (for nul) + whatever makes it a multiple of 4 */
+ size_t buflen = input_len + 1 + ((input_len + 1) % 4);
+ uint8_t buf[buflen];
+ memset(buf, 0x00, buflen);
+ memcpy(buf, input, input_len);
+ buf[input_len] = '\0';
+
+ /* return value through luastate, as a luastring */
+ lua_pushlstring(luastate, (char *)buf, input_len);
+ } else {
+ lua_pushlstring(luastate, (char *)input, input_len);
+ }
+ return 1;
+}
+
+#endif /* HAVE_LUA */
diff --git a/framework/src/suricata/src/util-lua.h b/framework/src/suricata/src/util-lua.h
new file mode 100644
index 00000000..4ea4c605
--- /dev/null
+++ b/framework/src/suricata/src/util-lua.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_LUA_H__
+#define __UTIL_LUA_H__
+
+#ifdef HAVE_LUA
+
+typedef struct LuaStreamingBuffer_ {
+ const uint8_t *data;
+ uint32_t data_len;
+ uint8_t flags;
+} LuaStreamingBuffer;
+
+#define LUA_FLOW_LOCKED_BY_PARENT 0
+#define LUA_FLOW_NOT_LOCKED_BY_PARENT 1
+
+/* gets */
+
+/** \brief get tv pointer from the lua state */
+ThreadVars *LuaStateGetThreadVars(lua_State *luastate);
+
+Packet *LuaStateGetPacket(lua_State *luastate);
+void *LuaStateGetTX(lua_State *luastate);
+
+/** \brief get flow pointer from lua state
+ *
+ * \param locked_by_parent[out] bool indicating if flow is locked
+ * (LUA_FLOW_LOCKED_BY_PARENT) or unlocked
+ * (LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ *
+ * \retval f flow poiner or NULL if it was not set
+ */
+Flow *LuaStateGetFlow(lua_State *luastate, int *locked_by_parent);
+
+PacketAlert *LuaStateGetPacketAlert(lua_State *luastate);
+
+/** \brief get file pointer from the lua state */
+File *LuaStateGetFile(lua_State *luastate);
+
+LuaStreamingBuffer *LuaStateGetStreamingBuffer(lua_State *luastate);
+
+int LuaStateGetDirection(lua_State *luastate);
+
+/* sets */
+
+void LuaStateSetPacket(lua_State *luastate, Packet *p);
+void LuaStateSetTX(lua_State *luastate, void *tx);
+
+/** \brief set a flow pointer in the lua state
+ *
+ * \param f flow pointer
+ * \param locked_by_parent bool indicating if flow is locked
+ * (LUA_FLOW_LOCKED_BY_PARENT) or unlocked
+ * (LUA_FLOW_NOT_LOCKED_BY_PARENT)
+ */
+void LuaStateSetFlow(lua_State *luastate, Flow *f, int locked_by_parent);
+
+void LuaStateSetPacketAlert(lua_State *luastate, PacketAlert *pa);
+
+void LuaStateSetFile(lua_State *luastate, File *file);
+
+void LuaStateSetThreadVars(lua_State *luastate, ThreadVars *tv);
+
+void LuaStateSetStreamingBuffer(lua_State *luastate, LuaStreamingBuffer *b);
+
+void LuaStateSetDirection(lua_State *luastate, int direction);
+
+void LuaPrintStack(lua_State *state);
+
+int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_H__ */
diff --git a/framework/src/suricata/src/util-magic.c b/framework/src/suricata/src/util-magic.c
new file mode 100644
index 00000000..fa05c6d9
--- /dev/null
+++ b/framework/src/suricata/src/util-magic.c
@@ -0,0 +1,676 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Wrappers and tests for libmagic usage.
+ *
+ * Libmagic's API is not thread safe. The data the pointer returned by
+ * magic_buffer is overwritten by the next magic_buffer call. This is
+ * why we need to lock calls and copy the returned string.
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "util-unittest.h"
+#include <magic.h>
+
+static magic_t g_magic_ctx = NULL;
+static SCMutex g_magic_lock;
+
+/**
+ * \brief Initialize the "magic" context.
+ */
+int MagicInit(void)
+{
+ BUG_ON(g_magic_ctx != NULL);
+
+ SCEnter();
+
+ char *filename = NULL;
+ FILE *fd = NULL;
+
+ SCMutexInit(&g_magic_lock, NULL);
+ SCMutexLock(&g_magic_lock);
+
+ g_magic_ctx = magic_open(0);
+ if (g_magic_ctx == NULL) {
+ SCLogError(SC_ERR_MAGIC_OPEN, "magic_open failed: %s", magic_error(g_magic_ctx));
+ goto error;
+ }
+
+ (void)ConfGet("magic-file", &filename);
+ if (filename != NULL) {
+ SCLogInfo("using magic-file %s", filename);
+
+ if ( (fd = fopen(filename, "r")) == NULL) {
+ SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
+ goto error;
+ }
+ fclose(fd);
+ }
+
+ if (magic_load(g_magic_ctx, filename) != 0) {
+ SCLogError(SC_ERR_MAGIC_LOAD, "magic_load failed: %s", magic_error(g_magic_ctx));
+ goto error;
+ }
+
+ SCMutexUnlock(&g_magic_lock);
+ SCReturnInt(0);
+
+error:
+ if (g_magic_ctx != NULL) {
+ magic_close(g_magic_ctx);
+ g_magic_ctx = NULL;
+ }
+
+ SCMutexUnlock(&g_magic_lock);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief Find the magic value for a buffer.
+ *
+ * \param buf the buffer
+ * \param buflen length of the buffer
+ *
+ * \retval result pointer to null terminated string
+ */
+char *MagicGlobalLookup(uint8_t *buf, uint32_t buflen)
+{
+ const char *result = NULL;
+ char *magic = NULL;
+
+ SCMutexLock(&g_magic_lock);
+
+ if (buf != NULL && buflen > 0) {
+ result = magic_buffer(g_magic_ctx, (void *)buf, (size_t)buflen);
+ if (result != NULL) {
+ magic = SCStrdup(result);
+ if (unlikely(magic == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup magic");
+ }
+ }
+ }
+
+ SCMutexUnlock(&g_magic_lock);
+ SCReturnPtr(magic, "const char");
+}
+
+/**
+ * \brief Find the magic value for a buffer.
+ *
+ * \param buf the buffer
+ * \param buflen length of the buffer
+ *
+ * \retval result pointer to null terminated string
+ */
+char *MagicThreadLookup(magic_t *ctx, uint8_t *buf, uint32_t buflen)
+{
+ const char *result = NULL;
+ char *magic = NULL;
+
+ if (buf != NULL && buflen > 0) {
+ result = magic_buffer(*ctx, (void *)buf, (size_t)buflen);
+ if (result != NULL) {
+ magic = SCStrdup(result);
+ if (unlikely(magic == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup magic");
+ }
+ }
+ }
+
+ SCReturnPtr(magic, "const char");
+}
+
+void MagicDeinit(void)
+{
+ SCMutexLock(&g_magic_lock);
+ if (g_magic_ctx != NULL) {
+ magic_close(g_magic_ctx);
+ g_magic_ctx = NULL;
+ }
+ SCMutexUnlock(&g_magic_lock);
+ SCMutexDestroy(&g_magic_lock);
+}
+
+#ifdef UNITTESTS
+
+#if defined OS_FREEBSD || defined OS_DARWIN
+#define MICROSOFT_OFFICE_DOC "OLE 2 Compound Document"
+#else
+#define MICROSOFT_OFFICE_DOC "Microsoft Office Document"
+#endif
+
+/** \test magic lib calls -- init */
+int MagicInitTest01(void)
+{
+ int result = 0;
+ magic_t magic_ctx;
+
+ magic_ctx = magic_open(0);
+ if (magic_ctx == NULL) {
+ printf("failure retrieving magic_ctx\n");
+ return 0;
+ }
+
+ if (magic_load(magic_ctx, NULL) == -1) {
+ printf("failure magic_load\n");
+ goto end;
+ }
+
+ result = 1;
+ end:
+ magic_close(magic_ctx);
+ return result;
+}
+
+/** \test magic init through api */
+int MagicInitTest02(void)
+{
+ if (g_magic_ctx != NULL) {
+ printf("g_magic_ctx != NULL at start of the test: ");
+ return 0;
+ }
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ if (g_magic_ctx == NULL) {
+ printf("g_magic_ctx == NULL: ");
+ return 0;
+ }
+
+ MagicDeinit();
+
+ if (g_magic_ctx != NULL) {
+ printf("g_magic_ctx != NULL at end of the test: ");
+ return 0;
+ }
+
+ return 1;
+}
+
+/** \test magic lib calls -- lookup */
+int MagicDetectTest01(void)
+{
+ magic_t magic_ctx;
+ char *result = NULL;
+ char buffer[] = { 0x25, 'P', 'D', 'F', '-', '1', '.', '3', 0x0d, 0x0a};
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ magic_ctx = magic_open(0);
+ if (magic_ctx == NULL) {
+ printf("failure retrieving magic_ctx\n");
+ return 0;
+ }
+
+ if (magic_load(magic_ctx, NULL) == -1) {
+ printf("magic_load failure\n");
+ goto end;
+ }
+
+ result = (char *)magic_buffer(magic_ctx, (void *)buffer, buffer_len);
+ if (result == NULL || strncmp(result, "PDF document", 12) != 0) {
+ printf("result %p:%s, not \"PDF document\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ magic_close(magic_ctx);
+ return retval;
+}
+#if 0
+/** \test magic lib calls -- lookup */
+int MagicDetectTest02(void)
+{
+ magic_t magic_ctx;
+ char *result = NULL;
+
+ char buffer[] = {
+ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x03, 0x00, 0xfe, 0xff, 0x09, 0x00,
+
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+
+ 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ magic_ctx = magic_open(0);
+ if (magic_ctx == NULL) {
+ printf("failure retrieving magic_ctx\n");
+ return 0;
+ }
+
+ if (magic_load(magic_ctx, NULL) == -1) {
+ printf("magic_load failure\n");
+ goto end;
+ }
+
+ result = (char *)magic_buffer(magic_ctx, (void *)buffer, buffer_len);
+ if (result == NULL || strcmp(result, MICROSOFT_OFFICE_DOC) != 0) {
+ printf("result %p:%s, not \"Microsoft Office Document\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ magic_close(magic_ctx);
+ return retval;
+}
+#endif
+/** \test magic lib calls -- lookup */
+int MagicDetectTest03(void)
+{
+ magic_t magic_ctx;
+ char *result = NULL;
+
+ char buffer[] = {
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0b, 0x55, 0x2a, 0x36, 0x5e, 0xc6,
+ 0x32, 0x0c, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6d, 0x69,
+
+ 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x6f, 0x61,
+ 0x73, 0x69, 0x73, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+
+ 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x2e, 0x74, 0x65, 0x78, 0x74, 0x50, 0x4b, 0x03,
+ 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
+ 0x55, 0x2a, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x32, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75,
+
+ 0x73, 0x62, 0x61, 0x72, 0x2f, 0x50, 0x4b, 0x03,
+ 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ magic_ctx = magic_open(0);
+ if (magic_ctx == NULL) {
+ printf("failure retrieving magic_ctx\n");
+ return 0;
+ }
+
+ if (magic_load(magic_ctx, NULL) == -1) {
+ printf("magic_load failure\n");
+ goto end;
+ }
+
+ result = (char *)magic_buffer(magic_ctx, (void *)buffer, buffer_len);
+ if (result == NULL || strcmp(result, "OpenDocument Text") != 0) {
+ printf("result %p:%s, not \"OpenDocument Text\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ magic_close(magic_ctx);
+ return retval;
+}
+
+/** \test magic lib calls -- lookup */
+int MagicDetectTest04(void)
+{
+ magic_t magic_ctx;
+ char *result = NULL;
+
+ char buffer[] = {
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x52, 0x7b, 0x86, 0x3c, 0x8b, 0x70,
+ 0x96, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6d, 0x69,
+
+ 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x73, 0x75,
+ 0x6e, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x62, 0x61,
+
+ 0x73, 0x65, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x52, 0x7b, 0x86, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+
+ 0x4d, 0x45, 0x54, 0x41, 0x2d, 0x49, 0x4e, 0x46,
+ 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00,
+ 0x08, 0x08, 0x00, 0xa8, 0x42, 0x1d, 0x37, 0x5d,
+ 0xa7, 0xb2, 0xc1, 0xde, 0x01, 0x00, 0x00, 0x7e,
+
+ 0x04, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x78,
+ 0x6d, 0x6c, 0x95, 0x54, 0x4d, 0x6f, 0xdb, 0x30,
+ 0x0c, 0xbd, 0xe7, 0x57, 0x18, 0x02, 0x06, 0x6c,
+
+ 0x07, 0xc5, 0xe9, 0xb6, 0xc3, 0x22, 0xc4, 0x29,
+ 0x86, 0x7d, 0x00, 0x05, 0x8a, 0x9d, 0xb2, 0x43,
+ 0x8f, 0xb2, 0x24, 0xa7, 0xc2, 0x64, 0xc9, 0x15,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ magic_ctx = magic_open(0);
+ if (magic_ctx == NULL) {
+ printf("failure retrieving magic_ctx\n");
+ return 0;
+ }
+
+ if (magic_load(magic_ctx, NULL) == -1) {
+ printf("magic_load failure\n");
+ goto end;
+ }
+
+ result = (char *)magic_buffer(magic_ctx, (void *)buffer, buffer_len);
+ if (result == NULL || strncmp(result, "OpenOffice.org 1.x", 18) != 0) {
+ printf("result %p:%s, not \"OpenOffice.org 1.x\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ magic_close(magic_ctx);
+ return retval;
+}
+
+/** \test magic api calls -- lookup */
+int MagicDetectTest05(void)
+{
+ const char *result = NULL;
+ uint8_t buffer[] = { 0x25, 'P', 'D', 'F', '-', '1', '.', '3', 0x0d, 0x0a};
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result = MagicGlobalLookup(buffer, buffer_len);
+ if (result == NULL || strncmp(result, "PDF document", 12) != 0) {
+ printf("result %p:%s, not \"PDF document\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ MagicDeinit();
+ return retval;
+}
+#if 0
+/** \test magic api calls -- lookup */
+int MagicDetectTest06(void)
+{
+ const char *result = NULL;
+ uint8_t buffer[] = {
+ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x03, 0x00, 0xfe, 0xff, 0x09, 0x00,
+
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+
+ 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result = MagicGlobalLookup(buffer, buffer_len);
+ if (result == NULL || strcmp(result, MICROSOFT_OFFICE_DOC) != 0) {
+ printf("result %p:%s, not \"Microsoft Office Document\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+
+end:
+ MagicDeinit();
+ return retval;
+}
+#endif
+/** \test magic api calls -- lookup */
+int MagicDetectTest07(void)
+{
+ const char *result = NULL;
+ uint8_t buffer[] = {
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0b, 0x55, 0x2a, 0x36, 0x5e, 0xc6,
+ 0x32, 0x0c, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6d, 0x69,
+
+ 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x6f, 0x61,
+ 0x73, 0x69, 0x73, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+
+ 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x2e, 0x74, 0x65, 0x78, 0x74, 0x50, 0x4b, 0x03,
+ 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
+ 0x55, 0x2a, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x32, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75,
+
+ 0x73, 0x62, 0x61, 0x72, 0x2f, 0x50, 0x4b, 0x03,
+ 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result = MagicGlobalLookup(buffer, buffer_len);
+ if (result == NULL || strcmp(result, "OpenDocument Text") != 0) {
+ printf("result %p:%s, not \"OpenDocument Text\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ MagicDeinit();
+ return retval;
+}
+
+/** \test magic api calls -- lookup */
+int MagicDetectTest08(void)
+{
+ const char *result = NULL;
+ uint8_t buffer[] = {
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x52, 0x7b, 0x86, 0x3c, 0x8b, 0x70,
+ 0x96, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6d, 0x69,
+
+ 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x73, 0x75,
+ 0x6e, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x62, 0x61,
+
+ 0x73, 0x65, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x52, 0x7b, 0x86, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+
+ 0x4d, 0x45, 0x54, 0x41, 0x2d, 0x49, 0x4e, 0x46,
+ 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00,
+ 0x08, 0x08, 0x00, 0xa8, 0x42, 0x1d, 0x37, 0x5d,
+ 0xa7, 0xb2, 0xc1, 0xde, 0x01, 0x00, 0x00, 0x7e,
+
+ 0x04, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x78,
+ 0x6d, 0x6c, 0x95, 0x54, 0x4d, 0x6f, 0xdb, 0x30,
+
+ 0x0c, 0xbd, 0xe7, 0x57, 0x18, 0x02, 0x06, 0x6c,
+ 0x07, 0xc5, 0xe9, 0xb6, 0xc3, 0x22, 0xc4, 0x29,
+ 0x86, 0x7d, 0x00, 0x05, 0x8a, 0x9d, 0xb2, 0x43,
+ 0x8f, 0xb2, 0x24, 0xa7, 0xc2, 0x64, 0xc9, 0x15,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result = MagicGlobalLookup(buffer, buffer_len);
+ if (result == NULL || strncmp(result, "OpenOffice.org 1.x", 18) != 0) {
+ printf("result %p:%s, not \"OpenOffice.org 1.x\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ MagicDeinit();
+ return retval;
+}
+
+/** \test magic api calls -- make sure memory is shared */
+int MagicDetectTest09(void)
+{
+ const char *result1 = NULL;
+ const char *result2 = NULL;
+ uint8_t buffer[] = { 0x25, 'P', 'D', 'F', '-', '1', '.', '3', 0x0d, 0x0a};
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result1 = MagicGlobalLookup(buffer, buffer_len);
+ if (result1 == NULL || strncmp(result1, "PDF document", 12) != 0) {
+ printf("result %p:%s, not \"PDF document\": ", result1,result1?result1:"(null)");
+ goto end;
+ }
+
+ result2 = MagicGlobalLookup(buffer, buffer_len);
+ if (result2 == NULL || strncmp(result2, "PDF document", 12) != 0) {
+ printf("result %p:%s, not \"PDF document\": ", result2,result2?result2:"(null)");
+ goto end;
+ }
+
+ if (result1 != result2) {
+ printf("pointers not equal, weird... %p != %p: ", result1, result2);
+ goto end;
+ }
+
+ retval = 1;
+end:
+ MagicDeinit();
+ return retval;
+}
+
+/** \test results in valgrind warning about invalid read, tested with
+ * file 5.09 and 5.11 */
+static int MagicDetectTest10ValgrindError(void)
+{
+ const char *result = NULL;
+ uint8_t buffer[] = {
+ 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2C,
+ 0x01,0x2C,0x00,0x00,0xFF,0xFE,0x00,0x4C,0x53,0x69,0x67,0x6E,0x61,0x74,0x75,0x72,
+ 0x65,0x3A,0x34,0x31,0x31,0x65,0x33,0x38,0x61,0x61,0x61,0x31,0x37,0x65,0x33,0x30,
+ 0x66,0x30,0x32,0x38,0x62,0x61,0x30,0x31,0x36,0x32,0x36,0x37,0x66,0x66,0x30,0x31,
+ 0x36,0x36,0x61,0x65,0x35,0x39,0x65,0x38,0x31,0x39,0x62,0x61,0x32,0x34,0x63,0x39,
+ 0x62,0x31,0x33,0x37,0x33,0x62,0x31,0x61,0x35,0x61,0x38,0x65,0x64,0x63,0x36,0x30,
+ 0x65,0x37,0xFF,0xE2,0x02,0x2C,0x49,0x43,0x43,0x5F,0x50,0x52,0x4F,0x46,0x49,0x4C,
+ 0x45,0x00,0x01,0x01,0x00,0x00,0x02,0x1C,0x41,0x44,0x42,0x45,0x02,0x10,0x00,0x00,
+ 0x6D,0x6E,0x74,0x72,0x52,0x47,0x42,0x20,0x58,0x59,0x5A,0x20,0x07,0xCF,0x00,0x05,
+ 0x00,0x09,0x00,0x15,0x00,0x0B,0x00,0x21,0x61,0x63,0x73,0x70,0x41,0x50,0x50,0x4C,
+ 0x00,0x00,0x00,0x00,0x6E,0x6F,0x6E,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ };
+ size_t buffer_len = sizeof(buffer);
+ int retval = 0;
+
+ if (MagicInit() < 0) {
+ printf("MagicInit() failure\n");
+ return 0;
+ }
+
+ result = MagicGlobalLookup(buffer, buffer_len);
+ if (result == NULL || strncmp(result, "JPEG", 4) != 0) {
+ printf("result %p:%s, not \"JPEG\": ", result,result?result:"(null)");
+ goto end;
+ }
+
+ retval = 1;
+end:
+ MagicDeinit();
+ return retval;
+}
+
+#endif /* UNITTESTS */
+
+
+void MagicRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("MagicInitTest01", MagicInitTest01, 1);
+ UtRegisterTest("MagicInitTest02", MagicInitTest02, 1);
+ UtRegisterTest("MagicDetectTest01", MagicDetectTest01, 1);
+ //UtRegisterTest("MagicDetectTest02", MagicDetectTest02, 1);
+ UtRegisterTest("MagicDetectTest03", MagicDetectTest03, 1);
+ UtRegisterTest("MagicDetectTest04", MagicDetectTest04, 1);
+ UtRegisterTest("MagicDetectTest05", MagicDetectTest05, 1);
+ //UtRegisterTest("MagicDetectTest06", MagicDetectTest06, 1);
+ UtRegisterTest("MagicDetectTest07", MagicDetectTest07, 1);
+ UtRegisterTest("MagicDetectTest08", MagicDetectTest08, 1);
+ /* fails in valgrind, somehow it returns different pointers then.
+ UtRegisterTest("MagicDetectTest09", MagicDetectTest09, 1); */
+
+ UtRegisterTest("MagicDetectTest10ValgrindError", MagicDetectTest10ValgrindError, 1);
+#endif /* UNITTESTS */
+}
diff --git a/framework/src/suricata/src/util-magic.h b/framework/src/suricata/src/util-magic.h
new file mode 100644
index 00000000..0efdbd14
--- /dev/null
+++ b/framework/src/suricata/src/util-magic.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_MAGIC_H__
+#define __UTIL_MAGIC_H__
+
+#include <magic.h>
+
+int MagicInit(void);
+void MagicDeinit(void);
+char *MagicGlobalLookup(uint8_t *, uint32_t);
+char *MagicThreadLookup(magic_t *, uint8_t *, uint32_t);
+void MagicRegisterTests(void);
+
+#endif /* __UTIL_MAGIC_H__ */
diff --git a/framework/src/suricata/src/util-mem.h b/framework/src/suricata/src/util-mem.h
new file mode 100644
index 00000000..b720ac36
--- /dev/null
+++ b/framework/src/suricata/src/util-mem.h
@@ -0,0 +1,313 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Utility Macros for memory management
+ *
+ * \todo Add wrappers for functions that allocate/free memory here.
+ * Currently we have malloc, calloc, realloc, strdup and free,
+ * but there are more.
+ */
+
+#ifndef __UTIL_MEM_H__
+#define __UTIL_MEM_H__
+
+#include "util-atomic.h"
+
+#if CPPCHECK==1
+#define SCMalloc malloc
+#define SCCalloc calloc
+#define SCRealloc realloc
+#define SCFree free
+#define SCStrdup strdup
+#define SCMallocAligned _mm_malloc
+#define SCFreeAligned _mm_free
+#else /* CPPCHECK */
+
+
+#if defined(_WIN32) || defined(__WIN32)
+#include "mm_malloc.h"
+#endif
+
+#if defined(__tile__)
+/* Need to define __mm_ function alternatives, since these are SSE only.
+ */
+#include <malloc.h>
+#define _mm_malloc(a,b) memalign((b),(a))
+#define _mm_free(a) free((a))
+#endif /* defined(__tile__) */
+
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+/* Use this only if you want to debug memory allocation and free()
+ * It will log a lot of lines more, so think that is a performance killer */
+
+/* Uncomment this if you want to print memory allocations and free's() */
+//#define DBG_MEM_ALLOC
+
+#ifdef DBG_MEM_ALLOC
+
+/* Uncomment this if you want to print mallocs at the startup (recommended) */
+#define DBG_MEM_ALLOC_SKIP_STARTUP
+
+#define SCMalloc(a) ({ \
+ void *ptrmem = NULL; \
+ extern size_t global_mem; \
+ extern uint8_t print_mem_flag; \
+ \
+ ptrmem = malloc((a)); \
+ if (ptrmem == NULL && (a) > 0) { \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)(a)); \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ \
+ global_mem += (a); \
+ if (print_mem_flag == 1) { \
+ SCLogInfo("SCMalloc return at %p of size %"PRIuMAX, \
+ ptrmem, (uintmax_t)(a)); \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCRealloc(x, a) ({ \
+ void *ptrmem = NULL; \
+ extern size_t global_mem; \
+ extern uint8_t print_mem_flag; \
+ \
+ ptrmem = realloc((x), (a)); \
+ if (ptrmem == NULL && (a) > 0) { \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)(a)); \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ \
+ global_mem += (a); \
+ if (print_mem_flag == 1) { \
+ SCLogInfo("SCRealloc return at %p (old:%p) of size %"PRIuMAX, \
+ ptrmem, (x), (uintmax_t)(a)); \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCCalloc(nm, a) ({ \
+ void *ptrmem = NULL; \
+ extern size_t global_mem; \
+ extern uint8_t print_mem_flag; \
+ \
+ ptrmem = calloc((nm), (a)); \
+ if (ptrmem == NULL && (a) > 0) { \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ \
+ global_mem += (a)*(nm); \
+ if (print_mem_flag == 1) { \
+ SCLogInfo("SCCalloc return at %p of size %"PRIuMAX" (nm) %"PRIuMAX, \
+ ptrmem, (uintmax_t)(a), (uintmax_t)(nm)); \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCStrdup(a) ({ \
+ char *ptrmem = NULL; \
+ extern size_t global_mem; \
+ extern uint8_t print_mem_flag; \
+ size_t len = strlen((a)); \
+ \
+ ptrmem = strdup((a)); \
+ if (ptrmem == NULL) { \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)len); \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ \
+ global_mem += len; \
+ if (print_mem_flag == 1) { \
+ SCLogInfo("SCStrdup return at %p of size %"PRIuMAX, \
+ ptrmem, (uintmax_t)len); \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCFree(a) ({ \
+ extern uint8_t print_mem_flag; \
+ if (print_mem_flag == 1) { \
+ SCLogInfo("SCFree at %p", (a)); \
+ } \
+ free((a)); \
+})
+
+#else /* !DBG_MEM_ALLOC */
+
+#define SCMalloc(a) ({ \
+ void *ptrmem = NULL; \
+ \
+ ptrmem = malloc((a)); \
+ if (ptrmem == NULL) { \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ uintmax_t scmalloc_size_ = (uintmax_t)(a); \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), scmalloc_size_); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCRealloc(x, a) ({ \
+ void *ptrmem = NULL; \
+ \
+ ptrmem = realloc((x), (a)); \
+ if (ptrmem == NULL) { \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)(a)); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCCalloc(nm, a) ({ \
+ void *ptrmem = NULL; \
+ \
+ ptrmem = calloc((nm), (a)); \
+ if (ptrmem == NULL) { \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)(a)); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCStrdup(a) ({ \
+ char *ptrmem = NULL; \
+ \
+ ptrmem = strdup((a)); \
+ if (ptrmem == NULL) { \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ size_t len = strlen((a)); \
+ SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)len); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+#define SCFree(a) ({ \
+ free(a); \
+})
+
+#if defined(__WIN32) || defined(_WIN32)
+
+/** \brief wrapper for allocing aligned mem
+ * \param a size
+ * \param b alignement
+ */
+#define SCMallocAligned(a, b) ({ \
+ void *ptrmem = NULL; \
+ \
+ ptrmem = _mm_malloc((a), (b)); \
+ if (ptrmem == NULL) { \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_MEM_ALLOC, "SCMallocAligned(posix_memalign) failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes, alignment %"PRIuMAX, strerror(errno), (uintmax_t)(a), (uintmax_t)(b)); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+/** \brief Free aligned memory
+ *
+ * Not needed for mem alloc'd by posix_memalign,
+ * but for possible future use of _mm_malloc needing
+ * _mm_free.
+ */
+#define SCFreeAligned(a) ({ \
+ _mm_free(a); \
+})
+
+#else /* !win */
+
+/** \brief wrapper for allocing aligned mem
+ * \param a size
+ * \param b alignement
+ */
+#define SCMallocAligned(a, b) ({ \
+ void *ptrmem = NULL; \
+ \
+ int r = posix_memalign(&ptrmem, (b), (a)); \
+ if (r != 0 || ptrmem == NULL) { \
+ if (ptrmem != NULL) { \
+ free(ptrmem); \
+ ptrmem = NULL; \
+ } \
+ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\
+ SCLogError(SC_ERR_MEM_ALLOC, "SCMallocAligned(posix_memalign) failed: %s, while trying " \
+ "to allocate %"PRIuMAX" bytes, alignment %"PRIuMAX, strerror(errno), (uintmax_t)a, (uintmax_t)b); \
+ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \
+ exit(EXIT_FAILURE); \
+ } \
+ } \
+ (void*)ptrmem; \
+})
+
+/** \brief Free aligned memory
+ *
+ * Not needed for mem alloc'd by posix_memalign,
+ * but for possible future use of _mm_malloc needing
+ * _mm_free.
+ */
+#define SCFreeAligned(a) ({ \
+ free(a); \
+})
+
+#endif /* __WIN32 */
+
+#endif /* DBG_MEM_ALLOC */
+
+#endif /* CPPCHECK */
+
+#endif /* __UTIL_MEM_H__ */
+
diff --git a/framework/src/suricata/src/util-memcmp.c b/framework/src/suricata/src/util-memcmp.c
new file mode 100644
index 00000000..c1b03490
--- /dev/null
+++ b/framework/src/suricata/src/util-memcmp.c
@@ -0,0 +1,407 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Memcmp implementations.
+ */
+
+#include "suricata-common.h"
+
+#include "util-memcmp.h"
+#include "util-unittest.h"
+
+/* code is implemented in util-memcmp.h as it's all inlined */
+
+/* UNITTESTS */
+#ifdef UNITTESTS
+
+static int MemcmpTest01 (void)
+{
+ uint8_t a[] = "abcd";
+ uint8_t b[] = "abcd";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest02 (void)
+{
+ uint8_t a[] = "abcdabcdabcdabcd";
+ uint8_t b[] = "abcdabcdabcdabcd";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest03 (void)
+{
+ uint8_t a[] = "abcdabcd";
+ uint8_t b[] = "abcdabcd";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest04 (void)
+{
+ uint8_t a[] = "abcd";
+ uint8_t b[] = "abcD";
+
+ int r = SCMemcmp(a, b, sizeof(a)-1);
+ if (r != 1) {
+ printf("%s != %s, but memcmp returned %d: ", a, b, r);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int MemcmpTest05 (void)
+{
+ uint8_t a[] = "abcdabcdabcdabcd";
+ uint8_t b[] = "abcDabcdabcdabcd";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest06 (void)
+{
+ uint8_t a[] = "abcdabcd";
+ uint8_t b[] = "abcDabcd";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest07 (void)
+{
+ uint8_t a[] = "abcd";
+ uint8_t b[] = "abcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest08 (void)
+{
+ uint8_t a[] = "abcdabcdabcdabcd";
+ uint8_t b[] = "abcdabcdabcdabcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest09 (void)
+{
+ uint8_t a[] = "abcdabcd";
+ uint8_t b[] = "abcdabcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest10 (void)
+{
+ uint8_t a[] = "abcd";
+ uint8_t b[] = "Zbcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest11 (void)
+{
+ uint8_t a[] = "abcdabcdabcdabcd";
+ uint8_t b[] = "Zbcdabcdabcdabcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest12 (void)
+{
+ uint8_t a[] = "abcdabcd";
+ uint8_t b[] = "Zbcdabcde";
+
+ if (SCMemcmp(a, b, sizeof(a)-1) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int MemcmpTest13 (void)
+{
+ uint8_t a[] = "abcdefgh";
+ uint8_t b[] = "AbCdEfGhIjK";
+
+ if (SCMemcmpLowercase(a, b, sizeof(a)-1) != 0)
+ return 0;
+
+ return 1;
+}
+
+#include "util-cpu.h"
+
+#define TEST_RUNS 1000000
+
+static int MemcmpTest14 (void)
+{
+#ifdef PROFILING
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+ char *a[] = { "0123456789012345", "abc", "abcdefghij", "suricata", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+ char *b[] = { "1234567890123456", "abc", "abcdefghik", "suricatb", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+
+ int t = 0;
+ int i, j;
+ int r1 = 0;
+
+ printf("\n");
+
+ ticks_start = UtilCpuGetTicks();
+ for (t = 0; t < TEST_RUNS; t++) {
+ for (i = 0; a[i] != NULL; i++) {
+ // printf("a[%d] = %s\n", i, a[i]);
+ size_t alen = strlen(a[i]) - 1;
+
+ for (j = 0; b[j] != NULL; j++) {
+ // printf("b[%d] = %s\n", j, b[j]);
+ size_t blen = strlen(b[j]) - 1;
+
+ r1 += (memcmp((uint8_t *)a[i], (uint8_t *)b[j], (alen < blen) ? alen : blen) ? 1 : 0);
+ }
+ }
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("memcmp(%d) \t\t\t%"PRIu64"\n", TEST_RUNS, ((uint64_t)(ticks_end - ticks_start))/TEST_RUNS);
+ SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start);
+
+ printf("r1 %d\n", r1);
+ if (r1 != (51 * TEST_RUNS))
+ return 0;
+#endif
+ return 1;
+}
+
+static int MemcmpTest15 (void)
+{
+#ifdef PROFILING
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+ char *a[] = { "0123456789012345", "abc", "abcdefghij", "suricata", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+ char *b[] = { "1234567890123456", "abc", "abcdefghik", "suricatb", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+
+ int t = 0;
+ int i, j;
+ int r2 = 0;
+
+ printf("\n");
+
+ ticks_start = UtilCpuGetTicks();
+ for (t = 0; t < TEST_RUNS; t++) {
+ for (i = 0; a[i] != NULL; i++) {
+ // printf("a[%d] = %s\n", i, a[i]);
+ size_t alen = strlen(a[i]) - 1;
+
+ for (j = 0; b[j] != NULL; j++) {
+ // printf("b[%d] = %s\n", j, b[j]);
+ size_t blen = strlen(b[j]) - 1;
+
+ r2 += MemcmpLowercase((uint8_t *)a[i], (uint8_t *)b[j], (alen < blen) ? alen : blen);
+ }
+ }
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("MemcmpLowercase(%d) \t\t%"PRIu64"\n", TEST_RUNS, ((uint64_t)(ticks_end - ticks_start))/TEST_RUNS);
+ SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start);
+
+ printf("r2 %d\n", r2);
+ if (r2 != (51 * TEST_RUNS))
+ return 0;
+#endif
+ return 1;
+}
+
+static int MemcmpTest16 (void)
+{
+#ifdef PROFILING
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+ char *a[] = { "0123456789012345", "abc", "abcdefghij", "suricata", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+ char *b[] = { "1234567890123456", "abc", "abcdefghik", "suricatb", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+
+ int t = 0;
+ int i, j;
+ int r3 = 0;
+
+ printf("\n");
+
+ ticks_start = UtilCpuGetTicks();
+ for (t = 0; t < TEST_RUNS; t++) {
+ for (i = 0; a[i] != NULL; i++) {
+ // printf("a[%d] = %s\n", i, a[i]);
+ size_t alen = strlen(a[i]) - 1;
+
+ for (j = 0; b[j] != NULL; j++) {
+ // printf("b[%d] = %s\n", j, b[j]);
+ size_t blen = strlen(b[j]) - 1;
+
+ r3 += SCMemcmp((uint8_t *)a[i], (uint8_t *)b[j], (alen < blen) ? alen : blen);
+ }
+ }
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMemcmp(%d) \t\t\t%"PRIu64"\n", TEST_RUNS, ((uint64_t)(ticks_end - ticks_start))/TEST_RUNS);
+ SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start);
+
+ printf("r3 %d\n", r3);
+ if (r3 != (51 * TEST_RUNS))
+ return 0;
+#endif
+ return 1;
+}
+
+static int MemcmpTest17 (void)
+{
+#ifdef PROFILING
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+ char *a[] = { "0123456789012345", "abc", "abcdefghij", "suricata", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+ char *b[] = { "1234567890123456", "abc", "abcdefghik", "suricatb", "test", "xyz", "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "abcdefghijklmnopqrstuvwxyz", NULL };
+
+ int t = 0;
+ int i, j;
+ int r4 = 0;
+
+ printf("\n");
+
+ ticks_start = UtilCpuGetTicks();
+ for (t = 0; t < TEST_RUNS; t++) {
+ for (i = 0; a[i] != NULL; i++) {
+ // printf("a[%d] = %s\n", i, a[i]);
+ size_t alen = strlen(a[i]) - 1;
+
+ for (j = 0; b[j] != NULL; j++) {
+ // printf("b[%d] = %s\n", j, b[j]);
+ size_t blen = strlen(b[j]) - 1;
+
+ r4 += SCMemcmpLowercase((uint8_t *)a[i], (uint8_t *)b[j], (alen < blen) ? alen : blen);
+ }
+ }
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMemcmpLowercase(%d) \t\t%"PRIu64"\n", TEST_RUNS, ((uint64_t)(ticks_end - ticks_start))/TEST_RUNS);
+ SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start);
+
+ printf("r4 %d\n", r4);
+ if (r4 != (51 * TEST_RUNS))
+ return 0;
+#endif
+ return 1;
+}
+
+struct MemcmpTest18Tests {
+ char *a;
+ char *b;
+ int result;
+} memcmp_tests18_tests[] = {
+ { "abcdefgh", "!bcdefgh", 1, },
+ { "?bcdefgh", "!bcdefgh", 1, },
+ { "!bcdefgh", "abcdefgh", 1, },
+ { "!bcdefgh", "?bcdefgh", 1, },
+ { "zbcdefgh", "bbcdefgh", 1, },
+
+ { "abcdefgh12345678", "!bcdefgh12345678", 1, },
+ { "?bcdefgh12345678", "!bcdefgh12345678", 1, },
+ { "!bcdefgh12345678", "abcdefgh12345678", 1, },
+ { "!bcdefgh12345678", "?bcdefgh12345678", 1, },
+ { "bbcdefgh12345678", "zbcdefgh12345678", 1, },
+
+ { "abcdefgh", "abcdefgh", 0, },
+ { "abcdefgh", "Abcdefgh", 0, },
+ { "abcdefgh12345678", "Abcdefgh12345678", 0, },
+
+ { NULL, NULL, 0 },
+
+ };
+
+static int MemcmpTest18 (void)
+{
+ struct MemcmpTest18Tests *t = memcmp_tests18_tests;
+
+ while (t && t->a != NULL) {
+
+ if (SCMemcmpLowercase(t->a, t->b, strlen(t->a)-1) != t->result)
+ return 0;
+ SCLogInfo("ok");
+ t++;
+ }
+
+ return 1;
+}
+
+#endif /* UNITTESTS */
+
+void MemcmpRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("MemcmpTest01", MemcmpTest01, 1);
+ UtRegisterTest("MemcmpTest02", MemcmpTest02, 1);
+ UtRegisterTest("MemcmpTest03", MemcmpTest03, 1);
+ UtRegisterTest("MemcmpTest04", MemcmpTest04, 1);
+ UtRegisterTest("MemcmpTest05", MemcmpTest05, 1);
+ UtRegisterTest("MemcmpTest06", MemcmpTest06, 1);
+ UtRegisterTest("MemcmpTest07", MemcmpTest07, 1);
+ UtRegisterTest("MemcmpTest08", MemcmpTest08, 1);
+ UtRegisterTest("MemcmpTest09", MemcmpTest09, 1);
+ UtRegisterTest("MemcmpTest10", MemcmpTest10, 1);
+ UtRegisterTest("MemcmpTest11", MemcmpTest11, 1);
+ UtRegisterTest("MemcmpTest12", MemcmpTest12, 1);
+ UtRegisterTest("MemcmpTest13", MemcmpTest13, 1);
+ UtRegisterTest("MemcmpTest14", MemcmpTest14, 1);
+ UtRegisterTest("MemcmpTest15", MemcmpTest15, 1);
+ UtRegisterTest("MemcmpTest16", MemcmpTest16, 1);
+ UtRegisterTest("MemcmpTest17", MemcmpTest17, 1);
+ UtRegisterTest("MemcmpTest18", MemcmpTest18, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-memcmp.h b/framework/src/suricata/src/util-memcmp.h
new file mode 100644
index 00000000..9248f842
--- /dev/null
+++ b/framework/src/suricata/src/util-memcmp.h
@@ -0,0 +1,502 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Memcmp implementations for SSE3, SSE4.1, SSE4.2 and TILE-Gx SIMD.
+ *
+ * Both SCMemcmp and SCMemcmpLowercase return 0 on a exact match,
+ * 1 on a failed match.
+ */
+
+#ifndef __UTIL_MEMCMP_H__
+#define __UTIL_MEMCMP_H__
+
+#include "util-optimize.h"
+
+/** \brief compare two patterns, converting the 2nd to lowercase
+ * \warning *ONLY* the 2nd pattern is converted to lowercase
+ */
+static inline int SCMemcmpLowercase(const void *, const void *, size_t);
+
+void MemcmpRegisterTests(void);
+
+static inline int
+MemcmpLowercase(const void *s1, const void *s2, size_t n)
+{
+ ssize_t i;
+
+ /* check backwards because we already tested the first
+ * 2 to 4 chars. This way we are more likely to detect
+ * a miss and thus speed up a little... */
+ for (i = n - 1; i >= 0; i--) {
+ if (((uint8_t *)s1)[i] != u8_tolower(*(((uint8_t *)s2)+i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+#if defined(__SSE4_2__)
+
+#include <nmmintrin.h>
+
+/* No SIMD support, fall back to plain memcmp and a home grown lowercase one */
+
+static inline int SCMemcmp(const void *s1, const void *s2, size_t n)
+{
+ __m128i b1, b2;
+
+ int r;
+ /* counter for how far we already matched in the buffer */
+ size_t m = 0;
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(n - m < 16)) {
+ return memcmp(s1, s2, n - m) ? 1 : 0;
+ }
+
+ /* load the buffers into the 128bit vars */
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+
+ /* do the actual compare */
+ m += (r = _mm_cmpestri(b1, n - m, b2, 16,
+ _SIDD_CMP_EQUAL_EACH | _SIDD_MASKED_NEGATIVE_POLARITY));
+
+ s1 += 16;
+ s2 += 16;
+ } while (r == 16);
+
+ return ((m == n) ? 0 : 1);
+}
+
+/* Range of values of uppercase characters. We only use the first 2 bytes. */
+static char scmemcmp_uppercase[16] __attribute__((aligned(16))) = {
+ 'A', 'Z', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+
+/** \brief compare two buffers in a case insensitive way
+ * \param s1 buffer already in lowercase
+ * \param s2 buffer with mixed upper and lowercase
+ */
+static inline int SCMemcmpLowercase(const void *s1, const void *s2, size_t n)
+{
+ __m128i b1, b2, mask;
+
+ int r;
+ /* counter for how far we already matched in the buffer */
+ size_t m = 0;
+
+ __m128i ucase = _mm_load_si128((const __m128i *) scmemcmp_uppercase);
+ __m128i nulls = _mm_setzero_si128();
+ __m128i uplow = _mm_set1_epi8(0x20);
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(n - m < 16)) {
+ return MemcmpLowercase(s1, s2, n - m);
+ }
+
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+ size_t len = n - m;
+
+ /* The first step is creating a mask that is FF for all uppercase
+ * characters, 00 for all others */
+ mask = _mm_cmpestrm(ucase, 2, b2, len, _SIDD_CMP_RANGES | _SIDD_UNIT_MASK);
+ /* Next we use that mask to create a new: this one has 0x20 for
+ * the uppercase chars, 00 for all other. */
+ mask = _mm_blendv_epi8(nulls, uplow, mask);
+ /* finally, merge the mask and the buffer converting the
+ * uppercase to lowercase */
+ b2 = _mm_add_epi8(b2, mask);
+
+ /* search using our converted buffer */
+ m += (r = _mm_cmpestri(b1, len, b2, 16,
+ _SIDD_CMP_EQUAL_EACH | _SIDD_MASKED_NEGATIVE_POLARITY));
+
+ s1 += 16;
+ s2 += 16;
+ } while (r == 16);
+
+ return ((m == n) ? 0 : 1);
+}
+
+#elif defined(__SSE4_1__)
+
+#include <smmintrin.h>
+
+#define SCMEMCMP_BYTES 16
+
+static inline int SCMemcmp(const void *s1, const void *s2, size_t len)
+{
+ size_t offset = 0;
+ __m128i b1, b2, c;
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(len - offset < 16)) {
+ return memcmp(s1, s2, len - offset) ? 1 : 0;
+ }
+
+ /* do unaligned loads using _mm_loadu_si128. On my Core2 E6600 using
+ * _mm_lddqu_si128 was about 2% slower even though it's supposed to
+ * be faster. */
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+ c = _mm_cmpeq_epi8(b1, b2);
+
+ int diff = len - offset;
+ if (diff < 16) {
+ int rmask = ~(0xFFFFFFFF << diff);
+
+ if ((_mm_movemask_epi8(c) & rmask) != rmask) {
+ return 1;
+ }
+ } else {
+ if (_mm_movemask_epi8(c) != 0x0000FFFF) {
+ return 1;
+ }
+ }
+
+ offset += SCMEMCMP_BYTES;
+ s1 += SCMEMCMP_BYTES;
+ s2 += SCMEMCMP_BYTES;
+ } while (len > offset);
+
+ return 0;
+}
+
+#define UPPER_LOW 0x40 /* "A" - 1 */
+#define UPPER_HIGH 0x5B /* "Z" + 1 */
+
+static inline int SCMemcmpLowercase(const void *s1, const void *s2, size_t len)
+{
+ size_t offset = 0;
+ __m128i b1, b2, mask1, mask2, upper1, upper2, nulls, uplow;
+
+ /* setup registers for upper to lower conversion */
+ upper1 = _mm_set1_epi8(UPPER_LOW);
+ upper2 = _mm_set1_epi8(UPPER_HIGH);
+ nulls = _mm_setzero_si128();
+ uplow = _mm_set1_epi8(0x20);
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(len - offset < 16)) {
+ return MemcmpLowercase(s1, s2, len - offset);
+ }
+
+ /* unaligned loading of the bytes to compare */
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+
+ /* mark all chars bigger than upper1 */
+ mask1 = _mm_cmpgt_epi8(b2, upper1);
+ /* mark all chars lower than upper2 */
+ mask2 = _mm_cmplt_epi8(b2, upper2);
+ /* merge the two, leaving only those that are true in both */
+ mask1 = _mm_cmpeq_epi8(mask1, mask2);
+ /* Next we use that mask to create a new: this one has 0x20 for
+ * the uppercase chars, 00 for all other. */
+ mask1 = _mm_blendv_epi8(nulls, uplow, mask1);
+
+ /* add to b2, converting uppercase to lowercase */
+ b2 = _mm_add_epi8(b2, mask1);
+
+ /* now all is lowercase, let's do the actual compare (reuse mask1 reg) */
+ mask1 = _mm_cmpeq_epi8(b1, b2);
+
+ int diff = len - offset;
+ if (diff < 16) {
+ int rmask = ~(0xFFFFFFFF << diff);
+
+ if ((_mm_movemask_epi8(mask1) & rmask) != rmask) {
+ return 1;
+ }
+ } else {
+ if (_mm_movemask_epi8(mask1) != 0x0000FFFF) {
+ return 1;
+ }
+ }
+
+ offset += SCMEMCMP_BYTES;
+ s1 += SCMEMCMP_BYTES;
+ s2 += SCMEMCMP_BYTES;
+ } while (len > offset);
+
+ return 0;
+}
+
+
+
+#elif defined(__SSE3__)
+
+#include <pmmintrin.h> /* for SSE3 */
+
+#define SCMEMCMP_BYTES 16
+
+static inline int SCMemcmp(const void *s1, const void *s2, size_t len)
+{
+ size_t offset = 0;
+ __m128i b1, b2, c;
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(len - offset < 16)) {
+ return memcmp(s1, s2, len - offset) ? 1 : 0;
+ }
+
+ /* do unaligned loads using _mm_loadu_si128. On my Core2 E6600 using
+ * _mm_lddqu_si128 was about 2% slower even though it's supposed to
+ * be faster. */
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+ c = _mm_cmpeq_epi8(b1, b2);
+
+ int diff = len - offset;
+ if (diff < 16) {
+ int rmask = ~(0xFFFFFFFF << diff);
+
+ if ((_mm_movemask_epi8(c) & rmask) != rmask) {
+ return 1;
+ }
+ } else {
+ if (_mm_movemask_epi8(c) != 0x0000FFFF) {
+ return 1;
+ }
+ }
+
+ offset += SCMEMCMP_BYTES;
+ s1 += SCMEMCMP_BYTES;
+ s2 += SCMEMCMP_BYTES;
+ } while (len > offset);
+
+ return 0;
+}
+
+#define UPPER_LOW 0x40 /* "A" - 1 */
+#define UPPER_HIGH 0x5B /* "Z" + 1 */
+#define UPPER_DELTA 0xDF /* 0xFF - 0x20 */
+
+static inline int SCMemcmpLowercase(const void *s1, const void *s2, size_t len)
+{
+ size_t offset = 0;
+ __m128i b1, b2, mask1, mask2, upper1, upper2, delta;
+
+ /* setup registers for upper to lower conversion */
+ upper1 = _mm_set1_epi8(UPPER_LOW);
+ upper2 = _mm_set1_epi8(UPPER_HIGH);
+ delta = _mm_set1_epi8(UPPER_DELTA);
+
+ do {
+ /* apparently we can't just read 16 bytes even though
+ * it almost always works fine :) */
+ if (likely(len - offset < 16)) {
+ return MemcmpLowercase(s1, s2, len - offset);
+ }
+
+ /* unaligned loading of the bytes to compare */
+ b1 = _mm_loadu_si128((const __m128i *) s1);
+ b2 = _mm_loadu_si128((const __m128i *) s2);
+
+ /* mark all chars bigger than upper1 */
+ mask1 = _mm_cmpgt_epi8(b2, upper1);
+ /* mark all chars lower than upper2 */
+ mask2 = _mm_cmplt_epi8(b2, upper2);
+ /* merge the two, leaving only those that are true in both */
+ mask1 = _mm_cmpeq_epi8(mask1, mask2);
+
+ /* sub delta leaves 0x20 only for uppercase positions, the
+ rest is 0x00 due to the saturation (reuse mask1 reg)*/
+ mask1 = _mm_subs_epu8(mask1, delta);
+
+ /* add to b2, converting uppercase to lowercase */
+ b2 = _mm_add_epi8(b2, mask1);
+
+ /* now all is lowercase, let's do the actual compare (reuse mask1 reg) */
+ mask1 = _mm_cmpeq_epi8(b1, b2);
+
+ int diff = len - offset;
+ if (diff < 16) {
+ int rmask = ~(0xFFFFFFFF << diff);
+
+ if ((_mm_movemask_epi8(mask1) & rmask) != rmask) {
+ return 1;
+ }
+ } else {
+ if (_mm_movemask_epi8(mask1) != 0x0000FFFF) {
+ return 1;
+ }
+ }
+
+ offset += SCMEMCMP_BYTES;
+ s1 += SCMEMCMP_BYTES;
+ s2 += SCMEMCMP_BYTES;
+ } while (len > offset);
+
+ return 0;
+}
+
+#elif defined(__tile__)
+
+#include <ctype.h>
+
+/* Compare to non-zero sequence of bytes. */
+static inline int SCMemcmpNZ(const void *s1, const void *s2, size_t len)
+{
+ uint64_t b1, w1, aligned1;
+ uint64_t b2, w2, aligned2;
+
+ /* Load aligned words containing the beginning of each string.
+ * These loads don't trigger unaligned events.
+ */
+ w1 = __insn_ldna(s1);
+ w2 = __insn_ldna(s2);
+ /* Can't just read next 8 bytes because it might go past the end
+ * of a page. */
+ while (len > 8) {
+ /* Here, the buffer extends into the next word by at least one
+ * byte, so it is safe to read the next word. Do an aligned
+ * loads on the next word. Then use the two words to create
+ * an aligned word from each string. */
+ b1 = __insn_ldna(s1 + 8);
+ b2 = __insn_ldna(s2 + 8);
+ aligned1 = __insn_dblalign(w1, b1, s1);
+ aligned2 = __insn_dblalign(w2, b2, s2);
+ if (aligned1 != aligned2)
+ return 1;
+
+ /* Move forward one word (8 bytes) */
+ w1 = b1;
+ w2 = b2;
+ len -= 8;
+ s1 += 8;
+ s2 += 8;
+ }
+ /* Process the last up-to 8 bytes. */
+ do {
+ if (*(char*)s1 != *(char*)s2)
+ return 1;
+ s1++;
+ s2++;
+ len--;
+ } while (len);
+
+ return 0;
+}
+
+/* Compare two sequences of bytes. */
+static inline int SCMemcmp(const void *s1, const void *s2, size_t len)
+{
+ if (len == 0)
+ return 0;
+ return SCMemcmpNZ(s1, s2, len);
+}
+/** \brief Convert 8 characters to lower case using SIMD.
+ * \param Word containing the 8 bytes.
+ * \return Word containing 8-bytes each converted to lowercase.
+ */
+static inline uint64_t
+vec_tolower(uint64_t cc)
+{
+ /* For Uppercases letters, add 32 to convert to lower case. */
+ uint64_t less_than_eq_Z = __insn_v1cmpltui (cc, 'Z' + 1);
+ uint64_t less_than_A = __insn_v1cmpltui (cc, 'A');
+ uint64_t is_upper = __insn_v1cmpne (less_than_eq_Z, less_than_A);
+ return __insn_v1add (cc,__insn_v1shli (is_upper, 5));
+}
+
+/** \brief compare two buffers in a case insensitive way
+ * \param s1 buffer already in lowercase
+ * \param s2 buffer with mixed upper and lowercase
+ */
+static inline int SCMemcmpLowercase(const void *s1, const void *s2, size_t len)
+{
+ uint64_t b1, w1, aligned1;
+ uint64_t b2, w2, aligned2;
+
+ if (len == 0)
+ return 0;
+
+ /* TODO Check for already aligned cases. To optimize. */
+
+ /* Load word containing the beginning of each string.
+ * These loads don't trigger unaligned events.
+ */
+ w1 = __insn_ldna(s1);
+ w2 = __insn_ldna(s2);
+ /* Can't just read next 8 bytes because it might go past the end
+ * of a page. */
+ while (len > 8) {
+ /* Here, the buffer extends into the next word by at least one
+ * byte, so it is safe to read the next word. Do aligned
+ * loads on next word. Then use the two words to create an
+ * aligned word from each string. */
+ b1 = __insn_ldna(s1 + 8);
+ b2 = __insn_ldna(s2 + 8);
+ aligned1 = __insn_dblalign(w1, b1, s1);
+ aligned2 = vec_tolower(__insn_dblalign(w2, b2, s2));
+ if (aligned1 != aligned2)
+ return 1;
+
+ /* Move forward one word (8 bytes) */
+ w1 = b1;
+ w2 = b2;
+ len -= 8;
+ s1 += 8;
+ s2 += 8;
+ }
+
+ do {
+ if (*(char*)s1 != tolower(*(char*)s2))
+ return 1;
+ s1++;
+ s2++;
+ len--;
+ } while (len);
+
+ return 0;
+}
+
+#else
+
+/* No SIMD support, fall back to plain memcmp and a home grown lowercase one */
+
+/* wrapper around memcmp to match the retvals of the SIMD implementations */
+#define SCMemcmp(a,b,c) ({ \
+ memcmp((a), (b), (c)) ? 1 : 0; \
+})
+
+static inline int SCMemcmpLowercase(const void *s1, const void *s2, size_t len)
+{
+ return MemcmpLowercase(s1, s2, len);
+}
+
+#endif /* SIMD */
+
+#endif /* __UTIL_MEMCMP_H__ */
+
diff --git a/framework/src/suricata/src/util-memcpy.h b/framework/src/suricata/src/util-memcpy.h
new file mode 100644
index 00000000..bdb80242
--- /dev/null
+++ b/framework/src/suricata/src/util-memcpy.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ken Steele <suricata@tilera.com>
+ *
+ * Memcpy_tolower()
+ *
+ */
+
+#ifndef __UTIL_MEMCPY_H__
+#define __UTIL_MEMCPY_H__
+
+/**
+ * \internal
+ * \brief Does a memcpy of the input string to lowercase.
+ *
+ * \param d Pointer to the target area for memcpy.
+ * \param s Pointer to the src string for memcpy.
+ * \param len len of the string sent in s.
+ */
+static inline void memcpy_tolower(uint8_t *d, uint8_t *s, uint16_t len)
+{
+ uint16_t i;
+ for (i = 0; i < len; i++)
+ d[i] = u8_tolower(s[i]);
+
+ return;
+}
+
+#endif /* __UTIL_MEMCPY_H__ */
diff --git a/framework/src/suricata/src/util-memrchr.c b/framework/src/suricata/src/util-memrchr.c
new file mode 100644
index 00000000..ebac1764
--- /dev/null
+++ b/framework/src/suricata/src/util-memrchr.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+
+#ifndef HAVE_MEMRCHR
+void *memrchr (const void *s, int c, size_t n)
+{
+ const char *end = s + n;
+
+ while (end > (const char *)s) {
+ if (*end == (char)c)
+ return (void *)end;
+ end--;
+ }
+ return NULL;
+}
+#endif /* HAVE_MEMRCHR */
+
+#ifdef UNITTESTS
+static int MemrchrTest01 (void)
+{
+ char *haystack = "abcabc";
+ char needle = 'b';
+
+ char *ptr = memrchr(haystack, needle, strlen(haystack));
+ if (ptr == NULL)
+ return 0;
+
+ if (strlen(ptr) != 2)
+ return 0;
+
+ if (strcmp(ptr, "bc") != 0)
+ return 0;
+
+ return 1;
+}
+#endif
+
+void MemrchrRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("MemrchrTest01", MemrchrTest01, 1);
+#endif
+}
diff --git a/framework/src/suricata/src/util-memrchr.h b/framework/src/suricata/src/util-memrchr.h
new file mode 100644
index 00000000..c3b5c8d8
--- /dev/null
+++ b/framework/src/suricata/src/util-memrchr.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ */
+
+#ifndef __UTIL_MEMRCHR_H__
+#define __UTIL_MEMRCHR_H__
+
+void *memrchr(const void *s, int c, size_t n);
+
+void MemrchrRegisterTests(void);
+
+#endif /* __UTIL_MEMRCHR_H__ */
diff --git a/framework/src/suricata/src/util-misc.c b/framework/src/suricata/src/util-misc.c
new file mode 100644
index 00000000..b72aa05e
--- /dev/null
+++ b/framework/src/suricata/src/util-misc.c
@@ -0,0 +1,1141 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "suricata.h"
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+#define PARSE_REGEX "^\\s*(\\d+(?:.\\d+)?)\\s*([a-zA-Z]{2})?\\s*$"
+static pcre *parse_regex = NULL;
+static pcre_extra *parse_regex_study = NULL;
+
+void ParseSizeInit(void)
+{
+ const char *eb;
+ int eo;
+ int opts = 0;
+
+ parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
+ if (parse_regex == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset "
+ "%" PRId32 ": %s", PARSE_REGEX, eo, eb);
+ exit(EXIT_FAILURE);
+ }
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ exit(EXIT_FAILURE);
+ }
+}
+
+void ParseSizeDeinit(void)
+{
+
+ if (parse_regex != NULL)
+ pcre_free(parse_regex);
+ if (parse_regex_study != NULL)
+ pcre_free_study(parse_regex_study);
+}
+
+/* size string parsing API */
+
+static int ParseSizeString(const char *size, double *res)
+{
+#define MAX_SUBSTRINGS 30
+ int pcre_exec_ret;
+ int r;
+ int ov[MAX_SUBSTRINGS];
+ int retval = 0;
+ char str[128];
+ char str2[128];
+
+ *res = 0;
+
+ pcre_exec_ret = pcre_exec(parse_regex, parse_regex_study, size, strlen(size), 0, 0,
+ ov, MAX_SUBSTRINGS);
+ if (!(pcre_exec_ret == 2 || pcre_exec_ret == 3)) {
+ SCLogError(SC_ERR_PCRE_MATCH, "invalid size argument - %s. Valid size "
+ "argument should be in the format - \n"
+ "xxx <- indicates it is just bytes\n"
+ "xxxkb or xxxKb or xxxKB or xxxkB <- indicates kilobytes\n"
+ "xxxmb or xxxMb or xxxMB or xxxmB <- indicates megabytes\n"
+ "xxxgb or xxxGb or xxxGB or xxxgB <- indicates gigabytes.\n",
+ size);
+ retval = -2;
+ goto end;
+ }
+
+ r = pcre_copy_substring((char *)size, ov, MAX_SUBSTRINGS, 1,
+ str, sizeof(str));
+ if (r < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ retval = -2;
+ goto end;
+ }
+
+ char *endptr, *str_ptr = str;
+ errno = 0;
+ *res = strtod(str_ptr, &endptr);
+ if (errno == ERANGE) {
+ SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
+ retval = -1;
+ goto end;
+ } else if (endptr == str_ptr) {
+ SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
+ retval = -1;
+ goto end;
+ }
+
+ if (pcre_exec_ret == 3) {
+ r = pcre_copy_substring((char *)size, ov, MAX_SUBSTRINGS, 2,
+ str2, sizeof(str2));
+ if (r < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ retval = -2;
+ goto end;
+ }
+
+ if (strcasecmp(str2, "kb") == 0) {
+ *res *= 1024;
+ } else if (strcasecmp(str2, "mb") == 0) {
+ *res *= 1024 * 1024;
+ } else if (strcasecmp(str2, "gb") == 0) {
+ *res *= 1024 * 1024 * 1024;
+ } else {
+ /* Bad unit. */
+ retval = -1;
+ goto end;
+ }
+ }
+
+ retval = 0;
+end:
+ return retval;
+}
+
+int ParseSizeStringU8(const char *size, uint8_t *res)
+{
+ double temp_res = 0;
+
+ *res = 0;
+ int r = ParseSizeString(size, &temp_res);
+ if (r < 0)
+ return r;
+
+ if (temp_res > UINT8_MAX)
+ return -1;
+
+ *res = temp_res;
+
+ return 0;
+}
+
+int ParseSizeStringU16(const char *size, uint16_t *res)
+{
+ double temp_res = 0;
+
+ *res = 0;
+ int r = ParseSizeString(size, &temp_res);
+ if (r < 0)
+ return r;
+
+ if (temp_res > UINT16_MAX)
+ return -1;
+
+ *res = temp_res;
+
+ return 0;
+}
+
+int ParseSizeStringU32(const char *size, uint32_t *res)
+{
+ double temp_res = 0;
+
+ *res = 0;
+ int r = ParseSizeString(size, &temp_res);
+ if (r < 0)
+ return r;
+
+ if (temp_res > UINT32_MAX)
+ return -1;
+
+ *res = temp_res;
+
+ return 0;
+}
+
+int ParseSizeStringU64(const char *size, uint64_t *res)
+{
+ double temp_res = 0;
+
+ *res = 0;
+ int r = ParseSizeString(size, &temp_res);
+ if (r < 0)
+ return r;
+
+ if (temp_res > UINT64_MAX)
+ return -1;
+
+ *res = temp_res;
+
+ return 0;
+}
+
+/*********************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+int UtilMiscParseSizeStringTest01(void)
+{
+ const char *str;
+ double result;
+
+ /* no space */
+
+ str = "10";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = "10kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240UL) {
+ goto error;
+ }
+
+
+ /* space start */
+
+ str = " 10";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = " 10kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+ /* space end */
+
+ str = "10 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = "10kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+ /* space start - space end */
+
+ str = " 10 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = " 10kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+
+ /* space between number and scale */
+
+ /* no space */
+
+ str = "10";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = "10 kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10 gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+
+ /* space start */
+
+ str = " 10";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = " 10 kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10 gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+ /* space end */
+
+ str = "10 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = "10 kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = "10 mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10 gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+ /* space start - space end */
+
+ str = " 10 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10) {
+ goto error;
+ }
+
+ str = " 10 kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024) {
+ goto error;
+ }
+
+ str = " 10 mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10 gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10737418240) {
+ goto error;
+ }
+
+ /* no space */
+
+ str = "10.5";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = "10.5kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10.5gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+
+ /* space start */
+
+ str = " 10.5";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = " 10.5kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+ /* space end */
+
+ str = "10.5 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = "10.5kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10.5gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+ /* space start - space end */
+
+ str = " 10.5 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = " 10.5kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+
+ /* space between number and scale */
+
+ /* no space */
+
+ str = "10.5";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = "10.5 kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+
+ /* space start */
+
+ str = " 10.5";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = " 10.5 kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 Kb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 KB";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 mb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 gb";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+ /* space end */
+
+ str = "10.5 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = "10.5 kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = "10.5 gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+ /* space start - space end */
+
+ str = " 10.5 ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5) {
+ goto error;
+ }
+
+ str = " 10.5 kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 Kb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 KB ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 mb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024) {
+ goto error;
+ }
+
+ str = " 10.5 gb ";
+ result = 0;
+ if (ParseSizeString(str, &result) > 0) {
+ goto error;
+ }
+ if (result != 10.5 * 1024 * 1024 * 1024) {
+ goto error;
+ }
+
+ /* Should fail on unknown units. */
+ if (ParseSizeString("32eb", &result) > 0) {
+ goto error;
+ }
+
+ return 1;
+ error:
+ return 0;
+}
+
+#endif /* UNITTESTS */
+
+void UtilMiscRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("UtilMiscParseSizeStringTest01", UtilMiscParseSizeStringTest01, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-misc.h b/framework/src/suricata/src/util-misc.h
new file mode 100644
index 00000000..2245f829
--- /dev/null
+++ b/framework/src/suricata/src/util-misc.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_MISC_H__
+#define __UTIL_MISC_H__
+
+#include "util-error.h"
+
+/**
+ * \brief Generic API that can be used by all to log an
+ * invalid conf entry.
+ * \param param_name A string specifying the param name.
+ * \param format Format for the below value. For example "%s", "%"PRIu32,
+ etc.
+ * \param value Default value to be printed.
+ */
+#define WarnInvalidConfEntry(param_name, format, value) do { \
+ SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, \
+ "Invalid conf entry found for " \
+ "\"%s\". Using default value of \"" format "\".", \
+ param_name, value); \
+ } while (0)
+
+/* size string parsing API */
+
+int ParseSizeStringU8(const char *, uint8_t *);
+int ParseSizeStringU16(const char *, uint16_t *);
+int ParseSizeStringU32(const char *, uint32_t *);
+int ParseSizeStringU64(const char *, uint64_t *);
+void UtilMiscRegisterTests(void);
+
+void ParseSizeInit(void);
+void ParseSizeDeinit(void);
+
+#endif /* __UTIL_MISC_H__ */
diff --git a/framework/src/suricata/src/util-mpm-ac-bs.c b/framework/src/suricata/src/util-mpm-ac-bs.c
new file mode 100644
index 00000000..d6c4e785
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-bs.c
@@ -0,0 +1,2802 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * First iteration of aho-corasick MPM from -
+ *
+ * Efficient String Matching: An Aid to Bibliographic Search
+ * Alfred V. Aho and Margaret J. Corasick
+ *
+ * - Uses the delta table for calculating transitions, instead of having
+ * separate goto and failure transitions.
+ * - If we cross 2 ** 16 states, we use 4 bytes in the transition table
+ * to hold each state, otherwise we use 2 bytes.
+ * - This version of the MPM is heavy on memory, but it performs well.
+ * If you can fit the ruleset with this mpm on your box without hitting
+ * swap, this is the MPM to go for.
+ *
+ * \todo - Do a proper analyis of our existing MPMs and suggest a good one based
+ * on the pattern distribution and the expected traffic(say http).
+ * - Tried out loop unrolling without any perf increase. Need to dig deeper.
+ * - Irrespective of whether we cross 2 ** 16 states or not,shift to using
+ * uint32_t for state type, so that we can integrate it's status as a
+ * final state or not in the topmost byte. We are already doing it if
+ * state_count is > 2 ** 16.
+ * - Test case-senstive patterns if they have any ascii chars. If they
+ * don't treat them as nocase.
+ * - Carry out other optimizations we are working on. hashes, compression.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "util-mpm-ac-bs.h"
+
+#include "conf.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+
+void SCACBSInitCtx(MpmCtx *);
+void SCACBSInitThreadCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void SCACBSDestroyCtx(MpmCtx *);
+void SCACBSDestroyThreadCtx(MpmCtx *, MpmThreadCtx *);
+int SCACBSAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACBSAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACBSPreparePatterns(MpmCtx *mpm_ctx);
+uint32_t SCACBSSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen);
+void SCACBSPrintInfo(MpmCtx *mpm_ctx);
+void SCACBSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void SCACBSRegisterTests(void);
+
+/* a placeholder to denote a failure transition in the goto table */
+#define SC_AC_BS_FAIL (-1)
+/* size of the hash table used to speed up pattern insertions initially */
+#define INIT_HASH_SIZE 65536
+
+#define STATE_QUEUE_CONTAINER_SIZE 65536
+
+/**
+ * \brief Helper structure used by AC during state table creation
+ */
+typedef struct StateQueue_ {
+ int32_t store[STATE_QUEUE_CONTAINER_SIZE];
+ int top;
+ int bot;
+} StateQueue;
+
+/**
+ * \brief Register the aho-corasick mpm.
+ */
+void MpmACBSRegister(void)
+{
+ mpm_table[MPM_AC_BS].name = "ac-bs";
+ /* don't need this. isn't that awesome? no more chopping and blah blah */
+ mpm_table[MPM_AC_BS].max_pattern_length = 0;
+
+ mpm_table[MPM_AC_BS].InitCtx = SCACBSInitCtx;
+ mpm_table[MPM_AC_BS].InitThreadCtx = SCACBSInitThreadCtx;
+ mpm_table[MPM_AC_BS].DestroyCtx = SCACBSDestroyCtx;
+ mpm_table[MPM_AC_BS].DestroyThreadCtx = SCACBSDestroyThreadCtx;
+ mpm_table[MPM_AC_BS].AddPattern = SCACBSAddPatternCS;
+ mpm_table[MPM_AC_BS].AddPatternNocase = SCACBSAddPatternCI;
+ mpm_table[MPM_AC_BS].Prepare = SCACBSPreparePatterns;
+ mpm_table[MPM_AC_BS].Search = SCACBSSearch;
+ mpm_table[MPM_AC_BS].Cleanup = NULL;
+ mpm_table[MPM_AC_BS].PrintCtx = SCACBSPrintInfo;
+ mpm_table[MPM_AC_BS].PrintThreadCtx = SCACBSPrintSearchStats;
+ mpm_table[MPM_AC_BS].RegisterUnittests = SCACBSRegisterTests;
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Initialize the AC context with user specified conf parameters. We
+ * aren't retrieving anything for AC conf now, but we will certainly
+ * need it, when we customize AC.
+ */
+static void SCACBSGetConfig()
+{
+ //ConfNode *ac_conf;
+ //const char *hash_val = NULL;
+
+ //ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates a hash of the pattern. We use it for the hashing process
+ * during the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline uint32_t SCACBSInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+/**
+ * \internal
+ * \brief Looks up a pattern. We use it for the hashing process during the
+ * the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param ctx Pointer to the AC ctx.
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ * \param flags Flags. We don't need this.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline SCACBSPattern *SCACBSInitHashLookup(SCACBSCtx *ctx, uint8_t *pat,
+ uint16_t patlen, char flags,
+ uint32_t pid)
+{
+ uint32_t hash = SCACBSInitHashRaw(pat, patlen);
+
+ if (ctx->init_hash == NULL) {
+ return NULL;
+ }
+
+ SCACBSPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (t->id == pid)
+ return t;
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Allocs a new pattern instance.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval p Pointer to the newly created pattern.
+ */
+static inline SCACBSPattern *SCACBSAllocPattern(MpmCtx *mpm_ctx)
+{
+ SCACBSPattern *p = SCMalloc(sizeof(SCACBSPattern));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(SCACBSPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACBSPattern);
+
+ return p;
+}
+
+/**
+ * \internal
+ * \brief Used to free SCACBSPattern instances.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param p Pointer to the SCACBSPattern instance to be freed.
+ */
+static inline void SCACBSFreePattern(MpmCtx *mpm_ctx, SCACBSPattern *p)
+{
+ if (p != NULL && p->cs != NULL && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->ci != NULL) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->original_pat != NULL) {
+ SCFree(p->original_pat);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACBSPattern);
+ }
+ return;
+}
+
+static inline uint32_t SCACBSInitHash(SCACBSPattern *p)
+{
+ uint32_t hash = p->len * p->original_pat[0];
+ if (p->len > 1)
+ hash += p->original_pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int SCACBSInitHashAdd(SCACBSCtx *ctx, SCACBSPattern *p)
+{
+ uint32_t hash = SCACBSInitHash(p);
+
+ if (ctx->init_hash == NULL) {
+ return 0;
+ }
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ return 0;
+ }
+
+ SCACBSPattern *tt = NULL;
+ SCACBSPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Add a pattern to the mpm-ac context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param pat Pointer to the pattern.
+ * \param patlen Length of the pattern.
+ * \param pid Pattern id
+ * \param sid Signature id (internal id).
+ * \param flags Pattern's MPM_PATTERN_* flags.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCACBSAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+
+ SCLogDebug("Adding pattern for ctx %p, patlen %"PRIu16" and pid %" PRIu32,
+ ctx, patlen, pid);
+
+ if (patlen == 0) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "pattern length 0");
+ return 0;
+ }
+
+ /* check if we have already inserted this pattern */
+ SCACBSPattern *p = SCACBSInitHashLookup(ctx, pat, patlen, flags, pid);
+ if (p == NULL) {
+ SCLogDebug("Allocing new pattern");
+
+ /* p will never be NULL */
+ p = SCACBSAllocPattern(mpm_ctx);
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ p->original_pat = SCMalloc(patlen);
+ if (p->original_pat == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->original_pat, pat, patlen);
+
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci, pat, p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ /* put in the pattern hash */
+ SCACBSInitHashAdd(ctx, p);
+
+ //if (mpm_ctx->pattern_cnt == 65535) {
+ // SCLogError(SC_ERR_AHO_CORASICK, "Max search words reached. Can't "
+ // "insert anymore. Exiting");
+ // exit(EXIT_FAILURE);
+ //}
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen)
+ mpm_ctx->maxlen = patlen;
+
+ if (mpm_ctx->minlen == 0) {
+ mpm_ctx->minlen = patlen;
+ } else {
+ if (mpm_ctx->minlen > patlen)
+ mpm_ctx->minlen = patlen;
+ }
+
+ /* we need the max pat id */
+ if (pid > ctx->max_pat_id)
+ ctx->max_pat_id = pid;
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ } else {
+ /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
+
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ }
+ }
+
+ return 0;
+
+error:
+ SCACBSFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Initialize a new state in the goto and output tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval The state id, of the newly created state.
+ */
+static inline int SCACBSInitNewState(MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int size = 0;
+
+ /* reallocate space in the goto table to include a new state */
+ size = (ctx->state_count + 1) * ctx->single_state_size;
+ ptmp = SCRealloc(ctx->goto_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->goto_table = ptmp;
+
+ /* set all transitions for the newly assigned state as FAIL transitions */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ ctx->goto_table[ctx->state_count][ascii_code] = SC_AC_BS_FAIL;
+ }
+
+ /* reallocate space in the output table for the new state */
+ size = (ctx->state_count + 1) * sizeof(SCACBSOutputTable);
+ ptmp = SCRealloc(ctx->output_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->output_table);
+ ctx->output_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->output_table = ptmp;
+
+ memset(ctx->output_table + ctx->state_count, 0, sizeof(SCACBSOutputTable));
+
+ /* \todo using it temporarily now during dev, since I have restricted
+ * state var in SCACBSCtx->state_table to uint16_t. */
+ //if (ctx->state_count > 65536) {
+ // printf("state count exceeded\n");
+ // exit(EXIT_FAILURE);
+ //}
+
+ return ctx->state_count++;
+}
+
+/**
+ * \internal
+ * \brief Adds a pid to the output table for a state.
+ *
+ * \param state The state to whose output table we should add the pid.
+ * \param pid The pattern id to add.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACBSSetOutputState(int32_t state, uint32_t pid, MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ SCACBSOutputTable *output_state = &ctx->output_table[state];
+ uint32_t i = 0;
+
+ for (i = 0; i < output_state->no_of_entries; i++) {
+ if (output_state->pids[i] == pid)
+ return;
+ }
+
+ output_state->no_of_entries++;
+ ptmp = SCRealloc(output_state->pids,
+ output_state->no_of_entries * sizeof(uint32_t));
+ if (ptmp == NULL) {
+ SCFree(output_state->pids);
+ output_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_state->pids = ptmp;
+
+ output_state->pids[output_state->no_of_entries - 1] = pid;
+
+ return;
+}
+
+/**
+ * \brief Helper function used by SCACBSCreateGotoTable. Adds a pattern to the
+ * goto table.
+ *
+ * \param pattern Pointer to the pattern.
+ * \param pattern_len Pattern length.
+ * \param pid The pattern id, that corresponds to this pattern. We
+ * need it to updated the output table for this pattern.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSEnter(uint8_t *pattern, uint16_t pattern_len, uint32_t pid,
+ MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int32_t state = 0;
+ int32_t newstate = 0;
+ int i = 0;
+ int p = 0;
+
+ /* walk down the trie till we have a match for the pattern prefix */
+ state = 0;
+ for (i = 0; i < pattern_len; i++) {
+ if (ctx->goto_table[state][pattern[i]] != SC_AC_BS_FAIL) {
+ state = ctx->goto_table[state][pattern[i]];
+ } else {
+ break;
+ }
+ }
+
+ /* add the non-matching pattern suffix to the trie, from the last state
+ * we left off */
+ for (p = i; p < pattern_len; p++) {
+ newstate = SCACBSInitNewState(mpm_ctx);
+ ctx->goto_table[state][pattern[p]] = newstate;
+ state = newstate;
+ }
+
+ /* add this pattern id, to the output table of the last state, where the
+ * pattern ends in the trie */
+ SCACBSSetOutputState(state, pid, mpm_ctx);
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSCreateGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+
+ /* add each pattern to create the goto table */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ SCACBSEnter(ctx->parray[i]->ci, ctx->parray[i]->len,
+ ctx->parray[i]->id, mpm_ctx);
+ }
+
+ int ascii_code = 0;
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[0][ascii_code] == SC_AC_BS_FAIL) {
+ ctx->goto_table[0][ascii_code] = 0;
+ }
+ }
+
+ return;
+}
+
+static inline int SCACBSStateQueueIsEmpty(StateQueue *q)
+{
+ if (q->top == q->bot)
+ return 1;
+ else
+ return 0;
+}
+
+static inline void SCACBSEnqueue(StateQueue *q, int32_t state)
+{
+ int i = 0;
+
+ /*if we already have this */
+ for (i = q->bot; i < q->top; i++) {
+ if (q->store[i] == state)
+ return;
+ }
+
+ q->store[q->top++] = state;
+
+ if (q->top == STATE_QUEUE_CONTAINER_SIZE)
+ q->top = 0;
+
+ if (q->top == q->bot) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+static inline int32_t SCACBSDequeue(StateQueue *q)
+{
+ if (q->bot == STATE_QUEUE_CONTAINER_SIZE)
+ q->bot = 0;
+
+ if (q->bot == q->top) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "StateQueue behaving weirdly. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return q->store[q->bot++];
+}
+
+/*
+#define SCACBSStateQueueIsEmpty(q) (((q)->top == (q)->bot) ? 1 : 0)
+
+#define SCACBSEnqueue(q, state) do { \
+ int i = 0; \
+ \
+ for (i = (q)->bot; i < (q)->top; i++) { \
+ if ((q)->store[i] == state) \
+ return; \
+ } \
+ \
+ (q)->store[(q)->top++] = state; \
+ \
+ if ((q)->top == STATE_QUEUE_CONTAINER_SIZE) \
+ (q)->top = 0; \
+ \
+ if ((q)->top == (q)->bot) { \
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. " \
+ "Fatal Error. Exiting. Please file a bug report on this"); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+#define SCACBSDequeue(q) ( (((q)->bot == STATE_QUEUE_CONTAINER_SIZE)? ((q)->bot = 0): 0), \
+ (((q)->bot == (q)->top) ? \
+ (printf("StateQueue behaving " \
+ "weirdly. Fatal Error. Exiting. Please " \
+ "file a bug report on this"), \
+ exit(EXIT_FAILURE)) : 0), \
+ (q)->store[(q)->bot++]) \
+*/
+
+/**
+ * \internal
+ * \brief Club the output data from 2 states and store it in the 1st state.
+ * dst_state_data = {dst_state_data} UNION {src_state_data}
+ *
+ * \param dst_state First state(also the destination) for the union operation.
+ * \param src_state Second state for the union operation.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSClubOutputStates(int32_t dst_state, int32_t src_state,
+ MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ SCACBSOutputTable *output_dst_state = &ctx->output_table[dst_state];
+ SCACBSOutputTable *output_src_state = &ctx->output_table[src_state];
+
+ for (i = 0; i < output_src_state->no_of_entries; i++) {
+ for (j = 0; j < output_dst_state->no_of_entries; j++) {
+ if (output_src_state->pids[i] == output_dst_state->pids[j]) {
+ break;
+ }
+ }
+ if (j == output_dst_state->no_of_entries) {
+ output_dst_state->no_of_entries++;
+
+ ptmp = SCRealloc(output_dst_state->pids,
+ (output_dst_state->no_of_entries * sizeof(uint32_t)));
+ if (ptmp == NULL) {
+ SCFree(output_dst_state->pids);
+ output_dst_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ output_dst_state->pids = ptmp;
+ }
+
+ output_dst_state->pids[output_dst_state->no_of_entries - 1] =
+ output_src_state->pids[i];
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the failure table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSCreateFailureTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int32_t state = 0;
+ int32_t r_state = 0;
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ /* allot space for the failure table. A failure entry in the table for
+ * every state(SCACBSCtx->state_count) */
+ ctx->failure_table = SCMalloc(ctx->state_count * sizeof(int32_t));
+ if (ctx->failure_table == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->failure_table, 0, ctx->state_count * sizeof(int32_t));
+
+ /* add the failure transitions for the 0th state, and add every non-fail
+ * transition from the 0th state to the queue for further processing
+ * of failure states */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[0][ascii_code];
+ if (temp_state != 0) {
+ SCACBSEnqueue(&q, temp_state);
+ ctx->failure_table[temp_state] = 0;
+ }
+ }
+
+ while (!SCACBSStateQueueIsEmpty(&q)) {
+ /* pick up every state from the queue and add failure transitions */
+ r_state = SCACBSDequeue(&q);
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state == SC_AC_BS_FAIL)
+ continue;
+ SCACBSEnqueue(&q, temp_state);
+ state = ctx->failure_table[r_state];
+
+ while(ctx->goto_table[state][ascii_code] == SC_AC_BS_FAIL)
+ state = ctx->failure_table[state];
+ ctx->failure_table[temp_state] = ctx->goto_table[state][ascii_code];
+ SCACBSClubOutputStates(temp_state, ctx->failure_table[temp_state],
+ mpm_ctx);
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the delta table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSCreateDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int32_t r_state = 0;
+
+ if (ctx->state_count < 32767) {
+ ctx->state_table_u16 = SCMalloc(ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U16) * 256);
+ if (ctx->state_table_u16 == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_u16, 0,
+ ctx->state_count * sizeof(SC_AC_BS_STATE_TYPE_U16) * 256);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U16) * 256);
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ SC_AC_BS_STATE_TYPE_U16 temp_state = ctx->goto_table[0][ascii_code];
+ ctx->state_table_u16[0][ascii_code] = temp_state;
+ if (temp_state != 0)
+ SCACBSEnqueue(&q, temp_state);
+ }
+
+ while (!SCACBSStateQueueIsEmpty(&q)) {
+ r_state = SCACBSDequeue(&q);
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state != SC_AC_BS_FAIL) {
+ SCACBSEnqueue(&q, temp_state);
+ ctx->state_table_u16[r_state][ascii_code] = temp_state;
+ } else {
+ ctx->state_table_u16[r_state][ascii_code] =
+ ctx->state_table_u16[ctx->failure_table[r_state]][ascii_code];
+ }
+ }
+ }
+ } else {
+ /* create space for the state table. We could have used the existing goto
+ * table, but since we have it set to hold 32 bit state values, we will create
+ * a new state table here of type SC_AC_BS_STATE_TYPE(current set to uint16_t) */
+ ctx->state_table_u32 = SCMalloc(ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U32) * 256);
+ if (ctx->state_table_u32 == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_u32, 0,
+ ctx->state_count * sizeof(SC_AC_BS_STATE_TYPE_U32) * 256);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U32) * 256);
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ SC_AC_BS_STATE_TYPE_U32 temp_state = ctx->goto_table[0][ascii_code];
+ ctx->state_table_u32[0][ascii_code] = temp_state;
+ if (temp_state != 0)
+ SCACBSEnqueue(&q, temp_state);
+ }
+
+ while (!SCACBSStateQueueIsEmpty(&q)) {
+ r_state = SCACBSDequeue(&q);
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state != SC_AC_BS_FAIL) {
+ SCACBSEnqueue(&q, temp_state);
+ ctx->state_table_u32[r_state][ascii_code] = temp_state;
+ } else {
+ ctx->state_table_u32[r_state][ascii_code] =
+ ctx->state_table_u32[ctx->failure_table[r_state]][ascii_code];
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static inline void SCACBSClubOutputStatePresenceWithDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ uint32_t state = 0;
+ uint32_t temp_state = 0;
+
+ if (ctx->state_count < 32767) {
+ for (state = 0; state < ctx->state_count; state++) {
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ temp_state = ctx->state_table_u16[state & 0x7FFF][ascii_code];
+ if (ctx->output_table[temp_state & 0x7FFF].no_of_entries != 0)
+ ctx->state_table_u16[state & 0x7FFF][ascii_code] |= (1 << 15);
+ }
+ }
+ } else {
+ for (state = 0; state < ctx->state_count; state++) {
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ temp_state = ctx->state_table_u32[state & 0x00FFFFFF][ascii_code];
+ if (ctx->output_table[temp_state & 0x00FFFFFF].no_of_entries != 0)
+ ctx->state_table_u32[state & 0x00FFFFFF][ascii_code] |= (1 << 24);
+ }
+ }
+ }
+
+ return;
+}
+
+static inline void SCACBSInsertCaseSensitiveEntriesForPatterns(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ uint32_t state = 0;
+ uint32_t k = 0;
+
+ for (state = 0; state < ctx->state_count; state++) {
+ if (ctx->output_table[state].no_of_entries == 0)
+ continue;
+
+ for (k = 0; k < ctx->output_table[state].no_of_entries; k++) {
+ if (ctx->pid_pat_list[ctx->output_table[state].pids[k]].cs != NULL) {
+ ctx->output_table[state].pids[k] &= 0x0000FFFF;
+ ctx->output_table[state].pids[k] |= 1 << 16;
+ }
+ }
+ }
+
+ return;
+}
+
+#if 0
+static void SCACBSPrintDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int i = 0, j = 0;
+
+ printf("##############Delta Table##############\n");
+ for (i = 0; i < ctx->state_count; i++) {
+ printf("%d: \n", i);
+ for (j = 0; j < 256; j++) {
+ if (SCACBSGetDelta(i, j, mpm_ctx) != 0) {
+ printf(" %c -> %d\n", j, SCACBSGetDelta(i, j, mpm_ctx));
+ }
+ }
+ }
+
+ return;
+}
+#endif
+
+static inline int SCACBSZeroTransitionPresent(SCACBSCtx *ctx, uint32_t state)
+{
+ if (state == 0)
+ return 1;
+
+ if (ctx->state_count < 32767) {
+ int ascii;
+ for (ascii = 0; ascii < 256; ascii++) {
+ if ((ctx->state_table_u16[0][ascii] & 0x7fff) == (state & 0x7fff)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ } else {
+ int ascii;
+ for (ascii = 0; ascii < 256; ascii++) {
+ if ((ctx->state_table_u32[0][ascii] & 0x00FFFFFF) ==
+ (state & 0x00FFFFFF)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+}
+
+/**
+ * \internal
+ * \brief Creates a new goto table structure(throw out all the failure
+ * transitions), to hold the existing goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSCreateModDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+
+ if (ctx->state_count < 32767) {
+ int size = 0;
+ uint32_t state;
+
+ for (state = 1; state < ctx->state_count; state++) {
+ int ascii_code;
+ int k = 0;
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ uint32_t temp_state = ctx->state_table_u16[state][ascii_code];
+ if (SCACBSZeroTransitionPresent(ctx, temp_state))
+ continue;
+ k++;
+ }
+ size += sizeof(uint16_t) * k * 2;
+ }
+
+ /* Let us use uint16_t for all. That way we don//'t have to worry about
+ * alignment. Technically 8 bits is all we need to store ascii codes,
+ * but by avoiding it, we save a lot of time on handling alignment */
+ size += (ctx->state_count * sizeof(SC_AC_BS_STATE_TYPE_U16) +
+ 256 * sizeof(SC_AC_BS_STATE_TYPE_U16) * 1);
+ ctx->state_table_mod = SCMalloc(size);
+ if (ctx->state_table_mod == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_mod, 0, size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += size;
+
+ /* buffer to hold pointers in the buffer, so that a state can use it
+ * directly to access its state data */
+ ctx->state_table_mod_pointers = SCMalloc(ctx->state_count * sizeof(uint8_t *));
+ if (ctx->state_table_mod_pointers == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_mod_pointers, 0,
+ ctx->state_count * sizeof(uint8_t *));
+
+ SC_AC_BS_STATE_TYPE_U16 temp_states[256];
+ uint16_t *curr_loc = (uint16_t *)ctx->state_table_mod;
+ uint16_t *no_of_entries = NULL;
+ uint16_t *ascii_codes = NULL;
+ state = 0;
+ uint16_t ascii_code = 0;
+ uint16_t k = 0;
+ for (state = 0; state < ctx->state_count; state++) {
+ /* store the starting location in the buffer for this state */
+ ctx->state_table_mod_pointers[state] = (uint8_t *)curr_loc;
+ no_of_entries = curr_loc++;
+ ascii_codes = curr_loc;
+ k = 0;
+ /* store all states that have non 0 transitions in the temp buffer */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ uint32_t temp_state = ctx->state_table_u16[state][ascii_code];
+ if (state != 0 && SCACBSZeroTransitionPresent(ctx, temp_state))
+ continue;
+
+ ascii_codes[k] = ascii_code;
+ temp_states[k] = ctx->state_table_u16[state][ascii_code];
+ k++;
+ }
+ /* if we have any non 0 transitions from our previous for search,
+ * store the acii codes as well the corresponding states */
+ if (k > 0) {
+ no_of_entries[0] = k;
+ if (state != 0)
+ curr_loc += k;
+ memcpy(curr_loc, temp_states, k * sizeof(SC_AC_BS_STATE_TYPE_U16));
+ curr_loc += k;
+ }
+ }
+
+ /* > 33766 */
+ } else {
+ int size = 0;
+ uint32_t state;
+ for (state = 1; state < ctx->state_count; state++) {
+ int ascii_code;
+ int k = 0;
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ uint32_t temp_state = ctx->state_table_u32[state][ascii_code];
+ if (SCACBSZeroTransitionPresent(ctx, temp_state))
+ continue;
+ k++;
+ }
+ size += sizeof(uint32_t) * k * 2;
+ }
+
+ /* Let us use uint32_t for all. That way we don//'t have to worry about
+ * alignment. Technically 8 bits is all we need to store ascii codes,
+ * but by avoiding it, we save a lot of time on handling alignment */
+ size += (ctx->state_count * sizeof(SC_AC_BS_STATE_TYPE_U32) +
+ 256 * sizeof(SC_AC_BS_STATE_TYPE_U32) * 1);
+ ctx->state_table_mod = SCMalloc(size);
+ if (ctx->state_table_mod == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_mod, 0, size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += size;
+
+ /* buffer to hold pointers in the buffer, so that a state can use it
+ * directly to access its state data */
+ ctx->state_table_mod_pointers = SCMalloc(ctx->state_count * sizeof(uint8_t *));
+ if (ctx->state_table_mod_pointers == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_mod_pointers, 0,
+ ctx->state_count * sizeof(uint8_t *));
+
+ SC_AC_BS_STATE_TYPE_U32 temp_states[256];
+ uint32_t *curr_loc = (uint32_t *)ctx->state_table_mod;
+ uint32_t *no_of_entries = NULL;
+ uint32_t *ascii_codes = NULL;
+ state = 0;
+ uint32_t ascii_code = 0;
+ uint32_t k = 0;
+ for (state = 0; state < ctx->state_count; state++) {
+ /* store the starting location in the buffer for this state */
+ ctx->state_table_mod_pointers[state] = (uint8_t *)curr_loc;
+ no_of_entries = curr_loc++;
+ ascii_codes = curr_loc;
+ k = 0;
+ /* store all states that have non 0 transitions in the temp buffer */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ uint32_t temp_state = ctx->state_table_u32[state][ascii_code];
+ if (state != 0 && SCACBSZeroTransitionPresent(ctx, temp_state))
+ continue;
+
+ ascii_codes[k] = ascii_code;
+ temp_states[k] = ctx->state_table_u32[state][ascii_code];
+ k++;
+ }
+ /* if we have any non 0 transitions from our previous for search,
+ * store the acii codes as well the corresponding states */
+ if (k > 0) {
+ no_of_entries[0] = k;
+ if (state != 0)
+ curr_loc += k;
+ memcpy(curr_loc, temp_states, k * sizeof(SC_AC_BS_STATE_TYPE_U32));
+ curr_loc += k;
+ }
+ }
+
+ }
+
+ return;
+}
+
+/**
+ * \brief Process the patterns and prepare the state table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACBSPrepareStateTable(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+
+ /* create the 0th state in the goto table and output_table */
+ SCACBSInitNewState(mpm_ctx);
+
+ /* create the goto table */
+ SCACBSCreateGotoTable(mpm_ctx);
+ /* create the failure table */
+ SCACBSCreateFailureTable(mpm_ctx);
+ /* create the final state(delta) table */
+ SCACBSCreateDeltaTable(mpm_ctx);
+ /* club the output state presence with delta transition entries */
+ SCACBSClubOutputStatePresenceWithDeltaTable(mpm_ctx);
+ /* create the modified table */
+ SCACBSCreateModDeltaTable(mpm_ctx);
+
+ /* club nocase entries */
+ SCACBSInsertCaseSensitiveEntriesForPatterns(mpm_ctx);
+
+// int state = 0;
+// for (state = 0; state < ctx->state_count; state++) {
+// int i = 0;
+// for (i = 0; i < 256; i++) {
+// if (ctx->state_table_u16[state][i] != 0) {
+// printf("%d-%d-%d\n", state, i, ctx->state_table_u16[state][i] & 0x7fff) ;
+// }
+// }
+// }
+
+#if 0
+ SCACBSPrintDeltaTable(mpm_ctx);
+#endif
+
+ /* we don't need these anymore */
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCFree(ctx->failure_table);
+ ctx->failure_table = NULL;
+ SCFree(ctx->state_table_u16);
+ ctx->state_table_u16 = NULL;
+ SCFree(ctx->state_table_u32);
+ ctx->state_table_u32 = NULL;
+
+ return;
+}
+
+/**
+ * \brief Process the patterns added to the mpm, and create the internal tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+int SCACBSPreparePatterns(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+
+ if (mpm_ctx->pattern_cnt == 0 || ctx->init_hash == NULL) {
+ SCLogDebug("no patterns supplied to this mpm_ctx");
+ return 0;
+ }
+
+ /* alloc the pattern array */
+ ctx->parray = (SCACBSPattern **)SCMalloc(mpm_ctx->pattern_cnt *
+ sizeof(SCACBSPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(SCACBSPattern *));
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(SCACBSPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ SCACBSPattern *node = ctx->init_hash[i], *nnode = NULL;
+ while(node != NULL) {
+ nnode = node->next;
+ node->next = NULL;
+ ctx->parray[p++] = node;
+ node = nnode;
+ }
+ }
+
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* the memory consumed by a single state in our goto table */
+ ctx->single_state_size = sizeof(int32_t) * 256;
+
+ /* handle no case patterns */
+ ctx->pid_pat_list = SCMalloc((ctx->max_pat_id + 1)* sizeof(SCACBSPatternList));
+ if (ctx->pid_pat_list == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->pid_pat_list, 0, (ctx->max_pat_id + 1) * sizeof(SCACBSPatternList));
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (!(ctx->parray[i]->flags & MPM_PATTERN_FLAG_NOCASE)) {
+ ctx->pid_pat_list[ctx->parray[i]->id].cs = SCMalloc(ctx->parray[i]->len);
+ if (ctx->pid_pat_list[ctx->parray[i]->id].cs == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(ctx->pid_pat_list[ctx->parray[i]->id].cs,
+ ctx->parray[i]->original_pat, ctx->parray[i]->len);
+ ctx->pid_pat_list[ctx->parray[i]->id].patlen = ctx->parray[i]->len;
+ }
+
+ /* ACPatternList now owns this memory */
+ ctx->pid_pat_list[ctx->parray[i]->id].sids_size = ctx->parray[i]->sids_size;
+ ctx->pid_pat_list[ctx->parray[i]->id].sids = ctx->parray[i]->sids;
+ }
+
+ /* prepare the state table required by AC */
+ SCACBSPrepareStateTable(mpm_ctx);
+
+ /* free all the stored patterns. Should save us a good 100-200 mbs */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACBSFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACBSPattern *));
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Init the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param matchsize We don't need this.
+ */
+void SCACBSInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(SCACBSThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_thread_ctx->ctx, 0, sizeof(SCACBSThreadCtx));
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(SCACBSThreadCtx);
+
+ return;
+}
+
+/**
+ * \brief Initialize the AC context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param module_handle Cuda module handle from the cuda handler API. We don't
+ * have to worry about this here.
+ */
+void SCACBSInitCtx(MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx->ctx != NULL)
+ return;
+
+ mpm_ctx->ctx = SCMalloc(sizeof(SCACBSCtx));
+ if (mpm_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_ctx->ctx, 0, sizeof(SCACBSCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACBSCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(SCACBSPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->init_hash, 0, sizeof(SCACBSPattern *) * INIT_HASH_SIZE);
+
+ /* get conf values for AC from our yaml file. We have no conf values for
+ * now. We will certainly need this, as we develop the algo */
+ SCACBSGetConfig();
+
+ SCReturn;
+}
+
+/**
+ * \brief Destroy the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ */
+void SCACBSDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ SCACBSPrintSearchStats(mpm_thread_ctx);
+
+ if (mpm_thread_ctx->ctx != NULL) {
+ SCFree(mpm_thread_ctx->ctx);
+ mpm_thread_ctx->ctx = NULL;
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(SCACBSThreadCtx);
+ }
+
+ return;
+}
+
+/**
+ * \brief Destroy the mpm context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+void SCACBSDestroyCtx(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash != NULL) {
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(SCACBSPattern *));
+ }
+
+ if (ctx->parray != NULL) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACBSFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACBSPattern *));
+ }
+
+ if (ctx->state_table_u16 != NULL) {
+ SCFree(ctx->state_table_u16);
+ ctx->state_table_u16 = NULL;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size -= (ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U16) * 256);
+ } else if (ctx->state_table_u32 != NULL) {
+ SCFree(ctx->state_table_u32);
+ ctx->state_table_u32 = NULL;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size -= (ctx->state_count *
+ sizeof(SC_AC_BS_STATE_TYPE_U32) * 256);
+ }
+
+ if (ctx->output_table != NULL) {
+ uint32_t state_count;
+ for (state_count = 0; state_count < ctx->state_count; state_count++) {
+ if (ctx->output_table[state_count].pids != NULL) {
+ SCFree(ctx->output_table[state_count].pids);
+ }
+ }
+ SCFree(ctx->output_table);
+ }
+
+ if (ctx->pid_pat_list != NULL) {
+ int i;
+ for (i = 0; i < (ctx->max_pat_id + 1); i++) {
+ if (ctx->pid_pat_list[i].cs != NULL)
+ SCFree(ctx->pid_pat_list[i].cs);
+ if (ctx->pid_pat_list[i].sids != NULL)
+ SCFree(ctx->pid_pat_list[i].sids);
+ }
+ SCFree(ctx->pid_pat_list);
+ }
+
+ if (ctx->state_table_mod != NULL) {
+ SCFree(ctx->state_table_mod);
+ ctx->state_table_mod = NULL;
+ }
+
+ if (ctx->state_table_mod_pointers != NULL) {
+ SCFree(ctx->state_table_mod_pointers);
+ ctx->state_table_mod_pointers = NULL;
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACBSCtx);
+
+ return;
+}
+
+/**
+ * \brief The aho corasick search function.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param pmq Pointer to the Pattern Matcher Queue to hold
+ * search matches.
+ * \param buf Buffer to be searched.
+ * \param buflen Buffer length.
+ *
+ * \retval matches Match count.
+ */
+uint32_t SCACBSSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+ int i = 0;
+ int matches = 0;
+ uint8_t buf_local;
+
+ /* \todo tried loop unrolling with register var, with no perf increase. Need
+ * to dig deeper */
+ /* \todo Change it for stateful MPM. Supply the state using mpm_thread_ctx */
+ SCACBSPatternList *pid_pat_list = ctx->pid_pat_list;
+
+ uint8_t bitarray[pmq->pattern_id_bitarray_size];
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+
+ if (ctx->state_count < 32767) {
+ register SC_AC_BS_STATE_TYPE_U16 state = 0;
+ uint16_t no_of_entries;
+ uint16_t *ascii_codes;
+ uint16_t **state_table_mod_pointers = (uint16_t **)ctx->state_table_mod_pointers;
+ uint16_t *zero_state = state_table_mod_pointers[0] + 1;
+
+ for (i = 0; i < buflen; i++) {
+ if (state == 0) {
+ state = zero_state[u8_tolower(buf[i])];
+ } else {
+ no_of_entries = *(state_table_mod_pointers[state & 0x7FFF]);
+ if (no_of_entries == 1) {
+ ascii_codes = state_table_mod_pointers[state & 0x7FFF] + 1;
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0]) {
+ state = *(ascii_codes + no_of_entries);;
+ } else {
+ state = zero_state[buf_local];
+ }
+ } else {
+ if (no_of_entries == 0) {
+ state = zero_state[u8_tolower(buf[i])];
+ goto match_u16;
+ }
+ buf_local = u8_tolower(buf[i]);
+ ascii_codes = state_table_mod_pointers[state & 0x7FFF] + 1;
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ state = 0;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ state = ((ascii_codes + no_of_entries))[mid];
+ goto match_u16;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ } /* while */
+ state = zero_state[buf_local];
+ } /* else - if (no_of_entires == 1) */
+ }
+
+ match_u16:
+ if (state & 0x8000) {
+ uint32_t no_of_entries = ctx->output_table[state & 0x7FFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x7FFF].pids;
+ uint32_t k;
+ for (k = 0; k < no_of_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ pmq->pattern_id_bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ //loop1:
+ //;
+ }
+ }
+ } /* for (i = 0; i < buflen; i++) */
+
+ } else {
+ register SC_AC_BS_STATE_TYPE_U32 state = 0;
+ uint32_t no_of_entries;
+ uint32_t *ascii_codes;
+ uint32_t **state_table_mod_pointers = (uint32_t **)ctx->state_table_mod_pointers;
+ uint32_t *zero_state = state_table_mod_pointers[0] + 1;
+
+ for (i = 0; i < buflen; i++) {
+ if (state == 0) {
+ state = zero_state[u8_tolower(buf[i])];
+ } else {
+ no_of_entries = *(state_table_mod_pointers[state & 0x00FFFFFF]);
+ if (no_of_entries == 1) {
+ ascii_codes = state_table_mod_pointers[state & 0x00FFFFFF] + 1;
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0]) {
+ state = *(ascii_codes + no_of_entries);;
+ } else {
+ state = zero_state[buf_local];;
+ }
+ } else {
+ if (no_of_entries == 0) {
+ state = zero_state[u8_tolower(buf[i])];
+ goto match_u32;
+ }
+ buf_local = u8_tolower(buf[i]);
+ ascii_codes = state_table_mod_pointers[state & 0x00FFFFFF] + 1;
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ state = 0;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ state = ((ascii_codes + no_of_entries))[mid];
+ goto match_u32;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ } /* while */
+ state = zero_state[buf_local];
+ } /* else - if (no_of_entires == 1) */
+ }
+
+ match_u32:
+ if (state & 0xFF000000) {
+ uint32_t no_of_entries = ctx->output_table[state & 0x00FFFFFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x00FFFFFF].pids;
+ uint32_t k;
+ for (k = 0; k < no_of_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ pmq->pattern_id_bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ //loop1:
+ //;
+ }
+ }
+ } /* for (i = 0; i < buflen; i++) */
+ }
+
+ return matches;
+}
+
+/**
+ * \brief Add a case insensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACBSAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return SCACBSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+/**
+ * \brief Add a case sensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACBSAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ return SCACBSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+void SCACBSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+
+#ifdef SC_AC_BS_COUNTERS
+ SCACBSThreadCtx *ctx = (SCACBSThreadCtx *)mpm_thread_ctx->ctx;
+ printf("AC Thread Search stats (ctx %p)\n", ctx);
+ printf("Total calls: %" PRIu32 "\n", ctx->total_calls);
+ printf("Total matches: %" PRIu64 "\n", ctx->total_matches);
+#endif /* SC_AC_BS_COUNTERS */
+
+ return;
+}
+
+void SCACBSPrintInfo(MpmCtx *mpm_ctx)
+{
+ SCACBSCtx *ctx = (SCACBSCtx *)mpm_ctx->ctx;
+
+ printf("MPM AC Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeof:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" SCACBSCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCACBSCtx));
+ printf(" SCACBSPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACBSPattern));
+ printf(" SCACBSPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACBSPattern));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Total states in the state table: %" PRIu32 "\n", ctx->state_count);
+ printf("\n");
+
+ return;
+}
+
+/*************************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+static int SCACBSTest01(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest02(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest03(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest04(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest05(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest06(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcd";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest07(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0);
+ /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0);
+ /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0);
+ /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0);
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ 30, 0, 0, 5, 0, 0);
+ PmqSetup(&pmq, 6);
+ /* total matches: 135 */
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest08(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest09(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest10(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "abcdefgh"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest11(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1)
+ goto end;
+ PmqSetup(&pmq, 5);
+
+ if (SCACBSPreparePatterns(&mpm_ctx) == -1)
+ goto end;
+
+ result = 1;
+
+ char *buf = "he";
+ result &= (SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "she";
+ result &= (SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+ buf = "his";
+ result &= (SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "hers";
+ result &= (SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+
+ end:
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest12(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest13(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCD";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCD";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest14(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDE";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDE";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest15(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest16(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABC";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABC";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest17(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzAB";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzAB";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest18(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest19(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest20(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest21(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"AA", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest22(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest23(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest24(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest25(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest26(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "works";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest27(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "tone";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest28(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "tONE";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest29(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_BS);
+ SCACBSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdef", 5, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"cdefg", 5, 0, 0, 3, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"defgh", 5, 0, 0, 4, 0, 0);
+ PmqSetup(&pmq, 4);
+
+ SCACBSPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefgh";
+ uint32_t cnt = SCACBSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 4)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACBSDestroyCtx(&mpm_ctx);
+ SCACBSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACBSTest30(void)
+{
+ uint8_t *buf = (uint8_t *)"onetwothreefourfivesixseveneightnine";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->mpm_matcher = MPM_AC_BS;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; fast_pattern:3,3; sid:2;)");
+ if (de_ctx->sig_list->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 1) failure\n");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 2) failure\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCACBSRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCACBSTest01", SCACBSTest01, 1);
+ UtRegisterTest("SCACBSTest02", SCACBSTest02, 1);
+ UtRegisterTest("SCACBSTest03", SCACBSTest03, 1);
+ UtRegisterTest("SCACBSTest04", SCACBSTest04, 1);
+ UtRegisterTest("SCACBSTest05", SCACBSTest05, 1);
+ UtRegisterTest("SCACBSTest06", SCACBSTest06, 1);
+ UtRegisterTest("SCACBSTest07", SCACBSTest07, 1);
+ UtRegisterTest("SCACBSTest08", SCACBSTest08, 1);
+ UtRegisterTest("SCACBSTest09", SCACBSTest09, 1);
+ UtRegisterTest("SCACBSTest10", SCACBSTest10, 1);
+ UtRegisterTest("SCACBSTest11", SCACBSTest11, 1);
+ UtRegisterTest("SCACBSTest12", SCACBSTest12, 1);
+ UtRegisterTest("SCACBSTest13", SCACBSTest13, 1);
+ UtRegisterTest("SCACBSTest14", SCACBSTest14, 1);
+ UtRegisterTest("SCACBSTest15", SCACBSTest15, 1);
+ UtRegisterTest("SCACBSTest16", SCACBSTest16, 1);
+ UtRegisterTest("SCACBSTest17", SCACBSTest17, 1);
+ UtRegisterTest("SCACBSTest18", SCACBSTest18, 1);
+ UtRegisterTest("SCACBSTest19", SCACBSTest19, 1);
+ UtRegisterTest("SCACBSTest20", SCACBSTest20, 1);
+ UtRegisterTest("SCACBSTest21", SCACBSTest21, 1);
+ UtRegisterTest("SCACBSTest22", SCACBSTest22, 1);
+ UtRegisterTest("SCACBSTest23", SCACBSTest23, 1);
+ UtRegisterTest("SCACBSTest24", SCACBSTest24, 1);
+ UtRegisterTest("SCACBSTest25", SCACBSTest25, 1);
+ UtRegisterTest("SCACBSTest26", SCACBSTest26, 1);
+ UtRegisterTest("SCACBSTest27", SCACBSTest27, 1);
+ UtRegisterTest("SCACBSTest28", SCACBSTest28, 1);
+ UtRegisterTest("SCACBSTest29", SCACBSTest29, 1);
+ UtRegisterTest("SCACBSTest30", SCACBSTest30, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-mpm-ac-bs.h b/framework/src/suricata/src/util-mpm-ac-bs.h
new file mode 100644
index 00000000..a66757e7
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-bs.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ */
+
+#define SC_AC_BS_STATE_TYPE_U16 uint16_t
+#define SC_AC_BS_STATE_TYPE_U32 uint32_t
+
+typedef struct SCACBSPattern_ {
+ /* length of the pattern */
+ uint16_t len;
+ /* flags decribing the pattern */
+ uint8_t flags;
+ /* holds the original pattern that was added */
+ uint8_t *original_pat;
+ /* case sensitive */
+ uint8_t *cs;
+ /* case INsensitive */
+ uint8_t *ci;
+ /* pattern id */
+ uint32_t id;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct SCACBSPattern_ *next;
+} SCACBSPattern;
+
+typedef struct SCACBSPatternList_ {
+ uint8_t *cs;
+ uint16_t patlen;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+} SCACBSPatternList;
+
+typedef struct SCACBSOutputTable_ {
+ /* list of pattern sids */
+ uint32_t *pids;
+ /* no of entries we have in pids */
+ uint32_t no_of_entries;
+} SCACBSOutputTable;
+
+typedef struct SCACBSCtx_ {
+ /* hash used during ctx initialization */
+ SCACBSPattern **init_hash;
+
+ /* pattern arrays. We need this only during the goto table creation phase */
+ SCACBSPattern **parray;
+
+ /* no of states used by ac */
+ uint32_t state_count;
+ /* the all important memory hungry state_table */
+ SC_AC_BS_STATE_TYPE_U16 (*state_table_u16)[256];
+ /* the all important memory hungry state_table */
+ SC_AC_BS_STATE_TYPE_U32 (*state_table_u32)[256];
+ /* the modified goto_table */
+ uint8_t *state_table_mod;
+ uint8_t **state_table_mod_pointers;
+
+ /* goto_table, failure table and output table. Needed to create state_table.
+ * Will be freed, once we have created the state_table */
+ int32_t (*goto_table)[256];
+ int32_t *failure_table;
+ SCACBSOutputTable *output_table;
+ SCACBSPatternList *pid_pat_list;
+
+ /* the size of each state */
+ uint16_t single_state_size;
+ uint16_t max_pat_id;
+} SCACBSCtx;
+
+typedef struct SCACBSThreadCtx_ {
+ /* the total calls we make to the search function */
+ uint32_t total_calls;
+ /* the total patterns that we ended up matching against */
+ uint64_t total_matches;
+} SCACBSThreadCtx;
+
+void MpmACBSRegister(void);
diff --git a/framework/src/suricata/src/util-mpm-ac-cuda-kernel.cu b/framework/src/suricata/src/util-mpm-ac-cuda-kernel.cu
new file mode 100644
index 00000000..d7cc125b
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-cuda-kernel.cu
@@ -0,0 +1,96 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * The Cuda kernel for MPM AC.
+ *
+ * \todo - This is a basic version of the kernel.
+ * - Support 16 bit state tables.
+ * - Texture memory.
+ * - Multiple threads per blocks of threads. Make use of
+ * shared memory/texture memory.
+ */
+
+extern "C"
+__global__ void SCACCudaSearch64(unsigned char *d_buffer,
+ unsigned int d_buffer_start_offset,
+ unsigned int *o_buffer,
+ unsigned int *results_buffer,
+ unsigned int nop,
+ unsigned char *tolower)
+{
+ unsigned int u = 0;
+ unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
+ if (tid >= nop)
+ return;
+
+ unsigned int buflen = *((unsigned long *)(d_buffer + (o_buffer[tid] - d_buffer_start_offset)));
+ unsigned int (*state_table_u32)[256] =
+ (unsigned int (*)[256])*((unsigned long *)(d_buffer + (o_buffer[tid] - d_buffer_start_offset) + 8));
+ unsigned char *buf = (d_buffer + (o_buffer[tid] - d_buffer_start_offset) + 16);
+
+ unsigned int state = 0;
+ unsigned int matches = 0;
+ unsigned int *results = (results_buffer + ((o_buffer[tid] - d_buffer_start_offset) * 2) + 1);
+ for (u = 0; u < buflen; u++) {
+ state = state_table_u32[state & 0x00FFFFFF][tolower[buf[u]]];
+ if (state & 0xFF000000) {
+ results[matches++] = u;
+ results[matches++] = state & 0x00FFFFFF;
+ }
+ }
+
+ *(results - 1) = matches;
+ return;
+}
+
+extern "C"
+__global__ void SCACCudaSearch32(unsigned char *d_buffer,
+ unsigned int d_buffer_start_offset,
+ unsigned int *o_buffer,
+ unsigned int *results_buffer,
+ unsigned int nop,
+ unsigned char *tolower)
+{
+ unsigned int u = 0;
+ unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
+ if (tid >= nop)
+ return;
+
+ unsigned int buflen = *((unsigned int *)(d_buffer + (o_buffer[tid] - d_buffer_start_offset)));
+ unsigned int (*state_table_u32)[256] =
+ (unsigned int (*)[256])*((unsigned int *)(d_buffer + (o_buffer[tid] - d_buffer_start_offset) + 4));
+ unsigned char *buf = (d_buffer + (o_buffer[tid] - d_buffer_start_offset) + 8);
+
+ unsigned int state = 0;
+ unsigned int matches = 0;
+ unsigned int *results = (results_buffer + ((o_buffer[tid] - d_buffer_start_offset) * 2) + 1);
+ for (u = 0; u < buflen; u++) {
+ state = state_table_u32[state & 0x00FFFFFF][tolower[buf[u]]];
+ if (state & 0xFF000000) {
+ results[matches++] = u;
+ results[matches++] = state & 0x00FFFFFF;
+ }
+ }
+
+ *(results - 1) = matches;
+ return;
+}
diff --git a/framework/src/suricata/src/util-mpm-ac-gfbs.c b/framework/src/suricata/src/util-mpm-ac-gfbs.c
new file mode 100644
index 00000000..e0ace7f3
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-gfbs.c
@@ -0,0 +1,2722 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implementation of aho-corasick MPM from -
+ *
+ * Efficient String Matching: An Aid to Bibliographic Search
+ * Alfred V. Aho and Margaret J. Corasick
+ *
+ * - We use the goto-failure table to calculate transitions.
+ * - If we cross 2 ** 16 states, we use 4 bytes in the transition table
+ * to hold each state, otherwise we use 2 bytes.
+ * - To reduce memory consumption, we throw all the failure transitions
+ * out and use binary search to pick out the right transition in
+ * the modified goto table.
+ *
+ * \todo - Do a proper analyis of our existing MPMs and suggest a good one based
+ * on the pattern distribution and the expected traffic(say http).
+ * - Tried out loop unrolling without any perf increase. Need to dig deeper.
+ * - Try out holding whether they are any output strings from a particular
+ * state in one of the bytes of a state var. Will be useful in cuda esp.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "util-mpm-ac-gfbs.h"
+
+#include "conf.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+void SCACGfbsInitCtx(MpmCtx *);
+void SCACGfbsInitThreadCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void SCACGfbsDestroyCtx(MpmCtx *);
+void SCACGfbsDestroyThreadCtx(MpmCtx *, MpmThreadCtx *);
+int SCACGfbsAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACGfbsAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACGfbsPreparePatterns(MpmCtx *mpm_ctx);
+uint32_t SCACGfbsSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen);
+void SCACGfbsPrintInfo(MpmCtx *mpm_ctx);
+void SCACGfbsPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void SCACGfbsRegisterTests(void);
+
+/* a placeholder to denote a failure transition in the goto table */
+#define SC_AC_GFBS_FAIL (-1)
+/* size of the hash table used to speed up pattern insertions initially */
+#define INIT_HASH_SIZE 65536
+
+#define STATE_QUEUE_CONTAINER_SIZE 65536
+
+/**
+ * \brief Helper structure used by AC during state table creation
+ */
+typedef struct StateQueue_ {
+ int32_t store[STATE_QUEUE_CONTAINER_SIZE];
+ int top;
+ int bot;
+} StateQueue;
+
+/**
+ * \brief Register the goto failure table based aho-corasick mpm.
+ */
+void MpmACGfbsRegister(void)
+{
+ mpm_table[MPM_AC_GFBS].name = "ac-gfbs";
+ /* don't need this. isn't that awesome? no more chopping and blah blah */
+ mpm_table[MPM_AC_GFBS].max_pattern_length = 0;
+
+ mpm_table[MPM_AC_GFBS].InitCtx = SCACGfbsInitCtx;
+ mpm_table[MPM_AC_GFBS].InitThreadCtx = SCACGfbsInitThreadCtx;
+ mpm_table[MPM_AC_GFBS].DestroyCtx = SCACGfbsDestroyCtx;
+ mpm_table[MPM_AC_GFBS].DestroyThreadCtx = SCACGfbsDestroyThreadCtx;
+ mpm_table[MPM_AC_GFBS].AddPattern = SCACGfbsAddPatternCS;
+ mpm_table[MPM_AC_GFBS].AddPatternNocase = SCACGfbsAddPatternCI;
+ mpm_table[MPM_AC_GFBS].Prepare = SCACGfbsPreparePatterns;
+ mpm_table[MPM_AC_GFBS].Search = SCACGfbsSearch;
+ mpm_table[MPM_AC_GFBS].Cleanup = NULL;
+ mpm_table[MPM_AC_GFBS].PrintCtx = SCACGfbsPrintInfo;
+ mpm_table[MPM_AC_GFBS].PrintThreadCtx = SCACGfbsPrintSearchStats;
+ mpm_table[MPM_AC_GFBS].RegisterUnittests = SCACGfbsRegisterTests;
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Initialize the AC context with user specified conf parameters. We
+ * aren't retrieving anything for AC conf now, but we will certainly
+ * need it, when we customize AC.
+ */
+static void SCACGfbsGetConfig()
+{
+ //ConfNode *ac_conf;
+ //const char *hash_val = NULL;
+
+ //ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates a hash of the pattern. We use it for the hashing process
+ * during the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline uint32_t SCACGfbsInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+/**
+ * \internal
+ * \brief Looks up a pattern. We use it for the hashing process during the
+ * the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param ctx Pointer to the AC ctx.
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ * \param flags Flags. We don't need this.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline SCACGfbsPattern *SCACGfbsInitHashLookup(SCACGfbsCtx *ctx, uint8_t *pat,
+ uint16_t patlen, char flags,
+ uint32_t pid)
+{
+ uint32_t hash = SCACGfbsInitHashRaw(pat, patlen);
+
+ if (ctx->init_hash == NULL)
+ return NULL;
+
+ SCACGfbsPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (t->id == pid)
+ return t;
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Allocs a new pattern instance.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval p Pointer to the newly created pattern.
+ */
+static inline SCACGfbsPattern *SCACGfbsAllocPattern(MpmCtx *mpm_ctx)
+{
+ SCACGfbsPattern *p = SCMalloc(sizeof(SCACGfbsPattern));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(SCACGfbsPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACGfbsPattern);
+
+ return p;
+}
+
+/**
+ * \internal
+ * \brief Used to free SCACGfbsPattern instances.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param p Pointer to the SCACGfbsPattern instance to be freed.
+ */
+static inline void SCACGfbsFreePattern(MpmCtx *mpm_ctx, SCACGfbsPattern *p)
+{
+ if (p != NULL && p->cs != NULL && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->ci != NULL) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->original_pat != NULL) {
+ SCFree(p->original_pat);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACGfbsPattern);
+ }
+ return;
+}
+
+static inline uint32_t SCACGfbsInitHash(SCACGfbsPattern *p)
+{
+ uint32_t hash = p->len * p->original_pat[0];
+ if (p->len > 1)
+ hash += p->original_pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int SCACGfbsInitHashAdd(SCACGfbsCtx *ctx, SCACGfbsPattern *p)
+{
+ uint32_t hash = SCACGfbsInitHash(p);
+
+ if (ctx->init_hash == NULL) {
+ return 0;
+ }
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ return 0;
+ }
+
+ SCACGfbsPattern *tt = NULL;
+ SCACGfbsPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Add a pattern to the mpm-ac context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param pat Pointer to the pattern.
+ * \param patlen Length of the pattern.
+ * \param pid Pattern id
+ * \param sid Signature id (internal id).
+ * \param flags Pattern's MPM_PATTERN_* flags.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCACGfbsAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ SCLogDebug("Adding pattern for ctx %p, patlen %"PRIu16" and pid %" PRIu32,
+ ctx, patlen, pid);
+
+ if (patlen == 0) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "pattern length 0");
+ return 0;
+ }
+
+ /* check if we have already inserted this pattern */
+ SCACGfbsPattern *p = SCACGfbsInitHashLookup(ctx, pat, patlen, flags, pid);
+ if (p == NULL) {
+ SCLogDebug("Allocing new pattern");
+
+ /* p will never be NULL */
+ p = SCACGfbsAllocPattern(mpm_ctx);
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ p->original_pat = SCMalloc(patlen);
+ if (p->original_pat == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->original_pat, pat, patlen);
+
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci, pat, p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ /* put in the pattern hash */
+ SCACGfbsInitHashAdd(ctx, p);
+
+ if (mpm_ctx->pattern_cnt == 65535) {
+ SCLogError(SC_ERR_AHO_CORASICK, "Max search words reached. Can't "
+ "insert anymore. Exiting");
+ exit(EXIT_FAILURE);
+ }
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen)
+ mpm_ctx->maxlen = patlen;
+
+ if (mpm_ctx->minlen == 0) {
+ mpm_ctx->minlen = patlen;
+ } else {
+ if (mpm_ctx->minlen > patlen)
+ mpm_ctx->minlen = patlen;
+ }
+
+ /* we need the max pat id */
+ if (pid > ctx->max_pat_id)
+ ctx->max_pat_id = pid;
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ } else {
+ /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
+
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ }
+ }
+
+ return 0;
+
+error:
+ SCACGfbsFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Initialize a new state in the goto and output tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval The state id, of the newly created state.
+ */
+static inline int SCACGfbsInitNewState(MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int size = 0;
+
+ /* reallocate space in the goto table to include a new state */
+ size = (ctx->state_count + 1) * 1024;
+ ptmp = SCRealloc(ctx->goto_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->goto_table = ptmp;
+
+ /* set all transitions for the newly assigned state as FAIL transitions */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ ctx->goto_table[ctx->state_count][ascii_code] = SC_AC_GFBS_FAIL;
+ }
+
+ /* reallocate space in the output table for the new state */
+ size = (ctx->state_count + 1) * sizeof(SCACGfbsOutputTable);
+ ptmp = SCRealloc(ctx->output_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->output_table);
+ ctx->output_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->output_table = ptmp;
+
+ memset(ctx->output_table + ctx->state_count, 0, sizeof(SCACGfbsOutputTable));
+
+ /* \todo using it temporarily now during dev, since I have restricted
+ * state var in SCACGfbsCtx->state_table to uint16_t. */
+ //if (ctx->state_count > 65536) {
+ // printf("state count exceeded\n");
+ // exit(EXIT_FAILURE);
+ //}
+
+ return ctx->state_count++;
+}
+
+/**
+ * \internal
+ * \brief Adds a pid to the output table for a state.
+ *
+ * \param state The state to whose output table we should add the pid.
+ * \param pid The pattern id to add.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACGfbsSetOutputState(int32_t state, uint32_t pid, MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ SCACGfbsOutputTable *output_state = &ctx->output_table[state];
+ uint32_t i = 0;
+
+ for (i = 0; i < output_state->no_of_entries; i++) {
+ if (output_state->pids[i] == pid)
+ return;
+ }
+
+ output_state->no_of_entries++;
+ ptmp = SCRealloc(output_state->pids,
+ output_state->no_of_entries * sizeof(uint32_t));
+ if (ptmp == NULL) {
+ SCFree(output_state->pids);
+ output_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_state->pids = ptmp;
+
+ output_state->pids[output_state->no_of_entries - 1] = pid;
+
+ return;
+}
+
+/**
+ * \brief Helper function used by SCACGfbsCreateGotoTable. Adds a pattern to the
+ * goto table.
+ *
+ * \param pattern Pointer to the pattern.
+ * \param pattern_len Pattern length.
+ * \param pid The pattern id, that corresponds to this pattern. We
+ * need it to updated the output table for this pattern.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsEnter(uint8_t *pattern, uint16_t pattern_len, uint32_t pid,
+ MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ int32_t state = 0;
+ int32_t newstate = 0;
+ int i = 0;
+ int p = 0;
+
+ /* walk down the trie till we have a match for the pattern prefix */
+ state = 0;
+ for (i = 0; i < pattern_len; i++) {
+ if (ctx->goto_table[state][pattern[i]] != SC_AC_GFBS_FAIL) {
+ state = ctx->goto_table[state][pattern[i]];
+ } else {
+ break;
+ }
+ }
+
+ /* add the non-matching pattern suffix to the trie, from the last state
+ * we left off */
+ for (p = i; p < pattern_len; p++) {
+ newstate = SCACGfbsInitNewState(mpm_ctx);
+ ctx->goto_table[state][pattern[p]] = newstate;
+ state = newstate;
+ }
+
+ /* add this pattern id, to the output table of the last state, where the
+ * pattern ends in the trie */
+ SCACGfbsSetOutputState(state, pid, mpm_ctx);
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsCreateGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+
+ /* add each pattern to create the goto table */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ SCACGfbsEnter(ctx->parray[i]->ci, ctx->parray[i]->len,
+ ctx->parray[i]->id, mpm_ctx);
+ }
+
+ int ascii_code = 0;
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[0][ascii_code] == SC_AC_GFBS_FAIL) {
+ ctx->goto_table[0][ascii_code] = 0;
+ }
+ }
+
+ return;
+}
+
+static inline int SCACGfbsStateQueueIsEmpty(StateQueue *q)
+{
+ if (q->top == q->bot)
+ return 1;
+ else
+ return 0;
+}
+
+static inline void SCACGfbsEnqueue(StateQueue *q, int32_t state)
+{
+ int i = 0;
+
+ /*if we already have this */
+ for (i = q->bot; i < q->top; i++) {
+ if (q->store[i] == state)
+ return;
+ }
+
+ q->store[q->top++] = state;
+
+ if (q->top == STATE_QUEUE_CONTAINER_SIZE)
+ q->top = 0;
+
+ if (q->top == q->bot) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+static inline int32_t SCACGfbsDequeue(StateQueue *q)
+{
+ if (q->bot == STATE_QUEUE_CONTAINER_SIZE)
+ q->bot = 0;
+
+ if (q->bot == q->top) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "StateQueue behaving weirdly. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return q->store[q->bot++];
+}
+
+/*
+#define SCACGfbsStateQueueIsEmpty(q) (((q)->top == (q)->bot) ? 1 : 0)
+
+#define SCACGfbsEnqueue(q, state) do { \
+ int i = 0; \
+ \
+ for (i = (q)->bot; i < (q)->top; i++) { \
+ if ((q)->store[i] == state) \
+ return; \
+ } \
+ \
+ (q)->store[(q)->top++] = state; \
+ \
+ if ((q)->top == STATE_QUEUE_CONTAINER_SIZE) \
+ (q)->top = 0; \
+ \
+ if ((q)->top == (q)->bot) { \
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. " \
+ "Fatal Error. Exiting. Please file a bug report on this"); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+#define SCACGfbsDequeue(q) ( (((q)->bot == STATE_QUEUE_CONTAINER_SIZE)? ((q)->bot = 0): 0), \
+ (((q)->bot == (q)->top) ? \
+ (printf("StateQueue behaving " \
+ "weirdly. Fatal Error. Exiting. Please " \
+ "file a bug report on this"), \
+ exit(EXIT_FAILURE)) : 0), \
+ (q)->store[(q)->bot++]) \
+*/
+
+/**
+ * \internal
+ * \brief Club the output data from 2 states and store it in the 1st state.
+ * dst_state_data = {dst_state_data} UNION {src_state_data}
+ *
+ * \todo Use a better way to find union of 2 sets.
+ *
+ * \param dst_state First state(also the destination) for the union operation.
+ * \param src_state Second state for the union operation.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsClubOutputStates(int32_t dst_state, int32_t src_state,
+ MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ SCACGfbsOutputTable *output_dst_state = &ctx->output_table[dst_state];
+ SCACGfbsOutputTable *output_src_state = &ctx->output_table[src_state];
+
+ for (i = 0; i < output_src_state->no_of_entries; i++) {
+ for (j = 0; j < output_dst_state->no_of_entries; j++) {
+ if (output_src_state->pids[i] == output_dst_state->pids[j]) {
+ break;
+ }
+ }
+ if (j == output_dst_state->no_of_entries) {
+ output_dst_state->no_of_entries++;
+
+ ptmp = SCRealloc(output_dst_state->pids,
+ (output_dst_state->no_of_entries * sizeof(uint32_t)));
+ if (ptmp == NULL) {
+ SCFree(output_dst_state->pids);
+ output_dst_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_dst_state->pids = ptmp;
+
+ output_dst_state->pids[output_dst_state->no_of_entries - 1] =
+ output_src_state->pids[i];
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the failure table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsCreateFailureTable(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int32_t state = 0;
+ int32_t r_state = 0;
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ /* allot space for the failure table. A failure entry in the table for
+ * every state(SCACGfbsCtx->state_count) */
+ ctx->failure_table = SCMalloc(ctx->state_count * sizeof(int32_t));
+ if (ctx->failure_table == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->failure_table, 0, ctx->state_count * sizeof(int32_t));
+
+ /* add the failure transitions for the 0th state, and add every non-fail
+ * transition from the 0th state to the queue for further processing
+ * of failure states */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[0][ascii_code];
+ if (temp_state != 0) {
+ SCACGfbsEnqueue(&q, temp_state);
+ ctx->failure_table[temp_state] = 0;
+ }
+ }
+
+ while (!SCACGfbsStateQueueIsEmpty(&q)) {
+ /* pick up every state from the queue and add failure transitions */
+ r_state = SCACGfbsDequeue(&q);
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state == SC_AC_GFBS_FAIL)
+ continue;
+ SCACGfbsEnqueue(&q, temp_state);
+ state = ctx->failure_table[r_state];
+
+ while(ctx->goto_table[state][ascii_code] == SC_AC_GFBS_FAIL)
+ state = ctx->failure_table[state];
+ ctx->failure_table[temp_state] = ctx->goto_table[state][ascii_code];
+ SCACGfbsClubOutputStates(temp_state, ctx->failure_table[temp_state],
+ mpm_ctx);
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates a new goto table structure(throw out all the failure
+ * transitions), to hold the existing goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsCreateModGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ if (ctx->state_count < 32767) {
+ int size = 0;
+ int32_t state = 0;
+ for (state = 1; state < ctx->state_count; state++) {
+ int k = 0;
+ int ascii_code = 0;
+ for (; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[state][ascii_code] == SC_AC_GFBS_FAIL)
+ continue;
+ k++;
+ }
+
+ if ((k % 2) != 0)
+ size += 1;
+ }
+
+ /* Let us use uint16_t for all. That way we don't have to worry about
+ * alignment. Technically 8 bits is all we need to store ascii codes,
+ * but by avoiding it, we save a lot of time on handling alignment */
+ size += (ctx->state_count * sizeof(SC_AC_GFBS_STATE_TYPE_U16) * 3 +
+ ctx->state_count * sizeof(uint8_t) +
+ 256 * sizeof(SC_AC_GFBS_STATE_TYPE_U16) * 1);
+ ctx->goto_table_mod = SCMalloc(size);
+ if (ctx->goto_table_mod == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->goto_table_mod, 0, size);
+ //printf("size- %d\n", size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += size;
+
+ /* buffer to hold pointers in the buffer, so that a state can use it
+ * directly to access its state data */
+ ctx->goto_table_mod_pointers = SCMalloc(ctx->state_count * sizeof(uint8_t *));
+ if (ctx->goto_table_mod_pointers == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->goto_table_mod_pointers, 0,
+ ctx->state_count * sizeof(uint8_t *));
+
+ SC_AC_GFBS_STATE_TYPE_U16 temp_states[256];
+ uint16_t *curr_loc = (uint16_t *)ctx->goto_table_mod;
+ uint16_t *no_of_entries = NULL;
+ uint16_t *failure_entry = NULL;
+ uint8_t *ascii_codes = NULL;
+ uint16_t ascii_code = 0;
+ uint16_t k = 0;
+ for (state = 0; state < ctx->state_count; state++) {
+ /* store the starting location in the buffer for this state */
+ ctx->goto_table_mod_pointers[state] = (uint8_t *)curr_loc;
+ no_of_entries = curr_loc++;
+ failure_entry = curr_loc++;
+ ascii_codes = (uint8_t *)curr_loc;
+ k = 0;
+ /* store all states that have non fail transitions in the temp buffer */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[state][ascii_code] == SC_AC_GFBS_FAIL)
+ continue;
+ ascii_codes[k] = ascii_code;
+ temp_states[k] = ctx->goto_table[state][ascii_code];
+ k++;
+ }
+ /* if we have any non fail transitions from our previous for search,
+ * store the acii codes as well the corresponding states */
+ if (k > 0) {
+ no_of_entries[0] = k;
+ if (state != 0) {
+ int jump = (k + 1) & 0xFFE;
+ curr_loc += jump / 2;
+ }
+ memcpy(curr_loc, temp_states, k * sizeof(SC_AC_GFBS_STATE_TYPE_U16));
+ curr_loc += k;
+ }
+ failure_entry[0] = ctx->failure_table[state];
+ }
+
+ /* > 33766 */
+ } else {
+ int size = 0;
+ int32_t state = 0;
+ for (state = 1; state < ctx->state_count; state++) {
+ int k = 0;
+ int ascii_code = 0;
+ for (; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[state][ascii_code] == SC_AC_GFBS_FAIL)
+ continue;
+ k++;
+ }
+
+ if ( (k % 4) != 0)
+ size += (4 - (k % 4));
+ }
+
+ /* Let us use uint32_t for all. That way we don't have to worry about
+ * alignment. Technically 8 bits is all we need to store ascii codes,
+ * but by avoiding it, we save a lot of time on handling alignment */
+ size += (ctx->state_count * (sizeof(SC_AC_GFBS_STATE_TYPE_U32) * 3)+
+ ctx->state_count * sizeof(uint8_t) +
+ 256 * (sizeof(SC_AC_GFBS_STATE_TYPE_U32) * 1));
+ ctx->goto_table_mod = SCMalloc(size);
+ if (ctx->goto_table_mod == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->goto_table_mod, 0, size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += size;
+
+ /* buffer to hold pointers in the buffer, so that a state can use it
+ * directly to access its state data */
+ ctx->goto_table_mod_pointers = SCMalloc(ctx->state_count * sizeof(uint8_t *));
+ if (ctx->goto_table_mod_pointers == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->goto_table_mod_pointers, 0,
+ ctx->state_count * sizeof(uint8_t *));
+
+ SC_AC_GFBS_STATE_TYPE_U32 temp_states[256];
+ uint32_t *curr_loc = (uint32_t *)ctx->goto_table_mod;
+ uint32_t *no_of_entries = NULL;
+ uint32_t *failure_entry = NULL;
+ uint8_t *ascii_codes = NULL;
+ uint16_t ascii_code = 0;
+ uint16_t k = 0;
+ for (state = 0; state < ctx->state_count; state++) {
+ /* store the starting location in the buffer for this state */
+ ctx->goto_table_mod_pointers[state] = (uint8_t *)curr_loc;
+ no_of_entries = curr_loc++;
+ failure_entry = curr_loc++;
+ ascii_codes = (uint8_t *)curr_loc;
+ k = 0;
+ /* store all states that have non fail transitions in the temp buffer */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[state][ascii_code] == SC_AC_GFBS_FAIL)
+ continue;
+ ascii_codes[k] = ascii_code;
+ temp_states[k] = ctx->goto_table[state][ascii_code];
+ k++;
+ }
+ /* if we have any non fail transitions from our previous for search,
+ * store the acii codes as well the corresponding states */
+ if (k > 0) {
+ no_of_entries[0] = k;
+ if (state != 0) {
+ int jump = (k + 3) & 0xFFC;
+ curr_loc += jump / 4;
+ }
+ memcpy(curr_loc, temp_states, k * sizeof(SC_AC_GFBS_STATE_TYPE_U32));
+ curr_loc += k;
+ }
+ failure_entry[0] = ctx->failure_table[state];
+ }
+ }
+
+ return;
+}
+
+static inline void SCACGfbsClubOutputStatePresenceWithModGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ int state = 0;
+ int no_of_entries;
+ int i;
+
+ if (ctx->state_count < 32767) {
+ uint16_t *states;
+ for (state = 0; state < ctx->state_count; state++) {
+ no_of_entries = *((uint16_t *)ctx->goto_table_mod_pointers[state]);
+ if (no_of_entries == 0)
+ continue;
+
+ //if (*((uint16_t *)ctx->goto_table_mod_pointers[state] + 1) != 0) {
+ if (ctx->output_table[((uint16_t *)ctx->goto_table_mod_pointers[state] + 1)[0]].no_of_entries != 0) {
+ *((uint16_t *)ctx->goto_table_mod_pointers[state] + 1) |= (1 << 15);
+ }
+
+ if (state == 0)
+ states = ((uint16_t *)ctx->goto_table_mod_pointers[state] + 2);
+ else
+ states = ((uint16_t *)ctx->goto_table_mod_pointers[state] + 2 + ((no_of_entries + 1) & 0xFFE) / 2);
+ for (i = 0; i < no_of_entries; i++) {
+ //if (states[i] == 0)
+ if (ctx->output_table[states[i]].no_of_entries == 0)
+ continue;
+
+ states[i] |= (1 << 15);
+ }
+ }
+
+ } else {
+ uint32_t *states;
+ for (state = 0; state < ctx->state_count; state++) {
+ no_of_entries = *((uint32_t *)ctx->goto_table_mod_pointers[state]);
+ if (no_of_entries == 0)
+ continue;
+
+ //if (*((uint32_t *)ctx->goto_table_mod_pointers[state] + 1) != 0) {
+ if (ctx->output_table[((uint32_t *)ctx->goto_table_mod_pointers[state] + 1)[0]].no_of_entries != 0) {
+ *((uint32_t *)ctx->goto_table_mod_pointers[state] + 1) |= (1 << 24);
+ }
+
+ if (state == 0)
+ states = ((uint32_t *)ctx->goto_table_mod_pointers[state] + 2);
+ else
+ states = ((uint32_t *)ctx->goto_table_mod_pointers[state] + 2 + ((no_of_entries + 3) & 0xFFC) / 4);
+ for (i = 0; i < no_of_entries; i++) {
+ //if (states[i] == 0)
+ if (ctx->output_table[states[i]].no_of_entries == 0)
+ continue;
+
+ states[i] |= (1 << 24);
+ }
+ }
+ }
+
+ return;
+}
+
+static inline void SCACGfbsInsertCaseSensitiveEntriesForPatterns(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ int32_t state = 0;
+ uint32_t k = 0;
+
+ for (state = 0; state < ctx->state_count; state++) {
+ if (ctx->output_table[state].no_of_entries == 0)
+ continue;
+
+ for (k = 0; k < ctx->output_table[state].no_of_entries; k++) {
+ if (ctx->pid_pat_list[ctx->output_table[state].pids[k]].cs != NULL) {
+ ctx->output_table[state].pids[k] &= 0x0000FFFF;
+ ctx->output_table[state].pids[k] |= 1 << 16;
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ * \brief Process the patterns and prepare the state table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACGfbsPrepareStateTable(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ /* create the 0th state in the goto table and output_table */
+ SCACGfbsInitNewState(mpm_ctx);
+
+ /* create the goto table */
+ SCACGfbsCreateGotoTable(mpm_ctx);
+ /* create the failure table */
+ SCACGfbsCreateFailureTable(mpm_ctx);
+ /* create the final state(delta) table */
+ SCACGfbsCreateModGotoTable(mpm_ctx);
+ /* club the output state presence with transition entries */
+ SCACGfbsClubOutputStatePresenceWithModGotoTable(mpm_ctx);
+
+ /* club nocase entries */
+ SCACGfbsInsertCaseSensitiveEntriesForPatterns(mpm_ctx);
+
+ /* we don't need this anymore */
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCFree(ctx->failure_table);
+ ctx->failure_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Process the patterns added to the mpm, and create the internal tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+int SCACGfbsPreparePatterns(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ if (mpm_ctx->pattern_cnt == 0 || ctx->init_hash == NULL) {
+ SCLogDebug("No patterns supplied to this mpm_ctx");
+ return 0;
+ }
+
+ /* alloc the pattern array */
+ ctx->parray = (SCACGfbsPattern **)SCMalloc(mpm_ctx->pattern_cnt *
+ sizeof(SCACGfbsPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(SCACGfbsPattern *));
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(SCACGfbsPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ SCACGfbsPattern *node = ctx->init_hash[i], *nnode = NULL;
+ while(node != NULL) {
+ nnode = node->next;
+ node->next = NULL;
+ ctx->parray[p++] = node;
+ node = nnode;
+ }
+ }
+
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* the memory consumed by a single state in our goto table */
+ //ctx->single_state_size = sizeof(int32_t) * 256;
+
+ /* handle no case patterns */
+ ctx->pid_pat_list = SCMalloc((ctx->max_pat_id + 1)* sizeof(SCACGfbsPatternList));
+ if (ctx->pid_pat_list == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->pid_pat_list, 0, (ctx->max_pat_id + 1) * sizeof(SCACGfbsPatternList));
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (!(ctx->parray[i]->flags & MPM_PATTERN_FLAG_NOCASE)) {
+ ctx->pid_pat_list[ctx->parray[i]->id].cs = SCMalloc(ctx->parray[i]->len);
+ if (ctx->pid_pat_list[ctx->parray[i]->id].cs == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(ctx->pid_pat_list[ctx->parray[i]->id].cs,
+ ctx->parray[i]->original_pat, ctx->parray[i]->len);
+ ctx->pid_pat_list[ctx->parray[i]->id].patlen = ctx->parray[i]->len;
+ }
+
+ /* ACPatternList now owns this memory */
+ ctx->pid_pat_list[ctx->parray[i]->id].sids_size = ctx->parray[i]->sids_size;
+ ctx->pid_pat_list[ctx->parray[i]->id].sids = ctx->parray[i]->sids;
+ }
+
+ /* prepare the state table required by AC */
+ SCACGfbsPrepareStateTable(mpm_ctx);
+
+ /* free all the stored patterns. Should save us a good 100-200 mbs */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACGfbsFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACGfbsPattern *));
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Init the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param matchsize We don't need this.
+ */
+void SCACGfbsInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(SCACGfbsThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_thread_ctx->ctx, 0, sizeof(SCACGfbsThreadCtx));
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(SCACGfbsThreadCtx);
+
+ return;
+}
+
+/**
+ * \brief Initialize the AC context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param module_handle Cuda module handle from the cuda handler API. We don't
+ * have to worry about this here.
+ */
+void SCACGfbsInitCtx(MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx->ctx != NULL)
+ return;
+
+ mpm_ctx->ctx = SCMalloc(sizeof(SCACGfbsCtx));
+ if (mpm_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_ctx->ctx, 0, sizeof(SCACGfbsCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACGfbsCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(SCACGfbsPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->init_hash, 0, sizeof(SCACGfbsPattern *) * INIT_HASH_SIZE);
+
+ /* get conf values for AC from our yaml file. We have no conf values for
+ * now. We will certainly need this, as we develop the algo */
+ SCACGfbsGetConfig();
+
+ SCReturn;
+}
+
+/**
+ * \brief Destroy the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ */
+void SCACGfbsDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ SCACGfbsPrintSearchStats(mpm_thread_ctx);
+
+ if (mpm_thread_ctx->ctx != NULL) {
+ SCFree(mpm_thread_ctx->ctx);
+ mpm_thread_ctx->ctx = NULL;
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(SCACGfbsThreadCtx);
+ }
+
+ return;
+}
+
+/**
+ * \brief Destroy the mpm context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+void SCACGfbsDestroyCtx(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash != NULL) {
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(SCACGfbsPattern *));
+ }
+
+ if (ctx->parray != NULL) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACGfbsFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACGfbsPattern *));
+ }
+
+ if (ctx->goto_table_mod != NULL) {
+ SCFree(ctx->goto_table_mod);
+ ctx->goto_table_mod = NULL;
+
+ mpm_ctx->memory_cnt--;
+ if (ctx->state_count < 32767) {
+ mpm_ctx->memory_size -= (ctx->state_count * sizeof(SC_AC_GFBS_STATE_TYPE_U16) * 3 +
+ 256 * sizeof(SC_AC_GFBS_STATE_TYPE_U16) * 2);
+ } else {
+ mpm_ctx->memory_size -= (ctx->state_count * sizeof(SC_AC_GFBS_STATE_TYPE_U32) * 3 +
+ 256 * sizeof(SC_AC_GFBS_STATE_TYPE_U32) * 2);
+ }
+ }
+
+ if (ctx->goto_table_mod_pointers != NULL) {
+ SCFree(ctx->goto_table_mod_pointers);
+ ctx->goto_table_mod_pointers = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= ctx->state_count * sizeof(uint8_t *);
+ }
+
+ if (ctx->output_table != NULL) {
+ int32_t state_count;
+ for (state_count = 0; state_count < ctx->state_count; state_count++) {
+ if (ctx->output_table[state_count].pids != NULL) {
+ SCFree(ctx->output_table[state_count].pids);
+ }
+ }
+ SCFree(ctx->output_table);
+ }
+
+ if (ctx->pid_pat_list != NULL) {
+ int i;
+ for (i = 0; i < (ctx->max_pat_id + 1); i++) {
+ if (ctx->pid_pat_list[i].cs != NULL)
+ SCFree(ctx->pid_pat_list[i].cs);
+ if (ctx->pid_pat_list[i].sids != NULL)
+ SCFree(ctx->pid_pat_list[i].sids);
+ }
+ SCFree(ctx->pid_pat_list);
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACGfbsCtx);
+
+ return;
+}
+
+/**
+ * \brief The aho corasick search function.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param pmq Pointer to the Pattern Matcher Queue to hold
+ * search matches.
+ * \param buf Buffer to be searched.
+ * \param buflen Buffer length.
+ *
+ * \retval matches Match count.
+ */
+uint32_t SCACGfbsSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+ int matches = 0;
+ uint8_t buf_local;
+
+ SCACGfbsPatternList *pid_pat_list = ctx->pid_pat_list;
+
+ uint8_t bitarray[pmq->pattern_id_bitarray_size];
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+
+ /* really hate the extra cmp here, but can't help it */
+ if (ctx->state_count < 32767) {
+ /* \todo Change it for stateful MPM. Supply the state using mpm_thread_ctx */
+ int32_t temp_state;
+ uint16_t no_of_entries;
+ uint8_t *ascii_codes;
+ uint16_t **goto_table_mod_pointers = (uint16_t **)ctx->goto_table_mod_pointers;
+
+ //int32_t *failure_table = ctx->failure_table;
+ int i;
+ /* \todo tried loop unrolling with register var, with no perf increase. Need
+ * to dig deeper */
+ /* with so many var declarations the register declaration here is useless */
+ register int32_t state = 0;
+ for (i = 0; i < buflen; i++) {
+ if (state == 0) {
+ state = (goto_table_mod_pointers[0] + 2)[u8_tolower(buf[i])];
+ } else {
+
+ /* get the goto state transition */
+ no_of_entries = *(goto_table_mod_pointers[state & 0x7FFF]);
+ if (no_of_entries == 0) {
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ if (no_of_entries == 1) {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x7FFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0])
+ temp_state = ((uint16_t *)(ascii_codes + ((no_of_entries + 1) & 0xFFE)))[0];
+ else
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ buf_local = u8_tolower(buf[i]);
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x7FFF] + 2);
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ temp_state = SC_AC_GFBS_FAIL;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ temp_state = ((uint16_t *)(ascii_codes + ((no_of_entries + 1) & 0xFFE)))[mid];
+ break;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ }
+ }
+ while (temp_state == SC_AC_GFBS_FAIL) {
+ state = *(goto_table_mod_pointers[state & 0x7FFF] + 1);
+
+ /* get the goto state transition */
+ no_of_entries = *(goto_table_mod_pointers[state & 0x7FFF]);
+ if (no_of_entries == 0) {
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ if (no_of_entries == 1) {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x7FFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0])
+ temp_state = ((uint16_t *)(ascii_codes + ((no_of_entries + 1) & 0xFFE)))[0];
+ else
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x7FFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (state == 0) {
+ temp_state = ((uint16_t *)ascii_codes)[buf_local];
+ } else {
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ temp_state = SC_AC_GFBS_FAIL;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ temp_state = ((uint16_t *)(ascii_codes + ((no_of_entries + 1) & 0xFFE)))[mid];
+ break;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ }
+ }
+ } /* else - if (no_of_entries == 0) */
+ } /* while (temp_state == SC_AC_GFBS_FAIL) */
+
+ state = temp_state;
+
+ }
+
+ if (state & 0x8000) {
+ uint32_t no_of_pid_entries = ctx->output_table[state & 0x7FFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x7FFF].pids;
+ uint32_t k = 0;
+ for (k = 0; k < no_of_pid_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ pmq->pattern_id_bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ }
+ } /* if (ctx->output_table[state].no_of_entries != 0) */
+ } /* for (i = 0; i < buflen; i++) */
+
+ } else {
+ /* \todo Change it for stateful MPM. Supply the state using mpm_thread_ctx */
+ int32_t temp_state = 0;
+ uint32_t no_of_entries;
+ uint8_t *ascii_codes = NULL;
+ uint32_t **goto_table_mod_pointers = (uint32_t **)ctx->goto_table_mod_pointers;
+ //int32_t *failure_table = ctx->failure_table;
+ int i = 0;
+ /* \todo tried loop unrolling with register var, with no perf increase. Need
+ * to dig deeper */
+ register int32_t state = 0;
+ for (i = 0; i < buflen; i++) {
+ if (state == 0) {
+ state = (goto_table_mod_pointers[0] + 2)[u8_tolower(buf[i])];
+ } else {
+
+ /* get the goto state transition */
+ no_of_entries = *(goto_table_mod_pointers[state & 0x00FFFFFF]);
+ if (no_of_entries == 0) {
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ if (no_of_entries == 1) {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x00FFFFFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0])
+ temp_state = ((uint32_t *)(ascii_codes + ((no_of_entries + 3) & 0xFFC)))[0];
+ else
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ buf_local = u8_tolower(buf[i]);
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x00FFFFFF] + 2);
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ temp_state = SC_AC_GFBS_FAIL;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ temp_state = ((uint32_t *)(ascii_codes + ((no_of_entries + 3) & 0xFFC)))[mid];
+ break;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ }
+ }
+ while (temp_state == SC_AC_GFBS_FAIL) {
+ state = *(goto_table_mod_pointers[state & 0x00FFFFFF] + 1);
+
+ /* get the goto state transition */
+ no_of_entries = *(goto_table_mod_pointers[state & 0x00FFFFFF]);
+ if (no_of_entries == 0) {
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ if (no_of_entries == 1) {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x00FFFFFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (buf_local == ascii_codes[0])
+ temp_state = ((uint32_t *)(ascii_codes + ((no_of_entries + 3) & 0xFFC)))[0];
+ else
+ temp_state = SC_AC_GFBS_FAIL;
+ } else {
+ ascii_codes = (uint8_t *)(goto_table_mod_pointers[state & 0x00FFFFFF] + 2);
+ buf_local = u8_tolower(buf[i]);
+ if (state == 0) {
+ temp_state = ((uint32_t *)ascii_codes)[buf_local];
+ } else {
+ int low = 0;
+ int high = no_of_entries;
+ int mid;
+ temp_state = SC_AC_GFBS_FAIL;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (ascii_codes[mid] == buf_local) {
+ temp_state = ((uint32_t *)(ascii_codes + ((no_of_entries + 3) & 0xFFC)))[mid];
+ break;
+ } else if (ascii_codes[mid] < buf_local) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ }
+ } /* else - if (no_of_entries[0] == 1) */
+ } /* else - if (no_of_entries[0] == 0) */
+ } /* while (temp_state == SC_AC_GFBS_FAIL) */
+ state = temp_state;
+
+ }
+
+ if (state & 0x01000000) {
+ uint32_t no_of_pid_entries = ctx->output_table[state & 0x00FFFFFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x00FFFFFF].pids;
+ uint32_t k = 0;
+ for (k = 0; k < no_of_pid_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ pmq->pattern_id_bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ //loop1:
+ //;
+ }
+ } /* if (ctx->output_table[state].no_of_entries != 0) */
+ } /* for (i = 0; i < buflen; i++) */
+ }
+
+ return matches;
+}
+
+/**
+ * \brief Add a case insensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACGfbsAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return SCACGfbsAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+/**
+ * \brief Add a case sensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACGfbsAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ return SCACGfbsAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+void SCACGfbsPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+
+#ifdef SC_AC_COUNTERS
+ SCACGfbsThreadCtx *ctx = (SCACGfbsThreadCtx *)mpm_thread_ctx->ctx;
+ printf("AC Thread Search stats (ctx %p)\n", ctx);
+ printf("Total calls: %" PRIu32 "\n", ctx->total_calls);
+ printf("Total matches: %" PRIu64 "\n", ctx->total_matches);
+#endif /* SC_AC_COUNTERS */
+
+ return;
+}
+
+void SCACGfbsPrintInfo(MpmCtx *mpm_ctx)
+{
+ SCACGfbsCtx *ctx = (SCACGfbsCtx *)mpm_ctx->ctx;
+
+ printf("MPM AC Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeof:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" SCACGfbsCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCACGfbsCtx));
+ printf(" SCACGfbsPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACGfbsPattern));
+ printf(" SCACGfbsPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACGfbsPattern));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Total states in the state table: %" PRIu32 "\n", ctx->state_count);
+ printf("\n");
+
+ return;
+}
+
+/*************************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+static int SCACGfbsTest01(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest02(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest03(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest04(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest05(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest06(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcd";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest07(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0);
+ /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0);
+ /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0);
+ /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0);
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ 30, 0, 0, 5, 0, 0);
+ /* total matches: 135 */
+ PmqSetup(&pmq, 6);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest08(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest09(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest10(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "abcdefgh"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest11(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1)
+ goto end;
+ PmqSetup(&pmq, 4);
+
+ if (SCACGfbsPreparePatterns(&mpm_ctx) == -1)
+ goto end;
+
+ result = 1;
+
+ char *buf = "he";
+ result &= (SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "she";
+ result &= (SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+ buf = "his";
+ result &= (SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "hers";
+ result &= (SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+
+ end:
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest12(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest13(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCD";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCD";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest14(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDE";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDE";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest15(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest16(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABC";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABC";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest17(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzAB";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzAB";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest18(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest19(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest20(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest21(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"AA", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest22(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest23(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest24(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest25(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest26(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "works";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest27(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "tone";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest28(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_GFBS);
+ SCACGfbsInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACGfbsPreparePatterns(&mpm_ctx);
+
+ char *buf = "tONE";
+ uint32_t cnt = SCACGfbsSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACGfbsDestroyCtx(&mpm_ctx);
+ SCACGfbsDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACGfbsTest29(void)
+{
+ uint8_t *buf = (uint8_t *)"onetwothreefourfivesixseveneightnine";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+ de_ctx->mpm_matcher = MPM_AC_GFBS;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; fast_pattern:3,3; sid:2;)");
+ if (de_ctx->sig_list->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 1) failure\n");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 2) failure\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCACGfbsRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCACGfbsTest01", SCACGfbsTest01, 1);
+ UtRegisterTest("SCACGfbsTest02", SCACGfbsTest02, 1);
+ UtRegisterTest("SCACGfbsTest03", SCACGfbsTest03, 1);
+ UtRegisterTest("SCACGfbsTest04", SCACGfbsTest04, 1);
+ UtRegisterTest("SCACGfbsTest05", SCACGfbsTest05, 1);
+ UtRegisterTest("SCACGfbsTest06", SCACGfbsTest06, 1);
+ UtRegisterTest("SCACGfbsTest07", SCACGfbsTest07, 1);
+ UtRegisterTest("SCACGfbsTest08", SCACGfbsTest08, 1);
+ UtRegisterTest("SCACGfbsTest09", SCACGfbsTest09, 1);
+ UtRegisterTest("SCACGfbsTest10", SCACGfbsTest10, 1);
+ UtRegisterTest("SCACGfbsTest11", SCACGfbsTest11, 1);
+ UtRegisterTest("SCACGfbsTest12", SCACGfbsTest12, 1);
+ UtRegisterTest("SCACGfbsTest13", SCACGfbsTest13, 1);
+ UtRegisterTest("SCACGfbsTest14", SCACGfbsTest14, 1);
+ UtRegisterTest("SCACGfbsTest15", SCACGfbsTest15, 1);
+ UtRegisterTest("SCACGfbsTest16", SCACGfbsTest16, 1);
+ UtRegisterTest("SCACGfbsTest17", SCACGfbsTest17, 1);
+ UtRegisterTest("SCACGfbsTest18", SCACGfbsTest18, 1);
+ UtRegisterTest("SCACGfbsTest19", SCACGfbsTest19, 1);
+ UtRegisterTest("SCACGfbsTest20", SCACGfbsTest20, 1);
+ UtRegisterTest("SCACGfbsTest21", SCACGfbsTest21, 1);
+ UtRegisterTest("SCACGfbsTest22", SCACGfbsTest22, 1);
+ UtRegisterTest("SCACGfbsTest23", SCACGfbsTest23, 1);
+ UtRegisterTest("SCACGfbsTest24", SCACGfbsTest24, 1);
+ UtRegisterTest("SCACGfbsTest25", SCACGfbsTest25, 1);
+ UtRegisterTest("SCACGfbsTest26", SCACGfbsTest26, 1);
+ UtRegisterTest("SCACGfbsTest27", SCACGfbsTest27, 1);
+ UtRegisterTest("SCACGfbsTest28", SCACGfbsTest28, 1);
+ UtRegisterTest("SCACGfbsTest29", SCACGfbsTest29, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-mpm-ac-gfbs.h b/framework/src/suricata/src/util-mpm-ac-gfbs.h
new file mode 100644
index 00000000..4003a5b5
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-gfbs.h
@@ -0,0 +1,110 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ */
+
+#define SC_AC_GFBS_STATE_TYPE_U16 uint16_t
+#define SC_AC_GFBS_STATE_TYPE_U32 uint32_t
+
+typedef struct SCACGfbsPattern_ {
+ /* length of the pattern */
+ uint16_t len;
+ /* flags decribing the pattern */
+ uint8_t flags;
+ /* holds the original pattern that was added */
+ uint8_t *original_pat;
+ /* case sensitive */
+ uint8_t *cs;
+ /* case INsensitive */
+ uint8_t *ci;
+ /* pattern id */
+ uint32_t id;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct SCACGfbsPattern_ *next;
+} SCACGfbsPattern;
+
+typedef struct SCACGfbsPatternList_ {
+ uint8_t *cs;
+ uint16_t patlen;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+} SCACGfbsPatternList;
+
+typedef struct SCACGfbsOutputTable_ {
+ /* list of pattern sids */
+ uint32_t *pids;
+ /* no of entries we have in pids */
+ uint32_t no_of_entries;
+} SCACGfbsOutputTable;
+
+typedef struct SCACGfbsGotoTableMod_ {
+ /* each of these below declarations will be of type uint32_t, if the state
+ * count exceeds 65535, the maximum value a 16 bit unsigned var can hold */
+
+ /* no of entries stored below */
+ uint16_t no_of_entries;
+
+ /* the ascii codes over which we have state transitions */
+ uint16_t *ascii_codes;
+ /* the states that correspond to the ascii_codes above */
+ uint16_t *states;
+} SCACGfbsGotoTableMod_;
+
+typedef struct SCACGfbsCtx_ {
+ /* hash used during ctx initialization */
+ SCACGfbsPattern **init_hash;
+
+ /* pattern arrays. We need this only during the goto table creation phase */
+ SCACGfbsPattern **parray;
+
+ /* no of states used by ac */
+ int32_t state_count;
+ /* the modified goto_table */
+ uint8_t *goto_table_mod;
+ uint8_t **goto_table_mod_pointers;
+
+ /* goto_table, failure table and output table. Needed to create state_table.
+ * Will be freed, once we have created the goto_table_mod */
+ int32_t (*goto_table)[256];
+ int32_t *failure_table;
+ SCACGfbsOutputTable *output_table;
+ SCACGfbsPatternList *pid_pat_list;
+
+ /* the size of each state */
+ uint16_t single_state_size;
+ uint16_t max_pat_id;
+} SCACGfbsCtx;
+
+typedef struct SCACGfbsThreadCtx_ {
+ /* the total calls we make to the search function */
+ uint32_t total_calls;
+ /* the total patterns that we ended up matching against */
+ uint64_t total_matches;
+} SCACGfbsThreadCtx;
+
+void MpmACGfbsRegister(void);
diff --git a/framework/src/suricata/src/util-mpm-ac-tile-small.c b/framework/src/suricata/src/util-mpm-ac-tile-small.c
new file mode 100644
index 00000000..a99da063
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-tile-small.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ken Steele <suricata@tilera.com>
+
+ * Included by util-mpm-ac-tile.c with different SLOAD, SINDEX and
+ * FUNC_NAME
+ *
+ */
+
+/* Only included into util-mpm-ac-tile.c, which defines FUNC_NAME
+ *
+ */
+#ifdef FUNC_NAME
+
+/* This function handles (ctx->state_count < 32767) */
+uint32_t FUNC_NAME(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ int i = 0;
+ int matches = 0;
+
+ uint8_t mpm_bitarray[ctx->mpm_bitarray_size];
+ memset(mpm_bitarray, 0, ctx->mpm_bitarray_size);
+
+ uint8_t* restrict xlate = ctx->translate_table;
+ STYPE *state_table = (STYPE*)ctx->state_table;
+ STYPE state = 0;
+ int c = xlate[buf[0]];
+ /* If buflen at least 4 bytes and buf 4-byte aligned. */
+ if (buflen >= 4 && ((uint64_t)buf & 0x3) == 0) {
+ BTYPE data = *(BTYPE* restrict)(&buf[0]);
+ uint64_t index = 0;
+ /* Process 4*floor(buflen/4) bytes. */
+ i = 0;
+ while (i < (buflen & ~0x3)) {
+ BTYPE data1 = *(BTYPE* restrict)(&buf[i + 4]);
+ index = SINDEX(index, state);
+ state = SLOAD(state_table + index + c);
+ c = xlate[BYTE1(data)];
+ if (unlikely(SCHECK(state))) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ i++;
+ index = SINDEX(index, state);
+ state = SLOAD(state_table + index + c);
+ c = xlate[BYTE2(data)];
+ if (unlikely(SCHECK(state))) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ i++;
+ index = SINDEX(index, state);
+ state = SLOAD(state_table + index + c);
+ c = xlate[BYTE3(data)];
+ if (unlikely(SCHECK(state))) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ data = data1;
+ i++;
+ index = SINDEX(index, state);
+ state = SLOAD(state_table + index + c);
+ c = xlate[BYTE0(data)];
+ if (unlikely(SCHECK(state))) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ i++;
+ }
+ }
+ /* Process buflen % 4 bytes. */
+ for (; i < buflen; i++) {
+ uint64_t index = 0 ;
+ index = SINDEX(index, state);
+ state = SLOAD(state_table + index + c);
+ c = xlate[buf[i+1]];
+ if (unlikely(SCHECK(state))) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ } /* for (i = 0; i < buflen; i++) */
+
+ return matches;
+}
+
+#endif /* FUNC_NAME */
diff --git a/framework/src/suricata/src/util-mpm-ac-tile.c b/framework/src/suricata/src/util-mpm-ac-tile.c
new file mode 100644
index 00000000..f3a52d6e
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-tile.c
@@ -0,0 +1,2855 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ken Steele <suricata@tilera.com>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Aho-corasick MPM optimized for the Tilera Tile-Gx architecture.
+ *
+ * Efficient String Matching: An Aid to Bibliographic Search
+ * Alfred V. Aho and Margaret J. Corasick
+ *
+ * - Started with util-mpm-ac.c:
+ * - Uses the delta table for calculating transitions,
+ * instead of having separate goto and failure
+ * transitions.
+ * - If we cross 2 ** 16 states, we use 4 bytes in the
+ * transition table to hold each state, otherwise we use
+ * 2 bytes.
+ * - This version of the MPM is heavy on memory, but it
+ * performs well. If you can fit the ruleset with this
+ * mpm on your box without hitting swap, this is the MPM
+ * to go for.
+ *
+ * - Added these optimizations:
+ * - Compress the input alphabet from 256 characters down
+ * to the actual characters used in the patterns, plus
+ * one character for all the unused characters.
+ * - Reduce the size of the delta table so that each state
+ * is the smallest power of two that is larger than the
+ * size of the compressed alphabet.
+ * - Specialized the search function based on state count
+ * (small for 8-bit large for 16-bit) and the size of
+ * the alphabet, so that it is constant inside the
+ * function for better optimization.
+ *
+ * \todo - Do a proper analyis of our existing MPMs and suggest a good
+ * one based on the pattern distribution and the expected
+ * traffic(say http).
+
+ * - Irrespective of whether we cross 2 ** 16 states or
+ * not,shift to using uint32_t for state type, so that we can
+ * integrate it's status as a final state or not in the
+ * topmost byte. We are already doing it if state_count is >
+ * 2 ** 16.
+ * - Test case-senstive patterns if they have any ascii chars.
+ * If they don't treat them as nocase.
+ * - Reorder the compressed alphabet to put the most common characters
+ * first.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "conf.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+#include "util-mpm-ac-tile.h"
+
+#ifndef __tile__
+void MpmACTileRegister(void)
+{
+}
+#endif
+
+/* There are Tilera Tile-Gx specific optimizations in this code. */
+#ifdef __tile__
+
+void SCACTileInitCtx(MpmCtx *);
+void SCACTileInitThreadCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void SCACTileDestroyCtx(MpmCtx *);
+void SCACTileDestroyThreadCtx(MpmCtx *, MpmThreadCtx *);
+int SCACTileAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACTileAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACTilePreparePatterns(MpmCtx *mpm_ctx);
+uint32_t SCACTileSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf,
+ uint16_t buflen);
+void SCACTilePrintInfo(MpmCtx *mpm_ctx);
+void SCACTilePrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void SCACTileRegisterTests(void);
+
+uint32_t SCACTileSearchLarge(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall256(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall128(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall64(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall32(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall16(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchSmall8(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+
+uint32_t SCACTileSearchTiny256(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchTiny128(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchTiny64(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchTiny32(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchTiny16(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+uint32_t SCACTileSearchTiny8(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen);
+
+
+static void SCACTileDestroyInitCtx(MpmCtx *mpm_ctx);
+
+
+/* a placeholder to denote a failure transition in the goto table */
+#define SC_AC_TILE_FAIL (-1)
+/* size of the hash table used to speed up pattern insertions initially */
+#define INIT_HASH_SIZE 65536
+
+#define STATE_QUEUE_CONTAINER_SIZE 65536
+
+/**
+ * \brief Helper structure used by AC during state table creation
+ */
+typedef struct StateQueue_ {
+ int32_t store[STATE_QUEUE_CONTAINER_SIZE];
+ int top;
+ int bot;
+} StateQueue;
+
+/**
+ * \internal
+ * \brief Initialize the AC context with user specified conf parameters. We
+ * aren't retrieving anything for AC conf now, but we will certainly
+ * need it, when we customize AC.
+ */
+static void SCACTileGetConfig()
+{
+}
+
+/**
+ * \internal
+ * \brief Compares 2 patterns. We use it for the hashing process during the
+ * the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param p Pointer to the first pattern(SCACTilePattern).
+ * \param pat Pointer to the second pattern(raw pattern array).
+ * \param patlen Pattern length.
+ * \param flags Flags. We don't need this.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline int SCACTileCmpPattern(SCACTilePattern *p, uint8_t *pat,
+ uint16_t patlen, char flags)
+{
+ if (p->len != patlen)
+ return 0;
+
+ if (p->flags != flags)
+ return 0;
+
+ if (flags & MPM_PATTERN_FLAG_NOCASE) {
+ // Case insensitive
+ if (SCMemcmpLowercase(p->cs, pat, patlen) != 0)
+ return 0;
+
+ } else {
+ // Case sensitive
+ if (SCMemcmp(p->cs, pat, patlen) != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \internal
+ * \brief Creates a hash of the pattern. We use it for the hashing process
+ * during the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline uint32_t SCACTileInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+/**
+ * \internal
+ * \brief Looks up a pattern. We use it for the hashing process during the
+ * the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param ctx Pointer to the AC ctx.
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ * \param flags Flags. We don't need this.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline SCACTilePattern *SCACTileInitHashLookup(SCACTileCtx *ctx,
+ uint8_t *pat,
+ uint16_t patlen,
+ char flags,
+ uint32_t pid)
+{
+ uint32_t hash = SCACTileInitHashRaw(pat, patlen);
+
+ if (ctx->init_hash == NULL) {
+ return NULL;
+ }
+
+ SCACTilePattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (t->id == pid) {
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Allocs a new pattern instance.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval p Pointer to the newly created pattern.
+ */
+static inline SCACTilePattern *SCACTileAllocPattern(MpmCtx *mpm_ctx)
+{
+ SCACTilePattern *p = SCMalloc(sizeof(SCACTilePattern));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(SCACTilePattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACTilePattern);
+
+ return p;
+}
+
+/**
+ * \internal
+ * \brief Used to free SCACTilePattern instances.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param p Pointer to the SCACTilePattern instance to be freed.
+ * \param free Free the above pointer or not.
+ */
+static void SCACTileFreePattern(MpmCtx *mpm_ctx, SCACTilePattern *p)
+{
+ if (p != NULL && p->cs != NULL && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->ci != NULL) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->original_pat != NULL) {
+ SCFree(p->original_pat);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACTilePattern);
+ }
+}
+
+static inline uint32_t SCACTileInitHash(SCACTilePattern *p)
+{
+ uint32_t hash = p->len * p->original_pat[0];
+ if (p->len > 1)
+ hash += p->original_pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int SCACTileInitHashAdd(SCACTileCtx *ctx, SCACTilePattern *p)
+{
+ uint32_t hash = SCACTileInitHash(p);
+
+ if (ctx->init_hash == NULL) {
+ return 0;
+ }
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ return 0;
+ }
+
+ SCACTilePattern *tt = NULL;
+ SCACTilePattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+
+ return 0;
+}
+
+
+/**
+ * \internal
+ * \brief Count the occurences of each character in the pattern and
+ * accumulate into a histogram. Really only used to detect unused
+ * characters, so could just set to 1 instead of counting.
+ */
+static inline void SCACTileHistogramAlphabet(SCACTileCtx *ctx,
+ SCACTilePattern *p)
+{
+ for (int i = 0; i < p->len; i++) {
+ ctx->alpha_hist[p->ci[i]]++;
+ }
+}
+
+/* Use Alpahbet Histogram to create compressed alphabet.
+ */
+static void SCACTileInitTranslateTable(SCACTileCtx *ctx)
+{
+ /* Count the number of ASCII values actually appearing in any
+ * pattern. Create compressed mapping table with unused
+ * characters mapping to zero.
+ */
+ for (int i = 0; i < 256; i++) {
+ /* Move all upper case counts to lower case */
+ if (i >= 'A' && i <= 'Z') {
+ ctx->alpha_hist[i - 'A' + 'a'] += ctx->alpha_hist[i];
+ ctx->alpha_hist[i] = 0;
+ }
+ if (ctx->alpha_hist[i]) {
+ ctx->alphabet_size++;
+ ctx->translate_table[i] = ctx->alphabet_size;
+ } else
+ ctx->translate_table[i] = 0;
+ }
+ /* Fix up translation table for uppercase */
+ for (int i = 'A'; i <= 'Z'; i++)
+ ctx->translate_table[i] = ctx->translate_table[i - 'A' + 'a'];
+
+ SCLogDebug(" Alphabet size %d", ctx->alphabet_size);
+
+ /* Round alphabet size up to next power-of-two Leave one extra
+ * space For the unused-chararacters = 0 mapping.
+ */
+ ctx->alphabet_size += 1; /* Extra space for unused-character */
+ if (ctx->alphabet_size <= 8) {
+ ctx->alphabet_storage = 8;
+ } else if (ctx->alphabet_size <= 16) {
+ ctx->alphabet_storage = 16;
+ } else if (ctx->alphabet_size <= 32) {
+ ctx->alphabet_storage = 32;
+ } else if (ctx->alphabet_size <= 64) {
+ ctx->alphabet_storage = 64;
+ } else if (ctx->alphabet_size <= 128) {
+ ctx->alphabet_storage = 128;
+ } else
+ ctx->alphabet_storage = 256;
+}
+
+/**
+ * \internal
+ * \brief Add a pattern to the mpm-ac context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param pat Pointer to the pattern.
+ * \param patlen Length of the pattern.
+ * \param pid Pattern id
+ * \param sid Signature id (internal id).
+ * \param flags Pattern's MPM_PATTERN_* flags.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCACTileAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ uint32_t sid, uint8_t flags)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ SCLogDebug("Adding pattern for ctx %p, patlen %"PRIu16" and pid %" PRIu32,
+ ctx, patlen, pid);
+
+ if (patlen == 0) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "pattern length 0");
+ return 0;
+ }
+
+ /* Check if we have already inserted this pattern. */
+ SCACTilePattern *p = SCACTileInitHashLookup(ctx, pat, patlen, flags, pid);
+ if (p == NULL) {
+ SCLogDebug("Allocing new pattern");
+
+ /* p will never be NULL */
+ p = SCACTileAllocPattern(mpm_ctx);
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ p->original_pat = SCMalloc(patlen);
+ if (p->original_pat == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->original_pat, pat, patlen);
+
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci, pat, p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ /* put in the pattern hash */
+ SCACTileInitHashAdd(ctx, p);
+ /* Count alphabet usages */
+ SCACTileHistogramAlphabet(ctx, p);
+
+ //if (mpm_ctx->pattern_cnt == 65535) {
+ // SCLogError(SC_ERR_AHO_CORASICK, "Max search words reached. Can't "
+ // "insert anymore. Exiting");
+ // exit(EXIT_FAILURE);
+ //}
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen)
+ mpm_ctx->maxlen = patlen;
+
+ if (mpm_ctx->minlen == 0) {
+ mpm_ctx->minlen = patlen;
+ } else {
+ if (mpm_ctx->minlen > patlen)
+ mpm_ctx->minlen = patlen;
+ }
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ //SCLogInfo("MPM added %u:%u", pid, sid);
+ } else {
+ /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
+
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ //SCLogInfo("p->sids_size %u", p->sids_size);
+ //SCLogInfo("MPM added %u:%u (append)", pid, sid);
+ } else {
+ //SCLogInfo("rule %u already part of pid %u", sid, pid);
+ }
+ }
+
+ return 0;
+
+error:
+ SCACTileFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+static void SCACTileReallocOutputTable(SCACTileCtx *ctx, int new_state_count)
+{
+
+ /* reallocate space in the output table for the new state */
+ size_t size = ctx->allocated_state_count * sizeof(SCACTileOutputTable);
+ void *ptmp = SCRealloc(ctx->output_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->output_table);
+ ctx->output_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->output_table = ptmp;
+}
+
+static void SCACTileReallocState(SCACTileCtx *ctx, int new_state_count)
+{
+ /* reallocate space in the goto table to include a new state */
+ size_t size = ctx->allocated_state_count * sizeof(int32_t) * 256;
+ void *ptmp = SCRealloc(ctx->goto_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->goto_table = ptmp;
+
+ SCACTileReallocOutputTable(ctx, new_state_count);
+}
+
+/**
+ * \internal
+ * \brief Initialize a new state in the goto and output tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval The state id, of the newly created state.
+ */
+static inline int SCACTileInitNewState(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+ int aa = 0;
+
+ /* Exponentially increase the allocated space when needed. */
+ if (ctx->allocated_state_count < ctx->state_count + 1) {
+ if (ctx->allocated_state_count == 0)
+ ctx->allocated_state_count = 256;
+ else
+ ctx->allocated_state_count *= 2;
+
+ SCACTileReallocState(ctx, ctx->allocated_state_count);
+ }
+
+ /* set all transitions for the newly assigned state as FAIL transitions */
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ ctx->goto_table[ctx->state_count][aa] = SC_AC_TILE_FAIL;
+ }
+
+ memset(ctx->output_table + ctx->state_count, 0,
+ sizeof(SCACTileOutputTable));
+
+ return ctx->state_count++;
+}
+
+/**
+ * \internal
+ * \brief Adds a pid to the output table for a state.
+ *
+ * \param state The state to whose output table we should add the pid.
+ * \param pid The pattern id to add.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTileSetOutputState(int32_t state, MpmPatternIndex pindex, MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ SCACTileOutputTable *output_state = &ctx->output_table[state];
+ uint32_t i = 0;
+
+ /* Don't add the pattern more than once to the same state. */
+ for (i = 0; i < output_state->no_of_entries; i++) {
+ if (output_state->patterns[i] == pindex)
+ return;
+ }
+
+ /* Increase the size of the array of pids for this state and add
+ * the new pid. */
+ output_state->no_of_entries++;
+ ptmp = SCRealloc(output_state->patterns,
+ output_state->no_of_entries * sizeof(MpmPatternIndex));
+ if (ptmp == NULL) {
+ SCFree(output_state->patterns);
+ output_state->patterns = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_state->patterns = ptmp;
+
+ output_state->patterns[output_state->no_of_entries - 1] = pindex;
+}
+
+/**
+ * \brief Helper function used by SCACTileCreateGotoTable. Adds a
+ * pattern to the goto table.
+ *
+ * \param pattern Pointer to the pattern.
+ * \param pattern_len Pattern length.
+ * \param pid The pattern id, that corresponds to this pattern. We
+ * need it to updated the output table for this pattern.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTileEnter(uint8_t *pattern, uint16_t pattern_len,
+ MpmPatternIndex pindex, MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ int32_t state = 0;
+ int32_t newstate = 0;
+ int i = 0;
+ int p = 0;
+ int tc;
+
+ /* Walk down the trie till we have a match for the pattern prefix */
+ state = 0;
+ for (i = 0; i < pattern_len; i++) {
+ tc = ctx->translate_table[pattern[i]];
+ if (ctx->goto_table[state][tc] == SC_AC_TILE_FAIL)
+ break;
+ state = ctx->goto_table[state][tc];
+ }
+
+ /* Add the non-matching pattern suffix to the trie, from the last state
+ * we left off */
+ for (p = i; p < pattern_len; p++) {
+ newstate = SCACTileInitNewState(mpm_ctx);
+ tc = ctx->translate_table[pattern[p]];
+ ctx->goto_table[state][tc] = newstate;
+ state = newstate;
+ }
+
+ /* Add this pattern id, to the output table of the last state, where the
+ * pattern ends in the trie */
+ SCACTileSetOutputState(state, pindex, mpm_ctx);
+}
+
+/**
+ * \internal
+ * \brief Create the goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTileCreateGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ uint32_t i = 0;
+
+ /* add each pattern to create the goto table */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ SCACTileEnter(ctx->parray[i]->ci, ctx->parray[i]->len,
+ i, mpm_ctx);
+ }
+
+ int aa = 0;
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ if (ctx->goto_table[0][aa] == SC_AC_TILE_FAIL) {
+ ctx->goto_table[0][aa] = 0;
+ }
+ }
+}
+
+static inline int SCACTileStateQueueIsEmpty(StateQueue *q)
+{
+ if (q->top == q->bot)
+ return 1;
+ else
+ return 0;
+}
+
+static inline void SCACTileEnqueue(StateQueue *q, int32_t state)
+{
+ int i = 0;
+
+ /*if we already have this */
+ for (i = q->bot; i < q->top; i++) {
+ if (q->store[i] == state)
+ return;
+ }
+
+ q->store[q->top++] = state;
+
+ if (q->top == STATE_QUEUE_CONTAINER_SIZE)
+ q->top = 0;
+
+ if (q->top == q->bot) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static inline int32_t SCACTileDequeue(StateQueue *q)
+{
+ if (q->bot == STATE_QUEUE_CONTAINER_SIZE)
+ q->bot = 0;
+
+ if (q->bot == q->top) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "StateQueue behaving weirdly. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return q->store[q->bot++];
+}
+
+/**
+ * \internal
+ * \brief Club the output data from 2 states and store it in the 1st state.
+ * dst_state_data = {dst_state_data} UNION {src_state_data}
+ *
+ * \param dst_state First state(also the destination) for the union operation.
+ * \param src_state Second state for the union operation.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTileClubOutputStates(int32_t dst_state,
+ int32_t src_state,
+ MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ SCACTileOutputTable *output_dst_state = &ctx->output_table[dst_state];
+ SCACTileOutputTable *output_src_state = &ctx->output_table[src_state];
+
+ for (i = 0; i < output_src_state->no_of_entries; i++) {
+ for (j = 0; j < output_dst_state->no_of_entries; j++) {
+ if (output_src_state->patterns[i] == output_dst_state->patterns[j]) {
+ break;
+ }
+ }
+ if (j == output_dst_state->no_of_entries) {
+ output_dst_state->no_of_entries++;
+
+ ptmp = SCRealloc(output_dst_state->patterns,
+ (output_dst_state->no_of_entries * sizeof(uint32_t)));
+ if (ptmp == NULL) {
+ SCFree(output_dst_state->patterns);
+ output_dst_state->patterns = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_dst_state->patterns = ptmp;
+
+ output_dst_state->patterns[output_dst_state->no_of_entries - 1] =
+ output_src_state->patterns[i];
+ }
+ }
+}
+
+/**
+ * \internal
+ * \brief Create the failure table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTileCreateFailureTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ int aa = 0;
+ int32_t state = 0;
+ int32_t r_state = 0;
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ /* Allocate space for the failure table. A failure entry in the table for
+ * every state(SCACTileCtx->state_count) */
+ ctx->failure_table = SCMalloc(ctx->state_count * sizeof(int32_t));
+ if (ctx->failure_table == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->failure_table, 0, ctx->state_count * sizeof(int32_t));
+
+ /* Add the failure transitions for the 0th state, and add every non-fail
+ * transition from the 0th state to the queue for further processing
+ * of failure states */
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ int32_t temp_state = ctx->goto_table[0][aa];
+ if (temp_state != 0) {
+ SCACTileEnqueue(&q, temp_state);
+ ctx->failure_table[temp_state] = 0;
+ }
+ }
+
+ while (!SCACTileStateQueueIsEmpty(&q)) {
+ /* pick up every state from the queue and add failure transitions */
+ r_state = SCACTileDequeue(&q);
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ int32_t temp_state = ctx->goto_table[r_state][aa];
+ if (temp_state == SC_AC_TILE_FAIL)
+ continue;
+ SCACTileEnqueue(&q, temp_state);
+ state = ctx->failure_table[r_state];
+
+ while(ctx->goto_table[state][aa] == SC_AC_TILE_FAIL)
+ state = ctx->failure_table[state];
+ ctx->failure_table[temp_state] = ctx->goto_table[state][aa];
+ SCACTileClubOutputStates(temp_state, ctx->failure_table[temp_state],
+ mpm_ctx);
+ }
+ }
+}
+
+/*
+ * Set the next state for 1 byte next-state.
+ */
+static void SCACTileSetState1Byte(SCACTileCtx *ctx, int state, int aa,
+ int next_state, int outputs)
+{
+ uint8_t *state_table = (uint8_t*)ctx->state_table;
+ uint8_t encoded_next_state = next_state;
+
+ if (next_state == SC_AC_TILE_FAIL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error FAIL state in output");
+ exit(EXIT_FAILURE);
+ }
+
+ if (outputs == 0)
+ encoded_next_state |= (1 << 7);
+
+ state_table[state * ctx->alphabet_storage + aa] = encoded_next_state;
+}
+
+/*
+ * Set the next state for 2 byte next-state.
+ */
+static void SCACTileSetState2Bytes(SCACTileCtx *ctx, int state, int aa,
+ int next_state, int outputs)
+{
+ uint16_t *state_table = (uint16_t*)ctx->state_table;
+ uint16_t encoded_next_state = next_state;
+
+ if (next_state == SC_AC_TILE_FAIL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error FAIL state in output");
+ exit(EXIT_FAILURE);
+ }
+
+ if (outputs == 0)
+ encoded_next_state |= (1 << 15);
+
+ state_table[state * ctx->alphabet_storage + aa] = encoded_next_state;
+}
+
+/*
+ * Set the next state for 4 byte next-state.
+ */
+static void SCACTileSetState4Bytes(SCACTileCtx *ctx, int state, int aa,
+ int next_state, int outputs)
+{
+ uint32_t *state_table = (uint32_t*)ctx->state_table;
+ uint32_t encoded_next_state = next_state;
+
+ if (next_state == SC_AC_TILE_FAIL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error FAIL state in output");
+ exit(EXIT_FAILURE);
+ }
+
+ if (outputs == 0)
+ encoded_next_state |= (1 << 31);
+
+ state_table[state * ctx->alphabet_storage + aa] = encoded_next_state;
+}
+
+/**
+ * \internal
+ * \brief Create the delta table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACTileCreateDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ int aa = 0;
+ int32_t r_state = 0;
+
+ if (ctx->state_count < 32767) {
+ if (ctx->state_count < 128) {
+ ctx->bytes_per_state = 1;
+ ctx->set_next_state = SCACTileSetState1Byte;
+
+ switch(ctx->alphabet_storage) {
+ case 8:
+ ctx->search = SCACTileSearchTiny8;
+ break;
+ case 16:
+ ctx->search = SCACTileSearchTiny16;
+ break;
+ case 32:
+ ctx->search = SCACTileSearchTiny32;
+ break;
+ case 64:
+ ctx->search = SCACTileSearchTiny64;
+ break;
+ case 128:
+ ctx->search = SCACTileSearchTiny128;
+ break;
+ default:
+ ctx->search = SCACTileSearchTiny256;
+ }
+ } else {
+ /* 16-bit state needed */
+ ctx->bytes_per_state = 2;
+ ctx->set_next_state = SCACTileSetState2Bytes;
+
+ switch(ctx->alphabet_storage) {
+ case 8:
+ ctx->search = SCACTileSearchSmall8;
+ break;
+ case 16:
+ ctx->search = SCACTileSearchSmall16;
+ break;
+ case 32:
+ ctx->search = SCACTileSearchSmall32;
+ break;
+ case 64:
+ ctx->search = SCACTileSearchSmall64;
+ break;
+ case 128:
+ ctx->search = SCACTileSearchSmall128;
+ break;
+ default:
+ ctx->search = SCACTileSearchSmall256;
+ }
+ }
+ } else {
+ /* 32-bit next state */
+ ctx->search = SCACTileSearchLarge;
+ ctx->bytes_per_state = 4;
+ ctx->set_next_state = SCACTileSetState4Bytes;
+
+ ctx->alphabet_storage = 256; /* Change? */
+ }
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ int temp_state = ctx->goto_table[0][aa];
+ if (temp_state != 0)
+ SCACTileEnqueue(&q, temp_state);
+ }
+
+ while (!SCACTileStateQueueIsEmpty(&q)) {
+ r_state = SCACTileDequeue(&q);
+
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ int temp_state = ctx->goto_table[r_state][aa];
+ if (temp_state != SC_AC_TILE_FAIL) {
+ SCACTileEnqueue(&q, temp_state);
+ } else {
+ int f_state = ctx->failure_table[r_state];
+ ctx->goto_table[r_state][aa] = ctx->goto_table[f_state][aa];
+ }
+ }
+ }
+}
+
+static void SCACTileClubOutputStatePresenceWithDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ int aa = 0;
+ uint32_t state = 0;
+
+ /* Allocate next-state table. */
+ int size = ctx->state_count * ctx->bytes_per_state * ctx->alphabet_storage;
+ void *state_table = SCMalloc(size);
+ if (unlikely(state_table == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(state_table, 0, size);
+ ctx->state_table = state_table;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += size;
+
+ SCLogInfo("Delta Table size %d, alphabet: %d, %d-byte states: %d",
+ size, ctx->alphabet_size, ctx->bytes_per_state, ctx->state_count);
+
+ /* Copy next state from Goto table, which is 32 bits and encode it into the next
+ * state table, which can be 1, 2 or 4 bytes each and include if there is an
+ * output.
+ */
+ for (state = 0; state < ctx->state_count; state++) {
+ for (aa = 0; aa < ctx->alphabet_size; aa++) {
+ int next_state = ctx->goto_table[state][aa];
+ int next_state_outputs = ctx->output_table[next_state].no_of_entries;
+ ctx->set_next_state(ctx, state, aa, next_state, next_state_outputs);
+ }
+ }
+}
+
+static inline void SCACTileInsertCaseSensitiveEntriesForPatterns(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ uint32_t state = 0;
+ uint32_t k = 0;
+
+ for (state = 0; state < ctx->state_count; state++) {
+ if (ctx->output_table[state].no_of_entries == 0)
+ continue;
+
+ for (k = 0; k < ctx->output_table[state].no_of_entries; k++) {
+ if (ctx->pattern_list[ctx->output_table[state].patterns[k]].cs != NULL) {
+ /* TODO - Find better way to store this. */
+ ctx->output_table[state].patterns[k] &= 0x0FFFFFFF;
+ ctx->output_table[state].patterns[k] |= 1 << 31;
+ }
+ }
+ }
+}
+
+#if 0
+static void SCACTilePrintDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ int i = 0, j = 0;
+
+ printf("##############Delta Table##############\n");
+ for (i = 0; i < ctx->state_count; i++) {
+ printf("%d: \n", i);
+ for (j = 0; j < ctx->alphabet_size; j++) {
+ if (SCACTileGetDelta(i, j, mpm_ctx) != 0) {
+ printf(" %c -> %d\n", j, SCACTileGetDelta(i, j, mpm_ctx));
+ }
+ }
+ }
+}
+#endif
+
+/**
+ * \brief Process the patterns and prepare the state table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTilePrepareStateTable(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ /* Create Alphabet compression and Lower Case translation table. */
+ SCACTileInitTranslateTable(ctx);
+
+ /* create the 0th state in the goto table and output_table */
+ SCACTileInitNewState(mpm_ctx);
+
+ /* create the goto table */
+ SCACTileCreateGotoTable(mpm_ctx);
+ /* create the failure table */
+ SCACTileCreateFailureTable(mpm_ctx);
+ /* create the final state(delta) table */
+ SCACTileCreateDeltaTable(mpm_ctx);
+ /* club the output state presence with delta transition entries */
+ SCACTileClubOutputStatePresenceWithDeltaTable(mpm_ctx);
+
+ /* club nocase entries */
+ SCACTileInsertCaseSensitiveEntriesForPatterns(mpm_ctx);
+
+#if 0
+ SCACTilePrintDeltaTable(mpm_ctx);
+#endif
+
+ /* we don't need these anymore */
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCFree(ctx->failure_table);
+ ctx->failure_table = NULL;
+}
+
+
+/**
+ * \brief Process Internal AC MPM tables to create the Search Context
+ *
+ * The search context is only the data needed to search the MPM.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACTilePrepareSearch(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ /* Resize the output table to be only as big as its final size. */
+ SCACTileReallocOutputTable(ctx, ctx->state_count);
+
+ search_ctx->search = ctx->search;
+ memcpy(search_ctx->translate_table, ctx->translate_table, sizeof(ctx->translate_table));
+
+ /* Move the state table from the Init context */
+ search_ctx->state_table = ctx->state_table;
+ ctx->state_table = NULL; /* So that it won't get freed twice. */
+
+ /* Move the output_table from the Init context to the Search Context */
+ /* TODO: Could be made more compact */
+ search_ctx->output_table = ctx->output_table;
+ ctx->output_table = NULL;
+
+ search_ctx->pattern_list = ctx->pattern_list;
+ ctx->pattern_list = NULL;
+
+ /* One bit per pattern, rounded up to the next byte size. */
+ search_ctx->mpm_bitarray_size = (mpm_ctx->pattern_cnt + 7) / 8;
+
+ /* Can now free the Initialization data */
+ SCACTileDestroyInitCtx(mpm_ctx);
+}
+
+/**
+ * \brief Process the patterns added to the mpm, and create the internal tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+int SCACTilePreparePatterns(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+
+ if (mpm_ctx->pattern_cnt == 0 || search_ctx->init_ctx == NULL) {
+ SCLogDebug("no patterns supplied to this mpm_ctx");
+ return 0;
+ }
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+ if (ctx->init_hash == NULL) {
+ SCLogDebug("no patterns supplied to this mpm_ctx");
+ return 0;
+ }
+
+ /* alloc the pattern array */
+ ctx->parray = (SCACTilePattern **)SCMalloc(mpm_ctx->pattern_cnt *
+ sizeof(SCACTilePattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(SCACTilePattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ SCACTilePattern *node = ctx->init_hash[i], *nnode = NULL;
+ while(node != NULL) {
+ nnode = node->next;
+ node->next = NULL;
+ ctx->parray[p++] = node;
+ node = nnode;
+ }
+ }
+
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* Handle case patterns by storing a copy of the pattern to compare
+ * to each possible match (no-case).
+ *
+ * Allocate the memory for the array and each of the strings as one block.
+ */
+ size_t string_space_needed = 0;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (!(ctx->parray[i]->flags & MPM_PATTERN_FLAG_NOCASE)) {
+ /* Round up to next 8 byte aligned length */
+ uint32_t space = ((ctx->parray[i]->len + 7) / 8) * 8;
+ string_space_needed += space;
+ }
+ }
+
+ size_t pattern_list_size = mpm_ctx->pattern_cnt * sizeof(SCACTilePatternList);
+ size_t mem_size = string_space_needed + pattern_list_size;
+ void *mem_block = SCCalloc(1, mem_size);
+ if (mem_block == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += mem_size;
+ /* Split the allocated block into pattern list array and string space. */
+ ctx->pattern_list = mem_block;
+ uint8_t *string_space = mem_block + pattern_list_size;
+
+ /* Now make the copies of the no-case strings. */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (!(ctx->parray[i]->flags & MPM_PATTERN_FLAG_NOCASE)) {
+ uint32_t len = ctx->parray[i]->len;
+ uint32_t space = ((len + 7) / 8) * 8;
+ memcpy(string_space, ctx->parray[i]->original_pat, len);
+ ctx->pattern_list[i].cs = string_space;
+ ctx->pattern_list[i].patlen = len;
+ string_space += space;
+ }
+ ctx->pattern_list[i].pid = ctx->parray[i]->id;
+
+ /* ACPatternList now owns this memory */
+ ctx->pattern_list[i].sids_size = ctx->parray[i]->sids_size;
+ ctx->pattern_list[i].sids = ctx->parray[i]->sids;
+ }
+
+ /* prepare the state table required by AC */
+ SCACTilePrepareStateTable(mpm_ctx);
+
+ /* Convert to the Search Context structure */
+ SCACTilePrepareSearch(mpm_ctx);
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Init the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param matchsize We don't need this.
+ */
+void SCACTileInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(SCACTileThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_thread_ctx->ctx, 0, sizeof(SCACTileThreadCtx));
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(SCACTileThreadCtx);
+}
+
+/**
+ * \brief Initialize the AC context.
+ *
+ * \param mpm_ctx Mpm context.
+ */
+void SCACTileInitCtx(MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx->ctx != NULL)
+ return;
+
+ /* Search Context */
+ mpm_ctx->ctx = SCMalloc(sizeof(SCACTileSearchCtx));
+ if (mpm_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_ctx->ctx, 0, sizeof(SCACTileSearchCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACTileSearchCtx);
+
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+
+ /* MPM Creation context */
+ search_ctx->init_ctx = SCMalloc(sizeof(SCACTileCtx));
+ if (search_ctx->init_ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(search_ctx->init_ctx, 0, sizeof(SCACTileCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACTileCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+ ctx->init_hash = SCMalloc(sizeof(SCACTilePattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->init_hash, 0, sizeof(SCACTilePattern *) * INIT_HASH_SIZE);
+
+ /* get conf values for AC from our yaml file. We have no conf values for
+ * now. We will certainly need this, as we develop the algo */
+ SCACTileGetConfig();
+}
+
+/**
+ * \brief Destroy the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ */
+void SCACTileDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ SCACTilePrintSearchStats(mpm_thread_ctx);
+
+ if (mpm_thread_ctx->ctx != NULL) {
+ SCFree(mpm_thread_ctx->ctx);
+ mpm_thread_ctx->ctx = NULL;
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(SCACTileThreadCtx);
+ }
+}
+
+static void SCACTileDestroyInitCtx(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash != NULL) {
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+ }
+
+ if (ctx->parray != NULL) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACTileFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ }
+
+ if (ctx->state_table != NULL) {
+ SCFree(ctx->state_table);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (ctx->state_count *
+ ctx->bytes_per_state * ctx->alphabet_storage);
+ }
+
+ if (ctx->output_table != NULL) {
+ uint32_t state;
+ for (state = 0; state < ctx->state_count; state++) {
+ if (ctx->output_table[state].patterns != NULL) {
+ SCFree(ctx->output_table[state].patterns);
+ }
+ }
+ SCFree(ctx->output_table);
+ }
+
+ if (ctx->pattern_list != NULL) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->pattern_list[i].cs != NULL)
+ SCFree(ctx->pattern_list[i].cs);
+ if (ctx->pattern_list[i].sids != NULL)
+ SCFree(ctx->pattern_list[i].sids);
+ }
+ SCFree(ctx->pattern_list);
+ }
+
+ SCFree(ctx);
+ search_ctx->init_ctx = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACTileCtx);
+}
+
+/**
+ * \brief Destroy the mpm context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+void SCACTileDestroyCtx(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ if (search_ctx == NULL)
+ return;
+
+ /* Destroy Initialization data */
+ SCACTileDestroyInitCtx(mpm_ctx);
+
+ /* Free Search tables */
+ SCFree(search_ctx->state_table);
+ SCFree(search_ctx->pattern_list);
+ SCFree(search_ctx->output_table);
+
+ SCFree(search_ctx);
+ mpm_ctx->ctx = NULL;
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACTileSearchCtx);
+}
+
+/*
+ * Heavily optimized pattern matching routine for TILE-Gx.
+ */
+
+#define SCHECK(x) ((x) > 0)
+#define BTYPE int32_t
+// Extract byte N=0,1,2,3 from x
+#define BYTE0(x) __insn_bfextu(x, 0, 7)
+#define BYTE1(x) __insn_bfextu(x, 8, 15)
+#define BYTE2(x) __insn_bfextu(x, 16, 23)
+#define BYTE3(x) __insn_bfextu(x, 24, 31)
+
+int CheckMatch(SCACTileSearchCtx *ctx, PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen,
+ uint16_t state, int i, int matches,
+ uint8_t *mpm_bitarray)
+{
+ SCACTilePatternList *pattern_list = ctx->pattern_list;
+ uint8_t *buf_offset = buf + i + 1; // Lift out of loop
+ uint32_t no_of_entries = ctx->output_table[state].no_of_entries;
+ MpmPatternIndex *patterns = ctx->output_table[state].patterns;
+ uint8_t *pmq_bitarray = pmq->pattern_id_bitarray;
+ uint32_t k;
+
+ for (k = 0; k < no_of_entries; k++) {
+ MpmPatternIndex pindex = patterns[k] & 0x0FFFFFFF;
+ if (mpm_bitarray[pindex / 8] & (1 << (pindex % 8))) {
+ /* Pattern already seen by this MPM. */
+ /* NOTE: This is faster then rechecking if it is a case-sensitive match
+ * since we know this pattern has already been seen, but imcrementing
+ * matches here could over report matches. For example if the case-sensitive
+ * pattern is "Foo" and the string is "Foo bar foo", matches would be reported
+ * as 2, when it should really be 1, since "foo" is not a true match.
+ */
+ matches++;
+ continue;
+ }
+ uint32_t pid = pattern_list[pindex].pid;
+ /* Double check case-sensitve match now. */
+ if (patterns[k] >> 31) {
+ uint16_t patlen = pattern_list[pindex].patlen;
+ if (SCMemcmpNZ(pattern_list[pindex].cs, buf_offset - patlen, patlen) != 0) {
+ /* Case-sensitive match failed. */
+ continue;
+ }
+ }
+ /* New match found */
+ mpm_bitarray[pindex / 8] |= (1 << (pindex % 8));
+
+ if ((pmq_bitarray[pid / 8] & (1 << (pid % 8))) == 0) {
+ pmq_bitarray[(pid) / 8] |= (1 << ((pid) % 8));
+ MpmAddPid(pmq, pid);
+ }
+
+ /* Always add the Signature IDs, since they could be different in the current MPM
+ * than in a previous MPM on the same PMQ when finding the same pattern.
+ */
+ MpmAddSids(pmq, pattern_list[pindex].sids,
+ pattern_list[pindex].sids_size);
+ matches++;
+ }
+
+ return matches;
+}
+
+/**
+ * \brief The aho corasick search function.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param pmq Pointer to the Pattern Matcher Queue to hold
+ * search matches.
+ * \param buf Buffer to be searched.
+ * \param buflen Buffer length.
+ *
+ * \retval matches Match count.
+ */
+uint32_t SCACTileSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+
+ if (buflen == 0)
+ return 0;
+
+ /* Context specific matching function. */
+ return search_ctx->search(search_ctx, mpm_thread_ctx, pmq, buf, buflen);
+}
+
+/* This function handles (ctx->state_count >= 32767) */
+uint32_t SCACTileSearchLarge(SCACTileSearchCtx *ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq,
+ uint8_t *buf, uint16_t buflen)
+{
+ int i = 0;
+ int matches = 0;
+
+ uint8_t mpm_bitarray[ctx->mpm_bitarray_size];
+ memset(mpm_bitarray, 0, ctx->mpm_bitarray_size);
+
+ uint8_t* restrict xlate = ctx->translate_table;
+ register int state = 0;
+ int32_t (*state_table_u32)[256] = ctx->state_table;
+ for (i = 0; i < buflen; i++) {
+ state = state_table_u32[state & 0x00FFFFFF][xlate[buf[i]]];
+ if (SCHECK(state)) {
+ matches = CheckMatch(ctx, pmq, buf, buflen, state, i, matches, mpm_bitarray);
+ }
+ } /* for (i = 0; i < buflen; i++) */
+
+ return matches;
+}
+
+/*
+ * Search with Alphabet size of 256 and 16-bit next-state entries.
+ * Next state entry has MSB as "match" and 15 LSB bits as next-state index.
+ */
+// y = 1<<log_mult * (x & (1<<width -1))
+#define SINDEX_INTERNAL(y, x, log_mult, width) \
+ __insn_bfins(y, x, log_mult, log_mult + (width - 1))
+
+/* Type of next_state */
+#define STYPE int16_t
+// Hint to compiler to expect L2 hit latency for Load int16_t
+#define SLOAD(x) __insn_ld2s_L2((STYPE* restrict)(x))
+
+#define FUNC_NAME SCACTileSearchSmall256
+// y = 256 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 8, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 128 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchSmall128
+// y = 128 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 7, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 64 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchSmall64
+// y = 64 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 6, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 32 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchSmall32
+// y = 32 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 5, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 16 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchSmall16
+// y = 16 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 4, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 8 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchSmall8
+// y = 8 * (x & 0x7FFF)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 3, 15)
+#include "util-mpm-ac-tile-small.c"
+
+/*
+ * Search with Alphabet size of 256 and 8-bit next-state entries.
+ * Next state entry has MSB as "match" and 15 LSB bits as next-state index.
+ */
+#undef STYPE
+#define STYPE int8_t
+// Hint to compiler to expect L2 hit latency for Load int8_t
+#undef SLOAD
+#define SLOAD(x) __insn_ld1s_L2((STYPE* restrict)(x))
+
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny256
+// y = 256 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 8, 7)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 128 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny128
+// y = 128 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 7, 7)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 64 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny64
+// y = 64 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 6, 7)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 32 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny32
+// y = 32 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 5, 7)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 16 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny16
+// y = 16 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 4, 7)
+#include "util-mpm-ac-tile-small.c"
+
+/* Search with Alphabet size of 8 */
+#undef FUNC_NAME
+#undef SINDEX
+#define FUNC_NAME SCACTileSearchTiny8
+// y = 8 * (x & 0x7F)
+#define SINDEX(y,x) SINDEX_INTERNAL(y, x, 3, 7)
+#include "util-mpm-ac-tile-small.c"
+
+
+/**
+ * \brief Add a case insensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACTileAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return SCACTileAddPattern(mpm_ctx, pat, patlen, offset, depth,
+ pid, sid, flags);
+}
+
+/**
+ * \brief Add a case sensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACTileAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ return SCACTileAddPattern(mpm_ctx, pat, patlen, offset, depth,
+ pid, sid, flags);
+}
+
+void SCACTilePrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+#ifdef SC_AC_TILE_COUNTERS
+ SCACTileThreadCtx *ctx = (SCACTileThreadCtx *)mpm_thread_ctx->ctx;
+ printf("AC Thread Search stats (ctx %p)\n", ctx);
+ printf("Total calls: %" PRIu32 "\n", ctx->total_calls);
+ printf("Total matches: %" PRIu64 "\n", ctx->total_matches);
+#endif /* SC_AC_TILE_COUNTERS */
+}
+
+void SCACTilePrintInfo(MpmCtx *mpm_ctx)
+{
+ SCACTileSearchCtx *search_ctx = (SCACTileSearchCtx *)mpm_ctx->ctx;
+ SCACTileCtx *ctx = search_ctx->init_ctx;
+
+ printf("MPM AC Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeof:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" SCACTileCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCACTileCtx));
+ printf(" SCACTilePattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACTilePattern));
+ printf(" SCACTilePattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACTilePattern));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Total states in the state table: %d\n", ctx->state_count);
+ printf("\n");
+}
+
+/************************** Mpm Registration ***************************/
+
+/**
+ * \brief Register the aho-corasick mpm for Tilera Tile-Gx processor.
+ */
+void MpmACTileRegister(void)
+{
+ mpm_table[MPM_AC_TILE].name = "ac-tile";
+ mpm_table[MPM_AC_TILE].max_pattern_length = 0;
+
+ mpm_table[MPM_AC_TILE].InitCtx = SCACTileInitCtx;
+ mpm_table[MPM_AC_TILE].InitThreadCtx = SCACTileInitThreadCtx;
+ mpm_table[MPM_AC_TILE].DestroyCtx = SCACTileDestroyCtx;
+ mpm_table[MPM_AC_TILE].DestroyThreadCtx = SCACTileDestroyThreadCtx;
+ mpm_table[MPM_AC_TILE].AddPattern = SCACTileAddPatternCS;
+ mpm_table[MPM_AC_TILE].AddPatternNocase = SCACTileAddPatternCI;
+ mpm_table[MPM_AC_TILE].Prepare = SCACTilePreparePatterns;
+ mpm_table[MPM_AC_TILE].Search = SCACTileSearch;
+ mpm_table[MPM_AC_TILE].Cleanup = NULL;
+ mpm_table[MPM_AC_TILE].PrintCtx = SCACTilePrintInfo;
+ mpm_table[MPM_AC_TILE].PrintThreadCtx = SCACTilePrintSearchStats;
+ mpm_table[MPM_AC_TILE].RegisterUnittests = SCACTileRegisterTests;
+}
+
+
+/*************************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+static int SCACTileTest01(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest02(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest03(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest04(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest05(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest06(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcd";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest07(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0);
+ /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0);
+ /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0);
+ /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0);
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ 30, 0, 0, 5, 0, 0);
+ PmqSetup(&pmq, 6);
+ /* total matches: 135 */
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest08(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest09(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest10(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "abcdefgh"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest11(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1)
+ goto end;
+ PmqSetup(&pmq, 5);
+
+ if (SCACTilePreparePatterns(&mpm_ctx) == -1)
+ goto end;
+
+ result = 1;
+
+ char *buf = "he";
+ result &= (SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "she";
+ result &= (SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+ buf = "his";
+ result &= (SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "hers";
+ result &= (SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+
+ end:
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest12(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest13(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCD";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCD";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest14(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDE";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDE";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest15(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest16(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABC";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABC";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest17(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzAB";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzAB";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest18(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest19(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest20(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest21(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"AA", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest22(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest23(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest24(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest25(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest26(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "works";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest27(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "tone";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest28(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC_TILE);
+ SCACTileInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACTilePreparePatterns(&mpm_ctx);
+
+ char *buf = "tONE";
+ uint32_t cnt = SCACTileSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACTileDestroyCtx(&mpm_ctx);
+ SCACTileDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTileTest29(void)
+{
+ uint8_t *buf = (uint8_t *)"onetwothreefourfivesixseveneightnine";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; fast_pattern:3,3; sid:2;)");
+ if (de_ctx->sig_list->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 1) failure\n");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 2) failure\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCACTileRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCACTileTest01", SCACTileTest01, 1);
+ UtRegisterTest("SCACTileTest02", SCACTileTest02, 1);
+ UtRegisterTest("SCACTileTest03", SCACTileTest03, 1);
+ UtRegisterTest("SCACTileTest04", SCACTileTest04, 1);
+ UtRegisterTest("SCACTileTest05", SCACTileTest05, 1);
+ UtRegisterTest("SCACTileTest06", SCACTileTest06, 1);
+ UtRegisterTest("SCACTileTest07", SCACTileTest07, 1);
+ UtRegisterTest("SCACTileTest08", SCACTileTest08, 1);
+ UtRegisterTest("SCACTileTest09", SCACTileTest09, 1);
+ UtRegisterTest("SCACTileTest10", SCACTileTest10, 1);
+ UtRegisterTest("SCACTileTest11", SCACTileTest11, 1);
+ UtRegisterTest("SCACTileTest12", SCACTileTest12, 1);
+ UtRegisterTest("SCACTileTest13", SCACTileTest13, 1);
+ UtRegisterTest("SCACTileTest14", SCACTileTest14, 1);
+ UtRegisterTest("SCACTileTest15", SCACTileTest15, 1);
+ UtRegisterTest("SCACTileTest16", SCACTileTest16, 1);
+ UtRegisterTest("SCACTileTest17", SCACTileTest17, 1);
+ UtRegisterTest("SCACTileTest18", SCACTileTest18, 1);
+ UtRegisterTest("SCACTileTest19", SCACTileTest19, 1);
+ UtRegisterTest("SCACTileTest20", SCACTileTest20, 1);
+ UtRegisterTest("SCACTileTest21", SCACTileTest21, 1);
+ UtRegisterTest("SCACTileTest22", SCACTileTest22, 1);
+ UtRegisterTest("SCACTileTest23", SCACTileTest23, 1);
+ UtRegisterTest("SCACTileTest24", SCACTileTest24, 1);
+ UtRegisterTest("SCACTileTest25", SCACTileTest25, 1);
+ UtRegisterTest("SCACTileTest26", SCACTileTest26, 1);
+ UtRegisterTest("SCACTileTest27", SCACTileTest27, 1);
+ UtRegisterTest("SCACTileTest28", SCACTileTest28, 1);
+ UtRegisterTest("SCACTileTest29", SCACTileTest29, 1);
+#endif
+}
+
+#endif /* __tile__ */
diff --git a/framework/src/suricata/src/util-mpm-ac-tile.h b/framework/src/suricata/src/util-mpm-ac-tile.h
new file mode 100644
index 00000000..63d5bc95
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac-tile.h
@@ -0,0 +1,174 @@
+/* Copyright (C) 2013-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Ken Steele <suricata@tilera.com>
+ *
+ */
+
+#ifndef __UTIL_MPM_AC_TILE__H__
+#define __UTIL_MPM_AC_TILE__H__
+
+typedef struct SCACTilePattern_ {
+ /* length of the pattern */
+ uint16_t len;
+ /* flags decribing the pattern */
+ uint8_t flags;
+ /* holds the original pattern that was added */
+ uint8_t *original_pat;
+ /* case sensitive */
+ uint8_t *cs;
+ /* case INsensitive */
+ uint8_t *ci;
+ /* pattern id */
+ uint32_t id;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct SCACTilePattern_ *next;
+} SCACTilePattern;
+
+typedef struct SCACTilePatternList_ {
+ uint8_t *cs;
+ uint16_t patlen;
+
+ /* Pattern Id */
+ uint32_t pid;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+} SCACTilePatternList;
+
+typedef struct SCACTileOutputTable_ {
+ /* list of pattern indexes */
+ MpmPatternIndex *patterns;
+ /* number of entries in pattern list */
+ uint32_t no_of_entries;
+} SCACTileOutputTable;
+
+struct SCACTileSearchCtx_;
+
+/* Reordered for Tilera cache */
+typedef struct SCACTileCtx_ {
+
+ /* Convert input character to matching alphabet */
+ uint8_t translate_table[256];
+
+ /* The all important memory hungry state_table.
+ * The size of each next-state is determined by bytes_per_state.
+ */
+ void *state_table;
+
+ /* Specialized search function based on size of data in delta
+ * tables. The alphabet size determines address shifting and the
+ * number of states could make the next state could be 16 bits or
+ * 32 bits.
+ */
+ uint32_t (*search)(struct SCACTileSearchCtx_ *ctx, struct MpmThreadCtx_ *,
+ PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* Function to set the next state based on size of next state
+ * (bytes_per_state).
+ */
+ void (*set_next_state)(struct SCACTileCtx_ *ctx, int state, int aa,
+ int new_state, int outputs);
+
+ /* List of patterns that match for this state. Indexed by State Number */
+ SCACTileOutputTable *output_table;
+ /* Indexed by MpmPatternIndex */
+ SCACTilePatternList *pattern_list;
+
+ /* hash used during ctx initialization */
+ SCACTilePattern **init_hash;
+
+ /* pattern arrays. We need this only during the goto table
+ creation phase */
+ SCACTilePattern **parray;
+
+ /* goto_table, failure table and output table. Needed to create
+ * state_table. Will be freed, once we have created the
+ * state_table */
+ int32_t (*goto_table)[256];
+ int32_t *failure_table;
+
+ /* Number of states used by ac-tile */
+ uint32_t state_count;
+ /* Number of states allocated for ac-tile. */
+ uint32_t allocated_state_count;
+
+ uint32_t alpha_hist[256];
+ /* Number of characters in the compressed alphabet. */
+ uint16_t alphabet_size;
+ /* Space used to store compressed alphabet is the next
+ * larger or equal power-of-2.
+ */
+ uint16_t alphabet_storage;
+
+ /* How many bytes are used to store the next state. */
+ uint8_t bytes_per_state;
+
+} SCACTileCtx;
+
+
+/* Only the stuff used at search time. This
+ * structure is created after all the patterns are added.
+ */
+typedef struct SCACTileSearchCtx_ {
+
+ /* Specialized search function based on size of data in delta
+ * tables. The alphabet size determines address shifting and the
+ * number of states could make the next state could be 16 bits or
+ * 32 bits.
+ */
+ uint32_t (*search)(struct SCACTileSearchCtx_ *ctx, struct MpmThreadCtx_ *,
+ PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* Convert input character to matching alphabet */
+ uint8_t translate_table[256];
+
+ /* the all important memory hungry state_table */
+ void *state_table;
+
+ /* List of patterns that match for this state. Indexed by State Number */
+ SCACTileOutputTable *output_table;
+ SCACTilePatternList *pattern_list;
+
+ /* Number of bytes in the array of bits. One bit per pattern in this MPM. */
+ uint32_t mpm_bitarray_size;
+
+ /* MPM Creation data, only used at initialization. */
+ SCACTileCtx *init_ctx;
+
+} SCACTileSearchCtx;
+
+
+typedef struct SCACTileThreadCtx_ {
+ /* the total calls we make to the search function */
+ uint32_t total_calls;
+ /* the total patterns that we ended up matching against */
+ uint64_t total_matches;
+} SCACTileThreadCtx;
+
+void MpmACTileRegister(void);
+
+#endif /* __UTIL_MPM_AC_TILE__H__ */
diff --git a/framework/src/suricata/src/util-mpm-ac.c b/framework/src/suricata/src/util-mpm-ac.c
new file mode 100644
index 00000000..c8f6be77
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac.c
@@ -0,0 +1,3341 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * First iteration of aho-corasick MPM from -
+ *
+ * Efficient String Matching: An Aid to Bibliographic Search
+ * Alfred V. Aho and Margaret J. Corasick
+ *
+ * - Uses the delta table for calculating transitions, instead of having
+ * separate goto and failure transitions.
+ * - If we cross 2 ** 16 states, we use 4 bytes in the transition table
+ * to hold each state, otherwise we use 2 bytes.
+ * - This version of the MPM is heavy on memory, but it performs well.
+ * If you can fit the ruleset with this mpm on your box without hitting
+ * swap, this is the MPM to go for.
+ *
+ * \todo - Do a proper analyis of our existing MPMs and suggest a good one based
+ * on the pattern distribution and the expected traffic(say http).
+ * - Tried out loop unrolling without any perf increase. Need to dig deeper.
+ * - Irrespective of whether we cross 2 ** 16 states or not,shift to using
+ * uint32_t for state type, so that we can integrate it's status as a
+ * final state or not in the topmost byte. We are already doing it if
+ * state_count is > 2 ** 16.
+ * - Test case-senstive patterns if they have any ascii chars. If they
+ * don't treat them as nocase.
+ * - Carry out other optimizations we are working on. hashes, compression.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+
+#include "conf.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-memcmp.h"
+#include "util-mpm-ac.h"
+#include "util-memcpy.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-mpm.h"
+#include "tm-threads.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda.h"
+#include "util-cuda-handlers.h"
+#endif /* __SC_CUDA_SUPPORT__ */
+
+void SCACInitCtx(MpmCtx *);
+void SCACInitThreadCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void SCACDestroyCtx(MpmCtx *);
+void SCACDestroyThreadCtx(MpmCtx *, MpmThreadCtx *);
+int SCACAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
+ uint32_t, SigIntId, uint8_t);
+int SCACPreparePatterns(MpmCtx *mpm_ctx);
+uint32_t SCACSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen);
+void SCACPrintInfo(MpmCtx *mpm_ctx);
+void SCACPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void SCACRegisterTests(void);
+
+/* a placeholder to denote a failure transition in the goto table */
+#define SC_AC_FAIL (-1)
+/* size of the hash table used to speed up pattern insertions initially */
+#define INIT_HASH_SIZE 65536
+
+#define STATE_QUEUE_CONTAINER_SIZE 65536
+
+static int construct_both_16_and_32_state_tables = 0;
+
+/**
+ * \brief Helper structure used by AC during state table creation
+ */
+typedef struct StateQueue_ {
+ int32_t store[STATE_QUEUE_CONTAINER_SIZE];
+ int top;
+ int bot;
+} StateQueue;
+
+/**
+ * \internal
+ * \brief Initialize the AC context with user specified conf parameters. We
+ * aren't retrieving anything for AC conf now, but we will certainly
+ * need it, when we customize AC.
+ */
+static void SCACGetConfig()
+{
+ //ConfNode *ac_conf;
+ //const char *hash_val = NULL;
+
+ //ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Creates a hash of the pattern. We use it for the hashing process
+ * during the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline uint32_t SCACInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+/**
+ * \internal
+ * \brief Looks up a pattern. We use it for the hashing process during the
+ * the initial pattern insertion time, to cull duplicate sigs.
+ *
+ * \param ctx Pointer to the AC ctx.
+ * \param pat Pointer to the pattern.
+ * \param patlen Pattern length.
+ * \param flags Flags. We don't need this.
+ *
+ * \retval hash A 32 bit unsigned hash.
+ */
+static inline SCACPattern *SCACInitHashLookup(SCACCtx *ctx, uint8_t *pat,
+ uint16_t patlen, char flags,
+ uint32_t pid)
+{
+ uint32_t hash = SCACInitHashRaw(pat, patlen);
+
+ if (ctx->init_hash == NULL) {
+ return NULL;
+ }
+
+ SCACPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (t->id == pid)
+ return t;
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal
+ * \brief Allocs a new pattern instance.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval p Pointer to the newly created pattern.
+ */
+static inline SCACPattern *SCACAllocPattern(MpmCtx *mpm_ctx)
+{
+ SCACPattern *p = SCMalloc(sizeof(SCACPattern));
+ if (unlikely(p == NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ memset(p, 0, sizeof(SCACPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACPattern);
+
+ return p;
+}
+
+/**
+ * \internal
+ * \brief Used to free SCACPattern instances.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param p Pointer to the SCACPattern instance to be freed.
+ * \param free Free the above pointer or not.
+ */
+static inline void SCACFreePattern(MpmCtx *mpm_ctx, SCACPattern *p)
+{
+ if (p != NULL && p->cs != NULL && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->ci != NULL) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->original_pat != NULL) {
+ SCFree(p->original_pat);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->sids != NULL) {
+ SCFree(p->sids);
+ }
+
+ if (p != NULL) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACPattern);
+ }
+ return;
+}
+
+static inline uint32_t SCACInitHash(SCACPattern *p)
+{
+ uint32_t hash = p->len * p->original_pat[0];
+ if (p->len > 1)
+ hash += p->original_pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int SCACInitHashAdd(SCACCtx *ctx, SCACPattern *p)
+{
+ uint32_t hash = SCACInitHash(p);
+
+ if (ctx->init_hash == NULL) {
+ return 0;
+ }
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ return 0;
+ }
+
+ SCACPattern *tt = NULL;
+ SCACPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Add a pattern to the mpm-ac context.
+ *
+ * \param mpm_ctx Mpm context.
+ * \param pat Pointer to the pattern.
+ * \param patlen Length of the pattern.
+ * \param pid Pattern id
+ * \param sid Signature id (internal id).
+ * \param flags Pattern's MPM_PATTERN_* flags.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCACAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+
+ SCLogDebug("Adding pattern for ctx %p, patlen %"PRIu16" and pid %" PRIu32,
+ ctx, patlen, pid);
+
+ if (patlen == 0) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "pattern length 0");
+ return 0;
+ }
+
+ /* check if we have already inserted this pattern */
+ SCACPattern *p = SCACInitHashLookup(ctx, pat, patlen, flags, pid);
+ if (p == NULL) {
+ SCLogDebug("Allocing new pattern");
+
+ /* p will never be NULL */
+ p = SCACAllocPattern(mpm_ctx);
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ p->original_pat = SCMalloc(patlen);
+ if (p->original_pat == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->original_pat, pat, patlen);
+
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci, pat, p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ /* put in the pattern hash */
+ SCACInitHashAdd(ctx, p);
+
+ //if (mpm_ctx->pattern_cnt == 65535) {
+ // SCLogError(SC_ERR_AHO_CORASICK, "Max search words reached. Can't "
+ // "insert anymore. Exiting");
+ // exit(EXIT_FAILURE);
+ //}
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen)
+ mpm_ctx->maxlen = patlen;
+
+ if (mpm_ctx->minlen == 0) {
+ mpm_ctx->minlen = patlen;
+ } else {
+ if (mpm_ctx->minlen > patlen)
+ mpm_ctx->minlen = patlen;
+ }
+
+ /* we need the max pat id */
+ if (pid > ctx->max_pat_id)
+ ctx->max_pat_id = pid;
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ //SCLogInfo("MPM added %u:%u", pid, sid);
+ } else {
+ /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
+
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ //SCLogInfo("p->sids_size %u", p->sids_size);
+ //SCLogInfo("MPM added %u:%u (append)", pid, sid);
+ } else {
+ //SCLogInfo("rule %u already part of pid %u", sid, pid);
+ }
+ }
+
+ return 0;
+
+error:
+ SCACFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Initialize a new state in the goto and output tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ *
+ * \retval The state id, of the newly created state.
+ */
+static inline int SCACReallocState(SCACCtx *ctx, uint32_t cnt)
+{
+ void *ptmp;
+ int size = 0;
+
+ /* reallocate space in the goto table to include a new state */
+ size = cnt * ctx->single_state_size;
+ ptmp = SCRealloc(ctx->goto_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->goto_table = ptmp;
+
+ /* reallocate space in the output table for the new state */
+ int oldsize = ctx->state_count * sizeof(SCACOutputTable);
+ size = cnt * sizeof(SCACOutputTable);
+ SCLogDebug("oldsize %d size %d cnt %u ctx->state_count %u",
+ oldsize, size, cnt, ctx->state_count);
+
+ ptmp = SCRealloc(ctx->output_table, size);
+ if (ptmp == NULL) {
+ SCFree(ctx->output_table);
+ ctx->output_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->output_table = ptmp;
+
+ memset(((uint8_t *)ctx->output_table + oldsize), 0, (size - oldsize));
+
+ /* \todo using it temporarily now during dev, since I have restricted
+ * state var in SCACCtx->state_table to uint16_t. */
+ //if (ctx->state_count > 65536) {
+ // printf("state count exceeded\n");
+ // exit(EXIT_FAILURE);
+ //}
+
+ return 0;//ctx->state_count++;
+}
+
+/** \internal
+ * \brief Shrink state after setup is done
+ *
+ * Shrinks only the output table, goto table is freed after calling this
+ */
+static void SCACShrinkState(SCACCtx *ctx)
+{
+ /* reallocate space in the output table for the new state */
+#ifdef DEBUG
+ int oldsize = ctx->allocated_state_count * sizeof(SCACOutputTable);
+#endif
+ int newsize = ctx->state_count * sizeof(SCACOutputTable);
+
+ SCLogDebug("oldsize %d newsize %d ctx->allocated_state_count %u "
+ "ctx->state_count %u: shrink by %d bytes", oldsize,
+ newsize, ctx->allocated_state_count, ctx->state_count,
+ oldsize - newsize);
+
+ void *ptmp = SCRealloc(ctx->output_table, newsize);
+ if (ptmp == NULL) {
+ SCFree(ctx->output_table);
+ ctx->output_table = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ctx->output_table = ptmp;
+}
+
+static inline int SCACInitNewState(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;;
+
+ /* Exponentially increase the allocated space when needed. */
+ if (ctx->allocated_state_count < ctx->state_count + 1) {
+ if (ctx->allocated_state_count == 0)
+ ctx->allocated_state_count = 256;
+ else
+ ctx->allocated_state_count *= 2;
+
+ SCACReallocState(ctx, ctx->allocated_state_count);
+
+ }
+#if 0
+ if (ctx->allocated_state_count > 260) {
+ SCACOutputTable *output_state = &ctx->output_table[260];
+ SCLogInfo("output_state %p %p %u", output_state, output_state->pids, output_state->no_of_entries);
+ }
+#endif
+ int ascii_code = 0;
+ /* set all transitions for the newly assigned state as FAIL transitions */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ ctx->goto_table[ctx->state_count][ascii_code] = SC_AC_FAIL;
+ }
+
+ return ctx->state_count++;
+}
+
+/**
+ * \internal
+ * \brief Adds a pid to the output table for a state.
+ *
+ * \param state The state to whose output table we should add the pid.
+ * \param pid The pattern id to add.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static void SCACSetOutputState(int32_t state, uint32_t pid, MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ SCACOutputTable *output_state = &ctx->output_table[state];
+ uint32_t i = 0;
+
+ for (i = 0; i < output_state->no_of_entries; i++) {
+ if (output_state->pids[i] == pid)
+ return;
+ }
+
+ output_state->no_of_entries++;
+ ptmp = SCRealloc(output_state->pids,
+ output_state->no_of_entries * sizeof(uint32_t));
+ if (ptmp == NULL) {
+ SCFree(output_state->pids);
+ output_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_state->pids = ptmp;
+
+ output_state->pids[output_state->no_of_entries - 1] = pid;
+
+ return;
+}
+
+/**
+ * \brief Helper function used by SCACCreateGotoTable. Adds a pattern to the
+ * goto table.
+ *
+ * \param pattern Pointer to the pattern.
+ * \param pattern_len Pattern length.
+ * \param pid The pattern id, that corresponds to this pattern. We
+ * need it to updated the output table for this pattern.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACEnter(uint8_t *pattern, uint16_t pattern_len, uint32_t pid,
+ MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int32_t state = 0;
+ int32_t newstate = 0;
+ int i = 0;
+ int p = 0;
+
+ /* walk down the trie till we have a match for the pattern prefix */
+ state = 0;
+ for (i = 0; i < pattern_len; i++) {
+ if (ctx->goto_table[state][pattern[i]] != SC_AC_FAIL) {
+ state = ctx->goto_table[state][pattern[i]];
+ } else {
+ break;
+ }
+ }
+
+ /* add the non-matching pattern suffix to the trie, from the last state
+ * we left off */
+ for (p = i; p < pattern_len; p++) {
+ newstate = SCACInitNewState(mpm_ctx);
+ ctx->goto_table[state][pattern[p]] = newstate;
+ state = newstate;
+ }
+
+ /* add this pattern id, to the output table of the last state, where the
+ * pattern ends in the trie */
+ SCACSetOutputState(state, pid, mpm_ctx);
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the goto table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACCreateGotoTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+
+ /* add each pattern to create the goto table */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ SCACEnter(ctx->parray[i]->ci, ctx->parray[i]->len,
+ ctx->parray[i]->id, mpm_ctx);
+ }
+
+ int ascii_code = 0;
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ if (ctx->goto_table[0][ascii_code] == SC_AC_FAIL) {
+ ctx->goto_table[0][ascii_code] = 0;
+ }
+ }
+
+ return;
+}
+
+static inline void SCACDetermineLevel1Gap(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ uint32_t u = 0;
+
+ int map[256];
+ memset(map, 0, sizeof(map));
+
+ for (u = 0; u < mpm_ctx->pattern_cnt; u++)
+ map[ctx->parray[u]->ci[0]] = 1;
+
+ for (u = 0; u < 256; u++) {
+ if (map[u] == 0)
+ continue;
+ int32_t newstate = SCACInitNewState(mpm_ctx);
+ ctx->goto_table[0][u] = newstate;
+ }
+
+ return;
+}
+
+static inline int SCACStateQueueIsEmpty(StateQueue *q)
+{
+ if (q->top == q->bot)
+ return 1;
+ else
+ return 0;
+}
+
+static inline void SCACEnqueue(StateQueue *q, int32_t state)
+{
+ int i = 0;
+
+ /*if we already have this */
+ for (i = q->bot; i < q->top; i++) {
+ if (q->store[i] == state)
+ return;
+ }
+
+ q->store[q->top++] = state;
+
+ if (q->top == STATE_QUEUE_CONTAINER_SIZE)
+ q->top = 0;
+
+ if (q->top == q->bot) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+static inline int32_t SCACDequeue(StateQueue *q)
+{
+ if (q->bot == STATE_QUEUE_CONTAINER_SIZE)
+ q->bot = 0;
+
+ if (q->bot == q->top) {
+ SCLogCritical(SC_ERR_AHO_CORASICK, "StateQueue behaving weirdly. "
+ "Fatal Error. Exiting. Please file a bug report on this");
+ exit(EXIT_FAILURE);
+ }
+
+ return q->store[q->bot++];
+}
+
+/*
+#define SCACStateQueueIsEmpty(q) (((q)->top == (q)->bot) ? 1 : 0)
+
+#define SCACEnqueue(q, state) do { \
+ int i = 0; \
+ \
+ for (i = (q)->bot; i < (q)->top; i++) { \
+ if ((q)->store[i] == state) \
+ return; \
+ } \
+ \
+ (q)->store[(q)->top++] = state; \
+ \
+ if ((q)->top == STATE_QUEUE_CONTAINER_SIZE) \
+ (q)->top = 0; \
+ \
+ if ((q)->top == (q)->bot) { \
+ SCLogCritical(SC_ERR_AHO_CORASICK, "Just ran out of space in the queue. " \
+ "Fatal Error. Exiting. Please file a bug report on this"); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+#define SCACDequeue(q) ( (((q)->bot == STATE_QUEUE_CONTAINER_SIZE)? ((q)->bot = 0): 0), \
+ (((q)->bot == (q)->top) ? \
+ (printf("StateQueue behaving " \
+ "weirdly. Fatal Error. Exiting. Please " \
+ "file a bug report on this"), \
+ exit(EXIT_FAILURE)) : 0), \
+ (q)->store[(q)->bot++]) \
+*/
+
+/**
+ * \internal
+ * \brief Club the output data from 2 states and store it in the 1st state.
+ * dst_state_data = {dst_state_data} UNION {src_state_data}
+ *
+ * \param dst_state First state(also the destination) for the union operation.
+ * \param src_state Second state for the union operation.
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACClubOutputStates(int32_t dst_state, int32_t src_state,
+ MpmCtx *mpm_ctx)
+{
+ void *ptmp;
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ SCACOutputTable *output_dst_state = &ctx->output_table[dst_state];
+ SCACOutputTable *output_src_state = &ctx->output_table[src_state];
+
+ for (i = 0; i < output_src_state->no_of_entries; i++) {
+ for (j = 0; j < output_dst_state->no_of_entries; j++) {
+ if (output_src_state->pids[i] == output_dst_state->pids[j]) {
+ break;
+ }
+ }
+ if (j == output_dst_state->no_of_entries) {
+ output_dst_state->no_of_entries++;
+
+ ptmp = SCRealloc(output_dst_state->pids,
+ (output_dst_state->no_of_entries * sizeof(uint32_t)));
+ if (ptmp == NULL) {
+ SCFree(output_dst_state->pids);
+ output_dst_state->pids = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ output_dst_state->pids = ptmp;
+
+ output_dst_state->pids[output_dst_state->no_of_entries - 1] =
+ output_src_state->pids[i];
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the failure table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACCreateFailureTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int32_t state = 0;
+ int32_t r_state = 0;
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ /* allot space for the failure table. A failure entry in the table for
+ * every state(SCACCtx->state_count) */
+ ctx->failure_table = SCMalloc(ctx->state_count * sizeof(int32_t));
+ if (ctx->failure_table == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->failure_table, 0, ctx->state_count * sizeof(int32_t));
+
+ /* add the failure transitions for the 0th state, and add every non-fail
+ * transition from the 0th state to the queue for further processing
+ * of failure states */
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[0][ascii_code];
+ if (temp_state != 0) {
+ SCACEnqueue(&q, temp_state);
+ ctx->failure_table[temp_state] = 0;
+ }
+ }
+
+ while (!SCACStateQueueIsEmpty(&q)) {
+ /* pick up every state from the queue and add failure transitions */
+ r_state = SCACDequeue(&q);
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state == SC_AC_FAIL)
+ continue;
+ SCACEnqueue(&q, temp_state);
+ state = ctx->failure_table[r_state];
+
+ while(ctx->goto_table[state][ascii_code] == SC_AC_FAIL)
+ state = ctx->failure_table[state];
+ ctx->failure_table[temp_state] = ctx->goto_table[state][ascii_code];
+ SCACClubOutputStates(temp_state, ctx->failure_table[temp_state],
+ mpm_ctx);
+ }
+ }
+
+ return;
+}
+
+/**
+ * \internal
+ * \brief Create the delta table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACCreateDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ int32_t r_state = 0;
+
+ if ((ctx->state_count < 32767) || construct_both_16_and_32_state_tables) {
+ ctx->state_table_u16 = SCMalloc(ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U16) * 256);
+ if (ctx->state_table_u16 == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_u16, 0,
+ ctx->state_count * sizeof(SC_AC_STATE_TYPE_U16) * 256);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U16) * 256);
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ SC_AC_STATE_TYPE_U16 temp_state = ctx->goto_table[0][ascii_code];
+ ctx->state_table_u16[0][ascii_code] = temp_state;
+ if (temp_state != 0)
+ SCACEnqueue(&q, temp_state);
+ }
+
+ while (!SCACStateQueueIsEmpty(&q)) {
+ r_state = SCACDequeue(&q);
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state != SC_AC_FAIL) {
+ SCACEnqueue(&q, temp_state);
+ ctx->state_table_u16[r_state][ascii_code] = temp_state;
+ } else {
+ ctx->state_table_u16[r_state][ascii_code] =
+ ctx->state_table_u16[ctx->failure_table[r_state]][ascii_code];
+ }
+ }
+ }
+ }
+
+ if (!(ctx->state_count < 32767) || construct_both_16_and_32_state_tables) {
+ /* create space for the state table. We could have used the existing goto
+ * table, but since we have it set to hold 32 bit state values, we will create
+ * a new state table here of type SC_AC_STATE_TYPE(current set to uint16_t) */
+ ctx->state_table_u32 = SCMalloc(ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U32) * 256);
+ if (ctx->state_table_u32 == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->state_table_u32, 0,
+ ctx->state_count * sizeof(SC_AC_STATE_TYPE_U32) * 256);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U32) * 256);
+
+ StateQueue q;
+ memset(&q, 0, sizeof(StateQueue));
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ SC_AC_STATE_TYPE_U32 temp_state = ctx->goto_table[0][ascii_code];
+ ctx->state_table_u32[0][ascii_code] = temp_state;
+ if (temp_state != 0)
+ SCACEnqueue(&q, temp_state);
+ }
+
+ while (!SCACStateQueueIsEmpty(&q)) {
+ r_state = SCACDequeue(&q);
+
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ int32_t temp_state = ctx->goto_table[r_state][ascii_code];
+ if (temp_state != SC_AC_FAIL) {
+ SCACEnqueue(&q, temp_state);
+ ctx->state_table_u32[r_state][ascii_code] = temp_state;
+ } else {
+ ctx->state_table_u32[r_state][ascii_code] =
+ ctx->state_table_u32[ctx->failure_table[r_state]][ascii_code];
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static inline void SCACClubOutputStatePresenceWithDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int ascii_code = 0;
+ uint32_t state = 0;
+ uint32_t temp_state = 0;
+
+ if ((ctx->state_count < 32767) || construct_both_16_and_32_state_tables) {
+ for (state = 0; state < ctx->state_count; state++) {
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ temp_state = ctx->state_table_u16[state & 0x7FFF][ascii_code];
+ if (ctx->output_table[temp_state & 0x7FFF].no_of_entries != 0)
+ ctx->state_table_u16[state & 0x7FFF][ascii_code] |= (1 << 15);
+ }
+ }
+ }
+
+ if (!(ctx->state_count < 32767) || construct_both_16_and_32_state_tables) {
+ for (state = 0; state < ctx->state_count; state++) {
+ for (ascii_code = 0; ascii_code < 256; ascii_code++) {
+ temp_state = ctx->state_table_u32[state & 0x00FFFFFF][ascii_code];
+ if (ctx->output_table[temp_state & 0x00FFFFFF].no_of_entries != 0)
+ ctx->state_table_u32[state & 0x00FFFFFF][ascii_code] |= (1 << 24);
+ }
+ }
+ }
+
+ return;
+}
+
+static inline void SCACInsertCaseSensitiveEntriesForPatterns(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ uint32_t state = 0;
+ uint32_t k = 0;
+
+ for (state = 0; state < ctx->state_count; state++) {
+ if (ctx->output_table[state].no_of_entries == 0)
+ continue;
+
+ for (k = 0; k < ctx->output_table[state].no_of_entries; k++) {
+ if (ctx->pid_pat_list[ctx->output_table[state].pids[k]].cs != NULL) {
+ ctx->output_table[state].pids[k] &= 0x0000FFFF;
+ ctx->output_table[state].pids[k] |= 1 << 16;
+ }
+ }
+ }
+
+ return;
+}
+
+#if 0
+static void SCACPrintDeltaTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int i = 0, j = 0;
+
+ printf("##############Delta Table##############\n");
+ for (i = 0; i < ctx->state_count; i++) {
+ printf("%d: \n", i);
+ for (j = 0; j < 256; j++) {
+ if (SCACGetDelta(i, j, mpm_ctx) != 0) {
+ printf(" %c -> %d\n", j, SCACGetDelta(i, j, mpm_ctx));
+ }
+ }
+ }
+
+ return;
+}
+#endif
+
+/**
+ * \brief Process the patterns and prepare the state table.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+static inline void SCACPrepareStateTable(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+
+ /* create the 0th state in the goto table and output_table */
+ SCACInitNewState(mpm_ctx);
+
+ SCACDetermineLevel1Gap(mpm_ctx);
+
+ /* create the goto table */
+ SCACCreateGotoTable(mpm_ctx);
+ /* create the failure table */
+ SCACCreateFailureTable(mpm_ctx);
+ /* create the final state(delta) table */
+ SCACCreateDeltaTable(mpm_ctx);
+ /* club the output state presence with delta transition entries */
+ SCACClubOutputStatePresenceWithDeltaTable(mpm_ctx);
+
+ /* club nocase entries */
+ SCACInsertCaseSensitiveEntriesForPatterns(mpm_ctx);
+
+ /* shrink the memory */
+ SCACShrinkState(ctx);
+
+#if 0
+ SCACPrintDeltaTable(mpm_ctx);
+#endif
+
+ /* we don't need these anymore */
+ SCFree(ctx->goto_table);
+ ctx->goto_table = NULL;
+ SCFree(ctx->failure_table);
+ ctx->failure_table = NULL;
+
+ return;
+}
+
+/**
+ * \brief Process the patterns added to the mpm, and create the internal tables.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+int SCACPreparePatterns(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+
+ if (mpm_ctx->pattern_cnt == 0 || ctx->init_hash == NULL) {
+ SCLogDebug("no patterns supplied to this mpm_ctx");
+ return 0;
+ }
+
+ /* alloc the pattern array */
+ ctx->parray = (SCACPattern **)SCMalloc(mpm_ctx->pattern_cnt *
+ sizeof(SCACPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(SCACPattern *));
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(SCACPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ SCACPattern *node = ctx->init_hash[i], *nnode = NULL;
+ while(node != NULL) {
+ nnode = node->next;
+ node->next = NULL;
+ ctx->parray[p++] = node;
+ node = nnode;
+ }
+ }
+
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* the memory consumed by a single state in our goto table */
+ ctx->single_state_size = sizeof(int32_t) * 256;
+
+ /* handle no case patterns */
+ ctx->pid_pat_list = SCMalloc((ctx->max_pat_id + 1)* sizeof(SCACPatternList));
+ if (ctx->pid_pat_list == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->pid_pat_list, 0, (ctx->max_pat_id + 1) * sizeof(SCACPatternList));
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (!(ctx->parray[i]->flags & MPM_PATTERN_FLAG_NOCASE)) {
+ ctx->pid_pat_list[ctx->parray[i]->id].cs = SCMalloc(ctx->parray[i]->len);
+ if (ctx->pid_pat_list[ctx->parray[i]->id].cs == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(ctx->pid_pat_list[ctx->parray[i]->id].cs,
+ ctx->parray[i]->original_pat, ctx->parray[i]->len);
+ ctx->pid_pat_list[ctx->parray[i]->id].patlen = ctx->parray[i]->len;
+ }
+
+ /* ACPatternList now owns this memory */
+ //SCLogInfo("ctx->parray[i]->sids_size %u", ctx->parray[i]->sids_size);
+ ctx->pid_pat_list[ctx->parray[i]->id].sids_size = ctx->parray[i]->sids_size;
+ ctx->pid_pat_list[ctx->parray[i]->id].sids = ctx->parray[i]->sids;
+
+ ctx->parray[i]->sids_size = 0;
+ ctx->parray[i]->sids = NULL;
+ }
+
+ /* prepare the state table required by AC */
+ SCACPrepareStateTable(mpm_ctx);
+
+#ifdef __SC_CUDA_SUPPORT__
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ int r = SCCudaMemAlloc(&ctx->state_table_u32_cuda,
+ ctx->state_count * sizeof(unsigned int) * 256);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+
+ r = SCCudaMemcpyHtoD(ctx->state_table_u32_cuda,
+ ctx->state_table_u32,
+ ctx->state_count * sizeof(unsigned int) * 256);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemcpyHtoD failure.");
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+
+ /* free all the stored patterns. Should save us a good 100-200 mbs */
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACPattern *));
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Init the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param matchsize We don't need this.
+ */
+void SCACInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(SCACThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_thread_ctx->ctx, 0, sizeof(SCACThreadCtx));
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(SCACThreadCtx);
+
+ return;
+}
+
+/**
+ * \brief Initialize the AC context.
+ *
+ * \param mpm_ctx Mpm context.
+ */
+void SCACInitCtx(MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx->ctx != NULL)
+ return;
+
+ mpm_ctx->ctx = SCMalloc(sizeof(SCACCtx));
+ if (mpm_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_ctx->ctx, 0, sizeof(SCACCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SCACCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(SCACPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL) {
+ exit(EXIT_FAILURE);
+ }
+ memset(ctx->init_hash, 0, sizeof(SCACPattern *) * INIT_HASH_SIZE);
+
+ /* get conf values for AC from our yaml file. We have no conf values for
+ * now. We will certainly need this, as we develop the algo */
+ SCACGetConfig();
+
+ SCReturn;
+}
+
+/**
+ * \brief Destroy the mpm thread context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ */
+void SCACDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ SCACPrintSearchStats(mpm_thread_ctx);
+
+ if (mpm_thread_ctx->ctx != NULL) {
+ SCFree(mpm_thread_ctx->ctx);
+ mpm_thread_ctx->ctx = NULL;
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(SCACThreadCtx);
+ }
+
+ return;
+}
+
+/**
+ * \brief Destroy the mpm context.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ */
+void SCACDestroyCtx(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash != NULL) {
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(SCACPattern *));
+ }
+
+ if (ctx->parray != NULL) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ SCACFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ ctx->parray = NULL;
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(SCACPattern *));
+ }
+
+ if (ctx->state_table_u16 != NULL) {
+ SCFree(ctx->state_table_u16);
+ ctx->state_table_u16 = NULL;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size -= (ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U16) * 256);
+ }
+ if (ctx->state_table_u32 != NULL) {
+ SCFree(ctx->state_table_u32);
+ ctx->state_table_u32 = NULL;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size -= (ctx->state_count *
+ sizeof(SC_AC_STATE_TYPE_U32) * 256);
+ }
+
+ if (ctx->output_table != NULL) {
+ uint32_t state_count;
+ for (state_count = 0; state_count < ctx->state_count; state_count++) {
+ if (ctx->output_table[state_count].pids != NULL) {
+ SCFree(ctx->output_table[state_count].pids);
+ }
+ }
+ SCFree(ctx->output_table);
+ }
+
+ if (ctx->pid_pat_list != NULL) {
+ int i;
+ for (i = 0; i < (ctx->max_pat_id + 1); i++) {
+ if (ctx->pid_pat_list[i].cs != NULL)
+ SCFree(ctx->pid_pat_list[i].cs);
+ if (ctx->pid_pat_list[i].sids != NULL)
+ SCFree(ctx->pid_pat_list[i].sids);
+ }
+ SCFree(ctx->pid_pat_list);
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(SCACCtx);
+
+ return;
+}
+
+/**
+ * \brief The aho corasick search function.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param mpm_thread_ctx Pointer to the mpm thread context.
+ * \param pmq Pointer to the Pattern Matcher Queue to hold
+ * search matches.
+ * \param buf Buffer to be searched.
+ * \param buflen Buffer length.
+ *
+ * \retval matches Match count.
+ */
+uint32_t SCACSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
+ PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ int i = 0;
+ int matches = 0;
+
+ /* \todo tried loop unrolling with register var, with no perf increase. Need
+ * to dig deeper */
+ /* \todo Change it for stateful MPM. Supply the state using mpm_thread_ctx */
+ SCACPatternList *pid_pat_list = ctx->pid_pat_list;
+
+ uint8_t bitarray[pmq->pattern_id_bitarray_size];
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+
+ if (ctx->state_count < 32767) {
+ register SC_AC_STATE_TYPE_U16 state = 0;
+ SC_AC_STATE_TYPE_U16 (*state_table_u16)[256] = ctx->state_table_u16;
+ for (i = 0; i < buflen; i++) {
+ state = state_table_u16[state & 0x7FFF][u8_tolower(buf[i])];
+ if (state & 0x8000) {
+ uint32_t no_of_entries = ctx->output_table[state & 0x7FFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x7FFF].pids;
+ uint32_t k;
+ for (k = 0; k < no_of_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[(pids[k]) / 8] |= (1 << ((pids[k]) % 8));
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ //loop1:
+ //;
+ }
+ }
+ } /* for (i = 0; i < buflen; i++) */
+
+ } else {
+ register SC_AC_STATE_TYPE_U32 state = 0;
+ SC_AC_STATE_TYPE_U32 (*state_table_u32)[256] = ctx->state_table_u32;
+ for (i = 0; i < buflen; i++) {
+ state = state_table_u32[state & 0x00FFFFFF][u8_tolower(buf[i])];
+ if (state & 0xFF000000) {
+ uint32_t no_of_entries = ctx->output_table[state & 0x00FFFFFF].no_of_entries;
+ uint32_t *pids = ctx->output_table[state & 0x00FFFFFF].pids;
+ uint32_t k;
+ for (k = 0; k < no_of_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + i - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+ if (bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ MpmAddPid(pmq, lower_pid);
+ MpmAddSids(pmq, pid_pat_list[lower_pid].sids, pid_pat_list[lower_pid].sids_size);
+ }
+ matches++;
+ } else {
+ if (bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[(pids[k]) / 8] |= (1 << ((pids[k]) % 8));
+ bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ MpmAddPid(pmq, pids[k]);
+ MpmAddSids(pmq, pid_pat_list[pids[k]].sids, pid_pat_list[pids[k]].sids_size);
+ }
+ matches++;
+ }
+ //loop1:
+ //;
+ }
+ }
+ } /* for (i = 0; i < buflen; i++) */
+ }
+
+ return matches;
+}
+
+/**
+ * \brief Add a case insensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return SCACAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+/**
+ * \brief Add a case sensitive pattern. Although we have different calls for
+ * adding case sensitive and insensitive patterns, we make a single call
+ * for either case. No special treatment for either case.
+ *
+ * \param mpm_ctx Pointer to the mpm context.
+ * \param pat The pattern to add.
+ * \param patnen The pattern length.
+ * \param offset Ignored.
+ * \param depth Ignored.
+ * \param pid The pattern id.
+ * \param sid Ignored.
+ * \param flags Flags associated with this pattern.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCACAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid,
+ SigIntId sid, uint8_t flags)
+{
+ return SCACAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+void SCACPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+
+#ifdef SC_AC_COUNTERS
+ SCACThreadCtx *ctx = (SCACThreadCtx *)mpm_thread_ctx->ctx;
+ printf("AC Thread Search stats (ctx %p)\n", ctx);
+ printf("Total calls: %" PRIu32 "\n", ctx->total_calls);
+ printf("Total matches: %" PRIu64 "\n", ctx->total_matches);
+#endif /* SC_AC_COUNTERS */
+
+ return;
+}
+
+void SCACPrintInfo(MpmCtx *mpm_ctx)
+{
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+
+ printf("MPM AC Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeof:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" SCACCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCACCtx));
+ printf(" SCACPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACPattern));
+ printf(" SCACPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCACPattern));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Total states in the state table: %" PRIu32 "\n", ctx->state_count);
+ printf("\n");
+
+ return;
+}
+
+/****************************Cuda side of things****************************/
+
+#ifdef __SC_CUDA_SUPPORT__
+
+/* \todo Technically it's generic to all mpms, but since we use ac only, the
+ * code internally directly references ac and hence it has found its
+ * home in this file, instead of util-mpm.c
+ */
+void DetermineCudaStateTableSize(DetectEngineCtx *de_ctx)
+{
+ MpmCtx *mpm_ctx = NULL;
+
+ int ac_16_tables = 0;
+ int ac_32_tables = 0;
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_other_packet, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_uri, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_uri, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcbd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcbd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hhd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrhd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hmd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hmd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hcd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrud, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hrud, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsmd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hsmd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hscd, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_hscd, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_huad, 0);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_huad, 1);
+ if (mpm_ctx->mpm_type == MPM_AC_CUDA) {
+ SCACCtx *ctx = (SCACCtx *)mpm_ctx->ctx;
+ if (ctx->state_count < 32767)
+ ac_16_tables++;
+ else
+ ac_32_tables++;
+ }
+
+ if (ac_16_tables > 0 && ac_32_tables > 0)
+ SCACConstructBoth16and32StateTables();
+
+
+ SCLogDebug("Total mpm ac 16 bit state tables - %d\n", ac_16_tables);
+ SCLogDebug("Total mpm ac 32 bit state tables - %d\n", ac_32_tables);
+
+}
+
+void CudaReleasePacket(Packet *p)
+{
+ if (p->cuda_pkt_vars.cuda_mpm_enabled == 1) {
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+ SCMutexLock(&p->cuda_pkt_vars.cuda_mutex);
+ p->cuda_pkt_vars.cuda_done = 0;
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ }
+
+ return;
+}
+
+/* \todos
+ * - Use texture memory - Can we fit all the arrays into a 3d texture.
+ * Texture memory definitely offers slightly better performance even
+ * on gpus that offer cache for global memory.
+ * - Packetpool - modify to support > 65k max pending packets. We are
+ * hitting packetpool limit currently even with 65k packets.
+ * - Use streams. We have tried overlapping parsing results from the
+ * previous call with invoking the next call.
+ * - Offer higher priority to decode threads.
+ * - Modify pcap file mode to support reading from multiple pcap files
+ * and hence we will have multiple receive threads.
+ * - Split state table into many small pieces and have multiple threads
+ * run each small state table on the same payload.
+ * - Used a config peference of l1 over shared memory with no noticeable
+ * perf increase. Explore it in detail over cards/architectures.
+ * - Constant memory performance sucked. Explore it in detail.
+ * - Currently all our state tables are small. Implement 16 bit state
+ * tables on priority.
+ * - Introduce profiling.
+ * - Retrieve sgh before buffer packet.
+ * - Buffer smsgs too.
+ */
+
+void SCACConstructBoth16and32StateTables(void)
+{
+ construct_both_16_and_32_state_tables = 1;
+
+ return;
+}
+
+/* \todo Reduce offset buffer size. Probably a 100,000 entry would be sufficient. */
+static void *SCACCudaDispatcher(void *arg)
+{
+#define BLOCK_SIZE 32
+
+ int r = 0;
+ ThreadVars *tv = (ThreadVars *)arg;
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ uint32_t sleep_interval_ms = conf->batching_timeout;
+
+ SCLogInfo("AC Cuda Mpm Dispatcher using a timeout of "
+ "\"%"PRIu32"\" micro-seconds", sleep_interval_ms);
+
+ CudaBufferData *cb_data =
+ CudaHandlerModuleGetData(MPM_AC_CUDA_MODULE_NAME,
+ MPM_AC_CUDA_MODULE_CUDA_BUFFER_NAME);
+
+ CUcontext cuda_context =
+ CudaHandlerModuleGetContext(MPM_AC_CUDA_MODULE_NAME, conf->device_id);
+ if (cuda_context == 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "context is NULL.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaCtxPushCurrent(cuda_context);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "context push failed.");
+ exit(EXIT_FAILURE);
+ }
+ CUmodule cuda_module = 0;
+ if (CudaHandlerGetCudaModule(&cuda_module, "util-mpm-ac-cuda-kernel") < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error retrieving cuda module.");
+ exit(EXIT_FAILURE);
+ }
+ CUfunction kernel = 0;
+#if __WORDSIZE==64
+ if (SCCudaModuleGetFunction(&kernel, cuda_module, "SCACCudaSearch64") == -1) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error retrieving kernel");
+ exit(EXIT_FAILURE);
+ }
+#else
+ if (SCCudaModuleGetFunction(&kernel, cuda_module, "SCACCudaSearch32") == -1) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error retrieving kernel");
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ uint8_t g_u8_lowercasetable[256];
+ for (int c = 0; c < 256; c++)
+ g_u8_lowercasetable[c] = tolower((uint8_t)c);
+ CUdeviceptr cuda_g_u8_lowercasetable_d = 0;
+ CUdeviceptr cuda_packets_buffer_d = 0;
+ CUdeviceptr cuda_offset_buffer_d = 0;
+ CUdeviceptr cuda_results_buffer_d = 0;
+ uint32_t *cuda_results_buffer_h = NULL;
+ r = SCCudaMemAlloc(&cuda_g_u8_lowercasetable_d, sizeof(g_u8_lowercasetable));
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemcpyHtoD(cuda_g_u8_lowercasetable_d, g_u8_lowercasetable, sizeof(g_u8_lowercasetable));
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemcpyHtoD failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemAlloc(&cuda_packets_buffer_d, conf->gpu_transfer_size);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemAlloc(&cuda_offset_buffer_d, conf->gpu_transfer_size * 4);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemAlloc(&cuda_results_buffer_d, conf->gpu_transfer_size * 8);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemAllocHost((void **)&cuda_results_buffer_h, conf->gpu_transfer_size * 8);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemAlloc failure.");
+ exit(EXIT_FAILURE);
+ }
+
+ CudaBufferCulledInfo cb_culled_info;
+ memset(&cb_culled_info, 0, sizeof(cb_culled_info));
+
+ TmThreadsSetFlag(tv, THV_INIT_DONE);
+ while (1) {
+ if (TmThreadsCheckFlag(tv, THV_KILL))
+ break;
+
+ usleep(sleep_interval_ms);
+
+ /**************** 1 SEND ****************/
+ CudaBufferCullCompletedSlices(cb_data, &cb_culled_info, conf->gpu_transfer_size);
+ if (cb_culled_info.no_of_items == 0)
+ continue;
+#if 0
+ SCLogInfo("1 - cb_culled_info.no_of_items-%"PRIu32" "
+ "cb_culled_info.buffer_len - %"PRIu32" "
+ "cb_culled_info.average size - %f "
+ "cb_culled_info.d_buffer_start_offset - %"PRIu32" "
+ "cb_culled_info.op_buffer_start_offset - %"PRIu32" "
+ "cb_data.no_of_items - %"PRIu32" "
+ "cb_data.d_buffer_read - %"PRIu32" "
+ "cb_data.d_buffer_write - %"PRIu32" "
+ "cb_data.op_buffer_read - %"PRIu32" "
+ "cb_data.op_buffer_write - %"PRIu32"\n",
+ cb_culled_info.no_of_items,
+ cb_culled_info.d_buffer_len,
+ cb_culled_info.d_buffer_len / (float)cb_culled_info.no_of_items,
+ cb_culled_info.d_buffer_start_offset,
+ cb_culled_info.op_buffer_start_offset,
+ cb_data->no_of_items,
+ cb_data->d_buffer_read,
+ cb_data->d_buffer_write,
+ cb_data->op_buffer_read,
+ cb_data->op_buffer_write);
+#endif
+ r = SCCudaMemcpyHtoDAsync(cuda_packets_buffer_d, (cb_data->d_buffer + cb_culled_info.d_buffer_start_offset), cb_culled_info.d_buffer_len, 0);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemcpyHtoD failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemcpyHtoDAsync(cuda_offset_buffer_d, (cb_data->o_buffer + cb_culled_info.op_buffer_start_offset), sizeof(uint32_t) * cb_culled_info.no_of_items, 0);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemcpyHtoD failure.");
+ exit(EXIT_FAILURE);
+ }
+ void *args[] = { &cuda_packets_buffer_d,
+ &cb_culled_info.d_buffer_start_offset,
+ &cuda_offset_buffer_d,
+ &cuda_results_buffer_d,
+ &cb_culled_info.no_of_items,
+ &cuda_g_u8_lowercasetable_d };
+ r = SCCudaLaunchKernel(kernel,
+ (cb_culled_info.no_of_items / BLOCK_SIZE) + 1, 1, 1,
+ BLOCK_SIZE, 1, 1,
+ 0, 0,
+ args, NULL);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaLaunchKernel failure.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemcpyDtoHAsync(cuda_results_buffer_h, cuda_results_buffer_d, sizeof(uint32_t) * (cb_culled_info.d_buffer_len * 2), 0);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaMemcpyDtoH failure.");
+ exit(EXIT_FAILURE);
+ }
+
+
+
+ /**************** 1 SYNCHRO ****************/
+ r = SCCudaCtxSynchronize();
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "SCCudaCtxSynchronize failure.");
+ exit(EXIT_FAILURE);
+ }
+
+ /************* 1 Parse Results ************/
+ uint32_t i_op_start_offset = cb_culled_info.op_buffer_start_offset;
+ uint32_t no_of_items = cb_culled_info.no_of_items;
+ uint32_t *o_buffer = cb_data->o_buffer;
+ uint32_t d_buffer_start_offset = cb_culled_info.d_buffer_start_offset;
+ for (uint32_t i = 0; i < no_of_items; i++, i_op_start_offset++) {
+ Packet *p = (Packet *)cb_data->p_buffer[i_op_start_offset];
+
+ SCMutexLock(&p->cuda_pkt_vars.cuda_mutex);
+ if (p->cuda_pkt_vars.cuda_mpm_enabled == 0) {
+ p->cuda_pkt_vars.cuda_done = 0;
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ continue;
+ }
+
+ p->cuda_pkt_vars.cuda_gpu_matches =
+ cuda_results_buffer_h[((o_buffer[i_op_start_offset] - d_buffer_start_offset) * 2)];
+ if (p->cuda_pkt_vars.cuda_gpu_matches != 0) {
+ memcpy(p->cuda_pkt_vars.cuda_results,
+ cuda_results_buffer_h +
+ ((o_buffer[i_op_start_offset] - d_buffer_start_offset) * 2),
+ (cuda_results_buffer_h[((o_buffer[i_op_start_offset] -
+ d_buffer_start_offset) * 2)] * sizeof(uint32_t)) + 4);
+ }
+
+ p->cuda_pkt_vars.cuda_done = 1;
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ SCCondSignal(&p->cuda_pkt_vars.cuda_cond);
+ }
+ if (no_of_items != 0)
+ CudaBufferReportCulledConsumption(cb_data, &cb_culled_info);
+ } /* while (1) */
+
+ r = SCCudaModuleUnload(cuda_module);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error unloading cuda module.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemFree(cuda_packets_buffer_d);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda device memory.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemFree(cuda_offset_buffer_d);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda device memory.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemFree(cuda_results_buffer_d);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda device memory.");
+ exit(EXIT_FAILURE);
+ }
+ r = SCCudaMemFreeHost(cuda_results_buffer_h);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda host memory.");
+ exit(EXIT_FAILURE);
+ }
+
+ TmThreadsSetFlag(tv, THV_RUNNING_DONE);
+ TmThreadWaitForFlag(tv, THV_DEINIT);
+ TmThreadsSetFlag(tv, THV_CLOSED);
+
+ return NULL;
+
+#undef BLOCK_SIZE
+}
+
+uint32_t SCACCudaPacketResultsProcessing(Packet *p, MpmCtx *mpm_ctx,
+ PatternMatcherQueue *pmq)
+{
+ uint32_t u = 0;
+
+ while (!p->cuda_pkt_vars.cuda_done) {
+ SCMutexLock(&p->cuda_pkt_vars.cuda_mutex);
+ if (p->cuda_pkt_vars.cuda_done) {
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ break;
+ } else {
+ SCCondWait(&p->cuda_pkt_vars.cuda_cond, &p->cuda_pkt_vars.cuda_mutex);
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ }
+ } /* while */
+ p->cuda_pkt_vars.cuda_done = 0;
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+
+ uint32_t cuda_matches = p->cuda_pkt_vars.cuda_gpu_matches;
+ if (cuda_matches == 0)
+ return 0;
+
+ uint32_t matches = 0;
+ uint32_t *results = p->cuda_pkt_vars.cuda_results + 1;
+ uint8_t *buf = p->payload;
+ SCACCtx *ctx = mpm_ctx->ctx;
+ SCACOutputTable *output_table = ctx->output_table;
+ SCACPatternList *pid_pat_list = ctx->pid_pat_list;
+
+ for (u = 0; u < cuda_matches; u += 2) {
+ uint32_t offset = results[u];
+ uint32_t state = results[u + 1];
+ /* we should technically be doing state & 0x00FFFFFF, but we don't
+ * since the cuda kernel does that for us */
+ uint32_t no_of_entries = output_table[state].no_of_entries;
+ /* we should technically be doing state & 0x00FFFFFF, but we don't
+ * since the cuda kernel does that for us */
+ uint32_t *pids = output_table[state].pids;
+ uint32_t k;
+ /* note that this is not a verbatim copy from SCACSearch(). We
+ * don't copy the pattern id into the pattern_id_array. That's
+ * the only change */
+ for (k = 0; k < no_of_entries; k++) {
+ if (pids[k] & 0xFFFF0000) {
+ uint32_t lower_pid = pids[k] & 0x0000FFFF;
+ if (SCMemcmp(pid_pat_list[lower_pid].cs,
+ buf + offset - pid_pat_list[lower_pid].patlen + 1,
+ pid_pat_list[lower_pid].patlen) != 0) {
+ /* inside loop */
+ continue;
+ }
+ if (pmq->pattern_id_bitarray[(lower_pid) / 8] & (1 << ((lower_pid) % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[(lower_pid) / 8] |= (1 << ((lower_pid) % 8));
+ }
+ matches++;
+ } else {
+ if (pmq->pattern_id_bitarray[pids[k] / 8] & (1 << (pids[k] % 8))) {
+ ;
+ } else {
+ pmq->pattern_id_bitarray[pids[k] / 8] |= (1 << (pids[k] % 8));
+ }
+ matches++;
+ }
+ }
+ }
+
+ return matches;
+}
+
+void SCACCudaStartDispatcher(void)
+{
+ /* create the threads */
+ ThreadVars *tv = TmThreadCreate("Cuda_Mpm_AC_Dispatcher",
+ NULL, NULL,
+ NULL, NULL,
+ "custom", SCACCudaDispatcher, 0);
+ if (tv == NULL) {
+ SCLogError(SC_ERR_THREAD_CREATE, "Error creating a thread for "
+ "ac cuda dispatcher. Killing engine.");
+ exit(EXIT_FAILURE);
+ }
+ if (TmThreadSpawn(tv) != 0) {
+ SCLogError(SC_ERR_THREAD_SPAWN, "Failed to spawn thread for "
+ "ac cuda dispatcher. Killing engine.");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+int MpmCudaBufferSetup(void)
+{
+ int r = 0;
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ if (conf == NULL) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error obtaining cuda mpm profile.");
+ return -1;
+ }
+
+ CUcontext cuda_context = CudaHandlerModuleGetContext(MPM_AC_CUDA_MODULE_NAME, conf->device_id);
+ if (cuda_context == 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error retrieving cuda context.");
+ return -1;
+ }
+ r = SCCudaCtxPushCurrent(cuda_context);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error pushing cuda context.");
+ return -1;
+ }
+
+ uint8_t *d_buffer = NULL;
+ uint32_t *o_buffer = NULL;
+ void **p_buffer = NULL;
+
+ r = SCCudaMemAllocHost((void *)&d_buffer, conf->cb_buffer_size);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Cuda alloc host failure.");
+ return -1;
+ }
+ SCLogInfo("Allocated a cuda d_buffer - %"PRIu32" bytes", conf->cb_buffer_size);
+ r = SCCudaMemAllocHost((void *)&o_buffer, sizeof(uint32_t) * UTIL_MPM_CUDA_CUDA_BUFFER_OPBUFFER_ITEMS_DEFAULT);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Cuda alloc host failue.");
+ return -1;
+ }
+ r = SCCudaMemAllocHost((void *)&p_buffer, sizeof(void *) * UTIL_MPM_CUDA_CUDA_BUFFER_OPBUFFER_ITEMS_DEFAULT);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Cuda alloc host failure.");
+ return -1;
+ }
+
+ r = SCCudaCtxPopCurrent(NULL);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "cuda context pop failure.");
+ return -1;
+ }
+
+ CudaBufferData *cb = CudaBufferRegisterNew(d_buffer, conf->cb_buffer_size, o_buffer, p_buffer, UTIL_MPM_CUDA_CUDA_BUFFER_OPBUFFER_ITEMS_DEFAULT);
+ if (cb == NULL) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error registering new cb instance.");
+ return -1;
+ }
+ CudaHandlerModuleStoreData(MPM_AC_CUDA_MODULE_NAME, MPM_AC_CUDA_MODULE_CUDA_BUFFER_NAME, cb);
+
+ return 0;
+}
+
+int MpmCudaBufferDeSetup(void)
+{
+ int r = 0;
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ if (conf == NULL) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error obtaining cuda mpm profile.");
+ return -1;
+ }
+
+ CudaBufferData *cb_data = CudaHandlerModuleGetData(MPM_AC_CUDA_MODULE_NAME, MPM_AC_CUDA_MODULE_CUDA_BUFFER_NAME);
+ BUG_ON(cb_data == NULL);
+
+ CUcontext cuda_context = CudaHandlerModuleGetContext(MPM_AC_CUDA_MODULE_NAME, conf->device_id);
+ if (cuda_context == 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error retrieving cuda context.");
+ return -1;
+ }
+ r = SCCudaCtxPushCurrent(cuda_context);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error pushing cuda context.");
+ return -1;
+ }
+
+ r = SCCudaMemFreeHost(cb_data->d_buffer);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda host memory.");
+ return -1;
+ }
+ r = SCCudaMemFreeHost(cb_data->o_buffer);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda host memory.");
+ return -1;
+ }
+ r = SCCudaMemFreeHost(cb_data->p_buffer);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error freeing cuda host memory.");
+ return -1;
+ }
+
+ r = SCCudaCtxPopCurrent(NULL);
+ if (r < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "cuda context pop failure.");
+ return -1;
+ }
+
+ CudaBufferDeRegister(cb_data);
+
+ return 0;
+}
+
+#endif /* __SC_CUDA_SUPPORT */
+
+/************************** Mpm Registration ***************************/
+
+/**
+ * \brief Register the aho-corasick mpm.
+ */
+void MpmACRegister(void)
+{
+ mpm_table[MPM_AC].name = "ac";
+ /* don't need this. isn't that awesome? no more chopping and blah blah */
+ mpm_table[MPM_AC].max_pattern_length = 0;
+
+ mpm_table[MPM_AC].InitCtx = SCACInitCtx;
+ mpm_table[MPM_AC].InitThreadCtx = SCACInitThreadCtx;
+ mpm_table[MPM_AC].DestroyCtx = SCACDestroyCtx;
+ mpm_table[MPM_AC].DestroyThreadCtx = SCACDestroyThreadCtx;
+ mpm_table[MPM_AC].AddPattern = SCACAddPatternCS;
+ mpm_table[MPM_AC].AddPatternNocase = SCACAddPatternCI;
+ mpm_table[MPM_AC].Prepare = SCACPreparePatterns;
+ mpm_table[MPM_AC].Search = SCACSearch;
+ mpm_table[MPM_AC].Cleanup = NULL;
+ mpm_table[MPM_AC].PrintCtx = SCACPrintInfo;
+ mpm_table[MPM_AC].PrintThreadCtx = SCACPrintSearchStats;
+ mpm_table[MPM_AC].RegisterUnittests = SCACRegisterTests;
+
+ return;
+}
+
+#ifdef __SC_CUDA_SUPPORT__
+
+/**
+ * \brief Register the aho-corasick cuda mpm.
+ */
+void MpmACCudaRegister(void)
+{
+ mpm_table[MPM_AC_CUDA].name = "ac-cuda";
+ /* don't need this. isn't that awesome? no more chopping and blah blah */
+ mpm_table[MPM_AC_CUDA].max_pattern_length = 0;
+
+ mpm_table[MPM_AC_CUDA].InitCtx = SCACInitCtx;
+ mpm_table[MPM_AC_CUDA].InitThreadCtx = SCACInitThreadCtx;
+ mpm_table[MPM_AC_CUDA].DestroyCtx = SCACDestroyCtx;
+ mpm_table[MPM_AC_CUDA].DestroyThreadCtx = SCACDestroyThreadCtx;
+ mpm_table[MPM_AC_CUDA].AddPattern = SCACAddPatternCS;
+ mpm_table[MPM_AC_CUDA].AddPatternNocase = SCACAddPatternCI;
+ mpm_table[MPM_AC_CUDA].Prepare = SCACPreparePatterns;
+ mpm_table[MPM_AC_CUDA].Search = SCACSearch;
+ mpm_table[MPM_AC_CUDA].Cleanup = NULL;
+ mpm_table[MPM_AC_CUDA].PrintCtx = SCACPrintInfo;
+ mpm_table[MPM_AC_CUDA].PrintThreadCtx = SCACPrintSearchStats;
+ mpm_table[MPM_AC_CUDA].RegisterUnittests = SCACRegisterTests;
+
+ return;
+}
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+/*************************************Unittests********************************/
+
+#ifdef UNITTESTS
+
+static int SCACTest01(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest02(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest03(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest04(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest05(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghjiklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest06(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcd";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest07(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0);
+ /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0);
+ /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0);
+ /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0);
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ 30, 0, 0, 5, 0, 0);
+ PmqSetup(&pmq, 6);
+ /* total matches: 135 */
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest08(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest09(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest10(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "abcdefgh"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest11(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1)
+ goto end;
+ if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1)
+ goto end;
+ PmqSetup(&pmq, 5);
+
+ if (SCACPreparePatterns(&mpm_ctx) == -1)
+ goto end;
+
+ result = 1;
+
+ char *buf = "he";
+ result &= (SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "she";
+ result &= (SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+ buf = "his";
+ result &= (SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 1);
+ buf = "hers";
+ result &= (SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
+ strlen(buf)) == 2);
+
+ end:
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest12(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest13(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCD";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCD";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest14(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDE";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDE";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest15(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest16(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzABC";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzABC";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest17(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcdefghijklmnopqrstuvwxyzAB";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyzAB";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest18(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ char *pat = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcde""fghij""klmno""pqrst""uvwxy""z";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest19(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest20(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ char *pat = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest21(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"AA", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest22(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "abcdefghijklmnopqrstuvwxyz";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest23(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest24(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 1 */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)"aa", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest25(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0);
+ PmqSetup(&pmq, 3);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest26(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0);
+ PmqSetup(&pmq, 2);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "works";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest27(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "tone";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest28(void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmThreadCtx mpm_thread_ctx;
+ PatternMatcherQueue pmq;
+
+ memset(&mpm_ctx, 0, sizeof(MpmCtx));
+ memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+ MpmInitCtx(&mpm_ctx, MPM_AC);
+ SCACInitThreadCtx(&mpm_ctx, &mpm_thread_ctx, 0);
+
+ /* 0 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0);
+ PmqSetup(&pmq, 1);
+
+ SCACPreparePatterns(&mpm_ctx);
+
+ char *buf = "tONE";
+ uint32_t cnt = SCACSearch(&mpm_ctx, &mpm_thread_ctx, &pmq,
+ (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ SCACDestroyCtx(&mpm_ctx);
+ SCACDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
+ PmqFree(&pmq);
+ return result;
+}
+
+static int SCACTest29(void)
+{
+ uint8_t *buf = (uint8_t *)"onetwothreefourfivesixseveneightnine";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)");
+ if (de_ctx->sig_list == NULL)
+ goto end;
+ de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(content:\"onetwothreefourfivesixseveneightnine\"; fast_pattern:3,3; sid:2;)");
+ if (de_ctx->sig_list->next == NULL)
+ goto end;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 1) failure\n");
+ goto end;
+ }
+ if (PacketAlertCheck(p, 2) != 1) {
+ printf("if (PacketAlertCheck(p, 1) != 2) failure\n");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCACRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCACTest01", SCACTest01, 1);
+ UtRegisterTest("SCACTest02", SCACTest02, 1);
+ UtRegisterTest("SCACTest03", SCACTest03, 1);
+ UtRegisterTest("SCACTest04", SCACTest04, 1);
+ UtRegisterTest("SCACTest05", SCACTest05, 1);
+ UtRegisterTest("SCACTest06", SCACTest06, 1);
+ UtRegisterTest("SCACTest07", SCACTest07, 1);
+ UtRegisterTest("SCACTest08", SCACTest08, 1);
+ UtRegisterTest("SCACTest09", SCACTest09, 1);
+ UtRegisterTest("SCACTest10", SCACTest10, 1);
+ UtRegisterTest("SCACTest11", SCACTest11, 1);
+ UtRegisterTest("SCACTest12", SCACTest12, 1);
+ UtRegisterTest("SCACTest13", SCACTest13, 1);
+ UtRegisterTest("SCACTest14", SCACTest14, 1);
+ UtRegisterTest("SCACTest15", SCACTest15, 1);
+ UtRegisterTest("SCACTest16", SCACTest16, 1);
+ UtRegisterTest("SCACTest17", SCACTest17, 1);
+ UtRegisterTest("SCACTest18", SCACTest18, 1);
+ UtRegisterTest("SCACTest19", SCACTest19, 1);
+ UtRegisterTest("SCACTest20", SCACTest20, 1);
+ UtRegisterTest("SCACTest21", SCACTest21, 1);
+ UtRegisterTest("SCACTest22", SCACTest22, 1);
+ UtRegisterTest("SCACTest23", SCACTest23, 1);
+ UtRegisterTest("SCACTest24", SCACTest24, 1);
+ UtRegisterTest("SCACTest25", SCACTest25, 1);
+ UtRegisterTest("SCACTest26", SCACTest26, 1);
+ UtRegisterTest("SCACTest27", SCACTest27, 1);
+ UtRegisterTest("SCACTest28", SCACTest28, 1);
+ UtRegisterTest("SCACTest29", SCACTest29, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-mpm-ac.h b/framework/src/suricata/src/util-mpm-ac.h
new file mode 100644
index 00000000..a837f5f0
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-ac.h
@@ -0,0 +1,221 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ */
+
+#ifndef __UTIL_MPM_AC__H__
+#define __UTIL_MPM_AC__H__
+
+#define SC_AC_STATE_TYPE_U16 uint16_t
+#define SC_AC_STATE_TYPE_U32 uint32_t
+
+#ifdef __SC_CUDA_SUPPORT__
+#include "suricata-common.h"
+#include "util-cuda.h"
+#include "util-cuda-vars.h"
+#include "decode.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm.h"
+#include "flow.h"
+#endif /* __SC_CUDA_SUPPORT__ */
+
+typedef struct SCACPattern_ {
+ /* length of the pattern */
+ uint16_t len;
+ /* flags decribing the pattern */
+ uint8_t flags;
+ /* holds the original pattern that was added */
+ uint8_t *original_pat;
+ /* case sensitive */
+ uint8_t *cs;
+ /* case INsensitive */
+ uint8_t *ci;
+ /* pattern id */
+ uint32_t id;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct SCACPattern_ *next;
+} SCACPattern;
+
+typedef struct SCACPatternList_ {
+ uint8_t *cs;
+ uint16_t patlen;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+} SCACPatternList;
+
+typedef struct SCACOutputTable_ {
+ /* list of pattern sids */
+ uint32_t *pids;
+ /* no of entries we have in pids */
+ uint32_t no_of_entries;
+} SCACOutputTable;
+
+typedef struct SCACCtx_ {
+ /* hash used during ctx initialization */
+ SCACPattern **init_hash;
+
+ /* pattern arrays. We need this only during the goto table creation phase */
+ SCACPattern **parray;
+
+ /* no of states used by ac */
+ uint32_t state_count;
+ /* the all important memory hungry state_table */
+ SC_AC_STATE_TYPE_U16 (*state_table_u16)[256];
+ /* the all important memory hungry state_table */
+ SC_AC_STATE_TYPE_U32 (*state_table_u32)[256];
+
+ /* goto_table, failure table and output table. Needed to create state_table.
+ * Will be freed, once we have created the state_table */
+ int32_t (*goto_table)[256];
+ int32_t *failure_table;
+ SCACOutputTable *output_table;
+ SCACPatternList *pid_pat_list;
+
+ /* the size of each state */
+ uint16_t single_state_size;
+ uint16_t max_pat_id;
+
+ uint32_t allocated_state_count;
+
+#ifdef __SC_CUDA_SUPPORT__
+ CUdeviceptr state_table_u16_cuda;
+ CUdeviceptr state_table_u32_cuda;
+#endif /* __SC_CUDA_SUPPORT__ */
+} SCACCtx;
+
+typedef struct SCACThreadCtx_ {
+ /* the total calls we make to the search function */
+ uint32_t total_calls;
+ /* the total patterns that we ended up matching against */
+ uint64_t total_matches;
+} SCACThreadCtx;
+
+void MpmACRegister(void);
+
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#define MPM_AC_CUDA_MODULE_NAME "ac_cuda"
+#define MPM_AC_CUDA_MODULE_CUDA_BUFFER_NAME "ac_cuda_cb"
+
+static inline void CudaBufferPacket(CudaThreadVars *ctv, Packet *p)
+{
+ if (p->cuda_pkt_vars.cuda_mpm_enabled) {
+ while (!p->cuda_pkt_vars.cuda_done) {
+ SCMutexLock(&p->cuda_pkt_vars.cuda_mutex);
+ if (p->cuda_pkt_vars.cuda_done) {
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ break;
+ } else {
+ SCCondWait(&p->cuda_pkt_vars.cuda_cond, &p->cuda_pkt_vars.cuda_mutex);
+ SCMutexUnlock(&p->cuda_pkt_vars.cuda_mutex);
+ }
+ }
+ }
+ p->cuda_pkt_vars.cuda_done = 0;
+
+ if (p->payload_len == 0 ||
+ (p->flags & (PKT_NOPAYLOAD_INSPECTION & PKT_NOPACKET_INSPECTION)) ||
+ (p->flags & PKT_ALLOC) ||
+ (ctv->data_buffer_size_min_limit != 0 && p->payload_len < ctv->data_buffer_size_min_limit) ||
+ (p->payload_len > ctv->data_buffer_size_max_limit && ctv->data_buffer_size_max_limit != 0) ) {
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+ return;
+ }
+
+ MpmCtx *mpm_ctx = NULL;
+ if (p->proto == IPPROTO_TCP) {
+ if (p->flowflags & FLOW_PKT_TOSERVER)
+ mpm_ctx = ctv->mpm_proto_tcp_ctx_ts;
+ else
+ mpm_ctx = ctv->mpm_proto_tcp_ctx_tc;
+ } else if (p->proto == IPPROTO_UDP) {
+ if (p->flowflags & FLOW_PKT_TOSERVER)
+ mpm_ctx = ctv->mpm_proto_udp_ctx_ts;
+ else
+ mpm_ctx = ctv->mpm_proto_udp_ctx_tc;
+ } else {
+ mpm_ctx = ctv->mpm_proto_other_ctx;
+ }
+ if (mpm_ctx == NULL || mpm_ctx->pattern_cnt == 0) {
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+ return;
+ }
+
+#if __WORDSIZE==64
+ CudaBufferSlice *slice = CudaBufferGetSlice(ctv->cuda_ac_cb,
+ p->payload_len + sizeof(uint64_t) + sizeof(CUdeviceptr),
+ (void *)p);
+ if (slice == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error retrieving slice. Please report "
+ "this to dev.");
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+ return;
+ }
+ *((uint64_t *)(slice->buffer + slice->start_offset)) = p->payload_len;
+ *((CUdeviceptr *)(slice->buffer + slice->start_offset + sizeof(uint64_t))) = ((SCACCtx *)(mpm_ctx->ctx))->state_table_u32_cuda;
+ memcpy(slice->buffer + slice->start_offset + sizeof(uint64_t) + sizeof(CUdeviceptr), p->payload, p->payload_len);
+#else
+ CudaBufferSlice *slice = CudaBufferGetSlice(ctv->cuda_ac_cb,
+ p->payload_len + sizeof(uint32_t) + sizeof(CUdeviceptr),
+ (void *)p);
+ if (slice == NULL) {
+ SCLogError(SC_ERR_FATAL, "Error retrieving slice. Please report "
+ "this to dev.");
+ p->cuda_pkt_vars.cuda_mpm_enabled = 0;
+ return;
+ }
+ *((uint32_t *)(slice->buffer + slice->start_offset)) = p->payload_len;
+ *((CUdeviceptr *)(slice->buffer + slice->start_offset + sizeof(uint32_t))) = ((SCACCtx *)(mpm_ctx->ctx))->state_table_u32_cuda;
+ memcpy(slice->buffer + slice->start_offset + sizeof(uint32_t) + sizeof(CUdeviceptr), p->payload, p->payload_len);
+#endif
+ p->cuda_pkt_vars.cuda_mpm_enabled = 1;
+ SC_ATOMIC_SET(slice->done, 1);
+
+ SCLogDebug("cuda ac buffering packet %p, payload_len - %"PRIu16" and deviceptr - %"PRIu64"\n",
+ p, p->payload_len, (unsigned long)((SCACCtx *)(mpm_ctx->ctx))->state_table_u32_cuda);
+
+ return;
+}
+
+void MpmACCudaRegister(void);
+void SCACConstructBoth16and32StateTables(void);
+int MpmCudaBufferSetup(void);
+int MpmCudaBufferDeSetup(void);
+void SCACCudaStartDispatcher(void);
+void SCACCudaKillDispatcher(void);
+uint32_t SCACCudaPacketResultsProcessing(Packet *p, MpmCtx *mpm_ctx,
+ PatternMatcherQueue *pmq);
+void DetermineCudaStateTableSize(DetectEngineCtx *de_ctx);
+
+void CudaReleasePacket(Packet *p);
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+
+#endif /* __UTIL_MPM_AC__H__ */
diff --git a/framework/src/suricata/src/util-mpm-b2g.c b/framework/src/suricata/src/util-mpm-b2g.c
new file mode 100644
index 00000000..f7172839
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-b2g.c
@@ -0,0 +1,2832 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implementation of the SBNDMq pattern matching algorithm.
+ *
+ * Ideas:
+ * - B2g does a full match in the search of up to 'm' characters,
+ * in case of a case insensitive search we could say it's match if
+ * the pattern is of len 'm' or just compare the rest of the chars.
+ *
+ * \todo Try to get the S0 calculation right.
+ */
+
+//#define PRINTMATCH
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "detect.h"
+#include "util-bloomfilter.h"
+#include "util-mpm-b2g.h"
+#include "util-print.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+#include "conf.h"
+
+#define INIT_HASH_SIZE 65536
+
+#ifdef B2G_COUNTERS
+#define COUNT(counter) \
+ (counter)
+#else
+#define COUNT(counter)
+#endif /* B2G_COUNTERS */
+
+static uint32_t b2g_hash_size = 0;
+static uint32_t b2g_bloom_size = 0;
+static uint8_t b2g_hash_shift = 0;
+static void *b2g_func;
+
+#define B2G_HASH16(a,b) (((a) << b2g_hash_shift) | (b))
+
+void B2gInitCtx (MpmCtx *);
+void B2gThreadInitCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void B2gDestroyCtx(MpmCtx *);
+void B2gThreadDestroyCtx(MpmCtx *, MpmThreadCtx *);
+int B2gAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags);
+int B2gAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags);
+int B2gPreparePatterns(MpmCtx *mpm_ctx);
+uint32_t B2gSearchWrap(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t B2gSearch1(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+#ifdef B2G_SEARCH2
+uint32_t B2gSearch2(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+#endif
+uint32_t B2gSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t B2gSearchBNDMq(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen);
+void B2gPrintInfo(MpmCtx *mpm_ctx);
+void B2gPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void B2gRegisterTests(void);
+
+void MpmB2gRegister (void)
+{
+ mpm_table[MPM_B2G].name = "b2g";
+ mpm_table[MPM_B2G].max_pattern_length = B2G_WORD_SIZE;
+
+ mpm_table[MPM_B2G].InitCtx = B2gInitCtx;
+ mpm_table[MPM_B2G].InitThreadCtx = B2gThreadInitCtx;
+ mpm_table[MPM_B2G].DestroyCtx = B2gDestroyCtx;
+ mpm_table[MPM_B2G].DestroyThreadCtx = B2gThreadDestroyCtx;
+ mpm_table[MPM_B2G].AddPattern = B2gAddPatternCS;
+ mpm_table[MPM_B2G].AddPatternNocase = B2gAddPatternCI;
+ mpm_table[MPM_B2G].Prepare = B2gPreparePatterns;
+ mpm_table[MPM_B2G].Search = B2gSearchWrap;
+ mpm_table[MPM_B2G].Cleanup = NULL;
+ mpm_table[MPM_B2G].PrintCtx = B2gPrintInfo;
+ mpm_table[MPM_B2G].PrintThreadCtx = B2gPrintSearchStats;
+ mpm_table[MPM_B2G].RegisterUnittests = B2gRegisterTests;
+}
+
+#ifdef PRINTMATCH
+static void prt (uint8_t *buf, uint16_t buflen)
+{
+ uint16_t i;
+
+ for (i = 0; i < buflen; i++) {
+ if (isprint(buf[i])) printf("%c", buf[i]);
+ else printf("\\x%02X", buf[i]);
+ }
+ //printf("\n");
+}
+#endif
+
+void B2gPrintInfo(MpmCtx *mpm_ctx)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+
+ printf("MPM B2g Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeofs:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" B2gCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(B2gCtx));
+ printf(" B2gPattern %" PRIuMAX "\n", (uintmax_t)sizeof(B2gPattern));
+ printf(" B2gPattern %" PRIuMAX "\n", (uintmax_t)sizeof(B2gPattern));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Hash size: %" PRIu32 "\n", ctx->hash_size);
+ printf("\n");
+}
+
+/**
+ * \brief B2gAllocPattern allocates a new pattern structure
+ * and initialize the data
+ * \initonly
+ */
+static inline B2gPattern *B2gAllocPattern(MpmCtx *mpm_ctx)
+{
+ B2gPattern *p = SCMalloc(sizeof(B2gPattern));
+ if (unlikely(p == NULL))
+ return NULL;
+ memset(p,0,sizeof(B2gPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B2gPattern);
+ return p;
+}
+
+static inline B2gPattern *
+B2gAllocHashItem(MpmCtx *mpm_ctx)
+{
+ B2gPattern *hi = SCMalloc(sizeof(B2gPattern));
+ if (unlikely(hi == NULL))
+ return NULL;
+ memset(hi,0,sizeof(B2gPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B2gPattern);
+ return hi;
+}
+
+static void B2gHashFree(MpmCtx *mpm_ctx, B2gPattern *hi)
+{
+ if (hi == NULL)
+ return;
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B2gPattern);
+ B2gPattern *t = hi->next;
+ SCFree(hi);
+
+ B2gHashFree(mpm_ctx, t);
+}
+
+/*
+ * INIT HASH START
+ */
+static inline uint32_t B2gInitHash(B2gPattern *p)
+{
+ uint32_t hash = p->len * p->original_pat[0];
+ if (p->len > 1)
+ hash += p->original_pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline uint32_t B2gInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int B2gInitHashAdd(B2gCtx *ctx, B2gPattern *p)
+{
+ uint32_t hash = B2gInitHash(p);
+
+ //printf("B2gInitHashAdd: %" PRIu32 "\n", hash);
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ //printf("B2gInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+ return 0;
+ }
+
+ B2gPattern *tt = NULL;
+ B2gPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+
+ //printf("B2gInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+ return 0;
+}
+
+static inline int B2gCmpPattern(B2gPattern *p, uint8_t *pat, uint16_t patlen, char flags);
+
+static inline B2gPattern *B2gInitHashLookup(B2gCtx *ctx, uint8_t *pat, uint16_t patlen, char flags,
+ uint32_t pid)
+{
+ uint32_t hash = B2gInitHashRaw(pat,patlen);
+
+ //printf("B2gInitHashLookup: %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+
+ if (ctx->init_hash[hash] == NULL) {
+ return NULL;
+ }
+
+ B2gPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ //if (B2gCmpPattern(t,pat,patlen,flags) == 1)
+ if (t->id == pid)
+ return t;
+ }
+
+ return NULL;
+}
+
+static inline int B2gCmpPattern(B2gPattern *p, uint8_t *pat, uint16_t patlen, char flags)
+{
+ if (p->len != patlen)
+ return 0;
+
+ if (p->flags != flags)
+ return 0;
+
+ if (SCMemcmp(p->cs, pat, patlen) != 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * INIT HASH END
+ */
+
+void B2gFreePattern(MpmCtx *mpm_ctx, B2gPattern *p)
+{
+ if (p && p->cs && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->ci) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p != NULL && p->original_pat != NULL) {
+ SCFree(p->original_pat);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->sids) {
+ SCFree(p->sids);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->sids_size * sizeof(SigIntId);
+ }
+
+ if (p) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B2gPattern);
+ }
+}
+
+/** \internal
+ * \brief add a pattern to the mpm/b2g context
+ *
+ * \param pat ptr to the pattern
+ * \param patlen length of the pattern
+ * \param pid pattern id
+ * \param sid signature id (internal id)
+ * \param flags pattern MPM_PATTERN_* flags
+ *
+ * \initonly
+ */
+static int B2gAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+
+ SCLogDebug("ctx %p len %"PRIu16" pid %" PRIu32, ctx, patlen, pid);
+
+ if (patlen == 0)
+ return 0;
+
+ /* get a memory piece */
+ B2gPattern *p = B2gInitHashLookup(ctx, pat, patlen, flags, pid);
+ if (p == NULL) {
+ SCLogDebug("allocing new pattern");
+
+ p = B2gAllocPattern(mpm_ctx);
+ if (p == NULL)
+ goto error;
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ p->original_pat = SCMalloc(patlen);
+ if (p->original_pat == NULL)
+ goto error;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->original_pat, pat, patlen);
+
+ /* setup the case insensitive part of the pattern */
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (SCMemcmp(p->ci,pat,p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SigIntId);
+
+ //printf("B2gAddPattern: ci \""); prt(p->ci,p->len);
+ //printf("\" cs \""); prt(p->cs,p->len);
+ //printf("\"\n");
+
+ /* put in the pattern hash */
+ B2gInitHashAdd(ctx, p);
+
+ if (mpm_ctx->pattern_cnt == 65535) {
+ printf("Max search words reached\n");
+ exit(1);
+ }
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen) mpm_ctx->maxlen = patlen;
+ if (mpm_ctx->minlen == 0) mpm_ctx->minlen = patlen;
+ else if (mpm_ctx->minlen > patlen) mpm_ctx->minlen = patlen;
+ } else {
+ /* Multiple sids for the same pid, so keep an array of sids. */
+
+ /* TODO figure out how we can be called multiple times for the
+ * same CTX with the same sid */
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ mpm_ctx->memory_size += sizeof(SigIntId);
+ }
+ }
+
+ return 0;
+
+error:
+ B2gFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+int B2gAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return B2gAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+int B2gAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ return B2gAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+static inline uint32_t B2gBloomHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint16_t i;
+ uint32_t hash = (uint32_t)u8_tolower(*d);
+
+ for (i = 1; i < datalen; i++) {
+ d++;
+ hash += (u8_tolower(*d)) ^ i;
+ }
+ hash <<= (iter+1);
+
+ hash %= hash_size;
+ return hash;
+}
+
+static void B2gPrepareHash(MpmCtx *mpm_ctx)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ uint16_t i;
+ uint16_t idx = 0;
+ uint8_t idx8 = 0;
+
+ ctx->hash = (B2gPattern **)SCMalloc(sizeof(B2gPattern *) * ctx->hash_size);
+ if (ctx->hash == NULL)
+ goto error;
+ memset(ctx->hash, 0, sizeof(B2gPattern *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B2gPattern *) * ctx->hash_size);
+
+#ifdef B2G_SEARCH2
+ ctx->hash2 = (B2gPattern **)SCMalloc(sizeof(B2gPattern *) * ctx->hash_size);
+ if (ctx->hash2 == NULL)
+ goto error;
+ memset(ctx->hash2, 0, sizeof(B2gPattern *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B2gPattern *) * ctx->hash_size);
+#endif
+
+ /* alloc the pminlen array */
+ ctx->pminlen = (uint8_t *)SCMalloc(sizeof(uint8_t) * ctx->hash_size);
+ if (ctx->pminlen == NULL)
+ goto error;
+ memset(ctx->pminlen, 0, sizeof(uint8_t) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(uint8_t) * ctx->hash_size);
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++)
+ {
+ if(ctx->parray[i]->len == 1) {
+ idx8 = (uint8_t)ctx->parray[i]->ci[0];
+ if (ctx->hash1[idx8].flags == 0) {
+ ctx->hash1[idx8].flags |= MPM_PATTERN_ONE_BYTE;
+
+ B2gPattern *hi = &ctx->hash1[idx8];
+ hi->len = ctx->parray[i]->len;
+ hi->flags |= ctx->parray[i]->flags;
+ hi->id = ctx->parray[i]->id;
+ hi->ci = ctx->parray[i]->ci;
+ hi->cs = ctx->parray[i]->cs;
+ hi->sids = ctx->parray[i]->sids;
+ hi->sids_size = ctx->parray[i]->sids_size;
+
+ } else {
+ B2gPattern *hi = B2gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->flags |= MPM_PATTERN_ONE_BYTE;
+
+ hi->len = ctx->parray[i]->len;
+ hi->flags |= ctx->parray[i]->flags;
+ hi->id = ctx->parray[i]->id;
+ hi->ci = ctx->parray[i]->ci;
+ hi->cs = ctx->parray[i]->cs;
+ hi->sids = ctx->parray[i]->sids;
+ hi->sids_size = ctx->parray[i]->sids_size;
+
+ /* Append this HashItem to the list */
+ B2gPattern *thi = &ctx->hash1[idx8];
+ while (thi->next) thi = thi->next;
+ thi->next = hi;
+ }
+ ctx->pat_1_cnt++;
+#ifdef B2G_SEARCH2
+ } else if(ctx->parray[i]->len == 2) {
+ idx = B2G_HASH16(ctx->parray[i]->ci[0],ctx->parray[i]->ci[1]);
+ if (ctx->hash2[idx] == NULL) {
+ B2gPattern *hi = B2gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= MPM_PATTERN_ONE_BYTE;
+
+ ctx->hash2[idx] = hi;
+ } else {
+ B2gPattern *hi = B2gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= MPM_PATTERN_ONE_BYTE;
+
+ /* Append this HashItem to the list */
+ B2gPattern *thi = ctx->hash2[idx];
+ while (thi->next) thi = thi->next;
+ thi->next = hi;
+ }
+ ctx->pat_2_cnt++;
+#endif
+ } else {
+ idx = B2G_HASH16(ctx->parray[i]->ci[ctx->m - 2], ctx->parray[i]->ci[ctx->m - 1]);
+ SCLogDebug("idx %" PRIu32 ", %c.%c", idx, ctx->parray[i]->ci[ctx->m - 2], ctx->parray[i]->ci[ctx->m - 1]);
+
+ if (ctx->hash[idx] == NULL) {
+ B2gPattern *hi = B2gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ hi->len = ctx->parray[i]->len;
+ hi->flags |= ctx->parray[i]->flags;
+ hi->id = ctx->parray[i]->id;
+ hi->ci = ctx->parray[i]->ci;
+ hi->cs = ctx->parray[i]->cs;
+ hi->sids = ctx->parray[i]->sids;
+ hi->sids_size = ctx->parray[i]->sids_size;
+
+ ctx->hash[idx] = hi;
+ } else {
+ B2gPattern *hi = B2gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+
+ hi->flags |= MPM_PATTERN_ONE_BYTE;
+
+ hi->len = ctx->parray[i]->len;
+ hi->flags |= ctx->parray[i]->flags;
+ hi->id = ctx->parray[i]->id;
+ hi->ci = ctx->parray[i]->ci;
+ hi->cs = ctx->parray[i]->cs;
+ hi->sids = ctx->parray[i]->sids;
+ hi->sids_size = ctx->parray[i]->sids_size;
+
+ if (ctx->parray[i]->len < ctx->pminlen[idx])
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ /* Append this HashItem to the list */
+ B2gPattern *thi = ctx->hash[idx];
+ while (thi->next) thi = thi->next;
+ thi->next = hi;
+ }
+ ctx->pat_x_cnt++;
+ }
+ }
+
+ /* alloc the bloom array */
+ ctx->bloom = (BloomFilter **)SCMalloc(sizeof(BloomFilter *) * ctx->hash_size);
+ if (ctx->bloom == NULL)
+ goto error;
+ memset(ctx->bloom, 0, sizeof(BloomFilter *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(BloomFilter *) * ctx->hash_size);
+
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ B2gPattern *hi = ctx->hash[h];
+ if (hi == NULL)
+ continue;
+
+ ctx->bloom[h] = BloomFilterInit(b2g_bloom_size, 2, B2gBloomHash);
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt += BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size += BloomFilterMemorySize(ctx->bloom[h]);
+
+ if (ctx->pminlen[h] > 8)
+ ctx->pminlen[h] = 8;
+
+ B2gPattern *thi = hi;
+ do {
+ SCLogDebug("adding \"%c%c\" to the bloom", thi->ci[0], thi->ci[1]);
+ BloomFilterAdd(ctx->bloom[h], thi->ci, ctx->pminlen[h]);
+ thi = thi->next;
+ } while (thi != NULL);
+ }
+
+ return;
+error:
+ return;
+}
+
+int B2gBuildMatchArray(MpmCtx *mpm_ctx)
+{
+ SCEnter();
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+
+ ctx->B2G = SCMalloc(sizeof(B2G_TYPE) * ctx->hash_size);
+ if (ctx->B2G == NULL)
+ return -1;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B2G_TYPE) * ctx->hash_size);
+
+ memset(ctx->B2G,0, b2g_hash_size * sizeof(B2G_TYPE));
+
+ uint32_t j;
+ uint32_t a;
+
+ /* fill the match array */
+ for (j = 0; j <= (ctx->m - B2G_Q); j++) {
+ for (a = 0; a < mpm_ctx->pattern_cnt; a++) {
+ if (ctx->parray[a]->len < ctx->m)
+ continue;
+
+ uint16_t h = B2G_HASH16(u8_tolower(ctx->parray[a]->ci[j]),u8_tolower(ctx->parray[a]->ci[j+1]));
+ ctx->B2G[h] = ctx->B2G[h] | (1 << (ctx->m - j));
+
+ SCLogDebug("h %"PRIu16", ctx->B2G[h] %"PRIu32"", h, ctx->B2G[h]);
+ }
+ }
+
+ ctx->s0 = 1;
+ SCReturnInt(0);
+}
+
+int B2gPreparePatterns(MpmCtx *mpm_ctx)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+
+ /* alloc the pattern array */
+ ctx->parray = (B2gPattern **)SCMalloc(mpm_ctx->pattern_cnt * sizeof(B2gPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(B2gPattern *));
+ //printf("mpm_ctx %p, parray %p\n", mpm_ctx,ctx->parray);
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(B2gPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ B2gPattern *node = ctx->init_hash[i], *nnode = NULL;
+ for ( ; node != NULL; ) {
+ nnode = node->next;
+ node->next = NULL;
+
+ ctx->parray[p] = node;
+
+ p++;
+ node = nnode;
+ }
+ }
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* set 'm' to the smallest pattern size */
+ ctx->m = mpm_ctx->minlen;
+
+ /* make sure 'm' stays in bounds
+ m can be max WORD_SIZE - 1 */
+ if (ctx->m >= B2G_WORD_SIZE) {
+ ctx->m = B2G_WORD_SIZE - 1;
+ }
+ if (ctx->m < 2) ctx->m = 2;
+
+ ctx->hash_size = b2g_hash_size;
+ B2gPrepareHash(mpm_ctx);
+ B2gBuildMatchArray(mpm_ctx);
+
+ SCLogDebug("ctx->pat_1_cnt %"PRIu16"", ctx->pat_1_cnt);
+ if (ctx->pat_1_cnt) {
+ ctx->Search = B2gSearch1;
+#ifdef B2G_SEARCH2
+ ctx->Search = B2gSearch2;
+ if (ctx->pat_2_cnt) {
+ ctx->MBSearch2 = B2gSearch2;
+ }
+#endif
+ ctx->MBSearch = b2g_func;
+#ifdef B2G_SEARCH2
+ } else if (ctx->pat_2_cnt) {
+ ctx->Search = B2gSearch2;
+ ctx->MBSearch = b2g_func;
+#endif
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+void B2gPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+#ifdef B2G_COUNTERS
+ B2gThreadCtx *tctx = (B2gThreadCtx *)mpm_thread_ctx->ctx;
+
+ printf("B2g Thread Search stats (tctx %p)\n", tctx);
+ printf("Total calls: %" PRIu32 "\n", tctx->stat_calls);
+ printf("Avg m/search: %0.2f\n", tctx->stat_calls ? (float)((float)tctx->stat_m_total / (float)tctx->stat_calls) : 0);
+ printf("D != 0 (possible match): %" PRIu32 "\n", tctx->stat_d0);
+ printf("Avg hash items per bucket %0.2f (%" PRIu32 ")\n", tctx->stat_d0 ? (float)((float)tctx->stat_d0_hashloop / (float)tctx->stat_d0) : 0, tctx->stat_d0_hashloop);
+ printf("Loop match: %" PRIu32 "\n", tctx->stat_loop_match);
+ printf("Loop no match: %" PRIu32 "\n", tctx->stat_loop_no_match);
+ printf("Num shifts: %" PRIu32 "\n", tctx->stat_num_shift);
+ printf("Total shifts: %" PRIu32 "\n", tctx->stat_total_shift);
+ printf("Avg shifts: %0.2f\n", tctx->stat_num_shift ? (float)((float)tctx->stat_total_shift / (float)tctx->stat_num_shift) : 0);
+ printf("Total BloomFilter checks: %" PRIu32 "\n", tctx->stat_bloom_calls);
+ printf("BloomFilter hits: %0.4f%% (%" PRIu32 ")\n", tctx->stat_bloom_calls ? (float)((float)((float)tctx->stat_bloom_hits / (float)tctx->stat_bloom_calls)*(float)100) : 0, tctx->stat_bloom_hits);
+ printf("Avg pminlen: %0.2f\n\n", tctx->stat_pminlen_calls ? (float)((float)tctx->stat_pminlen_total / (float)tctx->stat_pminlen_calls) : 0);
+#endif /* B2G_COUNTERS */
+}
+
+/**
+ * \brief Function to get the user defined values for b2g algorithm from the
+ * config file 'suricata.yaml'
+ */
+static void B2gGetConfig()
+{
+ ConfNode *b2g_conf;
+ const char *hash_val = NULL;
+ const char *bloom_val = NULL;
+ const char *algo = NULL;
+
+ /* init defaults */
+ b2g_hash_size = HASHSIZE_LOW;
+ b2g_bloom_size = BLOOMSIZE_MEDIUM;
+ b2g_hash_shift = B2G_HASHSHIFT_LOW;
+ b2g_func = B2G_SEARCHFUNC;
+
+ ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ if (pm != NULL) {
+
+ TAILQ_FOREACH(b2g_conf, &pm->head, next) {
+ if (strcmp(b2g_conf->val, "b2g") == 0) {
+
+ algo = ConfNodeLookupChildValue
+ (b2g_conf->head.tqh_first, "algo");
+ hash_val = ConfNodeLookupChildValue
+ (b2g_conf->head.tqh_first, "hash_size");
+ bloom_val = ConfNodeLookupChildValue
+ (b2g_conf->head.tqh_first, "bf_size");
+
+ if (algo != NULL) {
+ if (strcmp(algo, "B2gSearch") == 0) {
+ b2g_func = B2gSearch;
+ } else if (strcmp(algo, "B2gSearchBNDMq") == 0) {
+ b2g_func = B2gSearchBNDMq;
+ }
+ }
+
+ if (hash_val != NULL) {
+ b2g_hash_size = MpmGetHashSize(hash_val);
+ switch (b2g_hash_size) {
+ case HASHSIZE_LOWEST:
+ b2g_hash_shift = B2G_HASHSHIFT_LOWEST;
+ break;
+ case HASHSIZE_LOW:
+ b2g_hash_shift = B2G_HASHSHIFT_LOW;
+ break;
+ case HASHSIZE_MEDIUM:
+ b2g_hash_shift = B2G_HASHSHIFT_MEDIUM;
+ break;
+ case HASHSIZE_HIGH:
+ b2g_hash_shift = B2G_HASHSHIFT_HIGH;
+ break;
+ case HASHSIZE_HIGHER:
+ b2g_hash_shift = B2G_HASHSHIFT_HIGHER;
+ break;
+ case HASHSIZE_MAX:
+ b2g_hash_shift = B2G_HASHSHIFT_MAX;
+ break;
+ }
+ }
+
+ if (bloom_val != NULL)
+ b2g_bloom_size = MpmGetBloomSize(bloom_val);
+
+ SCLogDebug("hash size is %"PRIu32" and bloom size is %"PRIu32"",
+ b2g_hash_size, b2g_bloom_size);
+ }
+ }
+ }
+}
+
+void B2gInitCtx (MpmCtx *mpm_ctx)
+{
+ SCLogDebug("mpm_ctx %p, ctx %p", mpm_ctx, mpm_ctx->ctx);
+
+ if (mpm_ctx->ctx != NULL)
+ return;
+
+ //BUG_ON(mpm_ctx->ctx != NULL);
+
+ mpm_ctx->ctx = SCMalloc(sizeof(B2gCtx));
+ if (mpm_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memset(mpm_ctx->ctx, 0, sizeof(B2gCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B2gCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(B2gPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memset(ctx->init_hash, 0, sizeof(B2gPattern *) * INIT_HASH_SIZE);
+
+ /* Initialize the defaults value from the config file. The given check make
+ sure that we query config file only once for config values */
+ if (b2g_hash_size == 0)
+ B2gGetConfig();
+
+ /* init defaults search functions */
+ ctx->Search = b2g_func;
+
+ SCReturn;
+}
+
+void B2gDestroyCtx(MpmCtx *mpm_ctx)
+{
+ SCLogDebug("mpm_ctx %p", mpm_ctx);
+
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash) {
+ SCFree(ctx->init_hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(B2gPattern *));
+ }
+
+ if (ctx->parray) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ B2gFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(B2gPattern));
+ }
+
+ if (ctx->B2G) {
+ SCFree(ctx->B2G);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(B2G_TYPE) * ctx->hash_size);
+ }
+
+ if (ctx->bloom) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt -= BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size -= BloomFilterMemorySize(ctx->bloom[h]);
+
+ BloomFilterFree(ctx->bloom[h]);
+ }
+
+ SCFree(ctx->bloom);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(BloomFilter *) * ctx->hash_size);
+ }
+
+ if (ctx->hash) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->hash[h] == NULL)
+ continue;
+
+ B2gHashFree(mpm_ctx, ctx->hash[h]);
+ }
+
+ SCFree(ctx->hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(B2gPattern) * ctx->hash_size);
+ }
+
+ if (ctx->pminlen) {
+ SCFree(ctx->pminlen);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(uint8_t) * ctx->hash_size);
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B2gCtx);
+}
+
+void B2gThreadInitCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ if (sizeof(B2gThreadCtx) > 0) { /* size can be null when optimized */
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(B2gThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ memset(mpm_thread_ctx->ctx, 0, sizeof(B2gThreadCtx));
+
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(B2gThreadCtx);
+ }
+}
+
+void B2gThreadDestroyCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ B2gThreadCtx *ctx = (B2gThreadCtx *)mpm_thread_ctx->ctx;
+
+ B2gPrintSearchStats(mpm_thread_ctx);
+
+ if (ctx != NULL) { /* can be NULL if B2gThreadCtx is optimized to 0 */
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(B2gThreadCtx);
+ SCFree(mpm_thread_ctx->ctx);
+ }
+}
+
+uint32_t B2gSearchWrap(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ return ctx ? ctx->Search(mpm_ctx, mpm_thread_ctx, pmq, buf, buflen) : 0;
+}
+
+uint32_t B2gSearchBNDMq(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+#ifdef B2G_COUNTERS
+ B2gThreadCtx *tctx = (B2gThreadCtx *)mpm_thread_ctx->ctx;
+#endif
+ uint32_t pos = ctx->m - B2G_Q + 1, matches = 0;
+ B2G_TYPE d;
+
+ //printf("\n");
+ //PrintRawDataFp(stdout, buf, buflen);
+
+ SCLogDebug("buflen %"PRIu16", ctx->m %"PRIu32", pos %"PRIu32"", buflen,
+ ctx->m, pos);
+
+ COUNT(tctx->stat_calls++);
+ COUNT(tctx->stat_m_total+=ctx->m);
+
+ if (buflen < ctx->m)
+ return 0;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (pos <= (uint32_t)(buflen - B2G_Q + 1)) {
+ uint16_t h = B2G_HASH16(u8_tolower(buf[pos - 1]),u8_tolower(buf[pos]));
+ d = ctx->B2G[h];
+
+ if (d != 0) {
+ COUNT(tctx->stat_d0++);
+ uint32_t j = pos;
+ uint32_t first = pos - (ctx->m - B2G_Q + 1);
+
+ do {
+ j = j - 1;
+
+ if (d >= (uint32_t)(1 << (ctx->m - 1))) {
+ if (j > first) pos = j;
+ else {
+ /* get our patterns from the hash */
+ h = B2G_HASH16(u8_tolower(buf[j + ctx->m - 2]),u8_tolower(buf[j + ctx->m - 1]));
+
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((buflen - j) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf+j, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+
+ SCLogDebug("Bloom: %p, buflen %" PRIu32 ", pos %" PRIu32 ", p_min_len %" PRIu32 "",
+ ctx->bloom[h], buflen, pos, ctx->pminlen[h]);
+ goto skip_loop;
+ }
+ }
+ }
+
+ B2gPattern *hi = ctx->hash[h], *thi;
+ for (thi = hi; thi != NULL; thi = thi->next) {
+ COUNT(tctx->stat_d0_hashloop++);
+ if ((buflen - j) < thi->len) {
+ continue;
+ }
+
+ if (thi->flags & MPM_PATTERN_FLAG_NOCASE) {
+
+ //if (memcmp_lowercase(thi->ci, buf+j, thi->len) == 0) {
+ if (SCMemcmpLowercase(thi->ci, buf+j, thi->len) == 0) {
+#ifdef PRINTMATCH
+ printf("CI Exact match: "); prt(p->ci, p->len); printf("\n");
+#endif
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id,
+ bitarray, thi->sids, thi->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (SCMemcmp(thi->cs, buf+j, thi->len) == 0) {
+ //if (memcmp(thi->cs, buf+j, thi->len) == 0) {
+#ifdef PRINTMATCH
+ printf("CS Exact match: "); prt(p->cs, p->len); printf("\n");
+#endif
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id,
+ bitarray, thi->sids, thi->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+skip_loop:
+ SCLogDebug("skipped");
+ //SCLogDebug("output at pos %" PRIu32 ": ", j); prt(buf + (j), ctx->m); printf("\n");
+ ;
+ }
+ }
+
+ if (j == 0) {
+ break;
+ }
+
+ h = B2G_HASH16(u8_tolower(buf[j - 1]),u8_tolower(buf[j]));
+ d = (d << 1) & ctx->B2G[h];
+ } while (d != 0);
+ }
+ COUNT(tctx->stat_num_shift++);
+ COUNT(tctx->stat_total_shift += (ctx->m - B2G_Q + 1));
+ pos = pos + ctx->m - B2G_Q + 1;
+
+ SCLogDebug("pos %"PRIu32"", pos);
+ }
+
+ SCLogDebug("matches %"PRIu32"", matches);
+ return matches;
+}
+
+uint32_t B2gSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+#ifdef B2G_COUNTERS
+ B2gThreadCtx *tctx = (B2gThreadCtx *)mpm_thread_ctx->ctx;
+#endif
+ uint32_t pos = 0, matches = 0;
+ B2G_TYPE d;
+ uint32_t j;
+
+ COUNT(tctx->stat_calls++);
+ COUNT(tctx->stat_m_total+=ctx->m);
+
+ if (buflen < ctx->m)
+ return 0;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (pos <= (buflen - ctx->m)) {
+ j = ctx->m - 1;
+ d = ~0;
+
+ do {
+ uint16_t h = B2G_HASH16(u8_tolower(buf[pos + j - 1]),u8_tolower(buf[pos + j]));
+ d = ((d << 1) & ctx->B2G[h]);
+ j = j - 1;
+ } while (d != 0 && j != 0);
+
+ /* (partial) match, move on to verification */
+ if (d != 0) {
+ COUNT(tctx->stat_d0++);
+ //printf("output at pos %" PRIu32 ": ", pos); prt(buf + pos, ctx->m); printf("\n");
+
+ /* get our patterns from the hash */
+ uint16_t h = B2G_HASH16(u8_tolower(buf[pos + ctx->m - 2]),u8_tolower(buf[pos + ctx->m - 1]));
+
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((buflen - pos) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf+pos, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+
+ //printf("Bloom: %p, buflen %" PRIu32 ", pos %" PRIu32 ", p_min_len %" PRIu32 "\n", ctx->bloom[h], buflen, pos, ctx->pminlen[h]);
+ goto skip_loop;
+ }
+ }
+ }
+
+ B2gPattern *hi = ctx->hash[h], *thi;
+ for (thi = hi; thi != NULL; thi = thi->next) {
+ COUNT(tctx->stat_d0_hashloop++);
+ //B2gPattern *p = ctx->parray[thi->idx];
+
+ if (buflen - pos < thi->len)
+ continue;
+
+ if (thi->flags & MPM_PATTERN_FLAG_NOCASE) {
+
+ if (SCMemcmpLowercase(thi->ci, buf+pos, thi->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id,
+ bitarray, thi->sids, thi->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (SCMemcmp(thi->cs, buf+pos, thi->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id,
+ bitarray, thi->sids, thi->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+skip_loop:
+ //pos = pos + ctx->s0;
+ pos = pos + 1;
+ } else {
+ COUNT(tctx->stat_num_shift++);
+ COUNT(tctx->stat_total_shift += (j + 1));
+
+ pos = pos + j + 1;
+ }
+ }
+
+ //printf("Total matches %" PRIu32 "\n", matches);
+ return matches;
+}
+
+#ifdef B2G_SEARCH2
+uint32_t B2gSearch2(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+ B2gPattern *p;
+ B2gPattern *thi, *hi;
+
+ if (buflen < 2)
+ return 0;
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ uint8_t h8 = u8_tolower(*buf);
+ hi = &ctx->hash1[h8];
+
+ if (hi->flags & MPM_PATTERN_ONE_BYTE) {
+ for (thi = hi; thi != NULL; thi = thi->next) {
+ p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (h8 == p->ci[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id,
+ bitarray, thi->sids, thi->sids_size);
+ }
+ } else {
+ if (*buf == p->cs[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id,
+ bitarray, thi->sids, thi->sids_size);
+ }
+ }
+ }
+ }
+
+ /* save one conversion by reusing h8 */
+ uint16_t h16 = B2G_HASH16(h8, u8_tolower(*(buf+1)));
+ hi = ctx->hash2[h16];
+
+ for (thi = hi; thi != NULL; thi = thi->next) {
+ p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (h8 == p->ci[0] && u8_tolower(*(buf+1)) == p->ci[1]) {
+ //printf("CI Exact match: "); prt(p->ci, p->len); printf(" in buf "); prt(buf, p->len);printf(" (B2gSearch1)\n");
+// for (em = p->em; em; em = em->next) {
+ if (MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size))
+ cnt++;
+// }
+ }
+ } else {
+ if (*buf == p->cs[0] && *(buf+1) == p->cs[1]) {
+ //printf("CS Exact match: "); prt(p->cs, p->len); printf(" in buf "); prt(buf, p->len);printf(" (B2gSearch1)\n");
+// for (em = p->em; em; em = em->next) {
+ if (MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size))
+ cnt++;
+// }
+ }
+ }
+ }
+ buf += 1;
+ }
+
+ //printf("B2gSearch2: after 2byte cnt %" PRIu32 "\n", cnt);
+ if (ctx->pat_x_cnt > 0) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ //printf("B2gSearch1: after 2+byte cnt %" PRIu32 "\n", cnt);
+ }
+ return cnt;
+}
+#endif
+
+uint32_t B2gSearch1(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ SCEnter();
+
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+// B2gPattern *p;
+ B2gPattern *thi, *hi;
+
+ if (buflen == 0)
+ SCReturnUInt(0);
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ uint8_t h = u8_tolower(*buf);
+ hi = &ctx->hash1[h];
+
+ for (thi = hi; thi != NULL; thi = thi->next) {
+ if (hi->flags & MPM_PATTERN_ONE_BYTE) {
+ if (thi->len != 1)
+ continue;
+
+ if (thi->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (u8_tolower(*buf) == thi->ci[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id, bitarray, thi->sids, thi->sids_size);
+ }
+ } else {
+ if (*buf == thi->cs[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, thi->id, bitarray, thi->sids, thi->sids_size);
+ }
+ }
+ }
+ }
+ buf += 1;
+ }
+
+ //printf("B2gSearch1: after 1byte cnt %" PRIu32 "\n", cnt);
+#ifdef B2G_SEARCH2
+ if (ctx->pat_2_cnt) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch2(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ //printf("B2gSearch1: after 2+byte cnt %" PRIu32 "\n", cnt);
+ } else
+#endif
+ if (ctx->pat_x_cnt) {
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ }
+ SCReturnUInt(cnt);
+}
+
+/*
+ * TESTS
+ */
+
+#ifdef UNITTESTS
+static int B2gTestInit01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->m == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 " ", ctx->m);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+#if 0
+static int B2gTestS0Init01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 " ", ctx->s0);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestS0Init02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"cdef", 4, 0, 0, 1, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ", ctx->s0);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestS0Init03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", ctx->s0);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestS0Init04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abab", 4, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ", ctx->s0);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestS0Init05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcab", 5, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ", ctx->s0);
+
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+#endif
+
+static int B2gTestSearch01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+/* test patterns longer than 'm'. M is 4 here. */
+static int B2gTestSearch04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+/* case insensitive test patterns longer than 'm'. M is 4 here. */
+static int B2gTestSearch05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch05a (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abCD", 4, 0, 0, 3, 0, 0); /* no match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"abcD", 4, 0, 0, 4, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"abCd", 4, 0, 0, 5, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 5)
+ result = 1;
+ else
+ printf("5 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch06 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch07 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch08 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch09 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch10 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ char *buf = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "abcdefgh"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)buf, strlen(buf));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch11 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2 /* 2 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyz", 26);
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2 /* 2 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyz", 26);
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch13 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCD", 30, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCD", 30);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCDE", 31, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCDE", 31);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCDEF", 32, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABCDEF", 32);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABC", 29, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyzABC", 29);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch17 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefghijklmnopqrstuvwxyzAB", 28, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghijklmnopqrstuvwxyzAB", 28);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch18 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde""fghij""klmno""pqrst""uvwxy""z", 26, 0, 0, 0, 0, 0); /* 1 match */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcde""fghij""klmno""pqrst""uvwxy""z", 26);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch19 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 0, 0, 0); /* 1 */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch20 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA", 32, 0, 0, 0, 0, 0); /* 1 */
+ //MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, 0, 0, 0, 0, 0); /* 1 */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 patterns */);
+
+ //uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32);
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AAAAA""AA", 32);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B2gTestSearch21 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0); /* 1 */
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AA", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ return result;
+}
+#endif /* UNITTESTS */
+
+#if 0
+static int B2gTestSearchXX (void)
+{
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B2G);
+ B2gCtx *ctx = (B2gCtx *)mpm_ctx.ctx;
+
+ FILE *fp = fopen("/usr/share/dict/words", "r");
+ if (fp == NULL)
+ exit(1);
+
+ char *word;
+ char line[128];
+ int w = 0;
+ int w_max = 4000;
+
+ while((word = fgets(line, sizeof(line), fp)) != NULL) {
+ word[strlen(word) - 1] = '\0';
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)word, strlen(word), 0, 0, (uint32_t)w, 0, 0);
+
+ w++;
+
+ if (w_max == w)
+ break;
+ }
+
+ B2gPreparePatterns(&mpm_ctx);
+ B2gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 patterns */);
+
+ char *text = "Yes this is a text, it is not very long. But, it is still sufficient for testing our search! "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "flkflkjjoijda893ur9r89h98hf9shflj;adm.,amnd,mna,mndabdayyugeq9e8u0q90-euajd;lsaldakljdlkajdl"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "we're adding a lot more text lines etc."
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "dlajd01438798749023749792739479ye9q8eu3291739847983274987e928u928eu98u3298eu982u938383888888 "
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "Bjdhfahflkahsf;phf[hfihasfkhsfkjhalhflkafljhfkhakhfkahfkahfkjhdkffkjhafkhafkjakjfhkjahf;aj;jh";
+ uint32_t len = strlen(text) - 1;
+
+ int i;
+ uint32_t cnt;
+ for (i = 0; i < 100; i++) {
+ cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)text, len);
+ }
+
+ printf("cnt %u ", cnt);
+
+ B2gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B2gDestroyCtx(&mpm_ctx);
+ fclose(fp);
+
+ return 1;
+}
+#endif
+
+void B2gRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("B2gTestInit01", B2gTestInit01, 1);
+/*
+ UtRegisterTest("B2gTestS0Init01", B2gTestS0Init01, 1);
+ UtRegisterTest("B2gTestS0Init02", B2gTestS0Init02, 1);
+ UtRegisterTest("B2gTestS0Init03", B2gTestS0Init03, 1);
+ UtRegisterTest("B2gTestS0Init04", B2gTestS0Init04, 1);
+ UtRegisterTest("B2gTestS0Init05", B2gTestS0Init05, 1);
+*/
+ UtRegisterTest("B2gTestSearch01", B2gTestSearch01, 1);
+ UtRegisterTest("B2gTestSearch02", B2gTestSearch02, 1);
+ UtRegisterTest("B2gTestSearch03", B2gTestSearch03, 1);
+ UtRegisterTest("B2gTestSearch04", B2gTestSearch04, 1);
+ UtRegisterTest("B2gTestSearch05", B2gTestSearch05, 1);
+ UtRegisterTest("B2gTestSearch05a", B2gTestSearch05a, 1);
+ UtRegisterTest("B2gTestSearch06", B2gTestSearch06, 1);
+ UtRegisterTest("B2gTestSearch07", B2gTestSearch07, 1);
+ UtRegisterTest("B2gTestSearch08", B2gTestSearch08, 1);
+ UtRegisterTest("B2gTestSearch09", B2gTestSearch09, 1);
+ UtRegisterTest("B2gTestSearch10", B2gTestSearch10, 1);
+ UtRegisterTest("B2gTestSearch11", B2gTestSearch11, 1);
+ UtRegisterTest("B2gTestSearch12", B2gTestSearch12, 1);
+ UtRegisterTest("B2gTestSearch13", B2gTestSearch13, 1);
+ UtRegisterTest("B2gTestSearch14", B2gTestSearch14, 1);
+ UtRegisterTest("B2gTestSearch15", B2gTestSearch15, 1);
+ UtRegisterTest("B2gTestSearch16", B2gTestSearch16, 1);
+ UtRegisterTest("B2gTestSearch17", B2gTestSearch17, 1);
+ UtRegisterTest("B2gTestSearch18", B2gTestSearch18, 1);
+ UtRegisterTest("B2gTestSearch19", B2gTestSearch19, 1);
+ UtRegisterTest("B2gTestSearch20", B2gTestSearch20, 1);
+ UtRegisterTest("B2gTestSearch21", B2gTestSearch21, 1);
+// UtRegisterTest("B2gTestSearchXX", B2gTestSearchXX, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-mpm-b2g.h b/framework/src/suricata/src/util-mpm-b2g.h
new file mode 100644
index 00000000..b3b59b09
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-b2g.h
@@ -0,0 +1,127 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_MPM_B2G_H__
+#define __UTIL_MPM_B2G_H__
+
+#include "util-mpm.h"
+#include "util-bloomfilter.h"
+
+#define B2G_HASHSHIFT_MAX 8
+#define B2G_HASHSHIFT_HIGHER 7
+#define B2G_HASHSHIFT_HIGH 6
+#define B2G_HASHSHIFT_MEDIUM 5
+#define B2G_HASHSHIFT_LOW 4
+#define B2G_HASHSHIFT_LOWEST 3
+
+//#define B2G_TYPE uint64_t
+#define B2G_TYPE uint32_t
+//#define B2G_TYPE uint16_t
+//#define B2G_TYPE uint8_t
+//#define B2G_WORD_SIZE 64
+#define B2G_WORD_SIZE 32
+//#define B2G_WORD_SIZE 16
+//#define B2G_WORD_SIZE 8
+
+#define B2G_Q 2
+
+#define B2G_SEARCHFUNC B2gSearchBNDMq
+//#define B2G_SEARCHFUNC B2gSearch
+
+//#define B2G_SEARCH2
+//#define B2G_COUNTERS
+
+typedef struct B2gPattern_ {
+ uint16_t len; /**< \todo we're limited to 32/64 byte lengths, uint8_t would be fine here */
+ uint8_t flags;
+ uint8_t pad0;
+ uint32_t id;
+ uint8_t *original_pat;
+ uint8_t *ci; /* case INsensitive */
+ uint8_t *cs; /* case sensitive */
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct B2gPattern_ *next;
+} B2gPattern;
+
+typedef struct B2gCtx_ {
+ B2G_TYPE *B2G;
+ B2G_TYPE m;
+ BloomFilter **bloom;
+ uint8_t *pminlen; /* array containing the minimal length
+ of the patters in a hash bucket. Used
+ for the BloomFilter. */
+ /* pattern arrays */
+ B2gPattern **parray;
+
+ uint16_t pat_1_cnt;
+#ifdef B2G_SEARCH2
+ uint16_t pat_2_cnt;
+#endif
+ uint16_t pat_x_cnt;
+
+ uint32_t hash_size;
+ B2gPattern **hash;
+ B2gPattern hash1[256];
+#ifdef B2G_SEARCH2
+ B2gHashItem **hash2;
+#endif
+
+ /* hash used during ctx initialization */
+ B2gPattern **init_hash;
+
+ uint8_t s0;
+
+ /* we store our own multi byte search func ptr here for B2gSearch1 */
+ uint32_t (*Search)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* we store our own multi byte search func ptr here for B2gSearch1 */
+ uint32_t (*MBSearch2)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+ uint32_t (*MBSearch)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+} B2gCtx;
+
+typedef struct B2gThreadCtx_ {
+#ifdef B2G_COUNTERS
+ uint32_t stat_pminlen_calls;
+ uint32_t stat_pminlen_total;
+ uint32_t stat_bloom_calls;
+ uint32_t stat_bloom_hits;
+ uint32_t stat_calls;
+ uint32_t stat_m_total;
+ uint32_t stat_d0;
+ uint32_t stat_d0_hashloop;
+ uint32_t stat_loop_match;
+ uint32_t stat_loop_no_match;
+ uint32_t stat_num_shift;
+ uint32_t stat_total_shift;
+#endif /* B2G_COUNTERS */
+} B2gThreadCtx;
+
+void MpmB2gRegister(void);
+
+
+#endif
+
diff --git a/framework/src/suricata/src/util-mpm-b3g.c b/framework/src/suricata/src/util-mpm-b3g.c
new file mode 100644
index 00000000..bc74bc20
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-b3g.c
@@ -0,0 +1,1807 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * 3 gram implementation of the (S)BNDMq pattern matching algorithm.
+ *
+ * Ideas:
+ * - B3g does a full match in the search of up to 'm' characters,
+ * in case of a case insensitive search we could say it's match if
+ * the pattern is of len 'm' or just compare the rest of the chars.
+ *
+ * \todo Try to get the S0 calculation right.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-bloomfilter.h"
+#include "util-mpm-b3g.h"
+#include "util-unittest.h"
+#include "conf.h"
+#include "util-debug.h"
+#include "util-memcpy.h"
+
+#define INIT_HASH_SIZE 65536
+
+#ifdef B3G_COUNTERS
+#define COUNT(counter) \
+ (counter)
+#else
+#define COUNT(counter)
+#endif /* B3G_COUNTERS */
+
+static uint32_t b3g_hash_size = 0;
+static uint32_t b3g_bloom_size = 0;
+static uint8_t b3g_hash_shift = 0;
+static uint8_t b3g_hash_shift2 = 0;
+static void *b3g_func;
+
+#define B3G_HASH(a,b,c) (((a) << b3g_hash_shift) | (b) << (b3g_hash_shift2) |(c))
+
+void B3gInitCtx (MpmCtx *);
+void B3gThreadInitCtx(MpmCtx *, MpmThreadCtx *, uint32_t);
+void B3gDestroyCtx(MpmCtx *);
+void B3gThreadDestroyCtx(MpmCtx *, MpmThreadCtx *);
+int B3gAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+int B3gAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+int B3gPreparePatterns(MpmCtx *);
+uint32_t B3gSearchWrap(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+uint32_t B3gSearch1(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+uint32_t B3gSearch2(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+uint32_t B3gSearch12(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+uint32_t B3gSearch(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+uint32_t B3gSearchBNDMq(MpmCtx *, MpmThreadCtx *, PatternMatcherQueue *, uint8_t *, uint16_t);
+void B3gPrintInfo(MpmCtx *);
+void B3gPrintSearchStats(MpmThreadCtx *);
+void B3gRegisterTests(void);
+
+/** \todo XXX Unused??? */
+#if 0
+static void prt (uint8_t *buf, uint16_t buflen)
+{
+ uint16_t i;
+
+ for (i = 0; i < buflen; i++) {
+ if (isprint(buf[i])) printf("%c", buf[i]);
+ else printf("\\x%" PRIX32, buf[i]);
+ }
+ //printf("\n");
+}
+#endif
+
+void B3gPrintInfo(MpmCtx *mpm_ctx)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+
+ printf("MPM B3g Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeofs:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" B3gCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(B3gCtx));
+ printf(" B3gPattern %" PRIuMAX "\n", (uintmax_t)sizeof(B3gPattern));
+ printf(" B3gHashItem %" PRIuMAX "\n", (uintmax_t)sizeof(B3gHashItem));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Hash size: %" PRIu32 "\n", ctx->hash_size);
+ printf("\n");
+}
+
+static inline B3gPattern *B3gAllocPattern(MpmCtx *mpm_ctx)
+{
+ B3gPattern *p = SCMalloc(sizeof(B3gPattern));
+ if (unlikely(p == NULL))
+ return NULL;
+ memset(p,0,sizeof(B3gPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B3gPattern);
+ return p;
+}
+
+static inline B3gHashItem *
+B3gAllocHashItem(MpmCtx *mpm_ctx)
+{
+ B3gHashItem *hi = SCMalloc(sizeof(B3gHashItem));
+ if (unlikely(hi == NULL))
+ return NULL;
+ memset(hi,0,sizeof(B3gHashItem));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B3gHashItem);
+ return hi;
+}
+
+static void B3gHashFree(MpmCtx *mpm_ctx, B3gHashItem *hi)
+{
+ if (hi == NULL)
+ return;
+
+ B3gHashItem *t = hi->nxt;
+ B3gHashFree(mpm_ctx, t);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B3gHashItem);
+ SCFree(hi);
+}
+
+/*
+ * INIT HASH START
+ */
+static inline uint32_t B3gInitHash(B3gPattern *p)
+{
+ uint32_t hash = p->len * p->cs[0];
+ if (p->len > 1)
+ hash += p->cs[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline uint32_t B3gInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int B3gInitHashAdd(B3gCtx *ctx, B3gPattern *p)
+{
+ uint32_t hash = B3gInitHash(p);
+
+ //printf("B3gInitHashAdd: %" PRIu32 "\n", hash);
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ //printf("B3gInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+ return 0;
+ }
+
+ B3gPattern *tt = NULL;
+ B3gPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+ //printf("B3gInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+
+ return 0;
+}
+
+static inline int B3gCmpPattern(B3gPattern *p, uint8_t *pat, uint16_t patlen, char flags);
+
+static inline B3gPattern *B3gInitHashLookup(B3gCtx *ctx, uint8_t *pat, uint16_t patlen, char flags)
+{
+ uint32_t hash = B3gInitHashRaw(pat,patlen);
+
+ //printf("B3gInitHashLookup: %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+
+ if (ctx->init_hash[hash] == NULL) {
+ return NULL;
+ }
+
+ B3gPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (B3gCmpPattern(t,pat,patlen,flags) == 1)
+ return t;
+ }
+
+ return NULL;
+}
+
+static inline int B3gCmpPattern(B3gPattern *p, uint8_t *pat, uint16_t patlen, char flags)
+{
+ if (p->len != patlen)
+ return 0;
+
+ if (p->flags != flags)
+ return 0;
+
+ if (memcmp(p->cs, pat, patlen) != 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * INIT HASH END
+ */
+
+void B3gFreePattern(MpmCtx *mpm_ctx, B3gPattern *p)
+{
+ if (p && p->cs && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->ci) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->sids) {
+ SCFree(p->sids);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->sids_size * sizeof(SigIntId);
+ }
+
+ if (p) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B3gPattern);
+ }
+}
+
+/* B3gAddPattern
+ *
+ * pat: ptr to the pattern
+ * patlen: length of the pattern
+ * nocase: nocase flag: 1 enabled, 0 disable
+ * pid: pattern id
+ * sid: signature id (internal id)
+ */
+static int B3gAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, uint32_t sid, uint8_t flags)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+
+ if (patlen == 0)
+ return 0;
+
+ /* get a memory piece */
+ B3gPattern *p = B3gInitHashLookup(ctx, pat, patlen, flags);
+ if (p == NULL) {
+ p = B3gAllocPattern(mpm_ctx);
+ if (p == NULL)
+ goto error;
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ /* setup the case insensitive part of the pattern */
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci,pat,p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ //printf("B3gAddPattern: ci \""); prt(p->ci,p->len);
+ //printf("\" cs \""); prt(p->cs,p->len);
+ //printf("\" prefix_ci %" PRIu32 ", prefix_cs %" PRIu32 "\n", p->prefix_ci, p->prefix_cs);
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SigIntId);
+
+ /* put in the pattern hash */
+ B3gInitHashAdd(ctx, p);
+
+ if (mpm_ctx->pattern_cnt == 65535) {
+ printf("Max search words reached\n");
+ exit(1);
+ }
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen) mpm_ctx->maxlen = patlen;
+ if (mpm_ctx->minlen == 0) mpm_ctx->minlen = patlen;
+ else if (mpm_ctx->minlen > patlen) mpm_ctx->minlen = patlen;
+ } else {
+ /* Multiple sids for the same pid, so keep an array of sids. */
+
+ /* TODO figure out how we can be called multiple times for the
+ * same CTX with the same sid */
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ mpm_ctx->memory_size += sizeof(SigIntId);
+ }
+ }
+
+ return 0;
+
+error:
+ B3gFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+int B3gAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return B3gAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+int B3gAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ return B3gAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+static uint32_t B3gBloomHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint16_t i;
+ uint32_t hash = (uint32_t)u8_tolower(*d);
+
+ for (i = 1; i < datalen; i++) {
+ d++;
+ hash += (u8_tolower(*d)) ^ i;
+ }
+ hash <<= (iter+1);
+
+ hash %= hash_size;
+ return hash;
+}
+
+static void B3gPrepareHash(MpmCtx *mpm_ctx)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ uint16_t i;
+ uint16_t idx = 0;
+ uint8_t idx8 = 0;
+
+ ctx->hash = (B3gHashItem **)SCMalloc(sizeof(B3gHashItem *) * ctx->hash_size);
+ if (ctx->hash == NULL)
+ goto error;
+ memset(ctx->hash, 0, sizeof(B3gHashItem *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B3gHashItem *) * ctx->hash_size);
+
+ /* 2 byte pattern hash */
+ ctx->hash2 = (B3gHashItem **)SCMalloc(sizeof(B3gHashItem *) * ctx->hash_size);
+ if (ctx->hash2 == NULL)
+ goto error;
+ memset(ctx->hash2, 0, sizeof(B3gHashItem *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B3gHashItem *) * ctx->hash_size);
+
+ /* alloc the pminlen array */
+ ctx->pminlen = (uint8_t *)SCMalloc(sizeof(uint8_t) * ctx->hash_size);
+ if (ctx->pminlen == NULL)
+ goto error;
+ memset(ctx->pminlen, 0, sizeof(uint8_t) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(uint8_t) * ctx->hash_size);
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++)
+ {
+ if(ctx->parray[i]->len == 1) {
+ idx8 = (uint8_t)ctx->parray[i]->ci[0];
+ if (ctx->hash1[idx8].flags == 0) {
+ ctx->hash1[idx8].idx = i;
+ ctx->hash1[idx8].flags |= 0x01;
+ } else {
+ B3gHashItem *hi = B3gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ /* Append this HashItem to the list */
+ B3gHashItem *thi = &ctx->hash1[idx8];
+ while (thi->nxt) thi = thi->nxt;
+ thi->nxt = hi;
+ }
+ ctx->pat_1_cnt++;
+ } else if(ctx->parray[i]->len == 2) {
+ idx = (uint16_t)(ctx->parray[i]->ci[0] << b3g_hash_shift | ctx->parray[i]->ci[1]);
+ if (ctx->hash2[idx] == NULL) {
+ B3gHashItem *hi = B3gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ ctx->hash2[idx] = hi;
+ } else {
+ B3gHashItem *hi = B3gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ /* Append this HashItem to the list */
+ B3gHashItem *thi = ctx->hash2[idx];
+ while (thi->nxt) thi = thi->nxt;
+ thi->nxt = hi;
+ }
+ ctx->pat_2_cnt++;
+ } else {
+ idx = B3G_HASH(ctx->parray[i]->ci[ctx->m - 3], ctx->parray[i]->ci[ctx->m - 2], ctx->parray[i]->ci[ctx->m - 1]);
+ //printf("idx %" PRIu32 ", %c.%c.%c\n", idx, ctx->parray[i]->ci[ctx->m - 3], ctx->parray[i]->ci[ctx->m - 2], ctx->parray[i]->ci[ctx->m - 1]);
+
+ if (ctx->hash[idx] == NULL) {
+ B3gHashItem *hi = B3gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ ctx->hash[idx] = hi;
+ } else {
+ B3gHashItem *hi = B3gAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ if (ctx->parray[i]->len < ctx->pminlen[idx])
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ /* Append this HashItem to the list */
+ B3gHashItem *thi = ctx->hash[idx];
+ while (thi->nxt) thi = thi->nxt;
+ thi->nxt = hi;
+ }
+ ctx->pat_x_cnt++;
+ }
+ }
+
+ /* alloc the bloom array */
+ ctx->bloom = (BloomFilter **)SCMalloc(sizeof(BloomFilter *) * ctx->hash_size);
+ if (ctx->bloom == NULL)
+ goto error;
+ memset(ctx->bloom, 0, sizeof(BloomFilter *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(BloomFilter *) * ctx->hash_size);
+
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ B3gHashItem *hi = ctx->hash[h];
+ if (hi == NULL)
+ continue;
+
+ ctx->bloom[h] = BloomFilterInit(b3g_bloom_size, 2, B3gBloomHash);
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt += BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size += BloomFilterMemorySize(ctx->bloom[h]);
+
+ if (ctx->pminlen[h] > 8)
+ ctx->pminlen[h] = 8;
+
+ B3gHashItem *thi = hi;
+ do {
+ BloomFilterAdd(ctx->bloom[h], ctx->parray[thi->idx]->ci, ctx->pminlen[h]);
+ thi = thi->nxt;
+ } while (thi != NULL);
+ }
+
+ return;
+error:
+ return;
+}
+
+int B3gBuildMatchArray(MpmCtx *mpm_ctx)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+
+ ctx->B3G = SCMalloc(sizeof(B3G_TYPE) * ctx->hash_size);
+ if (ctx->B3G == NULL)
+ return -1;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(B3G_TYPE) * ctx->hash_size);
+
+ memset(ctx->B3G,0, b3g_hash_size * sizeof(B3G_TYPE));
+
+ uint32_t j;
+ uint32_t a;
+
+ /* fill the match array */
+ for (j = 0; j <= (ctx->m - B3G_Q); j++) {
+ for (a = 0; a < mpm_ctx->pattern_cnt; a++) {
+ if (ctx->parray[a]->len < ctx->m)
+ continue;
+
+ uint16_t h = B3G_HASH(u8_tolower(ctx->parray[a]->ci[j]),u8_tolower(ctx->parray[a]->ci[j+1]), u8_tolower(ctx->parray[a]->ci[j+2]));
+//printf("B3gBuildMatchArray: h %" PRIu32 ", %c.%c.%c\n", h, u8_tolower(ctx->parray[a]->ci[j]),u8_tolower(ctx->parray[a]->ci[j+1]), u8_tolower(ctx->parray[a]->ci[j+2]));
+ ctx->B3G[h] = ctx->B3G[h] | (1 << (ctx->m - j));
+ }
+ }
+
+ ctx->s0 = 1;
+ return 0;
+}
+
+int B3gPreparePatterns(MpmCtx *mpm_ctx)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+
+ /* alloc the pattern array */
+ ctx->parray = (B3gPattern **)SCMalloc(mpm_ctx->pattern_cnt * sizeof(B3gPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(B3gPattern *));
+ //printf("mpm_ctx %p, parray %p\n", mpm_ctx,ctx->parray);
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(B3gPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ B3gPattern *node = ctx->init_hash[i], *nnode = NULL;
+ for ( ; node != NULL; ) {
+ nnode = node->next;
+ node->next = NULL;
+
+ ctx->parray[p] = node;
+
+ p++;
+ node = nnode;
+ }
+ }
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* set 'm' to the smallest pattern size */
+ ctx->m = mpm_ctx->minlen;
+
+ /* make sure 'm' stays in bounds
+ m can be max WORD_SIZE - 1 */
+ if (ctx->m >= B3G_WORD_SIZE) {
+ ctx->m = B3G_WORD_SIZE - 1;
+ }
+ if (ctx->m < 3) ctx->m = 3;
+
+
+ ctx->hash_size = b3g_hash_size;
+ B3gPrepareHash(mpm_ctx);
+ B3gBuildMatchArray(mpm_ctx);
+
+ if (ctx->pat_1_cnt) {
+ ctx->Search = B3gSearch1;
+ if (ctx->pat_2_cnt) {
+ ctx->Search = B3gSearch12;
+ ctx->MBSearch = b3g_func;
+ }
+ ctx->MBSearch = b3g_func;
+ } else if (ctx->pat_2_cnt) {
+ ctx->Search = B3gSearch2;
+ ctx->MBSearch = b3g_func;
+ }
+
+
+ return 0;
+error:
+ return -1;
+}
+
+void B3gPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+#ifdef B3G_COUNTERS
+ B3gThreadCtx *tctx = (B3gThreadCtx *)mpm_thread_ctx->ctx;
+
+ printf("B3g Thread Search stats (tctx %p)\n", tctx);
+ printf("Total calls: %" PRIu32 "\n", tctx->stat_calls);
+ printf("Avg m/search: %0.2f\n", tctx->stat_calls ? (float)((float)tctx->stat_m_total / (float)tctx->stat_calls) : 0);
+ printf("D != 0 (possible match): %" PRIu32 "\n", tctx->stat_d0);
+ printf("Avg hash items per bucket %0.2f (%" PRIu32 ")\n", tctx->stat_d0 ? (float)((float)tctx->stat_d0_hashloop / (float)tctx->stat_d0) : 0, tctx->stat_d0_hashloop);
+ printf("Loop match: %" PRIu32 "\n", tctx->stat_loop_match);
+ printf("Loop no match: %" PRIu32 "\n", tctx->stat_loop_no_match);
+ printf("Num shifts: %" PRIu32 "\n", tctx->stat_num_shift);
+ printf("Total shifts: %" PRIu32 "\n", tctx->stat_total_shift);
+ printf("Avg shifts: %0.2f\n", tctx->stat_num_shift ? (float)((float)tctx->stat_total_shift / (float)tctx->stat_num_shift) : 0);
+ printf("Total BloomFilter checks: %" PRIu32 "\n", tctx->stat_bloom_calls);
+ printf("BloomFilter hits: %0.4f%% (%" PRIu32 ")\n", tctx->stat_bloom_calls ? (float)((float)((float)tctx->stat_bloom_hits / (float)tctx->stat_bloom_calls)*(float)100) : 0, tctx->stat_bloom_hits);
+ printf("Avg pminlen: %0.2f\n\n", tctx->stat_pminlen_calls ? (float)((float)tctx->stat_pminlen_total / (float)tctx->stat_pminlen_calls) : 0);
+#endif /* B3G_COUNTERS */
+}
+
+static inline int
+memcmp_lowercase(uint8_t *s1, uint8_t *s2, uint16_t n)
+{
+ size_t i;
+
+ /* check backwards because we already tested the first
+ * 2 to 4 chars. This way we are more likely to detect
+ * a miss and thus speed up a little... */
+ for (i = n - 1; i; i--) {
+ if (u8_tolower(*(s2+i)) != s1[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Function to get the user defined values for b3g algorithm from the
+ * config file 'suricata.yaml'
+ */
+void B3gGetConfig()
+{
+ ConfNode *b3g_conf;
+ const char *hash_val = NULL;
+ const char *bloom_val = NULL;
+ const char *algo = NULL;
+
+ /* init defaults */
+ b3g_hash_size = HASHSIZE_LOW;
+ b3g_bloom_size = BLOOMSIZE_MEDIUM;
+ b3g_func = B3G_SEARCHFUNC;
+
+ ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ if (pm != NULL) {
+
+ TAILQ_FOREACH(b3g_conf, &pm->head, next) {
+ if (strncmp(b3g_conf->val, "b3g", 3) == 0) {
+ algo = ConfNodeLookupChildValue(b3g_conf->head.tqh_first,
+ "algo");
+ hash_val = ConfNodeLookupChildValue(b3g_conf->head.tqh_first,
+ "hash_size");
+ bloom_val = ConfNodeLookupChildValue(b3g_conf->head.tqh_first,
+ "bf_size");
+
+ if (algo != NULL) {
+ if (strcmp(algo, "B3gSearch") == 0) {
+ b3g_func = B3gSearch;
+ } else if (strcmp(algo, "B3gSearchBNDMq") == 0) {
+ b3g_func = B3gSearchBNDMq;
+ }
+ }
+
+ if (hash_val != NULL) {
+ b3g_hash_size = MpmGetHashSize(hash_val);
+ switch (b3g_hash_size) {
+ case HASHSIZE_LOWEST:
+ b3g_hash_shift = B3G_HASHSHIFT_LOWEST;
+ b3g_hash_shift2 = B3G_HASHSHIFT_LOWEST2;
+ break;
+ case HASHSIZE_LOW:
+ b3g_hash_shift = B3G_HASHSHIFT_LOW;
+ b3g_hash_shift2 = B3G_HASHSHIFT_LOW2;
+ break;
+ case HASHSIZE_MEDIUM:
+ b3g_hash_shift = B3G_HASHSHIFT_MEDIUM;
+ b3g_hash_shift2 = B3G_HASHSHIFT_MEDIUM2;
+ break;
+ case HASHSIZE_HIGH:
+ b3g_hash_shift = B3G_HASHSHIFT_HIGH;
+ b3g_hash_shift2 = B3G_HASHSHIFT_HIGH2;
+ break;
+ case HASHSIZE_HIGHER:
+ b3g_hash_shift = B3G_HASHSHIFT_HIGHER;
+ b3g_hash_shift2 = B3G_HASHSHIFT_HIGHER2;
+ break;
+ case HASHSIZE_MAX:
+ b3g_hash_shift = B3G_HASHSHIFT_MAX;
+ b3g_hash_shift2 = B3G_HASHSHIFT_MAX2;
+ break;
+ }
+ }
+
+ if (bloom_val != NULL)
+ b3g_bloom_size = MpmGetBloomSize(bloom_val);
+
+ SCLogDebug("hash size is %"PRIu32" and bloom size is %"PRIu32"",
+ b3g_hash_size, b3g_bloom_size);
+ }
+ }
+ }
+}
+
+void B3gInitCtx (MpmCtx *mpm_ctx)
+{
+ //printf("B3gInitCtx: mpm_ctx %p\n", mpm_ctx);
+
+ mpm_ctx->ctx = SCMalloc(sizeof(B3gCtx));
+ if (mpm_ctx->ctx == NULL)
+ return;
+
+ memset(mpm_ctx->ctx, 0, sizeof(B3gCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(B3gCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(B3gPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL)
+ return;
+
+ memset(ctx->init_hash, 0, sizeof(B3gPattern *) * INIT_HASH_SIZE);
+
+ /* Initialize the defaults value from the config file. The given check make
+ sure that we query config file only once for config values */
+ if (b3g_hash_size == 0)
+ B3gGetConfig();
+
+ /* init default */
+ ctx->Search = b3g_func;
+}
+
+void B3gDestroyCtx(MpmCtx *mpm_ctx)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash) {
+ SCFree(ctx->init_hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(B3gPattern *));
+ }
+
+ if (ctx->parray) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ B3gFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(B3gPattern));
+ }
+
+ if (ctx->B3G) {
+ SCFree(ctx->B3G);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(B3G_TYPE) * ctx->hash_size);
+ }
+
+ if (ctx->bloom) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt -= BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size -= BloomFilterMemorySize(ctx->bloom[h]);
+
+ BloomFilterFree(ctx->bloom[h]);
+ }
+
+ SCFree(ctx->bloom);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(BloomFilter *) * ctx->hash_size);
+ }
+
+ if (ctx->hash) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->hash[h] == NULL)
+ continue;
+
+ B3gHashFree(mpm_ctx, ctx->hash[h]);
+ }
+
+ SCFree(ctx->hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(B3gHashItem) * ctx->hash_size);
+ }
+ if (ctx->hash2) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->hash2[h] == NULL)
+ continue;
+
+ B3gHashFree(mpm_ctx, ctx->hash2[h]);
+ }
+
+ SCFree(ctx->hash2);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(B3gHashItem) * ctx->hash_size);
+ }
+
+ if (ctx->pminlen) {
+ SCFree(ctx->pminlen);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(uint8_t) * ctx->hash_size);
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(B3gCtx);
+}
+
+void B3gThreadInitCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+ if (sizeof(B3gThreadCtx) > 0) { /* size can be 0 when optimized */
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(B3gThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL)
+ return;
+
+ memset(mpm_thread_ctx->ctx, 0, sizeof(B3gThreadCtx));
+
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(B3gThreadCtx);
+ }
+}
+
+void B3gThreadDestroyCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ B3gThreadCtx *ctx = (B3gThreadCtx *)mpm_thread_ctx->ctx;
+
+ B3gPrintSearchStats(mpm_thread_ctx);
+
+ if (ctx != NULL) { /* can be NULL when optimized */
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(B3gThreadCtx);
+ SCFree(mpm_thread_ctx->ctx);
+ }
+}
+
+inline uint32_t B3gSearchWrap(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ return ctx->Search(mpm_ctx, mpm_thread_ctx, pmq, buf, buflen);
+}
+
+uint32_t B3gSearchBNDMq(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+#ifdef B3G_COUNTERS
+ B3gThreadCtx *tctx = (B3gThreadCtx *)mpm_thread_ctx->ctx;
+#endif
+ uint32_t pos = ctx->m - B3G_Q + 1, matches = 0;
+ B3G_TYPE d;
+
+ COUNT(tctx->stat_calls++);
+ COUNT(tctx->stat_m_total+=ctx->m);
+
+ if (buflen < ctx->m)
+ return 0;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (pos <= (uint32_t)(buflen - B3G_Q + 1)) {
+ uint16_t h = B3G_HASH(u8_tolower(buf[pos - 1]), u8_tolower(buf[pos]),u8_tolower(buf[pos + 1]));
+ d = ctx->B3G[h];
+
+ if (d != 0) {
+ COUNT(tctx->stat_d0++);
+ uint32_t j = pos;
+ uint32_t first = pos - (ctx->m - B3G_Q + 1);
+
+ do {
+ j = j - 1;
+ if (d >= (uint32_t)(1 << (ctx->m - 1))) {
+ if (j > first) pos = j;
+ else {
+ /* get our patterns from the hash */
+ h = B3G_HASH(u8_tolower(buf[j + ctx->m - 3]), u8_tolower(buf[j + ctx->m - 2]),u8_tolower(buf[j + ctx->m - 1]));
+
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((buflen - j) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf+j, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+
+ //printf("Bloom: %p, buflen %" PRIu32 ", pos %" PRIu32 ", p_min_len %" PRIu32 "\n", ctx->bloom[h], buflen, pos, ctx->pminlen[h]);
+ goto skip_loop;
+ }
+ }
+ }
+
+ B3gHashItem *hi = ctx->hash[h], *thi;
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ COUNT(tctx->stat_d0_hashloop++);
+ B3gPattern *p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (buflen - j < p->len)
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf+j, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (buflen - j < p->len)
+ continue;
+
+ if (memcmp(p->cs, buf+j, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+skip_loop:
+ //printf("output at pos %" PRIu32 ": ", j); prt(buf + (j), ctx->m); printf("\n");
+ ; // gcc doesn't like the goto label without this :-S
+ }
+ }
+
+ if (j == 0)
+ break;
+
+ h = B3G_HASH(u8_tolower(buf[j - 1]), u8_tolower(buf[j - 0]),u8_tolower(buf[j+1]));
+ d = (d << 1) & ctx->B3G[h];
+ } while (d != 0);
+ }
+ COUNT(tctx->stat_num_shift++);
+ COUNT(tctx->stat_total_shift += (ctx->m - B3G_Q + 1));
+ pos = pos + ctx->m - B3G_Q + 1;
+ }
+ return matches;
+}
+
+uint32_t B3gSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+#ifdef B3G_COUNTERS
+ B3gThreadCtx *tctx = (B3gThreadCtx *)mpm_thread_ctx->ctx;
+#endif
+ uint32_t pos = 0, matches = 0;
+ B3G_TYPE d;
+ uint32_t j;
+
+ COUNT(tctx->stat_calls++);
+ COUNT(tctx->stat_m_total+=ctx->m);
+
+ if (buflen < ctx->m)
+ return 0;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (pos <= (buflen - ctx->m)) {
+ j = ctx->m - 2;
+ d = ~0;
+
+ do {
+ uint16_t h = B3G_HASH(u8_tolower(buf[pos + j - 1]), u8_tolower(buf[pos + j - 0]),u8_tolower(buf[pos + j + 1]));
+ d = ((d << 1) & ctx->B3G[h]);
+ j = j - 1;
+ } while (d != 0 && j != 0);
+
+ /* (partial) match, move on to verification */
+ if (d != 0) {
+ COUNT(tctx->stat_d0++);
+ //printf("output at pos %" PRIu32 ": ", pos); prt(buf + pos, ctx->m); printf("\n");
+
+ /* get our patterns from the hash */
+ uint16_t h = B3G_HASH(u8_tolower(buf[pos + ctx->m - 3]), u8_tolower(buf[pos + ctx->m - 2]),u8_tolower(buf[pos + ctx->m - 1]));
+
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((buflen - pos) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf+pos, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+
+ //printf("Bloom: %p, buflen %" PRIu32 ", pos %" PRIu32 ", p_min_len %" PRIu32 "\n", ctx->bloom[h], buflen, pos, ctx->pminlen[h]);
+ goto skip_loop;
+ }
+ }
+ }
+
+ B3gHashItem *hi = ctx->hash[h], *thi;
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ COUNT(tctx->stat_d0_hashloop++);
+ B3gPattern *p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (buflen - pos < p->len)
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf+pos, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (buflen - pos < p->len)
+ continue;
+
+ if (memcmp(p->cs, buf+pos, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ matches += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+skip_loop:
+ pos = pos + 1;
+ //pos = pos + ctx->s0;
+ } else {
+ COUNT(tctx->stat_num_shift++);
+ COUNT(tctx->stat_total_shift += (j + 1));
+
+ pos = pos + j + 1;
+ }
+ }
+
+ //printf("Total matches %" PRIu32 "\n", matches);
+ return matches;
+}
+
+uint32_t B3gSearch12(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+ B3gPattern *p;
+ B3gHashItem *thi, *hi;
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ uint8_t h8 = u8_tolower(*buf);
+ hi = &ctx->hash1[h8];
+
+ if (hi->flags & 0x01) {
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (h8 == p->ci[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ } else {
+ if (*buf == p->cs[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ }
+ }
+ }
+
+ if (buf != bufend) {
+ /* save one conversion by reusing h8 */
+ uint16_t h16 = (uint16_t)(h8 << b3g_hash_shift | u8_tolower(*(buf+1)));
+ hi = ctx->hash2[h16];
+
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (h8 == p->ci[0] && u8_tolower(*(buf+1)) == p->ci[1]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ } else {
+ if (*buf == p->cs[0] && *(buf+1) == p->cs[1]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ }
+ }
+ }
+ buf += 1;
+ }
+
+ //printf("B3gSearch12: after 1/2byte cnt %" PRIu32 "\n", cnt);
+ if (ctx->pat_x_cnt > 0) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ //printf("B3gSearch1: after 2+byte cnt %" PRIu32 "\n", cnt);
+ }
+ return cnt;
+}
+
+uint32_t B3gSearch2(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+ B3gPattern *p;
+ B3gHashItem *thi, *hi;
+
+ if (buflen < 2)
+ return 0;
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ uint16_t h = u8_tolower(*buf) << b3g_hash_shift | u8_tolower(*(buf+1));
+ hi = ctx->hash2[h];
+
+ if (hi != NULL) {
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ if (p->len != 2)
+ continue;
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (u8_tolower(*buf) == p->ci[0] && u8_tolower(*(buf+1)) == p->ci[1]) {
+ //printf("CI Exact match: "); prt(p->ci, p->len); printf(" in buf "); prt(buf, p->len);printf(" (B3gSearch1)\n");
+ if (MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size))
+ cnt++;
+ }
+ } else {
+ if (*buf == p->cs[0] && *(buf+1) == p->cs[1]) {
+ //printf("CS Exact match: "); prt(p->cs, p->len); printf(" in buf "); prt(buf, p->len);printf(" (B3gSearch1)\n");
+ if (MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size))
+ cnt++;
+ }
+ }
+ }
+ }
+ buf += 1;
+ }
+
+ //printf("B3gSearch2: after 2byte cnt %" PRIu32 "\n", cnt);
+ if (ctx->pat_x_cnt) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ //printf("B3gSearch1: after 2+byte cnt %" PRIu32 "\n", cnt);
+ }
+ return cnt;
+}
+uint32_t B3gSearch1(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+ B3gPattern *p;
+ B3gHashItem *thi, *hi;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ uint8_t h = u8_tolower(*buf);
+ hi = &ctx->hash1[h];
+
+ if (hi->flags & 0x01) {
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ if (p->len != 1)
+ continue;
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (u8_tolower(*buf) == p->ci[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ } else {
+ if (*buf == p->cs[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ }
+ }
+ }
+ buf += 1;
+ }
+
+ if (ctx->pat_2_cnt) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch2(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ } else if (ctx->pat_x_cnt) {
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ }
+ return cnt;
+}
+
+void MpmB3gRegister (void)
+{
+ mpm_table[MPM_B3G].name = "b3g";
+ mpm_table[MPM_B3G].max_pattern_length = B3G_WORD_SIZE;
+ mpm_table[MPM_B3G].InitCtx = B3gInitCtx;
+ mpm_table[MPM_B3G].InitThreadCtx = B3gThreadInitCtx;
+ mpm_table[MPM_B3G].DestroyCtx = B3gDestroyCtx;
+ mpm_table[MPM_B3G].DestroyThreadCtx = B3gThreadDestroyCtx;
+ mpm_table[MPM_B3G].AddPattern = B3gAddPatternCS;
+ mpm_table[MPM_B3G].AddPatternNocase = B3gAddPatternCI;
+ mpm_table[MPM_B3G].Prepare = B3gPreparePatterns;
+ mpm_table[MPM_B3G].Search = B3gSearchWrap;
+ mpm_table[MPM_B3G].Cleanup = NULL;
+ mpm_table[MPM_B3G].PrintCtx = B3gPrintInfo;
+ mpm_table[MPM_B3G].PrintThreadCtx = B3gPrintSearchStats;
+ mpm_table[MPM_B3G].RegisterUnittests = B3gRegisterTests;
+}
+
+/*
+ * TESTS
+ */
+
+#ifdef UNITTESTS
+static int B3gTestInit01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->m == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 " ", ctx->m);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+#if 0
+static int B3gTestS0Init01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 " ", ctx->s0);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestS0Init02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"cdef", 4, 0, 0, 1, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ", ctx->s0);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestS0Init03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", ctx->s0);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestS0Init04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abab", 4, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ", ctx->s0);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestS0Init05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcab", 5, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+
+ if (ctx->s0 == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ", ctx->s0);
+
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+#endif
+
+static int B3gTestSearch01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+/* test patterns longer than 'm'. M is 4 here. */
+static int B3gTestSearch04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+/* case insensitive test patterns longer than 'm'. M is 4 here. */
+static int B3gTestSearch05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0); /* 1 match */
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3 /* 3 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch06 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch07 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch08 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"a", 1);
+
+ if (cnt == 0)
+ result = 1;
+ else
+ printf("0 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch09 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch10 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1 /* 1 pattern */);
+
+ char *input = "012345679012345679012345679012345679012345679012345679"
+ "012345679012345679012345679012345679abcdefgh0123456790"
+ "123456790123456790123456790123456790123456790123456790"
+ "12345679012345679012345679";
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)input, strlen(input));
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch11 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2 /* 2 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int B3gTestSearch12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_B3G);
+ B3gCtx *ctx = (B3gCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0); /* 1 match */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 0, 0, 0); /* 1 match */
+
+ B3gPreparePatterns(&mpm_ctx);
+ B3gThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2 /* 2 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefghjiklmnopqrstuvwxyz", 26);
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 " ",cnt);
+
+ B3gThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ B3gDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void B3gRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("B3gTestInit01", B3gTestInit01, 1);
+/*
+ UtRegisterTest("B3gTestS0Init01", B3gTestS0Init01, 1);
+ UtRegisterTest("B3gTestS0Init02", B3gTestS0Init02, 1);
+ UtRegisterTest("B3gTestS0Init03", B3gTestS0Init03, 1);
+ UtRegisterTest("B3gTestS0Init04", B3gTestS0Init04, 1);
+ UtRegisterTest("B3gTestS0Init05", B3gTestS0Init05, 1);
+*/
+ UtRegisterTest("B3gTestSearch01", B3gTestSearch01, 1);
+
+ UtRegisterTest("B3gTestSearch02", B3gTestSearch02, 1);
+ UtRegisterTest("B3gTestSearch03", B3gTestSearch03, 1);
+ UtRegisterTest("B3gTestSearch04", B3gTestSearch04, 1);
+ UtRegisterTest("B3gTestSearch05", B3gTestSearch05, 1);
+ UtRegisterTest("B3gTestSearch06", B3gTestSearch06, 1);
+ UtRegisterTest("B3gTestSearch07", B3gTestSearch07, 1);
+ UtRegisterTest("B3gTestSearch08", B3gTestSearch08, 1);
+ UtRegisterTest("B3gTestSearch09", B3gTestSearch09, 1);
+ UtRegisterTest("B3gTestSearch10", B3gTestSearch10, 1);
+ UtRegisterTest("B3gTestSearch11", B3gTestSearch11, 1);
+ UtRegisterTest("B3gTestSearch12", B3gTestSearch12, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-mpm-b3g.h b/framework/src/suricata/src/util-mpm-b3g.h
new file mode 100644
index 00000000..d35aa033
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-b3g.h
@@ -0,0 +1,130 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_MPM_B3G_H__
+#define __UTIL_MPM_B3G_H__
+
+#include "util-mpm.h"
+#include "util-bloomfilter.h"
+
+#define B3G_HASHSHIFT_MAX 8
+#define B3G_HASHSHIFT_MAX2 5
+#define B3G_HASHSHIFT_HIGHER 7
+#define B3G_HASHSHIFT_HIGHER2 4
+#define B3G_HASHSHIFT_HIGH 6
+#define B3G_HASHSHIFT_HIGH2 3
+#define B3G_HASHSHIFT_MEDIUM 5
+#define B3G_HASHSHIFT_MEDIUM2 2
+#define B3G_HASHSHIFT_LOW 4
+#define B3G_HASHSHIFT_LOW2 1
+#define B3G_HASHSHIFT_LOWEST 3
+#define B3G_HASHSHIFT_LOWEST2 1
+
+#define B3G_TYPE uint32_t
+//#define B3G_TYPE uint16_t
+//#define B3G_TYPE uint8_t
+//#define B3G_WORD_SIZE 16
+//#define B3G_WORD_SIZE 8
+#define B3G_WORD_SIZE 32
+
+#define B3G_Q 3
+
+//#define B3G_SEARCHFUNC B3gSearch
+#define B3G_SEARCHFUNC B3gSearchBNDMq
+
+//#define B3G_COUNTERS
+
+typedef struct B3gPattern_ {
+ uint8_t *cs; /* case sensitive */
+ uint8_t *ci; /* case INsensitive */
+ uint16_t len;
+ uint8_t flags;
+ uint32_t id;
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+ struct B3gPattern_ *next;
+
+} B3gPattern;
+
+typedef struct B3gHashItem_ {
+ uint8_t flags;
+ uint16_t idx;
+ struct B3gHashItem_ *nxt;
+} B3gHashItem;
+
+typedef struct B3gCtx_ {
+ /* hash used during ctx initialization */
+ B3gPattern **init_hash;
+
+ B3G_TYPE m;
+ B3G_TYPE *B3G;
+
+ uint8_t s0;
+
+ uint16_t pat_1_cnt;
+ uint16_t pat_2_cnt;
+ uint16_t pat_x_cnt;
+
+ uint32_t hash_size;
+ B3gHashItem **hash;
+ BloomFilter **bloom;
+ uint8_t *pminlen; /* array containing the minimal length
+ of the patters in a hash bucket. Used
+ for the BloomFilter. */
+ B3gHashItem hash1[256];
+ B3gHashItem **hash2;
+
+ uint32_t (*Search)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* we store our own multi byte search func ptr here for B3gSearch1 */
+ uint32_t (*MBSearch2)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+ uint32_t (*MBSearch)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* pattern arrays */
+ B3gPattern **parray;
+} B3gCtx;
+
+typedef struct B3gThreadCtx_ {
+#ifdef B3G_COUNTERS
+ uint32_t stat_pminlen_calls;
+ uint32_t stat_pminlen_total;
+ uint32_t stat_bloom_calls;
+ uint32_t stat_bloom_hits;
+ uint32_t stat_calls;
+ uint32_t stat_m_total;
+ uint32_t stat_d0;
+ uint32_t stat_d0_hashloop;
+ uint32_t stat_loop_match;
+ uint32_t stat_loop_no_match;
+ uint32_t stat_num_shift;
+ uint32_t stat_total_shift;
+#endif /* B3G_COUNTERS */
+} B3gThreadCtx;
+
+void MpmB3gRegister(void);
+
+#endif
+
diff --git a/framework/src/suricata/src/util-mpm-wumanber.c b/framework/src/suricata/src/util-mpm-wumanber.c
new file mode 100644
index 00000000..dc78cde2
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-wumanber.c
@@ -0,0 +1,3219 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implementation of the Wu-Manber pattern matching algorithm.
+ *
+ * Ideas:
+ * - the hash contains a list of patterns. Maybe we can 'train' the hash
+ * so the most common patterns always appear first in this list.
+ *
+ * \todo make hash1 a array of ptr and get rid of the flag field in the
+ * WmHashItem
+ * \todo remove exit() calls
+ * \todo only calc prefixci_buf for nocase patterns? -- would be in a
+ * loop though, so probably not a performance inprovement.
+ * \todo make sure runtime counters can be disabled (at compile time)
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-mpm.h"
+#include "util-mpm-wumanber.h"
+#include "conf.h"
+
+#include "util-unittest.h"
+#include "util-debug.h"
+
+#define INIT_HASH_SIZE 65535
+
+#define HASH16_SIZE 65536
+#define HASH16(a,b) (((a)<<8) | (b))
+#define HASH15_SIZE 32768
+#define HASH15(a,b) (((a)<<7) | (b))
+#define HASH14_SIZE 16384
+#define HASH14(a,b) (((a)<<6) | (b))
+#define HASH12_SIZE 4096
+#define HASH12(a,b) (((a)<<4) | (b))
+#define HASH9_SIZE 512
+#define HASH9(a,b) (((a)<<1) | (b))
+
+static uint32_t wm_hash_size = 0;
+static uint32_t wm_bloom_size = 0;
+
+void WmInitCtx (MpmCtx *mpm_ctx);
+void WmThreadInitCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t);
+void WmDestroyCtx(MpmCtx *mpm_ctx);
+void WmThreadDestroyCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx);
+int WmAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+int WmAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+int WmPreparePatterns(MpmCtx *mpm_ctx);
+uint32_t WmSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch1(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch2Hash9(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch2Hash12(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch2Hash14(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch2Hash15(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+uint32_t WmSearch2Hash16(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *, uint8_t *buf, uint16_t buflen);
+void WmPrintInfo(MpmCtx *mpm_ctx);
+void WmPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
+void WmRegisterTests(void);
+
+/* uppercase to lowercase conversion lookup table */
+static uint8_t lowercasetable[256];
+/* marco to do the actual lookup */
+#define wm_tolower(c) lowercasetable[(c)]
+
+#ifdef WUMANBER_COUNTERS
+#define COUNT(counter) \
+ (counter)
+#else
+#define COUNT(counter)
+#endif /* WUMANBER_COUNTERS */
+
+void prt (uint8_t *buf, uint16_t buflen)
+{
+ uint16_t i;
+
+ for (i = 0; i < buflen; i++) {
+ if (isprint(buf[i])) printf("%c", buf[i]);
+ else printf("\\x%" PRIX32, buf[i]);
+ }
+ //printf("\n");
+}
+
+void WmPrintInfo(MpmCtx *mpm_ctx)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+
+ printf("MPM WuManber Information:\n");
+ printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
+ printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
+ printf(" Sizeofs:\n");
+ printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
+ printf(" WmCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(WmCtx));
+ printf(" WmPattern %" PRIuMAX "\n", (uintmax_t)sizeof(WmPattern));
+ printf(" WmHashItem %" PRIuMAX "\n", (uintmax_t)sizeof(WmHashItem));
+ printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
+ printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
+ printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
+ printf("Max shiftlen: %" PRIu32 "\n", ctx->shiftlen);
+ printf("Hash size: %" PRIu32 "\n", ctx->hash_size);
+ printf("Search function: ");
+ if (ctx->Search == WmSearch1) {
+ printf("WmSearch1 (allows single byte patterns)\n");
+ printf("MBSearch funct: ");
+ if (ctx->MBSearch == WmSearch2Hash16) printf("WmSearch2Hash16\n");
+ else if (ctx->MBSearch == WmSearch2Hash15) printf("WmSearch2Hash15\n");
+ else if (ctx->MBSearch == WmSearch2Hash14) printf("WmSearch2Hash14\n");
+ else if (ctx->MBSearch == WmSearch2Hash12) printf("WmSearch2Hash12\n");
+ else if (ctx->MBSearch == WmSearch2Hash9) printf("WmSearch2Hash9\n");
+ }
+ if (ctx->Search == WmSearch2Hash16) printf("WmSearch2Hash16 (only for multibyte patterns)\n");
+ else if (ctx->Search == WmSearch2Hash15) printf("WmSearch2Hash15 (only for multibyte patterns)\n");
+ else if (ctx->Search == WmSearch2Hash14) printf("WmSearch2Hash14 (only for multibyte patterns)\n");
+ else if (ctx->Search == WmSearch2Hash12) printf("WmSearch2Hash12 (only for multibyte patterns)\n");
+ else if (ctx->Search == WmSearch2Hash9) printf("WmSearch2Hash9 (only for multibyte patterns)\n");
+ else printf("ERROR\n");
+ printf("\n");
+}
+
+static inline WmPattern *WmAllocPattern(MpmCtx *mpm_ctx)
+{
+ WmPattern *p = SCMalloc(sizeof(WmPattern));
+ if (unlikely(p == NULL))
+ return NULL;
+ memset(p,0,sizeof(WmPattern));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(WmPattern);
+ return p;
+}
+
+static inline WmHashItem *
+WmAllocHashItem(MpmCtx *mpm_ctx)
+{
+ WmHashItem *hi = SCMalloc(sizeof(WmHashItem));
+ if (unlikely(hi == NULL))
+ return NULL;
+ memset(hi,0,sizeof(WmHashItem));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(WmHashItem);
+ return hi;
+}
+
+static void WmHashFree(MpmCtx *mpm_ctx, WmHashItem *hi)
+{
+ if (hi == NULL)
+ return;
+
+ WmHashItem *t = hi->nxt;
+ WmHashFree(mpm_ctx, t);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(WmHashItem);
+ SCFree(hi);
+}
+
+static inline void memcpy_tolower(uint8_t *d, uint8_t *s, uint16_t len)
+{
+ uint16_t i;
+ for (i = 0; i < len; i++) {
+ d[i] = wm_tolower(s[i]);
+ }
+}
+
+/*
+ * INIT HASH START
+ */
+static inline uint32_t WmInitHash(WmPattern *p)
+{
+ uint32_t hash = p->len * p->cs[0];
+ if (p->len > 1)
+ hash += p->cs[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline uint32_t WmInitHashRaw(uint8_t *pat, uint16_t patlen)
+{
+ uint32_t hash = patlen * pat[0];
+ if (patlen > 1)
+ hash += pat[1];
+
+ return (hash % INIT_HASH_SIZE);
+}
+
+static inline int WmInitHashAdd(WmCtx *ctx, WmPattern *p)
+{
+ uint32_t hash = WmInitHash(p);
+
+ //printf("WmInitHashAdd: %" PRIu32 "\n", hash);
+
+ if (ctx->init_hash[hash] == NULL) {
+ ctx->init_hash[hash] = p;
+ //printf("WmInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+ return 0;
+ }
+
+ WmPattern *tt = NULL;
+ WmPattern *t = ctx->init_hash[hash];
+
+ /* get the list tail */
+ do {
+ tt = t;
+ t = t->next;
+ } while (t != NULL);
+
+ tt->next = p;
+ //printf("WmInitHashAdd: hash %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+
+ return 0;
+}
+
+static inline int WmCmpPattern(WmPattern *p, uint8_t *pat, uint16_t patlen, char flags);
+
+static inline WmPattern *WmInitHashLookup(WmCtx *ctx, uint8_t *pat, uint16_t patlen, char flags)
+{
+ uint32_t hash = WmInitHashRaw(pat,patlen);
+
+ //printf("WmInitHashLookup: %" PRIu32 ", head %p\n", hash, ctx->init_hash[hash]);
+
+ if (ctx->init_hash[hash] == NULL) {
+ return NULL;
+ }
+
+ WmPattern *t = ctx->init_hash[hash];
+ for ( ; t != NULL; t = t->next) {
+ if (WmCmpPattern(t,pat,patlen,flags) == 1)
+ return t;
+ }
+
+ return NULL;
+}
+
+static inline int WmCmpPattern(WmPattern *p, uint8_t *pat, uint16_t patlen, char flags)
+{
+ if (p->len != patlen)
+ return 0;
+
+ if (p->flags != flags)
+ return 0;
+
+ if (memcmp(p->cs, pat, patlen) != 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * INIT HASH END
+ */
+
+void WmFreePattern(MpmCtx *mpm_ctx, WmPattern *p)
+{
+ if (p && p->cs && p->cs != p->ci) {
+ SCFree(p->cs);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->ci) {
+ SCFree(p->ci);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->len;
+ }
+
+ if (p && p->sids) {
+ SCFree(p->sids);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= p->sids_size * sizeof(uint32_t);
+ }
+
+ if (p) {
+ SCFree(p);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(WmPattern);
+ }
+}
+
+/* WmAddPattern
+ *
+ * pat: ptr to the pattern
+ * patlen: length of the pattern
+ * nocase: nocase flag: 1 enabled, 0 disable
+ * pid: pattern id
+ * sid: signature id (internal id)
+ */
+static int WmAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, uint32_t sid, uint8_t flags)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+
+// printf("WmAddPattern: ctx %p \"", mpm_ctx); prt(pat, patlen);
+// printf("\" id %" PRIu32 ", nocase %s\n", id, nocase ? "true" : "false");
+
+ if (patlen == 0)
+ return 0;
+
+ /* get a memory piece */
+ WmPattern *p = WmInitHashLookup(ctx, pat, patlen, flags);
+ if (p == NULL) {
+// printf("WmAddPattern: allocing new pattern\n");
+ p = WmAllocPattern(mpm_ctx);
+ if (p == NULL)
+ goto error;
+
+ p->len = patlen;
+ p->flags = flags;
+ p->id = pid;
+
+ /* setup the case insensitive part of the pattern */
+ p->ci = SCMalloc(patlen);
+ if (p->ci == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy_tolower(p->ci, pat, patlen);
+
+ /* setup the case sensitive part of the pattern */
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ /* nocase means no difference between cs and ci */
+ p->cs = p->ci;
+ } else {
+ if (memcmp(p->ci,pat,p->len) == 0) {
+ /* no diff between cs and ci: pat is lowercase */
+ p->cs = p->ci;
+ } else {
+ p->cs = SCMalloc(patlen);
+ if (p->cs == NULL)
+ goto error;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += patlen;
+ memcpy(p->cs, pat, patlen);
+ }
+ }
+
+ if (p->len > 1) {
+ p->prefix_cs = (uint16_t)(*(p->cs)+*(p->cs+1));
+ p->prefix_ci = (uint16_t)(*(p->ci)+*(p->ci+1));
+ }
+
+ //printf("WmAddPattern: ci \""); prt(p->ci,p->len);
+ //printf("\" cs \""); prt(p->cs,p->len);
+ //printf("\" prefix_ci %" PRIu32 ", prefix_cs %" PRIu32 "\n", p->prefix_ci, p->prefix_cs);
+
+ /* put in the pattern hash */
+ WmInitHashAdd(ctx, p);
+
+ if (mpm_ctx->pattern_cnt == 65535) {
+ printf("Max search words reached\n");
+ exit(1);
+ }
+ mpm_ctx->pattern_cnt++;
+
+ if (mpm_ctx->maxlen < patlen) mpm_ctx->maxlen = patlen;
+ if (mpm_ctx->minlen == 0) mpm_ctx->minlen = patlen;
+ else if (mpm_ctx->minlen > patlen) mpm_ctx->minlen = patlen;
+
+ p->sids_size = 1;
+ p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
+ BUG_ON(p->sids == NULL);
+ p->sids[0] = sid;
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(SigIntId);
+
+ } else {
+ /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
+
+ int found = 0;
+ uint32_t x = 0;
+ for (x = 0; x < p->sids_size; x++) {
+ if (p->sids[x] == sid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
+ BUG_ON(sids == NULL);
+ p->sids = sids;
+ p->sids[p->sids_size] = sid;
+ p->sids_size++;
+ mpm_ctx->memory_size += sizeof(uint32_t);
+ }
+ }
+
+ return 0;
+
+error:
+ WmFreePattern(mpm_ctx, p);
+ return -1;
+}
+
+int WmAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ flags |= MPM_PATTERN_FLAG_NOCASE;
+ return WmAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+int WmAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ return WmAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
+}
+
+static uint32_t WmBloomHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint16_t i;
+ uint32_t hash = (uint32_t)wm_tolower(*d);
+
+ for (i = 1; i < datalen - 1; i++) {
+ hash += (wm_tolower((*d++))) ^ i;
+ }
+ hash <<= (iter+1);
+
+ hash %= hash_size;
+ return hash;
+}
+/*
+static uint32_t BloomHash(void *data, uint16_t datalen, uint8_t iter, uint32_t hash_size)
+{
+ uint8_t *d = (uint8_t *)data;
+ uint32_t i;
+ uint32_t hash = 0;
+
+ for (i = 0; i < datalen; i++) {
+ if (i == 0) hash += (((uint32_t)*d++));
+ else if (i == 1) hash += (((uint32_t)*d++) * datalen);
+ else hash *= (((uint32_t)*d++) * i);
+ }
+
+ hash *= (iter + datalen);
+ hash %= hash_size;
+ return hash;
+}
+*/
+static void WmSearchPrepareHash(MpmCtx *mpm_ctx)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+ uint16_t i;
+ uint16_t idx = 0;
+ uint8_t idx8 = 0;
+
+ ctx->hash = (WmHashItem **)SCMalloc(sizeof(WmHashItem *) * ctx->hash_size);
+ if (ctx->hash == NULL)
+ goto error;
+ memset(ctx->hash, 0, sizeof(WmHashItem *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(WmHashItem *) * ctx->hash_size);
+
+ /* alloc the pminlen array */
+ ctx->pminlen = (uint8_t *)SCMalloc(sizeof(uint8_t) * ctx->hash_size);
+ if (ctx->pminlen == NULL)
+ goto error;
+ memset(ctx->pminlen, 0, sizeof(uint8_t) * ctx->hash_size);
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++)
+ {
+ if(ctx->parray[i]->len == 1) {
+ idx8 = (uint8_t)ctx->parray[i]->ci[0];
+ if (ctx->hash1[idx8].flags == 0) {
+ ctx->hash1[idx8].idx = i;
+ ctx->hash1[idx8].flags |= 0x01;
+ } else {
+ WmHashItem *hi = WmAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ /* Append this HashItem to the list */
+ WmHashItem *thi = &ctx->hash1[idx8];
+ while (thi->nxt) thi = thi->nxt;
+ thi->nxt = hi;
+ }
+ } else {
+ uint16_t patlen = ctx->shiftlen;
+
+ if (ctx->hash_size == HASH9_SIZE)
+ idx = HASH9(ctx->parray[i]->ci[patlen-1], ctx->parray[i]->ci[patlen-2]);
+ else if (ctx->hash_size == HASH12_SIZE)
+ idx = HASH12(ctx->parray[i]->ci[patlen-1], ctx->parray[i]->ci[patlen-2]);
+ else if (ctx->hash_size == HASH14_SIZE)
+ idx = HASH14(ctx->parray[i]->ci[patlen-1], ctx->parray[i]->ci[patlen-2]);
+ else if (ctx->hash_size == HASH15_SIZE)
+ idx = HASH15(ctx->parray[i]->ci[patlen-1], ctx->parray[i]->ci[patlen-2]);
+ else
+ idx = HASH16(ctx->parray[i]->ci[patlen-1], ctx->parray[i]->ci[patlen-2]);
+
+ if (ctx->hash[idx] == NULL) {
+ WmHashItem *hi = WmAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ ctx->hash[idx] = hi;
+ } else {
+ WmHashItem *hi = WmAllocHashItem(mpm_ctx);
+ if (hi == NULL)
+ goto error;
+ hi->idx = i;
+ hi->flags |= 0x01;
+
+ if (ctx->parray[i]->len < ctx->pminlen[idx])
+ ctx->pminlen[idx] = ctx->parray[i]->len;
+
+ /* Append this HashItem to the list */
+ WmHashItem *thi = ctx->hash[idx];
+ while (thi->nxt) thi = thi->nxt;
+ thi->nxt = hi;
+ }
+ }
+ }
+
+ /* alloc the bloom array */
+ ctx->bloom = (BloomFilter **)SCMalloc(sizeof(BloomFilter *) * ctx->hash_size);
+ if (ctx->bloom == NULL)
+ goto error;
+ memset(ctx->bloom, 0, sizeof(BloomFilter *) * ctx->hash_size);
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(BloomFilter *) * ctx->hash_size);
+
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ WmHashItem *hi = ctx->hash[h];
+ if (hi == NULL)
+ continue;
+
+ ctx->bloom[h] = BloomFilterInit(wm_bloom_size, 2, WmBloomHash);
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt += BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size += BloomFilterMemorySize(ctx->bloom[h]);
+
+ if (ctx->pminlen[h] > 8)
+ ctx->pminlen[h] = 8;
+
+ WmHashItem *thi = hi;
+ do {
+ BloomFilterAdd(ctx->bloom[h], ctx->parray[thi->idx]->ci, ctx->pminlen[h]);
+ thi = thi->nxt;
+ } while (thi != NULL);
+ }
+ return;
+error:
+ return;
+}
+static void WmSearchPrepareShiftTable(MpmCtx *mpm_ctx)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+
+ uint16_t shift = 0, k = 0, idx = 0;
+ uint32_t i = 0;
+
+ uint16_t smallest = mpm_ctx->minlen;
+ if (smallest > 255) smallest = 255;
+ if (smallest < 2) smallest = 2;
+
+ ctx->shiftlen = smallest;
+
+ ctx->shifttable = SCMalloc(sizeof(uint16_t) * ctx->hash_size);
+ if (ctx->shifttable == NULL)
+ return;
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (sizeof(uint16_t) * ctx->hash_size);
+
+ /* default shift table is set to minimal shift */
+ for (i = 0; i < ctx->hash_size; i++)
+ ctx->shifttable[i] = ctx->shiftlen;
+
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++)
+ {
+ /* ignore one byte patterns */
+ if (ctx->parray[i]->len == 1)
+ continue;
+
+ /* add the first character of the pattern preceeded by
+ * every possible other character. */
+ for (k = 0; k < 256; k++) {
+ shift = ctx->shiftlen - 1;
+ if (shift > 255) shift = 255;
+
+ if (ctx->hash_size == HASH9_SIZE) {
+ idx = HASH9(ctx->parray[i]->ci[0], (uint8_t)k);
+ //printf("HASH9 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH12_SIZE) {
+ idx = HASH12(ctx->parray[i]->ci[0], (uint8_t)k);
+ //printf("HASH12 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH14_SIZE) {
+ idx = HASH14(ctx->parray[i]->ci[0], (uint8_t)k);
+ //printf("HASH14 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH15_SIZE) {
+ idx = HASH15(ctx->parray[i]->ci[0], (uint8_t)k);
+ //printf("HASH15 idx %" PRIu32 "\n", idx);
+ } else {
+ idx = HASH16(ctx->parray[i]->ci[0], (uint8_t)k);
+ //printf("HASH15 idx %" PRIu32 "\n", idx);
+ }
+ if (shift < ctx->shifttable[idx]) {
+ ctx->shifttable[idx] = shift;
+ }
+ }
+
+ for (k = 0; k < ctx->shiftlen-1; k++)
+ {
+ shift = (ctx->shiftlen - 2 - k);
+ if (shift > 255) shift = 255;
+
+ if (ctx->hash_size == HASH9_SIZE) {
+ idx = HASH9(ctx->parray[i]->ci[k+1], ctx->parray[i]->ci[k]);
+ //printf("HASH9 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH12_SIZE) {
+ idx = HASH12(ctx->parray[i]->ci[k+1], ctx->parray[i]->ci[k]);
+ //printf("HASH12 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH14_SIZE) {
+ idx = HASH14(ctx->parray[i]->ci[k+1], ctx->parray[i]->ci[k]);
+ //printf("HASH14 idx %" PRIu32 "\n", idx);
+ } else if (ctx->hash_size == HASH15_SIZE) {
+ idx = HASH15(ctx->parray[i]->ci[k+1], ctx->parray[i]->ci[k]);
+ //printf("HASH15 idx %" PRIu32 "\n", idx);
+ } else {
+ idx = HASH16(ctx->parray[i]->ci[k+1], ctx->parray[i]->ci[k]);
+ //printf("HASH15 idx %" PRIu32 "\n", idx);
+ }
+ if (shift < ctx->shifttable[idx]) {
+ ctx->shifttable[idx] = shift;
+ }
+ //printf("WmPrepareShiftTable: i %" PRIu32 ", k %" PRIu32 ", idx %" PRIu32 ", shift set to %" PRIu32 ": \"%c%c\"\n",
+ // i, k, idx, shift, ctx->parray[i]->ci[k], ctx->parray[i]->ci[k+1]);
+ }
+ }
+}
+
+int WmPreparePatterns(MpmCtx *mpm_ctx)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+
+ /* alloc the pattern array */
+ ctx->parray = (WmPattern **)SCMalloc(mpm_ctx->pattern_cnt * sizeof(WmPattern *));
+ if (ctx->parray == NULL)
+ goto error;
+ memset(ctx->parray, 0, mpm_ctx->pattern_cnt * sizeof(WmPattern *));
+ //printf("mpm_ctx %p, parray %p\n", mpm_ctx,ctx->parray);
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += (mpm_ctx->pattern_cnt * sizeof(WmPattern *));
+
+ /* populate it with the patterns in the hash */
+ uint32_t i = 0, p = 0;
+ for (i = 0; i < INIT_HASH_SIZE; i++) {
+ WmPattern *node = ctx->init_hash[i], *nnode = NULL;
+ for ( ; node != NULL; ) {
+ nnode = node->next;
+ node->next = NULL;
+
+ ctx->parray[p] = node;
+
+ p++;
+ node = nnode;
+ }
+ }
+ /* we no longer need the hash, so free it's memory */
+ SCFree(ctx->init_hash);
+ ctx->init_hash = NULL;
+
+ /* TODO VJ these values are chosen pretty much randomly, so
+ * we should do some performance testing
+ * */
+
+ if (ctx->hash_size == 0) {
+ if (mpm_ctx->pattern_cnt < 50) {
+ ctx->hash_size = HASH9_SIZE;
+ } else if(mpm_ctx->pattern_cnt < 300) {
+ ctx->hash_size = HASH12_SIZE;
+ } else if(mpm_ctx->pattern_cnt < 1200) {
+ ctx->hash_size = HASH14_SIZE;
+ } else if(mpm_ctx->pattern_cnt < 2400) {
+ ctx->hash_size = HASH15_SIZE;
+ } else {
+ ctx->hash_size = HASH16_SIZE;
+ }
+ }
+
+ WmSearchPrepareShiftTable(mpm_ctx);
+ WmSearchPrepareHash(mpm_ctx);
+
+ if (ctx->hash_size == HASH9_SIZE) {
+ ctx->MBSearch = WmSearch2Hash9;
+ ctx->Search = WmSearch2Hash9;
+ } else if (ctx->hash_size == HASH12_SIZE) {
+ ctx->MBSearch = WmSearch2Hash12;
+ ctx->Search = WmSearch2Hash12;
+ } else if (ctx->hash_size == HASH14_SIZE) {
+ ctx->MBSearch = WmSearch2Hash14;
+ ctx->Search = WmSearch2Hash14;
+ } else if (ctx->hash_size == HASH15_SIZE) {
+ ctx->MBSearch = WmSearch2Hash15;
+ ctx->Search = WmSearch2Hash15;
+ } else {
+ ctx->MBSearch = WmSearch2Hash16;
+ ctx->Search = WmSearch2Hash16;
+ }
+
+ if (mpm_ctx->minlen == 1) {
+ ctx->Search = WmSearch1;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+void WmPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
+{
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+
+ printf("Shift 0: %" PRIu32 "\n", tctx->stat_shift_null);
+ printf("Loop match: %" PRIu32 "\n", tctx->stat_loop_match);
+ printf("Loop no match: %" PRIu32 "\n", tctx->stat_loop_no_match);
+ printf("Num shifts: %" PRIu32 "\n", tctx->stat_num_shift);
+ printf("Total shifts: %" PRIu32 "\n", tctx->stat_total_shift);
+#endif /* WUMANBER_COUNTERS */
+}
+
+static inline int
+memcmp_lowercase(uint8_t *s1, uint8_t *s2, uint16_t n)
+{
+ size_t i;
+
+ /* check backwards because we already tested the first
+ * 2 to 4 chars. This way we are more likely to detect
+ * a miss and thus speed up a little... */
+ for (i = n - 1; i; i--) {
+ if (wm_tolower(*(s2+i)) != s1[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+inline uint32_t WmSearch(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+ return ctx->Search(mpm_ctx, mpm_thread_ctx, pmq, buf, buflen);
+}
+
+/* SCAN FUNCTIONS */
+uint32_t WmSearch2Hash9(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+#endif /* WUMANBER_COUNTERS */
+ uint32_t cnt = 0;
+ uint8_t *bufend = buf + buflen - 1;
+ uint16_t sl = ctx->shiftlen;
+ uint16_t h;
+ uint8_t shift;
+ WmHashItem *thi, *hi;
+ WmPattern *p;
+ uint16_t prefixci_buf;
+ uint16_t prefixcs_buf;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF(%" PRIu32 ") ", buflen); prt(buf,buflen); printf(" (sl %" PRIu32 ")\n", sl);
+
+ buf+=(sl-1);
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ h = HASH9(wm_tolower(*buf),(wm_tolower(*(buf-1))));
+ shift = ctx->shifttable[h];
+ //printf("%p %" PRIu32 " search: h %" PRIu32 ", shift %" PRIu32 "\n", buf, buf - bufmin, h, shift);
+
+ if (shift == 0) {
+ COUNT(tctx->stat_shift_null++);
+
+ hi = ctx->hash[h];
+ //printf("search: hi %p\n", hi);
+ if (hi != NULL) {
+ /* get our patterns from the hash */
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((bufend - (buf-sl)) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf-sl+1, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+ goto skip_loop;
+ }
+ }
+ }
+
+ prefixci_buf = (uint16_t)(wm_tolower(*(buf-sl+1)) + wm_tolower(*(buf-sl+2)));
+ prefixcs_buf = (uint16_t)(*(buf-sl+1) + *(buf-sl+2));
+
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ //printf("WmSearch2: p->prefix_ci %" PRIu32 ", p->prefix_cs %" PRIu32 "\n",
+ // p->prefix_ci, p->prefix_cs);
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (p->prefix_ci != prefixci_buf || p->len > (bufend-(buf-sl)))
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf-sl+1, p->len) == 0) {
+ //printf("CI Exact match: "); prt(p->ci, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (p->prefix_cs != prefixcs_buf || p->len > (bufend-(buf-sl)))
+ continue;
+ if (memcmp(p->cs, buf-sl+1, p->len) == 0) {
+ //printf("CS Exact match: "); prt(p->cs, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+ }
+skip_loop:
+ shift = 1;
+ } else {
+ COUNT(tctx->stat_total_shift += shift);
+ COUNT(tctx->stat_num_shift++);
+ }
+ buf += shift;
+ }
+
+ //printf("cnt %" PRIu32 "\n", cnt);
+ return cnt;
+}
+
+uint32_t WmSearch2Hash12(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+#endif /* WUMANBER_COUNTERS */
+ uint32_t cnt = 0;
+ uint8_t *bufend = buf + buflen - 1;
+ uint16_t sl = ctx->shiftlen;
+ uint16_t h;
+ uint8_t shift;
+ WmHashItem *thi, *hi;
+ WmPattern *p;
+ uint16_t prefixci_buf;
+ uint16_t prefixcs_buf;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF(%" PRIu32 ") ", buflen); prt(buf,buflen); printf("\n");
+
+ buf+=(sl-1);
+ //buf++;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ //h = (wm_tolower(*buf)<<8)+(wm_tolower(*(buf-1)));
+ h = HASH12(wm_tolower(*buf),(wm_tolower(*(buf-1))));
+ shift = ctx->shifttable[h];
+ //printf("search: h %" PRIu32 ", shift %" PRIu32 "\n", h, shift);
+
+ if (shift == 0) {
+ COUNT(tctx->stat_shift_null++);
+ /* get our hash item */
+ hi = ctx->hash[h];
+ //printf("search: hi %p\n", hi);
+ if (hi != NULL) {
+ /* get our patterns from the hash */
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((bufend - (buf-sl)) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf-sl+1, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+ goto skip_loop;
+ }
+ }
+ }
+
+ prefixci_buf = (uint16_t)(wm_tolower(*(buf-sl+1)) + wm_tolower(*(buf-sl+2)));
+ prefixcs_buf = (uint16_t)(*(buf-sl+1) + *(buf-sl+2));
+ //printf("WmSearch2: prefixci_buf %" PRIu32 ", prefixcs_buf %" PRIu32 "\n", prefixci_buf, prefixcs_buf);
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ //printf("WmSearch2: p->prefix_ci %" PRIu32 ", p->prefix_cs %" PRIu32 "\n",
+ // p->prefix_ci, p->prefix_cs);
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (p->prefix_ci != prefixci_buf || p->len > (bufend-(buf-sl)))
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf-sl+1, p->len) == 0) {
+ //printf("CI Exact match: "); prt(p->ci, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (p->prefix_cs != prefixcs_buf || p->len > (bufend-(buf-sl)))
+ continue;
+ if (memcmp(p->cs, buf-sl+1, p->len) == 0) {
+ //printf("CS Exact match: "); prt(p->cs, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+ }
+skip_loop:
+ shift = 1;
+ } else {
+ COUNT(tctx->stat_total_shift += shift);
+ COUNT(tctx->stat_num_shift++);
+ }
+ buf += shift;
+ }
+
+ return cnt;
+}
+
+uint32_t WmSearch2Hash14(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+#endif /* WUMANBER_COUNTERS */
+ uint32_t cnt = 0;
+ uint8_t *bufend = buf + buflen - 1;
+ uint16_t sl = ctx->shiftlen;
+ uint16_t h;
+ uint8_t shift;
+ WmHashItem *thi, *hi;
+ WmPattern *p;
+ uint16_t prefixci_buf;
+ uint16_t prefixcs_buf;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF(%" PRIu32 ") ", buflen); prt(buf,buflen); printf("\n");
+
+ buf+=(sl-1);
+ //buf++;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ //h = (wm_tolower(*buf)<<8)+(wm_tolower(*(buf-1)));
+ h = HASH14(wm_tolower(*buf),(wm_tolower(*(buf-1))));
+ shift = ctx->shifttable[h];
+ //printf("search: h %" PRIu32 ", shift %" PRIu32 "\n", h, shift);
+
+ if (shift == 0) {
+ COUNT(tctx->stat_shift_null++);
+ /* get our hash item */
+ hi = ctx->hash[h];
+ //printf("search: hi %p\n", hi);
+ if (hi != NULL) {
+ /* get our patterns from the hash */
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((bufend - (buf-sl)) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf-sl+1, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+ goto skip_loop;
+ }
+ }
+ }
+
+ prefixci_buf = (uint16_t)(wm_tolower(*(buf-sl+1)) + wm_tolower(*(buf-sl+2)));
+ prefixcs_buf = (uint16_t)(*(buf-sl+1) + *(buf-sl+2));
+ //printf("WmSearch2: prefixci_buf %" PRIu32 ", prefixcs_buf %" PRIu32 "\n", prefixci_buf, prefixcs_buf);
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ //printf("WmSearch2: p->prefix_ci %" PRIu32 ", p->prefix_cs %" PRIu32 "\n",
+ // p->prefix_ci, p->prefix_cs);
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (p->prefix_ci != prefixci_buf || p->len > (bufend-(buf-sl)))
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf-sl+1, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (p->prefix_cs != prefixcs_buf || p->len > (bufend-(buf-sl)))
+ continue;
+ if (memcmp(p->cs, buf-sl+1, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+ }
+skip_loop:
+ shift = 1;
+ } else {
+ COUNT(tctx->stat_total_shift += shift);
+ COUNT(tctx->stat_num_shift++);
+ }
+ buf += shift;
+ }
+
+ return cnt;
+}
+
+uint32_t WmSearch2Hash15(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+#endif /* WUMANBER_COUNTERS */
+ uint32_t cnt = 0;
+ uint8_t *bufend = buf + buflen - 1;
+ uint16_t sl = ctx->shiftlen;
+ uint16_t h;
+ uint8_t shift;
+ WmHashItem *thi, *hi;
+ WmPattern *p;
+ uint16_t prefixci_buf;
+ uint16_t prefixcs_buf;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF(%" PRIu32 ") ", buflen); prt(buf,buflen); printf("\n");
+
+ buf+=(sl-1);
+ //buf++;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ //h = (wm_tolower(*buf)<<8)+(wm_tolower(*(buf-1)));
+ h = HASH15(wm_tolower(*buf),(wm_tolower(*(buf-1))));
+ shift = ctx->shifttable[h];
+ //printf("search: h %" PRIu32 ", shift %" PRIu32 "\n", h, shift);
+
+ if (shift == 0) {
+ COUNT(tctx->stat_shift_null++);
+ /* get our hash item */
+ hi = ctx->hash[h];
+ //printf("search: hi %p\n", hi);
+ if (hi != NULL) {
+ /* get our patterns from the hash */
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((bufend - (buf-sl)) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf-sl+1, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+ goto skip_loop;
+ }
+ }
+ }
+
+ prefixci_buf = (uint16_t)(wm_tolower(*(buf-sl+1)) + wm_tolower(*(buf-sl+2)));
+ prefixcs_buf = (uint16_t)(*(buf-sl+1) + *(buf-sl+2));
+ //printf("WmSearch2: prefixci_buf %" PRIu32 ", prefixcs_buf %" PRIu32 "\n", prefixci_buf, prefixcs_buf);
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ //printf("WmSearch2: p->prefix_ci %" PRIu32 ", p->prefix_cs %" PRIu32 "\n",
+ // p->prefix_ci, p->prefix_cs);
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (p->prefix_ci != prefixci_buf || p->len > (bufend-(buf-sl)))
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf-sl+1, p->len) == 0) {
+ //printf("CI Exact match: "); prt(p->ci, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (p->prefix_cs != prefixcs_buf || p->len > (bufend-(buf-sl)))
+ continue;
+ if (memcmp(p->cs, buf-sl+1, p->len) == 0) {
+ //printf("CS Exact match: "); prt(p->cs, p->len); printf("\n");
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+ }
+skip_loop:
+ shift = 1;
+ } else {
+ COUNT(tctx->stat_total_shift += shift);
+ COUNT(tctx->stat_num_shift++);
+ }
+ buf += shift;
+ }
+
+ return cnt;
+}
+
+uint32_t WmSearch2Hash16(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+#ifdef WUMANBER_COUNTERS
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+#endif /* WUMANBER_COUNTERS */
+ uint32_t cnt = 0;
+ uint8_t *bufend = buf + buflen - 1;
+ uint16_t sl = ctx->shiftlen;
+ uint16_t h;
+ uint8_t shift;
+ WmHashItem *thi, *hi;
+ WmPattern *p;
+ uint16_t prefixci_buf;
+ uint16_t prefixcs_buf;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF(%" PRIu32 ") ", buflen); prt(buf,buflen); printf("\n");
+
+ buf+=(sl-1);
+ //buf++;
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ while (buf <= bufend) {
+ //h = (wm_tolower(*buf)<<8)+(wm_tolower(*(buf-1)));
+ h = HASH16(wm_tolower(*buf),(wm_tolower(*(buf-1))));
+ shift = ctx->shifttable[h];
+ //printf("search: h %" PRIu32 ", shift %" PRIu32 "\n", h, shift);
+
+ if (shift == 0) {
+ COUNT(tctx->stat_shift_null++);
+ /* get our hash item */
+ hi = ctx->hash[h];
+ //printf("search: hi %p\n", hi);
+ if (hi != NULL) {
+ /* get our patterns from the hash */
+ if (ctx->bloom[h] != NULL) {
+ COUNT(tctx->stat_pminlen_calls++);
+ COUNT(tctx->stat_pminlen_total+=ctx->pminlen[h]);
+
+ if ((bufend - (buf-sl)) < ctx->pminlen[h]) {
+ goto skip_loop;
+ } else {
+ COUNT(tctx->stat_bloom_calls++);
+
+ if (BloomFilterTest(ctx->bloom[h], buf-sl+1, ctx->pminlen[h]) == 0) {
+ COUNT(tctx->stat_bloom_hits++);
+ goto skip_loop;
+ }
+ }
+ }
+
+ prefixci_buf = (uint16_t)(wm_tolower(*(buf-sl+1)) + wm_tolower(*(buf-sl+2)));
+ prefixcs_buf = (uint16_t)(*(buf-sl+1) + *(buf-sl+2));
+ //printf("WmSearch2: prefixci_buf %" PRIu32 ", prefixcs_buf %" PRIu32 "\n", prefixci_buf, prefixcs_buf);
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ //printf("WmSearch2: p->prefix_ci %" PRIu32 ", p->prefix_cs %" PRIu32 "\n",
+ // p->prefix_ci, p->prefix_cs);
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (p->prefix_ci != prefixci_buf || p->len > (bufend-(buf-sl)))
+ continue;
+
+ if (memcmp_lowercase(p->ci, buf-sl+1, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ } else {
+ if (p->prefix_cs != prefixcs_buf || p->len > (bufend-(buf-sl)))
+ continue;
+ if (memcmp(p->cs, buf-sl+1, p->len) == 0) {
+ COUNT(tctx->stat_loop_match++);
+
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ } else {
+ COUNT(tctx->stat_loop_no_match++);
+ }
+ }
+ }
+ }
+skip_loop:
+ shift = 1;
+ } else {
+ COUNT(tctx->stat_total_shift += shift);
+ COUNT(tctx->stat_num_shift++);
+ }
+ buf += shift;
+ }
+
+ return cnt;
+}
+
+uint32_t WmSearch1(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, PatternMatcherQueue *pmq, uint8_t *buf, uint16_t buflen)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+ uint8_t *bufmin = buf;
+ uint8_t *bufend = buf + buflen - 1;
+ uint32_t cnt = 0;
+ WmPattern *p;
+ WmHashItem *thi, *hi;
+
+ if (buflen == 0)
+ return 0;
+
+ //printf("BUF "); prt(buf,buflen); printf("\n");
+
+ uint8_t *bitarray = NULL;
+ if (pmq) {
+ bitarray = alloca(pmq->pattern_id_bitarray_size);
+ memset(bitarray, 0, pmq->pattern_id_bitarray_size);
+ }
+
+ if (mpm_ctx->minlen == 1) {
+ while (buf <= bufend) {
+ uint8_t h = wm_tolower(*buf);
+ hi = &ctx->hash1[h];
+
+ if (hi->flags & 0x01) {
+ for (thi = hi; thi != NULL; thi = thi->nxt) {
+ p = ctx->parray[thi->idx];
+
+ if (p->len != 1)
+ continue;
+
+ if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
+ if (wm_tolower(*buf) == p->ci[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ } else {
+ if (*buf == p->cs[0]) {
+ cnt += MpmVerifyMatch(mpm_thread_ctx, pmq, p->id, bitarray, p->sids, p->sids_size);
+ }
+ }
+ }
+ }
+ buf += 1;
+ }
+ }
+ //printf("WmSearch1: after 1byte cnt %" PRIu32 "\n", cnt);
+ if (mpm_ctx->maxlen > 1) {
+ /* Pass bufmin on because buf no longer points to the
+ * start of the buffer. */
+ cnt += ctx->MBSearch(mpm_ctx, mpm_thread_ctx, pmq, bufmin, buflen);
+ //printf("WmSearch1: after 2+byte cnt %" PRIu32 "\n", cnt);
+ }
+ return cnt;
+}
+
+/**
+ * \brief Function to get the user defined values for wumanber algorithm from
+ * the config file 'suricata.yaml'
+ */
+void WmGetConfig()
+{
+ ConfNode *wm_conf;
+ const char *hash_val = NULL;
+ const char *bloom_val = NULL;
+
+ /* init defaults */
+ wm_hash_size = HASHSIZE_LOW;
+ wm_bloom_size = BLOOMSIZE_MEDIUM;
+
+ ConfNode *pm = ConfGetNode("pattern-matcher");
+
+ if (pm != NULL) {
+
+ TAILQ_FOREACH(wm_conf, &pm->head, next) {
+ if (strncmp(wm_conf->val, "wumanber", 8) == 0) {
+ hash_val = ConfNodeLookupChildValue(wm_conf->head.tqh_first,
+ "hash_size");
+ bloom_val = ConfNodeLookupChildValue(wm_conf->head.tqh_first,
+ "bf_size");
+
+ if (hash_val != NULL)
+ wm_hash_size = MpmGetHashSize(hash_val);
+
+ if (bloom_val != NULL)
+ wm_bloom_size = MpmGetBloomSize(bloom_val);
+
+ SCLogDebug("hash size is %"PRIu32" and bloom size is %"PRIu32"",
+ wm_hash_size, wm_bloom_size);
+ }
+ }
+ }
+}
+
+void WmInitCtx (MpmCtx *mpm_ctx)
+{
+ SCLogDebug("mpm_ctx %p", mpm_ctx);
+
+ mpm_ctx->ctx = SCMalloc(sizeof(WmCtx));
+ if (mpm_ctx->ctx == NULL)
+ return;
+ memset(mpm_ctx->ctx, 0, sizeof(WmCtx));
+
+ mpm_ctx->memory_cnt++;
+ mpm_ctx->memory_size += sizeof(WmCtx);
+
+ /* initialize the hash we use to speed up pattern insertions */
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+ ctx->init_hash = SCMalloc(sizeof(WmPattern *) * INIT_HASH_SIZE);
+ if (ctx->init_hash == NULL)
+ return;
+
+ memset(ctx->init_hash, 0, sizeof(WmPattern *) * INIT_HASH_SIZE);
+
+ /* Initialize the defaults value from the config file. The given check make
+ sure that we query config file only once for config values */
+ if (wm_hash_size == 0)
+ WmGetConfig();
+
+}
+
+void WmDestroyCtx(MpmCtx *mpm_ctx)
+{
+ WmCtx *ctx = (WmCtx *)mpm_ctx->ctx;
+ if (ctx == NULL)
+ return;
+
+ if (ctx->init_hash) {
+ SCFree(ctx->init_hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(WmPattern *));
+ }
+
+ if (ctx->parray) {
+ uint32_t i;
+ for (i = 0; i < mpm_ctx->pattern_cnt; i++) {
+ if (ctx->parray[i] != NULL) {
+ WmFreePattern(mpm_ctx, ctx->parray[i]);
+ }
+ }
+
+ SCFree(ctx->parray);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (mpm_ctx->pattern_cnt * sizeof(WmPattern));
+ }
+
+ if (ctx->bloom) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->bloom[h] == NULL)
+ continue;
+
+ mpm_ctx->memory_cnt -= BloomFilterMemoryCnt(ctx->bloom[h]);
+ mpm_ctx->memory_size -= BloomFilterMemorySize(ctx->bloom[h]);
+
+ BloomFilterFree(ctx->bloom[h]);
+ }
+
+ SCFree(ctx->bloom);
+
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(BloomFilter *) * ctx->hash_size);
+ }
+
+ if (ctx->hash) {
+ uint32_t h;
+ for (h = 0; h < ctx->hash_size; h++) {
+ if (ctx->hash[h] == NULL)
+ continue;
+
+ WmHashFree(mpm_ctx, ctx->hash[h]);
+ }
+
+ SCFree(ctx->hash);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(WmHashItem) * ctx->hash_size);
+ }
+
+ if (ctx->shifttable) {
+ SCFree(ctx->shifttable);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(uint16_t) * ctx->hash_size);
+ }
+
+ if (ctx->pminlen) {
+ SCFree(ctx->pminlen);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= (sizeof(uint8_t) * ctx->hash_size);
+ }
+
+ SCFree(mpm_ctx->ctx);
+ mpm_ctx->memory_cnt--;
+ mpm_ctx->memory_size -= sizeof(WmCtx);
+}
+
+void WmThreadInitCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, uint32_t matchsize)
+{
+ memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
+
+#ifdef WUMANBER_COUNTERS
+ mpm_thread_ctx->ctx = SCMalloc(sizeof(WmThreadCtx));
+ if (mpm_thread_ctx->ctx == NULL)
+ return;
+
+ memset(mpm_thread_ctx->ctx, 0, sizeof(WmThreadCtx));
+
+ mpm_thread_ctx->memory_cnt++;
+ mpm_thread_ctx->memory_size += sizeof(WmThreadCtx);
+#endif
+}
+
+void WmThreadDestroyCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
+{
+ WmThreadCtx *ctx = (WmThreadCtx *)mpm_thread_ctx->ctx;
+ if (ctx != NULL) { /* size can be 0 when optimized */
+ mpm_thread_ctx->memory_cnt--;
+ mpm_thread_ctx->memory_size -= sizeof(WmThreadCtx);
+ SCFree(mpm_thread_ctx->ctx);
+ }
+}
+
+void MpmWuManberRegister (void)
+{
+ mpm_table[MPM_WUMANBER].name = "wumanber";
+ mpm_table[MPM_WUMANBER].max_pattern_length = 0;
+ mpm_table[MPM_WUMANBER].InitCtx = WmInitCtx;
+ mpm_table[MPM_WUMANBER].InitThreadCtx = WmThreadInitCtx;
+ mpm_table[MPM_WUMANBER].DestroyCtx = WmDestroyCtx;
+ mpm_table[MPM_WUMANBER].DestroyThreadCtx = WmThreadDestroyCtx;
+ mpm_table[MPM_WUMANBER].AddPattern = WmAddPatternCS;
+ mpm_table[MPM_WUMANBER].AddPatternNocase = WmAddPatternCI;
+ mpm_table[MPM_WUMANBER].Prepare = WmPreparePatterns;
+ mpm_table[MPM_WUMANBER].Search = WmSearch;
+ mpm_table[MPM_WUMANBER].Cleanup = NULL;
+ mpm_table[MPM_WUMANBER].PrintCtx = WmPrintInfo;
+ mpm_table[MPM_WUMANBER].PrintThreadCtx = WmPrintSearchStats;
+ mpm_table[MPM_WUMANBER].RegisterUnittests = WmRegisterTests;
+
+ /* create table for O(1) lowercase conversion lookup */
+ int c = 0;
+ for ( ; c < 256; c++) {
+ if (c >= 'A' && c <= 'Z')
+ lowercasetable[c] = (c + ('a' - 'A'));
+ else
+ lowercasetable[c] = c;
+ }
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+int WmTestInitCtx01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ WmInitCtx(&mpm_ctx);
+
+ if (mpm_ctx.ctx != NULL)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitCtx02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ WmInitCtx(&mpm_ctx);
+
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ if (ctx->parray == NULL)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitCtx03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ if (mpm_table[MPM_WUMANBER].Search == WmSearch)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestThreadInitCtx01 (void)
+{
+#ifdef WUMANBER_COUNTERS
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ if (mpm_thread_ctx.memory_cnt == 2)
+ result = 1;
+ else
+ printf("mpm_thread_ctx.memory_cnt %"PRIu32", expected 2: ", mpm_thread_ctx.memory_cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+#else
+ return 1;
+#endif
+}
+
+int WmTestThreadInitCtx02 (void)
+{
+#ifdef WUMANBER_COUNTERS
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ WmThreadCtx *tctx = (WmThreadCtx *)mpm_thread_ctx.ctx;
+
+ if (tctx->search_stat_shift_null == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+#else
+ int result = 1;
+#endif
+ return result;
+}
+
+int WmTestInitAddPattern01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ int ret = MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ if (ret == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitAddPattern02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ if (ctx->init_hash != NULL)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitAddPattern03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ WmPattern *pat = WmInitHashLookup(ctx, (uint8_t *)"abcd", 4, 0);
+ if (pat != NULL) {
+ if (pat->len == 4)
+ result = 1;
+ }
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitAddPattern04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ WmPattern *pat = WmInitHashLookup(ctx, (uint8_t *)"abcd", 4, MPM_PATTERN_FLAG_NOCASE);
+ if (pat != NULL) {
+ if (pat->flags & MPM_PATTERN_FLAG_NOCASE)
+ result = 1;
+ }
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitAddPattern05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ WmPattern *pat = WmInitHashLookup(ctx, (uint8_t *)"abcd", 4, 0);
+ if (pat != NULL) {
+ if (!(pat->flags & MPM_PATTERN_FLAG_NOCASE))
+ result = 1;
+ }
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestInitAddPattern06 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 1234, 0, 0);
+ WmPattern *pat = WmInitHashLookup(ctx, (uint8_t *)"abcd", 4, 0);
+ if (pat != NULL) {
+ if (memcmp(pat->cs, "abcd", 4) == 0)
+ result = 1;
+ }
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"a", 1, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+
+ if (ctx->Search == WmSearch1)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ if (ctx->shiftlen == 4)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ if (ctx->shifttable[1] == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 ": ", ctx->shifttable[1]);
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"a", 1, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+
+ if (ctx->Search == WmSearch1)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ if (ctx->shiftlen == 4)
+ result = 1;
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestPrepare06 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ if (ctx->shifttable[1] == 4)
+ result = 1;
+ else
+ printf("4 != %" PRIu32 ": ", ctx->shifttable[1]);
+
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch01 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //mpm_ctx.PrintCtx(&mpm_ctx);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch01Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+
+ ctx->hash_size = HASH12_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //mpm_ctx.PrintCtx(&mpm_ctx);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch01Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+
+ ctx->hash_size = HASH14_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //mpm_ctx.PrintCtx(&mpm_ctx);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch01Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+
+ ctx->hash_size = HASH15_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //mpm_ctx.PrintCtx(&mpm_ctx);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch01Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+
+ ctx->hash_size = HASH16_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //mpm_ctx.PrintCtx(&mpm_ctx);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcd", 4);
+
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch02 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abce", 4);
+
+ if (cnt == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch03 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+ if (cnt == 1)
+ result = 1;
+ else
+ printf("1 != %" PRIu32 " ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch04 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch05 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"efgh", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch06 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"eFgH", 4, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdEfGh", 8);
+
+ if (cnt == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch07 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"eFgH", 4, 0, 0, 1, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdEfGh", 8);
+
+ if (cnt == 2)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch08 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 2)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch09 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"ab", 2);
+
+ if (cnt == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch10 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bc", 2, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"gh", 2, 0, 0, 1, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 2);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 2)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch11 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"a", 1, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"d", 1, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"h", 1, 0, 0, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 3)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"d", 1, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Z", 1, 0, 0, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 2)
+ result = 1;
+ else
+ printf("2 != %" PRIu32 ": ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch13 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"a", 1, 0, 0, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"de",2, 0, 0, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"h", 1, 0, 0, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 3)
+ result = 1;
+ else
+ printf("3 != %" PRIu32 ": ", cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"de",2, 0, 0, 1, 0, 0);
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Z", 1, 0, 0, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ if (cnt == 2)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+/** \todo VJ disabled because it tests the old match storage */
+#if 0
+int WmTestSearch15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 1, 1, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"de",2, 0, 0, 1, 1, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Z", 1, 0, 0, 1, 1, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ uint32_t len = mpm_thread_ctx.match[1].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 1, 1, 0, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"de",2, 0, 0, 1, 1, 1, 0, 0);
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Z", 1, 0, 0, 1, 1, 2, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 3);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"abcdefgh", 8);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch17 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch18Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH12_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch18Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH14_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch18Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH15_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch18 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH16_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch18Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH16_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch19 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch19Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH12_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch19Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH14_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch19Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH15_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch19Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCI(&mpm_ctx, (uint8_t *)"/VideoAccessCodecInstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH16_SIZE;
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstaLL.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch20 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch20Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH12_SIZE; /* force hash12 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch20Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH14_SIZE; /* force hash14 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch20Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH15_SIZE; /* force hash15 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch20Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH16_SIZE; /* force hash16 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/VideoAccessCodecInstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 0)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+int WmTestSearch21 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/videoaccesscodecinstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch21Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH12_SIZE; /* force hash16 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //WmPrintInfo(&mpm_ctx);
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/videoaccesscodecinstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch21Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH14_SIZE; /* force hash16 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //WmPrintInfo(&mpm_ctx);
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/videoaccesscodecinstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch21Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH15_SIZE; /* force hash16 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //WmPrintInfo(&mpm_ctx);
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/videoaccesscodecinstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch21Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"/videoaccesscodecinstall.exe", 28, 0, 0, 0, 0, 0);
+ ctx->hash_size = HASH16_SIZE; /* force hash16 */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 1);
+ //WmPrintInfo(&mpm_ctx);
+ ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"/videoaccesscodecinstall.exe", 28);
+
+ uint32_t len = mpm_thread_ctx.match[0].len;
+
+
+ if (len == 1)
+ result = 1;
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+#endif
+static int WmTestSearch22Hash9 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ ctx->hash_size = HASH9_SIZE; /* force hash size */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch22Hash12 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ ctx->hash_size = HASH12_SIZE; /* force hash size */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch22Hash14 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ ctx->hash_size = HASH14_SIZE; /* force hash size */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch22Hash15 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ ctx->hash_size = HASH15_SIZE; /* force hash size */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+
+static int WmTestSearch22Hash16 (void)
+{
+ int result = 0;
+ MpmCtx mpm_ctx;
+ memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
+ MpmThreadCtx mpm_thread_ctx;
+ MpmInitCtx(&mpm_ctx, MPM_WUMANBER);
+ WmCtx *ctx = (WmCtx *)mpm_ctx.ctx;
+
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); /* should match 30 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); /* should match 29 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); /* should match 28 times */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); /* 26 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); /* 21 */
+ MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, 0, 0, 5, 0, 0); /* 1 */
+ /* total matches: 135 */
+
+ ctx->hash_size = HASH16_SIZE; /* force hash size */
+ WmPreparePatterns(&mpm_ctx);
+ WmThreadInitCtx(&mpm_ctx, &mpm_thread_ctx, 6 /* 6 patterns */);
+
+ uint32_t cnt = ctx->Search(&mpm_ctx, &mpm_thread_ctx, NULL, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30);
+
+
+ if (cnt == 135)
+ result = 1;
+ else
+ printf("135 != %" PRIu32 " ",cnt);
+
+ WmThreadDestroyCtx(&mpm_ctx, &mpm_thread_ctx);
+ WmDestroyCtx(&mpm_ctx);
+ return result;
+}
+#endif /* UNITTESTS */
+
+void WmRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("WmTestInitCtx01", WmTestInitCtx01, 1);
+ UtRegisterTest("WmTestInitCtx02", WmTestInitCtx02, 1);
+ UtRegisterTest("WmTestInitCtx03", WmTestInitCtx03, 1);
+
+ UtRegisterTest("WmTestThreadInitCtx01", WmTestThreadInitCtx01, 1);
+ UtRegisterTest("WmTestThreadInitCtx02", WmTestThreadInitCtx02, 1);
+
+ UtRegisterTest("WmTestInitAddPattern01", WmTestInitAddPattern01, 1);
+ UtRegisterTest("WmTestInitAddPattern02", WmTestInitAddPattern02, 1);
+ UtRegisterTest("WmTestInitAddPattern03", WmTestInitAddPattern03, 1);
+ UtRegisterTest("WmTestInitAddPattern04", WmTestInitAddPattern04, 1);
+ UtRegisterTest("WmTestInitAddPattern05", WmTestInitAddPattern05, 1);
+ UtRegisterTest("WmTestInitAddPattern06", WmTestInitAddPattern06, 1);
+
+ UtRegisterTest("WmTestPrepare01", WmTestPrepare01, 1);
+ UtRegisterTest("WmTestPrepare02", WmTestPrepare02, 1);
+ UtRegisterTest("WmTestPrepare03", WmTestPrepare03, 1);
+ UtRegisterTest("WmTestPrepare04", WmTestPrepare01, 1);
+ UtRegisterTest("WmTestPrepare05", WmTestPrepare02, 1);
+ UtRegisterTest("WmTestPrepare06", WmTestPrepare03, 1);
+
+ UtRegisterTest("WmTestSearch01", WmTestSearch01, 1);
+ UtRegisterTest("WmTestSearch01Hash12", WmTestSearch01Hash12, 1);
+ UtRegisterTest("WmTestSearch01Hash14", WmTestSearch01Hash14, 1);
+ UtRegisterTest("WmTestSearch01Hash15", WmTestSearch01Hash15, 1);
+ UtRegisterTest("WmTestSearch01Hash16", WmTestSearch01Hash16, 1);
+
+ UtRegisterTest("WmTestSearch02", WmTestSearch02, 1);
+ UtRegisterTest("WmTestSearch03", WmTestSearch03, 1);
+ UtRegisterTest("WmTestSearch04", WmTestSearch04, 1);
+ UtRegisterTest("WmTestSearch05", WmTestSearch05, 1);
+ UtRegisterTest("WmTestSearch06", WmTestSearch06, 1);
+ UtRegisterTest("WmTestSearch07", WmTestSearch07, 1);
+ UtRegisterTest("WmTestSearch08", WmTestSearch08, 1);
+ UtRegisterTest("WmTestSearch09", WmTestSearch09, 1);
+ UtRegisterTest("WmTestSearch10", WmTestSearch10, 1);
+ UtRegisterTest("WmTestSearch11", WmTestSearch11, 1);
+ UtRegisterTest("WmTestSearch12", WmTestSearch12, 1);
+ UtRegisterTest("WmTestSearch13", WmTestSearch13, 1);
+
+ UtRegisterTest("WmTestSearch14", WmTestSearch14, 1);
+#if 0
+ UtRegisterTest("WmTestSearch15", WmTestSearch15, 1);
+ UtRegisterTest("WmTestSearch16", WmTestSearch16, 1);
+ UtRegisterTest("WmTestSearch17", WmTestSearch17, 1);
+
+ UtRegisterTest("WmTestSearch18", WmTestSearch18, 1);
+ UtRegisterTest("WmTestSearch18Hash12", WmTestSearch18Hash12, 1);
+ UtRegisterTest("WmTestSearch18Hash14", WmTestSearch18Hash14, 1);
+ UtRegisterTest("WmTestSearch18Hash15", WmTestSearch18Hash15, 1);
+ UtRegisterTest("WmTestSearch18Hash16", WmTestSearch18Hash16, 1);
+
+ UtRegisterTest("WmTestSearch19", WmTestSearch19, 1);
+ UtRegisterTest("WmTestSearch19Hash12", WmTestSearch19Hash12, 1);
+ UtRegisterTest("WmTestSearch19Hash14", WmTestSearch19Hash14, 1);
+ UtRegisterTest("WmTestSearch19Hash15", WmTestSearch19Hash15, 1);
+ UtRegisterTest("WmTestSearch19Hash16", WmTestSearch19Hash16, 1);
+
+ UtRegisterTest("WmTestSearch20", WmTestSearch20, 1);
+ UtRegisterTest("WmTestSearch20Hash12", WmTestSearch20Hash12, 1);
+ UtRegisterTest("WmTestSearch20Hash14", WmTestSearch20Hash14, 1);
+ UtRegisterTest("WmTestSearch20Hash15", WmTestSearch20Hash15, 1);
+ UtRegisterTest("WmTestSearch20Hash16", WmTestSearch20Hash16, 1);
+
+ UtRegisterTest("WmTestSearch21", WmTestSearch21, 1);
+ UtRegisterTest("WmTestSearch21Hash12", WmTestSearch21Hash12, 1);
+ UtRegisterTest("WmTestSearch21Hash14", WmTestSearch21Hash14, 1);
+ UtRegisterTest("WmTestSearch21Hash15", WmTestSearch21Hash15, 1);
+ UtRegisterTest("WmTestSearch21Hash16", WmTestSearch21Hash16, 1);
+#endif
+ UtRegisterTest("WmTestSearch22Hash9", WmTestSearch22Hash9, 1);
+ UtRegisterTest("WmTestSearch22Hash12", WmTestSearch22Hash12, 1);
+ UtRegisterTest("WmTestSearch22Hash14", WmTestSearch22Hash14, 1);
+ UtRegisterTest("WmTestSearch22Hash15", WmTestSearch22Hash15, 1);
+ UtRegisterTest("WmTestSearch22Hash16", WmTestSearch22Hash16, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-mpm-wumanber.h b/framework/src/suricata/src/util-mpm-wumanber.h
new file mode 100644
index 00000000..ef699814
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm-wumanber.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_MPM_WUMANBER_H__
+#define __UTIL_MPM_WUMANBER_H__
+
+#include "util-mpm.h"
+#include "util-bloomfilter.h"
+
+//#define WUMANBER_COUNTERS
+
+typedef struct WmPattern_ {
+ uint8_t *cs; /* case sensitive */
+ uint8_t *ci; /* case INsensitive */
+ uint16_t len;
+ struct WmPattern_ *next;
+ uint16_t prefix_ci;
+ uint16_t prefix_cs;
+ uint8_t flags;
+ uint32_t id; /* global pattern id */
+
+ /* sid(s) for this pattern */
+ uint32_t sids_size;
+ SigIntId *sids;
+
+} WmPattern;
+
+typedef struct WmHashItem_ {
+ uint8_t flags;
+ uint16_t idx;
+ struct WmHashItem_ *nxt;
+} WmHashItem;
+
+typedef struct WmCtx_ {
+ /* hash used during ctx initialization */
+ WmPattern **init_hash;
+
+ uint16_t shiftlen;
+
+ uint32_t hash_size;
+ WmHashItem **hash;
+ BloomFilter **bloom;
+ uint8_t *pminlen; /* array containing the minimal length
+ of the patters in a hash bucket. Used
+ for the BloomFilter. */
+ WmHashItem hash1[256];
+
+ /* we store our own search func ptr here for WmSearch1 */
+ uint32_t (*Search)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+ /* we store our own multi byte search func ptr here for WmSearch1 */
+ uint32_t (*MBSearch)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+
+ /* pattern arrays */
+ WmPattern **parray;
+
+ /* only used for multibyte pattern search */
+ uint16_t *shifttable;
+} WmCtx;
+
+typedef struct WmThreadCtx_ {
+#ifdef WUMANBER_COUNTERS
+ uint32_t stat_pminlen_calls;
+ uint32_t stat_pminlen_total;
+ uint32_t stat_bloom_calls;
+ uint32_t stat_bloom_hits;
+ uint32_t stat_shift_null;
+ uint32_t stat_loop_match;
+ uint32_t stat_loop_no_match;
+ uint32_t stat_num_shift;
+ uint32_t stat_total_shift;
+#endif /* WUMANBER_COUNTERS */
+} WmThreadCtx;
+
+void MpmWuManberRegister(void);
+
+#endif /* __UTIL_MPM_WUMANBER_H__ */
+
diff --git a/framework/src/suricata/src/util-mpm.c b/framework/src/suricata/src/util-mpm.c
new file mode 100644
index 00000000..ac185e30
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm.c
@@ -0,0 +1,785 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pattern matcher utility Functions
+ */
+
+#include "suricata-common.h"
+#include "util-mpm.h"
+#include "util-debug.h"
+
+/* include pattern matchers */
+#include "util-mpm-wumanber.h"
+#include "util-mpm-b2g.h"
+#include "util-mpm-b3g.h"
+#include "util-mpm-ac.h"
+#include "util-mpm-ac-gfbs.h"
+#include "util-mpm-ac-bs.h"
+#include "util-mpm-ac-tile.h"
+#include "util-hashlist.h"
+
+#include "detect-engine.h"
+#include "util-cuda.h"
+#include "util-misc.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+#include "queue.h"
+#include "util-unittest.h"
+#ifdef __SC_CUDA_SUPPORT__
+#include "util-cuda-handlers.h"
+#include "detect-engine-mpm.h"
+#endif
+
+/**
+ * \brief Register a new Mpm Context.
+ *
+ * \param name A new profile to be registered to store this MpmCtx.
+ *
+ * \retval id Return the id created for the new MpmCtx profile.
+ */
+int32_t MpmFactoryRegisterMpmCtxProfile(DetectEngineCtx *de_ctx, const char *name, uint8_t flags)
+{
+ void *ptmp;
+ /* the very first entry */
+ if (de_ctx->mpm_ctx_factory_container == NULL) {
+ de_ctx->mpm_ctx_factory_container = SCMalloc(sizeof(MpmCtxFactoryContainer));
+ if (de_ctx->mpm_ctx_factory_container == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(de_ctx->mpm_ctx_factory_container, 0, sizeof(MpmCtxFactoryContainer));
+
+ MpmCtxFactoryItem *item = SCMalloc(sizeof(MpmCtxFactoryItem));
+ if (unlikely(item == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+
+ item[0].name = SCStrdup(name);
+ if (item[0].name == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+
+ /* toserver */
+ item[0].mpm_ctx_ts = SCMalloc(sizeof(MpmCtx));
+ if (item[0].mpm_ctx_ts == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(item[0].mpm_ctx_ts, 0, sizeof(MpmCtx));
+ item[0].mpm_ctx_ts->global = 1;
+
+ /* toclient */
+ item[0].mpm_ctx_tc = SCMalloc(sizeof(MpmCtx));
+ if (item[0].mpm_ctx_tc == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(item[0].mpm_ctx_tc, 0, sizeof(MpmCtx));
+ item[0].mpm_ctx_tc->global = 1;
+
+ /* our id starts from 0 always. Helps us with the ctx retrieval from
+ * the array */
+ item[0].id = 0;
+
+ /* store the flag */
+ item[0].flags = flags;
+
+ /* store the newly created item */
+ de_ctx->mpm_ctx_factory_container->items = item;
+ de_ctx->mpm_ctx_factory_container->no_of_items++;
+
+ /* the first id is always 0 */
+ return item[0].id;
+ } else {
+ int i;
+ MpmCtxFactoryItem *items = de_ctx->mpm_ctx_factory_container->items;
+ for (i = 0; i < de_ctx->mpm_ctx_factory_container->no_of_items; i++) {
+ if (items[i].name != NULL && strcmp(items[i].name, name) == 0) {
+ /* looks like we have this mpm_ctx freed */
+ if (items[i].mpm_ctx_ts == NULL) {
+ items[i].mpm_ctx_ts = SCMalloc(sizeof(MpmCtx));
+ if (items[i].mpm_ctx_ts == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(items[i].mpm_ctx_ts, 0, sizeof(MpmCtx));
+ items[i].mpm_ctx_ts->global = 1;
+ }
+ if (items[i].mpm_ctx_tc == NULL) {
+ items[i].mpm_ctx_tc = SCMalloc(sizeof(MpmCtx));
+ if (items[i].mpm_ctx_tc == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(items[i].mpm_ctx_tc, 0, sizeof(MpmCtx));
+ items[i].mpm_ctx_tc->global = 1;
+ }
+ items[i].flags = flags;
+ return items[i].id;
+ }
+ }
+
+ /* let's make the new entry */
+ ptmp = SCRealloc(items,
+ (de_ctx->mpm_ctx_factory_container->no_of_items + 1) * sizeof(MpmCtxFactoryItem));
+ if (unlikely(ptmp == NULL)) {
+ SCFree(items);
+ items = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ items = ptmp;
+
+ de_ctx->mpm_ctx_factory_container->items = items;
+
+ MpmCtxFactoryItem *new_item = &items[de_ctx->mpm_ctx_factory_container->no_of_items];
+ new_item[0].name = SCStrdup(name);
+ if (new_item[0].name == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+
+ /* toserver */
+ new_item[0].mpm_ctx_ts = SCMalloc(sizeof(MpmCtx));
+ if (new_item[0].mpm_ctx_ts == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(new_item[0].mpm_ctx_ts, 0, sizeof(MpmCtx));
+ new_item[0].mpm_ctx_ts->global = 1;
+
+ /* toclient */
+ new_item[0].mpm_ctx_tc = SCMalloc(sizeof(MpmCtx));
+ if (new_item[0].mpm_ctx_tc == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(new_item[0].mpm_ctx_tc, 0, sizeof(MpmCtx));
+ new_item[0].mpm_ctx_tc->global = 1;
+
+ new_item[0].id = de_ctx->mpm_ctx_factory_container->no_of_items;
+ new_item[0].flags = flags;
+ de_ctx->mpm_ctx_factory_container->no_of_items++;
+
+ /* the newly created id */
+ return new_item[0].id;
+ }
+}
+
+int32_t MpmFactoryIsMpmCtxAvailable(DetectEngineCtx *de_ctx, MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx == NULL)
+ return 0;
+
+ if (de_ctx->mpm_ctx_factory_container == NULL) {
+ return 0;
+ } else {
+ int i;
+ for (i = 0; i < de_ctx->mpm_ctx_factory_container->no_of_items; i++) {
+ if (mpm_ctx == de_ctx->mpm_ctx_factory_container->items[i].mpm_ctx_ts ||
+ mpm_ctx == de_ctx->mpm_ctx_factory_container->items[i].mpm_ctx_tc) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+}
+
+MpmCtx *MpmFactoryGetMpmCtxForProfile(DetectEngineCtx *de_ctx, int32_t id, int direction)
+{
+ if (id == MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ MpmCtx *mpm_ctx = SCMalloc(sizeof(MpmCtx));
+ if (unlikely(mpm_ctx == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ memset(mpm_ctx, 0, sizeof(MpmCtx));
+ return mpm_ctx;
+ } else if (id < -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument - %d\n", id);
+ return NULL;
+ } else if (id >= de_ctx->mpm_ctx_factory_container->no_of_items) {
+ /* this id does not exist */
+ return NULL;
+ } else {
+ return (direction == 0) ?
+ de_ctx->mpm_ctx_factory_container->items[id].mpm_ctx_ts :
+ de_ctx->mpm_ctx_factory_container->items[id].mpm_ctx_tc;
+ }
+}
+
+void MpmFactoryReClaimMpmCtx(DetectEngineCtx *de_ctx, MpmCtx *mpm_ctx)
+{
+ if (mpm_ctx == NULL)
+ return;
+
+ if (!MpmFactoryIsMpmCtxAvailable(de_ctx, mpm_ctx)) {
+ if (mpm_ctx->mpm_type != MPM_NOTSET)
+ mpm_table[mpm_ctx->mpm_type].DestroyCtx(mpm_ctx);
+ SCFree(mpm_ctx);
+ }
+
+ return;
+}
+
+void MpmFactoryDeRegisterAllMpmCtxProfiles(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->mpm_ctx_factory_container == NULL)
+ return;
+
+ int i = 0;
+ MpmCtxFactoryItem *items = de_ctx->mpm_ctx_factory_container->items;
+ for (i = 0; i < de_ctx->mpm_ctx_factory_container->no_of_items; i++) {
+ if (items[i].name != NULL)
+ SCFree(items[i].name);
+ if (items[i].mpm_ctx_ts != NULL) {
+ if (items[i].mpm_ctx_ts->mpm_type != MPM_NOTSET)
+ mpm_table[items[i].mpm_ctx_ts->mpm_type].DestroyCtx(items[i].mpm_ctx_ts);
+ SCFree(items[i].mpm_ctx_ts);
+ }
+ if (items[i].mpm_ctx_tc != NULL) {
+ if (items[i].mpm_ctx_tc->mpm_type != MPM_NOTSET)
+ mpm_table[items[i].mpm_ctx_tc->mpm_type].DestroyCtx(items[i].mpm_ctx_tc);
+ SCFree(items[i].mpm_ctx_tc);
+ }
+ }
+
+ SCFree(de_ctx->mpm_ctx_factory_container->items);
+ SCFree(de_ctx->mpm_ctx_factory_container);
+ de_ctx->mpm_ctx_factory_container = NULL;
+
+ return;
+}
+
+#ifdef __SC_CUDA_SUPPORT__
+
+static void MpmCudaConfFree(void *conf)
+{
+ SCFree(conf);
+ return;
+}
+
+static void *MpmCudaConfParse(ConfNode *node)
+{
+ const char *value;
+
+ MpmCudaConf *conf = SCMalloc(sizeof(MpmCudaConf));
+ if (unlikely(conf == NULL))
+ exit(EXIT_FAILURE);
+ memset(conf, 0, sizeof(*conf));
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "data-buffer-size-min-limit");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->data_buffer_size_min_limit = UTIL_MPM_CUDA_DATA_BUFFER_SIZE_MIN_LIMIT_DEFAULT;
+ } else if (ParseSizeStringU16(value, &conf->data_buffer_size_min_limit) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "data-buffer-size-min-limit - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "data-buffer-size-max-limit");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->data_buffer_size_max_limit = UTIL_MPM_CUDA_DATA_BUFFER_SIZE_MAX_LIMIT_DEFAULT;
+ } else if (ParseSizeStringU16(value, &conf->data_buffer_size_max_limit) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "data-buffer-size-max-limit - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "cudabuffer-buffer-size");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->cb_buffer_size = UTIL_MPM_CUDA_CUDA_BUFFER_DBUFFER_SIZE_DEFAULT;
+ } else if (ParseSizeStringU32(value, &conf->cb_buffer_size) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "cb-buffer-size - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "gpu-transfer-size");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->gpu_transfer_size = UTIL_MPM_CUDA_GPU_TRANSFER_SIZE;
+ } else if (ParseSizeStringU32(value, &conf->gpu_transfer_size) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "gpu-transfer-size - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "batching-timeout");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->batching_timeout = UTIL_MPM_CUDA_BATCHING_TIMEOUT_DEFAULT;
+ } else if ((conf->batching_timeout = atoi(value)) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "batching-timeout - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "device-id");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->device_id = UTIL_MPM_CUDA_DEVICE_ID_DEFAULT;
+ } else if ((conf->device_id = atoi(value)) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "device-id - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ if (node != NULL)
+ value = ConfNodeLookupChildValue(node, "cuda-streams");
+ else
+ value = NULL;
+ if (value == NULL) {
+ /* default */
+ conf->cuda_streams = UTIL_MPM_CUDA_CUDA_STREAMS_DEFAULT;
+ } else if ((conf->cuda_streams = atoi(value)) < 0) {
+ SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for %s."
+ "cuda-streams - \"%s\"", node->name, value);
+ exit(EXIT_FAILURE);
+ }
+
+ return conf;
+}
+
+void MpmCudaEnvironmentSetup()
+{
+ if (PatternMatchDefaultMatcher() != MPM_AC_CUDA)
+ return;
+
+ CudaHandlerAddCudaProfileFromConf("mpm", MpmCudaConfParse, MpmCudaConfFree);
+
+ MpmCudaConf *conf = CudaHandlerGetCudaProfile("mpm");
+ if (conf == NULL) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error obtaining cuda mpm "
+ "profile.");
+ exit(EXIT_FAILURE);
+ }
+
+ if (MpmCudaBufferSetup() < 0) {
+ SCLogError(SC_ERR_AC_CUDA_ERROR, "Error setting up env for ac "
+ "cuda");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+}
+
+#endif
+
+/**
+ * \brief Setup a pmq
+ *
+ * \param pmq Pattern matcher queue to be initialized
+ * \param maxid Max sig id to be matched on
+ * \param patmaxid Max pattern id to be matched on
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int PmqSetup(PatternMatcherQueue *pmq, uint32_t patmaxid)
+{
+ SCEnter();
+ SCLogDebug("patmaxid %u", patmaxid);
+
+ if (pmq == NULL) {
+ SCReturnInt(-1);
+ }
+
+ memset(pmq, 0, sizeof(PatternMatcherQueue));
+
+ if (patmaxid > 0) {
+ pmq->pattern_id_array_size = 32; /* Intial size, TODO Make this configure option */
+ pmq->pattern_id_array_cnt = 0;
+
+ pmq->pattern_id_array = SCCalloc(pmq->pattern_id_array_size, sizeof(uint32_t));
+ if (pmq->pattern_id_array == NULL) {
+ SCReturnInt(-1);
+ }
+
+ /* lookup bitarray */
+ pmq->pattern_id_bitarray_size = (patmaxid / 8) + 1;
+
+ pmq->pattern_id_bitarray = SCMalloc(pmq->pattern_id_bitarray_size);
+ if (pmq->pattern_id_bitarray == NULL) {
+ SCReturnInt(-1);
+ }
+ memset(pmq->pattern_id_bitarray, 0, pmq->pattern_id_bitarray_size);
+
+ SCLogDebug("pmq->pattern_id_array %p, pmq->pattern_id_bitarray %p",
+ pmq->pattern_id_array, pmq->pattern_id_bitarray);
+
+ pmq->rule_id_array_size = 128; /* Initial size, TODO: Make configure option. */
+ pmq->rule_id_array_cnt = 0;
+
+ size_t bytes = pmq->rule_id_array_size * sizeof(SigIntId);
+ pmq->rule_id_array = (SigIntId*)SCMalloc(bytes);
+ if (pmq->rule_id_array == NULL) {
+ pmq->rule_id_array_size = 0;
+ SCReturnInt(-1);
+ }
+ // Don't need to zero memory since it is always written first.
+ }
+
+ SCReturnInt(0);
+}
+
+/** \brief Add array of Signature IDs to rule ID array.
+ *
+ * Checks size of the array first
+ *
+ * \param pmq storage for match results
+ * \param new_size number of Signature IDs needing to be stored.
+ *
+ */
+int
+MpmAddSidsResize(PatternMatcherQueue *pmq, uint32_t new_size)
+{
+ /* Need to make the array bigger. Double the size needed to
+ * also handle the case that sids_size might still be
+ * larger than the old size.
+ */
+ new_size = new_size * 2;
+ SigIntId *new_array = (SigIntId*)SCRealloc(pmq->rule_id_array,
+ new_size * sizeof(SigIntId));
+ if (unlikely(new_array == NULL)) {
+ /* Try again just big enough. */
+ new_size = new_size / 2;
+ new_array = (SigIntId*)SCRealloc(pmq->rule_id_array,
+ new_size * sizeof(SigIntId));
+ if (unlikely(new_array == NULL)) {
+
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to realloc PatternMatchQueue"
+ " rule ID array. Some signature ID matches lost");
+ return 0;
+ }
+ }
+ pmq->rule_id_array = new_array;
+ pmq->rule_id_array_size = new_size;
+
+ return new_size;
+}
+
+/** \brief Increase the size of the Pattern rule ID array.
+ *
+ * \param pmq storage for match results
+ * \param new_size number of Signature IDs needing to be stored.
+ *
+ * \return 0 on failure.
+ */
+int
+MpmAddPidResize(PatternMatcherQueue *pmq, uint32_t new_size)
+{
+ /* Need to make the array bigger. Double the size needed to
+ * also handle the case that sids_size might still be
+ * larger than the old size.
+ */
+ new_size = new_size * 2;
+ uint32_t *new_array = (uint32_t*)SCRealloc(pmq->pattern_id_array,
+ new_size * sizeof(uint32_t));
+ if (unlikely(new_array == NULL)) {
+ // Failed to allocate 2x, so try 1x.
+ new_size = new_size / 2;
+ new_array = (uint32_t*)SCRealloc(pmq->pattern_id_array,
+ new_size * sizeof(uint32_t));
+ if (unlikely(new_array == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to realloc PatternMatchQueue"
+ " pattern ID array. Some new Pattern ID matches were lost.");
+ return 0;
+ }
+ }
+ pmq->pattern_id_array = new_array;
+ pmq->pattern_id_array_size = new_size;
+
+ return new_size;
+}
+
+/** \brief Verify and store a match
+ *
+ * used at search runtime
+ *
+ * \param thread_ctx mpm thread ctx
+ * \param pmq storage for match results
+ * \param patid pattern ID being checked
+ * \param bitarray Array of bits for patterns IDs found in current search
+ * \param sids pointer to array of Signature IDs
+ * \param sids_size number of Signature IDs in sids array.
+ *
+ * \retval 0 no match after all
+ * \retval 1 (new) match
+ */
+int
+MpmVerifyMatch(MpmThreadCtx *thread_ctx, PatternMatcherQueue *pmq, uint32_t patid,
+ uint8_t *bitarray, SigIntId *sids, uint32_t sids_size)
+{
+ SCEnter();
+
+ /* Handle pattern id storage */
+ if (pmq != NULL && pmq->pattern_id_bitarray != NULL) {
+ SCLogDebug("using pattern id arrays, storing %"PRIu32, patid);
+
+ if ((bitarray[(patid / 8)] & (1<<(patid % 8))) == 0) {
+ bitarray[(patid / 8)] |= (1<<(patid % 8));
+ /* flag this pattern id as being added now */
+ pmq->pattern_id_bitarray[(patid / 8)] |= (1<<(patid % 8));
+ /* append the pattern_id to the array with matches */
+ MpmAddPid(pmq, patid);
+ MpmAddSids(pmq, sids, sids_size);
+ }
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief Merge two pmq's bitarrays
+ *
+ * \param src source pmq
+ * \param dst destination pmq to merge into
+ */
+void PmqMerge(PatternMatcherQueue *src, PatternMatcherQueue *dst)
+{
+ uint32_t u;
+
+ if (src->pattern_id_array_cnt == 0)
+ return;
+
+ for (u = 0; u < src->pattern_id_bitarray_size && u < dst->pattern_id_bitarray_size; u++) {
+ dst->pattern_id_bitarray[u] |= src->pattern_id_bitarray[u];
+ }
+
+ /** \todo now set merged flag? */
+
+ if (src->rule_id_array && dst->rule_id_array) {
+ MpmAddSids(dst, src->rule_id_array, src->rule_id_array_cnt);
+ }
+}
+
+/** \brief Reset a Pmq for reusage. Meant to be called after a single search.
+ * \param pmq Pattern matcher to be reset.
+ * \todo memset is expensive, but we need it as we merge pmq's. We might use
+ * a flag so we can clear pmq's the old way if we can.
+ */
+void PmqReset(PatternMatcherQueue *pmq)
+{
+ if (pmq == NULL)
+ return;
+
+ memset(pmq->pattern_id_bitarray, 0, pmq->pattern_id_bitarray_size);
+
+ pmq->pattern_id_array_cnt = 0;
+
+ pmq->rule_id_array_cnt = 0;
+ /* TODO: Realloc the rule id array smaller at some size? */
+}
+
+/** \brief Cleanup a Pmq
+ * \param pmq Pattern matcher queue to be cleaned up.
+ */
+void PmqCleanup(PatternMatcherQueue *pmq)
+{
+ if (pmq == NULL)
+ return;
+
+ if (pmq->pattern_id_array != NULL) {
+ SCFree(pmq->pattern_id_array);
+ pmq->pattern_id_array = NULL;
+ }
+
+ if (pmq->pattern_id_bitarray != NULL) {
+ SCFree(pmq->pattern_id_bitarray);
+ pmq->pattern_id_bitarray = NULL;
+ }
+
+ if (pmq->rule_id_array != NULL) {
+ SCFree(pmq->rule_id_array);
+ pmq->rule_id_array = NULL;
+ }
+
+ pmq->pattern_id_array_cnt = 0;
+ pmq->pattern_id_array_size = 0;
+}
+
+/** \brief Cleanup and free a Pmq
+ * \param pmq Pattern matcher queue to be free'd.
+ */
+void PmqFree(PatternMatcherQueue *pmq)
+{
+ if (pmq == NULL)
+ return;
+
+ PmqCleanup(pmq);
+}
+
+void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, uint16_t matcher, uint32_t max_id)
+{
+ mpm_table[matcher].InitThreadCtx(NULL, mpm_thread_ctx, max_id);
+}
+
+void MpmInitCtx (MpmCtx *mpm_ctx, uint16_t matcher)
+{
+ mpm_ctx->mpm_type = matcher;
+ mpm_table[matcher].InitCtx(mpm_ctx);
+}
+
+void MpmTableSetup(void)
+{
+ memset(mpm_table, 0, sizeof(mpm_table));
+
+ MpmWuManberRegister();
+ MpmB2gRegister();
+ MpmB3gRegister();
+ MpmACRegister();
+ MpmACBSRegister();
+ MpmACGfbsRegister();
+ MpmACTileRegister();
+#ifdef __SC_CUDA_SUPPORT__
+ MpmACCudaRegister();
+#endif /* __SC_CUDA_SUPPORT__ */
+}
+
+/** \brief Function to return the default hash size for the mpm algorithm,
+ * which has been defined by the user in the config file
+ *
+ * \param conf_val pointer to the string value of hash size
+ * \retval hash_value returns the hash value as defined by user, otherwise
+ * default low size value
+ */
+uint32_t MpmGetHashSize(const char *conf_val)
+{
+ SCEnter();
+ uint32_t hash_value = HASHSIZE_LOW;
+
+ if(strcmp(conf_val, "lowest") == 0) {
+ hash_value = HASHSIZE_LOWEST;
+ } else if(strcmp(conf_val, "low") == 0) {
+ hash_value = HASHSIZE_LOW;
+ } else if(strcmp(conf_val, "medium") == 0) {
+ hash_value = HASHSIZE_MEDIUM;
+ } else if(strcmp(conf_val, "high") == 0) {
+ hash_value = HASHSIZE_HIGH;
+ /* "highest" is supported in 1.0 to 1.0.2, so we keep supporting
+ * it for backwards compatibility */
+ } else if(strcmp(conf_val, "highest") == 0) {
+ hash_value = HASHSIZE_HIGHER;
+ } else if(strcmp(conf_val, "higher") == 0) {
+ hash_value = HASHSIZE_HIGHER;
+ } else if(strcmp(conf_val, "max") == 0) {
+ hash_value = HASHSIZE_MAX;
+ }
+
+ SCReturnInt(hash_value);
+}
+
+/** \brief Function to return the default bloomfilter size for the mpm algorithm,
+ * which has been defined by the user in the config file
+ *
+ * \param conf_val pointer to the string value of bloom filter size
+ * \retval bloom_value returns the bloom filter value as defined by user,
+ * otherwise default medium size value
+ */
+uint32_t MpmGetBloomSize(const char *conf_val)
+{
+ SCEnter();
+ uint32_t bloom_value = BLOOMSIZE_MEDIUM;
+
+ if(strncmp(conf_val, "low", 3) == 0) {
+ bloom_value = BLOOMSIZE_LOW;
+ } else if(strncmp(conf_val, "medium", 6) == 0) {
+ bloom_value = BLOOMSIZE_MEDIUM;
+ } else if(strncmp(conf_val, "high", 4) == 0) {
+ bloom_value = BLOOMSIZE_HIGH;
+ }
+
+ SCReturnInt(bloom_value);
+}
+
+int MpmAddPatternCS(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth,
+ uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ return mpm_table[mpm_ctx->mpm_type].AddPattern(mpm_ctx, pat, patlen,
+ offset, depth,
+ pid, sid, flags);
+}
+
+int MpmAddPatternCI(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth,
+ uint32_t pid, SigIntId sid, uint8_t flags)
+{
+ return mpm_table[mpm_ctx->mpm_type].AddPatternNocase(mpm_ctx, pat, patlen,
+ offset, depth,
+ pid, sid, flags);
+}
+
+
+
+/************************************Unittests*********************************/
+
+#ifdef UNITTESTS
+#endif /* UNITTESTS */
+
+void MpmRegisterTests(void)
+{
+#ifdef UNITTESTS
+ uint16_t i;
+
+ for (i = 0; i < MPM_TABLE_SIZE; i++) {
+ if (i == MPM_NOTSET)
+ continue;
+
+ g_ut_modules++;
+
+ if (mpm_table[i].RegisterUnittests != NULL) {
+ g_ut_covered++;
+ mpm_table[i].RegisterUnittests();
+ } else {
+ if (coverage_unittests)
+ SCLogWarning(SC_WARN_NO_UNITTESTS, "mpm module %s has no "
+ "unittest registration function.", mpm_table[i].name);
+ }
+ }
+
+#endif
+}
diff --git a/framework/src/suricata/src/util-mpm.h b/framework/src/suricata/src/util-mpm.h
new file mode 100644
index 00000000..da650884
--- /dev/null
+++ b/framework/src/suricata/src/util-mpm.h
@@ -0,0 +1,324 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_MPM_H__
+#define __UTIL_MPM_H__
+#include "suricata-common.h"
+
+#define MPM_ENDMATCH_SINGLE 0x01 /**< A single match is sufficient. No
+ depth, offset, etc settings. */
+#define MPM_ENDMATCH_OFFSET 0x02 /**< has offset setting */
+#define MPM_ENDMATCH_DEPTH 0x04 /**< has depth setting */
+#define MPM_ENDMATCH_NOSEARCH 0x08 /**< if this matches, no search is
+ required (for this pattern) */
+
+#define HASHSIZE_LOWEST 2048 /**< Lowest hash size for the multi
+ pattern matcher algorithms */
+#define HASHSIZE_LOW 4096 /**< Low hash size for the multi
+ pattern matcher algorithms */
+#define HASHSIZE_MEDIUM 8192 /**< Medium hash size for the multi
+ pattern matcher algorithms */
+#define HASHSIZE_HIGH 16384 /**< High hash size for the multi
+ pattern matcher algorithms */
+#define HASHSIZE_HIGHER 32768 /**< Higher hash size for the multi
+ pattern matcher algorithms */
+#define HASHSIZE_MAX 65536 /**< Max hash size for the multi
+ pattern matcher algorithms */
+#define BLOOMSIZE_LOW 512 /*<* Low bloomfilter size for the multi
+ pattern matcher algorithms */
+#define BLOOMSIZE_MEDIUM 1024 /**< Medium bloomfilter size for the multi
+ pattern matcher algorithms */
+#define BLOOMSIZE_HIGH 2048 /**< High bloomfilter size for the multi
+ pattern matcher algorithms */
+
+enum {
+ MPM_NOTSET = 0,
+
+ /* wumanber as the name suggests */
+ MPM_WUMANBER,
+ /* bndmq 2 gram */
+ MPM_B2G,
+ /* bndmq 3 gram */
+ MPM_B3G,
+ MPM_B2GC,
+ MPM_B2GM,
+
+ /* aho-corasick */
+ MPM_AC,
+#ifdef __SC_CUDA_SUPPORT__
+ MPM_AC_CUDA,
+#endif
+ /* aho-corasick-goto-failure state based */
+ MPM_AC_GFBS,
+ MPM_AC_BS,
+ MPM_AC_TILE,
+ /* table size */
+ MPM_TABLE_SIZE,
+};
+
+#ifdef __tile__
+#define DEFAULT_MPM MPM_AC_TILE
+#else
+#define DEFAULT_MPM MPM_AC
+#endif
+
+/* Internal Pattern Index: 0 to pattern_cnt-1 */
+typedef uint32_t MpmPatternIndex;
+
+typedef struct MpmMatchBucket_ {
+ uint32_t len;
+} MpmMatchBucket;
+
+typedef struct MpmThreadCtx_ {
+ void *ctx;
+
+ uint32_t memory_cnt;
+ uint32_t memory_size;
+
+} MpmThreadCtx;
+
+/** \brief helper structure for the pattern matcher engine. The Pattern Matcher
+ * thread has this and passes a pointer to it to the pattern matcher.
+ * The actual pattern matcher will fill the structure. */
+typedef struct PatternMatcherQueue_ {
+ uint32_t *pattern_id_array; /** array with pattern id's that had a
+ pattern match. These will be inspected
+ futher by the detection engine. */
+ uint32_t pattern_id_array_cnt; /**< Number currently stored */
+ uint32_t pattern_id_array_size; /**< Allocated size in bytes */
+
+ uint8_t *pattern_id_bitarray; /** bitarray with pattern id matches */
+ uint32_t pattern_id_bitarray_size; /**< size in bytes */
+
+ /* used for storing rule id's */
+ /* Array of rule IDs found. */
+ SigIntId *rule_id_array;
+ /* Number of rule IDs in the array. */
+ uint32_t rule_id_array_cnt;
+ /* The number of slots allocated for storing rule IDs */
+ uint32_t rule_id_array_size;
+
+} PatternMatcherQueue;
+
+typedef struct MpmCtx_ {
+ void *ctx;
+ uint16_t mpm_type;
+
+ /* Indicates if this a global mpm_ctx. Global mpm_ctx is the one that
+ * is instantiated when we use "single". Non-global is "full", i.e.
+ * one per sgh. We are using a uint16_t here to avoiding using a pad.
+ * You can use a uint8_t here as well. */
+ uint16_t global;
+
+ /* unique patterns */
+ uint32_t pattern_cnt;
+
+ uint16_t minlen;
+ uint16_t maxlen;
+
+ uint32_t memory_cnt;
+ uint32_t memory_size;
+} MpmCtx;
+
+/* if we want to retrieve an unique mpm context from the mpm context factory
+ * we should supply this as the key */
+#define MPM_CTX_FACTORY_UNIQUE_CONTEXT -1
+
+#define MPM_CTX_FACTORY_FLAGS_PREPARE_WITH_SIG_GROUP_BUILD 0x01
+
+typedef struct MpmCtxFactoryItem_ {
+ char *name;
+ MpmCtx *mpm_ctx_ts;
+ MpmCtx *mpm_ctx_tc;
+ int32_t id;
+ uint8_t flags;
+} MpmCtxFactoryItem;
+
+typedef struct MpmCtxFactoryContainer_ {
+ MpmCtxFactoryItem *items;
+ int32_t no_of_items;
+} MpmCtxFactoryContainer;
+
+/** pattern is case insensitive */
+#define MPM_PATTERN_FLAG_NOCASE 0x01
+/** pattern is negated */
+#define MPM_PATTERN_FLAG_NEGATED 0x02
+/** pattern has a depth setting */
+#define MPM_PATTERN_FLAG_DEPTH 0x04
+/** pattern has an offset setting */
+#define MPM_PATTERN_FLAG_OFFSET 0x08
+/** one byte pattern (used in b2g) */
+#define MPM_PATTERN_ONE_BYTE 0x10
+
+typedef struct MpmTableElmt_ {
+ char *name;
+ uint8_t max_pattern_length;
+ void (*InitCtx)(struct MpmCtx_ *);
+ void (*InitThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *, uint32_t);
+ void (*DestroyCtx)(struct MpmCtx_ *);
+ void (*DestroyThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *);
+
+ /** function pointers for adding patterns to the mpm ctx.
+ *
+ * \param mpm_ctx Mpm context to add the pattern to
+ * \param pattern pointer to the pattern
+ * \param pattern_len length of the pattern in bytes
+ * \param offset pattern offset setting
+ * \param depth pattern depth setting
+ * \param pid pattern id
+ * \param sid signature _internal_ id
+ * \param flags pattern flags
+ */
+ int (*AddPattern)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+ int (*AddPatternNocase)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
+ int (*Prepare)(struct MpmCtx_ *);
+ uint32_t (*Search)(struct MpmCtx_ *, struct MpmThreadCtx_ *, PatternMatcherQueue *, uint8_t *, uint16_t);
+ void (*Cleanup)(struct MpmThreadCtx_ *);
+ void (*PrintCtx)(struct MpmCtx_ *);
+ void (*PrintThreadCtx)(struct MpmThreadCtx_ *);
+ void (*RegisterUnittests)(void);
+ uint8_t flags;
+} MpmTableElmt;
+
+MpmTableElmt mpm_table[MPM_TABLE_SIZE];
+
+/* macros decides if cuda is enabled for the platform or not */
+#ifdef __SC_CUDA_SUPPORT__
+
+/* the min size limit of a payload(or any other data) to be buffered */
+#define UTIL_MPM_CUDA_DATA_BUFFER_SIZE_MIN_LIMIT_DEFAULT 0
+/* the max size limit of a payload(or any other data) to be buffered */
+#define UTIL_MPM_CUDA_DATA_BUFFER_SIZE_MAX_LIMIT_DEFAULT 1500
+/* Default value for data buffer used by cuda mpm engine for CudaBuffer reg */
+#define UTIL_MPM_CUDA_CUDA_BUFFER_DBUFFER_SIZE_DEFAULT 500 * 1024 * 1024
+/* Default value for the max data chunk that would be sent to gpu */
+#define UTIL_MPM_CUDA_GPU_TRANSFER_SIZE 50 * 1024 * 1024
+/* Default value for offset/pointer buffer to be used by cuda mpm
+ * engine for CudaBuffer reg */
+#define UTIL_MPM_CUDA_CUDA_BUFFER_OPBUFFER_ITEMS_DEFAULT 500000
+#define UTIL_MPM_CUDA_BATCHING_TIMEOUT_DEFAULT 2000
+#define UTIL_MPM_CUDA_CUDA_STREAMS_DEFAULT 2
+#define UTIL_MPM_CUDA_DEVICE_ID_DEFAULT 0
+
+/**
+ * \brief Cuda configuration for "mpm" profile. We can further extend this
+ * to have conf for specific mpms. For now its common for all mpms.
+ */
+typedef struct MpmCudaConf_ {
+ uint16_t data_buffer_size_min_limit;
+ uint16_t data_buffer_size_max_limit;
+ uint32_t cb_buffer_size;
+ uint32_t gpu_transfer_size;
+ int batching_timeout;
+ int device_id;
+ int cuda_streams;
+} MpmCudaConf;
+
+void MpmCudaEnvironmentSetup();
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+struct DetectEngineCtx_;
+
+int32_t MpmFactoryRegisterMpmCtxProfile(struct DetectEngineCtx_ *, const char *, uint8_t);
+void MpmFactoryReClaimMpmCtx(struct DetectEngineCtx_ *, MpmCtx *);
+MpmCtx *MpmFactoryGetMpmCtxForProfile(struct DetectEngineCtx_ *, int32_t, int);
+void MpmFactoryDeRegisterAllMpmCtxProfiles(struct DetectEngineCtx_ *);
+int32_t MpmFactoryIsMpmCtxAvailable(struct DetectEngineCtx_ *, MpmCtx *);
+
+int PmqSetup(PatternMatcherQueue *, uint32_t);
+void PmqMerge(PatternMatcherQueue *src, PatternMatcherQueue *dst);
+void PmqReset(PatternMatcherQueue *);
+void PmqCleanup(PatternMatcherQueue *);
+void PmqFree(PatternMatcherQueue *);
+
+void MpmTableSetup(void);
+void MpmRegisterTests(void);
+
+int MpmVerifyMatch(MpmThreadCtx *thread_ctx, PatternMatcherQueue *pmq, uint32_t patid,
+ uint8_t *bitarray, SigIntId *sids, uint32_t sids_size);
+void MpmInitCtx(MpmCtx *mpm_ctx, uint16_t matcher);
+void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, uint16_t, uint32_t);
+uint32_t MpmGetHashSize(const char *);
+uint32_t MpmGetBloomSize(const char *);
+
+int MpmAddPatternCS(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth,
+ uint32_t pid, SigIntId sid, uint8_t flags);
+int MpmAddPatternCI(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
+ uint16_t offset, uint16_t depth,
+ uint32_t pid, SigIntId sid, uint8_t flags);
+
+/* Resize Signature ID array. Only called from MpmAddSids(). */
+int MpmAddSidsResize(PatternMatcherQueue *pmq, uint32_t new_size);
+
+/** \brief Add array of Signature IDs to rule ID array.
+ *
+ * Checks size of the array first. Calls MpmAddSidsResize to increase
+ * The size of the array, since that is the slow path.
+ *
+ * \param pmq storage for match results
+ * \param sids pointer to array of Signature IDs
+ * \param sids_size number of Signature IDs in sids array.
+ *
+ */
+static inline void
+MpmAddSids(PatternMatcherQueue *pmq, SigIntId *sids, uint32_t sids_size)
+{
+ if (sids_size == 0)
+ return;
+
+ uint32_t new_size = pmq->rule_id_array_cnt + sids_size;
+ if (new_size > pmq->rule_id_array_size) {
+ if (MpmAddSidsResize(pmq, new_size) == 0) {
+ // Failed to allocate larger memory for all the SIDS, but
+ // keep as many as we can.
+ sids_size = pmq->rule_id_array_size - pmq->rule_id_array_cnt;
+ }
+ }
+ SCLogDebug("Adding %u sids", sids_size);
+ // Add SIDs for this pattern to the end of the array
+ SigIntId *ptr = pmq->rule_id_array + pmq->rule_id_array_cnt;
+ SigIntId *end = ptr + sids_size;
+ do {
+ *ptr++ = *sids++;
+ } while (ptr != end);
+ pmq->rule_id_array_cnt += sids_size;
+}
+
+/* Resize Pattern ID array. Only called from MpmAddPid(). */
+int MpmAddPidResize(PatternMatcherQueue *pmq, uint32_t new_size);
+
+static inline void
+MpmAddPid(PatternMatcherQueue *pmq, uint32_t patid)
+{
+ uint32_t new_size = pmq->pattern_id_array_cnt + 1;
+ if (new_size > pmq->pattern_id_array_size) {
+ if (MpmAddPidResize(pmq, new_size) == 0)
+ return;
+ }
+ pmq->pattern_id_array[pmq->pattern_id_array_cnt] = patid;
+ pmq->pattern_id_array_cnt = new_size;
+ SCLogDebug("pattern_id_array_cnt %u", pmq->pattern_id_array_cnt);
+}
+#endif /* __UTIL_MPM_H__ */
diff --git a/framework/src/suricata/src/util-optimize.h b/framework/src/suricata/src/util-optimize.h
new file mode 100644
index 00000000..bde35ddd
--- /dev/null
+++ b/framework/src/suricata/src/util-optimize.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __UTIL_OPTIMIZE_H__
+#define __UTIL_OPTIMIZE_H__
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#if CPPCHECK==1
+#define likely
+#define unlikely
+#else
+#ifndef likely
+#define likely(expr) __builtin_expect(!!(expr), 1)
+#endif
+#ifndef unlikely
+#define unlikely(expr) __builtin_expect(!!(expr), 0)
+#endif
+#endif
+
+/** from http://en.wikipedia.org/wiki/Memory_ordering
+ *
+ * C Compiler memory barrier
+ */
+#define cc_barrier() __asm__ __volatile__("": : :"memory")
+
+/** from http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
+ *
+ * Hardware memory barrier
+ */
+#define hw_barrier() __sync_synchronize()
+
+#endif /* __UTIL_OPTIMIZE_H__ */
+
diff --git a/framework/src/suricata/src/util-path.c b/framework/src/suricata/src/util-path.c
new file mode 100644
index 00000000..d1b812f0
--- /dev/null
+++ b/framework/src/suricata/src/util-path.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "util-debug.h"
+
+/**
+ * \brief Check if a path is absolute
+ *
+ * \param path string with the path
+ *
+ * \retval 1 absolute
+ * \retval 0 not absolute
+ */
+int PathIsAbsolute(const char *path)
+{
+ if (strlen(path) > 1 && path[0] == '/') {
+ return 1;
+ }
+
+#if (defined OS_WIN32 || defined __CYGWIN__)
+ if (strlen(path) > 2) {
+ if (isalpha((unsigned char)path[0]) && path[1] == ':') {
+ return 1;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * \brief Check if a path is relative
+ *
+ * \param path string with the path
+ *
+ * \retval 1 relative
+ * \retval 0 not relative
+ */
+int PathIsRelative(const char *path)
+{
+ return PathIsAbsolute(path) ? 0 : 1;
+}
diff --git a/framework/src/suricata/src/util-path.h b/framework/src/suricata/src/util-path.h
new file mode 100644
index 00000000..2243722a
--- /dev/null
+++ b/framework/src/suricata/src/util-path.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#ifndef __UTIL_PATH_H__
+#define __UTIL_PATH_H__
+
+int PathIsAbsolute(const char *);
+int PathIsRelative(const char *);
+
+#endif /* __UTIL_PATH_H__ */
diff --git a/framework/src/suricata/src/util-pidfile.c b/framework/src/suricata/src/util-pidfile.c
new file mode 100644
index 00000000..a13e54ef
--- /dev/null
+++ b/framework/src/suricata/src/util-pidfile.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Utility code for dealing with a pidfile.
+ * Adaptation of Steve Grubbs patch to our coding guidelines
+ * (thanks for the patch Steve ;)
+ */
+
+#include "suricata-common.h"
+#include "util-pidfile.h"
+
+/**
+ * \brief Write a pid file (used at the startup)
+ * This commonly needed by the init scripts
+ *
+ * \param pointer to the name of the pid file to write (optarg)
+ *
+ * \retval 0 if succes
+ * \retval -1 on failure
+ */
+int SCPidfileCreate(const char *pidfile)
+{
+ SCEnter();
+
+ int pidfd = 0;
+ char val[16];
+
+ size_t len = snprintf(val, sizeof(val), "%"PRIuMAX"\n", (uintmax_t)getpid());
+ if (len <= 0) {
+ SCLogError(SC_ERR_PIDFILE_SNPRINTF, "Pid error (%s)", strerror(errno));
+ SCReturnInt(-1);
+ }
+
+ pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
+ if (pidfd < 0) {
+ SCLogError(SC_ERR_PIDFILE_OPEN, "unable to set pidfile '%s': %s",
+ pidfile,
+ strerror(errno));
+ SCReturnInt(-1);
+ }
+
+ ssize_t r = write(pidfd, val, (unsigned int)len);
+ if (r == -1) {
+ SCLogError(SC_ERR_PIDFILE_WRITE, "unable to write pidfile: %s", strerror(errno));
+ close(pidfd);
+ SCReturnInt(-1);
+ } else if ((size_t)r != len) {
+ SCLogError(SC_ERR_PIDFILE_WRITE, "unable to write pidfile: wrote"
+ " %"PRIdMAX" of %"PRIuMAX" bytes.", (intmax_t)r, (uintmax_t)len);
+ close(pidfd);
+ SCReturnInt(-1);
+ }
+
+ close(pidfd);
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Remove the pid file (used at the startup)
+ *
+ * \param pointer to the name of the pid file to write (optarg)
+ */
+void SCPidfileRemove(const char *pid_filename)
+{
+ if (pid_filename != NULL) {
+ /* we ignore the result, the user may have removed the file already. */
+ (void)unlink(pid_filename);
+ }
+}
+
+/**
+ * \brief Check a pid file (used at the startup)
+ * This commonly needed by the init scripts
+ *
+ * \param pointer to the name of the pid file to write (optarg)
+ *
+ * \retval 0 if succes
+ * \retval -1 on failure
+ */
+int SCPidfileTestRunning(const char *pid_filename)
+{
+ if (access(pid_filename, F_OK) == 0) {
+ /* Check if the existing process is still alive. */
+ pid_t pidv;
+ FILE *pf;
+
+ pf = fopen(pid_filename, "r");
+ if (pf == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION,
+ "pid file '%s' exists and can not be read. Aborting!",
+ pid_filename);
+ return -1;
+ }
+
+ if (fscanf(pf, "%d", &pidv) == 1 && kill(pidv, 0) == 0) {
+ fclose(pf);
+ SCLogError(SC_ERR_INITIALIZATION,
+ "pid file '%s' exists. Is Suricata already running? Aborting!",
+ pid_filename);
+ return -1;
+ }
+
+ if (pf != NULL)
+ fclose(pf);
+ }
+ return 0;
+}
diff --git a/framework/src/suricata/src/util-pidfile.h b/framework/src/suricata/src/util-pidfile.h
new file mode 100644
index 00000000..ef071f4e
--- /dev/null
+++ b/framework/src/suricata/src/util-pidfile.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_PID_H__
+#define __UTIL_PID_H__
+
+int SCPidfileCreate(const char *);
+void SCPidfileRemove(const char *);
+int SCPidfileTestRunning(const char *pid_filename);
+
+#endif /* __UTIL_PID_H__ */
+
diff --git a/framework/src/suricata/src/util-pool-thread.c b/framework/src/suricata/src/util-pool-thread.c
new file mode 100644
index 00000000..fe83a788
--- /dev/null
+++ b/framework/src/suricata/src/util-pool-thread.c
@@ -0,0 +1,458 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup utilpool Pool
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pool utility functions
+ */
+
+#include "suricata-common.h"
+#include "util-pool.h"
+#include "util-pool-thread.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+PoolThread *PoolThreadInit(int threads, uint32_t size, uint32_t prealloc_size, uint32_t elt_size, void *(*Alloc)(), int (*Init)(void *, void *), void *InitData, void (*Cleanup)(void *), void (*Free)(void *))
+{
+ PoolThread *pt = NULL;
+ int i;
+
+ if (threads <= 0) {
+ SCLogDebug("error");
+ goto error;
+ }
+
+ pt = SCMalloc(sizeof(*pt));
+ if (unlikely(pt == NULL)) {
+ SCLogDebug("memory alloc error");
+ goto error;
+ }
+
+ SCLogDebug("size %d", threads);
+ pt->array = SCMalloc(threads * sizeof(PoolThreadElement));
+ if (pt->array == NULL) {
+ SCLogDebug("memory alloc error");
+ goto error;
+ }
+ pt->size = threads;
+
+ for (i = 0; i < threads; i++) {
+ PoolThreadElement *e = &pt->array[i];
+
+ SCMutexInit(&e->lock, NULL);
+ SCMutexLock(&e->lock);
+// SCLogDebug("size %u prealloc_size %u elt_size %u Alloc %p Init %p InitData %p Cleanup %p Free %p",
+// size, prealloc_size, elt_size,
+// Alloc, Init, InitData, Cleanup, Free);
+ e->pool = PoolInit(size, prealloc_size, elt_size, Alloc, Init, InitData, Cleanup, Free);
+ SCMutexUnlock(&e->lock);
+ if (e->pool == NULL) {
+ SCLogDebug("error");
+ goto error;
+ }
+ }
+
+ return pt;
+error:
+ if (pt != NULL)
+ PoolThreadFree(pt);
+ return NULL;
+}
+
+/**
+ *
+ */
+int PoolThreadGrow(PoolThread *pt, uint32_t size, uint32_t prealloc_size, uint32_t elt_size, void *(*Alloc)(), int (*Init)(void *, void *), void *InitData, void (*Cleanup)(void *), void (*Free)(void *)) {
+ void *ptmp;
+ size_t newsize;
+ PoolThreadElement *e = NULL;
+
+ if (pt == NULL || pt->array == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
+ return -1;
+ }
+
+ newsize = pt->size + 1;
+ SCLogDebug("newsize %"PRIuMAX, (uintmax_t)newsize);
+
+ ptmp = SCRealloc(pt->array, (newsize * sizeof(PoolThreadElement)));
+ if (ptmp == NULL) {
+ SCFree(pt->array);
+ pt->array = NULL;
+ SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
+ return -1;
+ }
+ pt->array = ptmp;
+
+ pt->size = newsize;
+
+ e = &pt->array[newsize - 1];
+ memset(e, 0x00, sizeof(*e));
+ SCMutexInit(&e->lock, NULL);
+ SCMutexLock(&e->lock);
+ e->pool = PoolInit(size, prealloc_size, elt_size, Alloc, Init, InitData, Cleanup, Free);
+ SCMutexUnlock(&e->lock);
+ if (e->pool == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
+ return -1;
+ }
+
+ return (int)(newsize - 1);
+}
+
+int PoolThreadSize(PoolThread *pt)
+{
+ if (pt == NULL)
+ return -1;
+ return (int)pt->size;
+}
+
+void PoolThreadFree(PoolThread *pt)
+{
+ int i;
+
+ if (pt == NULL)
+ return;
+
+ if (pt->array != NULL) {
+ for (i = 0; i < (int)pt->size; i++) {
+ PoolThreadElement *e = &pt->array[i];
+ SCMutexLock(&e->lock);
+ PoolFree(e->pool);
+ SCMutexUnlock(&e->lock);
+ SCMutexDestroy(&e->lock);
+ }
+ SCFree(pt->array);
+ }
+ SCFree(pt);
+}
+
+void *PoolThreadGetById(PoolThread *pt, uint16_t id)
+{
+ void *data = NULL;
+
+ if (pt == NULL || id >= pt->size)
+ return NULL;
+
+ PoolThreadElement *e = &pt->array[id];
+ SCMutexLock(&e->lock);
+ data = PoolGet(e->pool);
+ SCMutexUnlock(&e->lock);
+ if (data) {
+ PoolThreadReserved *did = data;
+ *did = id;
+ }
+
+ return data;
+}
+
+void PoolThreadReturn(PoolThread *pt, void *data)
+{
+ PoolThreadReserved *id = data;
+
+ if (pt == NULL || *id >= pt->size)
+ return;
+
+ SCLogDebug("returning to id %u", *id);
+
+ PoolThreadElement *e = &pt->array[*id];
+ SCMutexLock(&e->lock);
+ PoolReturn(e->pool, data);
+ SCMutexUnlock(&e->lock);
+}
+
+#ifdef UNITTESTS
+struct PoolThreadTestData {
+ PoolThreadReserved res;
+ int abc;
+};
+
+static void *PoolThreadTestAlloc(void)
+{
+ void *data = SCMalloc(sizeof(struct PoolThreadTestData));
+ return data;
+}
+
+static
+int PoolThreadTestInit(void *data, void *allocdata)
+{
+ if (!data)
+ return 0;
+
+ memset(data,0x00,sizeof(allocdata));
+ struct PoolThreadTestData *pdata = data;
+ pdata->abc = *(int *)allocdata;
+ return 1;
+}
+
+static
+void PoolThreadTestFree(void *data)
+{
+}
+
+static int PoolThreadTestInit01(void)
+{
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, NULL, NULL, NULL, NULL);
+ if (pt == NULL)
+ return 0;
+
+ PoolThreadFree(pt);
+ return 1;
+}
+
+static int PoolThreadTestInit02(void)
+{
+ int i = 123;
+
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
+ if (pt == NULL)
+ return 0;
+
+ PoolThreadFree(pt);
+ return 1;
+}
+
+static int PoolThreadTestGet01(void)
+{
+ int result = 0;
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, NULL, NULL, NULL, NULL);
+ if (pt == NULL)
+ return 0;
+
+ void *data = PoolThreadGetById(pt, 3);
+ if (data == NULL) {
+ printf("data == NULL: ");
+ goto end;
+ }
+
+ struct PoolThreadTestData *pdata = data;
+ if (pdata->res != 3) {
+ printf("res != 3, but %d: ", pdata->res);
+ goto end;
+ }
+
+ result = 1;
+end:
+ PoolThreadFree(pt);
+ return result;
+}
+
+static int PoolThreadTestGet02(void)
+{
+ int i = 123;
+ int result = 0;
+
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
+ if (pt == NULL)
+ return 0;
+
+ void *data = PoolThreadGetById(pt, 3);
+ if (data == NULL) {
+ printf("data == NULL: ");
+ goto end;
+ }
+
+ struct PoolThreadTestData *pdata = data;
+ if (pdata->res != 3) {
+ printf("res != 3, but %d: ", pdata->res);
+ goto end;
+ }
+
+ if (pdata->abc != 123) {
+ printf("abc != 123, but %d: ", pdata->abc);
+ goto end;
+ }
+
+ result = 1;
+end:
+ PoolThreadFree(pt);
+ return result;
+}
+
+static int PoolThreadTestReturn01(void)
+{
+ int i = 123;
+ int result = 0;
+
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
+ if (pt == NULL)
+ return 0;
+
+ void *data = PoolThreadGetById(pt, 3);
+ if (data == NULL) {
+ printf("data == NULL: ");
+ goto end;
+ }
+
+ struct PoolThreadTestData *pdata = data;
+ if (pdata->res != 3) {
+ printf("res != 3, but %d: ", pdata->res);
+ goto end;
+ }
+
+ if (pdata->abc != 123) {
+ printf("abc != 123, but %d: ", pdata->abc);
+ goto end;
+ }
+
+ if (pt->array[3].pool->outstanding != 1) {
+ printf("pool outstanding count wrong %u: ",
+ pt->array[3].pool->outstanding);
+ goto end;
+ }
+
+ PoolThreadReturn(pt, data);
+
+ if (pt->array[3].pool->outstanding != 0) {
+ printf("pool outstanding count wrong %u: ",
+ pt->array[3].pool->outstanding);
+ goto end;
+ }
+
+
+ result = 1;
+end:
+ PoolThreadFree(pt);
+ return result;
+}
+
+static int PoolThreadTestGrow01(void)
+{
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, NULL, NULL, NULL, NULL);
+ if (pt == NULL)
+ return 0;
+
+ if (PoolThreadGrow(pt,
+ 10, 5, 10, PoolThreadTestAlloc, NULL, NULL, NULL, NULL) < 0) {
+ PoolThreadFree(pt);
+ return 0;
+ }
+
+ PoolThreadFree(pt);
+ return 1;
+}
+
+static int PoolThreadTestGrow02(void)
+{
+ int i = 123;
+
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
+ if (pt == NULL)
+ return 0;
+
+ if (PoolThreadGrow(pt,
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL) < 0) {
+ PoolThreadFree(pt);
+ return 0;
+ }
+
+ PoolThreadFree(pt);
+ return 1;
+}
+
+static int PoolThreadTestGrow03(void)
+{
+ int i = 123;
+ int result = 0;
+
+ PoolThread *pt = PoolThreadInit(4, /* threads */
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
+ if (pt == NULL)
+ return 0;
+
+ if (PoolThreadGrow(pt,
+ 10, 5, 10, PoolThreadTestAlloc, PoolThreadTestInit, &i, PoolThreadTestFree, NULL) < 0) {
+ PoolThreadFree(pt);
+ return 0;
+ }
+
+ void *data = PoolThreadGetById(pt, 4);
+ if (data == NULL) {
+ printf("data == NULL: ");
+ goto end;
+ }
+
+ struct PoolThreadTestData *pdata = data;
+ if (pdata->res != 4) {
+ printf("res != 5, but %d: ", pdata->res);
+ goto end;
+ }
+
+ if (pdata->abc != 123) {
+ printf("abc != 123, but %d: ", pdata->abc);
+ goto end;
+ }
+
+ if (pt->array[4].pool->outstanding != 1) {
+ printf("pool outstanding count wrong %u: ",
+ pt->array[4].pool->outstanding);
+ goto end;
+ }
+
+ PoolThreadReturn(pt, data);
+
+ if (pt->array[4].pool->outstanding != 0) {
+ printf("pool outstanding count wrong %u: ",
+ pt->array[4].pool->outstanding);
+ goto end;
+ }
+
+
+ result = 1;
+end:
+ PoolThreadFree(pt);
+ return result;
+}
+
+#endif
+
+void PoolThreadRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("PoolThreadTestInit01", PoolThreadTestInit01, 1);
+ UtRegisterTest("PoolThreadTestInit02", PoolThreadTestInit02, 1);
+
+ UtRegisterTest("PoolThreadTestGet01", PoolThreadTestGet01, 1);
+ UtRegisterTest("PoolThreadTestGet02", PoolThreadTestGet02, 1);
+
+ UtRegisterTest("PoolThreadTestReturn01", PoolThreadTestReturn01, 1);
+
+ UtRegisterTest("PoolThreadTestGrow01", PoolThreadTestGrow01, 1);
+ UtRegisterTest("PoolThreadTestGrow02", PoolThreadTestGrow02, 1);
+ UtRegisterTest("PoolThreadTestGrow03", PoolThreadTestGrow03, 1);
+#endif
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/util-pool-thread.h b/framework/src/suricata/src/util-pool-thread.h
new file mode 100644
index 00000000..1d2bbd47
--- /dev/null
+++ b/framework/src/suricata/src/util-pool-thread.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup utilpool
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+/**
+ * Consumers of this API MUST add PoolThreadReserved as the first
+ * member in the data structure. They also MUST ignore that data
+ * completely. It's managed by this API.
+ *
+ * It's purpose is to make sure thread X can return data to a pool
+ * from thread Y.
+ */
+
+#ifndef __UTIL_POOL_THREAD_H__
+#define __UTIL_POOL_THREAD_H__
+
+struct PoolThreadElement_ {
+ SCMutex lock; /**< lock, should have low contention */
+ Pool *pool; /**< actual pool */
+};
+// __attribute__((aligned(CLS))); <- VJ: breaks on clang 32bit, segv in PoolThreadTestGrow01
+
+typedef struct PoolThreadElement_ PoolThreadElement;
+
+typedef struct PoolThread_ {
+ size_t size; /**< size of the array */
+ PoolThreadElement *array; /**< array of elements */
+} PoolThread;
+
+/** per data item reserved data containing the
+ * thread pool id */
+typedef uint16_t PoolThreadReserved;
+
+void PoolThreadRegisterTests(void);
+
+/** \brief initialize a thread pool
+ * \note same as PoolInit() except for "threads"
+ * \param threads number of threads to use this
+ * \retval pt thread pool or NULL on error */
+PoolThread *PoolThreadInit(int threads, uint32_t size, uint32_t prealloc_size, uint32_t elt_size, void *(*Alloc)(), int (*Init)(void *, void *), void *InitData, void (*Cleanup)(void *), void (*Free)(void *));
+
+/** \brief grow a thread pool by one
+ * \note calls PoolInit so all args but 'pt' are the same
+ * \param pt thread pool to grow
+ * \retval r id of new entry on succes, -1 on error */
+int PoolThreadGrow(PoolThread *pt, uint32_t size, uint32_t prealloc_size, uint32_t elt_size, void *(*Alloc)(), int (*Init)(void *, void *), void *InitData, void (*Cleanup)(void *), void (*Free)(void *));
+
+/** \brief destroy the thread pool
+ * \note wrapper around PoolFree()
+ * \param pt thread pool */
+void PoolThreadFree(PoolThread *pt);
+
+/** \brief get data from thread pool by thread id
+ * \note wrapper around PoolGet()
+ * \param pt thread pool
+ * \param id thread id
+ * \retval ptr data or NULL */
+void *PoolThreadGetById(PoolThread *pt, uint16_t id);
+
+/** \brief return data to thread pool
+ * \note wrapper around PoolReturn()
+ * \param pt thread pool
+ * \param data memory block to return, with PoolThreadReserved as it's first member */
+void PoolThreadReturn(PoolThread *pt, void *data);
+
+/** \brief get size of PoolThread (number of 'threads', so array elements)
+ * \param pt thread pool
+ * \retval size or -1 on error */
+int PoolThreadSize(PoolThread *pt);
+
+#endif /* __UTIL_POOL_THREAD_H__ */
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/util-pool.c b/framework/src/suricata/src/util-pool.c
new file mode 100644
index 00000000..6f405252
--- /dev/null
+++ b/framework/src/suricata/src/util-pool.c
@@ -0,0 +1,737 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \defgroup utilpool Pool
+ *
+ * ::Pool are an effective way to maintain a set of ready to use
+ * structures.
+ *
+ * To create a ::Pool, you need to use PoolInit(). You can
+ * get an item from the ::Pool by using PoolGet(). When you're
+ * done with it call PoolReturn().
+ * To destroy the ::Pool, call PoolFree(), it will free all used
+ * memory.
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Pool utility functions
+ */
+
+#include "suricata-common.h"
+#include "util-pool.h"
+#include "util-pool-thread.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+
+static int PoolMemset(void *pitem, void *initdata)
+{
+ Pool *p = (Pool *) initdata;
+
+ memset(pitem, 0, p->elt_size);
+ return 1;
+}
+
+/**
+ * \brief Check if data is preallocated
+ * \retval 0 or -1 if not inside */
+static int PoolDataPreAllocated(Pool *p, void *data)
+{
+ int delta = data - p->data_buffer;
+ if ((delta < 0) || (delta > p->data_buffer_size)) {
+ return 0;
+ }
+ return 1;
+}
+
+/** \brief Init a Pool
+ *
+ * PoolInit() creates a ::Pool. The Alloc function must only do
+ * allocation stuff. The Cleanup function must not try to free
+ * the PoolBucket::data. This is done by the ::Pool management
+ * system.
+ *
+ * \param size
+ * \param prealloc_size
+ * \param elt_size Memory size of an element
+ * \param Alloc An allocation function or NULL to use a standard SCMalloc
+ * \param Init An init function or NULL to use a standard memset to 0
+ * \param InitData Init data
+ * \param Cleanup a free function or NULL if no special treatment is needed
+ * \param Free free func
+ * \retval the allocated Pool
+ */
+Pool *PoolInit(uint32_t size, uint32_t prealloc_size, uint32_t elt_size, void *(*Alloc)(), int (*Init)(void *, void *), void *InitData, void (*Cleanup)(void *), void (*Free)(void *))
+{
+ Pool *p = NULL;
+
+ if (size != 0 && prealloc_size > size) {
+ SCLogError(SC_ERR_POOL_INIT, "size error");
+ goto error;
+ }
+ if (size != 0 && elt_size == 0) {
+ SCLogError(SC_ERR_POOL_INIT, "size != 0 && elt_size == 0");
+ goto error;
+ }
+ if (elt_size && Free) {
+ SCLogError(SC_ERR_POOL_INIT, "elt_size && Free");
+ goto error;
+ }
+
+ /* setup the filter */
+ p = SCMalloc(sizeof(Pool));
+ if (unlikely(p == NULL)) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ goto error;
+ }
+
+ memset(p,0,sizeof(Pool));
+
+ p->max_buckets = size;
+ p->preallocated = prealloc_size;
+ p->elt_size = elt_size;
+ p->data_buffer_size = prealloc_size * elt_size;
+ p->Alloc = Alloc;
+ p->Init = Init;
+ p->InitData = InitData;
+ p->Cleanup = Cleanup;
+ p->Free = Free;
+ if (p->Init == NULL) {
+ p->Init = PoolMemset;
+ p->InitData = p;
+ }
+
+ /* alloc the buckets and place them in the empty list */
+ uint32_t u32 = 0;
+ if (size > 0) {
+ PoolBucket *pb = SCCalloc(size, sizeof(PoolBucket));
+ if (unlikely(pb == NULL)) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ goto error;
+ }
+ p->pb_buffer = pb;
+ memset(pb, 0, size * sizeof(PoolBucket));
+ for (u32 = 0; u32 < size; u32++) {
+ /* populate pool */
+ pb->next = p->empty_stack;
+ pb->flags |= POOL_BUCKET_PREALLOCATED;
+ p->empty_stack = pb;
+ p->empty_stack_size++;
+ pb++;
+ }
+ }
+
+ if (size > 0) {
+ p->data_buffer = SCCalloc(prealloc_size, elt_size);
+ /* FIXME better goto */
+ if (p->data_buffer == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ goto error;
+ }
+ }
+ /* prealloc the buckets and requeue them to the alloc list */
+ for (u32 = 0; u32 < prealloc_size; u32++) {
+ if (size == 0) { /* unlimited */
+ PoolBucket *pb = SCMalloc(sizeof(PoolBucket));
+ if (unlikely(pb == NULL)) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ goto error;
+ }
+
+ memset(pb, 0, sizeof(PoolBucket));
+
+ if (p->Alloc) {
+ pb->data = p->Alloc();
+ } else {
+ pb->data = SCMalloc(p->elt_size);
+ }
+ if (pb->data == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ SCFree(pb);
+ goto error;
+ }
+ if (p->Init(pb->data, p->InitData) != 1) {
+ SCLogError(SC_ERR_POOL_INIT, "init error");
+ if (p->Cleanup)
+ p->Cleanup(pb->data);
+ if (p->Free)
+ p->Free(pb->data);
+ else
+ SCFree(pb->data);
+ SCFree(pb);
+ goto error;
+ }
+ p->allocated++;
+
+ pb->next = p->alloc_stack;
+ p->alloc_stack = pb;
+ p->alloc_stack_size++;
+ } else {
+ PoolBucket *pb = p->empty_stack;
+ if (pb == NULL) {
+ SCLogError(SC_ERR_POOL_INIT, "alloc error");
+ goto error;
+ }
+
+ pb->data = (char *)p->data_buffer + u32 * elt_size;
+ if (p->Init(pb->data, p->InitData) != 1) {
+ SCLogError(SC_ERR_POOL_INIT, "init error");
+ if (p->Cleanup)
+ p->Cleanup(pb->data);
+ goto error;
+ }
+
+ p->empty_stack = pb->next;
+ p->empty_stack_size--;
+
+ p->allocated++;
+
+ pb->next = p->alloc_stack;
+ p->alloc_stack = pb;
+ p->alloc_stack_size++;
+ }
+ }
+
+ return p;
+
+error:
+ if (p != NULL) {
+ PoolFree(p);
+ }
+ return NULL;
+}
+
+
+void PoolFree(Pool *p)
+{
+ if (p == NULL)
+ return;
+
+ while (p->alloc_stack != NULL) {
+ PoolBucket *pb = p->alloc_stack;
+ p->alloc_stack = pb->next;
+ if (p->Cleanup)
+ p->Cleanup(pb->data);
+ if (PoolDataPreAllocated(p, pb->data) == 0) {
+ if (p->Free)
+ p->Free(pb->data);
+ else
+ SCFree(pb->data);
+ }
+ pb->data = NULL;
+ if (! pb->flags & POOL_BUCKET_PREALLOCATED) {
+ SCFree(pb);
+ }
+ }
+
+ while (p->empty_stack != NULL) {
+ PoolBucket *pb = p->empty_stack;
+ p->empty_stack = pb->next;
+ if (pb->data!= NULL) {
+ if (p->Cleanup)
+ p->Cleanup(pb->data);
+ if (PoolDataPreAllocated(p, pb->data) == 0) {
+ if (p->Free)
+ p->Free(pb->data);
+ else
+ SCFree(pb->data);
+ }
+ pb->data = NULL;
+ }
+ if (! pb->flags & POOL_BUCKET_PREALLOCATED) {
+ SCFree(pb);
+ }
+ }
+
+ if (p->pb_buffer)
+ SCFree(p->pb_buffer);
+ if (p->data_buffer)
+ SCFree(p->data_buffer);
+ SCFree(p);
+}
+
+void PoolPrint(Pool *p)
+{
+ printf("\n----------- Hash Table Stats ------------\n");
+ printf("Buckets: %" PRIu32 "\n", p->empty_stack_size + p->alloc_stack_size);
+ printf("-----------------------------------------\n");
+}
+
+void *PoolGet(Pool *p)
+{
+ SCEnter();
+
+ PoolBucket *pb = p->alloc_stack;
+ if (pb != NULL) {
+ /* pull from the alloc list */
+ p->alloc_stack = pb->next;
+ p->alloc_stack_size--;
+
+ /* put in the empty list */
+ pb->next = p->empty_stack;
+ p->empty_stack = pb;
+ p->empty_stack_size++;
+ } else {
+ if (p->max_buckets == 0 || p->allocated < p->max_buckets) {
+ void *pitem;
+ SCLogDebug("max_buckets %"PRIu32"", p->max_buckets);
+
+ if (p->Alloc != NULL) {
+ pitem = p->Alloc();
+ } else {
+ pitem = SCMalloc(p->elt_size);
+ }
+
+ if (pitem != NULL) {
+ if (p->Init(pitem, p->InitData) != 1) {
+ if (p->Cleanup)
+ p->Cleanup(pitem);
+ if (p->Free != NULL)
+ p->Free(pitem);
+ else
+ SCFree(pitem);
+ SCReturnPtr(NULL, "void");
+ }
+
+ p->allocated++;
+
+ p->outstanding++;
+ if (p->outstanding > p->max_outstanding)
+ p->max_outstanding = p->outstanding;
+ }
+
+ SCReturnPtr(pitem, "void");
+ } else {
+ SCReturnPtr(NULL, "void");
+ }
+ }
+
+ void *ptr = pb->data;
+ pb->data = NULL;
+ p->outstanding++;
+ if (p->outstanding > p->max_outstanding)
+ p->max_outstanding = p->outstanding;
+ SCReturnPtr(ptr,"void");
+}
+
+void PoolReturn(Pool *p, void *data)
+{
+ SCEnter();
+
+ PoolBucket *pb = p->empty_stack;
+
+ SCLogDebug("pb %p", pb);
+
+ if (pb == NULL) {
+ p->allocated--;
+ p->outstanding--;
+ if (p->Cleanup != NULL) {
+ p->Cleanup(data);
+ }
+ if (PoolDataPreAllocated(p, data) == 0) {
+ if (p->Free)
+ p->Free(data);
+ else
+ SCFree(data);
+ }
+
+ SCLogDebug("tried to return data %p to the pool %p, but no more "
+ "buckets available. Just freeing the data.", data, p);
+ SCReturn;
+ }
+
+ /* pull from the alloc list */
+ p->empty_stack = pb->next;
+ p->empty_stack_size--;
+
+ /* put in the alloc list */
+ pb->next = p->alloc_stack;
+ p->alloc_stack = pb;
+ p->alloc_stack_size++;
+
+ pb->data = data;
+ p->outstanding--;
+ SCReturn;
+}
+
+void PoolPrintSaturation(Pool *p)
+{
+ SCLogDebug("pool %p is using %"PRIu32" out of %"PRIu32" items (%02.1f%%), max %"PRIu32" (%02.1f%%): pool struct memory %"PRIu64".", p, p->outstanding, p->max_buckets, (float)(p->outstanding/(float)(p->max_buckets))*100, p->max_outstanding, (float)(p->max_outstanding/(float)(p->max_buckets))*100, (uint64_t)(p->max_buckets * sizeof(PoolBucket)));
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+void *PoolTestAlloc()
+{
+ void *ptr = SCMalloc(10);
+ if (unlikely(ptr == NULL))
+ return NULL;
+ return ptr;
+}
+int PoolTestInitArg(void *data, void *allocdata)
+{
+ size_t len = strlen((char *)allocdata) + 1;
+ char *str = data;
+ if (str != NULL)
+ strlcpy(str,(char *)allocdata,len);
+ return 1;
+}
+
+void PoolTestFree(void *ptr)
+{
+ return;
+}
+
+#ifdef UNITTESTS
+static int PoolTestInit01 (void)
+{
+ Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ return 0;
+
+ PoolFree(p);
+ return 1;
+}
+
+static int PoolTestInit02 (void)
+{
+ int retval = 0;
+
+ Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ if (p->alloc_stack == NULL || p->empty_stack == NULL) {
+ printf("list(s) not properly initialized (a:%p e:%p): ",
+ p->alloc_stack, p->empty_stack);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->Alloc != PoolTestAlloc) {
+ printf("Alloc func ptr %p != %p: ",
+ p->Alloc, PoolTestAlloc);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->Cleanup != PoolTestFree) {
+ printf("Free func ptr %p != %p: ",
+ p->Cleanup, PoolTestFree);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+
+static int PoolTestInit03 (void)
+{
+ int retval = 0;
+ void *data = NULL;
+
+ Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ data = PoolGet(p);
+ if (data == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->alloc_stack_size != 4) {
+ printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->empty_stack_size != 6) {
+ printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+
+static int PoolTestInit04 (void)
+{
+ int retval = 0;
+ char *str = NULL;
+
+ Pool *p = PoolInit(10,5,strlen("test") + 1,NULL, PoolTestInitArg,(void *)"test",PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ str = PoolGet(p);
+ if (str == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (strcmp(str, "test") != 0) {
+ printf("Memory not properly initialized: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->alloc_stack_size != 4) {
+ printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->empty_stack_size != 6) {
+ printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+
+static int PoolTestInit05 (void)
+{
+ int retval = 0;
+ void *data = NULL;
+
+ Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL, NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ data = PoolGet(p);
+ if (data == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->alloc_stack_size != 4) {
+ printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->empty_stack_size != 6) {
+ printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ PoolReturn(p, data);
+ data = NULL;
+
+ if (p->alloc_stack_size != 5) {
+ printf("p->alloc_stack_size 5 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->empty_stack_size != 5) {
+ printf("p->empty_stack_size 5 != %" PRIu32 ": ", p->empty_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+
+static int PoolTestInit06 (void)
+{
+ int retval = 0;
+ void *data = NULL;
+ void *data2 = NULL;
+
+ Pool *p = PoolInit(1,0,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ if (p->allocated != 0) {
+ printf("p->allocated 0 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ data = PoolGet(p);
+ if (data == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->allocated != 1) {
+ printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ data2 = PoolGet(p);
+ if (data2 != NULL) {
+ printf("PoolGet returned %p, expected NULL: ", data2);
+ retval = 0;
+ goto end;
+ }
+
+ PoolReturn(p,data);
+ data = NULL;
+
+ if (p->allocated != 1) {
+ printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->alloc_stack_size != 1) {
+ printf("p->alloc_stack_size 1 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+
+/** \test pool with unlimited size */
+static int PoolTestInit07 (void)
+{
+ int retval = 0;
+ void *data = NULL;
+ void *data2 = NULL;
+
+ Pool *p = PoolInit(0,1,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
+ if (p == NULL)
+ goto end;
+
+ if (p->max_buckets != 0) {
+ printf("p->max_buckets 0 != %" PRIu32 ": ", p->max_buckets);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->allocated != 1) {
+ printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ data = PoolGet(p);
+ if (data == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->allocated != 1) {
+ printf("(2) p->allocated 1 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ data2 = PoolGet(p);
+ if (data2 == NULL) {
+ printf("PoolGet returned NULL: ");
+ retval = 0;
+ goto end;
+ }
+
+ if (p->allocated != 2) {
+ printf("(3) p->allocated 2 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ PoolReturn(p,data);
+ data = NULL;
+
+ if (p->allocated != 2) {
+ printf("(4) p->allocated 2 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ if (p->alloc_stack_size != 1) {
+ printf("p->alloc_stack_size 1 != %" PRIu32 ": ", p->alloc_stack_size);
+ retval = 0;
+ goto end;
+ }
+
+ PoolReturn(p,data2);
+ data2 = NULL;
+
+ if (p->allocated != 1) {
+ printf("(5) p->allocated 1 != %" PRIu32 ": ", p->allocated);
+ retval = 0;
+ goto end;
+ }
+
+ retval = 1;
+end:
+ if (p != NULL)
+ PoolFree(p);
+ return retval;
+}
+#endif /* UNITTESTS */
+
+void PoolRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("PoolTestInit01", PoolTestInit01, 1);
+ UtRegisterTest("PoolTestInit02", PoolTestInit02, 1);
+ UtRegisterTest("PoolTestInit03", PoolTestInit03, 1);
+ UtRegisterTest("PoolTestInit04", PoolTestInit04, 1);
+ UtRegisterTest("PoolTestInit05", PoolTestInit05, 1);
+ UtRegisterTest("PoolTestInit06", PoolTestInit06, 1);
+ UtRegisterTest("PoolTestInit07", PoolTestInit07, 1);
+
+ PoolThreadRegisterTests();
+#endif /* UNITTESTS */
+}
+
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/util-pool.h b/framework/src/suricata/src/util-pool.h
new file mode 100644
index 00000000..266d1f4d
--- /dev/null
+++ b/framework/src/suricata/src/util-pool.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup utilpool
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_POOL_H__
+#define __UTIL_POOL_H__
+
+#define POOL_BUCKET_PREALLOCATED (1 << 0)
+
+/* pool bucket structure */
+typedef struct PoolBucket_ {
+ void *data;
+ uint8_t flags;
+ struct PoolBucket_ *next;
+} PoolBucket;
+
+/* pool structure */
+typedef struct Pool_ {
+ uint32_t max_buckets;
+ uint32_t preallocated;
+ uint32_t allocated; /**< counter of data elements, both currently in
+ * the pool and outside of it (outstanding) */
+
+ uint32_t alloc_stack_size;
+
+ PoolBucket *alloc_stack;
+
+ PoolBucket *empty_stack;
+ uint32_t empty_stack_size;
+
+ int data_buffer_size;
+ void *data_buffer;
+ PoolBucket *pb_buffer;
+
+ void *(*Alloc)();
+ int (*Init)(void *, void *);
+ void *InitData;
+ void (*Cleanup)(void *);
+ void (*Free)(void *);
+
+ uint32_t elt_size;
+ uint32_t outstanding; /**< counter of data items 'in use'. Pretty much
+ * the diff between PoolGet and PoolReturn */
+ uint32_t max_outstanding; /**< max value of outstanding we saw */
+} Pool;
+
+/* prototypes */
+Pool* PoolInit(uint32_t, uint32_t, uint32_t, void *(*Alloc)(), int (*Init)(void *, void *), void *, void (*Cleanup)(void *), void (*Free)(void *));
+void PoolFree(Pool *);
+void PoolPrint(Pool *);
+void PoolPrintSaturation(Pool *p);
+
+void *PoolGet(Pool *);
+void PoolReturn(Pool *, void *);
+
+void PoolRegisterTests(void);
+
+#endif /* __UTIL_POOL_H__ */
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/util-print.c b/framework/src/suricata/src/util-print.c
new file mode 100644
index 00000000..3e34756c
--- /dev/null
+++ b/framework/src/suricata/src/util-print.c
@@ -0,0 +1,272 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Print utility functions
+ */
+
+#include "suricata-common.h"
+#include "util-print.h"
+#include "util-error.h"
+#include "util-debug.h"
+
+/**
+ * \brief print a buffer as hex on a single line
+ *
+ * Prints in the format "00 AA BB"
+ *
+ * \param nbuf buffer into which the output is written
+ * \param offset of where to start writting into the buffer
+ * \param max_size the size of the output buffer
+ * \param buf buffer to print from
+ * \param buflen length of the input buffer
+ */
+void PrintBufferRawLineHex(char *nbuf, int *offset, int max_size, uint8_t *buf, uint32_t buflen)
+{
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u++) {
+ PrintBufferData(nbuf, offset, max_size, "%02X ", buf[u]);
+ }
+}
+
+/**
+ * \brief print a buffer as hex on a single line in to retbuf buffer
+ *
+ * Prints in the format "00 AA BB"
+ *
+ * \param retbuf pointer to the buffer which will have the result
+ * \param rebuflen lenght of the buffer
+ * \param buf buffer to print from
+ * \param buflen length of the input buffer
+ */
+void PrintRawLineHexBuf(char *retbuf, uint32_t retbuflen, uint8_t *buf, uint32_t buflen)
+{
+ uint32_t offset = 0;
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u++) {
+ PrintBufferData(retbuf, &offset, retbuflen, "%02X ", buf[u]);
+ }
+}
+
+void PrintRawJsonFp(FILE *fp, uint8_t *buf, uint32_t buflen)
+{
+#define BUFFER_LENGTH 2048
+ char nbuf[BUFFER_LENGTH] = "";
+ uint32_t offset = 0;
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u++) {
+ if (buf[u] == '\\' || buf[u] == '/' || buf[u] == '\"') {
+ PrintBufferData(nbuf, &offset, BUFFER_LENGTH,
+ "\\%c", buf[u]);
+ } else if (isprint(buf[u])) {
+ PrintBufferData(nbuf, &offset, BUFFER_LENGTH,
+ "%c", buf[u]);
+ } else {
+ PrintBufferData(nbuf, &offset, BUFFER_LENGTH,
+ "\\\\x%02X", buf[u]);
+ }
+ }
+ fprintf(fp, "%s", nbuf);
+}
+
+void PrintRawUriFp(FILE *fp, uint8_t *buf, uint32_t buflen)
+{
+#define BUFFER_LENGTH 2048
+ char nbuf[BUFFER_LENGTH] = "";
+ uint32_t offset = 0;
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u++) {
+ if (isprint(buf[u]) && buf[u] != '\"') {
+ PrintBufferData(nbuf, &offset, BUFFER_LENGTH,
+ "%c", buf[u]);
+ } else {
+ PrintBufferData(nbuf, &offset, BUFFER_LENGTH,
+ "\\x%02X", buf[u]);
+ }
+ }
+
+ fprintf(fp, "%s", nbuf);
+}
+
+void PrintRawUriBuf(char *retbuf, uint32_t *offset, uint32_t retbuflen,
+ uint8_t *buf, uint32_t buflen)
+{
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u++) {
+ if (isprint(buf[u]) && buf[u] != '\"') {
+ if (buf[u] == '\\') {
+ PrintBufferData(retbuf, offset, retbuflen,
+ "\\\\");
+ } else {
+ PrintBufferData(retbuf, offset, retbuflen,
+ "%c", buf[u]);
+ }
+ } else {
+ PrintBufferData(retbuf, offset, retbuflen,
+ "\\x%02X", buf[u]);
+ }
+ }
+
+ return;
+}
+
+void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
+{
+ int ch = 0;
+ uint32_t u = 0;
+
+ for (u = 0; u < buflen; u+=16) {
+ fprintf(fp ," %04X ", u);
+ for (ch = 0; (u+ch) < buflen && ch < 16; ch++) {
+ fprintf(fp, "%02X ", (uint8_t)buf[u+ch]);
+
+ if (ch == 7) fprintf(fp, " ");
+ }
+ if (ch == 16) fprintf(fp, " ");
+ else if (ch < 8) {
+ int spaces = (16 - ch) * 3 + 2 + 1;
+ int s = 0;
+ for ( ; s < spaces; s++) fprintf(fp, " ");
+ } else if(ch < 16) {
+ int spaces = (16 - ch) * 3 + 2;
+ int s = 0;
+ for ( ; s < spaces; s++) fprintf(fp, " ");
+ }
+
+ for (ch = 0; (u+ch) < buflen && ch < 16; ch++) {
+ fprintf(fp, "%c", isprint((uint8_t)buf[u+ch]) ? (uint8_t)buf[u+ch] : '.');
+
+ if (ch == 7) fprintf(fp, " ");
+ if (ch == 15) fprintf(fp, "\n");
+ }
+ }
+ if (ch != 16)
+ fprintf(fp, "\n");
+}
+
+void PrintRawDataToBuffer(uint8_t *dst_buf, uint32_t *dst_buf_offset_ptr, uint32_t dst_buf_size,
+ uint8_t *src_buf, uint32_t src_buf_len)
+{
+ int ch = 0;
+ uint32_t u = 0;
+
+ for (u = 0; u < src_buf_len; u+=16) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size,
+ " %04X ", u);
+ for (ch = 0; (u + ch) < src_buf_len && ch < 16; ch++) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size,
+ "%02X ", (uint8_t)src_buf[u + ch]);
+
+ if (ch == 7) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size,
+ " ");
+ }
+ }
+ if (ch == 16) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, " ");
+ } else if (ch < 8) {
+ int spaces = (16 - ch) * 3 + 2 + 1;
+ int s = 0;
+ for ( ; s < spaces; s++)
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, " ");
+ } else if(ch < 16) {
+ int spaces = (16 - ch) * 3 + 2;
+ int s = 0;
+ for ( ; s < spaces; s++)
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, " ");
+ }
+
+ for (ch = 0; (u+ch) < src_buf_len && ch < 16; ch++) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size,
+ "%c",
+ isprint((uint8_t)src_buf[u + ch]) ? (uint8_t)src_buf[u + ch] : '.');
+
+ if (ch == 7)
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, " ");
+ if (ch == 15)
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, "\n");
+ }
+ }
+ if (ch != 16)
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size, "\n");
+
+ return;
+}
+
+void PrintStringsToBuffer(uint8_t *dst_buf, uint32_t *dst_buf_offset_ptr, uint32_t dst_buf_size,
+ uint8_t *src_buf, uint32_t src_buf_len)
+{
+ uint32_t ch = 0;
+ for (ch = 0; ch < src_buf_len; ch++) {
+ PrintBufferData((char *)dst_buf, dst_buf_offset_ptr, dst_buf_size,
+ "%c",
+ (isprint((uint8_t)src_buf[ch]) ||
+ src_buf[ch] == '\n' ||
+ src_buf[ch] == '\r') ? (uint8_t)src_buf[ch] : '.');
+ }
+
+ return;
+}
+
+#ifndef s6_addr16
+# define s6_addr16 __u6_addr.__u6_addr16
+#endif
+
+static const char *PrintInetIPv6(const void *src, char *dst, socklen_t size)
+{
+ struct in6_addr * insrc = (struct in6_addr *) src;
+ int i;
+ char s_part[6];
+
+ /* current IPv6 format is fixed size */
+ if (size < 8 * 5) {
+ SCLogWarning(SC_ERR_ARG_LEN_LONG, "Too small buffer to write IPv6 address");
+ return NULL;
+ }
+ memset(dst, 0, size);
+ for(i = 0; i < 8; i++) {
+ snprintf(s_part, 6, "%04x:", htons(insrc->s6_addr16[i]));
+ strlcat(dst, s_part, size);
+ }
+ /* suppress last ':' */
+ dst[strlen(dst) - 1] = 0;
+
+ return dst;
+}
+
+const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return inet_ntop(af, src, dst, size);
+ case AF_INET6:
+ /* Format IPv6 without deleting zeroes */
+ return PrintInetIPv6(src, dst, size);
+ default:
+ SCLogError(SC_ERR_INVALID_VALUE, "Unsupported protocol: %d", af);
+ }
+ return NULL;
+}
diff --git a/framework/src/suricata/src/util-print.h b/framework/src/suricata/src/util-print.h
new file mode 100644
index 00000000..a696a98a
--- /dev/null
+++ b/framework/src/suricata/src/util-print.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+
+
+#ifndef __UTIL_PRINT_H__
+#define __UTIL_PRINT_H__
+
+#define PrintBufferData(buf, buf_offset_ptr, buf_size, ...) do { \
+ int cw = snprintf((buf) + *(buf_offset_ptr), \
+ (buf_size) - *(buf_offset_ptr), \
+ __VA_ARGS__); \
+ if (cw >= 0) { \
+ if ( (*(buf_offset_ptr) + cw) >= buf_size) { \
+ SCLogDebug("Truncating data write since it exceeded buffer " \
+ "limit of - %"PRIu32"\n", buf_size); \
+ *(buf_offset_ptr) = buf_size - 1; \
+ } else { \
+ *(buf_offset_ptr) += cw; \
+ } \
+ } \
+ } while (0)
+
+void PrintBufferRawLineHex(char *, int *,int, uint8_t *, uint32_t);
+void PrintRawUriFp(FILE *, uint8_t *, uint32_t);
+void PrintRawUriBuf(char *, uint32_t *, uint32_t,
+ uint8_t *, uint32_t);
+void PrintRawJsonFp(FILE *, uint8_t *, uint32_t);
+void PrintRawDataFp(FILE *, const uint8_t *, uint32_t);
+void PrintRawDataToBuffer(uint8_t *dst_buf, uint32_t *dst_buf_offset_ptr, uint32_t dst_buf_size,
+ uint8_t *src_buf, uint32_t src_buf_len);
+void PrintStringsToBuffer(uint8_t *dst_buf, uint32_t *dst_buf_offset_ptr, uint32_t dst_buf_size,
+ uint8_t *src_buf, uint32_t src_buf_len);
+void PrintRawLineHexBuf(char *, uint32_t, uint8_t *, uint32_t );
+const char *PrintInet(int , const void *, char *, socklen_t);
+
+#endif /* __UTIL_PRINT_H__ */
+
diff --git a/framework/src/suricata/src/util-privs.c b/framework/src/suricata/src/util-privs.c
new file mode 100644
index 00000000..635247c3
--- /dev/null
+++ b/framework/src/suricata/src/util-privs.c
@@ -0,0 +1,246 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * File to drop the engine capabilities using libcap-ng by
+ * Steve Grubb
+ */
+
+#ifndef OS_WIN32
+
+#include <grp.h>
+#include <pwd.h>
+#include "util-debug.h"
+#include "suricata-common.h"
+#include "suricata.h"
+
+#ifdef HAVE_LIBCAP_NG
+
+#include <cap-ng.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#include "threadvars.h"
+#include "util-cpu.h"
+#include "util-privs.h"
+#include "runmodes.h"
+
+/** flag indicating if we'll be using caps */
+extern int sc_set_caps;
+
+/** our current runmode */
+extern int run_mode;
+
+/**
+ * \brief Drop all the previliges of the given thread
+ */
+void SCDropAllCaps()
+{
+ capng_clear(CAPNG_SELECT_BOTH);
+ if (capng_apply(CAPNG_SELECT_BOTH) < 0) {
+ SCLogError(SC_ERR_CHANGING_CAPS_FAILED, "failed in dropping the caps");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/**
+ * \brief Drop the previliges of the main thread
+ */
+void SCDropMainThreadCaps(uint32_t userid, uint32_t groupid)
+{
+ if (sc_set_caps == FALSE)
+ return;
+
+ capng_clear(CAPNG_SELECT_BOTH);
+
+ switch (run_mode) {
+ case RUNMODE_PCAP_DEV:
+ case RUNMODE_AFP_DEV:
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
+ CAP_NET_RAW, /* needed for pcap live mode */
+ -1);
+ break;
+ case RUNMODE_PFRING:
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
+ CAP_NET_ADMIN, CAP_NET_RAW,
+ -1);
+ break;
+ case RUNMODE_NFQ:
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
+ CAP_NET_ADMIN, /* needed for nfqueue inline mode */
+ -1);
+ break;
+ }
+
+ if (capng_change_id(userid, groupid, CAPNG_DROP_SUPP_GRP |
+ CAPNG_CLEAR_BOUNDING) < 0)
+ {
+ SCLogError(SC_ERR_CHANGING_CAPS_FAILED, "capng_change_id for main thread"
+ " failed");
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("dropped the caps for main thread");
+}
+
+void SCDropCaps(ThreadVars *tv)
+{
+#if 0
+ capng_clear(CAPNG_SELECT_BOTH);
+ capng_apply(CAPNG_SELECT_BOTH);
+ if (tv->cap_flags & SC_CAP_IPC_LOCK) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_IPC_LOCK);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_IPC_LOCK has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_NET_ADMIN) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_ADMIN);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_NET_ADMIN has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_NET_BIND_SERVICE) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BIND_SERVICE);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_NET_BIND_SERVICE has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_NET_BROADCAST) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BROADCAST);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_NET_BROADCAST has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_NET_RAW) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_RAW);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_NET_RAW has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_SYS_ADMIN) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_ADMIN);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_SYS_ADMIN has been set", tv->name);
+ }
+ if (tv->cap_flags & SC_CAP_SYS_RAW_IO) {
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_RAWIO);
+ capng_apply(CAPNG_SELECT_CAPS);
+ SCLogDebug("For thread \"%s\" CAP_SYS_RAWIO has been set", tv->name);
+ }
+#endif
+}
+
+#endif /* HAVE_LIBCAP_NG */
+
+/**
+ * \brief Function to get the user and group ID from the specified user name
+ *
+ * \param user_name pointer to the given user name
+ * \param uid pointer to the user id in which result will be stored
+ * \param gid pointer to the group id in which result will be stored
+ *
+ * \retval upon success it return 0
+ */
+int SCGetUserID(char *user_name, char *group_name, uint32_t *uid, uint32_t *gid)
+{
+ uint32_t userid = 0;
+ uint32_t groupid = 0;
+ struct passwd *pw;
+
+ /* Get the user ID */
+ if (isdigit((unsigned char)user_name[0]) != 0) {
+ userid = atoi(user_name);
+ pw = getpwuid(userid);
+ if (pw == NULL) {
+ SCLogError(SC_ERR_UID_FAILED, "unable to get the user ID, "
+ "check if user exist!!");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ pw = getpwnam(user_name);
+ if (pw == NULL) {
+ SCLogError(SC_ERR_UID_FAILED, "unable to get the user ID, "
+ "check if user exist!!");
+ exit(EXIT_FAILURE);
+ }
+ userid = pw->pw_uid;
+ }
+
+ /* Get the group ID */
+ if (group_name != NULL) {
+ struct group *gp;
+
+ if (isdigit((unsigned char)group_name[0]) != 0) {
+ groupid = atoi(group_name);
+ } else {
+ gp = getgrnam(group_name);
+ if (gp == NULL) {
+ SCLogError(SC_ERR_GID_FAILED, "unable to get the group"
+ " ID, check if group exist!!");
+ exit(EXIT_FAILURE);
+ }
+ groupid = gp->gr_gid;
+ }
+ } else {
+ groupid = pw->pw_gid;
+ }
+
+ /* close the group database */
+ endgrent();
+ /* close the user database */
+ endpwent();
+
+ *uid = userid;
+ *gid = groupid;
+
+ return 0;
+}
+
+/**
+ * \brief Function to get the group ID from the specified group name
+ *
+ * \param group_name pointer to the given group name
+ * \param gid pointer to the group id in which result will be stored
+ *
+ * \retval upon success it return 0
+ */
+int SCGetGroupID(char *group_name, uint32_t *gid)
+{
+ uint32_t grpid = 0;
+ struct group *gp;
+
+ /* Get the group ID */
+ if (isdigit((unsigned char)group_name[0]) != 0) {
+ grpid = atoi(group_name);
+ } else {
+ gp = getgrnam(group_name);
+ if (gp == NULL) {
+ SCLogError(SC_ERR_GID_FAILED, "unable to get the group ID,"
+ " check if group exist!!");
+ exit(EXIT_FAILURE);
+ }
+ grpid = gp->gr_gid;
+ }
+
+ /* close the group database */
+ endgrent();
+
+ *gid = grpid;
+
+ return 0;
+}
+#endif /* OS_WIN32 */
diff --git a/framework/src/suricata/src/util-privs.h b/framework/src/suricata/src/util-privs.h
new file mode 100644
index 00000000..aef33a48
--- /dev/null
+++ b/framework/src/suricata/src/util-privs.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef _UTIL_PRIVS_H
+#define _UTIL_PRIVS_H
+
+#define SC_CAP_NONE 0x01
+#define SC_CAP_SYS_ADMIN 0x02
+#define SC_CAP_SYS_RAW_IO 0x04
+#define SC_CAP_IPC_LOCK 0x08
+#define SC_CAP_NET_ADMIN 0x10
+#define SC_CAP_NET_RAW 0x20
+#define SC_CAP_NET_BIND_SERVICE 0x40
+#define SC_CAP_NET_BROADCAST 0x80
+
+#ifndef HAVE_LIBCAP_NG
+#define SCDropCaps(...)
+#define SCDropMainThreadCaps(...)
+#else
+#include "threadvars.h"
+#include "util-debug.h"
+#include <cap-ng.h>
+
+/**Drop the previliges of the given thread tv, based on the thread cap_flags
+ * which implies the capability requirement of the given thread. Initially all
+ * caps are dropped and later, the required caps are set for the given thread
+ */
+void SCDropCaps(ThreadVars *tv);
+/*
+#define SCDropCaps(tv) ({ \
+ capng_clear(CAPNG_SELECT_BOTH); \
+ capng_apply(CAPNG_SELECT_BOTH); \
+ if (tv->cap_flags & SC_CAP_IPC_LOCK) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_IPC_LOCK); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_IPC_LOCK has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_NET_ADMIN) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_ADMIN); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_NET_ADMIN has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_NET_BIND_SERVICE) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BIND_SERVICE); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_NET_BIND_SERVICE has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_NET_BROADCAST) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BROADCAST); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_NET_BROADCAST has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_NET_RAW) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_RAW); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_NET_RAW has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_SYS_ADMIN) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_ADMIN); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_SYS_ADMIN has been set", tv->name); \
+ } \
+ if (tv->cap_flags & SC_CAP_SYS_RAW_IO) { \
+ capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_RAWIO); \
+ capng_apply(CAPNG_SELECT_CAPS); \
+ SCLogDebug("For thread \"%s\" CAP_SYS_RAWIO has been set", tv->name); \
+ } \
+})
+*/
+void SCDropMainThreadCaps(uint32_t , uint32_t );
+
+#endif /* HAVE_LIBCAP_NG */
+
+int SCGetUserID(char *, char *, uint32_t *, uint32_t *);
+int SCGetGroupID(char *, uint32_t *);
+
+#endif /* _UTIL_PRIVS_H */
+
diff --git a/framework/src/suricata/src/util-profiling-keywords.c b/framework/src/suricata/src/util-profiling-keywords.c
new file mode 100644
index 00000000..9f3019aa
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling-keywords.c
@@ -0,0 +1,390 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * An API for rule profiling operations.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "conf.h"
+
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-byte.h"
+#include "util-profiling.h"
+#include "util-profiling-locks.h"
+
+#ifdef PROFILING
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/**
+ * Extra data for rule profiling.
+ */
+typedef struct SCProfileKeywordData_ {
+ uint64_t checks;
+ uint64_t matches;
+ uint64_t max;
+ uint64_t ticks_match;
+ uint64_t ticks_no_match;
+} SCProfileKeywordData;
+
+typedef struct SCProfileKeywordDetectCtx_ {
+ uint32_t id;
+ SCProfileKeywordData *data;
+ pthread_mutex_t data_m;
+} SCProfileKeywordDetectCtx;
+
+static int profiling_keywords_output_to_file = 0;
+int profiling_keyword_enabled = 0;
+__thread int profiling_keyword_entered = 0;
+static char *profiling_file_name = "";
+static const char *profiling_file_mode = "a";
+
+void SCProfilingKeywordsGlobalInit(void)
+{
+ ConfNode *conf;
+
+ conf = ConfGetNode("profiling.keywords");
+ if (conf != NULL) {
+ if (ConfNodeChildValueIsTrue(conf, "enabled")) {
+ profiling_keyword_enabled = 1;
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename != NULL) {
+
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name");
+ exit(EXIT_FAILURE);
+ }
+ snprintf(profiling_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ const char *v = ConfNodeLookupChildValue(conf, "append");
+ if (v == NULL || ConfValIsTrue(v)) {
+ profiling_file_mode = "a";
+ } else {
+ profiling_file_mode = "w";
+ }
+
+ profiling_keywords_output_to_file = 1;
+ }
+ }
+ }
+}
+
+void DoDump(SCProfileKeywordDetectCtx *rules_ctx, FILE *fp, const char *name)
+{
+ int i;
+ fprintf(fp, " ----------------------------------------------"
+ "------------------------------------------------------"
+ "----------------------------\n");
+ fprintf(fp, " Stats for: %s\n", name);
+ fprintf(fp, " ----------------------------------------------"
+ "------------------------------------------------------"
+ "----------------------------\n");
+ fprintf(fp, " %-16s %-15s %-15s %-15s %-15s %-15s %-15s %-15s\n", "Keyword", "Ticks", "Checks", "Matches", "Max Ticks", "Avg", "Avg Match", "Avg No Match");
+ fprintf(fp, " ---------------- "
+ "--------------- "
+ "--------------- "
+ "--------------- "
+ "--------------- "
+ "--------------- "
+ "--------------- "
+ "--------------- "
+ "\n");
+ for (i = 0; i < DETECT_TBLSIZE; i++) {
+ SCProfileKeywordData *d = &rules_ctx->data[i];
+ if (d == NULL || d->checks == 0)
+ continue;
+
+ uint64_t ticks = d->ticks_match + d->ticks_no_match;
+ double avgticks = 0;
+ double avgticks_match = 0;
+ double avgticks_no_match = 0;
+ if (ticks && d->checks) {
+ avgticks = (ticks / d->checks);
+
+ if (d->ticks_match && d->matches)
+ avgticks_match = (d->ticks_match / d->matches);
+ if (d->ticks_no_match && (d->checks - d->matches) != 0)
+ avgticks_no_match = (d->ticks_no_match / (d->checks - d->matches));
+ }
+
+ fprintf(fp,
+ " %-16s %-15"PRIu64" %-15"PRIu64" %-15"PRIu64" %-15"PRIu64" %-15.2f %-15.2f %-15.2f\n",
+ sigmatch_table[i].name,
+ ticks,
+ d->checks,
+ d->matches,
+ d->max,
+ avgticks,
+ avgticks_match,
+ avgticks_no_match);
+ }
+}
+
+void
+SCProfilingKeywordDump(DetectEngineCtx *de_ctx)
+{
+ int i;
+ FILE *fp;
+ struct timeval tval;
+ struct tm *tms;
+ struct tm local_tm;
+
+ if (profiling_keyword_enabled == 0)
+ return;
+
+ gettimeofday(&tval, NULL);
+ tms = SCLocalTime(tval.tv_sec, &local_tm);
+
+ if (profiling_keywords_output_to_file == 1) {
+ SCLogDebug("file %s mode %s", profiling_file_name, profiling_file_mode);
+
+ fp = fopen(profiling_file_name, profiling_file_mode);
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", profiling_file_name,
+ strerror(errno));
+ return;
+ }
+ } else {
+ fp = stdout;
+ }
+
+ fprintf(fp, " ----------------------------------------------"
+ "------------------------------------------------------"
+ "----------------------------\n");
+ fprintf(fp, " Date: %" PRId32 "/%" PRId32 "/%04d -- "
+ "%02d:%02d:%02d\n", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
+ tms->tm_hour,tms->tm_min, tms->tm_sec);
+
+ /* global stats first */
+ DoDump(de_ctx->profile_keyword_ctx, fp, "total");
+ /* per buffer stats next, but only if there are stats to print */
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ int j;
+ uint64_t checks = 0;
+ for (j = 0; j < DETECT_TBLSIZE; j++) {
+ checks += de_ctx->profile_keyword_ctx_per_list[i]->data[j].checks;
+ }
+
+ if (checks)
+ DoDump(de_ctx->profile_keyword_ctx_per_list[i], fp,
+ DetectSigmatchListEnumToString(i));
+ }
+
+ fprintf(fp,"\n");
+ if (fp != stdout)
+ fclose(fp);
+
+ SCLogInfo("Done dumping keyword profiling data.");
+}
+
+/**
+ * \brief Update a rule counter.
+ *
+ * \param id The ID of this counter.
+ * \param ticks Number of CPU ticks for this rule.
+ * \param match Did the rule match?
+ */
+void
+SCProfilingKeywordUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks, int match)
+{
+ if (det_ctx != NULL && det_ctx->keyword_perf_data != NULL && id < DETECT_TBLSIZE) {
+ SCProfileKeywordData *p = &det_ctx->keyword_perf_data[id];
+
+ p->checks++;
+ p->matches += match;
+ if (ticks > p->max)
+ p->max = ticks;
+ if (match == 1)
+ p->ticks_match += ticks;
+ else
+ p->ticks_no_match += ticks;
+
+ /* store per list (buffer type) as well */
+ if (det_ctx->keyword_perf_list >= 0 && det_ctx->keyword_perf_list < DETECT_SM_LIST_MAX) {
+ p = &det_ctx->keyword_perf_data_per_list[det_ctx->keyword_perf_list][id];
+ p->checks++;
+ p->matches += match;
+ if (ticks > p->max)
+ p->max = ticks;
+ if (match == 1)
+ p->ticks_match += ticks;
+ else
+ p->ticks_no_match += ticks;
+ }
+ }
+}
+
+SCProfileKeywordDetectCtx *SCProfilingKeywordInitCtx(void)
+{
+ SCProfileKeywordDetectCtx *ctx = SCMalloc(sizeof(SCProfileKeywordDetectCtx));
+ if (ctx != NULL) {
+ memset(ctx, 0x00, sizeof(SCProfileKeywordDetectCtx));
+
+ if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX,
+ "Failed to initialize hash table mutex.");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return ctx;
+}
+
+static void DetroyCtx(SCProfileKeywordDetectCtx *ctx)
+{
+ if (ctx) {
+ if (ctx->data != NULL)
+ SCFree(ctx->data);
+ pthread_mutex_destroy(&ctx->data_m);
+ SCFree(ctx);
+ }
+}
+
+void SCProfilingKeywordDestroyCtx(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx != NULL) {
+ SCProfilingKeywordDump(de_ctx);
+
+ DetroyCtx(de_ctx->profile_keyword_ctx);
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ DetroyCtx(de_ctx->profile_keyword_ctx_per_list[i]);
+ }
+ }
+}
+
+void SCProfilingKeywordThreadSetup(SCProfileKeywordDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ SCProfileKeywordData *a = SCMalloc(sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ if (a != NULL) {
+ memset(a, 0x00, sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ det_ctx->keyword_perf_data = a;
+ }
+
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ SCProfileKeywordData *b = SCMalloc(sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ if (b != NULL) {
+ memset(b, 0x00, sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ det_ctx->keyword_perf_data_per_list[i] = b;
+ }
+
+ }
+}
+
+static void SCProfilingKeywordThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (de_ctx == NULL || de_ctx->profile_keyword_ctx == NULL ||
+ de_ctx->profile_keyword_ctx->data == NULL || det_ctx == NULL ||
+ det_ctx->keyword_perf_data == NULL)
+ return;
+
+ int i;
+ for (i = 0; i < DETECT_TBLSIZE; i++) {
+ de_ctx->profile_keyword_ctx->data[i].checks += det_ctx->keyword_perf_data[i].checks;
+ de_ctx->profile_keyword_ctx->data[i].matches += det_ctx->keyword_perf_data[i].matches;
+ de_ctx->profile_keyword_ctx->data[i].ticks_match += det_ctx->keyword_perf_data[i].ticks_match;
+ de_ctx->profile_keyword_ctx->data[i].ticks_no_match += det_ctx->keyword_perf_data[i].ticks_no_match;
+ if (det_ctx->keyword_perf_data[i].max > de_ctx->profile_keyword_ctx->data[i].max)
+ de_ctx->profile_keyword_ctx->data[i].max = det_ctx->keyword_perf_data[i].max;
+ }
+
+ int j;
+ for (j = 0; j < DETECT_SM_LIST_MAX; j++) {
+ for (i = 0; i < DETECT_TBLSIZE; i++) {
+ de_ctx->profile_keyword_ctx_per_list[j]->data[i].checks += det_ctx->keyword_perf_data_per_list[j][i].checks;
+ de_ctx->profile_keyword_ctx_per_list[j]->data[i].matches += det_ctx->keyword_perf_data_per_list[j][i].matches;
+ de_ctx->profile_keyword_ctx_per_list[j]->data[i].ticks_match += det_ctx->keyword_perf_data_per_list[j][i].ticks_match;
+ de_ctx->profile_keyword_ctx_per_list[j]->data[i].ticks_no_match += det_ctx->keyword_perf_data_per_list[j][i].ticks_no_match;
+ if (det_ctx->keyword_perf_data_per_list[j][i].max > de_ctx->profile_keyword_ctx_per_list[j]->data[i].max)
+ de_ctx->profile_keyword_ctx_per_list[j]->data[i].max = det_ctx->keyword_perf_data_per_list[j][i].max;
+ }
+ }
+}
+
+void SCProfilingKeywordThreadCleanup(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->keyword_perf_data == NULL)
+ return;
+
+ pthread_mutex_lock(&det_ctx->de_ctx->profile_keyword_ctx->data_m);
+ SCProfilingKeywordThreadMerge(det_ctx->de_ctx, det_ctx);
+ pthread_mutex_unlock(&det_ctx->de_ctx->profile_keyword_ctx->data_m);
+
+ SCFree(det_ctx->keyword_perf_data);
+ det_ctx->keyword_perf_data = NULL;
+
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ SCFree(det_ctx->keyword_perf_data_per_list[i]);
+ det_ctx->keyword_perf_data_per_list[i] = NULL;
+ }
+
+}
+
+/**
+ * \brief Register the keyword profiling counters.
+ *
+ * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
+ */
+void
+SCProfilingKeywordInitCounters(DetectEngineCtx *de_ctx)
+{
+ if (profiling_keyword_enabled == 0)
+ return;
+
+ de_ctx->profile_keyword_ctx = SCProfilingKeywordInitCtx();
+ BUG_ON(de_ctx->profile_keyword_ctx == NULL);
+
+ de_ctx->profile_keyword_ctx->data = SCMalloc(sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ BUG_ON(de_ctx->profile_keyword_ctx->data == NULL);
+ memset(de_ctx->profile_keyword_ctx->data, 0x00, sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+
+ int i;
+ for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
+ de_ctx->profile_keyword_ctx_per_list[i] = SCProfilingKeywordInitCtx();
+ BUG_ON(de_ctx->profile_keyword_ctx_per_list[i] == NULL);
+ de_ctx->profile_keyword_ctx_per_list[i]->data = SCMalloc(sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ BUG_ON(de_ctx->profile_keyword_ctx_per_list[i]->data == NULL);
+ memset(de_ctx->profile_keyword_ctx_per_list[i]->data, 0x00, sizeof(SCProfileKeywordData) * DETECT_TBLSIZE);
+ }
+
+ SCLogInfo("Registered %"PRIu32" keyword profiling counters.", DETECT_TBLSIZE);
+}
+
+#endif /* PROFILING */
diff --git a/framework/src/suricata/src/util-profiling-locks.c b/framework/src/suricata/src/util-profiling-locks.c
new file mode 100644
index 00000000..97cc3e0d
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling-locks.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * An API for profiling locks.
+ *
+ */
+
+#ifdef PROFILING
+#ifdef PROFILE_LOCKING
+
+#include "suricata-common.h"
+#include "util-profiling-locks.h"
+#include "util-hashlist.h"
+
+__thread ProfilingLock locks[PROFILING_MAX_LOCKS];
+__thread int locks_idx = 0;
+__thread int record_locks = 0;
+
+int profiling_locks_enabled = 0;
+int profiling_locks_output_to_file = 0;
+char *profiling_locks_file_name = NULL;
+char *profiling_locks_file_mode = "a";
+
+typedef struct LockRecord_ {
+ char *file; // hash
+
+ char *func; // info
+ int type; // info
+
+ int line; // hash
+
+ uint32_t cont;
+ uint32_t ticks_cnt;
+ uint64_t ticks_total;
+ uint64_t ticks_max;
+} LockRecord;
+
+HashListTable *lock_records;
+pthread_mutex_t lock_records_mutex;
+
+static uint32_t LockRecordHash(HashListTable *ht, void *buf, uint16_t buflen)
+{
+ LockRecord *fn = (LockRecord *)buf;
+ uint32_t hash = strlen(fn->file) + fn->line;
+ uint16_t u;
+
+ for (u = 0; u < strlen(fn->file); u++) {
+ hash += fn->file[u];
+ }
+
+ return hash % ht->array_size;
+}
+
+static char LockRecordCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
+{
+ LockRecord *fn1 = (LockRecord *)buf1;
+ LockRecord *fn2 = (LockRecord *)buf2;
+
+ if (fn1->line != fn2->line)
+ return 0;
+
+ if (fn1->file == fn2->file)
+ return 1;
+
+ return 0;
+}
+
+static void LockRecordFree(void *data)
+{
+ LockRecord *fn = (LockRecord *)data;
+
+ if (fn == NULL)
+ return;
+ SCFree(fn);
+}
+
+int LockRecordInitHash()
+{
+ pthread_mutex_init(&lock_records_mutex, NULL);
+ pthread_mutex_lock(&lock_records_mutex);
+
+ lock_records = HashListTableInit(512, LockRecordHash, LockRecordCompare, LockRecordFree);
+ BUG_ON(lock_records == NULL);
+
+ pthread_mutex_unlock(&lock_records_mutex);
+
+ return 0;
+}
+
+void LockRecordAdd(ProfilingLock *l)
+{
+ LockRecord fn = { NULL, NULL, 0,0,0,0,0,0}, *ptr = &fn;
+ fn.file = l->file;
+ fn.line = l->line;
+
+ LockRecord *lookup_fn = (LockRecord *)HashListTableLookup(lock_records, (void *)ptr, 0);
+ if (lookup_fn == NULL) {
+ LockRecord *new = SCMalloc(sizeof(LockRecord));
+ BUG_ON(new == NULL);
+
+ new->file = l->file;
+ new->line = l->line;
+ new->type = l->type;
+ new->cont = l->cont;
+ new->func = l->func;
+ new->ticks_max = l->ticks;
+ new->ticks_total = l->ticks;
+ new->ticks_cnt = 1;
+
+ HashListTableAdd(lock_records, (void *)new, 0);
+ } else {
+ lookup_fn->ticks_total += l->ticks;
+ if (l->ticks > lookup_fn->ticks_max)
+ lookup_fn->ticks_max = l->ticks;
+ lookup_fn->ticks_cnt++;
+ lookup_fn->cont += l->cont;
+ }
+
+ return;
+}
+
+/** \param p void ptr to Packet struct */
+void SCProfilingAddPacketLocks(void *p)
+{
+ int i;
+
+ if (profiling_locks_enabled == 0)
+ return;
+
+ for (i = 0; i < locks_idx; i++) {
+ pthread_mutex_lock(&lock_records_mutex);
+ LockRecordAdd(&locks[i]);
+ pthread_mutex_unlock(&lock_records_mutex);
+ }
+}
+
+void SCProfilingListLocks(void)
+{
+ FILE *fp = NULL;
+
+ if (profiling_locks_output_to_file == 1) {
+ fp = fopen(profiling_locks_file_name, profiling_locks_file_mode);
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s",
+ profiling_locks_file_name, strerror(errno));
+ return;
+ }
+ } else {
+ fp = stdout;
+ }
+
+ fprintf(fp, "\n\nLock Cnt Avg ticks Max ticks Total ticks Cont Func\n");
+ fprintf(fp, "------------------ ---------- --------- ------------ ------------ ------- ---------\n");
+
+ uint64_t total = 0;
+ uint32_t cont = 0;
+ uint64_t cnt = 0;
+
+ HashListTableBucket *b = HashListTableGetListHead(lock_records);
+ while (b) {
+ LockRecord *r = HashListTableGetListData(b);
+
+ char *lock;
+ switch (r->type) {
+ case LOCK_MUTEX:
+ lock = "mtx";
+ break;
+ case LOCK_SPIN:
+ lock = "spn";
+ break;
+ case LOCK_RWW:
+ lock = "rww";
+ break;
+ case LOCK_RWR:
+ lock = "rwr";
+ break;
+ default:
+ lock = "bug";
+ break;
+ }
+
+ char str[128] = "";
+ snprintf(str, sizeof(str), "(%s) %s:%d", lock,r->file, r->line);
+
+ fprintf(fp, "%-50s %-10u %-9"PRIu64" %-12"PRIu64" %-12"PRIu64" %-7u %-s\n",
+ str, r->ticks_cnt, (uint64_t)((uint64_t)r->ticks_total/(uint64_t)r->ticks_cnt), r->ticks_max, r->ticks_total, r->cont, r->func);
+
+ total += r->ticks_total;
+ cnt += r->ticks_cnt;
+ cont += r->cont;
+
+ b = HashListTableGetListNext(b);
+ }
+
+ fprintf(fp, "\nOverall: locks %"PRIu64", average cost %"PRIu64", contentions %"PRIu32", total ticks %"PRIu64"\n",
+ cnt, (uint64_t)((uint64_t)total/(uint64_t)cnt), cont, total);
+
+ fclose(fp);
+}
+
+void LockRecordFreeHash()
+{
+ if (profiling_locks_enabled == 0)
+ return;
+
+ pthread_mutex_lock(&lock_records_mutex);
+
+ SCProfilingListLocks();
+
+ if (lock_records != NULL) {
+ HashListTableFree(lock_records);
+ lock_records = NULL;
+ }
+ pthread_mutex_unlock(&lock_records_mutex);
+
+ pthread_mutex_destroy(&lock_records_mutex);
+}
+
+#endif
+#endif
+
diff --git a/framework/src/suricata/src/util-profiling-locks.h b/framework/src/suricata/src/util-profiling-locks.h
new file mode 100644
index 00000000..453928a0
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling-locks.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_PROFILE_LOCKS_H__
+#define __UTIL_PROFILE_LOCKS_H__
+
+#ifdef PROFILING
+
+#define PROFILING_MAX_LOCKS 64
+
+enum {
+ LOCK_MUTEX,
+ LOCK_SPIN,
+ LOCK_RWW, /**< rwlock, writer */
+ LOCK_RWR, /**< rwlock, reader */
+};
+
+void SCProfilingAddPacketLocks(void *);
+
+int LockRecordInitHash();
+void LockRecordFreeHash();
+
+#endif /* PROFILING */
+#endif /* __UTIL_PROFILE_LOCKS_H__ */
+
diff --git a/framework/src/suricata/src/util-profiling-rules.c b/framework/src/suricata/src/util-profiling-rules.c
new file mode 100644
index 00000000..2f4ec5c7
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling-rules.c
@@ -0,0 +1,593 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * An API for rule profiling operations.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "conf.h"
+
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-byte.h"
+#include "util-profiling.h"
+#include "util-profiling-locks.h"
+
+#ifdef PROFILING
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/**
+ * Extra data for rule profiling.
+ */
+typedef struct SCProfileData_ {
+ uint32_t sid;
+ uint32_t gid;
+ uint32_t rev;
+ uint64_t checks;
+ uint64_t matches;
+ uint64_t max;
+ uint64_t ticks_match;
+ uint64_t ticks_no_match;
+} SCProfileData;
+
+typedef struct SCProfileDetectCtx_ {
+ uint32_t size;
+ uint32_t id;
+ SCProfileData *data;
+ pthread_mutex_t data_m;
+} SCProfileDetectCtx;
+
+/**
+ * Used for generating the summary data to print.
+ */
+typedef struct SCProfileSummary_ {
+ uint32_t sid;
+ uint32_t gid;
+ uint32_t rev;
+ uint64_t ticks;
+ double avgticks;
+ double avgticks_match;
+ double avgticks_no_match;
+ uint64_t checks;
+ uint64_t matches;
+ uint64_t max;
+ uint64_t ticks_match;
+ uint64_t ticks_no_match;
+} SCProfileSummary;
+
+extern int profiling_output_to_file;
+int profiling_rules_enabled = 0;
+static char *profiling_file_name = "";
+static const char *profiling_file_mode = "a";
+
+/**
+ * Sort orders for dumping profiled rules.
+ */
+enum {
+ SC_PROFILING_RULES_SORT_BY_TICKS = 0,
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
+ SC_PROFILING_RULES_SORT_BY_CHECKS,
+ SC_PROFILING_RULES_SORT_BY_MATCHES,
+ SC_PROFILING_RULES_SORT_BY_MAX_TICKS,
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH,
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH,
+};
+static int profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_TICKS;
+
+/**
+ * Maximum number of rules to dump.
+ */
+static uint32_t profiling_rules_limit = UINT32_MAX;
+
+void SCProfilingRulesGlobalInit(void)
+{
+ ConfNode *conf;
+ const char *val;
+
+ conf = ConfGetNode("profiling.rules");
+ if (conf != NULL) {
+ if (ConfNodeChildValueIsTrue(conf, "enabled")) {
+ profiling_rules_enabled = 1;
+
+ val = ConfNodeLookupChildValue(conf, "sort");
+ if (val != NULL) {
+ if (strcmp(val, "ticks") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_TICKS;
+ }
+ else if (strcmp(val, "avgticks") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS;
+ }
+ else if (strcmp(val, "avgticks_match") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH;
+ }
+ else if (strcmp(val, "avgticks_no_match") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH;
+ }
+ else if (strcmp(val, "checks") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_CHECKS;
+ }
+ else if (strcmp(val, "matches") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_MATCHES;
+ }
+ else if (strcmp(val, "maxticks") == 0) {
+ profiling_rules_sort_order =
+ SC_PROFILING_RULES_SORT_BY_MAX_TICKS;
+ }
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid profiling sort order: %s", val);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ val = ConfNodeLookupChildValue(conf, "limit");
+ if (val != NULL) {
+ if (ByteExtractStringUint32(&profiling_rules_limit, 10,
+ (uint16_t)strlen(val), val) <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid limit: %s", val);
+ exit(EXIT_FAILURE);
+ }
+ }
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename != NULL) {
+
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name");
+ exit(EXIT_FAILURE);
+ }
+ snprintf(profiling_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ const char *v = ConfNodeLookupChildValue(conf, "append");
+ if (v == NULL || ConfValIsTrue(v)) {
+ profiling_file_mode = "a";
+ } else {
+ profiling_file_mode = "w";
+ }
+
+ profiling_output_to_file = 1;
+ }
+ }
+ }
+}
+
+/**
+ * \brief Qsort comparison function to sort by ticks.
+ */
+static int
+SCProfileSummarySortByTicks(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->ticks == s0->ticks)
+ return 0;
+ else
+ return s0->ticks > s1->ticks ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by average ticks per match.
+ */
+static int
+SCProfileSummarySortByAvgTicksMatch(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->avgticks_match == s0->avgticks_match)
+ return 0;
+ else
+ return s0->avgticks_match > s1->avgticks_match ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by average ticks per non match.
+ */
+static int
+SCProfileSummarySortByAvgTicksNoMatch(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->avgticks_no_match == s0->avgticks_no_match)
+ return 0;
+ else
+ return s0->avgticks_no_match > s1->avgticks_no_match ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by average ticks.
+ */
+static int
+SCProfileSummarySortByAvgTicks(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->avgticks == s0->avgticks)
+ return 0;
+ else
+ return s0->avgticks > s1->avgticks ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by checks.
+ */
+static int
+SCProfileSummarySortByChecks(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->checks == s0->checks)
+ return 0;
+ else
+ return s0->checks > s1->checks ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by matches.
+ */
+static int
+SCProfileSummarySortByMatches(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->matches == s0->matches)
+ return 0;
+ else
+ return s0->matches > s1->matches ? -1 : 1;
+}
+
+/**
+ * \brief Qsort comparison function to sort by max ticks.
+ */
+static int
+SCProfileSummarySortByMaxTicks(const void *a, const void *b)
+{
+ const SCProfileSummary *s0 = a;
+ const SCProfileSummary *s1 = b;
+ if (s1->max == s0->max)
+ return 0;
+ else
+ return s0->max > s1->max ? -1 : 1;
+}
+
+/**
+ * \brief Dump rule profiling information to file
+ *
+ * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
+ */
+void
+SCProfilingRuleDump(SCProfileDetectCtx *rules_ctx)
+{
+ uint32_t i;
+ FILE *fp;
+
+ if (rules_ctx == NULL)
+ return;
+
+ struct timeval tval;
+ struct tm *tms;
+ if (profiling_output_to_file == 1) {
+ fp = fopen(profiling_file_name, profiling_file_mode);
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", profiling_file_name,
+ strerror(errno));
+ return;
+ }
+ } else {
+ fp = stdout;
+ }
+
+ int summary_size = sizeof(SCProfileSummary) * rules_ctx->size;
+ SCProfileSummary *summary = SCMalloc(summary_size);
+ if (unlikely(summary == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for profiling summary");
+ return;
+ }
+
+ uint32_t count = rules_ctx->size;
+ uint64_t total_ticks = 0;
+
+ SCLogInfo("Dumping profiling data for %u rules.", count);
+
+ memset(summary, 0, summary_size);
+ for (i = 0; i < count; i++) {
+ summary[i].sid = rules_ctx->data[i].sid;
+ summary[i].rev = rules_ctx->data[i].rev;
+ summary[i].gid = rules_ctx->data[i].gid;
+
+ summary[i].ticks = rules_ctx->data[i].ticks_match + rules_ctx->data[i].ticks_no_match;
+ summary[i].checks = rules_ctx->data[i].checks;
+
+ if (summary[i].ticks > 0) {
+ summary[i].avgticks = (long double)summary[i].ticks / (long double)summary[i].checks;
+ }
+
+ summary[i].matches = rules_ctx->data[i].matches;
+ summary[i].max = rules_ctx->data[i].max;
+ summary[i].ticks_match = rules_ctx->data[i].ticks_match;
+ summary[i].ticks_no_match = rules_ctx->data[i].ticks_no_match;
+ if (summary[i].ticks_match > 0) {
+ summary[i].avgticks_match = (long double)summary[i].ticks_match /
+ (long double)summary[i].matches;
+ }
+
+ if (summary[i].ticks_no_match > 0) {
+ summary[i].avgticks_no_match = (long double)summary[i].ticks_no_match /
+ ((long double)summary[i].checks - (long double)summary[i].matches);
+ }
+ total_ticks += summary[i].ticks;
+ }
+
+ switch (profiling_rules_sort_order) {
+ case SC_PROFILING_RULES_SORT_BY_TICKS:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByTicks);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_AVG_TICKS:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByAvgTicks);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_CHECKS:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByChecks);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_MATCHES:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByMatches);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_MAX_TICKS:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByMaxTicks);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByAvgTicksMatch);
+ break;
+ case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH:
+ qsort(summary, count, sizeof(SCProfileSummary),
+ SCProfileSummarySortByAvgTicksNoMatch);
+ break;
+ }
+
+ gettimeofday(&tval, NULL);
+ struct tm local_tm;
+ tms = SCLocalTime(tval.tv_sec, &local_tm);
+
+ fprintf(fp, " ----------------------------------------------"
+ "----------------------------\n");
+ fprintf(fp, " Date: %" PRId32 "/%" PRId32 "/%04d -- "
+ "%02d:%02d:%02d\n", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
+ tms->tm_hour,tms->tm_min, tms->tm_sec);
+ fprintf(fp, " ----------------------------------------------"
+ "----------------------------\n");
+ fprintf(fp, " %-8s %-12s %-8s %-8s %-12s %-6s %-8s %-8s %-11s %-11s %-11s %-11s\n", "Num", "Rule", "Gid", "Rev", "Ticks", "%", "Checks", "Matches", "Max Ticks", "Avg Ticks", "Avg Match", "Avg No Match");
+ fprintf(fp, " -------- "
+ "------------ "
+ "-------- "
+ "-------- "
+ "------------ "
+ "------ "
+ "-------- "
+ "-------- "
+ "----------- "
+ "----------- "
+ "----------- "
+ "-------------- "
+ "\n");
+ for (i = 0; i < MIN(count, profiling_rules_limit); i++) {
+
+ /* Stop dumping when we hit our first rule with 0 checks. Due
+ * to sorting this will be the beginning of all the rules with
+ * 0 checks. */
+ if (summary[i].checks == 0)
+ break;
+
+ double percent = (long double)summary[i].ticks /
+ (long double)total_ticks * 100;
+ fprintf(fp,
+ " %-8"PRIu32" %-12u %-8"PRIu32" %-8"PRIu32" %-12"PRIu64" %-6.2f %-8"PRIu64" %-8"PRIu64" %-11"PRIu64" %-11.2f %-11.2f %-11.2f\n",
+ i + 1,
+ summary[i].sid,
+ summary[i].gid,
+ summary[i].rev,
+ summary[i].ticks,
+ percent,
+ summary[i].checks,
+ summary[i].matches,
+ summary[i].max,
+ summary[i].avgticks,
+ summary[i].avgticks_match,
+ summary[i].avgticks_no_match);
+ }
+
+ fprintf(fp,"\n");
+ if (fp != stdout)
+ fclose(fp);
+ SCFree(summary);
+ SCLogInfo("Done dumping profiling data.");
+}
+
+/**
+ * \brief Register a rule profiling counter.
+ *
+ * \retval Returns the ID of the counter on success, 0 on failure.
+ */
+static uint16_t
+SCProfilingRegisterRuleCounter(SCProfileDetectCtx *ctx)
+{
+ ctx->size++;
+ return ctx->id++;
+}
+
+/**
+ * \brief Update a rule counter.
+ *
+ * \param id The ID of this counter.
+ * \param ticks Number of CPU ticks for this rule.
+ * \param match Did the rule match?
+ */
+void
+SCProfilingRuleUpdateCounter(DetectEngineThreadCtx *det_ctx, uint16_t id, uint64_t ticks, int match)
+{
+ if (det_ctx != NULL && det_ctx->rule_perf_data != NULL && det_ctx->rule_perf_data_size > id) {
+ SCProfileData *p = &det_ctx->rule_perf_data[id];
+
+ p->checks++;
+ p->matches += match;
+ if (ticks > p->max)
+ p->max = ticks;
+ if (match == 1)
+ p->ticks_match += ticks;
+ else
+ p->ticks_no_match += ticks;
+ }
+}
+
+SCProfileDetectCtx *SCProfilingRuleInitCtx(void)
+{
+ SCProfileDetectCtx *ctx = SCMalloc(sizeof(SCProfileDetectCtx));
+ if (ctx != NULL) {
+ memset(ctx, 0x00, sizeof(SCProfileDetectCtx));
+
+ if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX,
+ "Failed to initialize hash table mutex.");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return ctx;
+}
+
+void SCProfilingRuleDestroyCtx(SCProfileDetectCtx *ctx)
+{
+ if (ctx != NULL) {
+ SCProfilingRuleDump(ctx);
+ if (ctx->data != NULL)
+ SCFree(ctx->data);
+ pthread_mutex_destroy(&ctx->data_m);
+ SCFree(ctx);
+ }
+}
+
+void SCProfilingRuleThreadSetup(SCProfileDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (ctx == NULL|| ctx->size == 0)
+ return;
+
+ SCProfileData *a = SCMalloc(sizeof(SCProfileData) * ctx->size);
+ if (a != NULL) {
+ memset(a, 0x00, sizeof(SCProfileData) * ctx->size);
+
+ det_ctx->rule_perf_data = a;
+ det_ctx->rule_perf_data_size = ctx->size;
+ }
+}
+
+static void SCProfilingRuleThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
+{
+ if (de_ctx == NULL || de_ctx->profile_ctx == NULL || de_ctx->profile_ctx->data == NULL ||
+ det_ctx == NULL || det_ctx->rule_perf_data == NULL)
+ return;
+
+ int i;
+ for (i = 0; i < det_ctx->rule_perf_data_size; i++) {
+ de_ctx->profile_ctx->data[i].checks += det_ctx->rule_perf_data[i].checks;
+ de_ctx->profile_ctx->data[i].matches += det_ctx->rule_perf_data[i].matches;
+ de_ctx->profile_ctx->data[i].ticks_match += det_ctx->rule_perf_data[i].ticks_match;
+ de_ctx->profile_ctx->data[i].ticks_no_match += det_ctx->rule_perf_data[i].ticks_no_match;
+ if (det_ctx->rule_perf_data[i].max > de_ctx->profile_ctx->data[i].max)
+ de_ctx->profile_ctx->data[i].max = det_ctx->rule_perf_data[i].max;
+ }
+}
+
+void SCProfilingRuleThreadCleanup(DetectEngineThreadCtx *det_ctx)
+{
+ if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->rule_perf_data == NULL)
+ return;
+
+ pthread_mutex_lock(&det_ctx->de_ctx->profile_ctx->data_m);
+ SCProfilingRuleThreadMerge(det_ctx->de_ctx, det_ctx);
+ pthread_mutex_unlock(&det_ctx->de_ctx->profile_ctx->data_m);
+
+ SCFree(det_ctx->rule_perf_data);
+ det_ctx->rule_perf_data = NULL;
+ det_ctx->rule_perf_data_size = 0;
+}
+
+/**
+ * \brief Register the rule profiling counters.
+ *
+ * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
+ */
+void
+SCProfilingRuleInitCounters(DetectEngineCtx *de_ctx)
+{
+ if (profiling_rules_enabled == 0)
+ return;
+
+ de_ctx->profile_ctx = SCProfilingRuleInitCtx();
+ BUG_ON(de_ctx->profile_ctx == NULL);
+
+ Signature *sig = de_ctx->sig_list;
+ uint32_t count = 0;
+ while (sig != NULL) {
+ sig->profiling_id = SCProfilingRegisterRuleCounter(de_ctx->profile_ctx);
+ sig = sig->next;
+ count++;
+ }
+
+ if (count > 0) {
+ de_ctx->profile_ctx->data = SCMalloc(sizeof(SCProfileData) * de_ctx->profile_ctx->size);
+ BUG_ON(de_ctx->profile_ctx->data == NULL);
+ memset(de_ctx->profile_ctx->data, 0x00, sizeof(SCProfileData) * de_ctx->profile_ctx->size);
+
+ sig = de_ctx->sig_list;
+ while (sig != NULL) {
+ de_ctx->profile_ctx->data[sig->profiling_id].sid = sig->id;
+ de_ctx->profile_ctx->data[sig->profiling_id].gid = sig->gid;
+ de_ctx->profile_ctx->data[sig->profiling_id].rev = sig->rev;
+ sig = sig->next;
+ }
+ }
+
+ SCLogInfo("Registered %"PRIu32" rule profiling counters.", count);
+}
+
+#endif /* PROFILING */
+
diff --git a/framework/src/suricata/src/util-profiling.c b/framework/src/suricata/src/util-profiling.c
new file mode 100644
index 00000000..dfe8c774
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling.c
@@ -0,0 +1,1160 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * An API for profiling operations.
+ *
+ * Really just a wrapper around the existing perf counters.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "conf.h"
+
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-byte.h"
+#include "util-profiling.h"
+#include "util-profiling-locks.h"
+
+#ifdef PROFILING
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define DEFAULT_LOG_FILENAME "profile.log"
+#define DEFAULT_LOG_MODE_APPEND "yes"
+
+static pthread_mutex_t packet_profile_lock;
+static FILE *packet_profile_csv_fp = NULL;
+
+extern int profiling_locks_enabled;
+extern int profiling_locks_output_to_file;
+extern char *profiling_locks_file_name;
+extern char *profiling_locks_file_mode;
+
+typedef struct SCProfilePacketData_ {
+ uint64_t min;
+ uint64_t max;
+ uint64_t tot;
+ uint64_t cnt;
+#ifdef PROFILE_LOCKING
+ uint64_t lock;
+ uint64_t ticks;
+ uint64_t contention;
+
+ uint64_t slock;
+ uint64_t sticks;
+ uint64_t scontention;
+#endif
+} SCProfilePacketData;
+SCProfilePacketData packet_profile_data4[257]; /**< all proto's + tunnel */
+SCProfilePacketData packet_profile_data6[257]; /**< all proto's + tunnel */
+
+/* each module, each proto */
+SCProfilePacketData packet_profile_tmm_data4[TMM_SIZE][257];
+SCProfilePacketData packet_profile_tmm_data6[TMM_SIZE][257];
+
+SCProfilePacketData packet_profile_app_data4[TMM_SIZE][257];
+SCProfilePacketData packet_profile_app_data6[TMM_SIZE][257];
+
+SCProfilePacketData packet_profile_app_pd_data4[257];
+SCProfilePacketData packet_profile_app_pd_data6[257];
+
+SCProfilePacketData packet_profile_detect_data4[PROF_DETECT_SIZE][257];
+SCProfilePacketData packet_profile_detect_data6[PROF_DETECT_SIZE][257];
+
+int profiling_packets_enabled = 0;
+int profiling_packets_csv_enabled = 0;
+
+int profiling_output_to_file = 0;
+int profiling_packets_output_to_file = 0;
+char *profiling_file_name;
+char *profiling_packets_file_name;
+char *profiling_csv_file_name;
+const char *profiling_packets_file_mode = "a";
+
+static int rate = 1;
+static SC_ATOMIC_DECLARE(uint64_t, samples);
+
+/**
+ * Used as a check so we don't double enter a profiling run.
+ */
+__thread int profiling_rules_entered = 0;
+
+void SCProfilingDumpPacketStats(void);
+const char * PacketProfileDetectIdToString(PacketProfileDetectId id);
+
+static void FormatNumber(uint64_t num, char *str, size_t size)
+{
+ if (num < 1000UL)
+ snprintf(str, size, "%"PRIu64, num);
+ else if (num < 1000000UL)
+ snprintf(str, size, "%3.1fk", (float)num/1000UL);
+ else if (num < 1000000000UL)
+ snprintf(str, size, "%3.1fm", (float)num/1000000UL);
+ else
+ snprintf(str, size, "%3.1fb", (float)num/1000000000UL);
+}
+
+/**
+ * \brief Initialize profiling.
+ */
+void
+SCProfilingInit(void)
+{
+ ConfNode *conf;
+
+ SC_ATOMIC_INIT(samples);
+
+ intmax_t rate_v = 0;
+ (void)ConfGetInt("profiling.sample-rate", &rate_v);
+ if (rate_v > 0 && rate_v < INT_MAX) {
+ rate = (int)rate_v;
+ if (rate != 1)
+ SCLogInfo("profiling runs for every %dth packet", rate);
+ else
+ SCLogInfo("profiling runs for every packet");
+ }
+
+ conf = ConfGetNode("profiling.packets");
+ if (conf != NULL) {
+ if (ConfNodeChildValueIsTrue(conf, "enabled")) {
+ profiling_packets_enabled = 1;
+
+ if (pthread_mutex_init(&packet_profile_lock, NULL) != 0) {
+ SCLogError(SC_ERR_MUTEX,
+ "Failed to initialize packet profiling mutex.");
+ exit(EXIT_FAILURE);
+ }
+ memset(&packet_profile_data4, 0, sizeof(packet_profile_data4));
+ memset(&packet_profile_data6, 0, sizeof(packet_profile_data6));
+ memset(&packet_profile_tmm_data4, 0, sizeof(packet_profile_tmm_data4));
+ memset(&packet_profile_tmm_data6, 0, sizeof(packet_profile_tmm_data6));
+ memset(&packet_profile_app_data4, 0, sizeof(packet_profile_app_data4));
+ memset(&packet_profile_app_data6, 0, sizeof(packet_profile_app_data6));
+ memset(&packet_profile_app_pd_data4, 0, sizeof(packet_profile_app_pd_data4));
+ memset(&packet_profile_app_pd_data6, 0, sizeof(packet_profile_app_pd_data6));
+ memset(&packet_profile_detect_data4, 0, sizeof(packet_profile_detect_data4));
+ memset(&packet_profile_detect_data6, 0, sizeof(packet_profile_detect_data6));
+
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename != NULL) {
+
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_packets_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_packets_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name");
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(profiling_packets_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ const char *v = ConfNodeLookupChildValue(conf, "append");
+ if (v == NULL || ConfValIsTrue(v)) {
+ profiling_packets_file_mode = "a";
+ } else {
+ profiling_packets_file_mode = "w";
+ }
+
+ profiling_packets_output_to_file = 1;
+ }
+ }
+
+ conf = ConfGetNode("profiling.packets.csv");
+ if (conf != NULL) {
+ if (ConfNodeChildValueIsTrue(conf, "enabled")) {
+
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename == NULL) {
+ filename = "packet_profile.csv";
+ }
+
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_csv_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_csv_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "out of memory");
+ exit(EXIT_FAILURE);
+ }
+ snprintf(profiling_csv_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ packet_profile_csv_fp = fopen(profiling_csv_file_name, "w");
+ if (packet_profile_csv_fp == NULL) {
+ return;
+ }
+ fprintf(packet_profile_csv_fp, "pcap_cnt,ipver,ipproto,total,");
+ int i;
+ for (i = 0; i < TMM_SIZE; i++) {
+ fprintf(packet_profile_csv_fp, "%s,", TmModuleTmmIdToString(i));
+ }
+ fprintf(packet_profile_csv_fp, "threading,");
+ for (i = 0; i < ALPROTO_MAX; i++) {
+ fprintf(packet_profile_csv_fp, "%s,", AppProtoToString(i));
+ }
+ fprintf(packet_profile_csv_fp, "STREAM (no app),proto detect,");
+ for (i = 0; i < PROF_DETECT_SIZE; i++) {
+ fprintf(packet_profile_csv_fp, "%s,", PacketProfileDetectIdToString(i));
+ }
+ fprintf(packet_profile_csv_fp, "\n");
+
+ profiling_packets_csv_enabled = 1;
+ }
+ }
+ }
+
+ conf = ConfGetNode("profiling.locks");
+ if (conf != NULL) {
+ if (ConfNodeChildValueIsTrue(conf, "enabled")) {
+#ifndef PROFILE_LOCKING
+ SCLogWarning(SC_WARN_PROFILE, "lock profiling not compiled in. Add --enable-profiling-locks to configure.");
+#else
+ profiling_locks_enabled = 1;
+
+ LockRecordInitHash();
+
+ const char *filename = ConfNodeLookupChildValue(conf, "filename");
+ if (filename != NULL) {
+ char *log_dir;
+ log_dir = ConfigGetLogDirectory();
+
+ profiling_locks_file_name = SCMalloc(PATH_MAX);
+ if (unlikely(profiling_locks_file_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name");
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(profiling_locks_file_name, PATH_MAX, "%s/%s", log_dir, filename);
+
+ const char *v = ConfNodeLookupChildValue(conf, "append");
+ if (v == NULL || ConfValIsTrue(v)) {
+ profiling_locks_file_mode = "a";
+ } else {
+ profiling_locks_file_mode = "w";
+ }
+
+ profiling_locks_output_to_file = 1;
+ }
+#endif
+ }
+ }
+
+}
+
+/**
+ * \brief Free resources used by profiling.
+ */
+void
+SCProfilingDestroy(void)
+{
+ if (profiling_packets_enabled) {
+ pthread_mutex_destroy(&packet_profile_lock);
+ }
+
+ if (profiling_packets_csv_enabled) {
+ if (packet_profile_csv_fp != NULL)
+ fclose(packet_profile_csv_fp);
+ packet_profile_csv_fp = NULL;
+ }
+
+ if (profiling_csv_file_name != NULL)
+ SCFree(profiling_csv_file_name);
+ profiling_csv_file_name = NULL;
+
+ if (profiling_file_name != NULL)
+ SCFree(profiling_file_name);
+ profiling_file_name = NULL;
+
+#ifdef PROFILE_LOCKING
+ LockRecordFreeHash();
+#endif
+}
+
+void
+SCProfilingDump(void)
+{
+ SCProfilingDumpPacketStats();
+ SCLogInfo("Done dumping profiling data.");
+}
+
+void SCProfilingDumpPacketStats(void)
+{
+ int i;
+ FILE *fp;
+ char totalstr[256];
+ uint64_t total;
+
+ if (profiling_packets_enabled == 0)
+ return;
+
+ if (profiling_packets_output_to_file == 1) {
+ fp = fopen(profiling_packets_file_name, profiling_packets_file_mode);
+
+ if (fp == NULL) {
+ SCLogError(SC_ERR_FOPEN, "failed to open %s: %s",
+ profiling_packets_file_name, strerror(errno));
+ return;
+ }
+ } else {
+ fp = stdout;
+ }
+
+ fprintf(fp, "\n\nPacket profile dump:\n");
+
+ fprintf(fp, "\n%-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s\n",
+ "IP ver", "Proto", "cnt", "min", "max", "avg", "tot", "%%");
+ fprintf(fp, "%-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s\n",
+ "------", "-----", "----------", "------------", "------------", "-----------", "-----------", "---");
+ total = 0;
+ for (i = 0; i < 257; i++) {
+ SCProfilePacketData *pd = &packet_profile_data4[i];
+ total += pd->tot;
+ pd = &packet_profile_data6[i];
+ total += pd->tot;
+ }
+
+ for (i = 0; i < 257; i++) {
+ SCProfilePacketData *pd = &packet_profile_data4[i];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, " IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f\n", i, pd->cnt,
+ pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+
+ for (i = 0; i < 257; i++) {
+ SCProfilePacketData *pd = &packet_profile_data6[i];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, " IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f\n", i, pd->cnt,
+ pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ fprintf(fp, "Note: Protocol 256 tracks pseudo/tunnel packets.\n");
+
+ fprintf(fp, "\nPer Thread module stats:\n");
+
+ fprintf(fp, "\n%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s",
+ "Thread Module", "IP ver", "Proto", "cnt", "min", "max", "avg", "tot", "%%");
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %-10s %-10s %-12s %-12s %-10s %-10s %-12s %-12s\n",
+ "locks", "ticks", "cont.", "cont.avg", "slocks", "sticks", "scont.", "scont.avg");
+#else
+ fprintf(fp, "\n");
+#endif
+ fprintf(fp, "%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s",
+ "------------------------", "------", "-----", "----------", "------------", "------------", "-----------", "-----------", "---");
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %-10s %-10s %-12s %-12s %-10s %-10s %-12s %-12s\n",
+ "--------", "--------", "----------", "-----------", "--------", "--------", "------------", "-----------");
+#else
+ fprintf(fp, "\n");
+#endif
+ int m;
+ total = 0;
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (tmm_modules[m].flags & TM_FLAG_LOGAPI_TM)
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data4[m][p];
+ total += pd->tot;
+
+ pd = &packet_profile_tmm_data6[m][p];
+ total += pd->tot;
+ }
+ }
+
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (tmm_modules[m].flags & TM_FLAG_LOGAPI_TM)
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data4[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f",
+ TmModuleTmmIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %10.2f %12"PRIu64" %12"PRIu64" %10.2f %10.2f %12"PRIu64" %12"PRIu64" %10.2f\n",
+ (float)pd->lock/pd->cnt, (uint64_t)pd->ticks/pd->cnt, pd->contention, (float)pd->contention/pd->cnt, (float)pd->slock/pd->cnt, (uint64_t)pd->sticks/pd->cnt, pd->scontention, (float)pd->scontention/pd->cnt);
+#else
+ fprintf(fp, "\n");
+#endif
+ }
+ }
+
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (tmm_modules[m].flags & TM_FLAG_LOGAPI_TM)
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data6[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f\n",
+ TmModuleTmmIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+ fprintf(fp, "Note: TMM_STREAMTCP includes TCP app layer parsers, see below.\n");
+
+ fprintf(fp, "\nPer App layer parser stats:\n");
+
+ fprintf(fp, "\n%-20s %-6s %-5s %-12s %-12s %-12s %-12s\n",
+ "App Layer", "IP ver", "Proto", "cnt", "min", "max", "avg");
+ fprintf(fp, "%-20s %-6s %-5s %-12s %-12s %-12s %-12s\n",
+ "--------------------", "------", "-----", "----------", "------------", "------------", "-----------");
+
+ total = 0;
+ for (m = 0; m < ALPROTO_MAX; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_app_data4[m][p];
+ total += pd->tot;
+
+ pd = &packet_profile_app_data6[m][p];
+ total += pd->tot;
+ }
+ }
+ for (m = 0; m < ALPROTO_MAX; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_app_data4[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-20s IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %-6.2f\n",
+ AppProtoToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+
+ for (m = 0; m < ALPROTO_MAX; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_app_data6[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-20s IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %-6.2f\n",
+ AppProtoToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+
+ /* proto detect output */
+ {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_app_pd_data4[p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ fprintf(fp, "%-20s IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s\n",
+ "Proto detect", p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr);
+ }
+
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_app_pd_data6[p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ fprintf(fp, "%-20s IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s\n",
+ "Proto detect", p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr);
+ }
+ }
+
+ total = 0;
+ for (m = 0; m < PROF_DETECT_SIZE; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_detect_data4[m][p];
+ total += pd->tot;
+
+ pd = &packet_profile_detect_data6[m][p];
+ total += pd->tot;
+ }
+ }
+
+
+ fprintf(fp, "\n%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s",
+ "Log Thread Module", "IP ver", "Proto", "cnt", "min", "max", "avg", "tot", "%%");
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %-10s %-10s %-12s %-12s %-10s %-10s %-12s %-12s\n",
+ "locks", "ticks", "cont.", "cont.avg", "slocks", "sticks", "scont.", "scont.avg");
+#else
+ fprintf(fp, "\n");
+#endif
+ fprintf(fp, "%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s %-3s",
+ "------------------------", "------", "-----", "----------", "------------", "------------", "-----------", "-----------", "---");
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %-10s %-10s %-12s %-12s %-10s %-10s %-12s %-12s\n",
+ "--------", "--------", "----------", "-----------", "--------", "--------", "------------", "-----------");
+#else
+ fprintf(fp, "\n");
+#endif
+ total = 0;
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (!(tmm_modules[m].flags & TM_FLAG_LOGAPI_TM))
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data4[m][p];
+ total += pd->tot;
+
+ pd = &packet_profile_tmm_data6[m][p];
+ total += pd->tot;
+ }
+ }
+
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (!(tmm_modules[m].flags & TM_FLAG_LOGAPI_TM))
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data4[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f",
+ TmModuleTmmIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+#ifdef PROFILE_LOCKING
+ fprintf(fp, " %10.2f %12"PRIu64" %12"PRIu64" %10.2f %10.2f %12"PRIu64" %12"PRIu64" %10.2f\n",
+ (float)pd->lock/pd->cnt, (uint64_t)pd->ticks/pd->cnt, pd->contention, (float)pd->contention/pd->cnt, (float)pd->slock/pd->cnt, (uint64_t)pd->sticks/pd->cnt, pd->scontention, (float)pd->scontention/pd->cnt);
+#else
+ fprintf(fp, "\n");
+#endif
+ }
+ }
+
+ for (m = 0; m < TMM_SIZE; m++) {
+ if (!(tmm_modules[m].flags & TM_FLAG_LOGAPI_TM))
+ continue;
+
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_tmm_data6[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %6.2f\n",
+ TmModuleTmmIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+
+ fprintf(fp, "\nGeneral detection engine stats:\n");
+
+ total = 0;
+ for (m = 0; m < PROF_DETECT_SIZE; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_detect_data4[m][p];
+ total += pd->tot;
+ pd = &packet_profile_detect_data6[m][p];
+ total += pd->tot;
+ }
+ }
+
+ fprintf(fp, "\n%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s\n",
+ "Detection phase", "IP ver", "Proto", "cnt", "min", "max", "avg", "tot");
+ fprintf(fp, "%-24s %-6s %-5s %-12s %-12s %-12s %-12s %-12s\n",
+ "------------------------", "------", "-----", "----------", "------------", "------------", "-----------", "-----------");
+ for (m = 0; m < PROF_DETECT_SIZE; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_detect_data4[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv4 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %-6.2f\n",
+ PacketProfileDetectIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+ for (m = 0; m < PROF_DETECT_SIZE; m++) {
+ int p;
+ for (p = 0; p < 257; p++) {
+ SCProfilePacketData *pd = &packet_profile_detect_data6[m][p];
+
+ if (pd->cnt == 0) {
+ continue;
+ }
+
+ FormatNumber(pd->tot, totalstr, sizeof(totalstr));
+ double percent = (long double)pd->tot /
+ (long double)total * 100;
+
+ fprintf(fp, "%-24s IPv6 %3d %12"PRIu64" %12"PRIu64" %12"PRIu64" %12"PRIu64" %12s %-6.2f\n",
+ PacketProfileDetectIdToString(m), p, pd->cnt, pd->min, pd->max, (uint64_t)(pd->tot / pd->cnt), totalstr, percent);
+ }
+ }
+ fclose(fp);
+}
+
+void SCProfilingPrintPacketProfile(Packet *p)
+{
+ if (profiling_packets_csv_enabled == 0 || p == NULL || packet_profile_csv_fp == NULL || p->profile == NULL) {
+ return;
+ }
+
+ uint64_t delta = p->profile->ticks_end - p->profile->ticks_start;
+
+ fprintf(packet_profile_csv_fp, "%"PRIu64",%c,%"PRIu8",%"PRIu64",",
+ p->pcap_cnt, PKT_IS_IPV4(p) ? '4' : (PKT_IS_IPV6(p) ? '6' : '?'), p->proto,
+ delta);
+
+ int i;
+ uint64_t tmm_total = 0;
+ uint64_t tmm_streamtcp_tcp = 0;
+
+ for (i = 0; i < TMM_SIZE; i++) {
+ PktProfilingTmmData *pdt = &p->profile->tmm[i];
+
+ uint64_t tmm_delta = pdt->ticks_end - pdt->ticks_start;
+ fprintf(packet_profile_csv_fp, "%"PRIu64",", tmm_delta);
+ tmm_total += tmm_delta;
+
+ if (p->proto == IPPROTO_TCP && i == TMM_STREAMTCP) {
+ tmm_streamtcp_tcp = tmm_delta;
+ }
+ }
+
+ fprintf(packet_profile_csv_fp, "%"PRIu64",", delta - tmm_total);
+
+ uint64_t app_total = 0;
+ for (i = 0; i < ALPROTO_MAX; i++) {
+ PktProfilingAppData *pdt = &p->profile->app[i];
+
+ fprintf(packet_profile_csv_fp,"%"PRIu64",", pdt->ticks_spent);
+
+ if (p->proto == IPPROTO_TCP) {
+ app_total += pdt->ticks_spent;
+ }
+ }
+
+ uint64_t real_tcp = 0;
+ if (tmm_streamtcp_tcp > app_total)
+ real_tcp = tmm_streamtcp_tcp - app_total;
+ fprintf(packet_profile_csv_fp, "%"PRIu64",", real_tcp);
+
+ fprintf(packet_profile_csv_fp, "%"PRIu64",", p->profile->proto_detect);
+
+ for (i = 0; i < PROF_DETECT_SIZE; i++) {
+ PktProfilingDetectData *pdt = &p->profile->detect[i];
+
+ fprintf(packet_profile_csv_fp,"%"PRIu64",", pdt->ticks_spent);
+ }
+ fprintf(packet_profile_csv_fp,"\n");
+}
+
+static void SCProfilingUpdatePacketDetectRecord(PacketProfileDetectId id, uint8_t ipproto, PktProfilingDetectData *pdt, int ipver)
+{
+ if (pdt == NULL) {
+ return;
+ }
+
+ SCProfilePacketData *pd;
+ if (ipver == 4)
+ pd = &packet_profile_detect_data4[id][ipproto];
+ else
+ pd = &packet_profile_detect_data6[id][ipproto];
+
+ if (pd->min == 0 || pdt->ticks_spent < pd->min) {
+ pd->min = pdt->ticks_spent;
+ }
+ if (pd->max < pdt->ticks_spent) {
+ pd->max = pdt->ticks_spent;
+ }
+
+ pd->tot += pdt->ticks_spent;
+ pd->cnt ++;
+}
+
+void SCProfilingUpdatePacketDetectRecords(Packet *p)
+{
+ PacketProfileDetectId i;
+ for (i = 0; i < PROF_DETECT_SIZE; i++) {
+ PktProfilingDetectData *pdt = &p->profile->detect[i];
+
+ if (pdt->ticks_spent > 0) {
+ if (PKT_IS_IPV4(p)) {
+ SCProfilingUpdatePacketDetectRecord(i, p->proto, pdt, 4);
+ } else {
+ SCProfilingUpdatePacketDetectRecord(i, p->proto, pdt, 6);
+ }
+ }
+ }
+}
+
+static void SCProfilingUpdatePacketAppPdRecord(uint8_t ipproto, uint32_t ticks_spent, int ipver)
+{
+ SCProfilePacketData *pd;
+ if (ipver == 4)
+ pd = &packet_profile_app_pd_data4[ipproto];
+ else
+ pd = &packet_profile_app_pd_data6[ipproto];
+
+ if (pd->min == 0 || ticks_spent < pd->min) {
+ pd->min = ticks_spent;
+ }
+ if (pd->max < ticks_spent) {
+ pd->max = ticks_spent;
+ }
+
+ pd->tot += ticks_spent;
+ pd->cnt ++;
+}
+
+static void SCProfilingUpdatePacketAppRecord(int alproto, uint8_t ipproto, PktProfilingAppData *pdt, int ipver)
+{
+ if (pdt == NULL) {
+ return;
+ }
+
+ SCProfilePacketData *pd;
+ if (ipver == 4)
+ pd = &packet_profile_app_data4[alproto][ipproto];
+ else
+ pd = &packet_profile_app_data6[alproto][ipproto];
+
+ if (pd->min == 0 || pdt->ticks_spent < pd->min) {
+ pd->min = pdt->ticks_spent;
+ }
+ if (pd->max < pdt->ticks_spent) {
+ pd->max = pdt->ticks_spent;
+ }
+
+ pd->tot += pdt->ticks_spent;
+ pd->cnt ++;
+}
+
+void SCProfilingUpdatePacketAppRecords(Packet *p)
+{
+ int i;
+ for (i = 0; i < ALPROTO_MAX; i++) {
+ PktProfilingAppData *pdt = &p->profile->app[i];
+
+ if (pdt->ticks_spent > 0) {
+ if (PKT_IS_IPV4(p)) {
+ SCProfilingUpdatePacketAppRecord(i, p->proto, pdt, 4);
+ } else {
+ SCProfilingUpdatePacketAppRecord(i, p->proto, pdt, 6);
+ }
+ }
+ }
+
+ if (p->profile->proto_detect > 0) {
+ if (PKT_IS_IPV4(p)) {
+ SCProfilingUpdatePacketAppPdRecord(p->proto, p->profile->proto_detect, 4);
+ } else {
+ SCProfilingUpdatePacketAppPdRecord(p->proto, p->profile->proto_detect, 6);
+ }
+ }
+}
+
+void SCProfilingUpdatePacketTmmRecord(int module, uint8_t proto, PktProfilingTmmData *pdt, int ipver)
+{
+ if (pdt == NULL) {
+ return;
+ }
+
+ SCProfilePacketData *pd;
+ if (ipver == 4)
+ pd = &packet_profile_tmm_data4[module][proto];
+ else
+ pd = &packet_profile_tmm_data6[module][proto];
+
+ uint32_t delta = (uint32_t)pdt->ticks_end - pdt->ticks_start;
+ if (pd->min == 0 || delta < pd->min) {
+ pd->min = delta;
+ }
+ if (pd->max < delta) {
+ pd->max = delta;
+ }
+
+ pd->tot += (uint64_t)delta;
+ pd->cnt ++;
+
+#ifdef PROFILE_LOCKING
+ pd->lock += pdt->mutex_lock_cnt;
+ pd->ticks += pdt->mutex_lock_wait_ticks;
+ pd->contention += pdt->mutex_lock_contention;
+ pd->slock += pdt->spin_lock_cnt;
+ pd->sticks += pdt->spin_lock_wait_ticks;
+ pd->scontention += pdt->spin_lock_contention;
+#endif
+}
+
+void SCProfilingUpdatePacketTmmRecords(Packet *p)
+{
+ int i;
+ for (i = 0; i < TMM_SIZE; i++) {
+ PktProfilingTmmData *pdt = &p->profile->tmm[i];
+
+ if (pdt->ticks_start == 0 || pdt->ticks_end == 0 || pdt->ticks_start > pdt->ticks_end) {
+ continue;
+ }
+
+ if (PKT_IS_IPV4(p)) {
+ SCProfilingUpdatePacketTmmRecord(i, p->proto, pdt, 4);
+ } else {
+ SCProfilingUpdatePacketTmmRecord(i, p->proto, pdt, 6);
+ }
+ }
+}
+
+void SCProfilingAddPacket(Packet *p)
+{
+ if (p == NULL || p->profile == NULL ||
+ p->profile->ticks_start == 0 || p->profile->ticks_end == 0 ||
+ p->profile->ticks_start > p->profile->ticks_end)
+ return;
+
+ pthread_mutex_lock(&packet_profile_lock);
+ {
+
+ if (profiling_packets_csv_enabled)
+ SCProfilingPrintPacketProfile(p);
+
+ if (PKT_IS_IPV4(p)) {
+ SCProfilePacketData *pd = &packet_profile_data4[p->proto];
+
+ uint64_t delta = p->profile->ticks_end - p->profile->ticks_start;
+ if (pd->min == 0 || delta < pd->min) {
+ pd->min = delta;
+ }
+ if (pd->max < delta) {
+ pd->max = delta;
+ }
+
+ pd->tot += delta;
+ pd->cnt ++;
+
+ if (IS_TUNNEL_PKT(p)) {
+ pd = &packet_profile_data4[256];
+
+ if (pd->min == 0 || delta < pd->min) {
+ pd->min = delta;
+ }
+ if (pd->max < delta) {
+ pd->max = delta;
+ }
+
+ pd->tot += delta;
+ pd->cnt ++;
+ }
+
+ SCProfilingUpdatePacketTmmRecords(p);
+ SCProfilingUpdatePacketAppRecords(p);
+ SCProfilingUpdatePacketDetectRecords(p);
+
+ } else if (PKT_IS_IPV6(p)) {
+ SCProfilePacketData *pd = &packet_profile_data6[p->proto];
+
+ uint64_t delta = p->profile->ticks_end - p->profile->ticks_start;
+ if (pd->min == 0 || delta < pd->min) {
+ pd->min = delta;
+ }
+ if (pd->max < delta) {
+ pd->max = delta;
+ }
+
+ pd->tot += delta;
+ pd->cnt ++;
+
+ if (IS_TUNNEL_PKT(p)) {
+ pd = &packet_profile_data6[256];
+
+ if (pd->min == 0 || delta < pd->min) {
+ pd->min = delta;
+ }
+ if (pd->max < delta) {
+ pd->max = delta;
+ }
+
+ pd->tot += delta;
+ pd->cnt ++;
+ }
+
+ SCProfilingUpdatePacketTmmRecords(p);
+ SCProfilingUpdatePacketAppRecords(p);
+ SCProfilingUpdatePacketDetectRecords(p);
+ }
+ }
+ pthread_mutex_unlock(&packet_profile_lock);
+}
+
+PktProfiling *SCProfilePacketStart(void)
+{
+ uint64_t sample = SC_ATOMIC_ADD(samples, 1);
+ if (sample % rate == 0)
+ return SCCalloc(1, sizeof(PktProfiling));
+ else
+ return NULL;
+}
+
+/* see if we want to profile rules for this packet */
+int SCProfileRuleStart(Packet *p)
+{
+#ifdef PROFILE_LOCKING
+ if (p->profile != NULL) {
+ p->flags |= PKT_PROFILE;
+ return 1;
+ }
+#else
+ uint64_t sample = SC_ATOMIC_ADD(samples, 1);
+ if (sample % rate == 0) {
+ p->flags |= PKT_PROFILE;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+#define CASE_CODE(E) case E: return #E
+
+/**
+ * \brief Maps the PacketProfileDetectId, to its string equivalent
+ *
+ * \param id PacketProfileDetectId id
+ *
+ * \retval string equivalent for the PacketProfileDetectId id
+ */
+const char * PacketProfileDetectIdToString(PacketProfileDetectId id)
+{
+ switch (id) {
+ CASE_CODE (PROF_DETECT_MPM);
+ CASE_CODE (PROF_DETECT_MPM_PACKET);
+// CASE_CODE (PROF_DETECT_MPM_PKT_STREAM);
+ CASE_CODE (PROF_DETECT_MPM_STREAM);
+ CASE_CODE (PROF_DETECT_MPM_URI);
+ CASE_CODE (PROF_DETECT_MPM_HCBD);
+ CASE_CODE (PROF_DETECT_MPM_HSBD);
+ CASE_CODE (PROF_DETECT_MPM_HHD);
+ CASE_CODE (PROF_DETECT_MPM_HRHD);
+ CASE_CODE (PROF_DETECT_MPM_HMD);
+ CASE_CODE (PROF_DETECT_MPM_HCD);
+ CASE_CODE (PROF_DETECT_MPM_HRUD);
+ CASE_CODE (PROF_DETECT_MPM_HSMD);
+ CASE_CODE (PROF_DETECT_MPM_HSCD);
+ CASE_CODE (PROF_DETECT_MPM_HUAD);
+ CASE_CODE (PROF_DETECT_MPM_DNSQUERY);
+ CASE_CODE (PROF_DETECT_IPONLY);
+ CASE_CODE (PROF_DETECT_RULES);
+ CASE_CODE (PROF_DETECT_PREFILTER);
+ CASE_CODE (PROF_DETECT_STATEFUL);
+ CASE_CODE (PROF_DETECT_ALERT);
+ CASE_CODE (PROF_DETECT_CLEANUP);
+ CASE_CODE (PROF_DETECT_GETSGH);
+ CASE_CODE (PROF_DETECT_NONMPMLIST);
+ case PROF_DETECT_MPM_PKT_STREAM:
+ return "PROF_DETECT_MPM_PKT_STR";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+
+#ifdef UNITTESTS
+
+static int
+ProfilingGenericTicksTest01(void)
+{
+#define TEST_RUNS 1024
+ uint64_t ticks_start = 0;
+ uint64_t ticks_end = 0;
+ void *ptr[TEST_RUNS];
+ int i;
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ ptr[i] = SCMalloc(1024);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("malloc(1024) %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCFree(ptr[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCFree(1024) %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ SCMutex m[TEST_RUNS];
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCMutexInit(&m[i], NULL);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMutexInit() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCMutexLock(&m[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMutexLock() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCMutexUnlock(&m[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMutexUnlock() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCMutexDestroy(&m[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCMutexDestroy() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ SCSpinlock s[TEST_RUNS];
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCSpinInit(&s[i], 0);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCSpinInit() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCSpinLock(&s[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCSpinLock() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCSpinUnlock(&s[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCSpinUnlock() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SCSpinDestroy(&s[i]);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SCSpinDestroy() %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ SC_ATOMIC_DECL_AND_INIT(unsigned int, test);
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ (void) SC_ATOMIC_ADD(test,1);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SC_ATOMIC_ADD %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+
+ ticks_start = UtilCpuGetTicks();
+ for (i = 0; i < TEST_RUNS; i++) {
+ SC_ATOMIC_CAS(&test,i,i+1);
+ }
+ ticks_end = UtilCpuGetTicks();
+ printf("SC_ATOMIC_CAS %"PRIu64"\n", (ticks_end - ticks_start)/TEST_RUNS);
+ return 1;
+}
+
+#endif /* UNITTESTS */
+
+void
+SCProfilingRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("ProfilingGenericTicksTest01", ProfilingGenericTicksTest01, 1);
+#endif /* UNITTESTS */
+}
+
+#endif /* PROFILING */
diff --git a/framework/src/suricata/src/util-profiling.h b/framework/src/suricata/src/util-profiling.h
new file mode 100644
index 00000000..763d8414
--- /dev/null
+++ b/framework/src/suricata/src/util-profiling.h
@@ -0,0 +1,282 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Endace Technology Limited.
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_PROFILE_H__
+#define __UTIL_PROFILE_H__
+
+#ifdef PROFILING
+
+#include "util-profiling-locks.h"
+#include "util-cpu.h"
+
+extern int profiling_rules_enabled;
+extern int profiling_packets_enabled;
+extern __thread int profiling_rules_entered;
+
+void SCProfilingPrintPacketProfile(Packet *);
+void SCProfilingAddPacket(Packet *);
+int SCProfileRuleStart(Packet *p);
+
+#define RULE_PROFILING_START(p) \
+ uint64_t profile_rule_start_ = 0; \
+ uint64_t profile_rule_end_ = 0; \
+ if (profiling_rules_enabled && SCProfileRuleStart((p))) { \
+ if (profiling_rules_entered > 0) { \
+ SCLogError(SC_ERR_FATAL, "Re-entered profiling, exiting."); \
+ exit(1); \
+ } \
+ profiling_rules_entered++; \
+ profile_rule_start_ = UtilCpuGetTicks(); \
+ }
+
+#define RULE_PROFILING_END(ctx, r, m, p) \
+ if (profiling_rules_enabled && ((p)->flags & PKT_PROFILE)) { \
+ profile_rule_end_ = UtilCpuGetTicks(); \
+ SCProfilingRuleUpdateCounter(ctx, r->profiling_id, \
+ profile_rule_end_ - profile_rule_start_, m); \
+ profiling_rules_entered--; \
+ }
+
+extern int profiling_keyword_enabled;
+extern __thread int profiling_keyword_entered;
+
+#define KEYWORD_PROFILING_SET_LIST(ctx, list) { \
+ (ctx)->keyword_perf_list = (list); \
+}
+
+#define KEYWORD_PROFILING_START \
+ uint64_t profile_keyword_start_ = 0; \
+ uint64_t profile_keyword_end_ = 0; \
+ if (profiling_keyword_enabled) { \
+ if (profiling_keyword_entered > 0) { \
+ SCLogError(SC_ERR_FATAL, "Re-entered profiling, exiting."); \
+ abort(); \
+ } \
+ profiling_keyword_entered++; \
+ profile_keyword_start_ = UtilCpuGetTicks(); \
+ }
+
+/* we allow this macro to be called if profiling_keyword_entered == 0,
+ * so that we don't have to refactor some of the detection code. */
+#define KEYWORD_PROFILING_END(ctx, type, m) \
+ if (profiling_keyword_enabled && profiling_keyword_entered) { \
+ profile_keyword_end_ = UtilCpuGetTicks(); \
+ SCProfilingKeywordUpdateCounter((ctx),(type),(profile_keyword_end_ - profile_keyword_start_),(m)); \
+ profiling_keyword_entered--; \
+ }
+
+PktProfiling *SCProfilePacketStart(void);
+
+#define PACKET_PROFILING_START(p) \
+ if (profiling_packets_enabled) { \
+ (p)->profile = SCProfilePacketStart(); \
+ if ((p)->profile != NULL) \
+ (p)->profile->ticks_start = UtilCpuGetTicks(); \
+ }
+
+#define PACKET_PROFILING_END(p) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ (p)->profile->ticks_end = UtilCpuGetTicks(); \
+ SCProfilingAddPacket((p)); \
+ }
+
+#ifdef PROFILE_LOCKING
+#define PACKET_PROFILING_RESET_LOCKS do { \
+ mutex_lock_cnt = 0; \
+ mutex_lock_wait_ticks = 0; \
+ mutex_lock_contention = 0; \
+ spin_lock_cnt = 0; \
+ spin_lock_wait_ticks = 0; \
+ spin_lock_contention = 0; \
+ rww_lock_cnt = 0; \
+ rww_lock_wait_ticks = 0; \
+ rww_lock_contention = 0; \
+ rwr_lock_cnt = 0; \
+ rwr_lock_wait_ticks = 0; \
+ rwr_lock_contention = 0; \
+ locks_idx = 0; \
+ record_locks = 1;\
+ } while (0)
+
+#define PACKET_PROFILING_COPY_LOCKS(p, id) do { \
+ (p)->profile->tmm[(id)].mutex_lock_cnt = mutex_lock_cnt; \
+ (p)->profile->tmm[(id)].mutex_lock_wait_ticks = mutex_lock_wait_ticks; \
+ (p)->profile->tmm[(id)].mutex_lock_contention = mutex_lock_contention; \
+ (p)->profile->tmm[(id)].spin_lock_cnt = spin_lock_cnt; \
+ (p)->profile->tmm[(id)].spin_lock_wait_ticks = spin_lock_wait_ticks; \
+ (p)->profile->tmm[(id)].spin_lock_contention = spin_lock_contention; \
+ (p)->profile->tmm[(id)].rww_lock_cnt = rww_lock_cnt; \
+ (p)->profile->tmm[(id)].rww_lock_wait_ticks = rww_lock_wait_ticks; \
+ (p)->profile->tmm[(id)].rww_lock_contention = rww_lock_contention; \
+ (p)->profile->tmm[(id)].rwr_lock_cnt = rwr_lock_cnt; \
+ (p)->profile->tmm[(id)].rwr_lock_wait_ticks = rwr_lock_wait_ticks; \
+ (p)->profile->tmm[(id)].rwr_lock_contention = rwr_lock_contention; \
+ record_locks = 0; \
+ SCProfilingAddPacketLocks((p)); \
+ } while(0)
+#else
+#define PACKET_PROFILING_RESET_LOCKS
+#define PACKET_PROFILING_COPY_LOCKS(p, id)
+#endif
+
+#define PACKET_PROFILING_TMM_START(p, id) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ if ((id) < TMM_SIZE) { \
+ (p)->profile->tmm[(id)].ticks_start = UtilCpuGetTicks();\
+ PACKET_PROFILING_RESET_LOCKS; \
+ } \
+ }
+
+#define PACKET_PROFILING_TMM_END(p, id) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ if ((id) < TMM_SIZE) { \
+ PACKET_PROFILING_COPY_LOCKS((p), (id)); \
+ (p)->profile->tmm[(id)].ticks_end = UtilCpuGetTicks(); \
+ } \
+ }
+
+#define PACKET_PROFILING_RESET(p) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ SCFree((p)->profile); \
+ (p)->profile = NULL; \
+ }
+
+#define PACKET_PROFILING_APP_START(dp, id) \
+ if (profiling_packets_enabled) { \
+ (dp)->ticks_start = UtilCpuGetTicks(); \
+ (dp)->alproto = (id); \
+ }
+
+#define PACKET_PROFILING_APP_END(dp, id) \
+ if (profiling_packets_enabled) { \
+ BUG_ON((id) != (dp)->alproto); \
+ (dp)->ticks_end = UtilCpuGetTicks(); \
+ if ((dp)->ticks_start != 0 && (dp)->ticks_start < ((dp)->ticks_end)) { \
+ (dp)->ticks_spent = ((dp)->ticks_end - (dp)->ticks_start); \
+ } \
+ }
+
+#define PACKET_PROFILING_APP_PD_START(dp) \
+ if (profiling_packets_enabled) { \
+ (dp)->proto_detect_ticks_start = UtilCpuGetTicks(); \
+ }
+
+#define PACKET_PROFILING_APP_PD_END(dp) \
+ if (profiling_packets_enabled) { \
+ (dp)->proto_detect_ticks_end = UtilCpuGetTicks(); \
+ if ((dp)->proto_detect_ticks_start != 0 && (dp)->proto_detect_ticks_start < ((dp)->proto_detect_ticks_end)) { \
+ (dp)->proto_detect_ticks_spent = \
+ ((dp)->proto_detect_ticks_end - (dp)->proto_detect_ticks_start); \
+ } \
+ }
+
+#define PACKET_PROFILING_APP_RESET(dp) \
+ if (profiling_packets_enabled) { \
+ (dp)->ticks_start = 0; \
+ (dp)->ticks_end = 0; \
+ (dp)->ticks_spent = 0; \
+ (dp)->alproto = 0; \
+ (dp)->proto_detect_ticks_start = 0; \
+ (dp)->proto_detect_ticks_end = 0; \
+ (dp)->proto_detect_ticks_spent = 0; \
+ }
+
+#define PACKET_PROFILING_APP_STORE(dp, p) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ if ((dp)->alproto < ALPROTO_MAX) { \
+ (p)->profile->app[(dp)->alproto].ticks_spent += (dp)->ticks_spent; \
+ (p)->profile->proto_detect += (dp)->proto_detect_ticks_spent; \
+ } \
+ }
+
+#define PACKET_PROFILING_DETECT_START(p, id) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ if ((id) < PROF_DETECT_SIZE) { \
+ (p)->profile->detect[(id)].ticks_start = UtilCpuGetTicks(); \
+ } \
+ }
+
+#define PACKET_PROFILING_DETECT_END(p, id) \
+ if (profiling_packets_enabled && (p)->profile != NULL) { \
+ if ((id) < PROF_DETECT_SIZE) { \
+ (p)->profile->detect[(id)].ticks_end = UtilCpuGetTicks();\
+ if ((p)->profile->detect[(id)].ticks_start != 0 && \
+ (p)->profile->detect[(id)].ticks_start < (p)->profile->detect[(id)].ticks_end) { \
+ (p)->profile->detect[(id)].ticks_spent += \
+ ((p)->profile->detect[(id)].ticks_end - (p)->profile->detect[(id)].ticks_start); \
+ } \
+ } \
+ }
+
+
+void SCProfilingRulesGlobalInit(void);
+void SCProfilingRuleDestroyCtx(struct SCProfileDetectCtx_ *);
+void SCProfilingRuleInitCounters(DetectEngineCtx *);
+void SCProfilingRuleUpdateCounter(DetectEngineThreadCtx *, uint16_t, uint64_t, int);
+void SCProfilingRuleThreadSetup(struct SCProfileDetectCtx_ *, DetectEngineThreadCtx *);
+void SCProfilingRuleThreadCleanup(DetectEngineThreadCtx *);
+
+void SCProfilingKeywordsGlobalInit(void);
+void SCProfilingKeywordDestroyCtx(DetectEngineCtx *);//struct SCProfileKeywordDetectCtx_ *);
+void SCProfilingKeywordInitCounters(DetectEngineCtx *);
+void SCProfilingKeywordUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks, int match);
+void SCProfilingKeywordThreadSetup(struct SCProfileKeywordDetectCtx_ *, DetectEngineThreadCtx *);
+void SCProfilingKeywordThreadCleanup(DetectEngineThreadCtx *);
+
+void SCProfilingInit(void);
+void SCProfilingDestroy(void);
+void SCProfilingRegisterTests(void);
+void SCProfilingDump(void);
+
+#else
+
+#define RULE_PROFILING_START(p)
+#define RULE_PROFILING_END(a,b,c,p)
+
+#define KEYWORD_PROFILING_SET_LIST(a,b)
+#define KEYWORD_PROFILING_START
+#define KEYWORD_PROFILING_END(a,b,c)
+
+#define PACKET_PROFILING_START(p)
+#define PACKET_PROFILING_END(p)
+
+#define PACKET_PROFILING_TMM_START(p, id)
+#define PACKET_PROFILING_TMM_END(p, id)
+
+#define PACKET_PROFILING_RESET(p)
+
+#define PACKET_PROFILING_APP_START(dp, id)
+#define PACKET_PROFILING_APP_END(dp, id)
+#define PACKET_PROFILING_APP_RESET(dp)
+#define PACKET_PROFILING_APP_STORE(dp, p)
+
+#define PACKET_PROFILING_APP_PD_START(dp)
+#define PACKET_PROFILING_APP_PD_END(dp)
+
+#define PACKET_PROFILING_DETECT_START(p, id)
+#define PACKET_PROFILING_DETECT_END(p, id)
+
+#endif /* PROFILING */
+
+#endif /* ! __UTIL_PROFILE_H__ */
diff --git a/framework/src/suricata/src/util-proto-name.c b/framework/src/suricata/src/util-proto-name.c
new file mode 100644
index 00000000..0b958884
--- /dev/null
+++ b/framework/src/suricata/src/util-proto-name.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * File to provide the protocol names based on protocol numbers defined in the
+ * specified protocol file.
+ */
+
+#include "suricata-common.h"
+#include "util-proto-name.h"
+
+static int init_once = 0;
+
+/**
+ * \brief Function to load the protocol names from the specified protocol
+ * file.
+ */
+void SCProtoNameInit()
+{
+ BUG_ON(init_once);
+ init_once++;
+ memset(known_proto, 0x00, sizeof(known_proto));
+
+ /* Load the known protocols name from the /etc/protocols file */
+ FILE *fp = fopen(PROTO_FILE,"r");
+ if (fp != NULL) {
+ char line[200];
+ char *ptr = NULL;
+
+ while(fgets(line, sizeof(line), fp) != NULL) {
+ if (line[0] == '#')
+ continue;
+
+ char *name = strtok_r(line," \t", &ptr);
+ if (name == NULL)
+ continue;
+
+ char *proto_ch = strtok_r(NULL," \t", &ptr);
+ if (proto_ch == NULL)
+ continue;
+
+ int proto = atoi(proto_ch);
+ if (proto >= 255)
+ continue;
+
+ char *cname = strtok_r(NULL, " \t", &ptr);
+
+ if (known_proto[proto] != NULL) {
+ SCFree(known_proto[proto]);
+ }
+
+ if (cname != NULL) {
+ known_proto[proto] = SCStrdup(cname);
+ } else {
+ known_proto[proto] = SCStrdup(name);
+ }
+ if (unlikely(known_proto[proto] == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed proto name allocation");
+ continue;
+ }
+ int proto_len = strlen(known_proto[proto]);
+ if (proto_len > 0 && known_proto[proto][proto_len - 1] == '\n')
+ known_proto[proto][proto_len - 1] = '\0';
+ }
+ fclose(fp);
+ }
+}
+
+/**
+ * \brief Function to check if the received protocol number is valid and do
+ * we have corresponding name entry for this number or not.
+ *
+ * \param proto Protocol number to be validated
+ * \retval ret On success returns TRUE otherwise FALSE
+ */
+uint8_t SCProtoNameValid(uint16_t proto)
+{
+ uint8_t ret = FALSE;
+
+ if (proto <= 255 && known_proto[proto] != NULL) {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Function to clears the memory used in storing the protocol names.
+ */
+void SCProtoNameDeInit()
+{
+ int cnt;
+ /* clears the memory of loaded protocol names */
+ for (cnt = 0; cnt < 255; cnt++) {
+ if (known_proto[cnt] != NULL)
+ SCFree(known_proto[cnt]);
+ }
+}
diff --git a/framework/src/suricata/src/util-proto-name.h b/framework/src/suricata/src/util-proto-name.h
new file mode 100644
index 00000000..e349a6db
--- /dev/null
+++ b/framework/src/suricata/src/util-proto-name.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ */
+
+#ifndef __UTIL_PROTO_NAME_H__
+#define __UTIL_PROTO_NAME_H__
+
+#ifndef OS_WIN32
+#define PROTO_FILE "/etc/protocols"
+#else
+#define PROTO_FILE "C:\\Windows\\system32\\drivers\\etc\\protocol"
+#endif /* OS_WIN32 */
+
+/** Lookup array to hold the information related to known protocol
+ * in /etc/protocols */
+char *known_proto[256];
+
+uint8_t SCProtoNameValid(uint16_t);
+void SCProtoNameInit(void);
+void SCProtoNameDeInit(void);
+
+#endif /* __UTIL_PROTO_NAME_H__ */
+
diff --git a/framework/src/suricata/src/util-radix-tree.c b/framework/src/suricata/src/util-radix-tree.c
new file mode 100644
index 00000000..cbf49c7d
--- /dev/null
+++ b/framework/src/suricata/src/util-radix-tree.c
@@ -0,0 +1,4228 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Implementation of radix trees
+ */
+
+#include "suricata-common.h"
+#include "util-radix-tree.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-ip.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+
+/**
+ * \brief Allocates and returns a new instance of SCRadixUserData.
+ *
+ * \param netmask The netmask entry (cidr) that has to be made in the new
+ * SCRadixUserData instance
+ * \param user The user data that has to be set for the above
+ * netmask in the newly created SCRadixUserData instance.
+ *
+ * \retval user_data Pointer to a new instance of SCRadixUserData.
+ */
+static SCRadixUserData *SCRadixAllocSCRadixUserData(uint8_t netmask, void *user)
+{
+ SCRadixUserData *user_data = SCMalloc(sizeof(SCRadixUserData));
+ if (unlikely(user_data == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ return NULL;
+ }
+
+ memset(user_data, 0, sizeof(SCRadixUserData));
+
+ user_data->netmask = netmask;
+ user_data->user = user;
+
+ return user_data;
+}
+
+/**
+ * \brief Deallocates an instance of SCRadixUserData.
+ *
+ * \param user_data Pointer to the instance of SCRadixUserData that has to be
+ * freed.
+ */
+static void SCRadixDeAllocSCRadixUserData(SCRadixUserData *user_data)
+{
+ SCFree(user_data);
+
+ return;
+}
+
+/**
+ * \brief Appends a user_data instance(SCRadixUserData) to a
+ * user_data(SCRadixUserData) list. We add the new entry in descending
+ * order with respect to the netmask contained in the SCRadixUserData.
+ *
+ * \param new Pointer to the SCRadixUserData to be added to the list.
+ * \param list Pointer to the SCRadixUserData list head, to which "new" has to
+ * be appended.
+ */
+static void SCRadixAppendToSCRadixUserDataList(SCRadixUserData *new,
+ SCRadixUserData **list)
+{
+ SCRadixUserData *temp = NULL;
+ SCRadixUserData *prev = NULL;
+
+ if (new == NULL || list == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "new or list supplied as NULL");
+ exit(EXIT_FAILURE);
+ }
+
+ /* add to the list in descending order. The reason we do this is for
+ * optimizing key retrieval for a ip key under a netblock */
+ prev = temp = *list;
+ while (temp != NULL) {
+ if (new->netmask > temp->netmask)
+ break;
+ prev = temp;
+ temp = temp->next;
+ }
+
+ if (temp == *list) {
+ new->next = *list;
+ *list = new;
+ } else {
+ new->next = prev->next;
+ prev->next = new;
+ }
+
+ return;
+}
+
+/**
+ * \brief Creates a new Prefix for a key. Used internally by the API.
+ *
+ * \param key_stream Data that has to be wrapped in a SCRadixPrefix instance to
+ * be processed for insertion/lookup/removal of a node by the
+ * radix tree
+ * \param key_bitlen The bitlen of the the above stream. For example if the
+ * stream holds the ipv4 address(4 bytes), bitlen would be 32
+ * \param user Pointer to the user data that has to be associated with
+ * this key
+ *
+ * \retval prefix The newly created prefix instance on success; NULL on failure
+ */
+static SCRadixPrefix *SCRadixCreatePrefix(uint8_t *key_stream,
+ uint16_t key_bitlen, void *user,
+ uint8_t netmask)
+{
+ SCRadixPrefix *prefix = NULL;
+
+ if ((key_bitlen % 8 != 0)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid argument bitlen - %d",
+ key_bitlen);
+ return NULL;
+ }
+
+ if (key_stream == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Argument \"stream\" NULL");
+ return NULL;
+ }
+
+ if ( (prefix = SCMalloc(sizeof(SCRadixPrefix))) == NULL)
+ goto error;
+
+ memset(prefix, 0, sizeof(SCRadixPrefix));
+
+ if ( (prefix->stream = SCMalloc(key_bitlen / 8)) == NULL)
+ goto error;
+
+ memset(prefix->stream, 0, key_bitlen / 8);
+
+ memcpy(prefix->stream, key_stream, key_bitlen / 8);
+ prefix->bitlen = key_bitlen;
+
+ prefix->user_data = SCRadixAllocSCRadixUserData(netmask, user);
+ if (prefix->user_data == NULL) {
+ goto error;
+ }
+
+ return prefix;
+
+error:
+ if (prefix != NULL) {
+ if (prefix->stream != NULL) {
+ SCFree(prefix->stream);
+ }
+ SCFree(prefix);
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Adds a netmask and its user_data for a particular prefix stream.
+ *
+ * \param prefix The prefix stream to which the netmask and its corresponding
+ * user data has to be added.
+ * \param netmask The netmask value (cidr) that has to be added to the prefix.
+ * \param user The pointer to the user data corresponding to the above
+ * netmask.
+ */
+static void SCRadixAddNetmaskUserDataToPrefix(SCRadixPrefix *prefix,
+ uint8_t netmask,
+ void *user)
+{
+ if (prefix == NULL || user == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "prefix or user NULL");
+ exit(EXIT_FAILURE);
+ }
+
+ SCRadixAppendToSCRadixUserDataList(SCRadixAllocSCRadixUserData(netmask, user),
+ &prefix->user_data);
+
+ return;
+}
+
+/**
+ * \brief Removes a particular user_data corresponding to a particular netmask
+ * entry, from a prefix.
+ *
+ * \param prefix Pointer to the prefix from which the user_data/netmask entry
+ * has to be removed.
+ * \param netmask The netmask value (cidr) whose user_data has to be deleted.
+ */
+static void SCRadixRemoveNetmaskUserDataFromPrefix(SCRadixPrefix *prefix,
+ uint8_t netmask)
+{
+ SCRadixUserData *temp = NULL, *prev = NULL;
+
+ if (prefix == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "prefix NULL");
+ exit(EXIT_FAILURE);
+ }
+
+ prev = temp = prefix->user_data;
+ while (temp != NULL) {
+ if (temp->netmask == netmask) {
+ if (temp == prefix->user_data)
+ prefix->user_data = temp->next;
+ else
+ prev->next = temp->next;
+
+ SCRadixDeAllocSCRadixUserData(temp);
+ break;
+ }
+ prev = temp;
+ temp = temp->next;
+ }
+
+ return;
+}
+
+/**
+ * \brief Indicates if prefix contains an entry for an ip with a specific netmask.
+ *
+ * \param prefix Pointer to the ip prefix that is being checked.
+ * \param netmask The netmask value (cidr) that has to be checked for
+ * presence in the prefix.
+ *
+ * \retval 1 On match.
+ * \retval 0 On no match.
+ */
+static int SCRadixPrefixContainNetmask(SCRadixPrefix *prefix, uint8_t netmask)
+{
+ SCRadixUserData *user_data = NULL;
+
+ if (prefix == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "prefix is NULL");
+ goto no_match;
+ }
+
+ user_data = prefix->user_data;
+ while (user_data != NULL) {
+ if (user_data->netmask == netmask)
+ return 1;
+ user_data = user_data->next;
+ }
+
+ no_match:
+ return 0;
+}
+
+/**
+ * \brief Returns the total netmask count for this prefix.
+ *
+ * \param prefix Pointer to the prefix
+ *
+ * \retval count The total netmask count for this prefix.
+ */
+static int SCRadixPrefixNetmaskCount(SCRadixPrefix *prefix)
+{
+ SCRadixUserData *user_data = NULL;
+ uint32_t count = 0;
+
+ if (prefix == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "prefix is NULL");
+ return 0;
+ }
+
+ user_data = prefix->user_data;
+ while (user_data != NULL) {
+ count++;
+ user_data = user_data->next;
+ }
+
+ return count;
+}
+
+/**
+ * \brief Indicates if prefix contains an entry for an ip with a specific netmask
+ * and if it does, it sets the user data field
+ * SCRadixPrefix->user_data_result to the netmask user_data entry.
+ *
+ * \param prefix Pointer to the ip prefix that is being checked.
+ * \param netmask The netmask value for which we will have to return the user_data
+ * \param exact_match Bool flag which indicates if we should check if the prefix
+ * holds proper netblock(< 32 for ipv4 and < 128 for ipv6) or not.
+ *
+ * \retval 1 On match.
+ * \retval 0 On no match.
+ */
+static int SCRadixPrefixContainNetmaskAndSetUserData(SCRadixPrefix *prefix,
+ uint16_t netmask,
+ int exact_match,
+ void **user_data_result)
+{
+ SCRadixUserData *user_data = NULL;
+
+ if (prefix == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "prefix is NULL");
+ goto no_match;
+ }
+
+ user_data = prefix->user_data;
+ /* Check if we have a match for an exact ip. An exact ip as in not a proper
+ * netblock, i.e. an ip with a netmask of 32(ipv4) or 128(ipv6) */
+ if (exact_match) {
+ if (user_data->netmask == netmask) {
+ if (user_data_result)
+ *user_data_result = user_data->user;
+ return 1;
+ } else {
+ goto no_match;
+ }
+ }
+
+ /* Check for the user_data entry for this netmask_value */
+ while (user_data != NULL) {
+ if (user_data->netmask == netmask) {
+ if (user_data_result)
+ *user_data_result = user_data->user;
+ return 1;
+ }
+ user_data = user_data->next;
+ }
+
+no_match:
+ if (user_data_result != NULL)
+ *user_data_result = NULL;
+ return 0;
+}
+
+/**
+ * \brief Frees a SCRadixPrefix instance
+ *
+ * \param prefix Pointer to a prefix instance
+ * \param tree Pointer to the Radix tree to which this prefix belongs
+ */
+static void SCRadixReleasePrefix(SCRadixPrefix *prefix, SCRadixTree *tree)
+{
+ SCRadixUserData *user_data_temp1 = NULL;
+ SCRadixUserData *user_data_temp2 = NULL;
+
+ if (prefix != NULL) {
+ if (prefix->stream != NULL)
+ SCFree(prefix->stream);
+
+ user_data_temp1 = prefix->user_data;
+ if (tree->Free != NULL) {
+ while (user_data_temp1 != NULL) {
+ user_data_temp2 = user_data_temp1;
+ user_data_temp1 = user_data_temp1->next;
+ tree->Free(user_data_temp2->user);
+ SCRadixDeAllocSCRadixUserData(user_data_temp2);
+ }
+ } else if (user_data_temp1 != NULL) {
+ SCFree(user_data_temp1);
+ }
+
+ SCFree(prefix);
+ }
+
+ return;
+}
+
+/**
+ * \brief Creates a new node for the Radix tree
+ *
+ * \retval node The newly created node for the radix tree
+ */
+static inline SCRadixNode *SCRadixCreateNode()
+{
+ SCRadixNode *node = NULL;
+
+ if ( (node = SCMalloc(sizeof(SCRadixNode))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixCreateNode. Mem not allocated...");
+ return NULL;
+ }
+ memset(node, 0, sizeof(SCRadixNode));
+
+ return node;
+}
+
+/**
+ * \brief Frees a Radix tree node
+ *
+ * \param node Pointer to a Radix tree node
+ * \param tree Pointer to the Radix tree to which this node belongs
+ */
+static void SCRadixReleaseNode(SCRadixNode *node, SCRadixTree *tree)
+{
+ if (node != NULL) {
+ SCRadixReleasePrefix(node->prefix, tree);
+
+ if (node->netmasks != NULL)
+ SCFree(node->netmasks);
+
+ SCFree(node);
+ }
+
+ return;
+}
+
+/**
+ * \brief Creates a new Radix tree
+ *
+ * \param Free Function pointer supplied by the user to be used by the Radix
+ * cleanup API to free the user suppplied data
+ *
+ * \retval tree The newly created radix tree on success
+ *
+ * \initonly (all radix trees should be created at init)
+ */
+SCRadixTree *SCRadixCreateRadixTree(void (*Free)(void*), void (*PrintData)(void*))
+{
+ SCRadixTree *tree = NULL;
+
+ if ( (tree = SCMalloc(sizeof(SCRadixTree))) == NULL) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixCreateRadixTree. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ memset(tree, 0, sizeof(SCRadixTree));
+
+ tree->Free = Free;
+ tree->PrintData = PrintData;
+
+ return tree;
+}
+
+/**
+ * \brief Internal helper function used by SCRadixReleaseRadixTree to free a
+ * subtree
+ *
+ * \param node Pointer to the root of the subtree that has to be freed
+ * \param tree Pointer to the Radix tree to which this subtree belongs
+ */
+static void SCRadixReleaseRadixSubtree(SCRadixNode *node, SCRadixTree *tree)
+{
+ if (node != NULL) {
+ SCRadixReleaseRadixSubtree(node->left, tree);
+ SCRadixReleaseRadixSubtree(node->right, tree);
+ SCRadixReleaseNode(node, tree);
+ }
+
+ return;
+}
+
+/**
+ * \brief Frees a Radix tree and all its nodes
+ *
+ * \param tree Pointer to the Radix tree that has to be freed
+ */
+void SCRadixReleaseRadixTree(SCRadixTree *tree)
+{
+ if (tree == NULL)
+ return;
+
+ SCRadixReleaseRadixSubtree(tree->head, tree);
+ tree->head = NULL;
+ SCFree(tree);
+ return;
+}
+
+/**
+ * \brief Adds a key to the Radix tree. Used internally by the API.
+ *
+ * \param key_stream Data that has to added to the Radix tree
+ * \param key_bitlen The bitlen of the the above stream. For example if the
+ * stream is the string "abcd", the bitlen would be 32. If
+ * the stream is an IPV6 address bitlen would be 128
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with
+ * this key
+ * \param netmask The netmask (cidr) if we are adding an IP netblock; 255
+ * if we are not adding an IP netblock
+ *
+ * \retval node Pointer to the newly created node
+ */
+static SCRadixNode *SCRadixAddKey(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree, void *user, uint8_t netmask)
+{
+ SCRadixNode *node = NULL;
+ SCRadixNode *new_node = NULL;
+ SCRadixNode *parent = NULL;
+ SCRadixNode *inter_node = NULL;
+ SCRadixNode *bottom_node = NULL;
+
+ SCRadixPrefix *prefix = NULL;
+
+ void *ptmp;
+
+ uint8_t *stream = NULL;
+ uint8_t bitlen = 0;
+
+ int check_bit = 0;
+ int differ_bit = 0;
+
+ int i = 0;
+ int j = 0;
+ int temp = 0;
+
+ if (tree == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Argument \"tree\" NULL");
+ return NULL;
+ }
+
+ /* chop the ip address against a netmask */
+ MaskIPNetblock(key_stream, netmask, key_bitlen);
+
+ if ( (prefix = SCRadixCreatePrefix(key_stream, key_bitlen, user,
+ netmask)) == NULL) {
+ SCLogError(SC_ERR_RADIX_TREE_GENERIC, "Error creating prefix");
+ return NULL;
+ }
+
+ /* the very first element in the radix tree */
+ if (tree->head == NULL) {
+ node = SCRadixCreateNode();
+ if (node == NULL)
+ return NULL;
+ node->prefix = prefix;
+ node->bit = prefix->bitlen;
+ tree->head = node;
+ if (netmask == 255 || (netmask == 32 && key_bitlen == 32) || (netmask == 128 && key_bitlen == 128))
+ return node;
+
+ /* if we have reached here, we are actually having a proper netblock in
+ * our hand(i.e. < 32 for ipv4 and < 128 for ipv6). Add the netmask for
+ * this node. The reason we add netmasks other than 32 and 128, is
+ * because we need those netmasks in case of searches for ips contained
+ * in netblocks. If the netmask is 32 or 128, either ways we will be
+ * having an exact match for that ip value. If it is not, we start
+ * chopping the incoming search ip key using the netmask values added
+ * into the tree and then verify for a match */
+ node->netmask_cnt++;
+ if ( (ptmp = SCRealloc(node->netmasks, (node->netmask_cnt *
+ sizeof(uint8_t)))) == NULL) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Fatal error encountered in SCRadixAddKey. Mem not allocated");
+ return NULL;
+ }
+ node->netmasks = ptmp;
+ node->netmasks[0] = netmask;
+ return node;
+ }
+
+ node = tree->head;
+ stream = prefix->stream;
+ bitlen = prefix->bitlen;
+
+ /* we walk down the tree only when we satisfy 2 conditions. The first one
+ * being the incoming prefix is shorter than the differ bit of the current
+ * node. In case we fail in this aspect, we walk down to the tree, till we
+ * arrive at a node that ends in a prefix */
+ while (node->bit < bitlen || node->prefix == NULL) {
+ /* if the bitlen isn't long enough to handle the bit test, we just walk
+ * down along one of the paths, since either paths should end up with a
+ * node that has a common prefix whose differ bit is greater than the
+ * bitlen of the incoming prefix */
+ if (bitlen <= node->bit) {
+ if (node->right == NULL)
+ break;
+ node = node->right;
+ } else {
+ if (SC_RADIX_BITTEST(stream[node->bit >> 3],
+ (0x80 >> (node->bit % 8))) ) {
+ if (node->right == NULL)
+ break;
+ node = node->right;
+ } else {
+ if (node->left == NULL)
+ break;
+ node = node->left;
+ }
+ }
+ }
+
+ /* we need to keep a reference to the bottom-most node, that actually holds
+ * the prefix */
+ bottom_node = node;
+
+ /* get the first bit position where the ips differ */
+ check_bit = (node->bit < bitlen)? node->bit: bitlen;
+ for (i = 0; (i * 8) < check_bit; i++) {
+ if ((temp = (stream[i] ^ bottom_node->prefix->stream[i])) == 0) {
+ differ_bit = (i + 1) * 8;
+ continue;
+ }
+
+ /* find out the position where the first bit differs. This method is
+ * faster, but at the cost of being larger. But with larger caches
+ * these days we don't have to worry about cache misses */
+ temp = temp * 2;
+ if (temp >= 256)
+ j = 0;
+ else if (temp >= 128)
+ j = 1;
+ else if (temp >= 64)
+ j = 2;
+ else if (temp >= 32)
+ j = 3;
+ else if (temp >= 16)
+ j = 4;
+ else if (temp >= 8)
+ j = 5;
+ else if (temp >= 4)
+ j = 6;
+ else if (temp >= 2)
+ j = 7;
+
+ differ_bit = i * 8 + j;
+ break;
+ }
+ if (check_bit < differ_bit)
+ differ_bit = check_bit;
+
+ /* walk up the tree till we find the position, to fit our new node in */
+ parent = node->parent;
+ while (parent && differ_bit <= parent->bit) {
+ node = parent;
+ parent = node->parent;
+ }
+
+ /* We already have the node in the tree with the same differing bit pstn */
+ if (differ_bit == bitlen && node->bit == bitlen) {
+ if (node->prefix != NULL) {
+ /* Check if we already have this netmask entry covered by this prefix */
+ if (SCRadixPrefixContainNetmask(node->prefix, netmask)) {
+ /* Basically we already have this stream prefix, as well as the
+ * netblock entry for this. A perfect duplicate. */
+ SCLogDebug("Duplicate entry for this ip address/netblock");
+ } else {
+ /* Basically we already have this stream prefix, but we don't
+ * have an entry for this particular netmask value for this
+ * prefix. For example, we have an entry for 192.168.0.0 and
+ * 192.168.0.0/16 and now we are trying to enter 192.168.0.0/20 */
+ SCRadixAddNetmaskUserDataToPrefix(node->prefix, netmask, user);
+
+ /* if we are adding a netmask of 32(for ipv4) or 128(for ipv6)
+ * it indicates we are adding an exact host ip into the radix
+ * tree, in which case we don't need to add the netmask value
+ * into the tree */
+ if (netmask == 255 || (netmask == 32 && bitlen == 32) || (netmask == 128 && bitlen == 128))
+ return node;
+
+ /* looks like we have a netmask which is != 32 or 128, in which
+ * case we walk up the tree to insert this netmask value in the
+ * correct node */
+ parent = node->parent;
+ while (parent != NULL && netmask < (parent->bit + 1)) {
+ node = parent;
+ parent = parent->parent;
+ }
+
+ node->netmask_cnt++;
+ new_node = node;
+
+ if ( (ptmp = SCRealloc(node->netmasks, (node->netmask_cnt *
+ sizeof(uint8_t)))) == NULL) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Mem not allocated...");
+ return NULL;
+ }
+ node->netmasks = ptmp;
+
+ if (node->netmask_cnt == 1) {
+ node->netmasks[0] = netmask;
+ return new_node;
+ }
+
+ node->netmasks[node->netmask_cnt - 1] = netmask;
+
+ for (i = node->netmask_cnt - 2; i >= 0; i--) {
+ if (netmask < node->netmasks[i]) {
+ node->netmasks[i + 1] = netmask;
+ break;
+ }
+
+ node->netmasks[i + 1] = node->netmasks[i];
+ node->netmasks[i] = netmask;
+ }
+ }
+ } else {
+ node->prefix = SCRadixCreatePrefix(prefix->stream, prefix->bitlen,
+ user, 255);
+ }
+ return node;
+ }
+
+ /* create the leaf node for the new key */
+ new_node = SCRadixCreateNode();
+ new_node->prefix = prefix;
+ new_node->bit = prefix->bitlen;
+
+ /* indicates that we have got a key that has length that is already covered
+ * by a prefix of some other key in the tree. We create a new intermediate
+ * node with a single child and stick it in. We need the if only in the
+ * case of variable length keys */
+ if (differ_bit == bitlen) {
+ if (SC_RADIX_BITTEST(bottom_node->prefix->stream[differ_bit >> 3],
+ (0x80 >> (differ_bit % 8))) ) {
+ new_node->right = node;
+ } else {
+ new_node->left = node;
+ }
+ new_node->parent = node->parent;
+
+ if (node->parent == NULL)
+ tree->head = new_node;
+ else if (node->parent->right == node)
+ node->parent->right = new_node;
+ else
+ node->parent->left = new_node;
+
+ node->parent = new_node;
+ /* stick our new_node into the tree. Create a node that holds the
+ * differing bit position and break the branch. Also handle the
+ * tranfer of netmasks between node and inter_node(explained in more
+ * detail below) */
+ } else {
+ inter_node = SCRadixCreateNode();
+ inter_node->prefix = NULL;
+ inter_node->bit = differ_bit;
+ inter_node->parent = node->parent;
+
+ if (node->netmasks != NULL) {
+ for (i = 0; i < node->netmask_cnt; i++) {
+ if (node->netmasks[i] < differ_bit + 1)
+ break;
+ }
+
+ if ( (inter_node->netmasks = SCMalloc((node->netmask_cnt - i) *
+ sizeof(uint8_t))) == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Fatal error encountered in SCRadixAddKey. Mem not allocated...");
+ return NULL;
+ }
+
+ for (j = 0; j < (node->netmask_cnt - i); j++)
+ inter_node->netmasks[j] = node->netmasks[i + j];
+
+ inter_node->netmask_cnt = (node->netmask_cnt - i);
+ node->netmask_cnt = i;
+
+ if (node->netmask_cnt == 0) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ }
+ }
+
+ if (SC_RADIX_BITTEST(stream[differ_bit >> 3],
+ (0x80 >> (differ_bit % 8))) ) {
+ inter_node->left = node;
+ inter_node->right = new_node;
+ } else {
+ inter_node->left = new_node;
+ inter_node->right = node;
+ }
+ new_node->parent = inter_node;
+
+ if (node->parent == NULL)
+ tree->head = inter_node;
+ else if (node->parent->right == node)
+ node->parent->right = inter_node;
+ else
+ node->parent->left = inter_node;
+
+ node->parent = inter_node;
+ }
+
+ /* insert the netmask into the tree */
+ if (netmask != 255 && (netmask != 32 || (netmask == 32 && bitlen != 32)) && netmask != 128) {
+ node = new_node;
+ parent = new_node->parent;
+ while (parent != NULL && netmask < (parent->bit + 1)) {
+ node = parent;
+ parent = parent->parent;
+ }
+
+ node->netmask_cnt++;
+ if ( (ptmp = SCRealloc(node->netmasks, (node->netmask_cnt *
+ sizeof(uint8_t)))) == NULL) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+ node->netmasks = ptmp;
+
+ if (node->netmask_cnt == 1) {
+ node->netmasks[0] = netmask;
+ return new_node;
+ }
+
+ node->netmasks[node->netmask_cnt - 1] = netmask;
+
+ for (i = node->netmask_cnt - 2; i >= 0; i--) {
+ if (netmask < node->netmasks[i]) {
+ node->netmasks[i + 1] = netmask;
+ break;
+ }
+
+ node->netmasks[i + 1] = node->netmasks[i];
+ node->netmasks[i] = netmask;
+ }
+ }
+
+ return new_node;
+}
+
+/**
+ * \brief Adds a new generic key to the Radix tree
+ *
+ * \param key_stream Data that has to be added to the Radix tree
+ * \param key_bitlen The bitlen of the the above stream. For example if the
+ * stream is the string "abcd", the bitlen would be 32
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with the
+ * key
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyGeneric(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree, void *user)
+{
+ SCRadixNode *node = SCRadixAddKey(key_stream, key_bitlen, tree, user, 255);
+
+ return node;
+}
+
+/**
+ * \brief Adds a new IPV4 address to the Radix tree
+ *
+ * \param key_stream Data that has to be added to the Radix tree. In this case
+ * a pointer to an IPV4 address
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with the
+ * key
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV4(uint8_t *key_stream, SCRadixTree *tree,
+ void *user)
+{
+ SCRadixNode *node = SCRadixAddKey(key_stream, 32, tree, user, 32);
+
+ return node;
+}
+
+/**
+ * \brief Adds a new IPV6 address to the Radix tree
+ *
+ * \param key_stream Data that has to be added to the Radix tree. In this case
+ * the pointer to an IPV6 address
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with the
+ * key
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV6(uint8_t *key_stream, SCRadixTree *tree,
+ void *user)
+{
+ SCRadixNode *node = SCRadixAddKey(key_stream, 128, tree, user, 128);
+
+ return node;
+}
+
+/**
+ * \brief Adds a new IPV4 netblock to the Radix tree
+ *
+ * \param key_stream Data that has to be added to the Radix tree. In this case
+ * a pointer to an IPV4 netblock
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with the
+ * key
+ * \param netmask The netmask (cidr) if we are adding a netblock
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ void *user, uint8_t netmask)
+{
+ SCRadixNode *node = SCRadixAddKey(key_stream, 32, tree, user, netmask);
+
+ return node;
+}
+
+/**
+ * \brief Adds a new IPV6 netblock to the Radix tree
+ *
+ * \param key_stream Data that has to be added to the Radix tree. In this case
+ * a pointer to an IPV6 netblock
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with the
+ * key
+ * \param netmask The netmask (cidr) if we are adding a netblock
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ void *user, uint8_t netmask)
+{
+ SCRadixNode *node = SCRadixAddKey(key_stream, 128, tree, user, netmask);
+
+ return node;
+}
+
+/**
+ * \brief Adds a new IPV4/netblock to the Radix tree from a string
+ *
+ * \param str IPV4 string with optional /cidr netmask
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with
+ * the key
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV4String(const char *str, SCRadixTree *tree, void *user)
+{
+ uint32_t ip;
+ uint8_t netmask = 32;
+ char ip_str[32]; /* Max length for full ipv4/mask string with NUL */
+ char *mask_str = NULL;
+ struct in_addr addr;
+
+ /* Make a copy of the string so it can be modified */
+ strlcpy(ip_str, str, sizeof(ip_str) - 2);
+ *(ip_str + (sizeof(ip_str) - 1)) = '\0';
+
+ /* Does it have a mask? */
+ if (NULL != (mask_str = strchr(ip_str, '/'))) {
+ int cidr;
+ *(mask_str++) = '\0';
+
+ /* Dotted type netmask not supported (yet) */
+ if (strchr(mask_str, '.') != NULL) {
+ return NULL;
+ }
+
+ /* Get binary values for cidr mask */
+ cidr = atoi(mask_str);
+ if ((cidr < 0) || (cidr > 32)) {
+ return NULL;
+ }
+ netmask = (uint8_t)cidr;
+ }
+
+ /* Validate the IP */
+ if (inet_pton(AF_INET, ip_str, &addr) <= 0) {
+ return NULL;
+ }
+ ip = addr.s_addr;
+
+ return SCRadixAddKey((uint8_t *)&ip, 32, tree, user, netmask);
+}
+
+/**
+ * \brief Adds a new IPV6/netblock to the Radix tree from a string
+ *
+ * \param str IPV6 string with optional /cidr netmask
+ * \param tree Pointer to the Radix tree
+ * \param user Pointer to the user data that has to be associated with
+ * the key
+ *
+ * \retval node Pointer to the newly created node
+ */
+SCRadixNode *SCRadixAddKeyIPV6String(const char *str, SCRadixTree *tree, void *user)
+{
+ uint8_t netmask = 128;
+ char ip_str[80]; /* Max length for full ipv6/mask string with NUL */
+ char *mask_str = NULL;
+ struct in6_addr addr;
+
+ /* Make a copy of the string so it can be modified */
+ strlcpy(ip_str, str, sizeof(ip_str) - 2);
+ *(ip_str + sizeof(ip_str) - 1) = '\0';
+
+ /* Does it have a mask? */
+ if (NULL != (mask_str = strchr(ip_str, '/'))) {
+ int cidr;
+ *(mask_str++) = '\0';
+
+ /* Dotted type netmask not supported (yet) */
+ if (strchr(mask_str, '.') != NULL) {
+ return NULL;
+ }
+
+ /* Get binary values for cidr mask */
+ cidr = atoi(mask_str);
+ if ((cidr < 0) || (cidr > 128)) {
+ return NULL;
+ }
+ netmask = (uint8_t)cidr;
+ }
+
+ /* Validate the IP */
+ if (inet_pton(AF_INET6, ip_str, &addr) <= 0) {
+ return NULL;
+ }
+
+ return SCRadixAddKey(addr.s6_addr, 128, tree, user, netmask);
+}
+
+static void SCRadixTransferNetmasksBWNodes(SCRadixNode *dest, SCRadixNode *src)
+{
+ int i = 0, j = 0;
+ void *ptmp = NULL;
+
+ if (src == NULL || dest == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "src or dest NULL");
+ return;
+ }
+
+ /* no netmasks in the source node, to transfer to the destination node */
+ if (src->netmasks == NULL)
+ return;
+
+ if ( (ptmp = SCRealloc(dest->netmasks,
+ (src->netmask_cnt + dest->netmask_cnt) *
+ sizeof(uint8_t))) == NULL) {
+ SCFree(dest->netmasks);
+ dest->netmasks = NULL;
+ return;
+ }
+ dest->netmasks = ptmp;
+
+ for (i = dest->netmask_cnt, j = 0; j < src->netmask_cnt; i++, j++)
+ dest->netmasks[i] = src->netmasks[j];
+
+ return;
+}
+
+/**
+ * \brief Removes a netblock entry from an ip node. The function first
+ * deletes the netblock/user_data entry for the prefix and then
+ * removes the netmask entry that has been made in the tree, by
+ * walking up the tree and deleting the entry from the specific node.
+ *
+ * \param node The node from which the netblock entry has to be removed.
+ * \param netmask The netmask entry (cidr) that has to be removed.
+ */
+static void SCRadixRemoveNetblockEntry(SCRadixNode *node, uint8_t netmask)
+{
+ void *ptmp;
+ SCRadixNode *parent = NULL;
+ int i = 0;
+
+ if (node == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid argument. Node is NULL");
+ return;
+ }
+
+ SCRadixRemoveNetmaskUserDataFromPrefix(node->prefix, netmask);
+
+ if (netmask == 32 || netmask == 128)
+ return;
+
+ parent = node->parent;
+ while (parent != NULL && netmask < (parent->bit + 1)) {
+ parent = parent->parent;
+ }
+
+ for (i = 0; i < node->netmask_cnt; i++) {
+ if (node->netmasks[i] == netmask)
+ break;
+ }
+
+ if (i == node->netmask_cnt) {
+ SCLogDebug("Something's wrong with the tree. We are unable to find the "
+ "netmask entry");
+ return;
+ }
+
+ for ( ; i < node->netmask_cnt - 1; i++)
+ node->netmasks[i] = node->netmasks[i + 1];
+
+ node->netmask_cnt--;
+ if (node->netmask_cnt == 0) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ return;
+ }
+
+ ptmp = SCRealloc(node->netmasks, node->netmask_cnt * sizeof(uint8_t));
+ if (ptmp == NULL) {
+ SCFree(node->netmasks);
+ node->netmasks = NULL;
+ return;
+ }
+ node->netmasks = ptmp;
+
+ return;
+}
+
+/**
+ * \brief Removes a key from the Radix tree
+ *
+ * \param key_stream Data that has to be removed from the Radix tree
+ * \param key_bitlen The bitlen of the the above stream. For example if the
+ * stream holds an IPV4 address(4 bytes), bitlen would be 32
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+static void SCRadixRemoveKey(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree, uint8_t netmask)
+{
+ SCRadixNode *node = tree->head;
+ SCRadixNode *parent = NULL;
+ SCRadixNode *temp_dest = NULL;
+
+ SCRadixPrefix *prefix = NULL;
+
+ int mask = 0;
+ int i = 0;
+
+ if (node == NULL)
+ return;
+
+ if ( (prefix = SCRadixCreatePrefix(key_stream, key_bitlen, NULL, 255)) == NULL)
+ return;
+
+ while (node->bit < prefix->bitlen) {
+ if (SC_RADIX_BITTEST(prefix->stream[node->bit >> 3],
+ (0x80 >> (node->bit % 8))) ) {
+ node = node->right;
+ } else {
+ node = node->left;
+ }
+
+ if (node == NULL) {
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+ }
+
+ if (node->bit != prefix->bitlen || node->prefix == NULL) {
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+
+ i = prefix->bitlen / 8;
+ if (SCMemcmp(node->prefix->stream, prefix->stream, i) == 0) {
+ mask = -1 << (8 - prefix->bitlen % 8);
+
+ if (prefix->bitlen % 8 == 0 ||
+ (node->prefix->stream[i] & mask) == (prefix->stream[i] & mask)) {
+ if (!SCRadixPrefixContainNetmask(node->prefix, netmask)) {
+ SCLogDebug("The ip key exists in the Radix Tree, but this(%d) "
+ "netblock entry doesn't exist", netmask);
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+ } else {
+ SCLogDebug("You are trying to remove a key that doesn't exist in "
+ "the Radix Tree");
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+ } else {
+ SCLogDebug("You are trying to remove a key that doesn't exist in the "
+ "Radix Tree");
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+
+ /* The ip node does exist, and the netblock entry does exist in this node, if
+ * we have reached this point. If we have more than one netblock entry, it
+ * indicates we have multiple entries for this key. So we delete that
+ * particular netblock entry, and make our way out of this function */
+ if (SCRadixPrefixNetmaskCount(node->prefix) > 1) {
+ SCRadixRemoveNetblockEntry(node, netmask);
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+
+ /* we are deleting the root of the tree. This would be the only node left
+ * in the tree */
+ if (tree->head == node) {
+ SCFree(node);
+ tree->head = NULL;
+ SCRadixReleasePrefix(prefix, tree);
+ return;
+ }
+
+ parent = node->parent;
+ /* parent->parent is not the root of the tree */
+ if (parent->parent != NULL) {
+ if (parent->parent->left == parent) {
+ if (node->parent->left == node) {
+ temp_dest = parent->right;
+ parent->parent->left = parent->right;
+ parent->right->parent = parent->parent;
+ } else {
+ temp_dest = parent->left;
+ parent->parent->left = parent->left;
+ parent->left->parent = parent->parent;
+ }
+ } else {
+ if (node->parent->left == node) {
+ temp_dest = parent->right;
+ parent->parent->right = parent->right;
+ parent->right->parent = parent->parent;
+ } else {
+ temp_dest = parent->left;
+ parent->parent->right = parent->left;
+ parent->left->parent = parent->parent;
+ }
+ }
+ /* parent is the root of the tree */
+ } else {
+ if (parent->left == node) {
+ temp_dest = tree->head->right;
+ tree->head->right->parent = NULL;
+ tree->head = tree->head->right;
+ } else {
+ temp_dest = tree->head->left;
+ tree->head->left->parent = NULL;
+ tree->head = tree->head->left;
+ }
+ }
+ /* We need to shift the netmask entries from the node that would be
+ * deleted to its immediate descendant */
+ SCRadixTransferNetmasksBWNodes(temp_dest, parent);
+ /* release the nodes */
+ SCRadixReleaseNode(parent, tree);
+ SCRadixReleaseNode(node, tree);
+ SCRadixReleasePrefix(prefix, tree);
+
+ return;
+}
+
+/**
+ * \brief Removes a key from the Radix tree
+ *
+ * \param key_stream Data that has to be removed from the Radix tree
+ * \param key_bitlen The bitlen of the the above stream.
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+void SCRadixRemoveKeyGeneric(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree)
+{
+ SCRadixRemoveKey(key_stream, key_bitlen, tree, 255);
+ return;
+}
+
+/**
+ * \brief Removes an IPV4 address netblock key from the Radix tree.
+ *
+ * \param key_stream Data that has to be removed from the Radix tree. In this
+ * case an IPV4 address
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+void SCRadixRemoveKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ uint8_t netmask)
+{
+ SCRadixRemoveKey(key_stream, 32, tree, netmask);
+ return;
+}
+
+/**
+ * \brief Removes an IPV4 address key(not a netblock) from the Radix tree.
+ * Instead of using this function, we can also used
+ * SCRadixRemoveKeyIPV4Netblock(), by supplying a netmask value of 32.
+ *
+ * \param key_stream Data that has to be removed from the Radix tree. In this
+ * case an IPV4 address
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+void SCRadixRemoveKeyIPV4(uint8_t *key_stream, SCRadixTree *tree)
+{
+ SCRadixRemoveKey(key_stream, 32, tree, 32);
+ return;
+}
+
+/**
+ * \brief Removes an IPV6 netblock address key from the Radix tree.
+ *
+ * \param key_stream Data that has to be removed from the Radix tree. In this
+ * case an IPV6 address
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+void SCRadixRemoveKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ uint8_t netmask)
+{
+ SCRadixRemoveKey(key_stream, 128, tree, netmask);
+ return;
+}
+
+/**
+ * \brief Removes an IPV6 address key(not a netblock) from the Radix tree.
+ * Instead of using this function, we can also used
+ * SCRadixRemoveKeyIPV6Netblock(), by supplying a netmask value of 128.
+ *
+ * \param key_stream Data that has to be removed from the Radix tree. In this
+ * case an IPV6 address
+ * \param tree Pointer to the Radix tree from which the key has to be
+ * removed
+ */
+void SCRadixRemoveKeyIPV6(uint8_t *key_stream, SCRadixTree *tree)
+{
+ SCRadixRemoveKey(key_stream, 128, tree, 128);
+ return;
+}
+
+/**
+ * \brief Checks if an IP prefix falls under a netblock, in the path to the root
+ * of the tree, from the node. Used internally by SCRadixFindKey()
+ *
+ * \param prefix Pointer to the prefix that contains the ip address
+ * \param node Pointer to the node from where we have to climb the tree
+ */
+static inline SCRadixNode *SCRadixFindKeyIPNetblock(uint8_t *key_stream, uint8_t key_bitlen,
+ SCRadixNode *node, void **user_data_result)
+{
+ SCRadixNode *netmask_node = NULL;
+ int mask = 0;
+ int bytes = 0;
+ int i = 0;
+ int j = 0;
+
+ while (node != NULL && node->netmasks == NULL)
+ node = node->parent;
+
+ if (node == NULL)
+ return NULL;
+ /* hold the node found containing a netmask. We will need it when we call
+ * this function recursively */
+ netmask_node = node;
+
+ for (j = 0; j < netmask_node->netmask_cnt; j++) {
+ bytes = key_bitlen / 8;
+ for (i = 0; i < bytes; i++) {
+ mask = -1;
+ if ( ((i + 1) * 8) > netmask_node->netmasks[j]) {
+ if ( ((i + 1) * 8 - netmask_node->netmasks[j]) < 8)
+ mask = -1 << ((i + 1) * 8 - netmask_node->netmasks[j]);
+ else
+ mask = 0;
+ }
+ key_stream[i] &= mask;
+ }
+
+ while (node->bit < key_bitlen) {
+ if (SC_RADIX_BITTEST(key_stream[node->bit >> 3],
+ (0x80 >> (node->bit % 8))) ) {
+ node = node->right;
+ } else {
+ node = node->left;
+ }
+
+ if (node == NULL)
+ return NULL;
+ }
+
+ if (node->bit != key_bitlen || node->prefix == NULL)
+ return NULL;
+
+ if (SCMemcmp(node->prefix->stream, key_stream, bytes) == 0) {
+ mask = -1 << (8 - key_bitlen % 8);
+
+ if (key_bitlen % 8 == 0 ||
+ (node->prefix->stream[bytes] & mask) == (key_stream[bytes] & mask)) {
+ if (SCRadixPrefixContainNetmaskAndSetUserData(node->prefix, netmask_node->netmasks[j], 0, user_data_result))
+ return node;
+ }
+ }
+ }
+
+ return SCRadixFindKeyIPNetblock(key_stream, key_bitlen, netmask_node->parent, user_data_result);
+}
+
+/**
+ * \brief Checks if an IP address key is present in the tree. The function
+ * apart from handling any normal data, also handles ipv4/ipv6 netblocks
+ *
+ * \param key_stream Data that has to be found in the Radix tree
+ * \param key_bitlen The bitlen of the above stream.
+ * \param tree Pointer to the Radix tree
+ * \param exact_match The key to be searched is an ip address
+ */
+static SCRadixNode *SCRadixFindKey(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree, int exact_match, void **user_data_result)
+{
+ if (tree == NULL || tree->head == NULL)
+ return NULL;
+
+ SCRadixNode *node = tree->head;
+ int mask = 0;
+ int bytes = 0;
+ uint8_t tmp_stream[255];
+
+ if (key_bitlen > 255)
+ return NULL;
+
+ memset(tmp_stream, 0, 255);
+ memcpy(tmp_stream, key_stream, key_bitlen / 8);
+
+ while (node->bit < key_bitlen) {
+ if (SC_RADIX_BITTEST(tmp_stream[node->bit >> 3],
+ (0x80 >> (node->bit % 8))) ) {
+ node = node->right;
+ } else {
+ node = node->left;
+ }
+
+ if (node == NULL) {
+ return NULL;
+ }
+ }
+
+ if (node->bit != key_bitlen || node->prefix == NULL) {
+ return NULL;
+ }
+
+ bytes = key_bitlen / 8;
+ if (SCMemcmp(node->prefix->stream, tmp_stream, bytes) == 0) {
+ mask = -1 << (8 - key_bitlen % 8);
+
+ if (key_bitlen % 8 == 0 ||
+ (node->prefix->stream[bytes] & mask) == (tmp_stream[bytes] & mask)) {
+ if (SCRadixPrefixContainNetmaskAndSetUserData(node->prefix, key_bitlen, 1, user_data_result)) {
+ return node;
+ }
+ }
+ }
+
+ /* if you are not an ip key, get out of here */
+ if (exact_match) {
+ return NULL;
+ }
+
+ SCRadixNode *ret = SCRadixFindKeyIPNetblock(tmp_stream, key_bitlen, node, user_data_result);
+ return ret;
+}
+
+/**
+ * \brief Checks if a key is present in the tree
+ *
+ * \param key_stream Data that has to be found in the Radix tree
+ * \param key_bitlen The bitlen of the the above stream.
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyGeneric(uint8_t *key_stream, uint16_t key_bitlen,
+ SCRadixTree *tree, void **user_data_result)
+{
+ return SCRadixFindKey(key_stream, key_bitlen, tree, 1, user_data_result);
+}
+
+/**
+ * \brief Checks if an IPV4 address is present in the tree
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV4 address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV4ExactMatch(uint8_t *key_stream, SCRadixTree *tree, void **user_data_result)
+{
+ return SCRadixFindKey(key_stream, 32, tree, 1, user_data_result);
+}
+
+/**
+ * \brief Checks if an IPV4 address is present in the tree under a netblock
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV4 address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV4BestMatch(uint8_t *key_stream, SCRadixTree *tree, void **user_data_result)
+{
+ return SCRadixFindKey(key_stream, 32, tree, 0, user_data_result);
+}
+
+/**
+ * \brief Checks if an IPV4 Netblock address is present in the tree
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV4 netblock address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ uint8_t netmask, void **user_data_result)
+{
+ SCRadixNode *node = NULL;
+ node = SCRadixFindKey(key_stream, 32, tree, 0, user_data_result);
+ if (node == NULL)
+ return node;
+
+ if (SCRadixPrefixContainNetmaskAndSetUserData(node->prefix, netmask, 1, user_data_result))
+ return node;
+ else
+ return NULL;
+}
+
+/**
+ * \brief Checks if an IPV6 Netblock address is present in the tree
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV6 netblock address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
+ uint8_t netmask, void **user_data_result)
+{
+ SCRadixNode *node = NULL;
+ node = SCRadixFindKey(key_stream, 128, tree, 0, user_data_result);
+ if (node == NULL)
+ return node;
+
+ if (SCRadixPrefixContainNetmaskAndSetUserData(node->prefix, (uint16_t)netmask, 1, user_data_result))
+ return node;
+ else
+ return NULL;
+}
+
+/**
+ * \brief Checks if an IPV6 address is present in the tree
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV6 address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV6ExactMatch(uint8_t *key_stream, SCRadixTree *tree, void **user_data_result)
+{
+ return SCRadixFindKey(key_stream, 128, tree, 1, user_data_result);
+}
+
+/**
+ * \brief Checks if an IPV6 address is present in the tree under a netblock
+ *
+ * \param key_stream Data that has to be found in the Radix tree. In this case
+ * an IPV6 address
+ * \param tree Pointer to the Radix tree instance
+ */
+SCRadixNode *SCRadixFindKeyIPV6BestMatch(uint8_t *key_stream, SCRadixTree *tree, void **user_data_result)
+{
+ return SCRadixFindKey(key_stream, 128, tree, 0, user_data_result);
+}
+
+/**
+ * \brief Prints the node information from a Radix tree
+ *
+ * \param node Pointer to the Radix node whose information has to be printed
+ * \param level Used for indentation purposes
+ */
+void SCRadixPrintNodeInfo(SCRadixNode *node, int level, void (*PrintData)(void*))
+{
+ int i = 0;
+
+ if (node == NULL)
+ return;
+
+ for (i = 0; i < level; i++)
+ printf(" ");
+
+ printf("%d [", node->bit);
+
+ if (node->netmasks == NULL) {
+ printf("%d, ", -1);
+ } else {
+ for (i = 0; i < node->netmask_cnt; i++)
+ printf("%s%d", (0 == i ? "" : ", "), node->netmasks[i]);
+ }
+
+ printf("] (");
+ if (node->prefix != NULL) {
+ for (i = 0; i * 8 < node->prefix->bitlen; i++)
+ printf("%s%d", (0 == i ? "" : "."), node->prefix->stream[i]);
+ printf(")\n");
+
+ SCRadixUserData *ud = NULL;
+ if (PrintData != NULL) {
+ do {
+ ud = node->prefix->user_data;
+ printf(" [%d], ", ud->netmask);
+ PrintData(ud->user);
+ ud = ud->next;
+ } while (ud != NULL);
+ } else {
+ //ud = node->prefix->user_data;
+ //while (ud != NULL) {
+ // printf(" [nm %d with data], ", ud->netmask);
+ // ud = ud->next;
+ //}
+ printf("No print function provided");
+ }
+ printf("\n");
+ } else {
+ printf("NULL)\n");
+ }
+
+ return;
+}
+
+/**
+ * \brief Helper function used by SCRadixPrintTree. Prints the subtree with
+ * node as the root of the subtree
+ *
+ * \param node Pointer to the node that is the root of the subtree to be printed
+ * \param level Used for indentation purposes
+ */
+static void SCRadixPrintRadixSubtree(SCRadixNode *node, int level, void (*PrintData)(void*))
+{
+ if (node != NULL) {
+ SCRadixPrintNodeInfo(node, level, PrintData);
+ SCRadixPrintRadixSubtree(node->left, level + 1, PrintData);
+ SCRadixPrintRadixSubtree(node->right, level + 1, PrintData);
+ }
+
+ return;
+}
+
+/**
+ * \brief Prints the Radix Tree. While printing the radix tree we use the
+ * following format
+ *
+ * Parent_0
+ * Left_Child_1
+ * Left_Child_2
+ * Right_Child_2
+ * Right_Child_1
+ * Left_Child_2
+ * Right_Child_2 and so on
+ *
+ * Each node printed out holds details on the next bit that differs
+ * amongst its children, and if the node holds a prefix, the perfix is
+ * printed as well.
+ *
+ * \param tree Pointer to the Radix tree that has to be printed
+ */
+void SCRadixPrintTree(SCRadixTree *tree)
+{
+ printf("Printing the Radix Tree: \n");
+
+ SCRadixPrintRadixSubtree(tree->head, 0, tree->PrintData);
+
+ return;
+}
+
+/*------------------------------------Unit_Tests------------------------------*/
+
+#ifdef UNITTESTS
+
+int SCRadixTestInsertion01(void)
+{
+ SCRadixTree *tree = NULL;
+ SCRadixNode *node[2];
+
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+
+ node[0] = SCRadixAddKeyGeneric((uint8_t *)"abaa", 32, tree, NULL);
+ node[1] = SCRadixAddKeyGeneric((uint8_t *)"abab", 32, tree, NULL);
+
+ result &= (tree->head->bit == 30);
+ result &= (tree->head->right == node[0]);
+ result &= (tree->head->left == node[1]);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestInsertion02(void)
+{
+ SCRadixTree *tree = NULL;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ SCRadixAddKeyGeneric((uint8_t *)"aaaaaa", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"aaaaab", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"aaaaaba", 56, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"abab", 32, tree, NULL);
+ SCRadixReleaseRadixTree(tree);
+
+ /* If we don't have a segfault till here we have succeeded :) */
+ return result;
+}
+
+int SCRadixTestIPV4Insertion03(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ /* add a key that already exists in the tree */
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ /* continue adding keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.18", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ /* test the existence of keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "127.234.2.62", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.18", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV4Removal04(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.18", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ /* remove the keys from the tree */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.18", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4((uint8_t *)&servaddr.sin_addr, tree);
+
+ result &= (tree->head == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestCharacterInsertion05(void)
+{
+ SCRadixTree *tree = NULL;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* Let us have our team here ;-) */
+ SCRadixAddKeyGeneric((uint8_t *)"Victor", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Matt", 32, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Josh", 32, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Margaret", 64, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Pablo", 40, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Brian", 40, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Jasonish", 64, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Jasonmc", 56, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Nathan", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Anoop", 40, tree, NULL);
+
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Victor", 48, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Matt", 32, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Josh", 32, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Margaret", 64, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Pablo", 40, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Brian", 40, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Jasonish", 64, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Jasonmc", 56, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Nathan", 48, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Anoop", 40, tree, NULL) != NULL);
+
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"bamboo", 48, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"bool", 32, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"meerkat", 56, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Victor", 48, tree, NULL) == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestCharacterRemoval06(void)
+{
+ SCRadixTree *tree = NULL;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* Let us have our team here ;-) */
+ SCRadixAddKeyGeneric((uint8_t *)"Victor", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Matt", 32, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Josh", 32, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Margaret", 64, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Pablo", 40, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Brian", 40, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Jasonish", 64, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Jasonmc", 56, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Nathan", 48, tree, NULL);
+ SCRadixAddKeyGeneric((uint8_t *)"Anoop", 40, tree, NULL);
+
+ SCRadixRemoveKeyGeneric((uint8_t *)"Nathan", 48, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Brian", 40, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Margaret", 64, tree);
+
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Victor", 48, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Matt", 32, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Josh", 32, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Margaret", 64, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Brian", 40, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Nathan", 48, tree, NULL) == NULL);
+
+ SCRadixRemoveKeyGeneric((uint8_t *)"Victor", 48, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Josh", 32, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Jasonmc", 56, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Matt", 32, tree);
+
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Pablo", 40, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Jasonish", 64, tree, NULL) != NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Anoop", 40, tree, NULL) != NULL);
+
+ SCRadixRemoveKeyGeneric((uint8_t *)"Pablo", 40, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Jasonish", 64, tree);
+ SCRadixRemoveKeyGeneric((uint8_t *)"Anoop", 40, tree);
+
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Pablo", 40, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Jasonish", 64, tree, NULL) == NULL);
+ result &= (SCRadixFindKeyGeneric((uint8_t *)"Anoop", 40, tree, NULL) == NULL);
+
+ result &= (tree->head == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV6Insertion07(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* Try to add the prefix that already exists in the tree */
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* test the existence of keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABC2:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF5:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV6Removal08(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* Try to add the prefix that already exists in the tree */
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* test the existence of keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "8888:0BF1:5346:BDEA:6422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2006:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* test for existance */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:DDDD:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ /* remove keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ /* test for existance */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ /* remove keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree);
+
+ /* test for existance */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV4NetblockInsertion09(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.3", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.1.2", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.18", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.192.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 18);
+
+ if (inet_pton(AF_INET, "192.175.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.170.1.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.145", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.64.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.191.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.224.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.174.224.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.175.224.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV4NetblockInsertion10(void)
+{
+ SCRadixTree *tree = NULL;
+ SCRadixNode *node[2];
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.235.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ node[0] = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL,
+ 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ node[1] = SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 18);
+
+ if (inet_pton(AF_INET, "192.175.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.53", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[0]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[1]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[1]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[0]);
+
+ /* let us remove a netblock */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.127.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV4NetblockInsertion11(void)
+{
+ SCRadixTree *tree = NULL;
+ SCRadixNode *node = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.235.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 18);
+
+ if (inet_pton(AF_INET, "192.175.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ node = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 0);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.53", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.127.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "1.1.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.255.254.25", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "169.255.254.25", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL &&
+ SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "245.63.62.121", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL &&
+ SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL &&
+ SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node);
+
+ /* remove node 0.0.0.0 */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixRemoveKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, 0);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.6", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.127.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "1.1.1.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.255.254.25", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "169.255.254.25", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV4NetblockInsertion12(void)
+{
+ SCRadixTree *tree = NULL;
+ SCRadixNode *node[2];
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.192.235.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.167.1.4", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "220.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "253.224.1.5", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ node[0] = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 24);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ node[1] = SCRadixAddKeyIPV4((uint8_t *)&servaddr.sin_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 18);
+
+ if (inet_pton(AF_INET, "225.175.21.228", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 32);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.53", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[0]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.53", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[1]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[1]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.45", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[1]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.128.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == node[0]);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.171.127.78", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "225.175.21.228", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "225.175.21.224", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "225.175.21.229", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "225.175.21.230", &servaddr.sin_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV4ExactMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) == NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV6NetblockInsertion13(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DB00:0000:0000:0000:0000",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, NULL, 56);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1145:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ /* test the existence of keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABC2:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF5:5346:1251:7422:1112:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBCA:1245:2342:1111:2212",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1146:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1356:1241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DAAA:1245:2342:1146:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+int SCRadixTestIPV6NetblockInsertion14(void)
+{
+ SCRadixTree *tree = NULL;
+ SCRadixNode *node = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ /* add the keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2003:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "BD15:9791:5346:6223:AADB:8713:9882:2432",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "1111:A21B:6221:BDEA:BBBA::DBAA:9861",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "4444:0BF7:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "5555:0BF1:ABCD:ADEA:7922:ABCD:9124:2375",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DB00:0000:0000:0000:0000",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, NULL, 56);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1145:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV6((uint8_t *)&servaddr.sin6_addr, tree, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "::", &servaddr.sin6_addr) <= 0)
+ return 0;
+ node = SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, NULL,
+ 0);
+
+ /* test the existence of keys */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2004:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2004:0BF1:5346:BDEA:7422:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2004:0BF1:5346:B116:2362:8713:9124:2315",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "2004:0B23:3252:BDEA:7422:8713:9124:2341",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) == node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1145:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL &&
+ SCRadixFindKeyIPV6ExactMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != node);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DBCA:ABCD:ABCD:DBAA:1245:2342:1145:6241",
+ &servaddr.sin6_addr) <= 0)
+ return 0;
+ result &= (SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != NULL &&
+ SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, NULL) != node);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV4NetBlocksAndBestSearch15(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.1", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 32; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV4NetBlocksAndBestSearch16(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 32; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV4NetBlocksAndBestSearch17(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "10.0.0.1", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 32; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV4NetBlocksAndBestSearch18(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "172.26.0.1", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 32; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check special combinations of netblocks and addresses
+ * on best search checking the returned userdata
+ */
+int SCRadixTestIPV4NetBlocksAndBestSearch19(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+ void *user_data = NULL;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 100;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, 0);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ SCRadixNode *node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 100) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.0.0.0", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 200;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, 8);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.168.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 200) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "178.168.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t*)user_data) != 100) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.168.0.0", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 300;
+
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, user, 12);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.168.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t*)user_data) != 300) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.167.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 300) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "177.178.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 200) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "197.178.1.15", &servaddr.sin_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 100) {
+ result = 0;
+ goto end;
+ }
+
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV6NetBlocksAndBestSearch20(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABAB:CDCD:ABAB:CDCD:1234:4321:1234:4321", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 128; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV6NetBlocksAndBestSearch21(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ff00::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 128; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV6NetBlocksAndBestSearch22(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ff00::192:168:1:1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 128; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check that the best match search works for all the
+ * possible netblocks of a fixed address
+ */
+int SCRadixTestIPV6NetBlocksAndBestSearch23(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t i = 0;
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "FF00:ABCD:BCDA::ABCD", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ for (; i <= 128; i++) {
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = i;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, i);
+
+ void *user_data = NULL;
+ SCRadixNode *node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != i) {
+ printf("User data == %"PRIu32"; i == %"PRIu32": ", *( (uint32_t *)user_data), i);
+ result = 0;
+ goto end;
+ }
+ }
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test Check special combinations of netblocks and addresses
+ * on best search checking the returned userdata
+ */
+int SCRadixTestIPV6NetBlocksAndBestSearch24(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in6 servaddr;
+ int result = 1;
+ void *user_data = NULL;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ uint32_t *user;
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "::", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 100;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, 0);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ SCRadixNode *node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t*)user_data) != 100) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD::0", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 200;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, 8);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 200) {
+ printf("User data == %"PRIu32"; i != 200 ", *( (uint32_t *)user_data));
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "DCBA::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 100) {
+ printf("User data == %"PRIu32"; != 100 ", *( (uint32_t *)user_data));
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD:ABCD::0", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ user = SCMalloc(sizeof(uint32_t));
+ if (unlikely(user == NULL)) {
+ result = 0;
+ goto end;
+ }
+
+ *user = 300;
+
+ SCRadixAddKeyIPV6Netblock((uint8_t *)&servaddr.sin6_addr, tree, user, 12);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD:ABCD::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 300) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABCD:AAAA::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 300) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "ABAB::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 200) {
+ result = 0;
+ goto end;
+ }
+
+ user_data = NULL;
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET6, "CABD::1", &servaddr.sin6_addr) <= 0) {
+ result = 0;
+ goto end;
+ }
+
+ node = SCRadixFindKeyIPV6BestMatch((uint8_t *)&servaddr.sin6_addr, tree, &user_data);
+ if (node == NULL) {
+ printf("node == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (user_data == NULL) {
+ printf("User data == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if ( *( (uint32_t *)user_data) != 100) {
+ result = 0;
+ goto end;
+ }
+
+
+end:
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+
+/**
+ * \test SCRadixTestIPV4NetblockInsertion15 insert a node searching on it.
+ * Should always return true but the purposse of the test is to monitor
+ * the memory usage to detect memleaks (there was one on searching)
+ */
+int SCRadixTestIPV4NetblockInsertion25(void)
+{
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, NULL, 16);
+
+ /* test for the existance of a key */
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "192.168.128.53", &servaddr.sin_addr) <= 0)
+ return 0;
+
+ result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree, NULL) != NULL);
+
+ SCRadixReleaseRadixTree(tree);
+
+ return result;
+}
+
+/**
+ * \test SCRadixTestIPV4NetblockInsertion26 insert a node searching on it.
+ * Should always return true but the purposse of the test is to monitor
+ * the memory usage to detect memleaks (there was one on searching)
+ */
+int SCRadixTestIPV4NetblockInsertion26(void)
+{
+ SCRadixNode *tmp = NULL;
+ SCRadixTree *tree = NULL;
+ struct sockaddr_in servaddr;
+ int result = 1;
+ char *str = SCStrdup("Hello1");
+
+ tree = SCRadixCreateRadixTree(free, NULL);
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0)
+ return 0;
+ tmp = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, str, 0);
+ if (!tmp) {
+ printf("Not inserted correctly 1:");
+ result = 0;
+ goto this_end;
+ }
+ str = SCStrdup("Hello1");
+
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "176.0.0.1", &servaddr.sin_addr) <= 0)
+ return 0;
+ tmp = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, str, 5);
+ if (!tmp) {
+ printf("Not inserted correctly 2:");
+ result = 0;
+ goto this_end;
+ }
+
+ str = SCStrdup("Hello1");
+ bzero(&servaddr, sizeof(servaddr));
+ if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) <= 0) {
+ SCFree(str);
+ return 0;
+ }
+ tmp = SCRadixAddKeyIPV4Netblock((uint8_t *)&servaddr.sin_addr, tree, str, 7);
+ if (!tmp) {
+ printf("Not inserted correctly 3:");
+ result = 0;
+ goto this_end;
+ }
+
+ /* test for the existance of a key */
+ //result &= (SCRadixFindKeyIPV4BestMatch((uint8_t *)&servaddr.sin_addr, tree) != NULL);
+
+this_end:
+ SCRadixReleaseRadixTree(tree);
+
+ //SCFree(str);
+ return result;
+}
+
+#endif
+
+void SCRadixRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ //UtRegisterTest("SCRadixTestInsertion01", SCRadixTestInsertion01, 1);
+ //UtRegisterTest("SCRadixTestInsertion02", SCRadixTestInsertion02, 1);
+ UtRegisterTest("SCRadixTestIPV4Insertion03", SCRadixTestIPV4Insertion03, 1);
+ UtRegisterTest("SCRadixTestIPV4Removal04", SCRadixTestIPV4Removal04, 1);
+ //UtRegisterTest("SCRadixTestCharacterInsertion05",
+ // SCRadixTestCharacterInsertion05, 1);
+ //UtRegisterTest("SCRadixTestCharacterRemoval06",
+ // SCRadixTestCharacterRemoval06, 1);
+ UtRegisterTest("SCRadixTestIPV6Insertion07", SCRadixTestIPV6Insertion07, 1);
+ UtRegisterTest("SCRadixTestIPV6Removal08", SCRadixTestIPV6Removal08, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion09",
+ SCRadixTestIPV4NetblockInsertion09, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion10",
+ SCRadixTestIPV4NetblockInsertion10, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion11",
+ SCRadixTestIPV4NetblockInsertion11, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion12",
+ SCRadixTestIPV4NetblockInsertion12, 1);
+ UtRegisterTest("SCRadixTestIPV6NetblockInsertion13",
+ SCRadixTestIPV6NetblockInsertion13, 1);
+ UtRegisterTest("SCRadixTestIPV6NetblockInsertion14",
+ SCRadixTestIPV6NetblockInsertion14, 1);
+ UtRegisterTest("SCRadixTestIPV4NetBlocksAndBestSearch15",
+ SCRadixTestIPV4NetBlocksAndBestSearch15, 1);
+ UtRegisterTest("SCRadixTestIPV4NetBlocksAndBestSearch16",
+ SCRadixTestIPV4NetBlocksAndBestSearch16, 1);
+ UtRegisterTest("SCRadixTestIPV4NetBlocksAndBestSearch17",
+ SCRadixTestIPV4NetBlocksAndBestSearch17, 1);
+ UtRegisterTest("SCRadixTestIPV4NetBlocksAndBestSearch18",
+ SCRadixTestIPV4NetBlocksAndBestSearch18, 1);
+ UtRegisterTest("SCRadixTestIPV4NetBlocksAndBestSearch19",
+ SCRadixTestIPV4NetBlocksAndBestSearch19, 1);
+ UtRegisterTest("SCRadixTestIPV6NetBlocksAndBestSearch20",
+ SCRadixTestIPV6NetBlocksAndBestSearch20, 1);
+ UtRegisterTest("SCRadixTestIPV6NetBlocksAndBestSearch21",
+ SCRadixTestIPV6NetBlocksAndBestSearch21, 1);
+ UtRegisterTest("SCRadixTestIPV6NetBlocksAndBestSearch22",
+ SCRadixTestIPV6NetBlocksAndBestSearch22, 1);
+ UtRegisterTest("SCRadixTestIPV6NetBlocksAndBestSearch23",
+ SCRadixTestIPV6NetBlocksAndBestSearch23, 1);
+ UtRegisterTest("SCRadixTestIPV6NetBlocksAndBestSearch24",
+ SCRadixTestIPV6NetBlocksAndBestSearch24, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion25",
+ SCRadixTestIPV4NetblockInsertion25, 1);
+ UtRegisterTest("SCRadixTestIPV4NetblockInsertion26",
+ SCRadixTestIPV4NetblockInsertion26, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-radix-tree.h b/framework/src/suricata/src/util-radix-tree.h
new file mode 100644
index 00000000..e9e63457
--- /dev/null
+++ b/framework/src/suricata/src/util-radix-tree.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_RADIX_TREE_H__
+#define __UTIL_RADIX_TREE_H__
+
+#define SC_RADIX_BITTEST(x, y) ((x) & (y))
+
+/**
+ * \brief Structure that hold the user data and the netmask associated with it.
+ */
+typedef struct SCRadixUserData_ {
+ /* holds a pointer to the user data associated with the particular netmask */
+ void *user;
+ /* pointer to the next user data in the list */
+ struct SCRadixUserData_ *next;
+ /* holds the netmask value that corresponds to this user data pointer */
+ uint8_t netmask;
+} SCRadixUserData;
+
+/**
+ * \brief Structure for the prefix/key in the radix tree
+ */
+typedef struct SCRadixPrefix_ {
+ /* length of the stream */
+ uint16_t bitlen;
+
+ /* the key that has been stored in the tree */
+ uint8_t *stream;
+
+ /* any user data that has to be associated with this key. We need a user
+ * data field for each netblock value possible since one ip can be associated
+ * with any of the the 32 or 128 netblocks. Also for non-ips, we store the
+ * netmask as 255 in SCRadixUserData->netmask */
+ SCRadixUserData *user_data;
+} SCRadixPrefix;
+
+/**
+ * \brief Structure for the node in the radix tree
+ */
+typedef struct SCRadixNode_ {
+ /* the bit position where the bits differ in the nodes children. Used
+ * to determine the path to be taken during a lookup*/
+ uint16_t bit;
+
+ uint16_t pad0;
+
+ /* total no of netmasks that are registered under this node */
+ int netmask_cnt;
+ /* holds a list of netmaks that come under this node in the tree */
+ uint8_t *netmasks;
+
+ /* holds the prefix that the path to this node holds */
+ SCRadixPrefix *prefix;
+
+ /* the left and the right children of a node */
+ struct SCRadixNode_ *left, *right;
+
+ /* the parent node for this tree */
+ struct SCRadixNode_ *parent;
+} SCRadixNode;
+
+/**
+ * \brief Structure for the radix tree
+ */
+typedef struct SCRadixTree_ {
+ /* the root node in the radix tree */
+ SCRadixNode *head;
+
+ /* function pointer that is supplied by the user to free the user data
+ * held by the user field of SCRadixNode */
+ void (*PrintData)(void *);
+ void (*Free)(void *);
+} SCRadixTree;
+
+
+struct in_addr *SCRadixValidateIPV4Address(const char *);
+struct in6_addr *SCRadixValidateIPV6Address(const char *);
+void SCRadixChopIPAddressAgainstNetmask(uint8_t *, uint8_t, uint16_t);
+
+SCRadixTree *SCRadixCreateRadixTree(void (*Free)(void*), void (*PrintData)(void*));
+void SCRadixReleaseRadixTree(SCRadixTree *);
+
+SCRadixNode *SCRadixAddKeyGeneric(uint8_t *, uint16_t, SCRadixTree *, void *);
+SCRadixNode *SCRadixAddKeyIPV4(uint8_t *, SCRadixTree *, void *);
+SCRadixNode *SCRadixAddKeyIPV6(uint8_t *, SCRadixTree *, void *);
+SCRadixNode *SCRadixAddKeyIPV4Netblock(uint8_t *, SCRadixTree *, void *,
+ uint8_t);
+SCRadixNode *SCRadixAddKeyIPV6Netblock(uint8_t *, SCRadixTree *, void *,
+ uint8_t);
+SCRadixNode *SCRadixAddKeyIPV4String(const char *, SCRadixTree *, void *);
+SCRadixNode *SCRadixAddKeyIPV6String(const char *, SCRadixTree *, void *);
+
+void SCRadixRemoveKeyGeneric(uint8_t *, uint16_t, SCRadixTree *);
+void SCRadixRemoveKeyIPV4Netblock(uint8_t *, SCRadixTree *, uint8_t);
+void SCRadixRemoveKeyIPV4(uint8_t *, SCRadixTree *);
+void SCRadixRemoveKeyIPV6Netblock(uint8_t *, SCRadixTree *, uint8_t);
+void SCRadixRemoveKeyIPV6(uint8_t *, SCRadixTree *);
+
+SCRadixNode *SCRadixFindKeyGeneric(uint8_t *, uint16_t, SCRadixTree *, void **);
+
+SCRadixNode *SCRadixFindKeyIPV4ExactMatch(uint8_t *, SCRadixTree *, void **);
+SCRadixNode *SCRadixFindKeyIPV4Netblock(uint8_t *, SCRadixTree *, uint8_t, void **);
+SCRadixNode *SCRadixFindKeyIPV4BestMatch(uint8_t *, SCRadixTree *, void **);
+
+SCRadixNode *SCRadixFindKeyIPV6ExactMatch(uint8_t *, SCRadixTree *, void **);
+SCRadixNode *SCRadixFindKeyIPV6Netblock(uint8_t *, SCRadixTree *, uint8_t, void **);
+SCRadixNode *SCRadixFindKeyIPV6BestMatch(uint8_t *, SCRadixTree *, void **);
+
+void SCRadixPrintTree(SCRadixTree *);
+void SCRadixPrintNodeInfo(SCRadixNode *, int, void (*PrintData)(void*));
+
+void SCRadixRegisterTests(void);
+
+#endif /* __UTIL_RADIX_TREE_H__ */
diff --git a/framework/src/suricata/src/util-random.c b/framework/src/suricata/src/util-random.c
new file mode 100644
index 00000000..3cf5e075
--- /dev/null
+++ b/framework/src/suricata/src/util-random.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ * Utility function for seeding rand
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "threads.h"
+#include "util-debug.h"
+
+/**
+ * \brief create a seed number to pass to rand() , rand_r(), and similars
+ * \retval seed for rand()
+ */
+unsigned int RandomTimePreseed(void)
+{
+ /* preseed rand() */
+ time_t now = time ( 0 );
+ unsigned char *p = (unsigned char *)&now;
+ unsigned seed = 0;
+ size_t ind;
+
+ for ( ind = 0; ind < sizeof now; ind++ )
+ seed = seed * ( UCHAR_MAX + 2U ) + p[ind];
+
+ return seed;
+}
+
diff --git a/framework/src/suricata/src/util-random.h b/framework/src/suricata/src/util-random.h
new file mode 100644
index 00000000..9adddd01
--- /dev/null
+++ b/framework/src/suricata/src/util-random.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_RANDOM_H__
+#define __UTIL_RANDOM_H__
+
+unsigned int RandomTimePreseed(void);
+
+#endif /* __UTIL_RANDOM_H__ */
+
diff --git a/framework/src/suricata/src/util-reference-config.c b/framework/src/suricata/src/util-reference-config.c
new file mode 100644
index 00000000..299897ab
--- /dev/null
+++ b/framework/src/suricata/src/util-reference-config.c
@@ -0,0 +1,808 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "util-hash.h"
+
+#include "util-reference-config.h"
+#include "conf.h"
+#include "util-unittest.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-fmemopen.h"
+
+/* Regex to parse each line from reference.config file. The first substring
+ * is for the system name and the second for the url */
+/*-----------------------------------------------------------system-------------------url----*/
+#define SC_RCONF_REGEX "^\\s*config\\s+reference\\s*:\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s+(.+)\\s*$"
+
+/* Default path for the reference.conf file */
+#define SC_RCONF_DEFAULT_FILE_PATH CONFIG_DIR "/reference.config"
+
+static pcre *regex = NULL;
+static pcre_extra *regex_study = NULL;
+
+/* the hash functions */
+uint32_t SCRConfReferenceHashFunc(HashTable *ht, void *data, uint16_t datalen);
+char SCRConfReferenceHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2);
+void SCRConfReferenceHashFree(void *ch);
+
+/* used to get the reference.config file path */
+static char *SCRConfGetConfFilename(const DetectEngineCtx *de_ctx);
+
+void SCReferenceConfInit(void)
+{
+ const char *eb = NULL;
+ int eo;
+ int opts = 0;
+
+ regex = pcre_compile(SC_RCONF_REGEX, opts, &eb, &eo, NULL);
+ if (regex == NULL) {
+ SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s",
+ SC_RCONF_REGEX, eo, eb);
+ return;
+ }
+
+ regex_study = pcre_study(regex, 0, &eb);
+ if (eb != NULL) {
+ pcre_free(regex);
+ regex = NULL;
+ SCLogDebug("pcre study failed: %s", eb);
+ return;
+ }
+
+ return;
+}
+
+void SCReferenceConfDeinit(void)
+{
+ if (regex != NULL) {
+ pcre_free(regex);
+ regex = NULL;
+ }
+ if (regex_study != NULL) {
+ pcre_free(regex_study);
+ regex_study = NULL;
+ }
+}
+
+
+/**
+ * \brief Inits the context to be used by the Reference Config parsing API.
+ *
+ * This function initializes the hash table to be used by the Detection
+ * Engine Context to hold the data from reference.config file,
+ * obtains the file descriptor to parse the reference.config file, and
+ * inits the regex used to parse the lines from reference.config file.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static FILE *SCRConfInitContextAndLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ char *filename = NULL;
+
+ /* init the hash table to be used by the reference config references */
+ de_ctx->reference_conf_ht = HashTableInit(128, SCRConfReferenceHashFunc,
+ SCRConfReferenceHashCompareFunc,
+ SCRConfReferenceHashFree);
+ if (de_ctx->reference_conf_ht == NULL) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "Error initializing the hash "
+ "table");
+ goto error;
+ }
+
+ /* if it is not NULL, use the file descriptor. The hack so that we can
+ * avoid using a dummy reference file for testing purposes and
+ * instead use an input stream against a buffer containing the
+ * reference strings */
+ if (fd == NULL) {
+ filename = SCRConfGetConfFilename(de_ctx);
+ if ((fd = fopen(filename, "r")) == NULL) {
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests())
+ goto error; // silently fail
+#endif
+ SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename,
+ strerror(errno));
+ goto error;
+ }
+ }
+
+ return fd;
+
+ error:
+ if (de_ctx->reference_conf_ht != NULL) {
+ HashTableFree(de_ctx->reference_conf_ht);
+ de_ctx->reference_conf_ht = NULL;
+ }
+ if (fd != NULL) {
+ fclose(fd);
+ fd = NULL;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * \brief Returns the path for the Reference Config file. We check if we
+ * can retrieve the path from the yaml conf file. If it is not present,
+ * return the default path for the reference.config file which is
+ * "./reference.config".
+ *
+ * \retval log_filename Pointer to a string containing the path for the
+ * reference.config file.
+ */
+static char *SCRConfGetConfFilename(const DetectEngineCtx *de_ctx)
+{
+ char *path = NULL;
+ char config_value[256] = "";
+
+ if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
+ snprintf(config_value, sizeof(config_value),
+ "%s.reference-config-file", de_ctx->config_prefix);
+
+ /* try loading prefix setting, fall back to global if that
+ * fails. */
+ if (ConfGet(config_value, &path) != 1) {
+ if (ConfGet("reference-config-file", &path) != 1) {
+ return (char *)SC_RCONF_DEFAULT_FILE_PATH;
+ }
+ }
+ } else {
+ if (ConfGet("reference-config-file", &path) != 1) {
+ return (char *)SC_RCONF_DEFAULT_FILE_PATH;
+ }
+ }
+ return path;
+}
+
+/**
+ * \brief Releases local resources used by the Reference Config API.
+ */
+static void SCRConfDeInitLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ if (fd != NULL) {
+ fclose(fd);
+ fd = NULL;
+ }
+
+ return;
+}
+
+/**
+ * \brief Releases de_ctx resources related to Reference Config API.
+ */
+void SCRConfDeInitContext(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->reference_conf_ht != NULL)
+ HashTableFree(de_ctx->reference_conf_ht);
+
+ de_ctx->reference_conf_ht = NULL;
+
+ return;
+}
+
+/**
+ * \brief Converts a string to lowercase.
+ *
+ * \param str Pointer to the string to be converted.
+ */
+static char *SCRConfStringToLowercase(const char *str)
+{
+ char *new_str = NULL;
+ char *temp_str = NULL;
+
+ if ((new_str = SCStrdup(str)) == NULL) {
+ return NULL;
+ }
+
+ temp_str = new_str;
+ while (*temp_str != '\0') {
+ *temp_str = tolower((unsigned char)*temp_str);
+ temp_str++;
+ }
+
+ return new_str;
+}
+
+/**
+ * \brief Parses a line from the reference config file and adds it to Reference
+ * Config hash table DetectEngineCtx->reference_conf_ht.
+ *
+ * \param rawstr Pointer to the string to be parsed.
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+static int SCRConfAddReference(char *rawstr, DetectEngineCtx *de_ctx)
+{
+ char system[64];
+ char url[1024];
+
+ SCRConfReference *ref_new = NULL;
+ SCRConfReference *ref_lookup = NULL;
+
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
+ if (ret < 0) {
+ SCLogError(SC_ERR_REFERENCE_CONFIG, "Invalid Reference Config in "
+ "reference.config file");
+ goto error;
+ }
+
+ /* retrieve the reference system */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, system, sizeof(system));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring() failed");
+ goto error;
+ }
+
+ /* retrieve the reference url */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 2, url, sizeof(url));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring() failed");
+ goto error;
+ }
+
+ /* Create a new instance of the parsed Reference string */
+ ref_new = SCRConfAllocSCRConfReference(system, url);
+ if (ref_new == NULL)
+ goto error;
+
+ /* Check if the Reference is present in the HashTable. In case it's present
+ * ignore it, as it's a duplicate. If not present, add it to the table */
+ ref_lookup = HashTableLookup(de_ctx->reference_conf_ht, ref_new, 0);
+ if (ref_lookup == NULL) {
+ if (HashTableAdd(de_ctx->reference_conf_ht, ref_new, 0) < 0) {
+ SCLogDebug("HashTable Add failed");
+ }
+ } else {
+ SCLogDebug("Duplicate reference found inside reference.config");
+ SCRConfDeAllocSCRConfReference(ref_new);
+ }
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/**
+ * \brief Checks if a string is a comment or a blank line.
+ *
+ * Comments lines are lines of the following format -
+ * "# This is a comment string" or
+ * " # This is a comment string".
+ *
+ * \param line String that has to be checked.
+ *
+ * \retval 1 On the argument string being a comment or blank line.
+ * \retval 0 Otherwise.
+ */
+static int SCRConfIsLineBlankOrComment(char *line)
+{
+ while (*line != '\0') {
+ /* we have a comment */
+ if (*line == '#')
+ return 1;
+
+ /* this line is neither a comment line, nor a blank line */
+ if (!isspace((unsigned char)*line))
+ return 0;
+
+ line++;
+ }
+
+ /* we have a blank line */
+ return 1;
+}
+
+/**
+ * \brief Parses the Reference Config file and updates the
+ * DetectionEngineCtx->reference_conf_ht with the Reference information.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ */
+static void SCRConfParseFile(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ char line[1024];
+ uint8_t i = 1;
+
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if (SCRConfIsLineBlankOrComment(line))
+ continue;
+
+ SCRConfAddReference(line, de_ctx);
+ i++;
+ }
+
+#ifdef UNITTESTS
+ SCLogInfo("Added \"%d\" reference types from the reference.config file",
+ de_ctx->reference_conf_ht->count);
+#endif /* UNITTESTS */
+ return;
+}
+
+/**
+ * \brief Returns a new SCRConfReference instance. The reference string
+ * is converted into lowercase, before being assigned to the instance.
+ *
+ * \param system Pointer to the system.
+ * \param url Pointer to the reference url.
+ *
+ * \retval ref Pointer to the new instance of SCRConfReference.
+ */
+SCRConfReference *SCRConfAllocSCRConfReference(const char *system,
+ const char *url)
+{
+ SCRConfReference *ref = NULL;
+
+ if (system == NULL) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid arguments. system NULL");
+ return NULL;
+ }
+
+ if ((ref = SCMalloc(sizeof(SCRConfReference))) == NULL) {
+ return NULL;
+ }
+ memset(ref, 0, sizeof(SCRConfReference));
+
+ if ((ref->system = SCRConfStringToLowercase(system)) == NULL) {
+ SCFree(ref);
+ return NULL;
+ }
+
+ if (url != NULL && (ref->url = SCStrdup(url)) == NULL) {
+ SCFree(ref->system);
+ SCFree(ref);
+ return NULL;
+ }
+
+ return ref;
+}
+
+/**
+ * \brief Frees a SCRConfReference instance.
+ *
+ * \param Pointer to the SCRConfReference instance that has to be freed.
+ */
+void SCRConfDeAllocSCRConfReference(SCRConfReference *ref)
+{
+ if (ref != NULL) {
+ if (ref->system != NULL)
+ SCFree(ref->system);
+
+ if (ref->url != NULL)
+ SCFree(ref->url);
+
+ SCFree(ref);
+ }
+
+ return;
+}
+
+/**
+ * \brief Hashing function to be used to hash the Reference name. Would be
+ * supplied as an argument to the HashTableInit function for
+ * DetectEngineCtx->reference_conf_ht.
+ *
+ * \param ht Pointer to the HashTable.
+ * \param data Pointer to the data to be hashed. In this case, the data
+ * would be a pointer to a SCRConfReference instance.
+ * \param datalen Not used by this function.
+ */
+uint32_t SCRConfReferenceHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ SCRConfReference *ref = (SCRConfReference *)data;
+ uint32_t hash = 0;
+ int i = 0;
+
+ int len = strlen(ref->system);
+
+ for (i = 0; i < len; i++)
+ hash += tolower((unsigned char)ref->system[i]);
+
+ hash = hash % ht->array_size;
+
+ return hash;
+}
+
+/**
+ * \brief Used to compare two References that have been stored in the HashTable.
+ * This function is supplied as an argument to the HashTableInit function
+ * for DetectionEngineCtx->reference_conf_ct.
+ *
+ * \param data1 Pointer to the first SCRConfReference to be compared.
+ * \param len1 Not used by this function.
+ * \param data2 Pointer to the second SCRConfReference to be compared.
+ * \param len2 Not used by this function.
+ *
+ * \retval 1 On data1 and data2 being equal.
+ * \retval 0 On data1 and data2 not being equal.
+ */
+char SCRConfReferenceHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2)
+{
+ SCRConfReference *ref1 = (SCRConfReference *)data1;
+ SCRConfReference *ref2 = (SCRConfReference *)data2;
+ int len1 = 0;
+ int len2 = 0;
+
+ if (ref1 == NULL || ref2 == NULL)
+ return 0;
+
+ if (ref1->system == NULL || ref2->system == NULL)
+ return 0;
+
+ len1 = strlen(ref1->system);
+ len2 = strlen(ref2->system);
+
+ if (len1 == len2 && memcmp(ref1->system, ref2->system, len1) == 0) {
+ SCLogDebug("Match found inside Reference-Config hash function");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Used to free the Reference Config Hash Data that was stored in
+ * DetectEngineCtx->reference_conf_ht Hashtable.
+ *
+ * \param data Pointer to the data that has to be freed.
+ */
+void SCRConfReferenceHashFree(void *data)
+{
+ SCRConfDeAllocSCRConfReference(data);
+
+ return;
+}
+
+/**
+ * \brief Loads the Reference info from the reference.config file.
+ *
+ * The reference.config file contains references that can be used in
+ * Signatures. Each line of the file should have the following format -
+ * config reference: system_name, reference_url.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context that should be updated
+ * with reference information.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCRConfLoadReferenceConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ fd = SCRConfInitContextAndLocalResources(de_ctx, fd);
+ if (fd == NULL) {
+#ifdef UNITTESTS
+ if (RunmodeIsUnittests() && fd == NULL) {
+ return -1;
+ }
+#endif
+ SCLogError(SC_ERR_OPENING_FILE, "please check the \"reference-config-file\" "
+ "option in your suricata.yaml file");
+ return -1;
+ }
+
+ SCRConfParseFile(de_ctx, fd);
+ SCRConfDeInitLocalResources(de_ctx, fd);
+
+ return 0;
+}
+
+/**
+ * \brief Gets the refernce config from the corresponding hash table stored
+ * in the Detection Engine Context's reference conf ht, given the
+ * reference name.
+ *
+ * \param ct_name Pointer to the reference name that has to be looked up.
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval lookup_rconf_info Pointer to the SCRConfReference instance from
+ * the hash table on success; NULL on failure.
+ */
+SCRConfReference *SCRConfGetReference(const char *rconf_name,
+ DetectEngineCtx *de_ctx)
+{
+ SCRConfReference *ref_conf = SCRConfAllocSCRConfReference(rconf_name, NULL);
+ if (ref_conf == NULL)
+ return NULL;
+ SCRConfReference *lookup_ref_conf = HashTableLookup(de_ctx->reference_conf_ht,
+ ref_conf, 0);
+
+ SCRConfDeAllocSCRConfReference(ref_conf);
+ return lookup_ref_conf;
+}
+
+/*----------------------------------Unittests---------------------------------*/
+
+
+#ifdef UNITTESTS
+
+/**
+ * \brief Creates a dummy reference config, with all valid references, for
+ * testing purposes.
+ */
+FILE *SCRConfGenerateValidDummyReferenceConfigFD01(void)
+{
+ const char *buffer =
+ "config reference: one http://www.one.com\n"
+ "config reference: two http://www.two.com\n"
+ "config reference: three http://www.three.com\n"
+ "config reference: one http://www.one.com\n"
+ "config reference: three http://www.three.com\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Reference Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy reference config, with some valid references and a
+ * couple of invalid references, for testing purposes.
+ */
+FILE *SCRConfGenerateInValidDummyReferenceConfigFD02(void)
+{
+ const char *buffer =
+ "config reference: one http://www.one.com\n"
+ "config_ reference: two http://www.two.com\n"
+ "config reference_: three http://www.three.com\n"
+ "config reference: four\n"
+ "config reference five http://www.five.com\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Reference Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy reference config, with all invalid references, for
+ * testing purposes.
+ */
+FILE *SCRConfGenerateInValidDummyReferenceConfigFD03(void)
+{
+ const char *buffer =
+ "config reference one http://www.one.com\n"
+ "config_ reference: two http://www.two.com\n"
+ "config reference_: three http://www.three.com\n"
+ "config reference: four\n";
+
+ FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Reference Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check that the reference file is loaded and the detection engine
+ * content reference_conf_ht loaded with the reference data.
+ */
+int SCRConfTest01(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCRConfGenerateValidDummyReferenceConfigFD01();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 3);
+ if (result == 0)
+ printf("FAILED: de_ctx->reference_conf_ht->count %u: ", de_ctx->reference_conf_ht->count);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check that invalid references present in the reference.config file
+ * aren't loaded.
+ */
+int SCRConfTest02(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCRConfGenerateInValidDummyReferenceConfigFD03();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 0);
+
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check that only valid references are loaded into the hash table from
+ * the reference.config file.
+ */
+int SCRConfTest03(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 0;
+
+ if (de_ctx == NULL)
+ return result;
+
+ FILE *fd = SCRConfGenerateInValidDummyReferenceConfigFD02();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 1);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the reference info from the reference.config file have
+ * been loaded into the hash table.
+ */
+int SCRConfTest04(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCRConfGenerateValidDummyReferenceConfigFD01();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 3);
+
+ result &= (SCRConfGetReference("one", de_ctx) != NULL);
+ result &= (SCRConfGetReference("two", de_ctx) != NULL);
+ result &= (SCRConfGetReference("three", de_ctx) != NULL);
+ result &= (SCRConfGetReference("four", de_ctx) == NULL);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the reference info from the invalid reference.config file
+ * have not been loaded into the hash table, and cross verify to check
+ * that the hash table contains no reference data.
+ */
+int SCRConfTest05(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCRConfGenerateInValidDummyReferenceConfigFD03();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 0);
+
+ result &= (SCRConfGetReference("one", de_ctx) == NULL);
+ result &= (SCRConfGetReference("two", de_ctx) == NULL);
+ result &= (SCRConfGetReference("three", de_ctx) == NULL);
+ result &= (SCRConfGetReference("four", de_ctx) == NULL);
+ result &= (SCRConfGetReference("five", de_ctx) == NULL);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the reference info from the reference.config file have
+ * been loaded into the hash table.
+ */
+int SCRConfTest06(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ int result = 1;
+
+ if (de_ctx == NULL)
+ return 0;
+
+ FILE *fd = SCRConfGenerateInValidDummyReferenceConfigFD02();
+ SCRConfLoadReferenceConfigFile(de_ctx, fd);
+
+ if (de_ctx->reference_conf_ht == NULL)
+ goto end;
+
+ result = (de_ctx->reference_conf_ht->count == 1);
+
+ result &= (SCRConfGetReference("one", de_ctx) != NULL);
+ result &= (SCRConfGetReference("two", de_ctx) == NULL);
+ result &= (SCRConfGetReference("three", de_ctx) == NULL);
+ result &= (SCRConfGetReference("four", de_ctx) == NULL);
+ result &= (SCRConfGetReference("five", de_ctx) == NULL);
+
+ end:
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for Reference Config API.
+ */
+void SCRConfRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+ UtRegisterTest("SCRConfTest01", SCRConfTest01, 1);
+ UtRegisterTest("SCRConfTest02", SCRConfTest02, 1);
+ UtRegisterTest("SCRConfTest03", SCRConfTest03, 1);
+ UtRegisterTest("SCRConfTest04", SCRConfTest04, 1);
+ UtRegisterTest("SCRConfTest05", SCRConfTest05, 1);
+ UtRegisterTest("SCRConfTest06", SCRConfTest06, 1);
+#endif /* UNITTESTS */
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-reference-config.h b/framework/src/suricata/src/util-reference-config.h
new file mode 100644
index 00000000..f7fe4cc1
--- /dev/null
+++ b/framework/src/suricata/src/util-reference-config.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_REFERENCE_CONFIG_H__
+#define __UTIL_REFERENCE_CONFIG_H__
+
+/**
+ * \brief Holds a reference from the file - reference.config.
+ */
+typedef struct SCRConfReference_ {
+ /* The system name. This is the primary key for a reference. */
+ char *system;
+ /* The url for the above reference */
+ char *url;
+} SCRConfReference;
+
+SCRConfReference *SCRConfAllocSCRConfReference(const char *, const char *);
+void SCRConfDeAllocSCRConfReference(SCRConfReference *);
+int SCRConfLoadReferenceConfigFile(DetectEngineCtx *, FILE *);
+void SCRConfDeInitContext(DetectEngineCtx *);
+SCRConfReference *SCRConfGetReference(const char *,
+ DetectEngineCtx *);
+void SCRConfRegisterTests(void);
+
+/* these below functions are only used by unittests */
+FILE *SCRConfGenerateValidDummyReferenceConfigFD01(void);
+FILE *SCRConfGenerateInValidDummyReferenceConfigFD02(void);
+FILE *SCRConfGenerateInValidDummyReferenceConfigFD03(void);
+
+void SCReferenceConfInit(void);
+void SCReferenceConfDeinit(void);
+
+#endif /* __UTIL_REFERENCE_CONFIG_H__ */
diff --git a/framework/src/suricata/src/util-ringbuffer.c b/framework/src/suricata/src/util-ringbuffer.c
new file mode 100644
index 00000000..4566bf9f
--- /dev/null
+++ b/framework/src/suricata/src/util-ringbuffer.c
@@ -0,0 +1,1088 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Ringbuffer implementation that is lockless for the most part IF atomic
+ * operations are available.
+ *
+ * Two sizes are implemented currently: 256 and 65536. Those sizes are chosen
+ * for simplicity when working with the read and write indexes. Both can just
+ * wrap around.
+ *
+ * Implemented are:
+ * Single reader, single writer (lockless)
+ * Single reader, multi writer (partly locked)
+ * Multi reader, single writer (lockless)
+ * Multi reader, multi writer (partly locked)
+ */
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-ringbuffer.h"
+#include "util-atomic.h"
+#include "util-unittest.h"
+
+#define USLEEP_TIME 5
+
+/** \brief wait function for condition where ringbuffer is either
+ * full or empty.
+ *
+ * \param rb ringbuffer
+ *
+ * Based on RINGBUFFER_MUTEX_WAIT define, we either sleep and spin
+ * or use thread condition to wait.
+ */
+static inline void RingBuffer8DoWait(RingBuffer8 *rb)
+{
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexLock(&rb->wait_mutex);
+ SCCondWait(&rb->wait_cond, &rb->wait_mutex);
+ SCMutexUnlock(&rb->wait_mutex);
+#else
+ usleep(USLEEP_TIME);
+#endif
+}
+
+/** \brief wait function for condition where ringbuffer is either
+ * full or empty.
+ *
+ * \param rb ringbuffer
+ *
+ * Based on RINGBUFFER_MUTEX_WAIT define, we either sleep and spin
+ * or use thread condition to wait.
+ */
+static inline void RingBufferDoWait(RingBuffer16 *rb)
+{
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexLock(&rb->wait_mutex);
+ SCCondWait(&rb->wait_cond, &rb->wait_mutex);
+ SCMutexUnlock(&rb->wait_mutex);
+#else
+ usleep(USLEEP_TIME);
+#endif
+}
+
+/** \brief wait function for condition where ringbuffer is either
+ * full or empty.
+ *
+ * \param rb ringbuffer
+ *
+ * Based on RINGBUFFER_MUTEX_WAIT define, we either sleep and spin
+ * or use thread condition to wait.
+ */
+void RingBufferWait(RingBuffer16 *rb)
+{
+ RingBufferDoWait(rb);
+}
+
+/** \brief tell the ringbuffer to shut down
+ *
+ * \param rb ringbuffer
+ */
+void RingBuffer8Shutdown(RingBuffer8 *rb)
+{
+ rb->shutdown = 1;
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+}
+
+/** \brief check the ringbuffer is empty (no data in it)
+ *
+ * \param rb ringbuffer
+ *
+ * \retval 1 empty
+ * \retval 0 not empty
+ */
+int RingBuffer8IsEmpty(RingBuffer8 *rb)
+{
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \brief check the ringbuffer is full (no more data will fit)
+ *
+ * \param rb ringbuffer
+ *
+ * \retval 1 empty
+ * \retval 0 not empty
+ */
+int RingBuffer8IsFull(RingBuffer8 *rb)
+{
+ if ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \brief tell the ringbuffer to shut down
+ *
+ * \param rb ringbuffer
+ */
+void RingBufferShutdown(RingBuffer16 *rb)
+{
+ rb->shutdown = 1;
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+}
+
+/** \brief get number of items in the ringbuffer */
+uint16_t RingBufferSize(RingBuffer16 *rb)
+{
+ SCEnter();
+ uint16_t size = (uint16_t)(SC_ATOMIC_GET(rb->write) - SC_ATOMIC_GET(rb->read));
+ SCReturnUInt(size);
+}
+
+/** \brief check the ringbuffer is empty (no data in it)
+ *
+ * \param rb ringbuffer
+ *
+ * \retval 1 empty
+ * \retval 0 not empty
+ */
+int RingBufferIsEmpty(RingBuffer16 *rb)
+{
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \brief check the ringbuffer is full (no more data will fit)
+ *
+ * \param rb ringbuffer
+ *
+ * \retval 1 empty
+ * \retval 0 not empty
+ */
+int RingBufferIsFull(RingBuffer16 *rb)
+{
+ if ((unsigned short)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Single Reader, Single Writer, 8 bits */
+
+void *RingBufferSrSw8Get(RingBuffer8 *rb)
+{
+ void *ptr = NULL;
+
+ /* buffer is empty, wait... */
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ ptr = rb->array[SC_ATOMIC_GET(rb->read)];
+ (void) SC_ATOMIC_ADD(rb->read, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+int RingBufferSrSw8Put(RingBuffer8 *rb, void *ptr)
+{
+ /* buffer is full, wait... */
+ while ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+/* Single Reader, Multi Writer, 8 bites */
+
+void *RingBufferSrMw8Get(RingBuffer8 *rb)
+{
+ void *ptr = NULL;
+
+ /* buffer is empty, wait... */
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ ptr = rb->array[SC_ATOMIC_GET(rb->read)];
+ (void) SC_ATOMIC_ADD(rb->read, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief put a ptr in the RingBuffer.
+ *
+ * As we support multiple writers we need to protect 2 things:
+ * 1. writing the ptr to the array
+ * 2. incrementing the rb->write idx
+ *
+ * We can't do both at the same time in one atomic operation, so
+ * we need to (spin) lock it. We do increment rb->write atomically
+ * after that, so that we don't need to use the lock in our *Get
+ * function.
+ *
+ * \param rb the ringbuffer
+ * \param ptr ptr to store
+ *
+ * \retval 0 ok
+ * \retval -1 wait loop interrupted because of engine flags
+ */
+int RingBufferSrMw8Put(RingBuffer8 *rb, void *ptr)
+{
+ SCLogDebug("ptr %p", ptr);
+
+ /* buffer is full, wait... */
+retry:
+ while ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ /* get our lock */
+ SCSpinLock(&rb->spin);
+ /* if while we got our lock the buffer changed, we need to retry */
+ if ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ SCSpinUnlock(&rb->spin);
+ goto retry;
+ }
+
+ SCLogDebug("rb->write %u, ptr %p", SC_ATOMIC_GET(rb->write), ptr);
+
+ /* update the ring buffer */
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+ SCSpinUnlock(&rb->spin);
+ SCLogDebug("ptr %p, done", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+/* Multi Reader, Single Writer, 8 bits */
+
+/**
+ * \brief get the next ptr from the ring buffer
+ *
+ * Because we allow for multiple readers we take great care in making sure
+ * that the threads don't interfere with one another.
+ *
+ */
+void *RingBufferMrSw8Get(RingBuffer8 *rb)
+{
+ void *ptr;
+ /** local pointer for data races. If SCAtomicCompareAndSwap (CAS)
+ * fails we increase our local array idx to try the next array member
+ * until we succeed. Or when the buffer is empty again we jump back
+ * to the waiting loop. */
+ unsigned char readp;
+
+ /* buffer is empty, wait... */
+retry:
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ /* atomically update rb->read */
+ readp = SC_ATOMIC_GET(rb->read) - 1;
+ do {
+ /* with multiple readers we can get in the situation that we exitted
+ * from the wait loop but the rb is empty again once we get here. */
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read))
+ goto retry;
+
+ readp++;
+ ptr = rb->array[readp];
+ } while (!(SC_ATOMIC_CAS(&rb->read, readp, (readp + 1))));
+
+ SCLogDebug("ptr %p", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief put a ptr in the RingBuffer
+ */
+int RingBufferMrSw8Put(RingBuffer8 *rb, void *ptr)
+{
+ SCLogDebug("ptr %p", ptr);
+
+ /* buffer is full, wait... */
+ while ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+
+/* Multi Reader, Single Writer */
+
+/**
+ * \brief get the next ptr from the ring buffer
+ *
+ * Because we allow for multiple readers we take great care in making sure
+ * that the threads don't interfere with one another.
+ *
+ */
+void *RingBufferMrSwGet(RingBuffer16 *rb)
+{
+ void *ptr;
+ /** local pointer for data races. If SCAtomicCompareAndSwap (CAS)
+ * fails we increase our local array idx to try the next array member
+ * until we succeed. Or when the buffer is empty again we jump back
+ * to the waiting loop. */
+ unsigned short readp;
+
+ /* buffer is empty, wait... */
+retry:
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBufferDoWait(rb);
+ }
+
+ /* atomically update rb->read */
+ readp = SC_ATOMIC_GET(rb->read) - 1;
+ do {
+ /* with multiple readers we can get in the situation that we exitted
+ * from the wait loop but the rb is empty again once we get here. */
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read))
+ goto retry;
+
+ readp++;
+ ptr = rb->array[readp];
+ } while (!(SC_ATOMIC_CAS(&rb->read, readp, (readp + 1))));
+
+ SCLogDebug("ptr %p", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief put a ptr in the RingBuffer
+ */
+int RingBufferMrSwPut(RingBuffer16 *rb, void *ptr)
+{
+ SCLogDebug("ptr %p", ptr);
+
+ /* buffer is full, wait... */
+ while ((unsigned short)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBufferDoWait(rb);
+ }
+
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+
+/* Single Reader, Single Writer */
+
+void *RingBufferSrSwGet(RingBuffer16 *rb)
+{
+ void *ptr = NULL;
+
+ /* buffer is empty, wait... */
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBufferDoWait(rb);
+ }
+
+ ptr = rb->array[SC_ATOMIC_GET(rb->read)];
+ (void) SC_ATOMIC_ADD(rb->read, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+int RingBufferSrSwPut(RingBuffer16 *rb, void *ptr)
+{
+ /* buffer is full, wait... */
+ while ((unsigned short)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBufferDoWait(rb);
+ }
+
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+/* Multi Reader, Multi Writer, 8 bits */
+
+RingBuffer8 *RingBuffer8Init(void)
+{
+ RingBuffer8 *rb = SCMalloc(sizeof(RingBuffer8));
+ if (unlikely(rb == NULL)) {
+ return NULL;
+ }
+
+ memset(rb, 0x00, sizeof(RingBuffer8));
+
+ SC_ATOMIC_INIT(rb->write);
+ SC_ATOMIC_INIT(rb->read);
+
+ SCSpinInit(&rb->spin, 0);
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexInit(&rb->wait_mutex, NULL);
+ SCCondInit(&rb->wait_cond, NULL);
+#endif
+ return rb;
+}
+
+void RingBuffer8Destroy(RingBuffer8 *rb)
+{
+ if (rb != NULL) {
+ SC_ATOMIC_DESTROY(rb->write);
+ SC_ATOMIC_DESTROY(rb->read);
+
+ SCSpinDestroy(&rb->spin);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexDestroy(&rb->wait_mutex);
+ SCCondDestroy(&rb->wait_cond);
+#endif
+ SCFree(rb);
+ }
+}
+
+/**
+ * \brief get the next ptr from the ring buffer
+ *
+ * Because we allow for multiple readers we take great care in making sure
+ * that the threads don't interfere with one another.
+ *
+ */
+void *RingBufferMrMw8Get(RingBuffer8 *rb)
+{
+ void *ptr;
+ /** local pointer for data races. If SCAtomicCompareAndSwap (CAS)
+ * fails we increase our local array idx to try the next array member
+ * until we succeed. Or when the buffer is empty again we jump back
+ * to the waiting loop. */
+ unsigned char readp;
+
+ /* buffer is empty, wait... */
+retry:
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ /* atomically update rb->read */
+ readp = SC_ATOMIC_GET(rb->read) - 1;
+ do {
+ /* with multiple readers we can get in the situation that we exitted
+ * from the wait loop but the rb is empty again once we get here. */
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read))
+ goto retry;
+
+ readp++;
+ ptr = rb->array[readp];
+ } while (!(SC_ATOMIC_CAS(&rb->read, readp, (readp + 1))));
+
+ SCLogDebug("ptr %p", ptr);
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief put a ptr in the RingBuffer.
+ *
+ * As we support multiple writers we need to protect 2 things:
+ * 1. writing the ptr to the array
+ * 2. incrementing the rb->write idx
+ *
+ * We can't do both at the same time in one atomic operation, so
+ * we need to (spin) lock it. We do increment rb->write atomically
+ * after that, so that we don't need to use the lock in our *Get
+ * function.
+ *
+ * \param rb the ringbuffer
+ * \param ptr ptr to store
+ *
+ * \retval 0 ok
+ * \retval -1 wait loop interrupted because of engine flags
+ */
+int RingBufferMrMw8Put(RingBuffer8 *rb, void *ptr)
+{
+ SCLogDebug("ptr %p", ptr);
+
+ /* buffer is full, wait... */
+retry:
+ while ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBuffer8DoWait(rb);
+ }
+
+ /* get our lock */
+ SCSpinLock(&rb->spin);
+ /* if while we got our lock the buffer changed, we need to retry */
+ if ((unsigned char)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ SCSpinUnlock(&rb->spin);
+ goto retry;
+ }
+
+ SCLogDebug("rb->write %u, ptr %p", SC_ATOMIC_GET(rb->write), ptr);
+
+ /* update the ring buffer */
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+ SCSpinUnlock(&rb->spin);
+ SCLogDebug("ptr %p, done", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+/* Multi Reader, Multi Writer, 16 bits */
+
+RingBuffer16 *RingBufferInit(void)
+{
+ RingBuffer16 *rb = SCMalloc(sizeof(RingBuffer16));
+ if (unlikely(rb == NULL)) {
+ return NULL;
+ }
+
+ memset(rb, 0x00, sizeof(RingBuffer16));
+
+ SC_ATOMIC_INIT(rb->write);
+ SC_ATOMIC_INIT(rb->read);
+
+ SCSpinInit(&rb->spin, 0);
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexInit(&rb->wait_mutex, NULL);
+ SCCondInit(&rb->wait_cond, NULL);
+#endif
+ return rb;
+}
+
+void RingBufferDestroy(RingBuffer16 *rb)
+{
+ if (rb != NULL) {
+ SC_ATOMIC_DESTROY(rb->write);
+ SC_ATOMIC_DESTROY(rb->read);
+
+ SCSpinDestroy(&rb->spin);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCMutexDestroy(&rb->wait_mutex);
+ SCCondDestroy(&rb->wait_cond);
+#endif
+
+ SCFree(rb);
+ }
+}
+
+/**
+ * \brief get the next ptr from the ring buffer
+ *
+ * Because we allow for multiple readers we take great care in making sure
+ * that the threads don't interfere with one another.
+ *
+ */
+void *RingBufferMrMwGet(RingBuffer16 *rb)
+{
+ void *ptr;
+ /** local pointer for data races. If SCAtomicCompareAndSwap (CAS)
+ * fails we increase our local array idx to try the next array member
+ * until we succeed. Or when the buffer is empty again we jump back
+ * to the waiting loop. */
+ unsigned short readp;
+
+ /* buffer is empty, wait... */
+retry:
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return NULL;
+
+ RingBufferDoWait(rb);
+ }
+
+ /* atomically update rb->read */
+ readp = SC_ATOMIC_GET(rb->read) - 1;
+ do {
+ /* with multiple readers we can get in the situation that we exitted
+ * from the wait loop but the rb is empty again once we get here. */
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read))
+ goto retry;
+
+ readp++;
+ ptr = rb->array[readp];
+ } while (!(SC_ATOMIC_CAS(&rb->read, readp, (readp + 1))));
+
+ SCLogDebug("ptr %p", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief get the next ptr from the ring buffer
+ *
+ * Because we allow for multiple readers we take great care in making sure
+ * that the threads don't interfere with one another.
+ *
+ * This version does NOT enter a wait if the buffer is empty loop.
+ *
+ * \retval ptr pointer to the data, or NULL if buffer is empty
+ */
+void *RingBufferMrMwGetNoWait(RingBuffer16 *rb)
+{
+ void *ptr;
+ /** local pointer for data races. If SCAtomicCompareAndSwap (CAS)
+ * fails we increase our local array idx to try the next array member
+ * until we succeed. Or when the buffer is empty again we jump back
+ * to the waiting loop. */
+ unsigned short readp;
+
+ /* buffer is empty, wait... */
+retry:
+ while (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read)) {
+ /* break if buffer is empty */
+ return NULL;
+ }
+
+ /* atomically update rb->read */
+ readp = SC_ATOMIC_GET(rb->read) - 1;
+ do {
+ /* with multiple readers we can get in the situation that we exitted
+ * from the wait loop but the rb is empty again once we get here. */
+ if (SC_ATOMIC_GET(rb->write) == SC_ATOMIC_GET(rb->read))
+ goto retry;
+
+ readp++;
+ ptr = rb->array[readp];
+ } while (!(SC_ATOMIC_CAS(&rb->read, readp, (readp + 1))));
+
+ SCLogDebug("ptr %p", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return ptr;
+}
+
+/**
+ * \brief put a ptr in the RingBuffer.
+ *
+ * As we support multiple writers we need to protect 2 things:
+ * 1. writing the ptr to the array
+ * 2. incrementing the rb->write idx
+ *
+ * We can't do both at the same time in one atomic operation, so
+ * we need to (spin) lock it. We do increment rb->write atomically
+ * after that, so that we don't need to use the lock in our *Get
+ * function.
+ *
+ * \param rb the ringbuffer
+ * \param ptr ptr to store
+ *
+ * \retval 0 ok
+ * \retval -1 wait loop interrupted because of engine flags
+ */
+int RingBufferMrMwPut(RingBuffer16 *rb, void *ptr)
+{
+ SCLogDebug("ptr %p", ptr);
+
+ /* buffer is full, wait... */
+retry:
+ while ((unsigned short)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ /* break out if the engine wants to shutdown */
+ if (rb->shutdown != 0)
+ return -1;
+
+ RingBufferDoWait(rb);
+ }
+
+ /* get our lock */
+ SCSpinLock(&rb->spin);
+ /* if while we got our lock the buffer changed, we need to retry */
+ if ((unsigned short)(SC_ATOMIC_GET(rb->write) + 1) == SC_ATOMIC_GET(rb->read)) {
+ SCSpinUnlock(&rb->spin);
+ goto retry;
+ }
+
+ SCLogDebug("rb->write %u, ptr %p", SC_ATOMIC_GET(rb->write), ptr);
+
+ /* update the ring buffer */
+ rb->array[SC_ATOMIC_GET(rb->write)] = ptr;
+ (void) SC_ATOMIC_ADD(rb->write, 1);
+ SCSpinUnlock(&rb->spin);
+ SCLogDebug("ptr %p, done", ptr);
+
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondSignal(&rb->wait_cond);
+#endif
+ return 0;
+}
+
+#ifdef UNITTESTS
+static int RingBuffer8SrSwInit01 (void)
+{
+ int result = 0;
+
+ RingBuffer8 *rb = NULL;
+
+ rb = RingBuffer8Init();
+ if (rb == NULL) {
+ printf("rb == NULL: ");
+ goto end;
+ }
+
+ int r = SCSpinLock(&rb->spin);
+ if (r != 0) {
+ printf("r = %d, expected %d: ", r, 0);
+ goto end;
+ }
+ SCSpinUnlock(&rb->spin);
+
+ if (SC_ATOMIC_GET(rb->read) != 0) {
+ printf("read %u, expected 0: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != 0) {
+ printf("write %u, expected 0: ", SC_ATOMIC_GET(rb->write));
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (rb != NULL) {
+ RingBuffer8Destroy(rb);
+ }
+ return result;
+}
+
+static int RingBuffer8SrSwPut01 (void)
+{
+ int result = 0;
+
+ RingBuffer8 *rb = NULL;
+
+ rb = RingBuffer8Init();
+ if (rb == NULL) {
+ printf("rb == NULL: ");
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->read) != 0) {
+ printf("read %u, expected 0: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != 0) {
+ printf("write %u, expected 0: ", SC_ATOMIC_GET(rb->write));
+ goto end;
+ }
+
+ void *ptr = &result;
+
+ RingBufferSrSw8Put(rb, ptr);
+
+ if (SC_ATOMIC_GET(rb->read) != 0) {
+ printf("read %u, expected 0: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != 1) {
+ printf("write %u, expected 1: ", SC_ATOMIC_GET(rb->write));
+ goto end;
+ }
+
+ if (rb->array[0] != ptr) {
+ printf("ptr is %p, expected %p: ", rb->array[0], ptr);
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (rb != NULL) {
+ RingBuffer8Destroy(rb);
+ }
+ return result;
+}
+
+static int RingBuffer8SrSwPut02 (void)
+{
+ int result = 0;
+ RingBuffer8 *rb = NULL;
+
+ int array[255];
+ int cnt = 0;
+ for (cnt = 0; cnt < 255; cnt++) {
+ array[cnt] = cnt;
+ }
+
+ rb = RingBuffer8Init();
+ if (rb == NULL) {
+ printf("rb == NULL: ");
+ goto end;
+ }
+
+ for (cnt = 0; cnt < 255; cnt++) {
+ RingBufferSrSw8Put(rb, (void *)&array[cnt]);
+
+ if (SC_ATOMIC_GET(rb->read) != 0) {
+ printf("read %u, expected 0: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != (unsigned char)(cnt+1)) {
+ printf("write %u, expected %u: ", SC_ATOMIC_GET(rb->write), (unsigned char)(cnt+1));
+ goto end;
+ }
+
+ if (rb->array[cnt] != (void *)&array[cnt]) {
+ printf("ptr is %p, expected %p: ", rb->array[cnt], (void *)&array[cnt]);
+ goto end;
+ }
+ }
+
+ if (!(RingBuffer8IsFull(rb))) {
+ printf("ringbuffer should be full, isn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (rb != NULL) {
+ RingBuffer8Destroy(rb);
+ }
+ return result;
+}
+
+static int RingBuffer8SrSwGet01 (void)
+{
+ int result = 0;
+
+ RingBuffer8 *rb = NULL;
+
+ rb = RingBuffer8Init();
+ if (rb == NULL) {
+ printf("rb == NULL: ");
+ goto end;
+ }
+
+ void *ptr = &result;
+
+ RingBufferSrSw8Put(rb, ptr);
+ void *ptr2 = RingBufferSrSw8Get(rb);
+
+ if (ptr != ptr2) {
+ printf("ptr %p != ptr2 %p: ", ptr, ptr2);
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->read) != 1) {
+ printf("read %u, expected 1: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != 1) {
+ printf("write %u, expected 1: ", SC_ATOMIC_GET(rb->write));
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (rb != NULL) {
+ RingBuffer8Destroy(rb);
+ }
+ return result;
+}
+
+static int RingBuffer8SrSwGet02 (void)
+{
+ int result = 0;
+ RingBuffer8 *rb = NULL;
+
+ int array[255];
+ int cnt = 0;
+ for (cnt = 0; cnt < 255; cnt++) {
+ array[cnt] = cnt;
+ }
+
+ rb = RingBuffer8Init();
+ if (rb == NULL) {
+ printf("rb == NULL: ");
+ goto end;
+ }
+
+ for (cnt = 0; cnt < 255; cnt++) {
+ RingBufferSrSw8Put(rb, (void *)&array[cnt]);
+
+ if (SC_ATOMIC_GET(rb->read) != 0) {
+ printf("read %u, expected 0: ", SC_ATOMIC_GET(rb->read));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != (unsigned char)(cnt+1)) {
+ printf("write %u, expected %u: ", SC_ATOMIC_GET(rb->write), (unsigned char)(cnt+1));
+ goto end;
+ }
+
+ if (rb->array[cnt] != (void *)&array[cnt]) {
+ printf("ptr is %p, expected %p: ", rb->array[cnt], (void *)&array[cnt]);
+ goto end;
+ }
+ }
+
+ if (!(RingBuffer8IsFull(rb))) {
+ printf("ringbuffer should be full, isn't: ");
+ goto end;
+ }
+
+ for (cnt = 0; cnt < 255; cnt++) {
+ void *ptr = RingBufferSrSw8Get(rb);
+
+ if (SC_ATOMIC_GET(rb->read) != (unsigned char)(cnt+1)) {
+ printf("read %u, expected %u: ", SC_ATOMIC_GET(rb->read), (unsigned char)(cnt+1));
+ goto end;
+ }
+
+ if (SC_ATOMIC_GET(rb->write) != 255) {
+ printf("write %u, expected %u: ", SC_ATOMIC_GET(rb->write), 255);
+ goto end;
+ }
+
+ if (ptr != (void *)&array[cnt]) {
+ printf("ptr is %p, expected %p: ", ptr, (void *)&array[cnt]);
+ goto end;
+ }
+ }
+
+ if (!(RingBuffer8IsEmpty(rb))) {
+ printf("ringbuffer should be empty, isn't: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (rb != NULL) {
+ RingBuffer8Destroy(rb);
+ }
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void DetectRingBufferRegisterTests(void)
+{
+#ifdef UNITTESTS /* UNITTESTS */
+ UtRegisterTest("RingBuffer8SrSwInit01", RingBuffer8SrSwInit01, 1);
+ UtRegisterTest("RingBuffer8SrSwPut01", RingBuffer8SrSwPut01, 1);
+ UtRegisterTest("RingBuffer8SrSwPut02", RingBuffer8SrSwPut02, 1);
+ UtRegisterTest("RingBuffer8SrSwGet01", RingBuffer8SrSwGet01, 1);
+ UtRegisterTest("RingBuffer8SrSwGet02", RingBuffer8SrSwGet02, 1);
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-ringbuffer.h b/framework/src/suricata/src/util-ringbuffer.h
new file mode 100644
index 00000000..c9272330
--- /dev/null
+++ b/framework/src/suricata/src/util-ringbuffer.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * See the .c file for a full explanation.
+ */
+
+#ifndef __UTIL_RINGBUFFER_H__
+
+#include "util-atomic.h"
+#include "threads.h"
+
+/** When the ringbuffer is full we have two options, either we spin & sleep
+ * or we use a pthread condition to wait.
+ *
+ * \warning this approach isn't working due to a race condition between the
+ * time it takes for a thread to enter the condwait and the
+ * signalling. I've obverved the following case: T1 sees that the
+ * ringbuffer is empty, so it decides to start the wait condition.
+ * While it is acquiring the lock and entering the wait, T0 puts a
+ * number of items in the buffer. For each of these it signals T1.
+ * However, as that thread isn't in the "wait" mode yet, the signals
+ * are lost. T0 now is done as well and enters it's own wait
+ * condition. T1 completes it's "wait" initialization. It waits for
+ * signals, but T0 won't be able to send them as it's waiting itself.
+ */
+//#define RINGBUFFER_MUTEX_WAIT
+
+/** \brief ring buffer api
+ *
+ * Ring buffer api for a single writer and a single reader. It uses a
+ * read and write pointer. Only the read ptr needs atomic updating.
+ */
+
+#define RING_BUFFER_8_SIZE 256
+typedef struct RingBuffer8_ {
+ SC_ATOMIC_DECLARE(unsigned char, write); /**< idx where we put data */
+ SC_ATOMIC_DECLARE(unsigned char, read); /**< idx where we read data */
+ uint8_t shutdown;
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondT wait_cond;
+ SCMutex wait_mutex;
+#endif /* RINGBUFFER_MUTEX_WAIT */
+ SCSpinlock spin; /**< lock protecting writes for multi writer mode*/
+ void *array[RING_BUFFER_8_SIZE];
+} RingBuffer8;
+
+#define RING_BUFFER_16_SIZE 65536
+typedef struct RingBuffer16_ {
+ SC_ATOMIC_DECLARE(unsigned short, write); /**< idx where we put data */
+ SC_ATOMIC_DECLARE(unsigned short, read); /**< idx where we read data */
+ uint8_t shutdown;
+#ifdef RINGBUFFER_MUTEX_WAIT
+ SCCondT wait_cond;
+ SCMutex wait_mutex;
+#endif /* RINGBUFFER_MUTEX_WAIT */
+ SCSpinlock spin; /**< lock protecting writes for multi writer mode*/
+ void *array[RING_BUFFER_16_SIZE];
+} RingBuffer16;
+
+RingBuffer8 *RingBuffer8Init(void);
+void RingBuffer8Destroy(RingBuffer8 *);
+RingBuffer16 *RingBufferInit(void);
+void RingBufferDestroy(RingBuffer16 *);
+
+int RingBufferIsEmpty(RingBuffer16 *);
+int RingBufferIsFull(RingBuffer16 *);
+uint16_t RingBufferSize(RingBuffer16 *);
+
+void RingBuffer8Shutdown(RingBuffer8 *);
+void RingBufferShutdown(RingBuffer16 *);
+
+void RingBufferWait(RingBuffer16 *rb);
+
+/** Single Reader, Single Writer ring buffer, fixed at
+ * 256 items so we can use unsigned char's that just
+ * wrap around */
+void *RingBufferSrSw8Get(RingBuffer8 *);
+int RingBufferSrSw8Put(RingBuffer8 *, void *);
+
+/** Multiple Reader, Single Writer ring buffer, fixed at
+ * 256 items so we can use unsigned char's that just
+ * wrap around */
+void *RingBufferMrSw8Get(RingBuffer8 *);
+int RingBufferMrSw8Put(RingBuffer8 *, void *);
+
+/** Multiple Reader, Single Writer ring buffer, fixed at
+ * 65536 items so we can use unsigned shorts that just
+ * wrap around */
+void *RingBufferMrSwGet(RingBuffer16 *);
+int RingBufferMrSwPut(RingBuffer16 *, void *);
+
+/** Single Reader, Single Writer ring buffer, fixed at
+ * 65536 items so we can use unsigned shorts that just
+ * wrap around */
+void *RingBufferSrSwGet(RingBuffer16 *);
+int RingBufferSrSwPut(RingBuffer16 *, void *);
+
+/** Multiple Reader, Multi Writer ring buffer, fixed at
+ * 256 items so we can use unsigned char's that just
+ * wrap around */
+void *RingBufferMrMw8Get(RingBuffer8 *);
+int RingBufferMrMw8Put(RingBuffer8 *, void *);
+
+/** Multiple Reader, Multi Writer ring buffer, fixed at
+ * 65536 items so we can use unsigned char's that just
+ * wrap around */
+void *RingBufferMrMwGet(RingBuffer16 *);
+void *RingBufferMrMwGetNoWait(RingBuffer16 *);
+int RingBufferMrMwPut(RingBuffer16 *, void *);
+
+void *RingBufferSrMw8Get(RingBuffer8 *);
+int RingBufferSrMw8Put(RingBuffer8 *, void *);
+
+void DetectRingBufferRegisterTests(void);
+
+#endif /* __UTIL_RINGBUFFER_H__ */
+
diff --git a/framework/src/suricata/src/util-rohash.c b/framework/src/suricata/src/util-rohash.c
new file mode 100644
index 00000000..a8e842b7
--- /dev/null
+++ b/framework/src/suricata/src/util-rohash.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Chained read only hash table implementation, meaning that
+ * after the initial fill no changes are allowed.
+ *
+ * Loading takes 2 stages.
+ * - stage1 maps data
+ * - stage2 fills blob
+ *
+ * \todo a bloomfilter in the ROHashTableOffsets could possibly prevent
+ * a lot of cache misses when validating a potential match
+ *
+ * \todo maybe add a user ctx to be returned instead, something like a
+ * 4/8 byte ptr or simply a flag
+ */
+
+#include "suricata-common.h"
+#include "util-hash.h"
+#include "util-unittest.h"
+#include "util-memcmp.h"
+#include "util-hash-lookup3.h"
+#include "queue.h"
+#include "util-rohash.h"
+
+/** item_size data beyond this header */
+typedef struct ROHashTableItem_ {
+ uint32_t pos; /**< position relative to other values with same hash */
+ TAILQ_ENTRY(ROHashTableItem_) next;
+} ROHashTableItem;
+
+/** offset table */
+typedef struct ROHashTableOffsets_ {
+ uint32_t cnt; /**< number of items for this hash */
+ uint32_t offset; /**< position in the blob of the first item */
+} ROHashTableOffsets;
+
+/** \brief initialize a new rohash
+ *
+ * \param hash_bits hash size as 2^hash_bits, so power of 2, max 31
+ * \param item_size size of the data to store
+ *
+ * \retval table ptr or NULL on error
+ */
+ROHashTable *ROHashInit(uint8_t hash_bits, uint16_t item_size)
+{
+ if (item_size % 4 != 0 || item_size == 0) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "data size must be multiple of 4");
+ return NULL;
+ }
+ if (hash_bits < 4 || hash_bits > 31) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "invalid hash_bits setting, valid range is 4-31");
+ return NULL;
+ }
+
+ uint32_t size = hashsize(hash_bits) * sizeof(ROHashTableOffsets);
+
+ ROHashTable *table = SCMalloc(sizeof(ROHashTable) + size);
+ if (unlikely(table == NULL)) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "failed to alloc memory");
+ return NULL;
+ }
+ memset(table, 0, sizeof(ROHashTable) + size);
+
+ table->items = 0;
+ table->item_size = item_size;
+ table->hash_bits = hash_bits;
+ TAILQ_INIT(&table->head);
+
+ return table;
+}
+
+void ROHashFree(ROHashTable *table)
+{
+ if (table != NULL) {
+ if (table->data != NULL) {
+ SCFree(table->data);
+ }
+
+ SCFree(table);
+ }
+}
+
+uint32_t ROHashMemorySize(ROHashTable *table)
+{
+ return (uint32_t)(hashsize(table->hash_bits) * sizeof(ROHashTableOffsets) +
+ table->items * table->item_size + sizeof(ROHashTable));
+}
+
+/**
+ * \retval NULL not found
+ * \retval ptr found
+ */
+void *ROHashLookup(ROHashTable *table, void *data, uint16_t size)
+{
+ if (data == NULL || size != table->item_size) {
+ SCReturnPtr(NULL, "void");
+ }
+
+ uint32_t hash = hashword(data, table->item_size/4, 0) & hashmask(table->hash_bits);
+
+ /* get offsets start */
+ ROHashTableOffsets *os = (void *)table + sizeof(ROHashTable);
+ ROHashTableOffsets *o = &os[hash];
+
+ /* no matches */
+ if (o->cnt == 0) {
+ SCReturnPtr(NULL, "void");
+ }
+
+ uint32_t u;
+ for (u = 0; u < o->cnt; u++) {
+ uint32_t offset = (o->offset + u) * table->item_size;
+
+ if (SCMemcmp(table->data + offset, data, table->item_size) == 0) {
+ SCReturnPtr(table->data + offset, "void");
+ }
+ }
+ SCReturnPtr(NULL, "void");
+}
+
+/** \brief Add a new value to the hash
+ *
+ * \note can only be done when table isn't in a locked state yet
+ *
+ * \param table the hash table
+ * \param value value to add
+ * \param size value size. *MUST* match table item_size
+ *
+ * \retval 0 error
+ * \retval 1 ok
+ */
+int ROHashInitQueueValue(ROHashTable *table, void *value, uint16_t size)
+{
+ if (table->locked) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "can't add value to locked table");
+ return 0;
+ }
+ if (table->item_size != size) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "wrong size for data %u != %u", size, table->item_size);
+ return 0;
+ }
+
+ ROHashTableItem *item = SCMalloc(sizeof(ROHashTableItem) + table->item_size);
+ if (item != NULL) {
+ memset(item, 0x00, sizeof(ROHashTableItem));
+ memcpy((void *)item + sizeof(ROHashTableItem), value, table->item_size);
+ TAILQ_INSERT_TAIL(&table->head, item, next);
+ return 1;
+ }
+
+ return 0;
+}
+
+/** \brief create final hash data structure
+ *
+ * \param table the hash table
+ *
+ * \retval 0 error
+ * \retval 1 ok
+ *
+ * \note after this call the nothing can be added to the hash anymore.
+ */
+int ROHashInitFinalize(ROHashTable *table)
+{
+ if (table->locked) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "table already locked");
+ return 0;
+ }
+
+ ROHashTableItem *item = NULL;
+ ROHashTableOffsets *os = (void *)table + sizeof(ROHashTable);
+
+ /* count items per hash value */
+ TAILQ_FOREACH(item, &table->head, next) {
+ uint32_t hash = hashword((void *)item + sizeof(*item), table->item_size/4, 0) & hashmask(table->hash_bits);
+ ROHashTableOffsets *o = &os[hash];
+
+ item->pos = o->cnt;
+ o->cnt++;
+ table->items++;
+ }
+
+ if (table->items == 0) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "no items");
+ return 0;
+ }
+
+ /* get the data block */
+ uint32_t newsize = table->items * table->item_size;
+ table->data = SCMalloc(newsize);
+ if (table->data == NULL) {
+ SCLogError(SC_ERR_HASH_TABLE_INIT, "failed to alloc memory");
+ return 0;
+ }
+ memset(table->data, 0x00, newsize);
+
+ /* calc offsets into the block per hash value */
+ uint32_t total = 0;
+ uint32_t x;
+ for (x = 0; x < hashsize(table->hash_bits); x++) {
+ ROHashTableOffsets *o = &os[x];
+
+ if (o->cnt == 0)
+ continue;
+
+ o->offset = total;
+ total += o->cnt;
+ }
+
+ /* copy each value into the data block */
+ TAILQ_FOREACH(item, &table->head, next) {
+ uint32_t hash = hashword((void *)item + sizeof(*item), table->item_size/4, 0) & hashmask(table->hash_bits);
+
+ ROHashTableOffsets *o = &os[hash];
+ uint32_t offset = (o->offset + item->pos) * table->item_size;
+
+ memcpy(table->data + offset, (void *)item + sizeof(*item), table->item_size);
+
+ }
+
+ /* clean up temp items */
+ while ((item = TAILQ_FIRST(&table->head))) {
+ TAILQ_REMOVE(&table->head, item, next);
+ SCFree(item);
+ }
+
+ table->locked = 1;
+ return 1;
+}
diff --git a/framework/src/suricata/src/util-rohash.h b/framework/src/suricata/src/util-rohash.h
new file mode 100644
index 00000000..720eace5
--- /dev/null
+++ b/framework/src/suricata/src/util-rohash.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_ROHASH_H__
+#define __UTIL_ROHASH_H__
+
+#include "queue.h"
+
+typedef struct ROHashTable_ {
+ uint8_t locked;
+ uint8_t hash_bits;
+ uint16_t item_size;
+ uint32_t items;
+ void *data;
+ TAILQ_HEAD(, ROHashTableItem_) head;
+} ROHashTable;
+
+/* init time */
+ROHashTable *ROHashInit(uint8_t hash_bits, uint16_t item_size);
+int ROHashInitFinalize(ROHashTable *table);
+void ROHashFree(ROHashTable *table);
+int ROHashInitQueueValue(ROHashTable *table, void *value, uint16_t size);
+uint32_t ROHashMemorySize(ROHashTable *table);
+
+/* run time */
+void *ROHashLookup(ROHashTable *table, void *data, uint16_t size);
+
+#endif /* __UTIL_ROHASH_H__ */
diff --git a/framework/src/suricata/src/util-rule-vars.c b/framework/src/suricata/src/util-rule-vars.c
new file mode 100644
index 00000000..f3b5604f
--- /dev/null
+++ b/framework/src/suricata/src/util-rule-vars.c
@@ -0,0 +1,532 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Rule variable utility functions
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "conf-yaml-loader.h"
+
+#include "detect.h"
+#include "detect-content.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "util-rule-vars.h"
+#include "util-enum.h"
+#include "util-debug.h"
+#include "util-unittest.h"
+
+/** An enum-string map, that maps the different vars type in the yaml conf
+ * type with the mapping path in the yaml conf file */
+SCEnumCharMap sc_rule_vars_type_map[ ] = {
+ { "vars.address-groups", SC_RULE_VARS_ADDRESS_GROUPS },
+ { "vars.port-groups", SC_RULE_VARS_PORT_GROUPS }
+};
+
+/**
+ * \internal
+ * \brief Retrieves a value for a yaml mapping. The sequence from the yaml
+ * conf file, from which the conf value has to be retrieved can be
+ * specified by supplying a SCRuleVarsType enum. The string mapping
+ * for each of the SCRuleVarsType is present in sc_rule_vars_type_map.
+ *
+ * \param conf_var_name Pointer to a character string containing the conf var
+ * name, whose value has to be retrieved from the yaml
+ * conf file.
+ * \param conf_vars_type Holds an enum value that indicates the kind of yaml
+ * mapping that has to be retrieved. Can be one of the
+ * values in SCRuleVarsType.
+ *
+ * \retval conf_var_name_value Pointer to the string containing the conf value
+ * on success; NULL on failure.
+ */
+char *SCRuleVarsGetConfVar(const DetectEngineCtx *de_ctx,
+ const char *conf_var_name,
+ SCRuleVarsType conf_vars_type)
+{
+ SCEnter();
+
+ const char *conf_var_type_name = NULL;
+ char conf_var_full_name[2048] = "";
+ char *conf_var_full_name_value = NULL;
+
+ if (conf_var_name == NULL)
+ goto end;
+
+ while (conf_var_name[0] != '\0' && isspace(conf_var_name[0])) {
+ conf_var_name++;
+ }
+
+ (conf_var_name[0] == '$') ? conf_var_name++ : conf_var_name;
+ conf_var_type_name = SCMapEnumValueToName(conf_vars_type,
+ sc_rule_vars_type_map);
+ if (conf_var_type_name == NULL)
+ goto end;
+
+ if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
+ if (snprintf(conf_var_full_name, sizeof(conf_var_full_name), "%s.%s.%s",
+ de_ctx->config_prefix, conf_var_type_name, conf_var_name) < 0) {
+ goto end;
+ }
+ } else {
+ if (snprintf(conf_var_full_name, sizeof(conf_var_full_name), "%s.%s",
+ conf_var_type_name, conf_var_name) < 0) {
+ goto end;
+ }
+ }
+
+ if (ConfGet(conf_var_full_name, &conf_var_full_name_value) != 1) {
+ SCLogError(SC_ERR_UNDEFINED_VAR, "Variable \"%s\" is not defined in "
+ "configuration file", conf_var_name);
+ goto end;
+ }
+
+ SCLogDebug("Value obtained from the yaml conf file, for the var "
+ "\"%s\" is \"%s\"", conf_var_name, conf_var_full_name_value);
+
+ end:
+ SCReturnCharPtr(conf_var_full_name_value);
+}
+
+
+/**********************************Unittests***********************************/
+#ifdef UNITTESTS
+
+static const char *dummy_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "\n"
+ "default-log-dir: /var/log/suricata\n"
+ "\n"
+ "logging:\n"
+ "\n"
+ " default-log-level: debug\n"
+ "\n"
+ " default-format: \"<%t> - <%l>\"\n"
+ "\n"
+ " default-startup-message: Your IDS has started.\n"
+ "\n"
+ " default-output-filter:\n"
+ "\n"
+ " output:\n"
+ "\n"
+ " - interface: console\n"
+ " log-level: info\n"
+ "\n"
+ " - interface: file\n"
+ " filename: /var/log/suricata.log\n"
+ "\n"
+ " - interface: syslog\n"
+ " facility: local5\n"
+ " format: \"%l\"\n"
+ "\n"
+ "pfring:\n"
+ "\n"
+ " interface: eth0\n"
+ "\n"
+ " clusterid: 99\n"
+ "\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[192.168.0.0/16,10.8.0.0/16,127.0.0.1,2001:888:"
+ "13c5:5AFE::/64,2001:888:13c5:CAFE::/64]\"\n"
+ "\n"
+ " EXTERNAL_NET: \"[!192.168.0.0/16,2000::/3]\"\n"
+ "\n"
+ " HTTP_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " SMTP_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " SQL_SERVERS: \"!192.168.0.0/16\"\n"
+ "\n"
+ " DNS_SERVERS: any\n"
+ "\n"
+ " TELNET_SERVERS: any\n"
+ "\n"
+ " AIM_SERVERS: any\n"
+ "\n"
+ " port-groups:\n"
+ "\n"
+ " HTTP_PORTS: \"80:81,88\"\n"
+ "\n"
+ " SHELLCODE_PORTS: 80\n"
+ "\n"
+ " ORACLE_PORTS: 1521\n"
+ "\n"
+ " SSH_PORTS: 22\n"
+ "\n";
+
+/**
+ * \test Check that valid address and port group vars are correctly retrieved
+ * from the configuration.
+ */
+int SCRuleVarsPositiveTest01(void)
+{
+ int result = 1;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ /* check for address-groups */
+ result &= (SCRuleVarsGetConfVar(NULL,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS),
+ "[192.168.0.0/16,10.8.0.0/16,127.0.0.1,2001:888:13c5:"
+ "5AFE::/64,2001:888:13c5:CAFE::/64]") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$EXTERNAL_NET", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$EXTERNAL_NET", SC_RULE_VARS_ADDRESS_GROUPS),
+ "[!192.168.0.0/16,2000::/3]") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$HTTP_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$HTTP_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "!192.168.0.0/16") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$SMTP_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$SMTP_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "!192.168.0.0/16") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$SQL_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$SQL_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "!192.168.0.0/16") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$DNS_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$DNS_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "any") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$TELNET_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$TELNET_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "any") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$AIM_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$AIM_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "any") == 0);
+
+ /* Test that a leading space is stripped. */
+ result &= (SCRuleVarsGetConfVar(NULL," $AIM_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL," $AIM_SERVERS", SC_RULE_VARS_ADDRESS_GROUPS),
+ "any") == 0);
+
+ /* check for port-groups */
+ result &= (SCRuleVarsGetConfVar(NULL,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "80:81,88") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$SHELLCODE_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$SHELLCODE_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "80") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$ORACLE_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$ORACLE_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "1521") == 0);
+ result &= (SCRuleVarsGetConfVar(NULL,"$SSH_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$SSH_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "22") == 0);
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that invalid address and port groups are properly handled by the
+ * API.
+ */
+int SCRuleVarsNegativeTest02(void)
+{
+ int result = 1;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ result &= (SCRuleVarsGetConfVar(NULL,"$HOME_NETW", SC_RULE_VARS_ADDRESS_GROUPS) == NULL);
+ result &= (SCRuleVarsGetConfVar(NULL,"$home_net", SC_RULE_VARS_ADDRESS_GROUPS) == NULL);
+
+ result &= (SCRuleVarsGetConfVar(NULL,"$TOMCAT_PORTSW", SC_RULE_VARS_PORT_GROUPS) == NULL);
+ result &= (SCRuleVarsGetConfVar(NULL,"$tomcat_ports", SC_RULE_VARS_PORT_GROUPS) == NULL);
+
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ return result;
+}
+
+/**
+ * \test Check that Signatures with valid address and port groups are parsed
+ * without any errors by the Signature parsing API.
+ */
+int SCRuleVarsPositiveTest03(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+/*
+ s = SigInit(de_ctx, "alert tcp $HTTP_SERVERS any -> any any (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $SMTP_SERVERS any -> $HTTP_SERVERS any (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $AIM_SERVERS any -> $AIM_SERVERS any (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $TELNET_SERVERS any -> any $SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $TELNET_SERVERS any -> any !$SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $TELNET_SERVERS 80 -> any !$SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $TELNET_SERVERS !80 -> any !$SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !$HTTP_SERVERS !80 -> any !$SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp 192.168.1.2 any -> any $HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !192.168.1.2 any -> any $HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !192.168.1.2 any -> any !$HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !192.168.1.2 any -> !$HTTP_SERVERS !$HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !192.168.1.2 $HTTP_PORTS -> !$HTTP_SERVERS !$HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp [!192.168.24.0/23,!167.12.0.0/24] any -> any $HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp ![192.168.24.0/23,!167.12.0.0/24] any -> any $HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp [$HOME_NET,!192.168.1.2] $HTTP_PORTS -> !$HTTP_SERVERS !$HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp [[192.168.1.3,$EXTERNAL_NET],192.168.2.5] $HTTP_PORTS -> !$HTTP_SERVERS !$HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp [[192.168.1.3,$EXTERNAL_NET],192.168.2.5] $HTTP_PORTS -> !$HTTP_SERVERS [80,[!$HTTP_PORTS,$ORACLE_PORTS]] (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+*/
+ s = SigInit(de_ctx, "alert tcp [$HTTP_SERVERS,$HOME_NET,192.168.2.5] $HTTP_PORTS -> $EXTERNAL_NET [80,[!$HTTP_PORTS,$ORACLE_PORTS]] (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s == NULL)
+ goto end;
+ SigFree(s);
+
+ result = 1;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check that Signatures with invalid address and port groups, are
+ * are invalidated by the Singature parsing API.
+ */
+int SCRuleVarsNegativeTest04(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineCtx *de_ctx = NULL;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ goto end;
+ de_ctx->flags |= DE_QUIET;
+
+ s = SigInit(de_ctx, "alert tcp $HTTP_SERVER any -> any any (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s != NULL)
+ goto end;
+
+ s = SigInit(de_ctx, "alert tcp $http_servers any -> any any (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s != NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp $http_servers any -> any $HTTP_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s != NULL)
+ goto end;
+ SigFree(s);
+
+ s = SigInit(de_ctx, "alert tcp !$TELNET_SERVERS !80 -> any !$SSH_PORTS (msg:\"Rule Vars Test\"; sid:1;)");
+ if (s != NULL)
+ goto end;
+ SigFree(s);
+
+ result = 1;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+static const char *dummy_mt_conf_string =
+ "%YAML 1.1\n"
+ "---\n"
+ "vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[1.2.3.4]\"\n"
+ " port-groups:\n"
+ " HTTP_PORTS: \"12345\"\n"
+ "multi-detect:\n"
+ " 0:\n"
+ " vars:\n"
+ "\n"
+ " address-groups:\n"
+ "\n"
+ " HOME_NET: \"[8.8.8.8]\"\n"
+ " port-groups:\n"
+ " HTTP_PORTS: \"54321\"\n"
+ "\n";
+
+/**
+ * \test Check that valid address and port group vars are correctly retrieved
+ * from the configuration.
+ */
+int SCRuleVarsMTest01(void)
+{
+ int result = 0;
+ DetectEngineCtx *de_ctx = NULL;
+
+ ConfCreateContextBackup();
+ ConfInit();
+ ConfYamlLoadString(dummy_mt_conf_string, strlen(dummy_mt_conf_string));
+
+ if ( (de_ctx = DetectEngineCtxInit()) == NULL)
+ return 0;
+ de_ctx->flags |= DE_QUIET;
+ snprintf(de_ctx->config_prefix, sizeof(de_ctx->config_prefix),
+ "multi-detect.0");
+
+ /* check for address-groups */
+ result = (SCRuleVarsGetConfVar(de_ctx,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(de_ctx,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS),
+ "[8.8.8.8]") == 0);
+ if (result == 0)
+ goto end;
+
+ result = (SCRuleVarsGetConfVar(NULL,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$HOME_NET", SC_RULE_VARS_ADDRESS_GROUPS),
+ "[1.2.3.4]") == 0);
+ if (result == 0)
+ goto end;
+
+ /* check for port-groups */
+ result = (SCRuleVarsGetConfVar(de_ctx,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(de_ctx,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "54321") == 0);
+ if (result == 0)
+ goto end;
+
+ result = (SCRuleVarsGetConfVar(NULL,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS) != NULL &&
+ strcmp(SCRuleVarsGetConfVar(NULL,"$HTTP_PORTS", SC_RULE_VARS_PORT_GROUPS),
+ "12345") == 0);
+ if (result == 0)
+ goto end;
+
+end:
+ ConfDeInit();
+ ConfRestoreContextBackup();
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+void SCRuleVarsRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SCRuleVarsPositiveTest01", SCRuleVarsPositiveTest01, 1);
+ UtRegisterTest("SCRuleVarsNegativeTest02", SCRuleVarsNegativeTest02, 1);
+ UtRegisterTest("SCRuleVarsPositiveTest03", SCRuleVarsPositiveTest03, 1);
+ UtRegisterTest("SCRuleVarsNegativeTest04", SCRuleVarsNegativeTest04, 1);
+
+ UtRegisterTest("SCRuleVarsMTest01", SCRuleVarsMTest01, 1);
+#endif
+
+ return;
+}
diff --git a/framework/src/suricata/src/util-rule-vars.h b/framework/src/suricata/src/util-rule-vars.h
new file mode 100644
index 00000000..57d161e9
--- /dev/null
+++ b/framework/src/suricata/src/util-rule-vars.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_RULE_VARS_H__
+#define __UTIL_RULE_VARS_H__
+
+/** Enum indicating the various vars type in the yaml conf file */
+typedef enum {
+ SC_RULE_VARS_ADDRESS_GROUPS,
+ SC_RULE_VARS_PORT_GROUPS,
+} SCRuleVarsType;
+
+char *SCRuleVarsGetConfVar(const DetectEngineCtx *, const char *, SCRuleVarsType);
+void SCRuleVarsRegisterTests(void);
+
+#endif /* __UTIL_RULE_VARS_H__ */
diff --git a/framework/src/suricata/src/util-runmodes.c b/framework/src/suricata/src/util-runmodes.c
new file mode 100644
index 00000000..c9b61e6a
--- /dev/null
+++ b/framework/src/suricata/src/util-runmodes.c
@@ -0,0 +1,751 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ *
+ * Helper function for runmode.
+ *
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-af-packet.h"
+#include "log-httplog.h"
+#include "output.h"
+
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+
+#include "alert-fastlog.h"
+#include "alert-prelude.h"
+#include "alert-unified2-alert.h"
+#include "alert-debuglog.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+
+#include "util-runmodes.h"
+
+#include "flow-hash.h"
+
+/** set to true if flow engine and stream engine run in different
+ * threads. */
+static int runmode_flow_stream_async = 0;
+
+void RunmodeSetFlowStreamAsync(void)
+{
+ runmode_flow_stream_async = 1;
+ FlowDisableTcpReuseHandling();
+}
+
+int RunmodeGetFlowStreamAsync(void)
+{
+ return runmode_flow_stream_async;
+}
+
+/** \brief create a queue string for autofp to pass to
+ * the flow queue handler.
+ *
+ * The string will be "pickup1,pickup2,pickup3\0"
+ */
+char *RunmodeAutoFpCreatePickupQueuesString(int n)
+{
+ char *queues = NULL;
+ /* 13 because pickup12345, = 12 + \0 */
+ size_t queues_size = n * 13;
+ int thread;
+ char qname[TM_QUEUE_NAME_MAX];
+
+ queues = SCMalloc(queues_size);
+ if (unlikely(queues == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "failed to alloc queues buffer: %s", strerror(errno));
+ return NULL;
+ }
+ memset(queues, 0x00, queues_size);
+
+ for (thread = 0; thread < n; thread++) {
+ if (strlen(queues) > 0)
+ strlcat(queues, ",", queues_size);
+
+ snprintf(qname, sizeof(qname), "pickup%d", thread+1);
+ strlcat(queues, qname, queues_size);
+ }
+
+ SCLogDebug("%d %"PRIuMAX", queues %s", n, (uintmax_t)queues_size, queues);
+ return queues;
+}
+
+/**
+ */
+int RunModeSetLiveCaptureAutoFp(ConfigIfaceParserFunc ConfigParser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev)
+{
+ char tname[TM_THREAD_NAME_MAX];
+ char qname[TM_QUEUE_NAME_MAX];
+ char *queues = NULL;
+ int thread = 0;
+ /* Available cpus */
+ uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
+ int nlive = LiveGetDeviceCount();
+ int thread_max = TmThreadGetNbThreads(DETECT_CPU_SET);
+ /* always create at least one thread */
+ if (thread_max == 0)
+ thread_max = ncpus * threading_detect_ratio;
+ if (thread_max < 1)
+ thread_max = 1;
+
+ RunmodeSetFlowStreamAsync();
+
+ queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
+ if (queues == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "RunmodeAutoFpCreatePickupQueuesString failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((nlive <= 1) && (live_dev != NULL)) {
+ void *aconf;
+ int threads_count;
+
+ SCLogDebug("live_dev %s", live_dev);
+
+ aconf = ConfigParser(live_dev);
+ if (aconf == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "Failed to allocate config for %s (%d)",
+ live_dev, thread);
+ exit(EXIT_FAILURE);
+ }
+
+ threads_count = ModThreadsCount(aconf);
+ SCLogInfo("Going to use %" PRId32 " %s receive thread(s)",
+ threads_count, recv_mod_name);
+
+ /* create the threads */
+ for (thread = 0; thread < threads_count; thread++) {
+ snprintf(tname, sizeof(tname), "%s%d", thread_name, thread+1);
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate thread name");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_receive =
+ TmThreadCreatePacketHandler(thread_name,
+ "packetpool", "packetpool",
+ queues, "flow", "pktacqloop");
+ if (tv_receive == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName(recv_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE,
+ "TmModuleGetByName failed for %s",
+ recv_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, aconf);
+
+ tm_module = TmModuleGetByName(decode_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE,
+ "TmModuleGetByName %s failed", decode_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
+
+ TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
+
+ if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else { /* Multiple input device */
+ SCLogInfo("Using %d live device(s).", nlive);
+ int lthread;
+
+ for (lthread = 0; lthread < nlive; lthread++) {
+ char *live_dev = LiveGetDeviceName(lthread);
+ void *aconf;
+ int threads_count;
+
+ if (live_dev == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "Failed to lookup live dev %d", lthread);
+ exit(EXIT_FAILURE);
+ }
+ SCLogDebug("live_dev %s", live_dev);
+
+ aconf = ConfigParser(live_dev);
+ if (aconf == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "Multidev: Failed to allocate config for %s (%d)",
+ live_dev, lthread);
+ exit(EXIT_FAILURE);
+ }
+
+ threads_count = ModThreadsCount(aconf);
+ for (thread = 0; thread < threads_count; thread++) {
+ snprintf(tname, sizeof(tname), "%s%s%d", thread_name,
+ live_dev, thread+1);
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate thread name");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_receive =
+ TmThreadCreatePacketHandler(thread_name,
+ "packetpool", "packetpool",
+ queues, "flow", "pktacqloop");
+ if (tv_receive == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName(recv_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName failed for %s", recv_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, aconf);
+
+ tm_module = TmModuleGetByName(decode_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName %s failed", decode_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
+
+ TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
+
+ if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+
+ for (thread = 0; thread < thread_max; thread++) {
+ snprintf(tname, sizeof(tname), "Detect%d", thread+1);
+ snprintf(qname, sizeof(qname), "pickup%d", thread+1);
+
+ SCLogDebug("tname %s, qname %s", tname, qname);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate thread name");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_detect_ncpu =
+ TmThreadCreatePacketHandler(thread_name,
+ qname, "flow",
+ "packetpool", "packetpool",
+ "varslot");
+ if (tv_detect_ncpu == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+ }
+
+ TmThreadSetCPU(tv_detect_ncpu, DETECT_CPU_SET);
+
+ char *thread_group_name = SCStrdup("Detect");
+ if (unlikely(thread_group_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ tv_detect_ncpu->thread_group_name = thread_group_name;
+
+ tm_module = TmModuleGetByName("RespondReject");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName RespondReject failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+
+ /* add outputs as well */
+ SetupOutputs(tv_detect_ncpu);
+
+ if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ SCFree(queues);
+ return 0;
+}
+
+/**
+ */
+static int RunModeSetLiveCaptureWorkersForDevice(ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev, void *aconf,
+ unsigned char single_mode)
+{
+ int thread;
+ int threads_count;
+
+ if (single_mode) {
+ threads_count = 1;
+ } else {
+ threads_count = ModThreadsCount(aconf);
+ SCLogInfo("Going to use %" PRId32 " thread(s)", threads_count);
+ }
+
+ /* create the threads */
+ for (thread = 0; thread < threads_count; thread++) {
+ char tname[TM_THREAD_NAME_MAX];
+ char *n_thread_name = NULL;
+ ThreadVars *tv = NULL;
+ TmModule *tm_module = NULL;
+
+ if (single_mode) {
+ snprintf(tname, sizeof(tname), "%s", thread_name);
+ } else {
+ snprintf(tname, sizeof(tname), "%s%s%d",
+ thread_name, live_dev, thread+1);
+ }
+ n_thread_name = SCStrdup(tname);
+ if (unlikely(n_thread_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate thread name");
+ exit(EXIT_FAILURE);
+ }
+ tv = TmThreadCreatePacketHandler(n_thread_name,
+ "packetpool", "packetpool",
+ "packetpool", "packetpool",
+ "pktacqloop");
+ if (tv == NULL) {
+ SCLogError(SC_ERR_THREAD_CREATE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+
+ tm_module = TmModuleGetByName(recv_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for %s", recv_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, aconf);
+
+ tm_module = TmModuleGetByName(decode_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %s failed", decode_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+ }
+
+ tm_module = TmModuleGetByName("RespondReject");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName RespondReject failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ SetupOutputs(tv);
+
+ TmThreadSetCPU(tv, DETECT_CPU_SET);
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return 0;
+}
+
+int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev)
+{
+ int nlive = LiveGetDeviceCount();
+ void *aconf;
+ int ldev;
+
+ for (ldev = 0; ldev < nlive; ldev++) {
+ char *live_dev_c = NULL;
+ if (live_dev != NULL) {
+ aconf = ConfigParser(live_dev);
+ live_dev_c = SCStrdup(live_dev);
+ if (unlikely(live_dev_c == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate interface name");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ live_dev_c = LiveGetDeviceName(ldev);
+ aconf = ConfigParser(live_dev_c);
+ }
+ RunModeSetLiveCaptureWorkersForDevice(ModThreadsCount,
+ recv_mod_name,
+ decode_mod_name,
+ thread_name,
+ live_dev_c,
+ aconf,
+ 0);
+ }
+
+ return 0;
+}
+
+int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc ConfigParser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev)
+{
+ int nlive = LiveGetDeviceCount();
+ void *aconf;
+
+ if (nlive > 1) {
+ SCLogError(SC_ERR_RUNMODE,
+ "Can't use single runmode with multiple device");
+ exit(EXIT_FAILURE);
+ }
+
+ if (live_dev != NULL) {
+ aconf = ConfigParser(live_dev);
+ } else {
+ char *live_dev_c = LiveGetDeviceName(0);
+ aconf = ConfigParser(live_dev_c);
+ /* \todo Set threads number in config to 1 */
+ }
+
+ return RunModeSetLiveCaptureWorkersForDevice(
+ ModThreadsCount,
+ recv_mod_name,
+ decode_mod_name,
+ thread_name,
+ live_dev,
+ aconf,
+ 1);
+}
+
+
+/**
+ */
+int RunModeSetIPSAutoFp(ConfigIPSParserFunc ConfigParser,
+ char *recv_mod_name,
+ char *verdict_mod_name,
+ char *decode_mod_name)
+{
+ SCEnter();
+ char tname[TM_THREAD_NAME_MAX];
+ char qname[TM_QUEUE_NAME_MAX];
+ TmModule *tm_module ;
+ char *cur_queue = NULL;
+ char *queues = NULL;
+ int thread;
+
+ /* Available cpus */
+ uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
+ int nqueue = LiveGetDeviceCount();
+
+ int thread_max = TmThreadGetNbThreads(DETECT_CPU_SET);
+ /* always create at least one thread */
+ if (thread_max == 0)
+ thread_max = ncpus * threading_detect_ratio;
+ if (thread_max < 1)
+ thread_max = 1;
+
+ RunmodeSetFlowStreamAsync();
+
+ queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
+ if (queues == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "RunmodeAutoFpCreatePickupQueuesString failed");
+ exit(EXIT_FAILURE);
+ }
+
+ for (int i = 0; i < nqueue; i++) {
+ /* create the threads */
+ cur_queue = LiveGetDeviceName(i);
+ if (cur_queue == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "invalid queue number");
+ exit(EXIT_FAILURE);
+ }
+ memset(tname, 0, sizeof(tname));
+ snprintf(tname, sizeof(tname), "Recv-Q%s", cur_queue);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "thread name creation failed");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_receive =
+ TmThreadCreatePacketHandler(thread_name,
+ "packetpool", "packetpool",
+ queues, "flow", "pktacqloop");
+ if (tv_receive == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName(recv_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName failed for %s", recv_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, (void *) ConfigParser(i));
+
+ tm_module = TmModuleGetByName(decode_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName %s failed", decode_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
+
+ TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
+
+ if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+
+ }
+ for (thread = 0; thread < thread_max; thread++) {
+ snprintf(tname, sizeof(tname), "Detect%d", thread+1);
+ snprintf(qname, sizeof(qname), "pickup%d", thread+1);
+
+ SCLogDebug("tname %s, qname %s", tname, qname);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate thread name");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_detect_ncpu =
+ TmThreadCreatePacketHandler(thread_name,
+ qname, "flow",
+ "verdict-queue", "simple",
+ "varslot");
+ if (tv_detect_ncpu == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ TmModule *tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
+ }
+
+ TmThreadSetCPU(tv_detect_ncpu, DETECT_CPU_SET);
+
+ SetupOutputs(tv_detect_ncpu);
+
+ char *thread_group_name = SCStrdup("Detect");
+ if (unlikely(thread_group_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ tv_detect_ncpu->thread_group_name = thread_group_name;
+
+ if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* create the threads */
+ for (int i = 0; i < nqueue; i++) {
+ memset(tname, 0, sizeof(tname));
+ snprintf(tname, sizeof(tname), "Verdict%d", i);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ ThreadVars *tv_verdict =
+ TmThreadCreatePacketHandler(thread_name,
+ "verdict-queue", "simple",
+ "packetpool", "packetpool",
+ "varslot");
+ if (tv_verdict == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+ tm_module = TmModuleGetByName(verdict_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName %s failed", verdict_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_verdict, tm_module, (void *)ConfigParser(i));
+
+ tm_module = TmModuleGetByName("RespondReject");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName for RespondReject failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv_verdict, tm_module, NULL);
+
+ TmThreadSetCPU(tv_verdict, VERDICT_CPU_SET);
+
+ if (TmThreadSpawn(tv_verdict) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ SCFree(queues);
+ return 0;
+}
+
+/**
+ */
+int RunModeSetIPSWorker(ConfigIPSParserFunc ConfigParser,
+ char *recv_mod_name,
+ char *verdict_mod_name,
+ char *decode_mod_name)
+{
+ char tname[TM_THREAD_NAME_MAX];
+ ThreadVars *tv = NULL;
+ TmModule *tm_module = NULL;
+ char *cur_queue = NULL;
+
+ int nqueue = LiveGetDeviceCount();
+
+ for (int i = 0; i < nqueue; i++) {
+ /* create the threads */
+ cur_queue = LiveGetDeviceName(i);
+ if (cur_queue == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "invalid queue number");
+ exit(EXIT_FAILURE);
+ }
+ memset(tname, 0, sizeof(tname));
+ snprintf(tname, sizeof(tname), "Worker-Q%s", cur_queue);
+
+ char *thread_name = SCStrdup(tname);
+ if (unlikely(thread_name == NULL)) {
+ SCLogError(SC_ERR_RUNMODE, "Error allocating memory");
+ exit(EXIT_FAILURE);
+ }
+ tv = TmThreadCreatePacketHandler(thread_name,
+ "packetpool", "packetpool",
+ "packetpool", "packetpool",
+ "pktacqloop");
+ if (tv == NULL) {
+ SCLogError(SC_ERR_THREAD_CREATE, "TmThreadsCreate failed");
+ exit(EXIT_FAILURE);
+ }
+
+ tm_module = TmModuleGetByName(recv_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for %s", recv_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, (void *) ConfigParser(i));
+
+ tm_module = TmModuleGetByName(decode_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %s failed", decode_mod_name);
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("StreamTcp");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcp failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ if (DetectEngineEnabled()) {
+ tm_module = TmModuleGetByName("Detect");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+ }
+
+ tm_module = TmModuleGetByName(verdict_mod_name);
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName %s failed", verdict_mod_name);
+ exit(EXIT_FAILURE);
+ }
+
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ tm_module = TmModuleGetByName("RespondReject");
+ if (tm_module == NULL) {
+ SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName for RespondReject failed");
+ exit(EXIT_FAILURE);
+ }
+ TmSlotSetFuncAppend(tv, tm_module, NULL);
+
+ SetupOutputs(tv);
+
+ TmThreadSetCPU(tv, DETECT_CPU_SET);
+
+ if (TmThreadSpawn(tv) != TM_ECODE_OK) {
+ SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return 0;
+}
diff --git a/framework/src/suricata/src/util-runmodes.h b/framework/src/suricata/src/util-runmodes.h
new file mode 100644
index 00000000..607895a4
--- /dev/null
+++ b/framework/src/suricata/src/util-runmodes.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_RUNMODES_H__
+#define __UTIL_RUNMODES_H__
+
+void RunmodeSetFlowStreamAsync(void);
+int RunmodeGetFlowStreamAsync(void);
+
+typedef void *(*ConfigIfaceParserFunc) (const char *);
+typedef void *(*ConfigIPSParserFunc) (int);
+typedef int (*ConfigIfaceThreadsCountFunc) (void *);
+
+int RunModeSetLiveCaptureAuto(ConfigIfaceParserFunc configparser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev);
+
+int RunModeSetLiveCaptureAutoFp(ConfigIfaceParserFunc configparser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev);
+
+int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc configparser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev);
+
+int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc configparser,
+ ConfigIfaceThreadsCountFunc ModThreadsCount,
+ char *recv_mod_name,
+ char *decode_mod_name, char *thread_name,
+ const char *live_dev);
+
+int RunModeSetIPSAutoFp(ConfigIPSParserFunc ConfigParser,
+ char *recv_mod_name,
+ char *verdict_mod_name,
+ char *decode_mod_name);
+
+int RunModeSetIPSWorker(ConfigIPSParserFunc ConfigParser,
+ char *recv_mod_name,
+ char *verdict_mod_name,
+ char *decode_mod_name);
+
+char *RunmodeAutoFpCreatePickupQueuesString(int n);
+
+#endif /* __UTIL_RUNMODES_H__ */
diff --git a/framework/src/suricata/src/util-running-modes.c b/framework/src/suricata/src/util-running-modes.c
new file mode 100644
index 00000000..8ad5a1ee
--- /dev/null
+++ b/framework/src/suricata/src/util-running-modes.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#include "suricata-common.h"
+#include "config.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "util-cuda.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "conf-yaml-loader.h"
+
+int ListKeywords(const char *keyword_info)
+{
+ if (ConfYamlLoadFile(DEFAULT_CONF_FILE) != -1)
+ SCLogLoadConfig(0, 0);
+ MpmTableSetup();
+ AppLayerSetup();
+ SigTableSetup(); /* load the rule keywords */
+ SigTableList(keyword_info);
+ exit(EXIT_SUCCESS);
+}
+
+int ListAppLayerProtocols()
+{
+ if (ConfYamlLoadFile(DEFAULT_CONF_FILE) != -1)
+ SCLogLoadConfig(0, 0);
+ MpmTableSetup();
+ AppLayerSetup();
+ AppLayerListSupportedProtocols();
+
+ exit(EXIT_SUCCESS);
+}
+
+#ifdef __SC_CUDA_SUPPORT__
+int ListCudaCards()
+{
+ SCCudaInitCudaEnvironment();
+ SCCudaListCards();
+ exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/framework/src/suricata/src/util-running-modes.h b/framework/src/suricata/src/util-running-modes.h
new file mode 100644
index 00000000..b373f5aa
--- /dev/null
+++ b/framework/src/suricata/src/util-running-modes.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/** \file
+ *
+ * \author Eric Leblond <eric@regit.org>
+ */
+
+#ifndef __UTIL_RUNNING_MODES_H__
+#define __UTIL_RUNNING_MODES_H__
+
+
+int ListKeywords(const char *keyword_info);
+int ListAppLayerProtocols();
+#ifdef __SC_CUDA_SUPPORT__
+int ListCudaCards();
+#endif
+
+#endif /* __UTIL_RUNNING_MODES_H__ */
diff --git a/framework/src/suricata/src/util-signal.c b/framework/src/suricata/src/util-signal.c
new file mode 100644
index 00000000..0ba1a2b8
--- /dev/null
+++ b/framework/src/suricata/src/util-signal.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-debug.h"
+
+int UtilSignalBlock(int signum)
+{
+ sigset_t x;
+ if (sigemptyset(&x) < 0)
+ return -1;
+ if (sigaddset(&x, signum) < 0)
+ return -1;
+ if (sigprocmask(SIG_BLOCK, &x, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+void UtilSignalHandlerSetup(int sig, void (*handler)())
+{
+#if defined (OS_WIN32)
+ signal(sig, handler);
+#else
+ struct sigaction action;
+ memset(&action, 0x00, sizeof(struct sigaction));
+
+ action.sa_handler = handler;
+ sigemptyset(&(action.sa_mask));
+ sigaddset(&(action.sa_mask),sig);
+ action.sa_flags = 0;
+ sigaction(sig, &action, 0);
+#endif /* OS_WIN32 */
+
+ return;
+}
+
+int UtilSignalIsHandler(int sig, void (*handler)())
+{
+ struct sigaction action;
+ memset(&action, 0x00, sizeof(struct sigaction));
+
+ sigaction(sig, NULL, &action);
+
+ return (action.sa_handler == handler);
+}
diff --git a/framework/src/suricata/src/util-signal.h b/framework/src/suricata/src/util-signal.h
new file mode 100644
index 00000000..7209b3da
--- /dev/null
+++ b/framework/src/suricata/src/util-signal.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ */
+
+#ifndef __UTIL_SIGNAL_H__
+#define __UTIL_SIGNAL_H__
+
+int UtilSignalBlock(int);
+void UtilSignalHandlerSetup(int, void (*handler)());;
+int UtilSignalIsHandler(int sig, void (*handler)());
+
+#endif /* __UTIL_SIGNAL_H__ */
diff --git a/framework/src/suricata/src/util-spm-bm.c b/framework/src/suricata/src/util-spm-bm.c
new file mode 100644
index 00000000..7c5b8d2d
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bm.c
@@ -0,0 +1,382 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Boyer Moore simple pattern matcher implementation
+ *
+ * Boyer Moore algorithm has a really good performance. It need two arrays
+ * of context for each pattern that hold applicable shifts on the text
+ * to seach in, based on characters not available in the pattern
+ * and combinations of characters that start a sufix of the pattern.
+ * If possible, we should store the context of patterns that we are going
+ * to search for multiple times, so we don't spend time on rebuilding them.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "util-spm-bm.h"
+#include "util-debug.h"
+#include "util-error.h"
+#include "util-memcpy.h"
+
+static int PreBmGs(const uint8_t *x, uint16_t m, uint16_t *bmGs);
+static void PreBmBc(const uint8_t *x, uint16_t m, uint16_t *bmBc);
+static void PreBmBcNocase(const uint8_t *x, uint16_t m, uint16_t *bmBc);
+static void BoyerMooreSuffixesNocase(const uint8_t *x, uint16_t m,
+ uint16_t *suff);
+static void PreBmGsNocase(const uint8_t *x, uint16_t m, uint16_t *bmGs);
+
+/**
+ * \brief Given a BmCtx structure, recreate the pre/suffixes for
+ * nocase
+ *
+ * \retval BmCtx pointer to the already created BmCtx (with BoyerMooreCtxInit())
+ * \param str pointer to the pattern string
+ * \param size length of the string
+ */
+void BoyerMooreCtxToNocase(BmCtx *bm_ctx, uint8_t *needle, uint16_t needle_len)
+{
+ /* Store the content as lower case to make searching faster */
+ memcpy_tolower(needle, needle, needle_len);
+
+ /* Prepare bad chars with nocase chars */
+ PreBmBcNocase(needle, needle_len, bm_ctx->bmBc);
+
+ /* Prepare good Suffixes with nocase chars */
+ PreBmGsNocase(needle, needle_len, bm_ctx->bmGs);
+}
+
+/**
+ * \brief Setup a Booyer Moore context.
+ *
+ * \param str pointer to the pattern string
+ * \param size length of the string
+ * \retval BmCtx pointer to the newly created Context for the pattern
+ * \initonly BoyerMoore contexts should be created at init
+ */
+BmCtx *BoyerMooreCtxInit(uint8_t *needle, uint16_t needle_len)
+{
+ BmCtx *new = SCMalloc(sizeof(BmCtx));
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in BoyerMooreCtxInit. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Prepare bad chars */
+ PreBmBc(needle, needle_len, new->bmBc);
+
+ new->bmGs = SCMalloc(sizeof(uint16_t) * (needle_len + 1));
+ if (new->bmGs == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Prepare good Suffixes */
+ if (PreBmGs(needle, needle_len, new->bmGs) == -1) {
+ SCLogError(SC_ERR_FATAL, "Fatal error encountered in BooyerMooreCtxInit. Exiting...");
+ exit(EXIT_FAILURE);
+ }
+
+
+ return new;
+}
+
+/**
+ * \brief Setup a Booyer Moore context for nocase search
+ *
+ * \param str pointer to the pattern string
+ * \param size length of the string
+ * \retval BmCtx pointer to the newly created Context for the pattern
+ * \initonly BoyerMoore contexts should be created at init
+ */
+BmCtx *BoyerMooreNocaseCtxInit(uint8_t *needle, uint16_t needle_len)
+{
+ BmCtx *bm_ctx = BoyerMooreCtxInit(needle, needle_len);
+
+ BoyerMooreCtxToNocase(bm_ctx, needle, needle_len);
+
+ return bm_ctx;
+}
+
+/**
+ * \brief Free the memory allocated to Booyer Moore context.
+ *
+ * \param bmCtx pointer to the Context for the pattern
+ */
+void BoyerMooreCtxDeInit(BmCtx *bmctx)
+{
+ SCEnter();
+ if (bmctx == NULL)
+ SCReturn;
+
+ if (bmctx->bmGs != NULL)
+ SCFree(bmctx->bmGs);
+
+ SCFree(bmctx);
+
+ SCReturn;
+}
+/**
+ * \brief Array setup function for bad characters that split the pattern
+ * Remember that the result array should be the length of ALPHABET_SIZE
+ *
+ * \param str pointer to the pattern string
+ * \param size length of the string
+ * \param result pointer to an empty array that will hold the badchars
+ */
+static void PreBmBc(const uint8_t *x, uint16_t m, uint16_t *bmBc)
+{
+ int32_t i;
+
+ for (i = 0; i < 256; ++i) {
+ bmBc[i] = m;
+ }
+ for (i = 0; i < m - 1; ++i) {
+ bmBc[(unsigned char)x[i]] = m - i - 1;
+ }
+}
+
+/**
+ * \brief Array setup function for building prefixes (shift for valid prefixes) for boyermoore context
+ *
+ * \param x pointer to the pattern string
+ * \param m length of the string
+ * \param suff pointer to an empty array that will hold the prefixes (shifts)
+ */
+static void BoyerMooreSuffixes(const uint8_t *x, uint16_t m, uint16_t *suff)
+{
+ int32_t f = 0, g, i;
+ suff[m - 1] = m;
+ g = m - 1;
+ for (i = m - 2; i >= 0; --i) {
+ if (i > g && suff[i + m - 1 - f] < i - g)
+ suff[i] = suff[i + m - 1 - f];
+ else {
+ if (i < g)
+ g = i;
+ f = i;
+ while (g >= 0 && x[g] == x[g + m - 1 - f])
+ --g;
+ suff[i] = f - g;
+ }
+ }
+}
+
+/**
+ * \brief Array setup function for building prefixes (shift for valid prefixes) for boyermoore context
+ *
+ * \param x pointer to the pattern string
+ * \param m length of the string
+ * \param bmGs pointer to an empty array that will hold the prefixes (shifts)
+ * \retval 0 ok, -1 failed
+ */
+static int PreBmGs(const uint8_t *x, uint16_t m, uint16_t *bmGs)
+{
+ int32_t i, j;
+ uint16_t suff[m + 1];
+
+ BoyerMooreSuffixes(x, m, suff);
+
+ for (i = 0; i < m; ++i)
+ bmGs[i] = m;
+
+ j = 0;
+
+ for (i = m - 1; i >= -1; --i)
+ if (i == -1 || suff[i] == i + 1)
+ for (; j < m - 1 - i; ++j)
+ if (bmGs[j] == m)
+ bmGs[j] = m - 1 - i;
+
+ for (i = 0; i <= m - 2; ++i)
+ bmGs[m - 1 - suff[i]] = m - 1 - i;
+ return 0;
+}
+
+/**
+ * \brief Array setup function for bad characters that split the pattern
+ * Remember that the result array should be the length of ALPHABET_SIZE
+ *
+ * \param str pointer to the pattern string
+ * \param size length of the string
+ * \param result pointer to an empty array that will hold the badchars
+ */
+static void PreBmBcNocase(const uint8_t *x, uint16_t m, uint16_t *bmBc)
+{
+ int32_t i;
+
+ for (i = 0; i < 256; ++i) {
+ bmBc[i] = m;
+ }
+ for (i = 0; i < m - 1; ++i) {
+ bmBc[u8_tolower((unsigned char)x[i])] = m - 1 - i;
+ }
+}
+
+static void BoyerMooreSuffixesNocase(const uint8_t *x, uint16_t m,
+ uint16_t *suff)
+{
+ int32_t f = 0, g, i;
+
+ suff[m - 1] = m;
+ g = m - 1;
+ for (i = m - 2; i >= 0; --i) {
+ if (i > g && suff[i + m - 1 - f] < i - g) {
+ suff[i] = suff[i + m - 1 - f];
+ } else {
+ if (i < g) {
+ g = i;
+ }
+ f = i;
+ while (g >= 0 && u8_tolower(x[g]) == u8_tolower(x[g + m - 1 - f])) {
+ --g;
+ }
+ suff[i] = f - g;
+ }
+ }
+}
+
+/**
+ * \brief Array setup function for building prefixes (shift for valid prefixes)
+ * for boyermoore context case less
+ *
+ * \param x pointer to the pattern string
+ * \param m length of the string
+ * \param bmGs pointer to an empty array that will hold the prefixes (shifts)
+ */
+static void PreBmGsNocase(const uint8_t *x, uint16_t m, uint16_t *bmGs)
+{
+ int32_t i, j;
+ uint16_t suff[m + 1];
+
+ BoyerMooreSuffixesNocase(x, m, suff);
+
+ for (i = 0; i < m; ++i) {
+ bmGs[i] = m;
+ }
+ j = 0;
+ for (i = m - 1; i >= 0; --i) {
+ if (i == -1 || suff[i] == i + 1) {
+ for (; j < m - 1 - i; ++j) {
+ if (bmGs[j] == m) {
+ bmGs[j] = m - 1 - i;
+ }
+ }
+ }
+ }
+ for (i = 0; i <= m - 2; ++i) {
+ bmGs[m - 1 - suff[i]] = m - 1 - i;
+ }
+}
+
+/**
+ * \brief Boyer Moore search algorithm
+ * Is better as the pattern length increases and for big buffers to search in.
+ * The algorithm needs a context of two arrays already prepared
+ * by prep_bad_chars() and prep_good_suffix()
+ *
+ * \param y pointer to the buffer to search in
+ * \param n length limit of the buffer
+ * \param x pointer to the pattern we ar searching for
+ * \param m length limit of the needle
+ * \param bmBc pointer to an array of BoyerMooreSuffixes prepared by prep_good_suffix()
+ * \param bmGs pointer to an array of bachars prepared by prep_bad_chars()
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t *BoyerMoore(uint8_t *x, uint16_t m, uint8_t *y, int32_t n, BmCtx *bm_ctx)
+{
+ uint16_t *bmGs = bm_ctx->bmGs;
+ uint16_t *bmBc = bm_ctx->bmBc;
+
+ int i, j, m1, m2;
+#if 0
+ printf("\nBad:\n");
+ for (i=0;i<ALPHABET_SIZE;i++)
+ printf("%c,%d ", i, bmBc[i]);
+
+ printf("\ngood:\n");
+ for (i=0;i<m;i++)
+ printf("%c, %d ", x[i],bmBc[i]);
+ printf("\n");
+#endif
+ j = 0;
+
+ while (j <= n - m ) {
+ for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);
+
+ if (i < 0) {
+ return y + j;
+ //j += bmGs[0];
+ } else {
+ // printf("%c", y[i+j]);
+ j += (m1 = bmGs[i]) > (m2 = bmBc[y[i + j]] - m + 1 + i)? m1: m2;
+// printf("%d, %d\n", m1, m2);
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * \brief Boyer Moore search algorithm
+ * Is better as the pattern length increases and for big buffers to search in.
+ * The algorithm needs a context of two arrays already prepared
+ * by prep_bad_chars() and prep_good_suffix()
+ *
+ * \param y pointer to the buffer to search in
+ * \param n length limit of the buffer
+ * \param x pointer to the pattern we ar searching for
+ * \param m length limit of the needle
+ * \param bmBc pointer to an array of BoyerMooreSuffixes prepared by prep_good_suffix()
+ * \param bmGs pointer to an array of bachars prepared by prep_bad_chars()
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t *BoyerMooreNocase(uint8_t *x, uint16_t m, uint8_t *y, int32_t n, BmCtx *bm_ctx)
+{
+ uint16_t *bmGs = bm_ctx->bmGs;
+ uint16_t *bmBc = bm_ctx->bmBc;
+ int i, j, m1, m2;
+#if 0
+ printf("\nBad:\n");
+ for (i=0;i<ALPHABET_SIZE;i++)
+ printf("%c,%d ", i, bmBc[i]);
+
+ printf("\ngood:\n");
+ for (i=0;i<m;i++)
+ printf("%c, %d ", x[i],bmBc[i]);
+ printf("\n");
+#endif
+ j = 0;
+ while (j <= n - m ) {
+ /* x is stored in lowercase. */
+ for (i = m - 1; i >= 0 && x[i] == u8_tolower(y[i + j]); --i);
+
+ if (i < 0) {
+ return y + j;
+ } else {
+ j += (m1=bmGs[i]) > (m2=bmBc[u8_tolower(y[i + j])] - m + 1 + i)?m1:m2;
+ }
+ }
+ return NULL;
+}
+
diff --git a/framework/src/suricata/src/util-spm-bm.h b/framework/src/suricata/src/util-spm-bm.h
new file mode 100644
index 00000000..b3b97ced
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bm.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ */
+
+#ifndef __UTIL_SPM_BM__
+#define __UTIL_SPM_BM__
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#define ALPHABET_SIZE 256
+
+/* Context for booyer moore */
+typedef struct BmCtx_ {
+ uint16_t bmBc[ALPHABET_SIZE];
+ uint16_t *bmGs; // = SCMalloc(sizeof(int32_t)*(needlelen + 1));
+} BmCtx;
+
+/** Prepare and return a Boyer Moore context */
+BmCtx *BoyerMooreCtxInit(uint8_t *needle, uint16_t needle_len);
+BmCtx *BoyerMooreNocaseCtxInit(uint8_t *needle, uint16_t needle_len);
+
+void BoyerMooreCtxToNocase(BmCtx *, uint8_t *, uint16_t);
+uint8_t *BoyerMoore(uint8_t *x, uint16_t m, uint8_t *y, int32_t n, BmCtx *bm_ctx);
+uint8_t *BoyerMooreNocase(uint8_t *x, uint16_t m, uint8_t *y, int32_t n, BmCtx *bm_ctx);
+void BoyerMooreCtxDeInit(BmCtx *);
+
+#endif /* __UTIL_SPM_BM__ */
+
diff --git a/framework/src/suricata/src/util-spm-bs.c b/framework/src/suricata/src/util-spm-bs.c
new file mode 100644
index 00000000..6d49ff65
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bs.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * bs is a bruteforce search. It will try to search the pattern
+ * from all characters until the available text len is less
+ * than the length of the pattern. It needs no context but it
+ * time cost is not good.
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "util-debug.h"
+#include "util-spm-bs.h"
+
+
+/**
+ * \brief Basic search improved. Limits are better handled, so
+ * it doesn't start searches that wont fit in the remaining buffer
+ *
+ * \param haystack pointer to the buffer to search in
+ * \param haystack_len length limit of the buffer
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t *BasicSearch(const uint8_t *haystack, uint32_t haystack_len, const uint8_t *needle, uint16_t needle_len)
+{
+ SCEnter();
+
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + needle_len;
+
+ if (needle_len == 0 || needle_len > haystack_len) {
+ SCReturnPtr(NULL, "uint8_t");
+ }
+
+ //PrintRawDataFp(stdout,needle,needle_len);
+
+ //PrintRawDataFp(stdout,haystack,haystack_len);
+
+ for (n = needle; nmax - n <= hmax - haystack; haystack++) {
+ if (*haystack != *n) {
+ continue;
+ }
+
+ SCLogDebug("*haystack == *n, %c == %c", *haystack, *n);
+
+ /* one byte needles */
+ if (needle_len == 1) {
+ SCReturnPtr((uint8_t *)haystack, "uint8_t");
+ }
+
+ for (h = haystack+1, n++; nmax - n <= hmax - haystack; h++, n++) {
+ if (*h != *n) {
+ break;
+ }
+ SCLogDebug("*haystack == *n, %c == %c", *haystack, *n);
+ /* if we run out of needle we fully matched */
+ if (n == nmax - 1) {
+ SCReturnPtr((uint8_t *)haystack, "uint8_t");
+ }
+ }
+ n = needle;
+ }
+
+ SCReturnPtr(NULL, "uint8_t");
+}
+
+/**
+ * \brief Basic search case less
+ *
+ * \param haystack pointer to the buffer to search in
+ * \param haystack_len length limit of the buffer
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t *BasicSearchNocase(const uint8_t *haystack, uint32_t haystack_len, const uint8_t *needle, uint16_t needle_len)
+{
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + needle_len;
+
+ if (needle_len == 0 || needle_len > haystack_len)
+ return NULL;
+
+ for (n = needle; nmax - n <= hmax - haystack; haystack++) {
+ if (u8_tolower(*haystack) != u8_tolower(*n)) {
+ continue;
+ }
+ /* one byte needles */
+ if (needle_len == 1) {
+ return (uint8_t *)haystack;
+ }
+
+ for (h = haystack+1, n++; nmax - n <= hmax - h ; h++, n++) {
+ if (u8_tolower(*h) != u8_tolower(*n)) {
+ break;
+ }
+ /* if we run out of needle we fully matched */
+ if (n == nmax - 1) {
+ return (uint8_t *)haystack;
+ }
+ }
+ n = needle;
+ }
+
+ return NULL;
+}
+
+void BasicSearchInit (void)
+{
+ /* nothing no more */
+}
+
diff --git a/framework/src/suricata/src/util-spm-bs.h b/framework/src/suricata/src/util-spm-bs.h
new file mode 100644
index 00000000..1e97f0ba
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bs.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_SPM_BS__
+#define __UTIL_SPM_BS__
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+uint8_t *BasicSearch(const uint8_t *, uint32_t, const uint8_t *, uint16_t);
+uint8_t *BasicSearchNocase(const uint8_t *, uint32_t, const uint8_t *, uint16_t);
+void BasicSearchInit (void);
+
+#endif /* __UTIL_SPM_BS__ */
+
diff --git a/framework/src/suricata/src/util-spm-bs2bm.c b/framework/src/suricata/src/util-spm-bs2bm.c
new file mode 100644
index 00000000..d6529df8
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bs2bm.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * Bs2Bm use a simple context array to determine the charactes
+ * that are not present on the pattern. This way on partial matches
+ * broken by a char not present, we can skip to the next character
+ * making less checks
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "util-spm-bs2bm.h"
+
+/**
+ * \brief Array setup function for Bs2Bm of bad characters index (not found at the needle)
+ *
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ * \param badchars pointer to an empty array of bachars. The array prepared contains
+ * characters that can't be inside the needle_len. So the skips can be
+ * faster
+ */
+void Bs2BmBadchars(const uint8_t *needle, uint16_t needle_len, uint8_t *badchars)
+{
+ uint32_t i;
+ for (i = 0; i < ALPHABET_SIZE; i++)
+ badchars[i] = 1;
+
+ /* set to 0 the values where index as ascii is present
+ * because they are not badchars
+ */
+ for (i = 0; i < needle_len; i++)
+ badchars[needle[i]] = 0;
+}
+
+/**
+ * \brief Array setup function for Bs2BmNocase of bad characters index (not found at the needle)
+ *
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ * \param badchars pointer to an empty array of bachars. The array prepared contains
+ * characters that can't be inside the needle_len. So the skips can be
+ * faster
+ */
+void Bs2BmBadcharsNocase(const uint8_t *needle, uint16_t needle_len, uint8_t *badchars)
+{
+ uint32_t i;
+ for (i = 0; i < ALPHABET_SIZE; i++)
+ badchars[i] = 1;
+
+ /* set to 0 the values where index as ascii is present
+ * because they are not badchars
+ */
+ for (i = 0; i < needle_len; i++) {
+ badchars[u8_tolower(needle[i])] = 0;
+ }
+}
+
+
+/**
+ * \brief Basic search with a bad characters array. The array badchars contains
+ * flags at character's ascii index that can't be inside the needle. So the skips can be
+ * faster
+ *
+ * \param haystack pointer to the buffer to search in
+ * \param haystack_len length limit of the buffer
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ * \param badchars pointer to an array of bachars prepared by Bs2BmBadchars()
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t * Bs2Bm(const uint8_t *haystack, uint32_t haystack_len, const uint8_t *needle, uint16_t needle_len, uint8_t badchars[])
+{
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + needle_len;
+
+ if (needle_len == 0 || needle_len > haystack_len)
+ return NULL;
+
+ for (n = needle; nmax - n <= hmax - haystack; haystack++) {
+ if (*haystack != *n) {
+ continue;
+ }
+ /* one byte needles */
+ if (needle_len == 1)
+ return (uint8_t *)haystack;
+
+ for (h = haystack+1, n++; nmax - n <= hmax - haystack; h++, n++) {
+ if (*h != *n) {
+ if (badchars[*h] == 1) {
+ /* skip it! */
+ haystack = h;
+ }
+ break;
+ }
+ /* if we run out of needle we fully matched */
+ if (n == nmax - 1 ) {
+ return (uint8_t *)haystack;
+ }
+ }
+ n = needle;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Basic search case less with a bad characters array. The array badchars contains
+ * flags at character's ascii index that can't be inside the needle. So the skips can be
+ * faster
+ *
+ * \param haystack pointer to the buffer to search in
+ * \param haystack_len length limit of the buffer
+ * \param neddle pointer to the pattern we ar searching for
+ * \param needle_len length limit of the needle
+ * \param badchars pointer to an array of bachars prepared by Bs2BmBadchars()
+ *
+ * \retval ptr to start of the match; NULL if no match
+ */
+uint8_t *Bs2BmNocase(const uint8_t *haystack, uint32_t haystack_len, const uint8_t *needle, uint16_t needle_len, uint8_t badchars[])
+{
+ const uint8_t *h, *n;
+ const uint8_t *hmax = haystack + haystack_len;
+ const uint8_t *nmax = needle + needle_len;
+
+ if (needle_len == 0 || needle_len > haystack_len)
+ return NULL;
+
+ for (n = needle; nmax - n <= hmax - haystack; haystack++) {
+ if (u8_tolower(*haystack) != u8_tolower(*n)) {
+ continue;
+ }
+ /* one byte needles */
+ if (needle_len == 1)
+ return (uint8_t *)haystack;
+
+ for (h = haystack+1, n++; nmax - n <= hmax - haystack; h++, n++) {
+ if (u8_tolower(*h) != u8_tolower(*n)) {
+ if (badchars[u8_tolower(*h)] == 1) {
+ /* skip it! */
+ haystack = h;
+ }
+ break;
+ }
+ /* if we run out of needle we fully matched */
+ if (n == nmax - 1) {
+ return (uint8_t *)haystack;
+ }
+ }
+ n = needle;
+ }
+
+ return NULL;
+}
diff --git a/framework/src/suricata/src/util-spm-bs2bm.h b/framework/src/suricata/src/util-spm-bs2bm.h
new file mode 100644
index 00000000..a71b1282
--- /dev/null
+++ b/framework/src/suricata/src/util-spm-bs2bm.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_SPM_BS2BM__
+#define __UTIL_SPM_BS2BM__
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#define ALPHABET_SIZE 256
+
+void Bs2BmBadchars(const uint8_t *, uint16_t, uint8_t *);
+void Bs2BmBadcharsNocase(const uint8_t *, uint16_t, uint8_t *);
+uint8_t * Bs2Bm(const uint8_t *, uint32_t, const uint8_t *, uint16_t, uint8_t []);
+uint8_t *Bs2BmNocase(const uint8_t *, uint32_t, const uint8_t *, uint16_t, uint8_t []);
+
+#endif /* __UTIL_SPM_BS2BM__ */
+
diff --git a/framework/src/suricata/src/util-spm.c b/framework/src/suricata/src/util-spm.c
new file mode 100644
index 00000000..2da12e77
--- /dev/null
+++ b/framework/src/suricata/src/util-spm.c
@@ -0,0 +1,2368 @@
+/* Copyright (C) 2007-2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * PR (17/01/2010): Single pattern search algorithms:
+ * Currently there are 3 algorithms to choose: BasicSearch, Bs2Bm and
+ * BoyerMoore (Boyer Moores algorithm). The first one doesn't need a context.
+ * But for Bs2Bm and BoyerMoore, you'll need to build some arrays.
+ *
+ * !! If you are going to use the same pattern multiple times,
+ * please, try to store the context some where. For Bs2Bm, the
+ * context is an array of "badchars". For BoyerMoore you need to store
+ * two arrays of shifts. Have a look at the wrappers and unittests
+ * for examples of this. If you cant store the context, use the
+ * wrappers: Bs2bmSearch, BoyerMooreSearch, and the ones caseless, or BasicSearch
+ * That is the most basic.
+ *
+ * Use the stats and util-clock.h to determine which one fit better for you
+ * Boyer Moore should be used for patterns greater than 1 of length
+ * In the range of 2 - 6, if the text length is greater than 1000 you could
+ * use boyer moore, otherwise, basic search. If the pattern is greater
+ * than 6 and the textlen is greater than 500, use boyer moore.
+ * This is an aproximation, but use the stats and util-clock to determine which one
+ * fit better for your case.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "util-unittest.h"
+
+#include "util-spm.h"
+#include "util-spm-bs.h"
+#include "util-spm-bs2bm.h"
+#include "util-spm-bm.h"
+#include "util-clock.h"
+
+
+/**
+ * Wrappers for building context and searching (Bs2Bm and boyermoore)
+ * Use them if you cant store the context
+ *
+ */
+
+/**
+ * \brief Search a pattern in the text using the Bs2Bm algorithm (build a bad characters array)
+ *
+ * \param text Text to search in
+ * \param textlen length of the text
+ * \param needle pattern to search for
+ * \param needlelen length of the pattern
+ */
+uint8_t *Bs2bmSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen)
+{
+ uint8_t badchars[ALPHABET_SIZE];
+ Bs2BmBadchars(needle, needlelen, badchars);
+
+ return Bs2Bm(text, textlen, needle, needlelen, badchars);
+}
+
+/**
+ * \brief Search a pattern in the text using the Bs2Bm nocase algorithm (build a bad characters array)
+ *
+ * \param text Text to search in
+ * \param textlen length of the text
+ * \param needle pattern to search for
+ * \param needlelen length of the pattern
+ */
+uint8_t *Bs2bmNocaseSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen)
+{
+ uint8_t badchars[ALPHABET_SIZE];
+ Bs2BmBadchars(needle, needlelen, badchars);
+
+ return Bs2BmNocase(text, textlen, needle, needlelen, badchars);
+}
+
+/**
+ * \brief Search a pattern in the text using Boyer Moore algorithm
+ * (build a bad character shifts array and good prefixes shift array)
+ *
+ * \param text Text to search in
+ * \param textlen length of the text
+ * \param needle pattern to search for
+ * \param needlelen length of the pattern
+ */
+uint8_t *BoyerMooreSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen)
+{
+ BmCtx *bm_ctx = BoyerMooreCtxInit(needle, needlelen);
+
+ uint8_t *ret = BoyerMoore(needle, needlelen, text, textlen, bm_ctx);
+ BoyerMooreCtxDeInit(bm_ctx);
+
+ return ret;
+}
+
+/**
+ * \brief Search a pattern in the text using Boyer Moore nocase algorithm
+ * (build a bad character shifts array and good prefixes shift array)
+ *
+ * \param text Text to search in
+ * \param textlen length of the text
+ * \param needle pattern to search for
+ * \param needlelen length of the pattern
+ */
+uint8_t *BoyerMooreNocaseSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen)
+{
+ BmCtx *bm_ctx = BoyerMooreNocaseCtxInit(needle, needlelen);
+
+ uint8_t *ret = BoyerMooreNocase(needle, needlelen, text, textlen, bm_ctx);
+ BoyerMooreCtxDeInit(bm_ctx);
+
+ return ret;
+}
+
+
+#ifdef UNITTESTS
+
+/** Comment out this if you want stats
+ * #define ENABLE_SEARCH_STATS 1
+ */
+
+/* Number of times to repeat the search (for stats) */
+#define STATS_TIMES 1000000
+
+/**
+ * \brief Unittest helper function wrappers for the search algorithms
+ * \param text pointer to the buffer to search in
+ * \param needle pointer to the pattern to search for
+ * \param times If you are testing performance, se the numebr of times
+ * that you want to repeat the search
+ */
+uint8_t *BasicSearchWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1)
+ CLOCK_START;
+
+ for (i = 0; i < times; i++) {
+ ret = BasicSearch(text, textlen, needle, needlelen);
+ }
+
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *BasicSearchNocaseWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = BasicSearchNocase(text, textlen, needle, needlelen);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *Bs2bmWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t badchars[ALPHABET_SIZE];
+ Bs2BmBadchars(needle, needlelen, badchars);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = Bs2Bm(text, textlen, needle, needlelen, badchars);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *Bs2bmNocaseWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t badchars[ALPHABET_SIZE];
+ Bs2BmBadchars(needle, needlelen, badchars);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = Bs2BmNocase(text, textlen, needle, needlelen, badchars);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *BoyerMooreWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ BmCtx *bm_ctx = BoyerMooreCtxInit(needle, needlelen);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = BoyerMoore(needle, needlelen, text, textlen, bm_ctx);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ BoyerMooreCtxDeInit(bm_ctx);
+ return ret;
+}
+
+uint8_t *BoyerMooreNocaseWrapper(uint8_t *text, uint8_t *in_needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)in_needle);
+
+ /* Make a copy of in_needle to be able to convert it to lowercase. */
+ uint8_t *needle = SCMalloc(needlelen);
+ if (needle == NULL)
+ return NULL;
+ memcpy(needle, in_needle, needlelen);
+
+ BmCtx *bm_ctx = BoyerMooreNocaseCtxInit(needle, needlelen);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = BoyerMooreNocase(needle, needlelen, text, textlen, bm_ctx);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ BoyerMooreCtxDeInit(bm_ctx);
+ free(needle);
+ return ret;
+
+}
+
+/**
+ * \brief Unittest helper function wrappers for the search algorithms
+ * \param text pointer to the buffer to search in
+ * \param needle pointer to the pattern to search for
+ * \param times If you are testing performance, se the numebr of times
+ * that you want to repeat the search
+ */
+uint8_t *BasicSearchCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ /* This wrapper is a fake, no context needed! */
+ ret = BasicSearch(text, textlen, needle, needlelen);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *BasicSearchNocaseCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ /* This wrapper is a fake, no context needed! */
+ ret = BasicSearchNocase(text, textlen, needle, needlelen);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *Bs2bmCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t badchars[ALPHABET_SIZE];
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ /* Stats including context building */
+ Bs2BmBadchars(needle, needlelen, badchars);
+ ret = Bs2Bm(text, textlen, needle, needlelen, badchars);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *Bs2bmNocaseCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t badchars[ALPHABET_SIZE];
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ /* Stats including context building */
+ Bs2BmBadchars(needle, needlelen, badchars);
+ ret = Bs2BmNocase(text, textlen, needle, needlelen, badchars);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *BoyerMooreCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ BmCtx *bm_ctx = BoyerMooreCtxInit(needle, needlelen);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ /* Stats including context building */
+ ret = BoyerMoore(needle, needlelen, text, textlen, bm_ctx);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ BoyerMooreCtxDeInit(bm_ctx);
+
+ return ret;
+}
+
+uint8_t *RawCtxWrapper(uint8_t *text, uint8_t *needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)needle);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = SpmSearch(text, textlen, needle, needlelen);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ return ret;
+}
+
+uint8_t *BoyerMooreNocaseCtxWrapper(uint8_t *text, uint8_t *in_needle, int times)
+{
+ uint32_t textlen = strlen((char *)text);
+ uint16_t needlelen = strlen((char *)in_needle);
+
+ /* Make a copy of in_needle to be able to convert it to lowercase. */
+ uint8_t *needle = SCMalloc(needlelen);
+ if (needle == NULL)
+ return NULL;
+ memcpy(needle, in_needle, needlelen);
+
+ BmCtx *bm_ctx = BoyerMooreNocaseCtxInit(needle, needlelen);
+
+ uint8_t *ret = NULL;
+ int i = 0;
+
+ CLOCK_INIT;
+ if (times > 1) CLOCK_START;
+ for (i = 0; i < times; i++) {
+ ret = BoyerMooreNocase(needle, needlelen, text, textlen, bm_ctx);
+ }
+ if (times > 1) { CLOCK_END; CLOCK_PRINT_SEC; };
+ BoyerMooreCtxDeInit(bm_ctx);
+ free(needle);
+ return ret;
+
+}
+
+/**
+ * \test Generic test for BasicSearch matching
+ */
+int UtilSpmBasicSearchTest01()
+{
+ uint8_t *needle = (uint8_t *)"oPqRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BasicSearchWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test Generic test for BasicSearch nocase matching
+ */
+int UtilSpmBasicSearchNocaseTest01()
+{
+ uint8_t *needle = (uint8_t *)"OpQrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BasicSearchNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test Generic test for Bs2Bm matching
+ */
+int UtilSpmBs2bmSearchTest01()
+{
+ uint8_t *needle = (uint8_t *)"oPqRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = Bs2bmWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test Generic test for Bs2Bm no case matching
+ */
+int UtilSpmBs2bmSearchNocaseTest01()
+{
+ uint8_t *needle = (uint8_t *)"OpQrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = Bs2bmNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test Generic test for boyer moore matching
+ */
+int UtilSpmBoyerMooreSearchTest01()
+{
+ uint8_t *needle = (uint8_t *)"oPqRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BoyerMooreWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test Generic test for boyer moore nocase matching
+ */
+int UtilSpmBoyerMooreSearchNocaseTest01()
+{
+ uint8_t *needle = (uint8_t *)"OpQrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BoyerMooreNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * \test issue 130 (@redmine) check to ensure that the
+ * problem is not the algorithm implementation
+ */
+int UtilSpmBoyerMooreSearchNocaseTestIssue130()
+{
+ uint8_t *needle = (uint8_t *)"WWW-Authenticate: ";
+ uint8_t *text = (uint8_t *)"Date: Mon, 23 Feb 2009 13:31:49 GMT"
+ "Server: Apache\r\n"
+ "Www-authenticate: Basic realm=\"Authentification user password\"\r\n"
+ "Vary: accept-language,accept-charset\r\n"
+ "Accept-ranges: bytes\r\n"
+ "Connection: close\r\n"
+ "Content-type: text/html; charset=iso-8859-1\r\n"
+ "Content-language: fr\r\n"
+ "Expires: Mon, 23 Feb 2009 13:31:49 GMT\r\n\r\n";
+ uint8_t *found = BoyerMooreNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+/* Generic tests that should not match */
+int UtilSpmBasicSearchTest02()
+{
+ uint8_t *needle = (uint8_t *)"oPQRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BasicSearchWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+int UtilSpmBasicSearchNocaseTest02()
+{
+ uint8_t *needle = (uint8_t *)"OpZrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BasicSearchNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+int UtilSpmBs2bmSearchTest02()
+{
+ uint8_t *needle = (uint8_t *)"oPQRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = Bs2bmWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+int UtilSpmBs2bmSearchNocaseTest02()
+{
+ uint8_t *needle = (uint8_t *)"OpZrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = Bs2bmNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+int UtilSpmBoyerMooreSearchTest02()
+{
+ uint8_t *needle = (uint8_t *)"oPQRsT";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BoyerMooreWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+int UtilSpmBoyerMooreSearchNocaseTest02()
+{
+ uint8_t *needle = (uint8_t *)"OpZrSt";
+ uint8_t *text = (uint8_t *)"aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+ uint8_t *found = BoyerMooreNocaseWrapper(text, needle, 1);
+ //printf("found: %s\n", found);
+ if (found != NULL)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * \test Check that all the algorithms work at any offset and any pattern length
+ */
+int UtilSpmSearchOffsetsTest01()
+{
+ char *text[26][27];
+ text[0][0]="azzzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][1]="zazzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][2]="zzazzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][3]="zzzazzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][4]="zzzzazzzzzzzzzzzzzzzzzzzzzz";
+ text[0][5]="zzzzzazzzzzzzzzzzzzzzzzzzzz";
+ text[0][6]="zzzzzzazzzzzzzzzzzzzzzzzzzz";
+ text[0][7]="zzzzzzzazzzzzzzzzzzzzzzzzzz";
+ text[0][8]="zzzzzzzzazzzzzzzzzzzzzzzzzz";
+ text[0][9]="zzzzzzzzzazzzzzzzzzzzzzzzzz";
+ text[0][10]="zzzzzzzzzzazzzzzzzzzzzzzzzz";
+ text[0][11]="zzzzzzzzzzzazzzzzzzzzzzzzzz";
+ text[0][12]="zzzzzzzzzzzzazzzzzzzzzzzzzz";
+ text[0][13]="zzzzzzzzzzzzzazzzzzzzzzzzzz";
+ text[0][14]="zzzzzzzzzzzzzzazzzzzzzzzzzz";
+ text[0][15]="zzzzzzzzzzzzzzzazzzzzzzzzzz";
+ text[0][16]="zzzzzzzzzzzzzzzzazzzzzzzzzz";
+ text[0][17]="zzzzzzzzzzzzzzzzzazzzzzzzzz";
+ text[0][18]="zzzzzzzzzzzzzzzzzzazzzzzzzz";
+ text[0][19]="zzzzzzzzzzzzzzzzzzzazzzzzzz";
+ text[0][20]="zzzzzzzzzzzzzzzzzzzzazzzzzz";
+ text[0][21]="zzzzzzzzzzzzzzzzzzzzzazzzzz";
+ text[0][22]="zzzzzzzzzzzzzzzzzzzzzzazzzz";
+ text[0][23]="zzzzzzzzzzzzzzzzzzzzzzzazzz";
+ text[0][24]="zzzzzzzzzzzzzzzzzzzzzzzzazz";
+ text[0][25]="zzzzzzzzzzzzzzzzzzzzzzzzzaz";
+ text[0][26]="zzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1][0]="aBzzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][1]="zaBzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][2]="zzaBzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][3]="zzzaBzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][4]="zzzzaBzzzzzzzzzzzzzzzzzzzzz";
+ text[1][5]="zzzzzaBzzzzzzzzzzzzzzzzzzzz";
+ text[1][6]="zzzzzzaBzzzzzzzzzzzzzzzzzzz";
+ text[1][7]="zzzzzzzaBzzzzzzzzzzzzzzzzzz";
+ text[1][8]="zzzzzzzzaBzzzzzzzzzzzzzzzzz";
+ text[1][9]="zzzzzzzzzaBzzzzzzzzzzzzzzzz";
+ text[1][10]="zzzzzzzzzzaBzzzzzzzzzzzzzzz";
+ text[1][11]="zzzzzzzzzzzaBzzzzzzzzzzzzzz";
+ text[1][12]="zzzzzzzzzzzzaBzzzzzzzzzzzzz";
+ text[1][13]="zzzzzzzzzzzzzaBzzzzzzzzzzzz";
+ text[1][14]="zzzzzzzzzzzzzzaBzzzzzzzzzzz";
+ text[1][15]="zzzzzzzzzzzzzzzaBzzzzzzzzzz";
+ text[1][16]="zzzzzzzzzzzzzzzzaBzzzzzzzzz";
+ text[1][17]="zzzzzzzzzzzzzzzzzaBzzzzzzzz";
+ text[1][18]="zzzzzzzzzzzzzzzzzzaBzzzzzzz";
+ text[1][19]="zzzzzzzzzzzzzzzzzzzaBzzzzzz";
+ text[1][20]="zzzzzzzzzzzzzzzzzzzzaBzzzzz";
+ text[1][21]="zzzzzzzzzzzzzzzzzzzzzaBzzzz";
+ text[1][22]="zzzzzzzzzzzzzzzzzzzzzzaBzzz";
+ text[1][23]="zzzzzzzzzzzzzzzzzzzzzzzaBzz";
+ text[1][24]="zzzzzzzzzzzzzzzzzzzzzzzzaBz";
+ text[1][25]="zzzzzzzzzzzzzzzzzzzzzzzzzaB";
+ text[2][0]="aBczzzzzzzzzzzzzzzzzzzzzzzz";
+ text[2][1]="zaBczzzzzzzzzzzzzzzzzzzzzzz";
+ text[2][2]="zzaBczzzzzzzzzzzzzzzzzzzzzz";
+ text[2][3]="zzzaBczzzzzzzzzzzzzzzzzzzzz";
+ text[2][4]="zzzzaBczzzzzzzzzzzzzzzzzzzz";
+ text[2][5]="zzzzzaBczzzzzzzzzzzzzzzzzzz";
+ text[2][6]="zzzzzzaBczzzzzzzzzzzzzzzzzz";
+ text[2][7]="zzzzzzzaBczzzzzzzzzzzzzzzzz";
+ text[2][8]="zzzzzzzzaBczzzzzzzzzzzzzzzz";
+ text[2][9]="zzzzzzzzzaBczzzzzzzzzzzzzzz";
+ text[2][10]="zzzzzzzzzzaBczzzzzzzzzzzzzz";
+ text[2][11]="zzzzzzzzzzzaBczzzzzzzzzzzzz";
+ text[2][12]="zzzzzzzzzzzzaBczzzzzzzzzzzz";
+ text[2][13]="zzzzzzzzzzzzzaBczzzzzzzzzzz";
+ text[2][14]="zzzzzzzzzzzzzzaBczzzzzzzzzz";
+ text[2][15]="zzzzzzzzzzzzzzzaBczzzzzzzzz";
+ text[2][16]="zzzzzzzzzzzzzzzzaBczzzzzzzz";
+ text[2][17]="zzzzzzzzzzzzzzzzzaBczzzzzzz";
+ text[2][18]="zzzzzzzzzzzzzzzzzzaBczzzzzz";
+ text[2][19]="zzzzzzzzzzzzzzzzzzzaBczzzzz";
+ text[2][20]="zzzzzzzzzzzzzzzzzzzzaBczzzz";
+ text[2][21]="zzzzzzzzzzzzzzzzzzzzzaBczzz";
+ text[2][22]="zzzzzzzzzzzzzzzzzzzzzzaBczz";
+ text[2][23]="zzzzzzzzzzzzzzzzzzzzzzzaBcz";
+ text[2][24]="zzzzzzzzzzzzzzzzzzzzzzzzaBc";
+ text[3][0]="aBcDzzzzzzzzzzzzzzzzzzzzzzz";
+ text[3][1]="zaBcDzzzzzzzzzzzzzzzzzzzzzz";
+ text[3][2]="zzaBcDzzzzzzzzzzzzzzzzzzzzz";
+ text[3][3]="zzzaBcDzzzzzzzzzzzzzzzzzzzz";
+ text[3][4]="zzzzaBcDzzzzzzzzzzzzzzzzzzz";
+ text[3][5]="zzzzzaBcDzzzzzzzzzzzzzzzzzz";
+ text[3][6]="zzzzzzaBcDzzzzzzzzzzzzzzzzz";
+ text[3][7]="zzzzzzzaBcDzzzzzzzzzzzzzzzz";
+ text[3][8]="zzzzzzzzaBcDzzzzzzzzzzzzzzz";
+ text[3][9]="zzzzzzzzzaBcDzzzzzzzzzzzzzz";
+ text[3][10]="zzzzzzzzzzaBcDzzzzzzzzzzzzz";
+ text[3][11]="zzzzzzzzzzzaBcDzzzzzzzzzzzz";
+ text[3][12]="zzzzzzzzzzzzaBcDzzzzzzzzzzz";
+ text[3][13]="zzzzzzzzzzzzzaBcDzzzzzzzzzz";
+ text[3][14]="zzzzzzzzzzzzzzaBcDzzzzzzzzz";
+ text[3][15]="zzzzzzzzzzzzzzzaBcDzzzzzzzz";
+ text[3][16]="zzzzzzzzzzzzzzzzaBcDzzzzzzz";
+ text[3][17]="zzzzzzzzzzzzzzzzzaBcDzzzzzz";
+ text[3][18]="zzzzzzzzzzzzzzzzzzaBcDzzzzz";
+ text[3][19]="zzzzzzzzzzzzzzzzzzzaBcDzzzz";
+ text[3][20]="zzzzzzzzzzzzzzzzzzzzaBcDzzz";
+ text[3][21]="zzzzzzzzzzzzzzzzzzzzzaBcDzz";
+ text[3][22]="zzzzzzzzzzzzzzzzzzzzzzaBcDz";
+ text[3][23]="zzzzzzzzzzzzzzzzzzzzzzzaBcD";
+ text[4][0]="aBcDezzzzzzzzzzzzzzzzzzzzzz";
+ text[4][1]="zaBcDezzzzzzzzzzzzzzzzzzzzz";
+ text[4][2]="zzaBcDezzzzzzzzzzzzzzzzzzzz";
+ text[4][3]="zzzaBcDezzzzzzzzzzzzzzzzzzz";
+ text[4][4]="zzzzaBcDezzzzzzzzzzzzzzzzzz";
+ text[4][5]="zzzzzaBcDezzzzzzzzzzzzzzzzz";
+ text[4][6]="zzzzzzaBcDezzzzzzzzzzzzzzzz";
+ text[4][7]="zzzzzzzaBcDezzzzzzzzzzzzzzz";
+ text[4][8]="zzzzzzzzaBcDezzzzzzzzzzzzzz";
+ text[4][9]="zzzzzzzzzaBcDezzzzzzzzzzzzz";
+ text[4][10]="zzzzzzzzzzaBcDezzzzzzzzzzzz";
+ text[4][11]="zzzzzzzzzzzaBcDezzzzzzzzzzz";
+ text[4][12]="zzzzzzzzzzzzaBcDezzzzzzzzzz";
+ text[4][13]="zzzzzzzzzzzzzaBcDezzzzzzzzz";
+ text[4][14]="zzzzzzzzzzzzzzaBcDezzzzzzzz";
+ text[4][15]="zzzzzzzzzzzzzzzaBcDezzzzzzz";
+ text[4][16]="zzzzzzzzzzzzzzzzaBcDezzzzzz";
+ text[4][17]="zzzzzzzzzzzzzzzzzaBcDezzzzz";
+ text[4][18]="zzzzzzzzzzzzzzzzzzaBcDezzzz";
+ text[4][19]="zzzzzzzzzzzzzzzzzzzaBcDezzz";
+ text[4][20]="zzzzzzzzzzzzzzzzzzzzaBcDezz";
+ text[4][21]="zzzzzzzzzzzzzzzzzzzzzaBcDez";
+ text[4][22]="zzzzzzzzzzzzzzzzzzzzzzaBcDe";
+ text[5][0]="aBcDeFzzzzzzzzzzzzzzzzzzzzz";
+ text[5][1]="zaBcDeFzzzzzzzzzzzzzzzzzzzz";
+ text[5][2]="zzaBcDeFzzzzzzzzzzzzzzzzzzz";
+ text[5][3]="zzzaBcDeFzzzzzzzzzzzzzzzzzz";
+ text[5][4]="zzzzaBcDeFzzzzzzzzzzzzzzzzz";
+ text[5][5]="zzzzzaBcDeFzzzzzzzzzzzzzzzz";
+ text[5][6]="zzzzzzaBcDeFzzzzzzzzzzzzzzz";
+ text[5][7]="zzzzzzzaBcDeFzzzzzzzzzzzzzz";
+ text[5][8]="zzzzzzzzaBcDeFzzzzzzzzzzzzz";
+ text[5][9]="zzzzzzzzzaBcDeFzzzzzzzzzzzz";
+ text[5][10]="zzzzzzzzzzaBcDeFzzzzzzzzzzz";
+ text[5][11]="zzzzzzzzzzzaBcDeFzzzzzzzzzz";
+ text[5][12]="zzzzzzzzzzzzaBcDeFzzzzzzzzz";
+ text[5][13]="zzzzzzzzzzzzzaBcDeFzzzzzzzz";
+ text[5][14]="zzzzzzzzzzzzzzaBcDeFzzzzzzz";
+ text[5][15]="zzzzzzzzzzzzzzzaBcDeFzzzzzz";
+ text[5][16]="zzzzzzzzzzzzzzzzaBcDeFzzzzz";
+ text[5][17]="zzzzzzzzzzzzzzzzzaBcDeFzzzz";
+ text[5][18]="zzzzzzzzzzzzzzzzzzaBcDeFzzz";
+ text[5][19]="zzzzzzzzzzzzzzzzzzzaBcDeFzz";
+ text[5][20]="zzzzzzzzzzzzzzzzzzzzaBcDeFz";
+ text[5][21]="zzzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6][0]="aBcDeFgzzzzzzzzzzzzzzzzzzzz";
+ text[6][1]="zaBcDeFgzzzzzzzzzzzzzzzzzzz";
+ text[6][2]="zzaBcDeFgzzzzzzzzzzzzzzzzzz";
+ text[6][3]="zzzaBcDeFgzzzzzzzzzzzzzzzzz";
+ text[6][4]="zzzzaBcDeFgzzzzzzzzzzzzzzzz";
+ text[6][5]="zzzzzaBcDeFgzzzzzzzzzzzzzzz";
+ text[6][6]="zzzzzzaBcDeFgzzzzzzzzzzzzzz";
+ text[6][7]="zzzzzzzaBcDeFgzzzzzzzzzzzzz";
+ text[6][8]="zzzzzzzzaBcDeFgzzzzzzzzzzzz";
+ text[6][9]="zzzzzzzzzaBcDeFgzzzzzzzzzzz";
+ text[6][10]="zzzzzzzzzzaBcDeFgzzzzzzzzzz";
+ text[6][11]="zzzzzzzzzzzaBcDeFgzzzzzzzzz";
+ text[6][12]="zzzzzzzzzzzzaBcDeFgzzzzzzzz";
+ text[6][13]="zzzzzzzzzzzzzaBcDeFgzzzzzzz";
+ text[6][14]="zzzzzzzzzzzzzzaBcDeFgzzzzzz";
+ text[6][15]="zzzzzzzzzzzzzzzaBcDeFgzzzzz";
+ text[6][16]="zzzzzzzzzzzzzzzzaBcDeFgzzzz";
+ text[6][17]="zzzzzzzzzzzzzzzzzaBcDeFgzzz";
+ text[6][18]="zzzzzzzzzzzzzzzzzzaBcDeFgzz";
+ text[6][19]="zzzzzzzzzzzzzzzzzzzaBcDeFgz";
+ text[6][20]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7][0]="aBcDeFgHzzzzzzzzzzzzzzzzzzz";
+ text[7][1]="zaBcDeFgHzzzzzzzzzzzzzzzzzz";
+ text[7][2]="zzaBcDeFgHzzzzzzzzzzzzzzzzz";
+ text[7][3]="zzzaBcDeFgHzzzzzzzzzzzzzzzz";
+ text[7][4]="zzzzaBcDeFgHzzzzzzzzzzzzzzz";
+ text[7][5]="zzzzzaBcDeFgHzzzzzzzzzzzzzz";
+ text[7][6]="zzzzzzaBcDeFgHzzzzzzzzzzzzz";
+ text[7][7]="zzzzzzzaBcDeFgHzzzzzzzzzzzz";
+ text[7][8]="zzzzzzzzaBcDeFgHzzzzzzzzzzz";
+ text[7][9]="zzzzzzzzzaBcDeFgHzzzzzzzzzz";
+ text[7][10]="zzzzzzzzzzaBcDeFgHzzzzzzzzz";
+ text[7][11]="zzzzzzzzzzzaBcDeFgHzzzzzzzz";
+ text[7][12]="zzzzzzzzzzzzaBcDeFgHzzzzzzz";
+ text[7][13]="zzzzzzzzzzzzzaBcDeFgHzzzzzz";
+ text[7][14]="zzzzzzzzzzzzzzaBcDeFgHzzzzz";
+ text[7][15]="zzzzzzzzzzzzzzzaBcDeFgHzzzz";
+ text[7][16]="zzzzzzzzzzzzzzzzaBcDeFgHzzz";
+ text[7][17]="zzzzzzzzzzzzzzzzzaBcDeFgHzz";
+ text[7][18]="zzzzzzzzzzzzzzzzzzaBcDeFgHz";
+ text[7][19]="zzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8][0]="aBcDeFgHizzzzzzzzzzzzzzzzzz";
+ text[8][1]="zaBcDeFgHizzzzzzzzzzzzzzzzz";
+ text[8][2]="zzaBcDeFgHizzzzzzzzzzzzzzzz";
+ text[8][3]="zzzaBcDeFgHizzzzzzzzzzzzzzz";
+ text[8][4]="zzzzaBcDeFgHizzzzzzzzzzzzzz";
+ text[8][5]="zzzzzaBcDeFgHizzzzzzzzzzzzz";
+ text[8][6]="zzzzzzaBcDeFgHizzzzzzzzzzzz";
+ text[8][7]="zzzzzzzaBcDeFgHizzzzzzzzzzz";
+ text[8][8]="zzzzzzzzaBcDeFgHizzzzzzzzzz";
+ text[8][9]="zzzzzzzzzaBcDeFgHizzzzzzzzz";
+ text[8][10]="zzzzzzzzzzaBcDeFgHizzzzzzzz";
+ text[8][11]="zzzzzzzzzzzaBcDeFgHizzzzzzz";
+ text[8][12]="zzzzzzzzzzzzaBcDeFgHizzzzzz";
+ text[8][13]="zzzzzzzzzzzzzaBcDeFgHizzzzz";
+ text[8][14]="zzzzzzzzzzzzzzaBcDeFgHizzzz";
+ text[8][15]="zzzzzzzzzzzzzzzaBcDeFgHizzz";
+ text[8][16]="zzzzzzzzzzzzzzzzaBcDeFgHizz";
+ text[8][17]="zzzzzzzzzzzzzzzzzaBcDeFgHiz";
+ text[8][18]="zzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9][0]="aBcDeFgHiJzzzzzzzzzzzzzzzzz";
+ text[9][1]="zaBcDeFgHiJzzzzzzzzzzzzzzzz";
+ text[9][2]="zzaBcDeFgHiJzzzzzzzzzzzzzzz";
+ text[9][3]="zzzaBcDeFgHiJzzzzzzzzzzzzzz";
+ text[9][4]="zzzzaBcDeFgHiJzzzzzzzzzzzzz";
+ text[9][5]="zzzzzaBcDeFgHiJzzzzzzzzzzzz";
+ text[9][6]="zzzzzzaBcDeFgHiJzzzzzzzzzzz";
+ text[9][7]="zzzzzzzaBcDeFgHiJzzzzzzzzzz";
+ text[9][8]="zzzzzzzzaBcDeFgHiJzzzzzzzzz";
+ text[9][9]="zzzzzzzzzaBcDeFgHiJzzzzzzzz";
+ text[9][10]="zzzzzzzzzzaBcDeFgHiJzzzzzzz";
+ text[9][11]="zzzzzzzzzzzaBcDeFgHiJzzzzzz";
+ text[9][12]="zzzzzzzzzzzzaBcDeFgHiJzzzzz";
+ text[9][13]="zzzzzzzzzzzzzaBcDeFgHiJzzzz";
+ text[9][14]="zzzzzzzzzzzzzzaBcDeFgHiJzzz";
+ text[9][15]="zzzzzzzzzzzzzzzaBcDeFgHiJzz";
+ text[9][16]="zzzzzzzzzzzzzzzzaBcDeFgHiJz";
+ text[9][17]="zzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10][0]="aBcDeFgHiJkzzzzzzzzzzzzzzzz";
+ text[10][1]="zaBcDeFgHiJkzzzzzzzzzzzzzzz";
+ text[10][2]="zzaBcDeFgHiJkzzzzzzzzzzzzzz";
+ text[10][3]="zzzaBcDeFgHiJkzzzzzzzzzzzzz";
+ text[10][4]="zzzzaBcDeFgHiJkzzzzzzzzzzzz";
+ text[10][5]="zzzzzaBcDeFgHiJkzzzzzzzzzzz";
+ text[10][6]="zzzzzzaBcDeFgHiJkzzzzzzzzzz";
+ text[10][7]="zzzzzzzaBcDeFgHiJkzzzzzzzzz";
+ text[10][8]="zzzzzzzzaBcDeFgHiJkzzzzzzzz";
+ text[10][9]="zzzzzzzzzaBcDeFgHiJkzzzzzzz";
+ text[10][10]="zzzzzzzzzzaBcDeFgHiJkzzzzzz";
+ text[10][11]="zzzzzzzzzzzaBcDeFgHiJkzzzzz";
+ text[10][12]="zzzzzzzzzzzzaBcDeFgHiJkzzzz";
+ text[10][13]="zzzzzzzzzzzzzaBcDeFgHiJkzzz";
+ text[10][14]="zzzzzzzzzzzzzzaBcDeFgHiJkzz";
+ text[10][15]="zzzzzzzzzzzzzzzaBcDeFgHiJkz";
+ text[10][16]="zzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11][0]="aBcDeFgHiJkLzzzzzzzzzzzzzzz";
+ text[11][1]="zaBcDeFgHiJkLzzzzzzzzzzzzzz";
+ text[11][2]="zzaBcDeFgHiJkLzzzzzzzzzzzzz";
+ text[11][3]="zzzaBcDeFgHiJkLzzzzzzzzzzzz";
+ text[11][4]="zzzzaBcDeFgHiJkLzzzzzzzzzzz";
+ text[11][5]="zzzzzaBcDeFgHiJkLzzzzzzzzzz";
+ text[11][6]="zzzzzzaBcDeFgHiJkLzzzzzzzzz";
+ text[11][7]="zzzzzzzaBcDeFgHiJkLzzzzzzzz";
+ text[11][8]="zzzzzzzzaBcDeFgHiJkLzzzzzzz";
+ text[11][9]="zzzzzzzzzaBcDeFgHiJkLzzzzzz";
+ text[11][10]="zzzzzzzzzzaBcDeFgHiJkLzzzzz";
+ text[11][11]="zzzzzzzzzzzaBcDeFgHiJkLzzzz";
+ text[11][12]="zzzzzzzzzzzzaBcDeFgHiJkLzzz";
+ text[11][13]="zzzzzzzzzzzzzaBcDeFgHiJkLzz";
+ text[11][14]="zzzzzzzzzzzzzzaBcDeFgHiJkLz";
+ text[11][15]="zzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12][0]="aBcDeFgHiJkLmzzzzzzzzzzzzzz";
+ text[12][1]="zaBcDeFgHiJkLmzzzzzzzzzzzzz";
+ text[12][2]="zzaBcDeFgHiJkLmzzzzzzzzzzzz";
+ text[12][3]="zzzaBcDeFgHiJkLmzzzzzzzzzzz";
+ text[12][4]="zzzzaBcDeFgHiJkLmzzzzzzzzzz";
+ text[12][5]="zzzzzaBcDeFgHiJkLmzzzzzzzzz";
+ text[12][6]="zzzzzzaBcDeFgHiJkLmzzzzzzzz";
+ text[12][7]="zzzzzzzaBcDeFgHiJkLmzzzzzzz";
+ text[12][8]="zzzzzzzzaBcDeFgHiJkLmzzzzzz";
+ text[12][9]="zzzzzzzzzaBcDeFgHiJkLmzzzzz";
+ text[12][10]="zzzzzzzzzzaBcDeFgHiJkLmzzzz";
+ text[12][11]="zzzzzzzzzzzaBcDeFgHiJkLmzzz";
+ text[12][12]="zzzzzzzzzzzzaBcDeFgHiJkLmzz";
+ text[12][13]="zzzzzzzzzzzzzaBcDeFgHiJkLmz";
+ text[12][14]="zzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13][0]="aBcDeFgHiJkLmNzzzzzzzzzzzzz";
+ text[13][1]="zaBcDeFgHiJkLmNzzzzzzzzzzzz";
+ text[13][2]="zzaBcDeFgHiJkLmNzzzzzzzzzzz";
+ text[13][3]="zzzaBcDeFgHiJkLmNzzzzzzzzzz";
+ text[13][4]="zzzzaBcDeFgHiJkLmNzzzzzzzzz";
+ text[13][5]="zzzzzaBcDeFgHiJkLmNzzzzzzzz";
+ text[13][6]="zzzzzzaBcDeFgHiJkLmNzzzzzzz";
+ text[13][7]="zzzzzzzaBcDeFgHiJkLmNzzzzzz";
+ text[13][8]="zzzzzzzzaBcDeFgHiJkLmNzzzzz";
+ text[13][9]="zzzzzzzzzaBcDeFgHiJkLmNzzzz";
+ text[13][10]="zzzzzzzzzzaBcDeFgHiJkLmNzzz";
+ text[13][11]="zzzzzzzzzzzaBcDeFgHiJkLmNzz";
+ text[13][12]="zzzzzzzzzzzzaBcDeFgHiJkLmNz";
+ text[13][13]="zzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14][0]="aBcDeFgHiJkLmNozzzzzzzzzzzz";
+ text[14][1]="zaBcDeFgHiJkLmNozzzzzzzzzzz";
+ text[14][2]="zzaBcDeFgHiJkLmNozzzzzzzzzz";
+ text[14][3]="zzzaBcDeFgHiJkLmNozzzzzzzzz";
+ text[14][4]="zzzzaBcDeFgHiJkLmNozzzzzzzz";
+ text[14][5]="zzzzzaBcDeFgHiJkLmNozzzzzzz";
+ text[14][6]="zzzzzzaBcDeFgHiJkLmNozzzzzz";
+ text[14][7]="zzzzzzzaBcDeFgHiJkLmNozzzzz";
+ text[14][8]="zzzzzzzzaBcDeFgHiJkLmNozzzz";
+ text[14][9]="zzzzzzzzzaBcDeFgHiJkLmNozzz";
+ text[14][10]="zzzzzzzzzzaBcDeFgHiJkLmNozz";
+ text[14][11]="zzzzzzzzzzzaBcDeFgHiJkLmNoz";
+ text[14][12]="zzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15][0]="aBcDeFgHiJkLmNoPzzzzzzzzzzz";
+ text[15][1]="zaBcDeFgHiJkLmNoPzzzzzzzzzz";
+ text[15][2]="zzaBcDeFgHiJkLmNoPzzzzzzzzz";
+ text[15][3]="zzzaBcDeFgHiJkLmNoPzzzzzzzz";
+ text[15][4]="zzzzaBcDeFgHiJkLmNoPzzzzzzz";
+ text[15][5]="zzzzzaBcDeFgHiJkLmNoPzzzzzz";
+ text[15][6]="zzzzzzaBcDeFgHiJkLmNoPzzzzz";
+ text[15][7]="zzzzzzzaBcDeFgHiJkLmNoPzzzz";
+ text[15][8]="zzzzzzzzaBcDeFgHiJkLmNoPzzz";
+ text[15][9]="zzzzzzzzzaBcDeFgHiJkLmNoPzz";
+ text[15][10]="zzzzzzzzzzaBcDeFgHiJkLmNoPz";
+ text[15][11]="zzzzzzzzzzzaBcDeFgHiJkLmNoP";
+ text[16][0]="aBcDeFgHiJkLmNoPqzzzzzzzzzz";
+ text[16][1]="zaBcDeFgHiJkLmNoPqzzzzzzzzz";
+ text[16][2]="zzaBcDeFgHiJkLmNoPqzzzzzzzz";
+ text[16][3]="zzzaBcDeFgHiJkLmNoPqzzzzzzz";
+ text[16][4]="zzzzaBcDeFgHiJkLmNoPqzzzzzz";
+ text[16][5]="zzzzzaBcDeFgHiJkLmNoPqzzzzz";
+ text[16][6]="zzzzzzaBcDeFgHiJkLmNoPqzzzz";
+ text[16][7]="zzzzzzzaBcDeFgHiJkLmNoPqzzz";
+ text[16][8]="zzzzzzzzaBcDeFgHiJkLmNoPqzz";
+ text[16][9]="zzzzzzzzzaBcDeFgHiJkLmNoPqz";
+ text[16][10]="zzzzzzzzzzaBcDeFgHiJkLmNoPq";
+ text[17][0]="aBcDeFgHiJkLmNoPqRzzzzzzzzz";
+ text[17][1]="zaBcDeFgHiJkLmNoPqRzzzzzzzz";
+ text[17][2]="zzaBcDeFgHiJkLmNoPqRzzzzzzz";
+ text[17][3]="zzzaBcDeFgHiJkLmNoPqRzzzzzz";
+ text[17][4]="zzzzaBcDeFgHiJkLmNoPqRzzzzz";
+ text[17][5]="zzzzzaBcDeFgHiJkLmNoPqRzzzz";
+ text[17][6]="zzzzzzaBcDeFgHiJkLmNoPqRzzz";
+ text[17][7]="zzzzzzzaBcDeFgHiJkLmNoPqRzz";
+ text[17][8]="zzzzzzzzaBcDeFgHiJkLmNoPqRz";
+ text[17][9]="zzzzzzzzzaBcDeFgHiJkLmNoPqR";
+ text[18][0]="aBcDeFgHiJkLmNoPqRszzzzzzzz";
+ text[18][1]="zaBcDeFgHiJkLmNoPqRszzzzzzz";
+ text[18][2]="zzaBcDeFgHiJkLmNoPqRszzzzzz";
+ text[18][3]="zzzaBcDeFgHiJkLmNoPqRszzzzz";
+ text[18][4]="zzzzaBcDeFgHiJkLmNoPqRszzzz";
+ text[18][5]="zzzzzaBcDeFgHiJkLmNoPqRszzz";
+ text[18][6]="zzzzzzaBcDeFgHiJkLmNoPqRszz";
+ text[18][7]="zzzzzzzaBcDeFgHiJkLmNoPqRsz";
+ text[18][8]="zzzzzzzzaBcDeFgHiJkLmNoPqRs";
+ text[19][0]="aBcDeFgHiJkLmNoPqRsTzzzzzzz";
+ text[19][1]="zaBcDeFgHiJkLmNoPqRsTzzzzzz";
+ text[19][2]="zzaBcDeFgHiJkLmNoPqRsTzzzzz";
+ text[19][3]="zzzaBcDeFgHiJkLmNoPqRsTzzzz";
+ text[19][4]="zzzzaBcDeFgHiJkLmNoPqRsTzzz";
+ text[19][5]="zzzzzaBcDeFgHiJkLmNoPqRsTzz";
+ text[19][6]="zzzzzzaBcDeFgHiJkLmNoPqRsTz";
+ text[19][7]="zzzzzzzaBcDeFgHiJkLmNoPqRsT";
+ text[20][0]="aBcDeFgHiJkLmNoPqRsTuzzzzzz";
+ text[20][1]="zaBcDeFgHiJkLmNoPqRsTuzzzzz";
+ text[20][2]="zzaBcDeFgHiJkLmNoPqRsTuzzzz";
+ text[20][3]="zzzaBcDeFgHiJkLmNoPqRsTuzzz";
+ text[20][4]="zzzzaBcDeFgHiJkLmNoPqRsTuzz";
+ text[20][5]="zzzzzaBcDeFgHiJkLmNoPqRsTuz";
+ text[20][6]="zzzzzzaBcDeFgHiJkLmNoPqRsTu";
+ text[21][0]="aBcDeFgHiJkLmNoPqRsTuVzzzzz";
+ text[21][1]="zaBcDeFgHiJkLmNoPqRsTuVzzzz";
+ text[21][2]="zzaBcDeFgHiJkLmNoPqRsTuVzzz";
+ text[21][3]="zzzaBcDeFgHiJkLmNoPqRsTuVzz";
+ text[21][4]="zzzzaBcDeFgHiJkLmNoPqRsTuVz";
+ text[21][5]="zzzzzaBcDeFgHiJkLmNoPqRsTuV";
+ text[22][0]="aBcDeFgHiJkLmNoPqRsTuVwzzzz";
+ text[22][1]="zaBcDeFgHiJkLmNoPqRsTuVwzzz";
+ text[22][2]="zzaBcDeFgHiJkLmNoPqRsTuVwzz";
+ text[22][3]="zzzaBcDeFgHiJkLmNoPqRsTuVwz";
+ text[22][4]="zzzzaBcDeFgHiJkLmNoPqRsTuVw";
+ text[23][0]="aBcDeFgHiJkLmNoPqRsTuVwXzzz";
+ text[23][1]="zaBcDeFgHiJkLmNoPqRsTuVwXzz";
+ text[23][2]="zzaBcDeFgHiJkLmNoPqRsTuVwXz";
+ text[23][3]="zzzaBcDeFgHiJkLmNoPqRsTuVwX";
+ text[24][0]="aBcDeFgHiJkLmNoPqRsTuVwXyzz";
+ text[24][1]="zaBcDeFgHiJkLmNoPqRsTuVwXyz";
+ text[24][2]="zzaBcDeFgHiJkLmNoPqRsTuVwXy";
+ text[25][0]="aBcDeFgHiJkLmNoPqRsTuVwXyZz";
+ text[25][1]="zaBcDeFgHiJkLmNoPqRsTuVwXyZ";
+
+ char *needle[26];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+ needle[16]="aBcDeFgHiJkLmNoPq";
+ needle[17]="aBcDeFgHiJkLmNoPqR";
+ needle[18]="aBcDeFgHiJkLmNoPqRs";
+ needle[19]="aBcDeFgHiJkLmNoPqRsT";
+ needle[20]="aBcDeFgHiJkLmNoPqRsTu";
+ needle[21]="aBcDeFgHiJkLmNoPqRsTuV";
+ needle[22]="aBcDeFgHiJkLmNoPqRsTuVw";
+ needle[23]="aBcDeFgHiJkLmNoPqRsTuVwX";
+ needle[24]="aBcDeFgHiJkLmNoPqRsTuVwXy";
+ needle[25]="aBcDeFgHiJkLmNoPqRsTuVwXyZ";
+
+ int i, j;
+ uint8_t *found = NULL;
+ for (i = 0; i < 26; i++) {
+ for (j = 0; j <= (26 - i); j++) {
+ found = BasicSearchWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ found = Bs2bmWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ found = BoyerMooreWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+/**
+ * \test Check that all the algorithms (no case) work at any offset and any pattern length
+ */
+int UtilSpmSearchOffsetsNocaseTest01()
+{
+ char *text[26][27];
+ text[0][0]="azzzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][1]="zazzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][2]="zzazzzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][3]="zzzazzzzzzzzzzzzzzzzzzzzzzz";
+ text[0][4]="zzzzazzzzzzzzzzzzzzzzzzzzzz";
+ text[0][5]="zzzzzazzzzzzzzzzzzzzzzzzzzz";
+ text[0][6]="zzzzzzazzzzzzzzzzzzzzzzzzzz";
+ text[0][7]="zzzzzzzazzzzzzzzzzzzzzzzzzz";
+ text[0][8]="zzzzzzzzazzzzzzzzzzzzzzzzzz";
+ text[0][9]="zzzzzzzzzazzzzzzzzzzzzzzzzz";
+ text[0][10]="zzzzzzzzzzazzzzzzzzzzzzzzzz";
+ text[0][11]="zzzzzzzzzzzazzzzzzzzzzzzzzz";
+ text[0][12]="zzzzzzzzzzzzazzzzzzzzzzzzzz";
+ text[0][13]="zzzzzzzzzzzzzazzzzzzzzzzzzz";
+ text[0][14]="zzzzzzzzzzzzzzazzzzzzzzzzzz";
+ text[0][15]="zzzzzzzzzzzzzzzazzzzzzzzzzz";
+ text[0][16]="zzzzzzzzzzzzzzzzazzzzzzzzzz";
+ text[0][17]="zzzzzzzzzzzzzzzzzazzzzzzzzz";
+ text[0][18]="zzzzzzzzzzzzzzzzzzazzzzzzzz";
+ text[0][19]="zzzzzzzzzzzzzzzzzzzazzzzzzz";
+ text[0][20]="zzzzzzzzzzzzzzzzzzzzazzzzzz";
+ text[0][21]="zzzzzzzzzzzzzzzzzzzzzazzzzz";
+ text[0][22]="zzzzzzzzzzzzzzzzzzzzzzazzzz";
+ text[0][23]="zzzzzzzzzzzzzzzzzzzzzzzazzz";
+ text[0][24]="zzzzzzzzzzzzzzzzzzzzzzzzazz";
+ text[0][25]="zzzzzzzzzzzzzzzzzzzzzzzzzaz";
+ text[0][26]="zzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1][0]="aBzzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][1]="zaBzzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][2]="zzaBzzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][3]="zzzaBzzzzzzzzzzzzzzzzzzzzzz";
+ text[1][4]="zzzzaBzzzzzzzzzzzzzzzzzzzzz";
+ text[1][5]="zzzzzaBzzzzzzzzzzzzzzzzzzzz";
+ text[1][6]="zzzzzzaBzzzzzzzzzzzzzzzzzzz";
+ text[1][7]="zzzzzzzaBzzzzzzzzzzzzzzzzzz";
+ text[1][8]="zzzzzzzzaBzzzzzzzzzzzzzzzzz";
+ text[1][9]="zzzzzzzzzaBzzzzzzzzzzzzzzzz";
+ text[1][10]="zzzzzzzzzzaBzzzzzzzzzzzzzzz";
+ text[1][11]="zzzzzzzzzzzaBzzzzzzzzzzzzzz";
+ text[1][12]="zzzzzzzzzzzzaBzzzzzzzzzzzzz";
+ text[1][13]="zzzzzzzzzzzzzaBzzzzzzzzzzzz";
+ text[1][14]="zzzzzzzzzzzzzzaBzzzzzzzzzzz";
+ text[1][15]="zzzzzzzzzzzzzzzaBzzzzzzzzzz";
+ text[1][16]="zzzzzzzzzzzzzzzzaBzzzzzzzzz";
+ text[1][17]="zzzzzzzzzzzzzzzzzaBzzzzzzzz";
+ text[1][18]="zzzzzzzzzzzzzzzzzzaBzzzzzzz";
+ text[1][19]="zzzzzzzzzzzzzzzzzzzaBzzzzzz";
+ text[1][20]="zzzzzzzzzzzzzzzzzzzzaBzzzzz";
+ text[1][21]="zzzzzzzzzzzzzzzzzzzzzaBzzzz";
+ text[1][22]="zzzzzzzzzzzzzzzzzzzzzzaBzzz";
+ text[1][23]="zzzzzzzzzzzzzzzzzzzzzzzaBzz";
+ text[1][24]="zzzzzzzzzzzzzzzzzzzzzzzzaBz";
+ text[1][25]="zzzzzzzzzzzzzzzzzzzzzzzzzaB";
+ text[2][0]="aBczzzzzzzzzzzzzzzzzzzzzzzz";
+ text[2][1]="zaBczzzzzzzzzzzzzzzzzzzzzzz";
+ text[2][2]="zzaBczzzzzzzzzzzzzzzzzzzzzz";
+ text[2][3]="zzzaBczzzzzzzzzzzzzzzzzzzzz";
+ text[2][4]="zzzzaBczzzzzzzzzzzzzzzzzzzz";
+ text[2][5]="zzzzzaBczzzzzzzzzzzzzzzzzzz";
+ text[2][6]="zzzzzzaBczzzzzzzzzzzzzzzzzz";
+ text[2][7]="zzzzzzzaBczzzzzzzzzzzzzzzzz";
+ text[2][8]="zzzzzzzzaBczzzzzzzzzzzzzzzz";
+ text[2][9]="zzzzzzzzzaBczzzzzzzzzzzzzzz";
+ text[2][10]="zzzzzzzzzzaBczzzzzzzzzzzzzz";
+ text[2][11]="zzzzzzzzzzzaBczzzzzzzzzzzzz";
+ text[2][12]="zzzzzzzzzzzzaBczzzzzzzzzzzz";
+ text[2][13]="zzzzzzzzzzzzzaBczzzzzzzzzzz";
+ text[2][14]="zzzzzzzzzzzzzzaBczzzzzzzzzz";
+ text[2][15]="zzzzzzzzzzzzzzzaBczzzzzzzzz";
+ text[2][16]="zzzzzzzzzzzzzzzzaBczzzzzzzz";
+ text[2][17]="zzzzzzzzzzzzzzzzzaBczzzzzzz";
+ text[2][18]="zzzzzzzzzzzzzzzzzzaBczzzzzz";
+ text[2][19]="zzzzzzzzzzzzzzzzzzzaBczzzzz";
+ text[2][20]="zzzzzzzzzzzzzzzzzzzzaBczzzz";
+ text[2][21]="zzzzzzzzzzzzzzzzzzzzzaBczzz";
+ text[2][22]="zzzzzzzzzzzzzzzzzzzzzzaBczz";
+ text[2][23]="zzzzzzzzzzzzzzzzzzzzzzzaBcz";
+ text[2][24]="zzzzzzzzzzzzzzzzzzzzzzzzaBc";
+ text[3][0]="aBcDzzzzzzzzzzzzzzzzzzzzzzz";
+ text[3][1]="zaBcDzzzzzzzzzzzzzzzzzzzzzz";
+ text[3][2]="zzaBcDzzzzzzzzzzzzzzzzzzzzz";
+ text[3][3]="zzzaBcDzzzzzzzzzzzzzzzzzzzz";
+ text[3][4]="zzzzaBcDzzzzzzzzzzzzzzzzzzz";
+ text[3][5]="zzzzzaBcDzzzzzzzzzzzzzzzzzz";
+ text[3][6]="zzzzzzaBcDzzzzzzzzzzzzzzzzz";
+ text[3][7]="zzzzzzzaBcDzzzzzzzzzzzzzzzz";
+ text[3][8]="zzzzzzzzaBcDzzzzzzzzzzzzzzz";
+ text[3][9]="zzzzzzzzzaBcDzzzzzzzzzzzzzz";
+ text[3][10]="zzzzzzzzzzaBcDzzzzzzzzzzzzz";
+ text[3][11]="zzzzzzzzzzzaBcDzzzzzzzzzzzz";
+ text[3][12]="zzzzzzzzzzzzaBcDzzzzzzzzzzz";
+ text[3][13]="zzzzzzzzzzzzzaBcDzzzzzzzzzz";
+ text[3][14]="zzzzzzzzzzzzzzaBcDzzzzzzzzz";
+ text[3][15]="zzzzzzzzzzzzzzzaBcDzzzzzzzz";
+ text[3][16]="zzzzzzzzzzzzzzzzaBcDzzzzzzz";
+ text[3][17]="zzzzzzzzzzzzzzzzzaBcDzzzzzz";
+ text[3][18]="zzzzzzzzzzzzzzzzzzaBcDzzzzz";
+ text[3][19]="zzzzzzzzzzzzzzzzzzzaBcDzzzz";
+ text[3][20]="zzzzzzzzzzzzzzzzzzzzaBcDzzz";
+ text[3][21]="zzzzzzzzzzzzzzzzzzzzzaBcDzz";
+ text[3][22]="zzzzzzzzzzzzzzzzzzzzzzaBcDz";
+ text[3][23]="zzzzzzzzzzzzzzzzzzzzzzzaBcD";
+ text[4][0]="aBcDezzzzzzzzzzzzzzzzzzzzzz";
+ text[4][1]="zaBcDezzzzzzzzzzzzzzzzzzzzz";
+ text[4][2]="zzaBcDezzzzzzzzzzzzzzzzzzzz";
+ text[4][3]="zzzaBcDezzzzzzzzzzzzzzzzzzz";
+ text[4][4]="zzzzaBcDezzzzzzzzzzzzzzzzzz";
+ text[4][5]="zzzzzaBcDezzzzzzzzzzzzzzzzz";
+ text[4][6]="zzzzzzaBcDezzzzzzzzzzzzzzzz";
+ text[4][7]="zzzzzzzaBcDezzzzzzzzzzzzzzz";
+ text[4][8]="zzzzzzzzaBcDezzzzzzzzzzzzzz";
+ text[4][9]="zzzzzzzzzaBcDezzzzzzzzzzzzz";
+ text[4][10]="zzzzzzzzzzaBcDezzzzzzzzzzzz";
+ text[4][11]="zzzzzzzzzzzaBcDezzzzzzzzzzz";
+ text[4][12]="zzzzzzzzzzzzaBcDezzzzzzzzzz";
+ text[4][13]="zzzzzzzzzzzzzaBcDezzzzzzzzz";
+ text[4][14]="zzzzzzzzzzzzzzaBcDezzzzzzzz";
+ text[4][15]="zzzzzzzzzzzzzzzaBcDezzzzzzz";
+ text[4][16]="zzzzzzzzzzzzzzzzaBcDezzzzzz";
+ text[4][17]="zzzzzzzzzzzzzzzzzaBcDezzzzz";
+ text[4][18]="zzzzzzzzzzzzzzzzzzaBcDezzzz";
+ text[4][19]="zzzzzzzzzzzzzzzzzzzaBcDezzz";
+ text[4][20]="zzzzzzzzzzzzzzzzzzzzaBcDezz";
+ text[4][21]="zzzzzzzzzzzzzzzzzzzzzaBcDez";
+ text[4][22]="zzzzzzzzzzzzzzzzzzzzzzaBcDe";
+ text[5][0]="aBcDeFzzzzzzzzzzzzzzzzzzzzz";
+ text[5][1]="zaBcDeFzzzzzzzzzzzzzzzzzzzz";
+ text[5][2]="zzaBcDeFzzzzzzzzzzzzzzzzzzz";
+ text[5][3]="zzzaBcDeFzzzzzzzzzzzzzzzzzz";
+ text[5][4]="zzzzaBcDeFzzzzzzzzzzzzzzzzz";
+ text[5][5]="zzzzzaBcDeFzzzzzzzzzzzzzzzz";
+ text[5][6]="zzzzzzaBcDeFzzzzzzzzzzzzzzz";
+ text[5][7]="zzzzzzzaBcDeFzzzzzzzzzzzzzz";
+ text[5][8]="zzzzzzzzaBcDeFzzzzzzzzzzzzz";
+ text[5][9]="zzzzzzzzzaBcDeFzzzzzzzzzzzz";
+ text[5][10]="zzzzzzzzzzaBcDeFzzzzzzzzzzz";
+ text[5][11]="zzzzzzzzzzzaBcDeFzzzzzzzzzz";
+ text[5][12]="zzzzzzzzzzzzaBcDeFzzzzzzzzz";
+ text[5][13]="zzzzzzzzzzzzzaBcDeFzzzzzzzz";
+ text[5][14]="zzzzzzzzzzzzzzaBcDeFzzzzzzz";
+ text[5][15]="zzzzzzzzzzzzzzzaBcDeFzzzzzz";
+ text[5][16]="zzzzzzzzzzzzzzzzaBcDeFzzzzz";
+ text[5][17]="zzzzzzzzzzzzzzzzzaBcDeFzzzz";
+ text[5][18]="zzzzzzzzzzzzzzzzzzaBcDeFzzz";
+ text[5][19]="zzzzzzzzzzzzzzzzzzzaBcDeFzz";
+ text[5][20]="zzzzzzzzzzzzzzzzzzzzaBcDeFz";
+ text[5][21]="zzzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6][0]="aBcDeFgzzzzzzzzzzzzzzzzzzzz";
+ text[6][1]="zaBcDeFgzzzzzzzzzzzzzzzzzzz";
+ text[6][2]="zzaBcDeFgzzzzzzzzzzzzzzzzzz";
+ text[6][3]="zzzaBcDeFgzzzzzzzzzzzzzzzzz";
+ text[6][4]="zzzzaBcDeFgzzzzzzzzzzzzzzzz";
+ text[6][5]="zzzzzaBcDeFgzzzzzzzzzzzzzzz";
+ text[6][6]="zzzzzzaBcDeFgzzzzzzzzzzzzzz";
+ text[6][7]="zzzzzzzaBcDeFgzzzzzzzzzzzzz";
+ text[6][8]="zzzzzzzzaBcDeFgzzzzzzzzzzzz";
+ text[6][9]="zzzzzzzzzaBcDeFgzzzzzzzzzzz";
+ text[6][10]="zzzzzzzzzzaBcDeFgzzzzzzzzzz";
+ text[6][11]="zzzzzzzzzzzaBcDeFgzzzzzzzzz";
+ text[6][12]="zzzzzzzzzzzzaBcDeFgzzzzzzzz";
+ text[6][13]="zzzzzzzzzzzzzaBcDeFgzzzzzzz";
+ text[6][14]="zzzzzzzzzzzzzzaBcDeFgzzzzzz";
+ text[6][15]="zzzzzzzzzzzzzzzaBcDeFgzzzzz";
+ text[6][16]="zzzzzzzzzzzzzzzzaBcDeFgzzzz";
+ text[6][17]="zzzzzzzzzzzzzzzzzaBcDeFgzzz";
+ text[6][18]="zzzzzzzzzzzzzzzzzzaBcDeFgzz";
+ text[6][19]="zzzzzzzzzzzzzzzzzzzaBcDeFgz";
+ text[6][20]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7][0]="aBcDeFgHzzzzzzzzzzzzzzzzzzz";
+ text[7][1]="zaBcDeFgHzzzzzzzzzzzzzzzzzz";
+ text[7][2]="zzaBcDeFgHzzzzzzzzzzzzzzzzz";
+ text[7][3]="zzzaBcDeFgHzzzzzzzzzzzzzzzz";
+ text[7][4]="zzzzaBcDeFgHzzzzzzzzzzzzzzz";
+ text[7][5]="zzzzzaBcDeFgHzzzzzzzzzzzzzz";
+ text[7][6]="zzzzzzaBcDeFgHzzzzzzzzzzzzz";
+ text[7][7]="zzzzzzzaBcDeFgHzzzzzzzzzzzz";
+ text[7][8]="zzzzzzzzaBcDeFgHzzzzzzzzzzz";
+ text[7][9]="zzzzzzzzzaBcDeFgHzzzzzzzzzz";
+ text[7][10]="zzzzzzzzzzaBcDeFgHzzzzzzzzz";
+ text[7][11]="zzzzzzzzzzzaBcDeFgHzzzzzzzz";
+ text[7][12]="zzzzzzzzzzzzaBcDeFgHzzzzzzz";
+ text[7][13]="zzzzzzzzzzzzzaBcDeFgHzzzzzz";
+ text[7][14]="zzzzzzzzzzzzzzaBcDeFgHzzzzz";
+ text[7][15]="zzzzzzzzzzzzzzzaBcDeFgHzzzz";
+ text[7][16]="zzzzzzzzzzzzzzzzaBcDeFgHzzz";
+ text[7][17]="zzzzzzzzzzzzzzzzzaBcDeFgHzz";
+ text[7][18]="zzzzzzzzzzzzzzzzzzaBcDeFgHz";
+ text[7][19]="zzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8][0]="aBcDeFgHizzzzzzzzzzzzzzzzzz";
+ text[8][1]="zaBcDeFgHizzzzzzzzzzzzzzzzz";
+ text[8][2]="zzaBcDeFgHizzzzzzzzzzzzzzzz";
+ text[8][3]="zzzaBcDeFgHizzzzzzzzzzzzzzz";
+ text[8][4]="zzzzaBcDeFgHizzzzzzzzzzzzzz";
+ text[8][5]="zzzzzaBcDeFgHizzzzzzzzzzzzz";
+ text[8][6]="zzzzzzaBcDeFgHizzzzzzzzzzzz";
+ text[8][7]="zzzzzzzaBcDeFgHizzzzzzzzzzz";
+ text[8][8]="zzzzzzzzaBcDeFgHizzzzzzzzzz";
+ text[8][9]="zzzzzzzzzaBcDeFgHizzzzzzzzz";
+ text[8][10]="zzzzzzzzzzaBcDeFgHizzzzzzzz";
+ text[8][11]="zzzzzzzzzzzaBcDeFgHizzzzzzz";
+ text[8][12]="zzzzzzzzzzzzaBcDeFgHizzzzzz";
+ text[8][13]="zzzzzzzzzzzzzaBcDeFgHizzzzz";
+ text[8][14]="zzzzzzzzzzzzzzaBcDeFgHizzzz";
+ text[8][15]="zzzzzzzzzzzzzzzaBcDeFgHizzz";
+ text[8][16]="zzzzzzzzzzzzzzzzaBcDeFgHizz";
+ text[8][17]="zzzzzzzzzzzzzzzzzaBcDeFgHiz";
+ text[8][18]="zzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9][0]="aBcDeFgHiJzzzzzzzzzzzzzzzzz";
+ text[9][1]="zaBcDeFgHiJzzzzzzzzzzzzzzzz";
+ text[9][2]="zzaBcDeFgHiJzzzzzzzzzzzzzzz";
+ text[9][3]="zzzaBcDeFgHiJzzzzzzzzzzzzzz";
+ text[9][4]="zzzzaBcDeFgHiJzzzzzzzzzzzzz";
+ text[9][5]="zzzzzaBcDeFgHiJzzzzzzzzzzzz";
+ text[9][6]="zzzzzzaBcDeFgHiJzzzzzzzzzzz";
+ text[9][7]="zzzzzzzaBcDeFgHiJzzzzzzzzzz";
+ text[9][8]="zzzzzzzzaBcDeFgHiJzzzzzzzzz";
+ text[9][9]="zzzzzzzzzaBcDeFgHiJzzzzzzzz";
+ text[9][10]="zzzzzzzzzzaBcDeFgHiJzzzzzzz";
+ text[9][11]="zzzzzzzzzzzaBcDeFgHiJzzzzzz";
+ text[9][12]="zzzzzzzzzzzzaBcDeFgHiJzzzzz";
+ text[9][13]="zzzzzzzzzzzzzaBcDeFgHiJzzzz";
+ text[9][14]="zzzzzzzzzzzzzzaBcDeFgHiJzzz";
+ text[9][15]="zzzzzzzzzzzzzzzaBcDeFgHiJzz";
+ text[9][16]="zzzzzzzzzzzzzzzzaBcDeFgHiJz";
+ text[9][17]="zzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10][0]="aBcDeFgHiJkzzzzzzzzzzzzzzzz";
+ text[10][1]="zaBcDeFgHiJkzzzzzzzzzzzzzzz";
+ text[10][2]="zzaBcDeFgHiJkzzzzzzzzzzzzzz";
+ text[10][3]="zzzaBcDeFgHiJkzzzzzzzzzzzzz";
+ text[10][4]="zzzzaBcDeFgHiJkzzzzzzzzzzzz";
+ text[10][5]="zzzzzaBcDeFgHiJkzzzzzzzzzzz";
+ text[10][6]="zzzzzzaBcDeFgHiJkzzzzzzzzzz";
+ text[10][7]="zzzzzzzaBcDeFgHiJkzzzzzzzzz";
+ text[10][8]="zzzzzzzzaBcDeFgHiJkzzzzzzzz";
+ text[10][9]="zzzzzzzzzaBcDeFgHiJkzzzzzzz";
+ text[10][10]="zzzzzzzzzzaBcDeFgHiJkzzzzzz";
+ text[10][11]="zzzzzzzzzzzaBcDeFgHiJkzzzzz";
+ text[10][12]="zzzzzzzzzzzzaBcDeFgHiJkzzzz";
+ text[10][13]="zzzzzzzzzzzzzaBcDeFgHiJkzzz";
+ text[10][14]="zzzzzzzzzzzzzzaBcDeFgHiJkzz";
+ text[10][15]="zzzzzzzzzzzzzzzaBcDeFgHiJkz";
+ text[10][16]="zzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11][0]="aBcDeFgHiJkLzzzzzzzzzzzzzzz";
+ text[11][1]="zaBcDeFgHiJkLzzzzzzzzzzzzzz";
+ text[11][2]="zzaBcDeFgHiJkLzzzzzzzzzzzzz";
+ text[11][3]="zzzaBcDeFgHiJkLzzzzzzzzzzzz";
+ text[11][4]="zzzzaBcDeFgHiJkLzzzzzzzzzzz";
+ text[11][5]="zzzzzaBcDeFgHiJkLzzzzzzzzzz";
+ text[11][6]="zzzzzzaBcDeFgHiJkLzzzzzzzzz";
+ text[11][7]="zzzzzzzaBcDeFgHiJkLzzzzzzzz";
+ text[11][8]="zzzzzzzzaBcDeFgHiJkLzzzzzzz";
+ text[11][9]="zzzzzzzzzaBcDeFgHiJkLzzzzzz";
+ text[11][10]="zzzzzzzzzzaBcDeFgHiJkLzzzzz";
+ text[11][11]="zzzzzzzzzzzaBcDeFgHiJkLzzzz";
+ text[11][12]="zzzzzzzzzzzzaBcDeFgHiJkLzzz";
+ text[11][13]="zzzzzzzzzzzzzaBcDeFgHiJkLzz";
+ text[11][14]="zzzzzzzzzzzzzzaBcDeFgHiJkLz";
+ text[11][15]="zzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12][0]="aBcDeFgHiJkLmzzzzzzzzzzzzzz";
+ text[12][1]="zaBcDeFgHiJkLmzzzzzzzzzzzzz";
+ text[12][2]="zzaBcDeFgHiJkLmzzzzzzzzzzzz";
+ text[12][3]="zzzaBcDeFgHiJkLmzzzzzzzzzzz";
+ text[12][4]="zzzzaBcDeFgHiJkLmzzzzzzzzzz";
+ text[12][5]="zzzzzaBcDeFgHiJkLmzzzzzzzzz";
+ text[12][6]="zzzzzzaBcDeFgHiJkLmzzzzzzzz";
+ text[12][7]="zzzzzzzaBcDeFgHiJkLmzzzzzzz";
+ text[12][8]="zzzzzzzzaBcDeFgHiJkLmzzzzzz";
+ text[12][9]="zzzzzzzzzaBcDeFgHiJkLmzzzzz";
+ text[12][10]="zzzzzzzzzzaBcDeFgHiJkLmzzzz";
+ text[12][11]="zzzzzzzzzzzaBcDeFgHiJkLmzzz";
+ text[12][12]="zzzzzzzzzzzzaBcDeFgHiJkLmzz";
+ text[12][13]="zzzzzzzzzzzzzaBcDeFgHiJkLmz";
+ text[12][14]="zzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13][0]="aBcDeFgHiJkLmNzzzzzzzzzzzzz";
+ text[13][1]="zaBcDeFgHiJkLmNzzzzzzzzzzzz";
+ text[13][2]="zzaBcDeFgHiJkLmNzzzzzzzzzzz";
+ text[13][3]="zzzaBcDeFgHiJkLmNzzzzzzzzzz";
+ text[13][4]="zzzzaBcDeFgHiJkLmNzzzzzzzzz";
+ text[13][5]="zzzzzaBcDeFgHiJkLmNzzzzzzzz";
+ text[13][6]="zzzzzzaBcDeFgHiJkLmNzzzzzzz";
+ text[13][7]="zzzzzzzaBcDeFgHiJkLmNzzzzzz";
+ text[13][8]="zzzzzzzzaBcDeFgHiJkLmNzzzzz";
+ text[13][9]="zzzzzzzzzaBcDeFgHiJkLmNzzzz";
+ text[13][10]="zzzzzzzzzzaBcDeFgHiJkLmNzzz";
+ text[13][11]="zzzzzzzzzzzaBcDeFgHiJkLmNzz";
+ text[13][12]="zzzzzzzzzzzzaBcDeFgHiJkLmNz";
+ text[13][13]="zzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14][0]="aBcDeFgHiJkLmNozzzzzzzzzzzz";
+ text[14][1]="zaBcDeFgHiJkLmNozzzzzzzzzzz";
+ text[14][2]="zzaBcDeFgHiJkLmNozzzzzzzzzz";
+ text[14][3]="zzzaBcDeFgHiJkLmNozzzzzzzzz";
+ text[14][4]="zzzzaBcDeFgHiJkLmNozzzzzzzz";
+ text[14][5]="zzzzzaBcDeFgHiJkLmNozzzzzzz";
+ text[14][6]="zzzzzzaBcDeFgHiJkLmNozzzzzz";
+ text[14][7]="zzzzzzzaBcDeFgHiJkLmNozzzzz";
+ text[14][8]="zzzzzzzzaBcDeFgHiJkLmNozzzz";
+ text[14][9]="zzzzzzzzzaBcDeFgHiJkLmNozzz";
+ text[14][10]="zzzzzzzzzzaBcDeFgHiJkLmNozz";
+ text[14][11]="zzzzzzzzzzzaBcDeFgHiJkLmNoz";
+ text[14][12]="zzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15][0]="aBcDeFgHiJkLmNoPzzzzzzzzzzz";
+ text[15][1]="zaBcDeFgHiJkLmNoPzzzzzzzzzz";
+ text[15][2]="zzaBcDeFgHiJkLmNoPzzzzzzzzz";
+ text[15][3]="zzzaBcDeFgHiJkLmNoPzzzzzzzz";
+ text[15][4]="zzzzaBcDeFgHiJkLmNoPzzzzzzz";
+ text[15][5]="zzzzzaBcDeFgHiJkLmNoPzzzzzz";
+ text[15][6]="zzzzzzaBcDeFgHiJkLmNoPzzzzz";
+ text[15][7]="zzzzzzzaBcDeFgHiJkLmNoPzzzz";
+ text[15][8]="zzzzzzzzaBcDeFgHiJkLmNoPzzz";
+ text[15][9]="zzzzzzzzzaBcDeFgHiJkLmNoPzz";
+ text[15][10]="zzzzzzzzzzaBcDeFgHiJkLmNoPz";
+ text[15][11]="zzzzzzzzzzzaBcDeFgHiJkLmNoP";
+ text[16][0]="aBcDeFgHiJkLmNoPqzzzzzzzzzz";
+ text[16][1]="zaBcDeFgHiJkLmNoPqzzzzzzzzz";
+ text[16][2]="zzaBcDeFgHiJkLmNoPqzzzzzzzz";
+ text[16][3]="zzzaBcDeFgHiJkLmNoPqzzzzzzz";
+ text[16][4]="zzzzaBcDeFgHiJkLmNoPqzzzzzz";
+ text[16][5]="zzzzzaBcDeFgHiJkLmNoPqzzzzz";
+ text[16][6]="zzzzzzaBcDeFgHiJkLmNoPqzzzz";
+ text[16][7]="zzzzzzzaBcDeFgHiJkLmNoPqzzz";
+ text[16][8]="zzzzzzzzaBcDeFgHiJkLmNoPqzz";
+ text[16][9]="zzzzzzzzzaBcDeFgHiJkLmNoPqz";
+ text[16][10]="zzzzzzzzzzaBcDeFgHiJkLmNoPq";
+ text[17][0]="aBcDeFgHiJkLmNoPqRzzzzzzzzz";
+ text[17][1]="zaBcDeFgHiJkLmNoPqRzzzzzzzz";
+ text[17][2]="zzaBcDeFgHiJkLmNoPqRzzzzzzz";
+ text[17][3]="zzzaBcDeFgHiJkLmNoPqRzzzzzz";
+ text[17][4]="zzzzaBcDeFgHiJkLmNoPqRzzzzz";
+ text[17][5]="zzzzzaBcDeFgHiJkLmNoPqRzzzz";
+ text[17][6]="zzzzzzaBcDeFgHiJkLmNoPqRzzz";
+ text[17][7]="zzzzzzzaBcDeFgHiJkLmNoPqRzz";
+ text[17][8]="zzzzzzzzaBcDeFgHiJkLmNoPqRz";
+ text[17][9]="zzzzzzzzzaBcDeFgHiJkLmNoPqR";
+ text[18][0]="aBcDeFgHiJkLmNoPqRszzzzzzzz";
+ text[18][1]="zaBcDeFgHiJkLmNoPqRszzzzzzz";
+ text[18][2]="zzaBcDeFgHiJkLmNoPqRszzzzzz";
+ text[18][3]="zzzaBcDeFgHiJkLmNoPqRszzzzz";
+ text[18][4]="zzzzaBcDeFgHiJkLmNoPqRszzzz";
+ text[18][5]="zzzzzaBcDeFgHiJkLmNoPqRszzz";
+ text[18][6]="zzzzzzaBcDeFgHiJkLmNoPqRszz";
+ text[18][7]="zzzzzzzaBcDeFgHiJkLmNoPqRsz";
+ text[18][8]="zzzzzzzzaBcDeFgHiJkLmNoPqRs";
+ text[19][0]="aBcDeFgHiJkLmNoPqRsTzzzzzzz";
+ text[19][1]="zaBcDeFgHiJkLmNoPqRsTzzzzzz";
+ text[19][2]="zzaBcDeFgHiJkLmNoPqRsTzzzzz";
+ text[19][3]="zzzaBcDeFgHiJkLmNoPqRsTzzzz";
+ text[19][4]="zzzzaBcDeFgHiJkLmNoPqRsTzzz";
+ text[19][5]="zzzzzaBcDeFgHiJkLmNoPqRsTzz";
+ text[19][6]="zzzzzzaBcDeFgHiJkLmNoPqRsTz";
+ text[19][7]="zzzzzzzaBcDeFgHiJkLmNoPqRsT";
+ text[20][0]="aBcDeFgHiJkLmNoPqRsTuzzzzzz";
+ text[20][1]="zaBcDeFgHiJkLmNoPqRsTuzzzzz";
+ text[20][2]="zzaBcDeFgHiJkLmNoPqRsTuzzzz";
+ text[20][3]="zzzaBcDeFgHiJkLmNoPqRsTuzzz";
+ text[20][4]="zzzzaBcDeFgHiJkLmNoPqRsTuzz";
+ text[20][5]="zzzzzaBcDeFgHiJkLmNoPqRsTuz";
+ text[20][6]="zzzzzzaBcDeFgHiJkLmNoPqRsTu";
+ text[21][0]="aBcDeFgHiJkLmNoPqRsTuVzzzzz";
+ text[21][1]="zaBcDeFgHiJkLmNoPqRsTuVzzzz";
+ text[21][2]="zzaBcDeFgHiJkLmNoPqRsTuVzzz";
+ text[21][3]="zzzaBcDeFgHiJkLmNoPqRsTuVzz";
+ text[21][4]="zzzzaBcDeFgHiJkLmNoPqRsTuVz";
+ text[21][5]="zzzzzaBcDeFgHiJkLmNoPqRsTuV";
+ text[22][0]="aBcDeFgHiJkLmNoPqRsTuVwzzzz";
+ text[22][1]="zaBcDeFgHiJkLmNoPqRsTuVwzzz";
+ text[22][2]="zzaBcDeFgHiJkLmNoPqRsTuVwzz";
+ text[22][3]="zzzaBcDeFgHiJkLmNoPqRsTuVwz";
+ text[22][4]="zzzzaBcDeFgHiJkLmNoPqRsTuVw";
+ text[23][0]="aBcDeFgHiJkLmNoPqRsTuVwXzzz";
+ text[23][1]="zaBcDeFgHiJkLmNoPqRsTuVwXzz";
+ text[23][2]="zzaBcDeFgHiJkLmNoPqRsTuVwXz";
+ text[23][3]="zzzaBcDeFgHiJkLmNoPqRsTuVwX";
+ text[24][0]="aBcDeFgHiJkLmNoPqRsTuVwXyzz";
+ text[24][1]="zaBcDeFgHiJkLmNoPqRsTuVwXyz";
+ text[24][2]="zzaBcDeFgHiJkLmNoPqRsTuVwXy";
+ text[25][0]="aBcDeFgHiJkLmNoPqRsTuVwXyZz";
+ text[25][1]="zaBcDeFgHiJkLmNoPqRsTuVwXyZ";
+
+ char *needle[26];
+ needle[0]="A";
+ needle[1]="Ab";
+ needle[2]="AbC";
+ needle[3]="AbCd";
+ needle[4]="AbCdE";
+ needle[5]="AbCdEf";
+ needle[6]="AbCdEfG";
+ needle[7]="AbCdEfGh";
+ needle[8]="AbCdEfGhI";
+ needle[9]="AbCdEfGhIJ";
+ needle[10]="AbCdEfGhIjK";
+ needle[11]="AbCdEfGhIjKl";
+ needle[12]="AbCdEfGhIjKlM";
+ needle[13]="AbCdEfGhIjKlMn";
+ needle[14]="AbCdEfGhIjKlMnO";
+ needle[15]="AbCdEfGhIjKlMnOp";
+ needle[16]="AbCdEfGhIjKlMnOpQ";
+ needle[17]="AbCdEfGhIjKlMnOpQr";
+ needle[18]="AbCdEfGhIjKlMnOpQrS";
+ needle[19]="AbCdEfGhIjKlMnOpQrSt";
+ needle[20]="AbCdEfGhIjKlMnOpQrStU";
+ needle[21]="AbCdEfGhIjKlMnOpQrStUv";
+ needle[22]="AbCdEfGhIjKlMnOpQrStUvW";
+ needle[23]="AbCdEfGhIjKlMnOpQrStUvWx";
+ needle[24]="AbCdEfGhIjKlMnOpQrStUvWxY";
+ needle[25]="AbCdEfGhIjKlMnOpQrStUvWxYZ";
+
+ int i, j;
+ uint8_t *found = NULL;
+ for (i = 0; i < 26; i++) {
+ for (j = 0; j <= (26-i); j++) {
+ found = BasicSearchNocaseWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ found = Bs2bmNocaseWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ found = BoyerMooreNocaseWrapper((uint8_t *)text[i][j], (uint8_t *)needle[i], 1);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i][j]);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats
+ */
+int UtilSpmSearchStatsTest01()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1]="aaaaaaaaazaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaazaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaazaaaaaraaaaazaaaaaaazaaaaaaaaaaaaaazaaaaaaaazaaaaaaaaazaaaaaaaaaaaaB";
+ text[2]="aBaBaBaBaBaBaBaBazaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBzBaBaBaBaBaBaBzBaBaBaBaBzBaBaBaBaBaBzBaBaBaBaBaBzBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBazaBaBaBaBaBc";
+ text[3]="aBcaBcaBcaBcaBczBcaBcaBzaBcaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBzaBcaBcaBcazcaBcaBcaBcaBcaBcD";
+ text[4]="aBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDzBcDaBcDaBcDaBcDzBcDaBcDaBczaBcDaBcDaBczaBcDaBcDaBcDaBcDaBzDaBcDaBcDaBcDaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDaBcDaBzDaBcDaBcDaBcDaBzDaBcDaBcDaBzDaBcDaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDazcDaBcDaBcDaBcDaBcDzBcDaBcDaBcDaBcDaBcDaBcDe";
+ text[5]="aBcDeaBcDeaBcDeazcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDezBcDeaBcDeaBcDzaBcDeaBcDeaBcDeazcDzaBcDeaBcDezBcDeaBzDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBczeaBcDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDzaBcDeaBcDezBcDeaBcDezBcDeaBczeaBcDeaBcDeaBzDeaBcDezBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeF";
+ text[6]="aBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBczzaBcDeaBcDeaBcDzazcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDeaBczeaBcDezzzaBcDeFg";
+ text[7]="aBcDeaBczeaBcDzaBcDezBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBzDzaBcDeaBcDeazcDeaBcDzaBcDeaBczeaBcDeaBcDeaBzDzaBcDeaBcDeaBcDezBcDzaBcDeaBzDeaBcDeaBcDezBcDzaBcDeaBcDeaBzDeaBcDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBrDeaBcDeaBcDezzzaBcDeFgH";
+ text[8]="aBcDeaBcDeaBczzaBcDeazcDeaBcDezBcDeaBcDzaBcDeaBcDeaBcDeaBczzaBcDeaBcDeaBczeaBcDeaBcDzzBcDeaBcDeaBcDzaBczeaBcDeaBcDzaBcDeaBczeaBcDeaBcDeaBzDeaBcDeaBcDeaBzDeaBcDeaBcDzaBcDeaBcDeazcDeaBcDeaBcDzaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBczeaBcDeaBzDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHi";
+ text[9]="aBcDeaBcDzaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeazcDeaBcDeaBcDzzBcDeaBcDeaBczeaBcDzaBcDezBcDeaBczeaBcDzaBcDezBcDeaBcDzaBczeaBcDeaBcDzaBcDeazcDeaBcDeaBcDzaBczeaBcDeaBcDzaBzDeaBcDeaBczeaBcDeaBcDzaBcDeaBcDeaBzDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDeaBzDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJ";
+ text[10]="aBcDeaBcDeaBczeaBcDzaBczeaBcDeaBczeaBcDeaBcDzaBcDeaBcDeazcDeaBcDeaBcDeaBzDzaBcDeazcDeaBcDeazcDeaBcDzaBcDeazcDeaBcDeaBczzaBcDeaBcDeaBzDeaBcDeaBcDzaBczeaBcDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDezBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDezBcDeaBcDeaBcDeaBzDeaBcDeaBcDezzzaBcDeFgHiJk";
+ text[11]="aBcDeaBcDeaBcDeaBcDeaBzDeaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDeazcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDzzBcDeaBcDeaBcDeaBcDzaBcDzaBcDeaBzDeaBcDeaBcDezBcDeaBcDeazcDeaBcDeaBcDezBcDeaBcDeaBcDeazcDeaBcDeaBzDeaBcDeaBczeaBcDeazcDeaBcDezBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkL";
+ text[12]="aBcDeaBcDeaBcDeaBcDeaBzDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDeazcDeaBcDeaBcDeazcDeaBcDeaBczeaBcDeaBcDeaBcDezBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLm";
+ text[13]="aBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLmN";
+ text[14]="aBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDezzzaBcDeFgHiJkLmNo";
+ text[15]="aBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of greater length (text with a lot of partial matches, worst case for a basic search):\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats for
+ */
+int UtilSpmSearchStatsTest02()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+
+int UtilSpmSearchStatsTest03()
+{
+ char *text[16];
+ text[0]="zzzzzza";
+ text[1]="zzzzzzaB";
+ text[2]="zzzzzzaBc";
+ text[3]="zzzzzzaBcD";
+ text[4]="zzzzzzaBcDe";
+ text[5]="zzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length (badcase for):\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats
+ */
+int UtilSpmSearchStatsTest04()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1]="aaaaaaaaazaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaazaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaazaaaaaaaaazaaaaaaaaaazaaaaaraaaaazaaaaaaazaaaaaaaaaaaaaazaaaaaaaazaaaaaaaaazaaaaaaaaaaaaB";
+ text[2]="aBaBaBaBaBaBaBaBazaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBzBaBaBaBaBaBaBzBaBaBaBaBzBaBaBaBaBaBzBaBaBaBaBaBzBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBaBaBzBaBaBaBaBaBaBaBaBaBaBazaBaBaBaBaBaBaBazaBaBaBaBaBc";
+ text[3]="aBcaBcaBcaBcaBczBcaBcaBzaBcaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBcaBcaBczBcaBcaBcaBcaBzaBcaBcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcazcaBcaBcaBcaBcaBcaBzaBcaBcaBcazcaBcaBcaBcaBcaBcD";
+ text[4]="aBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDzBcDaBcDaBcDaBcDzBcDaBcDaBczaBcDaBcDaBczaBcDaBcDaBcDaBcDaBzDaBcDaBcDaBcDaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDaBcDaBzDaBcDaBcDaBcDaBzDaBcDaBcDaBzDaBcDaBcDaBcDaBcDaBcDaBczaBcDaBcDaBcDaBcDazcDaBcDaBcDaBcDaBcDzBcDaBcDaBcDaBcDaBcDaBcDe";
+ text[5]="aBcDeaBcDeaBcDeazcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDezBcDeaBcDeaBcDzaBcDeaBcDeaBcDeazcDzaBcDeaBcDezBcDeaBzDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBczeaBcDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDzaBcDeaBcDezBcDeaBcDezBcDeaBczeaBcDeaBcDeaBzDeaBcDezBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeF";
+ text[6]="aBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBczzaBcDeaBcDeaBcDzazcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDeaBczeaBcDezzzaBcDeFg";
+ text[7]="aBcDeaBczeaBcDzaBcDezBcDeaBcDeaBcDeaBcDzaBzDeaBcDeaBcDeaBzDzaBcDeaBcDeazcDeaBcDzaBcDeaBczeaBcDeaBcDeaBzDzaBcDeaBcDeaBcDezBcDzaBcDeaBzDeaBcDeaBcDezBcDzaBcDeaBcDeaBzDeaBcDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBrDeaBcDeaBcDezzzaBcDeFgH";
+ text[8]="aBcDeaBcDeaBczzaBcDeazcDeaBcDezBcDeaBcDzaBcDeaBcDeaBcDeaBczzaBcDeaBcDeaBczeaBcDeaBcDzzBcDeaBcDeaBcDzaBczeaBcDeaBcDzaBcDeaBczeaBcDeaBcDeaBzDeaBcDeaBcDeaBzDeaBcDeaBcDzaBcDeaBcDeazcDeaBcDeaBcDzaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeazcDeaBcDeaBcDeaBczeaBcDeaBzDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHi";
+ text[9]="aBcDeaBcDzaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeazcDeaBcDeaBcDzzBcDeaBcDeaBczeaBcDzaBcDezBcDeaBczeaBcDzaBcDezBcDeaBcDzaBczeaBcDeaBcDzaBcDeazcDeaBcDeaBcDzaBczeaBcDeaBcDzaBzDeaBcDeaBczeaBcDeaBcDzaBcDeaBcDeaBzDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDeaBzDeaBcDeaBcDeazcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJ";
+ text[10]="aBcDeaBcDeaBczeaBcDzaBczeaBcDeaBczeaBcDeaBcDzaBcDeaBcDeazcDeaBcDeaBcDeaBzDzaBcDeazcDeaBcDeazcDeaBcDzaBcDeazcDeaBcDeaBczzaBcDeaBcDeaBzDeaBcDeaBcDzaBczeaBcDeaBcDeaBcDeaBczeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDezBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDezBcDeaBcDeaBcDeaBzDeaBcDeaBcDezzzaBcDeFgHiJk";
+ text[11]="aBcDeaBcDeaBcDeaBcDeaBzDeaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDeazcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDzaBcDzaBcDeaBcDeaBcDeaBcDzzBcDeaBcDeaBcDeaBcDzaBcDzaBcDeaBzDeaBcDeaBcDezBcDeaBcDeazcDeaBcDeaBcDezBcDeaBcDeaBcDeazcDeaBcDeaBzDeaBcDeaBczeaBcDeazcDeaBcDezBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkL";
+ text[12]="aBcDeaBcDeaBcDeaBcDeaBzDeaBcDeaBzDeaBcDeaBcDezBcDeaBcDeazcDeaBcDeaBcDeazcDeaBcDeaBczeaBcDeaBcDeaBcDezBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLm";
+ text[13]="aBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLmN";
+ text[14]="aBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDezzzaBcDeFgHiJkLmNo";
+ text[15]="aBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDzaBcDeaBcDeaBcDeaBcDezzzaBcDeFgHiJkLmNoP";
+
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of greater length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i + 1);
+ found = BasicSearchCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i + 1);
+ found = Bs2bmCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i + 1);
+ found = BoyerMooreCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with SpmSearch (Building Context):", i + 1);
+ found = RawCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats for
+ */
+int UtilSpmSearchStatsTest05()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+
+int UtilSpmSearchStatsTest06()
+{
+ char *text[16];
+ text[0]="zzzzkzzzzzzzkzzzzzza";
+ text[1]="BBBBkBBBBBBBkBBBBBaB";
+ text[2]="BcBckcBcBcBckcBcBcaBc";
+ text[3]="BcDBkDBcDBcDkcDBcDaBcD";
+ text[4]="BcDekcDeBcDekcDezzaBcDe";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length (badcase for):\n");
+ for (i = 0; i < 5; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+int UtilSpmSearchStatsTest07()
+{
+ char *text[16];
+ text[0]="zzzza";
+ text[1]="BBBaB";
+ text[2]="bbaBc";
+ text[3]="aaBcD";
+ text[4]="aBcDe";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of real lower length (badcase for):\n");
+ for (i = 0; i < 5; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats for no case algorithms
+ */
+int UtilSpmNocaseSearchStatsTest01()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of greater length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+int UtilSpmNocaseSearchStatsTest02()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+
+int UtilSpmNocaseSearchStatsTest03()
+{
+ char *text[16];
+ text[0]="zzzzkzzzzzzzkzzzzzza";
+ text[1]="BBBBkBBBBBBBkBBBBBaB";
+ text[2]="BcBckcBcBcBckcBcBcaBc";
+ text[3]="BcDBkDBcDBcDkcDBcDaBcD";
+ text[4]="BcDekcDeBcDekcDezzaBcDe";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length (badcase for):\n");
+ for (i = 0; i < 5; i++) {
+ printf("Pattern length %d with BasicSearch:", i+1);
+ found = BasicSearchNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch:", i+1);
+ found = Bs2bmNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch:", i+1);
+ found = BoyerMooreNocaseWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+/**
+ * \test Give some stats for no case algorithms
+ */
+int UtilSpmNocaseSearchStatsTest04()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of greater length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+int UtilSpmNocaseSearchStatsTest05()
+{
+ char *text[16];
+ text[0]="zzzzzzzzzzzzzzzzzza";
+ text[1]="zzzzzzzzzzzzzzzzzzaB";
+ text[2]="zzzzzzzzzzzzzzzzzzaBc";
+ text[3]="zzzzzzzzzzzzzzzzzzaBcD";
+ text[4]="zzzzzzzzzzzzzzzzzzaBcDe";
+ text[5]="zzzzzzzzzzzzzzzzzzzzaBcDeF";
+ text[6]="zzzzzzzzzzzzzzzzzzzzaBcDeFg";
+ text[7]="zzzzzzzzzzzzzzzzzzzzaBcDeFgH";
+ text[8]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHi";
+ text[9]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJ";
+ text[10]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJk";
+ text[11]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkL";
+ text[12]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLm";
+ text[13]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmN";
+ text[14]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNo";
+ text[15]="zzzzzzzzzzzzzzzzzzzzaBcDeFgHiJkLmNoP";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+ needle[5]="aBcDeF";
+ needle[6]="aBcDeFg";
+ needle[7]="aBcDeFgH";
+ needle[8]="aBcDeFgHi";
+ needle[9]="aBcDeFgHiJ";
+ needle[10]="aBcDeFgHiJk";
+ needle[11]="aBcDeFgHiJkL";
+ needle[12]="aBcDeFgHiJkLm";
+ needle[13]="aBcDeFgHiJkLmN";
+ needle[14]="aBcDeFgHiJkLmNo";
+ needle[15]="aBcDeFgHiJkLmNoP";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length:\n");
+ for (i = 0; i < 16; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+
+int UtilSpmNocaseSearchStatsTest06()
+{
+ char *text[16];
+ text[0]="zzzzkzzzzzzzkzzzzzza";
+ text[1]="BBBBkBBBBBBBkBBBBBaB";
+ text[2]="BcBckcBcBcBckcBcBcaBc";
+ text[3]="BcDBkDBcDBcDkcDBcDaBcD";
+ text[4]="BcDekcDeBcDekcDezzaBcDe";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of lower length (badcase for):\n");
+ for (i = 0; i < 5; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+int UtilSpmNocaseSearchStatsTest07()
+{
+ char *text[16];
+ text[0]="zzzza";
+ text[1]="bbbAb";
+ text[2]="bbAbC";
+ text[3]="bAbCd";
+ text[4]="AbCdE";
+
+ char *needle[16];
+ needle[0]="a";
+ needle[1]="aB";
+ needle[2]="aBc";
+ needle[3]="aBcD";
+ needle[4]="aBcDe";
+
+ int i;
+ uint8_t *found = NULL;
+ printf("\nStats for text of real lower length (badcase for):\n");
+ for (i = 0; i < 5; i++) {
+ printf("Pattern length %d with BasicSearch (Building Context):", i+1);
+ found = BasicSearchNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error1 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with Bs2BmSearch (Building Context):", i+1);
+ found = Bs2bmNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error2 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("Pattern length %d with BoyerMooreSearch (Building Context):", i+1);
+ found = BoyerMooreNocaseCtxWrapper((uint8_t *)text[i], (uint8_t *)needle[i], STATS_TIMES);
+ if (found == 0) {
+ printf("Error3 searching for %s in text %s\n", needle[i], text[i]);
+ return 0;
+ }
+ printf("\n");
+ }
+ return 1;
+}
+
+#endif
+
+/* Register unittests */
+void UtilSpmSearchRegistertests(void)
+{
+#ifdef UNITTESTS
+ /* Generic tests */
+ UtRegisterTest("UtilSpmBasicSearchTest01", UtilSpmBasicSearchTest01, 1);
+ UtRegisterTest("UtilSpmBasicSearchNocaseTest01", UtilSpmBasicSearchNocaseTest01, 1);
+
+ UtRegisterTest("UtilSpmBs2bmSearchTest01", UtilSpmBs2bmSearchTest01, 1);
+ UtRegisterTest("UtilSpmBs2bmSearchNocaseTest01", UtilSpmBs2bmSearchNocaseTest01, 1);
+
+ UtRegisterTest("UtilSpmBoyerMooreSearchTest01", UtilSpmBoyerMooreSearchTest01, 1);
+ UtRegisterTest("UtilSpmBoyerMooreSearchNocaseTest01", UtilSpmBoyerMooreSearchNocaseTest01, 1);
+ UtRegisterTest("UtilSpmBoyerMooreSearchNocaseTestIssue130", UtilSpmBoyerMooreSearchNocaseTestIssue130, 1);
+
+ UtRegisterTest("UtilSpmBs2bmSearchTest02", UtilSpmBs2bmSearchTest02, 1);
+ UtRegisterTest("UtilSpmBs2bmSearchNocaseTest02", UtilSpmBs2bmSearchNocaseTest02, 1);
+
+ UtRegisterTest("UtilSpmBasicSearchTest02", UtilSpmBasicSearchTest02, 1);
+ UtRegisterTest("UtilSpmBasicSearchNocaseTest02", UtilSpmBasicSearchNocaseTest02, 1);
+
+ UtRegisterTest("UtilSpmBoyerMooreSearchTest02", UtilSpmBoyerMooreSearchTest02, 1);
+ UtRegisterTest("UtilSpmBoyerMooreSearchNocaseTest02", UtilSpmBoyerMooreSearchNocaseTest02, 1);
+
+ /* test matches at any offset */
+ UtRegisterTest("UtilSpmSearchOffsetsTest01", UtilSpmSearchOffsetsTest01, 1);
+ UtRegisterTest("UtilSpmSearchOffsetsNocaseTest01", UtilSpmSearchOffsetsNocaseTest01, 1);
+
+#ifdef ENABLE_SEARCH_STATS
+ /* Give some stats searching given a prepared context (look at the wrappers) */
+ UtRegisterTest("UtilSpmSearchStatsTest01", UtilSpmSearchStatsTest01, 1);
+ UtRegisterTest("UtilSpmSearchStatsTest02", UtilSpmSearchStatsTest02, 1);
+ UtRegisterTest("UtilSpmSearchStatsTest03", UtilSpmSearchStatsTest03, 1);
+
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest01", UtilSpmNocaseSearchStatsTest01, 1);
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest02", UtilSpmNocaseSearchStatsTest02, 1);
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest03", UtilSpmNocaseSearchStatsTest03, 1);
+
+ /* Stats building context and searching */
+ UtRegisterTest("UtilSpmSearchStatsTest04", UtilSpmSearchStatsTest04, 1);
+ UtRegisterTest("UtilSpmSearchStatsTest05", UtilSpmSearchStatsTest05, 1);
+ UtRegisterTest("UtilSpmSearchStatsTest06", UtilSpmSearchStatsTest06, 1);
+ UtRegisterTest("UtilSpmSearchStatsTest07", UtilSpmSearchStatsTest07, 1);
+
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest04", UtilSpmNocaseSearchStatsTest04, 1);
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest05", UtilSpmNocaseSearchStatsTest05, 1);
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest06", UtilSpmNocaseSearchStatsTest06, 1);
+ UtRegisterTest("UtilSpmNocaseSearchStatsTest07", UtilSpmNocaseSearchStatsTest07, 1);
+
+#endif
+#endif
+}
diff --git a/framework/src/suricata/src/util-spm.h b/framework/src/suricata/src/util-spm.h
new file mode 100644
index 00000000..2dea657e
--- /dev/null
+++ b/framework/src/suricata/src/util-spm.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_SPM_H__
+#define __UTIL_SPM_H__
+
+#include "util-spm-bs.h"
+#include "util-spm-bs2bm.h"
+#include "util-spm-bm.h"
+
+/** Default algorithm to use: Boyer Moore */
+uint8_t *Bs2bmSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen);
+uint8_t *Bs2bmNocaseSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen);
+uint8_t *BoyerMooreSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen);
+uint8_t *BoyerMooreNocaseSearch(uint8_t *text, uint32_t textlen, uint8_t *needle, uint16_t needlelen);
+
+/* Macros for automatic algorithm selection (use them only when you can't store the context) */
+#define SpmSearch(text, textlen, needle, needlelen) ({\
+ uint8_t *mfound; \
+ if (needlelen < 4 && textlen < 512) \
+ mfound = BasicSearch(text, textlen, needle, needlelen); \
+ else if (needlelen < 4) \
+ mfound = BasicSearch(text, textlen, needle, needlelen); \
+ else \
+ mfound = BoyerMooreSearch(text, textlen, needle, needlelen); \
+ mfound; \
+ })
+
+#define SpmNocaseSearch(text, textlen, needle, needlelen) ({\
+ uint8_t *mfound; \
+ if (needlelen < 4 && textlen < 512) \
+ mfound = BasicSearchNocase(text, textlen, needle, needlelen); \
+ else if (needlelen < 4) \
+ mfound = BasicSearchNocase(text, textlen, needle, needlelen); \
+ else \
+ mfound = BoyerMooreNocaseSearch(text, textlen, needle, needlelen); \
+ mfound; \
+ })
+
+void UtilSpmSearchRegistertests(void);
+#endif /* __UTIL_SPM_H__ */
diff --git a/framework/src/suricata/src/util-storage.c b/framework/src/suricata/src/util-storage.c
new file mode 100644
index 00000000..ba9aa716
--- /dev/null
+++ b/framework/src/suricata/src/util-storage.c
@@ -0,0 +1,545 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Storage API
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+#include "util-storage.h"
+
+typedef struct StorageMapping_ {
+ const char *name;
+ StorageEnum type; // host, flow, tx, stream, ssn, etc
+ unsigned int size;
+ void *(*Alloc)(unsigned int);
+ void (*Free)(void *);
+} StorageMapping;
+
+/** \brief list of StorageMapping used at registration time */
+typedef struct StorageList_ {
+ StorageMapping map;
+ int id;
+ struct StorageList_ *next;
+} StorageList;
+
+static StorageList *storage_list = NULL;
+static int storage_max_id[STORAGE_MAX];
+static int storage_registraton_closed = 0;
+static StorageMapping **storage_map = NULL;
+
+const char *StoragePrintType(StorageEnum type)
+{
+ switch(type) {
+ case STORAGE_HOST:
+ return "host";
+ case STORAGE_FLOW:
+ return "flow";
+ case STORAGE_IPPAIR:
+ return "ippair";
+ case STORAGE_MAX:
+ return "max";
+ }
+ return "invalid";
+}
+
+void StorageInit(void)
+{
+ memset(&storage_max_id, 0x00, sizeof(storage_max_id));
+ storage_list = NULL;
+ storage_map = NULL;
+ storage_registraton_closed = 0;
+}
+
+void StorageCleanup(void)
+{
+ if (storage_map) {
+ int i;
+ for (i = 0; i < STORAGE_MAX; i++) {
+ if (storage_map[i] != NULL) {
+ SCFree(storage_map[i]);
+ storage_map[i] = NULL;
+ }
+ }
+ SCFree(storage_map);
+ storage_map = NULL;
+ }
+
+ StorageList *entry = storage_list;
+ while (entry) {
+ StorageList *next = entry->next;
+ SCFree(entry);
+ entry = next;
+ }
+
+ storage_list = NULL;
+}
+
+int StorageRegister(const StorageEnum type, const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *))
+{
+ if (storage_registraton_closed)
+ return -1;
+
+ if (type >= STORAGE_MAX || name == NULL || strlen(name) == 0 ||
+ size == 0 || (size != sizeof(void *) && Alloc == NULL) || Free == NULL)
+ return -1;
+
+ StorageList *list = storage_list;
+ while (list) {
+ if (strcmp(name, list->map.name) == 0 && type == list->map.type) {
+ SCLogError(SC_ERR_INVALID_VALUE, "storage for type \"%s\" with "
+ "name \"%s\" already registered", StoragePrintType(type),
+ name);
+ return -1;
+ }
+
+ list = list->next;
+ }
+
+ StorageList *entry = SCMalloc(sizeof(StorageList));
+ if (unlikely(entry == NULL))
+ return -1;
+
+ memset(entry, 0x00, sizeof(StorageList));
+
+ entry->map.type = type;
+ entry->map.name = name;
+ entry->map.size = size;
+ entry->map.Alloc = Alloc;
+ entry->map.Free = Free;
+
+ entry->id = storage_max_id[type]++;
+ entry->next = storage_list;
+ storage_list = entry;
+
+ return entry->id;
+}
+
+int StorageFinalize(void)
+{
+ int count = 0;
+ int i;
+
+ storage_registraton_closed = 1;
+
+ for (i = 0; i < STORAGE_MAX; i++) {
+ if (storage_max_id[i] > 0)
+ count++;
+ }
+ if (count == 0)
+ return 0;
+
+ storage_map = SCMalloc(sizeof(StorageMapping *) * STORAGE_MAX);
+ if (unlikely(storage_map == NULL)) {
+ return -1;
+ }
+ memset(storage_map, 0x00, sizeof(StorageMapping *) * STORAGE_MAX);
+
+ for (i = 0; i < STORAGE_MAX; i++) {
+ if (storage_max_id[i] > 0) {
+ storage_map[i] = SCMalloc(sizeof(StorageMapping) * storage_max_id[i]);
+ if (storage_map[i] == NULL)
+ return -1;
+ memset(storage_map[i], 0x00, sizeof(StorageMapping) * storage_max_id[i]);
+ }
+ }
+
+ StorageList *entry = storage_list;
+ while (entry) {
+ if (storage_map[entry->map.type] != NULL) {
+ storage_map[entry->map.type][entry->id].name = entry->map.name;
+ storage_map[entry->map.type][entry->id].type = entry->map.type;
+ storage_map[entry->map.type][entry->id].size = entry->map.size;
+ storage_map[entry->map.type][entry->id].Alloc = entry->map.Alloc;
+ storage_map[entry->map.type][entry->id].Free = entry->map.Free;
+ }
+
+ entry = entry->next;
+ };
+
+#ifdef DEBUG
+ for (i = 0; i < STORAGE_MAX; i++) {
+ if (storage_map[i] == NULL)
+ continue;
+
+ int j;
+ for (j = 0; j < storage_max_id[i]; j++) {
+ StorageMapping *m = &storage_map[i][j];
+ SCLogDebug("type \"%s\" name \"%s\" size \"%"PRIuMAX"\"",
+ StoragePrintType(m->type), m->name, (uintmax_t)m->size);
+ }
+ }
+#endif
+ return 0;
+}
+
+unsigned int StorageGetCnt(StorageEnum type)
+{
+ return storage_max_id[type];
+}
+
+/** \brief get the size of the void array used to store
+ * the pointers
+ * \retval size size in bytes, can return 0 if not storage is needed
+ *
+ * \todo we could return -1 when registration isn't closed yet, however
+ * this will break lots of tests currently, so not doing it now */
+unsigned int StorageGetSize(StorageEnum type)
+{
+ return storage_max_id[type] * sizeof(void *);
+}
+
+void *StorageGetById(const Storage *storage, const StorageEnum type, const int id)
+{
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+ SCLogDebug("storage %p id %d", storage, id);
+ if (storage == NULL)
+ return NULL;
+ return storage[id];
+}
+
+int StorageSetById(Storage *storage, const StorageEnum type, const int id, void *ptr)
+{
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+ SCLogDebug("storage %p id %d", storage, id);
+ if (storage == NULL)
+ return -1;
+ storage[id] = ptr;
+ return 0;
+}
+
+void *StorageAllocByIdPrealloc(Storage *storage, StorageEnum type, int id)
+{
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+ SCLogDebug("storage %p id %d", storage, id);
+
+ StorageMapping *map = &storage_map[type][id];
+ if (storage[id] == NULL && map->Alloc != NULL) {
+ storage[id] = map->Alloc(map->size);
+ if (storage[id] == NULL) {
+ return NULL;
+ }
+ }
+
+ return storage[id];
+}
+
+void *StorageAllocById(Storage **storage, StorageEnum type, int id)
+{
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+ SCLogDebug("storage %p id %d", storage, id);
+
+ StorageMapping *map = &storage_map[type][id];
+ Storage *store = *storage;
+ if (store == NULL) {
+ store = SCMalloc(sizeof(void *) * storage_max_id[type]);
+ if (unlikely(store == NULL))
+ return NULL;
+ memset(store, 0x00, sizeof(void *) * storage_max_id[type]);
+ }
+ SCLogDebug("store %p", store);
+
+ if (store[id] == NULL && map->Alloc != NULL) {
+ store[id] = map->Alloc(map->size);
+ if (store[id] == NULL) {
+ SCFree(store);
+ *storage = NULL;
+ return NULL;
+ }
+ }
+
+ *storage = store;
+ return store[id];
+}
+
+void StorageFreeById(Storage *storage, StorageEnum type, int id)
+{
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+#ifdef UNITTESTS
+ if (storage_map == NULL)
+ return;
+#endif
+ SCLogDebug("storage %p id %d", storage, id);
+
+ Storage *store = storage;
+ if (store != NULL) {
+ SCLogDebug("store %p", store);
+ if (store[id] != NULL) {
+ StorageMapping *map = &storage_map[type][id];
+ map->Free(store[id]);
+ store[id] = NULL;
+ }
+ }
+}
+
+void StorageFreeAll(Storage *storage, StorageEnum type)
+{
+ if (storage == NULL)
+ return;
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+#ifdef UNITTESTS
+ if (storage_map == NULL)
+ return;
+#endif
+
+ Storage *store = storage;
+ int i;
+ for (i = 0; i < storage_max_id[type]; i++) {
+ if (store[i] != NULL) {
+ StorageMapping *map = &storage_map[type][i];
+ map->Free(store[i]);
+ store[i] = NULL;
+ }
+ }
+}
+
+void StorageFree(Storage **storage, StorageEnum type)
+{
+ if (*storage == NULL)
+ return;
+
+#ifdef DEBUG
+ BUG_ON(!storage_registraton_closed);
+#endif
+#ifdef UNITTESTS
+ if (storage_map == NULL)
+ return;
+#endif
+
+ Storage *store = *storage;
+ int i;
+ for (i = 0; i < storage_max_id[type]; i++) {
+ if (store[i] != NULL) {
+ StorageMapping *map = &storage_map[type][i];
+ map->Free(store[i]);
+ store[i] = NULL;
+ }
+ }
+ SCFree(*storage);
+ *storage = NULL;
+}
+
+#ifdef UNITTESTS
+
+static void *StorageTestAlloc(unsigned int size)
+{
+ void *x = SCMalloc(size);
+ return x;
+}
+static void StorageTestFree(void *x)
+{
+ if (x)
+ SCFree(x);
+}
+
+static int StorageTest01(void)
+{
+ StorageInit();
+
+ int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
+ if (id < 0)
+ goto error;
+ id = StorageRegister(STORAGE_HOST, "variable", 24, StorageTestAlloc, StorageTestFree);
+ if (id < 0)
+ goto error;
+ id = StorageRegister(STORAGE_FLOW, "store", sizeof(void *), StorageTestAlloc, StorageTestFree);
+ if (id < 0)
+ goto error;
+
+ if (StorageFinalize() < 0)
+ goto error;
+
+ StorageCleanup();
+ return 1;
+error:
+ StorageCleanup();
+ return 0;
+}
+
+struct StorageTest02Data {
+ int abc;
+};
+
+static void *StorageTest02Init(unsigned int size)
+{
+ struct StorageTest02Data *data = (struct StorageTest02Data *)SCMalloc(size);
+ if (data != NULL)
+ data->abc = 1234;
+ return (void *)data;
+}
+
+static int StorageTest02(void)
+{
+ struct StorageTest02Data *test = NULL;
+
+ StorageInit();
+
+ int id1 = StorageRegister(STORAGE_HOST, "test", 4, StorageTest02Init, StorageTestFree);
+ if (id1 < 0) {
+ printf("StorageRegister failed (2): ");
+ goto error;
+ }
+ int id2 = StorageRegister(STORAGE_HOST, "test2", 4, StorageTest02Init, StorageTestFree);
+ if (id2 < 0) {
+ printf("StorageRegister failed (2): ");
+ goto error;
+ }
+
+ if (StorageFinalize() < 0) {
+ printf("StorageFinalize failed: ");
+ goto error;
+ }
+
+ Storage *storage = NULL;
+ void *data = StorageAllocById(&storage, STORAGE_HOST, id1);
+ if (data == NULL) {
+ printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
+ goto error;
+ }
+ test = (struct StorageTest02Data *)data;
+ if (test->abc != 1234) {
+ printf("setup failed, test->abc != 1234, but %d (1):", test->abc);
+ goto error;
+ }
+ test->abc = 4321;
+
+ data = StorageAllocById(&storage, STORAGE_HOST, id2);
+ if (data == NULL) {
+ printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
+ goto error;
+ }
+ test = (struct StorageTest02Data *)data;
+ if (test->abc != 1234) {
+ printf("setup failed, test->abc != 1234, but %d (2):", test->abc);
+ goto error;
+ }
+
+ data = StorageGetById(storage, STORAGE_HOST, id1);
+ if (data == NULL) {
+ printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
+ goto error;
+ }
+ test = (struct StorageTest02Data *)data;
+ if (test->abc != 4321) {
+ printf("setup failed, test->abc != 4321, but %d (3):", test->abc);
+ goto error;
+ }
+
+ //StorageFreeById(storage, STORAGE_HOST, id1);
+ //StorageFreeById(storage, STORAGE_HOST, id2);
+
+ StorageFree(&storage, STORAGE_HOST);
+
+ StorageCleanup();
+ return 1;
+error:
+ StorageCleanup();
+ return 0;
+}
+
+static int StorageTest03(void)
+{
+ StorageInit();
+
+ int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
+ if (id < 0)
+ goto error;
+ id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed: ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_HOST, "test1", 6, NULL, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (2): ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_HOST, "test2", 8, StorageTestAlloc, NULL);
+ if (id != -1) {
+ printf("duplicate registration should have failed (3): ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_HOST, "test3", 0, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (4): ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_HOST, "", 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (5): ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_HOST, NULL, 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (6): ");
+ goto error;
+ }
+
+ id = StorageRegister(STORAGE_MAX, "test4", 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (7): ");
+ goto error;
+ }
+
+ id = StorageRegister(38, "test5", 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (8): ");
+ goto error;
+ }
+
+ id = StorageRegister(-1, "test6", 8, StorageTestAlloc, StorageTestFree);
+ if (id != -1) {
+ printf("duplicate registration should have failed (9): ");
+ goto error;
+ }
+
+ StorageCleanup();
+ return 1;
+error:
+ StorageCleanup();
+ return 0;
+}
+
+void StorageRegisterTests(void)
+{
+ UtRegisterTest("StorageTest01", StorageTest01, 1);
+ UtRegisterTest("StorageTest02", StorageTest02, 1);
+ UtRegisterTest("StorageTest03", StorageTest03, 1);
+}
+#endif
diff --git a/framework/src/suricata/src/util-storage.h b/framework/src/suricata/src/util-storage.h
new file mode 100644
index 00000000..d88b030f
--- /dev/null
+++ b/framework/src/suricata/src/util-storage.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Storage API
+ */
+
+#ifndef __UTIL_STORAGE_H__
+#define __UTIL_STORAGE_H__
+
+typedef enum StorageEnum_ {
+ STORAGE_HOST,
+ STORAGE_FLOW,
+ STORAGE_IPPAIR,
+
+ STORAGE_MAX,
+} StorageEnum;
+
+/** void ptr array for now */
+typedef void* Storage;
+
+void StorageInit(void);
+void StorageCleanup(void);
+
+/** \brief Register new storage
+ *
+ * \param type type from StorageEnum
+ * \param name name
+ * \param size size of the per instance storage
+ * \param Alloc alloc function for per instance storage
+ * \param Free free function for per instance storage
+ *
+ * \note if size == ptr size (so sizeof(void *)) and Alloc == NULL the API just
+ * gives the caller a ptr to store something it alloc'ed itself.
+ */
+int StorageRegister(const StorageEnum type, const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *));
+int StorageFinalize(void);
+
+unsigned int StorageGetCnt(const StorageEnum type);
+unsigned int StorageGetSize(const StorageEnum type);
+
+/** \brief get storage for id */
+void *StorageGetById(const Storage *storage, const StorageEnum type, const int id);
+/** \brief set storage for id */
+int StorageSetById(Storage *storage, const StorageEnum type, const int id, void *ptr);
+
+/** \brief AllocById func for prealloc'd base storage (storage ptrs are part
+ * of another memory block) */
+void *StorageAllocByIdPrealloc(Storage *storage, StorageEnum type, int id);
+/** \brief AllocById func for when we manage the Storage ptr itself */
+void *StorageAllocById(Storage **storage, const StorageEnum type, const int id);
+void StorageFreeById(Storage *storage, const StorageEnum type, const int id);
+void StorageFreeAll(Storage *storage, const StorageEnum type);
+void StorageFree(Storage **storage, const StorageEnum type);
+
+void StorageRegisterTests(void);
+#endif
diff --git a/framework/src/suricata/src/util-strlcatu.c b/framework/src/suricata/src/util-strlcatu.c
new file mode 100644
index 00000000..2edb8080
--- /dev/null
+++ b/framework/src/suricata/src/util-strlcatu.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id: strlcatu.c,v 1.4 2003/10/20 15:03:27 chrisgreen Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_STRLCAT
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
+#endif
diff --git a/framework/src/suricata/src/util-strlcpyu.c b/framework/src/suricata/src/util-strlcpyu.c
new file mode 100644
index 00000000..80694580
--- /dev/null
+++ b/framework/src/suricata/src/util-strlcpyu.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id: strlcpyu.c,v 1.4 2003/10/20 15:03:27 chrisgreen Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_STRLCPY
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
diff --git a/framework/src/suricata/src/util-syslog.c b/framework/src/suricata/src/util-syslog.c
new file mode 100644
index 00000000..5b3f7da4
--- /dev/null
+++ b/framework/src/suricata/src/util-syslog.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Syslog utility file
+ *
+ */
+
+#include "suricata-common.h"
+
+/* holds the string-enum mapping for the syslog facility in SCLogOPIfaceCtx */
+SCEnumCharMap sc_syslog_facility_map[] = {
+ { "auth", LOG_AUTH },
+ { "authpriv", LOG_AUTHPRIV },
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+ { "ftp", LOG_FTP },
+ { "kern", LOG_KERN },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "news", LOG_NEWS },
+ { "security", LOG_AUTH },
+ { "syslog", LOG_SYSLOG },
+ { "user", LOG_USER },
+ { "uucp", LOG_UUCP },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { NULL, -1 }
+};
+
+/** \brief returns the syslog facility enum map */
+SCEnumCharMap *SCSyslogGetFacilityMap()
+{
+ return sc_syslog_facility_map;
+}
+
+SCEnumCharMap sc_syslog_level_map[ ] = {
+ { "Emergency", LOG_EMERG },
+ { "Alert", LOG_ALERT },
+ { "Critical", LOG_CRIT },
+ { "Error", LOG_ERR },
+ { "Warning", LOG_WARNING },
+ { "Notice", LOG_NOTICE },
+ { "Info", LOG_INFO },
+ { "Debug", LOG_DEBUG },
+ { NULL, -1 }
+};
+
+/** \brief returns the syslog facility enum map */
+SCEnumCharMap *SCSyslogGetLogLevelMap()
+{
+ return sc_syslog_level_map;
+}
+
diff --git a/framework/src/suricata/src/util-syslog.h b/framework/src/suricata/src/util-syslog.h
new file mode 100644
index 00000000..0efc1c5d
--- /dev/null
+++ b/framework/src/suricata/src/util-syslog.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ */
+
+#ifndef UTIL_SYSLOG_H
+#define UTIL_SYSLOG_H
+
+SCEnumCharMap *SCSyslogGetFacilityMap(void);
+SCEnumCharMap *SCSyslogGetLogLevelMap(void);
+
+#endif /* UTIL_SYSLOG_H */
diff --git a/framework/src/suricata/src/util-threshold-config.c b/framework/src/suricata/src/util-threshold-config.c
new file mode 100644
index 00000000..6698b16f
--- /dev/null
+++ b/framework/src/suricata/src/util-threshold-config.c
@@ -0,0 +1,2909 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup threshold
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva Pinto <breno.silva@gmail.com>
+ *
+ * \todo Need to support suppress
+ *
+ * Implements Threshold support
+ */
+
+#include "suricata-common.h"
+
+#include "host.h"
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-address.h"
+#include "detect-threshold.h"
+#include "detect-parse.h"
+
+#include "conf.h"
+#include "util-threshold-config.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+#include "util-time.h"
+#include "util-error.h"
+#include "util-debug.h"
+#include "util-fmemopen.h"
+
+typedef enum ThresholdRuleType {
+ THRESHOLD_TYPE_EVENT_FILTER,
+ THRESHOLD_TYPE_THRESHOLD,
+ THRESHOLD_TYPE_RATE,
+ THRESHOLD_TYPE_SUPPRESS,
+} ThresholdRuleType;
+
+/* File descriptor for unittests */
+
+/* common base for all options */
+#define DETECT_BASE_REGEX "^\\s*(event_filter|threshold|rate_filter|suppress)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*(.*)\\s*$"
+
+#define DETECT_THRESHOLD_REGEX "^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$"
+
+/* TODO: "apply_to" */
+#define DETECT_RATE_REGEX "^,\\s*track\\s*(by_dst|by_src|by_rule)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*,\\s*new_action\\s*(alert|drop|pass|log|sdrop|reject)\\s*,\\s*timeout\\s*(\\d+)\\s*$"
+
+/*
+ * suppress has two form:
+ * suppress gen_id 0, sig_id 0, track by_dst, ip 10.88.0.14
+ * suppress gen_id 1, sig_id 2000328
+ * suppress gen_id 1, sig_id 2000328, track by_src, ip fe80::/10
+*/
+#define DETECT_SUPPRESS_REGEX "^,\\s*track\\s*(by_dst|by_src|by_either)\\s*,\\s*ip\\s*([\\[\\],\\$\\da-zA-Z.:/_]+)*\\s*$"
+
+/* Default path for the threshold.config file */
+#if defined OS_WIN32 || defined __CYGWIN__
+#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\threshold.config"
+#else
+#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/threshold.config"
+#endif
+
+static pcre *regex_base = NULL;
+static pcre_extra *regex_base_study = NULL;
+
+static pcre *regex_threshold = NULL;
+static pcre_extra *regex_threshold_study = NULL;
+
+static pcre *regex_rate = NULL;
+static pcre_extra *regex_rate_study = NULL;
+
+static pcre *regex_suppress = NULL;
+static pcre_extra *regex_suppress_study = NULL;
+
+/**
+ * \brief Returns the path for the Threshold Config file. We check if we
+ * can retrieve the path from the yaml conf file. If it is not present,
+ * return the default path for the threshold file which is
+ * "./threshold.config".
+ *
+ * \retval log_filename Pointer to a string containing the path for the
+ * Threshold Config file.
+ */
+static char *SCThresholdConfGetConfFilename(const DetectEngineCtx *de_ctx)
+{
+ char *log_filename = NULL;
+ char config_value[256] = "";
+
+ if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
+ snprintf(config_value, sizeof(config_value),
+ "%s.threshold-file", de_ctx->config_prefix);
+
+ /* try loading prefix setting, fall back to global if that
+ * fails. */
+ if (ConfGet(config_value, &log_filename) != 1) {
+ if (ConfGet("threshold-file", &log_filename) != 1) {
+ log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
+ }
+ }
+ } else {
+ if (ConfGet("threshold-file", &log_filename) != 1) {
+ log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
+ }
+ }
+ return log_filename;
+}
+
+/**
+ * \brief Inits the context to be used by the Threshold Config parsing API.
+ *
+ * This function initializes the hash table to be used by the Detection
+ * Engine Context to hold the data from the threshold.config file,
+ * obtains the file desc to parse the threshold.config file, and
+ * inits the regex used to parse the lines from threshold.config
+ * file.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param utfd Pointer for unit test file descriptor.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCThresholdConfInitContext(DetectEngineCtx *de_ctx, FILE *utfd)
+{
+ char *filename = NULL;
+ const char *eb = NULL;
+ FILE *fd = utfd;
+ int eo;
+ int opts = 0;
+
+ if (fd == NULL) {
+ filename = SCThresholdConfGetConfFilename(de_ctx);
+ if ( (fd = fopen(filename, "r")) == NULL) {
+ SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
+ goto error;
+ }
+ }
+
+ regex_base = pcre_compile(DETECT_BASE_REGEX, opts, &eb, &eo, NULL);
+ if (regex_base == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_BASE_REGEX, eo, eb);
+ goto error;
+ }
+
+ regex_base_study = pcre_study(regex_base, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ regex_threshold = pcre_compile(DETECT_THRESHOLD_REGEX, opts, &eb, &eo, NULL);
+ if (regex_threshold == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_THRESHOLD_REGEX, eo, eb);
+ goto error;
+ }
+
+ regex_threshold_study = pcre_study(regex_threshold, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ regex_rate = pcre_compile(DETECT_RATE_REGEX, opts, &eb, &eo, NULL);
+ if (regex_rate == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_RATE_REGEX, eo, eb);
+ goto error;
+ }
+
+ regex_rate_study = pcre_study(regex_rate, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ regex_suppress = pcre_compile(DETECT_SUPPRESS_REGEX, opts, &eb, &eo, NULL);
+ if (regex_suppress == NULL) {
+ SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_SUPPRESS_REGEX, eo, eb);
+ goto error;
+ }
+
+ regex_suppress_study = pcre_study(regex_suppress, 0, &eb);
+ if (eb != NULL) {
+ SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+ goto error;
+ }
+
+ SCThresholdConfParseFile(de_ctx, fd);
+ SCThresholdConfDeInitContext(de_ctx, fd);
+
+ SCLogDebug("Global thresholding options defined");
+ return 0;
+
+error:
+ SCThresholdConfDeInitContext(de_ctx, fd);
+ return -1;
+}
+
+/**
+ * \brief Releases resources used by the Threshold Config API.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param fd Pointer to file descriptor.
+ */
+void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ if (fd != NULL)
+ fclose(fd);
+
+ if (regex_base != NULL) {
+ pcre_free(regex_base);
+ regex_base = NULL;
+ }
+ if (regex_base_study != NULL) {
+ pcre_free(regex_base_study);
+ regex_base_study = NULL;
+ }
+
+ if (regex_threshold != NULL) {
+ pcre_free(regex_threshold);
+ regex_threshold = NULL;
+ }
+ if (regex_threshold_study != NULL) {
+ pcre_free(regex_threshold_study);
+ regex_threshold_study = NULL;
+ }
+
+ if (regex_rate != NULL) {
+ pcre_free(regex_rate);
+ regex_rate = NULL;
+ }
+ if (regex_rate_study != NULL) {
+ pcre_free(regex_rate_study);
+ regex_rate_study = NULL;
+ }
+
+ if (regex_suppress != NULL) {
+ pcre_free(regex_suppress);
+ regex_suppress = NULL;
+ }
+ if (regex_suppress_study != NULL) {
+ pcre_free(regex_suppress_study);
+ regex_suppress_study = NULL;
+ }
+
+ return;
+}
+
+/** \internal
+ * \brief setup suppress rules
+ * \retval 0 ok
+ * \retval -1 error
+ */
+static int SetupSuppressRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
+ uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
+ uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
+ const char *th_ip)
+{
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectThresholdData *de = NULL;
+
+ BUG_ON(parsed_type != TYPE_SUPPRESS);
+
+ /* Install it */
+ if (id == 0 && gid == 0) {
+ if (parsed_track == TRACK_RULE) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules");
+ }
+
+ /* update each sig with our suppress info */
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ /* tag the rule as noalert */
+ if (parsed_track == TRACK_RULE) {
+ s->flags |= SIG_FLAG_NOALERT;
+ continue;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = TYPE_SUPPRESS;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ if (parsed_track != TRACK_RULE) {
+ if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
+ goto error;
+ }
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
+ }
+ } else if (id == 0 && gid > 0) {
+ if (parsed_track == TRACK_RULE) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules with gid %"PRIu32, gid);
+ }
+ /* set up suppression for each signature with a matching gid */
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->gid != gid)
+ continue;
+
+ /* tag the rule as noalert */
+ if (parsed_track == TRACK_RULE) {
+ s->flags |= SIG_FLAG_NOALERT;
+ continue;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = TYPE_SUPPRESS;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ if (parsed_track != TRACK_RULE) {
+ if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
+ goto error;
+ }
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
+ }
+ } else if (id > 0 && gid == 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
+ "sid > 0 and gid == 0. Please fix this "
+ "in your threshold.conf file");
+ goto error;
+ } else {
+ s = SigFindSignatureBySidGid(de_ctx, id, gid);
+ if (s == NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
+ "%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
+ } else {
+ if (parsed_track == TRACK_RULE) {
+ s->flags |= SIG_FLAG_NOALERT;
+ goto end;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = TYPE_SUPPRESS;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
+ SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
+ goto error;
+ }
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
+ }
+ }
+
+end:
+ return 0;
+error:
+ if (de != NULL) {
+ DetectAddressHeadCleanup(&de->addrs);
+ SCFree(de);
+ }
+ return -1;
+}
+
+/** \internal
+ * \brief setup suppress rules
+ * \retval 0 ok
+ * \retval -1 error
+ */
+static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
+ uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
+ uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
+ const char *th_ip)
+{
+ Signature *s = NULL;
+ SigMatch *sm = NULL;
+ DetectThresholdData *de = NULL;
+ void *ptmp;
+
+ BUG_ON(parsed_type == TYPE_SUPPRESS);
+
+ /* Install it */
+ if (id == 0 && gid == 0) {
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "an event var set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "We'll change this in the future though.", s->id);
+ continue;
+ }
+
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "an event var set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "We'll change this in the future though.", s->id);
+ continue;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = parsed_type;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ if (parsed_type == TYPE_RATE)
+ sm->type = DETECT_DETECTION_FILTER;
+ else
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+
+ if (parsed_track == TRACK_RULE) {
+ ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
+ if (ptmp == NULL) {
+ SCFree(de_ctx->ths_ctx.th_entry);
+ de_ctx->ths_ctx.th_entry = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
+ " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
+ } else {
+ de_ctx->ths_ctx.th_entry = ptmp;
+ de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
+ de_ctx->ths_ctx.th_size++;
+ }
+ }
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
+ }
+
+ } else if (id == 0 && gid > 0) {
+ for (s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->gid == gid) {
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "an event var set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "We'll change this in the future though.", id);
+ continue;
+ }
+
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "an event var set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "We'll change this in the future though.", id);
+ continue;
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = parsed_type;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ if (parsed_type == TYPE_RATE)
+ sm->type = DETECT_DETECTION_FILTER;
+ else
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+
+ if (parsed_track == TRACK_RULE) {
+ ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
+ if (ptmp == NULL) {
+ SCFree(de_ctx->ths_ctx.th_entry);
+ de_ctx->ths_ctx.th_entry = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
+ " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
+ } else {
+ de_ctx->ths_ctx.th_entry = ptmp;
+ de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
+ de_ctx->ths_ctx.th_size++;
+ }
+ }
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
+ }
+ }
+ } else if (id > 0 && gid == 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
+ "sid > 0 and gid == 0. Please fix this "
+ "in your threshold.conf file");
+ } else {
+ s = SigFindSignatureBySidGid(de_ctx, id, gid);
+ if (s == NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
+ "%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
+ } else {
+ if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD &&
+ parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT)
+ {
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "a threshold set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "Bug #425.", s->id);
+ goto end;
+ }
+
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm != NULL) {
+ SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
+ "a detection_filter set. The signature event var is "
+ "given precedence over the threshold.conf one. "
+ "Bug #425.", s->id);
+ goto end;
+ }
+
+ /* replace threshold on sig if we have a global override for it */
+#if 1
+ } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) {
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ if (sm == NULL) {
+ sm = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+ }
+ if (sm != NULL) {
+ SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_THRESHOLD);
+ SigMatchFree(sm);
+ sm = NULL;
+ }
+#endif
+ }
+
+ de = SCMalloc(sizeof(DetectThresholdData));
+ if (unlikely(de == NULL))
+ goto error;
+ memset(de,0,sizeof(DetectThresholdData));
+
+ de->type = parsed_type;
+ de->track = parsed_track;
+ de->count = parsed_count;
+ de->seconds = parsed_seconds;
+ de->new_action = parsed_new_action;
+ de->timeout = parsed_timeout;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
+ goto error;
+ }
+
+ if (parsed_type == TYPE_RATE)
+ sm->type = DETECT_DETECTION_FILTER;
+ else
+ sm->type = DETECT_THRESHOLD;
+ sm->ctx = (void *)de;
+
+ if (parsed_track == TRACK_RULE) {
+ ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
+ if (ptmp == NULL) {
+ SCFree(de_ctx->ths_ctx.th_entry);
+ de_ctx->ths_ctx.th_entry = NULL;
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
+ " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
+ } else {
+ de_ctx->ths_ctx.th_entry = ptmp;
+ de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
+ de_ctx->ths_ctx.th_size++;
+ }
+ }
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
+ }
+ }
+end:
+ return 0;
+error:
+ if (de != NULL) {
+ DetectAddressHeadCleanup(&de->addrs);
+ SCFree(de);
+ }
+ return -1;
+}
+
+static int ParseThresholdRule(DetectEngineCtx *de_ctx, char *rawstr,
+ uint32_t *ret_id, uint32_t *ret_gid,
+ uint8_t *ret_parsed_type, uint8_t *ret_parsed_track,
+ uint32_t *ret_parsed_count, uint32_t *ret_parsed_seconds, uint32_t *ret_parsed_timeout,
+ uint8_t *ret_parsed_new_action,
+ const char **ret_th_ip)
+{
+ char th_rule_type[32];
+ char th_gid[16];
+ char th_sid[16];
+ char rule_extend[1024];
+ const char *th_type = NULL;
+ const char *th_track = NULL;
+ const char *th_count = NULL;
+ const char *th_seconds = NULL;
+ const char *th_new_action= NULL;
+ const char *th_timeout = NULL;
+ const char *th_ip = NULL;
+
+ uint8_t parsed_type = 0;
+ uint8_t parsed_track = 0;
+ uint8_t parsed_new_action = 0;
+ uint32_t parsed_count = 0;
+ uint32_t parsed_seconds = 0;
+ uint32_t parsed_timeout = 0;
+
+#define MAX_SUBSTRINGS 30
+ int ret = 0;
+ int ov[MAX_SUBSTRINGS];
+ uint32_t id = 0, gid = 0;
+ ThresholdRuleType rule_type;
+
+ if (de_ctx == NULL)
+ return -1;
+
+ ret = pcre_exec(regex_base, regex_base_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 4) {
+ SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
+ goto error;
+ }
+
+ /* retrieve the classtype name */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, th_rule_type, sizeof(th_rule_type));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ /* retrieve the classtype name */
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 2, th_gid, sizeof(th_gid));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 3, th_sid, sizeof(th_sid));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ ret = pcre_copy_substring((char *)rawstr, ov, 30, 4, rule_extend, sizeof(rule_extend));
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ /* get type of rule */
+ if (strcasecmp(th_rule_type,"event_filter") == 0) {
+ rule_type = THRESHOLD_TYPE_EVENT_FILTER;
+ } else if (strcasecmp(th_rule_type,"threshold") == 0) {
+ rule_type = THRESHOLD_TYPE_THRESHOLD;
+ } else if (strcasecmp(th_rule_type,"rate_filter") == 0) {
+ rule_type = THRESHOLD_TYPE_RATE;
+ } else if (strcasecmp(th_rule_type,"suppress") == 0) {
+ rule_type = THRESHOLD_TYPE_SUPPRESS;
+ } else {
+ SCLogError(SC_ERR_INVALID_VALUE, "rule type %s is unknown", th_rule_type);
+ goto error;
+ }
+
+ /* get end of rule */
+ switch(rule_type) {
+ case THRESHOLD_TYPE_EVENT_FILTER:
+ case THRESHOLD_TYPE_THRESHOLD:
+ if (strlen(rule_extend) > 0) {
+ ret = pcre_exec(regex_threshold, regex_threshold_study,
+ rule_extend, strlen(rule_extend),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 4) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "pcre_exec parse error, ret %" PRId32 ", string %s",
+ ret, rule_extend);
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_type);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_track);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 3, &th_count);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 4, &th_seconds);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ if (strcasecmp(th_type,"limit") == 0)
+ parsed_type = TYPE_LIMIT;
+ else if (strcasecmp(th_type,"both") == 0)
+ parsed_type = TYPE_BOTH;
+ else if (strcasecmp(th_type,"threshold") == 0)
+ parsed_type = TYPE_THRESHOLD;
+ else {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "limit type not supported: %s", th_type);
+ goto error;
+ }
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
+ goto error;
+ }
+ break;
+ case THRESHOLD_TYPE_SUPPRESS:
+ if (strlen(rule_extend) > 0) {
+ ret = pcre_exec(regex_suppress, regex_suppress_study,
+ rule_extend, strlen(rule_extend),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 2) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "pcre_exec parse error, ret %" PRId32 ", string %s",
+ ret, rule_extend);
+ goto error;
+ }
+ /* retrieve the track mode */
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_track);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ /* retrieve the IP */
+ ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_ip);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+ } else {
+ parsed_track = TRACK_RULE;
+ }
+ parsed_type = TYPE_SUPPRESS;
+ break;
+ case THRESHOLD_TYPE_RATE:
+ if (strlen(rule_extend) > 0) {
+ ret = pcre_exec(regex_rate, regex_rate_study,
+ rule_extend, strlen(rule_extend),
+ 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 5) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "pcre_exec parse error, ret %" PRId32 ", string %s",
+ ret, rule_extend);
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 1, &th_track);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 2, &th_count);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 3, &th_seconds);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 4, &th_new_action);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 5, &th_timeout);
+ if (ret < 0) {
+ SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
+ goto error;
+ }
+
+ /* TODO: implement option "apply_to" */
+
+ if (ByteExtractStringUint32(&parsed_timeout, 10, strlen(th_timeout), th_timeout) <= 0) {
+ goto error;
+ }
+
+ /* Get the new action to take */
+ if (strcasecmp(th_new_action, "alert") == 0)
+ parsed_new_action = TH_ACTION_ALERT;
+ if (strcasecmp(th_new_action, "drop") == 0)
+ parsed_new_action = TH_ACTION_DROP;
+ if (strcasecmp(th_new_action, "pass") == 0)
+ parsed_new_action = TH_ACTION_PASS;
+ if (strcasecmp(th_new_action, "reject") == 0)
+ parsed_new_action = TH_ACTION_REJECT;
+ if (strcasecmp(th_new_action, "log") == 0) {
+ SCLogInfo("log action for rate_filter not supported yet");
+ parsed_new_action = TH_ACTION_LOG;
+ }
+ if (strcasecmp(th_new_action, "sdrop") == 0) {
+ SCLogInfo("sdrop action for rate_filter not supported yet");
+ parsed_new_action = TH_ACTION_SDROP;
+ }
+ parsed_type = TYPE_RATE;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
+ goto error;
+ }
+ break;
+ default:
+ SCLogError(SC_ERR_PCRE_MATCH, "unable to find rule type for string %s", rawstr);
+ goto error;
+ }
+
+ switch (rule_type) {
+ /* This part is common to threshold/event_filter/rate_filter */
+ case THRESHOLD_TYPE_EVENT_FILTER:
+ case THRESHOLD_TYPE_THRESHOLD:
+ case THRESHOLD_TYPE_RATE:
+ if (strcasecmp(th_track,"by_dst") == 0)
+ parsed_track = TRACK_DST;
+ else if (strcasecmp(th_track,"by_src") == 0)
+ parsed_track = TRACK_SRC;
+ else if (strcasecmp(th_track,"by_rule") == 0)
+ parsed_track = TRACK_RULE;
+ else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rawstr);
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&parsed_count, 10, strlen(th_count), th_count) <= 0) {
+ goto error;
+ }
+ if (parsed_count == 0) {
+ SCLogError(SC_ERR_INVALID_VALUE, "rate filter count should be > 0");
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&parsed_seconds, 10, strlen(th_seconds), th_seconds) <= 0) {
+ goto error;
+ }
+
+ break;
+ case THRESHOLD_TYPE_SUPPRESS:
+ /* need to get IP if extension is provided */
+ if (th_track != NULL) {
+ if (strcasecmp(th_track,"by_dst") == 0)
+ parsed_track = TRACK_DST;
+ else if (strcasecmp(th_track,"by_src") == 0)
+ parsed_track = TRACK_SRC;
+ else if (strcasecmp(th_track,"by_either") == 0) {
+ parsed_track = TRACK_EITHER;
+ }
+ else {
+ SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rule_extend);
+ goto error;
+ }
+ }
+ break;
+ }
+
+ if (ByteExtractStringUint32(&id, 10, strlen(th_sid), th_sid) <= 0) {
+ goto error;
+ }
+
+ if (ByteExtractStringUint32(&gid, 10, strlen(th_gid), th_gid) <= 0) {
+ goto error;
+ }
+
+ *ret_id = id;
+ *ret_gid = gid;
+ *ret_parsed_type = parsed_type;
+ *ret_parsed_track = parsed_track;
+ *ret_parsed_new_action = parsed_new_action;
+ *ret_parsed_count = parsed_count;
+ *ret_parsed_seconds = parsed_seconds;
+ *ret_parsed_timeout = parsed_timeout;
+ *ret_th_ip = th_ip;
+ return 0;
+error:
+ if (th_track != NULL)
+ SCFree((char *)th_track);
+ if (th_count != NULL)
+ SCFree((char *)th_count);
+ if (th_seconds != NULL)
+ SCFree((char *)th_seconds);
+ if (th_type != NULL)
+ SCFree((char *)th_type);
+ if (th_ip != NULL)
+ SCFree((char *)th_ip);
+ return -1;
+}
+
+/**
+ * \brief Parses a line from the threshold file and applies it to the
+ * detection engine
+ *
+ * \param rawstr Pointer to the string to be parsed.
+ * \param de_ctx Pointer to the Detection Engine Context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx)
+{
+ uint8_t parsed_type = 0;
+ uint8_t parsed_track = 0;
+ uint8_t parsed_new_action = 0;
+ uint32_t parsed_count = 0;
+ uint32_t parsed_seconds = 0;
+ uint32_t parsed_timeout = 0;
+ const char *th_ip = NULL;
+ uint32_t id, gid;
+
+ int r = 0;
+ r = ParseThresholdRule(de_ctx, rawstr, &id, &gid, &parsed_type, &parsed_track,
+ &parsed_count, &parsed_seconds, &parsed_timeout, &parsed_new_action,
+ &th_ip);
+ if (r < 0)
+ goto error;
+
+ if (parsed_type == TYPE_SUPPRESS) {
+ r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track,
+ parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
+ th_ip);
+ } else {
+ r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track,
+ parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
+ th_ip);
+ }
+ if (r < 0) {
+ goto error;
+ }
+
+ return 0;
+error:
+ if (th_ip != NULL)
+ SCFree((char *)th_ip);
+ return -1;
+}
+
+/**
+ * \brief Checks if a string is a comment or a blank line.
+ *
+ * Comments lines are lines of the following format -
+ * "# This is a comment string" or
+ * " # This is a comment string".
+ *
+ * \param line String that has to be checked
+ *
+ * \retval 1 On the argument string being a comment or blank line
+ * \retval 0 Otherwise
+ */
+int SCThresholdConfIsLineBlankOrComment(char *line)
+{
+ while (*line != '\0') {
+ /* we have a comment */
+ if (*line == '#')
+ return 1;
+
+ /* this line is neither a comment line, nor a blank line */
+ if (!isspace((unsigned char)*line))
+ return 0;
+
+ line++;
+ }
+
+ /* we have a blank line */
+ return 1;
+}
+
+/**
+ * \brief Checks if the rule is multiline, by searching an ending slash
+ *
+ * \param line String that has to be checked
+ *
+ * \retval the position of the slash making it multiline
+ * \retval 0 Otherwise
+ */
+int SCThresholdConfLineIsMultiline(char *line)
+{
+ int flag = 0;
+ char *rline = line;
+ int len = strlen(line);
+
+ while (line < rline + len && *line != '\n') {
+ /* we have a comment */
+ if (*line == '\\')
+ flag = line - rline;
+ else
+ if (!isspace((unsigned char)*line))
+ flag = 0;
+
+ line++;
+ }
+
+ /* we have a blank line */
+ return flag;
+}
+
+/**
+ * \brief Get the config line length to allocate the buffer needed
+ *
+ * \param fd Pointer to file descriptor.
+ * \retval int of the line length
+ */
+int SCThresholdConfLineLength(FILE *fd)
+{
+ long pos = ftell(fd);
+ int len = 0;
+ int c;
+
+ while ( (c = fgetc(fd)) && (char)c != '\n' && c != EOF && !feof(fd))
+ len++;
+
+ if (pos < 0)
+ pos = 0;
+
+ if (fseek(fd, pos, SEEK_SET) < 0) {
+ SCLogError(SC_ERR_THRESHOLD_SETUP, "threshold fseek failure: %s",
+ strerror(errno));
+ return -1;
+ }
+ return len;
+}
+
+/**
+ * \brief Parses the Threshold Config file
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param fd Pointer to file descriptor.
+ */
+void SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fd)
+{
+ char *line = NULL;
+ int len = 0;
+ int rule_num = 0;
+
+ /* position of "\", on multiline rules */
+ int esc_pos = 0;
+
+ if (fd == NULL)
+ return;
+
+ while (!feof(fd)) {
+ len = SCThresholdConfLineLength(fd);
+
+ if (len > 0) {
+ if (line == NULL) {
+ line = SCMalloc(len + 1);
+ if (unlikely(line == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ return;
+ }
+ } else {
+ char *newline = SCRealloc(line, strlen(line) + len + 1);
+ if (unlikely(newline == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
+ SCFree(line);
+ return;
+ }
+ line = newline;
+ }
+
+ if (fgets(line + esc_pos, len + 1, fd) == NULL)
+ break;
+
+ /* Skip EOL to inspect the next line (or read EOF) */
+ (void)fgetc(fd);
+
+ if (SCThresholdConfIsLineBlankOrComment(line)) {
+ continue;
+ }
+
+ esc_pos = SCThresholdConfLineIsMultiline(line);
+ if (esc_pos == 0) {
+ rule_num++;
+ SCLogDebug("Adding threshold.config rule num %"PRIu32"( %s )", rule_num, line);
+ SCThresholdConfAddThresholdtype(line, de_ctx);
+ }
+ } else {
+ /* Skip EOL to inspect the next line (or read EOF) */
+ (void)fgetc(fd);
+ if (feof(fd))
+ break;
+ }
+ }
+
+ SCLogInfo("Threshold config parsed: %d rule(s) found", rule_num);
+
+ /* Free the last line */
+ SCFree(line);
+
+ return;
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD01()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60\n"
+ "threshold gen_id 1, sig_id 100, type both, track by_dst, count 10, seconds 60\n"
+ "event_filter gen_id 1, sig_id 1000, type threshold, track by_src, count 100, seconds 60\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with some valid options and a couple of invalid options.
+ * For testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateInValidDummyFD02()
+{
+ FILE *fd;
+ const char *buffer =
+ "event_filter gen_id 1, sig_id 1000, type invalid, track by_src, count 100, seconds 60\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD03()
+{
+ FILE *fd;
+ const char *buffer =
+ "event_filter gen_id 0, sig_id 0, type threshold, track by_src, count 100, seconds 60\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD04()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 1, seconds 60\n"
+ "threshold gen_id 1, \\\nsig_id 100, type both\\\n, track by_dst, count 10, \\\n seconds 60\n"
+ "event_filter gen_id 1, sig_id 1000, \\\ntype threshold, track \\\nby_src, count 100, seconds 60\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD05()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "rate_filter gen_id 1, sig_id 10, track by_src, count 1, seconds 60, new_action drop, timeout 10\n"
+ "rate_filter gen_id 1, sig_id 100, track by_dst, count 10, seconds 60, new_action pass, timeout 5\n"
+ "rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD06()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "rate_filter \\\ngen_id 1, sig_id 10, track by_src, count 1, seconds 60\\\n, new_action drop, timeout 10\n"
+ "rate_filter gen_id 1, \\\nsig_id 100, track by_dst, \\\ncount 10, seconds 60, new_action pass, timeout 5\n"
+ "rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD07()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "rate_filter gen_id 1, sig_id 10, track by_src, count 3, seconds 3, new_action drop, timeout 10\n"
+ "rate_filter gen_id 1, sig_id 11, track by_src, count 3, seconds 1, new_action drop, timeout 5\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD08()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "rate_filter gen_id 1, sig_id 10, track by_rule, count 3, seconds 3, new_action drop, timeout 10\n"
+ "rate_filter gen_id 1, sig_id 11, track by_src, count 3, seconds 1, new_action drop, timeout 5\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD09()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 2, seconds 60\n"
+ "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 3, \\\n seconds 60\n"
+ "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 2, seconds 60\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, but
+ * with splitted rules (multiline), for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD10()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 5, seconds 2\n"
+ "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 5, \\\n seconds 2\n"
+ "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 5, seconds 2\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD11()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "suppress gen_id 1, sig_id 10000\n"
+ "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest01(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD01();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60))
+ result = 1;
+ }
+
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest02(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:100;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD01();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_BOTH && de->track == TRACK_DST && de->count == 10 && de->seconds == 60))
+ result = 1;
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest03(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD01();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60))
+ result = 1;
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest04(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateInValidDummyFD02();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de == NULL)
+ return result;
+ else
+ result = 1;
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest05(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ Signature *s = NULL, *ns = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:100;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD03();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ for (s = de_ctx->sig_list; s != NULL;) {
+
+ ns = s->next;
+
+ if(s->id == 1 || s->id == 10 || s->id == 100) {
+
+ m = SigMatchGetLastSMFromLists(s, 2,
+ DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m == NULL) {
+ goto end;
+ } else {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60))
+ result++;
+ }
+ }
+
+ s = ns;
+ }
+
+ if(result == 3)
+ result = 1;
+
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest06(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD04();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60))
+ result = 1;
+ }
+
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules are loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest07(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD05();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_DETECTION_FILTER, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60))
+ result = 1;
+ }
+
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules are loaded and well parsed
+ * with multilines
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest08(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD06();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_DETECTION_FILTER, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]);
+
+ if(m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60))
+ result = 1;
+ }
+
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest09(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD07();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ result = 0;
+ goto end;
+ }
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ result = 0;
+ goto end;
+ }
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ result = 0;
+ goto end;
+ }
+
+ TimeSetIncrementTime(2);
+ TimeGet(&p->ts);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || !(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ result = 0;
+ goto end;
+ }
+
+ TimeSetIncrementTime(3);
+ TimeGet(&p->ts);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || !(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ result = 0;
+ goto end;
+ }
+
+ TimeSetIncrementTime(10);
+ TimeGet(&p->ts);
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ result = 0;
+ goto end;
+ }
+
+ p->alerts.cnt = 0;
+ p->action = 0;
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP)) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules work with track by_rule
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest10(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
+ Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int alerts = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL || p2 == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD08();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts = PacketAlertCheck(p, 10);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ alerts += PacketAlertCheck(p2, 10);
+ if (alerts > 0) {
+ result = 0;
+ goto end;
+ }
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+ if (alerts != 1) {
+ result = 0;
+ goto end;
+ }
+
+ TimeSetIncrementTime(2);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+ alerts += PacketAlertCheck(p2, 10);
+ if (alerts != 2) {
+ result = 0;
+ goto end;
+ }
+
+ TimeSetIncrementTime(10);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts += PacketAlertCheck(p, 10);
+
+ if (alerts == 2)
+ result = 1;
+
+ /* Ensure that a Threshold entry was installed at the sig */
+ if (de_ctx->ths_ctx.th_entry[sig->num] == NULL) {
+ result = 0;
+ goto end;
+ }
+
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest11(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int alerts10 = 0;
+ int alerts11 = 0;
+ int alerts12 = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD09();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+
+ TimeSetIncrementTime(100);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+
+ TimeSetIncrementTime(10);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+
+ if (alerts10 == 4)
+ result = 1;
+
+ /* One on the first interval, another on the second */
+ if (alerts11 == 2)
+ result = 1;
+
+ if (alerts12 == 2)
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the rate_filter rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest12(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int alerts10 = 0;
+ int alerts11 = 0;
+ int alerts12 = 0;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD10();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+
+ TimeSetIncrementTime(100);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+
+ TimeSetIncrementTime(10);
+ TimeGet(&p->ts);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ alerts10 += PacketAlertCheck(p, 10);
+ alerts11 += PacketAlertCheck(p, 11);
+ alerts12 += PacketAlertCheck(p, 12);
+
+ /* Yes, none of the alerts will be out of the count of the given interval for type limit */
+ if (alerts10 == 10)
+ result = 1;
+
+ /* One on the first interval, another on the second */
+ if (alerts11 == 1)
+ result = 1;
+
+ if (alerts12 == 1)
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest13(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD11();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]);
+
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if(de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC))
+ result = 1;
+
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the suppress rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+int SCThresholdConfTest14(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
+ "192.168.0.100", 1234, 24);
+ Packet *p1 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
+ "192.168.0.100", 1234, 24);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:10000;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"suppress test 2\"; gid:1; sid:10;)");
+ sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"suppress test 3\"; gid:1; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD11();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+ if ((PacketAlertCheck(p, 10000) == 0) && (PacketAlertCheck(p, 10) == 1) &&
+ (PacketAlertCheck(p, 1000) == 1) && (PacketAlertCheck(p1, 1000) == 0))
+ result = 1;
+
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the suppress rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest15(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
+ "192.168.0.100", 1234, 24);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any any (msg:\"suppress test\"; content:\"lalala\"; gid:1; sid:10000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD11();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* 10000 shouldn't match */
+ if (PacketAlertCheck(p, 10000) != 0) {
+ printf("sid 10000 should not have alerted: ");
+ goto end;
+ }
+ /* however, it should have set the drop flag */
+ if (!(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ printf("sid 10000 should have set DROP flag even if suppressed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the suppress rules work
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest16(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
+ "192.168.0.100", 1234, 24);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"drop tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD11();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* 10000 shouldn't match */
+ if (PacketAlertCheck(p, 1000) != 0) {
+ printf("sid 1000 should not have alerted: ");
+ goto end;
+ }
+ /* however, it should have set the drop flag */
+ if (!(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ printf("sid 1000 should have set DROP flag even if suppressed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the suppress rules work - ip only rule
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest17(void)
+{
+ Signature *sig = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
+ "192.168.0.100", 1234, 24);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ struct timeval ts;
+
+ memset (&ts, 0, sizeof(struct timeval));
+ TimeGet(&ts);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL || p == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"drop tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:10000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD11();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ /* 10000 shouldn't match */
+ if (PacketAlertCheck(p, 10000) != 0) {
+ printf("sid 10000 should not have alerted: ");
+ goto end;
+ }
+ /* however, it should have set the drop flag */
+ if (!(PACKET_TEST_ACTION(p, ACTION_DROP))) {
+ printf("sid 10000 should have set DROP flag even if suppressed: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ UTHFreePacket(p);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+static FILE *SCThresholdConfGenerateInvalidDummyFD12()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n"
+ "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check if the suppress rule parsing handles errors correctly
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest18(void)
+{
+ Signature *s = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+ SigMatch *sm = NULL;
+ DetectThresholdData *de = NULL;
+
+ HostInitConfig(HOST_QUIET);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return result;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateInvalidDummyFD12();
+ SCThresholdConfInitContext(de_ctx,fd);
+ SigGroupBuild(de_ctx);
+
+ if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] == NULL) {
+ printf("no thresholds: ");
+ goto end;
+ }
+ sm = s->sm_lists[DETECT_SM_LIST_SUPPRESS];
+ if (sm == NULL) {
+ printf("no sm: ");
+ goto end;
+ }
+
+ de = (DetectThresholdData *)sm->ctx;
+ if (de == NULL) {
+ printf("no de: ");
+ goto end;
+ }
+ if (!(de->type == TYPE_SUPPRESS && de->track == TRACK_DST)) {
+ printf("de state wrong: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ DetectEngineCtxFree(de_ctx);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+static FILE *SCThresholdConfGenerateInvalidDummyFD13()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n"
+ "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check if the suppress rule parsing handles errors correctly
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest19(void)
+{
+ Signature *s = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+ SigMatch *sm = NULL;
+ DetectThresholdData *de = NULL;
+
+ HostInitConfig(HOST_QUIET);
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ return result;
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
+ if (s == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateInvalidDummyFD13();
+ SCThresholdConfInitContext(de_ctx,fd);
+ SigGroupBuild(de_ctx);
+
+ if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] == NULL) {
+ printf("no thresholds: ");
+ goto end;
+ }
+ sm = s->sm_lists[DETECT_SM_LIST_SUPPRESS];
+ if (sm == NULL) {
+ printf("no sm: ");
+ goto end;
+ }
+
+ de = (DetectThresholdData *)sm->ctx;
+ if (de == NULL) {
+ printf("no de: ");
+ goto end;
+ }
+ if (!(de->type == TYPE_SUPPRESS && de->track == TRACK_DST)) {
+ printf("de state wrong: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ DetectEngineCtxFree(de_ctx);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
+ *
+ * \retval fd Pointer to file descriptor.
+ */
+FILE *SCThresholdConfGenerateValidDummyFD20()
+{
+ FILE *fd = NULL;
+ const char *buffer =
+ "suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n"
+ "suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n"
+ "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
+
+ fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+ if (fd == NULL)
+ SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
+
+ return fd;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest20(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD20();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]);
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ m = m->next;
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ m = m->next;
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ result = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ HostShutdown();
+ return result;
+}
+
+/**
+ * \test Check if the threshold file is loaded and well parsed, and applied
+ * correctly to a rule with thresholding
+ *
+ * \retval 1 on succces
+ * \retval 0 on failure
+ */
+static int SCThresholdConfTest21(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ DetectThresholdData *de = NULL;
+ Signature *sig = NULL;
+ SigMatch *m = NULL;
+ int result = 0;
+ FILE *fd = NULL;
+
+ HostInitConfig(HOST_QUIET);
+
+ if (de_ctx == NULL)
+ return result;
+
+ de_ctx->flags |= DE_QUIET;
+
+ sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
+ if (sig == NULL) {
+ goto end;
+ }
+
+ fd = SCThresholdConfGenerateValidDummyFD20();
+ SCThresholdConfInitContext(de_ctx,fd);
+
+ m = SigMatchGetLastSMFromLists(sig, 2,
+ DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]);
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ m = m->next;
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ m = m->next;
+ if (m != NULL) {
+ de = (DetectThresholdData *)m->ctx;
+ if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) {
+ result = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+end:
+ SigGroupBuild(de_ctx);
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ HostShutdown();
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for Classification Config API.
+ */
+void SCThresholdConfRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01, 1);
+ UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02, 1);
+ UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03, 1);
+ UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04, 0);
+ UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05, 1);
+ UtRegisterTest("SCThresholdConfTest06", SCThresholdConfTest06, 1);
+ UtRegisterTest("SCThresholdConfTest07", SCThresholdConfTest07, 1);
+ UtRegisterTest("SCThresholdConfTest08", SCThresholdConfTest08, 1);
+ UtRegisterTest("SCThresholdConfTest09 - rate_filter", SCThresholdConfTest09, 1);
+ UtRegisterTest("SCThresholdConfTest10 - rate_filter", SCThresholdConfTest10, 1);
+ UtRegisterTest("SCThresholdConfTest11 - event_filter", SCThresholdConfTest11, 1);
+ UtRegisterTest("SCThresholdConfTest12 - event_filter", SCThresholdConfTest12, 1);
+ UtRegisterTest("SCThresholdConfTest13", SCThresholdConfTest13, 1);
+ UtRegisterTest("SCThresholdConfTest14 - suppress", SCThresholdConfTest14, 1);
+ UtRegisterTest("SCThresholdConfTest15 - suppress drop", SCThresholdConfTest15, 1);
+ UtRegisterTest("SCThresholdConfTest16 - suppress drop", SCThresholdConfTest16, 1);
+ UtRegisterTest("SCThresholdConfTest17 - suppress drop", SCThresholdConfTest17, 1);
+
+ UtRegisterTest("SCThresholdConfTest18 - suppress parsing", SCThresholdConfTest18, 1);
+ UtRegisterTest("SCThresholdConfTest19 - suppress parsing", SCThresholdConfTest19, 1);
+ UtRegisterTest("SCThresholdConfTest20 - suppress parsing", SCThresholdConfTest20, 1);
+ UtRegisterTest("SCThresholdConfTest21 - suppress parsing", SCThresholdConfTest21, 1);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/framework/src/suricata/src/util-threshold-config.h b/framework/src/suricata/src/util-threshold-config.h
new file mode 100644
index 00000000..906ed7c6
--- /dev/null
+++ b/framework/src/suricata/src/util-threshold-config.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Breno Silva Pinto <breno.silva@gmail.com>
+ */
+
+#ifndef __UTIL_THRESHOLD_CONFIG_H__
+#define __UTIL_THRESHOLD_CONFIG_H__
+
+void SCThresholdConfDeInitContext(DetectEngineCtx *, FILE *);
+void SCThresholdConfParseFile(DetectEngineCtx *, FILE *);
+int SCThresholdConfInitContext(DetectEngineCtx *, FILE *);
+
+void SCThresholdConfRegisterTests();
+
+#endif /* __UTIL_THRESHOLD_CONFIG_H__ */
diff --git a/framework/src/suricata/src/util-time.c b/framework/src/suricata/src/util-time.c
new file mode 100644
index 00000000..7c7a3624
--- /dev/null
+++ b/framework/src/suricata/src/util-time.c
@@ -0,0 +1,304 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Ken Steele <suricata@tilera.com>
+ *
+ * Time keeping for offline (non-live) packet handling (pcap files).
+ * And time string generation for alerts.
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "threads.h"
+#include "util-debug.h"
+
+static struct timeval current_time = { 0, 0 };
+//static SCMutex current_time_mutex = SCMUTEX_INITIALIZER;
+static SCSpinlock current_time_spinlock;
+static char live = TRUE;
+
+
+struct tm *SCLocalTime(time_t timep, struct tm *result);
+
+void TimeInit(void)
+{
+ SCSpinInit(&current_time_spinlock, 0);
+
+ /* Initialize Time Zone settings. */
+ tzset();
+}
+
+void TimeDeinit(void)
+{
+ SCSpinDestroy(&current_time_spinlock);
+}
+
+void TimeModeSetLive(void)
+{
+ live = TRUE;
+ SCLogDebug("live time mode enabled");
+}
+
+void TimeModeSetOffline (void)
+{
+ live = FALSE;
+ SCLogDebug("offline time mode enabled");
+}
+
+void TimeSet(struct timeval *tv)
+{
+ if (live == TRUE)
+ return;
+
+ if (tv == NULL)
+ return;
+
+ SCSpinLock(&current_time_spinlock);
+ current_time.tv_sec = tv->tv_sec;
+ current_time.tv_usec = tv->tv_usec;
+
+ SCLogDebug("time set to %" PRIuMAX " sec, %" PRIuMAX " usec",
+ (uintmax_t)current_time.tv_sec, (uintmax_t)current_time.tv_usec);
+
+ SCSpinUnlock(&current_time_spinlock);
+}
+
+/** \brief set the time to "gettimeofday" meant for testing */
+void TimeSetToCurrentTime(void)
+{
+ struct timeval tv;
+ memset(&tv, 0x00, sizeof(tv));
+
+ gettimeofday(&tv, NULL);
+
+ TimeSet(&tv);
+}
+
+void TimeGet(struct timeval *tv)
+{
+ if (tv == NULL)
+ return;
+
+ if (live == TRUE) {
+ gettimeofday(tv, NULL);
+ } else {
+ SCSpinLock(&current_time_spinlock);
+ tv->tv_sec = current_time.tv_sec;
+ tv->tv_usec = current_time.tv_usec;
+ SCSpinUnlock(&current_time_spinlock);
+ }
+
+ SCLogDebug("time we got is %" PRIuMAX " sec, %" PRIuMAX " usec",
+ (uintmax_t)tv->tv_sec, (uintmax_t)tv->tv_usec);
+}
+
+/** \brief increment the time in the engine
+ * \param tv_sec seconds to increment the time with */
+void TimeSetIncrementTime(uint32_t tv_sec)
+{
+ struct timeval tv;
+ memset(&tv, 0x00, sizeof(tv));
+ TimeGet(&tv);
+
+ tv.tv_sec += tv_sec;
+
+ TimeSet(&tv);
+}
+
+void CreateIsoTimeString (const struct timeval *ts, char *str, size_t size)
+{
+ time_t time = ts->tv_sec;
+ struct tm local_tm;
+ struct tm *t = (struct tm*)SCLocalTime(time, &local_tm);
+ char time_fmt[64] = { 0 };
+
+ if (likely(t != NULL)) {
+ strftime(time_fmt, sizeof(time_fmt), "%Y-%m-%dT%H:%M:%S.%%06u%z", t);
+ snprintf(str, size, time_fmt, ts->tv_usec);
+ } else {
+ snprintf(str, size, "ts-error");
+ }
+}
+
+/*
+ * Time Caching code
+ */
+
+#ifndef TLS
+/* OpenBSD does not support __thread, so don't use time caching on BSD
+ */
+struct tm *SCLocalTime(time_t timep, struct tm *result)
+{
+ return localtime_r(&timep, result);
+}
+
+void CreateTimeString (const struct timeval *ts, char *str, size_t size)
+{
+ time_t time = ts->tv_sec;
+ struct tm local_tm;
+ struct tm *t = (struct tm*)SCLocalTime(time, &local_tm);
+
+ if (likely(t != NULL)) {
+ snprintf(str, size, "%02d/%02d/%02d-%02d:%02d:%02d.%06u",
+ t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour,
+ t->tm_min, t->tm_sec, (uint32_t) ts->tv_usec);
+ } else {
+ snprintf(str, size, "ts-error");
+ }
+}
+
+#else
+
+/* On systems supporting __thread, use Per-thread values for caching
+ * in CreateTimeString */
+
+/* The maximum possible length of the time string.
+ * "%02d/%02d/%02d-%02d:%02d:%02d.%06u"
+ * Or "01/01/2013-15:42:21.123456", which is 26, so round up to 32. */
+#define MAX_LOCAL_TIME_STRING 32
+
+static __thread int mru_time_slot; /* Most recently used cached value */
+static __thread time_t last_local_time[2];
+static __thread short int cached_local_time_len[2];
+static __thread char cached_local_time[2][MAX_LOCAL_TIME_STRING];
+
+/* Per-thread values for caching SCLocalTime() These cached values are
+ * independent from the CreateTimeString cached values. */
+static __thread int mru_tm_slot; /* Most recently used local tm */
+static __thread time_t cached_minute_start[2];
+static __thread struct tm cached_local_tm[2];
+
+/** \brief Convert time_t into Year, month, day, hour and minutes.
+ * \param timep Time in seconds since defined date.
+ * \param result The structure into which the broken down time it put.
+ *
+ * To convert a time in seconds into year, month, day, hours, minutes
+ * and seconds, call localtime_r(), which uses the current time zone
+ * to compute these values. Note, glibc's localtime_r() aquires a lock
+ * each time it is called, which limits parallelism. To call
+ * localtime_r() less often, the values returned are cached for the
+ * current and previous minute and then seconds are adjusted to
+ * compute the returned result. This is valid as long as the
+ * difference between the start of the current minute and the current
+ * time is less than 60 seconds. Once the minute value changes, all
+ * the other values could change.
+ *
+ * Two values are cached to prevent thrashing when changing from one
+ * minute to the next. The two cached minutes are independent and are
+ * not required to be M and M+1. If more than two minutes are
+ * requested, the least-recently-used cached value is updated more
+ * often, the results are still correct, but performance will be closer
+ * to previous performance.
+ */
+struct tm *SCLocalTime(time_t timep, struct tm *result)
+{
+ /* Only get a new local time when the time crosses into a new
+ * minute. */
+ int mru = mru_tm_slot;
+ int lru = 1 - mru;
+ int mru_seconds = timep - cached_minute_start[mru];
+ int lru_seconds = timep - cached_minute_start[lru];
+ int new_seconds;
+ if (mru_seconds >= 0 && mru_seconds <= 59) {
+ /* Use most-recently cached time, adjusting the seconds. */
+ new_seconds = mru_seconds;
+ } else if (lru_seconds >= 0 && lru_seconds <= 59) {
+ /* Use least-recently cached time, update to most recently used. */
+ new_seconds = lru_seconds;
+ mru = lru;
+ mru_tm_slot = mru;
+ } else {
+ /* Update least-recent cached time. */
+ if (localtime_r(&timep, &cached_local_tm[lru]) == NULL)
+ return NULL;
+
+ /* Subtract seconds to get back to the start of the minute. */
+ new_seconds = cached_local_tm[lru].tm_sec;
+ cached_minute_start[lru] = timep - new_seconds;
+ mru = lru;
+ mru_tm_slot = mru;
+ }
+ memcpy(result, &cached_local_tm[mru], sizeof(struct tm));
+ result->tm_sec = new_seconds;
+
+ return result;
+}
+
+/* Update the cached time string in cache index N, for the current minute. */
+static int UpdateCachedTime(int n, time_t time)
+{
+ struct tm local_tm;
+ struct tm *t = (struct tm *)SCLocalTime(time, &local_tm);
+ int cached_len = snprintf(cached_local_time[n], MAX_LOCAL_TIME_STRING,
+ "%02d/%02d/%02d-%02d:%02d:",
+ t->tm_mon + 1, t->tm_mday, t->tm_year + 1900,
+ t->tm_hour, t->tm_min);
+ cached_local_time_len[n] = cached_len;
+ /* Store the time of the beginning of the minute. */
+ last_local_time[n] = time - t->tm_sec;
+ mru_time_slot = n;
+
+ return t->tm_sec;
+}
+
+/** \brief Return a formatted string for the provided time.
+ *
+ * Cache the Month/Day/Year - Hours:Min part of the time string for
+ * the current minute. Copy that result into the the return string and
+ * then only print the seconds for each call.
+ */
+void CreateTimeString (const struct timeval *ts, char *str, size_t size)
+{
+ time_t time = ts->tv_sec;
+ int seconds;
+
+ /* Only get a new local time when the time crosses into a new
+ * minute */
+ int mru = mru_time_slot;
+ int lru = 1 - mru;
+ int mru_seconds = time - last_local_time[mru];
+ int lru_seconds = time - last_local_time[lru];
+ if (mru_seconds >= 0 && mru_seconds <= 59) {
+ /* Use most-recently cached time. */
+ seconds = mru_seconds;
+ } else if (lru_seconds >= 0 && lru_seconds <= 59) {
+ /* Use least-recently cached time. Change this slot to Most-recent */
+ seconds = lru_seconds;
+ mru_time_slot = lru;
+ } else {
+ /* Update least-recent cached time. Lock accessing local time
+ * function because it keeps any internal non-spin lock. */
+ seconds = UpdateCachedTime(lru, time);
+ }
+
+ /* Copy the string up to the current minute then print the seconds
+ into the return string buffer. */
+ char *cached_str = cached_local_time[mru_time_slot];
+ int cached_len = cached_local_time_len[mru_time_slot];
+ if (cached_len >= (int)size)
+ cached_len = size;
+ memcpy(str, cached_str, cached_len);
+ snprintf(str + cached_len, size - cached_len,
+ "%02d.%06u",
+ seconds, (uint32_t) ts->tv_usec);
+}
+
+#endif /* defined(__OpenBSD__) */
diff --git a/framework/src/suricata/src/util-time.h b/framework/src/suricata/src/util-time.h
new file mode 100644
index 00000000..cdf1d5bf
--- /dev/null
+++ b/framework/src/suricata/src/util-time.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_TIME_H__
+#define __UTIL_TIME_H__
+
+/**
+ * A timeval with 32 bit fields.
+ *
+ * Used by the unified on disk file format.
+ */
+typedef struct SCTimeval32_ {
+ uint32_t tv_sec;
+ uint32_t tv_usec;
+} SCTimeval32;
+
+void TimeInit(void);
+void TimeDeinit(void);
+
+void TimeSet(struct timeval *);
+void TimeGet(struct timeval *);
+
+void TimeSetToCurrentTime(void);
+void TimeSetIncrementTime(uint32_t);
+
+void TimeModeSetLive(void);
+void TimeModeSetOffline (void);
+
+struct tm *SCLocalTime(time_t timep, struct tm *result);
+void CreateTimeString (const struct timeval *ts, char *str, size_t size);
+void CreateIsoTimeString (const struct timeval *ts, char *str, size_t size);
+
+#endif /* __UTIL_TIME_H__ */
+
diff --git a/framework/src/suricata/src/util-unittest-helper.c b/framework/src/suricata/src/util-unittest-helper.c
new file mode 100644
index 00000000..a318205d
--- /dev/null
+++ b/framework/src/suricata/src/util-unittest-helper.c
@@ -0,0 +1,1081 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ *
+ * This file provide a set of helper functions for reducing the complexity
+ * when constructing unittests
+ */
+
+#include "suricata-common.h"
+
+#include "decode.h"
+
+#include "flow-private.h"
+#include "flow-util.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-sigorder.h"
+
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-error.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#ifdef UNITTESTS
+
+/**
+ * \brief return the uint32_t for a ipv4 address string
+ *
+ * \param str Valid ipaddress in string form (e.g. 1.2.3.4)
+ *
+ * \retval uint the uin32_t representation
+ */
+uint32_t UTHSetIPv4Address(char *str)
+{
+ struct in_addr in;
+ if (inet_pton(AF_INET, str, &in) != 1) {
+ printf("invalid IPv6 address %s\n", str);
+ exit(EXIT_FAILURE);
+ }
+ return (uint32_t)in.s_addr;
+}
+
+/**
+ * \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
+ * specifying ip and port sources and destinations (IPV6)
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ * \param src pointer to a string containing the ip source
+ * \param dst pointer to a string containing the ip destination
+ * \param sport pointer to a string containing the port source
+ * \param dport pointer to a string containing the port destination
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacketIPV6Real(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto, char *src, char *dst,
+ uint16_t sport, uint16_t dport)
+{
+ uint32_t in[4];
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return NULL;
+
+ TimeSet(&p->ts);
+
+ p->src.family = AF_INET6;
+ p->dst.family = AF_INET6;
+ p->payload = payload;
+ p->payload_len = payload_len;
+ p->proto = ipproto;
+
+ p->ip6h = SCMalloc(sizeof(IPV6Hdr));
+ if (p->ip6h == NULL)
+ goto error;
+ memset(p->ip6h, 0, sizeof(IPV6Hdr));
+ p->ip6h->s_ip6_nxt = ipproto;
+ p->ip6h->s_ip6_plen = htons(payload_len + sizeof(TCPHdr));
+
+ if (inet_pton(AF_INET6, src, &in) != 1)
+ goto error;
+ p->src.addr_data32[0] = in[0];
+ p->src.addr_data32[1] = in[1];
+ p->src.addr_data32[2] = in[2];
+ p->src.addr_data32[3] = in[3];
+ p->sp = sport;
+ p->ip6h->s_ip6_src[0] = in[0];
+ p->ip6h->s_ip6_src[1] = in[1];
+ p->ip6h->s_ip6_src[2] = in[2];
+ p->ip6h->s_ip6_src[3] = in[3];
+
+ if (inet_pton(AF_INET6, dst, &in) != 1)
+ goto error;
+ p->dst.addr_data32[0] = in[0];
+ p->dst.addr_data32[1] = in[1];
+ p->dst.addr_data32[2] = in[2];
+ p->dst.addr_data32[3] = in[3];
+ p->dp = dport;
+ p->ip6h->s_ip6_dst[0] = in[0];
+ p->ip6h->s_ip6_dst[1] = in[1];
+ p->ip6h->s_ip6_dst[2] = in[2];
+ p->ip6h->s_ip6_dst[3] = in[3];
+
+ p->tcph = SCMalloc(sizeof(TCPHdr));
+ if (p->tcph == NULL)
+ goto error;
+ memset(p->tcph, 0, sizeof(TCPHdr));
+ p->tcph->th_sport = htons(sport);
+ p->tcph->th_dport = htons(dport);
+
+ SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(TCPHdr) + payload_len);
+ return p;
+
+error:
+ if (p != NULL) {
+ if (p->ip6h != NULL) {
+ SCFree(p->ip6h);
+ }
+ if (p->tcph != NULL) {
+ SCFree(p->tcph);
+ }
+ SCFree(p);
+ }
+ return NULL;
+}
+
+/**
+ * \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
+ * specifying ip and port sources and destinations
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ * \param src pointer to a string containing the ip source
+ * \param dst pointer to a string containing the ip destination
+ * \param sport pointer to a string containing the port source
+ * \param dport pointer to a string containing the port destination
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto, char *src, char *dst,
+ uint16_t sport, uint16_t dport)
+{
+ struct in_addr in;
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return NULL;
+
+ struct timeval tv;
+ TimeGet(&tv);
+ COPY_TIMESTAMP(&tv, &p->ts);
+
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->payload = payload;
+ p->payload_len = payload_len;
+ p->proto = ipproto;
+
+ if (inet_pton(AF_INET, src, &in) != 1)
+ goto error;
+ p->src.addr_data32[0] = in.s_addr;
+ p->sp = sport;
+
+ if (inet_pton(AF_INET, dst, &in) != 1)
+ goto error;
+ p->dst.addr_data32[0] = in.s_addr;
+ p->dp = dport;
+
+ p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+ if (p->ip4h == NULL)
+ goto error;
+
+ p->ip4h->s_ip_src.s_addr = p->src.addr_data32[0];
+ p->ip4h->s_ip_dst.s_addr = p->dst.addr_data32[0];
+ p->ip4h->ip_proto = ipproto;
+ p->ip4h->ip_verhl = sizeof(IPV4Hdr);
+ p->proto = ipproto;
+
+ int hdr_offset = sizeof(IPV4Hdr);
+ switch (ipproto) {
+ case IPPROTO_UDP:
+ p->udph = (UDPHdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
+ if (p->udph == NULL)
+ goto error;
+
+ p->udph->uh_sport = sport;
+ p->udph->uh_dport = dport;
+ hdr_offset += sizeof(UDPHdr);
+ break;
+ case IPPROTO_TCP:
+ p->tcph = (TCPHdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
+ if (p->tcph == NULL)
+ goto error;
+
+ p->tcph->th_sport = htons(sport);
+ p->tcph->th_dport = htons(dport);
+ hdr_offset += sizeof(TCPHdr);
+ break;
+ case IPPROTO_ICMP:
+ p->icmpv4h = (ICMPV4Hdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
+ if (p->icmpv4h == NULL)
+ goto error;
+
+ hdr_offset += sizeof(ICMPV4Hdr);
+ break;
+ default:
+ break;
+ /* TODO: Add more protocols */
+ }
+
+ PacketCopyDataOffset(p, hdr_offset, payload, payload_len);
+ SET_PKT_LEN(p, hdr_offset + payload_len);
+ p->payload = GET_PKT_DATA(p)+hdr_offset;
+
+ return p;
+
+error:
+ SCFree(p);
+ return NULL;
+}
+
+/**
+ * \brief UTHBuildPacket is a wrapper that build packets with default ip
+ * and port fields
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacket(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto)
+{
+ return UTHBuildPacketReal(payload, payload_len, ipproto,
+ "192.168.1.5", "192.168.1.1",
+ 41424, 80);
+}
+
+/**
+ * \brief UTHBuildPacketArrayFromEth is a wrapper that build a packets from an array of
+ * packets in ethernet rawbytes. Hint: It also share the flows.
+ *
+ * \param raw_eth pointer to the array of ethernet packets in rawbytes
+ * \param pktsize pointer to the array of sizes corresponding to each buffer pointed
+ * from pktsize.
+ * \param numpkts number of packets in the array
+ *
+ * \retval Packet pointer to the array of built in packets; NULL if something fail
+ */
+Packet **UTHBuildPacketArrayFromEth(uint8_t *raw_eth[], int *pktsize, int numpkts)
+{
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ if (raw_eth == NULL || pktsize == NULL || numpkts <= 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "The arrays cant be null, and the number"
+ " of packets should be grater thatn zero");
+ return NULL;
+ }
+ Packet **p = NULL;
+ p = SCMalloc(sizeof(Packet *) * numpkts);
+ if (unlikely(p == NULL))
+ return NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ int i = 0;
+ for (; i < numpkts; i++) {
+ p[i] = PacketGetFromAlloc();
+ if (p[i] == NULL) {
+ SCFree(p);
+ return NULL;
+ }
+ DecodeEthernet(&th_v, &dtv, p[i], raw_eth[i], pktsize[i], NULL);
+ }
+ return p;
+}
+
+/**
+ * \brief UTHBuildPacketFromEth is a wrapper that build a packet for the rawbytes
+ *
+ * \param raw_eth pointer to the rawbytes containing an ethernet packet
+ * (and any other headers inside)
+ * \param pktsize pointer to the length of the payload
+ *
+ * \retval Packet pointer to the built in packet; NULL if something fail
+ */
+Packet *UTHBuildPacketFromEth(uint8_t *raw_eth, uint16_t pktsize)
+{
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DecodeEthernet(&th_v, &dtv, p, raw_eth, pktsize, NULL);
+ return p;
+}
+
+/**
+ * \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
+ * and defaulting ports
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacketSrcDst(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto, char *src, char *dst)
+{
+ return UTHBuildPacketReal(payload, payload_len, ipproto,
+ src, dst,
+ 41424, 80);
+}
+
+/**
+ * \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
+ * and defaulting ports (IPV6)
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacketIPV6SrcDst(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto, char *src, char *dst)
+{
+ return UTHBuildPacketIPV6Real(payload, payload_len, ipproto,
+ src, dst,
+ 41424, 80);
+}
+
+/**
+ * \brief UTHBuildPacketSrcDstPorts is a wrapper that build packets specifying
+ * src and dst ports and defaulting IPs
+ *
+ * \param payload pointer to the payloadd buffer
+ * \param payload_len pointer to the length of the payload
+ * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
+ *
+ * \retval Packet pointer to the built in packet
+ */
+Packet *UTHBuildPacketSrcDstPorts(uint8_t *payload, uint16_t payload_len,
+ uint8_t ipproto, uint16_t sport, uint16_t dport)
+{
+ return UTHBuildPacketReal(payload, payload_len, ipproto,
+ "192.168.1.5", "192.168.1.1",
+ sport, dport);
+}
+
+/**
+ * \brief UTHFreePackets: function to release the allocated data
+ * from UTHBuildPacket and the packet itself
+ *
+ * \param p pointer to the Packet
+ */
+void UTHFreePackets(Packet **p, int numpkts)
+{
+ if (p == NULL)
+ return;
+
+ int i = 0;
+ for (; i < numpkts; i++) {
+ UTHFreePacket(p[i]);
+ }
+}
+
+/**
+ * \brief UTHFreePacket: function to release the allocated data
+ * from UTHBuildPacket and the packet itself
+ *
+ * \param p pointer to the Packet
+ */
+void UTHFreePacket(Packet *p)
+{
+ if (p == NULL)
+ return;
+#if 0 // VJ we now use one buffer
+ switch (p->proto) {
+ case IPPROTO_UDP:
+ if (p->udph != NULL)
+ SCFree(p->udph);
+ if (p->ip4h != NULL)
+ SCFree(p->ip4h);
+ break;
+ case IPPROTO_TCP:
+ if (p->tcph != NULL)
+ SCFree(p->tcph);
+ if (p->ip4h != NULL)
+ SCFree(p->ip4h);
+ break;
+ case IPPROTO_ICMP:
+ if (p->ip4h != NULL)
+ SCFree(p->ip4h);
+ break;
+ /* TODO: Add more protocols */
+ }
+#endif
+ SCFree(p);
+}
+
+Flow *UTHBuildFlow(int family, char *src, char *dst, Port sp, Port dp)
+{
+ struct in_addr in;
+
+ Flow *f = SCMalloc(sizeof(Flow));
+ if (unlikely(f == NULL)) {
+ printf("FlowAlloc failed\n");
+ ;
+ return NULL;
+ }
+ memset(f, 0x00, sizeof(Flow));
+
+ FLOW_INITIALIZE(f);
+
+ if (family == AF_INET) {
+ f->flags |= FLOW_IPV4;
+ } else if (family == AF_INET6) {
+ f->flags |= FLOW_IPV6;
+ }
+
+ if (src != NULL) {
+ if (family == AF_INET) {
+ if (inet_pton(AF_INET, src, &in) != 1) {
+ printf("invalid address %s\n", src);
+ SCFree(f);
+ return NULL;
+ }
+ f->src.addr_data32[0] = in.s_addr;
+ } else {
+ BUG_ON(1);
+ }
+ }
+ if (dst != NULL) {
+ if (family == AF_INET) {
+ if (inet_pton(AF_INET, dst, &in) != 1) {
+ printf("invalid address %s\n", dst);
+ SCFree(f);
+ return NULL;
+ }
+ f->dst.addr_data32[0] = in.s_addr;
+ } else {
+ BUG_ON(1);
+ }
+ }
+
+ f->sp = sp;
+ f->dp = dp;
+
+ return f;
+}
+
+void UTHFreeFlow(Flow *flow)
+{
+ if (flow != NULL) {
+ FlowFree(flow);
+ }
+}
+
+/**
+ * \brief UTHGenericTest: function that perfom a generic check taking care of
+ * as maximum common unittest elements as possible.
+ * It will create a detection engine, append an array
+ * of signatures an check the spected results for each
+ * of them, it check matches for an array of packets
+ *
+ * \param pkt pointer to the array of packets
+ * \param numpkts number of packets to match
+ * \param sigs array of char* pointing to signatures to load
+ * \param numsigs number of signatures to load and check
+ * \param results pointer to arrays of numbers, each of them foreach packet
+ * to check if sids matches that packet as expected with
+ * that number of times or not. The size of results should be
+ * numpkts * numsigs * sizeof(uint16_t *)
+ *
+ * Example:
+ * result[1][3] would mean the number of times the pkt[1]
+ * match the sid[3]
+ *
+ * \retval int 1 if the match of all the sids is the specified has the
+ * specified results; 0 if not
+ */
+int UTHGenericTest(Packet **pkt, int numpkts, char *sigs[], uint32_t sids[], uint32_t *results, int numsigs)
+{
+
+ int result = 0;
+ if (pkt == NULL || sigs == NULL || numpkts == 0
+ || sids == NULL || results == NULL || numsigs == 0) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Arguments invalid, that the pointer/arrays are not NULL, and the number of signatures and packets is > 0");
+ goto end;
+ }
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+ de_ctx->flags |= DE_QUIET;
+
+ if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
+ goto cleanup;
+
+ result = UTHMatchPacketsWithResults(de_ctx, pkt, numpkts, sids, results, numsigs);
+
+cleanup:
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+end:
+ return result;
+}
+
+/**
+ * \brief UTHCheckPacketMatches: function to check if a packet match some sids
+ *
+ *
+ * \param p pointer to the Packet
+ * \param sigs array of char* pointing to signatures to load
+ * \param numsigs number of signatures to load from the array
+ * \param results pointer to an array of numbers to check if sids matches
+ * that number of times or not.
+ *
+ * \retval int 1 if the match of all the sids is the specified has the
+ * specified results; 0 if not
+ */
+int UTHCheckPacketMatchResults(Packet *p, uint32_t sids[],
+ uint32_t results[], int numsids)
+{
+ if (p == NULL || sids == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Arguments invalid, check if the "
+ "packet is NULL, and if the array contain sids is set");
+ return 0;
+ }
+
+ int i = 0;
+ int res = 1;
+ for (; i < numsids; i++) {
+ uint16_t r = PacketAlertCheck(p, sids[i]);
+ if (r != results[i]) {
+ SCLogInfo("Sid %"PRIu32" matched %"PRIu16" times, and not %"PRIu16
+ " as expected", sids[i], r, results[i]);
+ res = 0;
+ } else {
+ SCLogInfo("Sid %"PRIu32" matched %"PRIu16" times, as expected", sids[i], r);
+ }
+ }
+ return res;
+}
+
+/**
+ * \brief UTHAppendSigs: Add sigs to the detection_engine checking for errors
+ *
+ * \param de_ctx pointer to the DetectEngineCtx used
+ * \param sigs array of char* pointing to signatures to load
+ * \param numsigs number of signatures to load from the array
+ * (size of the array)
+ *
+ * \retval int 0 if we have errors; 1 if all the signatures loaded succesfuly
+ */
+int UTHAppendSigs(DetectEngineCtx *de_ctx, char *sigs[], int numsigs)
+{
+ if (de_ctx == NULL || numsigs <= 0 || sigs == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Arguments invalid, check if sigs or de_ctx are NULL, and if the array contain sigs");
+ return 0;
+ }
+ //SCLogDebug("Adding %d signatures for the current unittest", numsigs);
+
+ Signature *s;
+ int i = 0;
+
+ for ( ; i < numsigs; i++) {
+ if (sigs[i] == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Check the signature"
+ " at position %d", i);
+ return 0;
+ }
+ s = DetectEngineAppendSig(de_ctx, sigs[i]);
+ if (s == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Check the signature at"
+ " position %d (%s)", i, sigs[i]);
+ return 0;
+ }
+ }
+ //SCLogDebug("Added %d signatures to the de_ctx of the unittest", i);
+ return 1;
+}
+
+/**
+ * \test UTHMatchPacketsWithResults Match a packet or a array of packets against sigs
+ * of a de_ctx, checking that each signature match match X times for certain packets
+ *
+ * \param de_ctx pointer with the signatures loaded
+ * \param p pointer to the array of packets
+ * \param num_packets number of packets in the array
+ *
+ * \retval return 1 if all goes well
+ * \retval return 0 if something fail
+ */
+int UTHMatchPacketsWithResults(DetectEngineCtx *de_ctx, Packet **p, int num_packets, uint32_t sids[], uint32_t *results, int numsigs)
+{
+ int result = 0;
+
+ if (de_ctx == NULL || p == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "packet or de_ctx was null");
+ result = 0;
+ goto end;
+ }
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ //de_ctx->flags |= DE_QUIET;
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++) {
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+ if (UTHCheckPacketMatchResults(p[i], sids, &results[(i * numsigs)], numsigs) == 0)
+ goto cleanup;
+ }
+
+ /* so far, so good ;) */
+ result = 1;
+
+cleanup:
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+end:
+ return result;
+}
+
+/**
+ * \test UTHMatchPackets Match a packet or a array of packets against sigs
+ * of a de_ctx, but note that the return value doesn't mean that we have a
+ * match, we have to check it later with PacketAlertCheck()
+ *
+ * \param de_ctx pointer with the signatures loaded
+ * \param p pointer to the array of packets
+ * \param num_packets number of packets in the array
+ *
+ * \retval return 1 if all goes well
+ * \retval return 0 if something fail
+ */
+int UTHMatchPackets(DetectEngineCtx *de_ctx, Packet **p, int num_packets)
+{
+ int result = 1;
+
+ if (de_ctx == NULL || p == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "packet or de_ctx was null");
+ result = 0;
+ goto end;
+ }
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ //de_ctx->flags |= DE_QUIET;
+
+ SCSigRegisterSignatureOrderingFuncs(de_ctx);
+ SCSigOrderSignatures(de_ctx);
+ SCSigSignatureOrderingModuleCleanup(de_ctx);
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ int i = 0;
+ for (; i < num_packets; i++)
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
+
+ /* Here we don't check if the packet matched or not, because
+ * the de_ctx can have multiple signatures, and some of them may match
+ * and others may not. That check will be outside
+ */
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+end:
+ if (de_ctx != NULL) SigGroupCleanup(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Test if a packet match a signature given as string and a mpm_type
+ * Hint: Useful for unittests with only one packet and one signature
+ *
+ * \param sig pointer to the string signature to test
+ * \param sid sid number of the signature
+ *
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+int UTHPacketMatchSigMpm(Packet *p, char *sig, uint16_t mpm_type)
+{
+ SCEnter();
+
+ int result = 0;
+
+ DecodeThreadVars dtv;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ printf("de_ctx == NULL: ");
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->mpm_matcher = mpm_type;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ printf("signature == NULL: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
+ printf("signature didn't alert: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ SCReturnInt(result);
+}
+
+/**
+ * \test Test if a packet match a signature given as string
+ * Hint: Useful for unittests with only one packet and one signature
+ *
+ * \param sig pointer to the string signature to test
+ * \param sid sid number of the signature
+ *
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+int UTHPacketMatchSig(Packet *p, char *sig)
+{
+ int result = 1;
+
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ result=0;
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
+ result = 0;
+ goto end;
+ }
+
+end:
+ if (de_ctx) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ }
+
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+uint32_t UTHBuildPacketOfFlows(uint32_t start, uint32_t end, uint8_t dir)
+{
+ uint32_t i = start;
+ uint8_t payload[] = "Payload";
+ for (; i < end; i++) {
+ Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+ if (dir == 0) {
+ p->src.addr_data32[0] = i;
+ p->dst.addr_data32[0] = i + 1;
+ } else {
+ p->src.addr_data32[0] = i + 1;
+ p->dst.addr_data32[0] = i;
+ }
+ FlowHandlePacket(NULL, NULL, p);
+ if (p->flow != NULL)
+ SC_ATOMIC_RESET(p->flow->use_cnt);
+
+ /* Now the queues shoul be updated */
+ UTHFreePacket(p);
+ }
+
+ return i;
+}
+
+/*
+ * unittests for the unittest helpers
+ */
+
+/**
+ * \brief CheckUTHTestPacket wrapper to check packets for unittests
+ */
+int CheckUTHTestPacket(Packet *p, uint8_t ipproto)
+{
+ uint16_t sport = 41424;
+ uint16_t dport = 80;
+ uint8_t payload[] = "Payload";
+
+ uint8_t len = sizeof(payload);
+
+ if (p == NULL)
+ return 0;
+
+ if (p->payload_len != len)
+ return 0;
+
+ if (strncmp((char *)payload, (char *)p->payload, len) != 0)
+ return 0;
+
+ if (p->src.family != AF_INET)
+ return 0;
+ if (p->dst.family != AF_INET)
+ return 0;
+ if (p->proto != ipproto)
+ return 0;
+
+ switch(ipproto) {
+ case IPPROTO_UDP:
+ if (p->udph == NULL)
+ return 0;
+ if (p->udph->uh_sport != sport)
+ return 0;
+ if (p->udph->uh_dport != dport)
+ return 0;
+ break;
+ case IPPROTO_TCP:
+ if (p->tcph == NULL)
+ return 0;
+ if (ntohs(p->tcph->th_sport) != sport)
+ return 0;
+ if (ntohs(p->tcph->th_dport) != dport)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+/**
+ * \brief UTHBuildPacketRealTest01 wrapper to check packets for unittests
+ */
+int UTHBuildPacketRealTest01(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1", 41424, 80);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketRealTest02 wrapper to check packets for unittests
+ */
+int UTHBuildPacketRealTest02(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1", 41424, 80);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
+ UTHFreePacket(p);
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketTest01 wrapper to check packets for unittests
+ */
+int UTHBuildPacketTest01(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketTest02 wrapper to check packets for unittests
+ */
+int UTHBuildPacketTest02(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_UDP);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketOfFlowsTest01 wrapper to check packets for unittests
+ */
+int UTHBuildPacketOfFlowsTest01(void)
+{
+ int result = 0;
+
+ FlowInitConfig(FLOW_QUIET);
+ uint32_t flow_spare_q_len = flow_spare_q.len;
+
+ UTHBuildPacketOfFlows(0, 100, 0);
+
+ if (flow_spare_q.len != flow_spare_q_len - 100)
+ result = 0;
+ else
+ result = 1;
+ FlowShutdown();
+
+ return result;
+}
+
+
+/**
+ * \brief UTHBuildPacketSrcDstTest01 wrapper to check packets for unittests
+ */
+int UTHBuildPacketSrcDstTest01(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_TCP,
+ "192.168.1.5", "192.168.1.1");
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketSrcDstTest02 wrapper to check packets for unittests
+ */
+int UTHBuildPacketSrcDstTest02(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_UDP,
+ "192.168.1.5", "192.168.1.1");
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketSrcDstPortsTest01 wrapper to check packets for unittests
+ */
+int UTHBuildPacketSrcDstPortsTest01(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_TCP,
+ 41424, 80);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+/**
+ * \brief UTHBuildPacketSrcDstPortsTest02 wrapper to check packets for unittests
+ */
+int UTHBuildPacketSrcDstPortsTest02(void)
+{
+ uint8_t payload[] = "Payload";
+
+ Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_UDP,
+ 41424, 80);
+
+ int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
+ UTHFreePacket(p);
+
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+void UTHRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("UTHBuildPacketRealTest01", UTHBuildPacketRealTest01, 1);
+ UtRegisterTest("UTHBuildPacketRealTest02", UTHBuildPacketRealTest02, 1);
+ UtRegisterTest("UTHBuildPacketTest01", UTHBuildPacketTest01, 1);
+ UtRegisterTest("UTHBuildPacketTest02", UTHBuildPacketTest02, 1);
+ UtRegisterTest("UTHBuildPacketSrcDstTest01", UTHBuildPacketSrcDstTest01, 1);
+ UtRegisterTest("UTHBuildPacketSrcDstTest02", UTHBuildPacketSrcDstTest02, 1);
+ UtRegisterTest("UTHBuildPacketSrcDstPortsTest01", UTHBuildPacketSrcDstPortsTest01, 1);
+ UtRegisterTest("UTHBuildPacketSrcDstPortsTest02", UTHBuildPacketSrcDstPortsTest02, 1);
+ UtRegisterTest("UTHBuildPacketOfFlowsTest01", UTHBuildPacketOfFlowsTest01, 1);
+
+#endif /* UNITTESTS */
+}
+
diff --git a/framework/src/suricata/src/util-unittest-helper.h b/framework/src/suricata/src/util-unittest-helper.h
new file mode 100644
index 00000000..3b57a9e4
--- /dev/null
+++ b/framework/src/suricata/src/util-unittest-helper.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
+ */
+
+#ifndef __UTIL_UNITTEST_HELPER__
+#define __UTIL_UNITTEST_HELPER__
+
+#ifdef UNITTESTS
+uint32_t UTHSetIPv4Address(char *);
+
+Packet *UTHBuildPacketReal(uint8_t *, uint16_t, uint8_t ipproto, char *, char *, uint16_t, uint16_t);
+Packet *UTHBuildPacket(uint8_t *, uint16_t, uint8_t ipproto);
+Packet *UTHBuildPacketSrcDst(uint8_t *, uint16_t, uint8_t ipproto, char *, char *);
+Packet *UTHBuildPacketSrcDstPorts(uint8_t *, uint16_t, uint8_t ipproto, uint16_t, uint16_t);
+
+Packet *UTHBuildPacketIPV6SrcDst(uint8_t *, uint16_t, uint8_t ipproto, char *, char *);
+
+int UTHPacketMatchSigMpm(Packet *, char *, uint16_t);
+Packet **UTHBuildPacketArrayFromEth(uint8_t **, int *, int);
+Packet *UTHBuildPacketFromEth(uint8_t *, uint16_t);
+
+void UTHFreePacket(Packet *);
+void UTHFreePackets(Packet **, int);
+
+Flow *UTHBuildFlow(int family, char *src, char *dst, Port sp, Port dp);
+void UTHFreeFlow(Flow *flow);
+
+int UTHAppendSigs(DetectEngineCtx *, char **, int);
+int UTHMatchPackets(DetectEngineCtx *, Packet **, int);
+int UTHPacketMatchSig(Packet *p, char *);
+int UTHCheckPacketMatch(Packet *, uint32_t *, uint32_t *, int);
+
+int UTHCheckPacketMatchResults(Packet *, uint32_t *, uint32_t *, int);
+int UTHMatchPacketsWithResults(DetectEngineCtx *, Packet **, int, uint32_t *, uint32_t *, int);
+int UTHGenericTest(Packet **, int, char **, uint32_t *, uint32_t *, int);
+
+uint32_t UTHBuildPacketOfFlows(uint32_t, uint32_t, uint8_t);
+Packet *UTHBuildPacketIPV6Real(uint8_t *, uint16_t , uint8_t ipproto, char *, char *,
+ uint16_t , uint16_t );
+#endif
+
+void UTHRegisterTests(void);
+
+#endif /* __UTIL_UNITTEST_HELPER__ */
diff --git a/framework/src/suricata/src/util-unittest.c b/framework/src/suricata/src/util-unittest.c
new file mode 100644
index 00000000..c3f0fdb2
--- /dev/null
+++ b/framework/src/suricata/src/util-unittest.c
@@ -0,0 +1,326 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Unit test framework
+ */
+
+#include "suricata-common.h"
+#include "runmodes.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "conf.h"
+
+#ifdef UNITTESTS
+
+static pcre *parse_regex;
+static pcre_extra *parse_regex_study;
+
+static UtTest *ut_list;
+
+/**
+ * \brief Allocate UtTest list member
+ *
+ * \retval ut Pointer to UtTest
+ */
+
+static UtTest *UtAllocTest(void)
+{
+ UtTest *ut = SCMalloc(sizeof(UtTest));
+ if (unlikely(ut == NULL))
+ return NULL;
+
+ memset(ut, 0, sizeof(UtTest));
+
+ return ut;
+}
+
+/**
+ * \brief Append test in UtTest list
+ *
+ * \param list Pointer to the start of the IP packet
+ * \param test Pointer to unit test
+ *
+ * \retval 0 Function always returns zero
+ */
+
+static int UtAppendTest(UtTest **list, UtTest *test)
+{
+ if (*list == NULL) {
+ *list = test;
+ } else {
+ UtTest *tmp = *list;
+
+ while (tmp->next != NULL) {
+ tmp = tmp->next;
+ }
+ tmp->next = test;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Register unit test
+ *
+ * \param name Unit test name
+ * \param TestFn Unit test function
+ * \param evalue Unit test function return value
+ *
+ */
+
+void UtRegisterTest(char *name, int(*TestFn)(void), int evalue)
+{
+ UtTest *ut = UtAllocTest();
+ if (ut == NULL)
+ return;
+
+ ut->name = name;
+ ut->TestFn = TestFn;
+ ut->evalue = evalue;
+ ut->next = NULL;
+
+ /* append */
+ UtAppendTest(&ut_list, ut);
+}
+
+/**
+ * \brief Compile a regex to run a specific unit test
+ *
+ * \param regex_arg The regular expression
+ *
+ * \retval 1 Regex compiled
+ * \retval -1 Regex error
+ */
+
+int UtRegex (char *regex_arg)
+{
+ const char *eb;
+ int eo;
+ int opts = PCRE_CASELESS;;
+
+ if(regex_arg == NULL)
+ return -1;
+
+ parse_regex = pcre_compile(regex_arg, opts, &eb, &eo, NULL);
+ if(parse_regex == NULL)
+ {
+ printf("pcre compile of \"%s\" failed at offset %" PRId32 ": %s\n", regex_arg, eo, eb);
+ goto error;
+ }
+
+ parse_regex_study = pcre_study(parse_regex, 0, &eb);
+ if(eb != NULL)
+ {
+ printf("pcre study failed: %s\n", eb);
+ goto error;
+ }
+
+ return 1;
+
+error:
+ return -1;
+}
+
+#define MAX_SUBSTRINGS 30
+
+/** \brief List all registered unit tests.
+ *
+ * \param regex_arg Regular expression to limit listed tests.
+ */
+void UtListTests(char *regex_arg)
+{
+ UtTest *ut;
+ int ret = 0, rcomp = 0;
+ int ov[MAX_SUBSTRINGS];
+
+ rcomp = UtRegex(regex_arg);
+
+ for (ut = ut_list; ut != NULL; ut = ut->next) {
+ if (rcomp == 1) {
+ ret = pcre_exec(parse_regex, parse_regex_study, ut->name,
+ strlen(ut->name), 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret >= 1) {
+ printf("%s\n", ut->name);
+ }
+ }
+ else {
+ printf("%s\n", ut->name);
+ }
+ }
+}
+
+/** \brief Run all registered unittests.
+ *
+ * \param regex_arg The regular expression
+ *
+ * \retval 0 all successful
+ * \retval result number of tests that failed
+ */
+
+uint32_t UtRunTests(char *regex_arg)
+{
+ UtTest *ut;
+ uint32_t good = 0, bad = 0, matchcnt = 0;
+ int ret = 0, rcomp = 0;
+ int ov[MAX_SUBSTRINGS];
+ int failure_fatal;
+
+ if (ConfGetBool("unittests.failure-fatal", &failure_fatal) != 1) {
+ SCLogDebug("ConfGetBool could not load the value.");
+ failure_fatal = 0;
+ }
+
+ rcomp = UtRegex(regex_arg);
+
+ if(rcomp == 1){
+ for (ut = ut_list; ut != NULL; ut = ut->next) {
+ ret = pcre_exec(parse_regex, parse_regex_study, ut->name, strlen(ut->name), 0, 0, ov, MAX_SUBSTRINGS);
+ if( ret >= 1 ) {
+ printf("Test %-60.60s : ", ut->name);
+ matchcnt++;
+ fflush(stdout); /* flush so in case of a segv we see the testname */
+
+ /* reset the time */
+ TimeModeSetOffline();
+ TimeSetToCurrentTime();
+
+ ret = ut->TestFn();
+ printf("%s\n", (ret == ut->evalue) ? "pass" : "FAILED");
+ if (ret != ut->evalue) {
+ if (failure_fatal == 1) {
+ fprintf(stderr, "ERROR: unittest failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ bad++;
+ } else {
+ good++;
+ }
+ }
+ }
+ if(matchcnt > 0){
+ printf("==== TEST RESULTS ====\n");
+ printf("PASSED: %" PRIu32 "\n", good);
+ printf("FAILED: %" PRIu32 "\n", bad);
+ printf("======================\n");
+ } else {
+ SCLogInfo("UtRunTests: regex provided regex_arg: %s did not match any tests",regex_arg);
+ }
+ } else {
+ SCLogInfo("UtRunTests: pcre compilation failed");
+ }
+ return bad;
+}
+/**
+ * \brief Initialize unit test list
+ */
+
+void UtInitialize(void)
+{
+ ut_list = NULL;
+}
+
+/**
+ * \brief Cleanup unit test list
+ */
+
+void UtCleanup(void)
+{
+
+ UtTest *tmp = ut_list, *otmp;
+
+ while (tmp != NULL) {
+ otmp = tmp->next;
+ SCFree(tmp);
+ tmp = otmp;
+ }
+
+ ut_list = NULL;
+}
+
+void UtRunModeRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_UNITTEST,
+ "unittest",
+ "Unittest mode",
+ NULL);
+
+ return;
+}
+
+/*
+ * unittests for the unittests code
+ */
+
+/** \brief True test
+ *
+ * \retval 1 True
+ * \retval 0 False
+ */
+
+int UtSelftestTrue(void)
+{
+ if (1)return 1;
+ else return 0;
+}
+
+/** \brief False test
+ *
+ * \retval 1 False
+ * \retval 0 True
+ */
+
+int UtSelftestFalse(void)
+{
+ if (0)return 1;
+ else return 0;
+}
+#endif /* UNITTESTS */
+
+/** \brief Run self tests
+ *
+ * \param regex_arg The regular expression
+ *
+ * \retval 0 all successful
+ */
+
+int UtRunSelftest (char *regex_arg)
+{
+#ifdef UNITTESTS
+ printf("* Running Unittesting subsystem selftests...\n");
+
+ UtInitialize();
+
+ UtRegisterTest("true", UtSelftestTrue, 1);
+ UtRegisterTest("false", UtSelftestFalse, 0);
+
+ int ret = UtRunTests(regex_arg);
+ if (ret == 0)
+ printf("* Done running Unittesting subsystem selftests...\n");
+ else
+ printf("* ERROR running Unittesting subsystem selftests failed...\n");
+
+ UtCleanup();
+#endif /* UNITTESTS */
+ return 0;
+}
diff --git a/framework/src/suricata/src/util-unittest.h b/framework/src/suricata/src/util-unittest.h
new file mode 100644
index 00000000..ebad9470
--- /dev/null
+++ b/framework/src/suricata/src/util-unittest.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Breno Silva <breno.silva@gmail.com>
+ *
+ * Unit test framework
+ */
+
+#ifndef __UTIL_UNITTEST_H__
+#define __UTIL_UNITTEST_H__
+
+#ifdef UNITTESTS
+
+typedef struct UtTest_ {
+
+ char *name;
+ int(*TestFn)(void);
+ int evalue;
+
+ struct UtTest_ *next;
+
+} UtTest;
+
+
+void UtRegisterTest(char *name, int(*TestFn)(void), int evalue);
+uint32_t UtRunTests(char *regex_arg);
+void UtInitialize(void);
+void UtCleanup(void);
+int UtRunSelftest (char *regex_arg);
+void UtListTests(char *regex_arg);
+void UtRunModeRegister(void);
+
+#endif
+
+#endif /* __UTIL_UNITTEST_H__ */
+
diff --git a/framework/src/suricata/src/util-validate.h b/framework/src/suricata/src/util-validate.h
new file mode 100644
index 00000000..b5c3da26
--- /dev/null
+++ b/framework/src/suricata/src/util-validate.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Functions & Macro's for validation of data structures. This is used for
+ * code correctness.
+ *
+ * These will abort() the program if they fail, so they should _only_ be
+ * used for testing.
+ */
+
+
+#ifndef __UTIL_VALIDATE_H__
+#define __UTIL_VALIDATE_H__
+
+#ifdef DEBUG_VALIDATION
+
+/** \brief test if a flow is locked.
+ *
+ * If trylock returns 0 it got a lock. Which means
+ * the flow was previously unlocked.
+ */
+#define DEBUG_ASSERT_FLOW_LOCKED(f) do { \
+ if ((f) != NULL) { \
+ int r = SCMutexTrylock(&(f)->m); \
+ if (r == 0) { \
+ BUG_ON(1); \
+ } \
+ } \
+} while(0)
+
+/** \brief validate the integrity of the flow
+ *
+ * BUG_ON's on problems
+ */
+#define DEBUG_VALIDATE_FLOW(f) do { \
+ if ((f) != NULL) { \
+ SCMutexLock(&(f)->m); \
+ BUG_ON((f)->flags & FLOW_IPV4 && \
+ (f)->flags & FLOW_IPV6); \
+ if ((f)->proto == IPPROTO_TCP) { \
+ BUG_ON((f)->alstate != NULL && \
+ (f)->alparser == NULL); \
+ } \
+ SCMutexUnlock(&(f)->m); \
+ } \
+} while(0)
+
+/** \brief validate the integrity of the packet
+ *
+ * BUG_ON's on problems
+ */
+#define DEBUG_VALIDATE_PACKET(p) do { \
+ if ((p) != NULL) { \
+ if ((p)->flow != NULL) { \
+ DEBUG_VALIDATE_FLOW((p)->flow); \
+ } \
+ if (!((p)->flags & (PKT_IS_FRAGMENT|PKT_IS_INVALID))) { \
+ if ((p)->proto == IPPROTO_TCP) { \
+ BUG_ON((p)->tcph == NULL); \
+ } else if ((p)->proto == IPPROTO_UDP) { \
+ BUG_ON((p)->udph == NULL); \
+ } else if ((p)->proto == IPPROTO_ICMP) { \
+ BUG_ON((p)->icmpv4h == NULL); \
+ } else if ((p)->proto == IPPROTO_SCTP) { \
+ BUG_ON((p)->sctph == NULL); \
+ } else if ((p)->proto == IPPROTO_ICMPV6) { \
+ BUG_ON((p)->icmpv6h == NULL); \
+ } \
+ } \
+ if ((p)->payload_len > 0) { \
+ BUG_ON((p)->payload == NULL); \
+ } \
+ BUG_ON((p)->ip4h != NULL && (p)->ip6h != NULL); \
+ BUG_ON((p)->flowflags != 0 && (p)->flow == NULL); \
+ BUG_ON((p)->flowflags & FLOW_PKT_TOSERVER &&\
+ (p)->flowflags & FLOW_PKT_TOCLIENT); \
+ } \
+} while(0)
+
+#else /* DEBUG_VALIDATE */
+
+#define DEBUG_ASSERT_FLOW_LOCKED(f)
+#define DEBUG_VALIDATE_FLOW(f)
+#define DEBUG_VALIDATE_PACKET(p)
+
+#endif /* DEBUG_VALIDATE */
+
+#endif /* __UTIL_VALIDATE_H__ */
+
diff --git a/framework/src/suricata/src/util-var-name.c b/framework/src/suricata/src/util-var-name.c
new file mode 100644
index 00000000..91322189
--- /dev/null
+++ b/framework/src/suricata/src/util-var-name.c
@@ -0,0 +1,204 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Generic variable name utility functions
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+#include "util-hashlist.h"
+
+/** \brief Name2idx mapping structure for flowbits, flowvars and pktvars. */
+typedef struct VariableName_ {
+ char *name;
+ uint8_t type; /* flowbit, pktvar, etc */
+ uint8_t flags;
+ uint16_t idx;
+} VariableName;
+
+static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen)
+{
+ VariableName *fn = (VariableName *)buf;
+ uint32_t hash = strlen(fn->name) + fn->type;
+ uint16_t u;
+
+ for (u = 0; u < buflen; u++) {
+ hash += fn->name[u];
+ }
+
+ return hash;
+}
+
+static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
+{
+ VariableName *fn1 = (VariableName *)buf1;
+ VariableName *fn2 = (VariableName *)buf2;
+
+ if (fn1->type != fn2->type)
+ return 0;
+
+ if (strcmp(fn1->name,fn2->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+static uint32_t VariableIdxHash(HashListTable *ht, void *buf, uint16_t buflen)
+{
+ VariableName *fn = (VariableName *)buf;
+ uint32_t hash = fn->idx + fn->type;
+ return hash;
+}
+
+static char VariableIdxCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
+{
+ VariableName *fn1 = (VariableName *)buf1;
+ VariableName *fn2 = (VariableName *)buf2;
+
+ if (fn1->type != fn2->type)
+ return 0;
+
+ if (fn1->idx == fn2->idx)
+ return 1;
+
+ return 0;
+}
+
+static void VariableNameFree(void *data)
+{
+ VariableName *fn = (VariableName *)data;
+
+ if (fn == NULL)
+ return;
+
+ if (fn->name != NULL) {
+ SCFree(fn->name);
+ fn->name = NULL;
+ }
+
+ SCFree(fn);
+}
+
+/** \brief Initialize the Name idx hash.
+ * \param de_ctx Ptr to the detection engine ctx.
+ * \retval -1 in case of error
+ * \retval 0 in case of success
+ */
+int VariableNameInitHash(DetectEngineCtx *de_ctx)
+{
+ de_ctx->variable_names = HashListTableInit(4096, VariableNameHash, VariableNameCompare, VariableNameFree);
+ if (de_ctx->variable_names == NULL)
+ return -1;
+
+ de_ctx->variable_idxs = HashListTableInit(4096, VariableIdxHash, VariableIdxCompare, NULL);
+ if (de_ctx->variable_idxs == NULL)
+ return -1;
+
+ de_ctx->variable_names_idx = 0;
+ return 0;
+}
+
+void VariableNameFreeHash(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->variable_names != NULL) {
+ HashListTableFree(de_ctx->variable_names);
+ HashListTableFree(de_ctx->variable_idxs);
+ de_ctx->variable_names = NULL;
+ de_ctx->variable_idxs = NULL;
+ }
+
+ return;
+}
+
+/** \brief Get a name idx for a name. If the name is already used reuse the idx.
+ * \param name nul terminated string with the name
+ * \param type variable type
+ * \retval 0 in case of error
+ * \retval idx the idx or 0
+ */
+uint16_t VariableNameGetIdx(DetectEngineCtx *de_ctx, char *name, enum VarTypes type)
+{
+ uint16_t idx = 0;
+
+ VariableName *fn = SCMalloc(sizeof(VariableName));
+ if (unlikely(fn == NULL))
+ goto error;
+
+ memset(fn, 0, sizeof(VariableName));
+
+ fn->type = type;
+ fn->name = SCStrdup(name);
+ if (fn->name == NULL)
+ goto error;
+
+ VariableName *lookup_fn = (VariableName *)HashListTableLookup(de_ctx->variable_names, (void *)fn, 0);
+ if (lookup_fn == NULL) {
+ de_ctx->variable_names_idx++;
+
+ idx = fn->idx = de_ctx->variable_names_idx;
+ HashListTableAdd(de_ctx->variable_names, (void *)fn, 0);
+ HashListTableAdd(de_ctx->variable_idxs, (void *)fn, 0);
+ } else {
+ idx = lookup_fn->idx;
+ VariableNameFree(fn);
+ }
+
+ return idx;
+error:
+ VariableNameFree(fn);
+ return 0;
+}
+
+/** \brief Get a name from the idx.
+ * \param idx index of the variable whose name is to be fetched
+ * \param type variable type
+ * \retval NULL in case of error
+ * \retval name of the variable if successful.
+ */
+char *VariableIdxGetName(DetectEngineCtx *de_ctx, uint16_t idx, enum VarTypes type)
+{
+ VariableName *fn = SCMalloc(sizeof(VariableName));
+ if (unlikely(fn == NULL))
+ goto error;
+
+ char *name = NULL;
+ memset(fn, 0, sizeof(VariableName));
+
+ fn->type = type;
+ fn->idx = idx;
+
+ VariableName *lookup_fn = (VariableName *)HashListTableLookup(de_ctx->variable_idxs, (void *)fn, 0);
+ if (lookup_fn != NULL) {
+ name = SCStrdup(lookup_fn->name);
+ if (unlikely(name == NULL))
+ goto error;
+
+ VariableNameFree(fn);
+ } else {
+ goto error;
+ }
+
+ return name;
+error:
+ VariableNameFree(fn);
+ return NULL;
+}
diff --git a/framework/src/suricata/src/util-var-name.h b/framework/src/suricata/src/util-var-name.h
new file mode 100644
index 00000000..2be14268
--- /dev/null
+++ b/framework/src/suricata/src/util-var-name.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_VAR_NAME_H__
+#define __UTIL_VAR_NAME_H__
+
+int VariableNameInitHash(DetectEngineCtx *);
+void VariableNameFreeHash(DetectEngineCtx *);
+
+uint16_t VariableNameGetIdx(DetectEngineCtx *, char *, enum VarTypes);
+char * VariableIdxGetName(DetectEngineCtx *, uint16_t , enum VarTypes);
+
+#endif
+
diff --git a/framework/src/suricata/src/util-var.c b/framework/src/suricata/src/util-var.c
new file mode 100644
index 00000000..e4e9689b
--- /dev/null
+++ b/framework/src/suricata/src/util-var.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Generic variable utility functions
+ */
+
+#include "suricata-common.h"
+#include "detect.h"
+
+#include "util-var.h"
+
+#include "flow-var.h"
+#include "flow-bit.h"
+#include "pkt-var.h"
+#include "host-bit.h"
+#include "ippair-bit.h"
+
+#include "util-debug.h"
+
+static void XBitFree(XBit *fb)
+{
+ if (fb == NULL)
+ return;
+
+ SCFree(fb);
+}
+
+void GenericVarFree(GenericVar *gv)
+{
+ if (gv == NULL)
+ return;
+
+ SCLogDebug("gv %p, gv->type %" PRIu32 "", gv, gv->type);
+ GenericVar *next_gv = gv->next;
+
+ switch (gv->type) {
+ case DETECT_FLOWBITS:
+ {
+ FlowBit *fb = (FlowBit *)gv;
+ //printf("GenericVarFree: fb %p, removing\n", fb);
+ FlowBitFree(fb);
+ break;
+ }
+ case DETECT_XBITS:
+ {
+ XBit *fb = (XBit *)gv;
+ //printf("GenericVarFree: fb %p, removing\n", fb);
+ XBitFree(fb);
+ break;
+ }
+ case DETECT_FLOWVAR:
+ {
+ FlowVar *fv = (FlowVar *)gv;
+ FlowVarFree(fv);
+ break;
+ }
+ case DETECT_PKTVAR:
+ {
+ PktVar *pv = (PktVar *)gv;
+ PktVarFree(pv);
+ break;
+ }
+ default:
+ {
+ printf("ERROR: GenericVarFree unknown type %" PRIu32 "\n", gv->type);
+ break;
+ }
+ }
+
+ GenericVarFree(next_gv);
+}
+
+void GenericVarAppend(GenericVar **list, GenericVar *gv)
+{
+ gv->next = NULL;
+
+ if (*list == NULL) {
+ *list = gv;
+ } else {
+ GenericVar *tgv = *list;
+ while(tgv) {
+ if (tgv->next == NULL) {
+ tgv->next = gv;
+ return;
+ }
+
+ tgv = tgv->next;
+ }
+ }
+}
+
+void GenericVarRemove(GenericVar **list, GenericVar *gv)
+{
+ if (*list == NULL)
+ return;
+
+ GenericVar *listgv = *list, *prevgv = NULL;
+ while (listgv != NULL) {
+ if (listgv == gv) {
+ if (prevgv == NULL)
+ *list = gv->next;
+ else
+ prevgv->next = gv->next;
+
+ return;
+ }
+
+ prevgv = listgv;
+ listgv = listgv->next;
+ }
+}
diff --git a/framework/src/suricata/src/util-var.h b/framework/src/suricata/src/util-var.h
new file mode 100644
index 00000000..0cd8c352
--- /dev/null
+++ b/framework/src/suricata/src/util-var.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_VAR_H__
+#define __UTIL_VAR_H__
+
+enum VarTypes {
+ VAR_TYPE_NOT_SET,
+
+ VAR_TYPE_PKT_BIT,
+ VAR_TYPE_PKT_INT,
+ VAR_TYPE_PKT_VAR,
+
+ VAR_TYPE_FLOW_BIT,
+ VAR_TYPE_FLOW_INT,
+ VAR_TYPE_FLOW_VAR,
+
+ VAR_TYPE_HOST_BIT,
+ VAR_TYPE_HOST_INT,
+ VAR_TYPE_HOST_VAR,
+
+ VAR_TYPE_IPPAIR_BIT,
+ VAR_TYPE_IPPAIR_INT,
+ VAR_TYPE_IPPAIR_VAR,
+};
+
+typedef struct GenericVar_ {
+ uint8_t type;
+ uint16_t idx;
+ struct GenericVar_ *next;
+} GenericVar;
+
+typedef struct XBit_ {
+ uint8_t type; /* type, DETECT_XBITS in this case */
+ uint16_t idx; /* name idx */
+ GenericVar *next;
+ uint32_t expire;
+} XBit;
+
+void GenericVarFree(GenericVar *);
+void GenericVarAppend(GenericVar **, GenericVar *);
+void GenericVarRemove(GenericVar **, GenericVar *);
+
+#endif /* __UTIL_VAR_H__ */
+
diff --git a/framework/src/suricata/src/util-vector.h b/framework/src/suricata/src/util-vector.h
new file mode 100644
index 00000000..c826964d
--- /dev/null
+++ b/framework/src/suricata/src/util-vector.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __UTIL_VECTOR_H__
+#define __UTIL_VECTOR_H__
+
+#if defined(__SSE3__)
+
+#include <pmmintrin.h>
+
+typedef struct Vector_ {
+ union {
+ __m128i v; /**< vector */
+ uint8_t c[16]; /**< character */
+ uint16_t w[8]; /**< word */
+ uint32_t dw[4]; /**< double word */
+ uint64_t qw[2]; /**< quad word */
+ };
+} Vector __attribute((aligned(16)));
+
+#endif /* defined(__SSE3__) */
+
+#endif /* __UTIL_VECTOR_H__ */
diff --git a/framework/src/suricata/src/win32-misc.c b/framework/src/suricata/src/win32-misc.c
new file mode 100644
index 00000000..80eb29cd
--- /dev/null
+++ b/framework/src/suricata/src/win32-misc.c
@@ -0,0 +1,108 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Jan Jezek <jjezek@kerio.com>
+ *
+ * Misc Windows utility functions
+ */
+
+#ifdef OS_WIN32
+
+#include "suricata-common.h"
+#include "win32-misc.h"
+#include "direct.h"
+
+void setenv(const char *name, const char *value, int overwrite)
+{
+ if (overwrite || NULL == getenv(name)) {
+ char *str = SCMalloc(strlen(name) + strlen(value) + 2);
+ if (unlikely(str == NULL))
+ return;
+ snprintf(str, strlen(name) + strlen(value) + 1, "%s=%s", name, value);
+ putenv(str);
+ SCFree(str);
+ }
+}
+
+void unsetenv(const char *name)
+{
+ char *str = SCMalloc(strlen(name) + 2);
+ if (unlikely(str == NULL))
+ return;
+ snprintf(str, strlen(name) + 1, "%s=", name);
+ putenv(str);
+ SCFree(str);
+}
+
+const char* inet_ntop(int af, const void *src, char *dst, uint32_t cnt)
+{
+ if (af == AF_INET)
+ {
+ struct sockaddr_in in;
+ memset(&in, 0, sizeof(in));
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr, src, sizeof(struct in_addr));
+ if (0 == getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST))
+ return dst;
+ }
+ else if (af == AF_INET6)
+ {
+ struct sockaddr_in6 in6;
+ memset(&in6, 0, sizeof(in6));
+ in6.sin6_family = AF_INET6;
+ memcpy(&in6.sin6_addr, src, sizeof(struct in_addr6));
+ if (0 == getnameinfo((struct sockaddr *)&in6, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST))
+ return dst;
+ }
+ return NULL;
+}
+
+int inet_pton(int af, const char *src, void *dst)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = af;
+
+ struct addrinfo* result = NULL;
+ if (0 != getaddrinfo(src, NULL, &hints, &result))
+ return -1;
+
+ if (result) {
+ if (result->ai_family == AF_INET) {
+ struct sockaddr_in* in = (struct sockaddr_in*)result->ai_addr;
+ memcpy(dst, &in->sin_addr, 4);
+ }
+ else if (result->ai_family == AF_INET6) {
+ struct sockaddr_in6* in6 = (struct sockaddr_in6*)result->ai_addr;
+ memcpy(dst, &in6->sin6_addr, 16);
+ }
+ else {
+ freeaddrinfo(result);
+ return -1;
+ }
+
+ freeaddrinfo(result);
+ return 1;
+ }
+
+ return -1;
+}
+
+#endif /* OS_WIN32 */
diff --git a/framework/src/suricata/src/win32-misc.h b/framework/src/suricata/src/win32-misc.h
new file mode 100644
index 00000000..7242f9c4
--- /dev/null
+++ b/framework/src/suricata/src/win32-misc.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Jan Jezek <jjezek@kerio.com>
+ */
+
+#ifndef __WIN32_MISC_H__
+#define __WIN32_MISC_H__
+
+#define index strchr
+#define rindex strrchr
+
+#define bzero(s, n) memset(s, 0, n)
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif /* O_NOFOLLOW */
+
+void setenv(const char *name, const char *value, int overwrite);
+void unsetenv(const char *name);
+
+const char* inet_ntop(int af, const void *src, char *dst, uint32_t cnt);
+int inet_pton(int af, const char *src, void *dst);
+
+#define geteuid() (0)
+
+#endif
diff --git a/framework/src/suricata/src/win32-service.c b/framework/src/suricata/src/win32-service.c
new file mode 100644
index 00000000..257447e5
--- /dev/null
+++ b/framework/src/suricata/src/win32-service.c
@@ -0,0 +1,394 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ondrej Slanina <oslanina@kerio.com>
+ *
+ * Windows service functions
+ */
+
+#ifdef OS_WIN32
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "win32-service.h"
+
+static SERVICE_STATUS_HANDLE service_status_handle = 0;
+
+static int service_argc = 0;
+
+static char **service_argv = NULL;
+
+static int service_initialized = 0;
+
+int main(int argc, char **argv);
+
+/**
+ * \brief Detect if running as service or console app
+ */
+int SCRunningAsService(void)
+{
+ HANDLE h = INVALID_HANDLE_VALUE;
+ if ((h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+ SCLogInfo("Running as service: yes");
+ return 1;
+ }
+ CloseHandle(h);
+ SCLogInfo("Running as service: no");
+ return 0;
+}
+
+/**
+ * \brief Detect if running as service or console app
+ */
+void SCAtExitHandler(void)
+{
+ SERVICE_STATUS status = {
+ SERVICE_WIN32,
+ SERVICE_STOPPED,
+ SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
+ NO_ERROR,
+ NO_ERROR,
+ 0,
+ 0
+ };
+
+ SCLogInfo("Exit handler called.");
+
+ /* mark service as stopped */
+ if (!SetServiceStatus(service_status_handle, &status)) {
+ SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
+ } else {
+ SCLogInfo("Service status set to: SERVICE_STOPPED");
+ }
+}
+
+/**
+ * \brief Service handler
+ */
+static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context)
+{
+ if (code == SERVICE_CONTROL_SHUTDOWN || code == SERVICE_CONTROL_STOP) {
+ SERVICE_STATUS status = {
+ SERVICE_WIN32,
+ SERVICE_STOP_PENDING,
+ SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
+ NO_ERROR,
+ NO_ERROR,
+ 0,
+ 0
+ };
+
+ SCLogInfo("Service control handler called with %s control code.",
+ ((code == SERVICE_CONTROL_SHUTDOWN) ? ("SERVICE_CONTROL_SHUTDOWN") : ("SERVICE_CONTROL_STOP")));
+
+ /* mark service as stop pending */
+ if (!SetServiceStatus(service_status_handle, &status)) {
+ SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
+ } else {
+ SCLogInfo("Service status set to: SERVICE_STOP_PENDING");
+ }
+
+ /* mark engine as stopping */
+ EngineStop();
+
+ return NO_ERROR;
+ }
+
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * \brief Service main function
+ */
+static void WINAPI SCServiceMain(uint32_t argc, char** argv)
+{
+ SERVICE_STATUS status = {
+ SERVICE_WIN32,
+ SERVICE_RUNNING,
+ SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
+ NO_ERROR,
+ NO_ERROR,
+ 0,
+ 0
+ };
+
+ if ((service_status_handle = RegisterServiceCtrlHandlerEx(PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) {
+ SCLogError(SC_ERR_SVC, "Can't register service control handler: %d", (int)GetLastError());
+ return;
+ }
+
+ /* register exit handler */
+ if (atexit(SCAtExitHandler)) {
+ SCLogWarning(SC_ERR_SVC, "Can't register exit handler: %d", (int)GetLastError());
+ }
+
+ /* mark service as running immediately */
+ if (!SetServiceStatus(service_status_handle, &status)) {
+ SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
+ } else {
+ SCLogInfo("Service status set to: SERVICE_RUNNING");
+ }
+
+ SCLogInfo("Entering main function...");
+
+ /* suricata initialization -> main loop -> uninitialization */
+ main(service_argc, service_argv);
+
+ SCLogInfo("Leaving main function.");
+
+ /* mark service as stopped */
+ status.dwCurrentState = SERVICE_STOPPED;
+
+ if (!SetServiceStatus(service_status_handle, &status)) {
+ SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
+ } else {
+ SCLogInfo("Service status set to: SERVICE_STOPPED");
+ }
+}
+
+/**
+ * \brief Init suricata service
+ *
+ * \param argc num of arguments
+ * \param argv passed arguments
+ */
+int SCServiceInit(int argc, char **argv)
+{
+ SERVICE_TABLE_ENTRY DispatchTable[] = {
+ {PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain},
+ {NULL, NULL}
+ };
+
+ /* continue with suricata initialization */
+ if (service_initialized) {
+ SCLogWarning(SC_ERR_SVC, "Service is already initialized.");
+ return 0;
+ }
+
+ /* save args */
+ service_argc = argc;
+ service_argv = argv;
+
+ service_initialized = 1;
+
+ SCLogInfo("Entering service control dispatcher...");
+
+ if (!StartServiceCtrlDispatcher(DispatchTable)) {
+ /* exit with failure */
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogInfo("Leaving service control dispatcher.");
+
+ /* exit with success */
+ exit(EXIT_SUCCESS);
+}
+
+/**
+ * \brief Install suricata as service
+ *
+ * \param argc num of arguments
+ * \param argv passed arguments
+ */
+int SCServiceInstall(int argc, char **argv)
+{
+ char path[2048];
+ SC_HANDLE service = NULL;
+ SC_HANDLE scm = NULL;
+ int ret = -1;
+ int i = 0;
+
+ do {
+ memset(path, 0, sizeof(path));
+
+ if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){
+ SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError());
+ break;
+ }
+
+ /* skip name of binary itself */
+ for (i = 1; i < argc; i++) {
+ if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) {
+ continue;
+ }
+ strlcat(path, " ", sizeof(path) - strlen(path) - 1);
+ strlcat(path, argv[i], sizeof(path) - strlen(path) - 1);
+ }
+
+ if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
+ break;
+ }
+
+ service = CreateService(
+ scm,
+ PROG_NAME,
+ PROG_NAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ path,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (service == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't create service: %d", (int)GetLastError());
+ break;
+ }
+
+ ret = 0;
+
+ } while(0);
+
+ if (service) {
+ CloseServiceHandle(service);
+ }
+
+ if (scm) {
+ CloseServiceHandle(scm);
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Remove suricata service
+ *
+ * \param argc num of arguments
+ * \param argv passed arguments
+ */
+int SCServiceRemove(int argc, char **argv)
+{
+ SERVICE_STATUS status;
+ SC_HANDLE service = NULL;
+ SC_HANDLE scm = NULL;
+ int ret = -1;
+
+ do {
+ if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
+ break;
+ }
+
+ if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError());
+ break;
+ }
+
+ if (!QueryServiceStatus(service, &status)) {
+ SCLogError(SC_ERR_SVC, "Can't query service status: %d", (int)GetLastError());
+ break;
+ }
+
+ if (status.dwCurrentState != SERVICE_STOPPED) {
+ SCLogError(SC_ERR_SVC, "Service isn't in stopped state: %d", (int)GetLastError());
+ break;
+ }
+
+ if (!DeleteService(service)) {
+ SCLogError(SC_ERR_SVC, "Can't delete service: %d", (int)GetLastError());
+ break;
+ }
+
+ ret = 0;
+
+ } while(0);
+
+ if (service) {
+ CloseServiceHandle(service);
+ }
+
+ if (scm) {
+ CloseServiceHandle(scm);
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Change suricata service startup parameters
+ *
+ * \param argc num of arguments
+ * \param argv passed arguments
+ */
+int SCServiceChangeParams(int argc, char **argv)
+{
+ char path[2048];
+ SC_HANDLE service = NULL;
+ SC_HANDLE scm = NULL;
+ int ret = -1;
+ int i = 0;
+
+ do {
+ memset(path, 0, sizeof(path));
+
+ if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){
+ SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError());
+ break;
+ }
+
+ /* skip name of binary itself */
+ for (i = 1; i < argc; i++) {
+ if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) {
+ continue;
+ }
+ strlcat(path, " ", sizeof(path) - strlen(path) - 1);
+ strlcat(path, argv[i], sizeof(path) - strlen(path) - 1);
+ }
+
+ if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
+ break;
+ }
+
+ if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
+ SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError());
+ break;
+ }
+
+ if (!ChangeServiceConfig(
+ service,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ path,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ PROG_NAME))
+ {
+ SCLogError(SC_ERR_SVC, "Can't change service configuration: %d", (int)GetLastError());
+ break;
+ }
+
+ ret = 0;
+
+ } while(0);
+
+ return ret;
+}
+
+#endif /* OS_WIN32 */
diff --git a/framework/src/suricata/src/win32-service.h b/framework/src/suricata/src/win32-service.h
new file mode 100644
index 00000000..6be80171
--- /dev/null
+++ b/framework/src/suricata/src/win32-service.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2010 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Ondrej Slanina <oslanina@kerio.com>
+ */
+
+#ifndef __WIN32_SERVICE_H__
+#define __WIN32_SERVICE_H__
+
+#ifdef OS_WIN32
+int SCRunningAsService(void);
+int SCServiceInit(int argc, char **argv);
+int SCServiceInstall(int argc, char **argv);
+int SCServiceRemove(int argc, char **argv);
+int SCServiceChangeParams(int argc, char **argv);
+#endif /* OS_WIN32 */
+
+#endif
diff --git a/framework/src/suricata/src/win32-syslog.h b/framework/src/suricata/src/win32-syslog.h
new file mode 100644
index 00000000..78aa0667
--- /dev/null
+++ b/framework/src/suricata/src/win32-syslog.h
@@ -0,0 +1,80 @@
+/**
+ * syslog.h does not exist in the mingw environment, this file replaces it
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)syslog.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef __WIN32_SYSLOG_H__
+#define __WIN32_SYSLOG_H__
+
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+
+#define LOG_KERN (0<<3) /* kernel messages */
+#define LOG_USER (1<<3) /* random user-level messages */
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
+#define LOG_LPR (6<<3) /* line printer subsystem */
+#define LOG_NEWS (7<<3) /* network news subsystem */
+#define LOG_UUCP (8<<3) /* UUCP subsystem */
+#define LOG_CRON (9<<3) /* clock daemon */
+#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
+#define LOG_FTP (11<<3) /* ftp daemon */
+
+ /* other codes through 15 reserved for system use */
+#define LOG_LOCAL0 (16<<3) /* reserved for local use */
+#define LOG_LOCAL1 (17<<3) /* reserved for local use */
+#define LOG_LOCAL2 (18<<3) /* reserved for local use */
+#define LOG_LOCAL3 (19<<3) /* reserved for local use */
+#define LOG_LOCAL4 (20<<3) /* reserved for local use */
+#define LOG_LOCAL5 (21<<3) /* reserved for local use */
+#define LOG_LOCAL6 (22<<3) /* reserved for local use */
+#define LOG_LOCAL7 (23<<3) /* reserved for local use */
+
+
+/*
+ * The current win32 implementation of syslog is dummy and does nothing.
+ */
+#define closelog()
+#define openlog(__ident, __option, __facility)
+#define setlogmask (__mask)
+#define syslog(__pri, __fmt, __param)
+
+#endif